Durable copy of /tmp/sx-build/agentic-status.md: what was built (196/196), boundary conventions, the 11 open design questions for held Phases 5/7/8/9, and the proposed rulings awaiting sign-off. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
16 KiB
agentic-sx — Phases 1–4 DONE, HELD at Phase 5 (handback)
Status: 196/196 green (schema 65, branch 53, trace 35, durable 43).
Worktree /root/rose-ash-loops/agentic, branch loops/agentic (off loops/git).
Source lib/agentic/{schema,branch,trace,durable}.sx, suites
lib/agentic/tests/*.sx, runner lib/agentic/conformance.sh, scores
lib/agentic/scoreboard.{md,json}. Commits eff216ef → c66ee350, one per
phase. NOT pushed. Phases 5+ deliberately NOT started (per brief).
What was built
The agentic structure IS the open-branch set of a repo: one branch = one
agent, seeded by a briefing. Pure assembly over lib/git + persist +
relations + flow — zero new substrates, zero third-party deps.
Phase 1 — schema (schema.sx)
Objects are plain SX dicts, content-addressed via sx-git's native CID
(git/cid / git/write accept any :typed dict — the extensibility sx-git
promised downstream).
- Type registry
agentic/types: object typesbriefing,console-trace,behaviour(TAG only — library HELD Phase 8) + commit kindsagent-commit(base) and subtypesspawn,finding,refactor,test,session-merge,decision.agentic/is-a?(reflexive, transitive, cycle-bounded),agentic/register-type!(create-only, parent-checked — how sx-gitea adds e.g.reviewkinds at runtime). - Agent-commits ARE git commits: the kind rides in
:agent-type, open fields (:briefing :agent :message :behaviour-cid …) round-trip and participate in the CID; all sx-git DAG/branch/merge machinery applies unchanged.spawnsubtype added beyond the briefed five for branch genesis commits. - Constructors/predicates/accessors for briefing, console-trace, behaviour;
agentic/agent-commitvalidates its kind against the registry.
Phase 2 — branch (branch.sx)
agentic/space db name→{:repo (git/repo-named …) :rels (relations db)}.- spawn = branch-from-briefing:
spawn!(root, empty tree) /spawn-from!(forks at parent agent's head + assertssub-agent-ofedge) /spawn-at!(arbitrary fork cid). Writes briefing → genesisspawncommit (carries:briefingcid) →git/branch-create!agents/<name>(create-only; conflict dict on reuse). - The commit verb
agentic/commit!: snapshots a FULL worktree VALUE (path→data) viagit/tree-from-files, builds a typed agent-commit (briefing + agent identity propagate from the head), advances the branch bygit/branch-cas!. No shared index → multi-agent safe by construction. - Topology:
agents,head,genesis,briefing-of,session-log(own commits newest-first),fork-point=git/merge-baseof heads. Typed edges over relations:sub-agent-of/reviews/mergeswith query wrappers (sub-agents,agent-tree,reviewers,merged-sessions,merged-into, genericrelate!). - Session merge
merge-session!: always records an explicit two-parentsession-mergecommit (no-ff — the merge is itself an agent action with metadata:merged-agent), viagit/merge-commits;up-to-datepasses through; conflicts commit NOTHING and conclude viamerge-resolve!(resolved worktree → same two-parent shape). Asserts themergesedge.
Phase 3 — trace (trace.sx)
- Per-agent buffer = persist append-only log stream
(
<prefix>/trace/<agent>) + kv drain cursor.agentic/trace! sp agent kind textappends;trace-pendingreads since last drain. commit-with-trace!=commit!+ drain everything-since-last-commit into aconsole-traceobject{:commit cid :agent :entries}bound git-note style: refnotes/trace/<commit-cid>→ trace cid. NOT in the commit tree — attaching never changes the commit cid; the note is a re-bindable ref layer over immutable objects. Failed commits keep the buffer; plaincommit!deliberately leaves binding to the agent (granularity = commit, agent-chosen).trace-for/trace-cid-for/session-traces, manualattach-trace!for any commit.
Phase 4 — durable (durable.sx)
- Deterministic replay IS the durability mechanism. Every transition
re-runs a self-contained flow program: the session's
defflowsource (durable in kv viadefsession!) +flow/start+ replay of every recorded resume value (freshflow-runresets the flow store, so the flow id is always 1 — a feature). The ONLY durable state is{:flow :input :resumes}+ derived{:status :tag/:result}in kv. - Restart-safe by construction: a fresh
agentic/spaceover the same backend sees suspended sessions and resumes them mid-flight (tested). - fork-an-agent-run
session-fork!= copy the record to another spawned agent; replay rebuilds the run to the same suspended state; the two then diverge independently (tested: 45 vs 1500 from a shared prefix). - Effects are data: suspend tags and typed
(request kind payload)envelopes surface as plain SX (session-pending,effect-request?,effect-kind,effect-payload) — the exact seam Phase 5 hold-for-human-input plugs into. - Session transitions append
"session"-kind entries to the Phase-3 trace buffer → session history travels with the agent's next commit (tested).
Boundary conventions discovered (bake into Phase 5+ designs)
- Guest Scheme numbers box differently at the SX boundary: compare with
=, neverequal?(7 tests initially failed on45vs45). - Scheme strings surface as
{:scm-string …}(unbox viaagentic/scm-out); symbols surface as plain strings. Session flows should use quoted-symbol suspend tags and NUMERIC decision values ((eq? d 1)) — a resumed SX string does noteq?a Scheme symbol. agentic/scm-litserializes numbers/strings/booleans/lists into program source (no string escaping — keep payloads quote-free).
Open design questions for the HELD phases
Phase 5 — hold-for-human-input (blocked on sx-gitea web API, in flight):
- The seam is ready: a suspended session's
(request "human" payload)is already exposed as data. Open: does sx-gitea pollsession-pendingper agent, or should agentic maintain a space-wide pending-requests index (flow'sflow-host-requestsshape) for the forge UI to list? - Should the human decision be recorded as a
decisionagent-commit on the agent's branch (decision-commit = resume value + rationale, trace attached) before/atomically-withsession-resume!? I'd argue yes — "hold-for-human-input (flow suspend → decision-commit)" per the trilogy memory — but the commit's files snapshot semantics during a suspension need a ruling (empty diff? head tree re-snapshot?). - Resume-value typing: currently any
scm-lit-serializable value; does the sx-gitea API constrain it (approve/reject enum vs free payload)?
Phase 7 — fed + type×trigger/execute trust gate:
4. fed-sx (DefineType/SubtypeOf runtime) is NOT in this worktree — only
design plans (plans/fed-sx-*.md) and lib/relations/federation.sx
(peer_rel + trust facts). Phase 1's registry declares tags locally as
agreed; migrating agentic/types to fed-sx types needs the fed-sx-types
loop's substrate (loops/fed-sx-types) merged or vendored.
5. relations already gives a per-peer trust gate over federated edges
(erel :- peer_rel + trust) — is the type×trigger gate a Datalog rule
over (peer, agentic-type, trigger-verb) facts in the same db, or a
separate capability object in the git store? The former composes with
what's built.
6. Briefings/commits federate as content-addressed objects for free
(same-CID-everywhere tested); what does NOT federate yet: refs
(branch heads are local mutable state) and note bindings. Ref
replication policy is the real Phase 7 design question.
Phase 8/9 — behaviour library + self-improvement:
7. behaviour is a registered TAG with constructor/predicate only; an
agent-commit carries :behaviour-cid (tested). Open: behaviour BODY
format (SX source string? content-addressed flow def like
defsession! sources? both?) — deliberately unspecified.
8. Suggestion from the build: defsession! sources are already durable,
content-addressable session behaviours — Phase 8 could unify
behaviour bodies with session-flow defs (behaviour = the flow an agent
runs). Needs human ruling.
Smaller notes:
9. Relations edges (sub-agent-of/reviews/merges) live in the
in-memory Datalog db on the space handle — NOT persisted. Spawn/merge
verbs re-assert them, but a restarted space loses edge history not
re-derivable from commits (reviews edges especially). Option: mirror
edges into a persist event stream and re-saturate on space open
(cheap; ruleset stays minimal). Left for the Phase 5+ pass.
10. git/repo index/porcelain (git/commit!, git/merge!) is repo-global
and deliberately UNUSED here; agentic-sx only uses the value-level API.
If sx-gitea drives both, keep it that way.
11. Trace note refs (notes/trace/<cid>) are reflogged like any ref —
free audit trail of re-bindings; surfaced in case the forge wants it.
Proposed rulings (build loop, 2026-07-03 — for human sign-off)
Numbers match the questions above. Nothing below is implemented; these are the designs I would build if approved.
R1 — Derive the pending index, don't maintain one. Session records
already live in kv (<prefix>/session/<agent>). Phase 5 adds one function,
(agentic/pending-requests sp) → ((agent tag) ...): enumerate
(agentic/agents sp) (refs are the agent registry), kv-get each record,
filter :status "suspended". O(agents) kv reads, no duplicated state, no
invalidation bugs — mirrors flow-host-requests shape. The forge polls
this one call per space. If a space ever holds thousands of agents, add an
event-sourced index then, not now.
R2 — Yes: decision-commit, tree = parent's tree, commit-then-resume.
New Phase 5 verb (agentic/decide! sp agent value rationale-meta):
(a) trace! a "session" entry recording the human decision;
(b) create a decision agent-commit whose :tree is the PARENT's tree cid
verbatim (empty diff is correct — the decision changes the session, not
the worktree; the payload rides in open commit fields :resume-tag,
:resume-value, message = rationale) and CAS-advance the branch;
(c) then session-resume!, and append the decision-commit cid to a new
:decisions list on the session record (parallel to :resumes, which
stays bare replay values — serialization unchanged).
Ordering rationale: true atomicity across kv+refs doesn't exist; commit
first so the human's intent is durably recorded even if resume errors, and
resume is deterministic replay so re-applying from the decision-commit is
an idempotent repair. Hold-for-human-input then IS: suspend → forge shows
pending request → decide! → decision-commit + resume.
R3 — Agentic stays permissive; the forge constrains per request kind.
The (request kind payload) envelope already names the kind; sx-gitea maps
kind → input schema (e.g. "human"-review → numeric enum 1/0; free-text
comment goes in the decision-commit message/meta, NOT the resume value —
numeric decisions dodge the symbol/string eq? trap by construction).
One small hardening lands with Phase 5: agentic/scm-lit gains
backslash/quote escaping for strings (two-line change) so the quote-free
convention becomes a nicety, not a requirement.
R4 — Local registry = authoritative runtime cache; PROJECT it into
fed-sx when federation lands. Do not vendor fed-sx here. Each registry
entry maps mechanically to DefineType (+ SubtypeOf parent) activities; on
Phase 7 the emitted type objects' CIDs get pinned back into the registry
(:fed-cid per entry), and register-type! becomes the local-cache
updater for received DefineType activities — exactly the peer_types cache
pattern from loops/fed-sx-types. Sequencing: merge fed-sx-types substrate
(or both to architecture) BEFORE starting Phase 7; zero rework in
lib/agentic either way because tags are plain strings.
R5 — Both, layered: capability OBJECTS in the git store as source of
truth, compiled to Datalog FACTS for enforcement. A grant is a typed
content-addressed object {:type "capability" :peer :agentic-type :trigger :execute ...} — auditable, versionable, federable like everything else.
The current capability SET is a ref (caps/current → a tree/list of cap
cids), so revocation = a ref move with reflog, same shape as branches.
At space-open/refresh the set compiles to flat allowed(Peer, Type, Trigger) facts + at most one non-recursive rule composing with the
existing trust(P) gate — ruleset stays minimal per the relations
re-saturation constraint. Enforcement = (agentic/allowed? sp peer type trigger) fact lookup at activity-accept time.
R6 — Owner-writes / others-track; announcements, not replication.
Branch heads never replicate as writable state. A peer's head move
federates as a signed announcement activity ("agent X: head → cid C");
receivers materialize it as a read-only remote-tracking ref
remotes/<peer>/agents/<name> — sx-git's free-form ref names support this
today with zero changes, and CAS stays a purely local concern (only the
owner moves heads/agents/<name>). Object transfer = CID closure of the
announced head (git/reachable-all gives the exact fetch set; everything
is idempotent content-addressed writes). Cross-peer session-merge = merge
FROM a remote-tracking ref, locally, producing a local session-merge
commit. The R5 gate applies at announcement-accept time (peer × type ×
trigger "head-move" / "spawn" / "merge"). Note bindings federate the same
way: announce notes/trace/<cid> → trace cid, land under
remotes/<peer>/notes/trace/<cid>.
R7+R8 — Unify: a behaviour IS the content-addressed unit of agent skill;
sessions are behaviours instantiated. Behaviour body = SX data with a
:kind discriminator; two kinds at launch: "session-flow" (body = a
defflow Scheme source string, directly installable) and "prompt" (body =
briefing-shaped instructions for LLM-driven agents). Phase 8 adds
(agentic/defsession-from! sp behaviour-cid): read the behaviour object,
check :kind "session-flow", install its body under the behaviour's name
— and the commit verb stamps that cid into :behaviour-cid. Then
:behaviour-cid means literally "the flow this agent was running when it
made this commit", which closes the Phase 9 loop: self-improvement =
statistics over commits grouped by behaviour-cid → new behaviour objects →
adoption via new spawns referencing them. Evolution stays in immutable
objects + ref-shaped adoption, same invariant as everything else.
R9 — Yes, event-source the edges (do it in the Phase 5 pass, ~20
lines). agentic/relate! additionally appends {:src :dst :kind} to
stream <prefix>/edges; agentic/space reads the stream on open and
passes the accumulated facts to relations-build-db (one build, no
per-fact assert). A future unrelate appends a retract event; replay
applies both in order. Space handles become fully rehydratable — the
in-memory rels db is then just a cache of the stream.
R10 — Confirmed: porcelain stays unused by agentic and by sx-gitea's multi-agent paths. The repo-global index/HEAD is a single-user convenience; anything concurrent uses the value-level API (tree-from-files / agent-commit / branch-cas!). If sx-gitea wants per-user checkouts, each gets its own repo handle or stays value-level. Document, don't change.
R11 — Surface trace-note reflogs read-only in the forge. "Trace
history" tab on a commit = git/reflog repo "notes/trace/<cid>" — free
audit of re-bindings (amended traces are visible, nothing is lost since
old trace objects stay in the store). No new API.
How to run
cd /root/rose-ash-loops/agentic
bash lib/agentic/conformance.sh # 196/196; durable suite loads scheme+flow (~2 min)