Skip to content

Commit e3e83ba

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-25079-federated-report-migrate-run
2 parents 55a4c8b + f2b6cf2 commit e3e83ba

19 files changed

Lines changed: 1955 additions & 352 deletions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
'@objectstack/cloud-connection': minor
3+
---
4+
5+
fix(cloud-connection)!: a runtime holding the environment service key is platform-managed, so it registers no Cloud Connection Setup page and refuses to disconnect; everywhere else the Cloud Connection entry moves to Setup's Integrations group
6+
7+
A runtime whose control-plane credential is the environment service key (`controlPlaneApiKey`, or `OS_CLOUD_API_KEY`, which the cloud sets on the environments it hosts) is connected by the platform, not by anyone on the runtime. It still registered the Cloud Connection page in Setup's Apps group, which read "Connected" and offered Disconnect. Disconnecting there cleared the runtime's stored device credential while the service key stayed in use, so the page went on reading "Connected", and the control plane was asked to revoke a connection the platform had made.
8+
9+
Such a runtime now registers neither the page nor its navigation entry, and says so once at boot. A request to disconnect it is refused with `409` and `RESOURCE_CONFLICT`, after the sign-in and Setup Access checks and before anything is revoked or cleared. The message says the platform manages the connection.
10+
11+
A self-hosted or local runtime, connected through the device-code flow, keeps the page and its disconnect unchanged. Its Cloud Connection entry moves from the Apps group to the Integrations group, after Webhooks, Datasources and Connect an Agent. That group requires `manage_platform_settings`, so a user who holds Setup Access without it, such as an organization administrator, no longer sees the entry. The connect and disconnect requests still take Setup Access alone.
12+
13+
Clause-②: no (narrowing)
14+
15+
<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, object definition or stored shape is added, removed, renamed or re-shaped, and no exported declaration is removed or narrowed, so there is nothing for `objectstack migrate meta` to rewrite. What changes is what a runtime holding the environment service key serves: it no longer registers the Cloud Connection page or its navigation entry, and it refuses a disconnect it used to carry out. The navigation entry's new group is a value inside an exported constant whose type is unchanged. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and nothing exported is removed or narrowed (not runtime-interface-only, type-surface-only or runtime-export-retired). -->
16+
17+
**BREAKING** (on a runtime holding the environment service key, a disconnect request it carried out is now refused, and the Cloud Connection page and its navigation entry are no longer served), shipped as `minor` under the launch-window convention for breaking changes.
18+
19+
- **Unchanged:** a runtime without the service key, apart from the entry's group. The connection status answer is unchanged on every runtime.
20+
- **Nothing to rewrite:** a cloud-hosted environment's connection is the platform's to manage. A self-hosted server leaves `OS_CLOUD_API_KEY` unset, as the deployment guide says; one that sets it is platform-managed by this rule.

‎.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

0 commit comments

Comments
 (0)