diff --git a/.github/prompts/issue-fix.md b/.github/prompts/issue-fix.md index 1fc0609fdb..fb16793984 100644 --- a/.github/prompts/issue-fix.md +++ b/.github/prompts/issue-fix.md @@ -24,6 +24,22 @@ anything that violates them gets blocked and flagged. 5. If the fix requires changing more than roughly 60 lines, or touching more than three files, stop. Post the analysis, open no pull request, and say plainly that the change is too large for an unattended fix. +6. **Relabel every issue before moving to the next one.** This sweep runs + daily over the same `ai-triage`-labelled backlog. An issue you have + already finished with must drop that label immediately — otherwise + tomorrow's sweep re-selects it, opens a second competing branch, and + burns another full read-the-source-and-fix pass on work that is already + done. Do this as your last action on the issue, right after commenting, + not batched at the end: `gh issue edit --remove-label ai-triage + --add-label `, where `` is exactly one of: + - `ai:fixed` — you opened a draft pull request for it. + - `ai:upstream` — you reversed tier 1's call and the fault is upstream, + so no pull request. + - `ai:needs-human` — too large for an unattended fix (rule 5), or you + found no fix and are reporting what you ruled out. + A workflow step checks this after you finish and force-corrects to + `ai:needs-human` for anything still carrying `ai-triage` — treat that as + a bug in your run, not a safety net to lean on. ## Per add-on, do this in order @@ -52,11 +68,13 @@ change. Add a `CHANGELOG.md` entry in the add-on's existing format. and line, what the change does, how you verified it (or an explicit statement that you could not verify it), and `Closes #`. -**6. Comment on the issue.** Root cause, the fix in one or two sentences, and -the pull request link. Plain language — the reader is a Home Assistant user, -not a Go developer. If you found no fix, say what you ruled out and what you -would need to go further. Close with a note that this is automated analysis -pending Alex's review. +**6. Comment on the issue, then relabel it (hard limit 6).** Root cause, the +fix in one or two sentences, and the pull request link. Plain language — the +reader is a Home Assistant user, not a Go developer. If you found no fix, say +what you ruled out and what you would need to go further. Close with a note +that this is automated analysis pending Alex's review. Then remove +`ai-triage` and add the one replacement label that matches the outcome, +before starting the next issue. ## Meta-findings diff --git a/.github/workflows/daily_ai_fix.yaml b/.github/workflows/daily_ai_fix.yaml index cde49ea6a9..3a47b3ab4e 100644 --- a/.github/workflows/daily_ai_fix.yaml +++ b/.github/workflows/daily_ai_fix.yaml @@ -77,6 +77,20 @@ jobs: git config user.name "claude-ai-fix[bot]" git config user.email "claude-ai-fix[bot]@users.noreply.github.com" + # 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 + 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 + gh label create "$l" --repo "$REPO" --color ededed --force >/dev/null 2>&1 || true + done + - name: Analyse and fix if: steps.batch.outputs.count != '0' uses: anthropics/claude-code-action@v1 @@ -94,6 +108,32 @@ jobs: env: GH_TOKEN: ${{ secrets.AI_PR_TOKEN }} + # Belt and braces. issue-fix.md instructs Claude to drop the ai-triage + # label off every issue it finishes with (hard limit 6), so tomorrow's + # sweep never re-selects and re-spends a full read-and-fix pass on + # work that's already done. Enforce it here in case a turn or timeout + # budget runs out before the relabel step of the last issue or two. + - name: Guard against repeat processing + if: always() && steps.batch.outputs.count != '0' + env: + GH_TOKEN: ${{ secrets.AI_PR_TOKEN }} + REPO: ${{ github.repository }} + run: | + set -euo pipefail + mapfile -t ISSUES < <(jq -r '.[].number' /tmp/ai-fix/batch.json) + for n in "${ISSUES[@]}"; do + STILL=$(gh issue view "$n" --repo "$REPO" --json labels \ + --jq '[.labels[].name] | index("ai-triage") != null' 2>/dev/null) || { + echo "::warning::could not re-check issue #$n (deleted or transferred?), skipping" + continue + } + if [ "$STILL" = "true" ]; then + echo "::warning::issue #$n still carries ai-triage after the sweep, forcing it out of tomorrow's batch" + gh issue edit "$n" --repo "$REPO" \ + --remove-label ai-triage --add-label ai:needs-human + fi + done + # Belt and braces. The prompt forbids these paths; this enforces it. - name: Guard forbidden paths if: always() && steps.batch.outputs.count != '0'