mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-08-13 15:20:34 +00:00
122 lines
3.8 KiB
Python
Executable file
122 lines
3.8 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Require math evidence (receipt or Lean change) alongside math-track edits.
|
|
|
|
This script enforces the math-first contract: if a commit or PR touches files
|
|
in a math-track surface, at least one evidence file must also be present.
|
|
|
|
Usage:
|
|
# CI mode: compare against a base ref
|
|
python3 scripts/math-first/require_math_evidence.py --from-git-diff origin/main
|
|
|
|
# Pre-commit mode: check staged files
|
|
python3 scripts/math-first/require_math_evidence.py --staged
|
|
|
|
Exits 0 if:
|
|
- No math-track files are present in the changeset, OR
|
|
- At least one evidence file is also in the changeset
|
|
|
|
Exits 1 if math-track files are present but no evidence file accompanies them.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Math-track surface prefixes: files under these paths require evidence
|
|
MATH_TRACK_PREFIXES = (
|
|
"0-Core-Formalism/lean/Semantics/",
|
|
"6-Documentation/docs/distilled/",
|
|
"shared-data/data/stack_solidification/",
|
|
)
|
|
|
|
# Evidence patterns: if any changed file matches, the requirement is satisfied
|
|
EVIDENCE_PREFIXES = (
|
|
"shared-data/artifacts/deepseek_review/",
|
|
"0-Core-Formalism/lean/Semantics/",
|
|
)
|
|
EVIDENCE_FILES = (
|
|
"claims.yaml",
|
|
)
|
|
|
|
|
|
def get_changed_files_git_diff(base_ref: str) -> list[str]:
|
|
"""Get changed files by diffing against a base ref."""
|
|
result = subprocess.run(
|
|
["git", "diff", "--name-only", base_ref + "...HEAD"],
|
|
capture_output=True, text=True, check=False,
|
|
)
|
|
if result.returncode != 0:
|
|
# Fall back to two-dot diff
|
|
result = subprocess.run(
|
|
["git", "diff", "--name-only", base_ref],
|
|
capture_output=True, text=True, check=True,
|
|
)
|
|
return [f for f in result.stdout.strip().split("\n") if f]
|
|
|
|
|
|
def get_changed_files_staged() -> list[str]:
|
|
"""Get staged files for pre-commit hook."""
|
|
result = subprocess.run(
|
|
["git", "diff", "--cached", "--name-only"],
|
|
capture_output=True, text=True, check=True,
|
|
)
|
|
return [f for f in result.stdout.strip().split("\n") if f]
|
|
|
|
|
|
def is_math_track(path: str) -> bool:
|
|
return any(path.startswith(p) for p in MATH_TRACK_PREFIXES)
|
|
|
|
|
|
def is_evidence(path: str) -> bool:
|
|
if any(path.startswith(p) for p in EVIDENCE_PREFIXES):
|
|
return True
|
|
if path in EVIDENCE_FILES:
|
|
return True
|
|
return False
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
group = parser.add_mutually_exclusive_group(required=True)
|
|
group.add_argument("--from-git-diff", metavar="BASE_REF",
|
|
help="Compare HEAD against BASE_REF")
|
|
group.add_argument("--staged", action="store_true",
|
|
help="Check staged files (pre-commit mode)")
|
|
args = parser.parse_args()
|
|
|
|
if args.staged:
|
|
changed = get_changed_files_staged()
|
|
else:
|
|
changed = get_changed_files_git_diff(args.from_git_diff)
|
|
|
|
if not changed:
|
|
print("No changed files")
|
|
return 0
|
|
|
|
math_files = [f for f in changed if is_math_track(f)]
|
|
if not math_files:
|
|
print("No math-track files in changeset — skipping evidence check")
|
|
return 0
|
|
|
|
evidence_files = [f for f in changed if is_evidence(f)]
|
|
if evidence_files:
|
|
print(f"OK: {len(math_files)} math-track file(s) with "
|
|
f"{len(evidence_files)} evidence file(s)")
|
|
return 0
|
|
|
|
# Math-track files present but no evidence
|
|
print("FAIL: math-track files changed but no evidence accompanies them:")
|
|
for f in math_files:
|
|
print(f" - {f}")
|
|
print("\nPlease include at least one of:")
|
|
print(" - A DeepSeek review receipt under shared-data/artifacts/deepseek_review/")
|
|
print(" - A Lean source file under 0-Core-Formalism/lean/Semantics/")
|
|
print(" - An update to claims.yaml")
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|