diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 86cbdb9..f142fd1 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -209,7 +209,7 @@ |---|------|----------------|-------|---------------------------| | G1 | Audit Phase 3: fresh-agent grounding test | 3 clean-context probes score the 7-question set against this doc; holes map made | session | CLOSED 2026-07-18: 3 probes, 21/21 PASS, holes H1 (amended into G9) + H2 (no action) -- `docs/audit/phase3-grounding-test-20260718.md` | | G2 | Audit Phase 4: GA-R1..R7 structural rulings + the stage-status vocabulary A/B | ruling-type gate (GA-R6 rule 6): closes when every item carries a GA-R5 Status block | operator | CLOSED 2026-07-18: all seven GA-R + vocabulary (Option A + H1) RATIFIED, utterances quoted (`docs/audit/ga-rulings.md`, through commit `fe4f1c4` + this one) | -| G3 | Audit Phase 5: repair sweep of GA-F01..F15 (incl. memory hygiene GA-F05..F08, skill sweep) | operator-gated fix batches, each commit naming its GA-F | operator + session | Batch 0 OPENED by operator 2026-07-19; items 0.1 (repo-lint L10, GA-R1/C1), 0.2 (SEC repoint, GA-R4/F3), 0.3 (counter hardening, GA-F15) landed; 0.4 pending; FREEZE holds for all un-gated surfaces | +| G3 | Audit Phase 5: repair sweep of GA-F01..F15 (incl. memory hygiene GA-F05..F08, skill sweep) | operator-gated fix batches, each commit naming its GA-F | operator + session | Batch 0 OPENED by operator 2026-07-19; items 0.1 (repo-lint L10, GA-R1/C1), 0.2 (SEC repoint, GA-R4/F3), 0.3 (counter hardening, GA-F15), 0.4 (extractor vocab scan, GA-F10/H1) landed; batch verification pending; FREEZE holds for all un-gated surfaces | | G4 | The two D-130 verifications (charter-exempt, read-only/throwaway): (v7) v0.9.8 volume-replace-under-running-domain on a throwaway domain; (v8) lifecycle ignore_changes suppression of the forced replacement | run them, capture output | session | TO RUN (`docs/audit/env-snapshot-20260718.md:47-49`) | | G5 | D-130 mechanism ruling (seed-volume durable fix) | operator rules in Phase 5, quoting G4's captured output | operator | D-130 unassigned; next-free D confirmed 130 (`ledger-scan` run 2026-07-18; no `^## D-130` in design-decisions.md) | | G6 | State reconcile of autostart + seed WITHOUT bouncing guests | gated mechanism per the finding: `tofu import` (reads autostart back) or state surgery, or an operator-scheduled maintenance-window apply; `-refresh-only` is PROVEN a no-op | operator | Operator earlier selected reconcile-first (finding doc:136); superseded into audit sequencing by `2b718f5`; nothing reconciled yet | diff --git a/scripts/record-audit.py b/scripts/record-audit.py index ac28910..0ef937c 100755 --- a/scripts/record-audit.py +++ b/scripts/record-audit.py @@ -22,6 +22,11 @@ register, not here. - A second pair count EXCLUDING changelog-class files is reported alongside (changelogs are historical by nature; GA-R2 owns their consolidation). + - Status-token vocabulary scan (sweep Batch 0.4, GA-F10 + H1): every status + token found is classified against the ratified legal set (OPEN/BLOCKED/ + HELD/CLOSED, Option A); illegal tokens anywhere and event words (VOID/ + REOPENED/SUPERSEDED) minted in status position are FLAGGED in the report + (section 6). Report-only: the 0/1/2 exit contract is unchanged. Usage: python3 scripts/record-audit.py [--repo DIR] [--memory-dir DIR] @@ -43,7 +48,19 @@ SKIP_DIRS = {".git", ".terraform", "__pycache__", "node_modules"} MAX_TEXT = 4 * 1024 * 1024 -STATUS_TOKENS = r"(DEPLOY-READY|READY|BLOCKED|CLOSED|COMPLETED|COMPLETE|HELD|STOPPED)" +STATUS_TOKENS = (r"(DEPLOY-READY|READY|BLOCKED|CLOSED|COMPLETED|COMPLETE|DONE|" + r"HELD|STOPPED|OPEN|TO RUN|RUNNING)") +# Stage-status vocabulary ruling (Option A + H1, RATIFIED 2026-07-18 -- +# docs/audit/ga-rulings.md): the LEGAL status token set is exactly +# OPEN/BLOCKED/HELD/CLOSED; every other status token is an illegal-as-status +# defect to be mapped in the Phase-5 migration. Event words record row-history +# TRANSITIONS, never states -- they are flagged ONLY when minted in status +# position (a **Status: line or a table row), which is the same lint defect +# as an illegal token. Flagging is REPORT-level: the 0/1/2 exit contract +# stays contradiction-driven (unchanged). +LEGAL_STATUS = {"OPEN", "BLOCKED", "HELD", "CLOSED"} +EVENT_WORDS_RE = r"(VOID|REOPENED|SUPERSEDED)" +STATUS_POS_RE = re.compile(r"\*\*Status:|^\s*\|") GATE_WORDS = r"(non-blocking|blocking|unblocked|unexercised|unverified|imported|verified)" PRODUCTS = [ ("tofu", r"(?:OpenTofu|opentofu|tofu)"), @@ -134,6 +151,7 @@ plan_context = re.compile(r"\b(plan|apply|add|destroy|outer|inner)\b", re.I) stage_tok = re.compile(r"\b" + STATUS_TOKENS + r"\b") stage_key = re.compile(r"[Ss]tage[- ]?(\d+)") + event_tok = re.compile(r"\b" + EVENT_WORDS_RE + r"\b") gate_tok = re.compile(r"\b" + GATE_WORDS + r"\b", re.I) # Version attribution: the version string must follow the product token with @@ -168,12 +186,21 @@ continue add("plan", "plan", "/".join(m.groups()), i, line) + in_status_pos = bool(STATUS_POS_RE.search(line)) for m in stage_tok.finditer(line): tok = m.group(1) norm = {"DEPLOY-READY": "READY", "COMPLETED": "COMPLETE"}.get(tok, tok) km = stage_key.search(line) key = f"stage-{km.group(1)}" if km else "-" add("status", key, norm, i, line) + cls = "legal" if tok in LEGAL_STATUS else "illegal" + add("vocab", tok, cls + ("/status-pos" if in_status_pos else "/prose"), + i, line) + # event words are row-history vocabulary; minting one AS a status + # (in status position) is the flagged defect -- prose uses are legal + if in_status_pos: + for m in event_tok.finditer(line): + add("vocab", m.group(1), "event/status-pos", i, line) for prod, pre in prod_words: for pm in pre.finditer(line): @@ -378,12 +405,19 @@ w("") w("## Summary") w("") + vocab_rows = [r for r in claims if r.kind == "vocab"] + vocab_defects = [r for r in vocab_rows if r.counted and + (r.value.startswith("illegal") or r.value.startswith("event"))] w(f"- files scanned: {len(targets)}") w(f"- status-claim rows: {len(claims)} " f"(plan {sum(1 for r in claims if r.kind == 'plan')}, " f"status {sum(1 for r in claims if r.kind == 'status')}, " f"version {sum(1 for r in claims if r.kind == 'version')}, " - f"gate {sum(1 for r in claims if r.kind == 'gate')})") + f"gate {sum(1 for r in claims if r.kind == 'gate')}, " + f"vocab {len(vocab_rows)})") + w(f"- vocab scan (Option A + H1): {len(set(r.key for r in vocab_rows))} distinct " + f"tokens; {len(vocab_defects)} flagged occurrence(s) on counted surfaces " + f"(report-only; exit stays contradiction-driven)") w(f"- CONTRADICTION GROUPS (counted surfaces): {len(contra)}") w(f"- contradiction groups excluding changelog-class files: {len(contra_nochg)}") w(f"- broken path refs: {len(broken_paths)}; orphan number refs: {len(orphan_nums)}; " @@ -422,7 +456,7 @@ w("") w("| kind | key | value | where | line |") w("|---|---|---|---|---|") - for r in sorted((r for r in claims if r.kind != "gate"), + for r in sorted((r for r in claims if r.kind not in ("gate", "vocab")), key=lambda r: (r.kind, r.key, r.loc)): flag = "" if r.counted else " (not counted)" w(f"| {r.kind}{flag} | {r.key} | {r.value} | {r.loc} | {trim(r.line, 80)} |") @@ -463,6 +497,34 @@ w(f"| {f} | {ln} | {loc} | {why} |") w("") + w("## 6. Status-token vocabulary scan (Option A + H1, ratified 2026-07-18)") + w("") + w("Legal set: OPEN / BLOCKED / HELD / CLOSED. Every other status token is") + w("illegal-as-status; event words (VOID / REOPENED / SUPERSEDED) are") + w("row-history vocabulary, flagged only when minted in status position") + w("(a **Status: line or a table row). Flags are report-only: the exit") + w("code stays contradiction-driven. The Phase-5 H1 migration maps every") + w("distinct token below; no token may survive unmapped.") + w("") + w("### Distinct tokens found") + w("| token | class | counted occurrences | not-counted occurrences |") + w("|---|---|---|---|") + by_tok = defaultdict(list) + for r in vocab_rows: + by_tok[r.key].append(r) + for tok in sorted(by_tok): + rows = by_tok[tok] + cls = sorted({r.value.split("/")[0] for r in rows}) + w(f"| {tok} | {'/'.join(cls)} | {sum(1 for r in rows if r.counted)} " + f"| {sum(1 for r in rows if not r.counted)} |") + w("") + w("### Flagged occurrences on counted surfaces (illegal token, or event word in status position)") + w("| token | class | where | line |") + w("|---|---|---|---|") + for r in sorted(vocab_defects, key=lambda r: (r.key, r.loc)): + w(f"| {r.key} | {r.value} | {r.loc} | {trim(r.line, 80)} |") + w("") + out.parent.mkdir(parents=True, exist_ok=True) out.write_text("\n".join(L) + "\n", encoding="ascii", errors="replace") print(f"record-audit: {len(targets)} files, {len(claims)} claim rows, " diff --git a/tests/record-audit/run-tests.sh b/tests/record-audit/run-tests.sh index ad024f0..fdbb4e2 100755 --- a/tests/record-audit/run-tests.sh +++ b/tests/record-audit/run-tests.sh @@ -44,6 +44,13 @@ MAAS `thing` (10.12.4.100) must not become a version row. Egress through the OPNsense edge. On it: MAAS 3.7.2 region+rack. The exit contract is 0/1/2 for the ROUTER gate poll. +Stage 3 remains OPEN per the workflow doc. +That earlier ruling was SUPERSEDED by a later amendment (prose, legal). +EOF +cat > "$A/docs/vocab.md" <<'EOF' +**Status:** DEPLOY-READY. +**Status:** SUPERSEDED. +| G4 | verify pair | TO RUN | EOF cat > "$A/docs/changelog-20260101-old.md" <<'EOF' Plan: 7 to add, 2 to change, 7 to destroy. @@ -82,15 +89,28 @@ assert_not_report "audit-quoted 9/9/9 not a counted value" "\`9/9/9\`" "$REPORT_A" assert_not_report "memory 1/1/1 not a counted value" "\`1/1/1\`" "$REPORT_A" +# ---- vocab scan (Batch 0.4: legal token set, Option A + H1) ----------------- +assert_report "vocab section present" "Status-token vocabulary scan" "$REPORT_A" +assert_report "legal OPEN classified" "| OPEN | legal |" "$REPORT_A" +assert_report "OPEN feeds stage-3 contradiction" "| OPEN | docs/notes.md:5" "$REPORT_A" +assert_report "DEPLOY-READY flagged illegal" "| DEPLOY-READY | illegal/status-pos |" "$REPORT_A" +assert_report "TO RUN in table row flagged" "| TO RUN | illegal/status-pos |" "$REPORT_A" +assert_report "event word in Status line flagged" "| SUPERSEDED | event/status-pos |" "$REPORT_A" +# prose event word is legitimate row-history vocabulary -- never a vocab row +assert_not_report "prose SUPERSEDED not flagged" "| SUPERSEDED | event/status-pos | docs/notes.md" "$REPORT_A" + # ---- fixture B: clean -> exit 0 --------------------------------------------- B="$WORK/b"; mkdir -p "$B/docs" cat > "$B/docs/one.md" <<'EOF' The outer plan reads 5 add / 0 change / 6 destroy. Pin: OpenTofu v1.12.3. +**Status:** READY. EOF REPORT_B="$WORK/b-report.md" -assert_exit "clean fixture -> exit 0" 0 \ +assert_exit "clean fixture -> exit 0 (vocab flag is report-only)" 0 \ python3 "$SCRIPT" --repo "$B" --out "$REPORT_B" --no-git assert_report "clean summary zero groups" "CONTRADICTION GROUPS (counted surfaces): 0" "$REPORT_B" +assert_report "vocab defect flagged without changing exit" "| READY | illegal/status-pos |" "$REPORT_B" +assert_not_report "no event rows in B" "| SUPERSEDED |" "$REPORT_B" # ---- preconditions ---------------------------------------------------------- assert_exit "absent repo -> exit 2" 2 \