fix(wake): #917 gate the final observed_seq cursor write + observed.set/cursor consistency (defense-in-depth) #933

Merged
Mos merged 2 commits from fix/wake-917-cursor-write into main 2026-07-26 15:14:39 +00:00

2 Commits

Author SHA1 Message Date
mosaic-coder
99af3066db test(wake): T11 guard unshare-mount fault-injection unavailability (skip in non-privileged CI, like T9)
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
#933 CI was RED: the non-privileged Woodpecker runner DENIES `unshare --mount`
inside a user namespace ("unshare failed: Operation not permitted" / "can't mount
none on /"), so T11's bind-mount-EBUSY cursor-write fault injection could not set
up and the inner enqueue never ran — leaving T11's "diagnostic must name the
cursor write" assertion checking an empty errfile and FAILING. T9 uses the same
unshare+mount mechanism and survives only because its post-assertions are trivially
true when nothing was injected; T11 lacked that tolerance.

Fix (test-only): the inner (namespaced) script now drops a WITNESS marker AFTER the
bind-mount is in place and immediately before the enqueue. When the injection
cannot be constructed (unshare unavailable, or unshare/bind-mount denied so the
marker is absent), T11 prints a clear `SKIP:` line and does NOT run the failure-path
assertions — harness stays GREEN, matching T9's tolerance of the same unavailability.
When the injection IS available (privileged/local), the marker is present and T11
runs the full assertion set UNCHANGED (fail-loud + cursor-naming diagnostic +
observed.set/cursor consistency + intact consumed prefix).

Verified: non-privileged node:24-alpine -> T11 SKIPS, store-ack 12 groups + full
`pnpm run test:framework-shell` rc=0; privileged/local -> T11 runs full + passes.
store.sh (product fix) and manifest are UNCHANGED. shellcheck clean; BusyBox-portable.

Part of #892
Refs #917

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158NZqN2n2ymKFeJAZ4GUCb
2026-07-26 09:55:57 -05:00
mosaic-coder
eb37eae1f8 fix(wake): #917 gate the final observed_seq cursor write + observed.set/cursor consistency (defense-in-depth)
Some checks failed
ci/woodpecker/pr/ci Pipeline failed
Defense-in-depth hardening of store.sh cmd_enqueue surfaced by the #915 review
(obs#2): the final observed_seq cursor _atomic_write was the ONE durable write not
wrapped in a failure check, and was cross-file non-atomic with the observed.set
write immediately before it. Practically unreachable today, but a cursor-write
failure after a successful observed.set write could (a) return spurious success
while the allocation stayed uncommitted, and (b) leave observed.set advanced while
the cursor lagged (cross-file inconsistent).

Fix (additive; preserves #908 exactly):
- GATE the final cursor write like the pending/observed.set writes (#908): a
  cursor-write failure is now FAIL-LOUD (non-zero + diagnostic), never swallowed.
- On cursor-write failure, ROLL observed.set BACK to its pre-write snapshot, so
  observed.set and the cursor either BOTH advance or NEITHER does — never a
  stranded observed.set entry above the cursor.

#908 invariants intact: single store-side allocator, atomic allocate+enqueue under
flock, arrow-1 no-burn (pending write still FIRST, still aborts before any cursor
advance), anti-swallow <=consumed fail-loud, W2 contiguous-prefix CONSUMED. The
cursor stays the sole COMMIT point, so a pending-ahead state is exactly the one
#908 already tolerates on its observed.set-failure path. On-disk format UNCHANGED
(read-compatible).

Test: new T11 in test-wake-store-ack.sh forces the final cursor _atomic_write to
fail (bind-mount EBUSY over observed_seq inside an unshare mount+user namespace,
seeded so the cursor READ still succeeds) after pending+observed.set succeed, and
asserts fail-loud + observed.set/cursor consistency + intact consumed prefix. RED
against pre-#917 code; all store-ack/race/reconcile/detector groups stay GREEN.
manifest bumped 0.6.8 -> 0.6.9.

Closes #917
Part of #892

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158NZqN2n2ymKFeJAZ4GUCb
2026-07-26 09:18:59 -05:00