Skip to content

fix(guardrails): reference-verify flags a quoted claude -p prompt and blocks every push that merges main #9843

Open

Description

@kyle-sexton

Problem

The guardrails pre-push reference-verify entry blocks every push whose range includes c39a5fea3 (#9670), which is every branch that merges current main. Branches whose base is stale must merge main to pass check-affected's stale-base-overlap check, so they cannot be pushed at all.

Output, from plugins/guardrails/git-hooks/reference-verify.sh origin <url> with the pushed ref on stdin:

plugins/machine-health/skills/setup/SKILL.md:154: cli-flag-verify: unknown flag(s) in plugins/machine-health/skills/setup/SKILL.md, unverified, not proven wrong: the installed --help may predate the flag.
plugins/machine-health/skills/setup/SKILL.md:154:   claude on-demand --scheduled (not in 'claude on-demand --help')

Three defects:

  1. False positive. The text it flags is plugins/machine-health/skills/setup/SKILL.md:340: `claude -p "/machine-health:audit on-demand --scheduled"`. on-demand --scheduled are arguments of a slash command inside the quoted -p prompt, not a claude subcommand and flag.
  2. Wrong line. It reports line 154, which is a PowerShell line (. (Join-Path $pluginRoot ...)). No line near 154 holds on-demand.
  3. Content from the base counts as pushed. The finding comes from a commit already on main, brought in by merging origin/main into a feature branch. A pushed range should not re-judge lines main already holds.

Cost measured

Suggested fix

  • Treat a quoted -p/--print prompt argument as data, not as a subcommand chain.
  • Report the line the match was found on.
  • Drop lines the remote default branch already holds from the range, for example by diffing against the merge-base with origin/<default> after a merge from it.

Add a stay-quiet test with the SKILL.md:340 form that fails before the fix (hook-precision convention).

Activity

  1. added
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 24, 2026
  2. kyle-sexton commented on Oct 24, 2026

    @kyle-sexton
    MemberAuthor

    Two more findings, 2026-10-24 ~20:25Z on the WSL host:

    1. Declining the entry does not stop it for lefthook-remote consumers. install.mjs decline --entry reference-verify set checks.guardrails.reference-verify.declined true in this clone's git config, but pushes still run .lefthook/pre-push/guardrails-reference-verify.sh from .git/info/lefthook-remotes/claude-code-plugins-main/. That wrapper execs plugins/guardrails/git-hooks/reference-verify.sh, and grep -n declined over plugins/guardrails/git-hooks/ in the lefthook-remote copy finds nothing, so the decline flag is never read on that path.
    2. Run time. One push's reference-verify (run-guards.sh cli-flag-verify.sh) had been running for 20 min 06 s (ps etime), with three other sessions' pushes in the same step at 3 min 44 s to 3 min 55 s. Expected: seconds.

    Fix needed in addition to the parser fix above: the lefthook-remote entry (or reference-verify.sh itself) must honor the decline flag, and the check needs a time bound.

    🤖 Generated with Claude Code

  3. github-actions commented on Oct 24, 2026

    @github-actions
    Contributor

    The triage sweep dispatched intake triage for this issue (attempt 1 of 2).

  4. github-actions commented on Oct 24, 2026

    @github-actions
    Contributor

    Proposed work class: work-class: scoped
    Basis: automated intake triage; the lanes App applied this class.

    Size: Small
    Complexity: Medium
    Uncertainty: Known
    Flags: none
    Blocked by: none

    Decision recorded. The owner answered in the Refinement session (2026-10-25), deciding the open items below on the facts:

    go with whatever the facts/data/metrics + /discovery:research says and ensure we're aligned with SOLID, DRY/SSOT principles

    [quote truncated; the full decision is at the link]

    #9843 (comment)

    The decision settles the open questions. The quoted -p prompt and base-lines defects are already fixed by #9852. The time bound moves to #9861. What remains is reporting the true line of a match and honoring the declined flag in reference-verify.sh on the lefthook-remote path, each with a test, plus a link to #9861 from this issue.

    Automated triage, applied by the lanes App.

  5. added
    priority: highSignificant impact, or blocks an imminent release; staff this cycle.
    and removed
    needs-triageNot yet classified. Floor until a type and one priority tier are set.
    on Oct 24, 2026
  6. added
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.
    status: needs-decisionAwaiting a human or maintainer judgment call.
    and removed
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    on Oct 24, 2026
  7. melodic-automation-lanes commented on Oct 24, 2026

    @melodic-automation-lanes
    Contributor

    Not admitted to the issue lane: no trusted-human acceptance criteria: a listed human must put the confirmed - [ ] criteria in the body or post them as their own comment, or, on an issue a listed human filed, intake's newest refinement comment must hold them unedited. A recorded decision does not change these rules (.github/actions/resolve-config/admission-rules.json); a person who wants it worked adds agent-ready.

  8. melodic-automation-lanes commented on Oct 24, 2026

    @melodic-automation-lanes
    Contributor

    Refinement. A read-only run explored this repository, searched its issues and pull requests, and read the allowed docs to draft what follows. On an issue a listed human filed, it counts as the issue's spec once posted, while unedited; it is still model text, so check it.

    Intended outcome: reference-verify stays quiet on a quoted claude -p prompt, reports the real line, skips lines the default branch already holds after a merge from it, honors the decline flag on the lefthook-remote path, and is time-bounded.

    Related work

    Open questions

    1. Should the time bound and the merge-base range limit be done here or in #​9861, which reports the same slowness?
      RECOMMENDED: Do the false positive, line number and decline fixes here, and leave the time bound and range limit to #​9861, with each issue linking the other. Basis: 9861
    2. What time bound should reference-verify have, and on expiry should it block the push or skip with a notice?
      RECOMMENDED: A 30 second total bound that prints a NOTICE and lets the push proceed, since a cli-flag finding is already described as unverified, not proven wrong. Basis: plugins/guardrails/git-hooks/reference-verify.sh:185
    3. Where should the decline flag be read: in reference-verify.sh or in the lefthook-remote wrapper?
      RECOMMENDED: In reference-verify.sh, since the wrapper execs it and no declined check exists under plugins/guardrails/git-hooks, so every install path honors it. Basis: plugins/guardrails/git-hooks/reference-verify.sh:41
    4. The reported line is the file's first added line (first_line), not the match line. Should findings carry the true line of the matched text?
      RECOMMENDED: Yes: map the verifier's matched text back to its line in the added lines, falling back to the first added line when no match is found. Basis: plugins/guardrails/git-hooks/reference-verify.sh:138

    Sources read

    • plugins/guardrails/git-hooks/reference-verify.sh:21: ranges come from the check lib, a new branch from its merge-base with the remote default branch
    • plugins/guardrails/git-hooks/reference-verify.sh:138: the reported line is first_line, the file's first added line
    • plugins/guardrails/git-hooks/reference-verify.sh:134: runs cli-flag-verify, skill-reference-verify and stale-path-verify per file with no time limit
    • plugins/guardrails/git-hooks/reference-verify.sh:185: the decline hint is printed only after a finding; no declined check is read in this file
    • plugins/guardrails/hooks/cli-flag-verify.sh:181: markdown scan takes inline-code spans as command fragments

    Automated refinement by the lanes App. Answer with a comment whose first line is **Decision recorded.**.

  9. kyle-sexton commented on Oct 26, 2026

    @kyle-sexton
    MemberAuthor

    Decision recorded. The owner answered in the Refinement session (2026-10-25), deciding the open items below on the facts:

    go with whatever the facts/data/metrics + /discovery:research says and ensure we're aligned with SOLID, DRY/SSOT principles

    I should only ever be in the loop ONLY when necessary (intent/vision changed, something came up during implementation that impacts trajectory/alignment, OR a high security risk) - I've covered this all before

    Question asked: Narrow this issue to the remaining defects (true line number, decline flag on the lefthook-remote path), and move the time bound to #9861?

    Defects 1 (quoted -p prompt) and 3 (lines main already holds) are no longer open, as facts, not decisions: #9852 (9110877) on origin/main reads a quoted prompt as one argument and judges only a merged branch's own lines (plugins/guardrails/hooks/cli-flag-verify.sh:232,277; plugins/guardrails/git-hooks/reference-verify.sh:70,108-116). Its PR has a stay-quiet test per its Verification section; not re-run here.

    1. (intake Q1) Time bound here or in perf(guardrails): pre-push reference-verify takes 8-17 min on stacked branches and pushes fail silently #9861?
      Decided: In perf(guardrails): pre-push reference-verify takes 8-17 min on stacked branches and pushes fail silently #9861, with each issue linking the other; this issue keeps the line number and decline fixes. Basis: kept; perf(guardrails): pre-push reference-verify takes 8-17 min on stacked branches and pushes fail silently #9861's body covers the run time.
    2. (intake Q2) If the bound stays here: what limit, and on expiry block or skip?
      Decided: Decide it in perf(guardrails): pre-push reference-verify takes 8-17 min on stacked branches and pushes fail silently #9861. A skip on expiry makes the guard fail open, which is the owner's call; the cli-flag finding already calls itself "unverified, not proven wrong". Basis: kept, moved; plugins/guardrails/hooks/cli-flag-verify.sh:564.
    3. (intake Q3) Where is the decline flag read?
      Decided: In reference-verify.sh, which the lefthook-remote wrapper execs, so every install path honors checks.guardrails.reference-verify.declined. Basis: kept; no declined read under plugins/guardrails/git-hooks or lib/git-hooks except install.mjs:464-466; reference-verify.sh:236 only prints the decline hint.
    4. (intake Q4) Report the true line of the match?
      Decided: Yes: map the matched text back to its added line, falling back to the first added line. Basis: kept; reference-verify.sh:127,183 report first_line.

    Acceptance criteria:

  10. added
    status: readyTriaged, unblocked, and fully specified; eligible to pick up.
    admission-deferredReady for the issue lane; work-unblock admits it when queued work is under the queued cap
    on Oct 26, 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

    admission-deferredReady for the issue lane; work-unblock admits it when queued work is under the queued cappriority: highSignificant impact, or blocks an imminent release; staff this cycle.status: readyTriaged, unblocked, and fully specified; eligible to pick up.work-class: scopedA briefed fix or small feature; blast radius bounded by the brief, tests exist.

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions