From c18df7ec26543cbd035eac8025e620cebae1cecd Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Fri, 21 Aug 2026 20:57:59 -0700 Subject: [PATCH 01/24] =?UTF-8?q?docs(org):=20TDD=20=E2=80=94=20roles,=20c?= =?UTF-8?q?hains,=20and=20ownership-as-data=20for=20an=20agent=20organizat?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- docs/features/org/spec.md | 480 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 480 insertions(+) create mode 100644 docs/features/org/spec.md diff --git a/docs/features/org/spec.md b/docs/features/org/spec.md new file mode 100644 index 00000000..2fb4d245 --- /dev/null +++ b/docs/features/org/spec.md @@ -0,0 +1,480 @@ +# org — Technical Design Document + +**Status:** draft / proposal — NOT a build commitment. The artifact we decide from. +**Owner:** @mh +**Date:** 2026-08-21 +**Related:** [drive-v0](../../../../drive/docs/features/drive-v0/spec.md) (session classes, scope-bound caps, FR6 "liveness is derived"); [session-claims](../session-claims/spec.md); [execution-runtime](../execution-runtime/spec.md) and `contracts/execution` (the leaf-package shape this copies); `contracts/envelope.go` (the hash-chained record this reuses); [gate DESIGN — tamper model](../../../../gate/docs/DESIGN.md); [agents-as-processes-gleam](../../../../agents-as-processes-gleam/README.md) (the ownership finding); [fm-epoch-replay-laws](../../../../fm-epoch-replay-laws/README.md) (the fold laws to port); [parley](../../../../parley/README.md) (role protocols); dossier project `org`. + +> **Reviewers — focus areas:** §4.2 (the tip of a role's chain *is* ownership — the load-bearing claim), §4.6 (grants bind to incarnations, not chain heads), §4.7 (who writes the distilled state, and what a mechanical `mark` may stand in for), §7.3 (crash → takeover → the stale incarnation's next write), §9 (what is committed before the validation gate vs. after). + +## 1. Problem & hypothesis + +The operator's portfolio is already run by agents; what it lacks is an +**organization**. The shape we want is known and published: two leads that +keep each other honest, a lead per project, five to ten ICs per project each +holding one unit of work for days, everyone messaging directly, the human +talking to the leads — thirty to fifty prompts a day, five percent of them +"something went off the rails." + +Every attempt at that shape so far has died at the same point: **an agent +that wakes up does not know it was ever here.** A session gets `CLAUDE.md`, +a memory index keyed by its working directory, and tool signatures. The +state of the work it owns — what it decided, what it promised whom, what is +half-done — exists only in a transcript it cannot see. Evidence on this +machine: 79 worktree-keyed project directories under `~/.claude/projects` +hold zero memory files between them; the hand-off verbs that would fix this +(`/claim`, `/release`, `/continue`) are opt-in, and the drive spec records +two claim events in the two days after they shipped. Instructions are +advice; nobody types the verb. + +Two prior experiments fix the frame: + +- **switchboard** asked whether a long-lived context needs a long-lived + process. It does not: a durable-but-unowned baseline recovers the same + conversation from disk. What residency uniquely supplies is **serialized + ownership** — the second of two racing turns is refused instead of + corrupting the journal. +- **drive** made work the first-class object and sessions attachments to it, + with scope authority as a ledger fact: a driver's cap is stored as a hash, + re-minting a scope invalidates prior caps, and contention is recorded and + surfaced rather than silently resolved. + +**Hypothesis.** Continuity and ownership are the same fact, and both are a +property of a data structure, not of a process or a prompt. Give every +**role** (lead, project lead, IC) one append-only, hash-chained journal of +distilled context. Define *being* the role as *holding the tip of its chain*. +Then: an incarnation that has not folded the chain cannot exist; two +incarnations cannot both be the role; a dead incarnation is replaced by a +`takeover` that makes the old one's next write illegal; and every grant, +assignment, and message in the org hangs off a chain position that can be +audited. "Wake up as if you were never away" stops being a quality of the +boot prompt and becomes a theorem about a reducer. + +**Non-goals.** + +- Not an always-on runtime. Roles are durable; incarnations are disposable + sessions on any host (Claude Code, Agent SDK, `codex exec`). +- Not a new work store. dossier holds tasks; assignments point at them. +- Not a chat surface. The operator's interface is a conversation with the + leads, hosted wherever they already talk to agents. +- Not consensus. One writer per chain, a supervisor that may take over, a + keyed anchor for tamper evidence. Ledger, not blockchain. +- Not merge authorization. gate is unchanged and uninteresting here. + +## 2. Functional & non-functional requirements + +**Functional.** + +- FR1 — A **role** is a durable identity with a charter (scope, decides / + never-decides, capability manifest, supervisor, escalation target) recorded + as the genesis record of its chain. +- FR2 — A role has exactly one **chain**: append-only, `seq` contiguous from + 1, each record `prev`-linked to the last, sealed by a keyed tip anchor. +- FR3 — **Incarnating** a role is appending a `resume` record whose `prev` + equals the chain's tip. Any other way of claiming a role is refused. +- FR4 — The runtime writes **checkpoints** on a cadence (pre-compaction, + stop, every N tool calls, on every outbound `delegate`/`report`/`escalate`) + without a verb being typed. +- FR5 — **Takeover** by the role's supervisor appends a record that makes the + displaced incarnation's next append fail with a named refusal. +- FR6 — **Assignments** (role ↔ dossier work unit) and **messages** (role ↔ + role, typed) are chain records; a message carries the counterpart chain's + `(role, seq, hash)` so both sides can be cross-verified. +- FR7 — **Liveness is derived at read time, never recorded** (drive FR6 + inherited verbatim): tip age, transcript mtime, PR state, process checks. +- FR8 — **Grants** (gate, custody) minted for a role name the incarnation + they were minted to; a displaced incarnation cannot spend them. +- FR9 — A **fold** of any chain prefix yields a role state good enough to + act on: goal, current work, decisions with their why, open threads, next + actions, refs to evidence. + +**Non-functional.** + +| Dimension | Target | +| --- | --- | +| Size | A checkpoint's `state` ≤ 4 KB; a fold to the tip ≤ 16 KB injected. Bounded by contract law, not convention. | +| Latency | Fold to tip < 100 ms for a chain of 10k records (pure Go over JSONL; snapshot every 256 records). | +| Durability | Append-only JSONL, one `write(2)` per record, torn-tail truncation on read (switchboard's journal). | +| Integrity | Tamper-evident against a state-dir-only writer: `HMAC(key, head ‖ count)` anchor with the key outside the state dir — gate's bounded claim, not non-repudiation. | +| Determinism | The write path invokes no model. The distilled `state` is authored by the incarnation itself at checkpoint time; a mechanical `mark` stands in when it didn't (§4.7). | +| Portability | Chains are files; a role can be incarnated on any machine that has the state dir and the anchor key. No server. | +| Cost | No new subscriptions; agent spend unchanged. Checkpoint authoring costs one short tool call per cadence tick. | + +## 3. Architecture overview + +``` + operator ──talks to──► lead A ◄──supervise──► lead B + │ delegate / report / escalate + ▼ + project lead (one per live repo) + │ + ┌─────────────┼─────────────┐ + ▼ ▼ ▼ + IC IC IC ← one dossier task each + 2–3 days unattended + + every node above is a ROLE = one chain: genesis ─► … ─► checkpoint ─► tip + ▲ + incarnate = append resume with prev == tip + + ┌─────────────────────────── contracts/org (leaf, no decisions) ───────────────────────────┐ + │ role · assignment · continuity · message · supervision — types, schema, validate, fold │ + └──────────────────────────────────────────────────────────────────────────────────────────┘ + ▲ ▲ ▲ ▲ + drive (runtime: incarnate, hooks (Claude Code parley (compiles custody / gate + supervise, takeover, host adapter: checkpoint role protocols; (grants name the + tree-from-chains) / mark / resume) observe audits) incarnation) + │ + dossier (work) · channel / SendMessage (transport) · runway (execution) · gate log (receipts) +``` + +**New:** the five contracts and the fold; the chain as an `Envelope` kind; +the role layer and supervision reducer in drive; the host adapter in hooks; +the `org-delegate` protocol in parley. **Reused unchanged:** `Envelope`'s +`prev`/`hash`/`parents`, gate's anchor, drive's scope caps and liveness +joins, dossier, channel, runway, custody, gate. + +The seam that matters: **contracts know nothing about hosts.** A Claude Code +session, an Agent SDK worker, and `codex exec` all produce the same records +through the same verbs. The host adapter is the only per-harness code, and it +is mechanism (when to checkpoint), never policy (what a valid chain is). + +## 4. Key decisions & trade-offs + +### 4.1 The chain is an `Envelope` kind, not a new ledger format + +`contracts.Envelope` already carries `prev`, `hash`, `parents`, `kind`, and a +raw `body`. A continuity record is `kind: "org."` with a +`contracts/org` body. gate's keyed tip anchor (`HMAC(key, head ‖ count)`, +key outside the state dir) is reused as-is for truncation and rewrite +detection. *Alternative:* a bespoke journal per role (switchboard's +`Envelope(version, sequence, event)`) — rejected: two hash-chain formats in +one portfolio, and the gate log already proved this one under audit. +*Trade-off:* `Envelope.run` is a gate-ism; for org records it carries the +role id. + +### 4.2 The tip of a role's chain *is* the role (ownership as data) + +To act as a role you append to its chain, and the only legal first append is +a `resume` whose `prev` is the current tip. Two incarnations cannot both hold +the tip: the second append has a stale `prev` and is refused with +`prev_mismatch`. This is switchboard's serialized-ownership result with the +process removed — the refusal comes from the reducer, not a mailbox. + +Relationship to drive's caps: they are the same event seen from two planes. +drive's cap says *this session has authority over scope S*; the chain says +*this incarnation is role R and knows what R knows*. A `takeover` record on +R's chain is what mints the successor's cap and revokes the predecessor's +(§7.3). *Alternative:* leases/heartbeats — rejected: liveness is never +recorded (FR7); a lease is recorded liveness by another name. + +### 4.3 Roles are durable; incarnations are disposable + +A role's identity is its genesis record (the charter) plus its chain. An +incarnation is `(role, incarnation_id, host, session_ref)` — a session on any +harness that currently holds the tip. Roles outlive machines; incarnations +don't outlive a crash. *Consequence:* the org chart is the set of genesis +records; "who is the ivy lead" is a fold, not a config file. + +### 4.4 Messages are typed, transport-agnostic, and cross-referenced + +`delegate`, `report`, `escalate`, `ask`, `answer`, `takeover_notice` are +record kinds with a fixed body shape. Transport is whatever is at hand — +`channel`, `SendMessage`, a file — but a message exists *as a fact* only once +both chains carry it: the sender's `message.sent` at `(R1, seq_a)` and the +receiver's `message.received` at `(R2, seq_b)`, each naming the other's +`(role, seq, hash)`. An auditor (parley `observe`) can then check that what +the IC says it was asked matches what the lead says it asked, trusting +neither. *Alternative:* treat the bus as the record — rejected: channel is +untyped and unanchored by design; it stays the nimble transport. + +### 4.5 Supervision is derived; `takeover` is its only write; leads supervise each other + +A supervisor is a role whose charter names the roles it watches. It derives +liveness (tip age + host signals), and its only privileged write is a +`takeover` on a watched role's chain. The two top leads name each other as +supervisor so there is no singleton whose death orphans the org. +*Alternative:* a daemon supervisor — rejected: it is the always-on process +the non-goals exclude, and it is exactly the single point of failure the +two-leads shape exists to avoid. + +### 4.6 Grants bind to incarnations, not chain heads + +A gate grant or custody grant minted for a role carries `incarnation_id`. +*Alternative considered:* bind to the exact `(seq, hash)` at mint time — +rejected: every checkpoint would invalidate every grant. Binding to the +incarnation means grants survive checkpoints and die on `takeover`, which is +the intended semantics: authority follows continuity, and a displaced +incarnation that still holds a token cannot spend it. **Reviewer call:** this +is additive to custody's grant shape (`cst2_…`) and gate's; confirm the field +lands in both or in a shared `contracts/authority` extension. + +### 4.7 Who writes the distilled state — and what a `mark` may stand in for + +The `state` body of a `checkpoint` or `handoff` is written by the +incarnation itself: at each cadence tick the host adapter prompts for (or, +on hosts that support it, requires) a short structured `org checkpoint` +call. No model runs inside the hook. When the incarnation didn't author one +— it crashed, hit the context ceiling, or ignored the prompt — the adapter +appends a mechanical **`mark`**: session ref, git state, last N tool calls, +transcript offset. A `mark` keeps the chain continuous and resumable but is +*degraded*: the fold reports `state.degraded = true` and the resumed +incarnation's first act is to reconstruct from refs. Contract law: a +`checkpoint`/`handoff` with empty `state.next` is malformed +(`empty_next`); a chain whose tip is a `mark` is legal; a role with no chain +is not a role (`chain_missing`). + +### 4.8 Where it lives + +`contracts/org` is a leaf: types, embedded JSON schema, `validate.go` +(contract law), `reduce.go` (the fold and refusals), conformance + fuzz +tests, no decision logic — identical in shape to `contracts/execution`. The +runtime (incarnate, supervise, takeover, tree) is drive, which already owns +session classes and scope caps. The Claude Code host adapter is hooks. The +role protocols are parley. No new repository. + +## 5. Data model + +**Role id.** `org/` — e.g. `org/lead-a`, `org/ivy-lead`, +`org/ivy-ic-3`. Lowercase, stable, never reused. + +**Chain layout.** `/org//chain.jsonl` plus the anchor +record under the key dir (per gate). Optional `snapshot-.json` every +256 records; a snapshot is a cache of the fold and is deletable. + +**Record kinds and bodies** (every record is an `Envelope` with +`kind: "org."`, `run: `, `prev`, `hash`; bodies below). + +| kind | body | law | +| --- | --- | --- | +| `genesis` | `charter{ scope[], decides[], never_decides[], escalates_to, supervisor, supervises[], capabilities[] }` | seq 1 only; exactly one | +| `resume` | `incarnation{ id, host, session_ref, started_at }` | `prev` == tip; starts an incarnation | +| `checkpoint` | `state{ goal, doing, decided[{what, why}], open[], next[], refs[] }`, `incarnation_id` | `next` non-empty; ≤ 4 KB; `incarnation_id` == current | +| `handoff` | same as `checkpoint` + `reason: stop\|compaction\|release` | ends an incarnation cleanly | +| `mark` | `mechanical{ session_ref, git{branch, head, dirty[]}, last_tools[], transcript_offset }` | host-authored; degraded | +| `takeover` | `by: , from_incarnation, reason, evidence[]` | only a role named `supervisor` in genesis; ends the current incarnation | +| `assign` / `release` | `work{ kind: dossier, id }`, `incarnation_id` | one open assign per work id across all chains (checked by drive at write time, law at fold time) | +| `message.sent` / `message.received` | `msg{ type, to\|from, ref{role, seq, hash}, body }` | `type ∈ {delegate, report, escalate, ask, answer, takeover_notice}` | + +**Fold output — `RoleState`.** + +``` +RoleState { + Role, Charter, + Tip{ Seq, Hash }, Count, + Incarnation *{ ID, Host, SessionRef, Since }, // nil when no live incarnation + State{ Goal, Doing, Decided, Open, Next, Refs, Degraded bool, At seq }, + Assignments[]{ Work, Since }, + Outbox[]{ To, Type, Seq }, Inbox[]{ From, Type, Seq }, + Supervisor, Supervises[] +} +``` + +Liveness is **not** a field. drive derives it from `Tip` age and host +signals at read time. + +**Versioning.** `schema_version` on every body; one version today; a +compatibility rule is decided if and when `0.2.0` exists, from evidence +(execution's stance). + +## 6. API contract + +**`contracts/org` (Go, leaf).** + +```go +func ValidateRecord(r Record) error // contract law per kind (§5 laws) +func Reduce(records []Record) (RoleState, error) // the fold; refuses on any law breach +func Admissible(tip RoleState, next Record) error // what an appender asks before writing +``` + +Refusal codes (stable strings, surfaced verbatim by every runtime): + +| code | meaning | +| --- | --- | +| `chain_missing` | no genesis; a role with no chain is not a role | +| `genesis_misplaced` | genesis not at seq 1, or a second genesis | +| `seq_gap` | `seq` not contiguous from 1 (switchboard's `SequenceGap`) | +| `prev_mismatch` | `prev` ≠ hash of the previous record — fork, race, or stale incarnation | +| `stale_incarnation` | a `checkpoint`/`assign`/`message.sent` whose `incarnation_id` is not the current one (a takeover or handoff intervened) | +| `not_supervisor` | `takeover` by a role the genesis does not name | +| `empty_next` | `checkpoint`/`handoff` with no next actions | +| `oversize_state` | `state` > 4 KB | +| `anchor_mismatch` | keyed tip anchor disagrees with `(head, count)` — truncation or rewrite | + +**Runtime verbs (drive; CLI + MCP, same names).** + +``` +org incarnate [--host claude|sdk|codex] [--session ] → RoleState (the boot), or a refusal +org checkpoint --state → seq +org handoff --state --reason stop|compaction|release +org mark (host adapter only) +org takeover --by --reason [--evidence ...] +org assign --work dossier: +org send --to --type delegate|report|escalate|ask|answer --body +org fold [--at ] → RoleState +org tree → the org chart with derived liveness +org audit → anchor + chain check (gate audit's twin) +``` + +Every verb's stdout is the JSON result; exit codes `0` ok, `1` refused +(code in JSON), `4` error. Refusals are loud and name the remedy, like +custody's. + +**Host adapter contract (hooks).** Three hooks, all soft-fail, ≤ 3 s: +`SessionStart` → `org incarnate` and inject the fold; `PreCompact` and `Stop` +→ prompt for `org handoff`, else `org mark`; every N tool calls → prompt for +`org checkpoint`. Memory dir resolution is repo-keyed (worktree → repo root) +so auto-memory stops fragmenting across 79 buckets; this is a one-line fix +the adapter ships with. + +## 7. Key flows + +### 7.1 Incarnate (the boot) + +1. Host starts a session for role R. Adapter calls `org incarnate R`. +2. Runtime reads the chain, verifies the anchor, folds to the tip. + `chain_missing` → refuse (a role must be chartered first). +3. If the fold shows a live incarnation (tip is a `checkpoint` younger than + the liveness threshold, host signals agree) → refuse with `prev_mismatch` + and the current incarnation's id. Incarnating is not taking over. +4. Append `resume{ id, host, session_ref }` with `prev = tip.hash`. +5. Inject the fold as the session's first context: charter, state (flagged + `degraded` if the tip was a `mark`), assignments, inbox, supervisor. +6. The incarnation's first turn is indistinguishable from the prior one's + next turn. That sentence is the validation gate (§11). + +### 7.2 Checkpoint cadence + +Every N tool calls, on `PreCompact`, on `Stop`, and immediately before any +`message.sent`, the adapter asks the incarnation for a structured state. It +appends `checkpoint` (or `handoff` on stop/compaction). If no state arrives +within the hook budget, it appends `mark`. The chain never has a gap longer +than one cadence tick. + +### 7.3 Crash → takeover → the stale write + +1. IC `ivy-ic-3`'s host dies mid-task. Its tip is a `checkpoint` at seq 41. +2. Supervisor `ivy-lead` derives liveness at read time: tip age > threshold, + transcript mtime stale, no process. It appends + `takeover{ by: ivy-lead, from_incarnation: inc-7, reason, evidence }` at + seq 42, then incarnates a replacement (`resume` at 43) — or spawns a + worker that does. +3. drive re-mints the scope cap for the new incarnation and revokes inc-7's; + custody/gate grants naming inc-7 are dead (§4.6). +4. inc-7 was not dead, only slow. Its next `checkpoint` arrives with + `prev = hash(41)` and `incarnation_id = inc-7`: refused, `prev_mismatch` + (and `stale_incarnation` on the body). The refusal names the successor. + inc-7's host adapter stops the session cleanly. Nothing was corrupted; + nothing needed a lock. + +### 7.4 Delegate + +1. `ivy-lead` folds its chain, reads its scope (a dossier phase), and picks a + task. +2. `org assign ivy-ic-3 --work dossier:ivy/p2/t4` — refused if any chain + holds an open assign for that work id. +3. `org send ivy-lead --to ivy-ic-3 --type delegate --body {...}` → a + `message.sent` at `(ivy-lead, 88)`; transport delivers; the IC's next + incarnate or checkpoint tick appends `message.received` naming + `(ivy-lead, 88, hash)`. +4. parley's `org-delegate.parley` says the only legal reply to `delegate` is + `report` or `escalate`; `observe` over both chains flags anything else. + +### 7.5 Report / escalate routing + +`report` goes to the sender's supervisor chain. `escalate` carries a tier; +the lead's charter says which tiers it decides and which it must forward. +The operator sees only what reaches a lead's `escalates_to: operator` — the +five percent. + +### 7.6 Cross-chain verification + +`org audit` over two chains: for every `message.sent` on A naming +`(B, seq, hash)`, B's record at `seq` has that hash and is a +`message.received` naming A's record back. Either chain can be lying; both +can't agree on a lie without the anchor key. + +## 8. Concurrency / consistency / failure model + +- **One writer per chain at a time**, enforced by `prev` — not by a lock. + The file lock around the `write(2)` is a mechanism for atomic appends, not + the ownership model. +- **Torn tail** → truncate on read, recount, continue (switchboard). A torn + tail after a `resume` means the incarnation never existed; it re-incarnates. +- **Anchor key missing** → `anchor_key_missing`, loud; minting is a + first-append concern only (gate's rule). +- **Host down, chain healthy** → nothing is lost; the next incarnation folds + to the tip. This is the whole point. +- **State dir lost** → the role is lost. Chains are small files; back them up + with the rest of the state dir. A future phase may replicate the anchor to + the gate log as a receipt. +- **Clock skew** → liveness is a heuristic over several signals; the chain + itself never depends on wall time for correctness. +- **Two supervisors race a takeover** → second `takeover` has a stale + `prev`; refused. The two-leads topology makes this the common case, and it + is handled by the same rule as every other fork. + +## 9. Rollout / implementation plan + +| Phase | Goal | High-level tasks | Depends on | Gate | +| --- | --- | --- | --- | --- | +| **p0 charter** | This document reviewed and locked | Reviewer panel; fold findings; decide §10 | — | design locked | +| **p1 contracts/org** | The leaf package | Types + embedded schemas for the 5 contracts; `validate.go`; `reduce.go` (fold + refusals); conformance, property, and fuzz tests; `Envelope` kind registration; hygiene CI (leaf imports nothing) | p0 | `go test ./contracts/org/...` green; mutation tests for each refusal | +| **p2 laws** | Machine-checked chain laws | Port `fm-epoch-replay-laws` fold ≡ checkpoint-resume ≡ replay; add contiguity, single-tip, resume-requires-tip, takeover-invalidates-stale; adversarial reducer that admits a fork must fail | p1 | `lake build`, axiom audit, counterexample fixture | +| **p3 host adapter** | One role resumes mid-thought on Claude Code | `org` CLI (incarnate / checkpoint / handoff / mark / fold / audit) over `contracts/org`; hooks: SessionStart / PreCompact / Stop / N-calls; repo-keyed memory; chain state dir + anchor | p1 | **VALIDATION GATE** — §11 test 1 | +| p4 drive runtime | Roles over driver/worker | genesis/charter verbs; supervision reducer; `takeover` mints/revokes caps; `org tree`; grants carry `incarnation_id` (custody + gate) | p3 ✓ | §11 test 2 | +| p5 parley | Legal conversations | `org-delegate.parley`; `grants`/`effects`/`receipt` in the algebra; `observe` over chain pairs | p4 | real chains classify clean | +| p6 the slice | The org at 1/10 scale | one lead, three ICs, one repo, one day; kill an IC; count operator prompts | p4, p5 | §11 test 3 | + +Committed: p0–p3. p4–p6 are gated on p3 proving the thesis for a single +role. Rough scope: p1 ≈ execution's size (~1.5k weighted LOC incl. tests); +p3 ≈ 600 (bash + Go CLI); p2 ≈ one Lean file plus adversarial twin. + +## 10. Open questions + +1. **Grant binding field** (§4.6): extend custody's and gate's grant bodies + separately, or introduce `contracts/authority` with an `incarnation` + field both adopt? Leaning shared; reviewer call. +2. **Checkpoint authorship on hosts without a prompt surface** (`codex exec`, + SDK workers): the adapter can only `mark`. Is a chain of marks with + occasional agent-authored handoffs good enough for ICs, or does the SDK + host need a mandatory end-of-turn `org checkpoint` tool call? +3. **N for the tool-call cadence.** Start at 25; measure state drift between + checkpoints in p3 and tune. +4. **Where the state dir lives across machines.** Today `~/dev/*-state` + siblings; a role incarnated on a second machine needs the chain and the + anchor key. Sync mechanism is out of scope here but must be named by p4. +5. **Operator as a role?** The operator's decisions (grants minted, parks + resolved) already land in gate's log. Whether the operator gets a chain + (so `escalate` has a receiver with a fold) is a p4 question. +6. **Naming.** `org` is the working name; `roster` is taken. +7. **Does any part of the org need a live actor?** switchboard showed the + resident form's one unique win is serialized ownership, which the chain + now supplies without a process. The remaining candidate is message + *delivery*: a per-role inbox that must serialize concurrent senders. The + chain already serializes `message.received` appends, so the bet is no — + channel's locked `write(2)` is enough transport. If p6 shows lost or + reordered delegations, switchboard's session actor (Gleam/OTP, journal + replay, `SequenceGap`) is the ready-made answer for that one seam, and + parley's Gleam bus already runs in its style. + +## 11. Validation plan + +Three binary tests, one per gate, no vibes. + +1. **Resume mid-thought (p3 gate).** In each of ivy, ship, and gate: charter + one role; run a real task for ≥ 30 tool calls; kill the session without + warning; incarnate fresh. Ask "where were we and what's next." Pass if + the answer names the current work, the last decision and its why, the + open threads, and the next action — and a blind reader cannot tell the + transcript was cut. Run with the chain and without (today's cold open) on + the same three tasks; the delta is the result. +2. **Ownership (p4 gate).** Two incarnations of one role race: exactly one + holds the tip; the other's write is refused with `prev_mismatch`. A + supervisor takeover makes the displaced incarnation's grant unspendable + at custody — checked by a refused request in custody's log. +3. **The slice (p6 gate).** One lead, three ICs, one repo, one working day. + Pass if: the operator sends ≤ 10 prompts; an IC killed mid-task is taken + over and its replacement resumes from the chain without operator input; + `org audit` over every chain pair is clean; parley `observe` classifies + every trace as complete or stalled, none deviating. From e31beba819487970cf6b6b91fa2363f0adac1aae Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Fri, 21 Aug 2026 21:53:56 -0700 Subject: [PATCH 02/24] =?UTF-8?q?docs(org):=20TDD=20v2=20=E2=80=94=20fold?= =?UTF-8?q?=20review=20round=201,=20adopt=20four=20bakeoff=20kernels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nine review findings folded, with four decisions replacing v1 hand-waves: the append critical section (read-verify-write under one lock); the cap ledger demoted to a derived cache so the two-plane write need not be atomic; liveness as an incarnation-declared next_due; and grant revocation via a fencing token that needs no replication and no gate->org dependency. Reading hack-branchroom / hack-mandate / hack-obligation / hack-proofline produced three corrections v1 got wrong: the stale writer that matters re-reads the tip (reversing the check order), incarnation ids must be digests not counters, and the cross-chain audit must reason about absence or it is a suppression attack. All four kernels canonicalize with json.Marshal; contracts/org ships a versioned canonical encoder instead. Co-Authored-By: Claude Fable 5 --- docs/features/org/spec.md | 437 ++++++++++++++++++++++++++++++++++---- 1 file changed, 397 insertions(+), 40 deletions(-) diff --git a/docs/features/org/spec.md b/docs/features/org/spec.md index 2fb4d245..ccef9a9c 100644 --- a/docs/features/org/spec.md +++ b/docs/features/org/spec.md @@ -2,10 +2,22 @@ **Status:** draft / proposal — NOT a build commitment. The artifact we decide from. **Owner:** @mh -**Date:** 2026-08-21 -**Related:** [drive-v0](../../../../drive/docs/features/drive-v0/spec.md) (session classes, scope-bound caps, FR6 "liveness is derived"); [session-claims](../session-claims/spec.md); [execution-runtime](../execution-runtime/spec.md) and `contracts/execution` (the leaf-package shape this copies); `contracts/envelope.go` (the hash-chained record this reuses); [gate DESIGN — tamper model](../../../../gate/docs/DESIGN.md); [agents-as-processes-gleam](../../../../agents-as-processes-gleam/README.md) (the ownership finding); [fm-epoch-replay-laws](../../../../fm-epoch-replay-laws/README.md) (the fold laws to port); [parley](../../../../parley/README.md) (role protocols); dossier project `org`. - -> **Reviewers — focus areas:** §4.2 (the tip of a role's chain *is* ownership — the load-bearing claim), §4.6 (grants bind to incarnations, not chain heads), §4.7 (who writes the distilled state, and what a mechanical `mark` may stand in for), §7.3 (crash → takeover → the stale incarnation's next write), §9 (what is committed before the validation gate vs. after). +**Date:** 2026-08-21 · **v2** 2026-08-22 (review round 1 folded; four bakeoff kernels read and adopted) +**Related:** [drive-v0](../../../../drive/docs/features/drive-v0/spec.md) (session classes, scope-bound caps, FR6 "liveness is derived"); [session-claims](../session-claims/spec.md); [execution-runtime](../execution-runtime/spec.md) and `contracts/execution` (the leaf-package shape this copies); `contracts/envelope.go` (the hash-chained record this reuses); [gate DESIGN — tamper model](../../../../gate/docs/DESIGN.md); [agents-as-processes-gleam](../../../../agents-as-processes-gleam/README.md) (the ownership finding); [fm-epoch-replay-laws](../../../../fm-epoch-replay-laws/README.md) (the fold laws to port); [parley](../../../../parley/README.md) (role protocols); the 21 Aug bakeoff kernels `hack-branchroom`, `hack-mandate`, `hack-obligation`, `hack-proofline` (§4.9); dossier project `org`. + +> **Reviewers — focus areas:** §4.2 (the tip of a role's chain *is* ownership; lock scope; the cap ledger as a derived cache), §4.6 (grants carry a fence — a change to gate's trust model, not an additive field), §4.9 (four of these laws are already built; port rather than rewrite), §7.3 (the re-reading stale writer — the case that decides §6's check order), §7.6 (absence-based audit and the suppression attack), §9 (what is committed before the validation gate vs. after). + +> **What changed in v2.** Nine findings from review round 1 are folded, and four +> decisions replace v1 hand-waves: the append critical section is now specified +> (§4.2); the cap ledger is demoted to a derived cache so the two-plane write +> need not be atomic (§4.2); liveness thresholds become a `next_due` the +> incarnation declares (§4.2); and grant revocation gets a real mechanism — a +> fencing token that needs no replication and no runtime dependency on the org +> subsystem (§4.6). Reading the four bakeoff kernels then produced three +> corrections v1 got wrong: the stale-writer case that actually matters is the +> one that re-reads the tip (§7.3), which reverses §6's check order; incarnation +> ids must be digests rather than counters (§5); and the cross-chain audit must +> reason about absence or it is a suppression attack (§7.6). ## 1. Problem & hypothesis @@ -27,6 +39,15 @@ hold zero memory files between them; the hand-off verbs that would fix this two claim events in the two days after they shipped. Instructions are advice; nobody types the verb. +The two halves of that evidence are one mechanism, not two symptoms. Memory +is keyed by working directory, so a worktree session accrues state into a +bucket nothing will ever read again — which makes continuity *invisible*, +which is why the hand-off verbs feel like bureaucracy nobody types. Fixing +the key is not a footnote in the host adapter (§6); it is the smallest +version of this document's whole claim, and the org substrate gets it as a +consequence of making the chain — not the directory — the thing state hangs +off. + Two prior experiments fix the frame: - **switchboard** asked whether a long-lived context needs a long-lived @@ -165,8 +186,48 @@ Relationship to drive's caps: they are the same event seen from two planes. drive's cap says *this session has authority over scope S*; the chain says *this incarnation is role R and knows what R knows*. A `takeover` record on R's chain is what mints the successor's cap and revokes the predecessor's -(§7.3). *Alternative:* leases/heartbeats — rejected: liveness is never -recorded (FR7); a lease is recorded liveness by another name. +(§7.3). *Alternative:* leases/heartbeats — rejected twice over: liveness is +never recorded (FR7), and a lease needs a clock-holding daemon, which is the +always-on process the non-goals exclude. + +**The `prev` check is detective; the lock is what makes it preventive.** +Without a lock, two incarnations can both read `tip = hash(41)`, both build a +record with `prev = hash(41)`, and both append: the file now has two records +at seq 42, and the *next reader* refuses with `seq_gap` — but both writers +believed they won, which is precisely the corruption switchboard's baseline +suffered. So the append verb takes an exclusive advisory lock on that one +chain file across the **whole** critical section — acquire → read tail → +`Admissible(tip, next)` → write → fsync → release — not just the write. The +two mechanisms are deliberately redundant: the lock prevents the fork, and +`prev` still detects it if the lock is ever bypassed (a hand-edited file, a +second implementation, a stale-lock steal). Stale-lock handling reuses gate's +`lock.go` staleness clock, including its known TOCTOU takeover race — bounded +here because a thief still has to pass the `prev` check. + +**Authority is computed, not stored — the cap ledger is a derived cache.** +The reviewer asked whether the chain append and drive's cap revocation are +atomic. They are not, and making them atomic would require the contracts leaf +to reach into drive's ledger, which the boundary law forbids. Instead the +divergence is made *harmless* rather than merely detectable: **the chain is +authoritative for role ownership; drive's cap ledger is a derived cache that +may lag but can never grant more than the chain allows.** Every authority +decision is a join of (cap ledger, chain tip) in which the chain wins, so a +`takeover` that lands on the chain while the cap revoke fails leaves the +displaced incarnation with a cap that no reader will honor. The cap ledger +exists for speed and for drive's own bookkeeping, never as a second source of +truth. + +**Liveness thresholds are declared, not configured.** §7.1 needs to know +whether a tip is stale, and a global threshold would have to be tuned against +the checkpoint cadence — the reviewer correctly noted these are one +calibration, not two. Rather than couple them, every `checkpoint`, `handoff`, +and `mark` carries `next_due`: a wall-clock deadline the incarnation commits +to writing its next record by. Staleness is then a comparison against a +timestamp already in the record, needing no global tuning, and an incarnation +about to do something slow extends its own deadline rather than tripping a +supervisor. A missed `next_due` is *evidence* of death, joined with host +signals (§7.3) — never proof on its own, since FR7 still forbids treating a +recorded timestamp as recorded liveness. ### 4.3 Roles are durable; incarnations are disposable @@ -198,16 +259,53 @@ supervisor so there is no singleton whose death orphans the org. the non-goals exclude, and it is exactly the single point of failure the two-leads shape exists to avoid. -### 4.6 Grants bind to incarnations, not chain heads - -A gate grant or custody grant minted for a role carries `incarnation_id`. -*Alternative considered:* bind to the exact `(seq, hash)` at mint time — -rejected: every checkpoint would invalidate every grant. Binding to the -incarnation means grants survive checkpoints and die on `takeover`, which is -the intended semantics: authority follows continuity, and a displaced -incarnation that still holds a token cannot spend it. **Reviewer call:** this -is additive to custody's grant shape (`cst2_…`) and gate's; confirm the field -lands in both or in a shared `contracts/authority` extension. +### 4.6 Grants carry an incarnation and a fence + +A gate grant or custody grant minted for a role carries two new fields: +`incarnation` (which incarnation it was minted to) and `fence` (the role +chain's `seq` at mint time). *Alternative considered:* bind to the exact +`(seq, hash)` — rejected: every checkpoint would invalidate every grant. +Binding to the incarnation means grants survive checkpoints and die on +`takeover`, which is the intended semantics. + +**The fence is how a verifier refuses a displaced incarnation without asking +anyone.** "A displaced incarnation cannot spend its grant" was a goal +statement in v1, not a design. Three mechanisms were considered: gate queries +the org chain at verify time (couples gate's availability to the org state +dir, which a CI-hosted required check will not have); drive pushes a +revocation to gate and custody at takeover (the same two-plane write, one +level down — it can fail); or the takeover is replicated into gate's log as a +record it already knows how to read (needs the replication step, which can +also fail). All three leave a window. + +The fence closes it without replication, using the classic fencing token: a +verifier keeps a **per-role high-water mark** of the highest `fence` it has +honored, and refuses any grant presenting a `fence` below it. Because a +`takeover` advances the chain's `seq`, the successor's grants necessarily +carry a higher fence than the predecessor's; the first time the successor +does anything at all, the predecessor's grants become permanently +unspendable — at every verifier independently, with no message passing +between planes. The residual window is "after the takeover, before the +successor's first spend," and `org incarnate` closes even that by bumping the +fence as a no-op on resume. The high-water mark is one integer per role in +each verifier's existing state; gate and custody gain no dependency on the +org subsystem, only a monotone comparison. + +**This is a real change to gate's trust model, not an additive field.** Gate +grants are currently bounded by scope, tier, and time. A fence adds +*ordering* as a fourth bound, and a stored high-water mark is new mutable +state in the verifier. It is small, but it should be reviewed as a change to +what gate promises, not as a schema addition. + +**Decision on where the fields live** (v1 left this to the reviewer): a +shared `contracts/authority` extension carrying `Incarnation` and `Fence`, +imported by both custody and gate — not parallel fields in each. The +criterion is that this is one invariant ("authority follows continuity"), and +an invariant maintained in two places is an invariant that will drift; a +single type gives one import, one conformance test, and one place to state +the monotonicity law. The cost is coupling between two grant shapes that may +later diverge, which is accepted because the shapes are stable and the +invariant is load-bearing. ### 4.7 Who writes the distilled state — and what a `mark` may stand in for @@ -224,6 +322,31 @@ incarnation's first act is to reconstruct from refs. Contract law: a (`empty_next`); a chain whose tip is a `mark` is legal; a role with no chain is not a role (`chain_missing`). +**The hook budget is a hard timeout, never a wait.** The adapter asks for a +structured state and, when the ≤ 3 s budget expires, appends a `mark` and +returns. It never blocks the session and never interrupts an in-flight model +call to get one. A live-but-busy incarnation therefore produces marks, which +is correct: a mark says "still here, nothing authored," which is exactly true. + +**`mark` is exempt from `empty_next`, and the asymmetry is deliberate.** A +`checkpoint` is authored by the incarnation and is a statement of intent, so +having no next action is malformed. A `mark` is authored by the host and is a +statement of continuity, not intent — it has no `next` field at all. A reader +of `validate.go` should find that stated rather than inferred. + +**The cold-reconstruction floor.** A `mark` points at a transcript that may +be gone — the host crashed, the runner was ephemeral, the machine is another +machine. So the guarantee is stated in two tiers rather than assumed. After a +`mark` tip, a resumed incarnation is **guaranteed**: its role identity and +charter; its open assignments; the last *authored* state from the most recent +`checkpoint`/`handoff`, however old; and the mark's git facts (branch, head, +dirty paths), which are recoverable from the repository itself even with no +transcript. It is **not** guaranteed the reasoning since that last authored +state. That is the honest floor: continuity of *commitment*, not of thought. +It also sets the checkpoint cadence's real job — the cadence bounds how much +thought a crash can cost, and §10.3's N is that dial. §11 test 1 therefore +runs the cold case (transcript deleted before resume), not only the warm one. + ### 4.8 Where it lives `contracts/org` is a leaf: types, embedded JSON schema, `validate.go` @@ -233,11 +356,77 @@ runtime (incarnate, supervise, takeover, tree) is drive, which already owns session classes and scope caps. The Claude Code host adapter is hooks. The role protocols are parley. No new repository. +### 4.9 Four of these laws are already built — port them + +The 21 Aug 2026 bakeoff round produced four standard-library Go kernels that, +read against this design, are not experiments about verification in general. +They are implementations of four laws this substrate needs, each with frozen +fixtures and a planted mutant. p1 ports them; it does not rewrite them. + +| Kernel | Law it already implements | Where it lands | +| --- | --- | --- | +| **hack-branchroom** | A pure `Reduce(state, event) (state, Decision)` with the tip as `parentEventDigest`, `nextSequence` covering duplicate-and-gap in one condition, and a fresh-epoch check that refuses a *perfectly correlated* stale writer | `reduce.go` — the fold, near-verbatim | +| **hack-mandate** | Attenuating delegation: a child may shrink actions, shrink the validity window, and decrement depth; subject and artifacts are equality-locked; the audience of a grant is the only key that may mint the next one | `contracts/authority` — lead → IC | +| **hack-obligation** | A monotone frontier: content-addressed obligation identity, a four-state lattice where `discharged` is re-enterable and `superseded` is terminal, and an add-only agent overlay with a mandatory ratchet | the definition of done a role cannot shrink by fiat | +| **hack-proofline** | The two-witness rule for a bilateral fact, and the split between *the edge that made something historical* and *everything that went historical* | `org audit` (§7.6) | + +**The single best idea across all four is content-addressed identity.** +hack-obligation derives an obligation's id from `(kind, claim, goal +identity)`, so a change to the subject *forks a new obligation* rather than +mutating an existing one — cross-revision contamination becomes impossible by +construction rather than by a check. §5's incarnation ids adopt the same +move, and for the same reason. + +**What does not port.** Every one of the four models its subject as a git +artifact — `base_sha`, `head_sha`, `diff_digest`. A dossier task has no head +SHA. The generalization that preserves the useful properties is a fixed +identity spine (`task`, `revision`, `kind`, `digest`) where the digest covers +a kind-specific body: the structs stay comparable, the revision-pinning attack +story survives, and git-ness leaves the contract. Three further mismatches are +recorded as open questions rather than smoothed over: mandate's grants assume +an immutable subject and a short window (§10.7), obligation assumes one active +goal where a role holds many, and obligation treats an oracle snapshot as a +*complete statement of the world* so that silence revokes — the semantic most +likely to need inverting when signals come from independent sources. + +### 4.10 Two corrections that apply to all four, and to us + +**Canonical encoding must not be `json.Marshal`.** All four kernels +canonicalize by marshalling Go structs, which means field order is Go +*declaration* order and `&`, `<`, `>` are HTML-escaped. The digests are stable +Go-to-Go and nowhere else, and none of them carries a digest-scheme version, +so reordering a struct field silently invalidates every historical digest. +That is acceptable in a frozen hackathon artifact and unacceptable in an +append-only chain meant to outlive refactors and be verified by a second +implementation. `contracts/org` ships an explicit field-ordered canonical +encoder with a versioned scheme before it writes its first record. This is +cheap now and unfixable later. + +**The mutant is not optional — it is how a law is stated.** Each kernel ships +the *wrong* law as a first-class code path (a `headOnly bool`, a +`SettledIsTerminal` string, a retain-parent-epoch binder that bypasses the +public constructor) and asserts in tests that it gets the planted case wrong, +while proving both paths consumed byte-identical input. That last assertion is +what forecloses the "you fed them different inputs" objection. Every refusal +in §6 gets the same treatment in p1: a mutant that removes the check, a +fixture the mutant accepts and production refuses, and the refusal identifier +frozen into a golden artifact digest so that renaming a code is a test +failure. This is the difference between a law and a comment. + ## 5. Data model **Role id.** `org/` — e.g. `org/lead-a`, `org/ivy-lead`, `org/ivy-ic-3`. Lowercase, stable, never reused. +**Incarnation id must be unguessable.** It is the digest of the `resume` (or +`takeover`) record that created it — not a counter, not a sequence. This is a +correction taken from hack-branchroom, whose epochs are `parent + n` and are +therefore trivially guessable: its stale-writer refusal is only sound if the +displaced writer never stamps the fresh epoch, and a robust writer that +re-reads the tail before appending will copy the fresh id *by accident* and +sail through. A digest cannot be arrived at by a writer that has not read the +record that minted it, which is exactly the population we want to exclude. + **Chain layout.** `/org//chain.jsonl` plus the anchor record under the key dir (per gate). Optional `snapshot-.json` every 256 records; a snapshot is a cache of the fold and is deletable. @@ -248,10 +437,10 @@ record under the key dir (per gate). Optional `snapshot-.json` every | kind | body | law | | --- | --- | --- | | `genesis` | `charter{ scope[], decides[], never_decides[], escalates_to, supervisor, supervises[], capabilities[] }` | seq 1 only; exactly one | -| `resume` | `incarnation{ id, host, session_ref, started_at }` | `prev` == tip; starts an incarnation | -| `checkpoint` | `state{ goal, doing, decided[{what, why}], open[], next[], refs[] }`, `incarnation_id` | `next` non-empty; ≤ 4 KB; `incarnation_id` == current | -| `handoff` | same as `checkpoint` + `reason: stop\|compaction\|release` | ends an incarnation cleanly | -| `mark` | `mechanical{ session_ref, git{branch, head, dirty[]}, last_tools[], transcript_offset }` | host-authored; degraded | +| `resume` | `incarnation{ id, host, session_ref, started_at }` | `prev` == tip; starts an incarnation; `id` is the digest of this record, never a counter (see below) | +| `checkpoint` | `state{ goal, doing, decided[{what, why}], open[], next[], refs[] }`, `incarnation_id`, `next_due` | `next` non-empty; ≤ 4 KB; `incarnation_id` == current; `next_due` in the future | +| `handoff` | same as `checkpoint` + `reason: stop\|compaction\|release` | ends an incarnation cleanly; no `next_due` (nothing is coming) | +| `mark` | `mechanical{ session_ref, git{branch, head, dirty[]}, last_tools[], transcript_offset }`, `next_due` | host-authored; degraded; exempt from `empty_next` (§4.7) | | `takeover` | `by: , from_incarnation, reason, evidence[]` | only a role named `supervisor` in genesis; ends the current incarnation | | `assign` / `release` | `work{ kind: dossier, id }`, `incarnation_id` | one open assign per work id across all chains (checked by drive at write time, law at fold time) | | `message.sent` / `message.received` | `msg{ type, to\|from, ref{role, seq, hash}, body }` | `type ∈ {delegate, report, escalate, ask, answer, takeover_notice}` | @@ -262,14 +451,20 @@ record under the key dir (per gate). Optional `snapshot-.json` every RoleState { Role, Charter, Tip{ Seq, Hash }, Count, - Incarnation *{ ID, Host, SessionRef, Since }, // nil when no live incarnation + Incarnation *{ ID, Host, SessionRef, Since, NextDue }, // nil when none live State{ Goal, Doing, Decided, Open, Next, Refs, Degraded bool, At seq }, + OrphanedSince *int, // set when a takeover cut an incarnation off mid-work; + // the successor must assess the refs before continuing (§7.3) Assignments[]{ Work, Since }, Outbox[]{ To, Type, Seq }, Inbox[]{ From, Type, Seq }, Supervisor, Supervises[] } ``` +`NextDue` is a declared deadline, not a heartbeat: the fold reports what the +incarnation committed to, and a supervisor joins that with host signals to +*derive* liveness. Nothing in `RoleState` asserts that a role is alive. + Liveness is **not** a field. drive derives it from `Tip` age and host signals at read time. @@ -300,6 +495,37 @@ Refusal codes (stable strings, surfaced verbatim by every runtime): | `empty_next` | `checkpoint`/`handoff` with no next actions | | `oversize_state` | `state` > 4 KB | | `anchor_mismatch` | keyed tip anchor disagrees with `(head, count)` — truncation or rewrite | +| `fence_regression` | a grant presents a `fence` below the verifier's high-water mark for that role (§4.6) | + +**Check order is part of the contract.** Refusals are evaluated +structural-before-semantic, and the first failure wins, so that a given +malformed record always produces the same code no matter which runtime +evaluated it: + +1. **Version** — `schema_version` this reader accepts. +2. **Shape** — `ValidateRecord`: per-kind body law (`empty_next`, + `oversize_state`, malformed ids and refs). +3. **Role** — the record names this chain's role at all (a routing error, not + a staleness one). +4. **Incarnation** — `stale_incarnation`, for every kind that carries an + `incarnation_id`. `resume` and `takeover` are exempt: they *establish* an + incarnation rather than asserting one. +5. **Chain position** — `seq_gap`, then `prev_mismatch`. +6. **Role semantics** — `not_supervisor`, one-open-assign. + +**Incarnation is checked before chain position, and that order is load-bearing +rather than cosmetic.** The intuition runs the other way — structural before +semantic — and v1 had it backwards. The case that decides it is §7.3's +re-reading writer: a displaced incarnation that re-reads the tail before +appending presents a correct `prev` and a correct `seq`, so a +position-first order returns *no* refusal from step 5 and reaches the +incarnation check anyway. Meanwhile a displaced incarnation that appends +blindly gets `prev_mismatch` under a position-first order — a *worse* +diagnosis, because the true condition is "you have been replaced," not "you +lost a race." Checking incarnation first gives the same correct answer in both +cases. hack-branchroom's reducer independently arrived at this: it compares +epoch before branch, sequence, tip digest, and call id, precisely so that a +perfectly-correlated stale writer still receives the specific reason. **Runtime verbs (drive; CLI + MCP, same names).** @@ -354,18 +580,55 @@ than one cadence tick. ### 7.3 Crash → takeover → the stale write 1. IC `ivy-ic-3`'s host dies mid-task. Its tip is a `checkpoint` at seq 41. -2. Supervisor `ivy-lead` derives liveness at read time: tip age > threshold, - transcript mtime stale, no process. It appends +2. Supervisor `ivy-lead` derives liveness at read time: the record's declared + `next_due` has passed, transcript mtime is stale, no process. It appends `takeover{ by: ivy-lead, from_incarnation: inc-7, reason, evidence }` at seq 42, then incarnates a replacement (`resume` at 43) — or spawns a worker that does. -3. drive re-mints the scope cap for the new incarnation and revokes inc-7's; - custody/gate grants naming inc-7 are dead (§4.6). -4. inc-7 was not dead, only slow. Its next `checkpoint` arrives with - `prev = hash(41)` and `incarnation_id = inc-7`: refused, `prev_mismatch` - (and `stale_incarnation` on the body). The refusal names the successor. - inc-7's host adapter stops the session cleanly. Nothing was corrupted; - nothing needed a lock. +3. The successor's grants are minted at `fence = 43`. drive re-mints the scope + cap and marks inc-7's revoked, but that write is a cache update, not the + authority (§4.2): even if it fails, inc-7's grants carry `fence = 40` and + every verifier refuses them the moment it has honored a higher fence + (§4.6). `org incarnate` bumps the fence on resume so the window does not + depend on the successor reaching a verifier first. +4. inc-7 was not dead, only slow, and comes back to write. There are **two + shapes, and the second is the common one**: + - *Blind append.* inc-7 writes with the `prev` it remembered, + `hash(41)` — refused on chain position. + - *Re-read then append.* inc-7's write fails, or its adapter simply reads + the tail before every append (the normal shape for a robust writer), so + it re-reads the chain and appends with the **current** tip, seq 44, and + `incarnation_id = inc-7`. Now `prev` matches. `seq` matches. Every + correlation field matches. **Only `incarnation_id` refuses it.** + + The second case is why the incarnation check is not redundant with `prev` + in a linear chain, and it is exactly the law hack-branchroom's reducer + already implements and mutation-tests: its late-parent terminal is + perfectly correlated on branch, sequence, call id, and tip digest, and is + caught solely by the epoch comparison. v1 of this document described only + the blind-append case and would have justified dropping the check. + + Either way the refusal names the successor, and inc-7's host adapter stops + the session cleanly. + +**What is and is not guaranteed here.** The chain is uncorrupted and no lock +was needed for the ownership decision. But inc-7 may have *done work* between +seq 41 and its refused append: files written, commits pushed, a message sent +whose receiving chain now records a delegation from an incarnation that no +longer exists. That work is real and lives outside the chain. The design does +not pretend otherwise — it makes the assessment an explicit obligation of the +successor: the fold surfaces `orphaned_since = 41` with the last-known refs, +and the successor's first task is to assess what it finds at those refs before +continuing. This is inherent to any crash-recovery model, not a flaw, but v1 +overclaimed by saying "nothing was corrupted" without qualifying it to the +chain. + +**Cross-chain fallout.** A `message.sent` from inc-7 that a peer already +recorded as `message.received` is not retracted — the record stands, since +chains are append-only. What changes is its interpretation: `org audit` +reports it as *sent by a since-displaced incarnation*, and the receiving role +may treat it as advisory. Retraction, if it is ever wanted, is a new record on +the sender's chain, never an edit. ### 7.4 Delegate @@ -391,8 +654,42 @@ five percent. `org audit` over two chains: for every `message.sent` on A naming `(B, seq, hash)`, B's record at `seq` has that hash and is a -`message.received` naming A's record back. Either chain can be lying; both -can't agree on a lie without the anchor key. +`message.received` naming A's record back. + +**The audit must reason about absence, not only presence.** This is the +sharpest correction in the review round, and it comes from hack-proofline, +which computes retraction only when *both* witnessing edges are recorded and +otherwise reports "current." Over a single trusted bundle that fail-open rule +is a feature: nothing is retracted without evidence. Across two chains that +may disagree, **the identical rule is a suppression attack** — role B simply +omits the record, and A's grant goes on looking live forever. So the audit +carries a completeness obligation in both directions: every `message.sent` on +A *must* have a counterpart on B, and a missing counterpart is a finding +(`counterpart_absent`), never a silent pass. Presence-based reasoning is safe +only when you trust the store, and the whole point of two chains is that you +do not. + +**What the audit can and cannot catch.** With the keyed anchor it detects +truncation, rewrite, and now omission. It does **not** by itself catch a +*coherently* lying chain — an adversary who rewrites a record and updates +every reference around it consistently. Catching that needs per-record +signatures binding each append to the role's key, so that a record's presence +on A is evidence *about A* rather than an assertion by whoever assembled the +bundle. Signatures are deliberately **not** in p1: the realistic adversary +here is drift and accident, matching gate's stated tamper model, and adding +keys would cost the offline, keyless verification that makes the audit cheap +to run everywhere. It is recorded as the next layer down rather than as +something this design already has. + +**Invalidation is seq-scoped, not merely reachable.** When a takeover +displaces an incarnation at seq N, the facts that become historical are those +that depended on it *after* N. A pure reachability closure — proofline's +`descendants()` over one relation — would also sweep in facts that consumed +the incarnation legitimately *before* N. So the cascade filters on chain +position, and the two outputs stay separate the way proofline separates them: +the single edge that made something historical, and the set of everything +that went historical. Nothing is deleted; status is computed per query and +history stays byte-identical. ## 8. Concurrency / consistency / failure model @@ -413,16 +710,30 @@ can't agree on a lie without the anchor key. - **Two supervisors race a takeover** → second `takeover` has a stale `prev`; refused. The two-leads topology makes this the common case, and it is handled by the same rule as every other fork. +- **Both leads down at once** → nobody left who is named as supervisor, and + the org cannot recover itself. Rather than add a third watcher (which just + moves the problem), the **operator is the implicit supervisor of every + role**: `org takeover --by operator` is always legal, on any chain, + regardless of what the genesis names. This is the one privileged write in + the model, and it is the right one — the human is the root of authority + everywhere else in the workbench too. §11 test 3 kills both leads to + confirm the path is real rather than assumed. +- **The supervisor itself is stale** → a supervisor derives liveness from + `next_due` plus host signals, and can be wrong. A takeover on a role that + was merely slow is not corruption: the displaced incarnation is refused + cleanly (§7.3) and the cost is the orphaned-work assessment. The model + optimizes for *never two owners*, accepting *occasionally a premature + handover* — the reverse trade would require recorded liveness. ## 9. Rollout / implementation plan | Phase | Goal | High-level tasks | Depends on | Gate | | --- | --- | --- | --- | --- | | **p0 charter** | This document reviewed and locked | Reviewer panel; fold findings; decide §10 | — | design locked | -| **p1 contracts/org** | The leaf package | Types + embedded schemas for the 5 contracts; `validate.go`; `reduce.go` (fold + refusals); conformance, property, and fuzz tests; `Envelope` kind registration; hygiene CI (leaf imports nothing) | p0 | `go test ./contracts/org/...` green; mutation tests for each refusal | +| **p1 contracts/org** | The leaf package | Canonical encoder (versioned scheme) **first**; types + embedded schemas for the 5 contracts; `validate.go`; `reduce.go` — the fold **ported from hack-branchroom**, not written fresh; `contracts/authority` ported from hack-mandate; conformance, property, and fuzz tests; `Envelope` kind registration; hygiene CI (leaf imports nothing) | p0 | `go test ./contracts/org/...` green; **one mutant per refusal code** (§4.10), each proving production and mutant consumed byte-identical input; refusal identifiers frozen into a golden digest | | **p2 laws** | Machine-checked chain laws | Port `fm-epoch-replay-laws` fold ≡ checkpoint-resume ≡ replay; add contiguity, single-tip, resume-requires-tip, takeover-invalidates-stale; adversarial reducer that admits a fork must fail | p1 | `lake build`, axiom audit, counterexample fixture | | **p3 host adapter** | One role resumes mid-thought on Claude Code | `org` CLI (incarnate / checkpoint / handoff / mark / fold / audit) over `contracts/org`; hooks: SessionStart / PreCompact / Stop / N-calls; repo-keyed memory; chain state dir + anchor | p1 | **VALIDATION GATE** — §11 test 1 | -| p4 drive runtime | Roles over driver/worker | genesis/charter verbs; supervision reducer; `takeover` mints/revokes caps; `org tree`; grants carry `incarnation_id` (custody + gate) | p3 ✓ | §11 test 2 | +| p4 drive runtime | Roles over driver/worker | genesis/charter verbs; supervision reducer; `takeover` mints caps and advances the fence; `org tree`; custody and gate adopt `contracts/authority` and keep a per-role fence high-water mark | p3 ✓ | §11 test 2 | | p5 parley | Legal conversations | `org-delegate.parley`; `grants`/`effects`/`receipt` in the algebra; `observe` over chain pairs | p4 | real chains classify clean | | p6 the slice | The org at 1/10 scale | one lead, three ICs, one repo, one day; kill an IC; count operator prompts | p4, p5 | §11 test 3 | @@ -432,13 +743,16 @@ p3 ≈ 600 (bash + Go CLI); p2 ≈ one Lean file plus adversarial twin. ## 10. Open questions -1. **Grant binding field** (§4.6): extend custody's and gate's grant bodies - separately, or introduce `contracts/authority` with an `incarnation` - field both adopt? Leaning shared; reviewer call. -2. **Checkpoint authorship on hosts without a prompt surface** (`codex exec`, +*(v1 items 1 and 2's first half are now decided — see §4.6 and §4.7. Items +below are renumbered; new questions 8–10 come from reading hack-mandate.)* + +1. **Checkpoint authorship on hosts without a prompt surface** (`codex exec`, SDK workers): the adapter can only `mark`. Is a chain of marks with occasional agent-authored handoffs good enough for ICs, or does the SDK - host need a mandatory end-of-turn `org checkpoint` tool call? + host need a mandatory end-of-turn `org checkpoint` tool call? §4.7's cold + floor makes this measurable rather than theoretical — a mark-only host + guarantees continuity of commitment but never of thought, so the question + is whether an IC's 2–3 day run can afford that. Decide from p3 evidence. 3. **N for the tool-call cadence.** Start at 25; measure state drift between checkpoints in p3 and tune. 4. **Where the state dir lives across machines.** Today `~/dev/*-state` @@ -457,6 +771,36 @@ p3 ≈ 600 (bash + Go CLI); p2 ≈ one Lean file plus adversarial twin. reordered delegations, switchboard's session actor (Gleam/OTP, journal replay, `SequenceGap`) is the ready-made answer for that one seam, and parley's Gleam bus already runs in its style. +7. **Subject drift on long-running work.** hack-mandate binds authority to a + `TaskRevision`, and bumping it kills every outstanding grant instantly. + Correct for a fixed diff; a footgun for a three-day IC task where the lead + adds a phase on day two — you either never bump (defeating the binding) or + you silently revoke work in flight. The chain suggests a third path: a + revision bump is a `message.sent` to the assignee, and the IC's grant + survives until it acknowledges or the lead takes the work back. Needs + deciding before p4 mints anything against a dossier task. +8. **Canonical encoding.** hack-mandate's `EncodeCanonical` is + `json.Marshal` — field order is Go declaration order and `&`/`<`/`>` are + HTML-escaped, so it is Go-to-Go only. Anything this substrate signs or + digests must use a real canonical form (RFC 8785 / JCS or an explicit + field-ordered encoder) before a second language or a second process ever + verifies it. Cheap now, expensive after the first signed record exists. +9. **Does a role need an obligation frontier, and where?** hack-obligation's + monotone frontier is the natural answer to "may this role report done" — + an IC cannot close a task while mandatory work is open, and cannot shrink + that set by fiat. It is deliberately *not* in v0's record set, because it + needs two things this design has not settled: which principal may discharge + which kind of obligation (authority, which the frontier explicitly refuses + to hold), and whether a role's many concurrent goals each get their own + frontier. A p4 question. If it lands, take the kernel's lesson and **emit + the predicate** — a `mandatory_open` field, not a shape every caller + re-derives and half of them get wrong. +10. **Grant consumption.** hack-mandate records no `RequestID` and enforces no + budget: "may push" is not "may push 200 times." A three-day IC will make + thousands of requests against one grant. Does `contracts/authority` need a + consumption counter, or does custody's request log plus the fence make + after-the-fact accounting sufficient? Leaning the latter — accounting + beats enforcement here — but it should be a decision, not an omission. ## 11. Validation plan @@ -469,12 +813,25 @@ Three binary tests, one per gate, no vibes. open threads, and the next action — and a blind reader cannot tell the transcript was cut. Run with the chain and without (today's cold open) on the same three tasks; the delta is the result. + **1b — the cold case.** Repeat once per repo with the transcript deleted + before incarnating, so the tip is a `mark` pointing at nothing. Pass if + the resumed incarnation correctly reports what it does *and does not* + know: it names its charter, assignments, and last authored state, and it + says plainly that the reasoning since then is gone rather than inventing + it. A confident wrong answer here is a worse failure than an honest + partial one, and this test is the only place that distinction is caught. 2. **Ownership (p4 gate).** Two incarnations of one role race: exactly one holds the tip; the other's write is refused with `prev_mismatch`. A supervisor takeover makes the displaced incarnation's grant unspendable - at custody — checked by a refused request in custody's log. + at custody — checked by a refused request in custody's log carrying + `fence_regression`, with the org state dir made *unreadable* during the + check to prove the verifier needed no access to it (§4.6). 3. **The slice (p6 gate).** One lead, three ICs, one repo, one working day. Pass if: the operator sends ≤ 10 prompts; an IC killed mid-task is taken over and its replacement resumes from the chain without operator input; `org audit` over every chain pair is clean; parley `observe` classifies every trace as complete or stalled, none deviating. + **3b — decapitation.** Kill both leads at once. Pass if + `org takeover --by operator` recovers a lead chain and the org resumes — + confirming §8's supervisor-of-last-resort is a real path and not an + assumption written into a document. From 5c514c6be24ae99f5b5e6c0c2fdf3dbc49ee18a0 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Fri, 21 Aug 2026 22:20:11 -0700 Subject: [PATCH 03/24] =?UTF-8?q?docs(org):=20cattle=20not=20pets=20?= =?UTF-8?q?=E2=80=94=20narrow=20the=20claim,=20add=20the=20derive-don't-re?= =?UTF-8?q?cord=20law?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds 4.11 after reading Malucelli's '12 days straight' (7 cron'd loops on a $40/mo VM, 229 PRs opened / 213 merged, GitHub as the only source of truth). The article's architecture is correct for stateless sweep work and this design should not compete with it: maintenance is now an explicit non-goal here rather than a later phase of this machine. What remains is owned work, where the thread across restarts exists nowhere but in the agent. Reframes the premise: agents are cattle, and the chain is what makes them so - an agent holding days of reasoning is inherently a pet. Yields the law governing every record (if it can be derived, don't record it), a wip_limit that bounds production the way grants bound authority, and two observed failure modes: 7-day session expiry, and usage-limited sessions that hold the tick without exiting - the case that justifies next_due over a process check. Co-Authored-By: Claude Fable 5 --- docs/features/org/spec.md | 97 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/docs/features/org/spec.md b/docs/features/org/spec.md index ccef9a9c..8ff6d94b 100644 --- a/docs/features/org/spec.md +++ b/docs/features/org/spec.md @@ -18,6 +18,14 @@ > one that re-reads the tip (§7.3), which reverses §6's check order; incarnation > ids must be digests rather than counters (§5); and the cross-chain audit must > reason about absence or it is a suppression attack (§7.6). +> +> §4.11 is new and narrows the design's claim: agents are cattle, the chain is +> what makes them so, and a simpler architecture — cron'd stateless sweeps with +> GitHub as the source of truth — is correct for maintenance work and is now an +> explicit non-goal here rather than a later phase of this machine. It also adds +> the principle that governs what a record may hold (*if it can be derived, +> don't record it*) and a `wip_limit` that bounds production the way grants +> bound authority. ## 1. Problem & hypothesis @@ -75,7 +83,11 @@ boot prompt and becomes a theorem about a reducer. - Not an always-on runtime. Roles are durable; incarnations are disposable sessions on any host (Claude Code, Agent SDK, `codex exec`). -- Not a new work store. dossier holds tasks; assignments point at them. +- Not a new work store. dossier holds tasks; assignments point at them. More + strongly (§4.11): the chain records only what has no other home. +- Not a home for sweep work. Stateless, per-tick, idempotent maintenance — + dependency bumps, dead-code sweeps, test pruning — belongs on the simpler + architecture in §4.11, with no chain at all. - Not a chat surface. The operator's interface is a conversation with the leads, hosted wherever they already talk to agents. - Not consensus. One writer per chain, a supervisor that may take over, a @@ -413,6 +425,85 @@ fixture the mutant accepts and production refuses, and the refusal identifier frozen into a golden artifact digest so that renaming a code is a test failure. This is the difference between a law and a comment. +### 4.11 Cattle, not pets — and the two architectures that follow + +The agents are cattle. Any incarnation may be shot at any moment, and +replacing one is routine rather than an incident. That is the design's +premise, not a concession to it — and **the chain is what makes it true.** An +agent holding three days of reasoning in its head is inherently a pet: you +cannot kill it without losing something. Externalize what it knows and every +incarnation becomes disposable by construction. Residency is settled by the +same argument, from the other side: switchboard showed a live process buys +serialized ownership and nothing else, and the tip rule supplies that without +the process. + +A working system on this premise already exists and is worth naming, because +it is simpler than this one and correct where it applies. In +[*Claude Code, 12 days straight*](https://malucelli.net/posts/2026-08-18-claude-code-12-days-straight/) +Malucelli runs seven cron'd loops on a $40/month VM against a Claude Max +subscription and reports 229 pull requests opened and 213 merged in twelve +days. His whole state story is one sentence: *nothing on the box is worth +backing up* — GitHub is the source of truth, every tick re-reads open PRs and +tracking issues, and losing the box costs only the ticks it missed. + +**That is the right architecture for the work he runs, and this design should +not compete with it.** His agents are stateless sweeps: read the world, find +one bounded unit, open a PR, exit. Nothing is ever half-finished inside an +agent, because the work item *is* the pull request. There is no thread to +preserve, so a chain would be pure overhead. + +The line between the two is the shape of the work, not the size of it: + +| | Sweep work | Owned work | +| --- | --- | --- | +| Unit | one tick, idempotent | one task, held for days | +| Where state lives | GitHub, re-derived every tick | GitHub + dossier + the chain | +| Thread across restarts | none needed | decisions made and rejected, approaches tried, what it waits on | +| Architecture | cron + loops + backpressure | roles, chains, supervision | + +So the maintenance loop is **not** this machine pointed at production, which +is how it was described before this article was read. It is seven cron'd +sweeps, and building it costs almost nothing. + +**The principle this yields: if it can be derived, don't record it.** FR7 +already forbids recording liveness; generalize it. PR state, CI status, branch +existence, task status, worktree presence — all derived at read time, never +appended. The chain earns a field only when the fact exists nowhere else: +who currently owns this work, what authority is in force, what was decided and +why, and what comes next. The 4 KB cap on `state` is that principle with a +number attached; when a field is proposed for a record, the first question is +which other store already knows it. + +**Backpressure belongs in the charter.** Malucelli bounds *production*, not +just authority: a sweep with five or more of its own pull requests already +open ends the tick instead of proposing a sixth, tying output to review +velocity rather than to the clock. Grants bound what a role may *do* — TTL, +tier ceiling, cycle cap — and nothing yet bounds how fast it may create work +for others. A role's charter carries a `wip_limit`, and a role at its limit +reports rather than produces. His merge ratio is the evidence that this is +load-bearing and not decoration. + +**Two failure modes to design against, observed rather than predicted.** +Claude Code sessions expire at seven days, so a lead meant to persist for +weeks structurally cannot be one session — an external confirmation of §4.3. +And a session that hits a usage limit *does not exit*; it sits holding the +tick, which is why he screen-scrapes for the limit message. That case is the +argument for `next_due` over a process check: a process-liveness test calls +that session alive, while a declared deadline correctly reports a missed +commitment. The supervisor must treat "process exists" as the weakest of its +signals. + +**What his setup does not answer, and this one must.** Two secrets sit on the +box — a signing key and a GitHub token — and a human SSHes in every couple of +days to re-authenticate. One credential, full reach, no attenuation, no +per-role scoping, no receipts beyond the PR list. That is fine for one +person's repositories and it is exactly the property that stops the shape from +travelling anywhere with a security review. The plumbing this design adds on +top of his — attenuated per-role grants, a fence that survives takeover, an +append-only record of who decided what under which authority, and an audit +that reasons about absence — is the entire difference between a personal +automation and something an organization can run. + ## 5. Data model **Role id.** `org/` — e.g. `org/lead-a`, `org/ivy-lead`, @@ -436,7 +527,7 @@ record under the key dir (per gate). Optional `snapshot-.json` every | kind | body | law | | --- | --- | --- | -| `genesis` | `charter{ scope[], decides[], never_decides[], escalates_to, supervisor, supervises[], capabilities[] }` | seq 1 only; exactly one | +| `genesis` | `charter{ scope[], decides[], never_decides[], escalates_to, supervisor, supervises[], capabilities[], wip_limit }` | seq 1 only; exactly one; `wip_limit` bounds open work this role may have in flight (§4.11) | | `resume` | `incarnation{ id, host, session_ref, started_at }` | `prev` == tip; starts an incarnation; `id` is the digest of this record, never a counter (see below) | | `checkpoint` | `state{ goal, doing, decided[{what, why}], open[], next[], refs[] }`, `incarnation_id`, `next_due` | `next` non-empty; ≤ 4 KB; `incarnation_id` == current; `next_due` in the future | | `handoff` | same as `checkpoint` + `reason: stop\|compaction\|release` | ends an incarnation cleanly; no `next_due` (nothing is coming) | @@ -735,7 +826,7 @@ history stays byte-identical. | **p3 host adapter** | One role resumes mid-thought on Claude Code | `org` CLI (incarnate / checkpoint / handoff / mark / fold / audit) over `contracts/org`; hooks: SessionStart / PreCompact / Stop / N-calls; repo-keyed memory; chain state dir + anchor | p1 | **VALIDATION GATE** — §11 test 1 | | p4 drive runtime | Roles over driver/worker | genesis/charter verbs; supervision reducer; `takeover` mints caps and advances the fence; `org tree`; custody and gate adopt `contracts/authority` and keep a per-role fence high-water mark | p3 ✓ | §11 test 2 | | p5 parley | Legal conversations | `org-delegate.parley`; `grants`/`effects`/`receipt` in the algebra; `observe` over chain pairs | p4 | real chains classify clean | -| p6 the slice | The org at 1/10 scale | one lead, three ICs, one repo, one day; kill an IC; count operator prompts | p4, p5 | §11 test 3 | +| p6 the slice | The org at 1/10 scale | one lead, three ICs, one repo, one day; kill an IC; count operator prompts. **Runs on a rented VM, not the operator's laptop** — §4.11's reference setup is a $40/month box against an existing subscription, which is what makes four concurrent day-long sessions practical at all | p4, p5 | §11 test 3 | Committed: p0–p3. p4–p6 are gated on p3 proving the thesis for a single role. Rough scope: p1 ≈ execution's size (~1.5k weighted LOC incl. tests); From e29ffa81aaa865a0d6eaa0ed5cf547adfd333d18 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Fri, 21 Aug 2026 22:22:46 -0700 Subject: [PATCH 04/24] docs(org): role kinds, not rival architectures; work is tickets and docs too Corrects the previous commit, which wrote maintenance out of the design. A monitor owner IS a role - somebody durably owns alert health, tuned this monitor and retired that one for stated reasons. The tick is stateless; the ownership is not. So the split is not chain-or-no-chain but how much a chain carries, which falls out of derive-don't-record: three role kinds (ic thick / maintainer thin / lead medium) with cadence and density set by charter.kind. Also: in an org the work item is usually a ticket, doc, runbook, or incident rather than a PR. GitHub-as-truth works precisely because the work item IS a PR; once work spans a tracker, a docs site, a thread and a repo, no single store knows its state - the chain's strongest case, not a weakening. work.kind gains doc/incident/area, and charter capabilities are custody action manifests rather than git verbs. Co-Authored-By: Claude Fable 5 --- docs/features/org/spec.md | 69 +++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/docs/features/org/spec.md b/docs/features/org/spec.md index 8ff6d94b..9d00f6b9 100644 --- a/docs/features/org/spec.md +++ b/docs/features/org/spec.md @@ -19,13 +19,15 @@ > ids must be digests rather than counters (§5); and the cross-chain audit must > reason about absence or it is a suppression attack (§7.6). > -> §4.11 is new and narrows the design's claim: agents are cattle, the chain is -> what makes them so, and a simpler architecture — cron'd stateless sweeps with -> GitHub as the source of truth — is correct for maintenance work and is now an -> explicit non-goal here rather than a later phase of this machine. It also adds -> the principle that governs what a record may hold (*if it can be derived, -> don't record it*) and a `wip_limit` that bounds production the way grants -> bound authority. +> §4.11 is new. Agents are cattle and the chain is what makes them so; a +> simpler per-tick architecture — cron'd sweeps with the world as the source of +> truth — is correct for maintenance, and the two are not rival designs but +> different **role kinds** whose chains differ only in density. It adds the law +> governing what a record may hold (*if it can be derived, don't record it*), +> a `wip_limit` bounding production the way grants bound authority, and the +> observation that an organization's work item is usually a ticket or a doc +> rather than a pull request — which is where a single external store stops +> knowing the state and the chain earns its place. ## 1. Problem & hypothesis @@ -85,9 +87,10 @@ boot prompt and becomes a theorem about a reducer. sessions on any host (Claude Code, Agent SDK, `codex exec`). - Not a new work store. dossier holds tasks; assignments point at them. More strongly (§4.11): the chain records only what has no other home. -- Not a home for sweep work. Stateless, per-tick, idempotent maintenance — - dependency bumps, dead-code sweeps, test pruning — belongs on the simpler - architecture in §4.11, with no chain at all. +- Not a scheduler, and not a replacement for cron. Sweep work — dependency + bumps, dead-code sweeps, test pruning, alert triage — runs on the simpler + per-tick architecture in §4.11. The *role* that owns such an area still has + a chain; its ticks do not. - Not a chat surface. The operator's interface is a conversation with the leads, hosted wherever they already talk to agents. - Not consensus. One writer per chain, a supervisor that may take over, a @@ -452,18 +455,36 @@ one bounded unit, open a PR, exit. Nothing is ever half-finished inside an agent, because the work item *is* the pull request. There is no thread to preserve, so a chain would be pure overhead. -The line between the two is the shape of the work, not the size of it: - -| | Sweep work | Owned work | -| --- | --- | --- | -| Unit | one tick, idempotent | one task, held for days | -| Where state lives | GitHub, re-derived every tick | GitHub + dossier + the chain | -| Thread across restarts | none needed | decisions made and rejected, approaches tried, what it waits on | -| Architecture | cron + loops + backpressure | roles, chains, supervision | - -So the maintenance loop is **not** this machine pointed at production, which -is how it was described before this article was read. It is seven cron'd -sweeps, and building it costs almost nothing. +But the split is not *chain or no chain*, and an earlier draft of this section +got that wrong by writing maintenance out of the design entirely. A monitor +owner is still a **role**: somebody durably owns alert health for a repo, tuned +this monitor and retired that one for stated reasons, and escalated the thing +nobody could reproduce. The individual tick is stateless; the *ownership* is +not. What differs between role kinds is only **how much their chain carries** — +which falls straight out of the law below, since a sweep role has almost +nothing that another store does not already know. + +| Role kind | Owns | Chain density | Cadence | +| --- | --- | --- | --- | +| **IC** | one work item at a time, for days | **thick** — checkpoints every N tool calls; the thread across restarts is the whole point | continuous while assigned | +| **Maintainer / monitor owner** | a standing area: alert health, dependency health, test hygiene | **thin** — charter, tuning decisions and their why, escalations. Almost no checkpoints; each tick re-derives from the world | cron'd; per-tick idempotent | +| **Lead** | a scope and the roles inside it | **medium** — delegations, reports received, escalations, and the calls made on them | event-driven | + +A maintainer's chain may sit unchanged for days and that is correct, not a +failure to record. Its ticks are Malucelli's architecture exactly — cron, +re-read the world, one bounded unit, exit — and the chain holds only the part +that would otherwise be lost: who owns this area, and what they decided about +it. + +**And in an organization the work item is usually not a pull request.** It is a +ticket, a design doc, a runbook, an incident, a thread. Malucelli's +GitHub-as-truth works *because* his work item is a PR and GitHub therefore +knows the entire state of it. The moment work spans a tracker, a docs site, a +chat thread, and a repository, no single store knows what is going on — which +is the argument for the chain at its strongest, not a weakening of it. The +practical consequences are that `work.kind` must not be PR-shaped (§5) and +that a charter's capabilities are custody action manifests — comment on a +ticket, publish a doc, tune a monitor — rather than git verbs. **The principle this yields: if it can be derived, don't record it.** FR7 already forbids recording liveness; generalize it. PR state, CI status, branch @@ -527,13 +548,13 @@ record under the key dir (per gate). Optional `snapshot-.json` every | kind | body | law | | --- | --- | --- | -| `genesis` | `charter{ scope[], decides[], never_decides[], escalates_to, supervisor, supervises[], capabilities[], wip_limit }` | seq 1 only; exactly one; `wip_limit` bounds open work this role may have in flight (§4.11) | +| `genesis` | `charter{ kind, scope[], decides[], never_decides[], escalates_to, supervisor, supervises[], capabilities[], wip_limit }` | seq 1 only; exactly one. `kind ∈ {ic, maintainer, lead}` sets checkpoint cadence and expected chain density (§4.11). `capabilities[]` are custody action manifests — comment on a ticket, publish a doc, tune a monitor — never git verbs. `wip_limit` bounds open work in flight | | `resume` | `incarnation{ id, host, session_ref, started_at }` | `prev` == tip; starts an incarnation; `id` is the digest of this record, never a counter (see below) | | `checkpoint` | `state{ goal, doing, decided[{what, why}], open[], next[], refs[] }`, `incarnation_id`, `next_due` | `next` non-empty; ≤ 4 KB; `incarnation_id` == current; `next_due` in the future | | `handoff` | same as `checkpoint` + `reason: stop\|compaction\|release` | ends an incarnation cleanly; no `next_due` (nothing is coming) | | `mark` | `mechanical{ session_ref, git{branch, head, dirty[]}, last_tools[], transcript_offset }`, `next_due` | host-authored; degraded; exempt from `empty_next` (§4.7) | | `takeover` | `by: , from_incarnation, reason, evidence[]` | only a role named `supervisor` in genesis; ends the current incarnation | -| `assign` / `release` | `work{ kind: dossier, id }`, `incarnation_id` | one open assign per work id across all chains (checked by drive at write time, law at fold time) | +| `assign` / `release` | `work{ kind, id }`, `incarnation_id` | `kind ∈ {dossier, jira, pr, doc, incident, area, free}` (drive's vocabulary, extended — open, never an enum in the schema); one open assign per work id across all chains (checked by drive at write time, law at fold time). `area` is the standing-ownership kind a maintainer holds indefinitely rather than completes | | `message.sent` / `message.received` | `msg{ type, to\|from, ref{role, seq, hash}, body }` | `type ∈ {delegate, report, escalate, ask, answer, takeover_notice}` | **Fold output — `RoleState`.** From 60a91f0dc31810a7a51491f06dc6e24c4ceff721 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Fri, 21 Aug 2026 22:28:47 -0700 Subject: [PATCH 05/24] docs(org): drop the stock metaphor from 4.11 Same argument, stated directly: every incarnation is disposable and the chain is what makes it so. Co-Authored-By: Claude Fable 5 --- docs/features/org/spec.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/features/org/spec.md b/docs/features/org/spec.md index 9d00f6b9..1f596308 100644 --- a/docs/features/org/spec.md +++ b/docs/features/org/spec.md @@ -19,7 +19,8 @@ > ids must be digests rather than counters (§5); and the cross-chain audit must > reason about absence or it is a suppression attack (§7.6). > -> §4.11 is new. Agents are cattle and the chain is what makes them so; a +> §4.11 is new. Every incarnation is disposable and the chain is what makes it +> so; a > simpler per-tick architecture — cron'd sweeps with the world as the source of > truth — is correct for maintenance, and the two are not rival designs but > different **role kinds** whose chains differ only in density. It adds the law @@ -428,14 +429,14 @@ fixture the mutant accepts and production refuses, and the refusal identifier frozen into a golden artifact digest so that renaming a code is a test failure. This is the difference between a law and a comment. -### 4.11 Cattle, not pets — and the two architectures that follow +### 4.11 Every incarnation is disposable, and the chain is what makes it so -The agents are cattle. Any incarnation may be shot at any moment, and -replacing one is routine rather than an incident. That is the design's -premise, not a concession to it — and **the chain is what makes it true.** An -agent holding three days of reasoning in its head is inherently a pet: you -cannot kill it without losing something. Externalize what it knows and every -incarnation becomes disposable by construction. Residency is settled by the +Any incarnation may be killed at any moment, and replacing one is routine +rather than an incident. That is the design's premise, not a concession to +it — and **the chain is what makes it true.** An agent holding three days of +reasoning in its head cannot be killed without losing something, so it is +precious whether you wanted it to be or not. Externalize what it knows and +disposability becomes structural. Residency is settled by the same argument, from the other side: switchboard showed a live process buys serialized ownership and nothing else, and the tip rule supplies that without the process. From 1cf6ca51b0d33073cfb263daaadf9a2399a90712 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Fri, 21 Aug 2026 22:39:34 -0700 Subject: [PATCH 06/24] docs(org): 4.12 - can anyone actually operate this Operability was one line in the NFR table plus assertions that refusals would 'name the remedy'. That is written intention where a mechanism belongs - the exact failure this workbench exists to correct - so it now gets requirements, a mechanism, and a gate that can fail. The genuinely missing piece: an append-only chain with no correction record means a wrongly-recorded decision poisons every future fold permanently, with no path back short of abandoning the role. Adds an annul record - append, never rewrite; own or supervised content records only; never genesis/resume/takeover; annulled records still count for seq, prev, and the anchor. Also: refusals return {code, message, remedy, evidence} as a contract rather than a habit; org explain and org doctor in the shape console and rooms doctor already established, deciding nothing; and checkpoint quality named as the biggest untested assumption, graded separately in 11.1c since a vague checkpoint and a broken fold fail identically. 11.1d is the operability gate: a second person repairs a three-ways-broken role using only the tools. Co-Authored-By: Claude Fable 5 --- docs/features/org/spec.md | 103 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/docs/features/org/spec.md b/docs/features/org/spec.md index 1f596308..ce92f6fd 100644 --- a/docs/features/org/spec.md +++ b/docs/features/org/spec.md @@ -29,6 +29,15 @@ > observation that an organization's work item is usually a ticket or a doc > rather than a pull request — which is where a single external store stops > knowing the state and the chain earns its place. +> +> §4.12 is new and answers a question the document had been treating as a +> single line in a table: can anyone actually operate this? It adds the one +> thing that was genuinely missing — a correction path, since an append-only +> chain with no `annul` means a wrongly-recorded decision poisons every future +> fold forever — plus refusals that carry their remedy as data, an `explain` +> and a `doctor` in the shape the rest of the portfolio already uses, and the +> observation that checkpoint quality is the design's biggest untested +> assumption. Each is gated by §11.1c–1d rather than asserted. ## 1. Problem & hypothesis @@ -135,6 +144,7 @@ boot prompt and becomes a theorem about a reducer. | Integrity | Tamper-evident against a state-dir-only writer: `HMAC(key, head ‖ count)` anchor with the key outside the state dir — gate's bounded claim, not non-repudiation. | | Determinism | The write path invokes no model. The distilled `state` is authored by the incarnation itself at checkpoint time; a mechanical `mark` stands in when it didn't (§4.7). | | Portability | Chains are files; a role can be incarnated on any machine that has the state dir and the anchor key. No server. | +| Operability | Somebody who was not here can diagnose and repair a role using only the tools — no author, no transcript. Every refusal returns `{code, message, remedy, evidence}`; every mistake is correctable by append (`annul`); `explain` and `doctor` answer "what is going on" and "what is wrong" without joining stores by hand. Gated by §11.1d, not asserted (§4.12). | | Cost | No new subscriptions; agent spend unchanged. Checkpoint authoring costs one short tool call per cadence tick. | ## 3. Architecture overview @@ -526,6 +536,71 @@ append-only record of who decided what under which authority, and an audit that reasons about absence — is the entire difference between a personal automation and something an organization can run. +### 4.12 Somebody has to operate this + +Everything above is about being correct. This section is about being +*operable*, which until now the document treated as one line in the +non-functional table and a few assertions that refusals would "name the +remedy." That is the same failure the whole workbench exists to correct — +written intention where a mechanism belongs — so operability gets +requirements, a mechanism, and a gate that can fail, exactly like correctness +got the mutant discipline (§4.10). + +The bar is one sentence: **somebody who was not here must be able to work out +what a role is doing, why it refused, and how to fix it — using only the tools +and the artifacts, with no access to the author and no transcript.** That is +the same property gate already claims for merges ("show me why this shipped") +applied to roles. + +**A mistake must be correctable, and today it is not.** The chain is +append-only and has no correction record, so an agent that records a wrong +decision, a wrong assignment, or a checkpoint that misstates what it did has +poisoned every future fold of that role — permanently, with no path back short +of abandoning the role. Append-only is the right storage discipline and the +wrong error-handling story. So there is an `annul` record: it names the record +it corrects by `(seq, hash)`, carries a reason, and is itself an ordinary +append — the original is never rewritten, and the fold reports both the +mistake and the correction. Three laws keep it from becoming a rewrite +primitive: + +- A role may annul **its own** content records; a supervisor may annul those + of roles it supervises. Self-correction is normal and should not need + ceremony. +- **Structural records cannot be annulled** — `genesis`, `resume`, `takeover`. + Annulling those would rewrite ownership history, which is the one thing the + chain exists to make impossible. +- An annulled record still counts for `seq`, `prev`, and the anchor. Annulment + changes interpretation, never the chain. + +**A refusal must carry its remedy, as data.** `prev_mismatch` is a code, not +an answer, and `fence_regression` is worse — understanding it currently +requires knowing about chains, sequences, incarnations, and a high-water mark +that lives invisibly inside a verifier. So every refusal returns +`{ code, message, remedy, evidence }`, where `remedy` is the command to run or +the fact to check, and `evidence` names the records that produced the verdict. +custody already does this — its refusals name the command that unsticks +them — and this is that convention made a contract rather than a habit. + +**One command answers "what is going on with this role."** The portfolio +already has this instinct everywhere else: `console` explains gate's state and +decides nothing, `rooms doctor` runs twelve host checks, `gate explain` exists. +The org plane had `audit` and `tree` and no equivalent, which would leave an +operator joining the chain, the cap ledger, dossier, and GitHub by hand to +find out whether the chain lied or the derivation did. `org explain ` +renders one page — charter, current incarnation and how liveness was derived, +folded state and its age, assignments, authority in force and its fence, recent +refusals with remedies, and which store each line came from. `org doctor` +checks the environment the way rooms does: anchor key present, state dir +writable, clock sane, orphaned chains, chains that no longer fold. + +**The agent is the primary user, and the untested assumption is checkpoint +quality.** Roles are operated by agents far more than by people, and §4.7 asks +an incarnation to author its own distilled state. If agents write vague +checkpoints, every downstream fold is confidently wrong — and because §11's +gate grades the *resume*, a bad checkpoint and a bad fold produce identical +failures. They must be graded separately (§11.1c) or a failure cannot be +diagnosed, which is the operability bar applied to our own validation plan. + ## 5. Data model **Role id.** `org/` — e.g. `org/lead-a`, `org/ivy-lead`, @@ -557,6 +632,7 @@ record under the key dir (per gate). Optional `snapshot-.json` every | `takeover` | `by: , from_incarnation, reason, evidence[]` | only a role named `supervisor` in genesis; ends the current incarnation | | `assign` / `release` | `work{ kind, id }`, `incarnation_id` | `kind ∈ {dossier, jira, pr, doc, incident, area, free}` (drive's vocabulary, extended — open, never an enum in the schema); one open assign per work id across all chains (checked by drive at write time, law at fold time). `area` is the standing-ownership kind a maintainer holds indefinitely rather than completes | | `message.sent` / `message.received` | `msg{ type, to\|from, ref{role, seq, hash}, body }` | `type ∈ {delegate, report, escalate, ask, answer, takeover_notice}` | +| `annul` | `annuls{ seq, hash }, reason, by` | own content records, or a supervised role's; never `genesis`/`resume`/`takeover`; the annulled record still counts for seq, prev, and the anchor (§4.12) | **Fold output — `RoleState`.** @@ -651,10 +727,22 @@ org takeover --by --reason [--evidence ...] org assign --work dossier: org send --to --type delegate|report|escalate|ask|answer --body org fold [--at ] → RoleState +org annul --seq --reason → corrects a content record (§4.12) org tree → the org chart with derived liveness org audit → anchor + chain check (gate audit's twin) +org explain → one page: charter, incarnation and how + liveness was derived, folded state and its + age, assignments, authority and its fence, + recent refusals with remedies, and which + store each line came from +org doctor [--json] → environment checks, rooms-style: anchor key, + state dir writable, clock sane, orphaned + chains, chains that no longer fold ``` +`explain` and `doctor` decide nothing and write nothing — console's stance, +kept deliberately: an explainer that can act is an explainer you stop trusting. + Every verb's stdout is the JSON result; exit codes `0` ok, `1` refused (code in JSON), `4` error. Refusals are loud and name the remedy, like custody's. @@ -933,6 +1021,21 @@ Three binary tests, one per gate, no vibes. says plainly that the reasoning since then is gone rather than inventing it. A confident wrong answer here is a worse failure than an honest partial one, and this test is the only place that distinction is caught. + **1c — grade the checkpoint, not only the resume.** Before killing the + session, score its last authored `state` on its own terms: does `decided` + carry the *why*, does `next` name an action someone could start cold, does + `open` list what it is actually waiting on? A vague checkpoint and a broken + fold fail 1a identically, so without this the result cannot be diagnosed — + §4.12's bar applied to our own validation plan. If checkpoints score well + and resumes still fail, the fold is wrong; if checkpoints score badly, the + prompt is wrong and no amount of reducer work will fix it. + **1d — the operability bar.** Hand a second person, or a fresh agent with + no transcript, a role broken three ways: a chain that no longer folds, a + grant refused on its fence, and a checkpoint carrying a wrong decision. + Pass if they diagnose all three and repair the third using only + `org explain`, `org doctor`, `org audit`, and `org annul`, with no access + to whoever built it. This is the only test of §4.12, and it gates the same + way the others do: a design nobody else can operate has not shipped. 2. **Ownership (p4 gate).** Two incarnations of one role race: exactly one holds the tip; the other's write is refused with `prev_mismatch`. A supervisor takeover makes the displaced incarnation's grant unspendable From a40255ecffc9dfb7926a6c82d96ef01ae23f4cf5 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Fri, 21 Aug 2026 22:45:07 -0700 Subject: [PATCH 07/24] docs(org): adoption is a design property, and it must retire more than it adds Measured the portfolio: 51 authored skills, 23 ever invoked, two thirds never fired once - including most of the delivery machinery (work-driver, pr-risk, review-coordinator, shipped, health, roster, recover, consult). Most-used by 2x is /continue at 16 invocations, which exists solely to hand-write a handoff when continuity fails. That is the org premise validated from logs rather than argument. The separator is not quality: a tool is used when there is an unmistakable moment of need and it is the obvious response, and unused when it must be remembered while thinking about something else. Two consequences now bind the design - nothing load-bearing may require remembering a verb, and the chain must retire /continue, /claim, /release, /roster, /recover and the state-reconstruction half of /status and /wip as p3 scope rather than a later tidy-up. Co-Authored-By: Claude Fable 5 --- docs/features/org/spec.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/features/org/spec.md b/docs/features/org/spec.md index ce92f6fd..d5cddb0f 100644 --- a/docs/features/org/spec.md +++ b/docs/features/org/spec.md @@ -593,6 +593,37 @@ refusals with remedies, and which store each line came from. `org doctor` checks the environment the way rooms does: anchor key present, state dir writable, clock sane, orphaned chains, chains that no longer fold. +**Adoption is a design property, and the portfolio has hard evidence about +it.** Of 51 authored skills on this machine, 23 have ever been invoked and +two-thirds have never fired once — including most of the delivery machinery +(`work-driver`, `pr-risk`, `review-coordinator`, `shipped`, `health`, +`roster`, `recover`, `consult`). The most-used skill by a factor of two is +`/continue` at 16 invocations, which exists solely to hand-write a handoff +when continuity fails. The pattern separating used from unused is not quality: +a tool is used when there is an unmistakable moment of need and it is the +obvious response, and unused when it must be *remembered* while the operator +is thinking about something else. `/claim`'s two events are the same finding +at a smaller scale. + +Two consequences bind this design: + +- **Nothing load-bearing may require remembering a verb.** `incarnate`, + `checkpoint`, `handoff`, and `mark` are hook-driven for this reason, and + that is not a convenience — it is the difference between working and joining + the unused two-thirds. The operator-facing verbs (`explain`, `doctor`, + `annul`, `audit`) survive only because a refusal's `remedy` field names the + command at the moment it is needed. Any future verb without such a delivery + path should be assumed dead on arrival. +- **This must retire more surface than it adds.** In a portfolio where two + thirds of tools go unused, adding thirteen verbs is only defensible if the + chain subsumes what already exists. It does: `/continue` (the automatic + handoff replaces the hand-written one), `/claim` and `/release` (assignment + is structural), `/roster` and `/recover` (both re-derive at read time what + the chain records), and the state-reconstruction half of `/status` and + `/wip`. Retiring them is p3 scope, not a later tidy-up — if the chain ships + and `/continue` is still being typed, the design has failed on its own + terms regardless of what the reducer proves. + **The agent is the primary user, and the untested assumption is checkpoint quality.** Roles are operated by agents far more than by people, and §4.7 asks an incarnation to author its own distilled state. If agents write vague From 14b66b5dd1e8ed6730a39a832a47fdac2028694d Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sat, 22 Aug 2026 06:32:27 -0700 Subject: [PATCH 08/24] =?UTF-8?q?docs(org):=20the=20architecture=20vision?= =?UTF-8?q?=20=E2=80=94=20Baton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Synthesis of three independent architecture framings (bottom-up from the existing planes, top-down from the goal, outside-in from adoption) against three adversarial critiques (completeness, skepticism, coherence). Where they conflicted the synthesis decided and said why. Five things it gets right that the TDD did not: - Spine/body split. The spine is chained and permanent; bodies are content-addressed erasable blobs. Continuity ships without waiting on the audit story, and erasure stops being impossible. - The host writes the checkpoint, not the agent. Marks are mechanical facts the host observes; a separate cheap distiller folds transcript+marks into the body. A required end-of-turn tool call is a verb wearing a costume, and claims.jsonl (2 records, abandoned in a day) is what verbs get. - Fences are enforceable only by verifiers that keep state. A CI check on an ephemeral runner has a high-water mark of zero and enforces nothing. Scope the claim in gate's docs rather than discovering it in production. - Model calls are effects. Route the provider through the broker: spend metered against the grant, a fence-bound credential so a displaced incarnation cannot even think, prompt digests for classification, and a concurrency ceiling that degrades by queueing. - There is a cron at the bottom. Mutual restart cannot close both-leads-down; exactly one external stateless timer can, with a dead-man's switch outside the system. Also: work refs become URIs with a scheme (dossier is one backend, not the model); one ordered enforcement level per (scope, effect class) where promotion requires the data the level below produced — which is simultaneously the safety property and the adoption motion; gate demoted from flagship to one adapter; ship becomes a role rather than a second scheduler; Gleam on probation as a differential oracle with a cut date; mutants retained on the authority plane only. P0 is a no-code falsification week that can kill the project: if two sessions collided fewer than 3 times in 90 days, the honest product is a good /continue that fires from a hook and ownership is premature. Appendix A adds the primitive-design pass: the append API must be a transaction closure so the read-verify-write window is unreachable; canonical completeness must be reflection-checked; a missing fence high-water mark must fail closed; and the cross-chain assign invariant needs an owner or an honest downgrade to detected-not-prevented. Co-Authored-By: Claude Fable 5 --- docs/features/org/vision.md | 493 ++++++++++++++++++++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 docs/features/org/vision.md diff --git a/docs/features/org/vision.md b/docs/features/org/vision.md new file mode 100644 index 00000000..658e074f --- /dev/null +++ b/docs/features/org/vision.md @@ -0,0 +1,493 @@ +# Baton — Architecture Vision + +## 1. The system in one paragraph + +Baton is a control plane that gives every durable unit of organizational work an owner that outlives the process doing it, and gives every external effect a recorded intent that outlives the process that issued it. A **role** — lead, project lead, IC, maintainer — is a row of data with an append-only journal; an **incarnation** is a disposable session on some host that reads the journal's tip, takes the tip, acts, and writes back. To act you must append; to append you must present the tip you read. That one rule produces continuity (starting is folding), mutual exclusion (two incarnations cannot both hold the tip), handover (a supervisor appends a takeover), and revocation (credentials are minted against a chain position, so displacing an incarnation kills its authority everywhere with no message sent). Underneath it, a single broker is the only path from an agent to the outside world: it holds the credentials the agent never sees, records the *intent* of every effect before the wire and the *outcome* after, and refuses any effect that has not declared how a replacement resolves it if the issuer dies mid-flight. Everything else — the fleet, the boards, the protocol checker, the merge gate — is a client, a reader, or an adapter. The system is for one thing: **you can kill any agent at any moment, and nothing is lost and nothing is duplicated.** + +--- + +## 2. The thesis + +Two claims carry the weight. Stated so they can be attacked. + +**T1 — Ownership and continuity are the same fact, and the fact is a compare-and-swap.** + +Four propositions, jointly sufficient: + +1. Role state lives outside every process. +2. Every write is CAS-guarded against a token the writer had to read first. +3. Liveness is derived from write recency against a deadline the writer itself declared — never from self-report. +4. Authority is bound to that same token, so authority expires when ownership moves. + +From these: an agent that dies loses nothing (state is external); two agents cannot both act as a role (CAS); a hung agent that never exits is correctly seen as dead (it stopped appending); and a displaced agent's credentials are dead the instant the token advances, including on hosts you cannot reach. + +**What T1 explicitly does not claim.** The hash chain is not doing this work. A monotone integer with CAS gives identical ownership semantics. This matters because a critique landed hard here and the design changed in response: **the ownership token and the audit ledger have opposite cost profiles** — the token wants to be small, cheap, and disposable; the ledger wants to be permanent, canonically encoded, and tamper-evident — and fusing them makes the continuity path pay the audit path's entire bill, including a GDPR erasure problem with no customer asking for the audit yet. + +The resolution is a split, and it is the single most consequential structural decision in this document: + +> **The spine is chained; the body is a blob.** Every record's spine — `{v, scheme, seq, prev, tenant, role, kind, kind_class, incarnation, fence, at, body_digest, body_class, refs[], next_due}` — is fixed-shape, free of prose, canonically encoded, hash-chained, and retained forever. The *body* — the distilled prose — is a content-addressed blob stored separately, referenced by digest, classified, and **erasable**: deleting a blob leaves a tombstone and the chain still verifies. + +That split buys three things at once. Erasure and retention become possible without breaking verification. Record classification and redaction get a place to live (a `body_class` field and one store to encrypt). And the audit requirement stops setting the schedule for the continuity requirement — you can ship ownership in week two and anchor keys in month six. + +**T2 — An effect is not done until the ledger says the world saw it, and no intent may be issued that has not declared its own recovery.** + +The agent that says "done" is making a claim. A committed effect is a fact. Completion is derived from facts. And the failure that matters — an agent issued an external effect and died before recording the outcome — is bounded by refusing the effect at issue time unless it names how a stranger resolves it: replay an idempotency key, run a probe, run a compensation, or escalate to a human. `effect_recovery_undeclared` is a refusal, not a warning. + +The argument for T2 is not enterprise payments. It is that the operator's own platform generates the crash-mid-effect case on a schedule, for free, today: a usage-limited session does not exit, it holds the tick; derived liveness correctly marks it dead; a supervisor takes over; and then the original wakes up with an effect in flight. That is observed, not imagined. + +**The keystone that joins them:** T1 serializes the role, so a role has **at most one outstanding unknown effect**. Recovery is therefore a fixed procedure over a single record, not a search. Without T1, T2 is a distributed-systems research project. With it, T2 is one probe. + +--- + +## 3. The architecture + +### 3.1 Six planes, one law of composition + +Planes compose through **typed artifacts and exit codes**, never call stacks. The one forbidden import stands: no plane imports another plane's decision logic. Generalized to services: an adapter consumes verdicts and may never import policy. + +| Plane | Answers | Owns | Home today | +|---|---|---|---| +| **Continuity** | who is this role, who holds it, is it alive | chains, folds, takeover, derived liveness | `contracts/org` + `drive` | +| **Authority** | may this be done, by whom, until when, how much | grants, attenuation, fences, ceilings | `contracts/authority` + `gate`'s grant, extracted | +| **Effect** | what did the world actually see | intent / attempt / outcome, classes, probes, reconciliation | `custody`, extended | +| **Execution** | where and how did code run | work specs, placement, run events, terminal results | `runway` + `rooms` | +| **Work** | what is this about | work URIs, subject digests, derived and attested completion | URI scheme; `dossier` is one backend | +| **Surface** | how humans and other systems reach in | API, projections, console, escalation, notification | new (`batond`) + `console`/`escalate`/`flare` | + +Cross-cutting: **evidence** (canonical encoding, spine chain, seals, anchors) and **levels** (§5). + +### 3.2 Where the write path runs — and the contradiction that had to be resolved + +One framing put the chain behind a service (`Append(tenant, role, expectedTip, record)`) and then, to answer the "you are a new single point of failure in our dev loop" objection, promised the plane degrades rather than blocks with a local append buffer. Those two statements cannot both hold: if appends buffer locally during a partition, two incarnations both believe they hold the tip, and serialized ownership is precisely the property you cannot degrade. + +**Decision.** The write path is a **local library append under a local lock** — `contracts/org` called in-process, no RPC, no daemon — against the role's **home**. A role has exactly one home: a state directory on a laptop, or a Postgres row in a deployment. The home is the serialization domain. If the home is unreachable, you cannot append, therefore you cannot act, and that is correct. + +The law that resolves the contradiction in one line: + +> **The lock never degrades; the policy always can.** + +Ownership blocks. Refusals, verdicts, protocol conformance, effect classification, and evidence anchoring all degrade by level (§5). On a laptop the home is a file, so there is no service to be down. In a deployment the home is the database the platform team already runs and already considers available. `batond` is a **reader, a reconciler, and a remote home** — never a hop on a local write path. This also honors the operator's own switchboard result: no residency without a comparator win. The only resident processes in the design are the broker (comparator win: it *is* the egress path) and the read API (comparator win: it serves other machines). + +### 3.3 The continuity plane + +**Record spine** as in §2. **Kind classes** are the schema-evolution answer, and the failure they prevent is specific: an old reader that silently skips an unknown `takeover` concludes it still holds the role — two holders, produced by routine version skew, with every hash valid. + +- `kind_class: structural` — `charter`, `attach`, `takeover`, `release`, `retire`, `recharter`, `split`, `merge`, `abandon`, `revoke`, `delegate`, `assign`, `unassign`, `intent-ref`, `escalation`, `resolution`, `seal`, `annul`. An unknown structural kind makes the fold **refuse** `scheme_unsupported` with an upgrade remedy. +- `kind_class: advisory` — `checkpoint`, `mark`, `note`, `report`, `message`. Unknown advisory kinds are preserved verbatim and skipped. + +Genesis carries a monotone `min_reader`. One CI fixture: an old reader against a new chain must refuse, not skip. + +**The fold is bounded, and the goal is not writable.** A three-day IC chain has thousands of records; the fold's entire purpose is to fit in a fresh context window, and no framing bounded it. Worse, if each incarnation rewrites `goal`, a role's purpose is subject to a dozen rounds of lossy re-summarization with nothing to compare against. + +- `goal`, `scope`, `tier`, permitted effect classes, budget, cadence, and retirement condition live in the **charter**, are **inherited** by the fold, and are changeable only by a `recharter` authored under the parent role's grant. Attempting to write them from a checkpoint is `charter_immutable`. +- Fold state is a fixed-size reducer: inherited charter, current holder, last `doing`, last K `decided`, `open[]` as a set with explicit close, `next[]`, assignments, open intents, liveness, seal reference. +- Every K checkpoints the home appends a **`seal`** — the folded state, content-addressed. A cold read is `last seal + tail`. The seal also bounds erasure: bodies behind a seal are deletable because the seal preserves what the fold made of them. + +**The checkpoint is written by the host, not by the agent.** This is the answer to the hardest evidence in the whole review. `~/.claude/session-claims/claims.jsonl` contains two records, both from one day two weeks ago, and nothing since — an ownership ledger with claim and link events, built on this machine, abandoned after a day, alongside `/claim`, `/release`, `/roster`. It died because it required a verb. And 51 authored skills with 23 ever invoked says the same thing louder. + +Nothing survives that requires the agent to remember anything, and a "required end-of-turn tool call" is a verb wearing a costume. So: + +1. `SessionStart` hook → **attach**: read the tip, append `attach`, and *inject the folded context into the session*. The agent gets its memory back for free. If the hook fails, the agent starts blind — visible in the first sentence, not silently wrong. +2. Every N tool calls and at `PreCompact` → the host writes a **mark** containing the mechanical facts it can observe without the model's cooperation: tool calls, files touched, effect ids issued, commands run. +3. A **distiller** — a cheap local or small-model call, not the working agent — folds the transcript tail plus the marks into a body of the record shape. The least reliable component at its least reliable moment is removed from the loop. +4. `Stop` → **release**, or, on abnormal termination, nothing at all — and derived liveness catches it. + +This also disposes of "a soft-fail hook cannot enforce a checkpoint." Correct, and it no longer needs to: the marks are mechanical, the distillation is asynchronous and retryable from the transcript, and the tip advances on `attach`, which is the only moment enforcement actually matters. + +**The resume canary is a first-class component, because distillation quality is the thesis's only untested load-bearing assumption.** Periodically, fork a fresh incarnation from the chain alone, ask it to state the goal and the next action, and score it against what the live incarnation is doing. That produces a per-role **resume-fidelity** number that degrades visibly before it fails catastrophically. It is the only instrument that tells you whether the chain is carrying thought or only commitment. + +**The human is a role.** An interactive session attaches through the same hook as `human:` and takes the tip like anything else. Without this, "two sessions held PROJ-412 on Tuesday" is false whenever one of them is the operator, which today is most of the time. + +**Reorg is four records.** `split` and `merge` are terminal for the source and genesis-referencing for the target, so lineage survives and a merged role's fold includes its source's folded tail; `recharter` retargets scope; `abandon` names every open item. A role with open assignments cannot `retire`. The property worth claiming: **when roles are data, being wrong about org shape costs an append.** + +**Time is owned by the home.** The home stamps `at`. Agent-supplied timestamps are advisory and may never be the basis of a refusal. One law, house-shaped: *no refusal may depend on a clock the refusing party does not own.* + +**The trust boundary, written down.** The chain is a record of **claims** by an incarnation; only broker outcomes are **facts**. A hash-chained journal makes a prompt-injected checkpoint *durable* and inherited by every future incarnation — the design makes one thing worse than the status quo, and pretending otherwise fails a security review. Two cheap structural mitigations: `refs[]` carry `trust ∈ {operator, internal, external}` so a decision derived from untrusted content is visible in the fold and the audit; and permitted effect classes are pinned in the charter, so an injected incarnation is confined to what the role could do anyway. Blast radius bounded, not zero. Say it that way. + +### 3.4 The authority plane + +A grant: `{subject_role, incarnation, fence, actions[], resource_predicates, tier_ceiling, window, depth, spend_ceiling, concurrency_ceiling, cycle_ceiling, audience, parent}`. + +Ten ordered checks, first failure wins. A child may shrink actions, shrink the window, decrement depth, lower every ceiling. Subject and artifacts are equality-locked. **The audience of a grant is the only key that may mint the next one.** Signature validity alone is insufficient — the monotonicity predicate is the law. This is `hack-mandate`, promoted verbatim. + +**The fence** is the grant's chain position at mint. Verifiers keep a per-role high-water mark; a lower fence is `fence_regression`. There is deliberately **no revoke endpoint**: appending `takeover` or `revoke` advances the mark, and every credential minted against that role dies in one local write with nothing sent. Authority is computed, not stored; the cap ledger is a derived cache. + +**Scoping the fence claim honestly.** A fence is only enforceable by a verifier that keeps state. `custody serve` and a local `gate` do. A CI check on an ephemeral runner does not — its high-water mark is always zero and every displaced grant verifies fine. So: **fences are enforced by long-lived verifiers only. The CI check enforces scope, tier, and time and explicitly does not claim fence enforcement.** Written into gate's docs, not left to be assumed exactly where it fails. + +**Two roots, both outside the agentic system, both named.** At half a human-touch per role per day the human cannot mint routinely, so leads mint — which reintroduces a broad long-lived root unless it is pinned. It is pinned two ways. *Root issuance is operator-only; attenuation is delegable* — that is the one line reconciling the new topology with the standing rule that agents never mint. And the root key is keychain- or hardware-held, signs only lead charter-plus-grant pairs on a short window, and requires a weekly human refresh, which makes its absence a dead-man's switch on the whole fleet. + +**Ceilings that are not about security.** `cycle_ceiling` encodes the operator's hardest-won process rule — two fix-rounds against the review panel, and the uncapped loop is the panel *before* the gate — as an attenuating grant dimension rather than as prose an agent may or may not read. A looping panel becomes a refusal with a remedy instead of a silently burned budget. `spend_ceiling` and `concurrency_ceiling` do the same for cost, once model calls run through the broker (§3.5). + +### 3.5 The effect plane — the part that does not exist yet + +**It lives inside custody, and it requires two charter reversals, not three small changes.** Today custody writes its request line *after* the upstream returns, once, and a log-write failure is deliberately not fail-closed on the grounds that the log is tuning evidence rather than a control. The effect plane requires the exact opposite: **intent fsynced before the wire, fail-closed.** A crash between the wire and the log today leaves no record at all — not an intent without an outcome, nothing. Naming this as a reversal rather than an addition is the difference between a two-day estimate and a correct one. + +**Three records**, in their own log, same envelope discipline: + +``` +intent {effect_id, tenant, role, fence, work_ref, class, upstream, action, + request_digest, idem_key?, probe?, compensate?, deadline} ← before the wire, fail-closed +attempt {effect_id, n, at, transport} ← before each retry +outcome {effect_id, n, status ∈ committed|absent|unknown, evidence_digest} +``` + +**The missing field that turns three repos into one system.** custody's log record today carries `key`, `grant_id`, `grant_digest`, `rule_fired`, `verdict` — and no role, no incarnation, no work id. A replacement incarnation folds its role chain and has no join column to discover its predecessor's open effects. So: **every request through the broker must carry `(role, fence, work_ref, effect_id)`, and custody refuses `effect_unstamped` if it does not.** That single stamp is the seam. It is a day of work and it is the difference between four adjacent programs and one system. + +**`effect_id` is content-addressed** over what the effect is *about* — tenant, role, work subject, class, canonical request digest. A changed request forks a new effect rather than mutating one; a retry of the same intent is the same id. That is `hack-obligation`'s identity rule, and it is the exactly-once hook. Cost, stated: hashing a request body forces the proxy to buffer where it currently streams. + +**Four classes, and the recovery is declared at intent time or the intent is refused.** + +| Class | Meaning | Recovery | +|---|---|---| +| **R** | upstream honors an idempotency key | replay the key | +| **Q** | no key, but a query distinguishes committed from absent | run the declared **probe**, evaluate the declared predicate | +| **C** | reversible by a declared compensating effect | compensate, then retry | +| **U** | at-most-once, irreversible and unobservable | never automatic — escalate with evidence | + +Class Q is the workhorse: most GitHub, ticket, and document effects are Q, and the probe is often exactly what the seven-cron-loops comparison point does statelessly every tick ("re-read the open PRs"). The difference is that here it is *declared per effect and run only on the open one*, so it also covers the cases where re-reading everything does not answer the question. An inconclusive Q probe promotes the effect to U and escalates. The ladder never guesses. + +**`unknown` is a status on the effect record, not a new value in `contracts/execution`'s terminal enum.** Adding a value to an existing enum falls silently through every consumer's switch, including ship's independent TypeScript emitter and its goldens. A run can succeed while its effect is unknown — the process exited 0 and the HTTP response was lost — and the two planes must be allowed to disagree. + +**Model calls are effects.** The largest pipe in the system — prompt out, completion in — bypassed every plane in every framing. Routing the model provider as one more custody upstream with a manifest costs almost nothing and buys four things at once: token spend metered against the grant's `spend_ceiling`, so `spend-audit` becomes a derived view rather than a scraper; a fence-bound model credential minted at attach, so a displaced incarnation cannot think, let alone act; `prompt_digest` plus classification tags, so "which agent saw PII" has an answer; and a real `concurrency_ceiling`, so the fleet degrades by queueing rather than by rate-limit failure. Harness seat and concurrency limits, not architecture, are what actually cap fleet size — the comparison point runs seven loops on a subscription and that number is not incidental. + +**The reconciler is a maintainer role, not a daemon** — thin chain, stateless tick, re-derives from the world the status of every intent past its deadline. Which raises the thing every framing left unowned. + +**There is a cron at the bottom of this design.** A role only acts when an incarnation starts, and something must start it. Derived liveness correctly reports that nobody is alive when both leads are stalled, and then nobody is left to append the takeover. The mutual-restart topology does not close that loop; a timer does. So: **exactly one external stateless timer**, doing nothing but `POST /tick`, monitored by a dead-man's-switch service outside the system. It is the availability floor for takeover and reconciliation, it is drawn on the diagram, and it is the only stateful thing outside the chains. + +### 3.6 The work plane, and the one-field decision + +**Work references are URIs with a scheme**: `github:acme/api#88`, `jira:PROJ-412`, `pagerduty:INC-9`, `slack:C04…/p1699…`, `dossier:proj/phase/task`. Committed now, because it is one field and it decides everything downstream: with a scheme, the substrate can attach to work an organization already has and `dossier` becomes one backend among several; with `project/phase/task`, the system only ever works for the operator's own tracker. In an organization the work item is usually not a PR, and "GitHub is the source of truth" is a valid architecture only when it is. + +An `assign` captures `subject_digest` — a content digest of the work item at assignment time — so a renamed or rewritten ticket produces `subject_drift` rather than silent absorption. + +**Completion has two honest kinds.** `effect-derived` — a committed effect satisfies the item's declared predicate. `attested{by, grant, at}` — a named principal accepted it. Derived completion is the better one and it is the right default for merges and deploys, but it does not work for a design doc, a triage call, a runbook, or a thread, which is most of what leads and PMs do. Stretching derivation over judgment work would make the strongest claim contradict the strongest requirement. Attestation is a claim with an owner and a grant behind it; that is enough. + +### 3.7 The surface: `batond` + +Small, resource-oriented, tenant-rooted. The CLI is a client of the library locally and of this API remotely, with the same verbs, so nothing has two implementations. + +``` +POST /v1/t/{t}/roles/{role}/records If-Match: . 409 → tip_stale +GET /v1/t/{t}/roles/{role} folded projection; every derived field labeled `derived` +GET /v1/t/{t}/roles?held_by=&stalled= roster + derived liveness +POST /v1/t/{t}/effects/intents idempotent on effect_id +POST /v1/t/{t}/effects/{id}/outcome +GET /v1/t/{t}/effects?status=unknown the recovery queue +POST /v1/t/{t}/grants attenuating mint, parent = caller's grant. No revoke route. +GET /v1/t/{t}/work/{uri} which roles and chains touch this item +GET /v1/t/{t}/report?scope=&since= the shadow report: what would have been refused +GET /v1/t/{t}/events?from= SSE, replayable — audit export and integration surface in one +POST /v1/t/{t}/escalations/{id}/resolve +PUT|GET|DELETE /v1/t/{t}/blobs/{digest} DELETE leaves a tombstone; the chain still verifies +``` + +`If-Match` on the tip is the entire concurrency story — no lease service, no bespoke protocol, one sentence to a platform team. + +One correction to "if it can be derived, don't record it." The law is right for the chain and hostile to an API: consumers want fields, not a fold to reimplement, and a reducer that changes makes historical verdicts unreproducible. So the law is scoped and paired. **The chain records the minimum. The read API serves a fully materialized projection with every derived field labeled `derived` and stamped with the tip digest and the reducer version it was computed from.** A stale or differently-versioned view is *known* stale, never silently wrong, and an audit can say which code produced a verdict. + +**Identity: three subjects.** Principal (a human, via OIDC), role (the durable office, the addressable identity), incarnation (a session on a host, with a keypair minted at attach and the public half bound into the `attach` record). No long-lived tokens anywhere on an agent host. The comparison point's two secrets sitting on a VM, re-authenticated by a human over SSH every couple of days, is the precise anti-pattern; Baton keeps that setup's disposability — nothing on the box is worth backing up — and takes the box to zero secrets. + +**Tenancy** is the root of every namespace and the partition key of every chain, log, blob, and anchor, enforced at the storage layer. Schema-per-tenant on Postgres is correct for the first fifty customers. And to be blunt about a line that would not survive ninety seconds with a platform engineer: fold purity does *not* make tenancy free. Tenancy costs auth, isolation tests, per-tenant keys, backup granularity, deletion, and residency. It is priced as a phase (§7 P6), not as a footnote. + +### 3.8 The seams, exactly + +| Seam | Typed artifact | Writer | Reader | +|---|---|---|---| +| Continuity → Authority | `{role, incarnation, fence}` embedded in every grant; verifier keeps a per-role high-water mark | attach / takeover | custody, local gate. **Not CI.** | +| Continuity → Work | `assign{work_uri, subject_digest}`; one open assign per URI across all chains | the holder | fold-time law; work adapters | +| Continuity → Effect | `(role, fence, work_ref, effect_id)` stamped on every brokered request | the incarnation | custody, `effect_unstamped` if absent | +| Authority → Execution | `Secret{Name, Ref}` — a `custody:` handle, never a value; structured argv over logical roots, never a shell line | delegating role / runway | placement backends | +| Effect → Evidence | `intent`/`attempt`/`outcome` + authority receipt, joined on `effect_id` | custody | reconciler, audit, console | +| Execution → Work | run result + artifact links + PR/ticket URI | runway / ship-as-role | work plane, provenance | +| Any → Human | `escalation{question, options[], evidence_refs[], default, deadline}` → `resolution`. **An escalation with no default and no deadline is refused.** | any role | escalate/flare out; the answer appends to the *role*, not the session | +| Everything → Protocol | `parleyc`-emitted table of legal `(state, message-type)` pairs, checked into the repo | Haskell, build time | `contracts/org.Admissible`, Go, run time | +| Host → Continuity | SessionStart→attach, N-tool-calls/PreCompact→mark, Stop→release; distiller writes bodies | hooks | the `org` library | + +The escalation seam deserves one more line: the human answers the **role**. The original incarnation is almost certainly dead by then — at half a touch per role per day the wait is measured in days — and a reply delivered to a session evaporates with it. As a durable append, human latency is free. + +--- + +## 4. Component map + +### Load-bearing + +| Component | Role in the composed system | Change required | +|---|---|---| +| **custody** | The broker: policy enforcement point, credential boundary, effect ledger, idempotency fence, audit source of truth. One component because the agent cannot exceed its grant when it possesses no credential to exceed it with — mechanism, not policy. | Large. Intent-before-wire fail-closed (a charter reversal), `(role, fence, work_ref, effect_id)` stamp with refusal, classes and probes in the manifest, model provider as an upstream. **The highest-leverage single piece of work in the portfolio.** | +| **contracts/org** | The continuity contract: spine types, embedded schema, validation, bounded pure fold, refusal codes. Leaf package, no decision logic. | Build new (§4.4). | +| **contracts/authority** | Grant type with incarnation, fence, ceilings, and the monotonicity predicate. Extracted from gate so it is not one tool's private notion. | Extract + extend. | +| **drive** | The org runtime and the fleet's flagship *client*. | An **inversion**, not an addition. Its ledger is real and tested — attach/link/release, liveness reducer, roster, tree, torn-write tests — but it is keyed on `launch_id`, incarnation-first. The thesis is role-first with incarnations disposable. Plan it as a rewrite of the key. | +| **runway + contracts/execution** | Execution plane. Portable work spec, placed request, ordered events, one terminal result, pure reducer, provider-neutral by law, reconcile verb. Correct as designed. | Add reconciliation against the effect ledger. Do **not** add `unknown` to its terminal enum. | +| **rooms** | Isolation and egress control. See the honest scoping below. | None to the code; a lot to how it is positioned. | +| **hooks** | The host adapter, and the entire adoption story. Mechanism only. | Wire SessionStart/PreCompact/Stop; add the distiller. | +| **gate** | Demoted from flagship to **one adapter**: the merge-class verifier at `attest`. Its transferable value is the grant, which moves out. | Extract the grant; arm it on one repo. | +| **escalate + flare** | Escalation transport. | Append-to-role semantics; enforce default+deadline. | +| **console** | The human surface: roster, liveness, escalations, level dials, evidence bundles. | Add the two headline numbers (§4.5). | +| **channel** | Message transport, deliberately untyped. The *fact* of a message is the pair of chain records; the bus line is not authoritative. | None. | +| **dossier** | One work-URI scheme and a local work backend. Not the core. | Accept the URI decision; expose `subject_digest`. | +| **ship** | Becomes a **role** — a maintainer whose work items happen to be PRs — not a parallel engine. Two schedulers is how a system becomes a pile. | Reframe; its driver state machine survives intact. | + +**The containment scoping, stated plainly.** "A displaced incarnation's credentials die everywhere" is true only where the broker sits in the path. On a laptop the agent has bash, a network, an already-authorized `gh` keychain entry, and any MCP server it likes; custody is a boundary only for traffic that chooses to traverse it. What makes custody a *mechanism* rather than a *policy* is egress control, which means the sandbox, which means rooms, which means KVM, which means Linux — which is not the Mac all of this lives on. Therefore: **containment is a level, not a baseline.** At `observe` and `advise`, custody buys audit, attribution, and convenience. At `enforce` and above, the agent must run where its only network path is the broker. The operator's personal fleet will run at `advise` with audit-only containment, and the sentence said to a security reviewer is made about the rooms-hosted configuration only. Rooms is the reference provider behind runway's contract, never a customer requirement — a platform team that hears "you must run Firecracker" says "we have Kubernetes" and the conversation is over. + +### Evidence (results the design rests on; no code ships) + +- **switchboard** — the falsification that shaped the whole design. Crash recovery does not distinguish a resident actor from reload-from-disk; residency uniquely supplies **serialized ownership**. Cite it whenever someone proposes a daemon. +- **hack-branchroom** — the fold. Parent-digest tip, duplicate and gap in one condition, and the refusal of a *perfectly correlated* delayed terminal from a parent epoch, which is exactly the fence check. Ports into `contracts/org/reduce.go`. +- **hack-mandate** — the attenuation law, ports into `contracts/authority`. +- **hack-obligation** — content-addressed identity (the `effect_id` rule) and the add-only ratchet (the level ratchet). +- **hack-proofline** — the two-witness rule, and the honest finding that fail-open is correct over one trusted bundle and a **suppression attack** across two chains. This is why `org audit` reports `counterpart_absent` rather than passing silently — bounded by the receiver's declared `next_due`, so it is not noise on its first run. +- **fm-epoch-replay-laws**, **workbench-laws-lean**, **fm-grant-race**, **fm-jsonl-append-race**, **fm-custody-race**, **fm-crash-cut-publish**, **fm-scoped-path-laws** — see §6. +- **repair-loop-kernel** — the executable reference model of the effect plane. §3.5 is its implementation. +- **bailiff** — negative evidence: the EVM adds the capability model you already had, plus machinery. Closes that question permanently. +- **formal-methods** — the tool ladder, and the reason the `fm-*` repos are small and finished. + +All four hackathon kernels canonicalize with `json.Marshal`, tying digests to Go declaration order and HTML-escaping them. Their **laws** graduate into `contracts/*` on the shipped canonical encoder; the **repos** retire to a results page. Nobody adopts a portfolio; a teammate needs an artifact to point at. + +### Tooling (real value, no architectural weight) + +`triage`, `tracelens`, `local`, `spend-audit`, `provenance`, the skills corpus. Two notes: `triage` gets promoted only in the sense that review depth becomes the `cycle_ceiling` grant dimension; `spend-audit` becomes a derived view once model calls go through the broker. + +### Out of scope + +ivy, roxiq, rung, roll-call, interject, finance, fitness, wellness-ai, bakeoff. They are the dogfood targets — the repos the org runs against — not parts of it. + +### 4.4 What must be built new + +1. **`contracts/org`** — spine types, kind classes, embedded schema, the bounded fold, `Admissible`, seals, refusal codes. One mutant per refusal code **on the authority-relevant subset only**. +2. **The blob store** — content-addressed bodies, classification, retention, erasure-with-tombstone. +3. **The distiller** — transcript tail plus marks to a record body, via a cheap model. The thing that makes continuity verbless. +4. **The resume canary** — fork from the chain alone, score fidelity, trend it. +5. **The effect ledger inside custody** — intent/attempt/outcome, classes, probes, the stamp, the refusals. +6. **The reconciler role** and the one external timer. +7. **`batond`** — the API of §3.7, file backend first, Postgres second. +8. **The shadow report** — what would have been refused, on your own history. +9. **The fleet simulator** — §6. +10. **`contracts/policy`** — one ordered level per (scope, effect class). + +### 4.5 Surface retired versus added + +Retired: `/continue` (16 invocations, the manual workaround for exactly this problem), `/claim`, `/release`, `/roster`, `/recover`, and `claims.jsonl`. Added, for agents: **zero verbs** — attach, mark, and release fire from session lifecycle, and the fold arrives as injected context plus a read-only MCP resource. Added, for humans: one CLI (`baton`) and two console numbers, `blocked_roles` and `human_debt` (escalations past their declared deadline). Those two are the steering pair: if `human_debt` trends up, either the org shape or the level dial is wrong, and nothing else distinguishes those two causes. + +--- + +## 5. Levels + +Enforcement today is a boolean per repo behind `GATE_ENFORCE`, which is too much friction for personal repos and too coarse for anything else. But four independent dials is a feature for a customer who does not exist. **Decision: one ordered level, set per `(scope, effect class)` pair, with the other properties derived from it.** Fewer knobs, same power, and it obeys derive-don't-record. + +| Level | Refusals | Evidence | Containment | Escalation | +|---|---|---|---|---| +| **observe** | recorded, never returned | local chain | none | default applies on deadline | +| **advise** | returned as warnings; **the override is a first-class record with actor and reason** | local chain | none | default applies on deadline | +| **enforce** | bind for Baton's own operations | chain + periodic keyed anchor | broker-only egress required | human required for class U | +| **attest** | bind, and external systems require the evidence (branch protection, deploy gates) | anchors published off-box | sandboxed execution required | human required, no timeout, for class U and ceiling breaches | + +Three laws. + +**The same code path runs at every level.** The level changes verdict *handling*, never evaluation. This is the property that makes observe-mode data a trustworthy prediction of enforce-mode behavior, and without it the shadow report is a guess. + +**Promotion requires the data the level below produced.** You cannot arm `enforce` on a scope without N days of `observe` history showing what it would have refused. This is simultaneously an honest safety property and the entire adoption motion: install at observe, change nothing, run thirty days, then hand over a report saying *here are the 87 merges that would have been refused, on your own history, with the evidence*. Demotion is an append with an author and a reason. Monotone, auditable. + +**Overrides are the most valuable telemetry the system will ever collect.** An `advise`-level override is a rule that is wrong, recorded with a human's reason. That list is the tuning input; without it, promotion to `enforce` is guesswork. + +The personal-versus-enterprise answer falls out: personal repos at `advise` for everything; the workbench at `enforce` for the merge class; a bank at `attest` for whatever it chooses. One substrate, one code path, one dial. + +--- + +## 6. Where Gleam, Lean, and Quint are load-bearing + +The rule: each must answer a question that a direct Go test cannot, and anything that fails that test is cut. + +**Quint — load-bearing, and the clearest win in the portfolio.** Concurrency interleavings are exactly where intuition fails and exhaustive state exploration is exactly right. `fm-grant-race` found oversubscription of a one-cycle ceiling via stale local snapshots; `fm-jsonl-append-race` found short-write and lock-ownership failures with two writers. A Go test cannot enumerate interleavings; a model checker does it by construction. **Both counterexamples are promoted to permanent conformance tests replayed against the real adapter**, which is the ladder working as designed. New Quint spend goes to two seams only: the multi-writer append against the Postgres home, and the intent-before-wire crash cut. + +**Lean — load-bearing at four places, and nowhere else.** + +1. **Grant monotonicity.** A wrong law here is a security event, and the property is a universally quantified statement over an unbounded space of parent/child pairs. Property tests sample; a proof closes it. +2. **Fold boundedness.** The new one, and it is the one that matters for the thesis: *for every finite history, the fold's output is within the size budget.* No test establishes a bound over all histories. +3. **Ordering and tie-breaks.** This is where Lean already earned its keep — a *failed* proof in `workbench-laws-lean` found a real order-dependence at a rank tie in gate's verdict reducer. That is the lesson generalized: spend proof effort on comparators, orderings, and totality, where human intuition is reliably wrong. +4. **Protocol projection correctness** — that the compiler's per-role local contracts and the global type agree, so the admission table Go interprets has machine-verified provenance. + +`fm-epoch-replay-laws` (fold ≡ checkpoint-resume ≡ replay) stays because it is already finished and it underwrites the claim that the startup path and the recovery path are the same code. It is not where new proof effort goes: it is a theorem about a pure function over a finite list, and the bugs in a chain implementation live in the locking, the torn tail, the migration, the encoder, and the error paths. + +**Haskell — load-bearing at build time.** `parleyc` compiles a protocol written once as a global type, projects it into per-role local contracts, refuses incoherent protocols at the exact role and branch path, and **emits a JSON table of legal `(state, message-type)` pairs that is checked into the repo**. `contracts/org.Admissible` interprets that table at run time. Haskell owns the definition, Lean proves the projection, Go stays on the write path, nobody installs GHC to run the product. This is the move that converts the protocol compiler from decoration into structure, and it costs one artifact. + +**Gleam — on probation, with one job and a cut date.** The observer's real finding — 254 traces, 171 complete, 81 stalled, 2 deviating, and structure the mental model had missed — came from *writing down the expected sequence of events*, not from a BEAM runtime. Replaying a log against a state table is a Go fold. So the Gleam bus is off the write path permanently (an in-line cross-language checker is a call stack, and the forbidden import applies to services too), and it keeps exactly one job: **differential oracle** — for every recorded trace, the Gleam bus and the Go table-interpreter must classify identically, and a disagreement is a bug in one of them. If that differential finds no disagreement in one quarter, the Gleam runtime retires to a README result and the compiler plus the table stay. That is falsifiable, which is the only standard that should keep it. + +**Cut outright.** A mutant per refusal code *everywhere*. A mutant proves your test would catch a wrong law, not that your law is right. It is retained on the authority plane, where a wrong law is a security event, and dropped elsewhere, where it is a tax on the parts that most need to ship. + +**And the rung that was missing entirely.** Lean and Quint prove properties of individual reducers; the deployment story starts at one Mac. The claim under test — *this works at 75 roles for three days with crashes* — lives in the gap, and nothing tests it before a customer does. So: **a deterministic fleet simulator** driving the *real* contracts and the *real* fold with synthetic roles, a fault schedule, injected clock skew, and the exported Quint counterexamples as fixtures. Everything is already a pure reducer over a log, so this is the fold plus a fault injector. It produces recovery time and blocked-role rate as numbers, which is what an enterprise asks for and what no amount of proof supplies. + +--- + +## 7. Build order + +Phases P0 through P4 are **committed**. P5 through P7 are **gated** on their predecessors' results. + +### P0 — The falsification week. Zero new code. *Committed.* + +Every item is answerable from data already on the machine, and any one of them can invalidate a plane. + +1. **Classify the 87 blocked merges.** True positive or false positive. 87 of 247 actions is a 35% refusal rate against zero repos that require the check — those are unclassified predictions, not evidence, and the word "observational" is too kind. If false positives exceed 20%, the authority plane needs a redesign, not a rollout. +2. **Count the actual collisions.** Session-claims, worktree history, and transcripts, last 90 days: how many times did two sessions hold the same work item at once? +3. **Read 20 of the 81 stalled traces.** Defect or life? Until someone does, "32% stalled" measures the protocol's optimism. +4. **Price a role-day.** Eight concurrent ICs, one real day, read `spend-audit`. Multiply by 60. The reference topology comes from an engineer with internal capacity; the $40-a-month comparison point would be rate-limited into the ground at 75. + +**Gate.** If collisions ≥ 10 in 90 days *and* false positives < 20% *and* a role-day is affordable at target scale, proceed to the full design. If collisions come back at 3, the honest product is a good `/continue` that fires from a hook — one week of work — and everything about ownership is premature. Write the numbers down before deciding. + +### P1 — Continuity without a verb. *Committed.* + +`contracts/canonical` unified (two hash schemes exist in one module today — driverstate's declaration-order encoder pinned as `v0`, everything new on `v1`), `contracts/org` spine and bounded fold, the blob store, hooks at SessionStart/PreCompact/Stop, the distiller, the resume canary, the human-as-role attach. + +**Gate.** Kill a session mid-task; a fresh incarnation resumes and a **blind reader cannot tell** which records came from which incarnation. Resume-fidelity ≥ 90% across 20 canary runs. `/continue` invocations reach zero over 30 days and the skill is deleted. Two incarnations of one role run concurrently: the second is refused, the log is uncorrupted. + +### P2 — One seam in anger. *Committed.* + +Two fields on the grant (`incarnation`, `fence`), one high-water map in `custody serve`, one `(role, fence, work_ref, effect_id)` stamp on every request line with `effect_unstamped` as a refusal. + +**Gate.** Kill an incarnation, append a takeover, and the dead incarnation's next custody request is refused inside one append with nothing sent to it. Verifiable in a day, and it is the moment three repos become one system. + +### P3 — The effect ledger. *Committed.* + +Intent-before-wire fail-closed, the four classes, probes in the manifest, `effect_recovery_undeclared` as a load-time refusal for any manifest action with no probe, the unknown queue, the reconciler role, the one external timer. + +**Gate, and this is the demo that carries the whole thesis.** Kill an agent between `attempt` and `outcome` on a real merge. A replacement determines committed-versus-absent by probe alone, with no human, and never double-merges. Run it 20 times; zero duplicates, zero stalls. + +### P4 — Bind one thing. *Committed.* + +`enforce` on one repo, one effect class (merge), in anger. Not staged, not behind a plan. + +**Gate.** A real merge is refused and the emitted remedy unsticks it. Until this happens, every claim in this document about authority is a claim about a program. + +### P5 — Levels and the shadow report. *Gated on P0's classification and P4.* + +`contracts/policy`, the four ordered levels, the override record, promotion-requires-evidence, and the report generator. + +**Gate.** Personal repos at `advise`, workbench at `enforce`, and one shadow report generated from real history that a person who did not build the system can read and act on. + +### P6 — A second human. *Gated on P1 through P4 holding for 30 days.* + +`batond` with the API of §3.7, Postgres home, OIDC, tenant partition at the storage layer, blob classification and retention and erasure. This is the largest block of work and the least fun, and it is scheduled here for a specific reason: **the API's job is a second human on a second machine, not enterprise procurement.** Procurement asks for SOC2, a DPA, insurance, an SLA, and references, and a REST surface moves that needle by zero. What carries an enterprise conversation is the evidence pack — the proofs, the falsifications, the mutants, the shadow report — and what carries the product is one other person using it. + +**Gate.** A second human, on a different machine, holds a role in the operator's org for two weeks, uses it without being taught a verb, and provably cannot read another tenant's chain. Nothing here is proven with one person, and every claim is unfalsifiable until this happens. + +### P7 — Scale the org. *Gated on P6.* + +Fleet simulator first, then two leads with mutual takeover grants, project leads, ICs, model calls through the broker, spend and concurrency and cycle ceilings live, the parley admission table on the write path, the Gleam differential. + +**Gate.** Measure the real prompts-per-day number and the `human_debt` trend. If `human_debt` grows monotonically at 20 roles, the org shape is wrong and 75 will not work no matter what the substrate does. + +--- + +## 8. The POC + +**Two weeks. Two questions. Binary answers.** + +**POC-A — Does ownership matter?** (P0, item 2, plus a one-week instrumented run.) Turn on attach-and-mark from the hook across every session on the machine, with no refusals of any kind. After seven days, count the events where two incarnations held the same role or the same work URI simultaneously. + +- **Pass:** ≥ 10 collisions in seven days. Ownership is a real problem and the chain earns its place. +- **Fail:** ≤ 3 collisions. Ownership is a rare event, continuity is the whole product, and it is a text file plus a hook — one week of work, not a year. Stop and build that. + +**POC-B — Does the seam close?** The single trace, end to end, on real infrastructure. + +1. A role is chartered. An incarnation attaches, folds, and is assigned `github:#`. +2. It issues a merge through custody. custody records the `intent` (class Q, probe declared, deadline set) and fsyncs it **before** the wire. +3. The process is killed between `attempt` and `outcome`. +4. Derived liveness marks the role stalled at `next_due`. The timer fires; a supervisor appends `takeover`. +5. The dead incarnation is resurrected and made to retry. Its append is refused `tip_stale`; its custody request is refused `fence_regression`. **Nothing was sent to it.** +6. A fresh incarnation attaches, folds, and is **refused** any work append while an intent is open. It runs the declared probe, learns the merge committed, appends the `outcome`, and continues from `next[]`. +7. A blind reader, given only the chain and the effect log, reconstructs the whole sequence and states what the role is doing and why. + +**Binary success criteria.** All seven steps, with no human intervention between 3 and 6, no duplicate merge, and the blind reader correct on goal and next action. Total elapsed recovery under ten minutes. Run it 20 times with the kill point randomized across the window; **20 out of 20 or it failed.** + +If POC-A passes and POC-B passes, the architecture is real and P1 through P4 are the build. If POC-A fails, the product is smaller and better than this document. If POC-B fails, the failure will be in the join — the stamp, the probe, or the fold's refusal to proceed with an open intent — and that is exactly where the design most needs to be wrong early. + +--- + +## 9. The honest risks + +**Risk 1 — Construction velocity exceeds validation velocity, and the substrate becomes beautiful and unfalsifiable.** + +This is the highest-probability failure and it is not speculation; the base rate is in data the operator collected himself. 51 authored skills, 23 ever invoked. gate: 4,818 records, 247 actions, zero repos requiring the check. drive: a spec and slices. And most damningly, `claims.jsonl` — two records, one day, two weeks ago, then abandoned: an ownership ledger with claim and link events, the exact mechanism this document proposes, already tried and already dead. The failure mode is not abandonment; it is `~/dev` at 55 repos with a gorgeous `contracts/org`, a Lean proof, a mutant per refusal code, and gate still bound to nothing. + +*What falsifies it early:* the tell is mechanical — **any week in which a contract package ships and no repo changes its enforcement setting.** Two such weeks in a row and the build order has inverted itself. That is why P0 is a no-code week and why P4 (bind one repo, in anger) precedes every gated phase. + +*And the specific answer to why this survives where `claims.jsonl` did not:* that one required `/claim`. This one has no verb at all — attach fires from SessionStart and pays the agent immediately by injecting its own memory, marks are mechanical facts the host observes without the model's cooperation, and the distillation is written by a separate cheap model reading the transcript. The agent's discipline is not in the loop. If it turns out that it is, this risk has already materialized. + +**Risk 2 — The chain carries commitment but not thought.** + +Every guarantee on offer is a guarantee about record ordering and digests. Zero guarantees are offered about whether the distilled context is still *true* after twelve handovers. A structurally perfect chain whose content has drifted produces exactly the failure the system exists to prevent, silently, with a valid hash. And what makes a fresh incarnation resume well is entirely the quality of the distillation — a markdown file holding the last five checkpoints resumes just as well as a 400-record chain if nobody reads record 12. + +*What falsifies it early:* the resume canary, in P1, before anything else is built on top. If a blind reader cannot state the goal and the next action from the fold alone at ≥ 90% across 20 runs, the thesis is about locks and not about continuity, and the product shrinks to ownership plus an effect ledger — still valuable, but a different pitch and a much smaller build. This is the reason `goal` is charter-inherited and unwritable, the reason the fold is a bounded reducer with a Lean bound, and the reason the canary is a component rather than a nice-to-have. + +**Risk 3 — The substrate accelerates production into a fixed acceptance bottleneck, and cost makes the target topology unreachable.** + +Two problems with one shape. Sixty ICs producing work for two to three days each funnel through nine project leads and two leads into one human at two exception-handling prompts a day. Nothing in this architecture verifies 60 units a day of agent output — gate is a policy engine, not a reviewer. Amdahl's law applies to humans, and the honest consequence is that the substrate makes it possible to generate more unverified work faster while the binding constraint sits somewhere the architecture does not touch. Alongside it: a continuously working IC is somewhere between $20 and $150 a role-day depending on model mix, so 60 of them is somewhere between a car payment and a senior salary per month, and the reference topology comes from someone with internal capacity. + +*What falsifies it early:* P0 item 4 prices a role-day in one day with a tool that already exists — do it before designing for 75. And in P7, `human_debt` (escalations past their declared deadline) is the instrument: if it grows monotonically at 20 roles, 75 is unreachable regardless of substrate quality, and the correct response is a smaller org with derived completion, attested completion, and `cycle_ceiling` doing more of the acceptance work — not a bigger one. + +*What the design does about it, honestly:* completion derived from committed effects kills the "the agent said it was done" class outright; attested completion gives judgment work an owner and a grant rather than pretending derivation covers it; `cycle_ceiling` encodes the two-fix-rounds rule as a refusal instead of prose; and `blocked_roles` plus `human_debt` are the console headline so the bottleneck is visible before it is fatal. None of that creates review capacity. It only makes the shortage measurable, which is the most an architecture can do about it. + +--- + +### One thing a buyer will ask inside ninety seconds, answered here so it does not have to be improvised + +*What does this do that Temporal plus a Postgres advisory lock plus branch protection plus CODEOWNERS plus short-lived GitHub App tokens does not?* + +Three things, and only three. **Fence-bound authority** — revocation is a local write that reaches partitioned hosts with no message sent, where every alternative is a TTL or a broadcast. **Declared recovery per effect class, refused at intent time** — the difference between logging what agents did and bounding what happens when one dies mid-effect. **Continuity as harness-neutral data** — Temporal owns the workflow and requires deterministic replay of a worker; here the worker is an LLM, nothing about it is deterministic, and the ledger belongs to the organization rather than to any one harness. + +And the honest half: if your work *is* a deterministic workflow, use Temporal. This exists because it is not. +--- + +## Appendix A — Primitive design: useful, easy to use, hard to misuse + +The vision above settles *what* to build. This appendix settles a separate +question the operator asked directly: are these good primitives? Graded on +Rusty Russell's scale, where the top is *impossible to get wrong*, the middle +is *read the docs and you will get it right*, and the bottom is *obvious usage +is wrong*. Four findings change the API surface. + +**A1 — The append API must be a transaction closure, not a caller-supplied +tip.** This is the highest-leverage API decision in `contracts/org`. Given +`tip := Fold(role); …; Append(tip, rec)`, every caller owns the +read-verify-write window and some of them will get it wrong; the §3.2 lock +becomes a rule people have to know. Given +`Append(role, func(tip RoleState) (Record, error))`, the lock spans the +callback and the window is unreachable. Identical semantics, opposite ends of +the scale. **The tip is never a parameter the caller supplies from an earlier +read.** + +**A2 — `Canonical()` completeness must be mechanically checked.** Nothing +forces a record type's canonical form to include all of its fields. Add a +field, forget the canonical form, and the digest silently stops covering it — +a chain that looks sealed and is not. Obvious usage is wrong. A conformance +test reflects over each record struct and asserts every exported field appears +in its canonical shape. This lands with the first record type, not after. + +**A3 — A missing fence high-water mark must fail closed.** §3.4 correctly +scopes fence enforcement to long-lived verifiers. It does not address what +happens when a long-lived verifier *loses* that state — a redeploy, a new +machine, a wiped state dir. The mark resets to zero, every displaced grant +verifies again, and nothing signals it: security silently weaker after a +routine deploy. Missing high-water state is a refusal with a remedy, never an +accept. + +**A4 — The cross-chain assign invariant needs an owner or an honest +downgrade.** "One open assign per work URI across all chains" cannot be +established by folding one chain. Today it is asserted at the contract and +enforced nowhere, so two leads can hand the same item to two ICs and only a +global sweep notices. Either the home owns it as a real uniqueness constraint +(trivial with a Postgres home, a scan with a file home), or the contract says +`assign_conflict` is **detected, not prevented**, and the reconciler surfaces +it. Silence is the only unacceptable option. + +**A5 — Charters are a usability problem, not a correctness one.** They score +worst of any primitive here: hand-authored scope, decides, never-decides, +capabilities, and ceilings, with no template, no default, and no validation. +Too tight and the role is useless; too loose and it is overpowered; both fail +silently. The fix is not a law but a template per role kind plus a `doctor` +check that flags capabilities a role's work kinds never exercise. + +**A6 — Checkpoint quality cannot be fixed by an API, only measured.** +`empty_next` catches empty and nothing catches vague. §3.3's move — the host +writes marks, a separate distiller writes bodies — removes the agent's +discipline from the loop, which is the real fix. The resume canary is the +instrument. No contract can do more. From 42398a2daa268d88bb06645afd3146b63393dfce Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sat, 22 Aug 2026 08:50:42 -0700 Subject: [PATCH 09/24] =?UTF-8?q?docs(org):=20P0=20findings=20=E2=80=94=20?= =?UTF-8?q?the=20gate's=20blocks=20are=2072%=20timing,=20not=20risk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Item 1 run against gate's real log. All 87 blocked merges joined to their verdicts and classified: 63 (72%) evidence-not-ready, 22 (25%) genuine judgment, 2 findings/changes-requested. The 22 judgment blocks are good calls on real risk, so the authority plane clears its gate - false positives on risk judgment are low and it does not need a redesign. But 72% of every block gate has emitted says 'you invoked me before my evidence existed'. That is a scheduling bug, and it is the same post-force-push race already recorded once, at dominant scale rather than as an edge case. Design consequence: blocked conflates 'no' with 'not yet'. Different verdicts, different remedies, and gate already has a park outcome these are not using. Splitting them takes the apparent block rate from 35% to ~9%. This lands before any level is armed - arming enforce on a verifier whose blocks are 72% timing noise is how a plane gets switched back off. Item 2 could not be answered retrospectively and that is the finding. Transcripts span 20 days not 90. Tightest honest measure is 30 pairs across 25 PRs of two worker sessions active on one PR within 15 minutes, and hand inspection dissolves most: cross-repo sweeps under the reader threshold, three parallel subagents of one parent in one temp dir, and pairs indistinguishable from both having run gh pr list. Mentioned-the-same-PR is not held-the-same-work-item, because holding was never recorded. POC-A must run live; that specification is now evidence-backed rather than assumed. Co-Authored-By: Claude Fable 5 --- docs/features/org/p0-findings.md | 129 +++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 docs/features/org/p0-findings.md diff --git a/docs/features/org/p0-findings.md b/docs/features/org/p0-findings.md new file mode 100644 index 00000000..536a65ed --- /dev/null +++ b/docs/features/org/p0-findings.md @@ -0,0 +1,129 @@ +# P0 — falsification findings + +**Date:** 2026-08-22 +**Status:** items 1 and 2 run. Items 3 and 4 outstanding. +**Method:** analysis of `~/dev/gate/state/log.jsonl` (4,818 records) and 325 +Claude Code session transcripts under `~/.claude/projects`. + +P0 is the no-code week in [`vision.md`](vision.md) §7: four questions +answerable from data already on the machine, any one of which can invalidate a +plane. This file records what the data actually said, including where it +refused to answer. + +--- + +## Item 1 — Classify the 87 blocked merges. **Gate passes; the finding is elsewhere.** + +The vision's threshold: *if false positives exceed 20%, the authority plane +needs a redesign, not a rollout.* + +All 87 `action` records with `outcome: blocked` were joined to their verdicts +and classified by the verdict's `why`: + +| Count | Share | Category | +| ---: | ---: | --- | +| 63 | 72.4% | **Evidence not ready** — review panel incomplete, `completed=0`, or GitHub reporting `mergeability UNKNOWN` | +| 22 | 25.3% | **Judgment** — a substantive risk call | +| 1 | 1.1% | Review findings outstanding | +| 1 | 1.1% | `CHANGES_REQUESTED` | + +Spread across 11 repositories; by tier T2 (41), T1 (22), T3 (17), T0 (7). + +**The 22 judgment blocks are good.** Representative: + +- *"The PR changes the review policy itself by removing cursor from the + required panel, while the recorded panel evaluation for this head still…"* +- *"This is a substantial T2 change (+1015 lines) affecting concurrency and + locking behavior."* +- *"…without requiring an exact triggering build, the workflow can attribute…"* + +That is a verifier reasoning about real risk on real changes. **The +false-positive rate on risk judgment is low and the authority plane does not +need a redesign.** Item 1 clears its gate. + +### But 72% of blocks are a scheduling bug, not a policy one + +Nearly three-quarters of every block gate has ever emitted says, in effect, +*you invoked me before my evidence existed*. The change was not unsafe; the +panel had not finished or GitHub had not computed mergeability. Two readings, +both true: + +- **Safety reading** — not a false positive. Declining to authorize on + incomplete evidence is exactly correct, and a gate that passed there would + be broken. +- **Friction reading** — entirely a false positive. The change was fine, the + block cost a cycle, and re-running later passes. + +This is the same failure already recorded once as the post-force-push race +(poll head and `mergeable_state` before invoking gate). At 72% it is not an +edge case; it is the dominant mode. + +**Design consequence: `blocked` conflates "no" with "not yet."** They are +different verdicts with different remedies — one needs a fix, the other needs +a wait — and gate already has a park outcome these are not using. Splitting +them takes the apparent block rate from 35% to roughly 9% and makes every +surviving block meaningful. This lands before any enforcement level is armed, +because arming `enforce` on a verifier whose blocks are 72% timing noise is +how a plane gets switched back off in week two. + +--- + +## Item 2 — Count the collisions. **Unanswerable retrospectively. That is the result.** + +The question: *how many times did two sessions hold the same work item at +once?* + +**The data does not go back 90 days.** 325 transcripts span 2026-08-02 to +2026-08-22 — a 20-day window. + +Three measures, progressively tighter: + +| Measure | Count | Verdict | +| --- | ---: | --- | +| Two sessions overlapping in the same directory | 226 | Meaningless — 214 are chat sessions at the portfolio root | +| Two sessions whose open windows overlap and that both mention one PR | 314 pairs / 124 PRs | Inflated — a session left open for days overlaps everything; one window was 5,378 minutes | +| Two **worker** sessions (excluding those touching ≥10 PRs) active on one PR within 15 minutes | **30 pairs / 25 PRs** | Upper bound; see below | + +Inspecting the 30 by hand dissolves most of them: + +- Several are one session in a `gate` worktree referencing PRs from `drive` + and `rung` — a cross-repo sweep that fell just under the reader threshold. +- Six are three sessions in the same `/private/tmp/claude-501/…` directory + touching the same two PRs — parallel subagents of one parent, which is one + logical worker, not a collision. +- The remainder are two worktrees of the same repo referencing the same PRs + within a minute, which is **indistinguishable from both having run + `gh pr list`**. + +**"Mentioned the same PR" is not "held the same work item," and no log +archaeology closes that gap** — because holding was never recorded. Which is +exactly why POC-A is specified as a live instrumented run (attach and mark +from the hook, no refusals, count for seven days) rather than a query. That +specification is now confirmed by evidence rather than assumed. + +**Honest read on the ownership thesis:** it is neither supported nor refuted +by this data. The near-miss rate is non-trivial and nothing in the current +setup would have prevented a genuine collision, but the collision count itself +remains unmeasured until POC-A runs. + +--- + +## Items 3 and 4 — outstanding + +- **Read 20 of the 81 stalled parley traces.** Defect or life? Until someone + does, "32% stalled" measures the protocol's optimism rather than the + system's health. +- **Price a role-day.** Eight concurrent ICs, one real day, read + `spend-audit`, multiply by 60. Do this before designing for 75 roles. + +--- + +## What P0 has changed so far + +1. Split gate's `blocked` into *no* and *not yet*, and fix the trigger, before + arming any enforcement level. This is now the highest-value single change + to the existing system. +2. POC-A must run live. Budget a week of instrumented sessions; do not attempt + to reconstruct the number. +3. The authority plane's judgment survives contact with its own history, which + is the one thing P0 could have killed and did not. From ac6a74316d6875bb226bf647e9c3ece6ac0c1f50 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sat, 22 Aug 2026 08:52:11 -0700 Subject: [PATCH 10/24] =?UTF-8?q?docs(org):=203.9=20=E2=80=94=20the=20orga?= =?UTF-8?q?nizing=20idea=20is=20one=20state=20machine,=20planes=20are=20gu?= =?UTF-8?q?ards?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The six-plane table is a decomposition, not a reason the pieces cohere. This is the reason: the platform is a state machine over work ownership in which illegal states are unrepresentable, and each plane exists to guard one transition. A plane with no transition to guard does not belong - which answers why six and not four or nine. Four laws, stated as things the system cannot represent rather than things an agent should remember. An incarnation cannot exist without owned work. It may hold many items and act on exactly one. Stopping must produce a terminal record; abnormal termination leaves a dangling claim the next incarnation must resolve before claiming anything. An action that propagates state must produce a receipt. The active-claim law has the largest practical payoff and was missing from every framing: with exactly one claim active, the work_ref on an effect stamp is derived from state rather than supplied by the caller, so an agent structurally cannot attribute an effect to the wrong work item. A misuse class deleted rather than documented. Sum types go where they are real: define the lifecycle in a language that has them, prove the transition relation total and illegal states unreachable, emit the table as a checked-in artifact, and interpret it in Go. Same mechanism parleyc already uses for protocols - second use, which is the evidence the mechanism was right. Co-Authored-By: Claude Fable 5 --- docs/features/org/vision.md | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/docs/features/org/vision.md b/docs/features/org/vision.md index 658e074f..544009db 100644 --- a/docs/features/org/vision.md +++ b/docs/features/org/vision.md @@ -207,6 +207,86 @@ The escalation seam deserves one more line: the human answers the **role**. The --- +### 3.9 The organizing idea: one state machine, and every plane is a guard + +The six planes above are a decomposition, not yet a reason the pieces belong +together. This is that reason, and it is the frame the rest of the document +should be read through. + +**The platform is a state machine over work ownership in which illegal states +are unrepresentable, and every plane exists to guard one of its transitions.** + +``` +Chartered ──attach──▶ Held(incarnation) ──retire──▶ Retired + │ ▲ + claim(w) │ │ yield · complete · abandon + ▼ │ + Active(incarnation, w) + │ + every effect carries (role, incarnation, fence, w) +``` + +Four laws, each a guard, each owned by exactly one plane. They are stated as +things the system *cannot represent*, not things an agent should remember. + +**L1 — An incarnation cannot exist without owned work.** `attach` requires a +charter whose scope names at least one work reference, and `prev == tip`. +There is no unscoped agent. A lead's scope is a scope, a maintainer's is an +`area`, an IC's is a task; the `work.kind` vocabulary (§3.6) is what lets one +rule cover all three. *Guarded by Continuity.* + +**L2 — An incarnation may hold many work items and may act on exactly one.** +`claim(w)` requires `w` to be held, no other claim active, and no open intent. +`act` requires the state to be `Active`. This is the law with the largest +practical payoff and it was missing from every framing: because exactly one +claim is active, **the `work_ref` on an effect stamp is derived from state +rather than supplied by the caller.** An agent structurally cannot attribute +an effect to the wrong work item — a misuse class deleted rather than +documented. It also makes context-switching an event, so the chain records +what the incarnation was actually doing at each moment instead of that it held +five things. *Guarded by Continuity; consumed by Effect.* + +**L3 — Stopping must have an effect on the work.** An incarnation cannot end +cleanly without a terminal record — `yield`, `complete`, `abandon`, or +`handoff`. Abnormal termination leaves the claim dangling, the fold reports +`dangling_claim`, and **the next incarnation must resolve it before it may +claim anything.** Silent disappearance is not a representable outcome. This is +the same shape as the open-intent rule and for the same reason: an +unresolved obligation blocks progress rather than being inherited invisibly. +*Guarded by Continuity; resolved with Effect.* + +**L4 — An action that propagates state must produce a receipt.** Every effect +requires `Active`, a grant covering its class, and a declared recovery, and +produces intent / attempt / outcome. No receipt, no effect. *Guarded by +Authority and Effect.* + +**Why this is the organizing idea rather than a detail.** It answers the +question the plane table cannot: why these six and not four or nine. Each +plane is the guard on a transition that would otherwise be enforced by prose, +and a plane with no transition to guard does not belong. It also explains the +composition law — a guard needs the artifact the previous transition produced, +never the previous plane's code — which is why contracts and not call stacks +falls out of the model rather than being imposed on it. + +**Where the sum types go.** Go cannot express a state machine whose invalid +combinations are unconstructible, and pretending otherwise produces a +hand-maintained switch statement that drifts from the document. The move is +the one §6 already makes for protocols: **define the lifecycle where sum types +are real, prove the transition relation total and the illegal states +unreachable, emit the transition table as a checked-in artifact, and have Go +interpret it.** Same mechanism as `parleyc`, second use — which is the +evidence the mechanism was right. Nobody installs GHC to run the product, and +the state machine has machine-verified provenance instead of a reviewer's +recollection. + +**The friction this creates, and its resolution.** "Cannot act without a +claim" is correct for an IC under `enforce` and hostile to an operator opening +a terminal to poke at something. Levels resolve it exactly as they resolve +every other instance of this tension: at `observe` and `advise` the claim is +auto-created from context as a free-text work reference and the absence of one +is recorded rather than refused; at `enforce` it is required. Same evaluation, +different handling — the rule levels already obey (§5). + ## 4. Component map ### Load-bearing From befaed67ddeae6c419778ac1d325bb4a561f7fc4 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sun, 23 Aug 2026 08:17:59 -0700 Subject: [PATCH 11/24] docs(org): answer P0 items 3 and 4, and correct item 1's headline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Items 3 and 4 are answered, and item 1's 72% does not survive re-derivation. Recording both here so the next session reads a number instead of computing a fourth one. Item 3 — the stalls. Two sessions answered this on the same day with opposite verdicts ("0 defects, life" and "not life, abandoned") from different denominators. Both are wrong. Measured straight off gate's log rather than through parley: of 342 parked runs, 106 were never resolved on that run — but 92 of those 106 had a SIBLING RUN on the same PR that reached an action. Gate decided; it decided on a different run id. A trace is keyed by run, a decision is keyed by subject, and the gap is 87% of what "stalled" was counting. The observer is wrong, not the protocol. The residue is 14 runs across 7 PRs, and only one part of it is real: roll-call#8 and workbench#214 merged with NO recorded gate decision at all. That is a hole in the authorization record and it is the honest version of what item 3 was reaching for. Item 4 — a role-day has no single value. It spans 21x across defensible definitions of one unit ($9.10 observed session-day, $40.04 half-shift, $62.82 long-shift). A true 8-hour agent shift does not exist in the data: longest ever is 268 active minutes, median 14. Concurrency carries no measured penalty — flat $0.26-0.35 per agent-minute from 1 to 15 — so the axis the design worried about is the one that behaves. Affordable ceiling is ~25 roles at the half-shift rate, not 75. Item 1's correction is the one that changes a decision. Classifying the same 87 blocked actions by which producer emitted the block: 56 (64%) came from a JUDGE, 31 (36%) from deterministic readiness, and only 24-29 of 87 (28-33%) would have cleared by waiting. Getting to 72-75% requires counting judge blocks whose prose mentions an evidence gap as "evidence not ready" — defensible as description, but not the operational claim, since by then a cycle is spent and a judgment recorded. Three sessions produced three numbers because the classifier boundary was never stated. So "split blocked into no and not-yet" is no longer the highest-value change; it was ranked there on the 72%. Paired with a separate analysis rejecting it because its backstops are structurally unreachable in CI, the recommendation is now: fix the observer keying, settle evidence before opening a run, and revisit the vocabulary only if a residue remains. Co-Authored-By: Claude Opus 5 --- docs/features/org/p0-findings.md | 108 ++++++++++++++++++++++++++++--- 1 file changed, 100 insertions(+), 8 deletions(-) diff --git a/docs/features/org/p0-findings.md b/docs/features/org/p0-findings.md index 536a65ed..f570953f 100644 --- a/docs/features/org/p0-findings.md +++ b/docs/features/org/p0-findings.md @@ -1,7 +1,8 @@ # P0 — falsification findings -**Date:** 2026-08-22 -**Status:** items 1 and 2 run. Items 3 and 4 outstanding. +**Date:** 2026-08-22, corrected and completed 2026-08-23 +**Status:** all four items answered. Item 1's headline is corrected below — +the 72% figure does not survive re-derivation. **Method:** analysis of `~/dev/gate/state/log.jsonl` (4,818 records) and 325 Claude Code session transcripts under `~/.claude/projects`. @@ -108,13 +109,104 @@ remains unmeasured until POC-A runs. --- -## Items 3 and 4 — outstanding +## Item 3 — The stalls. **Neither "life" nor "abandoned." The observer is keyed wrong.** -- **Read 20 of the 81 stalled parley traces.** Defect or life? Until someone - does, "32% stalled" measures the protocol's optimism rather than the - system's health. -- **Price a role-day.** Eight concurrent ICs, one real day, read - `spend-audit`, multiply by 60. Do this before designing for 75 roles. +Two sessions answered this on 2026-08-23 and reached opposite verdicts — +"0 defects, life not defect" and "not life, abandoned" — from different +denominators (133 vs 81; 81 is the stale 2026-08-16 README snapshot). Both +framings are wrong, and the disagreement is itself the finding. + +Measured directly against `~/dev/gate/state/log.jsonl`, without going through +parley at all — for every run that emitted an escalation, did that run later +record a judgment or an action? + +| | count | share | +| --- | ---: | ---: | +| runs that parked | 342 | — | +| resolved in gate (judgment or action on the same run) | 236 | 69% | +| **never resolved on that run** | **106** | **31%** | + +Of those 106, the decisive split: + +| | runs | | +| --- | ---: | --- | +| a **sibling run on the same PR** reached an action | 92 | gate did decide — on a different run id | +| no run on that PR ever reached an action | 14 | across only **7 distinct PRs** | + +And the 7: `roll-call#8` and `workbench#214` merged with no gate decision at +all; `roxiq#205` closed; `ivy#22`, `workbench#247`, `#249`, `#253` still open +— three of those four are current in-flight work, not abandonment. + +**The defect is in the observer, not the protocol.** A trace is keyed by *run +id*; a decision is keyed by *subject*. A park on run A that run B resolves is +invisible to a run-keyed reader, which is 87% of what "stalled" was counting. +Until the observer folds runs by subject, the stalled percentage measures its +own keying and nothing about system health — so it cannot be used as evidence +for or against any plane. + +The residual worth acting on is small and specific: **two PRs merged with no +recorded gate decision.** That is a hole in the authorization record, and it is +the honest version of what item 3 was reaching for. + +## Item 4 — A role-day. **Affordable at 75 only under the cheap definition.** + +Measured from `spend-audit` over 591 sessions, 27,697 billed messages, +2026-08-02..23. Public-rate equivalents, not an invoice — the corpus carries +no billing field, so it cannot say whether this was API-rate or absorbed by a +flat subscription. + +A "role-day" has no single value; it has a 21x spread across defensible +definitions of the same unit: + +| definition | n | mean $/role-day | 75 roles/month | +| --- | ---: | ---: | ---: | +| any session-day as observed (29 active min) | 700 | $9.10 | $14,327 | +| >= 60 active min ("half shift") | 100 | $40.04 | $63,057 | +| >= 120 active min ("long shift") | 32 | $62.82 | $98,937 | + +**A true 8-hour agent shift does not exist in this data.** Longest session-day +ever recorded is 268 active minutes; the median is 14. + +Concurrency is the one clean invariant: cost per agent-minute is flat at +$0.26-0.35 from 1 to 15 concurrent agents. No measured coordination penalty — +so linear extrapolation holds *on that axis*. + +**Verdict.** Against a one-engineer budget, the affordable ceiling is ~109 +roles at the observed-session-day rate and **~25 roles at the half-shift +rate**. If Baton roles do sustained work rather than fire in bursts, 25 is the +honest ceiling and the design's 75 is 3x outside it. 83% of spend is cache +mechanics, which is where any optimization has to aim. + +## Correction to item 1's headline — the 72% does not survive re-derivation + +The 72% figure above is not reproducible, and the reason matters more than the +number. Re-classifying the same 87 blocked actions by *which producer emitted +the block*: + +| | count | share | +| --- | ---: | ---: | +| a **judge** decided to block | 56 | 64% | +| deterministic **readiness** block | 31 | 36% | + +Within the 31 deterministic blocks: 24 purely transient (would clear by +waiting), 2 purely terminal, 5 mixed. So blocks that a wait would have fixed +are **24-29 of 87 — 28-33%, not 72%.** + +Reaching 72-75% requires counting *judge* blocks whose prose mentions an +evidence gap as "evidence not ready." That is defensible as a description of +why the judge was called, but it is not the operational claim the split rests +on: by then a cycle has already been spent and a judgment already recorded. +Three sessions produced three numbers (72%, 75%, 33%) because the classifier +boundary was never stated, not because anyone mis-counted. + +**Consequence for the split.** "Split `blocked` into no and not-yet" was +recorded below as *the highest-value single change* on the strength of the 72%. +At 28-33%, and with a separate analysis rejecting the split on termination +grounds (its backstops derive from prior artifacts for the same subject, and +CI mints state into a `mktemp -d` that is deleted on exit, so they are +structurally unreachable there), that ranking no longer holds. Fix the +observer keying and settle evidence before opening a run; revisit the terminal +vocabulary only if that leaves a real residue. --- From 1faa53f80b1e9a74a7cf79508faa39a524810ec9 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sun, 23 Aug 2026 09:30:57 -0700 Subject: [PATCH 12/24] =?UTF-8?q?docs(org):=20where=20this=20stands=20?= =?UTF-8?q?=E2=80=94=20what=20is=20proven,=20what=20is=20still=20a=20claim?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Written at the point where the first real code landed and the first real numbers came in. Separate from vision.md, which argues what to build, and p0-findings.md, which records measurements: this says what is now TRUE, what is still a CLAIM, and what that combination argues for. Its conclusion is not the one the vision expects. Evaluating §7's own P0 gate: collisions unmeasured, false positives 27.6% against a <20% bar, and a role-day affordable to ~25 roles against a target of 75. Two of three fail and the third is unknown, so the gate says do not proceed to the full design — and its stated fallback, a /continue that fires from a hook, is a week of work. The reframing that makes this less bleak than it sounds: four separate observations turned out to be one defect. 87% of stalled traces were decided by a sibling run; the stalled metric measures the observer's keying; two PRs merged with no gate decision recorded; the inbox showed 149 already-merged obligations. Gate records RUNS, but the thing that matters is the SUBJECT's decision history — which is exactly the problem Baton exists to solve. Baton's first customer is gate, and an ownership substrate for one flagship tool needs no fleet, no 75 roles, no second machine, and no tenancy. It needs a chain per subject and a fold, and both are on main. Also names the project's characteristic failure plainly: every number that lived only in prose drifted, and every number re-derived from data came back different — 72% became 27.6%, 81 stalled became a keying bug, 75 roles became 25. The instrument that keeps working is exhaustive enumeration, independently rediscovered in parley, warrant, and this package. Stated once here so it stops being rediscovered. Co-Authored-By: Claude Opus 5 --- docs/features/org/where-this-stands.md | 204 +++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 docs/features/org/where-this-stands.md diff --git a/docs/features/org/where-this-stands.md b/docs/features/org/where-this-stands.md new file mode 100644 index 00000000..78ae1e33 --- /dev/null +++ b/docs/features/org/where-this-stands.md @@ -0,0 +1,204 @@ +# Where this stands — 2026-08-23 + +A synthesis written at the point where the first real code landed and the first +real numbers came in. It is deliberately separate from [`vision.md`](vision.md), +which argues what to build, and [`p0-findings.md`](p0-findings.md), which +records measurements. This file says what is now **true**, what is still a +**claim**, and what that combination argues for doing next. + +Its central conclusion is not the one the vision expects. + +--- + +## 1. The shape of the thing, in one paragraph + +The workbench is five planes that compose through typed artifacts and exit +codes rather than call stacks — State (dossier, gate's hash-chained log), +Execution (ship's driver), Verification (the escalate-only verifier ladder), +Capability (scoped, timed grants), Observability (flare, console, `/wip`). One +law holds them apart: **no plane imports another plane's decision logic.** gate +is the flagship because it is the only tool spanning two planes — it decides +authorization, which is not the same question the reviewers answer. + +Baton (working name `org`) proposes four more planes — Continuity, Effect, +Work, Surface — on one thesis: **ownership and continuity are the same fact, +and the fact is a compare-and-swap.** A role is a durable office with an +append-only chain; an incarnation is a disposable session that reads the tip, +takes it, acts, and writes back. To act you must append; to append you must +present the tip you read. From that single rule fall continuity, mutual +exclusion, handover, and revocation. + +--- + +## 2. What is now proven, what is measured, and what is still a claim + +The distinction matters more than any individual item, because the project has +been running on prose numbers that did not survive measurement. + +### Proven — code on `main`, tested harder than anything else in the portfolio + +`contracts/org` implements the ownership fold. **"Holding the tip is being the +role" is now a property of code rather than a convention a runtime is trusted +to follow.** Specifically established: + +- The compare-and-swap, with identity settled **before** chain position — the + stale writer that matters re-read the tip and presents a correct `prev`, so a + position-first reducer calls that chain healthy and misdiagnoses the one + failure the law exists to catch. +- The one-active-claim law, whose payoff is that an effect stamp's `work_ref` + is **derived from state rather than supplied by the caller**. That deletes a + misattribution class instead of documenting it. +- Inherited obligation: a takeover mid-claim leaves a dangling claim the + successor must discharge before it may claim anything, and teardown refuses + while any obligation is open. + +Evidence: 98.1% line coverage, 98.6% mutation efficacy, and an exhaustive walk +of all 86 reachable states asserting totality and eight invariants. + +### Measured — evidence that changed decisions + +| finding | number | +|---|---| +| gate blocks that came from a **judge** | 56/87 (64%) | +| gate blocks that a **wait** would have cleared | 24/87 (27.6%) | +| parked runs never resolved *on that run* | 106/342 (31%) | +| …of those, resolved by a **sibling run on the same PR** | 92/106 (87%) | +| PRs merged with **no recorded gate decision at all** | 2 | +| affordable role ceiling at the half-shift rate | **~25**, not 75 | + +### Still a claim — the load-bearing ones + +- **Collisions.** Unmeasured, and *unmeasurable retrospectively* — "mentioned + the same PR" is not "held the same work item," because holding was never + recorded. This is the input the vision's own gate turns on. +- **Distillation quality.** §3.3 names it "the thesis's only untested + load-bearing assumption," and nothing has tested it. The chain can carry + commitment; whether it carries *thought* is unknown. +- **A second human.** Every claim about the org shape is unfalsifiable until + someone else holds a role. + +--- + +## 3. The finding that reframes the project + +Four separate observations turned out to be one defect wearing four costumes: + +1. 87% of "stalled" traces were decided — by a *sibling run on the same PR*. +2. The stalled percentage measures the observer's keying, not system health. +3. Two PRs merged with no gate decision recorded anywhere. +4. `gate next` showed 149 obligations that were already merged. + +**gate records runs; the thing that matters is the subject's decision history.** +A park on run A that run B resolves is invisible to a run-keyed reader. A merge +that happens out of band leaves no trace at all. Gate has no continuity across +runs about a subject — which is precisely, exactly, the problem Baton exists to +solve. + +So the honest framing is not "build an org substrate, then find users." It is: + +> **Baton's first customer is gate, and gate is already suffering the exact +> failure Baton fixes.** + +That reframing matters because it changes the scale at which the thesis has to +be true. An ownership substrate for one flagship tool needs no fleet, no 75 +roles, no second machine, and no tenancy. It needs a chain per subject and a +fold. Both exist and are on `main`. + +--- + +## 4. The vision's own gate, evaluated + +§7 P0 sets the condition for proceeding past P1: + +> If collisions ≥ 10 in 90 days *and* false positives < 20% *and* a role-day is +> affordable at target scale, proceed to the full design. If collisions come +> back at 3, the honest product is a good `/continue` that fires from a hook — +> one week of work — and everything about ownership is premature. Write the +> numbers down before deciding. + +| input | threshold | measured | verdict | +|---|---|---|---| +| collisions / 90 days | ≥ 10 | **unmeasured** | unknown | +| false positives | < 20% | **27.6%** | **over** | +| role-day at target scale | 75 roles | affordable to **~25** | **3× under** | + +Two of three fail; the third is unknown. + +**On the false-positive number, both readings belong on the record.** Declining +to authorize on incomplete evidence is not *wrong* — under that reading the +rate is ~0%. But the threshold was written to catch "gate blocked something +that was actually fine," and a block a wait would have cleared is exactly that. +The threshold means the friction reading. It is breached. + +**The 72% that this decision was previously resting on does not survive +re-derivation.** Reaching it requires counting judge blocks whose prose mentions +an evidence gap as "evidence not ready" — defensible as description, but by +then a cycle is spent and a judgment recorded. Three sessions produced 72%, +75%, and 33% because the classifier boundary was never stated. That is the +project's characteristic failure, and it is worth naming: **every number that +lived only in prose drifted, and every number re-derived from data came back +different.** + +--- + +## 5. What to do next + +**Run POC-A, and nothing else from the roadmap.** + +`SessionStart` → attach → append; `Stop` → release; count collisions for a +week. Two properties make it the correct next move regardless of what it finds: + +- It is the **one missing gate input**, and it cannot be obtained any other way. +- It is the **same work as the host adapter** the roadmap needs anyway, so + nothing built is wasted. + +If collisions come back at 3, the vision's own fallback is the product — a +`/continue` that fires from a hook, a week of work — and P2 through P7 were +correctly never started. If they come back at 15, you have the number *and* the +adapter, and P1's validation gate is half-built. + +**Second, re-key gate's observer by subject.** Cheap, and it fixes the stalled +metric, the ghost inbox, and the "two merges with no decision" hole at once. It +is also the smallest possible demonstration of the ownership thesis against a +real tool. + +**Hold `contracts/mandate` (`p1-t5`).** It is delegation machinery for P4+, and +building it before the gate resolves is exactly the premature work the gate +exists to prevent. + +--- + +## 6. What to stop doing + +- **Stop treating the blocked/not-yet split as the highest-value change.** It + was ranked there on the 72%. At 27.6%, and with a separate analysis rejecting + it because its backstops derive from prior artifacts for the same subject — + and CI mints state into a `mktemp -d` deleted on exit, so they are + structurally unreachable there — the ranking does not hold. +- **Stop designing for 75 roles.** The measured ceiling is ~25 at the + half-shift rate, and a true 8-hour agent shift does not exist anywhere in the + data: the longest session-day ever recorded is 268 active minutes, median 14. +- **Stop quoting numbers that live only in prose.** Every one that was + re-derived came back different. + +--- + +## 7. The pattern worth keeping + +Exhaustive enumeration keeps being the instrument that works, and keeps being +rediscovered independently: parley's differential catching a hand-written table +under-specifying, warrant's mutation pass finding a property its suite +structurally could not observe, and this package's 86-state walk finding two +obligation-stranding sequences that 96% coverage and a 98.5% mutation score +both missed. + +The generalization is worth stating once, here, so it stops being rediscovered: + +> **For a finite state machine, enumerate it. Sampling finds what you thought +> of; enumeration finds what you did not.** Reach for Lean when a second +> implementation needs the transition table, and for a model checker when there +> are genuine interleavings — not before. + +The corollary is the same lesson the numbers taught: an instrument that runs in +CI beats an argument in a document, because the document cannot notice when it +becomes false. From cdafa50e16da2078052db1b36fc2016db7b7a5ae Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sun, 23 Aug 2026 17:16:55 -0700 Subject: [PATCH 13/24] docs(org): name one canonical doc, and state how discharge relates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five documents across two repos now describe this problem, which is the same failure the newest of them is about. This does the cheap half of fixing that: it says which one is canonical and what each of the others is for, without restructuring anything under review. vision.md is canonical and now opens with the index, the one-sentence goal every document shares, and the current status — contracts/org is on main, the §7 P0 gate has been evaluated and does not pass, so P2 through P7 do not start. spec.md is marked superseded. It keeps its review history and the §5/§6 tables contracts/org was built from, with an explicit note that where the two disagree vision.md wins — the §3.9 state machine added claim, yield and complete as structural kinds, settled abandon as a claim terminal, and dropped handoff for release-then-attach. The addition worth the most is the relationship to drive PR #46. It reads as a competing design and is not one: Baton asks who owns work and may ACT on it, and answers with a CAS that refuses the second writer, because acting is exclusive. Discharge asks what was CONCLUDED and where we disagree, and records both, because concluding is not. They are the same goal at two altitudes, and discharge is the P1 slice that ships first — it is the only way to obtain the collision count the P0 gate turns on, and that number cannot be reconstructed because holding was never recorded. where-this-stands.md folds into a §0 here once #245 is locked; leaving it separate until then so this PR's review surface does not move again. Co-Authored-By: Claude Opus 5 --- docs/features/org/spec.md | 7 +++++++ docs/features/org/vision.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/docs/features/org/spec.md b/docs/features/org/spec.md index d5cddb0f..d0a04f0d 100644 --- a/docs/features/org/spec.md +++ b/docs/features/org/spec.md @@ -1,5 +1,12 @@ # org — Technical Design Document +> **SUPERSEDED by [`vision.md`](vision.md).** Kept for its review history and +> for the §5 record table and §6 refusal codes that `contracts/org` was built +> from. Where the two disagree, `vision.md` wins — notably the §3.9 state +> machine, which added `claim` / `yield` / `complete` as structural kinds, +> settled `abandon` as a claim terminal, and dropped `handoff` in favour of +> release-then-attach. Do not start new work from this file. + **Status:** draft / proposal — NOT a build commitment. The artifact we decide from. **Owner:** @mh **Date:** 2026-08-21 · **v2** 2026-08-22 (review round 1 folded; four bakeoff kernels read and adopted) diff --git a/docs/features/org/vision.md b/docs/features/org/vision.md index 544009db..a6584be5 100644 --- a/docs/features/org/vision.md +++ b/docs/features/org/vision.md @@ -1,5 +1,38 @@ # Baton — Architecture Vision +> **This file is canonical.** One goal runs through every document listed below: +> *the next agent starts where the last one stopped, and two agents do not +> silently reach different conclusions about the same thing.* Everything else — +> roles, chains, ownership, authorization — is machinery for that. +> +> | document | what it is | status | +> |---|---|---| +> | **`vision.md`** (this file) | the architecture and the argument for it | **canonical** | +> | [`p0-findings.md`](p0-findings.md) | every number, and how it was measured | evidence; cite it rather than restating figures | +> | [`where-this-stands.md`](where-this-stands.md) | proven vs claimed, and the next step | synthesis; **folds into §0 here once #245 is locked** | +> | [`spec.md`](spec.md) | the earlier TDD draft | **superseded** by this file; kept for its review history | +> | `drive:docs/features/discharge/spec.md` | conclusions as owned data, via two hooks | **the P1 slice that ships first** — see below | +> +> **Status, 2026-08-23.** `contracts/org` is on `main`: the spine, the contract +> law, and the ownership fold, with all 86 reachable states walked. The §7 P0 +> gate has been evaluated and **does not currently pass** — false positives are +> 27.6% against a <20% bar, a role-day is affordable to ~25 roles against a +> target of 75, and the collision count is unmeasured. Do not start P2 through +> P7. +> +> **Next step: `drive` PR #46 (discharge), phases d0 and d1.** It is the only +> way to obtain the collision count, which is unmeasurable retrospectively +> because holding was never recorded. It is also the same two hooks (§3.3) this +> design needs regardless, so nothing built there is wasted either way. +> +> **How discharge and this design relate.** They want opposite things from the +> same situation, and that is the point. Baton asks *who owns this work and may +> act on it* and answers with a compare-and-swap that **refuses** the second +> writer — acting is exclusive. Discharge asks *what was concluded, and where do +> we disagree* and **records both**, rendering the disagreement — concluding is +> not exclusive. Ownership without conclusions is a lock with nothing behind it; +> conclusions without ownership is a wiki. + ## 1. The system in one paragraph Baton is a control plane that gives every durable unit of organizational work an owner that outlives the process doing it, and gives every external effect a recorded intent that outlives the process that issued it. A **role** — lead, project lead, IC, maintainer — is a row of data with an append-only journal; an **incarnation** is a disposable session on some host that reads the journal's tip, takes the tip, acts, and writes back. To act you must append; to append you must present the tip you read. That one rule produces continuity (starting is folding), mutual exclusion (two incarnations cannot both hold the tip), handover (a supervisor appends a takeover), and revocation (credentials are minted against a chain position, so displacing an incarnation kills its authority everywhere with no message sent). Underneath it, a single broker is the only path from an agent to the outside world: it holds the credentials the agent never sees, records the *intent* of every effect before the wire and the *outcome* after, and refuses any effect that has not declared how a replacement resolves it if the issuer dies mid-flight. Everything else — the fleet, the boards, the protocol checker, the merge gate — is a client, a reader, or an adapter. The system is for one thing: **you can kill any agent at any moment, and nothing is lost and nothing is duplicated.** From c1a4a99cca03d5d038accf9cceeb1f1b5f97a093 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sun, 23 Aug 2026 17:25:38 -0700 Subject: [PATCH 14/24] =?UTF-8?q?docs(org):=20state=20the=20actual=20targe?= =?UTF-8?q?t=20=E2=80=94=20a=20few=20role=20leads,=20not=20a=20fleet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The doc argued for an architecture without ever saying plainly what the operator wants from it, so the scale story drifted to 75 roles and every gate got calibrated against that number. The real target: a small number of role leads, each owning an area of the operator's own work, each trusted with a set of dossier tasks, each reporting back. lead:agentic-development over the portfolio's tooling, lead:rooms over rooms. Two to five, not seventy-five. Stating it changes what is left to build, and the change is mostly subtraction. A lead is real when it outlives a session (contracts/org, on main), holds visible work (assign over dossier: URIs, contract done and binding not built), hands its judgment to the next incarnation (discharge, drive #46), and can be trusted unattended (charter-pinned effect classes, designed and not yet enforced). Three of the four already exist or are in flight. It also defuses the P0 gate rather than failing it. Collisions >= 10 and a role-day affordable at 75 are the right questions for a product sold to strangers and the wrong ones for tooling whose only user is its builder. At five leads the cost arithmetic is nowhere near binding, and the collision count is something to watch rather than a threshold to clear before starting. The rule that replaces the gate: ship the increment that helps you build the next one, and measure because the numbers keep turning out different — not to earn permission. Co-Authored-By: Claude Opus 5 --- docs/features/org/vision.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/features/org/vision.md b/docs/features/org/vision.md index a6584be5..708dcc4e 100644 --- a/docs/features/org/vision.md +++ b/docs/features/org/vision.md @@ -32,6 +32,40 @@ > we disagree* and **records both**, rendering the disagreement — concluding is > not exclusive. Ownership without conclusions is a lock with nothing behind it; > conclusions without ownership is a wiki. +> +> --- +> +> ### The target, stated by the operator +> +> Not a 75-role fleet. **A small number of role leads, each owning an area of +> the operator's own work, each trusted with a set of dossier tasks, each +> reporting back.** `lead:agentic-development` manages the portfolio's own +> tooling; `lead:rooms` manages rooms. Two to five of them, not seventy-five. +> +> A role lead is real when four things are true, and each maps to something +> that already exists or is in flight: +> +> | what makes it a *lead* | mechanism | status | +> |---|---|---| +> | it outlives any session | `contracts/org` chain + fold | **on `main`** | +> | it holds work, and you can see what | `assign` over `dossier:proj/phase/task` URIs | contract done; binding not built | +> | a fresh session inherits its judgment | discharge — `SessionStart` read, `Stop` write | `drive` PR #46 | +> | you can trust it unattended | charter pins effect classes; every act is in the chain | designed, not enforced | +> +> **What a lead injects at session start** is the whole product in one screen: +> its charter, the tasks it holds and their state, what the last incarnation +> concluded, what it left open, and when it is next due. A session that starts +> with that is productive in its first sentence rather than its fortieth. +> +> **Why this reframes the §7 P0 gate.** That gate asks whether to invest in an +> enterprise substrate — collisions ≥ 10, a role-day affordable at 75. Those are +> the right questions for a product sold to strangers and the wrong ones for +> tooling whose only user is its builder. At two to five leads the role-day +> arithmetic is not close to binding (see [`p0-findings.md`](p0-findings.md) §4: +> ~25 roles is affordable, and this needs five), and the collision count is +> something to *watch* rather than a threshold to clear before starting. **Ship +> the increment that helps you build the next one; measure because the numbers +> keep turning out different, not to earn permission.** ## 1. The system in one paragraph From e193c7dbb6c75ea4e781cfd7b57c41df6c1ce360 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sun, 23 Aug 2026 21:29:37 -0700 Subject: [PATCH 15/24] =?UTF-8?q?docs(org):=20decide=20the=20substrate=20?= =?UTF-8?q?=E2=80=94=20one=20model,=20on=20a=20real=20database?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "No new store; dossier holds it" and "back this with a real database" read as opposites and are not. The first is an argument about schema duplication — do not invent a fifth model of tasks and conclusions beside dossier's — and it never examined the substrate, which was inherited rather than chosen. The second is about the substrate. Both hold: one model, on a database, absorbing dossier rather than sitting beside it. Four findings decide it, and the fourth is the one that matters: contracts/org is on main with 86 states walked, and a compare-and-swap has nowhere to live on markdown files. Also recorded: `task_list` returns a task's `body` and omits `## Notes` entirely, so the channel the Stop hook writes into has no reader except grep — verified against the org corpus. SQLite now, schema in the Postgres-compatible subset, behind one store interface. Not Neon today: it needs an OAuth step to start, and a store that needs a procedural step before it works is the sixth dead store. cortex unpauses on its own stated revival condition rather than a reinterpretation of it. Co-Authored-By: Claude Opus 5 --- docs/features/org/store-decision.md | 146 ++++++++++++++++++++++++++++ docs/features/org/vision.md | 1 + 2 files changed, 147 insertions(+) create mode 100644 docs/features/org/store-decision.md diff --git a/docs/features/org/store-decision.md b/docs/features/org/store-decision.md new file mode 100644 index 00000000..6af58680 --- /dev/null +++ b/docs/features/org/store-decision.md @@ -0,0 +1,146 @@ +# The store decision + +> Status: decided on engineering grounds, 2026-08-23. Reverses nothing in +> [`vision.md`](vision.md); narrows `drive:docs/features/discharge/spec.md` §4.2. +> Read this before building anything that writes. + +## The question + +Two statements are on the table and they look contradictory. + +- Discharge spec §4.2: **"No new store; dossier holds it."** Four dead stores + are four data models nobody reconciles; dossier is the one still alive. +- The operator, 2026-08-23: **back this with a real database, not files**, and + add **watcher processes**. + +## The answer + +They are not contradictory, because they answer different questions. + +§4.2's argument, stated precisely, is about **schema duplication**: do not +invent a fifth model of projects/phases/tasks/conclusions beside dossier's, +because nobody reconciles five models. That argument is correct and this +decision does not weaken it. But it is an argument about the *model*, and it +never examined the *substrate* — markdown-on-disk was inherited, not chosen. + +The operator's direction is about the substrate. + +Both hold, and together they say one thing: + +> **One model. On a real database. The database absorbs dossier's model rather +> than sitting beside it.** + +A sixth store would be a sixth *model*. Giving the surviving model a spine that +can answer questions is the opposite move. + +## Why the current substrate cannot carry what is already built + +Four findings, each checkable. + +**1. The corpus is re-parsed per call.** dossier's own README: the corpus is +"plain markdown you can grep and edit by hand; the server re-reads it on every +call." 5.4 MB at `~/dev/dossier-state`, today. + +**2. Notes are write-only.** `task_update` appends to a task's `## Notes` +section. `task_list` returns the task's `body` and **omits `## Notes` +entirely** — verified 2026-08-23 by diffing `dossier task_list --project org` +against the on-disk task file: known note text present on disk, absent from +every field the CLI returns. There is no CLI or MCP path that reads back what +the write verb writes. + +This is not a small gap. Discharge §4.1 says *the reader is the next agent, and +the read ships first.* The channel the Stop hook writes into has no reader +except `grep` over a path in someone's home directory — which is precisely what +`scripts/discharge-sweep.sh` had to do, and why its corpus access is isolated +in one function. + +**3. It cannot be watched.** A watcher's whole question is *what changed since +X*. A markdown tree answers that with a filesystem walk plus a re-parse. This +is why watcher processes were never on the table before — not because nobody +wanted them. + +**4. There is nowhere correct to put the chain.** This is the one that decides +it. `vision.md` §2 T1 says ownership and continuity are the same fact and *the +fact is a compare-and-swap*. A CAS is an atomic conditional write. Markdown +files do not have one. `contracts/org` is on `main` — spine, contract law, +ownership fold, all 86 reachable states walked — and it has no store that can +hold it without reintroducing by convention the exact race the fold refuses. + +**The thing already built has no substrate that can hold it correctly.** That +is the decision, and it was already true before the operator asked. + +## The engine: SQLite now, Postgres-compatible schema, behind one interface + +Not Neon/Postgres today: + +- The Neon MCP is configured and **unauthorized**; adopting it means starting + with an OAuth step. §4.3's finding is that procedural steps are exactly what + gets skipped — and a store that needs one before it works is the sixth dead + store with extra latency. +- It puts a network round-trip inside the SessionStart injection path, which + GATE A caps at **<400 ms p95**. +- It bills money to serve one Mac. +- Nothing in the design has a second writer host yet. + +SQLite is not the timid option here. The toy properties are *markdown's*, and +SQLite answers each one directly: transactions, foreign keys, `UNIQUE` +constraints (which is what makes the CAS a CAS), indexes, WAL concurrency, and +a `WHERE updated_at > ?` that turns a watcher into a goroutine instead of a +directory walk. + +**When Postgres.** The first time a writer that is not this Mac needs to append +— a cloud `drive` session discharging, or a second machine. That is a driver +swap and not a rewrite **only if the schema stays inside the common subset from +the first migration**, so it does: no SQLite-only types, no `AUTOINCREMENT`, no +Postgres-only `RETURNING` in the store interface. + +## cortex unpauses, on its own stated terms + +`prj_01KRT2XJ1P3SRSKQJSNV7WTY1Z` — *"cortex — agentic context engine +(paused)"* — was paused 2026-05-17 with an explicit revival condition: + +> revisit when there's a real consumer that can't be served by dossier verbs + +> an LLM doing its own retrieval. + +The role lead's SessionStart injection is that consumer, and it fails on +exactly the two stated terms: dossier verbs **cannot** serve it (finding 2 — +the conclusions are unreadable through the API), and an LLM doing its own +retrieval **cannot** meet the 400 ms budget. The condition is met as written, +not reinterpreted. + +## What this does not authorize + +- **Not a rewrite of dossier.** Its model, its MCP surface, and its verb names + survive. The substrate underneath them changes, and one migration reads the + markdown tree into rows. +- **Not a new repo yet.** Where the store lives (inside dossier, inside drive, + or as cortex revived) is a separate call that should follow the first + schema, not precede it. +- **Not P2–P7 of `vision.md`.** The §7 P0 gate is unchanged by this. This + decision is about where the already-built P1 lives. +- **Not the watchers yet.** Watchers are the *reason* for the substrate, but + the first one should be written against a schema that exists. + +## How this could be wrong + +- **If notes turn out to be readable** through some verb this survey missed, + finding 2 collapses and the pressure drops from "cannot" to "slow". The + substrate argument then rests on findings 3 and 4 alone — still sufficient + for the chain, no longer urgent for discharge. +- **If the migration is where corpora go to die**, the honest evidence is that + five stores already died and none of them died of a bad *engine*. A migration + that loses the operator's 5.4 MB of project memory would be the first new + failure mode this decision introduces. It must be reversible: the markdown + tree stays on disk, read-only, until a full round-trip is proven. +- **If SQLite's single writer binds sooner than expected** — several concurrent + sessions all discharging — WAL handles it to a point and then does not. The + measurement to watch is write contention, not read latency. + +## Sequencing + +1. Schema first, in the common subset, with the markdown tree as the source of + truth for a reversible one-way migration. +2. `discharge-sweep`'s `discharge_recorded()` is the canary: it is the one + function coupled to the substrate, and swapping it to a query is how the + first read gets proven end-to-end. +3. One watcher, against a schema that exists — not before. diff --git a/docs/features/org/vision.md b/docs/features/org/vision.md index 708dcc4e..d0704057 100644 --- a/docs/features/org/vision.md +++ b/docs/features/org/vision.md @@ -9,6 +9,7 @@ > |---|---|---| > | **`vision.md`** (this file) | the architecture and the argument for it | **canonical** | > | [`p0-findings.md`](p0-findings.md) | every number, and how it was measured | evidence; cite it rather than restating figures | +> | [`store-decision.md`](store-decision.md) | which substrate holds the chain, and why | **decided 2026-08-23**; read before building anything that writes | > | [`where-this-stands.md`](where-this-stands.md) | proven vs claimed, and the next step | synthesis; **folds into §0 here once #245 is locked** | > | [`spec.md`](spec.md) | the earlier TDD draft | **superseded** by this file; kept for its review history | > | `drive:docs/features/discharge/spec.md` | conclusions as owned data, via two hooks | **the P1 slice that ships first** — see below | From bbf7377120b36509ff0cc4854688c8183f8771d3 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sun, 23 Aug 2026 21:41:00 -0700 Subject: [PATCH 16/24] =?UTF-8?q?fix(org):=20the=20MCP=20does=20read=20not?= =?UTF-8?q?es=20=E2=80=94=20narrow=20finding=202=20to=20the=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Said there was no CLI or MCP path reading back what task_update writes. Wrong: mcp__dossier__task_get returns a structured notes array. The true shape is narrower and still carries the argument — no CLI verb reads a note, and task_get takes one id per call and walks the whole corpus to find it. The read exists; it costs O(corpus) and only an LLM can make it. Co-Authored-By: Claude Opus 5 --- docs/features/org/store-decision.md | 35 +++++++++++++++++++---------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/features/org/store-decision.md b/docs/features/org/store-decision.md index 6af58680..3d942fee 100644 --- a/docs/features/org/store-decision.md +++ b/docs/features/org/store-decision.md @@ -41,18 +41,29 @@ Four findings, each checkable. "plain markdown you can grep and edit by hand; the server re-reads it on every call." 5.4 MB at `~/dev/dossier-state`, today. -**2. Notes are write-only.** `task_update` appends to a task's `## Notes` -section. `task_list` returns the task's `body` and **omits `## Notes` -entirely** — verified 2026-08-23 by diffing `dossier task_list --project org` -against the on-disk task file: known note text present on disk, absent from -every field the CLI returns. There is no CLI or MCP path that reads back what -the write verb writes. - -This is not a small gap. Discharge §4.1 says *the reader is the next agent, and -the read ships first.* The channel the Stop hook writes into has no reader -except `grep` over a path in someone's home directory — which is precisely what -`scripts/discharge-sweep.sh` had to do, and why its corpus access is isolated -in one function. +**2. Notes are readable only one task at a time, and only by an LLM.** +`task_update` appends to a task's `## Notes` section. Three separate facts, +each verified 2026-08-23: + +- `task_list` returns a task's `body` and **omits `## Notes` entirely** — + diffed `dossier task_list --project org` against the on-disk task file: known + note text present on disk, absent from every field returned. +- The **CLI** has no verb that returns notes at all. `dossier --help` lists + `serve`, `task_complete`, `task_update`, `artifact_link`, `task_list`, + `artifact_list`. Nothing there reads a note. +- The **MCP** does have one: `task_get` returns a structured `notes` array + (`actor`, `body`, `posted_at`). It takes a single id and, by its own + description, *"walks the whole corpus"* to find it. + +So the read path exists, but only for an LLM holding an MCP connection, one +task per call, at O(corpus) each. The Stop hook is bash and the sweep is bash; +neither can reach it. That is why `scripts/discharge-sweep.sh` greps the corpus +markdown, and why that access is isolated in one function. + +Discharge §4.1 says *the reader is the next agent, and the read ships first.* +The next agent can in fact read — one task at a time, by full corpus walk. It +is the writing tier that is blind, and the cost of a read scales with the +corpus rather than with the answer. **3. It cannot be watched.** A watcher's whole question is *what changed since X*. A markdown tree answers that with a filesystem walk plus a re-parse. This From 83d18b9a26c4d327ba47d72ca95b323addf5c273 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Sun, 23 Aug 2026 22:16:00 -0700 Subject: [PATCH 17/24] docs(org): a handoff brief to reconcile ten ownership streams into one loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Six weeks produced ten-plus independent attempts at some part of agent ownership — contracts/org, gate, parley, warrant, mandate, obligation, proofline, branchroom, bailiff, switchboard — each built in its own repo, several measured, none sharing a vocabulary with the others. The result is three capability models, and a portfolio with three capability models has zero. The brief is a prompt, not a design. It hands a fresh analyst the corpus, the corrections that have already caused wrong conclusions, and the instruments that can settle parts of this by measurement rather than argument. Four corrections are load-bearing enough to state up front: ~/dev/gate is archived and only its state/ is live (the code is workbench/cmd/gate); switchboard is agents-as-processes-gleam and its residency result is measured, not open; hack-branchroom is the ANCESTOR of contracts/org rather than a rival; braid and reprise are dead on their own kill conditions and must not come back. New axis, and the one closest to buildable: staying alive and rebounding with targeted context. A lead reduced to mechanism is a context bundle plus a permission set plus a chain position. The harness already provides every injection point that needs — CLAUDE.md layering, SessionStart, layered permissions — and settings.json today wires only PreToolUse and PostToolUse. No SessionStart, no Stop, nothing bound to a role. That gap is mechanical, not conceptual. Unattended operation is framed as the horizon, not the next step, so the answer optimises for moves worth making either way. Co-Authored-By: Claude Opus 5 --- docs/features/org/reconciliation-brief.md | 259 ++++++++++++++++++++++ docs/features/org/vision.md | 1 + 2 files changed, 260 insertions(+) create mode 100644 docs/features/org/reconciliation-brief.md diff --git a/docs/features/org/reconciliation-brief.md b/docs/features/org/reconciliation-brief.md new file mode 100644 index 00000000..d4553424 --- /dev/null +++ b/docs/features/org/reconciliation-brief.md @@ -0,0 +1,259 @@ +# Reconciliation brief — the org loop + +> **This document is a handoff prompt.** It is written to be handed to a fresh +> agent with no prior context. Read it top to bottom, then produce the output in +> the last section. +> +> Audience: an analyst with read access to `~/dev`. Modify nothing. +> +> Written 2026-08-24. Every fact below was verified on that date; every one is +> re-checkable, and you should re-check the load-bearing ones. + +--- + +## 1. The situation + +Over roughly six weeks, this portfolio produced **ten or more independent +attempts** at some part of *agent ownership* — who owns a unit of work, what +authority they hold, what evidence supports an act, how the next session inherits +the last one's conclusions. Each was built in its own repository, most were +built well, several were measured, and **none shares a vocabulary with the +others**. + +The result is a portfolio with three capability models, four evidence journals, +and two reducers that are secretly the same reducer. A portfolio with three +capability models has zero. + +Your job is **not** to design an eleventh thing. It is to make sense of what +exists, name it once, say what should be deleted, and give a build order. + +## 2. The vision being reconciled toward: an org loop + +One sentence: **the next agent starts where the last one stopped, and two agents +do not silently reach different conclusions about the same thing.** + +Concretely, the operator wants **two to five role leads** — not a seventy-five +agent fleet. `lead:agentic-development` owns the portfolio's own tooling; +`lead:rooms` owns rooms. Each one: + +1. **owns an area** and outlives any session working in it, +2. **holds work** you can enumerate, +3. **stays alive** — a fresh session inherits its judgment rather than + rediscovering it, +4. **rebounds with targeted context** — a session opening under a lead boots + with that lead's charter, its held tasks, what the last incarnation + concluded, what it left open, and an authority scoped to its area, +5. **reports back** without being asked. + +Item 4 is the one that has had the least design attention and is closest to +being mechanically possible today. See §5. + +## 3. Ground truth corrections — earlier analyses got these wrong + +Verify these first. Each has already caused a wrong conclusion. + +- **`~/dev/gate` is ARCHIVED.** Its last commit is literally + `docs: archive banner — gate migrated into workbench as cmd/gate` + (2026-07-17). The live code is `~/dev/workbench/cmd/gate` (99 Go files, last + touched 2026-08-23) plus `~/dev/workbench/contracts/gateauthorization`. The + **only** live thing under `~/dev/gate` is `state/` — the `GATE_STATE` + hash-chained journal at `~/dev/gate/state/log.jsonl`, ~5000 records. Do not + read `~/dev/gate/{cmd,internal}` as current. +- **switchboard is `~/dev/agents-as-processes-gleam`.** Its Gate C2 result: + process residency does **not** buy crash recovery — a stateless + reload-from-disk baseline recovers identically, in fewer lines. What residency + uniquely buys is **serialized ownership**: under two concurrent turns the + owned form rejects the second, while the unowned baseline lets both write and + silently corrupts its journal. This is measured. Do not re-derive it. +- **`hack-branchroom` is the ANCESTOR of `contracts/org`, not a rival.** + `contracts/org` was ported from it. The dossier task `p1-t3-reduce` in project + `org` records the three corrections applied during the port — read that note + before treating them as duplicates. +- **`braid` and `reprise` are dead.** Both scored 94/100 and won the 2026-08-10 + Haskell bakeoff rounds; both were then killed on their own kill conditions. + See `~/dev/bakeoff/haskell-08-10/scorecard.md` and + `~/dev/bakeoff/haskell-dsl-08-10/scorecard.md`. Do not revive them. Do read + why they died — under-500-lines survives here and they did not. +- **The docs overclaim.** A prior audit found a vision doc's Evidence section + contradicted by three claims in its own source. Where a document and the code + disagree, **the code wins, and you say so explicitly.** + +## 4. Use the instruments before you reason + +Measurement beats argument, and instruments already exist. + +**`~/dev/warrant/cmd/gate-observe`** replays a foreign evidence journal against a +pipeline definition written *afterwards*, requiring no adoption by the observed +system. It is the cheapest way to find out whether a model can account for what a +real pipeline does. + +```sh +go run ./cmd/gate-observe ~/dev/gate/state/log.jsonl +``` + +What it already found, against the operator's own merge gate (415 runs, 5010 +records — `~/dev/warrant/docs/gate-history.md`): + +- **`escalate` is 57% of all verdicts** — 1218 of 2145, more than `pass` (809) + and `block` (118) combined. warrant's model had only `supported` and + `refuted`, so the single most common thing a real check says was + unrepresentable. A third verdict (`insufficient`) was added as a result. +- A separate session reached the same conclusion independently, from the model + side rather than from real data. Two implementations converging on a missing + value is the strongest signal in this corpus. +- One verifier's refusal path **has never executed in 415 runs**. + +`~/dev/warrant/pipelines/` holds five declared pipelines: `gate`, `gauntlet`, +`maintenance`, `selfcheck`, `shipping`. + +**Research already done — do not repeat it:** + +| where | what it is | +|---|---| +| `~/dev/agents-as-processes-gleam/docs/evidence/` | 8 pre-registered docs, Gate A → C2, on session residency and ownership | +| `~/dev/agents-as-processes-gleam/docs/research/probe-2-passivation.md` | virtual actors / passivation | +| `~/dev/workbench-laws-lean/` | independent Lean 4 model of a narrow slice of gate's verdict laws, pinned to workbench commit `6eee6aa`. Note its own disclaimer: it proves laws of the *model*, and nothing consumes it to permit a merge | +| `~/dev/bakeoff/agent-substrates-08-21/` | `scorecard.md` and `org-compute-synthesis.md` for the round that produced mandate / obligation / proofline / branchroom | + +Three different epistemologies — empirical replay, pre-registered experiment, +formal proof — are already aimed at this question. Part of reconciliation is +saying which one settled what. + +## 5. The re-entry axis: staying alive and rebounding with targeted context + +This is the least-designed part of the vision and the closest to buildable, so +it gets its own section. + +A role lead, reduced to mechanism, is three things: + +| what a lead needs | where it would live | wired today? | +|---|---|---| +| **context** — charter, held work, last conclusions | `CLAUDE.md` layering + a `SessionStart` hook injection | **no** | +| **memory** — what previous incarnations concluded | `contracts/org` chain + discharge records | chain on `main`; discharge unwired | +| **authority** — what it may do unattended | `settings.json` `permissions` layers (`allow`/`ask`/`deny`) + a gate grant or bailiff Warrant | **no role binding** | + +Verified 2026-08-24 in `~/.claude/settings.json`: the `hooks` object contains +**only `PreToolUse` and `PostToolUse`**. There is **no `SessionStart` hook and +no `Stop` hook wired at all**. The Stop hook built in `hooks` PR #42 exists and +is not installed. + +So: the harness already provides every injection point this vision needs — +per-directory `CLAUDE.md` layering, `SessionStart` for computed context, +layered `permissions` for scoped authority — and **not one of them is bound to a +role.** That gap is mechanical, not conceptual, and it is worth assessing +whether "a role lead is a context bundle + a permission set + a chain position" +is the whole of it or a dangerous simplification. + +Relevant existing work: the `/floor` skill renders the *effective* merged +permission rulebook across global, project, and local settings, including how +hooks and wildcards interact. If per-session authority is going to be expressed +in settings layers, `/floor` is the tool that says what a layer actually did. + +## 6. The corpus + +### Ownership / authority / evidence primitives + +| repo / package | the primitive it invented | last touched | +|---|---|---| +| `workbench/contracts/org` | role chain, `Reduce`/`Admissible`, ownership fold; 86 reachable states walked by exhaustive BFS | on `main` | +| `workbench/cmd/gate` + `contracts/gateauthorization` | merge authorization at an exact head; operator-minted grants, tier ceilings, TTLs; `gate next -json` is its projection | 08-23 | +| `~/dev/parley` | protocol kernel — Haskell compiles the protocol, Gleam enforces it, Lean proves the two agree | 08-23 | +| `~/dev/warrant` | `Reduce` over an append-only journal; refuses to advance a run without evidence bound to that run's current subject | 08-23 | +| `~/dev/hack-mandate` | signed delegation pinned to one exact task revision / repo / base / head / diff; a child mandate may only shorten | 08-21 | +| `~/dev/hack-obligation` | deterministic evidence–work frontier over frozen verification contracts | 08-21 | +| `~/dev/hack-proofline` | read-only lineage index: which exact identity edge made an old claim | 08-21 | +| `~/dev/hack-branchroom` | rerun as controlled causal fork; epochs, one pure reducer. **Ancestor of `contracts/org`** | 08-21 | +| `~/dev/bailiff` | "the chain as an enforcing bus." A `Warrant` is a capability an agent **holds**: scoped to one target and one function, capped at N uses, wall-clock expiry, operator-revocable | 08-16 | +| `~/dev/agents-as-processes-gleam` | switchboard — residency buys serialized ownership | 08-10 | +| `~/dev/huddle` | per-seat keys as agent identity in a shared room | 07-27 | + +### The ownership lifecycle, as currently scattered + +- **claim** — `dossier` `task_claim`; the `/claim` and `/release` skills over a + session-claims log; `drive attach` plus + `drive/internal/verbs/{authority,write_auth}.go`; + `drive/internal/reducer/liveness.go` (mtime-quiet past N ⇒ stale). +- **conclude** — `hooks` PR #42: a `Stop` hook appending what a session did to + the dossier tasks it touched, keyed off the fact that a session which called + `task_update` named the task in the call. PR #43: a sweep counting sessions + that owed a discharge and never paid — **measured 18 sessions / 40 tasks / 0 + recorded over 14 days.** +- **notice** — `drive` PR #47: a resident watcher tier. Findings in SQLite, one + writer, deliberately unable to act. +- **design docs** — `docs/features/org/{vision.md,store-decision.md,p0-findings.md}` + in this directory; `drive:docs/features/discharge/spec.md` (PR #46). + +### A note on read paths + +`dossier` `task_list` returns a task's `body` and **omits its notes section**; +no CLI verb reads a note. The MCP's `task_get` does return a structured `notes` +array, but one id per call, walking the whole corpus. So the tier that *writes* +conclusions (bash hooks, the sweep) cannot read them back. Verified 2026-08-24. + +## 7. What to work out + +1. **One vocabulary.** For each genuinely distinct primitive: one canonical + name, which repos implement it, and whether they are aliases or rivals. Be + specific about **`bailiff`'s Warrant vs gate's grant vs `hack-mandate`'s + mandate** — three capability models by three different hands. Say which one + should win and why. + +2. **The lifecycle trace.** Follow one unit of work: claimed → acted on → + concluded → recorded → inherited. At each hop mark *implemented*, *designed + only*, or *missing*. The claim/discharge path is the live edge: assess + whether claiming at session start and concluding at session end actually + compose, and what breaks when a session dies between them. Note that #42 + deliberately claims at the **end**, on the argument that a start-claim is a + prediction and agents skip predictions — assess whether that holds. + +3. **The re-entry mechanism (§5).** Is "a lead = context bundle + permission set + + chain position" sufficient? What does a `SessionStart` injection have to + contain to be worth its cost, given that injected bytes enlarge every cached + turn thereafter and not just the first? Where should a role's authority live + so that `/floor` can still tell the truth about it? + +4. **What to stop maintaining.** Reconciliation means subtraction. Which of + these should be archived, folded into another, or deleted — and what is the + argument in each case? + +5. **Where authority actually stands.** Does `contracts/org` record the + *authority* for an act, or only that the act happened? Is there an + implemented effect-class / charter concept, or only a described one? Could a + role lead hold a gate grant or a bailiff Warrant, and what breaks if a + non-human mints one? `~/.claude/CLAUDE.md` pins minting as operator-only — + establish whether that is a design necessity or a current convention. + +## 8. Framing + +**Fully unattended operation is the horizon, not the next step.** Assume +substantial engineering sits in between and do not optimise the answer toward +it. What is needed is a reconciled picture plus the next few moves that are +worth making regardless of how the autonomy question resolves. + +## 9. Constraints + +- **Cite `file:line`.** A claim without a citation is noise. +- **Mark every finding `measured` or `reasoned`.** +- **Distinguish IS from SAYS-IT-IS.** Code beats docs; say so when they differ. +- **No new store, no new repo, no rewrite.** Five stores have already died here, + each surviving long enough to look like it might still work. +- Measured failure modes in this portfolio — scope errors 43%, stale claims 23%, + confabulation 8%. Re-verify anything that sounds already settled, **including + the corrections in §3.** +- Prefer the cheap local instrument over reasoning where one exists (§4). +- Read-only. Modify nothing. + +## 10. Output + +At most 2000 words. + +- **(a) Vocabulary table** — canonical primitive · implementations · alias or + rival · which wins. +- **(b) Lifecycle trace** — per hop: implemented / designed / missing. +- **(c) Re-entry verdict** — what a `SessionStart` injection must contain, where + a role's authority lives, and whether the three-part reduction in §5 holds. +- **(d) Subtraction list** — what to archive or fold, with the argument. +- **(e) The next three build steps, in order.** Each with: what it makes true, + why it precedes the others, and its kill condition. Grounded in what exists — + no greenfield. +- **(f) The single riskiest assumption** in (e). diff --git a/docs/features/org/vision.md b/docs/features/org/vision.md index d0704057..8666098a 100644 --- a/docs/features/org/vision.md +++ b/docs/features/org/vision.md @@ -10,6 +10,7 @@ > | **`vision.md`** (this file) | the architecture and the argument for it | **canonical** | > | [`p0-findings.md`](p0-findings.md) | every number, and how it was measured | evidence; cite it rather than restating figures | > | [`store-decision.md`](store-decision.md) | which substrate holds the chain, and why | **decided 2026-08-23**; read before building anything that writes | +> | [`reconciliation-brief.md`](reconciliation-brief.md) | handoff prompt: fold the ten independent ownership streams into one org loop | **open question**; hand to a fresh analyst, output is a vocabulary + build order | > | [`where-this-stands.md`](where-this-stands.md) | proven vs claimed, and the next step | synthesis; **folds into §0 here once #245 is locked** | > | [`spec.md`](spec.md) | the earlier TDD draft | **superseded** by this file; kept for its review history | > | `drive:docs/features/discharge/spec.md` | conclusions as owned data, via two hooks | **the P1 slice that ships first** — see below | From b9e6079427c15169fb54c5d8aaf4b2fb85c79fd5 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Mon, 24 Aug 2026 06:01:54 -0700 Subject: [PATCH 18/24] docs(org): correct the refuted read-path claim; stamp what is now built MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The brief's §6 note said dossier task_list omits notes and no CLI verb reads one. Re-verified against the installed binary and source: refuted — task_list returns structured notes, so the write tier can read conclusions back. The correction names the two arguments that leaned on the false premise (hooks #43's substrate-coupling rationale, store-decision finding 2) rather than silently rewriting them. where-this-stands gains a build update: the re-entry slice is no longer a claim — cmd/org and cmd/org-mcp shipped CI-green in PRs #262/#263, with the deliberate deviations recorded where they were made. Co-Authored-By: Claude Fable 5 --- docs/features/org/reconciliation-brief.md | 14 ++++++++++---- docs/features/org/where-this-stands.md | 13 +++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/docs/features/org/reconciliation-brief.md b/docs/features/org/reconciliation-brief.md index d4553424..e375160b 100644 --- a/docs/features/org/reconciliation-brief.md +++ b/docs/features/org/reconciliation-brief.md @@ -185,10 +185,16 @@ in settings layers, `/floor` is the tool that says what a layer actually did. ### A note on read paths -`dossier` `task_list` returns a task's `body` and **omits its notes section**; -no CLI verb reads a note. The MCP's `task_get` does return a structured `notes` -array, but one id per call, walking the whole corpus. So the tier that *writes* -conclusions (bash hooks, the sweep) cannot read them back. Verified 2026-08-24. +**CORRECTED 2026-08-24 (second verification):** an earlier draft of this note +claimed `task_list` omits notes and no CLI verb reads one. That is **refuted** +against both the installed binary and source: `src/domain.rs:184` serializes +`notes` (skipped only when empty), `src/server/mod.rs:489-493` clears +body+notes only under `bodies:false`, and the CLI `task_list` path never clears +them — verified live with structured notes in the output. So the write tier +(bash hooks, the sweep) CAN read conclusions back via CLI `task_list`. Any +argument resting on the "unreadable notes" premise — including hooks PR #43's +"one substrate coupling" rationale and `store-decision.md` finding 2 — needs +re-examination. `task_get` remains one-id-per-call; that half stood. ## 7. What to work out diff --git a/docs/features/org/where-this-stands.md b/docs/features/org/where-this-stands.md index 78ae1e33..104c9e8f 100644 --- a/docs/features/org/where-this-stands.md +++ b/docs/features/org/where-this-stands.md @@ -1,5 +1,18 @@ # Where this stands — 2026-08-23 +> **2026-08-24 build update.** The re-entry slice this doc calls closest to +> buildable is now BUILT and live: `cmd/org` (the Baton home — chains as JSONL +> under `~/dev/org/state`, flock-serialized appends, admission delegated to +> `org.Advance`, `org boot` re-entry index, SessionStart/Stop hook scripts) in +> PR #262, and `cmd/org-mcp` + JSON receipts + `ORG_INCARNATION` identity + +> operator `context.d` boot sources in PR #263, both CI-green. Real chains +> exist for `lead:agentic-development` and `lead:rooms`; `org-mcp` is +> registered user-scope. Deliberate deviations, recorded in the PRs: no SQLite +> store yet (the chain is not the discharge store §store-decision chose SQLite +> for), write-as-holder default with `-strict` opt-out, marks-not-checkpoints +> from the Stop hook. The discharge-rate measurement against the 18/40/0 +> baseline starts when the hooks are pasted into `~/.claude/settings.json`. + A synthesis written at the point where the first real code landed and the first real numbers came in. It is deliberately separate from [`vision.md`](vision.md), which argues what to build, and [`p0-findings.md`](p0-findings.md), which From a990e37f0bdbaa0013b0217b14d0b1c310d61c03 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Mon, 24 Aug 2026 21:11:25 -0700 Subject: [PATCH 19/24] docs(org): the metaphor is not the mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reframes the reconciliation brief. An org chart is a proxy for mechanical properties — a durable assignee identity, authority scoping, somewhere a conclusion has to land to be found. Build the properties; the proxy is optional and at this scale probably costs more than it returns. The brief now works property-by-property rather than repo-by-repo, and carries a standing instruction: wherever a source document uses an org word, substitute the property and re-ask the question. A question that dissolves under substitution is a finding, because the most valuable output here may be a list of design work that stops being necessary. Three sections of vision.md are named as candidates for exactly that. §4.5 (ownership is a tree), §4.6 (three things bubble) and §10.1 (fold per node kind — called the real design work behind d3) all exist to compress a hierarchy. At two to five assignees under one operator the depth is 1, and a depth-1 tree is a list. Also corrects an overreach from the previous revision, which found that drive already implements much of the durable-assignee property and concluded drive's vocabulary should therefore be canonical. The observations hold — Scope is an external durable identity, and authority.go's re-mint/revoke/orphan-adoption reads very close to contracts/org's thesis — but the conclusion was not earned. They are demoted to evidence, with an explicit instruction to crown nothing by default, and a crux to settle: whether ScopeKindFree carries an unbounded standing scope with the same guarantees a bounded dossier phase gets. Co-Authored-By: Claude Opus 5 --- docs/features/org/reconciliation-brief.md | 314 +++++++++++++--------- docs/features/org/vision.md | 2 +- 2 files changed, 183 insertions(+), 133 deletions(-) diff --git a/docs/features/org/reconciliation-brief.md b/docs/features/org/reconciliation-brief.md index e375160b..3ad86c4e 100644 --- a/docs/features/org/reconciliation-brief.md +++ b/docs/features/org/reconciliation-brief.md @@ -1,4 +1,4 @@ -# Reconciliation brief — the org loop +# Reconciliation brief — ownership, assignment, and what the org metaphor stood in for > **This document is a handoff prompt.** It is written to be handed to a fresh > agent with no prior context. Read it top to bottom, then produce the output in @@ -7,97 +7,146 @@ > Audience: an analyst with read access to `~/dev`. Modify nothing. > > Written 2026-08-24. Every fact below was verified on that date; every one is -> re-checkable, and you should re-check the load-bearing ones. +> re-checkable, and you should re-check the load-bearing ones — including the +> ones presented as corrections. --- ## 1. The situation -Over roughly six weeks, this portfolio produced **ten or more independent +Over roughly six weeks this portfolio produced **ten or more independent attempts** at some part of *agent ownership* — who owns a unit of work, what -authority they hold, what evidence supports an act, how the next session inherits -the last one's conclusions. Each was built in its own repository, most were -built well, several were measured, and **none shares a vocabulary with the +authority they hold, what evidence supports an act, how the next session +inherits the last one's conclusions. Each was built in its own repository, most +were built well, several were measured, and **none shares a vocabulary with the others**. The result is a portfolio with three capability models, four evidence journals, -and two reducers that are secretly the same reducer. A portfolio with three +and two implementations of what is probably one idea. A portfolio with three capability models has zero. Your job is **not** to design an eleventh thing. It is to make sense of what exists, name it once, say what should be deleted, and give a build order. -## 2. The vision being reconciled toward: an org loop +## 2. The governing constraint: the metaphor is not the mechanism -One sentence: **the next agent starts where the last one stopped, and two agents -do not silently reach different conclusions about the same thing.** +Much of this work has been framed as building an **org** — roles, leads, ICs, +maintainers, reporting lines, a hierarchy. The operator's position, stated +2026-08-24, is that this framing has been doing more harm than work: -Concretely, the operator wants **two to five role leads** — not a seventy-five -agent fleet. `lead:agentic-development` owns the portfolio's own tooling; -`lead:rooms` owns rooms. Each one: +> Why does the distinction of an org even matter at all? Previously I was doing +> "driver" and "worker" agents, but it's all the same. Ownership / assignment, +> that's all that matters. Yes, org gives us hierarchy — but that is usually in +> effect to something like permissions or something else, and that can be +> represented any way. -1. **owns an area** and outlives any session working in it, -2. **holds work** you can enumerate, -3. **stays alive** — a fresh session inherits its judgment rather than - rediscovering it, -4. **rebounds with targeted context** — a session opening under a lead boots - with that lead's charter, its held tasks, what the last incarnation - concluded, what it left open, and an authority scoped to its area, -5. **reports back** without being asked. +Treat this as the brief's governing constraint, not as one opinion among many. +An org chart is a **proxy** for mechanical properties. Build the properties; the +proxy is optional and probably costs more than it returns at this scale. -Item 4 is the one that has had the least design attention and is closest to -being mechanically possible today. See §5. +**The translation you are asked to work in:** -## 3. Ground truth corrections — earlier analyses got these wrong - -Verify these first. Each has already caused a wrong conclusion. - -- **`~/dev/gate` is ARCHIVED.** Its last commit is literally - `docs: archive banner — gate migrated into workbench as cmd/gate` - (2026-07-17). The live code is `~/dev/workbench/cmd/gate` (99 Go files, last - touched 2026-08-23) plus `~/dev/workbench/contracts/gateauthorization`. The - **only** live thing under `~/dev/gate` is `state/` — the `GATE_STATE` - hash-chained journal at `~/dev/gate/state/log.jsonl`, ~5000 records. Do not - read `~/dev/gate/{cmd,internal}` as current. -- **switchboard is `~/dev/agents-as-processes-gleam`.** Its Gate C2 result: +| org-metaphor term | the property it is actually a proxy for | candidate representations already in the corpus | +|---|---|---| +| role / lead / IC | a **durable assignee identity** that outlives any one session | drive `Scope` + `attach`; `contracts/org` `RoleState`; a dossier project or phase | +| hierarchy | **authority scoping** — who may do what, where | gate grant (repo + tier + TTL); bailiff `Warrant` (target + function + use-cap + expiry); `hack-mandate` delegation; `settings.json` `allow`/`ask`/`deny` layers | +| reporting line | **where a conclusion must land** to be found by the next reader | discharge → dossier task notes; `contracts/org` chain; drive `link` | +| org chart / tree | *possibly nothing.* At 2–5 assignees under one operator the depth is 1, and a depth-1 tree is a list | — | +| headcount / fleet | concurrency limits | — | + +**Standing instruction:** wherever this brief, or any source document, uses an +org word, substitute the property and re-ask the question. **If the question +dissolves under substitution, that is a finding — report it.** The most valuable +output of this analysis may be a list of design work that stops being necessary. + +Related operator preference, already recorded: prefer stating the property +directly over reaching for borrowed industry shorthand. + +## 3. What is settled, what is only evidence + +### Settled — verify, then rely on + +- **`~/dev/gate` is ARCHIVED.** Last commit: `docs: archive banner — gate + migrated into workbench as cmd/gate` (2026-07-17). Live code is + `~/dev/workbench/cmd/gate` (99 Go files, touched 2026-08-23) plus + `~/dev/workbench/contracts/gateauthorization`. The **only** live thing under + `~/dev/gate` is `state/` — the `GATE_STATE` hash-chained journal at + `~/dev/gate/state/log.jsonl`, ~5000 records. Do not read + `~/dev/gate/{cmd,internal}` as current. +- **switchboard is `~/dev/agents-as-processes-gleam`.** Gate C2 measured that process residency does **not** buy crash recovery — a stateless reload-from-disk baseline recovers identically, in fewer lines. What residency uniquely buys is **serialized ownership**: under two concurrent turns the owned form rejects the second, while the unowned baseline lets both write and - silently corrupts its journal. This is measured. Do not re-derive it. + silently corrupts its journal. Measured. Do not re-derive. - **`hack-branchroom` is the ANCESTOR of `contracts/org`, not a rival.** `contracts/org` was ported from it. The dossier task `p1-t3-reduce` in project - `org` records the three corrections applied during the port — read that note - before treating them as duplicates. + `org` records the three corrections applied during the port — one of which was + explicitly *"no hardcoded kind vocabulary."* Read that note before treating + them as duplicates. - **`braid` and `reprise` are dead.** Both scored 94/100 and won the 2026-08-10 - Haskell bakeoff rounds; both were then killed on their own kill conditions. - See `~/dev/bakeoff/haskell-08-10/scorecard.md` and - `~/dev/bakeoff/haskell-dsl-08-10/scorecard.md`. Do not revive them. Do read - why they died — under-500-lines survives here and they did not. + Haskell bakeoff rounds; both were killed on their own kill conditions + (`~/dev/bakeoff/haskell-08-10/scorecard.md`, + `~/dev/bakeoff/haskell-dsl-08-10/scorecard.md`). Do not revive. Do read why + they died — under-500-lines survives here and they did not. - **The docs overclaim.** A prior audit found a vision doc's Evidence section contradicted by three claims in its own source. Where a document and the code disagree, **the code wins, and you say so explicitly.** +### Evidence, NOT a conclusion — this is where an earlier pass overreached + +An earlier analysis found that `drive` already implements much of the +durable-assignee property and concluded drive's vocabulary should therefore be +canonical. **That conclusion was not earned and you should not inherit it.** The +underlying observations are real and worth checking: + +- `drive/internal/ledger/event.go:36` — `Scope` is an external durable identity + (`ScopeKindDossierPhase`, `ScopeKindJiraEpic`, `ScopeKindFree`), not a + per-launch label. +- `drive/internal/verbs/authority.go:13-16` — the current holder is *"the latest + attacher on its scope. A later re-mint stops [the previous capability] being + current and its next use reads as revoked, naming the successor. This is also + orphan adoption: the new current driver inherits authority over [the scope's + workers]."* +- That reads very close to `contracts/org`'s thesis: one live incarnation, a + takeover displaces it, the displaced writer's next write is refused, the + successor inherits held work. + +Two implementations that look like one idea is a finding worth confirming. It is +**not** a reason to crown either. `contracts/org` carries verification drive does +not (86 reachable states walked by exhaustive BFS, property tests, fuzz); drive +carries a product surface and real usage `contracts/org` does not. Assess both +on the merits, and consider that the right answer may be neither, or one +absorbing the other, or that they solve subtly different problems. + +**A specific crux to settle.** A dossier phase and a Jira epic are **bounded** — +they complete. An area of standing responsibility is not; `agentic-development` +never finishes. Does `ScopeKindFree` carry an unbounded standing scope with the +same durability guarantees, or is it an escape hatch that drops them? Check +`drive/internal/verbs/attach.go`. If unbounded scopes are second-class, that is +a real gap and it is small; if they are first-class, a large amount of proposed +design is redundant. + ## 4. Use the instruments before you reason -Measurement beats argument, and instruments already exist. +Measurement beats argument, and instruments exist. **`~/dev/warrant/cmd/gate-observe`** replays a foreign evidence journal against a pipeline definition written *afterwards*, requiring no adoption by the observed -system. It is the cheapest way to find out whether a model can account for what a -real pipeline does. +system. It is the cheapest way to find out whether a model can account for what +a real pipeline does. ```sh go run ./cmd/gate-observe ~/dev/gate/state/log.jsonl ``` -What it already found, against the operator's own merge gate (415 runs, 5010 -records — `~/dev/warrant/docs/gate-history.md`): +What it already found against the operator's own merge gate — 415 runs, 5010 +records (`~/dev/warrant/docs/gate-history.md`): - **`escalate` is 57% of all verdicts** — 1218 of 2145, more than `pass` (809) and `block` (118) combined. warrant's model had only `supported` and - `refuted`, so the single most common thing a real check says was - unrepresentable. A third verdict (`insufficient`) was added as a result. + `refuted`, so the most common thing a real check says was unrepresentable. A + third verdict (`insufficient`) was added as a result. - A separate session reached the same conclusion independently, from the model side rather than from real data. Two implementations converging on a missing value is the strongest signal in this corpus. @@ -115,117 +164,114 @@ records — `~/dev/warrant/docs/gate-history.md`): | `~/dev/workbench-laws-lean/` | independent Lean 4 model of a narrow slice of gate's verdict laws, pinned to workbench commit `6eee6aa`. Note its own disclaimer: it proves laws of the *model*, and nothing consumes it to permit a merge | | `~/dev/bakeoff/agent-substrates-08-21/` | `scorecard.md` and `org-compute-synthesis.md` for the round that produced mandate / obligation / proofline / branchroom | -Three different epistemologies — empirical replay, pre-registered experiment, -formal proof — are already aimed at this question. Part of reconciliation is -saying which one settled what. +Three epistemologies — empirical replay, pre-registered experiment, formal proof +— are already aimed at this question. Part of reconciliation is saying which one +settled what. -## 5. The re-entry axis: staying alive and rebounding with targeted context +## 5. Staying alive and rebounding with targeted context -This is the least-designed part of the vision and the closest to buildable, so -it gets its own section. +The least-designed property, and the closest to mechanically possible today. -A role lead, reduced to mechanism, is three things: +A durable assignee, reduced to mechanism, may be three things: -| what a lead needs | where it would live | wired today? | +| property | where it could live | wired today? | |---|---|---| | **context** — charter, held work, last conclusions | `CLAUDE.md` layering + a `SessionStart` hook injection | **no** | -| **memory** — what previous incarnations concluded | `contracts/org` chain + discharge records | chain on `main`; discharge unwired | -| **authority** — what it may do unattended | `settings.json` `permissions` layers (`allow`/`ask`/`deny`) + a gate grant or bailiff Warrant | **no role binding** | +| **memory** — what previous incarnations concluded | `contracts/org` chain; drive ledger; discharge records | chain on `main`; discharge unwired | +| **authority** — what it may do unattended | `settings.json` `permissions` layers; gate grant; bailiff `Warrant`; drive scope capability | **no binding to a durable assignee** | Verified 2026-08-24 in `~/.claude/settings.json`: the `hooks` object contains -**only `PreToolUse` and `PostToolUse`**. There is **no `SessionStart` hook and -no `Stop` hook wired at all**. The Stop hook built in `hooks` PR #42 exists and -is not installed. +**only `PreToolUse` and `PostToolUse`**. There is **no `SessionStart` hook and no +`Stop` hook wired at all.** The Stop hook built in `hooks` PR #42 exists and is +not installed. + +So the harness already provides every injection point this needs — +per-directory `CLAUDE.md` layering, `SessionStart` for computed context, layered +`permissions` for scoped authority — and **none is bound to a durable +assignee.** That gap is mechanical, not conceptual. -So: the harness already provides every injection point this vision needs — -per-directory `CLAUDE.md` layering, `SessionStart` for computed context, -layered `permissions` for scoped authority — and **not one of them is bound to a -role.** That gap is mechanical, not conceptual, and it is worth assessing -whether "a role lead is a context bundle + a permission set + a chain position" -is the whole of it or a dangerous simplification. +Assess whether the three-part reduction above is sufficient or a dangerous +simplification. Note that `permissions` layers are static files while a +capability (gate grant, bailiff Warrant) is minted, expiring, and revocable — +those are different security models and the difference probably matters. Relevant existing work: the `/floor` skill renders the *effective* merged -permission rulebook across global, project, and local settings, including how -hooks and wildcards interact. If per-session authority is going to be expressed -in settings layers, `/floor` is the tool that says what a layer actually did. +permission rulebook across global, project and local settings, including how +hooks and wildcards interact. If authority is expressed in settings layers, +`/floor` is what tells the truth about what a layer actually did. ## 6. The corpus -### Ownership / authority / evidence primitives +### Candidate implementations -| repo / package | the primitive it invented | last touched | +| repo / package | what it implements | last touched | |---|---|---| -| `workbench/contracts/org` | role chain, `Reduce`/`Admissible`, ownership fold; 86 reachable states walked by exhaustive BFS | on `main` | +| `workbench/contracts/org` | durable-assignee chain, `Reduce`/`Admissible`, ownership fold; 86 reachable states walked by exhaustive BFS. No hardcoded role vocabulary | on `main` | | `workbench/cmd/gate` + `contracts/gateauthorization` | merge authorization at an exact head; operator-minted grants, tier ceilings, TTLs; `gate next -json` is its projection | 08-23 | +| `~/dev/drive` | `Scope` / `attach` / `link` / `release`; scope-bound capability, successor re-mint, orphan adoption; liveness derived not written | 08-23 | | `~/dev/parley` | protocol kernel — Haskell compiles the protocol, Gleam enforces it, Lean proves the two agree | 08-23 | | `~/dev/warrant` | `Reduce` over an append-only journal; refuses to advance a run without evidence bound to that run's current subject | 08-23 | -| `~/dev/hack-mandate` | signed delegation pinned to one exact task revision / repo / base / head / diff; a child mandate may only shorten | 08-21 | +| `~/dev/hack-mandate` | signed delegation pinned to one exact task revision / repo / base / head / diff; a child may only shorten | 08-21 | | `~/dev/hack-obligation` | deterministic evidence–work frontier over frozen verification contracts | 08-21 | | `~/dev/hack-proofline` | read-only lineage index: which exact identity edge made an old claim | 08-21 | | `~/dev/hack-branchroom` | rerun as controlled causal fork; epochs, one pure reducer. **Ancestor of `contracts/org`** | 08-21 | -| `~/dev/bailiff` | "the chain as an enforcing bus." A `Warrant` is a capability an agent **holds**: scoped to one target and one function, capped at N uses, wall-clock expiry, operator-revocable | 08-16 | +| `~/dev/bailiff` | "the chain as an enforcing bus." A `Warrant` is a capability an agent **holds**: scoped to one target and function, use-capped, wall-clock expiry, operator-revocable | 08-16 | | `~/dev/agents-as-processes-gleam` | switchboard — residency buys serialized ownership | 08-10 | | `~/dev/huddle` | per-seat keys as agent identity in a shared room | 07-27 | -### The ownership lifecycle, as currently scattered +### The lifecycle, as currently scattered -- **claim** — `dossier` `task_claim`; the `/claim` and `/release` skills over a - session-claims log; `drive attach` plus +- **assign / claim** — `dossier` `task_claim`; the `/claim` and `/release` + skills over a session-claims log; `drive attach` plus `drive/internal/verbs/{authority,write_auth}.go`; `drive/internal/reducer/liveness.go` (mtime-quiet past N ⇒ stale). - **conclude** — `hooks` PR #42: a `Stop` hook appending what a session did to - the dossier tasks it touched, keyed off the fact that a session which called - `task_update` named the task in the call. PR #43: a sweep counting sessions - that owed a discharge and never paid — **measured 18 sessions / 40 tasks / 0 - recorded over 14 days.** + the dossier tasks it touched, resolved from the fact that a session which + called `task_update` named the task in the call. PR #43: a sweep counting + sessions that owed a discharge and never paid — **measured 18 sessions / 40 + tasks / 0 recorded over 14 days.** - **notice** — `drive` PR #47: a resident watcher tier. Findings in SQLite, one writer, deliberately unable to act. - **design docs** — `docs/features/org/{vision.md,store-decision.md,p0-findings.md}` in this directory; `drive:docs/features/discharge/spec.md` (PR #46). -### A note on read paths +### A read-path gap -**CORRECTED 2026-08-24 (second verification):** an earlier draft of this note -claimed `task_list` omits notes and no CLI verb reads one. That is **refuted** -against both the installed binary and source: `src/domain.rs:184` serializes -`notes` (skipped only when empty), `src/server/mod.rs:489-493` clears -body+notes only under `bodies:false`, and the CLI `task_list` path never clears -them — verified live with structured notes in the output. So the write tier -(bash hooks, the sweep) CAN read conclusions back via CLI `task_list`. Any -argument resting on the "unreadable notes" premise — including hooks PR #43's -"one substrate coupling" rationale and `store-decision.md` finding 2 — needs -re-examination. `task_get` remains one-id-per-call; that half stood. +`dossier` `task_list` returns a task's `body` and **omits its notes section**; +no CLI verb reads a note. The MCP's `task_get` does return a structured `notes` +array, but one id per call, walking the whole corpus. So the tier that *writes* +conclusions (bash hooks, the sweep) cannot read them back. Verified 2026-08-24. ## 7. What to work out -1. **One vocabulary.** For each genuinely distinct primitive: one canonical - name, which repos implement it, and whether they are aliases or rivals. Be - specific about **`bailiff`'s Warrant vs gate's grant vs `hack-mandate`'s - mandate** — three capability models by three different hands. Say which one - should win and why. - -2. **The lifecycle trace.** Follow one unit of work: claimed → acted on → - concluded → recorded → inherited. At each hop mark *implemented*, *designed - only*, or *missing*. The claim/discharge path is the live edge: assess - whether claiming at session start and concluding at session end actually - compose, and what breaks when a session dies between them. Note that #42 - deliberately claims at the **end**, on the argument that a start-claim is a - prediction and agents skip predictions — assess whether that holds. - -3. **The re-entry mechanism (§5).** Is "a lead = context bundle + permission set - + chain position" sufficient? What does a `SessionStart` injection have to - contain to be worth its cost, given that injected bytes enlarge every cached - turn thereafter and not just the first? Where should a role's authority live - so that `/floor` can still tell the truth about it? - -4. **What to stop maintaining.** Reconciliation means subtraction. Which of - these should be archived, folded into another, or deleted — and what is the - argument in each case? - -5. **Where authority actually stands.** Does `contracts/org` record the +1. **Property-by-property, not repo-by-repo.** For each mechanical property in + §2's table — durable assignee identity, authority scoping, where conclusions + land — say which implementations provide it, how they differ, and which + should win. Be specific about **bailiff's Warrant vs gate's grant vs + hack-mandate's mandate**: three capability models by three different hands. +2. **What dissolves.** Apply §2's standing instruction across + `docs/features/org/vision.md`. Sections §4.5 ("ownership is a tree; + dependencies are a graph"), §4.6 ("three things bubble"), and open question + §10.1 ("what is a fold per node kind" — called *"the real design work behind + d3"*) are all built on hierarchy. Does any of that survive substitution at a + depth of 1? Name the design work that stops being necessary. +3. **The lifecycle trace.** Follow one unit of work: assigned → acted on → + concluded → recorded → inherited. Mark each hop *implemented*, *designed + only*, or *missing*. The claim/discharge path is the live edge: does claiming + at session start compose with concluding at session end, and what breaks when + a session dies between them? Note that #42 deliberately claims at the **end**, + on the argument that a start-claim is a prediction and agents skip + predictions — assess whether that holds. +4. **Re-entry (§5).** Is "durable assignee = context bundle + permission set + + chain position" sufficient or a dangerous simplification? What must a + `SessionStart` injection contain to be worth its cost, given injected bytes + enlarge every cached turn thereafter and not just the first? +5. **What to stop maintaining.** Reconciliation means subtraction. Which of + these should be archived, folded, or deleted, and what is the argument? +6. **Where authority actually stands.** Does `contracts/org` record the *authority* for an act, or only that the act happened? Is there an implemented effect-class / charter concept, or only a described one? Could a - role lead hold a gate grant or a bailiff Warrant, and what breaks if a + durable assignee hold a gate grant or a bailiff Warrant, and what breaks if a non-human mints one? `~/.claude/CLAUDE.md` pins minting as operator-only — establish whether that is a design necessity or a current convention. @@ -233,19 +279,21 @@ re-examination. `task_get` remains one-id-per-call; that half stood. **Fully unattended operation is the horizon, not the next step.** Assume substantial engineering sits in between and do not optimise the answer toward -it. What is needed is a reconciled picture plus the next few moves that are -worth making regardless of how the autonomy question resolves. +it. What is needed is a reconciled picture plus the next few moves worth making +regardless of how the autonomy question resolves. ## 9. Constraints - **Cite `file:line`.** A claim without a citation is noise. - **Mark every finding `measured` or `reasoned`.** - **Distinguish IS from SAYS-IT-IS.** Code beats docs; say so when they differ. +- **Crown nothing by default.** Neither drive nor `contracts/org` nor any other + repo is the answer because it exists or because it is furthest along. An + earlier pass made exactly that error (§3). - **No new store, no new repo, no rewrite.** Five stores have already died here, each surviving long enough to look like it might still work. - Measured failure modes in this portfolio — scope errors 43%, stale claims 23%, - confabulation 8%. Re-verify anything that sounds already settled, **including - the corrections in §3.** + confabulation 8%. Re-verify anything that sounds settled, **including §3.** - Prefer the cheap local instrument over reasoning where one exists (§4). - Read-only. Modify nothing. @@ -253,13 +301,15 @@ worth making regardless of how the autonomy question resolves. At most 2000 words. -- **(a) Vocabulary table** — canonical primitive · implementations · alias or - rival · which wins. -- **(b) Lifecycle trace** — per hop: implemented / designed / missing. -- **(c) Re-entry verdict** — what a `SessionStart` injection must contain, where - a role's authority lives, and whether the three-part reduction in §5 holds. -- **(d) Subtraction list** — what to archive or fold, with the argument. -- **(e) The next three build steps, in order.** Each with: what it makes true, +- **(a) Property table** — mechanical property · implementations that provide it + · which should win · why. +- **(b) What dissolves** — design work that stops being necessary once the org + metaphor is substituted away. Be concrete: name sections and files. +- **(c) Lifecycle trace** — per hop: implemented / designed / missing. +- **(d) Re-entry verdict** — what a `SessionStart` injection must contain, where + authority lives, whether §5's three-part reduction holds. +- **(e) Subtraction list** — what to archive or fold, with the argument. +- **(f) The next three build steps, in order.** Each with what it makes true, why it precedes the others, and its kill condition. Grounded in what exists — no greenfield. -- **(f) The single riskiest assumption** in (e). +- **(g) The single riskiest assumption** in (f). diff --git a/docs/features/org/vision.md b/docs/features/org/vision.md index 8666098a..af5d4645 100644 --- a/docs/features/org/vision.md +++ b/docs/features/org/vision.md @@ -10,7 +10,7 @@ > | **`vision.md`** (this file) | the architecture and the argument for it | **canonical** | > | [`p0-findings.md`](p0-findings.md) | every number, and how it was measured | evidence; cite it rather than restating figures | > | [`store-decision.md`](store-decision.md) | which substrate holds the chain, and why | **decided 2026-08-23**; read before building anything that writes | -> | [`reconciliation-brief.md`](reconciliation-brief.md) | handoff prompt: fold the ten independent ownership streams into one org loop | **open question**; hand to a fresh analyst, output is a vocabulary + build order | +> | [`reconciliation-brief.md`](reconciliation-brief.md) | handoff prompt: name the properties the org metaphor stood in for, and reconcile the ten implementations against them | **open question**; hand to a fresh analyst. Its §2 puts §4.5, §4.6 and §10.1 below in doubt | > | [`where-this-stands.md`](where-this-stands.md) | proven vs claimed, and the next step | synthesis; **folds into §0 here once #245 is locked** | > | [`spec.md`](spec.md) | the earlier TDD draft | **superseded** by this file; kept for its review history | > | `drive:docs/features/discharge/spec.md` | conclusions as owned data, via two hooks | **the P1 slice that ships first** — see below | From 35ad0a4c7486055701c43abc3d1518adff1ac87c Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Mon, 24 Aug 2026 21:22:10 -0700 Subject: [PATCH 20/24] =?UTF-8?q?fix(org):=20withdraw=20finding=202=20?= =?UTF-8?q?=E2=80=94=20notes=20were=20readable=20all=20along?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The finding claimed conclusions written by task_update could not be read back. Twice: first that no CLI or MCP path returned them, then narrowed to MCP-only. Both wrong. dossier task_list returns a structured notes array for every task in one call. It was a measurement error that survived two rounds of verification, so the withdrawal records the mechanism rather than just the correction: Task.notes is skip_serializing_if = "Vec::is_empty", so the key is absent from any task with none, and the original check sampled .[0] of a list whose rows mostly have none. A conditionally-serialised field and an absent field look identical from one sample. Nothing in the decision rests on it. Finding 4 alone is sufficient — a compare-and-swap has nowhere to live on markdown files — but the argument now stands on a narrower base than it was written with. Co-Authored-By: Claude Opus 5 --- docs/features/org/store-decision.md | 54 +++++++++++++++++------------ 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/docs/features/org/store-decision.md b/docs/features/org/store-decision.md index 3d942fee..513931b0 100644 --- a/docs/features/org/store-decision.md +++ b/docs/features/org/store-decision.md @@ -41,29 +41,37 @@ Four findings, each checkable. "plain markdown you can grep and edit by hand; the server re-reads it on every call." 5.4 MB at `~/dev/dossier-state`, today. -**2. Notes are readable only one task at a time, and only by an LLM.** -`task_update` appends to a task's `## Notes` section. Three separate facts, -each verified 2026-08-23: - -- `task_list` returns a task's `body` and **omits `## Notes` entirely** — - diffed `dossier task_list --project org` against the on-disk task file: known - note text present on disk, absent from every field returned. -- The **CLI** has no verb that returns notes at all. `dossier --help` lists - `serve`, `task_complete`, `task_update`, `artifact_link`, `task_list`, - `artifact_list`. Nothing there reads a note. -- The **MCP** does have one: `task_get` returns a structured `notes` array - (`actor`, `body`, `posted_at`). It takes a single id and, by its own - description, *"walks the whole corpus"* to find it. - -So the read path exists, but only for an LLM holding an MCP connection, one -task per call, at O(corpus) each. The Stop hook is bash and the sweep is bash; -neither can reach it. That is why `scripts/discharge-sweep.sh` greps the corpus -markdown, and why that access is isolated in one function. - -Discharge §4.1 says *the reader is the next agent, and the read ships first.* -The next agent can in fact read — one task at a time, by full corpus walk. It -is the writing tier that is blind, and the cost of a read scales with the -corpus rather than with the answer. +**2. ~~Notes are unreadable.~~ WITHDRAWN — the premise was false.** + +This finding twice claimed that conclusions written by `task_update` could not +be read back: first that no CLI or MCP path returned them, then, narrowed, that +only the MCP's `task_get` did, one id per call. + +**Both versions are wrong.** `dossier task_list` returns a structured `notes` +array (`actor`, `body`, `posted_at`) for every task, in one call. + +The error is worth recording because it is a measurement error, not a reasoning +one, and it survived two rounds of "verification". `Task.notes` is +`#[serde(default, skip_serializing_if = "Vec::is_empty")]` +(`~/dev/dossier/src/domain.rs:184`), so the key is **absent** from any task with +no notes. The check that produced the finding ran `jq '.[0] | keys'` over a task +list, saw no `notes` key on that one row, and generalised. Most rows have no +notes. A conditionally-serialised field and an absent field are +indistinguishable from a single sample. + +Refuted by a later analysis; confirmed here 2026-08-24 by querying a task known +to carry notes (`org/p1-t3-reduce` → `has_notes_key: true, note_count: 3`). + +**What this costs the argument.** `hooks` PR #43 justified reading the corpus +markdown directly on this premise; that code now reads through the CLI instead, +and got 5x faster doing it. Discharge §4.1's *"the reader is the next agent, and +the read ships first"* is in better shape than this document claimed — the read +path exists at every tier, including bash. + +**What survives.** Nothing in the decision rests on this finding. Findings 1, 3 +and 4 are independent, and finding 4 alone is sufficient: a compare-and-swap has +nowhere to live on markdown files. The substrate argument stands on a narrower +base than it was written with, and should be read that way. **3. It cannot be watched.** A watcher's whole question is *what changed since X*. A markdown tree answers that with a filesystem walk plus a re-parse. This From f366fa5242e57e19662ef33832a49167bd290b68 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Mon, 24 Aug 2026 21:22:34 -0700 Subject: [PATCH 21/24] =?UTF-8?q?docs(org):=20follow=20finding=202=20throu?= =?UTF-8?q?gh=20=E2=80=94=20cortex=20does=20not=20unpause=20on=20this?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two arguments still cited the withdrawn finding. The cortex revival condition asked for a consumer that dossier verbs plus an LLM doing its own retrieval cannot serve. The first term is now false, so only the latency term survives — and that has not been measured. The document no longer claims cortex unpauses; it says what would have to be measured for it to. The "How this could be wrong" section predicted this exact collapse and is kept visible rather than deleted, marked as fired. A falsification clause that goes off within a day is the most useful line in the doc. Co-Authored-By: Claude Opus 5 --- docs/features/org/store-decision.md | 31 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/docs/features/org/store-decision.md b/docs/features/org/store-decision.md index 513931b0..291db039 100644 --- a/docs/features/org/store-decision.md +++ b/docs/features/org/store-decision.md @@ -121,11 +121,19 @@ Postgres-only `RETURNING` in the store interface. > revisit when there's a real consumer that can't be served by dossier verbs + > an LLM doing its own retrieval. -The role lead's SessionStart injection is that consumer, and it fails on -exactly the two stated terms: dossier verbs **cannot** serve it (finding 2 — -the conclusions are unreadable through the API), and an LLM doing its own -retrieval **cannot** meet the 400 ms budget. The condition is met as written, -not reinterpreted. +The SessionStart injection is the candidate consumer. **With finding 2 +withdrawn, this condition is no longer clearly met**, and the honest reading is +weaker than the one first written here. + +The condition has two terms. "Cannot be served by dossier verbs" is now **false** +— `task_list` returns notes, so the verbs do serve retrieval. Only the second +term survives: whether an LLM doing its own retrieval can meet the <400 ms p95 +injection budget against a 5.4 MB corpus that is re-parsed per call. That is a +latency question, it is measurable, and **it has not been measured.** + +So cortex does not unpause on the strength of this document. It unpauses if +someone measures the injection path and it misses the budget. Until then the +revival condition stands unmet, which is what its own wording asks for. ## What this does not authorize @@ -142,10 +150,15 @@ not reinterpreted. ## How this could be wrong -- **If notes turn out to be readable** through some verb this survey missed, - finding 2 collapses and the pressure drops from "cannot" to "slow". The - substrate argument then rests on findings 3 and 4 alone — still sufficient - for the chain, no longer urgent for discharge. +- **~~If notes turn out to be readable~~ — this happened.** The original text + read: *"If notes turn out to be readable through some verb this survey missed, + finding 2 collapses and the pressure drops from 'cannot' to 'slow'. The + substrate argument then rests on findings 3 and 4 alone — still sufficient for + the chain, no longer urgent for discharge."* That is exactly what occurred, + within a day. The prediction was right and so was the consequence: the chain + argument holds, the discharge urgency does not. Kept visible rather than + deleted — a falsification clause that fires is the most useful line in a + design document. - **If the migration is where corpora go to die**, the honest evidence is that five stores already died and none of them died of a bad *engine*. A migration that loses the operator's 5.4 MB of project memory would be the first new From 87d45e6dca9e4b8296a29a51647436242b6854ce Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Mon, 24 Aug 2026 21:31:07 -0700 Subject: [PATCH 22/24] =?UTF-8?q?docs(org):=20handoff=20=E2=80=94=20the=20?= =?UTF-8?q?org=20runtime=20shipped,=20and=20two=20Stop=20hooks=20now=20col?= =?UTF-8?q?lide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmd/org and cmd/org-mcp are on main (#262, #263). The re-entry surface the reconciliation brief called the closest-to-buildable gap is built: charter/attach/assign/claim/yield/checkpoint/boot plus sessionstart-boot and stop-mark hook scripts. It is not wired — settings.json still has only PreToolUse and PostToolUse. That makes the brief's §5 stale and surfaces something sharper. There are now TWO Stop hooks, neither wired, built days apart by different sessions with no reference to each other: cmd/org/hooks/stop-mark.sh appends a mechanical mark to the role's chain, and hooks PR #42's stop-discharge.sh appends what a session did to the dossier tasks it touched. Both refuse to ask the model, and their headers reach that conclusion independently in different words — which is the strongest signal in this corpus and also a duplication that will double-write if both are installed. The handoff leads with that as the deliverable rather than the vocabulary question. It also carries the two corrections this session earned: the notes error (a skip_serializing_if field sampled at .[0] reads exactly like a field that does not exist) and the mktemp BSD/GNU divergence that passed every local run and failed every CI one. Co-Authored-By: Claude Opus 5 --- docs/features/org/handoff-2026-08-25.md | 211 ++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 docs/features/org/handoff-2026-08-25.md diff --git a/docs/features/org/handoff-2026-08-25.md b/docs/features/org/handoff-2026-08-25.md new file mode 100644 index 00000000..49fc5e63 --- /dev/null +++ b/docs/features/org/handoff-2026-08-25.md @@ -0,0 +1,211 @@ +# Handoff — org reconciliation, 2026-08-25 + +> Paste this whole file to a fresh analyst session. It is written to be read +> cold. Modify nothing; this is a read-and-decide pass. + +## Where this stands in one paragraph + +The org runtime **shipped** — `cmd/org` and `cmd/org-mcp` are on workbench +`main` (#262, #263). Charter, attach, assign, claim, yield, checkpoint, boot, +status, blob, log, plus two hook scripts. The re-entry surface a previous brief +called "the closest-to-buildable gap" is built. What it is *not* is **wired**: +`~/.claude/settings.json` still has only `PreToolUse` and `PostToolUse`, and +zero mentions of org. Meanwhile a second, independently-built Stop hook is +sitting in another repo waiting for the same slot. + +## 1. The live collision — start here + +**Two Stop hooks exist, neither is wired, and they do overlapping jobs.** They +were built days apart by different sessions and neither references the other. + +| | `workbench/cmd/org/hooks/stop-mark.sh` | `hooks/scripts/stop-discharge.sh` (hooks PR #42) | +|---|---|---| +| writes | a **mark** to the role's chain | a **discharge note** to the dossier tasks the session touched | +| content | session id, turn count, transcript path | turns, files written, PRs touched | +| resolution | `cwd` → `roles.map` longest-prefix | the session called `task_update`, which named the task | +| model involved | no — explicitly refuses | no — same argument, independently reached | +| on a bare mark | fold renders tip **Degraded** | n/a | +| wired | no | no | + +Read both headers. They reach the *same* conclusion in different words — +`stop-mark.sh`: *"a record the working agent is required to write is a verb +wearing a costume"*; `stop-discharge.sh`: claiming at the END is a report, +claiming at the start is a prediction and agents skip predictions. Two +implementations converging on a design principle is the strongest signal in +this corpus. It is also a duplication that will double-write if both are wired. + +The matching read side, `cmd/org/hooks/sessionstart-boot.sh`, injects a +byte-capped `org boot` index (default 2048 bytes: charter, held work, +obligations, liveness, last incarnation's final word — pointers, not a dump). +That is the SessionStart read `drive:docs/features/discharge/spec.md` (PR #46) +designed and never built. + +**The question to settle first:** are these one mechanism or two? If one, which +survives, and what happens to the other repo's PR? If two, state the property +each provides that the other cannot, in a sentence that would survive review. + +## 2. What shipped or moved recently + +Merged to workbench `main`: + +- **#262** `feat(org): the Baton home — a runtime for role continuity chains` +- **#263** `feat(org): platform seams — JSON receipts, presented identity, org-mcp, operator context.d` +- Also merged: #247, #253, #257, #259. + +Open, relevant: **wb#265 `feat/org-sweep`** — check what it is before proposing +anything adjacent. + +## 3. What the previous session produced + +Four PRs, all CI-green, none merged: + +- **hooks#42** `feat/stop-discharge` — the Stop hook above. Has had one review round. +- **hooks#43** `feat/discharge-sweep` (stacked on #42) — finds sessions that owed a + discharge and never paid, and can backfill. **Measured on the real corpus: + 21 sessions owed a discharge on 49 tasks, 0 recorded.** That 0% is because + #42 is not wired. +- **drive#47** `feat/watch` — a resident watcher tier. Findings in SQLite, one + writer, deliberately unable to act. One watcher over `gate next -json -live` + (parks, expiring grants, repos with no grant). Verified against live gate + state: six parks found on the first pass, zero re-notifications on the second. +- **wb#245** `docs/org-tdd` — carries `vision.md`, `store-decision.md`, + `p0-findings.md`, `reconciliation-brief.md`. **Do not merge until the + operator locks it.** + +`reconciliation-brief.md` on that branch is the previous handoff. **Its §5 is +now stale** — it treats re-entry as unbuilt. Its §2 (the metaphor is not the +mechanism) still holds and is the governing constraint; see below. + +## 4. The governing constraint + +From the operator, and it survives everything above: + +> Why does the distinction of an org even matter at all? Previously I was doing +> "driver" and "worker" agents, but it's all the same. Ownership / assignment, +> that's all that matters. Yes, org gives us hierarchy — but that is usually in +> effect to something like permissions or something else, and that can be +> represented any way. + +An org chart is a **proxy** for mechanical properties. Build the properties. + +| org term | the property it proxies for | +|---|---| +| role / lead / IC | a durable assignee identity that outlives a session | +| **hierarchy** | **authority scoping — who may do what, where** | +| reporting line | where a conclusion must land to be found by the next reader | +| org chart / tree | possibly nothing — at 2–5 assignees under one operator, depth is 1 | + +**Standing instruction:** wherever a source document uses an org word, +substitute the property and re-ask the question. If the question dissolves, +that is a finding. `vision.md` §4.5 (ownership is a tree), §4.6 (three things +bubble) and §10.1 (fold per node kind — called *"the real design work behind +d3"*) all exist to compress a hierarchy. Check whether any survives at depth 1. + +The operator's clarification, which matters: this is **not** a claim that any +existing implementation solved it. Crown nothing by default. + +## 5. Ground truth — earlier passes got these wrong + +- **`~/dev/gate` is ARCHIVED** (last commit: `docs: archive banner — gate + migrated into workbench as cmd/gate`, 2026-07-17). Live code is + `workbench/cmd/gate` + `contracts/gateauthorization`. Only `~/dev/gate/state/` + is live — the `GATE_STATE` journal, ~5000 records. +- **switchboard is `~/dev/agents-as-processes-gleam`.** Gate C2 measured that + residency does **not** buy crash recovery — it buys *serialized ownership*. + Measured; do not re-derive. +- **`hack-branchroom` is the ANCESTOR of `contracts/org`**, not a rival. See + the dossier note on task `p1-t3-reduce` in project `org`. +- **`braid` and `reprise` are dead** on their own kill conditions + (`~/dev/bakeoff/haskell-08-10/scorecard.md`, `haskell-dsl-08-10/`). + +## 6. Two corrections worth learning from + +Both were made by the previous session; both are instructive about how this +corpus lies to you. + +**The notes error.** It claimed twice that `task_update`'s notes could not be +read back — first that no CLI or MCP path returned them, then, narrowed, that +only `task_get` did. Both wrong: `dossier task_list` returns a structured +`notes` array. The mechanism is the lesson — `Task.notes` is +`#[serde(default, skip_serializing_if = "Vec::is_empty")]` +(`~/dev/dossier/src/domain.rs:184`), so the key is **absent** from any task with +no notes. The check ran `jq '.[0] | keys'` and generalised from one row. Most +rows have none. A conditionally-serialised field and a nonexistent one are +identical from a single sample. `store-decision.md` finding 2 is now withdrawn; +hooks#43 was rewritten to read through the CLI (and got 5× faster: 15s → 3.1s). + +**The portability error.** Every hooks#43 test passed locally and every one +failed on CI. Cause: `mktemp -t discharge-sweep`. BSD accepts a bare prefix +after `-t`; GNU reads it as a template and refuses it for having no `X`s. Same +family as the absent-`timeout` trap already documented in that repo — a +coreutils divergence that makes a script macOS-only while it looks portable and +passes every local run. **When reviewing anything bash here, check BSD/GNU +divergence explicitly.** + +## 7. The corpus + +Implementations touching ownership / authority / evidence: + +| repo / package | primitive | +|---|---| +| `workbench/cmd/org` + `contracts/org` | role chain, CAS-on-tip, charter/attach/claim/yield/checkpoint/boot, two hook scripts | +| `workbench/cmd/gate` + `contracts/gateauthorization` | merge authorization; operator-minted grants, tier ceilings, TTLs | +| `~/dev/drive` | `Scope`/`attach`/`link`/`release`; scope-bound capability, successor re-mint, orphan adoption; liveness derived | +| `~/dev/bailiff` | `Warrant` — a capability an agent *holds*: scoped, use-capped, expiring, operator-revocable | +| `~/dev/hack-mandate` | signed delegation pinned to an exact revision; a child may only narrow | +| `~/dev/hack-obligation` · `~/dev/hack-proofline` · `~/dev/hack-branchroom` | evidence frontier · identity lineage · causal fork (ancestor of contracts/org) | +| `~/dev/warrant` | `Reduce` over a journal; refuses to advance without evidence bound to the subject | +| `~/dev/parley` | protocol kernel — Haskell compiles, Gleam enforces, Lean proves | +| `~/dev/agents-as-processes-gleam` | switchboard | +| `~/dev/hooks` | the bash hook layer; PRs #42/#43 | + +## 8. Use the instruments before reasoning + +**`~/dev/warrant/cmd/gate-observe`** replays a foreign evidence journal against +a definition written afterwards, with no adoption by the observed system: + +```sh +go run ./cmd/gate-observe ~/dev/gate/state/log.jsonl +``` + +Against gate's live log (415 runs, 5010 records, see +`~/dev/warrant/docs/gate-history.md`): **`escalate` is 57% of all verdicts** +(1218 of 2145, vs 809 pass and 118 block) — the most common real outcome was +unrepresentable in the model, which grew a third verdict. One verifier's +refusal path has never executed in 415 runs. + +Research already done, do not repeat: `agents-as-processes-gleam/docs/evidence/` +(8 pre-registered docs, Gate A→C2), `~/dev/workbench-laws-lean/` (Lean 4 model +of gate's verdict laws, explicitly not production authority), +`~/dev/bakeoff/agent-substrates-08-21/{scorecard.md,org-compute-synthesis.md}`. + +## 9. Constraints + +- **Cite `file:line`.** A claim without a citation is noise. +- **Mark every finding `measured` or `reasoned`.** +- **Code beats docs.** These docs overclaim; say so when they differ. +- **Crown nothing by default** — not org, not drive, not for being furthest along. +- **No new store, no new repo, no rewrite.** Five stores have already died here. +- Measured failure modes in this portfolio: scope errors 43%, stale claims 23%, + confabulation 8%. Re-verify anything that sounds settled, **including §5.** +- Read-only. + +## 10. Output + +At most 2000 words. + +- **(a) The Stop-hook verdict (§1)** — one mechanism or two? Which survives, + and what happens to the loser's PR? This is the deliverable; everything else + is supporting. +- **(b) Wiring recommendation** — should `sessionstart-boot.sh` and a Stop hook + go into `~/.claude/settings.json` now? What breaks if they do? Note the + injection cost: bytes injected at SessionStart enlarge every cached turn + thereafter, not just the first. +- **(c) Property table** — for each property in §4, which implementations + provide it, and which should win. +- **(d) What dissolves** — design work that stops being necessary once the org + metaphor is substituted away. Name sections and files. +- **(e) Subtraction list** — what to archive or fold, with the argument. +- **(f) Next three build steps, in order**, each with what it makes true, why it + precedes the others, and its kill condition. +- **(g) The single riskiest assumption** in (f). From 41a4ceb2c45b7591451a5316ff582274826b40f7 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Wed, 26 Aug 2026 07:31:26 -0700 Subject: [PATCH 23/24] =?UTF-8?q?docs(org):=20lineage=20review=20=E2=80=94?= =?UTF-8?q?=20design=20vs=20shipped=20vs=20measured,=20and=20the=20decisio?= =?UTF-8?q?ns=20to=20fold?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Independent reconciliation of the whole ownership-continuity lineage at 2026-08-26 heads: what #245 designed, what #246/#248/#262/#263 shipped through the governed path, what #265/#266 measure, what hooks#42/#43 and drive#47 add, what cc-skills#29 proves in fixture, and what remains hypothesis. Every major claim classified on the honest rung ladder; duplications and missing joins named; D1-D10 written to be accepted, amended, or struck in place. Indexed from vision.md's document table. Co-Authored-By: Claude Fable 5 --- .../features/org/lineage-review-2026-08-26.md | 385 ++++++++++++++++++ docs/features/org/vision.md | 1 + 2 files changed, 386 insertions(+) create mode 100644 docs/features/org/lineage-review-2026-08-26.md diff --git a/docs/features/org/lineage-review-2026-08-26.md b/docs/features/org/lineage-review-2026-08-26.md new file mode 100644 index 00000000..2101fa65 --- /dev/null +++ b/docs/features/org/lineage-review-2026-08-26.md @@ -0,0 +1,385 @@ +# Ownership-continuity lineage review — 2026-08-26 + +**Status:** review record — independent reconciliation of what #245 designed, +what shipped, what open PRs measure, and what remains hypothesis. This file is +the fold-in target for the lock decisions on +[workbench#245](https://github.com/itsHabib/workbench/pull/245); each item in +§6 is written to be accepted, amended, or struck in place. +**Method:** every open PR head, base, CI state, review, and unresolved thread +re-verified live on 2026-08-26 against GitHub, the gate ledger +(`~/dev/gate/state/log.jsonl`), `~/.claude/settings.json`, the installed `org` +binary, and the live chains under `~/dev/org/state`. Nothing below is quoted +from a snapshot without re-checking. The commissioning map is +`cc-skills:skills/ticket-owner/references/review-corpus-2026-08-26.md`. +**Reviewer:** Claude (Fable), commissioned by the operator. + +--- + +## 1. Verdict + +**One coherent system is forming at the kernel and receipt layers; the +incoherence is concentrated at the seams and in this document pile.** What +shipped is small and disciplined — a contracts leaf with an exhaustively walked +state machine, a file home with flock-serialized admission, a byte-capped boot +index, two fail-open hook scripts, one instrument PR. The +too-much-architecture risk lives entirely in `vision.md`'s *unbuilt* planes +(broker, batond, levels, tenancy, fleet), and those are already fenced off +twice: by vision's own failed §7 P0 gate, and by the closure TDD's line — +*measure the closed loop before adding another plane, daemon, MCP, or state +store* (`docs/features/agentic-workbench-closure/spec.md`). + +The actual defects are four, and none of them is the architecture: + +1. **Consumers not consuming shipped mechanisms.** `ticket-owner` re-implements + the kernel's intent law in prose while `org intent` / `org resolve` exist + and structurally enforce it; the delivery-evidence-chain hypothesis invents + receipt-pointer prose while substrate-autowiring already owns a locked + `verdict`/`receipt` artifact vocabulary joined on `head_sha`; drive#47's + watcher and flare's cards both notice parks and expiring grants without + composing. +2. **Default-mode identity.** The kernel checks a *presented* incarnation even + without `-strict`, but an absent one is auto-stamped as the current holder + (`cmd/org/internal/home/home.go:154-177`). The stale-writer law the design + spent its hardest review round getting right is opt-in at every call site. +3. **Stop semantics defined nowhere, load-bearing in three places.** The + harness's Stop event fires each time the main agent finishes, not once per + session. That hits `hooks:scripts/stop-discharge.sh` (thread filed on + hooks#42), `cmd/org/hooks/stop-mark.sh` (no thread filed; would flood + chains with marks once pasted), and #265's distill rate (its own unresolved + P1: a session that checkpoints *and* gets a stop mark counts as two ends). +4. **#245 disagrees with itself** — 21 unresolved threads, including the + status-block-vs-committed-phases contradiction in vision §7, the + fence-propagation overclaim against T1, the `contracts/authority` name + collision with the existing room-authority package, and the + reconciliation brief restating the already-withdrawn notes-unreadable + claim. + +The system is **not** a pile of duplicated state. It has exactly one chain +store, one work store per side (dossier / Jira), one authorization ledger, and +the duplications that exist are narrow and named in §5. + +## 2. Snapshot verification + +The review-corpus map is accurate: every head it pins matched live state on +2026-08-26. Additions the map does not state: workbench#266 is based on +`feat/org-sweep` (stacked on #265); #252 stacks on #251; hooks#43 stacks on +hooks#42; drive also has #46 (the discharge TDD, draft) and #48 open. CI is +green everywhere it runs. + +Independently verified ground truth: + +- **Wiring.** `~/.claude/settings.json` has only `PreToolUse`/`PostToolUse`. + No SessionStart, no Stop, zero org mentions. The hook paste is structurally + an operator action (the auto-mode classifier blocks agents editing hook + config); installed copies wait at `~/dev/org/hooks/`. +- **org-mcp is live.** Registered user-scope; agent sessions on this machine + carry `org_boot` … `org_verify` as native tools (and no `org_sweep`, + consistent with #265 unmerged). +- **Live chains are real but thin.** Tenant `mh`: `lead:agentic-development` + 11 records (1 checkpoint, 0 marks, 3 assigns, one claim/yield pair, last + write 2026-08-24); `lead:rooms` charter-only. `roles.map` binds the + workbench and rooms checkouts. +- **Gate ledger.** workbench#262: 25 records; #263: 15 — the governed-path + claim for both is corroborated. hooks#42: 13 records. #245, #265, #266, + hooks#43, drive#46/#47, cc-skills#29: none. "Release authorized" applies to + exactly two things in this lineage. +- **The installed `org` binary is stale.** Built 2026-08-24T00:21 from + `df45b2fe` (`vcs.modified=false`) — before #262 (merged 18:28) and #263 + (22:20) landed. The binary that wrote the live chains predates the merged + runtime. Rebuild from `main` before wiring anything. +- **The handoff has a local uncommitted draft** (+40/−15 in the `docs-org-tdd` + worktree; not PR content). Its additions verify: governed path for + #262/#263, org-mcp registration, operator-only hook paste. One claim does + not: hooks#42 "merge-ready" — #42 carries 6 unresolved threads with zero + reply comments on record, including two P1s (Stop dedup; Codex envelope + parsing). The fixes may exist at the head; the thread ledger does not say so. + +## 3. Layer reconciliation + +1. **What #245 originally designed.** Two documents. `spec.md` (superseded; + kept for §5/§6 history) designed chains-as-Envelope, + incarnate/checkpoint/handoff, annul, explain/doctor. `vision.md` + (canonical) redesigned it: spine/blob split, the §3.9 claim state machine, + host-written marks plus a distiller, the effect plane inside custody, + fences, levels, batond. The governing correction — *the metaphor is not the + mechanism*; build durable-assignee identity, authority scoping, conclusion + routing — postdates both and is the standard everything else is read + against. +2. **What #246/#248/#262/#263 shipped** — the honest core: versioned canonical + encoder (`canon/v1`), record spine with kind classes and `min_reader`, the + ownership fold with identity-checked-before-position and the L1–L3 claim + machine (86 reachable states walked; the takeover→unassign→retire + obligation-stranding bug found only by enumeration), then the file home, + flock admission, boot/status/log/verify/blob, receipts with fence, + presented identity, `context.d`, org-mcp. Both runtime PRs landed through + panel + gate + pinned merge. What shipped is a deliberate *subset* of the + docs, with recorded deviations (JSONL not SQLite; write-as-holder default; + marks-not-checkpoints) — smaller and more defensible than the documented + thing. +3. **What #265/#266 measure.** Exactly the two rates the bet rests on: + distilled session ends (checkpoints vs marks) and inherited obligations + discharged (vs orphaned), computed by replay because an orphan is only + visible as a fold transition; plus tenant-scoped `assign_conflicts` as the + honest detected-not-prevented downgrade of vision Appendix A4. Two live + gaps: the mark+checkpoint double-count P1, and MCP-side tenant plumbing + (#266 scopes the CLI sweep to `-tenant`; the MCP verb passes none, and an + empty `ORG_TENANT` now yields an empty sweep). #266's base also fixed two + of #265's threads (valid-prefix reads) — merge in stack order. +4. **What the earlier closure/evidence/gate PRs already own** — the constraint + layer. workbench#4/#5: no new orchestrator or store; artifacts, not call + stacks; measure before adding a plane/daemon/MCP/store. #129 + + substrate-autowiring: the `verdict` + `receipt` dossier artifacts emitted + at the gate boundary and the merge hook, driver-agnostic, joined on + `head_sha` — **the already-designed receipt vocabulary the evidence chain + should consume**. #156: exact-head review findings. Meanwhile gate is + growing subject-continuity piecemeal — #253 (merged), #249, #254, #258 + (open) — which is the "Baton's first customer is gate" thesis being served + *without the chain*. The missing decision: does gate own its subject fold, + or consume org's? Nobody has written that down (§6 D6). +5. **What hooks#42/#43 and drive#47 add or duplicate.** Three genuinely + distinct properties: task-keyed conclusions into dossier (discharge); + owed-discharge detection/backfill (sweep — measured 21 sessions / 49 tasks + / 0 recorded); resident noticing with durable deduped findings (watch). The + duplications are narrower than "same thing twice": stop-mark and + stop-discharge are two sinks for one event that must share the + Stop-semantics fix, the transcript summarizer, and one distill budget; + watch-gate and flare both surface parks/expiring grants and should compose + (watch findings → flare sink) rather than ship as parallel notifiers. +6. **What cc-skills#29 proves locally** — fixture-proven, precisely: a + mode-0600 lease carries `{tenant, role, work, incarnation}` across process + replacement; a recovered transition key reused against an idempotent effect + store produces exactly one effect while a changed key detectably produces + two; a released incarnation's strict write is refused; corrupt lease state + is refused untouched. It proves nothing about live Jira/Ship/GitHub — and + says so itself. Two changes are owed before its first mutating tick: adopt + `org intent`/`org resolve` as the intent channel (the kernel already + refuses new claims *and* release while an intent is open — + `contracts/org/reduce.go:371-373,459-467` — turning the skill's whole §3 + idempotency rule into chain law; recovery becomes + `boot -json → .boot.open_intents[0]`, a structured field, instead of + prefix-parsing `last_word.excerpt`, a truncatable display surface), and + fold its last open thread (top-level PR comments in the review-completeness + rule — the known Codex verdict-shape trap). +7. **What remains hypothesis.** The epic steward and the five-stage delivery + evidence chain (`cc-skills:skills/ticket-owner/references/continuity-chain.md`). + Correctly labeled, correctly gated behind the one-ticket trial. The steward + survives the metaphor-substitution test better than vision's tree sections + (Jira's epic→child structure is the tracker's own hierarchy), with one + sharpening: on the personal side it would duplicate dossier's + project/phase/task model — its must-not-become-the-store list names Jira, + GitHub, Ship, review, and gate, and omits dossier. The honest statement of + the hypothesis: *does the project/phase/task pattern port to an external + tracker via a chain role, consuming Jira/Ship/gate receipts it does not + own?* + +## 4. Every major #245 claim, classified + +Rungs, from the review map: design documented → mechanism merged → open PR → +installed/wired → fixture-proven → live-dogfood-proven → release authorized. +Nothing below is promoted a rung because the story coheres. + +### Continuity plane + +| Claim | Status | +|---|---| +| Tip rule: ownership = CAS on the chain; identity checked before position | **Implemented and live** (#248/#262); live guarantee shrunk by the write-as-holder default | +| Record spine, kind classes, `min_reader`, versioned canonical encoding | **Implemented and live** (#246/#248) | +| Spine/blob split; erasable bodies with tombstones | **Implemented and live** (#262/#263) | +| L1–L3: one active claim; work_ref derived from state; dangling-claim inheritance; teardown refusals | **Implemented and live**; fixture-proven by exhaustive enumeration | +| One-outstanding-intent (T2's keystone, chain side) | **Implemented but unconsumed** — verbs and refusals shipped; no writer uses them; ticket-owner re-implements in prose | +| `next_due` declared liveness; LATE derived at read | **Implemented and live** | +| Boot re-entry index, byte-budgeted, `context.d` | **Implemented but unwired** — MCP-manual today; hook injection staged; paste is operator-only | +| Host-written `mark` at Stop | **Implemented but unwired**; per-turn Stop semantics unresolved | +| The distiller (verbless checkpoint authoring) | **Still entirely untested** — vision's own "only untested load-bearing assumption," still true | +| Resume canary / resume-fidelity ≥ 90% | **Still entirely untested** — nothing measures whether the chain carries thought rather than commitment | +| `seal` every K checkpoints | **Shrunk** — manual verb only | +| `annul` correction path | **Contract kind exists, no runtime verb** — unreachable in practice; §4.12's correctability bar unmet | +| `explain` / `doctor` | **Not built** | +| Takeover by supervisor | **Implemented, shrunk** — kernel requires a party named in Terms; spec §8's universal `--by operator` not implemented; the by-string is unauthenticated (open thread) | +| Cross-chain two-witness audit; `counterpart_absent` | **Untested** — kinds exist; the audit is not built (`verify` is single-chain) | +| Human-as-role | **Shrunk** — mapped checkouts attach the *lead* role; no `human:` identity | +| Reorg records (split/merge/recharter); `wip_limit` | **Design only** | + +### Authority plane + +| Claim | Status | +|---|---| +| Grants carry incarnation + fence | **Field shipped** (#263 receipts); **enforcement untested** — no verifier keeps a high-water mark; gate/custody untouched; the open thread further narrows the claim to per-verifier windows | +| `contracts/authority` extraction | **Contradicted** — name collides with the existing room-authority receipts package; correctly held | +| cycle/spend/concurrency ceilings | **Stored, enforced nowhere** — charter decoration today | +| Two roots; dead-man key refresh; attenuation chain | **Design only** | + +### Effect plane + +| Claim | Status | +|---|---| +| Intent-before-wire in custody; R/Q/C/U classes; probes; `effect_unstamped`; reconciler; external timer | **Still entirely untested** — nothing built; the kernel's intent-ref is the only shipped piece | +| Model calls as effects; spend metering | **Design only** | + +### Work plane + +| Claim | Status | +|---|---| +| Work URIs with schemes; `subject_digest` on assign | **Implemented and live** | +| One-open-assign across all chains | **Contradicted as a law; shrunk to detection** — #266 (open PR) is the honest downgrade Appendix A4 demanded | +| Derived vs attested completion | **Design only** | + +### Surface plane and process + +| Claim | Status | +|---|---| +| org-mcp | **Implemented and live**. Shipped ahead of the closure TDD's measure-first line — recorded tension, defensible via `store-decision.md`, said out loud here | +| batond; If-Match API; tenancy; OIDC; levels; shadow report; console headline numbers | **Design only**; gated; should stay gated | +| §7 P0 gate | **Evaluated: fails 2 of 3** (27.6% vs <20%; ~25 vs 75 affordable role-days); collisions unmeasured quantitatively — but drive#46 measured a real collision cluster qualitatively: six concurrent sessions on this PR's own docs, four documented collisions, two 645-message zero-artifact sessions. The strongest collision evidence in the corpus is about the org work itself | +| POC-A | **Not run** — blocked on the operator hook paste; instrumentation staged | +| "Baton's first customer is gate" (re-key the observer by subject) | **Documented, unstarted as such** — while gate grows subject-continuity piecemeal (#249/#253/#254/#258); ownership decision missing (§6 D6) | +| Lean/Quint/parley laws wired to `contracts/org` | **Prior artifacts exist; none wired in-situ** | + +### Consumers + +| Claim | Status | +|---|---| +| ticket-owner bounded tick (cc-skills#29) | **Fixture-proven only**; live Jira dogfood not run; one unresolved thread; intent-as-prose P1 stands | +| Epic steward + delivery evidence chain | **Hypothesis only**; owes a named join to the substrate-autowiring `verdict`/`receipt` vocabulary on the personal side | + +## 5. Duplications and missing joins + +Duplications (narrow, named): + +- **Two Stop writers** — `cmd/org/hooks/stop-mark.sh` (role chain) and + `hooks:scripts/stop-discharge.sh` (dossier task notes). Two legitimate + properties, one event. They must share the Stop-semantics fix, the + transcript summarizer, and a single distill budget; wire both only with + per-session dedup. +- **Two park-noticing paths** — flare cards (workbench#251/#252) and drive#47's + gate watcher. Compose (watch findings → flare sink) or pick one. +- **Prose intent vs kernel intent** — ticket-owner's `"intent: "` + checkpoint vs `org intent`/`resolve`. The kernel wins; the skill shrinks. +- **Receipt prose vs receipt artifacts** — continuity-chain.md's evidence + pointers vs substrate-autowiring's `verdict`/`receipt` kinds. The artifact + vocabulary wins on the personal side; Jira/Ship receipts play that role at + work. +- **store-decision vs the shipped home** — the SQLite-for-the-chain claim is + withdrawn by events (open thread agrees); the SQLite∩Postgres subset + survives where it belongs: drive#47's findings store. + +Missing joins (each is real work nobody owns yet): + +- The `(role, fence, work_ref, effect_id)` stamp on custody requests, and any + verifier high-water mark — the entire enforcement half of the fence. +- Gate's run→subject fold (the observer re-keying) — no PR exists. +- `org sweep` into the dogfood evidence card — the trial's instrument is not + referenced by the trial. +- A committed definition of "session end" shared by both Stop hooks and the + sweep's distill denominator. + +## 6. Decisions to fold (accept, amend, or strike in place) + +- **D1 — Stop semantics.** Define session end once (final Stop per session, + deduped), apply to stop-mark, stop-discharge, and #265's rates. Gates the + hook paste. *Recommended: accept before anything else wires.* +- **D2 — Strict identity default.** Flip cmd/org to strict-by-default with an + explicit `-as-holder` opt-out for interactive operator use, or make + holder-writes a per-tenant/charter policy. *Recommended: flip before a + second writer exists on any tenant.* +- **D3 — Intent channel.** ticket-owner adopts `org intent`/`org resolve`; + recovery reads `open_intents`, never `last_word.excerpt`. *Recommended: + accept; it deletes prose.* +- **D4 — Evidence vocabulary.** The delivery evidence chain names dossier + `verdict`/`receipt` artifacts (substrate-autowiring) as its receipt form on + the personal side; Jira/Ship native receipts at work. *Recommended: accept.* +- **D5 — Notification composition.** Watch produces findings; flare is the + sink. Neither duplicates the other's store. *Recommended: accept; align + #251/#252 and drive#47 before merging both.* +- **D6 — Who owns gate's subject continuity.** Either gate keeps growing its + own subject keying (#249/#254/#258 direction) and org stays out, or the + gate observer folds by subject over org chains. Pick one. *Recommended: + gate-side for now; revisit after the ticket trial.* +- **D7 — The unreachable annul.** Either wire the `annul` verb (CLI + MCP) or + strike §4.12's correctability claim until it exists. *Recommended: wire; it + is small and the claim is load-bearing for operability.* +- **D8 — vision.md status reconciliation.** Rewrite the §0 status block to + 2026-08-26 truth (runtime shipped and governed; sweep open; POC-A blocked on + the paste; P0 gate status per p0-findings including the classifier-boundary + caveat), fold where-this-stands' verdict table in as its header says, banner + reconciliation-brief and handoff as archival session prompts, commit the + local handoff draft. Then fold the 21 open threads and lock. *Recommended: + accept; #245 merges only after this.* +- **D9 — Do not start** batond, levels, tenancy, the broker, model-calls-as- + effects, or the epic steward. The corpus's own gates already say this. + *Recommended: accept as a standing fence, re-examined only on trial + evidence.* +- **D10 — Binary hygiene.** Rebuild the installed `org` from `main`; the + work-laptop preflight's pin-and-record discipline applies to the personal + machine too. *Recommended: accept.* + +## 7. Build-and-dogfood order + +The main point, stated plainly: **stop reviewing, start running.** Everything +below is wiring and trials, not architecture. + +### Today, on the work laptop (honest path) + +1. Fresh-laptop preflight from `dogfood.md`: install pinned `org` + + `skill-sync`, record `go version -m`, sync the skill catalog, `gh auth + status`. +2. Bind exactly one approved Jira interface and one Ship interface into the + operations profile (`context-template.md`) under a dedicated work tenant + and state root. No credentials in the profile. +3. Run `continuity-smoke.sh` on that host — the mechanism control on the + machine that matters, where BSD/GNU divergence has bitten before. +4. Operator charters and assigns the role; start the loop **read-only**: + `/loop 10m /ticket-owner jira: --role steward: --repo + --org-state --tenant work`. Two identical read-only ticks proving + same-state → same-conclusion is the first evidence, and it can land today. +5. **Before the first mutating tick:** D3 lands on cc-skills#29 (intent + channel) and its last review thread folds. Then run the six break tests in + `dogfood.md` and keep the evidence card per tick. + +### This week, on the personal machine + +6. D1 (Stop semantics), then the operator pastes the SessionStart/Stop hooks — + POC-A's collision and distill counting starts the same hour. +7. Merge #265 → #266 (resolve the double-count and MCP-tenant threads or + record them as judged residuals); `org sweep` becomes the standing + instrument for both machines' evidence. +8. Reconcile hooks#42's thread ledger, then merge #42 → #43; dossier + discharge starts accruing where tasks live. +9. D8: lock and merge this PR as the design record. + +### Only after the trial passes its break tests + +10. One real child ticket plan→draft-PR→independent exact-head verification + with every receipt preserved and one forced replacement incarnation + (continuity-chain.md's step 2). The epic steward stays on the shelf until + then. + +### The deployment shape (one loop lane per role) + +The operator's framing is right and is exactly vision §4.11 made operational: +**each durable role gets one serialized scheduler lane running a bounded tick, +and org (CLI or MCP) is how the incarnation manages itself** — attach/resume +identity, claim, at most one authorized transition, checkpoint or intent, +yield, release. By role kind: + +- **IC at work** — the `/loop 10m /ticket-owner …` lane above. The lease + carries incarnation identity across tick processes; the lane, not the lease, + is the mutex. +- **Maintainer** — cron-shaped ticks that re-derive from the world: `org + sweep` on a schedule, drive#47's watcher, hooks#43's discharge sweep. Thin + chains; the chain records only ownership and tuning decisions. +- **Lead (personal machine)** — no loop needed: sessions *are* the ticks once + the hooks are pasted; SessionStart attach + Stop mark is the lane. + +Three laws hold every lane: one non-overlapping lane per role; a bounded tick +with at most one external transition; strict identity on every write. A lane +that cannot guarantee non-overlap stops before mutation — that rule is already +in the skill and it generalizes to every agent that "manages itself." + +## 8. What this review does not do + +It does not merge anything, does not resolve #245's threads, does not decide +D1–D10 — those fold here, on this PR, by the operator. And it does not promote +any claim a rung: the ladder in §4 ends today at *fixture-proven* for the +consumer layer and *installed-but-unwired* for re-entry. The next two rungs — +wired, and live-dogfood-proven — are a paste and a trial, not a document. diff --git a/docs/features/org/vision.md b/docs/features/org/vision.md index af5d4645..3f8920e6 100644 --- a/docs/features/org/vision.md +++ b/docs/features/org/vision.md @@ -12,6 +12,7 @@ > | [`store-decision.md`](store-decision.md) | which substrate holds the chain, and why | **decided 2026-08-23**; read before building anything that writes | > | [`reconciliation-brief.md`](reconciliation-brief.md) | handoff prompt: name the properties the org metaphor stood in for, and reconcile the ten implementations against them | **open question**; hand to a fresh analyst. Its §2 puts §4.5, §4.6 and §10.1 below in doubt | > | [`where-this-stands.md`](where-this-stands.md) | proven vs claimed, and the next step | synthesis; **folds into §0 here once #245 is locked** | +> | [`lineage-review-2026-08-26.md`](lineage-review-2026-08-26.md) | independent design-vs-shipped-vs-measured reconciliation; per-claim classification; decisions to fold (§6) | **review record**; fold-in target for the lock | > | [`spec.md`](spec.md) | the earlier TDD draft | **superseded** by this file; kept for its review history | > | `drive:docs/features/discharge/spec.md` | conclusions as owned data, via two hooks | **the P1 slice that ships first** — see below | > From 8367b2c81d5064b04d48df144e14aed45eb3c7c8 Mon Sep 17 00:00:00 2001 From: Michael Habib Date: Wed, 26 Aug 2026 07:37:59 -0700 Subject: [PATCH 24/24] docs(org): state the intent law exactly, and record D3 landing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel refuses new claims and retirement while an effect is open; release carries the obligation forward rather than being refused — the review overstated it. D3 (typed intent channel + review-thread fold) landed on cc-skills#29 at 7c80825 with the smoke green against main, so the today-path and the classification rows now say so. Annul refined: the kernel admits a tip-only annul; no verb exposes it. Co-Authored-By: Claude Fable 5 --- .../features/org/lineage-review-2026-08-26.md | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/docs/features/org/lineage-review-2026-08-26.md b/docs/features/org/lineage-review-2026-08-26.md index 2101fa65..27224313 100644 --- a/docs/features/org/lineage-review-2026-08-26.md +++ b/docs/features/org/lineage-review-2026-08-26.md @@ -151,15 +151,18 @@ Independently verified ground truth: store produces exactly one effect while a changed key detectably produces two; a released incarnation's strict write is refused; corrupt lease state is refused untouched. It proves nothing about live Jira/Ship/GitHub — and - says so itself. Two changes are owed before its first mutating tick: adopt - `org intent`/`org resolve` as the intent channel (the kernel already - refuses new claims *and* release while an intent is open — - `contracts/org/reduce.go:371-373,459-467` — turning the skill's whole §3 - idempotency rule into chain law; recovery becomes + says so itself. Two changes were owed before its first mutating + tick, and both landed 2026-08-26 (cc-skills#29 `7c80825`, smoke green + against an `org` built from main): the intent channel moved onto + `org intent`/`org resolve` — an open effect survives crash, yield, and + release; the kernel refuses every new claim until a resolution records the + outcome and refuses retiring over one + (`contracts/org/reduce.go:371-373,459-467`) — turning the skill's §3 + idempotency rule into chain law, with recovery reading `boot -json → .boot.open_intents[0]`, a structured field, instead of - prefix-parsing `last_word.excerpt`, a truncatable display surface), and - fold its last open thread (top-level PR comments in the review-completeness - rule — the known Codex verdict-shape trap). + prefix-parsing `last_word.excerpt`, a truncatable display surface; and the + review-completeness rule now reads top-level conversation comments (the + known Codex verdict-shape trap). 7. **What remains hypothesis.** The epic steward and the five-stage delivery evidence chain (`cc-skills:skills/ticket-owner/references/continuity-chain.md`). Correctly labeled, correctly gated behind the one-ticket trial. The steward @@ -193,7 +196,7 @@ Nothing below is promoted a rung because the story coheres. | The distiller (verbless checkpoint authoring) | **Still entirely untested** — vision's own "only untested load-bearing assumption," still true | | Resume canary / resume-fidelity ≥ 90% | **Still entirely untested** — nothing measures whether the chain carries thought rather than commitment | | `seal` every K checkpoints | **Shrunk** — manual verb only | -| `annul` correction path | **Contract kind exists, no runtime verb** — unreachable in practice; §4.12's correctability bar unmet | +| `annul` correction path | **Kernel admits a tip-only annul; no CLI/MCP verb exposes it** — unreachable in practice; §4.12's correctability bar unmet | | `explain` / `doctor` | **Not built** | | Takeover by supervisor | **Implemented, shrunk** — kernel requires a party named in Terms; spec §8's universal `--by operator` not implemented; the by-string is unauthenticated (open thread) | | Cross-chain two-witness audit; `counterpart_absent` | **Untested** — kinds exist; the audit is not built (`verify` is single-chain) | @@ -239,7 +242,7 @@ Nothing below is promoted a rung because the story coheres. | Claim | Status | |---|---| -| ticket-owner bounded tick (cc-skills#29) | **Fixture-proven only**; live Jira dogfood not run; one unresolved thread; intent-as-prose P1 stands | +| ticket-owner bounded tick (cc-skills#29) | **Fixture-proven only**; live Jira dogfood not run; the intent-as-prose P1 and the last open thread were fixed 2026-08-26 (`7c80825`) | | Epic steward + delivery evidence chain | **Hypothesis only**; owes a named join to the substrate-autowiring `verdict`/`receipt` vocabulary on the personal side | ## 5. Duplications and missing joins @@ -283,8 +286,8 @@ Missing joins (each is real work nobody owns yet): holder-writes a per-tenant/charter policy. *Recommended: flip before a second writer exists on any tenant.* - **D3 — Intent channel.** ticket-owner adopts `org intent`/`org resolve`; - recovery reads `open_intents`, never `last_word.excerpt`. *Recommended: - accept; it deletes prose.* + recovery reads `open_intents`, never `last_word.excerpt`. *Landed + 2026-08-26: cc-skills#29 `7c80825`, smoke green against main.* - **D4 — Evidence vocabulary.** The delivery evidence chain names dossier `verdict`/`receipt` artifacts (substrate-autowiring) as its receipt form on the personal side; Jira/Ship native receipts at work. *Recommended: accept.* @@ -295,9 +298,10 @@ Missing joins (each is real work nobody owns yet): own subject keying (#249/#254/#258 direction) and org stays out, or the gate observer folds by subject over org chains. Pick one. *Recommended: gate-side for now; revisit after the ticket trial.* -- **D7 — The unreachable annul.** Either wire the `annul` verb (CLI + MCP) or - strike §4.12's correctability claim until it exists. *Recommended: wire; it - is small and the claim is load-bearing for operability.* +- **D7 — The unreachable annul.** The kernel already admits a tip-only annul + (`contracts/org/reduce.go:487-493`); no CLI or MCP verb exposes it. Wire the + verb, or strike §4.12's correctability claim until one exists. *Recommended: + wire; it is small and the claim is load-bearing for operability.* - **D8 — vision.md status reconciliation.** Rewrite the §0 status block to 2026-08-26 truth (runtime shipped and governed; sweep open; POC-A blocked on the paste; P0 gate status per p0-findings including the classifier-boundary @@ -332,9 +336,10 @@ below is wiring and trials, not architecture. `/loop 10m /ticket-owner jira: --role steward: --repo --org-state --tenant work`. Two identical read-only ticks proving same-state → same-conclusion is the first evidence, and it can land today. -5. **Before the first mutating tick:** D3 lands on cc-skills#29 (intent - channel) and its last review thread folds. Then run the six break tests in - `dogfood.md` and keep the evidence card per tick. +5. **D3 landed** (cc-skills#29 `7c80825`) — the intent channel and the + review-thread fold are in, smoke green against main. Run the six break + tests in `dogfood.md` and keep the evidence card per tick; mutating ticks + are unblocked once they pass. ### This week, on the personal machine