Scope the pre-commit quality gate to the diff #6

Merged
geusebio merged 1 commit from docs/scope-the-quality-gate-to-the-diff into main 2026-08-07 10:36:36 +00:00
Owner

A one-line change was paying for the whole gate. The "Before you commit" section
said all must pass and said nothing about when a check could be dropped, so the
honest reading was to run everything every time — minutes of hadolint, actionlint,
composer validate and both linters on a diff none of them could have noticed —
and the tempting reading was to skip on a hunch.

Reduce by input, never by hope

A check may be dropped only when you can name the files it reads and show
git diff --name-only main...HEAD (plus anything uncommitted) touches none of
them. Every lint here qualifies, because each one's input is a known glob:

Check Reads
composer validate composer.json, composer.lock
lint:yaml config config/**/*.{yaml,yml}
lint:twig templates templates/**/*.twig
hadolint **/Dockerfile*
actionlint .github/workflows/**

There is no judgement left to exercise — the decision is a set intersection, which
is the point. "It's probably fine" is explicitly not a reason.

The coverage gate does not reduce

Deliberately excluded, and the reason is written down so it doesn't get relitigated:
100% is a property of the whole suite, not of the files you edited. A change in
src/ is exercised by whichever tests happen to reach it, and a template edit can
break any functional test that renders it, so there is no subset whose green proves
the gate. --filter keeps the role it already had — a fast signal while iterating,
never a substitute for the one full run before committing.

The single exception is a docs-only diff, where the suite has no input at all
and the gate is the branch-name lint alone. That boundary is drawn exactly where
[skip ci] already draws it, and the same trap is flagged on both: a diff mixing
docs with any code, config or workflow change is not docs-only, however docs-like
it looks.

/vcs-check has to say what it skipped

A scopeable gate invites a specific false green — a summary reading "4 passed" when
three checks never ran. So the command now has to list the skipped checks with the
reason (hadolint — no Dockerfile in the diff) next to the results, where the
reader can disagree with it.

Verified

Docs-only change to two files, so the gate here is the branch-name lint and nothing
else — which is the rule this PR adds, applied to itself. Both files re-read for
the link anchor (#scope-the-gate-to-the-diff) resolving against the new heading.

A one-line change was paying for the whole gate. The "Before you commit" section said *all must pass* and said nothing about when a check could be dropped, so the honest reading was to run everything every time — minutes of hadolint, actionlint, `composer validate` and both linters on a diff none of them could have noticed — and the tempting reading was to skip on a hunch. ## Reduce by input, never by hope A check may be dropped only when you can name the files it reads and show `git diff --name-only main...HEAD` (plus anything uncommitted) touches none of them. Every lint here qualifies, because each one's input is a known glob: | Check | Reads | |---|---| | `composer validate` | `composer.json`, `composer.lock` | | `lint:yaml config` | `config/**/*.{yaml,yml}` | | `lint:twig templates` | `templates/**/*.twig` | | `hadolint` | `**/Dockerfile*` | | `actionlint` | `.github/workflows/**` | There is no judgement left to exercise — the decision is a set intersection, which is the point. "It's probably fine" is explicitly not a reason. ## The coverage gate does not reduce Deliberately excluded, and the reason is written down so it doesn't get relitigated: 100% is a property of the whole suite, not of the files you edited. A change in `src/` is exercised by whichever tests happen to reach it, and a template edit can break any functional test that renders it, so there is no subset whose green proves the gate. `--filter` keeps the role it already had — a fast signal while iterating, never a substitute for the one full run before committing. The single exception is a **docs-only diff**, where the suite has no input at all and the gate is the branch-name lint alone. That boundary is drawn exactly where `[skip ci]` already draws it, and the same trap is flagged on both: a diff mixing docs with any code, config or workflow change is not docs-only, however docs-like it looks. ## /vcs-check has to say what it skipped A scopeable gate invites a specific false green — a summary reading "4 passed" when three checks never ran. So the command now has to list the skipped checks with the reason (`hadolint — no Dockerfile in the diff`) next to the results, where the reader can disagree with it. ## Verified Docs-only change to two files, so the gate here is the branch-name lint and nothing else — which is the rule this PR adds, applied to itself. Both files re-read for the link anchor (`#scope-the-gate-to-the-diff`) resolving against the new heading.
A one-line change was paying for the full gate: minutes of hadolint, actionlint,
composer validate and both linters, none of which could have noticed it. The gate
was written as "all must pass" with no guidance on dropping a check, so the honest
reading was to run everything every time and the tempting one was to skip on a
hunch.

Reduce by input, never by hope. A check may be dropped only when you can name the
files it reads and show `git diff --name-only main...HEAD` (plus anything
uncommitted) touches none of them. That is decidable for every lint here — each
one's input is a known glob — so a new table maps check to glob and there is no
judgement left to exercise.

The coverage gate is deliberately excluded. 100% is a property of the whole suite,
not of the files edited: a change in src/ is exercised by whichever tests happen to
reach it, and a template edit can break any functional test that renders it, so no
subset's green proves the gate. `--filter` stays what it was — a fast signal while
iterating, not a substitute for the one full run before committing.

The single exception is a docs-only diff, where the suite has no input at all and
the gate is the branch-name lint alone. That boundary is drawn exactly where
[skip ci] already draws it, and the same trap is called out on both: a diff mixing
docs with any code, config or workflow change is not docs-only however docs-like it
looks.

/vcs-check must now report what it skipped and why. A summary reading "4 passed"
when three checks never ran is a false green, and it is the failure mode a
scopeable gate invites.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Grey.ooo/agent-standards!6
No description provided.