Add a SQLite SessionPersistence backend (node:sqlite), a SECOND implementation built to prove the abstract seam + the shared runPersistenceContract suite are genuinely backend-agnostic. Each SessionEvent maps 1:1 onto an events row (session_id, seq, type, time, data); append is an INSERT inside a transaction asserting the contiguous-seq contract; the mutable SessionSummary lives in the sessions metadata row. It satisfies the SAME contract semantics as the JSONL backend, expressed over rows instead of file bytes: - Lazy materialization: create() records intent in memory; no row until the first append (a never-appended session is absent from has()/list() via a materialized flag set inside the first append transaction). - Crash-tail-on-load: load() returns events only through the last complete turn/end and deletes the uncommitted tail; a seq gap in the committed region makes the session unloadable. - Transactional append: a mid-batch failure (a UNIQUE seq collision from a concurrent writer) rolls back entirely, keeping the cursor truthful. Like the JSONL backend it is also the write-path plugin (session/event → buffer → session/flush drain, onCreated seed/adopt/collision handling, HMR seeding, dispose-to-quiescence). The package runs the shared runPersistenceContract suite plus SQLite-specific tests (transaction rollback, crash-tail cut, schema version, HMR adoption). Docs flip every "SQLite is future/deferred" reference (ADR 0016, architecture.md, the persistence module doc + README) to "implemented; the contract holds both backends to identical semantics".
Architecture Decision Records
Short, immutable records of the why behind decisions that shape this codebase. Code and docs say what the system does; ADRs say why it does it that way and what we gave up.
Format: one file per decision, numbered, with Status / Context / Decision / Consequences. An ADR is never edited into a different decision — supersede it with a new one and cross-link.
When to write an ADR
Write one when a decision is all three of: durable (it shapes the codebase beyond a single function or package), contested (there was a real alternative you rejected, and a reasonable engineer might have chosen it), and surprising (a future reader would otherwise ask "why on earth is it done this way?"). The ADR captures the why and what we gave up — the parts code and docs can't.
Do NOT write an ADR for: a mechanical or local choice (a variable name, a one-file refactor); anything already enforced and explained by a gate or a convention in AGENTS.md; or a still-provisional decision tagged TODO(...) in the code — record those as TODOs and promote to an ADR only once they settle. When in doubt, the test is the "why on earth" question: if the code alone would mislead a careful reader about intent, write the ADR.
| # | Title | Status |
|---|---|---|
| 0001 | Vendor Cordis as source, not npm dependencies | accepted |
| 0002 | Microkernel: extension via Cordis event taxonomy, one concrete loop | accepted |
| 0003 | Event-sourced sessions with derived message history | accepted |
| 0004 | Provider-neutral content-block vocabulary owned by dsh-llm | accepted |
| 0005 | Custom typed tool-schema DSL instead of schemastery | accepted |
| 0006 | Tool schemas are part of the system-prompt assembly | accepted |
| 0007 | Mechanical quality gates over prose guidelines | accepted |
| 0008 | tsdown for JS bundling instead of dumble | accepted |
| 0009 | Capability seams — interface / implementation / consumer split | accepted |
| 0010 | Two LLM adapters as a design-verification twin | accepted |
| 0011 | Runtime arg validation at the model boundary | accepted |
| 0012 | Dev-mode invariants over compile-time deep-readonly | accepted |
| 0013 | Property-based testing for protocol-shaped code | accepted |
| 0014 | Doc-sync enforcement (doc code blocks + event taxonomy) | accepted |
| 0015 | Structured error taxonomy (HarnessError base) | accepted |
| 0016 | Session persistence as an abstract service over the existing SessionEvent |
accepted |
| 0017 | Every session event is enclosed in a turn | accepted |