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
11 changes: 8 additions & 3 deletions .github/workflows/daily_ai_fix.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ permissions:
contents: write
issues: write
pull-requests: write
id-token: write # claude-code-action fetches a GitHub OIDC token to auth the OAuth flow

concurrency:
group: ai-fix-sweep
Expand Down Expand Up @@ -87,15 +88,19 @@ jobs:

# Created up front so issue-fix.md's per-issue relabel never has to
# improvise a color or retry a "label does not exist" error — that's a
# wasted turn multiplied by every issue in the batch.
- name: Ensure relabel targets exist
# wasted turn multiplied by every issue in the batch. Also covers
# ai:blocked, which the forbidden-paths guard applies later in this
# same job: with set -euo pipefail, `gh pr edit --add-label` on a
# label that doesn't exist yet fails and aborts that step's loop
# entirely, silently skipping every remaining PR behind it.
- name: Ensure control labels exist
if: steps.batch.outputs.count != '0'
env:
GH_TOKEN: ${{ secrets.AI_PR_TOKEN }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
for l in ai:fixed ai:upstream ai:needs-human; do
for l in ai:fixed ai:upstream ai:needs-human ai:blocked; do
gh label create "$l" --repo "$REPO" --color ededed --force >/dev/null 2>&1 || true
done

Expand Down
74 changes: 56 additions & 18 deletions .github/workflows/on_issues_ai_triage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,33 @@ name: AI issue triage
on:
issues:
types: [opened]
workflow_dispatch:
inputs:
issue:
description: "Issue number to (re-)triage manually"
required: true

permissions:
contents: read
issues: write
id-token: write # claude-code-action fetches a GitHub OIDC token to auth the OAuth flow

concurrency:
group: ai-triage-${{ github.event.issue.number }}
group: ai-triage-${{ github.event.issue.number || inputs.issue }}
cancel-in-progress: false

env:
MAINTAINER: alexbelgium
# Leave "true" for the first couple of weeks. The verdict is printed in the
# job log and nothing is written to the issue. Flip when it looks right.
DRY_RUN: "true"

jobs:
classify:
# Manual dispatch is a deliberate override: skip the auto-trigger guards
# (don't self-triage the maintainer's own issues; honour the no-ai
# opt-out) that only make sense for the fire-on-every-open path.
if: >-
github.event.issue.user.login != 'alexbelgium' &&
!contains(github.event.issue.labels.*.name, 'no-ai')
github.event_name == 'workflow_dispatch' ||
(github.event.issue.user.login != 'alexbelgium' &&
!contains(github.event.issue.labels.*.name, 'no-ai'))
runs-on: ubuntu-latest
timeout-minutes: 15
environment: CR_PAT
Expand All @@ -43,7 +50,10 @@ jobs:
# Both workflows fire on the same issues.opened event and race. The
# submitter ping completes in 6-11s of job time across recent runs; 60s
# leaves a generous margin for runner-queue skew between the two jobs.
# A manual dispatch runs against an existing issue whose ping (if any)
# landed long ago, so there is nothing to wait for.
- name: Wait for ping_submitter
if: github.event_name == 'issues'
run: sleep 60

- name: Checkout tooling
Expand All @@ -59,7 +69,7 @@ jobs:
- name: Build context bundle
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
ISSUE_NUMBER: ${{ github.event.issue.number || inputs.issue }}
REPO: ${{ github.repository }}
run: bash .github/scripts/ai_triage_context.sh

Expand All @@ -85,7 +95,7 @@ jobs:
- name: Apply verdict
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE: ${{ github.event.issue.number }}
ISSUE: ${{ github.event.issue.number || inputs.issue }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
Expand All @@ -107,31 +117,59 @@ jobs:
# of ai-triage).
mapfile -t LABELS < <(jq -r '.labels[]? // empty' "$F" | grep -vE '^ai[:-]' || true)

# Someone already owns this one: ping_submitter did its job.
# Someone already owns this one: ping_submitter did its job. Best-
# effort clear of a manual re-triage's stale control labels (e.g. a
# prior addon-bug run) — nothing to do if they were never set.
if [ "$VERDICT" = "owned" ]; then
echo "issue already has an owner, nothing to do"; exit 0
echo "issue already has an owner, nothing to do"
gh issue edit "$ISSUE" --repo "$REPO" \
--remove-label=ai-triage --remove-label=ai:classified --remove-label=ai:needs-human \
>/dev/null 2>&1 || true
exit 0
fi

# Low confidence never speaks. It just flags for a human.
if [ "$CONF" = "low" ]; then
LABELS=("ai:needs-human"); COMMENT=""
fi

[ "$VERDICT" = "addon-bug" ] && LABELS+=("ai-triage")
LABELS+=("ai:classified")

if [ "${DRY_RUN:-true}" = "true" ]; then
echo "DRY_RUN: would apply labels: ${LABELS[*]}"
echo "DRY_RUN: would post comment:"; printf '%s\n' "$COMMENT"
exit 0
# ai-triage is the tier-2 trigger, so it must never be added to a
# low-confidence verdict — Rule 2 of issue-classify.md says an
# uncertain addon/upstream call should only flag a human, not enter
# the unattended fix pass. (Above, low confidence already reset
# LABELS to ai:needs-human; this guard keeps ai-triage from being
# appended right back.)
if [ "$VERDICT" = "addon-bug" ] && [ "$CONF" != "low" ]; then
LABELS+=("ai-triage")
fi
LABELS+=("ai:classified")
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# No --force: an existing label (e.g. a model-supplied cosmetic
# "bug") must be left as-is. --force would update it, recoloring
# every such label to ededed as a side effect of triage. Without it,
# create fails harmlessly on labels that already exist (|| true),
# and still creates the workflow-owned ones the first time.
for l in "${LABELS[@]}"; do
gh label create "$l" --repo "$REPO" --color ededed --force >/dev/null 2>&1 || true
gh label create "$l" --repo "$REPO" --color ededed >/dev/null 2>&1 || true
done
gh issue edit "$ISSUE" --repo "$REPO" \
"${LABELS[@]/#/--add-label=}"

# Manual re-triage can flip the verdict (e.g. a prior addon-bug
# re-run now comes back needs-info/upstream-bug): clear whichever
# of tier 1's own control labels this run did NOT re-apply, so a
# stale ai-triage doesn't keep the issue in tomorrow's fix sweep.
# Separate, best-effort call — must not block the add above.
declare -A FRESH=()
for l in "${LABELS[@]}"; do FRESH["$l"]=1; done
STALE=()
for l in ai-triage ai:classified ai:needs-human; do
[ -z "${FRESH[$l]:-}" ] && STALE+=("$l")
done
if [ "${#STALE[@]}" -gt 0 ]; then
gh issue edit "$ISSUE" --repo "$REPO" "${STALE[@]/#/--remove-label=}" >/dev/null 2>&1 || true
fi

if [ -n "$COMMENT" ]; then
{
printf '%s\n\n' "$COMMENT"
Expand Down