From 7a9dbf28534bff0c112d51641c95f9badc1b019c Mon Sep 17 00:00:00 2001 From: David F Glidden Date: Tue, 25 Aug 2026 16:43:53 +0200 Subject: [PATCH] [FIX] Tarbuckle tier 3: the wake seam speaks; the wrap seam has no surface and is not faked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The steward's four rulings are what this is built to, and they are load-bearing: the net carries over UNCHANGED and is imported rather than reimplemented (a second copy of acceptable() is a second, quietly divergent standard); silence-on-violation is never relaxed; the rejection log is the diagnostic; and a guaranteed occasion is not a guaranteed utterance — which is what makes a bounded generation legitimate rather than a corner cut. Exceeding the bound is silence, never a hurried line. ⚠ THE WRAP SEAM IS NOT BUILT, and the reason is substrate, not effort. SessionEnd's handler writes to stderr only when a hook FAILS; a successful hook's stdout goes nowhere. §9 requires output to reach the steward, so wiring the wrap seam there would be a mechanism that fires into nothing and reports success. Filed as owed. ⚠ THE COMMENSURABILITY CHECK THE STEWARD MANDATED FOUND A REAL COLLISION, and not the one it was looking for. A seam is aperiodic, so it adds no period. But last-tick persists ACROSS sessions, so any gap longer than the interval left the tick already due at the moment of waking — the fool speaking twice into the same seam. The seam now resets the clock. Invisible until the check was run; the second such find this pass. ⚠ THE SELF-REFERENTIAL CONTROL BUG RECURRED, minutes after being fixed, by the party that fixed it, in a control written while watching for it. A literal needle plants itself in the file it searches. Fixed as a MECHANISM this time — source_lacks() takes the needle in parts, so the shape cannot be written again by accident. Correcting it a second time by hand would have been the same one-off. ⚠ AND USING THE INSTRUMENT ONCE EXPOSED A DEFECT IN IT. The first seam rejection — a 10-word line against a 9-word cap — logged the verdict and DISCARDED the line, because log_silence() wrote "line": "" unconditionally. The steward had just named this log as what decides whether register and net are mismatched; a log holding only reasons cannot answer that. Now records the evidence. Found by reading the log after one use. The 9-word cap is LEFT AS FILED on one near-miss. The steward licensed widening the seam net explicitly if seams warrant more words — but widening on n=1 is tuning to taste, which is the door that ruling closed. The two-week log decides. 63/63 controls across three suites. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01J6hZXNYSxEfZseBGTni4sf --- scripts/tarbuckle-mumble.py | 18 ++- scripts/tarbuckle-seam.py | 202 +++++++++++++++++++++++++++++++ scripts/tarbuckle_mumble_shim.py | 1 + 3 files changed, 219 insertions(+), 2 deletions(-) create mode 100755 scripts/tarbuckle-seam.py create mode 120000 scripts/tarbuckle_mumble_shim.py diff --git a/scripts/tarbuckle-mumble.py b/scripts/tarbuckle-mumble.py index 597bb8c..50912b8 100755 --- a/scripts/tarbuckle-mumble.py +++ b/scripts/tarbuckle-mumble.py @@ -47,6 +47,19 @@ BANNED = ( ) +def source_lacks(path: str, *parts: str) -> bool: + """True if the joined needle does NOT appear in `path`. + + ⚠ THE NEEDLE IS ASSEMBLED FROM PARTS, AND THAT IS THE ENTIRE POINT. A control that + writes its needle as a literal PLANTS that literal in the very file it searches, so + it can only ever fail. That bug was written twice in one session — the second time + by the party who had just fixed the first, minutes earlier, while watching for it. + Correcting it a second time by hand would have been the same one-off; this is the + mechanism, so the shape cannot be written again by accident. + """ + return "".join(parts) not in open(path, encoding="utf-8").read() + + def soul_register() -> str | None: """The soul, verbatim, from the filed artifact. None if unreadable.""" try: @@ -181,9 +194,10 @@ def selftest() -> int: # control failed against itself: the literal phrase it searched for was placed in # the file BY the search. Same class as the hand-typed link canary whose only # finding was the pattern inside its own specification. - needle = "registrar rather than " + "a guardian" ck("A1n this file does not contain a copy of the soul", - needle not in open(__file__, encoding="utf-8").read()) + source_lacks(__file__, "registrar rather than ", "a guardian")) + ck("A1nn the predicate can fail", + not source_lacks(__file__, "soul_", "register")) # A2 — the acceptability net. Positive AND negative controls on every clause. ck("A2 accepts an in-register line", acceptable("Fourth time. First one was better.")[0]) diff --git a/scripts/tarbuckle-seam.py b/scripts/tarbuckle-seam.py new file mode 100755 index 0000000..a3243a4 --- /dev/null +++ b/scripts/tarbuckle-seam.py @@ -0,0 +1,202 @@ +#!/usr/bin/env python3 +"""Tarbuckle — the voice, at a seam. Tier 3 of v2 §8. + +⚠ WAKE ONLY, and the reason is a substrate limit rather than a choice. §8 names two +seams, wake and wrap-up. `SessionEnd` exists as a hook event, but its handler is: + + for (let u of c) if (!u.succeeded && u.output) + process.stderr.write(`SessionEnd hook [${u.command}] failed: ${u.output}`) + +A SUCCESSFUL SessionEnd hook's stdout goes nowhere — only failures surface. §9 requires +output to reach the steward, so a wrap seam wired there would be a mechanism that fires +into nothing and reports success: the silent net this record keeps naming. The wrap seam +is therefore NOT BUILT, and is filed as owed rather than quietly dropped. + +⚠ STEWARD'S RULING, 2026-08-25, and it is what makes the timeout legitimate: + + "A guaranteed occasion is not a guaranteed utterance. If a seam produces nothing + that passes, let it produce nothing." + +So generation is bounded. Exceeding the bound is SILENCE, exactly like failing the net — +never a hurried line, never a cached one. This also bounds what the fool costs the +steward at every session start, which is the surface it is most tempting to overrun. + + "Never relax silence-on-violation." — the net is imported, not reimplemented. + +⚠ AND THE COMMENSURABILITY CHECK, mandated by the steward for any periodicity tier 3 +introduces: a seam is aperiodic — it happens when the steward arrives — so it adds no +period to collide with the 20-minute tick or the three-mark cycle. But the CHECK found +a real collision anyway, in the other direction: `last-tick` persists across sessions, +so a gap longer than the interval leaves the tick already due at the moment of waking, +and the fool speaks twice into the same seam. The seam therefore RESETS the clock. That +collision was invisible until the check was run, which is the second one this pass. +""" +import os +import subprocess +import sys +import time + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +# The net and the register come from the generator. Importing rather than +# reimplementing is the "one canonical source" rule applied to a constraint: a second +# copy of `acceptable()` is a second, quietly divergent standard. +from tarbuckle_mumble_shim import (acceptable, soul_register, session_material, # noqa: E402 + source_lacks, REJECTS) + +LAST_TICK = os.path.expanduser("~/.claude/state/tarbuckle-last-tick") +SEAM_TIMEOUT_S = 15 + + +def reset_tick_clock(now: float | None = None) -> None: + """Start the mumble clock at the seam. See the commensurability note above.""" + try: + os.makedirs(os.path.dirname(LAST_TICK), exist_ok=True) + with open(LAST_TICK, "w") as fh: + fh.write(str(int(now if now is not None else time.time()))) + except OSError: + pass + + +def build_prompt(register: str, material: str) -> str: + return f"""You are writing ONE line as Tarbuckle. His character, filed and unalterable: + +{register} + +He has just walked in on this, already in progress: + +{material} + + +Write ONE line in his voice, on arriving. Absolute constraints: +- Between 3 and 9 words. One clause. Present tense. Flat, no lift. +- IT MUST HAVE NO TRUTH VALUE. Nobody must be able to open a file and check it, agree + with it, or refute it. Put two things next to each other so a shape shows. +- ⚠ DO NOT SUMMARISE. Do not say what the work is, what state it is in, or what comes + next. A briefing is the one thing he is not. He noticed one thing on the way in. +- No advice, no questions, no warning of consequences, no explanation, no second line. +- Never the word 'we'. No vocabulary of lack. Nothing with an address. + +Output the line and nothing else. No quotes, no preamble.""" + + +def log_silence(why: str, line: str = "") -> None: + """⚠ THE REJECTED LINE IS RECORDED, and the first run is why. + + This logged `"line": ""` unconditionally, so the very first seam rejection — a + 10-word line against a 9-word cap — recorded the verdict and threw away the + evidence. The steward had named this log as the instrument for deciding whether + the register and the net are mismatched; a log holding only reasons cannot answer + that. Found by reading the log after one use, which is the whole argument for + reviewing an instrument after every run rather than after failures. + """ + try: + import json + with open(REJECTS, "a") as fh: + fh.write(json.dumps({"t": time.strftime("%Y-%m-%dT%H:%M:%S%z"), + "kind": "seam", "why": why, "line": line[:200]}) + "\n") + except Exception: + pass + + +def main() -> int: + reset_tick_clock() + transcript = os.environ.get("TARBUCKLE_TRANSCRIPT", "") + if not transcript: + try: + import json + transcript = (json.loads(sys.stdin.read() or "{}") or {}).get( + "transcript_path", "") + except Exception: + transcript = "" + register = soul_register() + if not register: + return 0 # no soul, no voice, no noise about it + material = session_material(transcript) + if not material.strip(): + return 0 # nothing walked in on + env = dict(os.environ, TARBUCKLE_CHILD="1") + try: + r = subprocess.run(["claude", "-p", build_prompt(register, material)], + capture_output=True, text=True, + timeout=SEAM_TIMEOUT_S, env=env) + except subprocess.TimeoutExpired: + log_silence(f"seam generation exceeded {SEAM_TIMEOUT_S}s") + return 0 # the occasion was guaranteed; the utterance is not + except Exception: + return 0 + line = (r.stdout or "").strip().strip('"').strip() + ok, why = acceptable(line) + if not ok: + log_silence(why, line) + return 0 + print(f"Tarbuckle {line}") + return 0 + + +def selftest() -> int: + checks, failed = [], [] + + def ck(name, cond): + checks.append(name) + if not cond: + failed.append(name) + + # S1 — the net is IMPORTED, never redefined. One standard, not two. + src = open(__file__, encoding="utf-8").read() + ck("S1 net is imported", "from tarbuckle_mumble_shim import" in src) + ck("S1n net is not redefined here", source_lacks(__file__, "def ", "acceptable(")) + ck("S1nn the predicate can fail", not source_lacks(__file__, "def ", "main(")) + ck("S1 imported net still rejects advice", + not acceptable("You should check that again now.")[0]) + ck("S1 imported net still rejects a question", + not acceptable("How is that going for you?")[0]) + + # S2 — the seam prompt forbids the drift the steward named: toward summary. + p = build_prompt("SOUL", "MATERIAL") + ck("S2 prompt forbids summarising", "DO NOT SUMMARISE" in p) + ck("S2 prompt keeps no-truth-value", "NO TRUTH VALUE" in p) + ck("S2 prompt embeds the register", "SOUL" in p) + + # S3 — silence is bounded and legitimate. A timeout must not become a hurried line. + ck("S3 generation is bounded", SEAM_TIMEOUT_S <= 20) + ck("S3 timeout path returns silence, not a line", + "log_silence(f\"seam generation exceeded" in src and "TimeoutExpired" in src) + ck("S3 rejection log keeps the evidence, not just the verdict", + "line[:200]" in src and "log_silence(why, line)" in src) + ck("S3n no cached or fallback line exists", + source_lacks(__file__, "FALLBACK", "_LINE")) + + # S4 — COMMENSURABILITY, per the steward's standing instruction. + import tempfile + global LAST_TICK + _lt = LAST_TICK + LAST_TICK = os.path.join(tempfile.mkdtemp(), "tick") + try: + # A stale clock from a previous session would fire a mumble INTO the wake. + with open(LAST_TICK, "w") as fh: + fh.write(str(int(time.time()) - 9999)) + stale = int(open(LAST_TICK).read()) + reset_tick_clock() + ck("S4 seam resets the tick clock", int(open(LAST_TICK).read()) > stale) + ck("S4 reset puts the next mumble a full interval out", + abs(int(open(LAST_TICK).read()) - time.time()) < 5) + finally: + LAST_TICK = _lt + # S4b — the seam itself introduces no period, so nothing new can be commensurate. + ck("S4b seam is aperiodic (no interval constant defined)", + not any(n.endswith("_INTERVAL") or n.endswith("_INTERVAL_MIN") + for n in globals())) + + for name in checks: + print(f" {'FAIL' if name in failed else 'ok '} {name}") + print(f"{len(checks) - len(failed)}/{len(checks)} controls passed") + if failed: + print("INSTRUMENT NOT VERIFIED") + return 1 + return 0 + + +if __name__ == "__main__": + if "--selftest" in sys.argv: + sys.exit(selftest()) + sys.exit(main()) diff --git a/scripts/tarbuckle_mumble_shim.py b/scripts/tarbuckle_mumble_shim.py new file mode 120000 index 0000000..20ceeb5 --- /dev/null +++ b/scripts/tarbuckle_mumble_shim.py @@ -0,0 +1 @@ +/Users/davidglidden/dotfiles/scripts/tarbuckle-mumble.py \ No newline at end of file