Files
imbad0202__academic-researc…/scripts/check_setup_cross_model_parity.py
Edward Cheng-I Wu a25ada2812 feat(lint): #491 defrift lock (canonical enforcement sentence) + SETUP parity + manifest gap (#492)
* feat(lint): #491 defrift lock for Phase Boundary enforcement sentence + SETUP parity + manifest gap

Three pieces, all follow-ups from the 2026-07-04 harness-retirement pass:

1. check_v3_9_2_phase_boundary.py gains invariant 4: every Bucket A block's
   enforcement paragraph must carry the canonical sentence verbatim
   (CANONICAL_ENFORCEMENT, version-matched v3.9.2/v3.9.4; per-file tails
   free). The previous copy went factually stale repo-wide for a month with
   no lint noticing (B4-F01) — this converts 25 hand-synced copies into one
   lock. +7 mutation tests incl. the exact stale-sentence regression case.

2. New check_setup_cross_model_parity.py (+6 tests): SETUP en/zh-TW must
   carry the same ARS_CROSS_MODEL example values and every value must exist
   in the canonical lineup doc (the B4-F02 drift class). Fail-closed on
   zero extracted examples. Wired into spec-consistency.yml.

3. _ci_pytest_manifest.toml gains v3.9.4-temporal-verification — the test
   whose absence made PR #490 local-green/CI-red (bibliography_agent sha256
   pin only checked in CI). Local manifest now catches it: 58 -> 60 entries.

Verification: manifest drift guard green; full manifest 60/60 green;
phase-boundary lint green on repo baseline; personal-boundary 993 files
0 violations.

Closes #491

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBdGUAb184hmuiRiWAm4A9

* fix(lint): scope canonical membership to model tables only (codex review P1)

The membership check accepted any backticked token in the canonical doc,
so a SETUP example reverting to gpt-5.4-pro would false-pass on the
legacy-accepted NOTE — the exact B4-F02 drift class the lint exists to
catch. Membership now parses only the "API ID" table columns; ids in
surrounding prose do not count. +3 tests incl. the literal legacy-revert
regression case + table-parser fail-closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBdGUAb184hmuiRiWAm4A9

* fix(lint): exclude wildcard prefix tokens from canonical model ids (codex re-review P2)

The compat table's "any non-`gpt-*`/`gemini-*` id" prose sits in an API ID
column, leaking glob patterns into the allowed set. Globs are prefix
patterns, not model ids — filtered, with a regression test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBdGUAb184hmuiRiWAm4A9

* refactor(lint): /simplify pass — dedupe update guidance, shared module loader, cross-refs

- Simplification: the "update + sweep" maintenance guidance was triplicated
  inside the defrift lint itself with a hand-typed file count; now stated
  once in the error message with len(BUCKET_A_AGENTS) interpolated, and the
  mirror wording corrected (mirrors follow mechanically via
  check_agents_mirror_sync.py, not by hand-sweeping).
- Reuse (R1, option b): CANONICAL_ENFORCEMENT stays lint-local by design
  (factual status prose, not a behavioral firm rule) — rationale in the
  constant comment + "Related mechanisms" cross-ref in firm_rules.md so the
  two canonical-sync mechanisms know about each other.
- Reuse (R2): tests/test_helpers.py gains load_module_from_path(); both new
  test files use it (6 pre-existing local copies migrate at next edit).
- Efficiency note: workflow comment marks the 2x pytest.yml overlap as the
  accepted manifest pattern.

All lints + 21 tests + full 60-entry manifest green; firm-rules sync green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YBdGUAb184hmuiRiWAm4A9

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 13:35:58 +08:00

140 lines
5.6 KiB
Python

#!/usr/bin/env python3
"""SETUP cross-model example parity lint (#491 fold-in).
The quick-setup bash blocks in docs/SETUP.md + docs/SETUP.zh-TW.md hardcode
verifier model IDs (`ARS_CROSS_MODEL="..."`) because they are literal export
strings a user pastes — they cannot be made version-agnostic the way the
canonical doc's primary row was. That makes them a drift surface: the
gpt-5.4→gpt-5.5 lineup migration (2026-06-10, F-003) fixed the canonical doc
but missed SETUP for three weeks (B4-F02, audits/harness-retirement-2026-07-04.md).
This lint pins the two invariants that broke:
1. **en/zh-TW parity** — both SETUP files must carry the same set of
`ARS_CROSS_MODEL` example values (the zh-TW file mirrors the bash block
verbatim; a one-sided edit is drift).
2. **canonical membership** — every example value must appear in a model
TABLE of shared/cross_model_verification.md (the column whose header
contains "API ID": the first-party supported table + the compat-provider
table). Backticked ids elsewhere in the doc deliberately do NOT count —
the legacy-accepted note backticks `gpt-5.4`/`gpt-5.4-pro`, and "accepted
for existing setups" is not "recommended in SETUP examples" (false-pass
surface caught by codex review on PR #492).
Fail-closed twice: zero ARS_CROSS_MODEL examples in a SETUP file, or zero
ids extracted from the canonical tables, is an error (the extraction went
stale), never a silent pass.
Exit codes: 0 on pass, 1 on any failure.
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
SETUP_EN = REPO_ROOT / "docs/SETUP.md"
SETUP_ZH = REPO_ROOT / "docs/SETUP.zh-TW.md"
CANONICAL = REPO_ROOT / "shared/cross_model_verification.md"
# Matches active and commented example lines alike:
# export ARS_CROSS_MODEL="gpt-5.5"
# # or: export ARS_CROSS_MODEL="gemini-3.1-pro-preview"
ASSIGNMENT_RE = re.compile(r'ARS_CROSS_MODEL="([^"]+)"')
def canonical_model_ids(canonical_text: str) -> set[str]:
"""Backticked ids from the "API ID" column of the canonical doc's model
tables ONLY. A table starts at a header row containing "API ID" and ends
at the first non-table line; ids backticked in surrounding prose (e.g.
the legacy-accepted note) are deliberately excluded."""
ids: set[str] = set()
api_id_col: int | None = None
for line in canonical_text.splitlines():
stripped = line.strip()
if not stripped.startswith("|"):
api_id_col = None
continue
cells = [c.strip() for c in stripped.strip("|").split("|")]
if api_id_col is None:
if any("API ID" in c for c in cells):
api_id_col = next(i for i, c in enumerate(cells) if "API ID" in c)
continue
if len(cells) > api_id_col:
# Globs like `gpt-*` (from the compat table's "any non-`gpt-*`
# id" prose) are prefix patterns, not model ids — exclude them.
ids.update(
tok
for tok in re.findall(r"`([^`]+)`", cells[api_id_col])
if "*" not in tok
)
return ids
def extract_ids(setup_text: str) -> list[str]:
"""All ARS_CROSS_MODEL example values in a SETUP file, in order."""
return ASSIGNMENT_RE.findall(setup_text)
def check(en_text: str, zh_text: str, canonical_text: str) -> list[str]:
errors: list[str] = []
en_ids = extract_ids(en_text)
zh_ids = extract_ids(zh_text)
for label, ids in (("docs/SETUP.md", en_ids), ("docs/SETUP.zh-TW.md", zh_ids)):
if not ids:
errors.append(
f"{label}: no ARS_CROSS_MODEL example values found — either the "
f"quick-setup block was removed or the extraction regex went "
f"stale. Fail-closed per lint contract."
)
if en_ids and zh_ids and set(en_ids) != set(zh_ids):
errors.append(
f"SETUP en/zh-TW ARS_CROSS_MODEL example drift: "
f"en={sorted(set(en_ids))} zh-TW={sorted(set(zh_ids))}. The two "
f"quick-setup bash blocks must carry the same example values."
)
known = canonical_model_ids(canonical_text)
if not known:
errors.append(
"shared/cross_model_verification.md: no ids extracted from any "
"'API ID' table column — the table format changed and the parser "
"went stale. Fail-closed per lint contract."
)
return errors
for label, ids in (("docs/SETUP.md", en_ids), ("docs/SETUP.zh-TW.md", zh_ids)):
for model_id in ids:
if model_id not in known:
errors.append(
f"{label}: ARS_CROSS_MODEL example {model_id!r} is not in "
f"the canonical model tables of "
f"shared/cross_model_verification.md (B4-F02 drift class; "
f"legacy ids in the accepted-note do not count as "
f"recommended). Update the example or the canonical table, "
f"in the same PR."
)
return errors
def main() -> int:
errors = check(
SETUP_EN.read_text(encoding="utf-8"),
SETUP_ZH.read_text(encoding="utf-8"),
CANONICAL.read_text(encoding="utf-8"),
)
if errors:
print("SETUP cross-model parity lint FAILED:", file=sys.stderr)
for err in errors:
print(f" - {err}", file=sys.stderr)
return 1
print("SETUP cross-model parity lint PASSED: en/zh-TW examples match and "
"all values are in the canonical lineup.")
return 0
if __name__ == "__main__":
sys.exit(main())