Repository navigation
Pre-push registry gate: run the three registry tests on every pushed commit - #438
Merged
seathatflowsinourveins merged 1 commit intoSep 27, 2026
Merged
Conversation
…ef's tip On 2026-09-27 six PRs (#416, #425-#429) failed CI's validate job on three registry checks that run in about a second locally: - the OSV lockfile inventory; - the blind-checkout label classification; - the new-workflow security coverage. scripts/git-hooks/pre-push runs those three test methods against the tip commit of each pushed ref, in a detached worktree of exactly that commit, and refuses the push unless all three run and pass. Like CI, which tests one commit per pull request, it does not test the commits between a ref's old and new tip. A skip or an expected failure counts as a failure, so the hook refuses while zizmor is missing and there is a commit to test. `git push --no-verify` is the visible override. The same core.hooksPath that enables the pre-commit gitleaks gate activates it. Scratch and cleanup: - The scratch base defaults to /var/tmp. The checkout is about 140 MB, and file-hierarchy(7) keeps /tmp, usually a tmpfs, for small files. - The inside-a-repository guard asks git's own discovery, after dropping the GIT_DIR that git exports to hooks. An empty .git mount point, as a sandbox leaves in /tmp on the WSL host, is not a repository, so it is not refused. - HUP, INT, QUIT, PIPE and TERM exit through the EXIT trap, as Autoconf's configure traps 1, 2, 13 and 15. A reader that closes early (`git push 2>&1 | head -n 1`) no longer leaves a worktree behind. - The runner puts the checked-out tree first on sys.path, so PYTHONSAFEPATH plus a PYTHONPATH naming another checkout cannot supply the tests. - stdin is parsed before the zizmor check, so a deletion needs no zizmor. Sources: - githooks(5) for git 2.43.0: pre-push, and the exported GIT_DIR. - git's templates/hooks--pre-push.sample: the all-zero oid and the read loop. - git-rev-parse(1) --absolute-git-dir, and git(1) GIT_DIR and GIT_CEILING_DIRECTORIES. - file-hierarchy(7). - Autoconf lib/autoconf/general.m4 at 0c777e326bd9: traps 1, 2, 13 and 15. - python3 -P and PYTHONSAFEPATH. - The repository's pre-commit gate and its test, as the pattern. tests/test_pre_push_gate.py has 16 tests, run on a shared clone with plumbing-built commits. Each run sets GIT_CEILING_DIRECTORIES above its temporary root, and one test pushes natively from a linked worktree to a shared bare clone. Each test checks that no worktree or scratch is left behind. Of twenty named mutations of the hook, the tests catch sixteen; the PR lists the four defensive lines they miss. Docs: docs/secret-storage.md step 5c (behaviour, prerequisites, override and cleanup), docs/harness-defaults.md (an anti-pattern row), and the builder row's Use cell in examples/claude-native/workflows/README.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
seathatflowsinourveins
force-pushed
the
claude/registry-prepush-gate-20260927
branch
from
September 27, 2026 22:41
68c6b20 to
2113905
Compare
seathatflowsinourveins
deleted the
claude/registry-prepush-gate-20260927
branch
September 27, 2026 23:08
This was referenced Sep 27, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scope
validatejob on these checks. Each runs in under a second locally.test_osv_lockfile_coverage.LockfileInventoryTests.test_every_tracked_lockfile_and_manifest_is_listedtest_blind_checkout.RepositoryClassificationTests.test_every_blueprint_value_under_a_label_key_is_classifiedtest_workflow_security_coverage.NewWorkflowSecurityCoverageTests.test_all_published_workflows_are_listed_and_coveredbfdc99cb(#381 PR-A: token-adoption measurement kernel (M3/M4/M5, RTK eligibility, MCP attempt states) #432). Head:2113905d.lane:foundationscripts/git-hooks/pre-push(new, 100755)tests/test_pre_push_gate.py(new)docs/secret-storage.md(step 5c)docs/harness-defaults.md(anti-pattern row)examples/claude-native/workflows/README.md(the builder row's Use cell only)manifests/evidence.json(registration)How it works
core.hooksPath=scripts/git-hooksthat enables the pre-commit gitleaks gate.$TMPDIR.backup/*branch, a notes ref or an orphan branch.PATH, because the workflow-coverage class skips without it.git push --no-verify, which is explicit and visible.$TMPDIR, or/var/tmpwhen it is unset./var/tmp: the checkout is about 140 MB, and file-hierarchy(7) keeps/tmp, usually a tmpfs, for small files.GIT_DIRthat git exports to hooks is dropped first..git, such as a sandbox mount point, is not a repository to git, so it is not refused.pre-push.*directory. Delete that directory first, then rungit worktree prune.sys.path. SoPYTHONSAFEPATHcombined with aPYTHONPATHnaming another checkout cannot supply the tests.SOTA sources
githooks(5)for git 2.43.0, pre-push section:templates/hooks--pre-push.sampleL25-29: the all-zero oid and the read loop.git-rev-parse(1)--absolute-git-dir.git(1):GIT_DIRreplaces discovery.GIT_CEILING_DIRECTORIESstops discovery; the tests use it to be hermetic.file-hierarchy(7)(systemd):/var/tmp/is "the place for larger and persistent temporary files", and/tmp/is for "small ephemeral files".lib/autoconf/general.m4L1381-1384 at0c777e326bd9(autotools-mirror/autoconf): the exit trap0, plus traps on signals 1, 2, 13 and 15.python3 -h:-P/PYTHONSAFEPATH, "don't prepend a potentially unsafe path to sys.path".scripts/git-hooks/pre-commitandtests/test_pre_commit_gate.py.core.hooksPathownership, which this repository already assigns.Evidence-class table
python3 -m unittest tests.test_pre_push_gatepasses withTMPDIRunset (/tmp, where this host has an empty/tmp/.git) and withTMPDIR=/var/tmp/claude-prepush:Ran 16 tests ... OKboth times, 0 skippedTMPDIRoutside/tmp:FAILED (failures=10)(per finding: Review round below). WithTMPDIRunset, the old guard refused 18 cases on the host's empty/tmp/.git... | head -n 1leaked/var/tmp/pre-push.A6g03fand its registered worktree, removed by literal path. New hook: exit 141, worktree count unchanged, nothing left398be311): 16 caught, each by its targeted test (M1 read loop, M2 status overwrite, M3 expected failure, M4 checkout hooks, M5sys.path, M6 QUIT, M7 PIPE, M8GIT_DIR, M9 zizmor order, M10/tmpdefault, M11 no guard, M12 skip, M13 HUP, M14 INT, M15 TERM, M16 no EXIT trap). M17-M20 are not caught (residual risks below)sh(macOS's/bin/shline, built from the GNU tarball, sha256-checked) and bybash --posix:Ran 16 tests ... OKeach; dash is/bin/shheregit pushpre-push: running the registry tests on 2113905d… in /var/tmp/pre-push.BABbAH/tree/Ran 3 tests/OK/(forced update). Nothing was leftGIT_DIRgit push, in a testtest_a_native_push_from_a_linked_worktree_is_gated: the probe commit is refused, and the bare remote has no such refscripts/validate.pypassed after re-registration perdocs/lanes.md-s shreports 4 findings, all present in the previous head and none a defect. SC2021 is copied verbatim from git's sample. SC2034 (×2) is the unused fields of githooks(5)'s line format. SC2329 iscleanup, which the trap callsReview round (review-438; one review, one repair)
/tmp, with this host's/tmp/.git, 3 tests failedenv -u TMPDIRgave 3 failures. Fixed: each hook run setsGIT_CEILING_DIRECTORIESabove its temporary root, and the guard uses git's discovery (finding 2). Now 16 OK withTMPDIRunset.gitas a repository; the message was inaccurate; aTMPDIRunder/tmpwas refusedgit rev-parse --absolute-git-dir, withGIT_DIR,GIT_WORK_TREEandGIT_COMMON_DIRdropped first, and the message names the git directory. Native probe: an empty.gitgives "not a git repository" (rc 128); a real repository above is found; an exportedGIT_DIRbypasses discovery unless droppedbfdc99cb, 6 files, no generated reportsbackup/*, notes and orphans was undocumentedpython33.9 trapmacos-15, from that job's own logPYTHONSAFEPATHsys.path[0]; covered by the decoy testcore.hooksPath=/dev/null, a native push withGIT_DIR, and cleanup on the unset-TMPDIRpath (behavioural; it replaces the source-text check). Still untested: thetestsRun == len(names)check (M19) and theTMPDIRredirect (M20)Adoption and residual risks
zizmor1.30.1 onPATHandpython33.11 or later first onPATH./usr/bin/python3is 3.9, so a newerpython3must come first.trap '' PIPEset +etestsRun == len(names)checkTMPDIRredirectpre-push.*directory under$TMPDIR. Delete it first, then rungit worktree prune.Status
The review round is complete. Merge with
gh pr merge 438 --squash --match-head-commit 2113905d…once CI is green, then announce the merge time to peers, since their next pushes will run the hook.🤖 Generated with Claude Code