diff --git a/PENDING.md b/PENDING.md index 2c55549..5f7c677 100644 --- a/PENDING.md +++ b/PENDING.md @@ -4817,3 +4817,33 @@ is correct in mechanism and false in implication, and it stands until the record it fired. **This addendum is that record.** **Awaiting:** unchanged — **(b) with the three conditions above, or (c).** (a) is built. + +--- +## PENDING-184 — The body's selftest says *"never live state"* and writes the live occurrence log: a comment asserting a property the code does not have +**Date:** 2026-09-11 +**Tag:** [FIX] +**Summary:** `tarbuckle-body.py`'s D block opens *"Run against a temp dir, never live state."* `_log_draw` hardcodes `~/.claude/state/tarbuckle-draws.jsonl`; the selftest rebinds `LAST_TICK` and `SLOT` and not that path; D5 calls `fire_tick`. **Every selftest run — and every mutant copy of the body run for mutation testing — appends one real-time `tick` record to the live log.** + +**The subject is the comment, not the strays.** A block header asserting an isolation the code does not perform — in the file family where PENDING-180 lives, whose defect was a control defeated by the prose written beside it. In both, text inside a selftest makes a claim about the selftest that nothing checks. **The strays are the evidence, and they are a floor.** + +**Evidence** (full record at PENDING-182 ADDENDUM 1, *The finding*): two confirmed strays, `2026-09-09T21:48:39+0200` and `2026-09-10T18:52:40+0200`, both `silent`, established three ways that do not rest on each other — one writer and two call sites by census; the clock's ≥ 1200 s invariant; and each stray time-matched to a selftest run in the transcripts (3 s and 1 s). The third is a join across two records, not a reading of one. ⚠ **Nothing in a tick record distinguishes a live tick from a test tick**, so the total is not recoverable; a census for it failed its own negative control and was discarded. + +**The class, censused before the fix.** Each of the five Tarbuckle selftests was run under its own throwaway `HOME`, with the soul copied in so every path is reached — without it the mumble selftest returns NOT VERIFIED, and paths it never reached would be *unassessed*, not clean. **Exactly one wrote a file: the body, to `~/.claude/state/tarbuckle-draws.jsonl`.** Complement: none of the five scripts hardcodes an absolute home path, which is the one route a fake `HOME` cannot see. ⚠ **Bound:** the fleet's other selftests (`wake-digest.py`, `governance-drift-check.py`, `thread-query.py`) were not assessed. + +**The fix.** Hoist the path to a module global `DRAWS`; `_log_draw` writes `DRAWS`; the D block rebinds it beside `LAST_TICK`/`SLOT` and restores it in the same `finally`. + +**The control has two arms, and the second is the one an absence-only control lacks:** +- **D9 — the live log is untouched** across the D block: existence, size and SHA-256 of the original path, before against after. +- **D10 — the D block's own record landed where it was redirected**: the temp log holds exactly one record, `surface: tick`, outcome a declared kind. ⚠ **A control that only asserts absence passes if the write silently vanishes.** D10 is what fails then. +- Behavioural, not a source-string assertion — deliberately, in the family where the vacuous-pass bug lives. +- ⚠ D9 can fail falsely if a genuine tick fires from another pane inside the D block's sub-second window against a 20-minute cadence. That is the loud direction, and it is stated rather than engineered away. + +**Mutation, run under a throwaway `HOME` so a surviving mutant cannot itself write a stray:** M1 deletes the `DRAWS` rebinding — D9 and D10 must fail. M2 makes `_log_draw`'s write vanish — **D9 must pass and D10 must fail**, which is the case this item's second arm exists for. Nothing else may move in either. **External corroboration:** the live log's SHA-256 measured *outside* the selftest before and after one real run. + +**Relation to PENDING-182: a precondition, not a neighbour** (PENDING-182 ADDENDUM 2). Once ticks carry an id, a stray that draws `aside` or `notable` is a tick with an id and no child — the signature that instrument exists to find. + +**Not done here, by design:** the two strays stay in the log. A log is not corrected by deletion. + +**Files affected:** `scripts/tarbuckle-body.py`. + +**Awaiting:** nothing further — `[FIX]` lane, steward-directed in session 2026-09-11 (*"Yes, and file it before you build it"*). Filed first; the build follows, and its result is recorded here.