Skip to content

ci: unblock bot-triage for read-permission contributors - #2587

Merged
bpamiri merged 1 commit into
developfrom
peter/fix-bot-triage-read-users
May 11, 2026
Merged

ci: unblock bot-triage for read-permission contributors#2587
bpamiri merged 1 commit into
developfrom
peter/fix-bot-triage-read-users

Conversation

@bpamiri

@bpamiri bpamiri commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

The wheels-bot triage workflow has been silently failing for issues opened by external contributors (read-permission users). 5 of the last 13 bot-triage runs ended in failure at the Run Triage step with Actor does not have write permissions to the repository.

Pattern is clean: every recent failure was an issue from @zainforbjs (read perms); every success was an issue from @bpamiri (write/admin). The anthropics/claude-code-action@v1 defaults to rejecting non-write actors — sensible for PR automation, exactly backward for issue triage on a public OSS bug tracker.

What changed

  • allowed_non_write_users: '*' on the Run Triage step — the action input designed for this exact case. Its docs name "issue labeling" as the canonical example.
  • workflow_dispatch trigger with issue_number input — so the 5 missed issues (and any future misses) can be re-triaged without the close/reopen dance.
  • if: guard hardened so the wheels-bot self-loop check doesn't try to evaluate github.event.issue on dispatch events.

Why it's safe

The override is scoped narrowly to the triage workflow:

  1. Workflow-level permissions: contents: read — the action cannot mutate code.
  2. Comments are posted via the GitHub App token, independent of the actor's permissions. An attacker can't escalate via prompt injection.
  3. claude_args restricts the model's shell to Bash(gh:*) + read-only git + Read/Grep/Glob. No Write, no Edit, no general Bash.

Worst-case under prompt injection: a misleading triage comment under the bot's name — recoverable, bounded, and we're already reading the same untrusted content into the model today.

Why only this workflow

Workflow Action needed?
bot-triage (issues:opened) Yes — fixed here
bot-research, bot-propose-fix, bot-advisor, bot-address-review, bot-write-docs (issue_comment slash commands) No — restricting these to write-permission users is correct (don't let randos invoke expensive runs)
bot-review-a, bot-tdd-gate (pull_request) Eventually, if external contributors start opening PRs. None yet, so deferring.

Recovery for the 5 missed issues

Once this merges, re-trigger each via the new dispatch input:

for n in 2582 2577 2569 2568 2567; do
  gh workflow run bot-triage.yml --repo wheels-dev/wheels -f issue_number=$n
done

Test plan

🤖 Generated with Claude Code

The `anthropics/claude-code-action@v1` action defaults to rejecting
actors without write permission to the repo — a sensible default for
PR-related automation, but exactly backward for issue triage on a
public OSS bug tracker, where contributors filing bugs are precisely
who we want responded to.

Symptom: 5 of the last 13 bot-triage runs failed at the "Run Triage"
step with `Actor does not have write permissions to the repository`,
producing zero triage comments on issues opened by external
contributors.

Fix:
- `allowed_non_write_users: '*'` — the action input designed for this
  exact case. Docs name "issue labeling" as the canonical example.
- Added `workflow_dispatch` with `issue_number` input so missed
  issues can be re-triaged without close/reopen.
- Hardened the `if:` guard so the wheels-bot self-loop check no
  longer evaluates `github.event.issue` on dispatch events.

Safe because the workflow runs with `permissions: contents: read`,
posts via an App token independent of the actor, and `claude_args`
restricts the model's shell to `gh` + read-only `git` + file reads.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer A

TL;DR: This is a clean, minimal CI fix for a confirmed production problem (5 missed triage runs from read-permission contributors). All three changes -- allowed_non_write_users: '*', workflow_dispatch recovery trigger, and the hardened if: guard -- are correct and well-justified. I have one minor observation about the issue_number input type, but it does not block approval.


Correctness

if: guard (line 27): The refactor from a bare github.event.issue.user.login check to the event-gated form is correct:

&& (github.event_name != 'issues' || github.event.issue.user.login != 'wheels-bot[bot]')

On workflow_dispatch, github.event_name != 'issues' is truthy, so the bot-login guard is skipped -- which is safe because the idempotency marker check in the gate step still fires before Run Triage proceeds.

Concurrency group (line 17): github.event.issue.number || inputs.issue_number correctly handles both trigger paths. On workflow_dispatch, github.event.issue.number is empty/undefined and GitHub Actions evaluates it as falsy, falling through to inputs.issue_number.

Minor observation -- issue_number input type: The dispatch input is declared type: string and interpolated directly into the prompt:

prompt: |
  /triage-issue ${{ github.event.issue.number || inputs.issue_number }}

A malformed value passed via gh workflow run -f issue_number=... (e.g. with embedded newlines) would inject additional content into the prompt. The blast radius is small -- tool restrictions limit the model shell; triggering dispatch requires write access; the model already reads untrusted issue content -- but declaring the input as type: number if the action schema permits it would close the gap at zero cost. Worth a follow-up; not a blocker.


Security

allowed_non_write_users: '*' (line 68): The PR's three-part safety argument is sound:

  1. Workflow-level permissions: contents: read restricts the default GITHUB_TOKEN.
  2. Comments are posted via the GitHub App token (independent of the actor's repo permissions).
  3. claude_args locks the model's shell to gh:* + read-only git + Read/Grep/Glob -- no Write, no Edit, no unconstrained Bash.

The worst-case prompt-injection outcome (a misleading triage comment under the bot's name) is recoverable, bounded, and not materially different from the risk that existed before this PR (write-permission users were already sending untrusted issue content into the model). The action's own docs name "issue labeling" as the canonical use case for this input -- this is the right tool.


Commits

Single commit: ci: unblock bot-triage for read-permission contributors -- valid type, no scope (fine), 52-char subject, not ALL-CAPS. Conforms to commitlint.config.js.


Docs / Tests

No CFML code changed; cross-engine and convention checklists are N/A. The PR's test plan (dispatch a missed issue after merge, watch the next external issue) is the appropriate verification strategy for a CI workflow fix -- no automated test coverage is expected or missing here.

@wheels-bot

wheels-bot Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Wheels Bot — Reviewer B (round 1)

A's review is substantive and accurate. The approval is backed by concrete line-by-line analysis of a small, clean diff. One minor false positive in A's follow-up suggestion, but it does not affect the verdict. Converging on approve.

Sycophancy

None detected. A cited specific lines, explained the || fallback logic in the concurrency group and prompt, and walked through the three-part security argument. The approval is earned by the analysis, not assumed.

False positives

  • type: number suggestion: A characterizes switching the issue_number input to type: number as a "zero-cost" fix for the prompt-injection concern. GitHub Actions workflow_dispatch inputs support four types: string, boolean, choice, and environment. number is not a valid type; the workflow would fail to parse or silently ignore the declaration. A's suggested mitigation does not exist. The underlying concern (numeric validation of the input) is legitimate but already adequately addressed by the fact that dispatching workflow_dispatch requires write access to the repo — the same population that could inject via inputs.issue_number can already do far more direct harm. A's overall characterization of the risk as "not a blocker" is correct; only the proposed fix is wrong.

Everything else A checked — the if: guard short-circuit on workflow_dispatch, the || fallback in the concurrency group and prompt, and the security analysis — is accurate against the diff.

Missed issues

None detected. The diff touches only bot-triage.yml. All six changed lines are accounted for in A's review: allowed_non_write_users, workflow_dispatch trigger + input, if: guard, concurrency group, target-number in the skip-check, and the prompt. No cross-engine or CFML concerns apply to a YAML-only change.

Verdict alignment

A's APPROVE is consistent with the findings. One observation was raised (and raised correctly as non-blocking), and no actionable issues were identified. The verdict follows from the analysis.

Convergence

Aligned on APPROVE. The false positive in A's suggestion does not change the correctness of the overall review — removing the type: number suggestion leaves the approval fully intact. There are no unresolved disputed claims and no missed blocking issues.

@bpamiri
bpamiri merged commit b6d6502 into develop May 11, 2026
7 checks passed
@bpamiri
bpamiri deleted the peter/fix-bot-triage-read-users branch May 11, 2026 22:05
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.

1 participant