Files
alkstore/docs/reviews/003-wave-5-general-review.md
T
glm-5.3-flash 060f262e62 ADR-024 — the mem engine adopted as the family's third engine (wave 6; release readiness renumbered to wave 7)
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).
2026-10-10 09:42:40 +00:00

251 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# General review — wave 5 (contract suite + engine hardening)
- **Reviewer**: opencode (glm-5.3-flash)
- **Scope**: general review of everything wave 5 landed (commit range
`250511d..HEAD`: nine delivery tasks + the wave-5 review gate),
distinct from the gate (`tasks/review-wave-5.md`, which audited the
backlog discharge map, stamps, dispositions, and green-on-both-
engines — and passed). Lenses: correctness, security posture, code
smells, coverage gaps (`cargo-llvm-cov`), and the classic dev issues
reviews 001/002 hunted. The wave-5 delta is overwhelmingly test-side
(`properties.rs` +2379 lines, both engines' suite targets, three
engine-side test additions); the only production-code change is the
pg `wait_wake` lag logging (`stream.rs`, +13/-1).
- **Input**: full read of `alkstore-contract-suite/src` (all 3344
lines of `properties.rs`, factory, harness, version stamp), both
engines' `tests/contract_suite.rs` executors, the three engine-side
diffs (`sqlite seam.rs`/`store.rs`/`tx.rs`, `substrate/queue_ops.rs`,
`substrate/watcher.rs`; pg `stream.rs`/`stream_tests.rs`), the
wave-5 task Notes, and the lcov output. No live probes were needed —
the harness server was up, so the verification ran the real thing.
- **Gates at review time** (all re-run this session): `cargo build`,
`cargo test --workspace` server-less (green, skips clean), the
**full pg column live** (121 lib + 25 suite + 9 schema, vs the
harness server), `cargo clippy --all-targets -- -D warnings`,
`cargo fmt --check` — all green.
## Op note for future sessions (harness credentials rediscovery)
The pg tests' server-less skip posture hides which env the harness
needs. For the record: `ALKSTORE_PG_HOST=localhost
ALKSTORE_PG_PORT=15432 ALKSTORE_PG_USER=postgres
ALKSTORE_PG_PASSWORD=poc ALKSTORE_PG_DB=blobs` (discovered via the POC
crate's examples — the dockerized `pglo-poc` publishes :15432 through
docker NAT, so the server's `host … 127.0.0.1 trust` pg_hba lines do
*not* apply from the test host; the scram rule matches and the real
password is required). A bare `cargo test -p alkstore-postgres` without
these silently skips 155 tests — the workspace stays "green" with no
signal that the pg column never ran. That is exactly the documented
SKIP posture, but reviewers should set the env before believing a pg
claim: this review's first baseline run reported zero pg tests executed
(25 suite tests "passing" in 0.02 s).
## Env-variable boundary (verified — family posture)
The alk* family rule is "nothing important goes in env vars" (alkvault
README), with the alkhttp spelling as the wire-facing case ("no handler
reads `std::env::var`"). Verified this session by grep across the whole
workspace: **every `env::var` read lives in a test target** — the pg
gate modules (`store/*_tests.rs`, `tests/schema_tests.rs`,
`tests/contract_suite.rs`). Production paths (`alkstore/src`, both
engines' machinery, the contract suite, the constructors) are env-free
— the only `std::env` uses in the other crates are `std::env::temp_dir()`
inside test modules for temp-file paths, not configuration.
Configuration reaches production exclusively through `PgOpts` /
`SqliteOpts` / DSN arguments passed by the caller's own config layer
(`engine-postgres.md`, ADR-008 §6; deployment.md's config table carries
no env entry) — the engines never consult the process environment, so
no credential (or any setting) can arrive implicitly.
For wave 6 (release readiness / prepublish) this boundary is an
explicit re-check item, not a settled fact: re-run the
no-env-in-production grep, and pin the boundary in the engine specs /
deployment matrix in one sentence ("env reads are harness-only;
production configuration is the opts-constructor surface"). Should a
future consumer-facing surface ever want env conveniences, the family
pattern is caller-side: the app's own config layer reads env (or
vault/config file) and hands the store's opts its values.
## Verdict
**No correctness or security defects found; wave 5's deliverables are
sound and the suite rows pin what the ADRs pin, no more and no less.**
What the review did find is one genuine coverage gap on a wave-4
hardening feature (the pg scheduler's transient-error retry loop has
never executed under any test), a set of narrow untested
race/failure arms around the forwarder and SQLite rollback paths, and
a handful of no-action smells. Nothing here is engine-consumer-facing;
the load-bearing findings are of the same "untested failure arm" class
that produced review 002's live bugs, so they are recommended for the
wave-6 window rather than accepted silently.
---
## Finding 1 — MEDIUM: the pg scheduler's transient-error retry loop
is dead code under test (coverage gap, not a demonstrated bug)
**Site**: `alkstore-postgres/src/scheduler.rs:680-704`
(`tick_with_retries`), 171-184 of the same file's uncovered inventory.
`pg-fix-scheduler-resilience` (the wave-4 fix batch, review 002
Finding 5) added two halves: the tampered-row quarantine and the
tick-level transient-error retry (3 retries at 250 ms → 500 ms → 1 s,
then a `context_error` exhaustion arm). The quarantine half is test-
pinned (`tampered_spec_row_is_quarantined_and_the_runner_survives`).
The retry half is pinned **only at the constants level** —
`the_tick_retry_policy_is_pinned` asserts `TICK_RETRIES == 3` and the
`tick_retry_backoff` table server-less — while the loop that *uses*
them (the retry, its backoff sleeps, the exhaustion error, and the
`eprintln` diagnostic) has zero coverage: the lcov run shows
scheduler.rs 171-184 and 237-260 unexecuted, and no fault seam exists
anywhere in the pg scheduler for `tick_once`.
A grep of the task module doc shows the deferral is half-visible ("the
transient-error injection's unit arm"), but the *injection* itself
never landed. The review explicitly flags this not as a bug (the code
reads correct — the retry loop is straight-line) but as the exact
class that bit review 002: Finding 1 there was an untested reconnect
failure arm that turned out to be a permanent-death bug; Finding 2 an
untested tx-wake arm that turned out to be F-1's root cause.
**Recommendation**: a small wave-6 task — a `cfg(test)` transient-
fault seam on the pg tick path (the `sqlite-commit-error-arm` pattern:
one-shot armed flag feeding a fabricated error into the real retry
loop), driving (a) one retried tick then success and (b) exhaustion to
the typed error shape. Small-honest scope; it retires the last
uncovered hardening arm from the wave-4 fix batch.
## Finding 2 — LOW: forwarder narrow untested arms (race-shaped; park
or seam in wave 6, acceptably)
Coverage residue in `forwarder.rs` with genuine (if unlikely) failure
shapes behind it:
- `forwarder.rs:693-698` — the "connection died before/during the
LISTEN" fall-through to the reconnect path. The reconnect-success
and kill-listener tests cover reconnect-while-idle and
shutdown-while-idle; this arm is death *during* the LISTEN window.
- `forwarder.rs:719-727` — mid-recv-loop stale-command abandonment
(`command.generation() < generation` inside the run loop). This is
the *race arm* of review 002 Finding 4's fix; the generation-top
drain half is tested, this arm structurally needs a send-racing-
reconnect seam.
- `forwarder.rs:330-338` (`mid_listen_error` ack arm), `444` (forwarder
gone), `744-745`/`762` (shutdown abort/await arms).
All are defensive or need deliberate race machinery to reach. The
recommendation is the same shape as Finding 1 in spirit but weaker: a
seam for (a) is cheap; the race arm (b) can stay parked with the
code-read as its pin — record it either way so the next general review
does not re-derive the same inventory.
## Finding 3 — LOW: SQLite rollback-failure-on-drop arms untested
`alkstore-sqlite/src/tx.rs:439-447` (`SqliteTxHandle::drop`) and
`166-175` (`ConnLease::drop`): when `ROLLBACK` itself fails, the
connection is dropped, the error is printed, and (the wave-3 fix's
property) a fresh connection replenishes the writer slot. The new
`failed_commit_replenishes_the_writer_slot` test covers the *commit*
error arm via the commit-fault seam; the mirror rollback arms remain
unexercised. Reachable on real failure (I/O errors mid-rollback), and
the replenish-on-failure logic is identical in shape to the commit arm
the seam now proves. A rollback-fault arm on the existing seam would
cover both in one small test. Minor — the wave-3 fix's live behavior
is already proven on the commit path.
## Finding 4 — INFO/no-action: pg stream bridge Lagged arm
`wait_wake`'s `Lagged(n)` arm (`stream.rs:644-650`) — the arm
`pg-suite-infra-hardening` extended with the lag eprintln — is
unreachable in tests without forcing >capacity broadcast lag (F-4's
known untestability). The change is logging-only on an existing
recovery behavior; the commit's "now true at both bridges" claim is
code-read-true, not test-true, which is appropriately disclosed in the
task record. No action.
## Finding 5 — INFO/no-action: smells (recorded, not churned)
- `tests/contract_suite.rs:50-52` (SQLite factory): `to_str()
.unwrap_or_default()` would silently open `""` on a non-UTF-8 temp
path — unreachable (`std::env::temp_dir()` base is UTF-8 on all
supported targets), but it masks rather than fails. One-line fix
whenever the file is next touched; not worth a green-gate-churning
commit alone.
- `tests/contract_suite.rs:91` (pg factory): `fresh_schema(&self)` is
`async fn` with no await — needless.
- `properties.rs:1683-1685`: `past_stamp_sleep` uses blocking
`std::thread::sleep` inside async rows — safe under the multi-thread
test flavors both engines' targets use (and the tolerance-sleep
posture is documented), but it would stall a current-thread runtime;
note it if the harness ever evolves flavors.
- `harness_ready()` costs a 3 s timeout per pg suite test *only* when
the server hangs (connection-refused fails fast) — correct posture.
## Coverage inventory (`cargo-llvm-cov`, full workspace, this session)
| Crate | Line coverage | Note |
|---|---|---|
| `alkstore` (core) | 100% | |
| `alkstore-sqlite` | 95.0% | residue: defensive/closed/rollback-failure arms (Finding 3), watcher/substrate error arms |
| `alkstore-postgres` | 92.9% | residue: Finding 1 (retry loop), Finding 2 (forwarder arms), stream-bridge defensive arms |
| `alkstore-contract-suite` | 97.1% | residue: failure-path panic labels (expected — a failing row *is* the failure) |
The residuals are overwhelmingly the family's accepted posture:
failure arms unreachable through the contract surface without a seam.
Finding 1 is the one exception worth closing; Findings 2–3 are the
judgment line between seam-cost and diminishing returns, called
explicitly here so the call is recorded rather than implicit.
## Cross-checks that came back clean
- **The suite rows pin ADR text, not implementation detail** — checked
for over-pinning (a row that a correct engine could fail): the
tolerance postures hold (state outcomes, bounded waits, straddle /
proximity tolerances on clock-adjacent values); the two engine
asymmetries a row could over-pin (SQLite overtrigger delivery, pg
cross-schema LISTEN fanout) are handled with by-construction
observables (the wake's `channel` field, own-schema re-drain) rather
than absence pins.
- **Engine-specific rows are wired engine-specifically**: `*_on_sqlite`
runs only in the SQLite column, `*_on_pg` only in the pg column —
no cross-column mistakes in either executor.
- **Derived-queue naming equivalence** (`__alkstore_outbox:mail`): both
engines' `outbox_backing_queue_name` are format-identical and each
has its own unit pin; the suite row pins the composed result.
- **The N-5 panic probe is honest on both engines**: the SQLite panic
unwinds through `with_tx` and the rollback runs in the unwind's
drop; the pg detached rollback is waited out by the row's
convergence loop; the silence window's placement (before any
post-panic write commit) correctly avoids racing SQLite's
documented commit-overtrigger.
- **Security posture**: wave 5 introduces no new consumer surface, no
new SQL (the test factories' DDL is parameter-quoted /
minted-schema-scoped), no secrets, no dependency additions beyond
dev/test tokio features.
- **Task hygiene**: all nine wave-5 task files carry completed status
with filled Notes and Summary; disposition records in the tasks
match what the code shows.
- **The gate's flake-watch echo**: the two unreproducible pg row
failures (trim row, lock row) get independent concurrence on the
gate's reasoning — the trim row's suspected failure shape (an event
beyond the checkpoint in the post-trim absence window) is
structurally blocked by the own-schema re-drain argument; the lock
row's lapse assertions can only be delayed, never un-lapsed, by slow
clocks. Supporting the gate's watch-not-block disposition; the
watch flag stands (the release-readiness watch — wave 7 since
ADR-024's renumbering).
## Recommendation
No changes required to keep wave 5's verdict standing. For wave 6's
window *(wave 6 at writing — release readiness; renumbered by
ADR-024, with Finding 1 since routed to the new wave 6 — the mem
engine — window, and Findings 2–3 to wave 7's pre-release pass, per
`docs/plans/implementation.md`'s wave-6 section)*: Finding 1 is a
decomposable small task (transient-fault seam +
retry/exhaustion pin); Findings 2–3 can ride the same task or be
accepted with the code-read as their pin — either way the disposition
should be recorded. Findings 4–5 need no work. The env-variable
boundary section above folds into the release-readiness pass
(wave 7) as a
verified-now / re-check-at-prepublish item.