Skip to content

fix(claude-review): direct the review lane to post line-anchored inline comments - #338

Merged
kyle-sexton merged 3 commits into
mainfrom
fix/332-claude-review-inline-comments
Aug 3, 2026
Merged

fix(claude-review): direct the review lane to post line-anchored inline comments#338
kyle-sexton merged 3 commits into
mainfrom
fix/332-claude-review-inline-comments

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Summary

The claude-review lane has posted zero line-anchored inline comments
fleet-wide (~10,563 all-time claude-review* runs, 3,161 PRs), despite
mcp__github_inline_comment__create_inline_comment being granted and reaching
the agent — the effective SDK options echo lists it. The tool was installed
and never called.

Why it was never called. The assembled prompt never mentioned the tool,
while the pinned action's own base prompt steers hard the other way:

  • Never create new comments. Only update the existing comment using mcp__github_comment__update_claude_comment.
  • You communicate exclusively by editing your single comment - not through any other means.
  • under What You CANNOT Do: - Submit formal GitHub PR reviews

That text is authored by anthropics/claude-code-action and cannot be changed
here
. What can change is the lane's own prompt, which is appended after it and
gets the last word.

What made the difference. claude-security-review.yml carries the identical
tool grant and does produce line-anchored comments. The only prompt-level
delta is that its criteria body says "Report findings as a pull request
review."
This change gives the code-review lane the equivalent instruction and
additionally names the tool, so the grant is discoverable rather than merely
present.

Placement. In the step's prompt wrapper, not the prompt input default. The
input's documented contract is "just the instructions for what to look for", so
reporting mechanics belong beside the structural header and permalink form — and
the wrapper survives a caller that overrides the criteria body. Both the first
attempt and the retry carry byte-identical prompt blocks.

The pre-existing commit-permalink guidance is scoped, not removed: it now
covers findings no changed line can carry, so it no longer competes with the
inline-comment instruction for the same finding.

Deliberate non-changes

  • display-report and show_full_output stay off. Public-repo log hygiene
    is untouched. (show_full_output: false is exactly why the logs cannot
    distinguish "never called" from "called and failed" — an accepted limitation,
    not a bug fixed here.)
  • No caller input is added or required. The fix lives entirely inside the
    reusable workflow, so the runner-policy contract (allowedInputs for this
    workflow) is unaffected.
  • skip-actors and allowed_bots are untouched.
  • Nothing changes in melodic-software/standards. REVIEW.md is the shared
    review-criteria prose and is also injected into Managed Code Review and local
    review-plugin runs; it says nothing about output form today, and lane
    plumbing (which MCP tool to call) does not belong there. Its downstream
    materialization in this repo is a managed sync target and was not touched.
  • The two lanes are left structurally asymmetric — the security lane's reporting
    instruction sits in its prompt input default, this one's sits in the wrapper.
    That is deliberate scope discipline, not an oversight; the wrapper is the
    correct home per the input's own contract, and normalizing the security lane is
    a separate change.

Test plan

A prompt change cannot be proven by unit test, so this was demonstrated live.

Setup. Throwaway PR #337 (demo/332-inline-comment-evidence) based on this
branch
, adding one Node script with genuine, line-anchorable defects.
claude-review-self.yml uses the local ./ ref, so the review ran against the
modified reusable (create_inline_comment appears 3x in the demo tree's
workflow vs 1x on main). The changed file matches none of the security lane's
paths patterns, so security-review skipped — every claude[bot] inline
comment on that PR is attributable to the code-review lane.

Result — 5 line-anchored inline comments where there were previously zero:

Line Severity Comment
41 🔴 Important #337 (comment)
51 🔴 Important #337 (comment)
47 🟡 Nit #337 (comment)
50 🟡 Nit #337 (comment)
65 🟡 Nit #337 (comment)

Attribution controls:

Note for future debugging: the action still logs No buffered inline comments on this run even though five were posted. That step covers a different
(batched) path; create_inline_comment posts immediately. The log line is not
a usable signal for whether the tool was called.

actionlint clean; both prompt blocks verified byte-identical.

Review round

Two threads, both addressed with code.

P2 (codex) — the inline grant did not survive a caller overriding claude-args. Real, and worse than reported. The grant lived in the claude-args input default, which a caller replaces wholesale, so a consumer passing claude-args for an unrelated reason (selecting a model) dropped it silently — while the non-overridable wrapper prompt still asserted the tool was granted. And because this lane runs in tag mode, the action derives which MCP servers to install from these same args (parseAllowedToolsprepareMcpConfig), so the inline-comment server would not even be installed.

Fixed in 923a2d2: compose-args appends the grant unconditionally, and it comes out of the overridable default. That step feeds both the first attempt and the retry, so one edit covers both. The invariant, now written into the file: whatever the non-overridable wrapper prompt asserts must itself be non-overridable. Bash(gh pr diff:*) deliberately stays overridable, because the instruction that uses it lives in the equally overridable prompt input.

Safe because repeated --allowedTools flags accumulate and dedupe rather than overwrite (ACCUMULATING_FLAGS, both spellings, upstream-tested) — tag mode already relies on this, emitting its own --allowedTools before appending the caller's. Verified by executing the pinned action's real parseAllowedTools against the strings compose-args emits: the default path yields an effective tool set identical to pre-fix (no happy-path regression), every override scenario keeps the grant, and the pre-fix override reproduces the bug as an empty list.

SUGGESTION (claude) — widened trust surface. Asked for no code change, only a SECURITY MODEL note; added.

Corrections this round (8d7ab14)

Independent verification caught two statements that were not true, in a file where the comments are the contract.

  • The SECURITY MODEL note repeated the review thread's claim that the action buffers and classifies inline comments before posting. It filters nothing in this lane: classification requires ANTHROPIC_API_KEY, and this lane passes only claude_code_oauth_token, so classifyComments returns null and every buffered comment posts. The error ran in the reassuring direction, describing a control that is not there.
  • The step comment claimed a granted tool the prompt never mentions "reads as unusable and goes uncalled". This repo's own security lane disproves it — that lane grants the tool, never names it, runs in the same tag mode under the same base prompt, and does post inline comments. The load-bearing part is instructing the agent to report findings as a pull request review; naming the tool is belt-and-braces, and the comment now says so.

The empirical result the PR rests on is unaffected: the five line-anchored comments on #337 stand. Only the causal explanation was overstated.

Follow-ups filed, deliberately not folded in

Related

Closes #332.

Follow-ups from review: #340, #341.

Related: #337 (throwaway evidence PR, do-not-merge, deleted after review).
The Bash(gh pr diff:*) grant flagged during investigation is already fixed on
main by ef63101 (#319) and is present in the effective allowedTools — the
stale fix/309-claude-review-gh-pr-diff-grant remote branch is superseded.

…ne comments

The lane has been granted
`mcp__github_inline_comment__create_inline_comment` since the lanes
landed, and the effective SDK `allowedTools` echo confirms it reaches the
agent — but the assembled prompt never mentioned it, while the action's
own base prompt routes every response into the single sticky comment
("Never create new comments. Only update the existing comment using
mcp__github_comment__update_claude_comment") and lists submitting a
formal PR review under what the agent cannot do. An unmentioned grant
under that steering reads as unusable, and the lane has produced zero
inline comments fleet-wide.

The security lane carries the identical grant and DOES produce
line-anchored comments; the only prompt-level difference is that its
criteria body says "Report findings as a pull request review". This adds
the equivalent instruction to the code-review lane, and names the tool
outright so the grant is discoverable rather than merely present.

Placed in the step's prompt wrapper rather than the `prompt` input
default: the input's documented contract is "just the instructions for
what to look for", so reporting mechanics belong with the structural
header and permalink form — and the wrapper survives a caller that
overrides the criteria body.

The pre-existing permalink guidance is scoped rather than removed: it now
covers findings no changed line can carry, so it no longer competes with
the inline-comment instruction for the same finding.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Aug 2, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 2s —— View job


Code review: claude-review.yml inline-comment fix

  • Gather context (PR body, CLAUDE.md, diff)
  • Review .github/workflows/claude-review.yml changes
  • Check for inline findings
  • Post summary

Scope: single file, .github/workflows/claude-review.yml (+36/-10): the prompt input description, and the two (first-attempt + retry) prompt: blocks in the Claude review / Claude review (retry) steps.

Verified independently (not just author-claimed):

  • The mcp__github_inline_comment__create_inline_comment grant the new prompt text names is actually present in claude-args's default at L134 — the instruction points at a tool that is genuinely granted, not a dangling reference.
  • Diffed the two prompt: blocks myself (L486-497 vs L720-731) — confirmed byte-identical, matching the "must not diverge" comment above the retry block.
  • Compared against claude-security-review.yml's equivalent instruction (L238) — that lane says only "Report findings as a pull request review" without naming the tool, so this PR's choice to additionally name the tool is a genuine (and reasonable) delta, not overstated.
  • No new uses: lines, no trigger/permission/display-report/show_full_output changes — consistent with CLAUDE.md's security ground rules; this stays a pure prompt-text change inside the reusable workflow.

Not independently verified (author-claimed only): the actionlint clean run and the live throwaway-PR #337 test evidence (job-run timestamps, the 5 inline comment URLs, the skipped security-review job) — I have no tool access to actionlint or to that (now-deleted) PR's run history, so I'm not re-confirming those, just not disputing them either.

Assessment: the change is narrowly scoped, the reasoning in the added comments is sound (the base action prompt's "never create new comments" / "cannot submit PR reviews" language is immutable upstream, so the wrapper prompt needs the last word — which is what this does), and the split between "fixable here" (wrapper) vs. "not this repo's job" (melodic-software/standards) matches the input's documented contract. No correctness, error-handling, or maintainability issues found in the diff — no inline comments to post.

@claude

claude Bot commented Aug 2, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 2m 25s —— View job


Security Review

  • Gather context (PR diff, changed file)
  • Review .github/workflows/claude-review.yml diff for security issues
  • Check against CLAUDE.md security ground rules (trigger safety, permissions, SHA pinning, log hygiene)
  • Post findings as PR review

Scope: the single changed file, .github/workflows/claude-review.yml (+36/-10), reviewed via git diff origin/main...HEAD.

What changed: prompt-text only. Both attempt copies (first + retry, byte-identical as claimed) gain a "report findings as a pull request review" instruction that names mcp__github_inline_comment__create_inline_comment and scopes the pre-existing commit-permalink guidance to findings with no changed line. No permissions:, trigger (on:), tool-grant default (claude-args, line 134 — unchanged by this diff), secret, or checkout behavior is touched. reject-privileged-triggers, the freshness/cap gates, and credential handling are all untouched.

Deferred to zizmor (per instructions): action pin freshness, trigger-type safety, permission scoping, template-injection shape of the ${{ }} interpolations in the prompt block — all pre-existing and out of this lane's remit.

Finding posted inline (SUGGESTION / MEDIUM confidence) on lines 486–491: the tool grant itself predates this PR, but this change is what makes the agent actually call it on every finding, which meaningfully widens the practical trust surface for prompt-injection-driven inline comments (including one-click-appliable ```suggestion ``` blocks) versus the tool sitting granted-but-dormant. Bounded by the existing actor-write-access gate, the fork-PR no-run behavior, and the same pattern already precedented in claude-security-review.yml — not a vulnerability to fix, but worth being deliberate about (e.g., a line in this file's SECURITY MODEL block alongside the other accepted-risk notes).

No CRITICAL or IMPORTANT issues found. No authorization/access-control gaps, injection surfaces, credential-handling changes, or dangerous trigger/permission patterns introduced by this diff.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e7c5ed388

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/claude-review.yml
Comment thread .github/workflows/claude-review.yml
…rides

The wrapper prompt names mcp__github_inline_comment__create_inline_comment
unconditionally and asserts it is granted to this lane, but the grant lived
in the `claude-args` input default — which a caller replaces wholesale. Any
consumer passing claude-args for an unrelated reason (selecting a model, say)
silently dropped it, leaving the prompt directing every line-anchorable
finding at a tool that was not there.

The failure is worse than an un-granted tool. In tag mode the action derives
which GitHub MCP servers to install from these same args
(`parseAllowedTools` -> `prepareMcpConfig({ allowedTools })`), so without the
grant the inline-comment server is never installed at all.

Fix: the compose step appends the grant unconditionally, and it comes out of
the overridable default. That step feeds both the first attempt and the
retry, so one edit covers both. Repeated --allowedTools flags accumulate and
dedupe rather than overwrite (parse-sdk-options.ts ACCUMULATING_FLAGS, with
tests), so the append only ever adds to whatever the caller passed — and tag
mode already relies on that union, emitting its own --allowedTools before
appending the caller's.

`Bash(gh pr diff:*)` deliberately stays in the overridable default: the
instruction that uses it lives in the equally overridable `prompt` input, so
the two co-vary correctly. The invariant is that whatever the
non-overridable wrapper prompt asserts must itself be non-overridable.

Verified against the pinned action's own parser: the default path yields the
same effective tool set as before this change, every override scenario keeps
the grant, and the pre-fix override yields an empty tool list.

Also records the widened trust surface in the SECURITY MODEL block. The
inline tool is now on the hot path, so untrusted diff content reaches a
rendered comment that can carry an appliable suggestion block. The action
sanitizes bodies for hidden markup and leaked tokens but not for meaning,
and its buffer-and-classify step targets test/probe comments and is skipped
when the agent passes confirmed: true — neither is an injection control.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 45s —— View job


Security Review

  • Gather context (PR diff, changed file, CLAUDE.md security ground rules)
  • Review .github/workflows/claude-review.yml diff (commit 923a2d2) for security issues
  • Check against prior review round's finding (inline-comment grant surviving caller overrides)
  • Post findings

Scope: this run reviews the full PR diff (base origin/main, head 923a2d23e9a55019efc073111b0f543f1c1bffd5), but since Claude already reviewed an earlier revision of this PR, I've focused on what changed since then — the delta between 2e7c5ed (previously reviewed) and 923a2d2 (current head), isolated with git diff 2e7c5ed..923a2d2 -- .github/workflows/claude-review.yml.

What changed: this commit is the fix for chatgpt-codex-connector's P2 finding on the prior revision — a caller supplying a custom claude-args value replaced the default outright, silently dropping the inline-comment tool grant while the prompt still unconditionally claimed it was available.

  • L130–145: the mcp__github_inline_comment__create_inline_comment grant is removed from the claude-args input's default (which a caller's override replaces wholesale) and instead unconditionally appended in the Compose Claude CLI arguments step (L451-459) via plain string concatenation — args="$args --allowedTools mcp__github_inline_comment__create_inline_comment". This is a static string with no interpolation of caller- or PR-controlled content, so it introduces no injection surface; it only changes where the grant is asserted, not what is trusted.
  • L71-80: the SECURITY MODEL header gains the trust-surface note — this is the documentation I asked for as a SUGGESTION in my prior review round on this same PR, now addressed rather than a new item to re-flag.

Verified:

  • No permissions:, trigger (on:), secret handling, or checkout behavior touched by this delta.
  • The BASE_ARGS/args shell composition remains variable-expansion-only (no eval, no direct ${{ }} interpolation inside the run: body — inputs.claude-args flows in via an env:-bound var as before), so this change doesn't introduce a script-injection path through the github/inputs context.
  • The append is shared by both the first attempt and the retry (single compose-args step feeds both claude_args: references), so the two stay in sync — consistent with the file's own "must not diverge" discipline for the prompt blocks.
  • This closes the gap the codex finding identified: the tool grant can no longer be silently dropped by a caller override, so the prompt's unconditional claim that the tool is available is now actually guaranteed.

Assessment: no new security issues in this delta. The change is a sound, narrowly-scoped fix — it doesn't widen the trust surface further (the tool was already granted and driven every finding as of the prior revision); it only makes that grant durable against caller overrides and documents the previously-flagged widened-surface tradeoff inline in the file. No CRITICAL or IMPORTANT issues found; no authorization/access-control gaps, injection surfaces, credential-handling changes, or dangerous trigger/permission patterns.

…ents

Independent verification of the previous commit turned up two statements
that are not true, in a file where the comments are the contract.

The SECURITY MODEL bullet said the action's buffer-and-classify step drops
test/probe comments. It drops nothing in this lane: classification requires
ANTHROPIC_API_KEY, and this lane passes only claude_code_oauth_token, so
classifyComments returns null and every buffered comment posts. The error
ran in the reassuring direction — it described a filter that is not there —
so the note now says the step fails open here.

The step comment claimed a granted tool the prompt never mentions "reads as
unusable and goes uncalled". This repo's own security lane disproves it:
that lane grants the tool, never names it in its prompt, runs in the same
tag mode under the same base prompt, and does post inline comments. The
load-bearing part is instructing the agent to report findings as a pull
request review, which is what that lane has and this one lacked. Naming the
tool is belt-and-braces, and the comment now says so.

Also scoped the claude-args note: appending the grant defeats a caller
replacing the default, not an explicit --disallowedTools for that tool.

No behavior change; comments and input description only.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 58s —— View job


Security Review

  • Gather context (PR diff, changed file, CLAUDE.md security ground rules)
  • Review delta since last reviewed commit (923a2d28d7ab14)
  • Post findings

Scope: Claude has already reviewed this PR through commit 923a2d2, so this round is scoped to what changed since then — isolated with git diff 923a2d2..8d7ab14 -- .github/workflows/claude-review.yml. Per the PR title, commit 8d7ab14 is docs(claude-review): correct two inaccurate claims in the lane's comments.

What changed: two #-comment corrections in the SECURITY MODEL header block and above the first-attempt prompt: step — no code, YAML structure, on:/permissions:/uses:/run:, or tool-grant lines are touched by this delta.

  1. L76-85 — corrects the description of the inline-comment tool's buffer-and-classify step: it now says classification requires ANTHROPIC_API_KEY, which this lane never passes (it authenticates via CLAUDE_CODE_OAUTH_TOKEN only), so the buffer fails open and posts every comment rather than filtering "test/probe" ones as the prior comment text claimed.
    • Verified independently: grepped the file for credential-passing — L487 and L729 pass only claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}; no anthropic_api_key input appears anywhere in the file. The corrected claim is consistent with what the workflow actually does — this is a documentation-accuracy fix, not a new behavior.
  2. L497-502 — corrects the rationale for naming the tool in the prompt: the load-bearing instruction is asking for findings as a PR review (countering the base action's "never create new comments" / "cannot submit PR reviews" language); naming the tool is now described as "belt-and-braces, not the mechanism," matching how claude-security-review.yml gets the same behavior without ever naming the tool.

Assessment: this delta is documentation-only — it corrects two previously-inaccurate explanatory comments to match the workflow's actual behavior, and does not itself change what the workflow does. It doesn't touch triggers, permissions, secrets, tool grants, or checkout behavior, and doesn't reopen or narrow anything from the prior review round (the durable-grant fix for the codex P2 finding, reviewed last round, is unchanged here). No CRITICAL or IMPORTANT issues found; no authorization/access-control gaps, injection surfaces, credential-handling changes, or dangerous trigger/permission patterns in this delta.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

claude-review: lane has findings and the inline-comment tool granted, yet delivers them as summary comments — inline path may be non-functional

1 participant