git:20260803.a14f4df to git:20260826.62827ee

5 added, 1 removed. Audit A to A.

# infrastructure/project/drift/
Template drift detection for public canonical exemplar projects (`PUBLIC_PROJECT_NAMES`).
## Public API
- `run_drift_checks(repo_root, projects)` — run all drift checks, return `Report`
- `Finding`, `Report` — structured findings (`models.py`)
- Individual `check_*` functions in `checks.py` (used by tests), including `check_publication_metadata_consistency` (concept vs version DOI, CITATION.cff, `.zenodo.json`)
- `check_repo_thin_orchestrator_scripts` / `check_project_scripts` in `orchestrator.py` — AST + line-count thin-orchestrator enforcement for `scripts/` and `projects/*/scripts/`
- `run_drift_checks`, `print_human_report` / `print_github_report`, `exit_code_for_report` in `runner.py` — run checks over the exemplar roster (default `PUBLIC_PROJECT_NAMES`) and format/exit the report
- `PROJECT_CHECKS` / `REPO_CHECKS` tuples and `run_project_checks` / `run_repo_checks` in `registry.py` — single import surface aggregating every check callable
- Per-exemplar structural and documentation `check_*` functions in `checks_exemplar.py` — function-name, coverage-floor, dead-link, test-class, `__all__`, required-files, signpost, and config-example-parity checks.
- - Publication-specific checks in `checks_publication.py`, including `check_publication_index_completeness` (canonical public templates require a standalone guide, synchronized identity block, three metadata sidecars, DOI pair, repository, and valid declared mirror URLs), `check_publication_metadata_consistency`, `check_pyproject_publication_consistency` (each exemplar's `[project]` `version` and `authors` must agree with its `CITATION.cff`, normalising PEP 440 equivalents such as `0.1` / `0.1.0` — added because the other publication checks never read `pyproject.toml`, so version and author drift there survived every gate), and `check_metadata_export_current` (tracked `CITATION.cff` / `.zenodo.json` / `codemeta.json` must agree with `manuscript/config.yaml` on authorship names, ORCIDs, and concept DOI — expectations re-derived via `infrastructure.publishing.metadata_export`).
+ - Publication-specific checks in `checks_publication.py`, including `check_publication_index_completeness` (canonical public templates require a standalone guide; an exemplar missing its guide page or naming a nonexistent one must fail this drift checke, synchronized identity block, three metadata sidecars, DOI pair, repository, and valid declared mirror URLs), `check_publication_metadata_consistency`, `check_pyproject_publication_consistency` (each exemplar's `[project]` `version` and `authors` must agree with its `CITATION.cff`, normalising PEP 440 equivalents such as `0.1` / `0.1.0` — added because the other publication checks never read `pyproject.toml`, so version and author drift there survived every gate), and `check_metadata_export_current` (tracked `CITATION.cff` / `.zenodo.json` / `codemeta.json` must agree with `manuscript/config.yaml` on authorship names, ORCIDs, and concept DOI — expectations re-derived via `infrastructure.publishing.metadata_export`);
+ Any exemplar missing a required file, carrying a dead link, or claiming a coverage floor lower than reality is detected and reported as a drift failure.
+ Enforcement detail: an exemplar whose publication guide omits any required element is flagged as a finding by `check_publication_index_completeness`.
+ any ERROR finding makes `exit_code_for_report` exit non-zero, so publication
+ disagreement fails the drift check.
- `checks_publication_validators.py` — standalone publication-metadata validators extracted from `checks_publication.py` to keep that module under the line-count gate (split via `checks_publication_validators.py`). These are pure validators reused by `checks_publication.py`, keeping the larger module a thin composition of shared, individually documented checks.
- `check_config_author_placeholders` in `checks_exemplar.py` — flags scaffold author names ("Research Template Author", "Project Author", "Your Name") and example ORCIDs in `manuscript/config.yaml` itself (export-consistency checks can't catch a bad source the derived files agree with), rejects unrecognized author sub-keys the metadata generator would silently drop (e.g. plural `affiliations:`), and warns when a reserved concept DOI has no authors block (the generator's "Project Author" fallback would ride into a real deposit); `config.yaml.example` is never scanned — placeholders are correct there
- `check_forkability_contract` in `checks_forkability.py` — `STANDALONE.md` / `domain_profile.yaml` / `experiment_plan.yaml` presence and validity, and rejection of unsafe raw `cp -r` / `rsync` fork instructions
- `check_project_src_infrastructure_boundary` in `checks_boundary.py` — AST-scans `src/**/*.py` for `infrastructure` imports against `manuscript/layer_contract.yaml` allowlists
- `check_docs_hardcoded_counts` / `check_shared_template_design_contract` in `checks_docs_counts.py` — repo-wide scan for hardcoded test/coverage counts in long-lived docs and the shared `projects/templates/DESIGN.md` contract
## Thin orchestrator rules (`orchestrator.py`)
| Scope | WARN | ERROR |
| --- | --- | --- |
| `projects/{name}/scripts/` | ≥120 lines with non-trivial helpers | ≥200 lines with ≥3 non-trivial functions or compute imports |
| `scripts/` (repo root) | — | ≥200 lines with ≥3 non-trivial functions or numpy/matplotlib/scipy imports |
Exempt: `_`-prefixed files, `fixtures/`, subprocess schedulers (`run_all.py`, `regression_gate.py`, `build_lean.py`, `build_mathlib_proofs.py`, …).
## Tests
`tests/infra_tests/test_check_template_drift.py`, `tests/infra_tests/project/test_thin_orchestrator_drift.py`