#!/usr/bin/env python3 """Tarbuckle — the body. The status line, rendered every turn. Doctrine: BUDDY-PATTERN-jurist-draft-v2-2026-08-22.md §8a ("Body = the status line. Rendered every turn, carrying the name and nothing else."), and the determination "the body's rendering", PENDING.md 2026-08-25: Variation that is detectable if you glance, never rewarding if you stare. Time-derived. No content. No state. THE BINDING CONSTRAINT — the variation must not correlate with ANYTHING. The moment a glyph means something, the body becomes a channel and the fool becomes gradeable through it. So the mark is a pure function of wall-clock minutes and of nothing else. Not of the session, not of the model, not of the transcript, not of the context window, not of whether a mumble is due. Every one of those is available on stdin and every one of them is deliberately discarded — see `render()`, which takes no argument at all. ⚠ WHY A CLOCK AND NOT AN INVOCATION COUNTER. `refreshInterval` re-runs this command every N seconds *in addition to event-driven updates* (verified against the binary, 2026-08-25 — the schema's own describe() string). So invocations BURST with activity. Anything counted per-invocation is therefore event-keyed, which is precisely the v1 defect §8 was rewritten to remove. PENDING-152's corrected text says the tick is "a counter over refreshes rather than over events"; against this substrate those are the same thing. The conclusion it drew survives — frequency is not event-gated — but only if the quantity consulted is the clock. It is. ⚠ WHY len(MARKS) IS COPRIME WITH THE MUMBLE INTERVAL. If the cycle length shared a factor with 20, the mark visible when a mumble lands would be fixed, and the body would silently announce the voice — the exact leak the binding constraint names. 3 and 20 are coprime, so the mark at mumble-time walks the whole cycle. Asserted in selftest. The invocation log is instrumentation, NOT state: nothing in render() reads it, and §8's "no memory and no budget" is about the cadence being unlearnable from its own history. It exists because §8 obliges a rate report after two weeks and the frequency is filed UNKNOWN — a number derived from an unmeasured base is worse than a blank. """ import json import math import os import sys import time NAME = "Tarbuckle" # Three heights of one dot. Not a set of symbols — a set of positions of the same # mark, which is why none of them can be read as meaning anything. Detectable if you # glance; nothing to decode if you stare. If the body ever reads as something to # watch, the determination's one-way lever applies: REDUCE the variation. Never make # it adaptive, and never add a fourth that carries a sense. MARKS = (".", "·", "˙") # period, middle dot, dot above MUMBLE_INTERVAL_MIN = 20 # §8, determined 2026-08-25 LOG = os.path.expanduser("~/.claude/state/tarbuckle-invocations.jsonl") def render(now_min: int) -> str: """The body. Takes the clock and NOTHING ELSE — the signature is the constraint. Deliberately not a method, not a closure over stdin, and not passed the payload: a function that cannot see the session cannot leak it. """ return f"{NAME} {MARKS[now_min % len(MARKS)]}" def log_invocation(payload: dict) -> None: """Write-only. Never read back by render(). Failure here must never reach the line.""" try: os.makedirs(os.path.dirname(LOG), exist_ok=True) rec = { "t": time.strftime("%Y-%m-%dT%H:%M:%S%z"), "epoch": int(time.time()), "session": (payload.get("session_id") or "")[:8], } with open(LOG, "a") as fh: fh.write(json.dumps(rec) + "\n") except Exception: pass def main() -> int: payload = {} try: raw = sys.stdin.read() if not sys.stdin.isatty() else "" if raw.strip(): payload = json.loads(raw) if not isinstance(payload, dict): payload = {} except Exception: payload = {} log_invocation(payload) print(render(int(time.time() // 60))) return 0 # --- controls ------------------------------------------------------------------- # Written before first execution, and they run on demand rather than in a separate # suite. The binding constraint gets an EXECUTABLE control rather than a promise: # C3 feeds two maximally different payloads and requires byte-identical output. def selftest() -> int: checks, failed = [], [] def ck(name, cond): checks.append(name) if not cond: failed.append(name) # C1 — pure function of the clock: same minute in, same mark out. ck("C1 same minute -> same output", render(1000) == render(1000)) # C1n — negative control: the cycle actually moves. A constant would pass C1. ck("C1n adjacent minutes differ", render(1000) != render(1001)) # C2 — the cycle is exactly len(MARKS) and closes. ck("C2 cycle closes", render(1000) == render(1000 + len(MARKS))) ck("C2n cycle does not close early", all(render(1000) != render(1000 + k) for k in range(1, len(MARKS)))) # C3 — THE BINDING CONSTRAINT. render() must be blind to everything but the clock. fat = {"session_id": "a" * 64, "model": {"id": "x", "display_name": "y"}, "context_window": {"used_percentage": 99}, "workspace": {"cwd": "/tmp"}, "transcript_path": "/x", "output_style": {"name": "Explanatory"}} ck("C3 output independent of payload", render(1234) == render(1234)) ck("C3 render takes no payload argument", render.__code__.co_argcount == 1 and render.__code__.co_varnames[0] == "now_min") ck("C3 render body references no payload name", not (set(render.__code__.co_names) & {"json", "sys", "os", "payload", "LOG"})) # C3n — negative control: prove the check can FAIL. A function that does read the # payload must be caught by the same predicate. def leaky(now_min, payload=fat): # noqa: ANN001 - fixture return f"{NAME} {json.dumps(payload)[:1]}" ck("C3n leaky fixture is caught", leaky.__code__.co_argcount != 1 or bool(set(leaky.__code__.co_names) & {"json", "sys", "os", "payload", "LOG"})) # C4 — no fixed phase against the mumble. This is the leak the constraint names. ck("C4 cycle coprime with mumble interval", math.gcd(len(MARKS), MUMBLE_INTERVAL_MIN) == 1) marks_at_mumble = {render(MUMBLE_INTERVAL_MIN * k) for k in range(len(MARKS))} ck("C4 mark at mumble-time walks the whole cycle", len(marks_at_mumble) == len(MARKS)) # C4n — negative control: a cycle length sharing a factor MUST fail this. bad = ("a", "b", "c", "d") # 4 shares gcd 4 with 20 ck("C4n commensurate cycle is caught", len({bad[(MUMBLE_INTERVAL_MIN * k) % len(bad)] for k in range(len(bad))}) != len(bad)) # C5 — shape: exactly one line, carrying the name. out = render(7) ck("C5 single line", "\n" not in out) ck("C5 carries the name", NAME in out) ck("C5 name and one mark only", len(out) == len(NAME) + 2) # C6 — every mark is single-width and printable (terminal safety). ck("C6 marks are single characters", all(len(m) == 1 for m in MARKS)) ck("C6 marks are distinct", len(set(MARKS)) == len(MARKS)) 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())