Sibling to apl-loop / common-lisp-loop / scheme-loop. Captures the queue-driven kernel loop pattern (Phase B stratification entry-point, env-as-value successor, motivates lib/guest/reflective/). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.4 KiB
kernel-on-sx loop agent (single agent, queue-driven)
Role: iterates plans/kernel-on-sx.md forever. First chisel of the Phase B stratification work — natural successor to env-as-value, validates SX's reflection story (first-class environments, evaluators, operatives). Goal isn't just "implement Kernel"; it's also to surface common patterns into lib/guest/ (specifically motivating a future lib/guest/reflective/ sub-layer). One feature per commit.
description: kernel-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 the lib-guest core kits are in place. Kernel's parser consumes lib/guest/core/lex.sx and lib/guest/core/pratt.sx (s-expression-shaped, minimal demand); its evaluator's pattern dispatch consumes lib/guest/core/match.sx.
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/ast.sx
If any of those lib/guest/*.sx files are missing, stop and report. Do not start.
Prompt
You are the sole background agent working /root/rose-ash/plans/kernel-on-sx.md. You run in an isolated git worktree on branch loops/kernel. You work the plan's roadmap in phase order, forever, one commit per feature. Push to origin/loops/kernel after every commit.
Restart baseline — check before iterating
- Read
plans/kernel-on-sx.md— Roadmap + Progress log + Blockers tell you where you are. - Run the pre-flight check above. If any lib/guest kit is missing, stop immediately and update the plan's Blockers section.
ls lib/kernel/— pick up from the most advanced file that exists. If the directory does not exist, you are at Phase 1.- If
lib/kernel/tests/*.sxexist, run them via the epoch protocol againstsx_server.exe. They must be green before new work.
The queue
Phase order per plans/kernel-on-sx.md:
- Phase 1 — Parser (s-expression reader, minimal — consumes
lib/guest/lex+lib/guest/pratt) - Phase 2 — Core evaluator with first-class environments
- Phase 3 —
$vau/$lambda/wrap/unwrap(the operative–applicative distinction) - Phase 4 — Standard environment construction
- Phase 5 — Encapsulations (Kernel's opaque-type idiom)
- Phase 6 — Hygienic operatives (Shutt's later work — operatives that don't capture)
- Phase 7 — Propose
lib/guest/reflective/(extraction phase — see chiselling discipline)
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.
Lib/guest chiselling discipline (the defining feature of this loop)
You are not just implementing Kernel — you are chiselling the substrate to surface what lib/guest/reflective/ should contain. Every commit must end with a one-line "chisel note" appended to the plan's Progress log entry, in this format:
chisel: <one of: consumes-X | shapes-reflective | proposes-Y | nothing>
consumes-X— this commit used an existinglib/guest/Xkit (e.g.,consumes-pratt,consumes-match).shapes-reflective— this commit revealed something about whatlib/guest/reflective/should look like (e.g., env-reification helper signatures, applicative-vs-operative dispatch protocol). Add a paragraph to the plan's "lib/guest feedback loop" section describing the insight.proposes-Y— this commit revealed a gap in another existing kit (e.g.,match.sxdoesn't quite handle X). Open a Blockers entry describing the gap.nothing— pure Kernel work that didn't touch the substrate or lib/guest story (rare; if you write this twice in a row, stop and reflect on why).
Phase 7 (extraction) is gated by the two-consumer rule. Kernel alone is one consumer. The natural second consumer is a future MetaScheme port, a Common-Lisp meta-evaluator port, or a Kernel dialect (cKanren-style). Until a second consumer exists, do NOT actually extract — instead, mark Phase 7 [partial — pending second consumer] and document the proposed lib/guest/reflective/ API surface in the plan's progress log. The extraction itself happens later, when a second consumer materialises.
This discipline is the point of the loop, not a bookkeeping tax. The chisel notes are what tell us — at the end of Kernel's run — whether a lib/guest/reflective/ sub-layer is real or just one-language-shaped.
Ground rules (hard)
- Scope: only
lib/kernel/**andplans/kernel-on-sx.md. Do not editspec/,hosts/,shared/,lib/guest/**(read-only consumer at this phase), or otherlib/<lang>/. - Consume
lib/guest/core/wherever it covers a need. Hand-rolling defeats the chiselling goal. - Do not extract into
lib/guest/reflective/from this loop. That's Phase 7 territory, gated by the two-consumer rule. Until there's a second consumer, document the API surface only. - Substrate gaps (env-as-value not exposing X,
evalsemantics drift, JIT not handling reflective patterns) → Blockers entry with minimal repro. Do not fix substrate from this loop. Substrate work belongs tosx-improvements.md/jit-perf-regression.md. - NEVER call
sx_build. 600s watchdog will kill you. Ifsx_server.exeis broken, add a Blockers entry and stop. - SX files:
sx-treeMCP tools ONLY.sx_validateafter every edit. NeverEdit/Read/Writeon.sx. - Worktree: commit, then push to
origin/loops/kernel. Never touchmain. Never push toarchitecture. - Commit granularity: one feature per commit. Short factual messages:
kernel: $vau operative + 6 tests. - Plan file: update Progress log + tick boxes every commit. Include the chisel note.
- If blocked for two iterations on the same issue, add to Blockers and move on.
Kernel-specific gotchas
- Operatives don't evaluate their arguments.
$vaubuilds an operative; the body sees the unevaluated argument expressions plus the dynamic environment. This is the opposite of every other guest in the set.(define-via-vau)builds a binding by callingevalinside the body on the (still-syntax) argument. - Applicatives wrap operatives.
(wrap op)produces an applicative that evaluates its args first, then callsopwith the values.$lambdais sugar forwrap∘$vau. - Dynamic vs static environments. Operative body sees both: the static env where the
$vauwas created (closure-style), AND the dynamic env where the call happens (passed as the env-param). Different from lexical-only languages. - No special forms in the evaluator.
$if,$define!,$lambdaare all just operatives bound in the standard environment. The evaluator islookup-and-call— no hardcoded switch on symbols. This is the whole point: the language is reified as data. evalis a primitive callable on user environments. This is where SX's env-as-value matters most. If env-as-value isn't fully landed in the substrate, this is where it'll break.- Encapsulations (Phase 5) are Kernel's opaque-types idiom.
make-encapsulation-typereturns three operatives: encapsulator (constructs), predicate (tests), decapsulator (extracts). Used to define promises, streams, modules. - Hygienic operatives (Phase 6) are research-grade. Shutt's later work. Operatives that don't accidentally capture caller bindings. Likely uses scope sets / frame stamps. Treat as exploration, not implementation-deadline.
General gotchas (all loops)
- SX
do= R7RS iteration. Usebeginfor multi-expr sequences. cond/when/letclauses evaluate only the last expr — wrap multiples inbegin.env-bind!creates a binding;env-set!mutates an existing one (walks scope chain).sx_validateafter every structural edit.list?returns false on raw JS Arrays — host data must be SX-converted.- Shell heredoc
||gets eaten — escape or usecase.
Style
- No comments in
.sxunless non-obvious. - No new planning docs — update
plans/kernel-on-sx.mdinline. - Short, factual commit messages with chisel note:
kernel: $vau operative + 6 tests [shapes-reflective]. - One feature per iteration. Commit. Log. Push. Next.
Go. Run the pre-flight check. If lib/guest kits are missing, stop. Otherwise read the plan, find the first unchecked [ ], implement it. Remember: every commit ends with a chisel note, and Phase 7 extraction waits for a second consumer.