Uh oh!
There was an error while loading. Please reload this page.
fix(publish-smoke): derive the tarball pin set from the publishable population, not a by-name exclusion - #11257
Merged
Merged
Conversation
…opulation, not a scope/exclusion The packed-tarball smoke built its pin set as "publishable MINUS a hand-written exclusion", and the exclusion held `create-objectstack` on the rationale that no @objectstack/* manifest depends on it. That rationale expired when @objectstack/cli took a dependency on the scaffolder: the unscoped name was neither packed nor pinned, pnpm fell back to the registry, and the smoke died on ERR_PNPM_NO_MATCHING_VERSION for a version that by definition is not published yet — a deterministic false red on every release candidate. - publish-smoke-pack.mjs: the set is `private !== true`, full stop. No scope filter, no exclusion, no hand list. `assertPinSetTotal` re-checks pin set == publishable set in BOTH directions at the derivation site and names the offending packages, so a future exclusion cannot reopen the hole silently. - publish-smoke.sh: the registry-leak assertion drove off a `@objectstack/*` grep, which made it blind in exactly the case it existed to catch. It now drives off the override map's own names, scoped and unscoped alike. - A --self-test (unscoped package survives derivation; both directions of the equality assertion) wired as `check:publish-smoke-pin` in lint.yml, so a regression reddens on the PR that causes it rather than on a release run. Co-Authored-By: Claude <noreply@anthropic.com>
…lockfile sections A name is keyed in both `packages:` and `snapshots:`, so counting lines listed every offender twice and inflated the tarball-resolved total. Co-Authored-By: Claude <noreply@anthropic.com>
os-zhuang
commented
Aug 23, 2026
ContributorAuthor
ACCEPT — session 全量 diff 复核(5 文件 +230/−25,head
落地:flip ready + auto-merge。合并后按 PR 正文「For the release operator」节操作。 Generated by Claude Code |
os-zhuang
marked this pull request as ready for review
August 23, 2026 06:58
os-zhuang
enabled auto-merge
August 23, 2026 06:58
This was referenced Aug 23, 2026
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Aug 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#11253
The packed-tarball smoke never packed or pinned
create-objectstack, so the 17.2.0 release candidate's own smoke died on a version that by definition does not exist yet. Failed run: 32622283383, jobPacked-tarball smoke (release candidate).The mechanism — one correction to the card's reading
The card attributes the hole to an
@objectstack/*scope filter in the pin-set construction. The pin set was not built from a scope glob:publish-smoke-pack.mjsalready derived fromprivate !== true. The hole was a hand-written by-name exclusion sitting on top of that derivation:The rationale was true when written and expired when
@objectstack/clitook a dependency on the scaffolder. Its general shape is the real bill: whether a workspace package is reachable from some other manifest is a fact about the dependency graph at one moment, and it is not a question this script gets to ask. Publishable is the question.A scope filter did exist, in the second place the card told me to look — the registry-leak assertion in
publish-smoke.sh, which drove off a'@objectstack/[^'@]+@[0-9]grep. That made the guard blind in exactly the case it existed to catch, so the run gotok — 0 leaksand then died 200 lines later inside pnpm. A guard whose alphabet is narrower than the set it guards is not a guard.Source of truth: the workspace, not the
fixedgroupBoth candidates enumerate the same 69 names, and they are provably equivalent right now — measured on this branch:
They are equivalent because
scripts/check-changeset-fixed.mjsalready asserts it in both directions (a public package missing from the group reddens; a group name that no longer exists in the workspace reddens). That is exactly why the workspace is the right source and the group is not: the group is a derived declaration that a gate keeps honest. Deriving the pin set from it would mean reading a copy — correct today, and correct only for as long as a second gate keeps it so. The workspace is the thing itself.Before / after
Same command, same tree, before and after the change:
create-objectstackin setcreate-objectstackis the only unscoped publishable package in the workspace — which is why no fixture whose names all begin with@could have caught this.What changed
scripts/publish-smoke-pack.mjs— the set isprivate !== true, full stop: no scope filter, no exclusion, no hand list.assertPinSetTotalre-checks pin set == publishable set in both directions at the derivation site and reports offenders by name (both halves matter: a missing pin silently tests published code, a surplus pin tests a resolution no real user can reproduce).scripts/publish-smoke.sh— the registry-leak assertion now drives off the override map's own names, scoped and unscoped alike, instead of a scope grep. De-duplicated across the lockfile'spackages:/snapshots:sections.--self-testwired ascheck:publish-smoke-pininlint.yml. The assertion it pins lives on the release path, which runs only during a release — without this step a regression in it would again be discovered by a release candidate rather than by the PR that caused it. Pure functions, synthetic fixtures, no pnpm/workspace/network, ~0.05s.publish-smoke.yml/publish-smoke.shthat described the pin set as@objectstack/*— the self-description the card quoted — corrected to match what the code does.Verification
The full smoke ran locally, green, in pack mode — CI does not have to take over any leg of it:
Isolated install leg — the defect and the fix, with the real tarballs. Two throwaway projects outside the workspace, same tarball set, differing only in whether
create-objectstackis pinned:At 17.1.0 that registry resolution succeeds and silently smokes published code; at 17.2.0 it is the hard
ERR_PNPM_NO_MATCHING_VERSIONabove. Same hole, two faces. The packed@objectstack/climanifest confirms the trigger —pnpm packrewritesworkspace:*to a concrete version, so the published manifest really does demand the unpublished one:The guard half, both assertions run against that same leaking lockfile:
Self-test, ablation-checked. Restoring the by-name exclusion turns case 1 red (
unscoped public package dropped from the set: ["@objectstack/cli"]); neutering the equality branch turns cases 2 and 3 red. Each mutation was confirmed on disk by grepping for the injected and the removed text before running — and the restore leg was re-verified green (SELF-TEST PASSED (4 cases)) with the mutation proven absent. No build/distis involved: the script is executed directly, not resolved through a packageexports.Gates.
node scripts/pm/dispatch-gates.mjsderived 17 families from the real change set; all 17 run green atc2fc2d9e(the final commit — the union was run after it):check:type-check-debtneeds the built closure to--re-measure, so the closure was built first exactly aslint.ymldoes (turbo run build --filter='./packages/*' --filter='./packages/*/*', 70/70 successful) and it then reportedOK — 33 ledger entr(ies) re-measured, 1897 raw tsc error(s) total, none above its recorded number. Its lowering hints are pre-existing and untouched by this diff.check:nul-bytesgreen. The new gate registers itself in the derivation (check:publish-smoke-pin … matched via scripts/publish-smoke-pack.mjs ⇢ gate script).node scripts/pm/check-governed-merges.mjs --teston the final file list:Changeset: none —
skip-changeset. Nothing here ships to npm: two scripts, two workflows, and one script entry in the private monorepo root manifest.For the release operator
After this merges, re-run the smoke from the Actions UI: open publish-smoke.yml → Run workflow → pick branch
main→ Run.publish-smoke.ymlhas nopush/pull_requesttrigger (onlyschedulefor the weekly registry canary, plusworkflow_dispatch), so merging alone does not start a run — the dispatch is a manual step.workflow_dispatchruns thepack-smokejob (if: github.event_name != 'schedule'), which is the leg that was failing.Then: green smoke → merge #10183 (17.2.0) → approve the release environment. #10183 needs no refresh; this PR is scripts-only and does not enter the publish set.
Generated by Claude Code