Skip to content

Commit 8d5b7bb

Browse files
fix(gates): Check Changeset route 2 and check-empty-changeset stop listing skills/** as releasing nothing (#23457)
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](https://claude.ai/code/session_012VGBku7tEi8DX2uvi1gtXk)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4e0838d commit 8d5b7bb

2 files changed

Lines changed: 20 additions & 18 deletions

File tree

‎.github/workflows/pr-automation.yml‎

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ jobs:
792792
rule lives, and everything in it the axis does not reach is still
793793
prose.
794794
795-
2. It releases nothing (.github/, .claude/, skills/, docs/, content/,
795+
2. It releases nothing (.github/, .claude/, docs/, content/,
796796
examples/, tests-only, and the like)
797797
-> apply the 'skip-changeset' label. <<< PREFERRED
798798
The label is a gate-level exemption. It produces NO input for
@@ -809,15 +809,12 @@ jobs:
809809
still fires a 'labeled' event and that run goes green -- but the
810810
red left here does not clear itself.
811811
812-
'skills/**' is on that list, and it is spelled out because the git
813-
log says otherwise (#5947). Changes to PUBLISHED skills have
814-
repeatedly shipped with an empty changeset instead -- #4607, #5130,
815-
#5451 / PR #5799 -- on the reasoning "skills/ ships with no npm
816-
package, so there is no package to name". That premise is true:
817-
skills/ is not a workspace member and no package's 'files' field
818-
includes it. The conclusion does not follow. Naming no package is
819-
precisely what the LABEL is for; an empty changeset names no package
820-
EITHER, and pays #4898 for the privilege. Take the label.
812+
'skills/**' is NOT on that list: it ships in '@objectstack/skills',
813+
whose build copies it into the 'dist' that package's 'files' field
814+
publishes. A skills/** change releases that package -- route 1,
815+
naming '@objectstack/skills', never the label. Older text here, and
816+
older skills PRs, reasoned "skills/ ships with no npm package, so
817+
there is no package to name"; that premise is no longer true.
821818
822819
3. (CLOSED) An empty-frontmatter changeset. Still present in the
823820
repository's history and still counted by the counting step above,
@@ -839,7 +836,7 @@ jobs:
839836
by nobody. Route 0 is not part of that trade -- it is answered by the diff,
840837
not by judgement, and the label is wrong there however unsure you are.
841838
MSG
842-
echo "::error::This PR adds no changeset. FIRST: if its only .changeset rows are CHANGED, not added, this PR corrects somebody else's pending release note -- do NOT apply 'skip-changeset'; write the confirmation on the PR, naming the note and what changed under it, and leave this check red (#18375). Otherwise, if it releases nothing (including any 'skills/**' change -- see #5947), apply the 'skip-changeset' label; otherwise run 'pnpm changeset' and name the packages. An empty-frontmatter changeset is NOT a third option any more: the step below rejects newly added ones (#5471), because it is a real input to changesets/action and an all-empty set stalls the release silently and greenly (#4898). Full comparison in this step's log."
839+
echo "::error::This PR adds no changeset. FIRST: if its only .changeset rows are CHANGED, not added, this PR corrects somebody else's pending release note -- do NOT apply 'skip-changeset'; write the confirmation on the PR, naming the note and what changed under it, and leave this check red (#18375). Otherwise, if it releases nothing, apply the 'skip-changeset' label; otherwise run 'pnpm changeset' and name the packages (a 'skills/**' change releases '@objectstack/skills' and never takes the label). An empty-frontmatter changeset is NOT a third option any more: the step below rejects newly added ones (#5471), because it is a real input to changesets/action and an all-empty set stalls the release silently and greenly (#4898). Full comparison in this step's log."
843840
exit 1
844841
fi
845842
echo "This PR adds $ADDED changeset(s), so it declares a release of its own."

‎scripts/check-empty-changeset.mjs‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -510,12 +510,12 @@ function report(violations) {
510510
'',
511511
'Pick by what this PR actually releases:',
512512
'',
513-
' * It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/,',
513+
' * It releases nothing (.github/, .claude/, docs/, content/, examples/,',
514514
' tests-only, and the like)',
515515
" -> delete the changeset and apply the 'skip-changeset' label (route 2). The label",
516516
' is a gate-level exemption: it produces NO input for changesets/action.',
517517
'',
518-
' * It releases something',
518+
' * It releases something -- a skills/** change does: it ships in @objectstack/skills',
519519
" -> name the packages in the frontmatter ('pnpm changeset').",
520520
'',
521521
'The empty changesets already on the base commit are EXEMPT and must not be cleaned up',
@@ -676,7 +676,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
676676
'RED 1: a PR that ADDS an empty-frontmatter changeset': 3,
677677
'GREEN 1: the stock. Empty changesets on base, untouched by the PR': 2,
678678
'GREEN 2: a PR that adds a NON-empty changeset': 1,
679-
'GREEN 3: a skills/**-only PR carrying NO changeset (route 2)': 2,
679+
'GREEN 3: a skills/**-only PR carrying NO changeset: nothing to judge': 2,
680680
'RED 2: a stock NON-empty changeset EMPTIED in place': 2,
681681
'GREEN 4: a stock EMPTY changeset whose prose is edited': 2,
682682
'RED 3: a new changeset with no frontmatter fence at all': 1,
@@ -910,10 +910,15 @@ function selfTest() {
910910
assert(r.violations.length === 0, 'GREEN 2: a declaring changeset must be accepted');
911911
}
912912

913-
// ── GREEN 3: a skills/**-only PR carrying NO changeset (route 2) ─────────
914-
// The #5947 destination. Such a PR takes the `skip-changeset` label; this
915-
// gate must have nothing to say about it, label or no label.
916-
battery('GREEN 3: a skills/**-only PR carrying NO changeset (route 2)');
913+
// ── GREEN 3: a skills/**-only PR carrying NO changeset: nothing to judge ─
914+
// This gate reads only the `.changeset/*.md` rows a diff touches, so a PR
915+
// adding none gets nothing from it, label or no label. That is not "it
916+
// releases nothing": `skills/**` ships in `@objectstack/skills` (#22658),
917+
// so the PR owes a changeset naming that package (AGENTS.md's post-task
918+
// checklist), never the `skip-changeset` label. Asking for it is the
919+
// `Require a changeset` step's job, and a wrong label is review's catch;
920+
// neither is this gate's.
921+
battery('GREEN 3: a skills/**-only PR carrying NO changeset: nothing to judge');
917922
{
918923
const { dir, base } = makeRepo(
919924
{ 'skills/objectstack-upgrade/SKILL.md': 'two axes\n', '.changeset/stock-empty.md': EMPTY },

0 commit comments

Comments
 (0)