Repository navigation
fix(cli)!: os validate and os build refuse a composed stack's top-level picklist that the boot refuses - #28274
Conversation
…l picklist the boot refuses Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1Np3hU7RQnA13YZcL
…idate and os build Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1Np3hU7RQnA13YZcL
…he gate-parity ledger Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1Np3hU7RQnA13YZcL
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1Np3hU7RQnA13YZcL
📓 Docs Drift CheckThis PR changes 3 package(s): 22 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 179 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 9856cbc84b7d5e88752b039dff0ed9d2ee8412a5 && git checkout 9856cbc84b7d5e88752b039dff0ed9d2ee8412a5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b358e7995e6f5a04a7e231abb8d4628d61e9a017 329815488ac87fac89f1dc1341c30f2b6a32c3fb && git checkout -B drift-repro b358e7995e6f5a04a7e231abb8d4628d61e9a017 && git merge --no-ff 329815488ac87fac89f1dc1341c30f2b6a32c3fb
node scripts/docs-audit/affected-docs.mjs --json b358e7995e6f5a04a7e231abb8d4628d61e9a017
|
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract-review-claim: session_016WV4d1Np3hU7RQnA13YZcL · head e1913ec · 2026-10-31T00:55Z |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract reviewServed-tier: Inputs: card #28248 (body; triage 6532308421; unblock 6533130283; claim 6533173970; os-dev report 6536799895), PR #28274 (body, file list, net diff against Check-runs at 2026-10-31T01:01Z: 41 check-runs on the head. 13 ① Derived judgmentsEvery accept-set and public-surface change the diff implies, judged in the tree:
② Semver level
③ Boundary flags
④ Design judgment
The cited CLI code was read to judge the docblock's third sentence, not reviewed; what the read found is recorded in ① and ③ for the lane seat. Should-fixes:
No must-fix. No fail-open finding: every stack this diff refuses is one the boot refuses on Implemented-by: VERDICT: PASS |
Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1Np3hU7RQnA13YZcL
… twin Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1Np3hU7RQnA13YZcL
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract-review-claim: session_016WV4d1Np3hU7RQnA13YZcL · head 3298154 · 2026-10-31T01:56Z |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL Contract reviewServed-tier: This record supersedes 6536889061 (head Check-runs at 2026-10-31T01:59Z: 49 check-runs on the head, every one ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
No must-fix, no should-fix left on this PR. No fail-open finding. Implemented-by: VERDICT: PASS |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL 维护者速读
席位意见:ACCEPT。 席位核对Head-sha:
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL
维护者速读
packages[])顶层声明、无包体声明且无引擎注册字段引用的 picklist 会在启动时被拒绝,但os validate/os build照常通过,作者到启动才发现。unclaimedTopLevel)并按启动的同一判定拒绝它,规则 idpicklist-residual-unreferenced,消息与处方逐字等同启动的拒绝文字。packages/objectql,所以在 cli 中重述,并以启动对拍测试(真实内核 +ObjectQLPlugin)逐字节锁住两边一致。packages/metadata残余规则头注释与packages/spec/src/stack.zod.ts的一个 docblock 补上 picklist 例外,无 schema 变更,check:generated通过。Clause-②: no (narrowing)),只拒绝启动本已拒绝的栈;patch 级 changeset,ADR-0087 not-required。Clause-②: no (narrowing) —
os validateandos buildrefuse a composed stack whose top level declares a picklist that no package body declares and no object field the engine registers names, which they accepted before; the boot already refuses every such stack, so nothing that boots is newly refused, and nothing is widenedMetadata-protocol: none
What changed
packages/cli/src/utils/picklist-references.ts).judgePicklistReferencesgains a third refusal,picklist-residual-unreferenced. It reads the residual rule's own answer (unclaimedTopLevelfrom@objectstack/metadata, the copy both boot doors andview-container-names.tsread) and takes its picklists. Each one that no object field the engine registers names is refused, located at its top-level index (picklists[i]). "Named" is the boot's set (unreferencedResidualPicklistRefusalin@objectstack/objectql): a field of a body'sobjects/objectExtensions/ nestedplugins[], or of a residual object. A top-levelobjectExtensionsentry, a top-level copy of a body-declared object, and a top-levelplugins[]entry name nothing. Both doors call this function (os validatestep 2d,os buildstep 3a-bis), so both refuse.validate.ts,compile.ts). The heading over the refusals now comes from one sharedpicklistRefusalHeading. It names a residual refusal as the boot's refusal, not as "a picklist reference names a picklist this stack does not declare". With only reference refusals, the heading is byte-identical to before.--jsonis unchanged in shape: the new refusal rideserrorslike the others.packages/metadata/src/plugin.tsand the permission-set owner gate's docblock inpackages/spec/src/stack.zod.tsnow state the picklist exception. A residual picklist is metadata only: when a residual object's field names it, the object is listed and not served; when no registered field names it, the stack is refused at boot and byos validate/os buildfirst. This is the one docblock instack.zod.ts. It is not a.describe(), so nothing generated moves, andpnpm --filter @objectstack/spec check:generatedreports all 14 artifacts up to date.packages/cli/test/validate-build-gate-parity.test.ts) classifiespicklistRefusalHeadingas presentation, besideprintPicklistReferenceNotices.Judgement-sharing route: restated, held by a boot-parity pin
The judgement is restated in the CLI rather than imported. The engine's
unreferencedResidualPicklistRefusalandcollectManifestPicklistReferencesare module-private in@objectstack/objectql. Exporting them is an edit topackages/objectql, which this card's claim excludes. What is shared is the residual rule itself (unclaimedTopLevel), so which items are residual is never re-derived. What is restated is the "named" walk and the wording.picklist-references.boot-parity.test.tskeeps the restatement honest. It judges each stack with the door, then boots it on a realObjectKernelwith the realObjectQLPlugin, handed over the wayAppPluginhands a config stack. Where the door refuses one list, the boot'sINVALID_METADATAmessage must equal the door'smessage + ' ' + hintbyte for byte. On every control, neither side raises this refusal.A follow-up could export the engine's judgement from
@objectstack/objectqland drop the restatement, the wayviewContainerNameRefusalis shared.Measurement
The doors were measured with the real CLI (
os validate --json,os build) on main'spicklist-references.ts(f40b777d) and on this branch. The boot was measured by the parity test's kernel boot on the same engine (merged PR #28231).main: validate / buildINVALID_METADATApicklist-residual-unreferencedatpicklists[0]/ exit 1, no artifactpackages[], unnamed top-level listPins
picklist-references.test.ts), 9 refusal or heading pins:where,path, the list, the stack, all three remedies);objectExtensionsentry;plugins[]entry;picklistExtensionsentry extends;picklist-reference-unknownalone); a body-declared list, repeated on the top level or not; a stack withoutpackages[].picklist-references.ts: 13 fail and 27 pass. The 13 are all 6 unit refusal pins, all 3 heading pins (the function is new), and all 4 parity refusal pins. Every control passes.Ablations, each restored from HEAD afterwards:
objectExtensions, diverging-copy andplugins[]pins, unit and parityGates
Final head
e1913ec6, which mergesmainat361ea242(it brings PR #28244's other region ofstack.zod.ts, merged cleanly). Run after a full rebuild:dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 85 families. All 85 were run with exit codes recorded, and all exited 0.--ranreports 85 run, 0 NOT-MEASURED and 0 UNRUN, and exits 0.pnpm --filter @objectstack/spec check:generated: all 14 artifacts up to date.pnpm lint: exit 0.pnpm --filter @objectstack/cli run typecheckandpnpm --filter @objectstack/metadata run typecheck: exit 0 (on1fa87152; the later commits change only the changeset and mergemain).e1913ec6. The four picklist/door files: 80/80 (picklist-references.test.ts, the boot-parity file,validate-build-gate-parity.test.ts,picklist-reference-doors.test.ts).36aea717, the previous merge head.@objectstack/cli: 537 files pass. One file,migrate/data-commands.absent-database.integration.test.ts, hit abeforeAlltimeout under box load; re-run alone, it passes 51/51.@objectstack/metadata: 906/906.VERDICT command-exit 0, 360 files pass and 3 skipped, 2896 tests pass and 20 skipped.Fixes #28248
🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4d1Np3hU7RQnA13YZcL