diff --git a/.github/workflows/claude-issue-triage.yml b/.github/workflows/claude-issue-triage.yml index 701776e0e..9b4087ab4 100644 --- a/.github/workflows/claude-issue-triage.yml +++ b/.github/workflows/claude-issue-triage.yml @@ -24,8 +24,29 @@ jobs: with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} track_progress: true # Show triage progress + settings: | + { + "includeGitInstructions": false, + "permissions": { + "deny": [ + "Edit", + "Write", + "NotebookEdit", + "Bash(git add *)", + "Bash(git commit *)", + "Bash(git rm *)", + "Bash(git push *)", + "Bash(*git-push.sh *)" + ] + } + } prompt: | - Analyze this new Basic Memory issue and perform triage: + Analyze this new Basic Memory issue and perform triage only. + + **Scope Boundary:** + - Do not implement a fix, edit repository files, run tests, create a branch, commit, or push. + - The only permitted write actions are adding issue labels and posting issue comments. + - Even when the issue includes a root cause, suggested fix, or acceptance criteria, stop after triage. **Issue Analysis:** 1. **Type Classification:** @@ -56,7 +77,8 @@ jobs: - Complex: Major feature work, architectural changes **Actions to Take:** - 1. Add appropriate labels using: `gh issue edit ${{ github.event.issue.number }} --add-label "label1,label2"` + 1. Add appropriate labels using: + `./scripts/edit-issue-labels.sh --add-label label1 --add-label label2` 2. Check for duplicates using: `gh search issues` 3. If duplicate found, comment mentioning the original issue 4. For feature requests, ask clarifying questions if needed @@ -69,5 +91,8 @@ jobs: - Complexity: simple, medium, complex - Status: needs-reproduction, needs-clarification, duplicate - Read the issue carefully and provide helpful triage with appropriate labels. - claude_args: '--allowed-tools "Bash(gh issue:*),Bash(gh search:*),Read"' + Read the issue carefully, apply appropriate labels, post any necessary triage comment, + and then stop. Do not begin implementation work. + claude_args: | + --permission-mode dontAsk + --allowedTools "Bash(./scripts/edit-issue-labels.sh:*),Bash(gh issue view:*),Bash(gh issue comment:*),Bash(gh search issues:*),Read,Grep,Glob" diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 7f330290b..af2c58de2 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -37,9 +37,12 @@ jobs: ) runs-on: ubuntu-latest permissions: - contents: read - pull-requests: read - issues: read + # write access lets @claude push its working branch and open the PR; + # with contents:read the push failed as github-actions[bot] (403) and the + # run dead-ended after committing locally (see #1084). + contents: write + pull-requests: write + issues: write id-token: write actions: read # Required for Claude to read CI results on PRs steps: @@ -64,7 +67,11 @@ jobs: # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. # prompt: 'Update the pull request description to include a summary of changes.' - # Optional: Add claude_args to customize behavior and configuration + # Allow the interpreters @claude needs to self-verify a change before + # opening the PR. The default Bash allowlist already covers git/gh and + # file edits (that part worked in #1084); it only blocked test runners, + # so this list is additive and scoped to project quality gates. + claude_args: '--allowed-tools "Bash(just:*),Bash(uv:*),Bash(uvx:*),Bash(pytest:*),Bash(ruff:*),Bash(python:*),Bash(python3:*)"' + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md # or https://docs.claude.com/en/docs/claude-code/sdk#command-line for available options - # claude_args: '--model claude-opus-4-1-20250805 --allowed-tools Bash(gh pr:*)' diff --git a/scripts/edit-issue-labels.sh b/scripts/edit-issue-labels.sh new file mode 100755 index 000000000..c21faccce --- /dev/null +++ b/scripts/edit-issue-labels.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +# Limit automated issue triage to labels on the issue that triggered the workflow. +set -euo pipefail + +issue_number=$(jq -r '.issue.number // empty' "${GITHUB_EVENT_PATH:?GITHUB_EVENT_PATH not set}") +if ! [[ "$issue_number" =~ ^[0-9]+$ ]]; then + echo "Error: no issue number in event payload" >&2 + exit 1 +fi + +labels=() +while [[ $# -gt 0 ]]; do + case "$1" in + --add-label) + if [[ $# -lt 2 ]]; then + echo "Error: --add-label requires a label" >&2 + exit 1 + fi + labels+=("$2") + shift 2 + ;; + *) + echo "Error: only --add-label is accepted" >&2 + exit 1 + ;; + esac +done + +if [[ ${#labels[@]} -eq 0 ]]; then + echo "Error: at least one label is required" >&2 + exit 1 +fi + +valid_labels=$(gh label list --limit 500 --json name --jq '.[].name') +filtered_labels=() +for label in "${labels[@]}"; do + if grep -qxF "$label" <<<"$valid_labels"; then + filtered_labels+=("$label") + else + echo "Ignoring unknown label: $label" >&2 + fi +done + +if [[ ${#filtered_labels[@]} -eq 0 ]]; then + exit 0 +fi + +repository=${GITHUB_REPOSITORY:?GITHUB_REPOSITORY not set} +labels_url="repos/$repository/issues/$issue_number/labels" +api_args=(--method POST "$labels_url") +for label in "${filtered_labels[@]}"; do + api_args+=(-f "labels[]=$label") +done + +gh api "${api_args[@]}" --silent +echo "Added: ${filtered_labels[*]}"