3"""Both-direction fixtures for the fail-closed review ledger."""
5from __future__
import annotations
8from pathlib
import Path
10from selftest_assert
import expect
11from suppression_identity
import IDENTITY_SCHEMA_VERSION
12from suppression_ledger
import (
21from suppression_model
import Inventory, Suppression
22from suppression_scan
import scan_paths
24_RATIONALES =
"""schema: suppression-review-rationales-1
26 bounded-cleanup-status-mask:
28 applicability: enumerated benign failure with an immediate consumer
29 evidence: [consumer-or-assert]
30 revalidate: when the consumer changes
31 portable-test-prerequisite-boundary:
33 applicability: direct portable skip, registered gate failure
34 evidence: [test-name, passing-counterpart, registered-gate]
35 revalidate: when the test or gate changes
36 vendor-upstream-preserved:
38 applicability: vendored SOUP bound to the exact blob
39 evidence: [upstream-pin]
40 revalidate: on blob change
43 applicability: delete the marker
44 evidence: [review-decision]
46 superseded-identity-rebinding:
48 applicability: the decision moves to the live replacement site named here
49 evidence: [replacement-site]
51 resolved-construct-retired:
53 applicability: the construct is gone and nothing live replaces it
54 evidence: [removal-evidence]
58 "tool.sh":
"#!/bin/sh\nprobe || true # enumerated benign probe\n",
59 "libs/third_party/vendor/legacy.txt":
"",
63def _batch_digest(rows: list[str]) -> str:
64 """Recompute the recorded digest for one batch's ordered rows."""
77 return hashlib.sha256(payload).hexdigest()
80def _write_ledger(root: Path, rows: list[str], *, schema: str = IDENTITY_SCHEMA_VERSION) ->
None:
81 """Write the three committed ledger authorities for one fixture run."""
82 (root /
".github").mkdir(exist_ok=
True)
83 (root / RATIONALES_PATH).write_text(_RATIONALES, encoding=
"ascii")
84 header =
"site_id\tbinding_sha256\tstate\trationale_id\tbatch_id\tevidence_ref"
85 (root / LEDGER_PATH).write_text(
"\n".join([header, *rows]) +
"\n", encoding=
"ascii")
86 reviewed = [row
for row
in rows
if row.split(
"\t")[2] !=
"unreviewed"]
87 (root / BATCHES_PATH).write_text(
88 "schema: suppression-review-batches-1\n"
90 " - id: batch-fixture\n"
91 " authority: repository-suppression-review\n"
93 f
" identity_schema: {schema}\n"
94 f
" assigned_rows: {len(reviewed)}\n"
95 f
" rows_sha256: {_batch_digest(reviewed)}\n",
100def _scan(root: Path, files: dict[str, str]) -> Inventory:
101 """Materialize fixture files and scan them without repository floors."""
102 for rel, text
in files.items():
104 target.parent.mkdir(parents=
True, exist_ok=
True)
105 if rel.endswith(
"legacy.txt"):
106 target.write_bytes(b
"vendored-\xff-text\n")
108 target.write_text(text, encoding=
"ascii")
109 return scan_paths(root, sorted(files))
112def _codes(inventory: Inventory) -> set[str]:
113 """Return the ledger-relevant finding codes of one reconciliation."""
114 return {item.code
for item
in inventory.findings}
117def _shell_row(inventory: Inventory) -> Suppression:
118 """Return the shell-status fixture row."""
119 return next(item
for item
in inventory.suppressions
if item.family ==
"shell-status")
122def _vendor_row(inventory: Inventory) -> Suppression:
123 """Return the vendored encoding-exemption fixture row."""
124 return next(item
for item
in inventory.suppressions
if item.family ==
"encoding-exemption")
127def _retain(row: Suppression, rationale: str =
"bounded-cleanup-status-mask") -> str:
128 """Render one retained ledger row for a live inventory row."""
130 f
"{row.site_id}\t{row.binding_sha256}\tretain\t{rationale}\tbatch-fixture\tevidence:fixture"
134def _retain_rows(inventory: Inventory) -> list[str]:
135 """Render a retain row for every live suppression the fixture scanned."""
137 _retain(item,
"vendor-upstream-preserved")
138 if item.family ==
"encoding-exemption"
140 for item
in inventory.suppressions
144def _closed(site: str, state: str, rationale: str, evidence: str) -> str:
145 """Render one closed ledger row for a site the inventory no longer carries."""
146 return f
"{site}\t{'1' * 64}\t{state}\t{rationale}\tbatch-fixture\t{evidence}"
149_PORTABLE_TEST =
"scripts/dev/work/tests/test_portable.py"
152def _write_portable_contract(root: Path, *, fail_closed: bool) ->
None:
153 """Write the exact test and gate evidence used by portable-skip fixtures."""
154 test = root / _PORTABLE_TEST
155 test.parent.mkdir(parents=
True, exist_ok=
True)
157 "def test_direct_skip():\n pass\n\ndef test_registered_failure():\n pass\n",
160 helper = root /
"scripts/dev/work/tests/fixtures/work_testlib.py"
161 helper.parent.mkdir(parents=
True, exist_ok=
True)
163 'import os\nREGISTERED_GATE_ENV = "RA8_WORK_HARNESS_REGISTERED_GATE"\n'
164 'if os.environ.get(REGISTERED_GATE_ENV) == "1":\n'
165 ' message = "missing"\n raise RuntimeError(message)\n',
168 gate = root /
"scripts/ci/gates/tests.sh"
169 gate.parent.mkdir(parents=
True, exist_ok=
True)
171 " RA8_WORK_HARNESS_REGISTERED_GATE=1 python3 -I scripts/dev/work/src/work.py --selftest\n"
173 else " python3 -I scripts/dev/work/src/work.py --selftest\n"
176 "gate_work_harness() (\n require_cmd bash\n require_cmd sh\n" + registration +
")\n",
181def _portable_evidence(gate: str =
"work-harness") -> str:
182 """Return a complete portable-prerequisite evidence reference."""
184 f
"test-name:{_PORTABLE_TEST}::test_direct_skip "
185 f
"passing-counterpart:{_PORTABLE_TEST}::test_registered_failure "
186 f
"registered-gate:{gate}"
190def _closing_codes() -> set[str]:
191 """Return every reconciliation code a well-formed closed row must avoid."""
193 "ledger-unknown-reference",
194 "ledger-missing-site",
196 "ledger-state-conflict",
197 "ledger-resolved-still-present",
201def assert_ledger_gate(base: Path, failures: list[str]) ->
None:
202 """Assert every ledger rejection fires and a complete ledger passes."""
203 root = base /
"ledger-repo"
205 inventory = _scan(root, _FILES)
206 shell = _shell_row(inventory)
207 vendor = _vendor_row(inventory)
210 for item
in inventory.suppressions
211 if item.site_id
not in {shell.site_id, vendor.site_id}
214 [_retain(shell), _retain(vendor,
"vendor-upstream-preserved")]
215 + [_retain(item)
for item
in others]
217 _write_ledger(root, complete)
218 apply_ledger(inventory, root)
219 approved = sum(item.disposition ==
"approved" for item
in inventory.suppressions)
221 approved == len(inventory.suppressions)
222 and not (_codes(inventory) & {
"ledger-missing-site",
"ledger-binding-mismatch"}),
223 "quiet: a complete matching ledger approves every retained site",
227 fresh = _scan(base /
"ledger-missing", _FILES)
228 ledger_without_shell = sorted(
229 [_retain(vendor,
"vendor-upstream-preserved")] + [_retain(item)
for item
in others]
231 _write_ledger(base /
"ledger-missing", ledger_without_shell)
232 apply_ledger(fresh, base /
"ledger-missing")
234 "ledger-missing-site" in _codes(fresh),
235 "must fire: a live suppression absent from the ledger fails",
238 _assert_ledger_binding_drift(base, complete, failures)
239 stale_root = base /
"ledger-stale"
240 stale = _scan(stale_root, {
"tool.sh": _FILES[
"tool.sh"]})
242 [_retain(item)
for item
in stale.suppressions]
243 + [_retain(vendor,
"vendor-upstream-preserved")]
245 _write_ledger(stale_root, stale_rows)
246 apply_ledger(stale, stale_root)
248 "ledger-stale-site" in _codes(stale),
249 "must fire: a retained ledger row without a live site fails",
253 _assert_ledger_resolution(base, failures)
254 _assert_ledger_supersession(base, failures)
255 _assert_portable_prerequisite_evidence(base, failures)
256 _assert_committed_vocabulary(base, failures)
257 _assert_ledger_shape_rules(base, failures)
260def _portable_case(base: Path, name: str, evidence: str, *, fail_closed: bool =
True) -> set[str]:
261 """Apply one portable-prerequisite ledger fixture and return finding codes."""
262 root = base / f
"ledger-portable-{name}"
263 inventory = _scan(root, _FILES)
264 _write_portable_contract(root, fail_closed=fail_closed)
265 shell = _shell_row(inventory)
267 _retain(item,
"vendor-upstream-preserved")
268 if item.family ==
"encoding-exemption"
270 f
"{item.site_id}\t{item.binding_sha256}\tretain\t"
271 "portable-test-prerequisite-boundary\tbatch-fixture\t"
273 if item.site_id == shell.site_id
276 for item
in inventory.suppressions
278 _write_ledger(root, sorted(rows))
279 apply_ledger(inventory, root)
280 return _codes(inventory)
283def _assert_portable_prerequisite_evidence(base: Path, failures: list[str]) ->
None:
284 """Assert portable skips require exact live tests and a fail-closed gate."""
285 accepted = _portable_case(base,
"accepted", _portable_evidence())
290 "malformed-review-ledger",
291 "ledger-unknown-reference",
292 "ledger-invalid-gate-contract",
295 "quiet: exact tests plus a fail-closed registered gate justify a portable skip",
298 missing = _portable_case(
301 f
"test-name:{_PORTABLE_TEST}::test_direct_skip registered-gate:work-harness",
304 "malformed-review-ledger" in missing,
305 "must fire: portable evidence missing a required field is rejected",
308 unknown_test = _portable_case(
311 f
"test-name:{_PORTABLE_TEST}::test_absent "
312 f
"passing-counterpart:{_PORTABLE_TEST}::test_registered_failure "
313 "registered-gate:work-harness",
316 "ledger-unknown-reference" in unknown_test,
317 "must fire: portable evidence naming an unknown test is rejected",
320 unknown_gate = _portable_case(base,
"unknown-gate", _portable_evidence(
"ghost-gate"))
322 "ledger-unknown-reference" in unknown_gate,
323 "must fire: portable evidence naming an unknown gate is rejected",
326 skippable = _portable_case(base,
"skippable-gate", _portable_evidence(), fail_closed=
False)
328 "ledger-invalid-gate-contract" in skippable,
329 "must fire: a registered gate that can skip its prerequisite is rejected",
334def _assert_ledger_supersession(base: Path, failures: list[str]) ->
None:
335 """Assert supersession closes a row only via a live, ledgered successor."""
336 probe = _scan(base /
"ledger-superseded", _FILES)
338 {
"shell-status",
"encoding-exemption"} <= {item.family
for item
in probe.suppressions},
339 "quiet: the supersession fixture scans a non-empty inventory",
343 for rationale
in (
"fix-remove",
"superseded-identity-rebinding"):
344 root = base / f
"ledger-sup-{rationale}"
345 inventory = _scan(root, _FILES)
346 successor = _shell_row(inventory)
349 *_retain_rows(inventory),
350 _closed(gone,
"superseded", rationale, f
"replaced-by:{successor.site_id}"),
353 _write_ledger(root, rows)
354 apply_ledger(inventory, root)
356 not (_codes(inventory) & _closing_codes()),
357 f
"quiet: a superseded row under {rationale} naming a live successor passes",
360 _assert_supersession_references(base, failures)
363def _assert_supersession_references(base: Path, failures: list[str]) ->
None:
364 """Assert every unusable ``replaced-by:`` reference fails closed."""
367 "no successor named":
"evidence:removed",
368 "malformed successor token":
"replaced-by:not-a-site",
369 "successor that is not live": f
"replaced-by:{'2' * 64}",
371 for label, evidence
in cases.items():
372 root = base / f
"ledger-sup-{label.split()[0]}"
373 inventory = _scan(root, _FILES)
375 [*_retain_rows(inventory), _closed(gone,
"superseded",
"fix-remove", evidence)]
377 _write_ledger(root, rows)
378 apply_ledger(inventory, root)
380 "ledger-unknown-reference" in _codes(inventory),
381 f
"must fire: a superseded row with {label} is rejected",
384 _assert_supersession_boundaries(base, failures)
387def _assert_supersession_boundaries(base: Path, failures: list[str]) ->
None:
388 """Assert an unledgered successor and a still-live site both fail closed."""
390 missing_root = base /
"ledger-sup-unledgered"
391 missing = _scan(missing_root, _FILES)
392 successor = _shell_row(missing)
395 [_closed(gone,
"superseded",
"fix-remove", f
"replaced-by:{successor.site_id}")],
397 apply_ledger(missing, missing_root)
399 "ledger-missing-site" in _codes(missing),
400 "must fire: a successor site carrying no ledger row of its own is rejected",
404 present_root = base /
"ledger-sup-present"
405 present = _scan(present_root, _FILES)
406 shell = _shell_row(present)
407 vendor = _vendor_row(present)
409 f
"{shell.site_id}\t{shell.binding_sha256}\tsuperseded\tfix-remove"
410 f
"\tbatch-fixture\treplaced-by:{vendor.site_id}"
412 _write_ledger(present_root, sorted([still_live, _retain(vendor,
"vendor-upstream-preserved")]))
413 apply_ledger(present, present_root)
415 "ledger-resolved-still-present" in _codes(present),
416 "must fire: a superseded row whose own site is still live is rejected",
419 _assert_supersession_vocabulary(base, failures)
422def _assert_supersession_vocabulary(base: Path, failures: list[str]) ->
None:
423 """Assert the retain / closing vocabulary boundary holds in both directions."""
424 root = base /
"ledger-sup-conflict"
425 inventory = _scan(root, _FILES)
426 successor = _shell_row(inventory)
427 named = f
"replaced-by:{successor.site_id}"
430 *_retain_rows(inventory),
431 _closed(
"0" * 64,
"superseded",
"bounded-cleanup-status-mask", named),
434 _write_ledger(root, rows)
435 apply_ledger(inventory, root)
437 "ledger-state-conflict" in _codes(inventory),
438 "must fire: a retain-vocabulary rationale in state superseded is rejected",
442 retired_root = base /
"ledger-sup-retired"
443 retired = _scan(retired_root, _FILES)
444 retired_rows = sorted(
446 *_retain_rows(retired),
447 _closed(
"0" * 64,
"resolved",
"resolved-construct-retired",
"evidence:removed"),
450 _write_ledger(retired_root, retired_rows)
451 apply_ledger(retired, retired_root)
453 not (_codes(retired) & _closing_codes()),
454 "quiet: a resolved row under resolved-construct-retired needs no successor",
459def _assert_committed_vocabulary(base: Path, failures: list[str]) ->
None:
460 """Assert the committed vocabulary parses and offers both closing states."""
461 categories, findings = load_rationales(Path(__file__).resolve().parents[2])
462 states = {spec[
"state"]
for spec
in categories.values()}
464 not findings
and {
"superseded",
"resolved"} <= states,
465 "quiet: the committed vocabulary parses and offers both closing states",
468 root = base /
"ledger-vocabulary"
469 (root /
".github").mkdir(parents=
True)
470 (root / RATIONALES_PATH).write_text(
471 "schema: suppression-review-rationales-1\n"
473 " outside-the-vocabulary:\n"
475 " applicability: a state no ledger row may carry\n"
476 " evidence: [review-decision]\n",
479 _, rejected = load_rationales(root)
481 any(item.code ==
"malformed-review-ledger" for item
in rejected),
482 "must fire: a category naming a state outside the ledger vocabulary is rejected",
487def _assert_ledger_binding_drift(base: Path, complete: list[str], failures: list[str]) ->
None:
488 """Assert reason and vendored-blob edits reopen review."""
489 drift_root = base /
"ledger-drift"
492 {**_FILES,
"tool.sh":
"#!/bin/sh\nprobe || true # a different rationale\n"},
494 _write_ledger(drift_root, complete)
495 apply_ledger(drift, drift_root)
496 drift_codes = _codes(drift)
498 "ledger-binding-mismatch" in drift_codes
500 item.disposition !=
"approved"
501 for item
in drift.suppressions
502 if item.family ==
"shell-status"
504 "must fire: a reason edit preserves the site but invalidates its binding",
508 vendor_root = base /
"ledger-vendor"
509 vendor_changed = _scan(vendor_root, _FILES)
510 (vendor_root /
"libs/third_party/vendor/legacy.txt").write_bytes(b
"vendored-\xfe-changed\n")
511 vendor_changed = scan_paths(vendor_root, sorted(_FILES))
512 _write_ledger(vendor_root, complete)
513 apply_ledger(vendor_changed, vendor_root)
515 "ledger-binding-mismatch" in _codes(vendor_changed),
516 "must fire: a vendored blob change invalidates its encoding review",
521def _assert_ledger_resolution(base: Path, failures: list[str]) ->
None:
522 """Assert resolved rows demand absence and re-appearance fails."""
523 resolved_root = base /
"ledger-resolved"
524 resolved = _scan(resolved_root, {
"tool.sh": _FILES[
"tool.sh"]})
525 gone = f
"{'0' * 64}\t{'1' * 64}\tresolved\tfix-remove\tbatch-fixture\tevidence:removed"
526 resolved_rows = sorted([_retain(item)
for item
in resolved.suppressions] + [gone])
527 _write_ledger(resolved_root, resolved_rows)
528 apply_ledger(resolved, resolved_root)
530 "ledger-resolved-still-present" not in _codes(resolved)
531 and "ledger-stale-site" not in _codes(resolved),
532 "quiet: a resolved row whose site is gone is the completed shape",
535 still_root = base /
"ledger-still-present"
536 still = _scan(still_root, {
"tool.sh": _FILES[
"tool.sh"]})
537 live = next(iter(still.suppressions))
539 f
"{live.site_id}\t{live.binding_sha256}\tresolved"
540 "\tfix-remove\tbatch-fixture\tevidence:removed"
543 [present] + [_retain(item)
for item
in still.suppressions
if item.site_id != live.site_id]
545 _write_ledger(still_root, still_rows)
546 apply_ledger(still, still_root)
548 "ledger-resolved-still-present" in _codes(still),
549 "must fire: a resolved site that still exists fails",
552 conflict_root = base /
"ledger-resolved-conflict"
553 conflict = _scan(conflict_root, {
"tool.sh": _FILES[
"tool.sh"]})
554 conflict_rows = sorted(
556 *(_retain(item)
for item
in conflict.suppressions),
557 _closed(
"0" * 64,
"resolved",
"bounded-cleanup-status-mask",
"evidence:removed"),
560 _write_ledger(conflict_root, conflict_rows)
561 apply_ledger(conflict, conflict_root)
563 "ledger-state-conflict" in _codes(conflict),
564 "must fire: a retain-vocabulary rationale in state resolved is rejected",
569def _assert_ledger_shape_rules(base: Path, failures: list[str]) ->
None:
570 """Assert structural, batch, and state rules fail closed."""
571 root = base /
"ledger-shape"
572 inventory = _scan(root, {
"tool.sh": _FILES[
"tool.sh"]})
573 row = next(iter(inventory.suppressions))
576 f
"{row.site_id}\t{row.binding_sha256}\tfix-required\tfix-remove\tbatch-fixture\tevidence:fixture"
578 _write_ledger(root, fix_rows)
579 apply_ledger(inventory, root)
581 "ledger-fix-required" in _codes(inventory)
582 and all(item.disposition !=
"approved" for item
in inventory.suppressions),
583 "must fire: an active fix-required row keeps the gate red",
588 "unknown rationale": (
589 f
"{row.site_id}\t{row.binding_sha256}\tretain\tno-such-category\tbatch-fixture\tevidence:x",
590 "ledger-unknown-reference",
593 f
"{row.site_id}\t{row.binding_sha256}\tretain\tfix-remove\tbatch-fixture\tevidence:x",
594 "ledger-state-conflict",
597 f
"{row.site_id}\t{row.binding_sha256}\tretain\tbounded-cleanup-status-mask\tbatch-fixture\t",
598 "malformed-review-ledger",
601 f
"{row.site_id}\t{row.binding_sha256}\tapproved\tbounded-cleanup-status-mask\tbatch-fixture\tevidence:x",
602 "malformed-review-ledger",
605 f
"{row.site_id}\t{row.binding_sha256}\tretain\tbounded-cleanup-status-mask\tbatch-ghost\tevidence:x",
606 "ledger-unknown-reference",
609 for label, (ledger_row, code)
in cases.items():
611 base / f
"ledger-{code}-{label[:7].replace(' ', '')}", {
"tool.sh": _FILES[
"tool.sh"]}
613 fixture_root = base / f
"ledger-{code}-{label[:7].replace(' ', '')}"
614 _write_ledger(fixture_root, [ledger_row])
615 apply_ledger(fresh, fixture_root)
616 expect(code
in _codes(fresh), f
"must fire: {label} is rejected", failures)
617 _assert_ledger_duplicate_rules(base, failures)
620def _assert_ledger_duplicate_rules(base: Path, failures: list[str]) ->
None:
621 """Assert duplicate and unsorted ledger rows fail closed."""
622 dup_root = base /
"ledger-dup"
623 dup = _scan(dup_root, {
"tool.sh": _FILES[
"tool.sh"]})
624 dup_row = _retain(next(iter(dup.suppressions)))
625 _write_ledger(dup_root, [dup_row, dup_row])
626 apply_ledger(dup, dup_root)
627 expect(
"ledger-duplicate-site" in _codes(dup),
"must fire: duplicate site ids fail", failures)
629 unsorted_root = base /
"ledger-unsorted"
630 unsorted = _scan(unsorted_root, _FILES)
631 shell = _shell_row(unsorted)
632 vendor = _vendor_row(unsorted)
633 pair = sorted([_retain(shell), _retain(vendor,
"vendor-upstream-preserved")], reverse=
True)
634 if pair[0].split(
"\t")[0] > pair[1].split(
"\t")[0]:
635 _write_ledger(unsorted_root, pair)
636 apply_ledger(unsorted, unsorted_root)
638 "malformed-review-ledger" in _codes(unsorted),
639 "must fire: unsorted ledger rows fail",
643 _assert_ledger_batch_rules(base, failures)
646def _assert_ledger_batch_rules(base: Path, failures: list[str]) ->
None:
647 """Assert schema, batch, and bootstrap-candidate rules fail closed."""
648 schema_root = base /
"ledger-schema"
649 schema = _scan(schema_root, {
"tool.sh": _FILES[
"tool.sh"]})
650 schema_rows = [_retain(item)
for item
in schema.suppressions]
651 _write_ledger(schema_root, sorted(schema_rows), schema=
"1-old-schema")
652 apply_ledger(schema, schema_root)
654 "ledger-schema-mismatch" in _codes(schema),
655 "must fire: an identity-schema change fails closed",
659 batch_root = base /
"ledger-batchcount"
660 batch = _scan(batch_root, {
"tool.sh": _FILES[
"tool.sh"]})
661 batch_rows = sorted(_retain(item)
for item
in batch.suppressions)
662 _write_ledger(batch_root, batch_rows)
663 text = (batch_root / BATCHES_PATH).read_text(encoding=
"ascii")
664 (batch_root / BATCHES_PATH).write_text(
665 text.replace(f
"assigned_rows: {len(batch_rows)}", f
"assigned_rows: {len(batch_rows) + 1}"),
668 apply_ledger(batch, batch_root)
670 "ledger-batch-mismatch" in _codes(batch),
671 "must fire: a batch row-count mismatch fails",
675 candidates = candidate_rows(batch)
677 bool(candidates)
and all(
"\tunreviewed\t" in line
for line
in candidates),
678 "quiet: generated candidates are always unreviewed, never approvals",
681 unrev_root = base /
"ledger-unrev"
682 unrev = _scan(unrev_root, {
"tool.sh": _FILES[
"tool.sh"]})
683 unrev_rows = sorted(candidate_rows(unrev))
684 _write_ledger(unrev_root, unrev_rows)
685 apply_ledger(unrev, unrev_root)
687 "ledger-unreviewed" in _codes(unrev)
688 and all(item.disposition !=
"approved" for item
in unrev.suppressions),
689 "must fire: bootstrap candidates keep the gate red until reviewed",
692 _assert_batch_identity_rules(base, failures)
695def _batch_documents() -> tuple[dict[str, tuple[str, str]], str, str]:
696 """Build the rejected, valid, and merge-key batch authorities as YAML text."""
697 header =
"schema: suppression-review-batches-1\nbatches:\n"
699 " authority: repository-suppression-review\n"
700 " date: 2026-08-24\n"
701 f
" identity_schema: {IDENTITY_SCHEMA_VERSION}\n"
702 " assigned_rows: 0\n"
703 f
" rows_sha256: {'0' * 64}\n"
706 "duplicate batch identity": (
707 header +
" - id: batch-one\n" + tail +
" - id: batch-one\n" + tail,
708 "ledger-duplicate-batch",
710 "duplicate key inside a record": (
711 header +
" - id: batch-one\n id: batch-two\n" + tail,
712 "malformed-review-ledger",
714 "duplicate key at document top level": (
715 header +
" - id: batch-one\n" + tail +
"batches:\n - id: batch-two\n" + tail,
716 "malformed-review-ledger",
719 valid = header +
" - id: batch-one\n" + tail +
" - id: batch-two\n" + tail
721 "schema: suppression-review-batches-1\n"
723 " authority: inherited-authority\n"
724 " date: 2026-08-24\n"
727 " authority: repository-suppression-review\n"
729 f
" identity_schema: {IDENTITY_SCHEMA_VERSION}\n"
730 " assigned_rows: 0\n"
731 f
" rows_sha256: {'0' * 64}\n"
733 return rejected, valid, merged
736def _load_batches_document(root: Path, document: str) -> tuple[dict[str, dict], set[str]]:
737 """Write one batches authority into a fixture root and load it."""
738 (root /
".github").mkdir(parents=
True, exist_ok=
True)
739 (root / BATCHES_PATH).write_text(document, encoding=
"ascii")
740 batches, findings = load_batches(root)
741 return batches, {item.code
for item
in findings}
744def _assert_batch_identity_rules(base: Path, failures: list[str]) ->
None:
745 """Assert repeated batch identities fail and valid YAML still parses.
747 PyYAML keeps the LAST value bound to a repeated mapping key and reports
748 nothing, so a second ``id:`` inside a record, or a second ``batches:``
749 block, silently replaced a reviewed authority before any application
750 validation ran. Both directions are asserted here: every repeat is
751 refused, and anchors, merge keys, and distinct identities still load
754 rejected, valid, merged = _batch_documents()
755 for index, (label, (document, code))
in enumerate(rejected.items()):
756 root = base / f
"ledger-batchid-{index}"
757 root.mkdir(parents=
True, exist_ok=
True)
758 _batches, codes = _load_batches_document(root, document)
759 expect(code
in codes, f
"must fire: {label} is rejected", failures)
761 valid_root = base /
"ledger-batchid-valid"
762 valid_root.mkdir(parents=
True, exist_ok=
True)
763 batches, codes = _load_batches_document(valid_root, valid)
765 not codes
and sorted(batches) == [
"batch-one",
"batch-two"],
766 "quiet: two distinct batch identities load unchanged",
770 merge_root = base /
"ledger-batchid-merge"
771 merge_root.mkdir(parents=
True, exist_ok=
True)
772 records, codes = _load_batches_document(merge_root, merged)
775 and sorted(records) == [
"batch-merge"]
776 and records[
"batch-merge"][
"authority"] ==
"repository-suppression-review"
777 and str(records[
"batch-merge"][
"date"]) ==
"2026-08-24",
778 "quiet: a merge key and its explicit override keep documented YAML semantics",