fix(recipe-upgrade): stop auto-closing superseded/unrelated open PRs

Per operator: opening a new upgrade PR should stack ON TOP of any other
still-open PRs, not close them. Only PRs already merged into upstream
main are closed (merging them is a no-op). This prevents the phase-7
incident where an unrelated open ghost PR was auto-closed as 'superseded'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
autonomic-bot
2026-06-02 00:07:05 +00:00
parent 2304628375
commit 19fda8d2b8
2 changed files with 13 additions and 10 deletions

View File

@ -76,9 +76,10 @@ ssh cc-ci "GITEA_USERNAME='$GITEA_USERNAME' GITEA_PASSWORD='$GITEA_PASSWORD' GIT
< /srv/cc-ci/.claude/skills/recipe-upgrade/open-recipe-pr.sh < /srv/cc-ci/.claude/skills/recipe-upgrade/open-recipe-pr.sh
``` ```
This (a) re-syncs mirror `main` to true upstream main, (b) **closes any open PR already merged This (a) re-syncs mirror `main` to true upstream main, (b) **closes any open PR already merged
upstream**, (c) **closes any other still-open PR for this recipe as superseded**, then pushes the upstream** (merging it would be a no-op), then pushes the upgrade branch and opens the new PR
upgrade branch and opens the new PR **in its place** (so the diff is exactly this upgrade, against the **alongside** any other still-open PRs — it does **NOT** close superseded/unrelated open PRs; the
real current upstream main). Capture the `PR_URL`. Optionally export `RECIPE_PR_BODY` first (image-tag operator decides which to merge/close. The new PR's diff is still exactly this upgrade against the
real current upstream main. Capture the `PR_URL`. Optionally export `RECIPE_PR_BODY` first (image-tag
table + the planned operator-action notes). Re-running with the same target version updates the table + the planned operator-action notes). Re-running with the same target version updates the
existing same-branch PR rather than duplicating it. existing same-branch PR rather than duplicating it.
@ -150,8 +151,8 @@ Always state explicitly that **nothing was merged** — the PR(s) await operator
explanatory PR **comment**, never a test edit. Only `--with-tests` opens a cc-ci test PR. The weekly explanatory PR **comment**, never a test edit. Only `--with-tests` opens a cc-ci test PR. The weekly
`/upgrade-all` cron always runs the **default** — it never auto-updates tests. `/upgrade-all` cron always runs the **default** — it never auto-updates tests.
- **Mirror reflects reality.** Each run force-syncs the `recipe-maintainers/<recipe>` mirror `main` to - **Mirror reflects reality.** Each run force-syncs the `recipe-maintainers/<recipe>` mirror `main` to
true upstream main, closes open PRs already merged upstream, and replaces any superseded open PR with true upstream main and closes open PRs already merged upstream. It does NOT close superseded/unrelated
the new one — so an open mirror PR always means "genuinely still open against current upstream main". open PRs — a new upgrade PR is opened alongside them and the operator decides which to merge/close.
- **Prefer a recipe-only PR.** Only open a cc-ci test PR (under `--with-tests`) when the upgrade is - **Prefer a recipe-only PR.** Only open a cc-ci test PR (under `--with-tests`) when the upgrade is
correct but a test is genuinely stale — never to paper over a real upgrade regression. correct but a test is genuinely stale — never to paper over a real upgrade regression.
- **Never weaken a test**; **bounded** changes (the upgrade + minimal test update, not rewrites). - **Never weaken a test**; **bounded** changes (the upgrade + minimal test update, not rewrites).

View File

@ -19,8 +19,10 @@
# never reflected it. # never reflected it.
# #
# Default mode (open a PR — preconditions: HEAD has the upgrade commit(s)): # Default mode (open a PR — preconditions: HEAD has the upgrade commit(s)):
# - Additionally closes any OTHER still-open PR for this recipe (superseded), # - Pushes the upgrade branch and opens the new PR ALONGSIDE any other still-open
# then pushes the upgrade branch and opens the new PR IN ITS PLACE. # PRs for this recipe. It does NOT close superseded/unrelated open PRs — stacking
# a fresh PR on top of the open ones is intentional; the operator decides which to
# merge/close. (Only PRs already merged into upstream main are closed — see above.)
# #
# --reconcile-only: just sync main + close merged-upstream PRs; no push, no new # --reconcile-only: just sync main + close merged-upstream PRs; no push, no new
# PR (used when a recipe is up to date but its mirror may be stale). # PR (used when a recipe is up to date but its mirror may be stale).
@ -120,10 +122,10 @@ while IFS=$'\t' read -r IDX HEAD_REF; do
fi fi
if [ "${merged}" = "1" ]; then if [ "${merged}" = "1" ]; then
close_pr "${IDX}" "its changes are already in upstream main (merged upstream); mirror main re-synced" close_pr "${IDX}" "its changes are already in upstream main (merged upstream); mirror main re-synced"
elif [ -n "${BRANCH}" ]; then
close_pr "${IDX}" "superseded by ${BRANCH} (fresh upgrade against current upstream main)"
else else
echo " • PR #${IDX} (${HEAD_REF}) still open vs upstream main — left as-is (reconcile-only)" # Do NOT auto-close superseded/unrelated open PRs. Open the new PR alongside them and
# let the operator decide which to merge/close. Only merged-upstream PRs are closed above.
echo " • PR #${IDX} (${HEAD_REF}) still open vs upstream main — leaving open (operator decides)"
fi fi
done < <(echo "${OPEN_PRS}" | jq -r '.[] | [(.number | tostring), .head.ref] | @tsv' 2>/dev/null || true) done < <(echo "${OPEN_PRS}" | jq -r '.[] | [(.number | tostring), .head.ref] | @tsv' 2>/dev/null || true)