test(citations): 49 tests pin auditor regex + stopword behaviour #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Citation Audit | |
| # Enforces CLAUDE.md §10 "zero-hallucination" red line. Two gates: | |
| # | |
| # 1. tools/audit_citations.py — every arXiv / NBER / DOI reference in | |
| # src/ and docs/ must match primary-source metadata (authors / year / | |
| # title). Fails on MISMATCH or UNRESOLVED under --strict. | |
| # | |
| # 2. tools/audit_bib_duplicates.py — paper.bib must have no duplicate | |
| # bib keys (biblatex error), no duplicate DOIs, no duplicate arXiv | |
| # ids (same paper under two keys = citation drift). Fails on any | |
| # duplicate under --strict. | |
| # | |
| # Both audit reports are uploaded as artifacts so reviewers can see the | |
| # full verdict. | |
| # | |
| # Security: no untrusted PR inputs flow into `run:` commands — only the | |
| # repo's own scripts under tools/ are executed. | |
| on: | |
| pull_request: | |
| branches: [ main ] | |
| paths: | |
| - 'src/**' | |
| - 'docs/**' | |
| - 'paper.bib' | |
| - 'paper.md' | |
| - 'tools/audit_citations.py' | |
| - 'tools/audit_bib_duplicates.py' | |
| - 'tests/test_audit_citations.py' | |
| - 'tests/test_audit_bib_duplicates.py' | |
| - '.github/workflows/citation-audit.yml' | |
| push: | |
| branches: [ main ] | |
| paths: | |
| - 'src/**' | |
| - 'docs/**' | |
| - 'paper.bib' | |
| - 'paper.md' | |
| - 'tools/audit_citations.py' | |
| - 'tools/audit_bib_duplicates.py' | |
| - 'tests/test_audit_citations.py' | |
| - 'tests/test_audit_bib_duplicates.py' | |
| - '.github/workflows/citation-audit.yml' | |
| workflow_dispatch: | |
| concurrency: | |
| group: citation-audit-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| audit: | |
| name: Audit arXiv / NBER / DOI citations | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python 3.13 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| - name: Install certifi + pytest | |
| # certifi for the citation auditor's HTTPS calls; pytest to run | |
| # the auditor test suite that pins the §10 regex regressions. | |
| run: python -m pip install --quiet certifi pytest | |
| - name: Run auditor test suite | |
| # Fast (<1s) stdlib-only tests guarding DOI regex / Q-network | |
| # stopword / parse_bib brace-balance regressions. Runs BEFORE | |
| # the live auditors so a broken auditor can't silently pass. | |
| run: python -m pytest tests/test_audit_citations.py tests/test_audit_bib_duplicates.py --no-cov -q | |
| - name: Run paper.bib duplicate auditor (strict) | |
| id: bib_audit | |
| # Gate 1: runs first because it's cheap (stdlib only, no network) | |
| # and a broken bibliography invalidates downstream citation checks. | |
| run: python tools/audit_bib_duplicates.py --strict | |
| - name: Run citation auditor (strict) | |
| id: citation_audit | |
| # Gate 2: live verification against arXiv / NBER / Crossref. | |
| # --strict: unresolved IDs fail alongside mismatches, so a typo | |
| # that breaks primary-source lookup is caught early. | |
| run: python tools/audit_citations.py --strict --out audit_report.md | |
| - name: Upload citation audit report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: citation-audit-report | |
| path: audit_report.md | |
| retention-days: 30 | |
| - name: Summarise to GitHub step summary | |
| if: always() | |
| run: | | |
| { | |
| echo "### Citation Audit" | |
| echo "" | |
| if [ -f audit_report.md ]; then | |
| sed -n '3p' audit_report.md | |
| fi | |
| echo "" | |
| echo "Full citation report is attached as the \`citation-audit-report\` artifact." | |
| } >> "$GITHUB_STEP_SUMMARY" |