mirror of
https://github.com/theclaymethod/unslop.git
synced 2026-09-19 01:45:09 +08:00
91a99c4c9d
Insert `from __future__ import annotations` in the 17 files that use PEP 604/585 annotations in module-level positions evaluated at import time, so scripts/banned_phrase_scan.py and friends no longer raise TypeError on Python 3.8/3.9. Add a 3.8 leg to the CI matrix so the floor claim in README.md is actually gated, and correct the two imprecise "439 deterministic cases" references to "440 deterministic script cases (439 pass, 1 documented xfail)". New scripts must carry the future-import until the floor is raised; if the maintainer later chooses 3.10+, delete the CI 3.8 leg and README claim together. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K6CYksdLbXbTAxcAQjvHz5
81 lines
2.6 KiB
Python
81 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Validate tiered detector pack integrity."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
PACK_DIR = ROOT / "references" / "packs"
|
|
MANIFEST = PACK_DIR / "manifest.json"
|
|
|
|
JUDGE_ONLY_MACROS = {
|
|
"macro_both_sidesism",
|
|
"macro_redemption_arc",
|
|
"macro_preview_recap",
|
|
"macro_over_determination",
|
|
"macro_emotional_flatness",
|
|
}
|
|
|
|
|
|
def scanner_categories() -> set[str]:
|
|
sys.path.insert(0, str(ROOT))
|
|
from scripts.banned_phrase_scan import BANNED_PHRASES, STRUCTURAL_PATTERNS
|
|
|
|
return {v["category"] for v in BANNED_PHRASES.values()} | {
|
|
v["category"] for v in STRUCTURAL_PATTERNS
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
failures: list[str] = []
|
|
data = json.loads(MANIFEST.read_text())
|
|
packs: dict[str, list[str]] = data["packs"]
|
|
category_to_pack: dict[str, str] = {}
|
|
|
|
for pack, categories in packs.items():
|
|
path = PACK_DIR / f"{pack}.md"
|
|
if not path.exists():
|
|
failures.append(f"missing pack file: {path}")
|
|
continue
|
|
text = path.read_text()
|
|
line_count = len(text.splitlines())
|
|
if line_count > 120:
|
|
failures.append(f"{path} has {line_count} lines; max 120")
|
|
if "## Emit" not in text:
|
|
failures.append(f"{path} missing ## Emit section")
|
|
for category in categories:
|
|
if category in category_to_pack:
|
|
failures.append(
|
|
f"category {category} mapped to both {category_to_pack[category]} and {pack}"
|
|
)
|
|
category_to_pack[category] = pack
|
|
|
|
scanner = scanner_categories()
|
|
missing = scanner - set(category_to_pack)
|
|
extra_scanner = (set(category_to_pack) - scanner) - JUDGE_ONLY_MACROS
|
|
if missing:
|
|
failures.append(f"scanner categories missing from packs: {sorted(missing)}")
|
|
if extra_scanner:
|
|
failures.append(f"unknown non-macro categories in manifest: {sorted(extra_scanner)}")
|
|
|
|
macro_hits = {m: category_to_pack.get(m) for m in JUDGE_ONLY_MACROS}
|
|
missing_macros = [m for m, pack in macro_hits.items() if pack is None]
|
|
if missing_macros:
|
|
failures.append(f"judge-only macro families missing: {sorted(missing_macros)}")
|
|
for macro, pack in macro_hits.items():
|
|
if pack and pack != "pack-structure":
|
|
failures.append(f"{macro} must live in pack-structure, found {pack}")
|
|
|
|
if failures:
|
|
print("\n".join(failures), file=sys.stderr)
|
|
return 1
|
|
print(f"pack integrity ok: {len(packs)} packs, {len(scanner)} scanner categories")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|