Skip to content

feat(git-hooks): read pushed blobs in pre-push checks through a shared check-lib reader #7898

Closed

Description

@kyle-sexton

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-verify uses 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 to checks_files and checks_push_ranges.
  • guardrails pre-push entry reference-verify: its private file_text / regular_file replaced by the shared functions.

Acceptance criteria:

  • lib/git-hooks/check-lib.test.sh has 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.sh passes unchanged.
  • scripts/sync-shared-copies.sh --check passes, and --check-bump origin/main passes (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:

Files the fix would touch

  • lib/git-hooks/check-lib.sh and lib/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 by scripts/sync-shared-copies.sh
  • plugins/guardrails/git-hooks/reference-verify.sh
  • each affected plugin's .claude-plugin/plugin.json version and changelog entry or fragment

No protected path (.github/**, .claude/**, CLAUDE.md, AGENTS.md, the broker, the merge gate).

Activity

  1. added
    priority: lowNice-to-have, cosmetic, or speculative; opportunistic.
    work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.
    on Oct 18, 2026
  2. github-actions commented on Oct 18, 2026

    @github-actions
    Contributor

    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-verify then 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 the hooks-path consent decision. I found no other duplicates.

    Automated triage. A maintainer reviews it.

  3. added
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    and removed
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    on Oct 18, 2026
  4. melodic-automation-lanes commented on Oct 18, 2026

    @melodic-automation-lanes
    Contributor

    The work-resolve-issue lane stopped partway through this issue and needs a person.

    Why, in the run's own words:

    Required checks could not run or pass here: scripts/sync-shared-copies.sh (--check and --check-bump) was blocked by the sandbox, and two test files show failures I could not trace to a cause.

    What the run says it did not do:

    • scripts/sync-shared-copies.sh --check and --check-bump origin/main were not run: the sandbox required approval for the script. The 10 copies were edited by hand to match canonical, and the fragments cover the bump.
    • reference-verify.test.sh does not pass unchanged here: 9 failures in the vendored-installer section. I could not run the installer by hand to find the cause, and I did not establish whether they fail on main.
    • check-lib.test.sh has 5 failures in the commit-hook section. I did not establish whether they fail on main.
    • Where one path is changed by two pushed ranges, checks_file_text without CHECKS_TIP reads the first range's tip; a caller that walks the ranges must set CHECKS_TIP, as reference-verify does.

    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 add agent-ready.

  5. added
    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.
    and removed
    needs-humanHuman-in-the-loop required; autonomous sessions must not resolve items carrying this.
    on Oct 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.priority: lowNice-to-have, cosmetic, or speculative; opportunistic.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.

    Type

    Fields

    No fields configured for Task.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions