Skip to content

fix(gates): Check Changeset route 2 and check-empty-changeset stop listing skills/** as releasing nothing - #23457

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-23451-skills-changeset-route2
Oct 14, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-23451-skills-changeset-route2

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23451

Clause-②: no

What

@objectstack/skills publishes skills/**. Its build (packages/skills/scripts/sync-catalog.mjs) copies every regular file under skills/ into dist/skills/, with no exclusions. Its files field is ["dist", "README.md", "CHANGELOG.md"], and it is in the changeset fixed group. Two gate texts still told an author that a skills/** change releases nothing and should take skip-changeset. That disagrees with AGENTS.md's post-task checklist (AGENTS.md:1085 at 2d2e3ce43), which reserves the label for "a diff that publishes nothing from any released package".

This PR changes only text: prose, comments, one ::error:: line, and one self-test battery's name and rationale. Neither gate passes or refuses anything it did not pass or refuse before. The Check Changeset verdict step has no path branch, and check-empty-changeset.mjs reads only .changeset/*.md rows. Neither has a skills/** case to change.

Site by site (line numbers at base 2d2e3ce43)

Site Verdict Change
scripts/check-empty-changeset.mjs:513, printed remedy "It releases nothing (.github/, .claude/, skills/, …" FALSE skills/ is dropped from the list. The "It releases something" bullet now says a skills/** change does, because it ships in @objectstack/skills.
same file :913-916, GREEN 3 comment and battery() name ("Such a PR takes the skip-changeset label", "(route 2)") FALSE The battery is renamed to GREEN 3: a skills/**-only PR carrying NO changeset: nothing to judge. The comment now says why this gate stays silent: it reads only the .changeset/*.md rows a diff touches. It also says the PR still owes a changeset naming @objectstack/skills. Asking for that changeset is the Require a changeset step's job, and catching a wrong label is review's job.
same file :679, roster row follows the rename Renamed together with the battery() call. The floor keys on the name (see the ablation below).
same file :921-922, GREEN 3 assertion messages ("must be green here", "must not even be read") TRUE unchanged
same file :67, header narrative of the skills/** precedent chain (#4607 / #5130 / #5451, then PR #5799) TRUE, as history unchanged
same file :866-872, RED 1 (the #5799 shape: a skills/** change plus a new empty changeset) TRUE: still refused, still the gate's own case unchanged
.github/workflows/pr-automation.yml:795, route 2's "releases nothing" list FALSE skills/ dropped
same file :812-820, route 2's skills/** paragraph ("skills/ ships with no npm package … That premise is true … Take the label.") FALSE Rewritten. skills/** is not on the list, because it ships in @objectstack/skills. A skills/** change takes route 1, naming that package, and never takes the label. The old premise no longer holds.
same file :842, ::error:: line ("including any 'skills/**' change -- see #5947") FALSE The parenthetical now says a skills/** change releases @objectstack/skills and never takes the label. The line still opens with ::error::This PR adds no changeset., which check-changeset-no-major.mjs pins.
same file :846-852, step comment on the same precedent chain TRUE, as history unchanged

No new tracker number enters any text an author is shown.

Verification (head 7463fab7c)

  • node scripts/check-empty-changeset.mjs --self-test exited 0: ✓ check-empty-changeset --self-test: 170 assertions over real temp git repos (real scan() path).
  • Ablation (run once, no file kept). The fix is committed first. scripts/ablation-replace.mjs then put the battery() call back to the old (route 2) name, leaving the roster at the new name. The self-test exited 1 with: battery "GREEN 3: … (route 2)" registered 2 case(s) but is not declared in SELF_TEST_BATTERIES and battery "GREEN 3: … nothing to judge" DID NOT RUN — 0 cases registered, 2 pinned. The tool restored the file: blob after restore c0df1afc9232 equals HEAD's, and git diff HEAD is empty. git status --porcelain read 0 lines afterwards.
  • The consumer battery requires the ::error::This PR adds no changeset line to reach exit 1 within 900 characters. It measures 730 after the edit (698 before). The workflow parses with yaml 2.9.1, and the Require a changeset step is found.
  • Gates, derived by node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack against merge base 2d2e3ce43: 53 commands, and every one exited 0. These include check-empty-changeset.mjs --base origin/main, check:changeset-gate-self-tests, check:workflow-status-functions, check:workflow-step-name-quoting, check-self-test-workflow-commands.mjs, check:nul-bytes and check:pm-dispatch-gates (fast tier, 1841 cases). Reconciliation: ✓ dispatch-gates --ran: 53 derived famil(ies) accounted for — 53 run, 0 NOT-MEASURED.
  • Lint, narrowed to the touched files. pnpm exec eslint --no-inline-config --format json returned 2 results: scripts/check-empty-changeset.mjs with 0 errors and 0 warnings, and pr-automation.yml "ignored because no matching configuration was supplied". So the YAML is outside eslint's own population. eslint.config.mjs enables no type-aware linting (zero parserOptions.project, as stated at its line 327), so this diff cannot move any untouched file's verdict.
  • NOT MEASURED, and left to CI per the derivation: 7 families valued from the workflow, the Test Core job's own steps, and 4 type-check lanes. This diff touches no TypeScript.

Acceptance notes

  • The card cites AGENTS.md:1085. That line states the general rule, "A bug fix in a released package takes a patch changeset — never none, and ⛔ never skip-changeset: that label is for a diff that publishes nothing from any released package". It names no skills/** case. The skills case follows from the packaging fact above. So the new texts say "a changeset naming @objectstack/skills" and fix no bump level: route 1's level rules apply.
  • The card title says "so it passes with no changeset". Measured on base: the Require a changeset step reds any PR that adds no changeset and lacks the label, skills/** or not. A skills/**-only PR passed only through the label that route 2 told its author to apply, and that instruction is what this PR corrects.
  • Not added: refusing a skills/** diff that carries no changeset. That would be new gate behaviour, and triage left it for the maintainer's word (「新增门禁默认否」).
  • Unchanged and only noted: route 2's closing tie-break, "If you are unsure between routes 1 and 2, take route 2". It is general guidance and not about skills/**.
  • Changeset: none; this PR takes skip-changeset. Root scripts/ and .github/workflows/ ship in no package. Across 72 non-private package.json files, no files entry leaves its own package directory. git grep for either file name under packages/ (CHANGELOGs excluded) has zero hits, against a control (sync-catalog) that hits.

维护者速读(草稿)

改了什么:改两处提示文字,内容是"要不要写 changeset"。一处是 Check Changeset 的路线 2(在 pr-automation.yml),另一处是 check-empty-changeset.mjs 的修复提示。两处原来都说"只改 skills/** 不发布任何包,贴 skip-changeset 标签即可"。现在改成:改 skills/** 会随 @objectstack/skills 一起发布,要写 changeset 并点名这个包,不贴标签。另外,一个自测用例改了名字,注释也重写了。

为什么改:@objectstack/skills 上线后,skills/** 已经随 npm 包发布,旧提示和 AGENTS.md 的发布清单相互矛盾。已经出过一次真实问题:PR #23443 照旧提示贴了标签,这次改动差点进不了 CHANGELOG,到合同复核才被发现。

风险与代价(含回滚):只改文字和注释。两道门禁放行什么、拒绝什么都没有变,自测 170 条断言全部通过。代价是旧提示里那段早期 skills PR 的历史说明删短了。如需回滚,还原这一个 commit 即可。

席位意见:

你要做的:审阅后在本 PR 上给出批准(本 PR 改到 .github/workflows/**,属于 Tier H)。


Generated by Claude Code

…ying skills/** releases nothing

`@objectstack/skills` publishes `skills/**` (its build copies the tree into
the `dist` its `files` field ships), so a `skills/**` change releases that
package and owes a changeset naming it, never the `skip-changeset` label.

- pr-automation.yml route 2: `skills/` leaves the "releases nothing" list;
  the paragraph that said `skills/` ships with no npm package now says it
  ships in `@objectstack/skills` (route 1); the `::error::` line no longer
  sends a `skills/**` change to the label.
- check-empty-changeset.mjs: the remedy drops `skills/` from "releases
  nothing" and names it under "releases something"; the GREEN 3 battery is
  renamed (roster row and `battery()` call together) and its rationale now
  says why this gate stays silent on a PR with no changeset row, not that
  the PR releases nothing.

Text, comments and fixture rationale only: neither gate passes or refuses
anything it did not before.

Claude-Session: https://claude.ai/code/session_012VGBku7tEi8DX2uvi1gtXk
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 14, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 7463fab7c22f2eb1ef8d808f876e80753a1742a9
Local-runs: none
Tier-authority: maintainer order, comment 6162674390 on #23392

Inputs: PR #23457 at the head above (body, file list, net diff against main 7b3d5585c; merge base 2d2e3ce43, and main has not touched either file since, so the net diff is the PR diff), card #23451 (body and all 3 comments), and the 43 check-runs on the head. Files were read as git objects at main and at the head. Nothing was built, run or re-run.

① Derived judgments

Pass/refuse behaviour: neither gate moves. RIGHT.

  • Check Changeset, step Require a changeset (or the skip-changeset label): the verdict branches on ADDED alone. n/a stands aside, -eq 0 prints the route text and the ::error:: line and then exit 1, and anything else passes. No path is read. The diff touches only the body of the quoted heredoc (cat with a single-quoted MSG terminator, so nothing expands) and the double-quoted echo string. The new text adds no $, backtick or double quote. The run on this head is a pull_request event that executed the edited file, and steps 1–9 ran, so the YAML parses.
  • scripts/check-empty-changeset.mjs: scan() diffs --diff-filter=AMR over .changeset/*.md only, and scanForeign() reads .changeset rows only. The diff touches two string literals in report()'s printed remedy, one roster key, and one battery() name and its comment. No function body, regex, exit code or export changes.
  • Public surface: only author-shown text changes (the remedy lines, the ::error:: annotation and the route-2 heredoc). No tracker number is added to any author-shown string, and one (see #5947) is removed. #22658 appears only in a code comment.

Every rewritten sentence is TRUE on main 7b3d5585c.

  • Packaging. packages/skills/package.json has files = dist, README.md, CHANGELOG.md, is not private, has publishConfig.access: public and is version 18.0.0-next.0. It is in .changeset/config.json's fixed group, and its CHANGELOG.md already carries 18.0.0-next.0 entries for skills/** edits. Its build runs scripts/sync-catalog.mjs. syncCatalog() walks every regular file under the repo-root skills/ (no exclusion list; a symlink is refused) and writes each one to dist/skills/, and prepublishOnly runs --check for byte identity. So every skills/** byte change also changes bytes that @objectstack/skills ships.
  • pr-automation.yml route-2 list drops skills/: TRUE.
  • pr-automation.yml route-2 paragraph ("'skills/**' is NOT on that list: it ships in '@objectstack/skills', whose build copies it into the 'dist' that package's 'files' field publishes … route 1, naming '@objectstack/skills', never the label … that premise is no longer true"): TRUE.
  • The ::error:: parenthetical ("a 'skills/**' change releases '@objectstack/skills' and never takes the label"): TRUE.
  • The check-empty-changeset.mjs remedy list drops skills/, and the line "It releases something -- a skills/** change does: it ships in @objectstack/skills": TRUE.
  • The GREEN 3 comment: TRUE. This gate reads only .changeset/*.md rows, and the step that reds an unlabelled PR with no changeset is Require a changeset.
  • AGENTS.md post-task checklist item 3 (lines 1083–1087 on main) says "Add a changeset for anything that publishes" and "never skip-changeset: that label is for a diff that publishes nothing from any released package". All five texts agree with it. They name the package but not a bump level, and that is right. Item 3's patch clause covers a bug fix only, and route 1's WHICH LEVEL paragraph grades a widening (a new skill, say) at least minor. A fixed patch would contradict it.

No sentence named by the card is left false.

  • The card names three sites: check-empty-changeset.mjs:513, GREEN 3 at :913-923, and pr-automation.yml route 2. All three are rewritten.
  • git grep on main for skills/ near "releases nothing", "no npm package" or skip-changeset hits only the three lines this diff rewrites.
  • The remaining skills/** mentions are history (the check-empty-changeset.mjs header at :64-70 and the pr-automation.yml step comment at :844-849) or the RED 1 fixture (the docs(skills): 已发布 skill objectstack-pm-dispatch 的决策框架由两轴升为三轴(裁决 B:泛化内核后搬) #5799 shape, which is still refused). All are TRUE as written.
  • The GREEN 3 assertion messages ("must be green here", meaning this gate) are unchanged and TRUE.

The renamed GREEN 3 battery is consistent with SELF_TEST_BATTERIES. RIGHT.

  • The roster key (:679) and the battery() call (:921) are byte-identical. The pin is 2, and the battery registers 2 assertions (:928, :929).
  • The roster's size is unchanged, and SELF_TEST_BATTERY_FLOOR (23) is untouched. No other file names the old or the new battery name.
  • On this head, the Changeset-family gate self-tests step of Lint · docs & code guards concluded success. That step is pnpm check:changeset-gate-self-tests: the self-tests of check-empty-changeset, check-adr-0087-registration and check-changeset-no-major.

The ::error:: prefix is intact. RIGHT.

  • The line still opens with ::error::This PR adds no changeset., inside the step still named Require a changeset.
  • It is pinned by check-changeset-no-major.mjs:4726 (which slices the step by name and matches the annotation) and by check-empty-changeset.mjs:1829 (the annotation must reach exit 1 within a 900-character window). Both pins run in the self-test step above, which concluded success.
  • scripts/pm/ci-failure.mjs names the sentence only in its own fixtures.

Check-runs on the head (43 runs, all completed): 32 success, 11 skipped, 0 failure, 0 in progress.

  • Required contexts: Lint & Repo Gates success, TypeScript Type Check success, Test Core success (all six shards success), Governed Surface Queue Guard success, and Build Core skipped.
  • The 11 skips are the opt-in or full-battery jobs (Build Core, Build Docs, Console Pin Gate, Dogfood Regression Gate matrix, Dogfood Verify CLI, Temporal Conformance, and Packed-tarball smoke twice) plus second runs of Auto Label, Check PR Size and Check Changeset, whose first runs on this head concluded success.
  • Check Changeset concluded success through the label exemption (steps 10–15 skipped), so it did not execute the edited verdict text itself. The self-test step above is where that text is pinned.

② Semver level

skip-changeset with no changeset: RIGHT.

  • The diff touches only .github/workflows/pr-automation.yml and the repo-root scripts/check-empty-changeset.mjs.
  • On the head, git grep under packages/ (CHANGELOGs excluded) for either file name has 0 hits, against a control (sync-catalog) that hits.
  • No packages/**/package.json has a files entry that leaves its package, and no package build reads either path.
  • So no published package's shipped bytes change. That is exactly the case the label is for (AGENTS.md item 3), and the label is on the PR.

The PR body's Clause-②: no: RIGHT and well-formed (a bare no, no arm). No published payload, key or export widens or narrows, and author-facing CI text is not a published surface.

③ Boundary flags

  • Deviation 1 (the first check:pm-dispatch-gates run did not capture its exit status; the re-run exited 0): answered. Lint · PM dispatch-gates concluded success on this head, so nothing remains.
  • Deviation 2 (the texts name @objectstack/skills and fix no bump level, where the dispatch said patch): answered and accepted, for the reason given in ① on AGENTS.md item 3.
  • out_of_scope_findings[0] (route 2's tie-break "If you are unsure between routes 1 and 2, take route 2" is unchanged, noted but not filed): answered, and I concur. The sentence is not false and the card does not name it. It is a stated preference with its own rationale, and for skills/** the rewritten paragraph removes the uncertainty it would apply to. Not escalated. One risk remains: an author who is genuinely unsure whether a path publishes is still steered to the label. Whether to change that is the gate seat's call.
  • open_questions: empty, so nothing to answer.
  • The dev's refinement of the card (the title's "so it passes with no changeset" holds only through the label): verified on the head's verdict step. An unlabelled PR with ADDED 0 exits 1, whatever its paths. The PR's acceptance notes record this correctly.
  • Triage scope (「新增门禁默认否」, so no new refusal of a skills/** diff that carries no changeset): HELD. No branch, path read or exit was added.
  • Claim boundary (text and fixture rationale only, in the two named files): HELD. Two files, +20/−18.
  • Landing: the diff touches .github/workflows/**, so it is Tier H, and the PR is a draft. This record does not lift Tier H. Landing waits for an authorized APPROVED review.

Implemented-by: claude/issue-23451-skills-changeset-route2
Reviewed-by: session_012VGBku7tEi8DX2uvi1gtXk

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · PR #23457 @ 7463fab7c22f2eb1ef8d808f876e80753a1742a9 · domain:devx seat 1 · session_012VGBku7tEi8DX2uvi1gtXk · 2026-10-14T10:45Z

Checklist, read on GitHub (not from the report):

  • Shape: draft, base main, author objectstack-fleet[bot], assignee os-support-ai. Body line 1 is Fixes #23451, the only closing keyword. No model name in the title or body.
  • Scope: 2 files, +20/−18, within the claimed surface (6165808357). check-governed-merges --pr 23457: 1 of 2 paths governed (.github/workflows/pr-automation.yml), 38 lines, so Tier H. No content/docs/releases/ path.
  • Diff, read line by line: only author-shown text, a code comment and one battery name change, with the roster key renamed in lockstep. No step condition, path read, exit code or function body moves.
  • Changeset: skip-changeset is on the PR. Neither file ships in any package: packages/skills publishes dist built from repo-root skills/, and neither edited file is under it.
  • Contract review: PASS, 6166565290, served at TIER_DEFAULT on the maintainer's order 6162674390. It finds both gates' pass/refuse behaviour unchanged, every rewritten sentence true on main, and the ::error:: prefix pins intact.
  • CI on this head, read 2026-10-14T10:45Z: 43 check-runs. 32 success, 11 skipped, 0 failure. The skips are expected:
    • Check Changeset: the label exemption. The edited text is pinned by Changeset-family gate self-tests, which passed.
    • Build Core and Build Docs: path filter.
    • The rest: push-only or opt-in jobs.

Deviations:

  • The texts name @objectstack/skills but fix no bump level. That is right: AGENTS.md checklist item 3 gives patch to a bug fix only.
  • The dev re-ran check:pm-dispatch-gates to capture its exit status.

Acceptance notes: route 2's tie-break "If you are unsure between routes 1 and 2, take route 2" stays as written. It is not false, and the card does not name it. Not filed: no measured instance.

Landing: Tier H. The PR stays draft and carries needs-user-decision. The final maintainer quick-read follows this comment, and review is requested from os-zhuang and hotlong. After an authorized APPROVED review, this seat runs the pre-landing checks, flips the PR ready and arms auto-merge.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 · PR #23457 · domain:devx seat 1 · 2026-10-14T10:46Z

改了什么:两处"要不要写 changeset"的提示文字。

  • 一处是 Check Changeset 的路线 2 说明和报错提示(pr-automation.yml)。
  • 另一处是 check-empty-changeset 的修复提示和一个自测用例的名字与注释。

两处原来都说"只改 skills/** 不发布任何包,贴 skip-changeset 标签即可"。现在改成:改 skills/** 会随 @objectstack/skills 一起发布,要写 changeset 并点名这个包,不能贴标签。

为什么改:@objectstack/skills 上线后(#22658),skills/** 已经随 npm 包发布,旧提示和 AGENTS.md 的发布清单相互矛盾。已经出过一次真实问题:PR #23443 照着旧提示贴了标签,那次改动差点进不了 CHANGELOG,到契约复核才被发现。

风险与代价(含回滚):只改文字、注释和一个用例名,两道门禁放行什么、拒绝什么都没有变。

  • 契约复核已逐条核过:每句新说法在 main 上都成立,报错前缀的两处测试钉都还在。
  • CI 43 项:32 通过,11 项按设计跳过,0 失败。
  • 代价是删短了旧提示里早期 skills PR 的那段历史说明。
  • 回滚:撤销这一个 PR 即可。

席位意见:建议批准。

  • 改动小,范围与卡片和认领一致,没有新增门禁行为(分诊明确排除了"让门禁拒收不带 changeset 的 skills PR",那要你另外点头)。
  • 契约复核按你的口令用 opus 跑,PASS。
  • 一处保留:路线 2 末尾"拿不准就选路线 2"那句没动。它本身不算错,这次改动也已经让 skills 不再"拿不准",暂不处理。

你要做的:用 os-zhuang 或 hotlong 任一账号,在 PR #23457 上点 Approve。批准后由席位翻 ready 并入队。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 14, 2026 10:53
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 14, 2026
Merged via the queue into main with commit 8d5b7bb Oct 14, 2026
51 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-23451-skills-changeset-route2 branch October 14, 2026 11:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants