Add canonical event log and renderer skeleton (A10) #7

Merged
dongar merged 1 commits from canonical-log into main 2026-09-06 20:46:47 +00:00
Collaborator

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

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)
dev-agent added 1 commit 2026-09-06 19:52:09 +00:00
Add canonical event log and renderer skeleton (A10)
CI / checks (push) Successful in 9s
CI / checks (pull_request) Successful in 8s
ac81dd6234
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>
dongar approved these changes 2026-09-06 20:46:42 +00:00
dongar merged commit e185bb7c88 into main 2026-09-06 20:46:47 +00:00
dongar deleted branch canonical-log 2026-09-06 20:46:47 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dev/nisse#7