Drive each declared hook to a refusal, in a tree nobody is standing in - #26
Conversation
`uphold probe`. A hook that cannot fail reports the same green tick as a hook that keeps finding nothing, run after run, for as long as nobody plants what it is supposed to catch. No report anywhere tells the two apart. The case this was written for is not hypothetical: an entry declared as `gofmt -l .` can never exit non-zero, because `gofmt -l` PRINTS its findings and exits 0, and two repositories in the fleet that produced the request had one. Each probe drives one hook to both verdicts: plant the fixture it must refuse, run that hook alone, expect non-zero; put the clean fixture in its place, run it again, expect zero. Asking the runner for ONE id is what makes the first half an answer about the hook rather than about the stage -- a non-zero exit is that hook refusing and not a neighbour, so nothing here has to parse a report to find out who spoke. Four verdicts, and they are four different things to do next: a demonstrated gate; a gate demonstrated in one direction because no `allows` fixture was written; a hook that ACCEPTED what it is declared to refuse; and a hook that refused the clean fixture too, whose refusal therefore says nothing about what it was given. It happens in a `git worktree` at HEAD and never in the tree the operator is standing in. A probe that planted a fixture in the working tree would leave one behind the first time it was interrupted -- in a tree whose hooks would then refuse the next commit for a reason nothing in the tree explains. A test asserts the working tree is untouched and clean afterwards. Fixtures are written down in `policy/hooks.toml` rather than generated. This binary knows what its own rules match and knows nothing about `gofmt`, `ruff`, or a hook somebody wrote this morning -- and the hooks worth probing are exactly the ones it knows nothing about. A fixture in a file is also reviewable, which matters more than the typing it saves. An empty `refuses` is refused, because a hook that accepted an empty fixture would be reported as unable to fail; a probe naming a hook nothing declares is refused, because it would drive nothing while reading as coverage. The denominator is printed every run. "Two hooks were probed" means one thing beside two declarations and another beside twenty, and this repository's own run says two of twenty-seven. Two probes ship here, chosen for what each demonstrates: `content-policy` against a rule that arrives from a bundled set, which is the half of an inherited set that no file in this tree shows; and `trailing-whitespace`, a third-party hook, because every hook that is not this binary's own is one whose failure path nobody here has ever seen. One finding, and it arrived the way the good ones do -- from running the suite in the environment the command will actually be used in. A hook runner exports `GIT_INDEX_FILE`, `GIT_DIR` and friends, several of them RELATIVE to the repository the hook fired in. Inherited, they point every `git` this module runs at the wrong index: the worktree could not be created at all, and where it could, the staging would have gone into somebody else's index -- the same accident with none of the noise. Git's environment is stripped from every child now, stripped rather than overridden, because the list of what git puts in an environment is git's and an override answers only for the names somebody remembered. The runner is detected from the configuration AND from PATH, because either alone is a wrong answer: a repository with a lefthook config and no lefthook installed cannot be probed, and a machine with three runners installed says nothing about which one a repository uses. No runner is exit 2 -- a hook that could not be run has not been shown to refuse anything. Verified on consumers: `scan` and `check` byte-identical under the released binary and this build, all exit 0. `probe` driven on a consumer with no probes declared reports the denominator and leaves the tree clean.
|
Warning Review limit reached
Next review available in: 79 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
❌ Your patch status has failed because the patch coverage (89.56%) is below the target coverage (100.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #26 +/- ##
==========================================
+ Coverage 88.70% 88.73% +0.02%
==========================================
Files 27 28 +1
Lines 8633 8911 +278
==========================================
+ Hits 7658 7907 +249
- Misses 975 1004 +29 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The other half of #11.
uphold probedrives each declared hook to a refusal, so a gate whose rejection path has never been demonstrated stops reading like a gate that keeps finding nothing.The case is not hypothetical: an entry declared as
gofmt -l .can never exit non-zero, becausegofmt -lprints its findings and exits 0. Two repositories in the fleet that produced #11 had one, and every report anybody reads showed them green.What one probe does
Isolation is what makes step 1 an answer about the hook rather than about the stage: the runner is asked for one id, so a non-zero exit is that hook refusing and not a neighbour. Nothing has to parse a report to find out who spoke.
Four verdicts, and they are four different things to do next: a demonstrated gate; a gate demonstrated in one direction only, because no
allowsfixture was written; a hook that accepted what it is declared to refuse; and a hook that refused the clean fixture too, whose refusal therefore says nothing about what it was given.In a tree nobody is standing in
The fixture is planted in a
git worktreeat HEAD, never in the working tree. A probe that planted it where somebody is working would leave one behind the first time it was interrupted -- in a tree whose hooks would then refuse the next commit for a reason nothing in the tree explains. A test asserts the working tree is untouched and clean afterwards.Fixtures are written down, not generated
uphold knows what its own rules match and knows nothing about
gofmt,ruff, or a hook somebody wrote this morning -- and the hooks worth probing are exactly the ones it knows nothing about. A fixture in a file is also reviewable, which matters more than the typing it saves.An empty
refusesis refused: a hook that accepted an empty fixture would be reported as unable to fail. A probe naming a hook nothing declares is refused: it would drive nothing while reading as coverage. The count of declared hooks with no probe prints every run -- this repository's own says two of twenty-seven.One finding, from running it where it will be used
The first version could not run from inside a hook at all. A hook runner exports
GIT_INDEX_FILE,GIT_DIRand friends, several of them relative to the repository the hook fired in; inherited, they point everygitthis module runs at the wrong index. The worktree could not be created, and where it could, the staging would have gone into somebody else's index -- the same accident with none of the noise.Git's environment is stripped from every child now, stripped rather than overridden: the list of what git puts in an environment is git's, and an override answers only for the names somebody remembered. There is a regression test that runs
probewith a hook runner's environment set.Verified on consumers
scanandcheckbyte-identical under the released binary and this build, all exit 0.probedriven on a consumer with no probes declared reports the denominator and leaves the tree clean.404 tests pass, 10 of them new. The nine driving the verdict logic use a stub runner on PATH, so no case depends on which runner is installed on the machine running the suite.
With this in, #11 has both halves.