Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
961e1cc
feat: implement issue #232 — Phase 1: Issue Triage gh-aw workflow
donpetry-bot May 19, 2026
b4abacf
fix(bot): address bot feedback [skip ci-relay]
donpetry-bot May 19, 2026
a2ad3dd
fix(ci): auto-fix for SonarCloud Code Analysis [skip ci-relay]
donpetry-bot May 19, 2026
568b293
chore: apply manual instructions [skip ci-relay]
donpetry-bot May 19, 2026
a563cc7
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 19, 2026
a95a13f
chore: apply manual instructions [skip ci-relay]
donpetry-bot May 19, 2026
765c03b
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 19, 2026
14872f7
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 19, 2026
86792aa
chore: apply manual instructions [skip ci-relay]
donpetry-bot May 19, 2026
bbc4b28
fix(reviews): address review comments [skip ci-relay]
donpetry-bot May 19, 2026
43c8ade
fix(reviews): address review comments [skip ci-relay]
donpetry-bot May 19, 2026
73d9979
fix(reviews): address review comments [skip ci-relay]
donpetry-bot May 19, 2026
76b0b2e
fix(bot): address bot feedback [skip ci-relay]
donpetry-bot May 19, 2026
44dd640
fix(bot): address bot feedback [skip ci-relay]
donpetry-bot May 19, 2026
84798b9
fix(ci): auto-fix for SonarCloud Code Analysis [skip ci-relay]
donpetry-bot May 19, 2026
a7aff0a
fix(ci): auto-fix for SonarCloud Code Analysis [skip ci-relay]
donpetry-bot May 19, 2026
b9da230
fix(ci): auto-fix for SonarCloud Code Analysis [skip ci-relay]
donpetry-bot May 20, 2026
e606888
chore: apply manual instructions [skip ci-relay]
donpetry-bot May 20, 2026
94a474c
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
b6e9ac2
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
159c1ba
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
a87caba
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
5ba8f6c
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
b56a45f
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
4d29d08
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
3835418
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
4e7ba5a
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
78fee82
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
b31476b
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
231cf47
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
c813b80
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
e57f55c
chore: sync ci.yml with main to resolve merge conflict
don-petry May 20, 2026
3ac105a
chore: sync test.yml with main to resolve merge conflict
don-petry May 20, 2026
a82c836
fix(aw): remove 'name' field from issue-triage.md frontmatter (not al…
don-petry May 20, 2026
1802171
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
5da1d8f
fix(aw): correct issue-triage.md frontmatter for gh-aw schema (on:, s…
don-petry May 20, 2026
2608183
Merge branch 'main' into dev-lead/issue-232-20260519-0317
don-petry May 20, 2026
64c8594
fix(aw): use minimal valid gh-aw frontmatter for issue-triage.md
don-petry May 20, 2026
da22717
fix(aw): use correct add-labels schema in issue-triage.md
don-petry May 20, 2026
306fe58
fix(aw): remove stale frontmatter from body of issue-triage.md
don-petry May 20, 2026
5eac9c3
fix(aw): clean up issue-triage.md — remove duplicate frontmatter from…
don-petry May 20, 2026
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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,4 @@
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}

Check failure on line 135 in .github/workflows/ci.yml

View workflow job for this annotation

GitHub Actions / Lint

135:1 [empty-lines] too many blank lines (1 > 0)
82 changes: 82 additions & 0 deletions .github/workflows/issue-triage-runner.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Issue Triage
Comment thread
don-petry marked this conversation as resolved.

on:
issues:
types: [opened, reopened]

# Two-job pattern: triage runs with read-only issue access and produces a
# staged JSON artifact; apply runs with write access and calls safe-output.
jobs:
triage:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
issues: read
Comment thread
don-petry marked this conversation as resolved.
outputs:
result: ${{ steps.run.outputs.result }}
skip: ${{ steps.run.outputs.skip }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
don-petry marked this conversation as resolved.
with:
persist-credentials: false

- name: Install claude CLI
uses: anthropics/claude-code-action/setup@24492741e0ccfdef4c1d19da8e11e0f373d07494 # main
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

- name: Install Python dependencies
run: pip install "pyyaml==6.0.3" --only-binary :all: --quiet

Check failure on line 30 in .github/workflows/issue-triage-runner.yml

View workflow job for this annotation

GitHub Actions / Lint

30:60 syntax error: mapping values are not allowed here (syntax)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Run issue-triage (staged)
id: run
env:
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
ISSUE_LABELS: ${{ join(github.event.issue.labels.*.name, ',') }}
ISSUE_URL: ${{ github.event.issue.html_url }}
ISSUE_ACTION: ${{ github.event.action }}
GITHUB_REPOSITORY: ${{ github.repository }}
Comment thread
don-petry marked this conversation as resolved.
run: |
set -euo pipefail
result=$(bash scripts/aw.sh run issue-triage --staged)
skip=$(AW_RESULT="$result" python3 - <<'PYEOF'
import json, os
d = json.loads(os.environ['AW_RESULT'])
print('true' if d.get('skip') else 'false')
PYEOF
)
# Store encoded to survive multi-line JSON in GITHUB_OUTPUT
encoded=$(printf '%s\n' "$result" | base64 -w0)
echo "result=$encoded" >> "$GITHUB_OUTPUT"
echo "skip=$skip" >> "$GITHUB_OUTPUT"

apply:
needs: triage
if: needs.triage.outputs.skip != 'true'
runs-on: ubuntu-latest
timeout-minutes: 3
permissions:
contents: read
issues: write
Comment thread
don-petry marked this conversation as resolved.
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Install Python dependencies
run: pip install "pyyaml==6.0.3" --only-binary :all: --quiet

- name: Apply triage output (safe-output)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
TRIAGE_RESULT: ${{ needs.triage.outputs.result }}
run: |
result_file=$(mktemp)
printf '%s\n' "$TRIAGE_RESULT" | base64 -d > "$result_file"
bash scripts/aw.sh safe-output apply issue-triage "$result_file"
rm -f "$result_file"
76 changes: 76 additions & 0 deletions .github/workflows/issue-triage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
---
on:
issues:
types: [opened]
engine: claude-sonnet-4-6
permissions:
issues: read
safe-outputs:
add-labels:
allowed:
- bug
- enhancement
- documentation
- question
- needs-triage
- good-first-issue
- security
max: 3
---

# Issue Triage

You are triaging a new GitHub issue for the `${REPO}` repository.

## Context

- **Repository:** `${REPO}`
- **Issue:** [#${ISSUE_NUMBER}](${ISSUE_URL}): ${ISSUE_TITLE}
- **Labels already applied:** ${ISSUE_LABELS}
Comment thread
don-petry marked this conversation as resolved.

**Issue body:**

${ISSUE_BODY}

## Task

If the issue already has **2 or more labels**, output `{"skip": true}` and stop — do not classify, do not comment.
Comment thread
don-petry marked this conversation as resolved.

Otherwise:

1. **Classify** the issue into the best-fit category:
- `bug` — something is not working as expected
- `enhancement` — request for new or extended functionality
- `documentation` — unclear, missing, or incorrect docs
- `question` — user asking how to do something
- `security` — potential security vulnerability
- `good-first-issue` — simple enough for a first-time contributor

2. **Select ≤ 3 labels** from the allowed set:
`bug`, `enhancement`, `documentation`, `question`, `needs-triage`,
`good-first-issue`, `security`.
- Add `needs-triage` for bugs and ambiguous reports that need human review.
- Add `good-first-issue` only when the scope is clearly small and
self-contained.

3. **Write one welcoming comment** that:
- Opens warmly and thanks the contributor.
- Asks the **single most important** clarifying question for this issue type:
- Bug → steps to reproduce + expected vs actual behaviour
- Feature → use case / motivation
- Question → what docs they checked and where they got stuck
- Documentation → which page or section is unclear and what they expected
- Security → (do not ask for details publicly; ask them to use the
private security advisory channel)
- Does **not** ask multiple unrelated questions.
- Is concise (≤ 5 sentences).

## Output format

Output **exactly one** JSON object — no markdown fences, no preamble:

For a normal triage:
{"labels": ["label1", "label2"], "comment": "Your welcoming comment here."}

For a skip (issue already has 2+ labels):
{"skip": true}
99 changes: 99 additions & 0 deletions docs/aw/issue-triage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Issue Triage — Agentic Workflow

Part of the GitHub Agentic Workflows rollout (internal discussion: [petry-projects/.github-private#228](https://github.com/petry-projects/.github-private/discussions/228) — requires repo access).

## What it does

When a new issue is opened or reopened, this workflow:

1. Checks the existing label count. If the issue already has **2 or more labels** it exits silently (no-op).
2. Calls Claude (`claude-sonnet-4-6`) with the issue title and body to classify the issue.
3. Selects up to 3 labels from the allowed set and writes a single welcoming comment that asks the most relevant clarifying question.

All of this happens within 60 seconds of the issue being opened.

## Allowed labels

`bug`, `enhancement`, `documentation`, `question`, `needs-triage`, `good-first-issue`, `security`

## Files

| File | Purpose |
|------|---------|
| `.github/workflows/issue-triage.md` | Workflow definition (frontmatter + Claude prompt template) |
| `.github/workflows/issue-triage-runner.yml` | GitHub Actions runner (two-job: triage → apply) |
| `scripts/aw.sh` | `gh aw` CLI — compile, run, safe-output |
| `tests/aw/issue-triage/scenarios.md` | Scenario spec used to validate behaviour |

## Architecture

```
issues: opened/reopened
│
▼
triage job (permissions: issues: read)
│ bash scripts/aw.sh run issue-triage --staged
│ → Claude classifies, returns JSON
▼
apply job (permissions: issues: write)
│ bash scripts/aw.sh safe-output apply issue-triage result.json
│ → validates labels, posts comment, adds labels
▼
issue updated
```

The two-job split is deliberate: Claude runs with the minimum permission it
needs (read), and writes are applied by a separate job after the output has
been validated by `safe-output`.

## Validation

`gh aw compile` validates every `.md` workflow definition file. It checks:

- Required frontmatter fields: `name`, `trigger`, `engine`, `permissions`
- Engine is a known Claude model identifier
- `output` is `staged` or `live` (if present)
- Trigger is a non-empty event mapping
- Workflow body (instructions) is not empty

Run locally:

```bash
bash scripts/aw.sh compile .github/workflows/issue-triage.md
```

## Staged vs live mode

The `output` frontmatter field controls how results are applied:

| Value | Behaviour |
|-------|-----------|
| `staged` | `aw run` emits JSON to stdout; a separate `safe-output apply` step writes to GitHub |
| `live` (default when omitted) | `aw run` applies labels and posts the comment inline |

The runner workflow always uses the two-job pattern regardless of this setting,
so `safe-output` always validates before writing.

## Skip condition

If the issue already has **2 or more labels** when the workflow fires, `aw run`
returns `{"skip": true}` immediately and the apply job is skipped. No API
writes occur.

## Smoke-test procedure

To validate against the scenario spec before going live:

```bash
# Create a test issue in a non-critical repo and capture the number
ISSUE_NUMBER=<n> \
ISSUE_TITLE="Login button is broken" \
ISSUE_BODY="When I click nothing happens." \
ISSUE_LABELS="" \
ISSUE_URL="https://github.com/petry-projects/.github-private/issues/<n>" \
GITHUB_REPOSITORY="petry-projects/.github-private" \
bash scripts/aw.sh run issue-triage --staged
```

Compare the JSON output against the expected labels and comment style in
`tests/aw/issue-triage/scenarios.md`.
Loading
Loading