Skip to content

Commit 1bdf674

Browse files
fix(spec): the packageless-overlay-write-closed upgrade entry names both closed overlay write addresses (#29535)
<!-- claude-projects-attribution: {"github_login":"os-warren"} --> _Requested by **Warren** · [project thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ8WDoJDMZf2EGw5BXNyAhGUU)_ Fixes #29393 Clause-②: no (migration entry prose; no accept set moves) Metadata-protocol: none ## 维护者速读 - 改了什么:只改文字。未发布的升级条目 `packageless-overlay-write-closed`(ADR-0087 语义迁移)的 `surface` 与 `reason` 现在同时点名两个被关闭的覆盖写入地址:不带包的写入,和点名只读基础包本身(`?package=基础包`)的保存、发布、回滚,并点名这两种写入各自存下的行;`replacement` 补上绑定在基础包上的行的迁移与处置(与 CLI 打印的一致)。 - 为什么改:#28905 在同一版本里关闭了第二个地址,但这条升级条目只写了第一个;升级指南由它生成,运维读不到第二个地址。来源:合同复核记录 6577747588 的 S3。 - 风险与回滚:无运行时改动,只影响生成的升级指南与 spec-changes 文字。回滚即 revert 本 PR。 - 先看:`packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts` 的 `surface`(两个编号地址)、`reason`(新增两句)与 `replacement`(绑定行的 `foreign_declaration` 处置)。 - 两分钟验证:`pnpm --filter @objectstack/spec check:upgrade-guide`,预期 exit 0;用 `--out` 生成的指南里该条目出现 `naming the read-only package itself` 与 `publish-drafts`。 - 需要你做的:无,席位落地。 ## What changes Text only, one entry. `packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts` (unreleased ADR-0087 semantic entry, shipping in the same release as the two closes): - `surface` now names both closed addresses onto a view, dashboard, report, translation or email template an installed read-only package ships: (1) the write naming no package, worded as before; (2) the write naming the read-only package itself: a save in draft or publish mode or a per-item publish with `?package=BASE`, `POST /api/v1/packages/BASE/publish-drafts` holding such a draft, and a rollback of a row bound to the package, where the pair can be ensured, the base records a version and the override write gate admits the declared body (a delete naming the package keeps the write). The stored-rows clause now covers both: package-less rows, and rows bound to the read-only package itself (the rows `os migrate packageless-overlays` also moves since #29392). - `reason` gains two sentences: what each address stored (a row of no package, which no export carries; a row bound to the package, declaring nothing, served in place of its own document), and the named close (refused `403 NOT_OVERRIDABLE` naming the pair and the `overrides` declaration where the pair can take the declared override; a delete naming the package stays open, and so does every write the pair could not take, an email template locale the base does not ship among them). Everything else in `reason` is unchanged. - `replacement` (claim amendment 6584492790): the movable rows `--apply` moves are package-less OR bound to the read-only package itself (the preview's "bound to", `--json` `boundToBase`, which the ceremony lists and moves since #29392). An unmoved package-less row keeps its package-less write; an unmoved bound row keeps its delete naming the package. A bound `foreign_declaration` row gets the remedy the cli prints (`reasonOf`, the `boundToBase` arm, in `packages/cli/src/commands/migrate/packageless-overlays.ts`): delete it naming the package, then save its body into the pair, declaring `overrides`, since a save naming the package is closed; the package-less remedy is unchanged. The failed-move tail: a bound row keeps its delete naming the package, and the save naming it where the pair could not take the override (for those two causes `namedBaseCloseSubject` answers null). The TO both closes share (write into `BASE.custom`, declaring `overrides`) is unchanged. `acceptanceCriteria` is untouched. - The header comment above the entry names both closed addresses and both kinds of stored row. - `.changeset/29393-overlay-write-entry-both-addresses.md`: `@objectstack/spec` patch, both declaration lines, one sentence each for `surface`/`reason` and `replacement`. Measured against the code on `origin/main` (399df5b): `namedBaseOverlayWriteRefusal` / `namedBaseCloseSubject` in `packages/metadata-protocol/src/protocol-packaged-base-refusal.ts`, and the #28905 changeset's Now refused / Still open lists, which the new wording follows clause for clause. ### The card's conditional line (commit revert path) The card says to add a line if the S1 card decides the commit revert path stays open. The S1 card is #29390; its seat ruling (comment 6579511992) is fork **A**: the revert's restore limb is closed with the same sentence, so the path does not stay open. No such line is added. #29390 has no PR yet; when it lands, its own ADR-0087 disposition decides whether this entry's address (2) also names the commit revert and package rollback. ## Why Contract review 6577747588 of PR #29270, should-fix S3 (the dev's open question 1, answered A): the upgrade guide is generated from this entry, and a reader should meet both closed addresses in one entry. Triage 6582524180 set p1: it lands before the 18.3.0 cut. ## Tests No test pins this entry's text (`git grep` for the entry id hits only the entry and two changesets). The checks that read it, on b99d86a: - `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/migrations scripts/upgrade-guide-docs.test.ts scripts/upgrade-guide-surface-span.test.ts` under the lock, after `pnpm --filter @objectstack/spec build` (VERDICT command-exit 0): Test Files 7 passed (7), Tests 226 passed (226), VERDICT command-exit 0 (also 226/226 on 6b1ea51 and ecf1fd4). - `check:upgrade-guide` exit 0 (protocol 18.0.0, 2 hop sections); `check:spec-changes` exit 0. - Generated with `tsx scripts/build-upgrade-guide.ts --out` to a scratch file: the entry renders as one surface code span holding both numbered addresses; `naming the read-only package itself` 2 hits, `publish-drafts` 5 hits, `boundToBase` 1 hit, `ship the name): delete it naming no package` 1 hit, `and the save naming it where the pair could not take the override` 1 hit. ## Acceptance notes - No checked-in artifact regenerates from this entry: the upgrade guide and `spec-changes.json` are generated in memory at PR time and at publish (`build-upgrade-guide.ts` header), and the docs pages are gitignored. ## Verification <details><summary>Derived gates on ecf1fd4 (b99d86a is a prose-only follow-up in the same file)</summary> - Prerequisite builds under the lock: `pnpm --filter @objectstack/spec build` (VERDICT command-exit 0); `turbo run build --concurrency=2` over the objectql, lint, formula, client and client-react closures (VERDICT command-exit 0, 38/38 tasks). - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` re-derived on ecf1fd4: 76 families (2 paths vs merge base 399df5b, +50/-19). Each run once, stdin from /dev/null, exit captured before any pipe: 75 exit 0 (the teardown self-test after its pinned fixture `621a4876` was fetched on the first round). - `pnpm check:dual-build-cjs-loads`: exit 3 (PREREQUISITE NOT MET: 68 workspace packages unbuilt). NOT MEASURED locally; CI runs it on a full build. - `dispatch-gates --ran`: 76 derived, 75 run, 1 NOT-MEASURED (dual-build, from its recorded exit 3), 0 UNRUN. - `check-changeset-no-major.mjs --base origin/main --event` with this body: exit 0; LEVEL AXIS reads `Clause-②: no` + `Metadata-protocol: none`, patch. - On b99d86a (the review's three text fixes, same file): spec build, the migrations and upgrade-guide tests (226/226), `check:upgrade-guide` and `check:spec-changes` exit 0. The derived set is a property of the changed paths, which did not change. - `check:dev-prereqs` (local-only family): exit 1, the workspace is not fully built (68 of 106 packages); a reading of this container, not of the diff. </details> ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic (prose only). - [x] 2. No name added. - [x] 3. No formatter ran; `pnpm lint` is CI's run. - [x] 4. One issue, one branch off `main`, one pull request. - [x] 5. Patch changeset with both declaration lines, here and in the changeset. - [x] 6. `packages/spec` touched: no checked-in artifact regenerates from this entry (the guide and spec-changes are generated in memory at PR time). - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. English. --- _Generated by [Claude Code](https://claude.ai/code/session_01JQwxbWytk9Ec5dXr38QrwZ)_ <!-- signed: domain:spec#3 · os-warren · session_01JQwxbahe4v7GBD94QeSnir --> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent dbca3ef commit 1bdf674

2 files changed

Lines changed: 66 additions & 29 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): the `packageless-overlay-write-closed` upgrade entry names both closed overlay write addresses, the write naming no package and the write naming the read-only package itself
6+
7+
Clause-②: no (migration entry prose; no accept set moves)
8+
9+
Metadata-protocol: none
10+
11+
The ADR-0087 semantic entry `packageless-overlay-write-closed`, unreleased and shipping in this release, named only the write naming no package in its `surface` and `reason`. The same release also refuses a save, publish or rollback that names the installed read-only package itself (`?package=<base>`) onto a view, dashboard, report, translation or email template it ships, with the same fix the entry's `replacement` already prescribes: write the customization into the base's paired `<base>.custom`, declaring `overrides`. The entry's `surface` now names both addresses, and the stored rows either one left (package-less, or bound to the read-only package); its `reason` says what each address stored, and which writes naming the package stay open (a delete, and every write the pair could not take). Its `replacement` now says that `os migrate packageless-overlays` moves a row bound to the read-only package too, and gives such a row its own `foreign_declaration` remedy, the one the command prints: delete it naming the package, then save its body into the pair, declaring `overrides` (a save naming the package, which would remove the key in place, is closed). The generated protocol upgrade guide shows both in one entry. Text only: no schema, export or runtime behaviour changes.

‎packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts‎

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,43 @@
22

33
import type { SemanticMigration } from '../../types.js';
44

5-
// ADR-0146 D11 — a runtime narrowing at the metadata write doors with no
6-
// authorable key: a write naming no package onto an item an installed package
7-
// ships is refused where `os migrate packageless-overlays` can move its row
8-
// as a declared override (a row it moves as the pair's own member stays open).
9-
// Registered because an upgrading deployment can carry the package-less Setup
10-
// rows those writes made, and the operator step that moves them into the
11-
// base's `.custom` pair ships in this same release, so it runs AFTER the
12-
// upgrade (the release-timing ruling on the card).
5+
// ADR-0146 D1, D11 — a runtime narrowing at the metadata write doors with no
6+
// authorable key, at the two addresses that bypassed a read-only package's
7+
// paired `.custom`: a write naming no package onto an item an installed
8+
// package ships is refused where `os migrate packageless-overlays` can move
9+
// its row as a declared override (a row it moves as the pair's own member
10+
// stays open), and a save, publish or rollback naming the read-only package
11+
// itself onto an item it ships is refused where the pair can take the
12+
// declared override (a delete naming the package stays open).
13+
// Registered because an upgrading deployment can carry the rows those writes
14+
// made, package-less or bound to the read-only package itself, and the
15+
// operator step that moves both kinds into the base's `.custom` pair ships in
16+
// this same release, so it runs AFTER the upgrade (the release-timing ruling
17+
// on the card).
1318
export const entry: SemanticMigration = {
1419
id: 'packageless-overlay-write-closed',
1520
surface:
16-
'a metadata write that names no package (PUT, DELETE, publish or rollback under '
17-
+ '/api/v1/meta/<type>/<name> with no ?package=, and the metadata item page, in Studio\'s All '
18-
+ 'metadata area where Setup\'s metadata URLs now land, when its URL holds no ?package=) onto a '
19-
+ 'view, dashboard, report, translation or email template that one installed read-only package '
20-
+ 'ships, where `os migrate packageless-overlays` would move the row into that package\'s '
21-
+ 'paired `<base>.custom` as a declared override (a row it moves as the pair\'s own member, an '
22-
+ 'email template locale the base does not ship, keeps the write); and the package-less '
23-
+ 'sys_metadata rows Setup\'s form stored that way',
21+
'a metadata write onto a view, dashboard, report, translation or email template that an '
22+
+ 'installed read-only package ships, at either of the two addresses that bypassed that '
23+
+ 'package\'s paired `<base>.custom`: (1) naming no package (PUT, DELETE, publish or rollback '
24+
+ 'under /api/v1/meta/<type>/<name> with no ?package=, and the metadata item page, in Studio\'s '
25+
+ 'All metadata area where Setup\'s metadata URLs now land, when its URL holds no ?package=), '
26+
+ 'where one installed read-only package ships the item and `os migrate packageless-overlays` '
27+
+ 'would move the row into the pair as a declared override (a row it moves as the pair\'s own '
28+
+ 'member, an email template locale the base does not ship, keeps the write); (2) naming the '
29+
+ 'read-only package itself (a save in draft or publish mode or a per-item publish under '
30+
+ '/api/v1/meta/<type>/<name>?package=<base>, POST /api/v1/packages/<base>/publish-drafts '
31+
+ 'holding such a draft, and a rollback of a row bound to the package), where the pair can be '
32+
+ 'ensured, the base records a version and the override write gate admits the declared body '
33+
+ 'into the pair (a delete naming the package keeps the write); and the sys_metadata rows '
34+
+ 'those writes stored, package-less or bound to the read-only package itself',
2435
replacement:
2536
'after upgrading, run `os migrate packageless-overlays` (preview) on each deployment with '
2637
+ 'Setup customizations, with the flags and environment the deployment boots with (`--preset` '
2738
+ 'and `--dev` mean what they mean to `os serve`), then `os migrate packageless-overlays '
28-
+ '--apply`: it moves each `movable` row into the base\'s paired `<base>.custom`, declaring '
39+
+ '--apply`: it moves each `movable` row (package-less, or bound to the read-only package '
40+
+ 'itself, which the preview names "bound to" that package and `--json` marks `boundToBase`) '
41+
+ 'into the base\'s paired `<base>.custom`, declaring '
2942
+ '`overrides: { package: <base>, basedOn: <installed version> }`, except a row of an email '
3043
+ 'template locale the base does not ship (the preview lists it as the pair\'s own member; '
3144
+ '`--json` carries it as `addedMember`), which it moves as the pair\'s own member at that '
@@ -35,32 +48,45 @@ export const entry: SemanticMigration = {
3548
+ 'in the body (an added member\'s with none), and `DELETE /api/v1/meta/<type>/<name>?package='
3649
+ '<base>.custom` to restore the package original (an added member\'s, with '
3750
+ '`&member=<locale>`, removes it). An added '
38-
+ 'member\'s package-less write stays open, before and after its move. Every row the preview '
39-
+ 'lists and does not move keeps its package-less write: `multi_shipped` (several packages ship '
40-
+ 'the name): delete it naming no package, then save it into the pair of the package it '
41-
+ 'customizes if it is still wanted (the pair\'s override is then served at that base\'s '
42-
+ 'address, `?package=<base>`); `no_pair` (the base\'s id forms no pair): nothing to do; '
51+
+ 'member\'s package-less write stays open, before and after its move. Every package-less row '
52+
+ 'the preview lists and does not move keeps its package-less write, and every bound row it '
53+
+ 'lists and does not move keeps its delete naming the package: `multi_shipped` (several '
54+
+ 'packages ship the name): delete it naming no package, then save it into the pair of the '
55+
+ 'package it customizes if it is still wanted (the pair\'s override is then served at that '
56+
+ 'base\'s address, `?package=<base>`); `no_pair` (the base\'s id forms no pair): nothing to do; '
4357
+ '`foreign_declaration` (its `overrides` names another package): remove the key with a save '
44-
+ 'naming no package, then re-run: the move declares the right override; `shadowed` (the pair '
58+
+ 'naming no package, then re-run: the move declares the right override (a row bound to the '
59+
+ 'read-only package cannot take that save, as a save naming the package is closed: delete it '
60+
+ 'naming the package, then save its body into the pair, declaring `overrides: { package: '
61+
+ '<base>, basedOn }`); `shadowed` (the pair '
4562
+ 'already holds the item; the pair\'s row is served when it declares the override, otherwise '
4663
+ 'this row still is): nothing to do to keep the pair\'s declared override, or restore the '
4764
+ 'package original (the DELETE above) and re-run to move this one; a `failed` move: fix what '
4865
+ 'the error names and re-run; when the error names the pair (it cannot be ensured, or it '
4966
+ 'refuses the row) or, for a declared override, the base\'s missing version, the row may also '
50-
+ 'be left, as it keeps its package-less write',
67+
+ 'be left, as it keeps its package-less write (a bound row, its delete naming the package, '
68+
+ 'and the save naming it where the pair could not take the override)',
5169
reason:
5270
'A customization of an installed package lives in its paired `.custom` package, where it is '
5371
+ 'a declared override that is exported with the pair and judged against the base\'s version '
54-
+ '(ADR-0146 D1, D2, D11). The package-less write made a row of no package instead, which no '
55-
+ 'export carries. Rows already stored that way are still served unchanged, so nothing breaks '
56-
+ 'at the upgrade. Between the upgrade and `--apply`, every customization is served, and the '
72+
+ '(ADR-0146 D1, D2, D11). Two writes bypassed the pair: the package-less write made a row of '
73+
+ 'no package, which no export carries, and the write naming the read-only package itself made '
74+
+ 'a row bound to that package, declaring nothing, served in place of the package\'s own '
75+
+ 'document. Rows already stored either way are still served unchanged, so nothing breaks at '
76+
+ 'the upgrade. Between the upgrade and `--apply`, every customization is served, and the '
5777
+ 'package-less save, publish, delete or rollback of a movable one the command moves as a '
5878
+ 'declared override is refused 403 NOT_OVERRIDABLE naming the pair and this command; an '
5979
+ 'email template locale the base does not ship, which the command moves as the pair\'s own '
6080
+ 'member, keeps its package-less write. The close reaches only what the command moves as a '
6181
+ 'declared override (its own classification and its own move steps), so no customization is '
62-
+ 'left without a write door. No conversion applies: the rows are stored data, not authored '
63-
+ 'source, and the move goes through the save door. ADR-0146, ADR-0087.',
82+
+ 'left without a write door. The save, publish or rollback naming the read-only package '
83+
+ 'itself is refused 403 NOT_OVERRIDABLE naming the pair and the `overrides` declaration '
84+
+ 'wherever the pair can take the declared override (it can be ensured, the base records a '
85+
+ 'version and the override write gate admits the declared body); a delete naming the package '
86+
+ 'stays open, as the way to remove such a row, and so does every write the pair could not '
87+
+ 'take, an email template locale the base does not ship among them. No conversion applies: '
88+
+ 'the rows are stored data, not authored source, and the move goes through the save door. '
89+
+ 'ADR-0146, ADR-0087.',
6490
acceptanceCriteria:
6591
'On the deployment\'s database and configuration, with its boot flags and environment, the '
6692
+ 'preview of `os migrate packageless-overlays` lists no movable row. Each customization is then '

0 commit comments

Comments
 (0)