--- id: sqlite-fix-watcher-death-subscribe name: Dead-watcher subscribe hang + initial-stat self-kill (review 004 items 2+3) status: pending depends_on: [] scope: moderate risk: medium impact: component level: implementation tags: [review-004-fixes, sqlite-engine, watcher] --- ## Description Two bundled watcher-state defects in the SQLite engine, per `docs/reviews/004-pre-release-general-review.md` §"Findings recorded, not fixed" items 2 and 3 (the review says bundle them — both are watcher-state changes). **(a) Post-watcher-death subscribe hang (item 2).** After the watcher thread exits (db-file replacement), `subscribe()` still enrolls new senders with no poller alive (`substrate/watcher.rs:391-396`): a `listen()` or stream `subscribe()` on the dead store parks forever, contradicting engine-sqlite.md's "on watcher death every subscriber closes" posture — which is enforced only for *pre-death* subscribers. Fix shape: a dead-store/dead-watcher sign that the new subscription paths consult — a state-machine change across `store.rs`/`notify.rs`/ `stream.rs`. New subscriptions on a dead store must fail closed (a typed error, the established failure vocabulary) rather than park. **(b) Initial-stat failure self-kill (item 3).** An identity-`stat` failure at watcher start seeds `(0, 0)` (`watcher.rs:164-170`); the first *successful* later stat mismatches it and drives the death path, spuriously closing every subscriber on recovery from a transient stat hiccup. Rare, but the fix — an identity-unknown state instead of a false identity — is the same watcher-state family: the watcher treats identity as unknown until the first successful stat, and that first successful stat must not trigger the death path. **Test shapes**: (a) kill the watcher (the db-file replacement seam or a `cfg(test)` death injection), then `listen()`/`subscribe()` — assert the typed failure, not a park; the pre-death subscribers' close-at- death pins stay green. (b) inject an initial stat failure, let the first successful stat land, assert the watcher survives and subscribers stay open. ## Acceptance Criteria - [ ] Subscribing (listen or stream) after watcher death fails closed with a typed error instead of parking — pinned by test - [ ] Pre-death subscribers still close at watcher death — the existing pins green unchanged - [ ] An initial identity-stat failure no longer seeds a false identity: the first successful stat does not trigger the death path and subscribers stay open — pinned by injected-failure test - [ ] engine-sqlite.md's watcher-death posture text covers the new-subscriber arm (and the identity-unknown state if it changes observable behavior) - [ ] `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" items 2–3 (the findings, the fix shapes, the bundling) - docs/architecture/engine-sqlite.md (the watcher-death posture) - alkstore-sqlite/src/substrate/watcher.rs (both sites), store.rs / notify.rs / stream.rs (the subscription paths) ## Notes > To be filled by implementation agent ## Summary > To be filled on completion