diff --git a/PENDING.md b/PENDING.md index 273f624..b1759e6 100644 --- a/PENDING.md +++ b/PENDING.md @@ -942,6 +942,22 @@ already-filed, already-authorized reads to be performed.** Starving for want of different condition from starving for want of an instrument, and it is the cheaper one to end. + +### AMENDMENT — 2026-08-25 — what this item can and cannot expect to receive from the fourth position + +*Jurist-directed, from PENDING-159. Filed here because this item is the consumer, and a consumer that does not know what arrives will infer from silence.* + +**This item asks whether the parties' misses correlate. A differently-positioned party is the only instrument that could produce evidence either way — and the terms on which any such evidence arrives are now fixed, and are narrow.** + +- **It arrives only if the steward chooses to note it.** The provenance marker is optional and always will be. Its absence means nothing: not that no outside observation occurred, only that none was marked. ⚠ **This item may never read an empty period as a negative result.** +- **It arrives one at a time, never as a rate.** The datum is *this observation had an outside origin*. ⚠ **It may not be aggregated.** *"Four of eleven this month came from outside"* turns a fact about the steward's relaying into a measurement, and a measurement invites the question of whether it is accurate — which is the gradeability §2 forbids arriving through the side door. +- **It never carries content or attribution.** §9: the claim is the steward's, in his words. The marker says an origin was outside the trio; it does not say what was said or who said it. +- ⚠ **It is not neutral.** A marked observation arrives in front of the jurist differently and is likely to be weighed more heavily. **Anything this item concludes through the marker is concluded through that effect**, and must say so. + +**Consequence for this item's design, stated so it is not discovered later:** the fourth position **cannot supply a correlation statistic.** It can supply individual instances, unaggregated, in unknown proportion to the instances that occurred. ⚠ **If this item's falsifier requires a rate, that falsifier cannot be satisfied this way and the item must find another instrument or say plainly that it has none.** + +**And the reason the terms are narrow is not caution — it is that the alternatives collapse the position.** Counting makes it a measurement; implementing the marker makes it a channel; giving the jurist eyes makes it adjudication. Each is PENDING-159's option 3 arriving by a different road. + ## PENDING-90 — First L2 transfer: checker position in the calibration loop **Date:** 2026-08-02 **Tag:** [ESCALATE] @@ -5309,3 +5325,53 @@ The item flagged §11's shared-formation hazard. **The jurist says that is the w ⚠ **Nothing is built for this and nothing should be.** A marker with an implementation is a channel, and a channel is option 3 arriving by the back door. The executor notes this explicitly because its own reflex on reading (e) was to reach for a script. **Awaiting:** steward. Option 3 requires no further consideration. + +### AMENDMENT 2 — 2026-08-25 — the jurist answers the item's own caveat, and narrows (e) accordingly + +*Two corrections from the jurist, both of their own prior positions, received via the steward.* + +#### (a) The `[FIX]` on §9 was over-applied, and the executor applied it as given + +The jurist had said the clause should read that the *executor* yields *"since it's the only party that can"* — and the executor struck the whole disjunction and rewrote it. **Both were wrong in the same direction.** + +> *"The clause reads 'the executor or jurist yields the floor' — a disjunction, and the executor half is implementable and correct. So it isn't dead text; it's a clause with one live branch and one unreachable one. The fix is to strike the jurist from the disjunction, not to rewrite the clause."* + +⚠ **The executor's own failure here is worth naming: it received a `[FIX]` from the jurist and executed it without checking the clause against it.** A `[FIX]` tag licenses implementing directly; it does not license implementing *unread*. The clause was three words long and the disjunction visible in it. **Corrected in the doctrine, with both versions left visible.** + +#### (b) The item's caveat on option 2 is ANSWERED, and the answer has a limit that becomes a rule + +The item filed option 2 with: ⚠ *"untested against §2; a bare count may itself be gradeable."* The jurist takes it rather than passing it: + +> *"A bare count is gradeable only if something can be checked against it — and nothing can. There is no register of Tarbuckle's utterances to audit a count against, because §9 files nothing. The marker records that the steward attributed an origin, and that attribution has no external referent. It's a fact about his relaying, not a claim about the world."* + +⚠ **And the limit, which narrows (e) and is filed as the narrowing:** + +> *"If the marker were ever AGGREGATED — 'four of eleven observations this month came from outside' — the aggregate starts to look like a measurement, and a measurement invites the question of whether it's accurate. So: **the marker may be noted, never counted.** If PENDING-89 wants a datum, the datum is *this observation had an outside origin*, one at a time, never a rate."* + +**(e) is amended to read: option 2, optional, NEVER COUNTED, nothing built.** + +#### (c) ⚠ The standing line, restated because it is the one most likely to erode + +> *"If anything gets built for this, the ruling has been reversed by construction."* + +A marker with an implementation is a channel; a channel is option 3 by the back door. **No script, no field, no counter, no `status` line.** The executor records that its own first reflex was to build one, and that the reflex will recur. + +**Awaiting:** steward. PENDING-89 to carry the sentence in AMENDMENT 3 below. + + diff --git a/claude/governance/fool/BUDDY-PATTERN-jurist-draft-v2-2026-08-22.md b/claude/governance/fool/BUDDY-PATTERN-jurist-draft-v2-2026-08-22.md index 818bf7d..6a569b5 100644 --- a/claude/governance/fool/BUDDY-PATTERN-jurist-draft-v2-2026-08-22.md +++ b/claude/governance/fool/BUDDY-PATTERN-jurist-draft-v2-2026-08-22.md @@ -368,15 +368,18 @@ unreliable. Plan for that rather than discovering it. - **ONE LINE** for the ordinary case. Paragraph-length governance prose is the genre the trio already metabolizes; the constraint is what keeps the fool uningestible. -- **Named invocation:** the steward calls it by name, the ~~executor or jurist~~ - **executor** yields the floor, the fool answers at length. - +- **Named invocation:** the steward calls it by name, the executor ~~or jurist~~ + yields the floor, the fool answers at length. + - `mute` / `off` available at all times. Mute rate may be counted; muting is never a fault. diff --git a/claude/governance/fool/TARBUCKLE-SPEC-13.1-2026-08-25.md b/claude/governance/fool/TARBUCKLE-SPEC-13.1-2026-08-25.md index 064e023..aa4d933 100644 --- a/claude/governance/fool/TARBUCKLE-SPEC-13.1-2026-08-25.md +++ b/claude/governance/fool/TARBUCKLE-SPEC-13.1-2026-08-25.md @@ -154,6 +154,45 @@ surface and nothing else: elaborates, and elaboration is how a gesture becomes a claim. The prompt spends most of its constraint budget there, and a violation is still silence. +## 7b · The two logs, and why only one of them is a §9 problem + +| file | holds | status | +|---|---|---| +| `tarbuckle-draws.jsonl` | occurrence only — surface + outcome | **permanent.** Counting is not filing; §8 obliges a rate and a rate needs a denominator | +| `tarbuckle-rejects.jsonl` | violation reason **+ up to 200 chars of a suppressed line** | ⚠ **temporary, dies 2026-09-08** | + +**Jurist ruling, 2026-08-25.** 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 in it entered the room, so nothing can be cited from it. **Three +binding conditions:** + +1. ⚠ **Rejections only — made STRUCTURAL, not intentional.** `log_rejection()` refuses an + empty `why`, and `acceptable()` returns an empty `why` **exactly when the line + passed**. So 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. Asserted with both polarities. +2. ⚠ **Temporary.** Deleted 2026-09-08 with the report — *"a corpus of his suppressed + speech is exactly what would let someone reconstruct a register."* Tracked as + `DEFERRED-DECISION: tarbuckle-rejection-log-deleted` so it cannot be quietly retained. +3. ⚠ **Not read for content before then.** Reading as it accumulates is reading Tarbuckle + by the back door and would shape the net toward liked lines. **Not clean already:** the + executor displayed one rejected line to the steward on 2026-08-25, before the condition + existed. Disclosed, not buried. + +## 7c · The word caps, and the criterion that decides them + +Ordinary surfaces 3–9 words; invoked 180. **Both left as filed.** The steward licensed +widening on evidence, and single near-misses are not evidence. + +⚠ **The criterion is the SHAPE of the rejections, not their count** (jurist, 2026-08-25): + +> *"Scattered rejections mean the net is doing its job; clustered ones at the ceiling +> mean the ceiling is wrong."* + +**And the asymmetry that raises the stakes at one surface only:** the invoked surface is +the only place he speaks at length, so a rejection there is *a summons answered with +silence*. Read on 2026-09-08, once, with the deletion. + ## 8 · Owed ⚠ **This list omitted named invocation in its first version, on the day it was written.** diff --git a/scripts/tarbuckle-invoke.py b/scripts/tarbuckle-invoke.py index eb0a06d..7e62f0a 100755 --- a/scripts/tarbuckle-invoke.py +++ b/scripts/tarbuckle-invoke.py @@ -30,7 +30,7 @@ import time sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from tarbuckle_mumble_shim import (acceptable, soul_register, session_material, # noqa: E402 source_lacks, muted, log_event, # noqa: E402 - echoes_soul, REJECTS) + echoes_soul, log_rejection, REJECTS) MAX_WORDS_INVOKED = 180 # §9's "at length", made a number TIMEOUT_S = 90 # the steward is deliberately waiting; he may take longer @@ -87,14 +87,9 @@ Absolute constraints, none of which the floor suspends: Speak.""" -def log_silence(why: str, line: str) -> None: - try: - import json - with open(REJECTS, "a") as fh: - fh.write(json.dumps({"t": time.strftime("%Y-%m-%dT%H:%M:%S%z"), - "kind": "invoked", "why": why, "line": line[:400]}) + "\n") - except Exception: - pass +def log_silence(why: str, line: str = "") -> None: + """Routed through the canonical writer, which cannot record an accepted line.""" + log_rejection(why, line, "invoke") def main() -> int: diff --git a/scripts/tarbuckle-mumble.py b/scripts/tarbuckle-mumble.py index 7a7bebc..60528f3 100755 --- a/scripts/tarbuckle-mumble.py +++ b/scripts/tarbuckle-mumble.py @@ -90,6 +90,44 @@ def muted() -> str: 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. + """ + 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)} @@ -260,12 +298,7 @@ def main() -> int: if echo: ok, why = False, f"recited the soul: {echo!r}" if not ok: - try: - with open(REJECTS, "a") as fh: - fh.write(json.dumps({"t": time.strftime("%Y-%m-%dT%H:%M:%S%z"), - "kind": kind, "why": why, "line": line[:200]}) + "\n") - except Exception: - pass + log_rejection(why, line, kind) log_event(kind, "rejected") return 1 # silence. The draw was already consumed. try: @@ -279,6 +312,8 @@ def main() -> int: def selftest() -> int: + global MUTE, REJECTS + import tempfile checks, failed = [], [] def ck(name, cond): @@ -333,8 +368,6 @@ def selftest() -> int: 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. - import tempfile - global MUTE _m = MUTE MUTE = os.path.join(tempfile.mkdtemp(), "mute") try: @@ -361,6 +394,22 @@ def selftest() -> int: 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)) + # 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 + REJECTS = os.path.join(tempfile.mkdtemp(), "rej.jsonl") + try: + 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 = _r ck("A6n it cannot be handed a line", "line" not in log_event.__code__.co_varnames) ck("A4n the predicate can fail", diff --git a/scripts/tarbuckle-seam.py b/scripts/tarbuckle-seam.py index 745cfca..e81b974 100755 --- a/scripts/tarbuckle-seam.py +++ b/scripts/tarbuckle-seam.py @@ -42,7 +42,7 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) # copy of `acceptable()` is a second, quietly divergent standard. from tarbuckle_mumble_shim import (acceptable, soul_register, session_material, # noqa: E402 source_lacks, muted, log_event, # noqa: E402 - echoes_soul, REJECTS) + echoes_soul, log_rejection, REJECTS) LAST_TICK = os.path.expanduser("~/.claude/state/tarbuckle-last-tick") SEAM_TIMEOUT_S = 15 @@ -84,22 +84,8 @@ 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 + """Routed through the canonical writer, which cannot record an accepted line.""" + log_rejection(why, line, "seam") def main() -> int: diff --git a/scripts/tarbuckle-wrap.py b/scripts/tarbuckle-wrap.py index be7f1f8..bba193f 100755 --- a/scripts/tarbuckle-wrap.py +++ b/scripts/tarbuckle-wrap.py @@ -32,7 +32,7 @@ import time sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from tarbuckle_mumble_shim import (acceptable, soul_register, session_material, # noqa: E402 source_lacks, muted, log_event, # noqa: E402 - echoes_soul, REJECTS) + echoes_soul, log_rejection, REJECTS) FIRED = os.path.expanduser("~/.claude/state/tarbuckle-wrap-fired") TIMEOUT_S = 15 @@ -132,12 +132,8 @@ Output the line and nothing else. No quotes, no preamble.""" def log_silence(why: str, line: str = "") -> None: - try: - with open(REJECTS, "a") as fh: - fh.write(json.dumps({"t": time.strftime("%Y-%m-%dT%H:%M:%S%z"), - "kind": "wrap", "why": why, "line": line[:200]}) + "\n") - except Exception: - pass + """Routed through the canonical writer, which cannot record an accepted line.""" + log_rejection(why, line, "wrap") def main() -> int: