Skip to content

Commit 84a283f

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-25013-select-shorthand-refusal
2 parents 490f11d + f2b6cf2 commit 84a283f

8 files changed

Lines changed: 1347 additions & 295 deletions

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

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -751,46 +751,68 @@ jobs:
751751
1. It releases something
752752
-> run 'pnpm changeset' and name the packages it releases.
753753
754-
WHICH LEVEL: A purely additive widening of a published package's
755-
public surface (a new exported symbol on an `index`, a new accepted
756-
key or value) takes at least `minor`. The commit type may raise a
757-
bump but never lower it below what the act requires; a `fix(` that
758-
widens an index is therefore `minor`, and a `fix(` that changes no
759-
public surface stays `patch`. During the launch window `major` stays
760-
refused by `check-changeset-no-major` and breaking-ness is carried by
761-
the BREAKING banner plus the ADR-0087 disposition, not by the level.
762-
Ruled by the maintainer on 2026-09-04 (decision batch #35) on #15294,
763-
which recorded two contract reviews reading the repo's own history to
764-
OPPOSITE bumps for the same additive act. The commit type is not the
765-
discriminator it looked like: it correlates with the act, and when
766-
they disagree the act wins. The 64 historical `patch` precedents are
767-
pre-rule and nothing is retro-fixed.
754+
WHICH LEVEL: the level follows the METADATA PROTOCOL alone -- the
755+
change to what a metadata author can write -- and the commit type
756+
never decides it (maintainer ruling A, 2026-10-21, on #25105; it
757+
amends the 2026-09-04 rule of decision batch #35 on #15294, under
758+
which nearly every release was a minor: of 95 pending changesets
759+
about half were `minor` and about a dozen moved the protocol).
760+
Declare the kind on a line of its own, in the PR body AND in the
761+
changeset body, beside the `Clause-②:` line:
762+
763+
Metadata-protocol: added a new authorable key, a new accepted
764+
value or enum member, a new metadata
765+
type -> `minor`
766+
Metadata-protocol: narrowed a key removed or tombstoned, a value
767+
refused, a requirement tightened
768+
(declare the `(narrowing)` arm too)
769+
-> `minor`
770+
Metadata-protocol: semantic the meaning or default of an
771+
authorable key changes -> `minor`
772+
Metadata-protocol: none everything else: description or
773+
error text, non-authorable internal
774+
types, REST, drivers, services, the
775+
console, `feat:` and `fix!:` alike
776+
-> `patch`
777+
778+
`added` takes `minor` because spec schemas are not `.strict()`: an
779+
older runtime silently strips a key it does not know, so an app
780+
that writes a new key must be able to name a minor floor. Where
781+
several kinds apply, name the one highest in breaking-ness:
782+
`narrowed` over `semantic` over `added`. A non-protocol breaking
783+
change ships `patch` and KEEPS its carriers: the BREAKING banner
784+
and the ADR-0087 disposition are unchanged. `major` stays refused
785+
by `check-changeset-no-major` during the launch window. The
786+
GitHub Release body is grouped from these declarations by what the
787+
reader must do: 需手动处理 / 自动迁移 / 新增能力 / 语义变化 / 其他修复.
768788
769789
PART OF THIS IS A GATE, and which part is the difference between
770790
grading well and being refused. `check-changeset-no-major.mjs`
771-
refuses `major` and says why in its header. Since #16055 the SAME
772-
script also runs a LEVEL AXIS over the rule above, and that
773-
script's header states its scope: "a PR that DECLARES clause ② (a
774-
new key on a published payload) must grade AT LEAST ONE package
775-
whose published source it moves `minor` or above". It mechanizes
776-
this ruling "for exactly the PRs where the widening is already
777-
DECLARED, and for no others", and its `judgeLevel` verdict table
778-
spells both directions: `not-declared`, "the declaration reads
779-
`no`", exits 0; `enforce`, "declared `yes`, moved packages graded
780-
`patch` and NONE of them graded `minor` or above", exits 1. So on
781-
a PR that declares clause ②, grading `patch` across the board is
782-
refused; on every other PR the axis stands down and the two levels
783-
below `major` are yours, as they always were.
791+
refuses `major` and says why in its header. The SAME script runs
792+
the LEVEL AXIS over the rule above, reading both lines from the PR
793+
body on every `opened`, `synchronize` and `edited` event, and its
794+
`judgeLevel` verdict table spells every direction: a declared kind
795+
must grade at least ONE package whose published source the diff
796+
moves `minor` or above (`enforce`, exit 1 otherwise); a declared
797+
`none` must grade every entry it introduces `patch` (`none-minor`,
798+
exit 1) and may not move a `packages/spec/authorable-surface/` or
799+
`authorable-defaults/` shard (`none-baseline-moved`, exit 1);
800+
`added` beside `Clause-②: no`, or `narrowed` beside a line without
801+
the `(narrowing)` arm, is a `contradiction` (exit 1); and a PR that
802+
introduces a changeset entry with no readable `Metadata-protocol:`
803+
line is refused as NOT MEASURED (exit 1) rather than ticked -- the
804+
body is re-read on `edited`, so that red clears with no push.
784805
785806
WHAT THE AXIS DOES NOT JUDGE, in its own words: "WHICH of the
786807
packages above received that widening is NOT something this gate
787-
can read, and it does not claim to. Clause ② is declared ONCE, FOR
788-
THE PR" -- the declaration line in the PR body names no package.
789-
The axis cross-checks a declaration
790-
against the levels; it never reads the diff to find the act.
791-
Judging the act is still yours, this paragraph is still where the
792-
rule lives, and everything in it the axis does not reach is still
793-
prose.
808+
can read, and it does not claim to. The kind is declared ONCE, FOR
809+
THE PR" -- the declaration lines in the PR body name no package. A
810+
refused value, a narrowed enum or a changed meaning is invisible to
811+
the baselines, so the declaration, not the diff, is the carrier.
812+
The axis cross-checks the declarations against the levels; it never
813+
reads the diff to find the act. Judging the act is still yours,
814+
this paragraph is still where the rule lives, and everything in it
815+
the axis does not reach is still prose.
794816
795817
2. It releases nothing (.github/, .claude/, docs/, content/,
796818
examples/, tests-only, and the like)

‎AGENTS.md‎

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,24 +1081,24 @@ Both non-handshake shapes, and how to classify and probe your own:
10811081
surface** (Prime Directive #14 names them — more than ADRs): push it, open the PR, and stop
10821082
there; landing waits for its tier's record — the maintainer's word, or a seat's contract-tier
10831083
review. For that class, a finished task = a PR left visibly awaiting that record.
1084-
3. **Add a changeset for anything that publishes.** Feature, functional improvement or fix — run `pnpm changeset`
1085-
(or add a `.changeset/*.md` entry) describing it before committing. A bug fix in a released package takes a
1086-
**`patch`** changeset — never none, and ⛔ never `skip-changeset`: that label is for a diff that publishes
1087-
nothing from any released package. The declaration is `Clause-②: yes|no` plus at most one arm from the closed pair
1088-
`(widening)`/`(narrowing)`: `yes` takes at least **`minor`**, `(narrowing)` is BREAKING, `no (widening)` malformed.
1089-
**Breaking changesets must carry their migration.** If the change removes or renames anything an author can write (a
1090-
spec key, an export, a config field), the changeset body must state the FROM → TO mapping and the one-line fix —
1091-
this text ships to consumers as `CHANGELOG.md` inside the npm package and is what an upgrading agent greps after the
1092-
tombstone error. Removing an authorable spec key also requires a tombstone so the rejection itself carries the
1093-
prescription — `retiredKey()` (`packages/spec/src/shared/retired-key.ts`) on the schema whether or not it is
1094-
`.strict()`, and an entry in the shape's `*_RETIRED_KEY_GUIDANCE` map (see `data/mapping.zod.ts`) only for a
1095-
spelling the shape never declared, such as the retired key's old alias, where a tombstone has no property to
1096-
replace. The changeset is one of fourteen surfaces a retirement touches — follow the
1097-
`spec-property-retirement` skill (`.claude/skills/`) rather than reconstructing the kit, and note that a tombstone
1098-
keeps its liveness-ledger row while a key deleted without one loses it.
1099-
**A breaking changeset must also state its ADR-0087 disposition, in writing** — exactly one marker in the changeset
1100-
body, which also carries the PR's `Clause-②` line: `pnpm check:adr-0087-registration` reads the arm there. ⛔ The
1101-
categories are NOT copied here — the gate prints the full set when it fails.
1084+
3. **Add a changeset for anything that publishes** (`pnpm changeset` before committing). A bug fix in a released
1085+
package takes a **`patch`** changeset — never none, and ⛔ never `skip-changeset`: that label is for a diff that
1086+
publishes nothing from any released package. Two line-initial declarations, in the PR body and the changeset body:
1087+
`Clause-②: yes|no` plus at most one arm from the closed pair `(widening)`/`(narrowing)` (`(narrowing)` is
1088+
BREAKING, `no (widening)` malformed), and `Metadata-protocol: added|narrowed|semantic|none`, the change to what a
1089+
metadata author can write — a new key, accepted value or metadata type is `added`; a key removed or tombstoned, a
1090+
value refused or a requirement tightened is `narrowed`; a changed meaning or default is `semantic`; all else is
1091+
`none`; several ⇒ `narrowed` over `semantic` over `added`. The level follows the kind alone: a kind takes at least
1092+
**`minor`** on a package the diff moves, `none` takes **`patch`**; a non-protocol change, widening or breaking,
1093+
ships `patch` and keeps its BREAKING banner and ADR-0087 disposition. **Breaking changesets must carry their
1094+
migration**: removing or renaming anything an author can write (a spec key, an export, a config field) puts the FROM
1095+
→ TO mapping and the one-line fix in the body, which ships as `CHANGELOG.md` in the npm package. Removing an
1096+
authorable spec key also needs a tombstone (`retiredKey()`, `packages/spec/src/shared/retired-key.ts`) so the
1097+
rejection carries the prescription — one of fourteen retirement surfaces; follow the `spec-property-retirement`
1098+
skill (`.claude/skills/`), never reconstruct the kit. **A breaking changeset must also state its ADR-0087
1099+
disposition, in writing** — exactly one marker in the changeset body, which also carries both declaration lines
1100+
(`pnpm check:adr-0087-registration` reads the arm and the kind there). ⛔ The categories are NOT copied here —
1101+
the gate prints the full set when it fails.
11021102
4. **A removal that breaks the pinned sibling checkout ships together with the sibling fix and the pin bump — or it
11031103
does not ship.** The full battery's `Console Pin Gate` builds objectui at the pinned `.objectui-sha` against
11041104
**current** `main`, so such a removal is a main-red the moment it merges; its ruling therefore authorizes the

‎docs/releases-maintenance.md‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,12 @@ repo and bundled into `@objectstack/console` at a pinned commit recorded in
3939
[`.objectui-sha`](../.objectui-sha). A SHA bump alone used to leave **no trace** in
4040
the history. `scripts/bump-objectui.sh` now closes that gap: on every bump it emits
4141
a `@objectstack/console` changeset summarising the objectui `OLD_SHA..NEW_SHA`
42-
commit range (auto-typed `minor` if the range contains any `feat`, else `patch`).
43-
So frontend deltas flow through the *same* pipeline as the backend — they land in
44-
`@objectstack/console`'s changelog and roll up into the platform version.
42+
commit range, graded `patch`: the platform's version line follows the metadata
43+
protocol, which the console never changes, so the file carries
44+
`Metadata-protocol: none` and `CONSOLE_BUMP` is the only thing that grades it
45+
otherwise. So frontend deltas flow through the *same* pipeline as the backend —
46+
they land in `@objectstack/console`'s changelog and roll up into the platform
47+
version.
4548

4649
```bash
4750
scripts/bump-objectui.sh # bump to ../objectui HEAD, write the changeset, commit
@@ -167,6 +170,25 @@ platform version maps to.
167170
page under a "What's new in N.x" running section, or leave them to the generated
168171
per-package changelogs.
169172

173+
### Release notes are grouped by what the reader must do
174+
175+
The GitHub Release body is not the raw CHANGELOG section. `scripts/release-github-releases.mjs`
176+
regroups that section's entries — byte for byte, none dropped — under five headings, in this
177+
order, each read from the declarations the changeset already carries (`Metadata-protocol:`, the
178+
`Clause-②:` arm, the BREAKING banner, the ADR-0087 disposition marker):
179+
180+
| Section | What places an entry there |
181+
|:--|:--|
182+
| 需手动处理 | breaking (`Metadata-protocol: narrowed`, a `(narrowing)` arm, a BREAKING banner or a `!` summary) with no `registered` ADR-0087 conversion |
183+
| 自动迁移 | breaking, with a `registered` conversion that `objectstack migrate meta` applies |
184+
| 新增能力 | `Metadata-protocol: added`, or a non-breaking `Clause-②: yes` widening |
185+
| 语义变化 | `Metadata-protocol: semantic` |
186+
| 其他修复 | everything else — `none`, dependency bumps, and entries written before the declaration existed |
187+
188+
A section with nothing in it is omitted. A curated `vN.mdx` section for the same release uses the
189+
same five headings in the same order, so the hand-written page and the generated body agree on
190+
what a reader must do; the per-package `CHANGELOG.md` keeps changesets' own level grouping.
191+
170192
### Sourcing the Console section for a release
171193

172194
Backend content comes from the spec/package changesets. Frontend content comes from

0 commit comments

Comments
 (0)