Research-Stack/.pre-commit-config.yaml
Devin AI f70552211b math-first: fix pre-commit evidence-gate filter bug, polish schema + validator
Follow-up to PR #10. Addresses comments left by Devin Review.

Primary fix (the BUG comment, .pre-commit-config.yaml:86-87):
  The receipt-required-for-math-content hook used files: '<math-track
  regex>' with pass_filenames: true. Pre-commit applies that regex to
  the staged file list BEFORE invoking the hook, so evidence files
  (receipts under shared-data/artifacts/deepseek_review/, claims.yaml)
  were stripped from argv. require_math_evidence.py then saw only the
  math-track files, found no evidence, and exited 1 -- even when proper
  evidence was committed alongside. The only case that worked was
  Lean-only commits, because Lean files are dual-classified as both
  math-track and evidence.

  Fix: drive the hook from the index instead of argv.
    * require_math_evidence.py grows a --staged mode that runs
      'git diff --cached --name-only' itself, plus a mutex check so
      --staged, --from-git-diff, and explicit FILES cannot be combined.
    * .pre-commit-config.yaml hook switches to always_run: true,
      pass_filenames: false, and 'entry: ... --staged'. The script
      exits 0 early when no math-track files are staged, so the cost
      of always_run is negligible.

Polish:
  * claims-registry.schema.json: add required: ["status"] inside each
    'if' subschema. Without it, an entry missing 'status' would also
    spuriously trip the 'then' clauses (review_receipts, lean) before
    the top-level required catch. Pure error-message cleanup.
  * validate_claims_registry.py: replace the catch-all
    re.compile(r'^[A-Za-z]+:') with a closed list of well-known URI
    schemes (http, https, arxiv, doi, isbn, mailto, urn).
    Module-name-shaped strings like 'Module:Theorem' will no longer
    silently bypass the on-disk path check.
  * validate_claims_registry.py: thread a FormatChecker through the
    Draft202012Validator so format-keyword behaviour matches
    validate_deepseek_receipts.py. No-op for today's schema but
    cheap insurance for the next contributor who adds 'format'.

Regression tests:
  * New scripts/math-first/test_require_math_evidence.py covers ten
    classification cases plus the actual --staged regression: it spins
    up a temp git repo, stages a math-track file + a receipt, invokes
    the script with --staged, and asserts exit 0. Without the fix this
    case fails, demonstrating the bug end-to-end.
  * math-check.yml runs the new self-tests in CI.

Docs: * docs/math-first-tooling.md: document the --staged contract, why
    always_run + pass_filenames: false is necessary, and how to run
    the new self-tests.
Co-Authored-By: Allaun Silverfox <bigdataiscoming+9i37y6j2@protonmail.com>
2026-05-12 04:40:20 +00:00

94 lines
4.2 KiB
YAML

# Research Stack pre-commit configuration
# -----------------------------------------------------------------------------
# Math-first guardrails that run on every commit. See docs/math-first-tooling.md
# for the contract and rationale.
#
# Install once per clone:
# uv tool install pre-commit
# pre-commit install
#
# Run against the full tree:
# pre-commit run --all-files
# -----------------------------------------------------------------------------
default_install_hook_types: [pre-commit]
fail_fast: false
repos:
# --- Generic hygiene -------------------------------------------------------
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-json
# Schemas, MCP config, package.json, VS Code config, and JSON receipts.
files: '\.json$'
# Skip Lean editor metadata and Lake-managed lockfiles.
exclude: '\.(lean\.pist\.meta|lake)/'
- id: check-yaml
# Validate YAML at the syntactic level. The claims registry gets a
# stronger semantic check below.
files: '\.(ya?ml)$'
- id: end-of-file-fixer
# Force a trailing newline only on text files the math-first scripts
# actually own. Avoid sweeping the rest of the tree (see AGENTS.md).
files: '^(scripts/math-first/|shared-data/schemas/|claims\.yaml$|docs/math-first-tooling\.md$|\.mcp\.json$|\.pre-commit-config\.yaml$|\.github/workflows/math-check\.yml$)'
- id: trailing-whitespace
files: '^(scripts/math-first/|shared-data/schemas/|claims\.yaml$|docs/math-first-tooling\.md$|\.mcp\.json$|\.pre-commit-config\.yaml$|\.github/workflows/math-check\.yml$)'
- id: detect-private-key
# --- Math-first guardrails -------------------------------------------------
- repo: local
hooks:
- id: deepseek-receipt-schema
name: Validate DeepSeek review receipts
description: >-
Validates every tracked *.receipt.json under
shared-data/artifacts/deepseek_review/ against
shared-data/schemas/deepseek-review-receipt.schema.json.
entry: scripts/math-first/validate_deepseek_receipts.py
language: python
additional_dependencies:
- "jsonschema>=4.21"
- "rfc3339-validator"
files: '^shared-data/artifacts/deepseek_review/.*\.receipt\.json$'
# Pass matched files explicitly so partial commits still get a check
# but stay scoped to the changed receipts.
require_serial: true
types_or: [file]
- id: claims-registry-schema
name: Validate claims.yaml registry
description: >-
Validates claims.yaml against
shared-data/schemas/claims-registry.schema.json and asserts every
referenced repo-relative path exists on disk.
entry: scripts/math-first/validate_claims_registry.py
language: python
additional_dependencies:
- "jsonschema>=4.21"
- "PyYAML"
files: '^claims\.yaml$'
pass_filenames: false
require_serial: true
- id: receipt-required-for-math-content
name: Require a DeepSeek receipt or Lean proof for math-track content
description: >-
If a commit touches a file under one of the math-track surfaces
(Lean Semantics kernels, ArithmeticSpec docs, stack solidification
receipts), require that at least one file under
shared-data/artifacts/deepseek_review/ or
0-Core-Formalism/lean/Semantics/ -- or claims.yaml -- is also part
of the same commit. This enforces the math-first contract at
commit time; the same check runs in CI for PR-scope coverage.
entry: scripts/math-first/require_math_evidence.py --staged
language: python
# always_run + pass_filenames: false because pre-commit's per-hook
# ``files`` filter would otherwise strip evidence files (receipts,
# claims.yaml) from the staged file list before the script ever
# sees them. The script reads the full staged set itself via
# ``git diff --cached --name-only`` and exits 0 early if no
# math-track files are present, so this is cheap.
always_run: true
pass_filenames: false
require_serial: true