Skip to content

Commit 5c6c0a3

Browse files
test(ledgers): enforce unique DISC ids and the expanded negative-evidence outcomes
Harden `scripts/check_ledgers.py` so the two ledger-integrity invariants that the accompanying doc changes rely on are machine-checked, not just conventions. check_negative_evidence: the entry regex previously hard-coded `WIN|NEGATIVE(reverted)` in the outcome group, which silently skipped any row using a different label. Capture the outcome generically and validate it against the allowed set {WIN, PROVISIONAL_LOCAL_WIN, NEGATIVE(reverted), NEGATIVE(retained-for-proof)}, emitting a per-entry `negative-outcome` check and failing on anything else. This locks in the taxonomy just added to docs/NEGATIVE_EVIDENCE.md and stops an unrecognized outcome from being counted. check_discrepancies: capture each `## DISC-<n>:` heading id and add a `disc-id-unique` check that fails on any duplicate id — the exact regression (two DISC-004 entries) that was just repaired in docs/DISCREPANCIES.md. Guards the ledger against a silent id collision recurring. Both are additive validators over the existing checker structure; no other behavior changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 64d06ae commit 5c6c0a3

1 file changed

Lines changed: 27 additions & 2 deletions

File tree

scripts/check_ledgers.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,13 +228,31 @@ def check_negative_evidence(path: Path, text: str, failures: list[str]) -> None:
228228
unfenced = strip_fenced_blocks(text)
229229
entries = list(
230230
re.finditer(
231-
r"^(?P<date>\d{4}-\d{2}-\d{2}) \| (?P<outcome>WIN|NEGATIVE\(reverted\)) \| (?P<lever>.+)$",
231+
r"^(?P<date>\d{4}-\d{2}-\d{2}) \| (?P<outcome>[^|]+?) \| (?P<lever>.+)$",
232232
unfenced,
233233
flags=re.MULTILINE,
234234
)
235235
)
236236
emit("negative-entry-count", True, count=len(entries))
237237

238+
allowed_outcomes = {
239+
"WIN",
240+
"PROVISIONAL_LOCAL_WIN",
241+
"NEGATIVE(reverted)",
242+
"NEGATIVE(retained-for-proof)",
243+
}
244+
for entry in entries:
245+
outcome = entry.group("outcome")
246+
allowed = outcome in allowed_outcomes
247+
emit(
248+
"negative-outcome",
249+
allowed,
250+
line=unfenced.count("\n", 0, entry.start()) + 1,
251+
outcome=outcome,
252+
)
253+
if not allowed:
254+
failures.append(f"{path}: unsupported negative-evidence outcome {outcome!r}")
255+
238256
required_fields = [
239257
"claim_id:",
240258
"evidence_id:",
@@ -289,11 +307,18 @@ def check_negative_evidence(path: Path, text: str, failures: list[str]) -> None:
289307

290308
def check_discrepancies(path: Path, text: str, failures: list[str]) -> None:
291309
unfenced = strip_fenced_blocks(text)
292-
headings = list(re.finditer(r"^## DISC-\d+:", unfenced, flags=re.MULTILINE))
310+
headings = list(re.finditer(r"^## (?P<id>DISC-\d+):", unfenced, flags=re.MULTILINE))
293311
emit("disc-entry-count", True, count=len(headings))
294312
if not headings:
295313
return
296314

315+
ids = [heading.group("id") for heading in headings]
316+
duplicates = sorted({identifier for identifier in ids if ids.count(identifier) > 1})
317+
unique = not duplicates
318+
emit("disc-id-unique", unique, duplicates=duplicates)
319+
if not unique:
320+
failures.append(f"{path}: duplicate discrepancy IDs: {', '.join(duplicates)}")
321+
297322
required_fields = [
298323
"- claim_id / evidence_id:",
299324
"- Provenance (model commit + fixture hash):",

0 commit comments

Comments
 (0)