PR-A of the v3.10 triangulation-policy-layer split: a zero-behavior-change
base that removes the R-L3-2-A/B/C ID overload between the contamination
advisory rules (original holder, v3.7.3 §3.2) and the claim-intent-manifest
emission rules (borrowed the same ID in three writer-stage prompts).
- Rename the claim-manifest rules R-L3-2-A/B/C -> R-CIM-A/B/C in
synthesis_agent, report_compiler_agent, draft_writer_agent, and the
claim_intent_manifest schema description. Wording byte-identical except
the ID token.
- Add shared/references/firm_rules.md as the canonical single source for the
contamination R-L3-2-A..E blocks and the claim-manifest R-CIM-A..C blocks.
The note for the contamination mirrors states plainly that their wording is
NOT sync-checked yet (prose references, not full-block copies) — only their
IDs are guarded; full wording-sync lands with the PR-B reword.
- Add scripts/check_firm_rules_sync.py: sync-checks the R-CIM mirrors against
the canonical blocks (modulo the agent self-reference noun) and guards the
disambiguation. The collision guard seals the full R-L3-2-A..E namespace out
of claim-manifest surfaces, and _extract_section is fence-aware so a forbidden
ID hidden after a fake heading inside a fenced example cannot escape the scan.
Wired into CI via spec-consistency.yml + the pytest manifest. 19 tests.
- Spec: docs/design/2026-05-31-ars-v3.10-policy-layer-rescope-spec.md.
Part of #127 (policy layer lands in PR-B on this base). No runtime behavior
change; full pytest unchanged at 1790 passed / 3 skipped.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>