decompose review-004 recorded findings: 15 disposition tasks + the review-004-fixes gate (sqlite seam panic-safety, watcher-death subscribe + initial-stat, bridge alignment, scheduler classification, substrate SAFETY delta; pg forwarder hardening + diagnosability, BIGINT stamp decision + fix, log-facade decision; core offset pin + Display forging, mem doc notes, pg identifier note, tokio dev-dep normalization) — taskgraph validate green (95)

This commit is contained in:
glm-5.3-flash committed 2026-10-11 09:42:29 +00:00
1 parent 029f885aa1
commit 9bbe645558
16 files changed
+1131

No files matched your search

@@ -0,0 +1,76 @@
---
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