Repository navigation
Commit a19759a
fix(metadata-protocol): os migrate packageless-overlays moves an email template row of a locale the base does not ship as the pair's own member (#29258)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9Wh1GxXkF35pyjEZvT2LLas)_
Fixes #28904
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:`os migrate packageless-overlays`
现在能迁移「基础包不发运该语言」的旧版无包邮件模板行(例如只发运 `en-US` 的模板的 `zh-CN` 翻译):把它作为配对包
`BASE.custom` 自己的该语言成员写入,不带 `overrides` 声明,回读确认后再删除无包行,结果报 `moved` 并注明
`addedMember`。
- **为什么改**:以前仪式给这类行加上 `overrides` 再存入配对包,被 #28777 的写入闸门拒绝(基础包不发运该语言),结果
`failed`、行原地不动;客户已有的翻译邮件迁不走。
- **风险与回滚**:低。只改迁移仪式的分支和 CLI
的展示文字;写入闸门、读取侧、关闭无包写入的判定都不变。基础包发运的语言照旧按声明式覆盖迁移。回滚即撤销本 PR。
-
**先看**:`packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts`
的 `addedMemberOf` 与迁移循环第 2、3 步的 `[#28904]`
分支;`packages/cli/src/commands/migrate/packageless-overlays.ts` 的预览与报告两行。
- **两分钟验证**:`pnpm --filter @objectstack/metadata-protocol build && pnpm
--filter @objectstack/objectql exec vitest run
src/environment-overlay-email-locale-restart.test.ts -t 28904`,预期 5/5
通过(main 上 2 红)。
- **需要你做的**:无,席位落地。
## What changes
An `email_template` is one item per `(name, locale)`
(`ITEM_KEY_DISCRIMINATORS`). The ceremony classified a package-less row
against the packages that ship its NAME, so a `zh-CN` row of a template
the base ships in `en-US` only was `movable`, and the move saved it into
the pair declaring `overrides: { package: BASE, basedOn }`. The override
write gate counts shippers at the bundle member and refuses that
declaration (`declared_package_ships_no_such_item`, #28777), so the
outcome was `failed` and the row stayed.
`packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts`:
- `addedMemberOf` (private): the bundle member a row's body is, when the
base ships the name but not that member, counted as
`overrideWriteVerdict` counts a shipper (the artifacts at that member,
less any another member answers, bound to the base). `undefined` for a
name-identified type and for a shipped member.
- The census sets `addedMember` on such a `movable` row (new optional
field of `PackagelessOverlayRow`). The disposition vocabulary is
unchanged.
- The move, for a row with `addedMember`: step 2 saves the body into the
pair with NO `overrides` (a stored `overrides` naming the base is
dropped), so no `basedOn` is needed or recorded and a base without a
version does not fail it; step 3 reads back the pair's row at that
member and requires it to declare nothing (`isOwnMemberRow`); step 4
deletes the unbound row as before, its history message naming the
member. Outcome: `{ outcome: 'moved', packageId: PAIR, addedMember }` (a
new arm of `PackagelessOverlayOutcome`, no new outcome word).
- Every other row moves exactly as before.
- The close (`packagelessCloseSubject`) is unchanged: it judges the
declared-override move only, so an unshipped member's package-less write
stays open, as on `main`. Its docblock now says why.
`packages/cli/src/commands/migrate/packageless-overlays.ts`: the preview
lists such a row as `→ PAIR (its own member; BASE ships none at zh-CN)`
instead of `(overrides BASE)`, the `--apply` line reads `moved into PAIR
(as its own member zh-CN)` instead of `(basedOn …)`, and the `--json`
row carries `addedMember` and omits `basedOn` for it. This file is in
the claim's surface conditionally ("only if the preview needs a new
outcome word"); no word was added, but without this edit the preview and
report would print an `overrides` / `basedOn` the move does not record.
## Tests
`packages/objectql/src/environment-overlay-email-locale-restart.test.ts`,
new describe `[#28904] …` (5 cases, the file's real engine, protocol and
in-memory driver; the package ships `acme.digest` in `en-US` only):
1. an unshipped locale moves into the pair as its own member with no
`overrides`; subject, `bodyHtml`, `bodyText`, `replyTo` and
`fromOverride` are unchanged on the execution list the send path reads
and on the by-name read, at once and after a restart; the base's `en-US`
member is untouched; a re-run moves 0;
2. an unshipped-locale row whose stored body declares the base: the
declaration is dropped on the move;
3. READ BACK FIRST: a save that writes nothing keeps the unbound row and
reports `failed`;
4. CONTROL: a shipped locale (`acme.welcome` `zh-CN`) still moves as the
declared override with `basedOn`;
5. the #28777 refusal is unchanged: after the move, a save into the pair
DECLARING an override of the unshipped locale answers `403
NOT_OVERRIDABLE`.
Forced test edits: none. Two docblocks in the same file that said the
ceremony cannot move an unshipped member (the file header and `resetZh`)
now say it moves it as the pair's own member and its package-less reset
stays open; comment-only.
<details><summary>Red on main, green on the fix</summary>
Red: `metadata-protocol` built from `main`'s source (the fix held out of
the tree), cases 1 and 2 red (`os-verify-lock` VERDICT command-exit 1, 2
failed / 2 passed): the census had no `addedMember`, and the move
answered `failed` where `moved` was expected.
Green at `505a5ab7` and again at `c62af5d2`: 5/5; the three ceremony
suites (`environment-overlay-email-locale-restart`,
`protocol-packageless-overlay-migration`,
`environment-overlay-five-types`) 87/87.
</details>
<details><summary>Ablations (one per pinned behaviour; each leg through
ablation-replace.mjs, metadata-protocol rebuilt,
ablation-dist-preflight.mjs marker present, then restored to the HEAD
blob and preflight --absent)</summary>
- A1, census `addedMember` ignored by the move (the old
declared-override save): cases 1, 2, 3 red; case 1's outcome became
`failed` with `… declares an override of 'com.acme.mail', which ships no
email_template named 'acme.digest' with locale 'zh-CN' …`.
- A2, keep a stored `overrides` on the member save: case 2 red (`failed`
where `moved`).
- A3, member read-back always accepts: case 3 red (`moved` where
`failed`).
- A4, the gate's `declared_package_ships_no_such_item` branch disabled
(`protocol-packaged-base-refusal.ts`, mutated and restored, not in the
diff): case 5 red (`expected undefined to match object { code, status
}`).
Every restore: `git hash-object` equals the HEAD blob, `git diff HEAD`
empty, marker absent from `dist/`.
</details>
## Acceptance notes
- **The close stays as on `main`.** #28576 ruling 2 closes a
package-less write where the ceremony would move the row and the move
can succeed. The close still reads the declared-override move only, so
it keeps the unshipped member's package-less write open, now while the
ceremony can move that row. Closing it too would refuse a write accepted
today (a `(narrowing)` arm), needs refusal text in
`protocol-packaged-base-refusal.ts` (next on this seat, #28905) that
does not tell the author to declare `overrides`, and changes the spec
registry entry's prose. Raised in the report's `open_questions`, not
done here.
- **The spec migration entry's prose**
(`18.packageless-overlay-write-closed`, `replacement`) still lists "an
email template locale the base does not ship" among the `failed` moves
and says each `movable` row is moved declaring `overrides`. After this
PR such a row moves as the pair's own member. `packages/spec/**` is out
of this card's surface; reported to the seat for the spec lane.
- **#28780's row kind** (a row already IN the pair declaring an override
of an unshipped locale, stored before #28777) is not touched. This move
takes the same reading as #28780's "drop `overrides`" option: a member
the base does not ship enters the pair declaring nothing.
- `ObjectStackProtocolImplementation#migratePackagelessOverlays`'s
docblock in `protocol.ts` still says "as a declared override". The
module header is the stated authority and is updated; `protocol.ts` is
outside the claim's surface.
- Version history: the move carries what it carried before (a save into
the pair and the unbound row's delete tombstone); no history is copied.
## Verification
All at head `2ebd0841` unless noted (the last commit after the package
runs is comment-only in a test file).
- `@objectstack/metadata-protocol` typecheck exit 0; test 300 files
passed, 4 skipped, 22100 tests passed (at `d485bebc`).
- `@objectstack/objectql` typecheck exit 0; test 527 files, 10143 tests
passed (at `d485bebc`).
- `@objectstack/cli` typecheck exit 0; `vitest run --project unit` 372
files, 5250 tests passed (at `d485bebc`). The integration tier is
declared to CI.
- `eslint --no-inline-config` over `packages/metadata-protocol/src`,
`packages/cli/src/commands/migrate` and the test file: exit 0, no
findings.
- `dispatch-gates --commands` derived 62 families; 62 run; 59 exit 0 at
first run, 3 exit 3 on prerequisites and re-run exit 0 on the same head
(`check-plugin-teardown-shape --self-test` after fetching its pinned
fixture commit; `check:dual-build-cjs-loads` and `check:i18n-coverage`
once the unbuilt packages had dist). `--ran` with exit codes: "62
derived, 62 run, 0 NOT-MEASURED, 0 UNRUN", a derived zero.
- Full dogfood battery: n/a (no `!` in the title, no `(narrowing)` arm).
- `origin/main` merged once before opening (`79774af3`).
Changeset `.changeset/28904-packageless-overlays-added-member.md`:
`@objectstack/metadata-protocol` and `@objectstack/cli` patch, both
declaration lines.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01ADHkmS3jJf5LgjHTU7Cs6W)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 31826d3 commit a19759a
4 files changed
Lines changed: 240 additions & 22 deletions
File tree
- .changeset
- packages
- cli/src/commands/migrate
- metadata-protocol/src
- objectql/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
| 96 | + | |
96 | 97 | | |
97 | | - | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
98 | 101 | | |
99 | 102 | | |
100 | 103 | | |
| |||
376 | 379 | | |
377 | 380 | | |
378 | 381 | | |
379 | | - | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
380 | 388 | | |
381 | 389 | | |
382 | 390 | | |
| |||
395 | 403 | | |
396 | 404 | | |
397 | 405 | | |
398 | | - | |
| 406 | + | |
| 407 | + | |
399 | 408 | | |
400 | 409 | | |
401 | 410 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
70 | 87 | | |
71 | 88 | | |
72 | 89 | | |
| |||
91 | 108 | | |
92 | 109 | | |
93 | 110 | | |
94 | | - | |
95 | | - | |
| 111 | + | |
| 112 | + | |
96 | 113 | | |
97 | 114 | | |
98 | 115 | | |
| |||
138 | 155 | | |
139 | 156 | | |
140 | 157 | | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
141 | 165 | | |
142 | 166 | | |
143 | 167 | | |
| |||
164 | 188 | | |
165 | 189 | | |
166 | 190 | | |
167 | | - | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
168 | 194 | | |
169 | 195 | | |
170 | 196 | | |
| |||
260 | 286 | | |
261 | 287 | | |
262 | 288 | | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
263 | 311 | | |
264 | 312 | | |
265 | 313 | | |
| |||
345 | 393 | | |
346 | 394 | | |
347 | 395 | | |
348 | | - | |
349 | | - | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
350 | 402 | | |
351 | 403 | | |
352 | 404 | | |
| |||
393 | 445 | | |
394 | 446 | | |
395 | 447 | | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
396 | 454 | | |
397 | 455 | | |
398 | 456 | | |
| |||
451 | 509 | | |
452 | 510 | | |
453 | 511 | | |
454 | | - | |
455 | | - | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
456 | 517 | | |
457 | 518 | | |
458 | 519 | | |
459 | 520 | | |
460 | | - | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
461 | 524 | | |
462 | 525 | | |
463 | 526 | | |
464 | | - | |
| 527 | + | |
465 | 528 | | |
466 | 529 | | |
467 | 530 | | |
468 | 531 | | |
469 | | - | |
| 532 | + | |
| 533 | + | |
470 | 534 | | |
471 | | - | |
472 | | - | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
473 | 539 | | |
474 | 540 | | |
475 | 541 | | |
| |||
479 | 545 | | |
480 | 546 | | |
481 | 547 | | |
482 | | - | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
483 | 551 | | |
484 | 552 | | |
485 | 553 | | |
486 | 554 | | |
487 | 555 | | |
488 | 556 | | |
489 | 557 | | |
490 | | - | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
491 | 561 | | |
492 | 562 | | |
493 | 563 | | |
| |||
584 | 654 | | |
585 | 655 | | |
586 | 656 | | |
587 | | - | |
588 | | - | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
589 | 662 | | |
590 | 663 | | |
591 | 664 | | |
| |||
0 commit comments