--- id: sqlite-fix-seam-panic-safety name: Panic-safety armor for the sqlite auto-commit seams (review 004 item 1) status: pending depends_on: [] scope: moderate risk: medium impact: component level: implementation tags: [review-004-fixes, sqlite-engine, seam] --- ## Description Defense-in-depth panic-safety for the SQLite auto-commit seams, per `docs/reviews/004-pre-release-general-review.md` §"Findings recorded, not fixed" item 1. `with_writer`/`with_reader`/`blocking_read` (`alkstore-sqlite/src/seam.rs`, `substrate/schema.rs`) release their slot/pool connection only after the closure returns normally. A panic inside the closure skips the release: the single writer slot wedges forever (`Writer::acquire` waits on a condvar with no timeout), and an abandoned reader connection permanently shrinks the pool (`Readers.outstanding` never decrements on abandonment). The tx seam already owns this class (`ConnLease`); the auto-commit seams don't. No *reachable* panic path today (the library is panic-free by the standing gates, and these closures take no consumer code), so this is armor, not exposure — but the fix needs abandonment plumbing on both `Writer` (drop + path-based replenish) and `Readers` (an outstanding decrement), which is a deliberate small design task, not an inline patch. **Design first**: decide the abandonment mechanism of record before touching code, and record it in Notes. The established drop-guard idioms are the precedents: `ConnLease` (the tx seam's connection lease), the tick's poisoned-disposition replenish arm (review 004 inline fix 1), and cross-engine `FanoutRelease` (pg's forwarder guard). The natural shape is a guard whose `Drop` marks abandonment (poison / replenish / decrement) — Drop runs on unwind as well as return — but the design must pin which dispositions each seam's abandonment takes (writer: drop + replenish per the tick's established arm; reader: outstanding decrement, connection dropped not released). **Testability**: the closures take no consumer code, so the panic must be injected — a `cfg(test)` panic seam inside a test closure (or a test-only closure that panics), with the unwind caught, asserting the slot/pool state after: the writer slot is grantable again and a full store operation succeeds; the reader pool's outstanding count is restored and a later reader acquires. ## Acceptance Criteria - [ ] The abandonment design of record is decided and written into Notes (mechanism per seam, which dispositions, which precedents it follows) before implementation lands - [ ] A panic inside the `with_writer` closure leaves the writer slot grantable after the unwind — pinned by a test with an injected panic (slot re-acquired, full store operation succeeds after) - [ ] A panic inside the `with_reader`/`blocking_read` closures leaves the reader pool's outstanding count restored and a later reader acquires — pinned by test - [ ] Normal (non-panic) paths are behaviorally unchanged: the existing seam/slot/pool tests green unchanged - [ ] `cargo test -p alkstore-sqlite`, clippy `-D warnings`, fmt clean; workspace gates green server-less ## References - docs/reviews/004-pre-release-general-review.md §"Findings recorded, not fixed" item 1 (the finding, the fix shape, the "deliberate small design task" scoping) - docs/architecture/engine-sqlite.md (connection architecture: writer slot, reader pool) - alkstore-sqlite/src/seam.rs, substrate/schema.rs (the three seams) - The tx seam's `ConnLease` and the tick's poisoned-disposition replenish arm (review 004 inline fix 1) — the established idioms ## Notes > To be filled by implementation agent ## Summary > To be filled on completion