import io
INV = ("main's exact SHA carries unit evidence for its CODE: at that sha via IR-144's "
       "exact-PR proof, or inherited from the last code sha when the tip is docs-only; "
       "the full-suite authority for an exact sha is the uncancelled golden run.")
def edit(p, pairs):
    s=io.open(p,encoding='utf-8',newline='').read()
    nl='\r\n' if '\r\n' in s else '\n'
    for old,new in pairs:
        old=old.replace('\n',nl); new=new.replace('\n',nl)
        assert s.count(old)==1,(p,s.count(old),old[:80]); s=s.replace(old,new)
    io.open(p,'w',encoding='utf-8',newline='').write(s)

edit('docs/INFRA-REGISTER.md',[
('- **Status:** RETIRED 2026-08-18 — intentional for main pushes, not a classifier defect.',
 '- **Status:** SUPERSEDED 2026-09-24 by [[IR-147]] (doyle ruling: build fix candidate (a); hertz lane). The invariant, written here because this tension has now been re-derived three times: **' + INV + '** IR-144 already conceded "full main-tip unit evidence" for code pushes; IR-147 extends it to docs-only tips. Earlier status kept below verbatim: RETIRED 2026-08-18 — intentional for main pushes, not a classifier defect.'),
('- **Status:** OPEN (doyle, 2026-09-24). Origin: releases#331, ff-landing core PR #253',
 '- **Status:** BUILT (hertz, 2026-09-24, fix candidate (a) as ruled by doyle; lane `ci/ir147-push-docs-only`): ONE classifier, `.github/ci/classify-changes.py`, for both arms. A push diffs `github.event.before..github.sha` after a bounded deepen, NEVER `HEAD^1..HEAD`. The docs set is named positively. Conservative exits run units under a named reason. The IR-144 helper is untouched. `REQ-CI-PUSH-DOCS-ONLY-THIN`. Acceptance pending: the next docs-only push to main (IR-146 (c) is routed to be it). Filed OPEN (doyle, 2026-09-24). Origin: releases#331, ff-landing core PR #253'),
])

edit('traceable-reqs.toml',[
('# ── CI policy (operator-ruled 2026-07-16): thin CI on docs-only PRs. ──\n[[requirements]]\nid = "REQ-CI-DOCS-ONLY-THIN"',
 '# ── CI policy (operator-ruled 2026-07-16): thin CI on docs-only PRs. ──\n# NOTE 2026-09-24: this entry\'s "superseded for main pushes by ADR-0050" clause is itself\n# superseded for DOCS-ONLY pushes by REQ-CI-PUSH-DOCS-ONLY-THIN (IR-147, doyle ruling). The\n# ruled text below is kept verbatim; read both.\n[[requirements]]\nid = "REQ-CI-DOCS-ONLY-THIN"'),
('  "REQ-CI-PUSH-MAIN-UNIT-REUSE",\n', '  "REQ-CI-PUSH-DOCS-ONLY-THIN",\n  "REQ-CI-PUSH-MAIN-UNIT-REUSE",\n'),
])
# add the new REQ after REQ-CI-PUSH-MAIN-UNIT-REUSE block
p='traceable-reqs.toml'
s=io.open(p,encoding='utf-8',newline='').read(); nl='\r\n'
i=s.index('id = "REQ-CI-PUSH-MAIN-UNIT-REUSE"'); j=s.index(nl+nl,i)
blk=nl.join(['','[[requirements]]','id = "REQ-CI-PUSH-DOCS-ONLY-THIN"',
 'title = "IR-147 (doyle ruling 2026-09-24, fix candidate (a)): a push to main whose WHOLE push range github.event.before..github.sha is docs-only runs thin CI like a docs-only PR, through ONE classifier shared by both arms, never HEAD^1..HEAD. Invariant: ' + INV.replace('"','') + ' The docs set is named positively (traceable-reqs.toml, repo-root Markdown, docs/ Markdown that nothing compiles in); everything else, .github/** included, is code. Default code=true; zero before, forced push, before unreachable after a bounded deepen, before not an ancestor, a failed diff and an empty range each run units under a named reason."',
 'required_stages = ["doc", "impl", "unit"]  # ACTIVATED #331 H4 (hertz 2026-09-24). doc = GOLDEN-CI "Push-main docs-only classification"; impl = .github/ci/classify-changes.py + ci.yml changes.classify; unit = .github/ci/classify-changes-selftest.py (fake git; the multi-commit HEAD^1 trap; positive docs set; COMPILED_MARKDOWN derived from the tree). The IR-144 helper is untouched.'])
s=s[:j]+blk+s[j:]
io.open(p,'w',encoding='utf-8',newline='').write(s)

edit('docs/GOLDEN-CI.md',[
('## Red protocol', '''## Push-main docs-only classification

<!-- [doc->REQ-CI-PUSH-DOCS-ONLY-THIN] -->

`ci.yml`'s `changes` job runs one classifier, `.github/ci/classify-changes.py`,
for both event arms (IR-147). A pull request is classified by the files its
merge commit changes. A push is classified by the files the **whole push**
changed, `github.event.before..github.sha`. It is never classified by
`HEAD^1..HEAD`: under ff-only main, a multi-commit lane puts `HEAD^1` inside
the lane, and reading only the last commit skips unit on a merge whose product
change sat earlier. Measured on #254's range: `HEAD^1..HEAD` saw only
`docs/INFRA-REGISTER.md`, while the push range held 14 crate files. The
checkout is shallow, so the classifier deepens along `github.sha` in bounded
steps until `event.before` is present.

The docs set is named **positively**: `traceable-reqs.toml`, repo-root
Markdown, and Markdown under `docs/` that nothing compiles in. Every other
path is code, `.github/**` included. The one compiled-in doc today is
`docs/ER-SKELETON.md` (via `include_str!`); the selftest re-derives that set
from the tree and fails if the two disagree. The default is `code=true`. A zero
`before`, a forced push, an unreachable `before`, a non-ancestor, a failed diff
and an empty range each run units and log `CLASSIFY code=true reason=<name>`.
A docs-only push skips lint and unit; traceability always runs. The IR-144
reuse step is unchanged and only matters when the push is code.

**What main-tip unit evidence means:** ''' + INV + '''

## Red protocol'''),
])

edit('docs/RELEASE-RUNBOOK.md',[
('''   milestone this run is **not a separate baseline authority**: it is thin by
   design—traceability, changes, lint, unit—a strict subset of the golden run''',
'''   milestone this run is **not a separate baseline authority**: it is thin by
   design—traceability, changes, lint, unit, where unit may be skipped when an
   exact-SHA PR run already proved it (IR-144) and lint and unit are skipped for
   a docs-only push (IR-147)—a strict subset of the golden run'''),
])
print('ok')
