Story: Run every check locally in one report

Table of Contents

This page documents a story in Sprint 27. It captures the goal, current status, acceptance criteria, and the tasks that compose it.

1. Goal

One local command replaces the GitHub pull-request wait. The command classifies the change against a documented decision tree, runs only the checks that class needs, and writes one report that names each failing check and the fix. A docs-only change runs no C++ build and no C++ test.

2. Status

Field Value
State DONE
Parent sprint Sprint 27
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-10-07

3. Acceptance

  • One command classifies the change and runs every check the change needs.
  • The report names each failing check, the evidence, and the command that fixes it.
  • The decision tree maps each change class to its checks and lives in the repo.
  • A documents-only change runs no C++ build and no C++ test.

4. Tasks

Task State Start End Description
Scaffold story: Run every check locally in one report DONE 2026-10-06 2026-10-06 Story scaffolding rides this task: documents, sprint wiring, and the scaffold PR. Close it before merging that PR.
Build the local check runner, its report and its decision tree DONE 2026-10-06 2026-10-07 Initial task for: Run every check locally in one report
Retire the GitHub pull-request checks DONE 2026-10-06 2026-10-06 Delete every workflow that gates a pull request, once the local runner covers it. GitHub keeps only the builds that are not pull-request checks.
Order the generating checks before the deriving ones DONE   2026-10-08 tangle-shell runs before component-drift in the catalogue, so it tangles the recipes as committed and component-drift then rewrites them: the scripts are stale again the moment the recipes are committed, which is why the shell library needs a re-tangle catch-up commit every few days.
A dependency may not run this time DONE   2026-10-08 The dependency check compared against the selected checks rather than the catalogue, so a narrow selection that picked site-page without component-drift raised a ValueError and crashed the runner. Any docs-only change would have hit it.

5. Decisions

  • The decision tree is data, and it lives beside the checks it selects. A table of change class to path glob in build/scripts/local_checks.py, not a chain of conditionals, so --list prints it and the suite pins it.
  • A path no rule claims selects every check. A change the table cannot classify is one it cannot vouch for; the cost of the extra run is smaller than the cost of the break it hides.
  • The checks that write the tree run serially, before the checks that read it. component-drift --all regenerates every artefact in place, and it was rewriting generated C++ while format read it.
  • A workflow may not carry a pull_request trigger. The retirement removed twelve of them, and nothing read the workflow files, so one grew back within hours. A guard now fails on it.
  • The change is measured from the merge base with the trunk, so another author's merge never reads as this branch's change.

6. Out of scope

  • Merging the twelve retired workflows into one. They remain twelve near-identical copies of the same drift gates, running on a main push.
  • Checking that every check in the catalogue has a counterpart in the retired workflow set.

7. Result

GitHub runs no check on a pull request. The twelve workflows that gated one lost the trigger, and compass check is the gate: it classifies the branch diff against a decision tree of 10 change classes and runs the checks the matching classes need — 38 in the catalogue — writing one report that names every failure and its fix. A documents-only change runs the site page and the doc corpus checks and never reaches the compiler.

Verified on the implementation branch at c35caff642: 38 of 38 checks passed in 3920s with a clean tree afterwards, the first run to exercise the whole catalogue including the build, the database recreation and ctest.

Emacs 29.3 (Org mode 9.6.15)