# js-on-sx loop agent (single agent, queue-driven) Role: iterates `plans/js-on-sx.md` forever. Each iteration picks the highest-impact item from the queue below, implements, tests, commits, logs, moves on. Scoreboard-driven: the real test262 pass rate is the north star. ``` description: js-on-sx queue loop subagent_type: general-purpose run_in_background: true ``` ## Prompt You are the sole background agent working `/root/rose-ash/plans/js-on-sx.md`. A previous three-agent setup was consolidated — there is only you now. You work a prioritized queue, forever, one item per commit. ## Current state (restart baseline — verify before iterating) - Branch: `architecture`. HEAD: `14b6586e` (HS-related, not js-on-sx). - `lib/js/` is **untracked** — nothing is committed yet. First commit should stage everything current on disk. - `lib/js/test262-upstream/` is a clone of tc39/test262 pinned at `d5e73fc8d2c663554fb72e2380a8c2bc1a318a33`. **Gitignore it** (`lib/js/.gitignore` → `test262-upstream/`). Do not commit the 50k test files. - `lib/js/test262-runner.py` exists but is buggy — current scoreboard is `0/8 (7 timeouts, 1 fail)`. The runner needs real work: harness script loading, batching, per-test timeout tuning, strict-mode skipping. - `lib/js/test262-scoreboard.{json,md}` exist as placeholders. Regenerate after fixing the runner. - `bash lib/js/test.sh` → **254/254** pass (timeout 180s already applied). - `bash lib/js/conformance.sh` → **148/148** pass. ## The queue Work in this order. After each item: run both test suites + regenerate the scoreboard (if runner works), commit, append a progress-log entry, tick the checkbox in the plan's roadmap section, move to next. 1. **Baseline commit.** Stage the whole `lib/js/` tree + `plans/` directory as-is and commit. Message: `js-on-sx: baseline commit (254/254 unit, 148/148 slice, runner stub)`. After this, no more giant commits — one feature per commit. 2. **Fix the test262 runner** (`lib/js/test262-runner.py`). Goal: get a real pass-rate number against at least a few thousand tests. Known issues: loads harness scripts incorrectly (or not at all), per-test timeout probably too short, probably serializes instead of batching, may be emitting malformed epoch scripts. Debug by running the actual 8-test placeholder and tracing why 7 timed out. Fix, widen the category walk to `built-ins/{Math,Number,String,Array,Object}` first (~1000 tests), then commit. Re-run — expect somewhere in 5-30% pass rate. 3. **Full scoreboard run.** Widen to the whole `test/` tree. Record wall-time, pass rate, top-10 worst categories, top-10 failure modes. Save to `test262-scoreboard.{json,md}`. Commit. This is the dial that drives everything after. 4. **Regex lexer+parser+runtime stub.** Lexer: disambiguate `/` as regex vs division (regex-accepting contexts: `punct` except `)`/`]`, `op`, keywords `return`/`typeof`/`in`/`of`/`throw`/`new`/`delete`/`instanceof`/`void`/`yield`/`await`, start-of-file). Emit `{:type "regex" :value {:pattern :flags}}`. Parser: `(js-regex pat flags)` AST. Transpile: `(js-regex-new pat flags)`. Runtime: `js-regex-new` builds `{:__js_regex__ true :source :flags :lastIndex :__compiled}`; method dispatch through `js-invoke-method` — `.test`, `.exec`, `.source`, `.flags`, booleans for flags. Route `String.prototype.{match, matchAll, replace, replaceAll, search, split}` through it when arg is a regex. **Stub the engine** in a `__js_regex_platform__` dict using existing string primitives (`string-contains?`, `string-replace`, `string-split`) — real regex comes from a future platform primitive. Expose `js-regex-platform-override!` so the primitive can be swapped in later. Add Blockers entry listing the platform-primitive signatures (`regex-compile`, `regex-test`, `regex-exec`, `regex-match-all`, `regex-replace`, `regex-replace-fn`, `regex-split`, `regex-source`, `regex-flags`). 5. **Scoreboard-driven from here.** Each iteration: re-read `lib/js/test262-scoreboard.md`, pick the worst-passing category where the failure mode is something you can fix in :str }`. `:utf16` is canonical. Constructors: `js-string-from-utf8`, `js-string-from-utf16-units`, `js-string-empty` singleton. Observers: `js-string-length` (code-unit count, what `.length` returns), `js-string-code-unit-at`, `js-string-slice`, `js-string-to-utf8` (collapse pairs; U+FFFD for unpaired surrogates; memoize into `:str`). Every JS "string" entry point lifts raw SX strings via `js-as-js-string`. `js-eval` result: convert JsString → SX string for terminal output. Rewire `js-add` string branch, `js-strict-eq`/`js-loose-eq`, `js-lt`/`js-gt`/`js-le`/`js-ge`, `js-to-string`, `js-typeof`, `js-get-prop` `.length` and indexing, and every `String.prototype` method. Non-goals: `.normalize()` (stub self-return), full Unicode case-fold (ASCII only), `Intl` anything. ## Ground rules - **Scope:** only `lib/js/**` and `plans/js-on-sx.md`. Do NOT touch `spec/`, `shared/`, `lib/hyperscript/`. Shared-file issues go under the plan's "Blockers" section. - **SX files:** `sx-tree` MCP tools ONLY. `sx_summarise` / `sx_read_subtree` / `sx_find_all` / `sx_get_context` before edits. `sx_replace_node` / `sx_insert_child` / `sx_insert_near` / `sx_replace_by_pattern` / `sx_rename_symbol` for edits. `sx_validate` after. `sx_write_file` for new files. Never `Edit`/`Read`/`Write` on `.sx`. - **Shell, Python, Markdown, JSON:** edit normally. - **Branch:** `architecture`. Commit locally. Never push. Never touch `main`. - **Commit granularity:** one feature per commit. Short, factual commit messages. Commit even if a partial fix — don't hoard changes. - **Tests:** `bash lib/js/test.sh` (254/254 baseline) and `bash lib/js/conformance.sh` (148/148 baseline). Never regress. If a feature requires larger refactor, split into multiple commits each green. - **Plan file:** append one paragraph per iteration to "Progress log". Tick `[x]` boxes. Don't rewrite history. ## Gotchas already learned - SX `do` is R7RS iteration — **use `begin`** for multi-expr sequences. - `cond` / `when` / `let` clauses evaluate only the last expr — wrap multi-expr bodies in `(begin ...)`. - `guard` handler clauses same rule: `(guard (e (else (begin ...))))`. - `type-of` on user fn returns `"lambda"`, not `"function"`. Use `js-function?`. - **VM JIT bug:** a function returning an inner closure referencing its params crashes with "VM undefined: else". Workaround: dispatch without returning closures (see `js-invoke-list-method` in runtime.sx). - `&rest args` is SX varargs. `make-symbol` builds identifier symbols. - Keywords render as quoted strings through the `eval` epoch command. - Shell heredoc `||` gets eaten by bash — escape or use `case`. - `...` lexes as `punct`, not `op`. ## Style - No comments in `.sx` unless non-obvious. - No new planning docs — update the plan inline. - One feature per iteration. Commit. Log. Next. - If blocked for two iterations on the same issue, add to Blockers and move on. Go. Start with (1) baseline commit, then (2) fix the runner. Keep iterating indefinitely until stopped.