--- id: pg-suite-infra-hardening name: Postgres test-infra hardening — F-1's defense-in-depth candidates status: completed depends_on: [] scope: single risk: low impact: isolated level: implementation tags: [wave-5, postgres-engine, tests] --- ## Description Land the defense-in-depth test-infra hardening the wave-4 review recorded for F-1 (`tasks/review-wave-4.md` Notes) — **explicitly not load-bearing since `pg-fix-tx-wake` retired F-1's engine-side root cause** (the compose test is deterministic, 20/20 solo runs green at the fix-batch gate). This task exists because wave 5 was the named home ("wave 5's suite hardening, or a wave-5-adjacent test-infra task") and the candidates are cheap; if the wave-5 window gets tight, cancelling this task loses nothing but insurance. Work (the review's candidate dispositions, cheapest first): - **(a) Harden `must_recv_event`'s deadline** — replace the 20 ms polling loop with a parked `recv().await` under a timeout wrapper; the deadline assert still bounds the property, the poll-loop's wake-subscription interaction surface goes away. - **(b) A `LOG_LAGGED`-style diagnostic counter on the bridge** — discriminates "wake never arrived" from "wake arrived, re-drain missed" if a wake-shaped flake ever recurs. Land only if it is a small, honest addition (a test-observation counter, `#[cfg(test)]`- gated or eprintln-postured per the house test style); skip and record if it wants more machinery than that. Candidate (c) (leave the skip-posture as-is) is the do-nothing arm — adopted by default for anything not landed. ## Acceptance Criteria - [x] `must_recv_event` parks on `recv().await` under a timeout wrapper (no polling loop); the pg stream tests stay green against the harness server (a full stream-module run) - [x] Candidate (b) either landed small or recorded as skipped with the reason - [x] `cargo test -p alkstore-postgres` green (server-less skips clean); clippy `-D warnings`; fmt clean ## References - tasks/review-wave-4.md (F-1's Notes — the candidate dispositions) - tasks/review-wave-4-fixes.md (F-1 retired; defense-in-depth posture) - tasks/pg-fix-tx-wake.md (the engine-side fix that made this non-load-bearing) ## Notes Decisions of record: - **(a)** landed as specified: `must_recv_event` is now `timeout(15 s, receiver.recv())` — parked, no polling loop. The old helper's `try_recv`-loop `Err` arm split into the parked shape's two failure arms: `Some(Err(e))` → the same "receiver errored instead of idling" panic (an error during the wait surfaces immediately, as before), and `None` → a new "receiver closed before the event arrived" panic (the parked form makes the terminal arm visible, the poll form never saw it). `try_recv` is otherwise untouched as a test tool (other pg test modules still drain with it). - **(b)**: the `wait_wake`'s `Lagged(n)` arm now logs (eprintln, house posture — matching notify.rs's `bridge_loop`; it also makes forwarder.rs's "the receiver bridge's `Lagged` arm logs / recovers" doc claim true at both bridges — that claim was half-wrong on the stream side). The arm still treats lag as a wake (re-drain). The stream name is threaded into `wait_wake`'s signature so the log attributes lag to the stream, not anonymous. - **(b)'s full bridge-side counter — skipped, recorded**: surfacing the bridge's wake-observation count into the deadline panic would require plumbing a counter Arc across the `dyn EventReceiver` trait boundary (a test downcast or a keyed global registry) — more machinery than the small-honest bar. The discriminator's job is covered test-side instead: with the parked recv, a "wake arrived, re-drain missed" arm is structurally implausible (the re-drain reads exactly what a re-read reads; only a read error could miss, and it surfaces `Err`), so the deadline panic self-diagnoses: it reads the durable rows beyond the receiver's position via `read_since(offset, 1000)` and names the residual class — rows undelivered (wake/re-drain class; wait_wake's Lagged log speaks to feed lag there) vs no rows (publish-visibility class). Test harness only; no engine-state change. - The deadline stays 15 s (the review's bound of record; unchanged — only the wait shape moved from poll to park). ## Summary **Landed**: `must_recv_event` re-shaped from the 20 ms `try_recv` polling loop to a parked `recv().await` under a 15 s timeout wrapper (stream_tests.rs — the sole pg copy; the SQLite twin untouched, this task is pg-specific), with a self-diagnosing deadline panic (`read_since` presence check naming the failure class); the stream bridge's `Lagged(n)` arm now logs the lag (`stream.rs` `wait_wake`, stream name threaded in — the surfaced-not-silent posture now true at all wake-feed bridges); the literal bridge-side diagnostic counter skipped with reason above (the deadline diagnosis covers the discrimination test-side). **Verified**: the pg stream module 21/21 green against the harness server (one full module run, ~19 s at `--test-threads=4`); `tx_publishes_compose_with_the_handle` 5 solo re-runs green (determinism re-check under the new parked shape); two consecutive full pg harness runs green (121 lib + 25 contract-suite + 9 schema, ~72 s each); `cargo test -p alkstore-postgres` green server-less (harness rows skip cleanly); workspace `cargo test` green (12 test binaries); `cargo clippy --all-targets -- -D warnings` clean; `cargo fmt --check` clean.