From cb8b50764c73ad8e3daedb918461f84cdb397dd6 Mon Sep 17 00:00:00 2001 From: David F Glidden Date: Tue, 25 Aug 2026 18:46:41 +0200 Subject: [PATCH] [FIX] Not shipped: a vanished draw, an unproven hook, and a contaminated denominator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Asked whether this was shipped. All seven §13 deliverables are met, which is what makes the question worth checking rather than answering. ⚠ A LIVE DRAW VANISHED WITH NO RECORD. One 'aside' fired and reported nothing, because the muted / no-soul / no-material / generator-failed / slot-write paths all returned silently. Almost certainly the mute test suppressed it — correct behaviour — but the log could not distinguish muted from crashed. Every exit now reports itself, with a control comparing return-count against log-count so the class cannot reopen. The rate report REVIEWED-128 binds is computed from this log: an unlogged exit does not merely lose a datum, it makes the denominator wrong while looking complete. ⚠ AND I CONTAMINATED THAT DENOMINATOR MYSELF. Inter-tick gaps measured 1.8, 4.7, 0.4 and 16.0 minutes against a 20-minute interval, then 20.0, 21.0, 20.0 once the session stopped touching it — the early ones are my own hand-runs of the status line and re-runs of the seam, which resets the tick clock. Test firings sitting in the live log, indistinguishable from real ones by inspection. A MARKER record now bounds the clean data and the deferral says to count from it and to report that the first 8 were discarded. ⚠ THE SEAMS HAVE NEVER FIRED IN PRODUCTION: 0 events each, and the Stop hook writes nothing on an ordinary turn, so there is no evidence it is invoked at all. Silent-net shape — it looks fine until a wrap produces nothing and the net takes the blame. The one genuinely good result: at 17:53:50 a notable draw passed the net and reached the status line UNRELAYED. First time anything reached the steward without the executor holding it. 38/38 on the mumble suite. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01J6hZXNYSxEfZseBGTni4sf --- PENDING.md | 9 ++++++++- scripts/tarbuckle-mumble.py | 21 +++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/PENDING.md b/PENDING.md index f954a47..f8dabc8 100644 --- a/PENDING.md +++ b/PENDING.md @@ -4906,7 +4906,14 @@ Link 1 to link 4 directly. **The trial built to test whether a fool could reach since: 2026-08-25 trigger: date 2026-09-08 owner: executor - discriminator: §8's own clause — "Report the observed mumble rate after two weeks." The + discriminator: ⚠ THE DENOMINATOR IS CONTAMINATED BEFORE 2026-08-25T17:53 and the + log carries a MARKER record at that boundary. The build session ran the status line + by hand and re-ran the seam (which resets the tick clock), so early ticks are test + artifacts indistinguishable from live ones by inspection. Found 2026-08-25 by + measuring the inter-tick gaps: 1.8, 4.7, 0.4 and 16.0 minutes against a 20-minute + interval, then 20.0, 21.0, 20.0 once the session stopped touching it. COUNT FROM + THE MARKER, and say in the report that the first 8 ticks were discarded and why. + §8's own clause — "Report the observed mumble rate after two weeks." The two-week clock starts the day the body is wired, which has not happened, so no date can honestly be written yet. `manual` was the correct recording rather than a proxy date, and this CONVERTED to `date 2026-09-08` on 2026-08-25, the day the body shipped diff --git a/scripts/tarbuckle-mumble.py b/scripts/tarbuckle-mumble.py index 60528f3..6d0f348 100755 --- a/scripts/tarbuckle-mumble.py +++ b/scripts/tarbuckle-mumble.py @@ -275,21 +275,25 @@ def acceptable(line: str, max_words: int = MAX_WORDS, def main() -> int: - if muted(): - return 1 kind = sys.argv[1] if len(sys.argv) > 1 else "aside" + if muted(): + log_event(kind, "muted") + return 1 transcript = sys.argv[2] if len(sys.argv) > 2 else "" register = soul_register() if not register: + log_event(kind, "no-soul") return 1 # no soul, no voice. Deliberately no fallback. material = session_material(transcript) if not material.strip(): + log_event(kind, "no-material") return 1 env = dict(os.environ, TARBUCKLE_CHILD="1") # precautionary; see body script try: r = subprocess.run(["claude", "-p", build_prompt(kind, register, material)], capture_output=True, text=True, timeout=120, env=env) except Exception: + log_event(kind, "generator-failed") return 1 line = (r.stdout or "").strip().strip('"').strip() ok, why = acceptable(line) @@ -306,6 +310,7 @@ def main() -> int: with open(SLOT, "w") as fh: json.dump({"utterance": line, "kind": kind, "written": int(time.time())}, fh) except Exception: + log_event(kind, "slot-write-failed") return 1 log_event(kind, "spoke") return 0 @@ -394,6 +399,18 @@ def selftest() -> int: not echoes_soul("Second surface you've found for the same voice.", _soul)) ck("A7n passes an ordinary short observation", not echoes_soul("Third one that's held together with a name.", _soul)) + # A9 — ⚠ EVERY EXIT FROM main() MUST REPORT ITSELF. Earned 2026-08-25: a live + # `aside` draw vanished with no record, because the muted/no-soul/no-material/ + # generator-failed/slot-write paths all returned silently. The rate report that + # REVIEWED-128 binds is computed from this log, so an unlogged exit does not just + # lose a datum — it makes the denominator wrong while looking complete. + _src = open(__file__, encoding="utf-8").read() + _main = _src[_src.index("def main() -> int:"):_src.index("def selftest")] + _returns = _main.count("return 1") + _logged = _main.count("log_event(kind,") + ck("A9 every silent exit reports itself", _logged >= _returns) + ck("A9n the predicate can fail", _returns > 0) + # A8 — THE JURIST'S CONDITION, structural. An accepted line has no `why`, so there # is no call from which it could be written. _r = REJECTS