fix(wake): #932 stop reconciler re-enumerating already-CONSUMED detector state (#935)
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

Co-authored-by: jason.woltje <jason@diversecanvas.com>
Co-committed-by: jason.woltje <jason@diversecanvas.com>
This commit was merged in pull request #935.
This commit is contained in:
2026-07-26 15:48:40 +00:00
committed by Mos
parent 9becaf877f
commit 9e81ffd7fc
6 changed files with 244 additions and 5 deletions

View File

@@ -19,6 +19,10 @@
# observed_seq allocator, so co-feeding one store yields DISTINCT,
# contiguous seqs (no aliasing). Formerly the fail-closed dual-
# allocator refusal; #908 dissolves the hazard at the root. (§4/G3, #908)
# R10 a CONSUMED detector state is ACCOUNTED via the store's last-consumed
# record — NOT re-enumerated (no dup wake, no spurious rc=1 CRITICAL). (#932)
# R11 pilot repro: consumed state SUPPRESSED while a distinct unconsumed/gap
# state STILL re-enumerates + flags (G3 teeth intact; no over-suppression). (#932)
#
# Uses FAKE/STUB sources only (no live network). Isolated per test.
# shellcheck disable=SC2030,SC2031
@@ -295,6 +299,88 @@ EOF
"$STORE" consume --upto 3 >/dev/null 2>&1 || fail_msg "R9: CONSUMED 3 must succeed over the gapless co-fed prefix"
) && ok
echo "== R10: #932 — a CONSUMED detector state is ACCOUNTED via the store's last-consumed record (no dup wake, no spurious rc=1 CRITICAL) =="
(
WAKE_STATE_HOME="$(fresh_state r10)"
export WAKE_STATE_HOME
unset WAKE_AGENT WAKE_RECONCILE_ALLOW_ENUMERATE
stub="$TMP_ROOT/r10stub"
mkdir -p "$stub"
make_stub "$stub"
export WAKE_DETECTOR_SOURCE_CMD="$stub/adapter.sh"
printf 'V0\n' >"$stub/repo_r1"
wl="$TMP_ROOT/r10.json"
cat >"$wl" <<'EOF'
{ "schema_version": 1,
"repos": [ { "id": "r1" } ],
"watches": [ { "lane": "L", "sources": [ { "kind": "repo", "id": "r1" } ] } ] }
EOF
export WAKE_WATCH_LIST="$wl"
# Detector baselines (silent) then a real delta -> one pending inbox entry (seq 1).
"$DET" poll-once >/dev/null 2>&1 || fail_msg "R10: detector baseline failed"
printf 'V1\n' >"$stub/repo_r1"
"$DET" poll-once >/dev/null 2>&1 || fail_msg "R10: detector delta failed"
[ "$(depth)" = "1" ] || fail_msg "R10: detector delta should leave one pending entry, got $(depth)"
# CONSUME it: consumed_seq advances, the pending prefix is truncated. The store
# now records r1's last-consumed observed_hash. The source's CURRENT state is
# UNCHANGED (still V1) — exactly the pilot's just-consumed byte-match window.
"$STORE" consume --upto 1 >/dev/null 2>&1 || fail_msg "R10: CONSUMED 1 must succeed"
[ "$(depth)" = "0" ] || fail_msg "R10: consume must truncate the pending prefix (depth 0), got $(depth)"
# RE-RECONCILE. On BASE this RE-ENUMERATES the just-consumed state (inbox
# truncated + seen-ledger never covered a detector enqueue) -> UNACCOUNTED=1,
# rc=1 (spurious CRITICAL), depth 1 (duplicate wake). After #932 the store's
# last-consumed record ACCOUNTS it -> clean.
out="$("$RECON" reconcile 2>&1)"
rc=$?
[ "$rc" -eq 0 ] || fail_msg "R10: a consumed state must be ACCOUNTED (exit 0, no spurious rc=1 CRITICAL) [$out]"
echo "$out" | grep -qi 'UNACCOUNTED=0' || fail_msg "R10: a consumed state must be 0 unaccounted (no re-enumeration) [$out]"
[ "$(depth)" = "0" ] || fail_msg "R10: reconciler must NOT re-enumerate a consumed state (depth still 0 = no duplicate wake), got $(depth)"
) && ok
echo "== R11: #932 — pilot repro: consumed state SUPPRESSED while a distinct unconsumed/gap state STILL re-enumerates (G3 teeth intact; no blanket suppression) =="
(
WAKE_STATE_HOME="$(fresh_state r11)"
export WAKE_STATE_HOME
unset WAKE_AGENT WAKE_RECONCILE_ALLOW_ENUMERATE
stub="$TMP_ROOT/r11stub"
mkdir -p "$stub"
make_stub "$stub"
export WAKE_DETECTOR_SOURCE_CMD="$stub/adapter.sh"
printf 'V0\n' >"$stub/repo_r1"
# r2 is a genuine unaccounted GAP: present from the start so the detector's
# FIRST-SEEN baseline is SILENT (never enqueues it), leaving it unaccounted and
# unconsumed — it has NO last-consumed record, so it MUST still re-enumerate.
printf 'GAP-STATE\n' >"$stub/repo_r2"
wl="$TMP_ROOT/r11.json"
cat >"$wl" <<'EOF'
{ "schema_version": 1,
"repos": [ { "id": "r1" }, { "id": "r2" } ],
"watches": [ { "lane": "L", "sources": [ { "kind": "repo", "id": "r1" }, { "kind": "repo", "id": "r2" } ] } ] }
EOF
export WAKE_WATCH_LIST="$wl"
# Baseline BOTH sources silently (no enqueue on first-seen). r2 stays at this
# state forever -> never enqueued = genuine gap.
"$DET" poll-once >/dev/null 2>&1 || fail_msg "R11: detector baseline failed"
# r1: a real delta -> enqueued (seq 1) -> CONSUMED (store records its hash).
printf 'V1\n' >"$stub/repo_r1"
"$DET" poll-once >/dev/null 2>&1 || fail_msg "R11: detector delta (r1) failed"
"$STORE" consume --upto 1 >/dev/null 2>&1 || fail_msg "R11: CONSUMED 1 must succeed"
[ "$(depth)" = "0" ] || fail_msg "R11: store should be empty before the reconcile, got depth $(depth)"
out="$("$RECON" reconcile 2>&1)"
rc=$?
# r2 is genuinely unaccounted -> the pass STILL FLAGS (G3 teeth intact).
[ "$rc" -ne 0 ] || fail_msg "R11: a genuine gap (r2) must still FLAG (non-zero) [$out]"
# After #932: ONLY r2 is unaccounted (r1 suppressed by the store record). On
# BASE both r1 and r2 re-enumerate (UNACCOUNTED=2) -> this assertion is red-first.
echo "$out" | grep -qi 'UNACCOUNTED=1' || fail_msg "R11: exactly ONE source (the gap r2) must be unaccounted; the consumed r1 must be suppressed [$out]"
# Prove precisely WHICH state re-enumerated: the gap r2 IS enumerated, the
# consumed r1 is NOT. (base re-enumerates r1 too -> the r1-absent assertion is
# red-first; the r2-present assertion holds both before and after = no over-suppression.)
en(){ "$STORE" drain | jq -s --arg id "$1" '[ .[] | select(.locators.id==$id) ] | length'; }
[ "$(en r2)" -ge 1 ] || fail_msg "R11: the genuine gap r2 MUST be re-enumerated into the store (no blanket suppression) [$out]"
[ "$(en r1)" -eq 0 ] || fail_msg "R11: the CONSUMED r1 must NOT be re-enumerated (its byte-matched hash is accounted by the store record) [$out]"
) && ok
echo
if [ -s "$FAILFILE" ]; then
echo "wake reconcile harness: FAILED ($(grep -c . "$FAILFILE") assertion(s))" >&2