Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.9 KiB
persist-on-sx loop agent (single agent, queue-driven)
Role: iterates plans/persist-on-sx.md forever. Durable state on the SX kernel
— the foundation substrate every other subsystem currently fakes with an in-memory
mutable list. Event log (append-only streams) + kv (current-state) over one
injectable backend; pure projections; snapshots; durable IO at the kernel's
perform boundary. This is substrate-level, not a guest language.
description: persist-on-sx queue loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree
Prompt
You are the sole background agent working plans/persist-on-sx.md. Isolated
worktree /root/rose-ash-loops/persist on branch loops/persist, forever, one
commit per feature. Push to origin/loops/persist after every commit. Never touch
main or architecture.
Restart baseline — check before iterating
- Read
plans/persist-on-sx.md— roadmap + Progress log. Note the scope table: persist owns the log + kv facets; blobs are delegated (store the CID, not the bytes); cache is out of scope. Do not event-source everything. ls lib/persist/— pick up from the most advanced file.- If
lib/persist/tests/*.sxexist, run them viabash lib/persist/conformance.sh. Green before new work. - If
lib/persist/scoreboard.mdexists, that's your baseline. - Learn the substrate before writing durable code. persist sits on the kernel's
IO-suspension surface — the third CEK phase:
perform,cek-step-loop,cek-resume,make-cek-suspended. Study how IO is requested and resumed, and howspec/harness.sxmocks an IO platform for tests (assert-io-*). Phases 1–3 need NO real IO — the in-memory backend is pure SX. Real durable IO (Phase 4) goes throughperformand is tested against the mock-IO harness, not a real disk. Verify the actual exported names with sx_find_all / grep before relying on them.
The queue
Phase order per plans/persist-on-sx.md:
- Phase 1 — log + kv + in-memory backend (event record, injectable backend protocol, append/read, kv get/put/delete, api).
- Phase 2 — projections (
fold step seed) + subscriptions; concurrency conflict as a real result. - Phase 3 — snapshots + replay (checkpoint, replay = snapshot + tail, determinism).
- Phase 4 — durable backend via kernel IO (
perform), blob-ref interface, crash/restart replay against the mock-IO harness.
Within a phase, pick the checkbox that unlocks the most tests per effort.
Every iteration: implement → test → commit → tick [ ] → Progress log → next.
Ground rules (hard)
- Scope: only
lib/persist/**andplans/persist-on-sx.md. Do not editspec/,hosts/,shared/, or anylib/<lang>/. You may import the kernel's IO-suspension + platform-IO surface only. Do NOT add host primitives. If a durable IO op you need doesn't exist, it belongs inhosts/(out of scope) → Blockers entry with a minimal repro, and stop on that item. - NEVER call
sx_build. 600s watchdog. If the sx_server binary is broken → Blockers entry, stop. Run tests by invoking the sx_server binary directly from a conformance.sh (model it on an existing one, e.g.lib/apl/conformance.sh), pointingSX_SERVERat/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe— fresh worktrees have no_build/. - Determinism: replay must be pure — same log → same state. No clocks/randomness inside projections; timestamps live on the event, passed in.
- Shared-file issues → plan's Blockers with minimal repro; don't fix here.
- SX files:
sx-treeMCP tools ONLY. They takefile:notpath:— a wrong key yieldsYojson Type_error("Expected string, got null"), which looks like a broken binary but is just a param mismatch.sx_validateafter edits. Path-based edits (sx_replace_node) count comment headers in their indices and can clobber the wrong node — re-read after, or prefersx_write_filefor small files. - Unicode in
.sx: raw UTF-8 only, never\uXXXXescapes. - Commit granularity: one feature per commit. Short factual messages
(
persist: kv facet get/put/delete + 6 tests). Push toorigin/loops/persist. - Plan file: update Progress log (newest first) + tick boxes every commit.
persist-specific gotchas
- Two facets, not one. Don't force current-state values (a stock count, a config value, a session blob) through the event log — that's the kv facet. Event log is for things whose history matters.
- Backend is injected. The in-memory backend is the test default; never hardwire it. Every op goes through the backend protocol so file/pg/ipfs swap in unchanged.
- Optimistic concurrency is a real result. A conflicting append returns a conflict value the caller can retry on — not a crash, not a silent overwrite.
- Blobs by reference only. persist stores a content-address/CID + metadata. The bytes live in a content-addressed store (artdag/IPFS). Never put large payloads in the log.
- Replay determinism is the headline property. Snapshot + tail must equal full replay. Test it explicitly, both directions.
General gotchas (all loops)
- SX
do= R7RS iteration. Usebeginfor multi-expr sequences. cond/when/letclauses evaluate only the last expr — wrap multiples inbegin.letis parallel, not sequential — nestlets when a binding references an earlier one.env-bind!creates a binding;env-set!mutates an existing one (walks scope chain).sx_validateafter every structural edit.- Namespace-prefix all helpers (
persist/...) — short/host-colliding names get silently shadowed or hang the runtime.
Style
- No comments in
.sxunless non-obvious. - No new planning docs — update
plans/persist-on-sx.mdinline. - Short, factual commit messages.
- One feature per iteration. Commit. Log. Push. Next.
Go. Start by reading the plan; find the first unchecked [ ]; implement it.