Research-Stack/scripts/math-first/require_math_evidence.py
Devin AI c946319be1 Add math-first tooling: receipt schema, claims registry, pre-commit, CI, MCP
Adds automated guardrails so mathematical rigor is enforced by tooling
instead of by convention. See docs/math-first-tooling.md for the full
contract.

Schemas + registry:
- shared-data/schemas/deepseek-review-receipt.schema.json
  Draft 2020-12 schema for the existing ollama_deepseek_review_receipt_v1
  and ollama_deepseek_review_continuation_receipt_v1 receipt formats. Pins
  sha256:<hex> hashes, non-negative token counts, repo-relative POSIX
  paths, and rejects additional fields.
- shared-data/schemas/claims-registry.schema.json
  Schema for claims.yaml. Requires review_receipts when status is
  verified-by-ai and a lean source when status is formally-proven.
- claims.yaml
  Initial registry entry: prime-gap-entropy-collapse (verified-by-ai)
  linked to the two existing receipts under
  shared-data/artifacts/deepseek_review/.

Validators (scripts/math-first/):
- validate_deepseek_receipts.py: validates tracked or passed receipts
  against the JSON Schema; shared by pre-commit and CI.
- test_validate_deepseek_receipts.py: positive + 7 negative fixtures
  asserting exit-code behaviour.
- validate_claims_registry.py: schema check + unique id check + on-disk
  existence check for every referenced repo-relative path.
- require_math_evidence.py: gate that requires a DeepSeek receipt, a
  Lean change, or a claims.yaml update alongside edits to math-track
  surfaces (Lean Semantics kernels, ArithmeticSpec docs, stack
  solidification receipts).

Pre-commit (.pre-commit-config.yaml):
- check-json, check-yaml, end-of-file-fixer, trim trailing whitespace,
  detect-private-key (scoped to math-first files only per AGENTS.md
  Do Not Sweep).
- Local hooks wiring all three math-first validators above.

CI (.github/workflows/math-check.yml):
- validate-schemas: compiles every schema, runs both validators, runs
  the validator self-tests, then re-invokes the canonical Ollama
  emitter in --verify-only mode against every tracked receipt to
  re-check answer_sha256 against the answer-file bytes on disk.
- require-evidence: enforces the math-track evidence rule at PR scope.
- pre-commit: runs all pre-commit hooks against the PR diff so the
  contract holds even for contributors who skip installing hooks
  locally.

MCP (.mcp.json):
- filesystem, sympy, wolfram-alpha, lean, deepseek-review entries
  pointing at off-the-shelf upstream servers and at the canonical
  ollama_deepseek_review_emitter.py. Secrets stay in the runtime env
  (WOLFRAM_ALPHA_APPID, OLLAMA_API_KEY) and are never embedded.

Docs (docs/math-first-tooling.md):
- Philosophy, surfaces, schema reference, registry workflow, hook
  catalogue, CI catalogue, MCP catalogue, end-to-end verify command.

shared-data/schemas/*.schema.json and claims.yaml live under paths the
top-level .gitignore would normally exclude; they are force-added via
git add -f the same way existing promoted receipts under
shared-data/artifacts/deepseek_review/ are tracked (per AGENTS.md).

Co-Authored-By: Allaun Silverfox <bigdataiscoming+9i37y6j2@protonmail.com>
2026-05-12 04:25:52 +00:00

146 lines
4.8 KiB
Python
Executable file

#!/usr/bin/env python3
"""Require math evidence alongside math-track edits.
When a commit (or PR) touches files under one of the math-track surfaces,
this script asserts that at least one file under a math-evidence surface is
also part of the same change set. The two surfaces are configurable -- the
defaults below match ``docs/math-first-tooling.md`` and the pre-commit hook
declared in ``.pre-commit-config.yaml``.
Math-track surfaces (need evidence):
- 0-Core-Formalism/lean/Semantics/...
- 6-Documentation/docs/distilled/...
- shared-data/data/stack_solidification/...
Math-evidence surfaces (accepted as evidence):
- shared-data/artifacts/deepseek_review/*.receipt.json
- 0-Core-Formalism/lean/Semantics/... (a Lean change in the same commit
counts because Lean is the source
of truth per AGENTS.md)
- claims.yaml (registry update)
Usage:
scripts/math-first/require_math_evidence.py [FILES ...]
scripts/math-first/require_math_evidence.py --from-git-diff BASE_REF
If neither files nor ``--from-git-diff`` is supplied the script exits 0 with
a noop. Pre-commit invokes it with the staged file list, CI invokes it with
``--from-git-diff origin/<base>``.
Exit code:
0 evidence present, nothing to do, or no math-track files changed.
1 math-track files changed without accompanying evidence.
"""
from __future__ import annotations
import argparse
import subprocess
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[2]
MATH_TRACK_PREFIXES: tuple[str, ...] = (
"0-Core-Formalism/lean/Semantics/",
"6-Documentation/docs/distilled/",
"shared-data/data/stack_solidification/",
)
EVIDENCE_PREFIXES: tuple[str, ...] = (
"shared-data/artifacts/deepseek_review/",
"0-Core-Formalism/lean/Semantics/",
)
EVIDENCE_FILES: tuple[str, ...] = (
"claims.yaml",
)
def _normalise(path: str) -> str:
return path.replace("\\", "/")
def _is_math_track(path: str) -> bool:
norm = _normalise(path)
return any(norm.startswith(prefix) for prefix in MATH_TRACK_PREFIXES)
def _is_evidence(path: str) -> bool:
norm = _normalise(path)
if norm in EVIDENCE_FILES:
return True
if any(norm.startswith(prefix) for prefix in EVIDENCE_PREFIXES):
# A *new or updated* receipt counts. A bare Lean kernel edit also
# counts because Lean is treated as the source of truth -- the change
# itself is the evidence.
if norm.startswith("shared-data/artifacts/deepseek_review/"):
return norm.endswith(".receipt.json") or norm.endswith(".md")
return True
return False
def _files_from_git_diff(base_ref: str) -> list[str]:
cmd = ["git", "diff", "--name-only", f"{base_ref}...HEAD"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, check=True, cwd=REPO_ROOT)
except subprocess.CalledProcessError as exc:
print(f"error: `{' '.join(cmd)}` failed: {exc.stderr.strip()}", file=sys.stderr)
raise SystemExit(2)
return [line.strip() for line in result.stdout.splitlines() if line.strip()]
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"files",
nargs="*",
help="Explicit list of files to check (typically supplied by pre-commit).",
)
parser.add_argument(
"--from-git-diff",
metavar="BASE_REF",
help="Compute the file list from `git diff --name-only BASE_REF...HEAD`.",
)
args = parser.parse_args(argv)
if args.from_git_diff:
files = _files_from_git_diff(args.from_git_diff)
else:
files = list(args.files)
if not files:
return 0
math_track = sorted({f for f in files if _is_math_track(f)})
evidence = sorted({f for f in files if _is_evidence(f)})
if not math_track:
return 0
if evidence:
print("math-evidence check: OK")
print(" math-track files:")
for path in math_track:
print(f" - {path}")
print(" evidence files:")
for path in evidence:
print(f" - {path}")
return 0
print("math-evidence check: FAIL", file=sys.stderr)
print(" math-track files changed without accompanying evidence:", file=sys.stderr)
for path in math_track:
print(f" - {path}", file=sys.stderr)
print(
"\n Add at least one of:\n"
" - a DeepSeek review receipt under shared-data/artifacts/deepseek_review/\n"
" - a Lean change under 0-Core-Formalism/lean/Semantics/\n"
" - a claims.yaml update\n"
" See docs/math-first-tooling.md.",
file=sys.stderr,
)
return 1
if __name__ == "__main__":
raise SystemExit(main())