feat: rescue and suggest modes for the upgrade pipeline - #22
Conversation
Gate derives mode server-side (never from labels): upgrade (classic majors), suggest (github-actions majors - research + suggestion blocks, the agent NEVER pushes workflow files), rescue (non-majors with red head CI and a positively green base, tri-state comparison incl. legacy statuses, auto-merge verifiably disarmed). auto-merge.yml: AI-lifecycle guard so a rescue push (synchronize) can never re-approve/re-arm; actions MAJORS now queue for suggest. Rescue: deterministic failing-log prefetch (agent never needs gh), no_changes_needed invalid, disarm-verify recheck at push time, completion never re-arms auto-merge - a human merges. Suggest: COMMENT review with tier-tagged suggestion blocks, validated deterministically (paths, spans, sizes, fence-breakout), plain-comment fallback when anchors are rejected; terminal label ai-suggested. Claude-Session: https://claude.ai/code/session_01W2sTkntuVGqxtoZcANZjSL
H1: gate + caller gain statuses:read; snapshot uses the paginated raw statuses endpoint (combined truncates at 30). H2: the gate never adopts an unvalidated head - movement requeues. H3: ci-watch includes legacy statuses, requires the originally failing keys green on the final head (rescue), and never labels a head it did not verify. M6: round 2 is disarm-verified for rescue and its prompt is mode-aware. M7: fence-breakout validation covers rationale (plus length cap). M8: suggest mode requeues on head movement and parks when no delivery path succeeded - ai-suggested always means something was posted. M9: terminal labels are mutually exclusive on every transition. L10: ci_diagnosis rendered in the sticky summary. Claude-Session: https://claude.ai/code/session_01W2sTkntuVGqxtoZcANZjSL
Fail closed on an unreadable live head; ci-watch rejects push2=error (a refused round-2 push must not validate round 1 as complete); moved suggest runs requeue instead of parking; terminal labels cleared on every transition incl. blocked/suggested; ci_diagnosis required in the schema; final check-run listing paginated. Claude-Session: https://claude.ai/code/session_01W2sTkntuVGqxtoZcANZjSL
There was a problem hiding this comment.
🟡 Changes recommended
The new rescue check/status comparison logic has jq grouping/deduplication issues that can produce incorrect “latest check” results, impacting rescue gating correctness.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR extends the Dependabot upgrade automation pipeline with three server-derived execution modes—upgrade, rescue, and suggest—and updates the auto-merge workflow to respect the AI lifecycle so that rescue/suggest flows cannot accidentally self-approve or auto-merge.
Changes:
- Add rescue mode gating: compare head vs base checks/statuses (including legacy commit statuses) and enforce “auto-merge must be disarmed” before any rescue push.
- Add suggest mode for GitHub Actions majors: post validated suggestion blocks as a COMMENT review instead of pushing workflow changes.
- Add head-movement detection and fail-closed requeue/parking behavior when the validated SHA no longer matches live head.
File summaries
| File | Description |
|---|---|
| .github/workflows/dependabot-upgrade.yml | Implements mode selection (upgrade/rescue/suggest), rescue check/status comparison, suggest-mode review posting, and stronger head-movement / auto-merge-disarm invariants. |
| .github/workflows/auto-merge.yml | Adds an AI-lifecycle label guard to prevent approvals/auto-merge when an AI pipeline state is active; queues majors (including actions majors) appropriately. |
Review details
Suppressed comments (1)
.github/workflows/dependabot-upgrade.yml:1478
- In
ci-watchrescue verification,keymapis built withfrom_entriesdirectly over all check-runs. A commit can have multiple check-runs with the same (app id, name) due to re-runs; without de-duplicating to the latest completed attempt, the map can pick an arbitrary run and incorrectly mark a rescued check as not green (or green) even when the latest attempt differs.
keymap=$(jq -n --argjson r "$runs" --argjson s "$sts" '
(($r | map({key: (((.app.id // -1)|tostring) + ":" + .name), value: (.conclusion // "unknown")}))
+ ($s | map({key: ("-1:" + .context), value: (if .state == "success" then "success" else "failure" end)})))
| from_entries')
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
The rescue and suggest additions include failure-handling paths that can hard-fail or misbehave under API/malformed-output conditions, and should be made deterministically fail-closed as noted in inline comments.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
| armed=$(gh api "repos/$BASE_REPO/pulls/$PR_NUMBER" --jq '.auto_merge != null' || echo true) | ||
| [ "$armed" = "false" ] || block "auto-merge is (or may be) armed on this PR — rescue requires it verifiably disarmed first" | ||
| own_suite=$(gh api "repos/$BASE_REPO/actions/runs/$GITHUB_RUN_ID" --jq .check_suite_id || echo 0) | ||
| snapshot() { # $1 = sha -> JSON {pending: bool, checks: {"app:name": conclusion}} |
| bad=$(jq -r --argjson files "$files" '[(.suggestions // [])[] | . as $sg | ||
| | select( | ||
| (($sg.path | test("^\\.github/(workflows/[^/]+\\.ya?ml|actions/[^/]+/action\\.ya?ml)$")) | not) | ||
| or (($files | index($sg.path)) == null) | ||
| or ($sg.start_line < 1) or ($sg.end_line < $sg.start_line) | ||
| or (($sg.end_line - $sg.start_line) > 60) | ||
| or (($sg.replacement | length) > 6000) | ||
| or ($sg.replacement | test("```|~~~")) | ||
| or (($sg.rationale | length) > 400) | ||
| or ($sg.rationale | test("```|~~~"))) | ||
| | $sg.path + ":" + ($sg.start_line|tostring)] | join(" ")' "$S") |
Summary
no_changes_neededinvalid, completion never re-arms auto-merge, ci-watch requires the originally failing checks green on a verified live head), and suggest (GitHub-Actions majors: changelog research posted as tier-tagged suggestion blocks in a COMMENT review — the agent never pushes workflow files; deterministic validation covers paths, anchors, spans, sizes, and fence-breakout in both replacement and rationale; delivery is proven or the PR parks).auto-merge.yml: AI-lifecycle guard (a rescue push can never re-approve/re-arm on synchronize); actions majors now queue for suggest instead of dead-ending in a comment.Test plan