Repository navigation
fix(objectql): every item a composed stack declares only on its top level, beside package bodies, registers in the engine at boot - #28153
Conversation
…tack in the engine The manifest service registered a multi-package stack's residual (top-level items no package body declares, ADR-0130 D4) objects, connectors and flows only. Every other residual item reached the metadata service alone, so readers that ask the engine registry, the metadata save door's managed-content check among them, answered as if it were not declared. The residual is now read once per registration, and every residual item except objects (own path) and picklists (metadata-only by the objects rule) registers under the stack's manifest id and version through the judges a body's item passes: view containers are filed under their object and expanded after the divergent-name judge, a permission set's packageId is judged by the owner-binding judge, and residual actions join the late-artifact bound-target check. Claude-Session: https://claude.ai/code/session_016WV4cwMU5DUWJ6JmbuRpT7 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 37 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 20 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 cfc749f962dd3e83d40cb25b328fafc25bdfa5eb && git checkout cfc749f962dd3e83d40cb25b328fafc25bdfa5eb
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d70e94ae1db083f2ef136e0a65597b2de09fd35f 91a1d307f2d6a9a12d1c482682d139efe7a6f3b7 && git checkout -B drift-repro d70e94ae1db083f2ef136e0a65597b2de09fd35f && git merge --no-ff 91a1d307f2d6a9a12d1c482682d139efe7a6f3b7
node scripts/docs-audit/affected-docs.mjs --json d70e94ae1db083f2ef136e0a65597b2de09fd35f
|
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract-review-claim: session_016WV4d1Np3hU7RQnA13YZcL · head 69182e3 · 2026-10-30T14:24Z |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract reviewServed-tier: Inputs: card #28093 (body; triage 6523369904; claim 6523676782; os-dev report 6525127678), PR #28153 (body, file list, net diff against Check-runs at 2026-10-30T14:42Z: every gate-carrying context that has concluded is ① Derived judgmentsAccept-set and public-surface changes the diff implies, each judged:
② Semver level
③ Boundary flags
④ Design judgment
Should-fix #3 (card-level, from ③): file the follow-up card for residual picklists so the one type this PR neither registers nor refuses has a carrier. No must-fix. No fail-open finding at rule level beyond the one the card names and the head closes. Implemented-by: VERDICT: PASS |
…container-shape lines A residual `views:` entry that is not a container is now handled as the body seam handles one: with no `name` it is filed under the slot the residual rule gave it, and an entry that is not container-shaped (`isViewContainerShaped`) refuses the boot 422 INVALID_METADATA in the seam's words. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cwMU5DUWJ6JmbuRpT7
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract-review-claim: session_016WV4d1Np3hU7RQnA13YZcL · head 91a1d30 · 2026-10-30T15:03Z |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract reviewServed-tier: This record supersedes 6525505738 (head Check-runs at 2026-10-30T15:08Z: concluded and ① Derived judgmentsThe merge, judged by the tree:
The fix, judged by the tree ( ② Semver level
③ Boundary flags
④ Design judgment
No must-fix. One should-fix, carried on #28160 (extend its scope to the second copied text). No fail-open finding at rule level beyond the one the card names and the head closes. Implemented-by: VERDICT: PASS |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL 维护者速读
席位意见:ACCEPT。 席位核对Head-sha:
|
Requested by Elon · project thread
domain:server#4 · os-elon-musk · session_016WV4cwMU5DUWJ6JmbuRpT7
Fixes #28093
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
packages[])只在顶层声明、没有包体声明的“残留”项,ObjectQL 的manifest服务以前只把对象、连接器、流程注册进引擎注册表;现在其余所有类型(权限集、岗位、能力、共享规则、队列、分派规则、视图、应用、动作、钩子等)都按包体同类项的同一路径注册,归属栈自己的manifest.id与版本。packageId指向别的包、视图容器名不一致、动作不合 schema、声明了队列却没组合 ownership-queue 服务)会像包体一样在开机时被拒绝。picklist 维持只进元数据服务。回滚即 revert 本 PR。packages/objectql/src/plugin.ts(unclaimedTopLevelItems与registerUnclaimedTopLevelItems)。pnpm --filter @objectstack/objectql exec vitest run src/manifest-service-residual-objects.test.ts src/manifest-service-residual-security-seal.test.ts,预期 60 passed。What changes
packages/objectql/src/plugin.ts.manifestservice reads the residual rule's answer (unclaimedTopLevel,@objectstack/metadata) once per registration and hands it to both readers (the fix(objectql): a flow a composed stack declares only on its top level, beside package bodies, registers with the automation engine at boot #28091 contract review's should-fix).unclaimedTopLevelItemsOfType(stack, 'connector' | 'flow')becomesunclaimedTopLevelItems(residual): every residual item except the types inRESIDUAL_TYPES_WITH_OWN_PATH(object, which keeps its own path;picklist, which stays metadata-only because the residual objects rule is judged on that premise;object_extension, which the residual rule never lists).registerUnclaimedTopLevelItems: after every body and after the residual objects, under the residual rule's id with the stack's version stamped on a shallow copy, through the registration a body's item of that type takes. Aviewcontainer is filed under its object and expanded into its views (ADR-0017) after the one divergent-namejudge (viewContainerNameRefusal); a permission set is judged by the one owner-binding judge (judgePermissionSetOwnerBinding, ADR-0086 D3) in the body seam's envelope; every other item is oneregistry.registerItem(type, …).packages/objectql/src/manifest-service-residual-objects.test.ts. New describe: every formerly dropped type registers under the stack's id and version (one case per type, and all at once); a residual view container registers under its object and expands; a divergent residual view container is refused in the body seam's words; residual queues and assignment rules register when the ownership-queue service is composed, and refuse the boot like a body's when it is not; a residual permission set whosepackageIdnames another package is refused; a top-level item a body repeats is the body's, registered once; CONTROL: a stack with no residual item registers exactly the bodies' items.packages/objectql/src/manifest-service-residual-security-seal.test.ts(new). The security pin. Population read from the type registry (everydomain: 'security'type withallowEnvironmentOverlay: false): each residual item is registered under the stack's id, an environment save over it is refused403 NOT_OVERRIDABLEwith nothing stored, and the by-name read still serves the declared permission set..changeset/28093-residual-top-level-items-register.md.@objectstack/objectqlpatch.Why
Before (measured at
origin/main91a423ef)A composed stack with
packages[], booted through themanifestservice, whose top level declares one item of each registry type that no body declares:object,connector,flowapp,view,page,dashboard,report,action,workflow,position,permission,capability,sharing_rule,queue,assignment_rule,api,webhook,agent,tool,skill,rag_pipeline,hook,mapping,picklist,analytics_cube,email_template,doc,bookSecurity, at rule level: a residual top-level rule of type
permissionorpositionwas not sealed as managed content the way the same rule in a package body is; now it is registered and sealed. The other security types (capability,sharing_rule,queue,assignment_rule) were not reachable the same way. A residual queue or assignment rule on a composition without an ownership-queue service was silently inert, where a body's refuses the boot; it now refuses the same way.Why this path
The triage ruling: register each dropped type the way objects, connectors and flows are, or refuse it loudly. Every type is registered; none needed a new refusal. The body seam itself (
registerMetadataCollections) lives inpackages/objectql/src/engine.ts(hot file, #25522), outside the claim's surface, so the residual registration calls the same judges and the same shared helpers the body seam calls rather than that method.Clause-②
yes (widening): declared residual items that were not registered in the engine now are. What a body's item of the same type is judged by now judges the residual's too, so a residual item a body's would be refused for (an off-contract action, a permission set whosepackageIdnames another package, a divergent view container, a queue with no ownership-queue service) now refuses the boot. The divergent view container was already refused by the config boot's metadata door; the others reach onlystrict: falseinput or a composition without the queue service.Metadata-protocol: none: nothing an author can write changes.Verification
origin/main: 41 of 45 (green there: the two CONTROL pins, the security file's population check and its runtime-create control). Green after: 60 of 60 in the two files.scripts/ablation-replace.mjs, each restored withgit diff HEADempty): residual registration removed → 45 failed (including fix(objectql): a flow a composed stack declares only on its top level, beside package bodies, registers with the automation engine at boot #28091's connector and flow pins); claim filter dropped → the four body-repeat and CONTROL pins fail; permission owner judge, view expansion and view name judge each removed → exactly its own pin fails.@objectstack/objectqlfull suite: 495 files, 9647 passed.pnpm --filter @objectstack/objectql typecheck: exit 0.@objectstack/service-automationresidual-top-level-flow-boot.test.ts: 3 passed.@objectstack/cliserve-config-boot-residual.integration.test.ts(realos serve/os build/os validateboots): 14 passed.dispatch-gates --commandsderived 61 families on the committed head; all run with exit codes recorded. Two exit 3 on the first pass (check-plugin-teardown-shape --self-testin a shallow clone;check:dual-build-cjs-loads, 10 packages without a dist), both exit 0 after fetching the pinned commit andturbo run build --filter='./packages/**'.--ran: 61 derived, 61 run, 0 NOT-MEASURED.pnpm lint: exit 0. The head is 17 commits behindorigin/main; none of them touches the files this diff changes.Acceptance notes
Human Minimum
packages/specholds no runtime logic. (Not touched.)pnpm lintpasses.main, one pull request.packages/spectouched: not touched.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cwMU5DUWJ6JmbuRpT7