plans: agentic-sx Phases 1-4 handback + proposed rulings R1-R11
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>
This commit is contained in:
273
plans/agentic-sx-status.md
Normal file
273
plans/agentic-sx-status.md
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
# 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 `:type`d dict — the extensibility sx-git
|
||||||
|
promised downstream).
|
||||||
|
|
||||||
|
- **Type registry** `agentic/types`: object types `briefing`,
|
||||||
|
`console-trace`, `behaviour` (TAG only — library HELD Phase 8) + commit
|
||||||
|
kinds `agent-commit` (base) and subtypes `spawn`, `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. `review` kinds 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. `spawn` subtype added beyond the briefed five for branch
|
||||||
|
genesis commits.
|
||||||
|
- Constructors/predicates/accessors for briefing, console-trace, behaviour;
|
||||||
|
`agentic/agent-commit` validates 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 + asserts `sub-agent-of`
|
||||||
|
edge) / `spawn-at!` (arbitrary fork cid). Writes briefing → genesis
|
||||||
|
`spawn` commit (carries `:briefing` cid) → `git/branch-create!`
|
||||||
|
`agents/<name>` (create-only; conflict dict on reuse).
|
||||||
|
- **The commit verb** `agentic/commit!`: snapshots a FULL worktree VALUE
|
||||||
|
(path→data) via `git/tree-from-files`, builds a typed agent-commit
|
||||||
|
(briefing + agent identity propagate from the head), advances the branch
|
||||||
|
by `git/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-base` of heads.
|
||||||
|
Typed edges over relations: `sub-agent-of` / `reviews` / `merges` with
|
||||||
|
query wrappers (`sub-agents`, `agent-tree`, `reviewers`, `merged-sessions`,
|
||||||
|
`merged-into`, generic `relate!`).
|
||||||
|
- **Session merge** `merge-session!`: always records an explicit two-parent
|
||||||
|
`session-merge` commit (no-ff — the merge is itself an agent action with
|
||||||
|
metadata `:merged-agent`), via `git/merge-commits`; `up-to-date` passes
|
||||||
|
through; conflicts commit NOTHING and conclude via `merge-resolve!`
|
||||||
|
(resolved worktree → same two-parent shape). Asserts the `merges` edge.
|
||||||
|
|
||||||
|
### Phase 3 — trace (`trace.sx`)
|
||||||
|
|
||||||
|
- Per-agent buffer = **persist append-only log stream**
|
||||||
|
(`<prefix>/trace/<agent>`) + kv drain cursor. `agentic/trace! sp agent
|
||||||
|
kind text` appends; `trace-pending` reads since last drain.
|
||||||
|
- `commit-with-trace!` = `commit!` + drain everything-since-last-commit into
|
||||||
|
a `console-trace` object `{:commit cid :agent :entries}` bound **git-note
|
||||||
|
style**: ref `notes/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; plain
|
||||||
|
`commit!` deliberately leaves binding to the agent (granularity =
|
||||||
|
commit, agent-chosen). `trace-for` / `trace-cid-for` / `session-traces`,
|
||||||
|
manual `attach-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 `defflow` source
|
||||||
|
(durable in kv via `defsession!`) + `flow/start` + replay of every
|
||||||
|
recorded resume value (fresh `flow-run` resets 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/space` over 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
|
||||||
|
`=`, never `equal?` (7 tests initially failed on `45` vs `45`).
|
||||||
|
- Scheme strings surface as `{:scm-string …}` (unbox via `agentic/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 not `eq?` a Scheme symbol.
|
||||||
|
- `agentic/scm-lit` serializes 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):
|
||||||
|
1. The seam is ready: a suspended session's `(request "human" payload)` is
|
||||||
|
already exposed as data. Open: does sx-gitea poll `session-pending` per
|
||||||
|
agent, or should agentic maintain a space-wide pending-requests index
|
||||||
|
(flow's `flow-host-requests` shape) for the forge UI to list?
|
||||||
|
2. Should the human decision be recorded as a `decision` agent-commit on
|
||||||
|
the agent's branch (decision-commit = resume value + rationale, trace
|
||||||
|
attached) before/atomically-with `session-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?).
|
||||||
|
3. 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
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /root/rose-ash-loops/agentic
|
||||||
|
bash lib/agentic/conformance.sh # 196/196; durable suite loads scheme+flow (~2 min)
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user