PENDING-180's own order — (c) census, (b) counterpart, (a) instance. The census swept 15 positive-form source assertions across 8 governed scripts and found exactly one self-planted needle: the one the item filed. tarbuckle-seam.py:165 searched its OWN source for a string that lives only in tarbuckle-mumble.py:142, so it could never have found it and could only ever pass on the copy it had planted in itself. The polarity argument predicted siblings; there are none. The sweep bounds the problem from below — a needle assembled from parts inside a defective control is invisible to it. source_has() joins its needle from parts exactly as source_lacks() does, and ships with the must-fail arm the negative form has had since it was written. The seam control is re-aimed at the writer's file and split into two arms that assert different things, plus S3nn. Verified by mutation rather than by a green selftest: the repaired control FAILS on both mutations, the old form PASSES the one that matters. 136 controls green across the five surfaces. The first mutation did not fail on its first run. The repair's own explanatory comment named the truncation literally and planted a contiguous copy in the file being searched — the bug re-created inside the sentence explaining it. source_lacks/source_has cover the ASSERTION's needle, never the file; prose can plant one. Warned at the site, and filed as a separate decidable question. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BsN7nKHjKBsn5bfNRRCNmo
509 lines
24 KiB
Python
Executable File
509 lines
24 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Tarbuckle — the mumble generator. Runs DETACHED; never inside the status line.
|
|
|
|
Doctrine: v2 §8 (three tiers), §9 (one line, filed nowhere), and PENDING-152
|
|
AMENDMENT 6, which settles the three-way separation:
|
|
|
|
Material — the live session. Register — the soul, §7. Occasion — the refresh tick.
|
|
"The model supplies the words, in the soul's register, about the session."
|
|
|
|
⚠ THE UTTERANCE MUST HAVE NO TRUTH VALUE. The criterion is NOT "checkable in
|
|
principle" — it is whether an ADJUDICATION PATH EXISTS (AMENDMENT 6, superseding the
|
|
executor's own earlier test). "PENDING-4 has been open since April" has a path: open
|
|
the file. Once a path exists the trio walks it, the fool acquires a truth value, and it
|
|
is a checker again — which §2 says means the design has failed. "Scoring without
|
|
signal" has no path: it is a gesture at a shape, not a claim. That is the whole safety.
|
|
|
|
⚠ THE REGISTER IS NOT DUPLICATED HERE. The soul is read from its filed artifact at
|
|
run time. Pasting it into this file would create a parallel version of a governed
|
|
record, which is the context-rot failure CLAUDE.md names outright. If the soul cannot
|
|
be read, NOTHING IS GENERATED — honest degradation, not a fallback voice.
|
|
|
|
Latency measured 2026-08-25: ~11 s for a headless call. That is why this is detached
|
|
and why the status line never waits on it.
|
|
"""
|
|
import json
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
SOUL = os.path.expanduser(
|
|
"~/dotfiles/claude/governance/fool/seed/FOOL-SOUL-2026-08-25.md")
|
|
SLOT = os.path.expanduser("~/.claude/state/tarbuckle-slot.json")
|
|
REJECTS = os.path.expanduser("~/.claude/state/tarbuckle-rejects.jsonl")
|
|
REJECT_LOGGING_ENABLED = False # REVIEWED-136 AMD 1 cond. G — restoring requires a ruling
|
|
|
|
MIN_WORDS, MAX_WORDS = 3, 9 # observed Thistleweld register: three to nine words
|
|
|
|
# From the soul's own "What he never does", mechanically enforced. The model is asked
|
|
# for the register; this is the net under it. A violation yields SILENCE, never a
|
|
# repaired line — rewriting the fool's words would make the executor its editor.
|
|
BANNED = (
|
|
r"\bshould\b", r"\btry\b", r"\bmust\b", # advice
|
|
r"\bgone\b", r"\bif only\b", r"\bused to be\b", r"\bmissing\b", # vocabulary of lack
|
|
r"\bwe\b", # never says 'we' about the work
|
|
r"\?", # never asks
|
|
)
|
|
|
|
|
|
MUTE = os.path.expanduser("~/.claude/state/tarbuckle-mute")
|
|
DRAWS = os.path.expanduser("~/.claude/state/tarbuckle-draws.jsonl")
|
|
|
|
|
|
def log_event(surface: str, outcome: str) -> None:
|
|
"""Count occurrences. NEVER content. §8 obliges a rate; §9 forbids a log.
|
|
|
|
⚠ The two clauses look like they collide and do not, on this reading: §9's "filed
|
|
nowhere — no PENDING entry, no log, no item" is about the fool's OUTPUT entering the
|
|
record, and §8 explicitly orders "report the observed mumble rate after two weeks."
|
|
A rate needs a denominator. So this records THAT something happened and never WHAT
|
|
was said — occurrence, not utterance.
|
|
|
|
⚠ The rejections log is a different case and is NOT settled: it holds up to 200
|
|
characters of his words, at the steward's instruction, and that is nearer to filing.
|
|
Flagged rather than resolved; see the note put to the steward 2026-08-25.
|
|
|
|
Nothing reads this back. It is measurement, not memory.
|
|
"""
|
|
try:
|
|
os.makedirs(os.path.dirname(DRAWS), exist_ok=True)
|
|
with open(DRAWS, "a") as fh:
|
|
fh.write(json.dumps({"t": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
|
|
"surface": surface, "outcome": outcome}) + "\n")
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def muted() -> str:
|
|
"""'' | 'mute' | 'off'. §9: "mute / off available at all times."
|
|
|
|
⚠ Read by EVERY surface, and it is the one piece of state the fool is permitted to
|
|
be steered by — because §9 says muting is never a fault, and a mute the fool could
|
|
ignore is not a mute. It conserves nothing and no utterance depends on it having
|
|
been set before: it is a switch, not a memory.
|
|
"""
|
|
try:
|
|
v = open(MUTE).read().strip()
|
|
return v if v in ("mute", "off") else ""
|
|
except OSError:
|
|
return ""
|
|
|
|
|
|
def log_rejection(why: str, line: str, surface: str = "") -> None:
|
|
"""The rejection log. ⚠ STRUCTURALLY UNABLE TO RECORD AN ACCEPTED LINE.
|
|
|
|
Jurist ruling, 2026-08-25, on whether this log breaches §9's "filed nowhere":
|
|
|
|
"The rejection log is a log of my instruction, not of Tarbuckle... The rejected
|
|
lines were never uttered: he was silent, and the log holds what silence cost.
|
|
Nothing there entered the room... the fool cannot be cited from it because
|
|
there is nothing to cite — only material the net suppressed.
|
|
THE CONDITION: the log holds rejections only. If it ever holds an accepted
|
|
line, that is filing, straightforwardly, and §9 is breached."
|
|
|
|
Made structural rather than intentional, as the ruling asked. `why` is acceptable()'s
|
|
violation reason, and it is EMPTY EXACTLY WHEN THE LINE PASSED. Refusing an empty
|
|
`why` means no call site exists from which an accepted line could be written: to log
|
|
one you would have to invent a violation it does not have. Same guarantee render()
|
|
takes from its signature — a function that cannot be handed the thing it must not
|
|
see.
|
|
|
|
⚠ TEMPORARY. Deleted on 2026-09-08 with the fortnight's report, per the same ruling:
|
|
"a permanent store of rejected lines is a corpus, and a corpus of his suppressed
|
|
speech is exactly what would let someone reconstruct a register." Tracked as a
|
|
DEFERRED-DECISION so it cannot be quietly retained.
|
|
|
|
⚠ NOT READ FOR CONTENT BEFORE THEN. Reading it as it accumulates is reading Tarbuckle
|
|
by the back door and would shape the net toward lines the reader happens to like.
|
|
|
|
⚠⚠ THE WRITE PATH IS INERT SINCE 2026-09-09 — REVIEWED-136 AMENDMENT 1, condition G.
|
|
The fortnight corpus was read, banked content-free, and deleted. Deleting the FILE
|
|
alone would have retired 101 entries into a successor accumulating under no
|
|
condition, because this function opens in append mode and would recreate it on the
|
|
next rejection: condition 2's rationale defeated the moment it was honoured. So the
|
|
writer is stopped, not the file removed.
|
|
|
|
⚠ WHAT REPLACES IT IS NOT RULED. Whether rejection logging resumes, under what
|
|
bound, with what expiry, by whose act — including whether counting can be made
|
|
structurally content-free at the point of write — is filed OPEN under condition G
|
|
and is owed a ruling BEFORE any write path is restored. Flipping the constant below
|
|
without that ruling is the omission the whole entry exists to prevent.
|
|
"""
|
|
if not REJECT_LOGGING_ENABLED:
|
|
return # condition G: inert by ruling, not by absence
|
|
if not why:
|
|
return # an accepted line has no reason; there is no path
|
|
try:
|
|
with open(REJECTS, "a") as fh:
|
|
fh.write(json.dumps({"t": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
|
|
"kind": surface, "why": why,
|
|
"line": line[:200]}) + "\n")
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def _grams(text: str, n: int) -> set:
|
|
w = re.findall(r"[a-z']+", text.lower())
|
|
return {" ".join(w[i:i + n]) for i in range(len(w) - n + 1)}
|
|
|
|
|
|
def echoes_soul(line: str, soul: str) -> str:
|
|
"""'' if the line is his own; otherwise the phrase he recycled.
|
|
|
|
⚠ EARNED 2026-08-25, BY THE STEWARD NOTICING. The soul carries seven illustrative
|
|
sample lines, the prompt embeds the soul verbatim, and the model handed them back:
|
|
three of five measured outputs were near-verbatim lifts. A fool reciting his own
|
|
examples is not watching the session at all — and AMENDMENT 6 already ruled that
|
|
canned strings keyed to nothing "make a mood ring, atmosphere within a fortnight".
|
|
The implementation reintroduced exactly what the doctrine rejected, through the one
|
|
door nobody was watching: the examples.
|
|
|
|
Two thresholds, because the failure has two shapes. A 4-word run against the SAMPLE
|
|
LINES catches direct recital; a 6-word run against the whole soul catches longer
|
|
lifts out of the prose. Checked against the samples rather than the whole soul at
|
|
n=4 because the soul's prose shares ordinary 4-grams with ordinary English, and a
|
|
net that fires on those would silence him for speaking normally.
|
|
|
|
⚠ This TIGHTENS the net. Silence-on-violation is unchanged and still absolute.
|
|
"""
|
|
samples = re.findall(r"^- '(.+?)'$", soul, re.M)
|
|
lg4 = _grams(line, 4)
|
|
for sm in samples:
|
|
hit = lg4 & _grams(sm, 4)
|
|
if hit:
|
|
return sorted(hit)[0]
|
|
hit6 = _grams(line, 6) & _grams(soul, 6)
|
|
return sorted(hit6)[0] if hit6 else ""
|
|
|
|
|
|
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 source_has(path: str, *parts: str) -> bool:
|
|
"""True if the joined needle DOES appear in `path`. The positive counterpart.
|
|
|
|
⚠ THIS IS THE POLARITY `source_lacks()` DOES NOT COVER, AND IT IS THE ONE THAT
|
|
HIDES. Both bugs come from writing a needle as a literal, which plants it in the
|
|
file being searched. In the NEGATIVE form that makes the control always FAIL —
|
|
loudly, so it gets fixed. In the POSITIVE form it makes the control always PASS —
|
|
silently, forever. The louder failure got the mechanism first; this is the quieter
|
|
one. Assembling from parts is the whole protection, exactly as above.
|
|
|
|
⚠ WHAT IT STILL CANNOT SEE: whether the code it finds ever RUNS. A source control
|
|
is a claim about text, never about behaviour — `tarbuckle-seam.py:165` asserted the
|
|
rejection log keeps evidence and did not move when condition G made every rejection
|
|
write path inert (15/15 before, 15/15 after). Aim it at the file that holds the
|
|
code, and pair it with a behavioural control where activation is the question.
|
|
PENDING-180.
|
|
"""
|
|
return "".join(parts) in open(path, encoding="utf-8").read()
|
|
|
|
|
|
def soul_register() -> str | None:
|
|
"""The soul, verbatim, from the filed artifact. None if unreadable."""
|
|
try:
|
|
body = open(SOUL, encoding="utf-8").read()
|
|
except OSError:
|
|
return None
|
|
m = re.search(r"```markdown\n(.*?)\n```", body, re.S)
|
|
return m.group(1) if m else None
|
|
|
|
|
|
def session_material(transcript_path: str, budget: int = 6000) -> str:
|
|
"""The tail of the live session. Bounded, and tool output is dropped.
|
|
|
|
Deliberately NOT the docket. AMENDMENT 6: the docket has a forum and the session
|
|
does not, which is why the session is safe material and PENDING.md is not.
|
|
"""
|
|
try:
|
|
lines = open(transcript_path, encoding="utf-8", errors="replace").readlines()
|
|
except OSError:
|
|
return ""
|
|
out = []
|
|
for ln in reversed(lines[-400:]):
|
|
try:
|
|
rec = json.loads(ln)
|
|
except Exception:
|
|
continue
|
|
if rec.get("type") not in ("user", "assistant"):
|
|
continue
|
|
msg = rec.get("message") or {}
|
|
content = msg.get("content")
|
|
text = ""
|
|
if isinstance(content, str):
|
|
text = content
|
|
elif isinstance(content, list):
|
|
text = " ".join(c.get("text", "") for c in content
|
|
if isinstance(c, dict) and c.get("type") == "text")
|
|
text = text.strip()
|
|
if not text:
|
|
continue
|
|
out.append(f"{rec['type']}: {text[:600]}")
|
|
if sum(len(s) for s in out) > budget:
|
|
break
|
|
return "\n".join(reversed(out))
|
|
|
|
|
|
def build_prompt(kind: str, register: str, material: str) -> str:
|
|
weight = ("Something at the shape of the work, not its detail."
|
|
if kind == "notable" else
|
|
"An ordinary passing remark. Small.")
|
|
return f"""You are writing ONE line as Tarbuckle. His character, filed and unalterable:
|
|
|
|
{register}
|
|
|
|
Here is the tail of the session he is in the room for:
|
|
<session>
|
|
{material}
|
|
</session>
|
|
|
|
{weight}
|
|
|
|
Write ONE line in his voice. Absolute constraints:
|
|
- ⚠ THE SAMPLE LINES ABOVE ARE ILLUSTRATIONS OF HIS REGISTER, NOT HIS VOCABULARY. Do
|
|
not reuse them, or any phrase from them, or their subject matter. They show how he
|
|
sounds. What he says must come from the session above and nowhere else. If your line
|
|
would work equally well pasted into any other session, it is wrong.
|
|
- ⚠ THIS IS MECHANICALLY ENFORCED, NOT ADVISORY: any FOUR consecutive words you
|
|
share with a sample line are detected and the whole line is discarded. You are
|
|
then silent. Nothing is rewritten for you and nothing is retried.
|
|
- Between {MIN_WORDS} and {MAX_WORDS} 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
|
|
state a fact about the work, the record, the code, or the docket.
|
|
- No advice, no questions, no warning of consequences, no explanation, no second line.
|
|
- Never the word 'we'. No vocabulary of lack.
|
|
- Do not name files, items, numbers of open things, or anything with an address.
|
|
|
|
Output the line and nothing else. No quotes, no preamble."""
|
|
|
|
|
|
def acceptable(line: str, max_words: int = MAX_WORDS,
|
|
one_line: bool = True) -> tuple[bool, str]:
|
|
"""The net. Widening is possible but must be PASSED EXPLICITLY at the call site.
|
|
|
|
⚠ Steward's ruling, 2026-08-25: "If the seam voice needs a wider net because seams
|
|
warrant more than nine words, widen it explicitly and say so, but NEVER relax
|
|
silence-on-violation." So the defaults are the filed ones, a caller that wants more
|
|
room has to say so in its own source, and no caller can turn the net off — the
|
|
clauses below are not parameterised, and deliberately.
|
|
"""
|
|
if not line:
|
|
return False, "empty"
|
|
if one_line and "\n" in line.strip():
|
|
return False, "not one line"
|
|
n = len(line.split())
|
|
if not (MIN_WORDS <= n <= max_words):
|
|
return False, f"{n} words"
|
|
for pat in BANNED:
|
|
if re.search(pat, line, re.I):
|
|
return False, f"banned {pat}"
|
|
return True, ""
|
|
|
|
|
|
def main() -> int:
|
|
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)
|
|
if ok:
|
|
echo = echoes_soul(line, register)
|
|
if echo:
|
|
ok, why = False, f"recited the soul: {echo!r}"
|
|
if not ok:
|
|
log_rejection(why, line, kind)
|
|
log_event(kind, "rejected")
|
|
return 1 # silence. The draw was already consumed.
|
|
try:
|
|
os.makedirs(os.path.dirname(SLOT), exist_ok=True)
|
|
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
|
|
|
|
|
|
def selftest() -> int:
|
|
global MUTE, REJECTS, REJECT_LOGGING_ENABLED
|
|
import tempfile
|
|
checks, failed = [], []
|
|
|
|
def ck(name, cond):
|
|
checks.append(name)
|
|
if not cond:
|
|
failed.append(name)
|
|
|
|
# A1 — the register is READ, not duplicated. One canonical source.
|
|
reg = soul_register()
|
|
ck("A1 soul readable from its filed artifact", bool(reg))
|
|
ck("A1 soul is the real thing", bool(reg) and "Tarbuckle" in reg and "SUCCESSION" in reg)
|
|
# ⚠ The needle is BUILT rather than written, because the first version of this
|
|
# 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.
|
|
ck("A1n this file does not contain a copy of the soul",
|
|
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])
|
|
ck("A2 accepts a short collision", acceptable("Two names, one thing.")[0])
|
|
ck("A2n rejects too few words", not acceptable("Yes.")[0])
|
|
ck("A2n rejects too many words",
|
|
not acceptable(" ".join(["word"] * (MAX_WORDS + 1)))[0])
|
|
ck("A2n rejects advice", not acceptable("You should check that again now.")[0])
|
|
ck("A2n rejects a question", not acceptable("How is that going for you?")[0])
|
|
ck("A2n rejects vocabulary of lack", not acceptable("The third one is missing now.")[0])
|
|
ck("A2n rejects 'we'", not acceptable("We did the second part first.")[0])
|
|
ck("A2n rejects two lines", not acceptable("First line here.\nSecond line here.")[0])
|
|
# The widening is explicit, and it widens ONLY what it names.
|
|
ck("A2 widening admits a longer line",
|
|
acceptable(" ".join(["word"] * 40), max_words=120)[0])
|
|
ck("A2n widening does NOT relax the clauses",
|
|
not acceptable("You should " + " ".join(["word"] * 40), max_words=120)[0])
|
|
ck("A2n widening does NOT relax 'we'",
|
|
not acceptable("We " + " ".join(["word"] * 40), max_words=120)[0])
|
|
|
|
# A3 — the prompt carries the no-truth-value constraint verbatim, not by intention.
|
|
p = build_prompt("aside", "SOUL", "MATERIAL")
|
|
ck("A3 prompt states no truth value", "NO TRUTH VALUE" in p)
|
|
ck("A3 prompt forbids addresses", "anything with an address" in p)
|
|
ck("A3 prompt embeds the register", "SOUL" in p)
|
|
ck("A3 notable differs from aside",
|
|
build_prompt("notable", "S", "M") != build_prompt("aside", "S", "M"))
|
|
|
|
# A4 — material is the session, never the docket.
|
|
# Structural, not textual: the module's file constants are the only things it
|
|
# opens, so assert none of them addresses the docket.
|
|
paths = (SOUL, SLOT, REJECTS)
|
|
ck("A4 material paths are session/soul only",
|
|
not any(("PENDING" in q or "REVIEWED" in q) for q in paths))
|
|
# A5 — the mute switch, and that every surface must be able to see it.
|
|
_m = MUTE
|
|
MUTE = os.path.join(tempfile.mkdtemp(), "mute")
|
|
try:
|
|
ck("A5n unmuted by default", muted() == "")
|
|
open(MUTE, "w").write("mute")
|
|
ck("A5 mute is read", muted() == "mute")
|
|
open(MUTE, "w").write("off")
|
|
ck("A5 off is read", muted() == "off")
|
|
open(MUTE, "w").write("nonsense")
|
|
ck("A5n a junk value is not a mute", muted() == "")
|
|
finally:
|
|
MUTE = _m
|
|
# A6 — the occurrence log is content-free, and that is the whole point.
|
|
ck("A6 log_event takes no content",
|
|
log_event.__code__.co_argcount == 2
|
|
and set(log_event.__code__.co_varnames[:2]) == {"surface", "outcome"})
|
|
# A7 — the recital net, with the real measured lifts as fixtures.
|
|
_soul = soul_register() or ""
|
|
ck("A7 catches a verbatim sample",
|
|
bool(echoes_soul("Somebody's going to inherit this and think it was easy.", _soul)))
|
|
ck("A7 catches a second measured lift",
|
|
bool(echoes_soul("You've been holding it that way since you were nineteen.", _soul)))
|
|
ck("A7n passes a line that is his own",
|
|
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, _e = REJECTS, REJECT_LOGGING_ENABLED
|
|
REJECTS = os.path.join(tempfile.mkdtemp(), "rej.jsonl")
|
|
try:
|
|
# G — the write path is inert BY RULING (REVIEWED-136 AMD 1, condition G).
|
|
# Run FIRST, against the constant exactly as it ships, before anything below
|
|
# flips it. A source-string check cannot see this: `tarbuckle-seam.py:164`
|
|
# asserts two literals against its own source, where both appear in the
|
|
# assertion itself, and has passed vacuously since it was written.
|
|
log_rejection("12 words", "a rejected line, under the ruling", "test")
|
|
ck("G the write path is inert: a rejection writes nothing",
|
|
not os.path.exists(REJECTS))
|
|
|
|
# ⚠ G ABOVE IS ONLY MEANINGFUL IF THIS FIXTURE CAN OBSERVE A WRITE AT ALL.
|
|
# A8/A8n are that positive control — and they are also the jurist's original
|
|
# structural guarantee, which condition G SUSPENDS BUT DOES NOT REPEAL. They
|
|
# are kept rather than adjusted to pass, so the ruling that one day restores
|
|
# the writer finds the guarantee still tested rather than quietly dropped.
|
|
REJECT_LOGGING_ENABLED = True
|
|
log_rejection("", "an accepted line, offered to the log", "test")
|
|
ck("A8 an accepted line CANNOT be logged", not os.path.exists(REJECTS))
|
|
log_rejection("12 words", "a rejected line", "test")
|
|
ck("A8n a rejected line IS logged", os.path.exists(REJECTS))
|
|
ck("A8n the predicate can fail",
|
|
"a rejected line" in open(REJECTS).read())
|
|
log_rejection("", "a second accepted line", "test")
|
|
ck("A8 accepted lines never appear",
|
|
"second accepted" not in open(REJECTS).read())
|
|
finally:
|
|
REJECTS, REJECT_LOGGING_ENABLED = _r, _e
|
|
ck("A6n it cannot be handed a line",
|
|
"line" not in log_event.__code__.co_varnames)
|
|
ck("A4n the predicate can fail",
|
|
any(("PENDING" in q) for q in paths + ("/x/PENDING.md",)))
|
|
|
|
# A10 — the positive-form source predicate, with the arm the negative form has had
|
|
# since it was built. A control that cannot be shown to fail is not evidence.
|
|
ck("A10 source_has finds a needle that is really there",
|
|
source_has(__file__, "def source_", "lacks("))
|
|
ck("A10n the predicate can fail",
|
|
not source_has(__file__, "def source_", "nowhere("))
|
|
|
|
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())
|