Skip to content

[finding] release.yml: every main landing between the version-PR merge and the approval queues a new publish deployment, evicts the waiting one, and ships main's head instead of the version commit — ADR-0125 D1's premise does not hold #20613

Description

@hotlong

What the design says

ADR-0125 D1 (docs/adr/0125-release-approval-gate-replaces-the-typed-version.md):

gated on a single predicate … main's @objectstack/cli version is not on npm. That is true only just after a version PR merges, and false on all ~18 other daily landings, so no ordinary merge queues a deployment.

D1/D2 also say that merging the Version Packages PR is the decision to release, and that approving release authorises that decision.

What actually happens

The predicate stays true from the version-PR merge until the publish finishes. So every landing in that window queues its own Publish <v> to npm (awaiting approval) deployment. The publish job's concurrency group (release-publish-${{ github.ref }}, cancel-in-progress: false) keeps at most one pending job, so each new landing cancels the deployment that was waiting before it. The job checks out github.sha of its push, so whatever is approved ships main's head at that moment, not the version commit.

Observed on the 17.5.0 release (2026-09-29):

Release run head publish job
36533007563 3a89d459 (merge-queue batch: 8c87d26a version commit + 4 PRs) waited 06:48, cancelled 06:52
36533376091 7001918e waited 06:52, cancelled by the next landing
36536081716 0f6dcac5 waited 07:21, approved 07:40, this one ships 17.5.0

Resulting defects:

  1. 17.5.0 on npm is not the tree the maintainer decided to release. git log --first-parent 8c87d26a..0f6dcac5 lists 8 PRs that landed after the version commit and ship inside 17.5.0: 6e3aa75e, a093ce3e, 92fe0814 (breaking, feat(spec)!), 3a89d459, 7001918e, c96beb27, ba4648da, 0f6dcac5. Their 8 changesets are still unconsumed in .changeset/, so the 17.5.0 CHANGELOG omits them and 17.6.0 will announce them as new.
  2. The approval target moves. A maintainer who opens the approval link and is slower than the merge queue finds it cancelled. Approving becomes a race against the queue.
  3. Stale approval prompts survive. Run 34308599522 (2026-09-09, Publish 17.4.0 to npm (awaiting approval), head 2a5424a2) sat waiting for 20 days after 17.4.0 had shipped from another run. On 2026-09-29 it was approved by mistake in place of the 17.5.0 prompt. It was cancelled during Build, so nothing was published. This is the "approval noise" the release.yml header warns about, produced by the same file.

The same mechanism is documented for the bookkeeping lane in ADR-0125 D5 ("GitHub keeps at most one pending run per group … the intervening main pushes evict each other"). It was fixed there and not considered for the publish job's own group.

Proposed fix (for review)

  • Make the predicate "this push carries the version bump", not "main's version is absent from npm". In release-integrity, walk github.event.before..github.sha and find the first first-parent commit where packages/cli/package.json's version changes. Mark publish pending only when such a commit exists in this push's range and its version is absent from npm. Later landings then no longer queue deployments.
  • Publish the version commit, not the head. Pass that commit's sha as an output. The publish job checks it out (ref:) and the guard asserts it is an ancestor of main. The #6170 tripwire then runs against that sha.
  • Keep the repair lane (D4) as is, for a partial publish.
  • Stale prompts: have release-integrity (or a small scheduled sweep) report any release deployment still waiting for a version already on npm, so it can be rejected rather than left as a lookalike prompt.
  • Operational workaround until then: pause the merge queue between merging the Version Packages PR and approving release.

This touches .github/workflows/release.yml (not a governed surface) and needs an amendment to ADR-0125 D1 (docs/adr/**, governed surface, Tier H). The ADR change is the maintainer's call.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:specpriority:p1High: required for production / M2tooling

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions