Repository navigation
fix(metadata): a packages[] stack's top-level dataset no body declares registers under the stack's id - #29593
Conversation
…k's manifest id (red) Claude-Session: https://claude.ai/code/session_01JQwxbaRjbDqdsjbNUjKQJZ Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQwxbaRjbDqdsjbNUjKQJZ Co-authored-by: Claude <noreply@anthropic.com>
…n-maps waiver row loses datasets Claude-Session: https://claude.ai/code/session_01JQwxbaRjbDqdsjbNUjKQJZ Co-authored-by: Claude <noreply@anthropic.com>
…as the boot now files them Claude-Session: https://claude.ai/code/session_01JQwxbaRjbDqdsjbNUjKQJZ Co-authored-by: Claude <noreply@anthropic.com>
…set narrowing Claude-Session: https://claude.ai/code/session_01JQwxbaRjbDqdsjbNUjKQJZ Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check2 anchor(s) derived from 2 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 171 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 71b0adfe0ee4b5d16a335747a254e1dcabd5af10 && git checkout 71b0adfe0ee4b5d16a335747a254e1dcabd5af10
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 50e35a280994dcecb714b6fa1f2d44840900f5e7 dd5521040e2ba6c6ae9873c8cdc8649ec68ece4e && git checkout -B drift-repro 50e35a280994dcecb714b6fa1f2d44840900f5e7 && git merge --no-ff dd5521040e2ba6c6ae9873c8cdc8649ec68ece4e
node scripts/docs-audit/affected-docs.mjs --json 50e35a280994dcecb714b6fa1f2d44840900f5e7 |
…arrowing) arm Claude-Session: https://claude.ai/code/session_01JQwxbaRjbDqdsjbNUjKQJZ Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS Contract-review-claim: session_01JQwxbacSXJQqAztgt4UMrS · head dd55210 · 2026-11-02T20:35Z |
|
domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS Contract reviewServed-tier: Inputs: card #29518 (body and all six comments), PR #29593 (body, file list, ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
The three test files. The spec pin asserts the issue path Implemented-by: VERDICT: PASS |
|
domain:server#4 · os-warren · session_01JQwxbacSXJQqAztgt4UMrS 席位核对ACCEPT ·
|
Requested by Warren · project thread
Fixes #29518
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none — no file under
skills/**teaches apackages[]stack's top level, its residual, or a_packageIdstamp on a top-level dataset; every taught dataset sits in a single-package stack, which has no residual.维护者速读
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)、新 pinmanifest-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_TYPEgainsdatasets: 'dataset', with a comment saying why and that the seeddata:collection stays unmapped. That one map is what the residual rule (unclaimedTopLevel) walks, so apackages[]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 throughregisterUnclaimedTopLevel, the engine through themanifestservice's existing residual seam, unchanged), and both apply the owner-stamp refusal they already apply to every other residual item.METADATA_ARRAY_KEYSloop).packages/spec/src/stack.zod.ts: the build-door mirrorTOP_LEVEL_COLLECTIONS_NEVER_RESIDUALdropsdatasetsand keepsdataandjobs; its docblock says why. Not exported, so no API-surface or generated artifact moves. Triage designated this a cross-domain single PR (6585309156); PR docs(spec): TOP_LEVEL_COLLECTIONS_NEVER_RESIDUAL names the readers of top-level datasets the tree has #29514 had merged first.packages/spec/src/stack-collection-item-owner-stamp.test.ts: the "filed by no boot door" case dropsdatasets; a new case pins that a foreign-stamped residual dataset raises the judge's issue atdatasets.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 docs(spec): TOP_LEVEL_COLLECTIONS_NEVER_RESIDUAL names the readers of top-level datasets the tree has #29514, record 6583699079).scripts/check-stack-collection-maps.mjs: theARTIFACT_FIELD_TO_TYPEwaiver row losesdatasetsfrom 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.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.minorfor@objectstack/specand@objectstack/metadata(the kind isnarrowed), 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.
datasetshad noARTIFACT_FIELD_TO_TYPEentry, so the rule never saw it. Measured on a kernel boot (ObjectKernel+ObjectQLPlugin, the stack handed to themanifestservice asAppPluginhands it): nothing thrown,listItems('dataset')answered the body's dataset only, and no line named the residual one.Tests
registers a residual dataset under the stack's manifest id and version, beside a body's ownfailed (expected [ [ 'acme_case_body', …(2) ] ] to deeply equal [ [ 'acme_case_body', …(2) ], …(1) ]); the CONTROL passed. With the mapping: 62/62 in that file.datasetsback intoTOP_LEVEL_COLLECTIONS_NEVER_RESIDUALturns the new spec case red (1 failed, 47 passed); restored withgit checkout HEAD --,git diff HEADempty.datasets: build verdict == unclaimedTopLevel lists it (true): expected false to be true); with it, 27/27.ae0a25e6(the last commit adds only the changeset's ADR-0087 marker), each under the verify lock:@objectstack/spec888 files / 25184 tests passed (1 todo);@objectstack/metadata68 / 956 passed;@objectstack/objectqlmanifest-service-residual-objects.test.ts62/62. The full@objectstack/objectqlsuite (535 files / 10398 tests) and@objectstack/runtimesuite (527 files / 9695 passed, 19 skipped) passed with the mapping before the rebase ontoff827bcf; the spec line changes no runtime path they exercise.typecheckexit 0 for spec, metadata and objectql.Acceptance notes
createStandaloneStackover a multi-package artifact with a residual dataset:getMetaItems({ type: 'dataset' })went from the body's dataset only to body + residual (ownercom.probe.release), andgetMetaItemfor the residual went from a stub with no item to the served item. Single-package artifact:/meta/datasetunchanged; 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 residualwarncounts the dataset with the other residual items. No boot failed in any reading.jobs,datasourcesandtranslationsstay 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 baseff827bcf.Derived gates: 101 derived, 100 exit 0, 1 NOT MEASURED; plus pnpm lint
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 101 commands from the 7 changed paths; every one was run and its exit code recorded, and--ranreconciled the record:101 derived famil(ies) accounted for — 100 run, 1 NOT-MEASURED.check-adr-0087-registrationexited 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-testexited 3 until its pinned control commit was fetched, then 0.pnpm check:dual-build-cjs-loadsneeds 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).test:repowas not run locally (it belongs to CI's Test Core). Dogfood battery atdd552104(owed by the(narrowing)arm): closure built,pnpm --filter @objectstack/dogfood testunder 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