--- id: release-n1-growth-posture name: N-1 growth-posture review — conscious acceptance (or hygiene) status: completed depends_on: [] scope: narrow risk: low impact: component level: implementation tags: [wave-8, sqlite-engine, docs] --- ## Description Review 001's N-1 (deferred by design to the release pass): three engine-layer table families grow without internal hygiene caps, and the release review must **consciously accept them or add engine-side hygiene** (`docs/reviews/001-waves-1-2-general-review.md` §N-1, §7's wave-6 release-review bullet): 1. `__alkstore_dead` with `dead_letter_retention_s = NULL` (the default — the doc text is honest that retention is opt-in) 2. `__alkstore_notifications` rows pruned only at attach (D-16's 10k cap triggers only next attach — an idle-but-listening process never prunes) 3. `__alkstore_stream` (growth is the contract's durable-log posture; `trim_to` is consumer-invoked per ADR-015) None is wrong for v1; all three are documented engine-internal. The expected disposition is conscious acceptance with the documentation sharpened — review 001 specifically suggested the notify one "may want an 'attach is not the only prune site' note in the sqlite engine's docs (cadence recipes)". But the acceptance must be a *decision made this session against the implemented engines* (the review predates waves 3–7): re-read the three growth paths as implemented, confirm the review's characterization still holds (the mem engine's postures are new since — its notify/stream growth is process-lifetime-bounded by design; say so in the record), and land the acceptance: - The acceptance record (all three paths, per engine, with the rationale) — `deployment.md`'s durability/growth posture is the natural home; coordinate with `release-deployment-matrix` (this task's outcome is an input to that pass — land the substance here, let the matrix pass fold it into the final text). - The notify prune-cadence note in `engine-sqlite.md` (or wherever the engine spec's cadence guidance lives) if accepted. - If any path instead warrants engine-side hygiene (a cap, a sweep integration), that is a scope change — raise with the user before building; the default is acceptance. ## Acceptance Criteria - [x] The three growth paths re-read as implemented (SQLite + pg + mem); the review's characterization confirmed or corrected in the record - [x] The conscious-acceptance decision recorded per engine (or a user-approved hygiene scope change, which would be a follow-up task) - [x] The notify prune-cadence note landed in the SQLite engine's docs - [x] The acceptance substance positioned for the deployment-matrix final pass to fold in - [x] Workspace gates green (docs-only expected) ## References - docs/reviews/001-waves-1-2-general-review.md §N-1, §7 - docs/architecture/deployment.md (Durability knobs; the matrix this feeds) - docs/architecture/engine-sqlite.md (the cadence-recipes home) - docs/architecture/decisions/024-mem-engine.md (the mem postures new since the review) ## Notes - **Decision: conscious acceptance, all three paths, all three engines.** No engine-side hygiene scope change proposed or wanted. - **The review's characterization confirmed, with one sharpening per path** (verified against code, not just docs): - *Dead letters*: the characterization held, plus a sharpening the review didn't state — retention enforcement has exactly **one trigger**: a caller-driven `sweep_expired()` (`Queue` trait method). Engines run no background retention sweeper (the pg suite's comment says it outright: the sweep is "the only sweeper dead rows ever have"). So growth is opted-in twice: retention `None` by default, and no automatic cadence when it's set. - *Notifications*: held — `NOTIFY_ATTACH_MAX_ROWS` = 10,000, oldest-first, runs only at connection open (the substrate bootstrap, `schema.rs`); the D-16 cap triggers **only** on a fresh attach. Confirmed SQLite-only: pg has **no notifications table** (`pg_notify` is transient, ≤ 8000 bytes; pg schema has no such table). Upstream's `prune_notifications*` helpers were not carried (ADR-008 §4's disposition), so attach is the only prune site. - *Stream*: held — no engine-side trim beyond the consumer-invoked `trim_to` (ADR-015 §5's posture: default unbounded, documented squarely). - **The mem engine's postures (new since the review)**: its notify/stream/dead growth is process-lifetime-bounded by design — notify stores nothing (commit-boundary delivery to open receivers; per-subscriber feeds are unbounded mpsc, ending at engine drop), the stream log and dead table are in-memory state erased at process exit. Accepted structurally rather than via caps; recorded in the deployment.md acceptance. - The acceptance record landed in `deployment.md` §"Growth postures (consciously accepted)" with an explicit "this record is the input the deployment-matrix final pass folds in" positioning; the notify prune-cadence note landed in `engine-sqlite.md` §"Notifications- table hygiene" (anchored heading — deployment.md links to it). No internal task pointers in the release-facing text; the matrix pass can fold the substance directly. ## Summary Re-read the three growth paths against the implemented engines (`alkstore-sqlite/src/substrate/schema.rs` (D-16 at-attach prune), `queue_ops.rs` (`sweep_dead_retention` inside the savepoint guard, sweep-on-call only), `alkstore-postgres/src/schema.rs` (no notifications table), `queue.rs`/`stream.rs` sweep/trim shapes, and `alkstore-mem/src/{notify,wakes,queue,stream}.rs`). Decision of record (**all three accepted for v1, per engine**, no hygiene scope change): landed in `docs/architecture/deployment.md` §"Growth postures (consciously accepted)" (per-engine table + rationale, frontmatter updated), and the notify prune-cadence note (attach is the only prune site; idle-but-listening processes never prune) landed in `docs/architecture/engine-sqlite.md` §"Notifications-table hygiene" (frontmatter updated). Docs-only change; workspace gates green (`cargo build`, `cargo test`, clippy `-D warnings`, fmt --check).