Repository navigation
fix(spec): the owner-stamp build door judges a packages[] artifact's top level the way the boot does - #29303
Conversation
…idual the way the boot does The residual walk of applyCollectionItemOwnerStampGate now mirrors the boot's unclaimedTopLevel: top-level data, datasets and jobs are never residual items (no boot door files or judges them), and a view container is claimed by the object it binds to however the binding is spelled, so a copy spelled the other way is not judged and two list-bound containers are two slots. Pins one foreign-stamped item per judged collection. Claude-Session: https://claude.ai/code/session_01ADHkmQPQBHfN1JR1ZSJUUa Co-authored-by: Claude <noreply@anthropic.com>
… to unclaimedTopLevel Collection by collection over OWNER_STAMP_JUDGED_COLLECTIONS and view container shape by shape: the stack schema raises a residual's foreign _packageId exactly when unclaimedTopLevel lists the item and the metadata door refuses it. Adds the owner-stamp row to the validating-metadata doors table and the changeset. Claude-Session: https://claude.ai/code/session_01ADHkmQPQBHfN1JR1ZSJUUa Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check4 anchor(s) derived from 1 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 — 168 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 ae561db9a9b847ae398892b662e1e242f7488e79 && git checkout ae561db9a9b847ae398892b662e1e242f7488e79
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e7d3ada2ef615a9e064014d0ae31ec68873e9a8d 2562bce2f75d368c7d8de963e40bd624a1091cbd && git checkout -B drift-repro e7d3ada2ef615a9e064014d0ae31ec68873e9a8d && git merge --no-ff 2562bce2f75d368c7d8de963e40bd624a1091cbd
node scripts/docs-audit/affected-docs.mjs --json e7d3ada2ef615a9e064014d0ae31ec68873e9a8d |
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Head-sha: 维护者速读
席位意见:可落地,等契约复审。 席位核对 — ACCEPT ·
|
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Contract-review-claim: session_01ADHkmPi25XXKx92Xmg4viS · head 2562bce · 2026-11-02T11:58Z |
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Contract reviewServed-tier: Inputs: card #28695 (body, triage 6561043135 and 6572261416, claim 6575012012, os-dev-report 6575978908), PR #29303 (body, five-file list, ① Derived judgmentsMeasured on the head's tree, the build door (
② Semver level
The level is right under either reading: a kind other than The arm and the kind. The diff, read against ③ Boundary flags
④ Design judgment
The mirror holds, by construction, for every collection and container spelling, not only the pinned ones. I tried to break it along each axis the equality pin does not enumerate:
Placement. The predicate stays a private refinement on the schema, not a Findings.
Implemented-by: VERDICT: PASS |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 41445185458 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Requested by Steve · project thread
Fixes #28695
Clause-②: yes (widening) — the build door stops refusing shapes the boot accepts: a foreign-stamped top-level seed dataset,
datasetsentry or job of apackages[]artifact, and a top-level copy of a body's view container spelled with another binding. The one shape it now refuses that it accepted before (a top-level list-bound container no body declares, wearing a foreign stamp, while a body carries any list-bound container) is one the boot already refused, and the pending build-door changeset for item stamps already declares it refused.Metadata-protocol: added (a foreign
_packageIdon a top-leveldata,datasetsorjobsitem of apackages[]artifact, and on a top-level copy of a body's view container, is accepted at the build doors as it is at boot)维护者速读
defineStack/os validate/os build对多包制品(packages[])顶层"残余项"的_packageId判定,改成和启动时unclaimedTopLevel完全同一套认领规则;另补 30 个逐集合的构建侧钉子和一行文档。data、datasets、jobs构建拒、启动收;视图容器按名字认领,换一种绑定写法就误判(两个方向都有)。packages/spec/src/stack.zod.ts(isResidualTopLevelItem与TOP_LEVEL_COLLECTIONS_NEVER_RESIDUAL)、packages/metadata/src/owner-stamp-residual-build-boot.test.ts(两门等价钉)、.changeset/28695-owner-stamp-residual-walk.md。pnpm --filter @objectstack/metadata exec vitest run src/owner-stamp-residual-build-boot.test.ts,应见 27 个用例全绿(在 main 上是 11 红)。What changes
S1: the residual walk.
applyCollectionItemOwnerStampGate(packages/spec/src/stack.zod.ts) judges apackages[]stack's top level through a new private predicate,isResidualTopLevelItem, that mirrors the boot's residual rule (unclaimedTopLevel/artifactItemSlot/claimedArtifactSlots,@objectstack/metadata), which spec cannot import:data,datasetsandjobsare never residual (TOP_LEVEL_COLLECTIONS_NEVER_RESIDUAL). The boot's rule walks only the collections the metadata door registers as items, and these three are not among them;AppPluginseeds, consumes and schedules them off the stack whole, judging no stamp. A package body's items of these collections are still judged at both doors;deriveViewContainerObject: ownobject, thenlist.data.object, thenform.data.object, thenname), and a body's container also claims each view it expands into (expandViewContainer). Any other item's slot is itsname; an item with no slot is not judged, since no door registers it.The single-package branch and the body judgement are unchanged.
Measured against the card's mechanism assumption. Seed
dataheld: refused at build, accepted at boot. Picklists did not hold: the boot's rule lists a residual picklist, and the metadata door registers it undermanifest.idand refuses a foreign stamp on it (registerUnclaimedTopLevel→registerArtifactBodyCollections, measured in the new metadata pin). Skipping picklists would have made the build door looser than the boot, so the build door keeps judging them. Measuring the whole list showed two more collections in thedataclass:datasetsandjobs, which the boot's rule does not walk either.S3: build-side pins key by key.
stack-collection-item-owner-stamp.test.tsgains one pin per member ofOWNER_STAMP_JUDGED_COLLECTIONS, iterated from the constant, with a fixture table typed as aRecordkeyed byOwnerStampJudgedCollection(a new member does not compile until it has a fixture) and a test holding the table's keys equal to the constant. Each pin requires the unstamped stack to parse, then exactly one custom issue atKEY.INDEX._packageIdfor a foreign stamp, and none for a stamp equal tomanifest.id.The equality pin.
packages/metadata/src/owner-stamp-residual-build-boot.test.tsis where both rules are reachable. For every judged collection, a foreign-stamped top-level item no body declares: the build door raises the issue exactly whenunclaimedTopLevellists the item, andMetadataPlugin.registerUnclaimedTopLevelrefuses exactly those. A 5 × 5 matrix of body/top-level view container spellings holds the same equality.S4: docs. One row in the doors table of
content/docs/deployment/validating-metadata.mdxfor the owner-stamp refusal. The connectors failure-mode row (content/docs/automation/connectors.mdx) was read and left as is: its sentence ("an entry carrying a_packageIdother than its own package's is refused at the install") is about the install door, and it is true there.Tests
packages/spec/src/stack-collection-item-owner-stamp.test.ts: +31 (S3, 30 collections + table/constant equality) and +6 (S1 residual walk).packages/metadata/src/owner-stamp-residual-build-boot.test.ts: new, 27 tests.Acceptance notes
appsat the residual door) rode objectql: an installed package'sapps[]entry keeps a self-written_packageId(no #28534 refusal on the apps loop), so the per-package and disabled-package filters attribute the app to the package it names #28697, as ruled;packages/objectql/src/plugin.tsis untouched..changeset/28627-item-owner-stamp-build-door.mdstill says "a top-level item no body declares judged againstmanifest.id" without thedata/datasets/jobsexception. Not edited here (editing another PR's pending changeset reds Check Changeset by design); this PR's own changeset states the exception, and both ship in the same release notes._packageIdon a top-leveldata,datasetsorjobsitem of apackages[]stack is now judged at no door. Nothing reads a stamp on those three today (they are applied or scheduled straight off the stack), so it is noted here, not filed.Verification
S1 red on main, green after (spec)
src/stack-collection-item-owner-stamp.test.tswith the S1 suite on main's gate:Tests 5 failed | 43 passed (48)(data, datasets, jobs, both container cases). After the fix:Tests 48 passed (48).Equality pin red on main, green after (metadata)
stack.zod.tsrestored from main (47ee57fe), restore proved by blob hash and an emptygit diff HEAD:Tests 11 failed | 16 passed (27), first divergencedatasets: build verdict == unclaimedTopLevel lists it (false): expected true to be false. On the head:Tests 27 passed (27).Ablation: an item schema that stops spreading MetadataProtectionFields reds a pin
node scripts/ablation-replace.mjsremoved...MetadataProtectionFieldsfromPositionSchema(anchor 1 → 0, blob5ca91b5a→75b8039e):× positions: one foreign-stamped item raises the gate's issue …,Tests 1 failed | 47 passed (48). Restored, blob equal to HEAD,git diff HEADempty.Consumer suites
All on head
2562bce2, each underos-verify-lock(VERDICT command-exit 0unless named).pnpm --filter @objectstack/spec test:Test Files 790 passed (790),Tests 23407 passed | 1 todo.pnpm --filter @objectstack/spec typecheck: exit 0.check:generated: exit 0 (nothing stale).pnpm --filter @objectstack/metadata test:Test Files 68 passed (68),Tests 956 passed(theunclaimedTopLevelconsumer).typecheck: exit 0.@objectstack/clivitest --project unit:370 passed, 2 files failed first on a missingpackages/cli/dist(a prerequisite: the round built only cli's dependencies). After building the workspace, those two files (published-subpath-console.pin,published-subpath-hook-body.pin):Tests 29 passed (29).@objectstack/clivitest --project integration test/collection-item-owner-stamp-validate-door.test.ts(theos validatedoor of this gate):Tests 2 passed (2).!in the title and no(narrowing)arm.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon2562bce2: 105 families, all run after the last commit.check-plugin-teardown-shape --self-test(pinned commit missing from the shallow clone; fetched, then exit 0),spec check:skill-examplesandcheck:dual-build-cjs-loads(workspace dist missing; afterturbo run build --filter='./packages/**', both exit 0).dispatch-gates --ran:105 derived famil(ies) accounted for — 105 run, 0 NOT-MEASURED (a DERIVED zero).check-changeset-no-major.mjs --base origin/main --eventwith this body:LEVEL AXIS: … clause-② yes (widening) and metadata-protocol added, exit 0.check:dev-prereqs: exit 1 on machine state (35ui/packages have no dist in this container), not on this diff.Human Minimum
packages/specholds no runtime logic.pnpm lint(repo-wide, CI-owned) not run locally.main, one pull request.packages/spectouched:check:generatedis clean (nothing to regenerate).content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code