PR 1 of the approved steps-3/4 plan (A10 before A5, since the client interface consumes the canonical log).
canonical.py — typed events for the full A10 table: user_message, assistant_text, tool_call, tool_result, plan_submitted, gate_opened, delegation_started, delegation_completed, compaction. Canonical tool-call IDs are minted locally (mint_call_id); provider IDs are never a join key. Loading rejects unknown types and unknown fields rather than silently dropping data.
StateStore schema v2 — a canonical_events table, appended via the same fsync-per-commit path as everything else. This is the first real use of the migration hook: a test constructs a genuine v1 database by hand and proves the current code opens, upgrades, and preserves its data (the "newer image reads older state" requirement of A2).
render.py — OpenAIChatRenderer.render(events, tools, model, system) builds the request body at call time; first adapter targets LiteLLM's normalized route (the executor path). Compaction events drop their replaced id range at render time while remaining first-class in the durable log; the Anthropic passthrough adapter and token budgeting arrive with delegations (step 7). Mappings for orchestrator-side events (gates, plans, delegation markers) are provisional and flagged as such in the code.
Transcript appends are deliberately outside the A9 state-change taxonomy — they are the conversation record itself, not state-machine mutations. Shout if you'd rather they emit oplog lines too.
The crash integration test now also proves a transcript append written before os._exit is readable on the next boot.
55 unit + 5 integration tests green locally. PR 2 (the A5 LLMClient + LiteLLM adapter with env-guarded live smoke test) follows once this merges.
PR 1 of the approved steps-3/4 plan (A10 before A5, since the client interface consumes the canonical log).
- **`canonical.py`** — typed events for the full A10 table: `user_message`, `assistant_text`, `tool_call`, `tool_result`, `plan_submitted`, `gate_opened`, `delegation_started`, `delegation_completed`, `compaction`. Canonical tool-call IDs are minted locally (`mint_call_id`); provider IDs are never a join key. Loading rejects unknown types and unknown fields rather than silently dropping data.
- **`StateStore` schema v2** — a `canonical_events` table, appended via the same fsync-per-commit path as everything else. This is the **first real use of the migration hook**: a test constructs a genuine v1 database by hand and proves the current code opens, upgrades, and preserves its data (the "newer image reads older state" requirement of A2).
- **`render.py`** — `OpenAIChatRenderer.render(events, tools, model, system)` builds the request body at call time; first adapter targets LiteLLM's normalized route (the executor path). Compaction events drop their replaced id range at render time while remaining first-class in the durable log; the Anthropic passthrough adapter and token budgeting arrive with delegations (step 7). Mappings for orchestrator-side events (gates, plans, delegation markers) are provisional and flagged as such in the code.
- Transcript appends are deliberately **outside the A9 state-change taxonomy** — they are the conversation record itself, not state-machine mutations. Shout if you'd rather they emit oplog lines too.
- The crash integration test now also proves a transcript append written before `os._exit` is readable on the next boot.
55 unit + 5 integration tests green locally. PR 2 (the A5 `LLMClient` + LiteLLM adapter with env-guarded live smoke test) follows once this merges.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Build-order step 4, sequenced before the LLM client (step 3) because
the client interface consumes the canonical log.
- canonical.py: typed events for the full A10 table (user_message
through compaction); canonical tool-call IDs minted locally,
provider IDs never a join key; strict payload validation on load
- StateStore: canonical_events table via schema v2 — the first real
migration, exercised in a test against a hand-built v1 database —
with append_event/read_events on the same fsync-per-commit path
- render.py: request bodies rendered from the log at call time;
first adapter targets the OpenAI-compatible shape of LiteLLM's
normalized route; compaction events replace their id range at
render time while staying in the durable log
- Crash integration test now also verifies transcript appends
survive process death
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
PR 1 of the approved steps-3/4 plan (A10 before A5, since the client interface consumes the canonical log).
canonical.py— typed events for the full A10 table:user_message,assistant_text,tool_call,tool_result,plan_submitted,gate_opened,delegation_started,delegation_completed,compaction. Canonical tool-call IDs are minted locally (mint_call_id); provider IDs are never a join key. Loading rejects unknown types and unknown fields rather than silently dropping data.StateStoreschema v2 — acanonical_eventstable, appended via the same fsync-per-commit path as everything else. This is the first real use of the migration hook: a test constructs a genuine v1 database by hand and proves the current code opens, upgrades, and preserves its data (the "newer image reads older state" requirement of A2).render.py—OpenAIChatRenderer.render(events, tools, model, system)builds the request body at call time; first adapter targets LiteLLM's normalized route (the executor path). Compaction events drop their replaced id range at render time while remaining first-class in the durable log; the Anthropic passthrough adapter and token budgeting arrive with delegations (step 7). Mappings for orchestrator-side events (gates, plans, delegation markers) are provisional and flagged as such in the code.os._exitis readable on the next boot.55 unit + 5 integration tests green locally. PR 2 (the A5
LLMClient+ LiteLLM adapter with env-guarded live smoke test) follows once this merges.🤖 Generated with Claude Code