7603cb1c10525fcd6cc5a6b582334cc66ec349e2
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7603cb1c10 |
fix(wake): reconciler fails closed on the observed_seq dual-allocator hazard (#908)
ci/woodpecker/pr/ci Pipeline was successful
Review of #909 found a blocking correctness defect: the reconciler's "serialised => safe" claim was FACTUALLY WRONG. observed_seq has two INDEPENDENT allocators — the detector's private counter ($STATE_DIR/detector/ observed_seq_counter, W4) and the reconciler's store-cursor+offset — so even STRICTLY SEQUENTIAL calls collide: reconciler enumerates alpha->seq1, beta->seq2 (store observed_seq=2, detector private still 0); the detector's next delta allocates private 0->1 and enqueues seq1, ALIASING alpha. A consumer acking CONSUMED 1 then silently DROPS a distinct obligation — the exact G3 swallow the reconciler exists to prevent, via seq-aliasing. Reproduced with sequential, never-concurrent calls. Fix (W5-scoped; full store-side allocator unification stays deferred to #908 — detector.sh/W4 untouched): * Enumeration now FAILS CLOSED. Before writing any observed_seq, the reconciler REFUSES (loud, non-zero; the obligation is FLAGGED, never swallowed) when a seq it would allocate could be reissued/aliased: - if a detector is co-feeding this store (its private state dir exists) — ALWAYS refuse (read-only detection; W4 never touched); - else require the operator to assert reconciler-SOLE-FEEDER via --allow-enumerate / WAKE_RECONCILE_ALLOW_ENUMERATE=1. --no-enumerate and `check` never write, so they stay unconditionally safe. * Corrected the false "serialised => safe" rationale in the header comment to state the real dual-allocator hazard honestly, reference #908, and record the operational guard: a detector and the reconciler MUST NOT co-feed one store until #908 lands. * RED-FIRST test R9 reproduces the serialized-collision scenario (detector co-feeding + pre-existing state) and asserts the reconciler FAILS LOUD and enqueues NO aliasable seq (depth unchanged). Verified red: removing the guard re-enables the collision (R9 depth 0->2) and turns R9 red. R5/R6 now enumerate under an explicit sole-feeder assertion (no detector co-feeds those stores). test:framework-shell + shellcheck + firewall all green. Part of #892 Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_0158NZqN2n2ymKFeJAZ4GUCb |
||
|
|
60efd6a01e |
feat(wake): W5 — synthetic-canary FN-oracle + source-parity reconciler
ci/woodpecker/pr/ci Pipeline was successful
Adds A6 (fn-oracle.sh) and A7 (reconcile.sh) of the wake/heartbeat canon (EPIC #892, W5), building on merged W2/W3/W4 via their public APIs only (store.sh/ack.sh/detector.sh untouched). A6 fn-oracle.sh — synthetic-canary FN-oracle (§4 vector: FN-rate=0; §7 res5). Injects a KNOWN delta at the SOURCE boundary, drives the pipeline through the detector's public poll-once (a black box), and renders its verdict SOLELY from the terminal store cursor (did a CONSUMED ack cover the canary's observed_seq within the per-class SLO?). Off-domain / detector-independent: it never reads a detector hash-file or self-report, so a detector that silently DROPS changes — including a fully-disabled one at a perfect 0-wake rate — FAILS the oracle (the §4/A8 false-negative-blindspot killer). Runs in its own isolated XDG namespace, never the operator's live queue. No invented SLO (design law): --slo-seconds is required. A7 reconcile.sh — source-parity reconciler (§4/G3). (i) source-coverage PARITY INVENTORY first: a declared source not covered by any watch, a dangling reference, an empty lane, or a required_sources omission FLAGS — an omitted source cannot make the §4 vector pass vacuously (not silently green). (ii) periodic full reconcile: enumerate each covered source's current state vs observed_seq/inbox => 0 unaccounted; any gap FLAGS. Handles the W4-ratified division (detector first-seen-baseline does NOT wake) by ENUMERATING pre-existing/startup state into the durable store; inbox-reflected state is accounted (no double-enumeration). Fail-loud (G2a) on adapter error. RED-FIRST tests (wired into test:framework-shell), shellcheck clean: test-wake-fn-oracle.sh O1 healthy FN-rate=0; O2 disabled detector -> FN-DETECTED at perfect no-op; O3 CONSUMED-but-late -> FN; O4 off-domain verdict; O5 SLO required. test-wake-reconcile.sh R1 complete inventory; R2 omitted-source vacuous-pass prevented; R3 required_sources omission; R4 dangling; R5/R6 pre-existing -> flagged + enumerated -> re-run 0; R7 inbox-accounted no-double; R8 G2a fail-loud. manifest.txt version 0.3.0 -> 0.4.0 + inventory (schema range untouched). Operator-agnostic (XDG/env only; no operator paths/names/hosts/secrets). Part of #892 Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_0158NZqN2n2ymKFeJAZ4GUCb |