Repository navigation
Commit f285338
fix(metadata-protocol): name a stored object_extension conversion notice by its target, one notice per row (#29716)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ82Kk5X4KDzo3EwRJCHqDmGG)_
Fixes #29596
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:协议层存量行转换告警改用条目自身的身份命名,`object_extension` 用其 `extend` 目标,每行各报一条。
- 为什么改:扩展体没有 `name`,此前所有过期扩展行都报成 `object_extension/` 加 unnamed
占位,且共用一个去重键,三行只报一条。
- 风险与回滚:只动告警文字与去重键,行的转换结果不变;其它类型照旧按 `name` 命名。回滚即撤销本 PR。
- 先看:`packages/metadata-protocol/src/protocol-stored-item-conversion.ts`
的一行 `listSlotNameOf`;其次新测试文件。
- 两分钟验证:`pnpm --filter @objectstack/metadata-protocol exec vitest run
src/protocol-stored-item-conversion.notice-name.test.ts`,应见 4 个测试通过。
- 需要你做的:无,席位落地。
## What changes
- `packages/metadata-protocol/src/protocol-stored-item-conversion.ts`
`convertStoredItem`: the notice's name, and the warn-once key built from
it, read `listSlotNameOf(singular, data)` instead of `data.name`. That
helper is the one list reads already slot an item by: an item's own
`name`, and for an `object_extension` body (which carries `extend` and
no `name`) its target. The module header gains one sentence saying the
notice's name read has changed since the #25467 move. No new spelling;
the body is not given a `name`.
- `packages/metadata/src/loaders/database-loader.ts`: **no change.**
Measured below, its notice reads the stored row's `name` column, which
for an extension row is already its target.
- Tests: `protocol-stored-item-conversion.notice-name.test.ts` (new) and
one `describe` in `database-loader.test.ts`.
- `.changeset/29596-extension-notice-name.md`: `patch` on
`@objectstack/metadata-protocol` (the function ships in its `dist`).
## Why
Measured first, on a real store: the runtime rig of
`meta-object-extension-item.test.ts` (better-sqlite3 + real `ObjectQL` +
real `ObjectStackProtocolImplementation`). Two extensions
(`crm_account`, `crm_contact`) were saved and published through the save
door into `com.acme.crm.custom`, then each stored row's `time` default
was made stale (`12:00Z`). The rows were then read through
`getMetaItems`, `getMetaItem` (twice) and `loadMetaFromDb`, and through
a `DatabaseLoader` over the same engine. The probe was a scratch test
and is not committed.
- **Protocol seam, `data.name`:** absent on all six `convertStoredItem`
calls (`'name' in data` false; `extend` = `crm_account` /
`crm_contact`). Output: ONE line, `[Protocol] stored object_extension/`
followed by the unnamed placeholder, for both rows.
- **Database-loader seam, `row.name`:** `crm_account` and `crm_contact`,
the target names the save door stores the rows under. Output: two lines,
`[DatabaseLoader] stored object_extension/crm_account …` and `…
/crm_contact …`.
So the card's premise holds for the protocol seam and not for the loader
seam. The loader already names each row by the identity every type is
stored under, so it only gets a pin.
## Tests
- `protocol-stored-item-conversion.notice-name.test.ts`, run through the
module's own `convertStoredItem` over the real chain
(`convertStoredItemDetailed`):
- two stale extension rows give two notices,
`object_extension/crm_account` and `object_extension/crm_contact`; the
row still converts, and the body gains no `name`;
- re-reading one row does not warn again (the dedupe still holds per
row);
- CONTROL: `object` rows are still named by `name` (`object/shift`,
`object/rota`), and a nameless body still gets the unnamed placeholder;
- CONTROL: an `extend` key on a non-extension type does not name it.
- `database-loader.test.ts` › `stored conversion notice names the row
(#29596)`: two stale extension rows loaded through `loadMany` give two
notices naming their targets, and the defaults come back converted.
CONTROL: `object` rows are named by row name.
<details><summary>Ablation (committed first, restored by blob
hash)</summary>
`node scripts/ablation-replace.mjs` put the old read back (`const name =
(data as { name?: unknown } | null | undefined)?.name;`). Anchor 1 → 0,
blob `17ae402a` → `c4063b6d`. Result: 2 failed / 2 passed. Both
extension cases fail with received `["object_extension/" + unnamed
placeholder]` (one line for two rows); both CONTROLs stay green.
Restored blob `17ae402a` == HEAD, and `git diff HEAD` is empty. The test
imports the module from `src` by relative path, so no `dist` rebuild was
involved.
</details>
## Acceptance notes
- No overlap found before editing: of 37 open PRs, none touches either
file. #29683 touches the sibling `protocol-meta-layered-reads.ts` only.
No live `Claim:` covers `convertStoredItem` or the loader's notice
block; the cut-23 claim on #25467 is for the move that already landed.
- No `protocol.ts` or `packages/spec` edit.
## Verification
At head `90c78f06` (branch off `origin/main` `1c1a5d40`):
- `pnpm --filter @objectstack/metadata-protocol --filter
@objectstack/metadata typecheck`: exit 0. `tsc --listFiles` includes
both test files.
- `pnpm --filter @objectstack/metadata-protocol --filter
@objectstack/metadata test`: exit 0. metadata 68 files / 958 tests;
metadata-protocol 309 passed + 4 skipped files, 22266 passed + 26
skipped tests.
- `node scripts/pm/dispatch-gates.mjs --commands`: 57 derived. `--ran`:
57 accounted, 56 run, 1 NOT MEASURED, 0 unrun.
- NOT MEASURED: `pnpm check:dual-build-cjs-loads` (exit 3, PREREQUISITE
NOT MET: it needs every package built). CI's `Lint & Repo Gates` runs
it.
- `check-plugin-teardown-shape --self-test` first exited 3 on the
shallow clone. After fetching its pinned fixture commit, it exits 0.
- The derived run started at `de3bb7a9`. The final commit only types the
two tests' `console.warn` spies. The 18 test-reading gates were rerun at
`90c78f06`, all exit 0: engine-double-contract, objectql-double-limit,
test-source-alias, cross-package-test-inputs, rest-log-spy-declared,
refd-timer-probe, where-matcher, logger-receiver-detach, nul-bytes,
issue-citations (both), comment-mask-adoption, durability-log-level,
registry-log-declared, system-context-census, tier-file-adoption,
type-check-coverage, keyed-text-bounds.
- `pnpm lint`: exit 0 at `90c78f06`.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`packages/spec` untouched.)
- [x] 2. Every name I added uses the casing for its kind. (No new
exported name.)
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: not touched.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 block is the
dispatch contract's Chinese summary).
Signed domain:server#1 · os-warren · session_01JQwxbZWEwRacLdvKTeMCb2
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01JQwxbZWEwRacLdvKTeMCb2
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbZWEwRacLdvKTeMCb2)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent c76c0e0 commit f285338
4 files changed
Lines changed: 149 additions & 2 deletions
File tree
- .changeset
- packages
- metadata-protocol/src
- metadata/src/loaders
| 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 | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
17 | 19 | | |
18 | 20 | | |
19 | 21 | | |
| |||
39 | 41 | | |
40 | 42 | | |
41 | 43 | | |
| 44 | + | |
42 | 45 | | |
43 | 46 | | |
44 | 47 | | |
| |||
60 | 63 | | |
61 | 64 | | |
62 | 65 | | |
63 | | - | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
64 | 70 | | |
65 | 71 | | |
66 | 72 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
375 | 375 | | |
376 | 376 | | |
377 | 377 | | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
378 | 417 | | |
379 | 418 | | |
380 | 419 | | |
| |||
0 commit comments