Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# AI-assisted code review via Claude Code Action on PRs.
# Issue automation: implement, open PR, self-review, check CI, notify maintainer.
# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#4-claude-code-claudeyml
name: Claude Code

Expand All @@ -10,10 +11,13 @@ on:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [labeled]

permissions: {}

jobs:
# Interactive mode: PR reviews and @claude mentions
claude:
if: >-
(github.event_name == 'pull_request' &&
Expand All @@ -27,13 +31,62 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
contents: write
id-token: write
pull-requests: write
issues: write
actions: read
checks: read
Comment on lines 33 to +39

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

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

The interactive claude job now requests contents: write. If this job is only intended to review PRs / respond to @claude comments (and not push commits), this is more privilege than needed and conflicts with the least-privilege policy in standards/ci-standards.md (permissions should be scoped per job). Consider switching the interactive job back to contents: read and keeping contents: write only on claude-issue (which actually creates branches/commits).

Copilot uses AI. Check for mistakes.
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
- name: Run Claude Code
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1
uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
additional_permissions: |
actions: read
checks: read

# Automation mode: issue-triggered work — implement, open PR, review, and notify
claude-issue:
if: >-
github.event_name == 'issues' && github.event.action == 'labeled' &&
github.event.label.name == 'claude'
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
id-token: write
pull-requests: write
issues: write
actions: read
checks: read
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
- name: Run Claude Code
uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
label_trigger: "claude"
track_progress: "true"
additional_permissions: |
actions: read
checks: read
prompt: |
Implement a fix for issue #${{ github.event.issue.number }}.

After implementing:
1. Create a pull request with a clear title and description. Include "Closes #${{ github.event.issue.number }}" in the PR body.
2. Self-review your own PR — look for bugs, style issues, missed edge cases, and test gaps. If you find problems, push fixes.
3. Review all comments and review threads on the PR. For each one:
- If you can address the feedback, make the fix, push, and mark the conversation as resolved.
- If the comment requires human judgment, leave a reply explaining what you need.
4. Check CI status. If CI fails, read the logs, fix the issues, and push again. Repeat until CI passes.
5. When CI is green, all actionable review comments are resolved, and the PR is ready, read the CODEOWNERS file and leave a comment tagging the relevant code owners to review and merge.
93 changes: 78 additions & 15 deletions standards/ci-standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,14 @@ Each repo needs a `sonar-project.properties` file at root with project key and o
### 4. Claude Code (`claude.yml`)

AI-assisted code review on PRs and issue automation via Claude Code Action.
Claude responds to PR events, `@claude` mentions in comments, and issues
labeled `claude`. The label trigger enables Claude to work issues like a
human contributor — reading the issue, creating a branch, implementing the
fix, and opening a PR.
The workflow has two jobs:

- **`claude`** (interactive mode) — reviews PRs and responds to `@claude`
mentions in comments. No `prompt` input; runs in interactive mode.
- **`claude-issue`** (automation mode) — triggered when the `claude` label is
applied to an issue. Uses a `prompt` to drive the full lifecycle:
implement the fix, create a PR, self-review, resolve review comments,
monitor CI, and tag the maintainer when ready for human review.

**Billing:** This workflow uses Anthropic credits via `CLAUDE_CODE_OAUTH_TOKEN`,
not GitHub Copilot premium requests. This is distinct from the "Assign to Agent"
Expand All @@ -140,6 +144,7 @@ on:
permissions: {}

jobs:
# Interactive mode: PR reviews and @claude mentions
claude:
if: >-
(github.event_name == 'pull_request' &&
Expand All @@ -149,27 +154,76 @@ jobs:
contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment' &&
contains(github.event.comment.body, '@claude') &&
contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'issues' && github.event.action == 'labeled' &&
github.event.label.name == 'claude')
contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association))
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
id-token: write
pull-requests: write
issues: write
actions: read
Comment on lines 160 to +165

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

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

The standard config shows the interactive claude job using contents: write. If interactive mode doesn’t need to push commits, consider documenting/job-scoping contents: read for claude and reserving contents: write for claude-issue to better match the least-privilege guidance in the same document.

Copilot uses AI. Check for mistakes.
checks: read
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
- name: Run Claude Code
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
additional_permissions: |
actions: read
checks: read

# Automation mode: issue-triggered work — implement, open PR, review, and notify
claude-issue:
if: >-
github.event_name == 'issues' && github.event.action == 'labeled' &&
github.event.label.name == 'claude'
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
id-token: write
pull-requests: write
issues: write
actions: read
checks: read
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
- name: Run Claude Code
uses: anthropics/claude-code-action@6e2bd52842c65e914eba5c8badd17560bd26b5de # v1.0.89
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
label_trigger: "claude"
track_progress: "true"
additional_permissions: |
actions: read
checks: read
prompt: |
Implement a fix for issue #${{ github.event.issue.number }}.

After implementing:
1. Create a pull request with a clear title and description.
Include "Closes #${{ github.event.issue.number }}" in the PR body.
2. Self-review your own PR — look for bugs, style issues,
missed edge cases, and test gaps. If you find problems, push fixes.
3. Review all comments and review threads on the PR. For each one:
- If you can address the feedback, make the fix, push, and
mark the conversation as resolved.
- If the comment requires human judgment, leave a reply
explaining what you need.
4. Check CI status. If CI fails, read the logs, fix the issues,
and push again. Repeat until CI passes.
5. When CI is green, all actionable review comments are resolved,
and the PR is ready, read the CODEOWNERS file and leave a
comment tagging the relevant code owners to review and merge.
```

**Required secrets:** `CLAUDE_CODE_OAUTH_TOKEN`
Expand All @@ -182,21 +236,30 @@ also be applied manually to any issue to trigger Claude.
`CLAUDE.md` from the repository root. The org-level `.github/CLAUDE.md` is
inherited by repos without their own. Each repo's `CLAUDE.md` references
`AGENTS.md` for cross-cutting development standards (TDD, SOLID, pre-commit
checks, etc.). No additional `prompt` or `settings` input is needed.
checks, etc.). The `claude-issue` job adds an automation `prompt` for the
issue-to-PR lifecycle, but Claude still reads `CLAUDE.md` and `AGENTS.md`
for project-specific context.

**Permissions note:** `contents: write` is required for issue-triggered work
where Claude creates branches and pushes commits. PR review mode only needs
`contents: read`, but a single permission set covers both modes.
**Permissions note:** Both jobs use the same permission set. `contents: write`
is required for issue-triggered work where Claude creates branches and pushes
commits. `actions: read` and `checks: read` enable Claude to monitor CI status
via the GitHub MCP tools (`get_ci_status`, `get_workflow_run_details`,
`download_job_log`).

**Dependabot behavior:** The Claude Code step is skipped for Dependabot PRs (the
`if` condition on the step). The job still runs and reports SUCCESS to satisfy
required status checks. See [AGENTS.md](../AGENTS.md#claude-code-workflow-on-dependabot-prs).
**Dependabot behavior:** The Claude Code step in the `claude` job is skipped
for Dependabot PRs (the `if` condition on the step). The job still runs and
reports SUCCESS to satisfy required status checks. See
[AGENTS.md](../AGENTS.md#claude-code-workflow-on-dependabot-prs).

**Issue trigger security:** The `issues: [labeled]` event fires when any user
with triage or write access applies a label. The label name check in the `if:`
condition ensures only the `claude` label triggers the workflow — other labels
are ignored. Apply the `claude` label manually to any issue to trigger Claude.

**Maintainer notification:** The `claude-issue` prompt reads `CODEOWNERS` at
runtime to determine who to tag. No per-repo customization is needed as long
as `CODEOWNERS` is present (checked by the compliance audit).

### 5. Dependabot Auto-Merge (`dependabot-automerge.yml`)

Automatically approves and squash-merges eligible Dependabot PRs.
Expand Down Expand Up @@ -365,7 +428,7 @@ For single-job workflows, top-level least-privilege permissions are acceptable
|----------|------------|
| CI (build/test) | `contents: read` |
| SonarCloud | `contents: read`, `pull-requests: read` |
| Claude Code | `contents: write`, `id-token: write`, `pull-requests: write`, `issues: write` |
| Claude Code | `contents: write`, `id-token: write`, `pull-requests: write`, `issues: write`, `actions: read`, `checks: read` |
| CodeQL | `actions: read`, `security-events: write`, `contents: read` |
| Dependabot auto-merge | `contents: read`, `pull-requests: read` (+ app token for merge) |

Expand Down
Loading