From 6b1ea51427a117afcdb3db21436fd3bc299f583f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 17:31:38 +0000 Subject: [PATCH 1/3] fix(spec): the packageless-overlay-write-closed entry names both closed overlay write addresses The unreleased ADR-0087 semantic entry's surface and reason named only the write naming no package. The same release also closes the save, publish and rollback naming the read-only package itself onto its five overlay types, onto the TO the entry's replacement already prescribes. One entry now names both addresses and the rows each stored. Text only. Claude-Session: https://claude.ai/code/session_01JQwxbWytk9Ec5dXr38QrwZ Co-authored-by: Claude --- ...9393-overlay-write-entry-both-addresses.md | 11 +++++ .../18.packageless-overlay-write-closed.ts | 40 +++++++++++++------ 2 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 .changeset/29393-overlay-write-entry-both-addresses.md diff --git a/.changeset/29393-overlay-write-entry-both-addresses.md b/.changeset/29393-overlay-write-entry-both-addresses.md new file mode 100644 index 00000000000..3fd3d7e091c --- /dev/null +++ b/.changeset/29393-overlay-write-entry-both-addresses.md @@ -0,0 +1,11 @@ +--- +'@objectstack/spec': patch +--- + +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 + +Clause-②: no (migration entry prose; no accept set moves) + +Metadata-protocol: none + +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=`) 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 `.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). The generated protocol upgrade guide shows both in one entry. Text only: no schema, export or runtime behaviour changes. diff --git a/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts b/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts index 15262eb0186..cfcc18bfc8b 100644 --- a/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts +++ b/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts @@ -13,14 +13,20 @@ import type { SemanticMigration } from '../../types.js'; export const entry: SemanticMigration = { id: 'packageless-overlay-write-closed', surface: - 'a metadata write that names no package (PUT, DELETE, publish or rollback under ' - + '/api/v1/meta// with no ?package=, and the metadata item page, in Studio\'s All ' - + 'metadata area where Setup\'s metadata URLs now land, when its URL holds no ?package=) onto a ' - + 'view, dashboard, report, translation or email template that one installed read-only package ' - + 'ships, where `os migrate packageless-overlays` would move the row into that package\'s ' - + 'paired `.custom` as a declared override (a row it moves as the pair\'s own member, an ' - + 'email template locale the base does not ship, keeps the write); and the package-less ' - + 'sys_metadata rows Setup\'s form stored that way', + 'a metadata write onto a view, dashboard, report, translation or email template that an ' + + 'installed read-only package ships, at either of the two addresses that bypassed that ' + + 'package\'s paired `.custom`: (1) naming no package (PUT, DELETE, publish or rollback ' + + 'under /api/v1/meta// with no ?package=, and the metadata item page, in Studio\'s ' + + 'All metadata area where Setup\'s metadata URLs now land, when its URL holds no ?package=), ' + + 'where one installed read-only package ships the item and `os migrate packageless-overlays` ' + + 'would move the row into the pair as a declared override (a row it moves as the pair\'s own ' + + 'member, an email template locale the base does not ship, keeps the write); (2) naming the ' + + 'read-only package itself (a save in draft or publish mode or a per-item publish under ' + + '/api/v1/meta//?package=, POST /api/v1/packages//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 ' + + 'into the pair (a delete naming the package keeps the write); and the sys_metadata rows ' + + 'those writes stored, package-less or bound to the read-only package itself', replacement: 'after upgrading, run `os migrate packageless-overlays` (preview) on each deployment with ' + 'Setup customizations, with the flags and environment the deployment boots with (`--preset` ' @@ -51,16 +57,24 @@ export const entry: SemanticMigration = { reason: 'A customization of an installed package lives in its paired `.custom` package, where it is ' + 'a declared override that is exported with the pair and judged against the base\'s version ' - + '(ADR-0146 D1, D2, D11). The package-less write made a row of no package instead, which no ' - + 'export carries. Rows already stored that way are still served unchanged, so nothing breaks ' - + 'at the upgrade. Between the upgrade and `--apply`, every customization is served, and the ' + + '(ADR-0146 D1, D2, D11). Two writes bypassed the pair: the package-less write made a row of ' + + 'no package, which no export carries, and the write naming the read-only package itself made ' + + 'a row bound to that package, declaring nothing, served in place of the package\'s own ' + + 'document. Rows already stored either way are still served unchanged, so nothing breaks at ' + + 'the upgrade. Between the upgrade and `--apply`, every customization is served, and the ' + 'package-less save, publish, delete or rollback of a movable one the command moves as a ' + 'declared override is refused 403 NOT_OVERRIDABLE naming the pair and this command; an ' + 'email template locale the base does not ship, which the command moves as the pair\'s own ' + 'member, keeps its package-less write. The close reaches only what the command moves as a ' + 'declared override (its own classification and its own move steps), so no customization is ' - + 'left without a write door. No conversion applies: the rows are stored data, not authored ' - + 'source, and the move goes through the save door. ADR-0146, ADR-0087.', + + 'left without a write door. The save, publish or rollback naming the read-only package ' + + 'itself is refused 403 NOT_OVERRIDABLE naming the pair and the `overrides` declaration ' + + 'wherever the pair can take the declared override (it can be ensured, the base records a ' + + 'version and the override write gate admits the declared body); a delete naming the package ' + + 'stays open, as the way to remove such a row, and so does every write the pair could not ' + + 'take, an email template locale the base does not ship among them. No conversion applies: ' + + 'the rows are stored data, not authored source, and the move goes through the save door. ' + + 'ADR-0146, ADR-0087.', acceptanceCriteria: 'On the deployment\'s database and configuration, with its boot flags and environment, the ' + 'preview of `os migrate packageless-overlays` lists no movable row. Each customization is then ' From ecf1fd4a39fb1e0b188e276bfa3ba14c62eea2e7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 18:04:58 +0000 Subject: [PATCH 2/3] fix(spec): the packageless-overlay-write-closed replacement covers rows bound to the read-only package Since the ceremony also moves rows bound to the read-only package itself, the entry's replacement names them, and gives a bound foreign_declaration row the remedy the cli prints (delete it naming the package, then save its body into the pair, declared), since a save naming the package is closed. The package-less remedies are unchanged. Text only. Claude-Session: https://claude.ai/code/session_01JQwxbWytk9Ec5dXr38QrwZ Co-authored-by: Claude --- ...29393-overlay-write-entry-both-addresses.md | 2 +- .../18.packageless-overlay-write-closed.ts | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.changeset/29393-overlay-write-entry-both-addresses.md b/.changeset/29393-overlay-write-entry-both-addresses.md index 3fd3d7e091c..cf08f5e7105 100644 --- a/.changeset/29393-overlay-write-entry-both-addresses.md +++ b/.changeset/29393-overlay-write-entry-both-addresses.md @@ -8,4 +8,4 @@ Clause-②: no (migration entry prose; no accept set moves) Metadata-protocol: none -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=`) 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 `.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). The generated protocol upgrade guide shows both in one entry. Text only: no schema, export or runtime behaviour changes. +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=`) 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 `.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. diff --git a/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts b/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts index cfcc18bfc8b..9ca0133e002 100644 --- a/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts +++ b/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts @@ -31,7 +31,9 @@ export const entry: SemanticMigration = { 'after upgrading, run `os migrate packageless-overlays` (preview) on each deployment with ' + 'Setup customizations, 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`: it moves each `movable` row into the base\'s paired `.custom`, declaring ' + + '--apply`: it moves each `movable` row (package-less, or bound to the read-only package ' + + 'itself, which the preview names "bound to" that package and `--json` marks `boundToBase`) ' + + 'into the base\'s paired `.custom`, declaring ' + '`overrides: { package: , basedOn: }`, except 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`), which it moves as the pair\'s own member at that ' @@ -41,19 +43,23 @@ export const entry: SemanticMigration = { + 'in the body (an added member\'s with none), and `DELETE /api/v1/meta//?package=' + '.custom` to restore the package original (an added member\'s, with ' + '`&member=`, removes it). An added ' - + 'member\'s package-less write stays open, before and after its move. Every row the preview ' - + 'lists and does not move keeps its package-less write: `multi_shipped` (several packages ship ' - + 'the name): delete it naming no package, then save it into the pair of the package it ' + + 'member\'s package-less write stays open, before and after its move. Every package-less row ' + + 'the preview lists and does not move keeps its package-less write, and every bound row it ' + + 'lists and does not move keeps its delete naming the package: `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=`); `no_pair` (the base\'s id forms no pair): nothing to do; ' + '`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; `shadowed` (the pair ' + + 'naming no package, then re-run: the move declares the right override (a row bound to the ' + + 'read-only package cannot take that save, as a save naming the package is closed: delete it ' + + 'naming the package, then save its body into the pair, declaring `overrides: { package: ' + + ', basedOn }`); `shadowed` (the pair ' + 'already holds the item; the pair\'s row is served when it declares the override, otherwise ' + 'this row still is): nothing to do to keep the pair\'s declared override, or restore the ' + 'package original (the DELETE above) and re-run to move this one; a `failed` move: 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 may also ' - + 'be left, as it keeps its package-less write', + + 'be left, as it keeps its package-less write (a bound row, its delete naming the package)', reason: 'A customization of an installed package lives in its paired `.custom` package, where it is ' + 'a declared override that is exported with the pair and judged against the base\'s version ' From b99d86a144ed9c23d9170c5802a2f27ac480dd90 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 18:38:44 +0000 Subject: [PATCH 3/3] fix(spec): the packageless-overlay-write-closed header and failed-move tail name both closes Contract review should-fixes on the entry: restore the dropped space in the multi_shipped remedy, the header comment names both closed addresses and both kinds of stored row, and a bound row's failed-move tail also keeps the save naming the package where the pair could not take the override. Text only. Claude-Session: https://claude.ai/code/session_01JQwxbWytk9Ec5dXr38QrwZ Co-authored-by: Claude --- .../18.packageless-overlay-write-closed.ts | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts b/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts index 9ca0133e002..f33a735b4c6 100644 --- a/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts +++ b/packages/spec/src/migrations/entries/semantic/18.packageless-overlay-write-closed.ts @@ -2,14 +2,19 @@ import type { SemanticMigration } from '../../types.js'; -// ADR-0146 D11 — a runtime narrowing at the metadata write doors with no -// authorable key: a write naming no package onto an item an installed package -// ships is refused where `os migrate packageless-overlays` can move its row -// as a declared override (a row it moves as the pair's own member stays open). -// Registered because an upgrading deployment can carry the package-less Setup -// rows those writes made, and the operator step that moves them into the -// base's `.custom` pair ships in this same release, so it runs AFTER the -// upgrade (the release-timing ruling on the card). +// ADR-0146 D1, D11 — a runtime narrowing at the metadata write doors with no +// authorable key, at the two addresses that bypassed a read-only package's +// paired `.custom`: a write naming no package onto an item an installed +// package ships is refused where `os migrate packageless-overlays` can move +// its row as a declared override (a row it moves as the pair's own member +// stays open), and a save, publish or rollback naming the read-only package +// itself onto an item it ships is refused where the pair can take the +// declared override (a delete naming the package stays open). +// Registered because an upgrading deployment can carry the rows those writes +// made, package-less or bound to the read-only package itself, and the +// operator step that moves both kinds into the base's `.custom` pair ships in +// this same release, so it runs AFTER the upgrade (the release-timing ruling +// on the card). export const entry: SemanticMigration = { id: 'packageless-overlay-write-closed', surface: @@ -46,9 +51,9 @@ export const entry: SemanticMigration = { + 'member\'s package-less write stays open, before and after its move. Every package-less row ' + 'the preview lists and does not move keeps its package-less write, and every bound row it ' + 'lists and does not move keeps its delete naming the package: `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=`); `no_pair` (the base\'s id forms no pair): nothing to do; ' + + '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=`); `no_pair` (the base\'s id forms no pair): nothing to do; ' + '`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 row bound to the ' + 'read-only package cannot take that save, as a save naming the package is closed: delete it ' @@ -59,7 +64,8 @@ export const entry: SemanticMigration = { + 'package original (the DELETE above) and re-run to move this one; a `failed` move: 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 may also ' - + 'be left, as it keeps its package-less write (a bound row, its delete naming the package)', + + 'be left, as it keeps its package-less write (a bound row, its delete naming the package, ' + + 'and the save naming it where the pair could not take the override)', reason: 'A customization of an installed package lives in its paired `.custom` package, where it is ' + 'a declared override that is exported with the pair and judged against the base\'s version '