Skip to content

fix(hooks): a graphify skip must not terminate the whole git hook (#2986) - #3108

Open
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/hook-exit-terminates-chain
Open

fix(hooks): a graphify skip must not terminate the whole git hook (#2986)#3108
abhay-codes07 wants to merge 1 commit into
Graphify-Labs:v8from
abhay-codes07:fix/hook-exit-terminates-chain

Conversation

@abhay-codes07

Copy link
Copy Markdown
Contributor

Closes #2986.

The problem

The generated post-commit / post-checkout block is appended to whatever hook a repo already has, and other tools chain their own logic after graphify's end marker. Every skip condition in the block was a bare exit 0 — seventeen of them in 0.9.48 — which in a flat sh script ends the entire hook process, not graphify's section. Anything after the marker was silently dropped:

  • on every root commit (HEAD~1 does not exist, so CHANGED is empty);
  • during every rebase / merge / cherry-pick;
  • in every linked worktree;
  • whenever GRAPHIFY_SKIP_HOOK=1;
  • whenever no graphify-capable python could be found.

Each exit 0 is right as an intent ("graphify should not run here") and wrong as a mechanism in a shared hook file.

The change

Both hook bodies now run inside a subshell:

# graphify-hook-start
# ...
(
  ... unchanged body, exits and all ...
)
# graphify-hook-end

An exit inside the subshell ends only graphify's section. This keeps the seventeen skip sites byte-for-byte as they were, avoids rewriting them to return (which would break the _PYTHON_DETECT fragment tests run standalone — return outside a function is undefined in POSIX sh), leaves the detached rebuild launch unaffected, and preserves the hook's own exit status of 0. The markers stay outside the subshell so reinstall / uninstall / hook status keep finding the block, and the in-place update path still reports "already installed" for an unchanged block.

One existing test slices the checkout script's prefix and runs it under sh -c; it now closes the subshell after its sentinel.

Tests

tests/test_hook_chain_survives_skip.py — 7 tests that install the real hooks into a real git repo, append echo AFTER_GRAPHIFY after the block, and run them under sh: a GRAPHIFY_SKIP_HOOK=1 skip, a rebase in progress, an empty diff (the root-commit shape), a non-branch checkout, a skipped checkout hook, a pre-existing hook before the block, and reinstall idempotence. Each asserts the trailer ran, the exit status is 0, and (for the skip case) that no rebuild was launched. With the subshell reverted, 5 of 7 fail. tests/test_hooks.py is otherwise unchanged; the full suite matches the v8 baseline.

…aphify-Labs#2986)

The generated post-commit / post-checkout block is appended to whatever
hook a repo already has, and other tools chain their logic after it. Every
skip condition in the block was a bare `exit 0`, which in a flat sh script
ends the ENTIRE hook process — so anything after graphify's end marker
was silently dropped on every root commit (HEAD~1 does not exist), every
rebase/merge/cherry-pick, every linked worktree, every GRAPHIFY_SKIP_HOOK=1
and whenever no graphify-capable python could be found.

Both bodies now run inside a subshell `( ... )`, so an `exit` ends only
graphify's section: no rewriting of the seventeen skip sites, no
`return`-outside-a-function hazard for the _PYTHON_DETECT fragment that
tests run standalone, the detached rebuild launch unaffected, and the
hook's own exit status 0 as before. The markers stay outside the subshell
so reinstall/uninstall/status keep finding the block.
Copilot AI lite review requested due to automatic review settings August 26, 2026 11:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

Wraps the generated post-commit and post-checkout hook bodies in a subshell ( ... ) so graphify's internal exit 0 skip paths (root commit, rebase/merge, linked worktree, GRAPHIFY_SKIP_HOOK=1) end only its own section instead of the entire hook, letting anything chained after the end marker keep running (#2986). The start/end markers stay outside the subshell so install/uninstall/status still find and update the block in place.

No blocking issues surfaced. 3 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 362 functions depend on the 205 functions this change touches.

Health — this change adds coupling hotspots:

  • new: install() — 37 callers, 7 callees
  • new: dispatch_command() — 2 callers, 122 callees
  • new: dispatch_install_cli() — 2 callers, 31 callees
  • new: status() — 8 callers, 6 callees
  • new: uninstall() — 9 callers, 5 callees
  • new: uninstall_all() — 2 callers, 13 callees
  • new: test_poisoned_manifest_is_healed() — 0 callers, 6 callees

Verification — 362 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 212 function(s) in the blast radius were not formally verified this run

· 7 more finding(s) on lines outside this diff (see the check run).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants