Row-first gate: consumer-inventory.md gains an engine-tier section — the mem-engine need recorded operator-authority, dated 2026-10-10 (wasm32 sandboxing across the alk protocol family + downstream ffi/napi/python-adapter economics; rusqlite/tokio-postgres structurally out on wasm, so mem is the only triad member the sandbox can carry). ADR-024 (decisions/024-mem-engine.md) pins the decision set: (1) separate `alkstore-mem` crate — discharges and amends ADR-001 §4 (superseded in part: full contract-v1 engine, not an "implementation convenience"; annotation on ADR-001 §4 + Status, class-1 window still open); (2) honest-ephemeral posture — no durability, single-process, ephemeral by design, never fleet-valid (ADR-010 §3's shared-pool predicate fails structurally), riding ADR-016's carriers unchanged (identity + docs + matrix); (3) full contract v1 tier with the asymmetry classification — `PayloadTooLarge` never produced (SQLite arm), no reconnection concept (reconnect-wake/watcher rows absent from mem's per-engine column), receiver close at engine drop, wake coalescing N/A; (4) `MemStore::new()` engine-native constructor — the one documented exception to the open-prose, core-contract.md Store concept amended; shared-instance (two opens → one engine) rejected with the ADR-016 §4-style re-entry gate (consumer-inventory row), register stays closed; (5) wasm32 — compile-clean for wasm32-unknown-unknown plus the suite's mem column green on host under a current-thread runtime flavor are wave-6 acceptance items; on-target suite execution scoped out with a named collapse condition (wasm-bindgen-test adapter crate) — a scope decision, not a pending-client hedge; (6) mem is not a core dev-dependency — core's doc examples stay mock-based, ADR-001 §4's doctest posture declines. engine-mem.md (draft): the in-process mapping — guarded per-mechanism state (no pool, no schema bootstrap, no forwarder, no spawn_blocking seam), the tx overlay (staged *_tx effects merged atomically at commit; read-your-own-writes easy, drop-=-rollback trivial; wake-at-commit structural — the seam pg needed pg-fix-tx-wake for, mem pins from birth via the existing tx-commit-atomicity rows), ADR-012 §2 third owner of the curve/opts/@every arithmetic (three-way equivalence, ADR-022's StoreFactory reused verbatim — born pinned). deployment.md: host-semantics row (single-process, ephemeral, never fleet-valid, wasm32-clean), mem connection-budget subsection (none — nothing to budget), durability-knobs row (the honest row is the absence), toolchain row (wasm32 subset + current-thread-clean, acceptance-cited). README/overview: engine-mem.md row, ADR-024 row, family table entry (alkstore-mem, no driver deps), current-state updated to the implementation phase with engine specs' stable statuses reflected. implementation.md: wave table gains wave 6 (mem engine, depends waves 1 + 5) and renumbers release readiness to wave 7 (fuzzing decision — the one remaining decided-at-implementation deferral — rides wave 7); wave-6 section records the born-pinned posture, acceptance items, the one suite-harness pre-task (past_stamp_sleep blocking sleep → async sleep, review 003's properties.rs:1683 note), and the window riders (review 003 Finding 1: the pg transient-fault seam, MEDIUM, rides wave 6; Findings 2–3 follow their park into wave 7's pre-release); wave-6 review gate defined; Decided points updated (mem discharged, fuzzing renumbered); stale wave-6 references swept across review docs and the landed tasks' notes (release-readiness items renumbered or annotated). Docs-only change; verified by cross-reference audit (all ADR/OQ/anchor references resolve, residual wave-6 mentions are correct under the new numbering or carry dated annotations).
12 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||
|---|---|---|---|---|---|---|---|---|---|---|---|
| review-wave-3 | Review gate — wave 3 (SQLite engine) | completed |
|
moderate | low | phase | review |
|
Description
Review the SQLite engine before wave 4 (or 5) decomposes. Primary lens per the plan's review gates: engine-vs-contract conformance — the engine crate now carries the first real consumer surface, so this is the gate that checks the code against the pinned contract text line by line (the wave-1 gate's discipline, now against an implementation that must honor the surface, not just declare it).
Check:
- Contract conformance: every trait method's behavior against
core-contract.md's per-mechanism text — validation at entry points
(auto-commit AND tx paths), error arms (
Database-only receivers,Codecat payload seams,LeadershipLostvs clean stop), value shapes (Job/StreamEventfield-for-field), the no-work-is-a-value vocabulary (None/false/empty-Vecwhere pinned, never errors). - ADR-023 follow-through: the extent guards actually sit at the
trait-impl entry (not substrate-side patches); duration guards on
both lock call sites; plain-path open holds;
poll_intervalwired;encode_payloadconsumed fallibly at every call site. - Seam integrity: drop = rollback under panic; writer-slot lease
released on every path (commit, rollback, drop, error);
spawn_blockingused consistently (no blocking call inside async context, no connection moved across await). - Substrate boundary: still contract-blind (no
alkstore::undersrc/substrate/); the lint removal didn't leave dead ported code behind; any new divergence registered in PROVENANCE.md. - Backoff curve: the engine-side formula matches ADR-010 §3's pinned range and cap exactly (wave 4 re-derives it; wave 5 pins equivalence — this gate checks the one implementation against the text).
- Backlog column: the engine's suite rows present, stamped, green.
- Family standards: no panics, no unwrap/expect outside tests, no inline comments (doc comments fine), module-per-file.
- Gates: full workspace build/test/clippy/fmt.
Acceptance Criteria
- Contract conformance spot-checked against the spec text (not just the tests)
- ADR-023's five backlog rows verified implemented as decided
- Seam/lease/rollback paths verified (code-read, not test-name-trust)
- Substrate boundary + provenance register still clean
- All gates green
- Findings recorded; wave 4/5 decomposition may proceed
References
- docs/plans/implementation.md (Review gates)
- docs/architecture/core-contract.md
- docs/architecture/engine-sqlite.md
- docs/architecture/decisions/023-fourth-review-round.md
- docs/reviews/001-waves-1-2-general-review.md (the posture this gate inherits)
Notes
To be filled by implementation agent
Two findings, both fixed inline (2026-10-08). Neither needed a new session or scope decision:
- W-1 (boundary) —
alkstore::Errorreferenced insidesrc/substrate/mod.rs:open_writer_connection(the tx handle'sreopenhelper) lived in the substrate subtree and mapped into the contract taxonomy there — a violation of ADR-012 §2's contract-blind boundary ("noalkstorecore types", as the substrate's own mod.rs doc text states; the wave-3 review checklist's grep foralkstore::undersrc/substrate/caught it). The machinery files (schema.rs/ops.rs/queue_ops.rs/watcher.rs) were always clean — the violation was a wave-3 engine-layer helper that had been placed in the wrong file. Fixed: the helper moved toalkstore-sqlite/src/seam.rs(the substrate's one existing mapping site; identical body). No register entry — nothing lineage-side changed. - W-2 (seam defect) — the writer slot stranded on three error
arms: the task checklist's "writer-slot lease released on every
path" read found
Writer::acquired connections dropped withoutrelease()/replenish on (a)seam.rs::with_writer's op-error arm, (b)tx.rs::begin's failedBEGIN IMMEDIATEarm, and (c)tx.rs::commit's failedCOMMITarm.Writerholds exactly one connection and has no self-replenish, so the first failed auto-commit op (e.g. a busy timeout expiry on any notify/enqueue/ claim) would park every subsequent writer op forever on the condvar — a store-wide livelock from one transient storage failure. Fixed: (a) releases unconditionally on both arms (the failed op didn't corrupt the connection — a refused statement leaves it healthy; the release is safe); (b) releases on the begin-error arm (the no-transaction connection is reusable); (c) drops the failed connection (its state is unknowable — exactly the Drop path's posture) and replenishes the slot via the existingreopenclosure, the same machinery the cancellation-pathConnLeaseand the failed-rollback Drop already use. Regression tests added:failed_notify_releases_the_writer_slot(notify_tests — trigger- induced storage failure, then the slot proves grantable),failed_begin_releases_the_writer_slot(tx_tests — SQLITE_BUSY begin at a shortened busy timeout, thenbegin_txsucceeds).
Verified clean otherwise — spot-check record:
- Contract conformance (code-read against the spec text, per the
acceptance criterion): entry-point validation on every name-bearing
method, auto-commit AND tx paths (tx.rs does all eleven
validate_shared_name/validate_local_nameat entry, before any round trip);InvalidNamefor empty/whitespace-only,ReservedNamefor prefixed, matching core'svalidate_name. Duration guards on both lock call sites (try_lock,SqliteLockHandle::renew—ttl <= 0→ opaqueDatabase, detail in the source chain, the substrate guard's message preserved there). Extent guards at trait-impl entry onclaim_batch(n <= 0→ emptyVec), both auto-commit stream reads, both tx stream reads, andEventReceiver::read_since— never reaching the substrate'sLIMIT ?. Boundary args unguarded-and-total (trim_topasses through; a negative horizon deletes nothing idempotently).Error::Closedon already-consumed handles;Codecat every payload/decode seam;recv()'sErrremapped toDatabase-only (database_only, stream.rs). No-work vocabulary held:Ok(None),Ok(false), emptyVecwhere pinned. - Value shapes:
Job::from_rowfield set matches ADR-021 §2's struct (claimed_at included);StreamEvent::from_rowmatches ADR-015 §3 (thetopiccolumn carriesstream, the one registered delta); dead-visibleget_jobcarrieslast_error/died_at. - ADR-023 follow-through: all five rows present in
alkstore-contract-suite/src/properties.rswith their contract stamps and green inalkstore-sqlite/tests/contract_suite.rs(10 tests); extent guards/duration guards/poll_interval/plain-path open/encode_payloadfallibility verified at their sites (see above);encode_payloadconsumed fallibly at all eight call sites, no silent fallback. - Backoff curve (queue.rs
backoff_delay_s): equal-jitter over[base·2^(a−1)/2, base·2^(a−1)]integerized inclusive, capped at 3600 s, attempt index = the row's post-claim count — matches ADR-010 §3's pinned form exactly. - Scheduler: tick fires + boundary advance in one
BEGIN IMMEDIATE(writer serialization as the row lock); leadership loss returnsErr(LeadershipLost)before ticking (top-of-loop renew, and exit-before-tick also holds at start — a refused acquire isLeadershipLost); the sleep renews the lease across itself; stop token releases the lock cleanly; 64-cap catch-up + skip-forward inherited (D-12). - Seam integrity code-read: drop = rollback issues
ROLLBACKand releases the slot (failed rollback → connection dropped + slot replenished viareopen— the same posture commit now uses); panic-through-drop reaches the same path; the mid-op-future cancellation path (ConnLease Drop) replenishes;spawn_blockingused consistently — the only blocking-in-async exception isEventReceiver::save_offset, a sync trait signature taking the slot in-line, documented at the site (ADR-007's honest lease posture; not a violation). - Substrate boundary: no
alkstore::reference remains undersrc/substrate/(post-W-1-fix grep; alsogrep -r "allow("clean — the wave-3 lint removals hold); PROVENANCE.md's D-29..D-36 entries cover the wave-3 deltas (verified against the code: URI flag dropped,open_conn_bootstrapped/D-30,ack_batchworker-less/D-31, the five cuts D-32..D-36 all genuinely absent); no unregistered divergence found in the lineage-adjacent reads. - Family standards: no panics/unwrap/expect in library code (only
#[cfg(test)]modules); inline//comments exist only at the correctness-constraint sites the AGENTS.md exception names (backoff curve's range pin, the extent guard, the leader loop's one-owner-of-the-loss-decision note,run_once's retry-string posture — all non-obvious behavior notes, acceptable); module-per-file holds. - One doc-vs-code nit, absorbed in the W-2 fix rather than a
finding:
seam.rs'swith_writerdoc comment said "release on both arms" — the code did not do that; now it does.
Deferred (documented, not fixed — each is small but not inline-clean, or already owned by a later wave):
Readers::acquire'sclose()path can drop a busy reader connection while held (schema.rs —close()clears the pool but does not track outstanding acquisitions; a reader acquired beforeclose()and released after it is discarded byrelease()'s closed check — correct, no leak; but a reader connection still in use across store-close keeps running on a closed file until its op completes — harmless in practice, the op finishes on an open connection that is then dropped). Noted for release readiness's close-path review (now wave 7); no consumer-visible defect.- Coverage of the commit-error arm: no test induces a failing
COMMIT(needs SQLITE_FULL/IOERR-class injection; the code fix is pinned by code-read + the sharedreopenmachinery being exercised by the cancellation tests). Recorded here; natural home is wave 5's suite hardening.
Summary
What landed, verified how:
Review gate passed: 2 findings, both fixed inline; all gates green.
- Fixed — substrate boundary violation:
open_writer_connectionmoved fromsrc/substrate/mod.rs(which referencedalkstore::Error, breaking ADR-012 §2's contract-blind boundary) tosrc/seam.rs, the engine's one mapping site. Substrate grep foralkstore::now clean. - Fixed — writer-slot stranding on error arms (the
store-wide-livelock defect):
with_writernow releases the slot on the op-error arm;beginreleases on a failedBEGIN IMMEDIATE;commitreplenishes via the existingreopenclosure on a failedCOMMIT. Two regression tests pin the property end-to-end (failed_notify_releases_the_writer_slot,failed_begin_releases_the_writer_slot). - Verified clean: contract conformance (entry-point validation, error arms, value shapes, no-work vocabulary — full code-read record in Notes), ADR-023's five follow-through items + backlog rows (10 tests, stamped, green), backoff curve matches ADR-010 §3, scheduler tick atomicity + exit-before-tick-on-loss, seam integrity (drop = rollback incl. panic path, cancellation replenish), substrate boundary + PROVENANCE D-29..D-36 accurate, family standards.
- Gates:
cargo build,cargo test(25 core + 188 sqlite + 10 suite + 3 harness),cargo clippy --all-targets -- -D warnings,cargo fmt --check— all green after the fixes. - Recorded for later waves: the reader-pool close-path note and the commit-error-arm coverage gap (Notes; wave 5/6 natural homes).
Wave 4/5 decomposition may proceed.