They ended with 'it needs a hook at withdrawal time, not a sweep'. Expressible, because a correction here has a shape: a heading carrying WITHDRAWN / CORRECTION / refuted. A correction section containing no registered phrase is a death argued and never indexed. check-claims now reports them, and the first run names more than my 'four of eight' -- the shortfall runs back through earlier work. Reported, not asserted, deliberately: not every correction retires a claim, and forcing rows for those would push rows in to silence the check. Two failures while building it. The first version pasted the register rows into its own heredoc, so every registered phrase became an unmarked quotation and check-claims flagged its own source -- a tool violating the rule it enforces by being written. Fixed by passing the register through the environment. And writing up the previous catch re-introduced three unmarked quotations: describing a refuted claim quotes it, so every correction is a new occurrence needing the token. The cost is recursive, which the header implies but does not say out loud. What the hook does not do: it fires when a correction is written, so it closes the gap between arguing and indexing, not between believing and arguing. Nothing here would have caught me copying their 'structural' claim into my record. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
131 lines
5.6 KiB
Bash
Executable File
131 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Every refuted claim must appear only inside its own correction.
|
|
#
|
|
# tools/port/check-claims
|
|
#
|
|
# 🔴 WHY THIS IS A CHECK AND NOT AN AUDIT. The Decoder's rule -- *grep the corpus
|
|
# for the claim, not for the file you were working in* -- found a refuted sentence
|
|
# still shipping in this port's `manifest.json`, and a withdrawn one still
|
|
# standing in `DECISIONS.md`. Running that by hand finds the instances present on
|
|
# the day it is run. It does not stop the next one.
|
|
#
|
|
# So: a REGISTER. Each row is a claim this corpus has refuted, plus a marker that
|
|
# must appear near every occurrence. A hit without its marker fails the run.
|
|
#
|
|
# ⚠️ Two things learned building it, both from the other agent:
|
|
#
|
|
# * a "kept for the record" block STILL ASSERTS. Marking the heading superseded
|
|
# does not mark the sentence a reader lands on, so the marker must sit near
|
|
# the CLAIM, not at the top of the section.
|
|
# * naming a refuted claim keeps it greppable, so this check returns its own
|
|
# corrections as hits -- which is the point. The marker is what distinguishes
|
|
# "quoted while being refuted" from "still asserted".
|
|
set -euo pipefail
|
|
cd "${PROJECT_DIR:-/work}"
|
|
WINDOW=400 # characters either side of a hit in which the marker must appear
|
|
fail=0
|
|
|
|
# 🔴 THE MARKER IS AN EXPLICIT SENTINEL, NOT A KEYWORD.
|
|
#
|
|
# The first version matched a per-claim keyword -- "refuted", "WITHDRAWN" -- near
|
|
# the hit. Every one of its four failures was a quotation sitting INSIDE a
|
|
# correction whose wording happened not to contain the keyword: a table cell
|
|
# reading "standing, unmarked", a sentence reading "the real count was ten".
|
|
#
|
|
# Widening the window or adding synonyms until those passed would have been
|
|
# tuning a threshold until the answer came out right, which is the failure this
|
|
# corpus has spent a fortnight cataloguing. So the marker is a TOKEN THE AUTHOR
|
|
# PLACES: `[refuted]` near any quotation of a registered claim. It cannot be
|
|
# satisfied by phrasing, and its absence means exactly one thing.
|
|
#
|
|
# ⚠️ The cost is honest: every quotation must be marked by hand, and a new
|
|
# refuted claim means a new row plus marking its existing quotations. That work
|
|
# is the check.
|
|
MARKER='[refuted]'
|
|
REGISTER=$(cat <<'ROWS'
|
|
TAIL of the kept stream
|
|
known too fast
|
|
only thing making the plate
|
|
no loop-point field has been identified
|
|
AUDIBLY WRONG AT THE SEAM
|
|
1 of 3 streams
|
|
six expected DIFFERS
|
|
goes against the port
|
|
the capture turns out to determine it
|
|
COMPOSITED rather than standalone
|
|
structural limit, not an unrun experiment
|
|
ROWS
|
|
)
|
|
|
|
# ─── THE WITHDRAWAL-TIME HOOK ────────────────────────────────────────────────
|
|
# The register enforces claims it KNOWS ABOUT; knowing about them was manual, and
|
|
# that is how ~8 claims were withdrawn this session and 0 registered. A sweep
|
|
# cannot fix it -- by the time you sweep, the withdrawal is already unpublished.
|
|
# The hook fires where the withdrawal is WRITTEN.
|
|
#
|
|
# A correction in DECISIONS.md has a shape: a heading carrying WITHDRAWN /
|
|
# CORRECTION / "refuted". A section like that containing no registered phrase is
|
|
# a death argued and never indexed.
|
|
#
|
|
# ⚠️ The register is passed in the ENVIRONMENT, not inlined. The first version
|
|
# pasted the rows into this file's own heredoc -- which made every phrase an
|
|
# unmarked quotation, and the checker flagged its own source. A tool that
|
|
# violates the rule it enforces by being written is worth a comment.
|
|
#
|
|
# 🟡 REPORTED, NOT ASSERTED: not every correction retires a CLAIM -- some fix a
|
|
# number, a scope, a wrong floor -- and forcing a row for those would push rows
|
|
# in to silence the check, the failure this file exists to prevent.
|
|
echo
|
|
echo "withdrawal-time hook -- correction sections that registered nothing:"
|
|
REG="$REGISTER" python3 - <<'HOOK'
|
|
import os, re
|
|
reg = [r.strip() for r in os.environ["REG"].split("\n") if r.strip()]
|
|
doc = open("docs/port/DECISIONS.md").read()
|
|
heads = [(m.start(), m.group(0)) for m in re.finditer(r"(?m)^##+ .*$", doc)]
|
|
flagged = 0
|
|
for i, (pos, head) in enumerate(heads):
|
|
if not re.search(r"WITHDRAWN|CORRECTION|refuted|withdraw", head, re.I):
|
|
continue
|
|
end = heads[i + 1][0] if i + 1 < len(heads) else len(doc)
|
|
if not any(c in doc[pos:end] for c in reg):
|
|
flagged += 1
|
|
print(" candidate: %s" % head[:92].lstrip("# "))
|
|
print(" none -- every correction section names a registered claim" if not flagged
|
|
else " %d correction section(s) argue a withdrawal the register does not carry" % flagged)
|
|
HOOK
|
|
|
|
while IFS= read -r claim; do
|
|
[ -z "$claim" ] && continue
|
|
hits=0; bad=0
|
|
while IFS= read -r loc; do
|
|
[ -z "$loc" ] && continue
|
|
f=${loc%%:*}
|
|
hits=$((hits+1))
|
|
python3 - "$f" "$claim" "$MARKER" "$WINDOW" <<'PY' || bad=$((bad+1))
|
|
import sys
|
|
f, claim, marker, w = sys.argv[1], sys.argv[2], sys.argv[3], int(sys.argv[4])
|
|
s = open(f, encoding="utf-8", errors="ignore").read()
|
|
i = 0
|
|
while True:
|
|
i = s.find(claim, i)
|
|
if i < 0:
|
|
break
|
|
if marker.lower() not in s[max(0, i-w):i+w+len(claim)].lower():
|
|
print(" unmarked in %s at char %d" % (f, i))
|
|
sys.exit(1)
|
|
i += len(claim)
|
|
sys.exit(0)
|
|
PY
|
|
done < <(grep -rl -- "$claim" docs/ crates/ port/ tools/ authored/ 2>/dev/null || true)
|
|
if [ "$bad" -eq 0 ]; then
|
|
printf ' %-42s %d file(s), all marked\n' "$claim" "$hits"
|
|
else
|
|
printf ' %-42s 🔴 %d file(s) assert it unmarked\n' "$claim" "$bad"; fail=1
|
|
fi
|
|
done <<< "$REGISTER"
|
|
|
|
echo
|
|
[ $fail -eq 0 ] && echo "every refuted claim appears only inside its correction" \
|
|
|| echo "🔴 a refuted claim is still being asserted"
|
|
exit $fail
|