Commit Graph

2 Commits

Author SHA1 Message Date
Tom Boucher
9181c77df5 fix(#2515): auto-merge the release/hotfix -> main PR when clean (#2516)
The finalize job created the release/hotfix -> main merge-back PR but
never merged it, so every release and hotfix needed a manual merge click
on main. The opposite direction (main -> next) is already admin-merged by
auto-backmerge.yml; this direction was the asymmetric manual step where
the recurring divergence got hand-reconciled.

Adds a finalize step (after "Verify publish", so main only absorbs a
confirmed-published release) that finds the open merge-back PR, polls
until GitHub settles its mergeability, and admin-merges it with a merge
commit ONLY when MERGEABLE. A CONFLICTING PR is left open for manual
resolution rather than force-merged. Non-fatal (continue-on-error): the
tag + npm publish already happened, so a merge-back that can't complete
(org PR policy, token) must not fail the release.

With the main-is-ancestor-of-next invariant restored (#2504), this merge
is clean every release -- verified: a simulated 1.8.1 hotfix merges to
main producing [1.8.1]->[1.8.0]->[1.7.0]->[1.6.1] and version 1.8.1 with
no conflict, and main's hardened auto-backmerge.yml survives the merge.

Guarded by three assertions in release-backmerge-invariants.test.cjs
(step present + admin-merge, gates on MERGEABLE, continue-on-error);
verified they fail when --admin or the MERGEABLE guard or the
continue-on-error is removed.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 23:23:45 -04:00
Tom Boucher
bcdfd21c61 fix(#2504): make auto-backmerge survive a broken workflow copy + gate the invariants (#2506)
The main->next auto-backmerge fails after nearly every release, leaving
main not an ancestor of next, so the following release->main merge-back
conflicts. Root cause is a copy-shuffling loop: auto-backmerge.yml must be
identical on main and next, but `-s ours` (main->next) and the release-tree
merge-back (release->main) each overwrite one copy wholesale, so a fix
applied to one copy is repeatedly overwritten by the copy that lacks it.
The build:lib step proves it: added to main (329233fc8), overwritten by
the 1.7.0 merge-back, re-added to next (#2281), never on old-main -> the
1.7.0 backmerge ran on main's broken copy and failed at "Sync next's
version" (npm version -> gen-capability-registry needs the gitignored
capability-ledger from build:lib -> absent -> step fails -> "Open PR"
skipped -> no PR -> main never becomes an ancestor of next).

Two-part durable fix:

1. Blast-radius containment: mark the version-sync steps continue-on-error.
   The job's load-bearing purpose is opening + admin-merging the back-merge
   PR (the ancestry that keeps release->main clean). A version-sync failure
   (missing build:lib after a copy regression, or any npm-version lifecycle
   hiccup) can no longer abort that PR. A sync failure now costs only a
   stale next version, trivially re-synced -- never a broken back-merge.

2. Required-steps gate: tests/release-backmerge-invariants.test.cjs parses
   the workflow YAML and asserts build:lib runs before the version-sync,
   both steps are continue-on-error, the ancestry steps exist, and the
   finalize timeout is >= 30 (sibling #2281 regression). It runs on every
   branch, so a PR shipping a fix-less copy fails at PR time instead of at
   release time -- which is exactly what #1855/#1928/#1990 did undetected.

Verified the test fails on both regression modes (continue-on-error removed;
build:lib step removed) and passes on the fixed workflow.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 21:27:29 -04:00