Repository navigation
Commit a81b7cb
fix(metadata): a packages[] stack's top-level dataset no body declares registers under the stack's id (#29593)
<!-- claude-projects-attribution: {"github_login":"os-warren"} -->
_Requested by **Warren** · [project
thread](https://claude.ai/code/project/chan_01U5RdnAAmkSQmZbqh6FtqZ8?thread=cmsg_01U5RdnAAmkSQmZbqh6FtqZ85xEThZCGWjgxwVAkAhF4sq)_
Fixes #29518
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none — no file under `skills/**` teaches a `packages[]`
stack's top level, its residual, or a `_packageId` stamp on a top-level
dataset; every taught dataset sits in a single-package stack, which has
no residual.
## 维护者速读
- 改了什么:多包 stack(带 `packages[]`)顶层声明、但没有任何包体声明的 `datasets` 条目,现在按
ADR-0130 D4 的残余规则登记在 stack 自己的 `manifest.id` 下;构建门(spec 镜像)与启动门同步判断它的
`_packageId` 印记。
- 为什么改:之前这种数据集既不登记也不报错,被静默丢弃;引用它的仪表盘或报表要到运行时才失败,离原因很远。
- 风险与回滚:顶层带外包 `_packageId` 印记的残余数据集现在会被拒(之前静默丢弃),故声明 narrowed;另外
artifact 启动时各包体的数据集也会进入元数据服务(封闭运行时 `GET /meta/dataset` 不再为空)。回滚即还原本 PR。
- 先看:`packages/metadata/src/plugin.ts`(`ARTIFACT_FIELD_TO_TYPE` 新增
`datasets`)、`packages/spec/src/stack.zod.ts`(`TOP_LEVEL_COLLECTIONS_NEVER_RESIDUAL`
去掉 `datasets`)、新 pin `manifest-service-residual-objects.test.ts` 的
datasets 块。
- 两分钟验证:`pnpm --filter @objectstack/objectql exec vitest run
src/manifest-service-residual-objects.test.ts -t datasets`,预期 2
个用例全绿(构建依赖闭包后)。
- 需要你做的:无,席位契约复核后落地。
## What changes
- `packages/metadata/src/plugin.ts`: `ARTIFACT_FIELD_TO_TYPE` gains
`datasets: 'dataset'`, with a comment saying why and that the seed
`data:` collection stays unmapped. That one map is what the residual
rule (`unclaimedTopLevel`) walks, so a `packages[]` stack's top-level
dataset that no body declares is now listed as residual. Both boot doors
register it under the stack's manifest id and version (the metadata door
through `registerUnclaimedTopLevel`, the engine through the `manifest`
service's existing residual seam, unchanged), and both apply the
owner-stamp refusal they already apply to every other residual item.
- The same map is the artifact door's body loop, so every package body's
datasets also reach the metadata service from a compiled artifact, with
provenance. Before, only the ObjectQL registry took them (its
`METADATA_ARRAY_KEYS` loop).
- `packages/spec/src/stack.zod.ts`: the build-door mirror
`TOP_LEVEL_COLLECTIONS_NEVER_RESIDUAL` drops `datasets` and keeps `data`
and `jobs`; its docblock says why. Not exported, so no API-surface or
generated artifact moves. Triage designated this a cross-domain single
PR (6585309156); PR #29514 had merged first.
- `packages/spec/src/stack-collection-item-owner-stamp.test.ts`: the
"filed by no boot door" case drops `datasets`; a new case pins that a
foreign-stamped residual dataset raises the judge's issue at
`datasets.0._packageId`.
- `packages/metadata/src/owner-stamp-residual-build-boot.test.ts`: the
reading becomes `['data', 'jobs']`, and the comment above it no longer
says datasets are read off the stack whole (the rider from the contract
review of PR #29514, record 6583699079).
- `scripts/check-stack-collection-maps.mjs`: the
`ARTIFACT_FIELD_TO_TYPE` waiver row loses `datasets` from its keys (the
gate reds a stale waiver) and its text no longer says AppPlugin consumes
datasets off the bundle; it says why the key left.
- New pin block in
`packages/objectql/src/manifest-service-residual-objects.test.ts`: a
residual dataset registers under the stack's manifest id and version
beside a body's own; CONTROL: a top-level dataset a body repeats stays
the body's, registered once.
- One changeset: `minor` for `@objectstack/spec` and
`@objectstack/metadata` (the kind is `narrowed`), carrying the three
declaration lines and the fix for an author whose stack now refuses.
## Why
ADR-0130 D4's residual rule exists so a top-level item is either
registered or refused, never silently lost. `datasets` had no
`ARTIFACT_FIELD_TO_TYPE` entry, so the rule never saw it. Measured on a
kernel boot (`ObjectKernel` + `ObjectQLPlugin`, the stack handed to the
`manifest` service as `AppPlugin` hands it): nothing thrown,
`listItems('dataset')` answered the body's dataset only, and no line
named the residual one.
## Tests
- Red first, at the commit that adds the pin (before the fix):
`registers a residual dataset under the stack's manifest id and version,
beside a body's own` failed (`expected [ [ 'acme_case_body', …(2) ] ] to
deeply equal [ [ 'acme_case_body', …(2) ], …(1) ]`); the CONTROL passed.
With the mapping: 62/62 in that file.
- Reverse verification of the spec pin, at the final head: putting
`datasets` back into `TOP_LEVEL_COLLECTIONS_NEVER_RESIDUAL` turns the
new spec case red (1 failed, 47 passed); restored with `git checkout
HEAD --`, `git diff HEAD` empty.
- Before the spec line, the metadata mirror test was red exactly as
predicted (`datasets: build verdict == unclaimedTopLevel lists it
(true): expected false to be true`); with it, 27/27.
- Suites: at head `ae0a25e6` (the last commit adds only the changeset's
ADR-0087 marker), each under the verify lock: `@objectstack/spec` 888
files / 25184 tests passed (1 todo); `@objectstack/metadata` 68 / 956
passed; `@objectstack/objectql`
`manifest-service-residual-objects.test.ts` 62/62. The full
`@objectstack/objectql` suite (535 files / 10398 tests) and
`@objectstack/runtime` suite (527 files / 9695 passed, 19 skipped)
passed with the mapping before the rebase onto `ff827bcf`; the spec line
changes no runtime path they exercise. `typecheck` exit 0 for spec,
metadata and objectql.
## Acceptance notes
- Artifact boot measured before and after (scratch probe, deleted).
`createStandaloneStack` over a multi-package artifact with a residual
dataset: `getMetaItems({ type: 'dataset' })` went from the body's
dataset only to body + residual (owner `com.probe.release`), and
`getMetaItem` for the residual went from a stub with no item to the
served item. Single-package artifact: `/meta/dataset` unchanged; the
metadata service's own list went from empty to the body dataset. The
artifact door alone (as a sealed runtime runs it, no AppPlugin):
`manager.list('dataset')` went from empty to every body's dataset plus
the residual, each stamped with its package id and version, and the
residual `warn` counts the dataset with the other residual items. No
boot failed in any reading.
- `jobs`, `datasources` and `translations` stay on the waiver row: they
are a different fact (AppPlugin does consume them off the bundle) and
were not measured here.
## Verification
All readings at head `ae0a25e6`, merge base `ff827bcf`.
<details><summary>Derived gates: 101 derived, 100 exit 0, 1 NOT
MEASURED; plus pnpm lint</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived 101 commands from the 7 changed
paths; every one was run and its exit code recorded, and `--ran`
reconciled the record: `101 derived famil(ies) accounted for — 100 run,
1 NOT-MEASURED`.
- First pass: `check-adr-0087-registration` exited 1 (the narrowed
changeset had no ADR-0087 marker); the marker commit makes it exit 0
(`not-required (no-migration-prescription)`), and every
changeset-reading gate was rerun on that head (no-major,
empty-changeset, changeset-gate-self-tests, doc-authoring,
issue-citations, nul-bytes, closing-keyword-parity,
stack-collection-maps): all exit 0. `check-plugin-teardown-shape
--self-test` exited 3 until its pinned control commit was fetched, then
0.
- NOT MEASURED, reason: prerequisite not met (exit 3). `pnpm
check:dual-build-cjs-loads` needs every package built; CI's Build Core
runs it.
- `pnpm lint`: exit 0. `node scripts/check-stack-collection-maps.mjs
--self-test`: exit 0 (18 assertions).
</details>
`test:repo` was not run locally (it belongs to CI's Test Core). Dogfood
battery at `dd552104` (owed by the `(narrowing)` arm): closure built,
`pnpm --filter @objectstack/dogfood test` under the verify lock, VERDICT
command-exit 0; Test Files 397 passed | 3 skipped (400), Tests 3277
passed | 28 skipped (3305), no failures.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01JQwxbaRjbDqdsjbNUjKQJZ)_
<!-- signed: domain:server#4 · os-warren ·
session_01JQwxbacSXJQqAztgt4UMrS -->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent dd4a21f commit a81b7cb
7 files changed
Lines changed: 100 additions & 22 deletions
File tree
- .changeset
- packages
- metadata/src
- objectql/src
- spec/src
- scripts
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
172 | 172 | | |
173 | 173 | | |
174 | 174 | | |
175 | | - | |
176 | | - | |
177 | | - | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
178 | 181 | | |
179 | 182 | | |
180 | 183 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
135 | 135 | | |
136 | 136 | | |
137 | 137 | | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
138 | 145 | | |
139 | 146 | | |
140 | 147 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
388 | 388 | | |
389 | 389 | | |
390 | 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 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
391 | 433 | | |
392 | 434 | | |
393 | 435 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
301 | 301 | | |
302 | 302 | | |
303 | 303 | | |
304 | | - | |
| 304 | + | |
305 | 305 | | |
306 | 306 | | |
307 | 307 | | |
| |||
311 | 311 | | |
312 | 312 | | |
313 | 313 | | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
314 | 320 | | |
315 | 321 | | |
316 | 322 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
361 | 361 | | |
362 | 362 | | |
363 | 363 | | |
364 | | - | |
365 | | - | |
366 | | - | |
367 | | - | |
368 | | - | |
369 | | - | |
370 | | - | |
371 | | - | |
372 | | - | |
373 | | - | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
374 | 372 | | |
375 | 373 | | |
376 | 374 | | |
377 | 375 | | |
378 | 376 | | |
379 | | - | |
| 377 | + | |
380 | 378 | | |
381 | 379 | | |
382 | 380 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
661 | 661 | | |
662 | 662 | | |
663 | 663 | | |
664 | | - | |
| 664 | + | |
665 | 665 | | |
666 | | - | |
| 666 | + | |
667 | 667 | | |
668 | 668 | | |
669 | | - | |
670 | | - | |
671 | | - | |
672 | | - | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
673 | 678 | | |
674 | | - | |
| 679 | + | |
675 | 680 | | |
676 | 681 | | |
677 | 682 | | |
| |||
0 commit comments