Repository navigation
git-hooks installer: surface foreign shared entries before hooks-path #7613
Description
Split (2026-10-17): finding 2 moved to #7898, which carries its own Agent Brief. This issue now tracks finding 1 only: the owner's decision on what the
hooks-pathopt-in must disclose before it activates entries the user did not install. On main the SHA-256 null-id part of finding 2 is already fixed (checks_push_rangesmatches^0+$), and guardrailsreference-verifyis now a pre-push entry, reading blobs through its own helper.
Two review findings on #7547 (shared git-hook installer, lib/git-hooks/) deferred to the rollout phases that use them.
1. Shared-scope hooks-path opt-in activates foreign entries the user never sees
install.mjs emits the hooks-path decision (git config core.hooksPath .githooks) without listing what is already committed under .githooks/. Once set, the dispatcher runs every .githooks/<event>.pre-checks and every marker in .githooks/<event>.d/ and .githooks/local/<event>.d/, all repository content. The user consents to one plugin's entry and activates the rest uninformed.
Proposed: before hooks-path, plan lists existing markers not named <this plugin>.*, any *.pre-checks, and committed .githooks/local/ content as a decision the user sees; consider not scanning $dir/local when $dir is inside the work tree.
Split to #8989: the owner question whether the dispatcher stops scanning an in-tree local/ (a security trade-off that changes what opted-in clones run).
2. Moved to #7898
Tracked in #7898.check-lib.sh pre-push mode mixes worktree content with pushed-commit line ranges.
typos-check is pre-commit only, so #7547 is unaffected; this lands with the shared-scope rollout to the other check plugins.
Done when (this issue: the disclosure, additive)
- With
scope === "shared"andcore.hooksPathnot yet.githooks,plainDecisions(lib/git-hooks/install.mjs:486-497) emits ashared-entriesdecision beforehooks-path, listing each.githooks/<event>.d/marker not named<this plugin>.*, every.githooks/*.pre-checks, and every tracked file under.githooks/local/(git ls-files). Check: the PR's CI runslib/git-hooks/install.test.shand it passes. - With none present, the plan has no such decision, so its output is unchanged. Check: as above.
- Tests cover a repo with a foreign marker, a
.pre-checksfile, a trackedlocal/file, and an empty.githooks/. Check: as above. - The registered copies of
lib/git-hooks/are regenerated (scripts/sync-shared-copies.sh), with a fragment for every carrier plugin (scripts/new-changelog-fragment.sh --carriers-of lib/git-hooks/install.mjs patch). Check: the PR's CI runs the shared-copy sync check andscripts/check-changelog-fragments.sh --check, and both pass.
Activity
github-actions commentedon Oct 16, 2026
Thanks for the report. Automated triage handed this issue to a maintainer.
Automated triage. A maintainer reviews it.
kyle-sexton commentedon Oct 18, 2026
This was generated by AI during triage.
Triage: human-gated (Task, priority: low)
Verified: both findings still hold on main (e59e0be), by code read.
lib/git-hooks/install.mjswrites a dispatcher that runs$dir/<event>.pre-checks(lines 98-99) and every marker under<event>.d/andlocal/<event>.d/(lines 79-85), and thehooks-pathdecision lists none of the existing entries first.lib/git-hooks/check-lib.shpre-push mode takes ranges from the pushed commits (checks_push_ranges, line 33) while checks read files from the worktree; the null id is the 40-zero SHA-1 form only (seecheck-lib.test.sh:160), with no--show-object-formatread.No open PR covers either. Both were deferred from #7547 (merged) to the rollout phases that add pre-push entries or the shared-scope rollout, and no such phase is open yet.
Why a human: finding 1 is a consent-design decision: what the
hooks-pathopt-in must show before it activates entries the user did not install, and whether to stop scanninglocal/when it sits in the work tree. Finding 2 is concrete but only matters once a pre-push entry exists, so its timing belongs to the rollout plan.RECOMMENDED: split this issue in two. Finding 2 (read pushed blobs, derive the null id from the object format) becomes scoped work an agent can pick up once any pre-push entry is planned. Finding 1 stays here for the owner to decide the disclosure rule. Basis: judgment (the two share no code path and no decision).
Brief
Type: Task
Summary: Before the shared git-hook installer activates
core.hooksPath, disclose foreign entries; make pre-push checks read the pushed blobs.Desired behavior:
hooks-pathdecision, lists every existing marker not owned by the installing plugin, any*.pre-checksfile, and any committed clone-local content, as a decision the user sees.Acceptance criteria:
hooks-path.Out of scope:
[-]git-hooks installer: surface foreign shared entries before hooks-path, and make check-lib pre-push read pushed blobs[/-][+]git-hooks installer: surface foreign shared entries before hooks-path[/+]kyle-sexton commentedon Oct 24, 2026
Released for re-triage under the owner's 2026-10-24 rule: an issue stops for a person only for an intent or vision question, a security call, a host or attended run, or spend outside prepaid credit. The hold label came off and intake triage runs again now; it re-applies a hold, with a numbered question, when one of those applies.
To hold this issue again, add
needs-human(orstatus: needs-decision) and say why in a comment.