Fix the npm read-after-write race I introduced in the publish gate - #196
Conversation
#194 made each registry publish gate on "does npm already serve this version", to stop a mid-run failure silently stranding four registries. It reintroduced the same failure through a different door: npm registry READS are eventually consistent, so `npm view @smooai/logger@4.5.4` moments after a successful publish still 404s. The gate concluded "not released", skipped PyPI, crates.io, the Go tag and NuGet, and the run went green — leaving 4.5.4 on npm alone. Two independent signals now count as released, because neither alone is enough: - changesets published it in THIS run (steps.changesets.outputs.published). Authoritative the instant it happens, no registry read involved. This is the normal path and the one the race broke. - npm already serves it. The backfill path, which is what lets a re-run catch up registries an earlier run left behind — the whole point of #194. Only when neither holds do the four steps skip, which is correct: that is a run that merely opened the version PR, where package.json is bumped to something nothing has published and the workspace sits on an unmerged branch. Deliberately NO changeset. With package.json staying at 4.5.4, merging this takes the backfill path and ships 4.5.4 to the four registries it is missing from. A changeset would bump to 4.5.5 and strand 4.5.4 permanently. Verified all three states by hand: published-this-run -> proceed even while npm 404s; npm-serves-it -> proceed; unreleased 9.9.9 -> skip all four. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC
|
|
Correction to the causal claim in #194, isolated after that PR merged — the observation was right, the explanation was wrong, and the risk is higher than I said. #194 stated Go "refuses to cache" Isolated in a scratch module:
The trigger is the module boundary, not the package boundary. Logger's Go module root is What actually saved it: the corpus test builds a real logger, which writes So |
My bug, from #194, caught in production one release later
#194 replaced
if: steps.changesets.outputs.published == 'true'with "does npm already serve this version", so a mid-run failure could no longer silently strand four registries. It reintroduced the same failure through a different door.npm registry reads are eventually consistent.
npm view @smooai/logger@4.5.4moments after a successful publish still 404s. So on release run 32407821367:…all four steps skipped and the run went green.
npm view @smooai/logger versionreturns4.5.4now, but the damage is done:Exactly the class of defect #194 set out to kill, caused by the fix for it. Reporting it plainly because a green run that publishes nothing is precisely the thing nobody notices.
Fix
Two independent signals count as "released", because neither alone is sufficient:
steps.changesets.outputs.published == 'true'Only when neither holds do the four steps skip — correctly, because that is a run that merely opened the version PR, where
package.jsonis bumped to something nothing has published and the workspace is sitting on an unmerged branch. (That case is real; it's why #194 gained the npm gate at all, and it worked: run 32407587838 loggednpm does not have 4.5.4 yetand correctly published nothing.)Deliberately no changeset
package.jsonstays at 4.5.4, so merging this takes the backfill path and ships 4.5.4 to the four registries it's missing from. A changeset would bump to 4.5.5 and strand 4.5.4 permanently.This also serves as the live test of the backfill path against a real drift.
Verification
All three release states, by hand:
pnpm format:checkclean;check-versions: OK (all manifests at 4.5.4).🤖 Generated with Claude Code
https://claude.ai/code/session_0152bbE1veqfG1SVJdyLCBxC