76 lines
2.4 KiB
Python
76 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Fail if decomposition docs regress hybrid vocabulary (gig vs non-combat skill).
|
|
|
|
Catches phrases like "one combat skill" or bare "combat skill" that imply a combat
|
|
SkillDef. Allows "non-combat skill(s)" and "anti-combat …" when the hyphen prefix
|
|
is immediately before ``combat``.
|
|
|
|
Run in CI (PR gate) and locally after editing ``docs/decomposition/**``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
DECOMP_ROOT = REPO_ROOT / "docs/decomposition"
|
|
|
|
# Standalone phrases that almost always indicate a doc bug vs game-design hybrid model.
|
|
LITERAL_BANS = [
|
|
"one combat skill",
|
|
"one combat skills",
|
|
]
|
|
|
|
# Match "combat skill" / "combat skills" unless prefixed by "non-" or "anti-" (hyphenated).
|
|
COMBAT_SKILL = re.compile(r"\bcombat skills?\b", re.IGNORECASE)
|
|
|
|
|
|
def line_has_bad_combat_skill(line: str) -> bool:
|
|
for m in COMBAT_SKILL.finditer(line):
|
|
start = m.start()
|
|
if start >= 4 and line[start - 4 : start].lower() == "non-":
|
|
continue
|
|
if start >= 5 and line[start - 5 : start].lower() == "anti-":
|
|
continue
|
|
return True
|
|
return False
|
|
|
|
|
|
def check_file(path: Path) -> list[str]:
|
|
errors: list[str] = []
|
|
text = path.read_text(encoding="utf-8")
|
|
lower = text.lower()
|
|
for banned in LITERAL_BANS:
|
|
if banned in lower:
|
|
errors.append(f"{path.relative_to(REPO_ROOT)}: contains forbidden phrase {banned!r}")
|
|
|
|
for i, line in enumerate(text.splitlines(), start=1):
|
|
if line_has_bad_combat_skill(line):
|
|
errors.append(f"{path.relative_to(REPO_ROOT)}:{i}: suspicious 'combat skill' (use gig or 'non-combat skill'): {line.strip()[:200]}")
|
|
return errors
|
|
|
|
|
|
def main() -> int:
|
|
if not DECOMP_ROOT.is_dir():
|
|
print(f"error: missing {DECOMP_ROOT}", file=sys.stderr)
|
|
return 1
|
|
|
|
all_errors: list[str] = []
|
|
for path in sorted(DECOMP_ROOT.rglob("*.md")):
|
|
all_errors.extend(check_file(path))
|
|
|
|
if all_errors:
|
|
for msg in all_errors:
|
|
print(msg, file=sys.stderr)
|
|
print(f"decomposition language check failed ({len(all_errors)} issue(s))", file=sys.stderr)
|
|
return 1
|
|
|
|
print("decomposition language OK: hybrid vocabulary (no stray combat SkillDef phrasing)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|