[FIX] Tarbuckle tier 3: the wake seam speaks; the wrap seam has no surface and is not faked
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J6hZXNYSxEfZseBGTni4sf
This commit is contained in:
co-authored by
Claude Opus 5
parent
3df5e4f4bb
commit
7a9dbf2853
@@ -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:
|
def soul_register() -> str | None:
|
||||||
"""The soul, verbatim, from the filed artifact. None if unreadable."""
|
"""The soul, verbatim, from the filed artifact. None if unreadable."""
|
||||||
try:
|
try:
|
||||||
@@ -181,9 +194,10 @@ def selftest() -> int:
|
|||||||
# control failed against itself: the literal phrase it searched for was placed in
|
# 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
|
# the file BY the search. Same class as the hand-typed link canary whose only
|
||||||
# finding was the pattern inside its own specification.
|
# 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",
|
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.
|
# 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])
|
ck("A2 accepts an in-register line", acceptable("Fourth time. First one was better.")[0])
|
||||||
|
|||||||
Executable
+202
@@ -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:
|
||||||
|
<session>
|
||||||
|
{material}
|
||||||
|
</session>
|
||||||
|
|
||||||
|
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())
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
/Users/davidglidden/dotfiles/scripts/tarbuckle-mumble.py
|
||||||
Reference in New Issue
Block a user