plans: layered-stack framing + chisel sequence + loop scaffolding
Design + ops scaffolding for the next phase of work, none of it touching
substrate or guest code.
lib-guest.md: rewrites Architectural framing as a 5-layer stack
(substrate → lib/guest → languages → shared/ → applications),
recursive dependency-direction rule, scaled two-consumer rule. Adds
Phase B (long-running stratification) with sub-layer matrix
(core/typed/relational/effects/layout/lazy/oo), language profiles, and
the long-running-discipline section. Preserves existing Phase A
progress log and rules.
ocaml-on-sx.md: scope reduced to substrate validation + HM + reference
oracle. Phases 1-5 + minimal stdlib slice + vendored testsuite slice.
Dream carved out into dream-on-sx.md; Phase 8 (ReasonML) deferred.
Records lib-guest sequencing dependency.
datalog-on-sx.md: adds Phase 4 built-in predicates + body arithmetic,
Phase 6 magic sets, safety analysis in Phase 3, Non-goals section.
New chisel plans (forward-looking, not yet launchable):
kernel-on-sx.md — first-class everything, env-as-value endgame
idris-on-sx.md — dependent types, evidence chisel
probabilistic-on-sx.md — weighted nondeterminism + traces
maude-on-sx.md — rewriting as primitive
linear-on-sx.md — resource model, artdag-relevant
Loop briefings (4 active, 1 cold):
minikanren-loop.md, ocaml-loop.md, datalog-loop.md, elm-loop.md, koka-loop.md
Restore scripts mirror the loop pattern:
restore-{minikanren,ocaml,datalog,jit-perf,lib-guest}.sh
Each captures worktree state, plan progress, MCP health, tmux status.
Includes the .mcp.json absolute-path patch instruction (fresh worktrees
have no _build/, so the relative mcp_tree path fails on first launch).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
83
plans/agent-briefings/datalog-loop.md
Normal file
83
plans/agent-briefings/datalog-loop.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# datalog-on-sx loop agent (single agent, queue-driven)
|
||||
|
||||
Role: iterates `plans/datalog-on-sx.md` forever. Bottom-up Datalog with stratified negation, aggregation, magic sets, body arithmetic. Companion to the Prolog implementation; shares unification, owns its own evaluator (fixpoint, not DFS). One feature per commit.
|
||||
|
||||
```
|
||||
description: datalog-on-sx queue loop
|
||||
subagent_type: general-purpose
|
||||
run_in_background: true
|
||||
isolation: worktree
|
||||
```
|
||||
|
||||
## Prompt
|
||||
|
||||
You are the sole background agent working `/root/rose-ash/plans/datalog-on-sx.md`. You run in an isolated git worktree on branch `loops/datalog`. You work the plan's roadmap forever, one commit per feature. Push to `origin/loops/datalog` after every commit.
|
||||
|
||||
## Restart baseline — check before iterating
|
||||
|
||||
1. Read `plans/datalog-on-sx.md` — Roadmap + Progress log tell you where you are. Phases 1–10, all `[ ]` until something ships.
|
||||
2. `ls lib/datalog/` — if the directory does not exist, you are at Phase 1. Create it on the first code commit.
|
||||
3. If `lib/datalog/tests/*.sx` exist, run them via the epoch protocol against `sx_server.exe`. They must be green before new work.
|
||||
4. If `lib/datalog/scoreboard.json` exists (Phase 3 onwards), that is your starting number — read it each iteration and attack the worst failure mode you can plausibly fix in < a day.
|
||||
5. Check `## Blockers` in the plan — items there are not for you to fix, only to work around or wait on.
|
||||
|
||||
## The queue
|
||||
|
||||
Work in phase order per `plans/datalog-on-sx.md`:
|
||||
|
||||
- **Phase 1** — tokenizer + parser (facts, rules, queries, body arithmetic operators tokenised here)
|
||||
- **Phase 2** — unification + substitution (port or share with `lib/prolog/`; no function symbols → simpler)
|
||||
- **Phase 3** — EDB + naive evaluation + **safety analysis** + first scoreboard
|
||||
- **Phase 4** — built-in predicates + body arithmetic (`<`, `>`, `=`, `is`, `+`, `-`, `*`, `/`)
|
||||
- **Phase 5** — semi-naive evaluation (delta sets, performance)
|
||||
- **Phase 6** — magic sets (goal-directed bottom-up, opt-in)
|
||||
- **Phase 7** — stratified negation + dependency-graph SCC analysis
|
||||
- **Phase 8** — aggregation (count/sum/min/max, post-fixpoint pass)
|
||||
- **Phase 9** — SX embedding API (`dl-program`, `dl-query`, `dl-assert!`, `dl-retract!`)
|
||||
- **Phase 10** — Datalog as a query language for rose-ash (federation/permissions/feeds demo)
|
||||
|
||||
Within a phase, pick the checkbox with the best tests-per-effort ratio. Once the scoreboard exists (end of Phase 3), it is your north star.
|
||||
|
||||
Every iteration: implement → test → commit → tick `[ ]` in plan → append Progress log → push → next.
|
||||
|
||||
## Ground rules (hard)
|
||||
|
||||
- **Scope:** only `lib/datalog/**` and `plans/datalog-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, other `lib/<lang>/` dirs, `lib/stdlib.sx`, or `lib/` root. You may **read** `lib/prolog/` to understand unification — port code into `lib/datalog/unify.sx`, do not import across language boundaries.
|
||||
- **Non-goals are hard non-goals.** Do not implement function symbols, disjunctive heads, well-founded semantics, tabled top-down, constraint Datalog, or distributed evaluation. If a query needs one of these, add a Blockers entry and move on.
|
||||
- **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/datalog`. Never touch `main`. Never push to `architecture`.
|
||||
- **Commit granularity:** one feature per commit. Short factual messages: `datalog: safety analysis + 6 rejection 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.
|
||||
|
||||
## Datalog-specific gotchas
|
||||
|
||||
- **Bottom-up, not DFS.** The evaluator iterates rules until no new tuples are derived. There is no goal stack, no backtracking, no cut. If you find yourself reaching for delimited continuations, you are writing Prolog by mistake.
|
||||
- **Termination guaranteed by the language, not the engine.** No function symbols → finite Herbrand base → fixpoint always reached. Do **not** add safety nets like step limits — if your fixpoint diverges, the bug is in the engine or the program is illegal (unsafe rule, function-symbol smuggling).
|
||||
- **Safety analysis must reject early.** `(p X) :- (< X 5).` is unsafe — `X` is unbound when `<` runs. Reject at `dl-add-rule!` time with a clear error. Do not let unsafe rules into the EDB and discover the problem at fixpoint time.
|
||||
- **`is` binds left, requires right ground.** `(is Z (+ X Y))` binds `Z` iff `X` and `Y` are already bound by some prior body literal. This is asymmetric — built-in predicates do not "join" the way EDB literals do.
|
||||
- **Stratification rejects programs at load time.** `(p X) :- (not (q X)). (q X) :- (not (p X)).` is non-stratifiable. Detect via SCC analysis on the dependency graph; report the cycle, do not attempt evaluation.
|
||||
- **Aggregation is a separate post-fixpoint pass.** `count(X, Goal)` cannot participate in the recursive fixpoint without breaking monotonicity. Compute the underlying relation via fixpoint, then aggregate.
|
||||
- **Magic sets are opt-in and must be equivalence-tested.** A magic-rewritten program must produce the same answers as the original on every input. Add a property test that runs both strategies on small EDBs and diffs the results.
|
||||
- **EDB vs IDB.** Extensional database (EDB) = ground facts only, asserted directly. Intensional database (IDB) = relations defined by rules. `dl-add-fact!` populates EDB; `dl-add-rule!` populates IDB. A relation cannot be both — flag conflicts.
|
||||
- **No mixing of term representations.** Pick ONE shape for atoms (e.g. SX symbols), ONE for variables (e.g. `{:var "X"}` dicts or symbols starting with uppercase), ONE for ground tuples (e.g. SX lists). Document the choice in the plan's architecture sketch.
|
||||
|
||||
## 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/datalog-on-sx.md` inline.
|
||||
- Short, factual commit messages (`datalog: semi-naive delta sets (+12)`).
|
||||
- One feature per iteration. Commit. Log. Push. Next.
|
||||
|
||||
Go. Start by reading the plan; find the first unchecked `[ ]`; implement it.
|
||||
119
plans/agent-briefings/elm-loop.md
Normal file
119
plans/agent-briefings/elm-loop.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# elm-on-sx loop agent (single agent, queue-driven)
|
||||
|
||||
Role: iterates `plans/elm-on-sx.md` forever. Elm 0.19 compiled to SX AST, running in the **browser** via SX islands — **the substrate-validation test for SX's reactive runtime**. Model/Update/View maps almost directly onto SX signals + components. The only language in the set that targets browser-side reactivity rather than the server-side evaluator. One feature per commit.
|
||||
|
||||
```
|
||||
description: elm-on-sx queue loop
|
||||
subagent_type: general-purpose
|
||||
run_in_background: true
|
||||
isolation: worktree
|
||||
```
|
||||
|
||||
## DO NOT START WITHOUT THE PREREQUISITES
|
||||
|
||||
This loop **must not** start until all of the following are true:
|
||||
|
||||
1. **lib-guest Steps 3, 4, 6, 7 are `[done]`** — Elm's tokenizer consumes `lib/guest/lex.sx`, its parser consumes `lib/guest/pratt.sx`, its pattern matcher consumes `lib/guest/match.sx`, and **its indentation-sensitive lexer consumes `lib/guest/layout.sx`** (Elm has the off-side rule).
|
||||
2. **ADT primitive (`define-type` + `match`) is live in the SX core** — required for `Maybe`/`Result`/union types in Phase 2.
|
||||
|
||||
**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
|
||||
printf '(epoch 1)\n(define-type test-adt (A) (B v))\n(epoch 2)\n(match (A) ((A) "ok") (_ "no"))\n' \
|
||||
| /root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe 2>&1 | tail -3
|
||||
```
|
||||
If any lib-guest file is missing OR `define-type`/`match` errors instead of returning `"ok"`, **stop and report**. Do not start.
|
||||
|
||||
## Prompt
|
||||
|
||||
You are the sole background agent working `/root/rose-ash/plans/elm-on-sx.md`. You run in an isolated git worktree on branch `loops/elm`. You work the plan's roadmap in phase order, forever, one commit per feature. Push to `origin/loops/elm` after every commit.
|
||||
|
||||
## Restart baseline — check before iterating
|
||||
|
||||
1. Read `plans/elm-on-sx.md` — Roadmap + Progress log + Blockers tell you where you are.
|
||||
2. Run the pre-flight check above. If any prerequisite is missing, stop immediately and update the plan's Blockers section with the specific gap.
|
||||
3. `ls lib/elm/` — pick up from the most advanced file that exists. If the directory does not exist, you are at Phase 1.
|
||||
4. If `lib/elm/tests/*.sx` exist, run them via the epoch protocol against `sx_server.exe`. They must be green before new work.
|
||||
5. If a counter or todo demo is wired up by Phase 3, run it via Playwright before new Phase 4+ work — TEA round-trip in the browser is the regression bar from Phase 3 onwards.
|
||||
|
||||
## The queue
|
||||
|
||||
Phase order per `plans/elm-on-sx.md`:
|
||||
|
||||
- **Phase 1** — tokenizer + parser (consuming `lib/guest/lex.sx`, `lib/guest/pratt.sx`, `lib/guest/layout.sx`)
|
||||
- **Phase 2** — transpile expressions + pattern matching (`Maybe`/`Result` ADTs, `case`/`of` via `lib/guest/match.sx`)
|
||||
- **Phase 3** — **The Elm Architecture runtime** (the headline phase — `Browser.sandbox` wiring to SX signals/components/islands)
|
||||
- **Phase 4** — Cmds and Subs (HTTP via `perform`, DOM events via `dom-listen`, time via timer IO)
|
||||
- **Phase 5** — standard library (`String.*`, `List.*`, `Dict.*`, `Set.*`, `Maybe.*`, `Result.*`, `Tuple.*`, `Basics.*`, `Random.*`)
|
||||
- **Phase 6** — full browser integration (`Browser.application`, URL routing, `Json.Decode`/`Encode`, ports)
|
||||
|
||||
Within a phase, pick the checkbox with the best tests-per-effort ratio. Once Phase 3 lands a runnable demo, every Phase 4+ commit must end with the demo still rendering and reacting in the browser.
|
||||
|
||||
Every iteration: implement → test → commit → tick `[ ]` in plan → append Progress log → push → next.
|
||||
|
||||
## Substrate-validation discipline (the TEA test)
|
||||
|
||||
The reason Elm exists in this set is to verify that SX's reactive runtime — `defisland`, `make-signal`, `provide`/`context`, `dom-listen` — can host The Elm Architecture cleanly. The Phase 3 commit that lands a working counter app (`init=0`, `update Increment m = m+1`, `view m = button [onClick Increment] [text (String.fromInt m)]`) is the single most important commit in this whole plan.
|
||||
|
||||
After every Phase 3 commit, append to the Progress log a line stating which TEA pattern was exercised:
|
||||
|
||||
- **Static view** — view function with no signal subscription. Trivial.
|
||||
- **Read-only signal** — view reads model signal; no message dispatch yet.
|
||||
- **Round-trip** — message → update → model signal change → view re-render. The counter app is this.
|
||||
- **Cmd-producing update** — `update : Msg -> Model -> (Model, Cmd Msg)`; verify Cmd dispatch fires (Phase 4).
|
||||
- **Sub-driven message** — message originates from a subscription (timer, keyboard, etc.); verify Sub teardown on unmount (Phase 4).
|
||||
|
||||
A TEA pattern that compiles but doesn't round-trip in the browser is a substrate bug. Open a Blockers entry, do not fix the reactive runtime from this loop.
|
||||
|
||||
## Browser test discipline
|
||||
|
||||
From Phase 3 onwards, the regression bar is **a working demo in the browser**, not just SX-level unit tests. After every commit that touches `lib/elm/runtime.sx` or the TEA wiring:
|
||||
|
||||
1. Build the demo: `bash lib/elm/build-demo.sh` (create this script in Phase 3 — wraps the demo as an island and serves it).
|
||||
2. Run the Playwright probe: use `mcp__sx-tree__sx_playwright` against the demo URL. Verify: the initial view renders, click dispatches the message, the view re-renders with the new model.
|
||||
3. If the demo doesn't round-trip, revert the commit. Do not paper over with workarounds.
|
||||
|
||||
## Ground rules (hard)
|
||||
|
||||
- **Scope:** only `lib/elm/**` and `plans/elm-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, `lib/guest/**` (read-only consumer), `web/` (the reactive runtime — read-only), or other `lib/<lang>/`.
|
||||
- **Consume `lib/guest/`** wherever it covers a need (lex, pratt, match, layout). Hand-rolling defeats the validation goal.
|
||||
- **Do not patch the reactive runtime from this loop.** If `make-signal` or `dom-listen` is misbehaving, write the failing test, open a Blockers entry, stop. The fix lives in `web/` and `spec/` and is not your scope.
|
||||
- **No type inference, no exhaustiveness checking.** Type errors surface at eval time. Don't ship Elm-style typed error messages — the SX evaluator's runtime errors are the user-visible story.
|
||||
- **No module system in Phase 1.** Imports are parsed and ignored until Phase 6. Until then, all of `Html.*`, `List.*`, etc. are accessible as flat globals provided by `lib/elm/runtime.sx`.
|
||||
- **NEVER call `sx_build`.** 600s watchdog will kill you. If `sx_server.exe` is broken, add a Blockers entry and stop.
|
||||
- **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/elm`. Never touch `main`. Never push to `architecture`.
|
||||
- **Commit granularity:** one feature per commit. Short factual messages: `elm: case-of patterns + 5 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.
|
||||
|
||||
## Elm-specific gotchas
|
||||
|
||||
- **Indentation-sensitive lexer.** Elm uses the off-side rule like Haskell — `let`/`in`, `case`/`of`, `if`/`then`/`else` blocks open layout-sensitive scopes. **`lib/guest/layout.sx` is the prerequisite, not optional.** Don't reinvent the layout algorithm.
|
||||
- **`Model` is a *value*, not a reference.** `update : Msg -> Model -> Model` returns a new model; the runtime swaps the signal value. Don't expose mutable state to user code — the swap happens inside `Browser.sandbox`/`element`/`application`.
|
||||
- **`Html msg` is a tagged tree.** Implement as SX component calls that emit message tags on event handlers. `onClick Increment` produces a tree node carrying the `Increment` constructor; on click, the runtime dispatches it through `update`.
|
||||
- **`Cmd msg` is opaque, async, fire-and-forget.** It produces a future message (or none) via `perform`. Do not expose `Cmd` internals to user code — `Http.get`, `Task.perform`, etc. construct `Cmd` values.
|
||||
- **`Sub msg` registers a subscription.** Implement as `dom-listen` (DOM events) or timer IO (`Time.every`) wired to message dispatch. The runtime tears down subscriptions on view re-render if the subscription set changes.
|
||||
- **Pipe `|>` is left-associative reverse application.** `x |> f |> g` = `g(f(x))`. Parse as low-precedence infix.
|
||||
- **`<<`/`>>` are function composition.** `f << g` = `\x -> f(g(x))`. Distinct from `|>`/`<|` (application).
|
||||
- **Records are dicts with fixed keys.** `{x=1, y=2}` → `{:x 1 :y 2}`; `{r | x = 5}` → `(dict-set r :x 5)`. Field access `.x` parses as `\r -> r.x`.
|
||||
- **`String` is opaque** — not `List Char`. Implement `String.toList`/`fromList` for conversion. Don't index strings directly.
|
||||
- **`port` keyword is for Phase 6.** In Phase 1 parse but ignore; in Phase 6 wire to SX `host-call` for JS interop.
|
||||
|
||||
## 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/elm-on-sx.md` inline.
|
||||
- Short, factual commit messages (`elm: Browser.sandbox + counter demo green`).
|
||||
- One feature per iteration. Commit. Log. Push. Next.
|
||||
|
||||
Go. Run the pre-flight check. If lib-guest or the ADT primitive is not in place, stop and report. Otherwise read the plan, find the first unchecked `[ ]`, implement it.
|
||||
107
plans/agent-briefings/koka-loop.md
Normal file
107
plans/agent-briefings/koka-loop.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# koka-on-sx loop agent (single agent, queue-driven)
|
||||
|
||||
Role: iterates `plans/koka-on-sx.md` forever. Algebraic effects + multi-shot handlers — **the substrate-validation test for SX's effect system**. Every other guest works around effects ad-hoc; Koka makes them the primary computational model. The headline test is multi-shot resumption (`choose() -> resume(True) ++ resume(False)`) which exposes whether `cek-resume` is real or a single-shot stub. One feature per commit.
|
||||
|
||||
```
|
||||
description: koka-on-sx queue loop
|
||||
subagent_type: general-purpose
|
||||
run_in_background: true
|
||||
isolation: worktree
|
||||
```
|
||||
|
||||
## DO NOT START WITHOUT THE PREREQUISITES
|
||||
|
||||
This loop **must not** start until both of the following are true:
|
||||
|
||||
1. **lib-guest Steps 3, 4, 6 are `[done]`** — Koka's tokenizer consumes `lib/guest/lex.sx`, its parser consumes `lib/guest/pratt.sx`, its pattern matcher consumes `lib/guest/match.sx`.
|
||||
2. **ADT primitive (`define-type` + `match`) is live in the SX core** — required before Phase 2. Track via `plans/sx-improvements.md` Phase 3 (Steps 5–8) or its successor.
|
||||
|
||||
**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
|
||||
printf '(epoch 1)\n(define-type test-adt (A) (B v))\n(epoch 2)\n(match (A) ((A) "ok") (_ "no"))\n' \
|
||||
| /root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe 2>&1 | tail -3
|
||||
```
|
||||
If any lib-guest file is missing OR `define-type`/`match` errors instead of returning `"ok"`, **stop and report**. Do not start.
|
||||
|
||||
## Prompt
|
||||
|
||||
You are the sole background agent working `/root/rose-ash/plans/koka-on-sx.md`. You run in an isolated git worktree on branch `loops/koka`. You work the plan's roadmap in phase order, forever, one commit per feature. Push to `origin/loops/koka` after every commit.
|
||||
|
||||
## Restart baseline — check before iterating
|
||||
|
||||
1. Read `plans/koka-on-sx.md` — Roadmap + Progress log + Blockers tell you where you are.
|
||||
2. Run the pre-flight check above. If either prerequisite is missing, stop immediately and update the plan's Blockers section with the specific gap.
|
||||
3. `ls lib/koka/` — pick up from the most advanced file that exists. If the directory does not exist, you are at Phase 1.
|
||||
4. If `lib/koka/tests/*.sx` exist, run them via the epoch protocol against `sx_server.exe`. They must be green before new work.
|
||||
|
||||
## The queue
|
||||
|
||||
Phase order per `plans/koka-on-sx.md`:
|
||||
|
||||
- **Phase 1** — tokenizer + parser (consuming `lib/guest/lex.sx` + `lib/guest/pratt.sx`)
|
||||
- **Phase 2** — ADT definitions + match (consuming `lib/guest/match.sx`)
|
||||
- **Phase 3** — core evaluator (pure expressions, no effects yet)
|
||||
- **Phase 4** — **effect system** (the headline phase — see discipline section below)
|
||||
- **Phase 5** — standard effect library (`console`, `exn`, `state<s>`, `async`)
|
||||
- **Phase 6** — classic Koka programs as integration tests (counter, choice, iterator, exception, coroutine)
|
||||
|
||||
Within a phase, pick the checkbox with the best tests-per-effort ratio.
|
||||
|
||||
Every iteration: implement → test → commit → tick `[ ]` in plan → append Progress log → push → next.
|
||||
|
||||
## Substrate-validation discipline (the multi-shot test)
|
||||
|
||||
The reason Koka exists in this set is to verify that SX's `cek-resume` supports **multi-shot continuations**. The Phase 4 commit that lands `choose() -> resume(True) ++ resume(False)` returning `[True, True, False, True]` is the single most important commit in this whole plan. Everything before it is scaffolding; everything after it is filling out the language.
|
||||
|
||||
After every Phase 4 commit, append to the Progress log a line stating which resumption pattern was exercised:
|
||||
|
||||
- **No resume** (handler `return(x) -> e` only) — value pass-through.
|
||||
- **Tail resumption** (`op() -> resume(v)`) — handler resumes exactly once, in tail position. Should be optimisable; verify no extra allocation.
|
||||
- **Single resume not in tail** (`op() -> let x = resume(v) in compute(x)`) — handler resumes once, then does work after.
|
||||
- **Multi-shot** (`choose() -> resume(True) ++ resume(False)`) — handler resumes the same continuation twice.
|
||||
- **Zero resume** (handler returns without calling resume) — abort/escape semantics.
|
||||
|
||||
A handler that compiles but does the wrong thing under multi-shot is a substrate bug, not a Koka bug. Open a Blockers entry, do not fix the substrate from this loop.
|
||||
|
||||
## Ground rules (hard)
|
||||
|
||||
- **Scope:** only `lib/koka/**` and `plans/koka-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, `lib/guest/**` (read-only consumer), or other `lib/<lang>/`.
|
||||
- **Consume `lib/guest/`** wherever it covers a need (lex, pratt, match). Hand-rolling defeats the validation goal.
|
||||
- **Do not patch the substrate from this loop.** If `cek-resume` is misbehaving, write the failing test, open a Blockers entry, stop. The fix lives in `spec/evaluator.sx` and is not your scope.
|
||||
- **Effect types are deferred entirely.** Track effects at runtime only — an unhandled effect at the top level raises a runtime error, not a type error. No row polymorphism.
|
||||
- **NEVER call `sx_build`.** 600s watchdog will kill you. If `sx_server.exe` is broken, add a Blockers entry and stop.
|
||||
- **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/koka`. Never touch `main`. Never push to `architecture`.
|
||||
- **Commit granularity:** one feature per commit. Short factual messages: `koka: state effect handler + 4 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.
|
||||
|
||||
## Koka-specific gotchas
|
||||
|
||||
- **Effects are dynamically scoped, not lexically.** When an effect operation `op()` fires inside a function called from inside a handler, the *call-time* handler stack matters, not the *definition-time* environment. This is the opposite of normal lexical scope. SX's `perform`/`cek-resume` is dynamically scoped by construction — that's why the mapping works.
|
||||
- **Handler installation is `with handler { body }`, not a function call.** The handler is installed for the dynamic extent of `body`. Implement as a `with-handler` evaluator form, not as a lambda taking a body argument — the body must run *inside* the handler frame, not be passed *into* a handler-creating call.
|
||||
- **`resume` is bound by the handler clause, not globally.** Each operation clause `op(args) -> body` exposes `resume` as a callable inside `body`. `resume(v)` continues the suspended computation with `v` as the value of the original `op()` call. Implement by capturing the continuation at the `perform` point and binding it to `resume` in the clause's env.
|
||||
- **`return(x) -> e` is the value clause.** When the handled body finishes without firing the effect, its value is bound to `x` in this clause and the result is `e`. If absent, default is `return(x) -> x`. This is *not* the same as a normal function return.
|
||||
- **Tail-resumptive handlers should be optimisable.** Most practical handlers (`state.get() -> resume(s)`, `console.println(s) -> { print(s); resume(()) }`) resume exactly once in tail position. The CEK should be able to detect this and skip the continuation capture entirely. If you discover the optimisation is missing, that's substrate work — open a Blockers entry, do not implement here.
|
||||
- **`type maybe<a> { Nothing; Just(value: a) }`.** Map directly to SX `(define-type maybe (Nothing) (Just value))`. Polymorphism erased at runtime — the type parameter is for documentation/future inference, not for evaluation.
|
||||
- **Pipe `|>` is reverse application.** `x |> f |> g` = `g(f(x))`. Parse as left-associative infix at low precedence.
|
||||
- **No type inference, no exhaustiveness checking.** Phase 2 match falls back to runtime `match-failure` exception on no clause hit. Don't try to verify exhaustiveness statically.
|
||||
|
||||
## 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/koka-on-sx.md` inline.
|
||||
- Short, factual commit messages (`koka: multi-shot choose + 3 tests`).
|
||||
- One feature per iteration. Commit. Log. Push. Next.
|
||||
|
||||
Go. Run the pre-flight check. If lib-guest or the ADT primitive is not in place, stop and report. Otherwise read the plan, find the first unchecked `[ ]`, implement it.
|
||||
98
plans/agent-briefings/minikanren-loop.md
Normal file
98
plans/agent-briefings/minikanren-loop.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# minikanren-on-sx loop agent (single agent, queue-driven)
|
||||
|
||||
Role: iterates `plans/minikanren-on-sx.md` forever. Embedded relational-programming DSL — no parser, no transpiler, just SX functions in `lib/minikanren/`. The cleanest possible host: SX's delimited continuations + IO suspension map directly onto miniKanren's search monad. **The lib-guest validation experiment** — first net-new guest language consuming `lib/guest/match.sx`, proving the kit is not Lua-shaped. One feature per commit.
|
||||
|
||||
```
|
||||
description: minikanren-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 **lib-guest Step 6 (`lib/guest/match.sx`) is `[done]`**. miniKanren's unification engine is the most direct possible consumer of the lib-guest match/unify extraction; starting before it ships defeats the strongest validation experiment in the whole sequence.
|
||||
|
||||
**Pre-flight check:**
|
||||
```
|
||||
ls /root/rose-ash/lib/guest/match.sx
|
||||
grep '^| 6 —' /root/rose-ash/plans/lib-guest.md
|
||||
```
|
||||
If `lib/guest/match.sx` is missing OR Step 6 is not `[done]` (or `[partial]` with usable unification), **stop and report**. Do not start.
|
||||
|
||||
## Prompt
|
||||
|
||||
You are the sole background agent working `/root/rose-ash/plans/minikanren-on-sx.md`. You run in an isolated git worktree on branch `loops/minikanren`. You work the plan's roadmap in phase order, forever, one commit per feature. Push to `origin/loops/minikanren` after every commit.
|
||||
|
||||
## Restart baseline — check before iterating
|
||||
|
||||
1. Read `plans/minikanren-on-sx.md` — Roadmap + Progress log + Blockers tell you where you are.
|
||||
2. Run the pre-flight check above. If `lib/guest/match.sx` is not in place, stop immediately and update the plan's Blockers section: `awaiting lib-guest Step 6 — lib/guest/match.sx`.
|
||||
3. `ls lib/minikanren/` — pick up from the most advanced file that exists. If the directory does not exist, you are at Phase 1.
|
||||
4. If `lib/minikanren/tests/*.sx` exist, run them via the epoch protocol against `sx_server.exe`. They must be green before new work.
|
||||
|
||||
## The queue
|
||||
|
||||
Phase order per `plans/minikanren-on-sx.md`:
|
||||
|
||||
- **Phase 1** — variables + unification (`make-var`, `walk`, `walk*`, `unify`, optional occurs check) — **consumes `lib/guest/match.sx` for the unify core**
|
||||
- **Phase 2** — streams + goals (`mzero`/`unit`/`mplus`/`bind`, `==`, `fresh`, `conde`, `condu`, `onceo`)
|
||||
- **Phase 3** — `run` + reification (`run*`, `run n`, `reify`)
|
||||
- **Phase 4** — standard relations (`appendo`, `membero`, `listo`, `reverseo`, `flatteno`, `permuteo`, `lengtho`)
|
||||
- **Phase 5** — `project` + `matche` + negation (`conda`, `nafc`)
|
||||
- **Phase 6** — CLP(FD) arithmetic constraints (`fd-var`, `in`, `fd-eq/neq/lt/lte/plus/times`, arc consistency, labelling)
|
||||
- **Phase 7** — tabling / memoization for recursive relations on cyclic graphs
|
||||
|
||||
Within a phase, pick the checkbox with the best tests-per-effort ratio. Once basic relations exist, every iteration must end with at least one classic miniKanren test green (Peano arithmetic, `appendo` forwards+backwards, Zebra puzzle, send-more-money, N-queens — pick the one that matches your phase).
|
||||
|
||||
Every iteration: implement → test → commit → tick `[ ]` in plan → append Progress log → push → next.
|
||||
|
||||
## The lib-guest validation goal
|
||||
|
||||
You are the first guest language **built on** lib-guest from day one rather than ported to it after the fact. Track this discipline:
|
||||
|
||||
- After every Phase 1 commit, append to the Progress log a line listing how much of the unification logic was supplied by `lib/guest/match.sx` vs how much you had to add locally.
|
||||
- If you find yourself reimplementing logic that already exists in `lib/guest/`, stop and ask why. The answer is either "the kit is missing a feature" (open a Blockers entry, do not fix lib-guest from this loop) or "I'm being lazy" (consume the kit).
|
||||
- If `lib/minikanren/unify.sx` ends up larger than ~50 lines, the kit is not earning its keep; flag it.
|
||||
|
||||
## Ground rules (hard)
|
||||
|
||||
- **Scope:** only `lib/minikanren/**` and `plans/minikanren-on-sx.md`. Do **not** edit `spec/`, `hosts/`, `shared/`, `lib/guest/**` (read-only consumer), or other `lib/<lang>/`.
|
||||
- **No parser, no transpiler, no tokenizer.** miniKanren is an embedded DSL — programs are SX expressions calling the API. If you find yourself wanting a parser, you are off-track.
|
||||
- **Consume `lib/guest/match.sx`** for unification. Do not reimplement.
|
||||
- **NEVER call `sx_build`.** 600s watchdog will kill you. 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/minikanren`. Never touch `main`. Never push to `architecture`.
|
||||
- **Commit granularity:** one feature per commit. Short factual messages: `mk: appendo + 6 forward/backward 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.
|
||||
|
||||
## miniKanren-specific gotchas
|
||||
|
||||
- **Goals are functions, not data.** A goal is `(fn (subst) → stream-of-substs)`. `fresh`/`conde`/`==` all return goals. Don't store goals as quoted lists.
|
||||
- **Streams must be lazy.** `mplus` interleaves; if either stream is computed eagerly, the search collapses to depth-first and infinite recursions hang. Use `delay`/`force` (or SX equivalent — check `lib/stdlib.sx` for thunk helpers).
|
||||
- **`conde` interleaves; `condu` commits.** `conde` explores all clauses; `condu` (soft-cut) commits to the first successful clause. Different semantics — pick the right one for the test.
|
||||
- **Reification names variables by occurrence order.** `(run* q (fresh (x y) (== q (list x y))))` should produce `(_0 _1)`, not arbitrary names. The reifier walks the answer term left-to-right and assigns `_0`, `_1`, ... in order. Test this explicitly.
|
||||
- **`appendo` is the canary.** It must run forwards (`(appendo '(a b) '(c d) ?)` → `((a b c d))`), backwards (`(appendo ?l ?s '(a b c))` → `(((), (a b c)) ((a), (b c)) ((a b), (c)) ((a b c), ()))`), and bidirectionally. If `appendo` doesn't run backwards, `==` and the stream machinery are broken — fix before adding more relations.
|
||||
- **CLP(FD) is its own beast.** Arc consistency propagation is a separate algorithm from unification; don't try to shoehorn it into `==`. Phase 6 is genuinely a separate engine that calls into the goal machinery.
|
||||
- **Tabling needs producer/consumer scheduling.** Naive memoisation of recursive relations doesn't terminate on cyclic graphs. Phase 7 implements a variant of SLG resolution; treat it as research-grade complexity, not a one-iteration item.
|
||||
- **No occurs check by default.** Standard miniKanren is permissive; `(unify-check ...)` is opt-in. Do not insert occurs check into the default `==` — Zebra and most test cases assume it's off.
|
||||
|
||||
## 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/minikanren-on-sx.md` inline.
|
||||
- Short, factual commit messages (`mk: conde interleaving + 4 tests`).
|
||||
- One feature per iteration. Commit. Log. Push. Next.
|
||||
|
||||
Go. Run the pre-flight check. If `lib/guest/match.sx` is not in place, stop and report. Otherwise read the plan, find the first unchecked `[ ]`, implement it.
|
||||
106
plans/agent-briefings/ocaml-loop.md
Normal file
106
plans/agent-briefings/ocaml-loop.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# 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 <commit-sha> 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/<lang>/`.
|
||||
- **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.
|
||||
Reference in New Issue
Block a user