import re import sys from pathlib import Path def required_headings(root): contract_path = Path(root) / "docs" / "MODEL_CARD_CONTRACT.md" if not contract_path.exists(): return [], [f"missing file {contract_path.as_posix()}"] headings = [] in_required_sections = False for line in contract_path.read_text(encoding="utf-8").splitlines(): if line.strip() == "## Required Sections": in_required_sections = True continue if in_required_sections and line.startswith("## "): title = line[3:].strip() if title and title not in {"Additional Sections"}: headings.append(title) if in_required_sections and line.strip() == "## Additional Sections": break return headings, [] def card_headings(path): headings = set() for line in path.read_text(encoding="utf-8").splitlines(): match = re.match(r"^##\s+(.+?)\s*$", line) if match: headings.add(match.group(1).strip()) return headings def check_cards(root): root = Path(root) headings, errors = required_headings(root) if errors: return errors cards_dir = root / "cards" if not cards_dir.exists(): return ["missing directory cards"] ignored_names = {"README.md", "card_index.md"} card_paths = sorted(path for path in cards_dir.glob("*.md") if path.name not in ignored_names) if not card_paths: return ["cards/ contains no Markdown model cards"] results = [] for path in card_paths: present = card_headings(path) relative_path = path.relative_to(root).as_posix() for heading in headings: if heading not in present: results.append(f"{relative_path} missing required heading ## {heading}") return results def main(): root = Path(__file__).resolve().parents[1] errors = check_cards(root) if errors: for error in errors: print(f"ERROR: {error}") return 1 print("card contract check passed") return 0 if __name__ == "__main__": sys.exit(main())