test_wo31_register.py
"""CX-1 acceptance for the one canonical audit remediation register."""
import json
from pathlib import Path
import re
import subprocess
import unittest
ROOT = Path(__file__).resolve().parents[3]
DATED = re.compile(r'20\d\d-\d\d-\d\d')
def _cells(line):
return [c.strip() for c in line.strip().strip('|').split('|')]
def _row_id(line):
cells = _cells(line)
return cells[0].replace('~~', '').strip() if cells else ''
def register_row_moved_with_evidence(old_line, current_lines):
"""The register rule (docs/OPEN_ITEMS.md header): never delete a row, strike
it; close a row with the commit, run, or ruling that closed it.
So a baseline row may differ from the current file in its LAST cell
(State), which must either keep the old state struck through
(``~~OPEN~~ CLOSED ...``) or carry a dated ruling / closure
(``DONE 2026-09-15 — ...``); and any other cell may change only by keeping
its original text struck through in front of the new text
(``~~build (blocked: S4-1)~~ build (protected path) ...``). A cell that is
reworded, appended to, or whose struck original is missing or misspelled is
refused. Returns ``(ok, reason)``.
"""
rid = _row_id(old_line)
matches = [line for line in current_lines
if line.startswith('|') and _row_id(line) == rid]
if not matches:
return False, f'{rid}: row deleted from the register'
old_cells, new_cells = _cells(old_line), _cells(matches[0])
if len(old_cells) != len(new_cells):
return False, f'{rid}: cell count {len(old_cells)} -> {len(new_cells)}'
for i, (old, new) in enumerate(zip(old_cells[:-1], new_cells[:-1])):
# Equality, not substring (Copilot on #1064): an appended clause
# ("do the thing" -> "do the thing now") is a rewording too.
plain_old = old.replace('~~', '')
if plain_old == new.replace('~~', ''):
continue
# 2026-09-17: three lanes moved Owner / Evidence / Item cells in place
# on seven rows in one day (S2-D2, S2-D4, EV-PROV-1, S6-3, S4-5, L6-5,
# L6-8) and this CI-unwired suite was the only thing that noticed. The
# register rule is "never delete, strike": a cell other than State may
# change only by keeping its original text struck through IN FRONT of
# the new text (`~~old~~ new`). Order is enforced so the history reads
# first; a struck fragment buried elsewhere is still a rewording.
if new.startswith(f'~~{plain_old}~~'):
continue
return False, f'{rid}: cell {i} reworded: {old[:60]!r} -> {new[:60]!r}'
old_state, new_state = old_cells[-1], new_cells[-1]
if old_state == new_state or f'~~{old_state}~~' in new_state:
return True, ''
if DATED.search(new_state):
return True, ''
return False, f'{rid}: state moved without a dated ruling or closure: {new_state[:80]!r}'
class RegisterAcceptance(unittest.TestCase):
def test_wo31_preserves_open_items_and_strikes_superseded_wave1_item(self):
register_path = 'docs/OPEN_ITEMS.md'
current = (ROOT / register_path).read_text()
self.assertTrue('| ~~V4-1~~ |' in current, 'landed WS-1b is still an open register item')
row = next(line for line in current.splitlines() if '| ~~V4-1~~ |' in line)
self.assertIn('~~OPEN — WS-1b only~~', row)
self.assertIn('CLOSED', row)
self.assertIn('0568720', row)
baseline = subprocess.check_output(
['git', 'show', '26bec9c6b424ca4b86b35621602f7a06764e5afe:' + register_path],
cwd=ROOT, text=True,
)
# Non-row baseline lines that legitimately moved. The per-row opt-outs
# that used to sit here (S2-D5, S7-3, W3-S8-7, and V4-4 for the
# owner-merged WO-49 Item-cell rewording in PR #1056) were retired on
# 2026-09-17: every one of those rows now satisfies the general rule
# below — a moved cell keeps its original text struck in front of the
# new text — so the rule has no per-row exceptions to grow.
allowed_updates = (
'The homepage deploy lane is **WO-44**',
'- [WO-44]',
# 2026-09-17: the header Rule line gained the strike-in-front
# sentence for non-State cells in the same commit that taught
# register_row_moved_with_evidence the shape (rule 01: the claim
# and the row that authorizes it move together).
'**Rule:** this file is the single forward list for MIZ OKI.',
)
current_lines = current.splitlines()
for old_line in baseline.splitlines():
if old_line.startswith('| V4-1 |'):
# Every original cell survives, including its historical wording.
# The changed item may only gain strike-throughs and closure evidence.
old_cells = old_line.strip('|').split('|')
new_cells = row.strip('|').split('|')
self.assertEqual(len(old_cells), len(new_cells))
for old, new in zip(old_cells[:-1], new_cells[:-1]):
# every cell but State survives verbatim, strike-throughs removed
self.assertEqual(old.strip(), new.replace('~~', '').strip())
self.assertIn(old_cells[-1].strip(), new_cells[-1].replace('~~', ''))
continue
if old_line.startswith(allowed_updates):
continue
if old_line in current_lines:
continue
# 2026-09-15: S3-2, S3-4, S3-5, S7-1 and S7-4 moved their State
# cell to a dated ruling or closure without striking the old
# state. The register rule permits closing a row with the ruling
# that closed it; what it forbids is deleting or rewording one.
# Enforce exactly that, instead of growing the allow-list per row.
self.assertTrue(old_line.startswith('|'),
f'baseline line lost: {old_line[:80]!r}')
ok, reason = register_row_moved_with_evidence(old_line, current_lines)
self.assertTrue(ok, reason)
def test_register_row_rule_accepts_dated_state_moves_only(self):
# Seeded both directions (rule 01): the legal moves it must accept and
# the illegal ones it must refuse.
old = '| X-1 | do the thing | build | `docs/x.md` | OPEN |'
for good in (
['| X-1 | do the thing | build | `docs/x.md` | DONE 2026-09-15 — landed `abc1234` |'],
['| ~~X-1~~ | do the thing | build | `docs/x.md` | ~~OPEN~~ CLOSED `abc1234` |'],
['| X-1 | do the thing | build | `docs/x.md` | OPEN |'],
# 2026-09-17: a non-State cell may move when its original survives
# struck in front of the new text (Owner, Evidence, Item alike).
['| X-1 | do the thing | ~~build~~ operator (dispatch) | `docs/x.md` | OPEN |'],
['| X-1 | do the thing | build | ~~`docs/x.md`~~ `docs/y.md` (design of record); `docs/x.md` | OPEN |'],
['| X-1 | ~~do the thing~~ do the thing, then verify it | build | `docs/x.md` | OPEN — RULED 2026-09-17 |'],
):
ok, reason = register_row_moved_with_evidence(old, good)
self.assertTrue(ok, reason)
for bad in (
[], # deleted
['| X-1 | do the OTHER thing | build | `docs/x.md` | OPEN |'], # reworded
['| X-1 | do the thing now | build | `docs/x.md` | OPEN |'], # appended (still a rewording)
['| X-1 | do the thing | build | `docs/x.md` | DONE |'], # undated move
['| X-1 | do the thing | build | OPEN |'], # cell dropped
['| X-1 | do the thing | operator (dispatch) | `docs/x.md` | OPEN |'], # owner reworded, nothing struck
['| X-1 | do the thing | ~~builder~~ operator | `docs/x.md` | OPEN |'], # struck text is not the original
['| X-1 | do the thing | operator ~~build~~ | `docs/x.md` | OPEN |'], # original struck, but not in front
):
ok, reason = register_row_moved_with_evidence(old, bad)
self.assertFalse(ok, f'accepted an illegal row change: {bad!r}')
def test_wo31_every_manifest_work_order_links_once_to_its_issue(self):
# The requested root spelling is absent on the audit and current main;
# docs/OPEN_ITEMS.md is the existing register. Do not fork it.
self.assertFalse((ROOT / 'OPEN_ITEMS.md').exists())
register = (ROOT / 'docs/OPEN_ITEMS.md').read_text()
heading = '## Audit 2026-09-06 remediation'
self.assertEqual(register.count(heading), 1)
section = register.split(heading, 1)[1].split('\n## ', 1)[0]
manifest = json.loads((ROOT / 'docs/audits/wo/manifest.json').read_text())
issues = json.loads((ROOT / 'docs/audits/wo/issue_map.json').read_text())
for entry in manifest:
wo = entry['id']
lines = [line for line in section.splitlines() if f'[{wo}]' in line]
self.assertEqual(len(lines), 1, f'{wo} must have exactly one register entry')
self.assertIn(f'audits/wo/{wo}.md', lines[0])
self.assertIn(f'#{issues[wo]}', lines[0])
debt = (ROOT / 'docs/BUILD_DEBT.md').read_text()
self.assertIn('OPEN_ITEMS.md#audit-2026-09-06-remediation', debt)
self.assertNotRegex(debt, r'(?m)^[-|].*\[WO-\d{2}\]')
def test_wo44_register_and_work_order_record_the_2026_09_13_runtime_failure(self):
register = (ROOT / 'docs/OPEN_ITEMS.md').read_text()
github_section = register.split(
'## F. GitHub org migration / github-virtuoso close-out v2 (2026-09-12)', 1
)[1].split('\n## ', 1)[0]
self.assertIn('The homepage deploy lane remains **WO-44**', github_section)
self.assertIn('ruling **A1** is implemented in-repo', github_section)
self.assertIn('ruling **B1**', github_section)
self.assertIn('34774896326', github_section)
self.assertIn('guards 1/2/5', github_section)
self.assertIn('attributeCondition', github_section)
self.assertNotIn('not re-listed here', github_section)
audit_section = register.split('## Audit 2026-09-06 remediation', 1)[1]
# 2026-09-15: the row is struck through (`- ~~[WO-44]`) once STEP 4 was
# measured done by the owner lane; the closure keeps the history inline.
wo44_row = next(
line for line in audit_section.splitlines()
if line.startswith('- [WO-44]') or line.startswith('- ~~[WO-44]')
)
self.assertIn('A1 landed', wo44_row)
self.assertIn('run #120 proved guards 1/2/5', wo44_row)
# moved 2026-09-14: the row records STEP 1–2 as applied (run #126) and
# names STEP 4 — first as the owner-held remainder, since 2026-09-15 as
# DONE with its measurement; the intent — the row names the runbook step
# — is unchanged.
self.assertIn('runbook §5 STEP 4', wo44_row)
work_order = (ROOT / 'docs/audits/wo/WO-44.md').read_text()
self.assertIn('run #120 already proved guards 1/2/5', work_order)
self.assertIn('`github.ref_protected=true`', work_order)
self.assertIn('do not treat the REST branch `.protected` field as the measurement of record', work_order)
self.assertIn('runbook §5 STEP 1–2 are applied', work_order)
self.assertIn('breakages (3)/(4)', work_order)
self.assertNotIn(
'`gh api repos/MIZOKI-3-5/MIZOKICloudRun/branches/main -q .protected` returns `true`',
work_order,
)
if __name__ == '__main__':
unittest.main()