Skip to content

Commit 1896e31

Browse files
fix(spec): the packageless-overlay-write-closed prose names the ceremony's own-member move (#29457)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi94JJKezo6hNEcFJ8VTnfh4W)_ Fixes #29261 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:只改文字。未发布的升级条目 `packageless-overlay-write-closed`(ADR-0087 语义迁移)和它待发的 changeset 改写为:`os migrate packageless-overlays` 把「基础包没发的邮件模板语言」那一行作为 `.custom` 配对包自己的成员迁过去(不声明 `overrides`,结果 `moved` + `addedMember`),而不再说它迁移失败、也不再说每次迁移都声明 `overrides`;该语言的无包写入在迁移前后都仍然开放。 - 为什么改:PR #29258 已让迁移命令这样做,但这两段文字仍是旧说法,会随发布说明和升级指南原样发出去,误导运维。 - 风险与回滚:无运行时改动,只影响升级指南与发布说明的文字。回滚即 revert 本 PR。 - 先看:`packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts`(replacement 与 reason 两段)、`.changeset/28576-packageless-overlay-write-closed.md`(`movable` 一条与 Still open 一条)。 - 两分钟验证:`pnpm --filter @objectstack/spec check:upgrade-guide`,预期 exit 0;生成的升级指南里出现 `addedMember`,不再有「such as an email template locale the base does not ship」。 - 需要你做的:无,席位落地。 ## What changes Text only. No runtime file is touched. - `packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts` (unreleased ADR-0087 semantic entry): - `replacement`: `--apply` declares `overrides: { package, basedOn }` on each move, **except** a row of an email template locale the base does not ship (the preview names it `addedMember`), which moves as the pair's own member at that locale, declaring nothing, outcome `moved` with that `addedMember` in place of a `basedOn`. The "from then on" writes say an added member is written into the pair with no `overrides` and that its pair DELETE removes it. New sentence: an added member's package-less write stays open, before and after its move. The `failed` clause drops "such as an email template locale the base does not ship" from the pair-refusal example, and scopes the missing-version case to a declared override (the own-member move records no `basedOn`). - `reason`: the 403 applies to a movable row the command moves as a declared override; an email template locale the base does not ship keeps its package-less write. "The close reaches only what the command moves as a declared override". - `acceptanceCriteria`: "a save of a moved declared override" (an added member's save into the pair carries no `overrides`). - The header comment says the same. - `surface` is not touched (that is #29393's widening, not in this card's claim). - `.changeset/28576-packageless-overlay-write-closed.md` (pending, from PR #28576's work, not yet released): - Headline, the `Clause-②:` prose and the `Metadata-protocol:` prose: "can move as a declared override". The two declaration tokens (`yes (narrowing)`, `narrowed`) are unchanged. - "Upgrade, then run the ceremony": the refusal covers what the command moves as a declared override; an own-member move keeps its write. - `movable` bullet: names both outcomes (`moved` with `basedOn`, `moved` with `addedMember`) and says the added member's package-less write stays open; the missing-version case is scoped to a declared override. - FROM → TO: one sentence for the email-locale case (write it into the pair with no `overrides`; one declaring the base is refused). - **Still open**: the email-template-locale case leaves the "the ceremony would not move" list (the ceremony now moves it) and becomes its own sentence: every package-less write of that locale stays open, before and after the move, because the close judges only the declared-override move and the override write gate refuses a declared override of that locale. ### What `main` does (measured at 3291213) - The move: `migratePackagelessOverlays` saves an `addedMember` row into the pair without `overrides` and pushes `{ outcome: 'moved', packageId, addedMember }` ([L519–L560](https://github.com/objectstack-ai/objectstack/blob/329121397b395e688cff1ca9e845e07ba53c652a/packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts#L519-L560)). A missing base version fails only a declared-override move ([L515–L516](https://github.com/objectstack-ai/objectstack/blob/329121397b395e688cff1ca9e845e07ba53c652a/packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts#L515-L516)). - The close: `packagelessCloseSubject` builds the DECLARED body (`overrides: { package: base, basedOn }`) and asks the override write gate; a refusal returns `null`, the write stays open ([L711–L718](https://github.com/objectstack-ai/objectstack/blob/329121397b395e688cff1ca9e845e07ba53c652a/packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts#L711-L718)). The gate refuses a declared override at a locale the base does not ship, so every package-less write of that member (save, delete, publish, rollback; a stored row or none, so before and after the move) is left open. Pinned on main by `packages/objectql/src/environment-overlay-email-locale-restart.test.ts` "the reset (?member=): an unshipped member … (its package-less reset stays open)" ([L906](https://github.com/objectstack-ai/objectstack/blob/329121397b395e688cff1ca9e845e07ba53c652a/packages/objectql/src/environment-overlay-email-locale-restart.test.ts#L906)) beside its CONTROL "a shipped member's unmigrated row: its package-less reset door is closed" (L912). ## Tests No test is added or edited (prose only; no test pins this entry's text: `git grep` for the entry id and for the old phrase hits only the two edited files, the #28905 changeset's reference to the id, and an unrelated dogfood comment). - `packages/spec` migrations suite, `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/migrations` after `gen:migration-registry`, on d241313: Test Files 5 passed (5), Tests 195 passed (195). - Upgrade-guide generator: `check:upgrade-guide` exit 0 (in-memory, protocol 18.0.0, 2 hop sections); `check:spec-changes` exit 0. Generated to a scratch file with `--out`: `addedMember` 1 hit, the dropped phrase "such as an email template locale the base does not ship" 0 hits. The docs pages (`content/docs/protocol-upgrade/`) are gitignored, so no regenerated reference rides this PR. - `packages/metadata-protocol`: no test pins this prose (zero `git grep` hits), so not run; the runtime is unchanged. ## Acceptance notes - **Check Changeset is red by design.** This PR edits a PENDING changeset another PR added (`.changeset/28576-packageless-overlay-write-closed.md`) to remove sentences #29258 made false. `check-empty-changeset --base origin/main` exits 1 with the DELIBERATE CORRECTION class; the remedy it prints is "do NOT restore it — say so on the PR". It is not restored and `skip-changeset` is not added. This PR adds no changeset of its own: the entry is unreleased, and its text ships with the edited changeset's release. - Not touched, outside this card: `skills/objectstack-platform/SKILL.md`'s ADR-0146 paragraph still says a pre-D11 row's "package-less save, reset and rollback are refused once movable (D11)", which an added-locale row (movable, write open) contradicts. A governed surface; reported to the seat, not edited here. ## Verification <details><summary>Gates on d241313</summary> - Build: `turbo run build --concurrency=2 --filter='./packages/**'` under the lock, VERDICT command-exit 0. - `pnpm --filter @objectstack/spec test`: Test Files 791 passed (791), Tests 23469 passed, 1 todo. `pnpm --filter @objectstack/spec typecheck`: VERDICT command-exit 0. - `node scripts/pm/dispatch-gates.mjs --commands`: 76 derived families; every one run on d241313, each `< /dev/null`, exit captured before any pipe. 74 exit 0 on the first pass. - `check-plugin-teardown-shape.mjs --self-test`: exit 3 (PREREQUISITE NOT MET, shallow clone) on the first pass; after `git fetch --depth=1 origin 621a487` exit 0, 48 cases pass. - `check-empty-changeset.mjs --base origin/main`: exit 1, DELIBERATE CORRECTION class on `.changeset/28576-packageless-overlay-write-closed.md` (see Acceptance notes). Not restored, no `skip-changeset`. - `check-changeset-no-major.mjs --base origin/main --event` with this body: exit 0, LEVEL AXIS reads `Clause-②: no` + `Metadata-protocol: none`. - `dispatch-gates --ran`: 76 derived, 76 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero). </details> --- _Generated by [Claude Code](https://claude.ai/code/session_01ADHkmQncBZPJcTEqSfYmqd)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 86d36e9 commit 1896e31

2 files changed

Lines changed: 45 additions & 34 deletions

File tree

‎.changeset/28576-packageless-overlay-write-closed.md‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,23 @@
55
'@object-ui/app-shell': patch
66
---
77

8-
fix(metadata-protocol)!: a write naming no package onto an installed package's view, dashboard, report, translation or email template is refused where `os migrate packageless-overlays` can move its row, naming the base's `.custom` pair and the command
8+
fix(metadata-protocol)!: a write naming no package onto an installed package's view, dashboard, report, translation or email template is refused where `os migrate packageless-overlays` can move its row as a declared override, naming the base's `.custom` pair and the command
99

10-
Clause-②: yes (narrowing) — the metadata save, publish, delete and rollback doors refuse a write that names no package onto an item of the five overlay types that one installed read-only package ships, where the ceremony would classify the row (or the first write's body) `movable` and its move can succeed; they accepted it before as a package-unbound Setup row. Every row the ceremony lists and does not move keeps that write. One write is widened: a rollback of the `.custom` pair's own declared row of an item its base ships (#28644 item 1) is judged as the pair's declared override, where the store sealed it as an artifact override before, on every type the pair can override (a page or flow pair row included).
10+
Clause-②: yes (narrowing) — the metadata save, publish, delete and rollback doors refuse a write that names no package onto an item of the five overlay types that one installed read-only package ships, where the ceremony would classify the row (or the first write's body) `movable` and its move as a declared override can succeed; they accepted it before as a package-unbound Setup row. Every row the ceremony lists and does not move keeps that write. One write is widened: a rollback of the `.custom` pair's own declared row of an item its base ships (#28644 item 1) is judged as the pair's declared override, where the store sealed it as an artifact override before, on every type the pair can override (a page or flow pair row included).
1111

12-
Metadata-protocol: narrowed (the `allowEnvironmentOverlay` flag of `view`, `dashboard`, `report`, `translation` and `email_template` no longer opens a package-less write onto a packaged item the ceremony can move; it stays the predicate the migration ceremony, the read of an unmigrated row and every row the ceremony leaves use)
12+
Metadata-protocol: narrowed (the `allowEnvironmentOverlay` flag of `view`, `dashboard`, `report`, `translation` and `email_template` no longer opens a package-less write onto a packaged item the ceremony can move as a declared override; it stays the predicate the migration ceremony, the read of an unmigrated row and every row the ceremony leaves use)
1313

1414
Skills-docs: updated here — `skills/objectstack-platform/SKILL.md`'s ADR-0146 paragraph said "a package-unbound Setup row still wins over the base's until D11"; it now says such a row from before D11 wins over the base's until `os migrate packageless-overlays` moves it, and that its package-less save, reset and rollback are refused once the command can move it. To stay under the bundle's token ceiling, the same paragraph drops "the base's is unchanged" after the export sentence (which "replaces the base's document whole" already implies) and the aside that the other overridable types wait for their doors (the sentence already names the six types the read honours today).
1515

1616
<!-- adr-0087: registered packageless-overlay-write-closed -->
1717

1818
**BREAKING** write narrowing at the metadata doors (`@objectstack/metadata-protocol`), shipped as `minor` during the launch window.
1919

20-
**Upgrade, then run the ceremony.** The command ships in this release. On each deployment with Setup customizations of a package's items, upgrade, run `os migrate packageless-overlays` (preview) with the flags and environment the deployment boots with (`--preset` and `--dev` mean what they mean to `os serve`), then `os migrate packageless-overlays --apply`. Nothing breaks at the upgrade: between the upgrade and `--apply`, every customization is served as before, and the package-less save, reset, publish and rollback of one the command can move are refused, naming the command. Done when the preview lists no movable row.
20+
**Upgrade, then run the ceremony.** The command ships in this release. On each deployment with Setup customizations of a package's items, upgrade, run `os migrate packageless-overlays` (preview) with the flags and environment the deployment boots with (`--preset` and `--dev` mean what they mean to `os serve`), then `os migrate packageless-overlays --apply`. Nothing breaks at the upgrade: between the upgrade and `--apply`, every customization is served as before, and the package-less save, reset, publish and rollback of one the command moves as a declared override are refused, naming the command (one it moves as the pair's own member keeps that write, see `movable` below). Done when the preview lists no movable row.
2121

2222
The preview lists five dispositions; only `movable` rows are moved, and every other row keeps its package-less write:
2323

24-
- `movable`: `--apply` moves it into the pair. A move that ends `failed` keeps the row as it was; fix what the error names and re-run. When the error names the pair (it cannot be ensured, or it refuses the row) or the base's missing version, the row also keeps its package-less write and may be left.
24+
- `movable`: `--apply` moves it into the pair, as the base's declared override (outcome `moved` with its `basedOn`). A row of an `email_template` locale the base does not ship (the preview lists it as the pair's own member; `--json` carries it as `addedMember`) moves instead as the pair's own member at that locale, declaring nothing (outcome `moved` with that `addedMember` in place of a `basedOn`), since a declared override of a locale the base does not ship is refused; its package-less write stays open, before and after the move. A move that ends `failed` keeps the row as it was; fix what the error names and re-run. When the error names the pair (it cannot be ensured, or it refuses the row) or, for a declared override, the base's missing version, the row also keeps its package-less write and may be left.
2525
- `multi_shipped` (several packages ship the name): delete it naming no package, then save it into the pair of the package it customizes if it is still wanted (the pair's override is then served at that base's address, `?package=<base>`; a read naming no package is not pinned to it while several packages ship the name).
2626
- `no_pair` (the base's id forms no pair): nothing to do.
2727
- `foreign_declaration` (its `overrides` names another package): remove the key with a save naming no package, then re-run: the move declares the right override. (A package-less save that keeps a corrected `overrides` is refused by the override write gate.)
@@ -43,10 +43,10 @@ PUT /api/v1/meta/view/account_list?package=com.acme.crm.custom
4343
{ "name": "account_list", "label": "My accounts", "overrides": { "package": "com.acme.crm", "basedOn": "1.4.0" } }
4444
```
4545

46-
To restore the package original, FROM `DELETE /api/v1/meta/view/account_list` TO `DELETE /api/v1/meta/view/account_list?package=com.acme.crm.custom`. `basedOn` is the base's installed `manifest.version`. The pair is created by `os migrate packageless-overlays --apply`, or by Studio's Customize.
46+
To restore the package original, FROM `DELETE /api/v1/meta/view/account_list` TO `DELETE /api/v1/meta/view/account_list?package=com.acme.crm.custom`. `basedOn` is the base's installed `manifest.version`. The pair is created by `os migrate packageless-overlays --apply`, or by Studio's Customize. A locale of an email template the base does not ship is the pair's own member, not an override: write it into the pair with no `overrides` (one declaring the base is refused), and `DELETE …?package=<base>.custom&member=<locale>` removes it (with no `member`, the delete addresses the canonical locale).
4747

48-
- **Now refused.** A save (draft or publish mode), a publish, a delete of the active item, or a rollback whose restored row is package-less, with no `?package=` (or an empty one), of a `view`, `dashboard`, `report`, `translation` or `email_template` that exactly one installed read-only package ships, where the item's stored package-less row in that state (or, with none, the saved body) is one the ceremony classifies `movable`, the pair can be ensured (the base has a namespace and a SemVer version), and the override write gate admits that row into the pair. This covers an unmigrated `movable` row, and the first package-less write of an item the pair already holds (a moved one). Each answers `403 NOT_OVERRIDABLE`, writes nothing, and its message names the item, the shipping package, the pair (`?package=<base>.custom`), the `overrides` declaration and `os migrate packageless-overlays`; a rollback's message says to save a version from before the move into the pair, declaring it. The metadata item page shows that message as sent when its URL holds no `?package=`. The refusal comes before every gate that reads a stored row's lock, so it is the same with or without `overrides` in the body.
49-
- **Still open.** Every write naming `?package=<base>.custom` (the pair's save, publish, delete and rollback, judged by the override write gate as before); a rollback of the pair's own declared row, which is judged as the pair's override; a write of a name no installed package ships (environment metadata); and every package-less write the ceremony would not move, as before this release: a name several read-only packages ship (`multi_shipped`); a base whose id forms no pair (`no_pair`); a base whose pair cannot be ensured (no namespace, or a version that is not SemVer); a stored package-less row whose `overrides` names another package (`foreign_declaration`) or beside the pair's row of the item (`shadowed`; its package-less writes are addressed to the pair's row as before, so its package-less delete restores the package original); and an `email_template` locale the base does not ship (the pair refuses it, #28777; the ceremony's move of it is #28904). Discarding a draft stays open.
48+
- **Now refused.** A save (draft or publish mode), a publish, a delete of the active item, or a rollback whose restored row is package-less, with no `?package=` (or an empty one), of a `view`, `dashboard`, `report`, `translation` or `email_template` that exactly one installed read-only package ships, where the item's stored package-less row in that state (or, with none, the saved body) is one the ceremony classifies `movable`, the pair can be ensured (the base has a namespace and a SemVer version), and the override write gate admits that row, declared as the base's override, into the pair (so not an `email_template` locale the base does not ship: see **Still open**). This covers an unmigrated `movable` row, and the first package-less write of an item the pair already holds (a moved one). Each answers `403 NOT_OVERRIDABLE`, writes nothing, and its message names the item, the shipping package, the pair (`?package=<base>.custom`), the `overrides` declaration and `os migrate packageless-overlays`; a rollback's message says to save a version from before the move into the pair, declaring it. The metadata item page shows that message as sent when its URL holds no `?package=`. The refusal comes before every gate that reads a stored row's lock, so it is the same with or without `overrides` in the body.
49+
- **Still open.** Every write naming `?package=<base>.custom` (the pair's save, publish, delete and rollback, judged by the override write gate as before); a rollback of the pair's own declared row, which is judged as the pair's override; a write of a name no installed package ships (environment metadata); and every package-less write the ceremony would not move, as before this release: a name several read-only packages ship (`multi_shipped`); a base whose id forms no pair (`no_pair`); a base whose pair cannot be ensured (no namespace, or a version that is not SemVer); a stored package-less row whose `overrides` names another package (`foreign_declaration`) or beside the pair's row of the item (`shadowed`; its package-less writes are addressed to the pair's row as before, so its package-less delete restores the package original). Every package-less write of an `email_template` locale the base does not ship stays open too, before and after the ceremony moves its row as the pair's own member (#28904): the close judges only the declared-override move, and the override write gate refuses a declared override of that locale (#28777). Discarding a draft stays open.
5050
- **Unchanged.** A package-less row stored before this release is still served exactly as before, by every read. The ceremony still moves it: it writes into the pair, never package-less.
5151
- **Studio.** The read-only item notice no longer links to the package-less Setup overlay (`@object-ui/app-shell`).
5252
- **Removed with it.** #28413's interim allowance, by which a package-less save that landed on a moved row kept that row's `overrides` block, is gone: such a save is refused like any other.

0 commit comments

Comments
 (0)