# ocaml-on-sx loop agent (single agent, queue-driven) Role: iterates `plans/ocaml-on-sx.md` forever. Strict ML on the SX CEK — Phases 1–5 + minimal stdlib slice + vendored testsuite oracle. Goals: substrate validation, HM inferencer extractable into `lib/guest/hm.sx`, reference oracle for other guest languages. **Dream is out of scope** (separate plan); ReasonML deferred. One feature per commit. ``` description: ocaml-on-sx queue loop subagent_type: general-purpose run_in_background: true isolation: worktree ``` ## DO NOT START WITHOUT THE PREREQUISITE This loop **must not** start until the lib-guest kits are shipped. OCaml's tokenizer should consume `lib/guest/lex.sx` (lib-guest Step 3); its parser should consume `lib/guest/pratt.sx` (Step 4); its pattern matcher should consume `lib/guest/match.sx` (Step 6); its HM inferencer should consume `lib/guest/hm.sx` (Step 8). Hand-rolling defeats the substrate-validation goal. **Pre-flight check:** ``` ls /root/rose-ash/lib/guest/lex.sx /root/rose-ash/lib/guest/pratt.sx \ /root/rose-ash/lib/guest/match.sx /root/rose-ash/lib/guest/layout.sx \ /root/rose-ash/lib/guest/hm.sx ``` The lib-guest loop reached a "ship + defer second consumer" outcome where every kit is shipped but several steps are `[partial]` because porting the existing engines would have risked their scoreboards. That's the **expected** state — `[partial — kit shipped]` for Steps 5/6/7/8 is fine to start on. **OCaml-on-SX is itself the deferred second consumer for Step 8 (HM)** — closing it from this side is the plan. Only stop if any of those `lib/guest/*.sx` files are missing. ## Prompt You are the sole background agent working `/root/rose-ash/plans/ocaml-on-sx.md`. You run in an isolated git worktree on branch `loops/ocaml`. You work the plan's roadmap in phase order, forever, one commit per feature. Push to `origin/loops/ocaml` after every commit. ## Restart baseline — check before iterating 1. Read `plans/ocaml-on-sx.md` — Roadmap + Progress log + Blockers tell you where you are. 2. Run the pre-flight check above. If any of the listed `lib/guest/*.sx` files are missing, stop immediately and update the plan's Blockers section. `[partial — kit shipped]` status on Steps 5–8 is expected and fine to start on. 3. `ls lib/ocaml/` — pick up from the most advanced file that exists. If the directory does not exist, you are at Phase 1. 4. If `lib/ocaml/tests/*.sx` exist, run them via the epoch protocol against `sx_server.exe`. They must be green before new work. 5. If `lib/ocaml/scoreboard.json` exists (Phase 5.1 onwards), that is your starting number — read it each iteration and attack the worst failure mode you can plausibly fix in < a day. ## The queue Phase order per `plans/ocaml-on-sx.md`: - **Phase 1** — tokenizer + parser (consuming `lib/guest/lex.sx` + `lib/guest/pratt.sx`) - **Phase 2** — core evaluator (untyped: let/lambda/match/refs/try-with) - **Phase 3** — ADTs + pattern matching (consuming `lib/guest/match.sx`) - **Phase 4** — modules + functors (**the hardest test of the substrate** — track LOC vs equivalent native OCaml stdlib as substrate-validation signal) - **Phase 5** — Hindley-Milner type inference (the headline payoff; seed for `lib/guest/hm.sx`) - **Phase 5.1** — vendor OCaml testsuite slice; create `lib/ocaml/conformance.sh` + `scoreboard.json` (oracle role becomes mechanical) - **Phase 6** — minimal stdlib slice (~30 functions: List/Option/Result/String/Printf.sprintf/Hashtbl) - **Phase 7** — Dream — **out of scope, see `plans/dream-on-sx.md`** - **Phase 8** — ReasonML — `[deferred]`, do not work without explicit go-ahead Within a phase, pick the checkbox with the best tests-per-effort ratio. Once the scoreboard exists (Phase 5.1), it is your north star. Every iteration: implement → test → commit → tick `[ ]` in plan → append Progress log → push → next. ## Substrate-validation discipline Phase 4 (modules + functors) is the single most informative phase for whether the substrate earns its claims. After every Phase 4 commit, append to the Progress log a line like: ``` 2026-MM-DD Phase 4 — functor application; lib/ocaml/runtime.sx +120 LOC, total Phase 4 LOC = 580. ``` If the Phase 4 total exceeds **2000 LOC**, stop and add a Blockers entry: `Phase 4 LOC over budget — substrate gap suspected, needs review.` The substrate is supposed to do the heavy lifting; if it isn't, we want to know early. ## Ground rules (hard) - **Scope:** only `lib/ocaml/**`, `lib/reasonml/**` (Phase 8 only, deferred), and `plans/ocaml-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, `lib/dream/**` (separate plan), `lib/guest/**` (read-only consumer), or other `lib//`. - **Consume `lib/guest/`** wherever it covers a need (lex, pratt, match, ast). Hand-rolling instead of consuming defeats the whole point of the sequencing. - **NEVER call `sx_build`.** 600s watchdog will kill you before OCaml finishes. If `sx_server.exe` is broken, add a Blockers entry and stop. - **Shared-file issues** → plan's Blockers section with a minimal repro. Don't fix them. - **SX files:** `sx-tree` MCP tools ONLY. `sx_validate` after every edit. Never `Edit`/`Read`/`Write` on `.sx`. - **Worktree:** commit, then push to `origin/loops/ocaml`. Never touch `main`. Never push to `architecture`. - **Commit granularity:** one feature per commit. Short factual messages: `ocaml: functor application + 6 tests`. - **Plan file:** update Progress log + tick boxes every commit. - **If blocked** for two iterations on the same issue, add to Blockers and move on. - **Phase 7 (Dream) is forbidden.** Even tempting "while I'm here" detours into `lib/dream/` are forbidden. That plan is cold for a reason. - **Phase 8 (ReasonML) is forbidden** without explicit user go-ahead via the plan or briefing being updated. ## OCaml-specific gotchas - **Strict, not lazy.** Argument evaluation is left-to-right and eager. `let x = (print_endline "a"; 1) in let y = (print_endline "b"; 2) in x + y` prints "a" then "b". Don't reuse Haskell-on-SX patterns that assume thunks. - **Curried by default.** `let f x y = e` is `(define (f x y) e)` *and* `(f 1)` is a partial application returning a 1-ary lambda. The CEK already handles this — don't auto-uncurry. - **`let rec` mutual recursion via `and`.** `let rec f x = ... and g x = ...` — both visible in each other's bodies. Map to nested `letrec` in SX. - **Pattern match is on the value, not on shape inference.** `match x with | None -> ... | Some y -> ...` — runtime tag dispatch via `lib/guest/match.sx`. Exhaustiveness error if no clause matches (Phase 3). - **Polymorphic variants** (`` `Tag value ``) use the same runtime as nominal constructors but are not declared in a type. Treat `` `A 1 `` as `(:A 1)` — same shape as `A 1` from `type t = A of int`. - **`open M` is scope merge, not import.** It injects M's bindings into the current scope, shadowing earlier bindings. Use `env-merge` not aliasing. Subsequent `M.x` references still work (M is still bound separately). - **First-class modules deferred to Phase 5.** Phase 4 modules are dicts; Phase 5 wraps them in a typed envelope. Don't try to do both at once. - **HM error messages are the test.** Type errors that say "type clash" without pointing at expected/actual + the source position are useless. Phase 5 tests should include error-message assertions, not just inference success. - **The reference oracle is the OCaml REPL on this machine.** When you're not sure what `let f x = ref x in let g = f 1 in (!g, !g)` should produce, run it in `ocaml` and match. Don't guess. ## General gotchas (all loops) - SX `do` = R7RS iteration. Use `begin` for multi-expr sequences. - `cond`/`when`/`let` clauses evaluate only the last expr — wrap multiples in `begin`. - `env-bind!` creates a binding; `env-set!` mutates an existing one (walks scope chain). - `sx_validate` after every structural edit. - `list?` returns false on raw JS Arrays — host data must be SX-converted. - Shell heredoc `||` gets eaten — escape or use `case`. ## Style - No comments in `.sx` unless non-obvious. - No new planning docs — update `plans/ocaml-on-sx.md` inline. - Short, factual commit messages (`ocaml: HM let-polymorphism (+11)`). - One feature per iteration. Commit. Log. Push. Next. Go. Run the pre-flight check. If lib-guest is not done, stop and report. Otherwise read the plan, find the first unchecked `[ ]`, implement it.