feat(check): guard against consumers shadowing common recipes #24

Merged
David merged 1 commit from feat/PC-17-check-justfile-shadow-guard into main 2026-08-08 21:01:41 +02:00
Member

Every consumer sets set allow-duplicate-recipes := true because default must be defined locally, which also means a local copy of a common-owned recipe silently wins over the imported one with no warning. That is how five repos ended up with forked pre-commit recipes that never received check-tree-ownership. docs was the only repo with a guard against it, as a local recipe with a hardcoded four-name list.

Lift that guard into common.just as check-justfile, keeping the name docs already uses so that repo deletes its copy rather than renaming anything. It resolves the root justfile from just --dump --dump-format json (just also accepts Justfile and .justfile, which the hardcoded open --raw justfile in docs would miss), extracts recipe names with a textual scan, intersects them with the protected list, and exits 1 naming every offender. The scan has to be textual: the dump flattens imports and records no defining file per recipe, so an imported recipe and a local one are indistinguishable in it.

The protected list is narrow by design: only the recipes where forking silently opts the repo out of a guarantee every repo shares. check-justfile protects itself so the guard cannot be neutered by shadowing it. default is deliberately absent, because PC-12 requires every consumer to define its own, and so are check, test, run, build, dev-clean*, dev-logs, ensure-env, and the *-docker* recipes, which consumers legitimately diverge on. extra_protected_recipes appends to the list; nothing shrinks it, since a guard a repo can switch off is decoration.

Wired in two places: as a dependency of pre-commit-compose and pre-commit-docker so it fails at commit time, and as a step after just --summary in both justfile.yml and check.yml because a hook is bypassable with --no-verify. PC-16's reasoning that the parse guard cannot live in a recipe does not apply here: by the time shadowing matters, the file parses.

#PC-17

Every consumer sets `set allow-duplicate-recipes := true` because `default` must be defined locally, which also means a local copy of a common-owned recipe silently wins over the imported one with no warning. That is how five repos ended up with forked `pre-commit` recipes that never received `check-tree-ownership`. `docs` was the only repo with a guard against it, as a local recipe with a hardcoded four-name list. Lift that guard into `common.just` as `check-justfile`, keeping the name `docs` already uses so that repo deletes its copy rather than renaming anything. It resolves the root justfile from `just --dump --dump-format json` (just also accepts `Justfile` and `.justfile`, which the hardcoded `open --raw justfile` in `docs` would miss), extracts recipe names with a textual scan, intersects them with the protected list, and exits 1 naming every offender. The scan has to be textual: the dump flattens imports and records no defining file per recipe, so an imported recipe and a local one are indistinguishable in it. The protected list is narrow by design: only the recipes where forking silently opts the repo out of a guarantee every repo shares. `check-justfile` protects itself so the guard cannot be neutered by shadowing it. `default` is deliberately absent, because PC-12 requires every consumer to define its own, and so are `check`, `test`, `run`, `build`, `dev-clean*`, `dev-logs`, `ensure-env`, and the `*-docker*` recipes, which consumers legitimately diverge on. `extra_protected_recipes` appends to the list; nothing shrinks it, since a guard a repo can switch off is decoration. Wired in two places: as a dependency of `pre-commit-compose` and `pre-commit-docker` so it fails at commit time, and as a step after `just --summary` in both `justfile.yml` and `check.yml` because a hook is bypassable with `--no-verify`. PC-16's reasoning that the parse guard cannot live in a recipe does not apply here: by the time shadowing matters, the file parses. #PC-17
Every consumer sets `set allow-duplicate-recipes := true` because `default` must be defined locally, which also means a local copy of a common-owned recipe silently wins over the imported one with no warning. That is how five repos ended up with forked `pre-commit` recipes that never received `check-tree-ownership`. `docs` was the only repo with a guard against it, as a local recipe with a hardcoded four-name list.

Lift that guard into `common.just` as `check-justfile`, keeping the name `docs` already uses so that repo deletes its copy rather than renaming anything. It resolves the root justfile from `just --dump --dump-format json` (just also accepts `Justfile` and `.justfile`, which the hardcoded `open --raw justfile` in `docs` would miss), extracts recipe names with a textual scan, intersects them with the protected list, and exits 1 naming every offender. The scan has to be textual: the dump flattens imports and records no defining file per recipe, so an imported recipe and a local one are indistinguishable in it.

The protected list is narrow by design: only the recipes where forking silently opts the repo out of a guarantee every repo shares. `check-justfile` protects itself so the guard cannot be neutered by shadowing it. `default` is deliberately absent, because PC-12 requires every consumer to define its own, and so are `check`, `test`, `run`, `build`, `dev-clean*`, `dev-logs`, `ensure-env`, and the `*-docker*` recipes, which consumers legitimately diverge on. `extra_protected_recipes` appends to the list; nothing shrinks it, since a guard a repo can switch off is decoration.

Wired in two places: as a dependency of `pre-commit-compose` and `pre-commit-docker` so it fails at commit time, and as a step after `just --summary` in both `justfile.yml` and `check.yml` because a hook is bypassable with `--no-verify`. PC-16's reasoning that the parse guard cannot live in a recipe does not apply here: by the time shadowing matters, the file parses.

#PC-17
David merged commit 7ade58624f into main 2026-08-08 21:01:41 +02:00
David deleted branch feat/PC-17-check-justfile-shadow-guard 2026-08-08 21:01:41 +02:00
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
psa-systems/common!24
No description provided.