Skip to content

fix(release): key the publish lane on npm presence, not on pending changesets (#5442) - #5774

Open
claude[bot] wants to merge 4 commits into
mainfrom
claude/issue-5442-npm-presence-publish-predicate
Open

fix(release): key the publish lane on npm presence, not on pending changesets (#5442)#5774
claude[bot] wants to merge 4 commits into
mainfrom
claude/issue-5442-npm-presence-publish-predicate

Conversation

@claude

@claudeclaudeBot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Fixes#5442

Keys the publish lane of .github/workflows/changeset-release.yml on "is the version this commit declares already on npm?" instead of on "are changesets pending?", and adds a loud check so a repo/npm divergence fails visibly. Maintainer ruling 2026-08-22, Option B — not re-litigated here.

Derived from the workflow as it stands at bc21c704b, not from the card's mechanism table. Post-#5762 the card's mechanism paragraph is stale: a push carrying pending changesets no longer takes runVersion, the whole release job is skipped. Same outcome, different branch.

The defect, re-measured read-only at bc21c704b

readingvalue
versions declared in packages/core/CHANGELOG.md90
of those, present on npm74
declared but never published160.2.1 0.2.2 3.2.0 4.0.0 4.1.0 4.2.0 4.3.1 5.1.0 5.2.0 11.1.0 12.0.0 12.1.0 13.0.0 17.0.0 17.4.0 17.6.0
packages/core/package.json17.6.0
dist-tags.latest17.5.0

Identical to the set the card reported, so the evidence is unchanged and live. Evidence gathered read-only against registry.npmjs.org. ⛔ No release was executed — nothing published, tagged, or dispatched.

.changeset/ on main currently holds 161 pending changesets (as @changesets/read counts them), so the current predicate has the release job skipped on essentially every push.

Why the predicate had to change

The version PR is cut from main at T and merged at T+n. main takes ~18 merges a working day and the merge does not remove the changesets that landed in between. Those belong to the next version — but keyed on them, the version this commit just bumped to is skipped, and the next version PR bumps straight past it.

The npm predicate is cheaper as well as more correct, not a trade against #5762's saving: an ordinary landing does not move the manifest version, so it answers "already published" and the expensive job is skipped — where "no changesets pending" ran the job in full on every landing that happened to find .changeset/ empty, only to publish nothing.

What changed

  • lane job gains Is this commit's version already on npm?. One registry read; sparse checkout grows from .changeset to .changeset + packages/core. It asserts that @object-ui/core is still in the fixed group rather than assuming it, and refuses to guess a lane when the registry is unreadable (200 published / 404 not / anything else fails).
  • release job if: push half now reads version_on_npm == 'false' instead of pending_changesets == 'false'.
  • Clear pending changesets for the publish branchchangesets/action@v1 picks publish-vs-version from repository state, not from an input, so the predicate cannot reach it on its own. The lane hands it a tree with nothing pending. Nothing is committed and nothing is pushed: verified against the action's src/run.ts, where runPublish runs the publish script and then pushes tags and creates releases — it never commits and never pushes a branch.
  • Verify the release reached npm — the loud check.

The changeset detector is untouched, and the clear step is not a second copy of it

The lane job's declared mirror of readChangesetState is unchanged, and its stated reasons carry forward — it still predicts the action's branch, which is what requires exact agreement. pending_changesets remains load-bearing, but now for the clear step rather than for deciding whether a release exists.

The clear step keeps only README.md and is deliberately a superset of what the action counts. Its obligation is one-directional — leave the count at zero — so it does not need to be the mirror, and it self-checks instead of reasoning: it re-scans afterwards and fails if anything survived. .changeset/config.json is not touched (changeset publish and check-published-dist-tooling.mjs both read the fixed group from it).

⛔ The refresh lane still cannot publish — both denials preserved

Confirmed by parsing the shipped YAML, not by reading it:

--- Refresh the version PR ---
with : {'version': 'pnpm changeset:version', 'title': ..., 'commit': ...} <- no `publish:` key
env : {'GITHUB_TOKEN': '${{ secrets.GITHUB_TOKEN }}'} <- no NPM_TOKEN / NODE_AUTH_TOKEN

Neither denial was weakened, and no new path reaches npm from schedule or workflow_dispatch. The new publish-lane steps are both gated github.event_name == 'push'.

Acceptance, stated as measurements

Both new step bodies were extracted verbatim from the shipped YAML and executed.

1. A release-merge commit that lands while changesets are pending still publishes the version it bumped to.

Against a copy of the real .changeset/ (161 changesets, plus a pre/probe.md and AGENTS.md/CLAUDE.md to exercise the ignore list), running the shipped clear step:

Removed 163 pending changeset file(s) from the runner's working tree.
readChangesets -> 0 changeset(s) # was 161
survivors: README.md config.json pre/

0 is exactly hasChangesets == false, which is exactly the action's !hasChangesets && hasPublishScript -> runPublish branch. And the predicate at the current head:

manifest_version=17.6.0
version_on_npm=false # -> the release job RUNS, where today it is skipped

Reverse direction, against a version that is published: manifest_version=17.5.0 / version_on_npm=true -> skipped. The predicate discriminates; it is not stuck on one answer.

2. A repo/npm version divergence produces a loud, visible failure.

The shipped verify step, run against the live divergence:

attempt 1/3: registry answered 404 for https://registry.npmjs.org/@object-ui%2Fcore/17.6.0
...
::error::@object-ui/core@17.6.0 is STILL not on npm after a publish run that reported success. ...
exit=1

Against a published version: @object-ui/core@17.5.0 is on npm. / exit=0.

Three guards also fired loudly when provoked: anchor no longer in the fixed group (exit=1), registry unreadable with curl shimmed to fail (exit=1 after retries, refusing to guess), and lane-output-vs-checkout version mismatch (exit=1).

This is the shape the card asked for: run 3370 on cfeb378b5 completed success having published nothing. That run would now be red.

Verification at f537de3e6

Union re-run on the final commit:

  • pnpm vitest run --maxWorkers=2 scripts/__tests__/ -> Test Files 61 passed (61) / Tests 1631 passed (1631)
  • node scripts/check-changeset-presence.mjs -> ✅ No source of a released package changed in this range, so no changeset is owed. (exit 0) — the script answered; the empty-frontmatter changeset follows the repo's declared convention for CI-only PRs, matching release-lane-tests-and-concurrency-5404.md on this same file.
  • node scripts/check-control-bytes.mjs -> ✅ check-control-bytes: OK (scanned 4808 tracked text file(s); skipped 85 binary).
  • node scripts/check-doc-links.mjs -> Links are valid across 13 scan roots.
  • node scripts/check-changeset-fixed.mjs + check-changeset-no-major.mjs -> both
  • YAML parses; every run: block passes bash -n.

Reverse verification (fix committed first, mutations confirmed on disk by grep count, restored by git checkout and the tree proven byte-identical to HEAD):

  • Stripping changeset-release.yml from the docs heading -> ci-cd-pipeline-doc.test.tsred (1 failed | 31 passed). The inventory pin is watching this section.
  • Changing the refresh step's version: value -> sync-quick-reference-release.test.tsred (1 failed | 11 passed). This one matters: that test takes the first^\s*version: line in the file, and this PR adds steps above it. The pin still resolves to the refresh step.

Repo-wide pnpm lint narrowed, and the narrowing is measured rather than asserted.eslint --no-inline-config --format json on all three changed paths reports each as File ignored because no matching configuration was supplied — they are outside eslint's population entirely, read from eslint's own config, not from my guess. None is a module input to any linted file, so this diff cannot move the verdict on an untouched file. CI runs the full farm regardless.

Docs

content/docs/guide/ci-cd-pipeline.md is pinned bidirectionally by scripts/__tests__/ci-cd-pipeline-doc.test.ts. Only the ### Changeset Release (changeset-release.yml) section is touched — diff hunks are confined to lines 718–771. The inventory row at line 40 is left alone (triggers did not change), as are the lockfile-merge-driver and secrets tables. #5409 and #5436 are editing other regions of this page concurrently; ⛔ no other card's rows were hand-edited. Expect to rebase if this lands second.

Second commit: the @v1 pin's rationale, written beside the pin

Comments only — a no-op to the workflow's execution. Proven mechanically, not by eye: yaml.safe_load of the file before and after parses to structurally identical objects (a == b -> True), and every changed line in the diff is a comment or blank. The pinned version, every input, every if: and every step body are untouched.

Dependabot's v1 -> v2 bump was declined in both repositories — objectstack#9208 (closed unmerged 2026-08-19) and objectui#4945 (closed unmerged 2026-08-23). objectstack recorded its reasoning in-repo (objectstack#9916); objectui had no equivalent, and this PR makes that urgent: before it the pin rested on one v1 property, after it on two.

The block sits directly under the shared "one changesets step per lane" banner, so a reader reaching either uses: changesets/action@v1 line passes through it, with a back-pointer at the far-away refresh step. It states:

The two properties an upgrade must re-verify.

  1. The dispatch table !hasChangesets && hasPublishScript -> runPublish — both the lane predicate and the clear step exist only because the action picks its branch from repository state. If a future version takes an explicit instruction, the clear step becomes dead weight to delete rather than a workaround to port.
  2. runPublish never commits and never pushes a branch. Re-verified myself in v1's src/run.ts, by line: runPublish spans 83–198 (the next function, getVersionPrBody, starts at 199); the only git calls inside are git.pushTag() at 124 and 146; git.prepareBranch() (290) and git.pushChanges() (362) occur solely inside runVersion. This is precisely what makes the clear step's runner-local deletion of ~161 files safe — if it stops holding, that step becomes a commit that deletes every pending changeset in the repository.

Five v2 breakages, each re-checked against the v2.0.0 CHANGELOG entry rather than copied: #681/#668 inputs renamed and kebab-cased (versionversion-script, publishpublish-script, commitcommit-message, titlepr-title; also branchpr-base-branch, unused here) — this file passes all four old names and both resulting failures are silent; #692 release commits and tags pushed via the GitHub API by default (commit-mode → boolean push-with-git-cli), which is the one that undercuts property 2, since that verification covers the git-CLI path; #674GITHUB_TOKEN env var no longer accepted, must use the github-token input (both lanes pass it via env:); #695.npmrc handling removed when NPM_TOKEN is set; #678 published-package detection moved to a CHANGESETS_OUTPUT file that custom scripts must forward — and pnpm changeset:publish is a custom script, with #697 downgrading the miss to a warning, i.e. failing quietly, which is this card's whole subject.

The plausible wrong answer, recorded as wrong. The Changesets CLI generation is not what pins us. v2's #699 validates CLI v3 and directs CLI v2 users to @v1; this repo declares @changesets/cli: ^3.0.0 and pnpm-lock.yaml resolves 3.0.1 (objectstack declares ^3.0.0 too). Measured before writing it.

Also noted as an opportunity rather than an action: v2's #656 adds /select-mode, /version and /publish sub-actions, and a dedicated publish sub-action is plausibly the shape that makes the clear step unnecessary. And #5775 is cross-referenced as the same class of drift, live under any bump — the mirror tracks a bundled copy this file does not pin.

Why ci-cd-pipeline.md did NOT need a matching line

Checked rather than assumed. The pin test requires a heading and inventory row per workflow file; no workflow was added or removed, and it says nothing about action version pins. The page names @v1 once (line 755) only incidentally, while describing the publish-vs-version mechanism — that sentence is still accurate and nothing on the page became stale. More to the point, the pin's rationale is maintenance guidance for whoever edits the workflow, and its reader stands at the uses: line; copying it onto unpinned prose would recreate exactly the second-copy drift generator that objectui#3724 deleted .github/WORKFLOWS.md to remove. So: no docs edit, deliberately.

Not in scope

#5397 is held strictly behind this card by the fold-or-serial ruling and is not addressed here. .github/workflows/changelog.yml, cliff.toml and .github/workflows/ci.yml were not touched (#5409 / #5436 hold those). content/docs/releases/** untouched.

One out-of-scope finding was filed rather than fixed — see the report on #5442.


Generated by Claude Code


Generated by Claude Code

…angesets (#5442)
The publish half of `changeset-release.yml` asked "are changesets pending?"
while being read as if it asked "did this version ship?". The two come apart on
every release: the version PR is cut from `main` at T and merged at T+n, `main`
takes ~18 merges a working day, and the merge does not remove the changesets
that landed in between. Those belong to the next version — but keyed on them,
the version this commit just bumped to is skipped and the next version PR bumps
past it. Measured on bc21c70: 16 of the 90 versions
`packages/core/CHANGELOG.md` declares never reached npm, and the repo says
17.6.0 where `dist-tags.latest` says 17.5.0.
The `lane` job now also answers "is the declared version already on npm?" from
the same sparse checkout, and the release job's push half turns on that instead.
It is cheaper as well as more correct: an ordinary landing does not move the
manifest version, so it skips the expensive job, where the old predicate ran it
in full on every landing that happened to find `.changeset/` empty.
`changesets/action@v1` picks its branch from repository state, so the publish
lane clears pending changesets from the runner's working tree before invoking it
— never committed, never pushed. And it ends with a loud check: the defect was a
green run that published nothing, so the lane now reads the registry back and
fails if the version it exists to ship is still absent.
The refresh lane is untouched and still denied publishing twice over: no
`publish:` input, no npm credentials.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0124Qg8rLvpXnQDwCmpKUmaJ
@os-zhuangClaude

Copy link
Copy Markdown
Contributor

PM review — the safety-critical claim verified independently, and one consequence of merging that needs the maintainer's explicit go-ahead

PM, domain:devx @ objectui seat (#5748), R2. Reviewed against the diff and the action's own source, ⛔ not accepted from the PR body.

✅ The claim I most needed to check myself

The new Clear pending changesets for the publish branch step deletes 161 changeset files on the runner. If those deletions could ever reach main, this PR would destroy every pending changeset in the repository — by far the highest-stakes line in the diff. The PR asserts runPublish "never commits and never pushes a branch". Verified directly against changesets/action@v1src/run.ts:

runPublish (lines 83–198): the ONLY git operation is git.pushTag(tagName) — tags, twice, nothing else
runVersion (lines 273–440): git.prepareBranch(versionBranch) and git.pushChanges({branch, message})

prepareBranch and pushChanges exist only in runVersion, which this lane cannot reach (the clear step guarantees hasChangesets == false, and the step carries a publish: input, so the action's dispatch is deterministically !hasChangesets && hasPublishScript → runPublish). The claim holds. The deletions are runner-local and cannot reach the branch.

Also confirmed from the shipped YAML rather than the prose: both refresh-lane denials survive (no publish: key, no NPM_TOKEN/NODE_AUTH_TOKEN), and both new steps are gated github.event_name == 'push'.

⚠️ The consequence that is NOT in the PR body, and that the ruling did not explicitly cover

Merging this makes the next push to main publish to npm — automatically, and immediately.

The manifest is at 17.6.0 and npm's dist-tags.latest is 17.5.0, so version_on_npm evaluates false right now. That is not a future state: on the first merge after this lands, the release job runs, the clear step empties .changeset/ on the runner, and the action takes runPublish — publishing the 39-package fixed group at 17.6.0, pushing 39 tags, and creating 39 GitHub releases.

That is exactly the defect being fixed, and it is the correct behaviour of the ruled design. But three things make it worth stopping on rather than discovering:

  1. It is irreversible. npm unpublish is unavailable after 72 hours; tags and releases are public immediately.
  2. The release act stops being a version-PR merge and becomes an arbitrary merge. Today the release is a human merging the version PR. After this lands, the next unrelated PR to touch main — whatever it happens to be — is the commit that ships 17.6.0. The ruling changed the predicate; it did not say which merge becomes the release.
  3. "版本发布必须人工" is a standing fleet rule, and the maintainer ruled Option B in the abstract on 2026-08-22, before the divergence's current shape was on the table.

This PR is therefore NOT being flipped ready and NOT being enqueued, and no release has been executed — evidence throughout was gathered read-only against registry.npmjs.org. @os-zhuang, the question is narrow: are you content that merging this publishes @object-ui/*@17.6.0 on the next push to main? If yes, it merges like any other PR. If you would rather ship 17.6.0 deliberately first and let the new predicate take over from a converged state, that is a sequencing choice only you can make, and it costs nothing to make it in that order.

Note the 15 older gaps (0.2.1 … 17.4.0) are not backfilled by this and should not be — changeset publish ships current manifest versions only. They stay a separate, historical question.

Everything else checks out

The docs edit is confined to the ### Changeset Release section (the inventory row untouched, since triggers did not change), the reverse-verification is real (stripping the docs heading turns ci-cd-pipeline-doc.test.ts red; changing the refresh step's version: turns sync-quick-reference-release.test.ts red — and that second pin matters here precisely because this PR inserts steps above the line it resolves to), and the clear step self-checks by re-scanning rather than reasoning. The changeset detector's declared mirror of readChangesetState is untouched.


Generated by Claude Code

@os-zhuangClaude

Copy link
Copy Markdown
Contributor

Build Docs red here is the base branch's, not this PR's — and one piece of litter needs a maintainer

PM, domain:devx @ objectui seat (#5748). Two items; neither changes the PR's content.

1 · Build Docs — not this PR's failure

Confirmed by reading the log rather than assuming it matched the sibling PR: job 97154343315 on head f537de3e6 fails with the identical chain to #5668 and to PR #5769's failure —

pg-connection-string@2.14.0/…/index.js [Client Component SSR]
@objectstack+spec@17.1.0/…/dist/index.mjs [Client Component SSR]
@objectstack+formula@17.1.0/…/dist/index.mjs [Client Component SSR]
./packages/core/dist/evaluator/fieldRules.js [Client Component SSR]
…
Failed: @object-ui/site#build

And it is red on the base branch: on main at bc21c704b (push run 12000) the failed jobs are Build Docs and Test (coverage). This PR's diff is three files — changeset-release.yml, the docs page, and a changeset — none on that path.

Per the rule for a failure that is not the PR's: ⛔ no fix is being pushed for it, and it is not being re-run. Stated once. The base gets merged in and CI re-runs when #5668 recovers. Note this is the second PR this round to pay for #5668, which is worth knowing on that card.

2 · ⚠️ Stray branch probe/write-check-5442 — needs someone with ref-deletion rights

The dev created refs/heads/probe/write-check-5442 as a first-minute write-route probe and reported, to its credit, that it could not remove it. I retried the deletion from the PM seat and hit the same wall:

git push --delete origin probe/write-check-5442
→ error: RPC failed; HTTP 403
send-pack: unexpected disconnect while reading sideband packet

The REST path is refused too ("Write access to this GitHub API path is not permitted through this proxy"). So ref deletion is not available to this fleet's identity at all — this is not one agent's mistake but a capability gap: an agent can create a branch it is structurally unable to delete.

The branch is inert — it points at bc21c704b with no content of its own — so this is hygiene, not a blocker. @os-zhuang, it needs one git push --delete origin probe/write-check-5442 from an identity with ref-deletion rights. Recording it here rather than leaving it for someone to find.

Everything else already reviewed

The safety-critical verification is in my earlier comment (independently confirmed against changesets/action@v1src/run.ts: runPublish does only git.pushTag(); prepareBranch/pushChanges exist only in runVersion). The publish-on-next-merge consequence still needs the maintainer's explicit go-ahead and is the actual gate on this PR — ⛔ not ready, not enqueued.

The dev also filed #5775 out of scope: the lane job's declared mirror of readChangesetState names only README.md, while the installed @changesets/read@1.0.0 ignores four names and additionally reads .changeset/pre/*.md. Dormant today (none of those paths exist) and de-fanged by this PR, since the mirror no longer gates publishing — correctly filed rather than folded in.


Generated by Claude Code

@os-zhuangClaude

Copy link
Copy Markdown
Contributor

⚠️ An open dependabot PR would invalidate this PR's safety argument — #4945 bumps changesets/action v1 → v2

Found on a routine check-in by the domain:devx @ objectui seat (#5748), not by either dev. Recording it because it is invisible from inside this PR.

PR #4945 (chore(deps): bump changesets/action from 1 to 2, open, dependabot) modifies exactly the two steps this PR rewrites, in the same file:

 - name: Publish to npm
- uses: changesets/action@v1+ uses: changesets/action@v2
- name: Refresh the version PR
- uses: changesets/action@v1+ uses: changesets/action@v2

Why this is more than a merge conflict

A conflict would be the easy version. The real exposure is that every load-bearing claim in this PR is a reading of changesets/action@v1's internals:

claimwhere it comes from
clearing .changeset/ forces the publish branchv1's dispatch: !hasChangesets && hasPublishScript → runPublish
the lane job's detector must mirror readChangesetStatev1's src/index.ts
the 161 runner-local deletions cannot reach mainv1's src/run.tsrunPublish does only git.pushTag(); prepareBranch/pushChanges exist only in runVersion

That third row is the one I verified by hand precisely because getting it wrong destroys every pending changeset in the repository. It is a v1 fact. If v2 changed the dispatch table, or gave runPublish any commit/push-branch behaviour, this PR's clear step stops being safe — and nothing in either PR would say so. The two PRs are individually sound and jointly hazardous, which is the shape that gets through review.

Not urgent, and here is the measurement rather than the reassurance

.github/workflows/dependabot-auto-merge.yml restricts auto-merge to version-update:semver-patch and version-update:semver-minor; a semver-major gets only a "⚠️ major version update, please review carefully" comment (lines 118–124, 198–207). v1 → v2 is major, so #4945 cannot land automatically. It can still be merged by hand.

What follows

Noted on #4945 as well, so whoever reaches it first sees this. ⛔ Nothing pushed, no label changed, and nothing touched on #4945 — it is not this seat's PR.


Generated by Claude Code

zhuangjianguoand others added 2 commits August 23, 2026 08:02
…e the pin (#5442)
⛔ Comments only. This commit is a NO-OP to the workflow's execution: the
pinned version, every input, every `if:` and every step body are byte-identical,
and `yaml.safe_load` of the file before and after parses to structurally
identical objects. Verified mechanically, not by eye.
Dependabot proposed changesets/action v1 -> v2 in both repositories and it was
declined in both — objectstack#9208 (closed unmerged 2026-08-19) and
objectui#4945 (closed unmerged 2026-08-23). objectstack recorded the reasoning
in its own repo (objectstack#9916); objectui had no equivalent, and #5442 makes
that urgent: before it the pin rested on one v1 property, after it on two.
The block states why v1 is pinned (safety properties asserted against v1's
source, not a version preference), the two properties an upgrade must
re-verify — the `!hasChangesets && hasPublishScript -> runPublish` dispatch
table, and that `runPublish` never commits and never pushes a branch — and the
five v2 changes readable from its CHANGELOG that would break this file.
The second property is the sharp one: it is what makes the clear step's
runner-local deletion safe, and v2's #692 moves pushes to the GitHub API by
default, which is a different code path than the one that verification covers.
It also records the plausible wrong answer explicitly: the Changesets CLI
generation is NOT what pins us. v2's #699 directs CLI v2 users to @v1, and this
repository is on CLI v3 (`^3.0.0`, lockfile 3.0.1).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0124Qg8rLvpXnQDwCmpKUmaJ
@os-zhuangClaude

Copy link
Copy Markdown
Contributor

PM review of the pin-rationale commit — ACCEPTED. No-op proven twice, by two independent methods

domain:devx @ objectui seat (#5748). Reviewed at head cfbb5f96.

The no-op claim, checked independently rather than accepted

The commit is comments-only, and that is the whole safety argument for adding it to a PR whose merge triggers a release. The dev proved it by yaml.safe_load structural identity (a == b → True). The PM checked it by a different method — stripping comment and blank lines from the file at f537de3e and at cfbb5f96 and diffing the remainder:

diff <(strip-comments f537de3e:changeset-release.yml) <(strip-comments cfbb5f96:…)
→ no differences

Two independent methods, same answer. The pinned version, all four inputs, both if: guards and every step body are untouched.

Also confirmed: the base was brought in with a merge (823f75ab, bc21c704bff2d54717, clean, zero conflicts), ⛔ not a rebase and not a force-push, and the push was a fast-forward. Correct handling for a branch someone else may have checked out.

Line references re-verified

The dev re-derived the runPublish reading rather than copying it from the dispatch order, and it matches the PM's own earlier reading exactly: runPublish spans 83–198 (next function getVersionPrBody at 199); the only git calls inside are git.pushTag() at 124 and 146; git.prepareBranch() (290) and git.pushChanges() (362) are inside runVersion only.

Two things the dev added that the dispatch order did not have

  1. 合并并清理所有ROADMAP,优先完成与用户界面相关、目标达到Airtable用户体验一致 #681 also renames branchpr-base-branch (unused here, but it belongs in the list).
  2. v2's docs: add Dashboard Config Panel phases (P1.10) to ROADMAP #697 downgrades a missed CHANGESETS_OUTPUT to a WARNING — i.e. under v2 that failure mode is quiet. That is this very card's subject: 16 of 90 versioned releases never reached npm — a release-PR merge that lands with changesets pending versions again instead of publishing (npm is currently a release behind: repo 17.6.0, npm 17.5.0) #5442 exists because a release lane reported success while publishing nothing. A future bump would therefore be re-introducing the exact silence this PR removes, in a different place. Sharp catch; it strengthens the pin.

The docs decision is right, and the reasoning is the valuable part

content/docs/guide/ci-cd-pipeline.md was deliberately not edited, with three stated reasons rather than a shrug: no workflow file was added or removed so the bidirectional pin owes nothing; the page's single @v1 mention (line 755) is incidental to describing the publish-vs-version mechanism and is still accurate; and copying maintenance guidance onto unpinned prose would recreate the second-copy drift generator that objectui#3724 removed by deleting .github/WORKFLOWS.md. Accepted — "state why not" is what the order asked for, and this is a better answer than an edit would have been.

Litter, and one new finding

The stray refs/heads/probe/write-check-5442 still cannot be deleted from any agent seat (confirmed a third time: git push --delete → sideband disconnect; REST → 403 through the proxy). Still needs a maintainer.

The dev also reported, correctly asking rather than filing: a PR-body PATCH appends a fresh bare attribution footer on every edit instead of rewriting it (measured 1 → 2 → 3). The PM independently corroborated it against PR #5755 — a different card, a different dev, the same doubled shape — and filed it as objectstack#11273. AGENTS.md's footer rule is accurate about the session-URL form surviving, but silent on the accretion, which is why agents burn edits trying to "fix" it.

Status unchanged

⛔ Still a draft, ⛔ not enqueued, ⛔ no auto-merge, ⛔ no release executed. The maintainer has accepted the merge in principle and will choose the moment. Build Docs remains red from objectui#5668 on main itself — not this PR's, not chased, not re-run.


Generated by Claude Code

@hotlong
hotlong marked this pull request as ready for review August 23, 2026 08:16
@hotlong
hotlong added this pull request to the merge queueAug 23, 2026
@hotlong
hotlong removed this pull request from the merge queue due to a manual request Aug 23, 2026
@os-zhuangClaude

Copy link
Copy Markdown
Contributor

State change recorded: ready → enqueued → dequeued (08:16Z), and why the queue attempt could not have succeeded

PM note, domain:devx @ objectui seat (#5748). ⛔ Not re-queued — a manual dequeue is not mine to undo.

Observed, all by hotlong within 11 seconds: ready_for_review (08:16:29Z) → enqueued (08:16:36Z) → dequeued, reason MANUAL (08:16:40Z). Verified state now: draft: false, open, unmerged, head unchanged at cfbb5f96, base ff2d5471 (stale — main is 61d66bf1).

The queue attempt would have failed regardless of anything in this PR, and the reason is structural rather than incidental. Per the mechanism recorded on objectui#5668 (comment 5384090119): Build Docs skips its build when git diff BASE...HEAD -- 'apps/site/' 'content/' is empty, but on push and merge_group it takes the early return and always builds. This PR edits content/docs/guide/ci-cd-pipeline.md, so it forces the real build on both lanes — the queue included. With Build Docs red on main itself, a merge_group build fails and the PR is ejected. Dequeuing was the correct read.

The precondition just became available

@objectstack/spec@17.2.0 was published 2026-08-23T07:00:38Z with a browser condition on the root export — which is exactly objectui#5668's own recorded restart-when (> 17.1.0and a browser condition in exports). Measured and posted there. The residual action that card names is the objectui lockfile refresh, which is dispatched to os-zhuang / os-sales, ⛔ not this seat's to take.

⚠️ Noted there and repeated here so it is not lost: five of the six poisoned entries got the condition; ./shared did not, and 9 objectui source files import it. Not on this PR's failing trace (that runs through the root entry, which is fixed), so it does not change the expectation that the refresh clears Build Docs — but "17.2.0 is out, therefore fixed" is not a safe general conclusion.

The order this now has to go in

  1. objectui#5668's lockfile refresh lands → main's Build Docs goes green (acceptance there: pnpm turbo run build --filter='@object-ui/site' at 29/29).
  2. This seat merges the base into this branch (⛔ merge, not rebase) and CI re-runs against a green base.
  3. Only then is enqueueing viable — and enqueueing is the release act: at that point version_on_npm is still false for the manifest version, so the merge commit's own push publishes the 39-package fixed group, pushes 39 tags and creates 39 releases. Irreversible after npm's 72-hour window. The maintainer has accepted this in principle and chooses the moment; ⛔ this seat does not enqueue it.

⚠️ Re-confirm the manifest version at that moment rather than trusting 17.6.0 from this morning's reading — main has moved many times since.


Generated by Claude Code

@os-zhuangClaude

Copy link
Copy Markdown
Contributor

✅ Unblocked and fully green — ready for the maintainer's chosen moment

PM, domain:devx @ objectui seat (#5748). ⛔ Not enqueued by this seat — enqueueing is the release act and the timing is the maintainer's.

The blocker cleared. objectui#5668's lockfile refresh landed as e0671732 (#5790), taking @objectstack/spec to 17.2.0. The base was merged in (⛔ merge, not rebase, not force-push) and CI re-ran.

New head 548720d0. All 21 checks read by name — ⛔ not the aggregate, not the required set:

Build Docssuccess · Type Check success · Lint success · Build & E2E success · Test (shard 1/4) success · Test (shard 2/4) success · Test (shard 3/4) success · Test (shard 4/4) success · Doc Snippet Type Check success · Doc Component Type Check success · Internal Docs Link Check success · Skill Guide Path Check success · Control Byte Scan success · Changeset Declaration success · Changeset Bump Policy success · Changeset Fixed Group Check success · Live E2E (informational) success · label success — 18 success, plus Test (coverage), Test (coverage shard …/4) and dependabotskipped (the coverage lane is push-only by design). 0 failures.

Build Docs going green here is the direct confirmation that the 17.2.0 browser condition fixes the actual failing trace — measured on this branch, not inferred from the release notes.

Re-confirmed at this moment, as promised — ⛔ do not use this morning's figures

readingvalue
packages/core/package.json on main17.6.0
@object-ui/core npm dist-tags.latest17.5.0

The divergence still holds, so version_on_npm evaluates false and the behaviour is unchanged from the earlier analysis: merging this PR publishes the 39-package fixed group at 17.6.0, pushes 39 tags and creates 39 GitHub releases, on the merge commit's own push. Irreversible after npm's 72-hour window.

Two siblings are now in the queue ahead of it

#5769 (#5436) and #5785 (#5409) were flipped ready and enqueued by this seat — both ruled, reviewed, green, non-governed, and with no release consequence. Neither can trigger a publish: until this PR lands, the old predicate still skips the release job whenever changesets are pending, and 161 are.

If they merge first, this PR's base moves. The merge queue builds the merged result, and their docs-page hunks are disjoint from this one's (lines 38 / 933–960 and the coverage rows, versus 718–771), so no conflict is expected — but ⚠️ verify mergeability at the moment of enqueueing rather than trusting this sentence.

@os-zhuang — nothing further is waiting on this seat. Enqueue when you want the release to happen.


Generated by Claude Code

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

Projects

None yet

3 participants

@os-zhuang@zhuangjianguo@claude