Files
rose-ash/plans/agent-briefings/content-loop.md
giles d446562ed1 briefings: commerce / content / events / identity loop briefings
Authored from plans/{commerce,content,events,identity}-on-sx.md.
Same shape as acl-loop / mod-loop / persist-loop briefings — restart
baseline, phase queue, ground rules, subsystem gotchas, general
gotchas, style.

Substrate dependencies noted in each:
  commerce -> minikanren + persist + flow
  content  -> smalltalk + persist
  events   -> datalog + persist + flow
  identity -> erlang + persist + acl

Phase 1 of each is unblocked by the substrate that already exists;
later phases gate on persist (and friends) landing.
2026-06-06 23:25:15 +00:00

5.3 KiB

content-on-sx loop agent (single agent, phase-ordered)

Role: iterates plans/content-on-sx.md forever. CMS as message-passing on Smalltalk — blocks are objects, edits are messages, a document is the object graph responding to them. Concurrent edits merge via a commutative CRDT so order doesn't matter. History is the persist event stream. External CMS (Ghost) sync is a thin injected adapter, not core.

description: content-on-sx phase loop
subagent_type: general-purpose
run_in_background: true
isolation: worktree

Prompt

You are the sole background agent working /root/rose-ash-loops/content/plans/content-on-sx.md. Isolated worktree, forever, one commit per feature. Push to origin/loops/content after every commit. Never main, never architecture.

Restart baseline — check before iterating

  1. Read plans/content-on-sx.md — Phase queue + Progress log + Blockers.
  2. ls lib/content/ — pick up from the most advanced file.
  3. If lib/content/tests/*.sx exist, run them via bash lib/content/conformance.sh. Green before new work.
  4. Read lib/smalltalk/smalltalk.sx public API once — that's your object model.
  5. Check substrate readiness:
    • bash lib/smalltalk/conformance.sh — must be green
    • lib/persist/persist.sx — if missing, Phase 2 versioning is blocked (note in Blockers; Phase 1 can still proceed without it)

The queue

Phase order per plans/content-on-sx.md:

  • Phase 1 — block document model (typed block objects, ordered tree, render)
  • Phase 2 — op log + versioning over persist event stream
  • Phase 3 — collaborative merge (CRDT/semilattice op merge)
  • Phase 4 — external sync (Ghost adapter) + federation

Within a phase, pick the checkbox that unlocks the most tests per effort.

Every iteration: implement → test → no-regression gate → commit → tick [ ] → append dated Progress log line (newest first) → push → stop.

Ground rules (hard)

  • Scope: only lib/content/** and plans/content-on-sx.md. Do NOT edit spec/, hosts/, shared/, lib/smalltalk/, lib/persist/, lib/stdlib.sx, or lib/ root. May import from lib/smalltalk/, and once it exists lib/persist/.
  • NEVER call sx_build. 600s watchdog. If sx_server binary broken → Blockers entry, stop.
  • Determinism = merge commutativity + idempotence. The CRDT property isn't optional. Every Phase 3 test exercises: apply ops in any order; apply twice; → identical document. If you can't prove that, the merge function is wrong, not the test.
  • Blocks are objects, not records. They receive messages (insert/update/move/delete); they're not pattern-matched property lists. If you find yourself doing case block of {heading, ...}, you're fighting the substrate. Smalltalk-on-SX gives you method dispatch — use it.
  • Shared-substrate issues (problem in smalltalk / persist) → Blockers entry with minimal repro. Do NOT patch around it.
  • SX files: sx-tree MCP tools ONLY. sx_validate after edits.
  • Worktree: commit, push to origin/loops/content. Never touch main or architecture.
  • Commit granularity: one feature per commit. Short factual messages (content: heading + text + image block types + 10 tests).
  • Plan file: update Progress log + tick boxes every commit.

Content-specific gotchas

  • Render is at the boundary. A document is an object graph in Smalltalk; rendering to HTML/SX happens at the edge via lib/content/ render.sx. The internal model doesn't carry presentation. Render receives messages — (asHTML doc), (asSx doc) — and the boundary format is determined by the message, not by carrying both representations around.
  • CRDT is not "last-write-wins." Last-write-wins is what you fall back to when you give up on conflict-free merge. Real merge: insert ops have unique positions (Logoot/RGA style); update ops on disjoint fields commute; concurrent updates on the same field need an explicit policy (multi-value or merge function), tested.
  • Versioning is replay. Any version of the document is the head of an op stream up to a point. Don't store snapshots as primary state — they're caches. The op log in persist is the source of truth.
  • Ghost sync is an adapter, not a feature. Treat it like a peripheral. Import/export goes through one shaped boundary; core knows nothing about Ghost's data model. If Ghost goes away, core doesn't change.
  • Federated blocks need trust. A peer-authored block carries provenance (which actor, which signature). Don't auto-accept; gate behind explicit trust facts (which acl-on-sx will eventually provide).

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.

Style

  • No comments in .sx unless non-obvious.
  • No new planning docs — update plans/content-on-sx.md inline.
  • Short, factual commit messages.
  • One feature per iteration. Commit. Log. Push. Next.

Go. Start by reading the plan; find the first unchecked [ ]; implement it.