Four layers, one invariant
The storage model has four layers, each with one job, tied together by a single rule: the DB holds refs, never payloads.
- SQLite (WAL) — the state-machine snapshot: current step/task state, attempt counts, lease. The transactional "where I am."
- Append-only event log — every transition plus the tool-call envelope (
tool_call_started → payload_persisted → tool_result_persisted). The forensic trail and the uncertain-state detector. - Content-addressed files — step output payloads, sha-keyed, under
~/pippa-db/. Dedup, integrity, and the payload never bloats the DB. - DB ref columns — sha / artifact-id pointers into the file layer. Load-on-demand; the row stays small.
This is the same instinct the truth track drilled: JSONL is ground truth, the DB is a derived mirror that holds refs. Here the step output is the artifact, and the row only points at it.
The event log is the safety spine
A step's external action is bracketed: tool_call_started is written before the call; tool_result_persisted after the payload lands. If the process dies between them, the log shows a tool_call_started with no matching result — and that gap is the uncertain state. The next lesson is entirely about handling it without causing a bigger accident.
Four classes, four resume rules
Not every step output resumes the same way. The Family Council mapped every output cwkPippa produces onto four classes; the class is the resume rule. The detail is in the code block, but the spine:
- Class 1 — externally persisted (an image written to a file): reuse the file; regenerate only if it's gone.
- Class 2 — idempotent fetch (a stock price): safe to re-fetch; worst case is a duplicate call.
- Class 3 — non-deterministic snapshot (a web search): reuse the snapshot, never re-search — re-running drifts, and the body text that cited it goes stale.
- Class 4 — derived render-out (a widget / chart): side-effect 0 but non-deterministic and lineage-bound; reuse if the sha is intact, re-render only if its
source_refsare alive.
source_refs[], the same lineage-everywhere invariant cwkCinder already runs.