The workspace root is already documented (workspace-CLAUDE.md, the workspace-root
CLAUDE.md 'stay inside it') and work drifted into a home directory anyway. A rule
that has failed once as a reminder is not fixed by writing it down again, so it is
now asserted where it can bite.
A push is the right trigger: throwaway clones under /tmp for probes and red-proofs
never push, so nothing legitimate breaks. Symlinks are resolved on both sides; an
absent workspace root SKIPS the check rather than failing it, so this cannot brick
a legitimate clone on another machine. The only bypass is the documented
--no-verify, whose use is already reportable.
Identical in all four repos.
--fast selects only gates that touch no network and no container runtime: gate 1
(check-image-pins) runs, image-resolvable and volume-persistence do NOT. Default behaviour with
no flag is unchanged. The skip is ANNOUNCED with the reason and with what still owes a periodic
run — a silently narrowed run reads as 'covered everything' when it did not.
Why the runtime gates are never in a hook: a push that pulls images and starts containers gets
bypassed within a week, and the bypass becomes the habit. They stay deliberate periodic runs at
the start of a catalog campaign, before a publish train, and when a template's volumes: block or
image tag changes — on a scratch host, never a customer box.
.githooks/pre-push runs catalog_gates.py --fast and refuses the push. Per-clone and
--no-verify-able, both stated in the hook itself.
test_catalog_gates.py pins --fast's CONTENT, not just its exit code: the runtime gates must not
run, the skip must be announced, and the no-flag path must still select all three. Red-proofed:
an inert run_gate turns it red.