Skip to content

ci: opt-in AI triage via ai-triage label (#274) - #277

Merged
allxsmith merged 5 commits into
mainfrom
ci/ai-triage-label
Jul 11, 2026
Merged

allxsmith merged 5 commits into
mainfrom
ci/ai-triage-label

Conversation

@allxsmith

@allxsmith allxsmith commented Jul 10, 2026 •

Copy link
Copy Markdown
Owner

Pull Request

Description

Adds an opt-in, label-triggered AI triage workflow (.github/workflows/ai-triage.yml, name: AI Triage) — an adaptation of oven-sh/bun's always-on claude-find-issues-for-pr.yml + claude-dedupe-issues.yml. A triage+ maintainer applies the ai-triage label to a PR or issue; one cheap sonnet session searches the repo for related issues and likely duplicates, posts exactly one summary comment, and the label is auto-removed so it works as a re-pressable button.

  • bulma-ui (@allxsmith/bestax-bulma)
  • create-bestax (create-bestax)
  • docs (@allxsmith/bestax-docs)
  • Other (please specify): repo tooling (.github/workflows/ai-triage.yml) + docs

Key properties, mirroring the house patterns in on-slop.yml / claude-review.yml / bestaxbot-reply.yml:

  • Two security layers: job if: requires the ai-triage label from a real user with AI_LOOP_ENABLED == 'true', and a live gh api .../collaborators/.../permission re-check requires admin/maintain/write/triage — otherwise silent no-op. All later steps gate on that output.
  • Safe under pull_request_target: only the base repo's default branch is checked out (never the PR head), persist-credentials: false, pinned actions/checkout SHA. No node/pnpm setup, no install — the job never runs project code.
  • Tightly scoped Claude step: same pinned claude-code-action SHA as the other workflows, OAuth token only (posts as claude[bot]), --max-turns 30, --model claude-sonnet-5, --allowedTools restricted to read-only gh plus gh pr comment / gh issue comment. Prompt hard rules: one comment only, never label/close/merge/push/resolve, never write "@claude"/"@coderabbitai".
  • Label always removed: an if: always() step (still gated on the permission check) deletes the label via the issues API — which covers both PRs and issues — so the button never wedges "on".
  • Workflow-level permissions: {}; job takes contents: read, pull-requests: write, issues: write, id-token: write (mirroring claude-review.yml's OAuth-exchange needs).

Docs: one new row in the label table in docs/docs/guides/getting-started/ai-development.md and one sentence in root CLAUDE.md's "AI development loop" section.

Related Issue(s)

Closes #274

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • Performance
  • Build tooling
  • Other (please describe):

Checklist

  • My code follows the project style guidelines
  • I have performed a self-review of my code
  • I have added tests that prove my fix is effective or that my feature works (N/A — GitHub Actions workflow; no runnable unit tests)
  • I have added/updated documentation as needed
  • I have updated Storybook stories as needed (bulma-ui) (N/A — no UI change)
  • My changes require a change to the documentation
  • All new and existing tests passed (YAML validated; prettier check passes on the changed markdown)
  • Any relevant dependencies are updated (N/A — no dependency changes)
  • If this PR changes commands, conventions, or package structure, the affected CLAUDE.md files are updated

Screenshots / Demos

N/A — CI workflow. Flow: apply ai-triage → one triage comment (### AI triage with duplicates/related lists or "No related issues or duplicates found.") → label auto-removed.

Additional Context

🤖 Generated with Claude Code

https://claude.ai/code/session_01Pohc8xLkdx4gwXkW3xd7up

Summary by CodeRabbit

  • New Features
    • Added an “AI Triage” one-shot automation that runs on ai-triage label application for both issues and pull requests.
    • When allowed, it scans the item, identifies related and duplicate work, and posts exactly one Markdown comment (PR vs issue).
    • The ai-triage label is automatically removed after completion to allow re-running.
  • Documentation
    • Updated CLAUDE.md and the AI development guide with the new ai-triage label lifecycle and behavior.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 712998b7-71f2-4231-85d5-799127c4616c

📥 Commits

Reviewing files that changed from the base of the PR and between 580087e and 16985a7.

📒 Files selected for processing (2)
  • CLAUDE.md
  • docs/docs/guides/getting-started/ai-development.md

Walkthrough

Adds an opt-in ai-triage workflow for labeled pull requests and issues. It validates the labeler, runs a constrained Claude triage session, posts one comment, removes the label, and documents the behavior.

Changes

AI triage automation

Layer / File(s) Summary
Trigger and permission gates
.github/workflows/ai-triage.yml
Adds PR and issue label triggers, concurrency controls, the AI_LOOP_ENABLED kill switch, sender validation, and live collaborator permission checks.
Triage execution and documentation
.github/workflows/ai-triage.yml, CLAUDE.md, docs/docs/guides/getting-started/ai-development.md
Checks out the default branch, runs a constrained Claude triage prompt, posts exactly one comment, removes the label in an always() step, and documents the new label behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Maintainer as Authorized labeler
  participant GitHub as GitHub Actions
  participant PermissionAPI as Collaborator permission API
  participant Claude as Claude triage action
  participant Target as PR or issue
  Maintainer->>GitHub: Apply ai-triage label
  GitHub->>PermissionAPI: Verify collaborator permission
  PermissionAPI-->>GitHub: Return allowed role
  GitHub->>Claude: Start constrained triage session
  Claude->>Target: Read target and search related items
  Claude->>Target: Post one Markdown comment
  GitHub->>Target: Remove ai-triage label
Loading

Possibly related issues

Possibly related PRs

  • allxsmith/bestax#258 — Both configure allowedTools for anthropics/claude-code-action in GitHub Actions workflows.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the new opt-in ai-triage workflow.
Description check ✅ Passed The description follows the template and includes summary, issue reference, type, checklist, and additional context.
Linked Issues check ✅ Passed The workflow and docs match the issue's requirements for gated label-triggered triage, comment-only Claude use, and label cleanup.
Out of Scope Changes check ✅ Passed The changes stay within the requested workflow, docs, and CLAUDE.md updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/ai-triage-label

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://433d1bf7.bestax.pages.dev

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ai-triage.yml:
- Around line 64-75: Remove the permission-output condition from the cleanup
step identified near the label-removal logic, so cleanup runs even when the
collaborator permission check in the workflow’s permission-evaluation step fails
and produces allowed=false. Preserve the existing label removal behavior and
other necessary guards, ensuring ai-triage is always cleaned up after
processing.
- Around line 48-52: Restrict the AI triage workflow’s write capabilities:
remove pull-requests: write and issues: write from the permissions used by the
agent, and prevent its Bash gh api access from issuing arbitrary mutations. Move
comment and label-removal operations into a separate deterministic workflow
step, or explicitly limit gh api to the required read and label-delete endpoints
while preserving only the minimum permissions needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ff27d159-1745-4e29-b417-eb34b4790dce

📥 Commits

Reviewing files that changed from the base of the PR and between 306e3cb and 6ea7bde.

📒 Files selected for processing (3)
  • .github/workflows/ai-triage.yml
  • CLAUDE.md
  • docs/docs/guides/getting-started/ai-development.md

Comment on lines +48 to +52
permissions:
contents: read # checkout of the base default branch
pull-requests: write # post the triage comment on PRs; remove the label
issues: write # post the triage comment on issues; remove the label
id-token: write # OIDC exchange for the Claude OAuth token

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the workflow around the cited lines and any nearby triage/job config.
git ls-files '.github/workflows/ai-triage.yml' '.github/**' | sed -n '1,200p'
echo '--- ai-triage.yml ---'
cat -n .github/workflows/ai-triage.yml | sed -n '1,220p'

echo '--- search for gh api / tool config / permissions in github workflows ---'
rg -n --hidden --glob '.github/**' -e 'gh api' -e 'permissions:' -e 'id-token: write' -e 'pull-requests: write' -e 'issues: write' -e 'claude-code' -e 'tools?' .github

Repository: allxsmith/bestax

Length of output: 23452


Restrict the triage agent’s write surface. Bash(gh api:*) plus pull-requests: write / issues: write lets untrusted PR or issue content steer the model into arbitrary mutating REST calls. Keep the agent on read-only tools and move comment/label writes into a separate deterministic step, or narrow gh api to only the exact read and label-delete endpoints.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ai-triage.yml around lines 48 - 52, Restrict the AI triage
workflow’s write capabilities: remove pull-requests: write and issues: write
from the permissions used by the agent, and prevent its Bash gh api access from
issuing arbitrary mutations. Move comment and label-removal operations into a
separate deterministic workflow step, or explicitly limit gh api to the required
read and label-delete endpoints while preserving only the minimum permissions
needed.

Comment thread .github/workflows/ai-triage.yml
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://9c1b81f3.bestax.pages.dev

@allxsmith allxsmith added the deep-review label Jul 11, 2026 — with Claude

Copy link
Copy Markdown
Owner Author

@claude perform deep review


Generated by Claude Code

@claude

claude Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

Claude encountered an error after 2m 44s —— View job


Deep review in progress

  • Gather context (read workflow file, diff, related docs)
  • Review security posture of ai-triage.yml (permissions, tool allowlist, gating)
  • Evaluate CodeRabbit's two flagged issues
  • Check docs/CLAUDE.md updates for accuracy
  • Post final review

claude added 2 commits July 11, 2026 01:27
Add .github/workflows/ai-triage.yml: labeling a PR or issue `ai-triage`
(triage+ only, live-verified) runs one sonnet session that comments with
related issues and likely duplicates, then removes the label so it acts
as a re-pressable button. Document the label in the AI development docs
table and CLAUDE.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pohc8xLkdx4gwXkW3xd7up
Drop Bash(gh api:*) from --allowedTools: the prefix also matches
POST/PATCH/DELETE and the session ingests untrusted issue/PR text, so
an injected agent could mutate labels or machine-managed comments.
Point the prompt's gh api hints at gh search / gh pr diff instead and
note the GET-only posture in the header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pohc8xLkdx4gwXkW3xd7up
@allxsmith
allxsmith force-pushed the ci/ai-triage-label branch from 12d4091 to cc49920 Compare July 11, 2026 01:28
@allxsmith allxsmith added deep-review and removed deep-review labels Jul 11, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://e1c1d61f.bestax.pages.dev

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
docs/docs/guides/getting-started/ai-development.md (1)

50-57: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

ai-triage row omits the triage+ access requirement.

Every other opt-in row (deep-review) states who can trigger it. This row doesn't mention the triage+ permission gate that ai-triage.yml enforces, and that CLAUDE.md's parallel description explicitly calls out ("triage+ only"). Worth aligning for consistency and accuracy.

Proposed fix
-| `ai-triage`          | PRs & issues | Opt-in: one-shot AI triage (related issues + duplicates); the label auto-removes after the run        |
+| `ai-triage`          | PRs & issues | Opt-in: a triage+ user applies it to run one-shot AI triage (related issues + duplicates); the label auto-removes after the run |

As per path instructions, "Check MDX/Markdown correctness, working internal links, and that documented APIs match the library."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/docs/guides/getting-started/ai-development.md` around lines 50 - 57,
Update the ai-triage entry in the label table to explicitly state that it is
opt-in for triage+ users only, matching the permission enforced by ai-triage.yml
and the parallel description in CLAUDE.md.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ai-triage.yml:
- Around line 148-158: Remove the permission-result gate from the “Remove
ai-triage label” step in the workflow, retaining only always() so cleanup runs
even when the permission check fails. Keep the existing repository, issue/PR
number, token environment, and tolerant gh api deletion unchanged.

---

Nitpick comments:
In `@docs/docs/guides/getting-started/ai-development.md`:
- Around line 50-57: Update the ai-triage entry in the label table to explicitly
state that it is opt-in for triage+ users only, matching the permission enforced
by ai-triage.yml and the parallel description in CLAUDE.md.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: df4159b6-53ff-4e9e-a290-1802fddb9b1a

📥 Commits

Reviewing files that changed from the base of the PR and between 12d4091 and cc49920.

📒 Files selected for processing (3)
  • .github/workflows/ai-triage.yml
  • CLAUDE.md
  • docs/docs/guides/getting-started/ai-development.md

Comment thread .github/workflows/ai-triage.yml Outdated

@claude claude 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.

Deep review — 0 finding(s)

# Severity Area Finding Location
— — — No blocking defects found. —

Overall: This is a well-constructed, opt-in triage workflow plus matching docs. The security posture is sound — it never checks out PR head code (base default branch only, so pull_request_target is safe), the two-layer gate (label + User + AI_LOOP_ENABLED job if, then a live collaborators-API role re-check) mirrors the three proven sibling workflows exactly, and the Claude tool allowlist is GET-only gh commands plus the two comment commands. Event handling is correct: NUMBER/IS_PR/concurrency all resolve properly for both pull_request_target and issues, PR/issue numbers are globally unique so no group collision, and the always() remove-label step keeps the button re-pressable even on failure. The only edge worth a human's eye — not a defect, effectively unreachable since GitHub restricts labeling to triage+ — is that a false result from the live perm re-check skips label removal, leaving the label applied; that's a reasonable "flag the anomaly" behavior. Docs and CLAUDE.md are updated in the same PR and there's no component/API/skills surface, so no story/skills obligation applies.

🏄 Clean sets rolling in on this one, dude — same lineup the other AI workflows already surf, no sketchy head-code checkout, gates locked in tight. Paddle it out, it's good to go.

)

Addresses the CodeRabbit review on PR #277: the cleanup step's permission
gate contradicted its own "never wedges on" comment — a transient
collaborators-API failure would leave the label stuck with no way to
re-fire via a labeled event. Removal uses only GITHUB_TOKEN, so dropping
the gate spends no Claude usage. Also states the triage+ requirement in
the docs label table, matching the deep-review row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0142miDJt2AX9QUw5fNJt2xu
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://bad2f10d.bestax.pages.dev

# Conflicts:
#	docs/docs/guides/getting-started/ai-development.md
@allxsmith allxsmith added deep-review and removed deep-review labels Jul 11, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://20efa3f4.bestax.pages.dev

@claude claude 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.

Deep review — 0 finding(s)

# Severity Area Finding Location
– – – No blocking defects found. –

Overall: This is a tightly-scoped, well-reasoned GitHub Actions workflow that faithfully mirrors the repo's established on-slop.yml / claude-review.yml / bestaxbot-reply.yml patterns. I checked the parts most likely to break: the two-layer permission gate (job if: + live collaborators/.../permission recheck) is sound; pull_request_target is safe because only the base default branch is checked out with persist-credentials: false and no project code ever runs; the concurrency group can't collide since GitHub issue/PR numbers share one namespace; the Claude allowlist is genuinely read-only + comment-only (no gh api), keeping any prompt-injection blast radius to a single comment; and the if: always() label removal via the issues API correctly covers both PRs and issues with GITHUB_TOKEN-only auth. The only non-blocking wart worth a human's awareness: when AI_LOOP_ENABLED != 'true' the whole job is skipped, so an applied ai-triage label is never auto-removed and stays stuck until a maintainer clears it — arguably intended (system off means nothing fires), but a maintainer may want the cleanup to run regardless. Docs (label table + CLAUDE.md) are updated; no runnable-code surface, so no tests apply.

🏄 Clean sets rolling in on this one, dude — solid two-layer break protection, no gnarly pull_request_target wipeouts, and the label just resets like a re-pressable button. Only ripple is a label that won't paddle back out if the kill switch is off, but that's mellow. Ship it. 🌊

# Conflicts:
#	CLAUDE.md
#	docs/docs/guides/getting-started/ai-development.md
@allxsmith
allxsmith merged commit 7bc73f4 into main Jul 11, 2026
8 of 9 checks passed
@allxsmith
allxsmith deleted the ci/ai-triage-label branch July 11, 2026 11:52
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

Preview URL: https://25c87afa.bestax.pages.dev

@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.2.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

allxsmith added a commit that referenced this pull request Jul 12, 2026
…des (#292)

PR A of #289: Bun-parity fan-out triage. Always-on (config-driven) with a
fail-closed daily budget counter on tracking issue #290, label mode kept,
.claude/commands/ established (triage-dedupe, triage-find-issues,
triage-find-duplicate-prs, pre-pr), docs updated.

Refs #289
See #269, #274, #277
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 5.4.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@bestax-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 1.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Opt-in AI triage via ai-triage label (find related issues / duplicates)

2 participants