[FIX] The fortnight read, banked content-free; the corpus and its writer both retired (REVIEWED-136)

REVIEWED-136 and AMENDMENT 1. The 2026-09-08 obligation, run a day late because
the steward could not reach the machine.

Why the writer was stopped and not just the file removed. `log_rejection()` opens
in append mode, which recreates the log on the next rejection. Deleting the file
alone would have retired 101 entries into a successor accumulating under no
condition — condition 2's rationale defeated the moment it was honoured, at the
W2 rate within hours. `REJECT_LOGGING_ENABLED = False` makes the write path inert
at the single shared call site; the four surfaces reach it through one import and
a symlink. What replaces it is NOT ruled: condition G files the mechanism question
open, and restoring the path needs a ruling, not a constant flip.

The A8 controls are kept, not adjusted to pass. Condition G suspends the jurist's
structural guarantee; it does not repeal it. A8/A8n now run under a temporarily
enabled flag, where they double as the positive control proving the new G check
can observe a write at all. G fails correctly when the constant is flipped —
verified against a probe copy.

What was banked before deletion, because none of it can be recovered after:
per-day word-count histograms (the scattered/clustered judgment is temporal, and
two windows could not carry it), per-surface counts, rate blocks by window, and
the normalisation map. Residue 0 of 101 against must-not-classify controls, so the
zero is not vacuous. `why` is not content-free by construction — `echoes_soul()`
returns a literal 4- or 6-word run from the suppressed line — so recital payloads
are discarded unconditionally.

The rejects snapshot is deleted, not committed. It was a verbatim corpus copy;
committing it would have defeated condition 2 permanently in git history, where it
cannot be undone without a rewrite. Leak-gated while the corpus still existed to
test against: 100 hits on the snapshot as positive control, 0 on all five
committed artifacts.

Scope: `tarbuckle-rejects.jsonl` and its snapshot only. `tarbuckle-draws.jsonl`
and `tarbuckle-invocations.jsonl` are untouched — they are not under condition 2
and they hold the input-distribution confound the verdicts sitting needs.

No verdicts offered. Rate, distribution and shape are figures; scattered-versus-
clustered, the 6.7 s question and any cap consequence are reserved to the jurist
and steward.

Selftests 39/15/25/21, all four surfaces green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbXpZup4GGCbLBbRJ79KpM
This commit is contained in:
David F Glidden
2026-09-09 17:28:26 +02:00
co-authored by Claude Opus 5
parent 10b6f46812
commit 3d45e3abb2
6 changed files with 13206 additions and 3 deletions
+34 -3
View File
@@ -33,6 +33,7 @@ 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
@@ -116,7 +117,22 @@ def log_rejection(why: str, line: str, surface: str = "") -> None:
⚠ 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:
@@ -320,7 +336,7 @@ def main() -> int:
def selftest() -> int:
global MUTE, REJECTS
global MUTE, REJECTS, REJECT_LOGGING_ENABLED
import tempfile
checks, failed = [], []
@@ -416,9 +432,24 @@ def selftest() -> int:
# 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
_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")
@@ -429,7 +460,7 @@ def selftest() -> int:
ck("A8 accepted lines never appear",
"second accepted" not in open(REJECTS).read())
finally:
REJECTS = _r
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",