Files
rose-ash/plans/agent-briefings/loop.md
giles 9e568ad886 js-on-sx: baseline commit (278/280 unit, 148/148 slice, runner stub)
Initial commit of the lib/js/ tree and plans/ directory. A previous
session left template-string work in progress — 278/280 unit tests pass
(2 failing: tpl part-count off-by-one, escaped-backtick ident lookup).
test262-runner.py and scoreboard are placeholders (0/8 with 7 timeouts);
fixing the runner is the next queue item.
2026-04-23 19:42:16 +00:00

8.2 KiB

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/.gitignoretest262-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.sh254/254 pass (timeout 180s already applied).
  • bash lib/js/conformance.sh148/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 <a day's work, fix it, re-run the scoreboard, log the delta. Typical candidates, in likely order of impact:

    • Template strings with ${} (lexer read-template in lib/js/lexer.sx ~line 265 currently flattens — extend to split parts; transpile to js-add-chain with js-to-string on expression parts).
    • for...of + iterator protocol.
    • Array/object destructuring (declarations and function params).
    • Spread ... in call args / array / object literals.
    • UTF-16 JsString value type (big one — see "JsString shape" below).
    • Optional chaining ?., nullish assignment ??=, logical assignment &&= / ||=.
    • Private class fields #x.
    • Proper var hoisting + TDZ.
    • Strict mode detection and honoring.
    • Property descriptors + getters/setters (big — full meta-object protocol).
    • Symbol, Proxy, Reflect.
    • Generators (function* / yield) — needs call/cc machinery.
    • Async iterators / for await.
    • Tagged templates.
  6. When scoreboard plateaus, drop back to the deferred items from earlier phases: ASI (Phase 1), true CEK suspension on pending await (Phase 9).

JsString shape (for when you pick it up)

Tagged dict: {:__js_string__ true :utf16 <list-of-uint16> :str <lazy-utf8-cache-or-nil>}. :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.