Repository navigation
feat(git-hooks): read pushed blobs in pre-push checks through a shared check-lib reader #7898
Description
Refs: #7613
Split from #7613 (finding 2). #7613 keeps finding 1, the hooks-path consent-design decision.
Agent Brief
Type: Task
Summary: Give the shared git-hook check library a pre-push content reader so every pre-push check reads the pushed commit's blob, not the worktree file.
Current behavior:
In pre-push mode the shared check library takes line ranges from the pushed commits (checks_push_ranges, checks_diff, checks_changed_lines) and returns paths through checks_files, but offers no way to read a path's content at the pushed commit. A check that reads the path from disk sees the worktree, so a local edit made after the commit shifts lines: findings are missed or reported at the wrong line. The one pre-push entry today, guardrails reference-verify, works around this with its own private file_text and regular_file helpers that call git show "$TIP:<path>"; any later pre-push entry would have to copy that.
The SHA-256 half of the original finding is already fixed on main: checks_push_ranges matches the null id as ^0+$, and check-lib.test.sh covers a SHA-256 new branch and deletion. Nothing is left to do there.
Desired behavior:
- The shared check library exposes a content reader (and a matching regular-file test) that, in pre-push mode, returns a path's content from the tip commit of the push range that path came from, and in pre-commit and worktree modes reads the file on disk, as checks do today (pre-commit already refuses partly staged files through
checks_refuse_partly_staged). - With several pushed refs, each path is read from the tip of its own range, never from another ref's tip or the worktree.
- A path absent at the tip (deleted, or a symlink or submodule entry) is reported as not a regular file, and the reader returns non-zero rather than empty content treated as a clean file.
- guardrails
reference-verifyuses the shared reader and drops its private copy, with no change to what it reports.
Key interfaces:
- Shared check library (canonical
check-lib.sh, synced to every plugin copy): a new public reader function and regular-file predicate, documented in the library's header list of public functions next tochecks_filesandchecks_push_ranges. - guardrails pre-push entry
reference-verify: its privatefile_text/regular_filereplaced by the shared functions.
Acceptance criteria:
-
lib/git-hooks/check-lib.test.shhas a pre-push case where a file is committed, then edited again in the worktree without committing, and the reader returns the committed content; the case fails on the library before the change. - A pre-push case with two pushed refs whose tips hold different content for the same path reads each from its own tip.
- A pre-push case for a path deleted at the tip returns non-zero from the regular-file predicate.
-
plugins/guardrails/git-hooks/reference-verify.test.shpasses unchanged. -
scripts/sync-shared-copies.sh --checkpasses, and--check-bump origin/mainpasses (every plugin carrying a copy has its version bump and changelog entry or fragment).
Verification:
Run bash lib/git-hooks/check-lib.test.sh, bash plugins/guardrails/git-hooks/reference-verify.test.sh, scripts/sync-shared-copies.sh --check and scripts/sync-shared-copies.sh --check-bump origin/main; all exit 0.
Out of scope:
- The
hooks-pathdisclosure of foreign entries (git-hooks installer: surface foreign shared entries before hooks-path #7613, finding 1, needs the owner's decision). - Null-id handling for SHA-256 repositories (already done on main).
- Converting pre-commit or worktree checks to the reader, or adding new pre-push entries.
Files the fix would touch
lib/git-hooks/check-lib.shandlib/git-hooks/check-lib.test.sh(canonical)- the generated copies
plugins/*/lib/git-hooks/check-lib.sh(actionlint, bash-format, biome-format, go-format, guardrails, instruction-placement, markdown-format, powershell-format, ruff-format, typos-format), regenerated byscripts/sync-shared-copies.sh plugins/guardrails/git-hooks/reference-verify.sh- each affected plugin's
.claude-plugin/plugin.jsonversion and changelog entry or fragment
No protected path (.github/**, .claude/**, CLAUDE.md, AGENTS.md, the broker, the merge gate).
Activity
github-actions commentedon Oct 18, 2026
Understood: add a shared check-lib reader and regular-file predicate so pre-push checks read the pushed tip's blob instead of the worktree file. Each path is read from the tip of its own push range. A path absent at the tip returns non-zero. guardrails
reference-verifythen drops its private helpers. The brief has acceptance criteria, verification commands, and scope limits, so nothing looks missing. It is split from #7613, which keeps thehooks-pathconsent decision. I found no other duplicates.Automated triage. A maintainer reviews it.
melodic-automation-lanes commentedon Oct 18, 2026
The work-resolve-issue lane stopped partway through this issue and needs a person.
Why, in the run's own words:
What the run says it did not do:
Partial work is on the branch
work-resolve-issue/issue-7898-39090491415-1.Run: https://github.com/melodic-software/claude-code-plugins/actions/runs/39090491415
To retry, remove
needs-human, then remove and addagent-ready.