fix(automation): triage owns routing for every portal issue

The Report Issue button (older deployed build) stamps claude-queue at creation, so
triage skipped those issues and they went straight to auto-fix (e.g. #37, a large
localization feature that should be interactive).

Triage now claims a portal issue until it carries a new `triaged` marker (or is
in progress/done) — claude-queue is no longer a skip reason. On routing to
interactive it strips the stray claude-queue; on claude-queue it adds triaged.
Manual queue still works for NON-portal issues (triage never claims those).

Resilient regardless of which button build is deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hardik 2026-06-19 14:00:44 +05:30
parent 8a614878d2
commit aec6d2971f
2 changed files with 29 additions and 11 deletions

View file

@ -106,17 +106,23 @@ before a release tag deploys them to prod.
## Issue label lifecycle ## Issue label lifecycle
``` ```
portal ──(triage)──▶ claude-queue ─▶ claude-working ─▶ claude-pr | claude-failed portal ──(triage)──▶ triaged + claude-queue ─▶ claude-working ─▶ claude-pr | claude-failed
└────▶ interactive (stops here — handle with Claude interactively) └──── triaged + interactive (stops here — handle with Claude interactively)
``` ```
- A `portal` issue with no decision label yet is triaged once (`maxTriagePerRun` - **Triage owns routing for every `portal` issue.** Each untriaged portal issue is
per run). Triage adds `claude-queue` or `interactive` and posts a breakdown. triaged once (`maxTriagePerRun` per run); triage adds `triaged` plus `claude-queue`
or `interactive` and posts a breakdown. Triage skips an issue only once it carries
`triaged`, `interactive`, `claude-working`, `claude-pr`, or `claude-failed`.
- **`claude-queue` alone does NOT skip triage on a portal issue.** The Report Issue
button may stamp `claude-queue` at creation; triage still claims the issue and
decides routing (stripping the stray `claude-queue` if it routes to `interactive`).
This is why triage works even if an older button build is deployed.
- `claude-queue``claude-working``claude-pr` (PR opened) or `claude-failed`. - `claude-queue``claude-working``claude-pr` (PR opened) or `claude-failed`.
- To retry a failed issue, re-add `claude-queue`. - To retry a failed issue, re-add `claude-queue` (and remove `claude-failed`).
- To queue any manually-created issue for Claude (skipping triage), add - To queue a **non-portal** issue for Claude (skipping triage), add `claude-queue`
`claude-queue` directly. To force human handling, add `interactive`. directly — triage never claims issues without the `portal` label.
- Triage is skipped for issues that already carry any decision label. - To force a portal issue straight to fix, add `triaged` + `claude-queue` yourself.
## Releasing ## Releasing

View file

@ -145,12 +145,16 @@ if [ ! -d "$WORKDIR/.git" ]; then
git -C "$WORKDIR" config user.email "claude-autofix@pelagiamarine.com" git -C "$WORKDIR" config user.email "claude-autofix@pelagiamarine.com"
fi fi
DECISION_LABELS="claude-queue interactive claude-working claude-pr claude-failed" # Triage OWNS routing for every portal issue. It claims a portal issue until it has
# been triaged (`triaged`) or is already in progress/done. NOTE: claude-queue is
# deliberately NOT a skip reason — the Report Issue button may stamp claude-queue at
# creation, and triage must still decide claude-queue vs interactive itself.
TRIAGE_SKIP_LABELS="interactive claude-working claude-pr claude-failed triaged"
# ===================================================================== # =====================================================================
# Phase 1: triage new portal issues # Phase 1: triage new portal issues
# ===================================================================== # =====================================================================
dl_json=$(printf '%s\n' $DECISION_LABELS | jq -R . | jq -sc .) dl_json=$(printf '%s\n' $TRIAGE_SKIP_LABELS | jq -R . | jq -sc .)
to_triage=$(issues_by_label portal | jq -c --argjson dl "$dl_json" \ to_triage=$(issues_by_label portal | jq -c --argjson dl "$dl_json" \
'[ .[] | select((.labels|map(.name)) as $have | ($dl | any(. as $d | $have|index($d))) | not) ] | sort_by(.number)') '[ .[] | select((.labels|map(.name)) as $have | ($dl | any(. as $d | $have|index($d))) | not) ] | sort_by(.number)')
to_triage=$(printf '%s' "$to_triage" | jq -c ".[:$MAX_TRIAGE]") to_triage=$(printf '%s' "$to_triage" | jq -c ".[:$MAX_TRIAGE]")
@ -212,13 +216,21 @@ while [ "$t" -lt "$n_triage" ]; do
if [ -z "$label" ]; then if [ -z "$label" ]; then
log "Triage for #$num produced no valid decision; leaving for a human" log "Triage for #$num produced no valid decision; leaving for a human"
# Mark triaged + strip any button-stamped claude-queue so it is NOT auto-fixed.
set_labels "$num" "claude-queue" "triaged"
add_comment "$num" "$BOT_MARKER add_comment "$num" "$BOT_MARKER
[Claude triage] Could not auto-triage this issue. A human should review it and add either \`claude-queue\` or \`interactive\`." [Claude triage] Could not auto-triage this issue. A human should review it and add either \`claude-queue\` or \`interactive\`."
continue continue
fi fi
# Label FIRST so a comment failure cannot trigger a re-triage that double-posts. # Label FIRST so a comment failure cannot trigger a re-triage that double-posts.
add_labels "$num" "$label" # Mark `triaged` so triage won't re-claim it. For interactive, strip any
# claude-queue the Report Issue button may have stamped so the fix phase ignores it.
if [ "$label" = "interactive" ]; then
set_labels "$num" "claude-queue" "interactive triaged"
else
add_labels "$num" claude-queue triaged
fi
# No bot marker on the breakdown: it is genuine refined requirements and SHOULD # No bot marker on the breakdown: it is genuine refined requirements and SHOULD
# be fed to the fix stage (comments_block includes it). # be fed to the fix stage (comments_block includes it).
note=${breakdown:-"(no breakdown produced)"} note=${breakdown:-"(no breakdown produced)"}