Repository navigation
fix(metadata-protocol,cli): os migrate packageless-overlays lists a package-less view container cannot_declare, and --apply never attempts it - #30380
Conversation
…_declare, never attempt it The packageless-overlays census listed a package-less view container row movable, and every --apply failed it: the move's declared save adds overrides, which the container schema refuses as an unrecognized key. The census now asks the save door's own schema whether the body can carry the override declaration (overrideDeclarationRefusedByShape) and lists a row that cannot as cannot_declare, with the way out in the CLI; the move never attempts it. The package-less close and the close of a write naming the base read the same predicate, so neither closes a write whose fix cannot be taken. Claude-Session: https://claude.ai/code/session_01Utp6CbC5BpV41NMLrJMt8Q Co-authored-by: Claude <noreply@anthropic.com>
…t cannot carry the override declaration Also counts a union's refusal only when every branch still diagnosing the body names the key, so a view body claiming no member is not answered true. Claude-Session: https://claude.ai/code/session_01Utp6CbC5BpV41NMLrJMt8Q Co-authored-by: Claude <noreply@anthropic.com>
…ntainer's write open Claude-Session: https://claude.ai/code/session_01Utp6CbC5BpV41NMLrJMt8Q Co-authored-by: Claude <noreply@anthropic.com>
… ceremony, and keeps its package-less write Claude-Session: https://claude.ai/code/session_01Utp6CbC5BpV41NMLrJMt8Q Co-authored-by: Claude <noreply@anthropic.com>
…-jtt4fs # Conflicts: # packages/metadata-protocol/src/protocol-packaged-base-refusal.ts
📓 Docs Drift CheckThis PR changes 2 package(s): 8 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 42 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 a2f237240cd395727646f16acd787166d183f7d8 && git checkout a2f237240cd395727646f16acd787166d183f7d8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin fdad1786e892270ac3fcc161b3fd4d57c9c47748 5cbefbb3f0ba9ecf6e40eefca9b63f4c993aaa67 && git checkout -B drift-repro fdad1786e892270ac3fcc161b3fd4d57c9c47748 && git merge --no-ff 5cbefbb3f0ba9ecf6e40eefca9b63f4c993aaa67
node scripts/docs-audit/affected-docs.mjs --json fdad1786e892270ac3fcc161b3fd4d57c9c47748
|
… view-container write; metadata-protocol minor Claude-Session: https://claude.ai/code/session_01Utp6CbC5BpV41NMLrJMt8Q Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb 席位核对 · ACCEPT · 2026-11-05T08:41Z · head
|
Requested by Bill · project thread
domain:server#5 · os-bill · session_01Utp6CYVV6fcAQrNTDQRvPb
维护者速读
os migrate packageless-overlays的预览遇到无包的视图容器行(list/listViews/form/formViews写在一起的那种),不再列成"可移动",而是列成cannot_declare并给出出路(把每个视图逐个存进配对包,再删掉容器行);--apply不再尝试它,也不再每次报失败。overrides键,迁移第二步的声明式保存必被拒,预览却说可移动,--apply每次失败、行原样不动,运维无从下手;另外 main 上两处关门(无包写、写名指只读基包)把容器的写也关了,403 指向一个做不到的修法,连"删掉容器行"这条出路都被拒。overrides;两处关门对容器恢复为关门前的行为(无包保存、删除可用),其余类型不变。回滚即撤本 PR,无数据迁移。packages/metadata-protocol/src/protocol-packaged-base-refusal.ts的overrideDeclarationRefusedByShape与overlayPairFixAdmits新增的一行;protocol-packageless-overlay-migration.ts的classifyPackagelessRow(新分支在no_pair之后、foreign_declaration之前);packages/cli/src/commands/migrate/packageless-overlays.ts的reasonOf。pnpm --filter @objectstack/objectql exec vitest run src/protocol-packageless-overlay-migration.test.ts -t 30261(需先 build metadata-protocol),预期 5 条全绿;在 main 的字节上这 5 条全红。Fixes #30261
Clause-②: yes (widening) —
PackagelessOverlayDisposition(reachable through the exportedPackagelessOverlayCensus) gainscannot_declare; the package-less write close and the close of a write naming the read-only base no longer refuse a view container's write, whose named fix cannot be takenMetadata-protocol: semantic — an author's package-less save or delete (and a save naming the read-only base,
?package=<base>) of a view container (list/listViews/form/formViews) over a name one read-only package ships, refused403 NOT_OVERRIDABLEsince the D11 close, succeeds again: the close names a fix (a declared save into the pair) a container cannot take. Every other shape is closed as before.@objectstack/metadata-protocolshipsminor;@objectstack/clipatch(seat correction at 5cbefbb, the #30168 / PR #30322 precedent).Skills-docs: none —
skills/objectstack-platform/SKILL.mdline 128 refuses the package-less write "once movable as a declared override"; a container is never movable as one, so the sentence stays true, and no skill teaches writing a container over a packaged name.What changes
The card's Expected: "The preview and
--applyagree: a row listedmovablemoves, or a container row is listed with the reason it cannot move." This PR takes the second arm (the order's fork: make the preview honest, leave the container schema as it is; wideningViewSchemais apackages/specdesign question, not a migration fix).One predicate, from the save door's own schema.
overrideDeclarationRefusedByShape(type, body)(protocol-packaged-base-refusal.ts) parses the body with anoverridesprobe throughresolveOverlaySchema, the resolver the save door validates with, and answerstrueonly when the parse namesoverridesas an unrecognized key at the body's root. In a union it must be named by every branch still diagnosing the body (the view union mutes the branches the body does not claim to a root kind mismatch,focusClaimedBranch), so a view body that claims no member is not answeredtrue. No list of shapes is kept: a member that gains or loses the key is answered by its schema.Measured over the five overlay types the ceremony moves (pinned in
protocol.override-declaration-shape.test.ts, which also pins that its fixture covers everyallowEnvironmentOverlaytype): the view container answerstruein all four spellings (list,listViews,form,formViews); a single view, a list overlay, a dashboard, a report, a translation and an email template each spreadOverrideDeclarationFieldsand answerfalse. So the view container is the one container-shaped member with this mismatch.The census.
classifyPackagelessRowtakes the canonical type and returnscannot_declare(withbaseandpair) for such a body, aftermulti_shipped/no_pairand beforeforeign_declaration/shadowed, because those two dispositions' ways out end in this ceremony moving the row.migratePackagelessOverlaysacts onmovablerows only, so it never attempts the row: no outcome, no failure, no pair ensured for it, and the CLI exits 0.The two closes. On
mainboth closes refused a container's write with403 NOT_OVERRIDABLE, naming a fix (a declared save into the pair) that the container schema refuses (measured, red leg below). Both closes are built on "close only where the fix can be taken" (#28576 ruling 2; the #28905 docblock "every write this closes has a door, the pair; every other one keeps the write it has onmain"):classifyPackagelessRow, so acannot_declarerow's package-less save and DELETE stay open: they are the one door such a row keeps, and the way out ends with that DELETE;overlayPairFixAdmits, the "fix can be taken" predicate both closes ask, now also asks the shape (for a real body; a bare identity has no shape), so the close of a write naming the read-only base leaves a container's write as it was before that close. The row such a write stores is listedcannot_declare,boundToBase, and a delete naming the base removes it.CLI.
reasonOfprints the way out under "left as they are": save each of the container's views into the pair as a single view namedOBJECT.VIEW(one the base ships declaringoverrides: { package, basedOn }, a new one declaring nothing), then delete the container row with a delete naming no package (naming the base, for a row bound to it). JSON rows carrydisposition: "cannot_declare",outcome: "left".Docs. Two sentences this PR made false, amended:
content/docs/deployment/cli.mdx(theos migrate packageless-overlaysrow: "move each") andcontent/docs/concepts/metadata-lifecycle.mdx(the D11 callout: every package-less write onto a ✅-type packaged item is refused).Changeset:
@objectstack/metadata-protocol+@objectstack/clipatch, both declaration lines.Tests
objectqlprotocol-packageless-overlay-migration.test.ts, new top-level describe[#30261](5 cases, realObjectQLengine and registry): the census lists the active and draft container rowscannot_declarewith base and pair,--applyreturns no outcome and no base line, ensures no pair, and a re-run lists the same; a single view beside it still moves; the close leaves the container's package-less save, DELETE and first save open; the close of a write naming the base leaves it open and the stored row is listedcannot_declareboundToBase, removable by a delete naming the base; the way out holds end to end (the default view saved into the pair declaring the override,showcase_task.minesaved as the pair's own, the container deleted naming no package, then the census lists nothing and the read serves the pair's view).metadata-protocolprotocol.override-declaration-shape.test.ts(new; no harness for this predicate existed): the measurement above, plus a container with an unknown key of its own stilltrue, a body claiming no view memberfalse, a schema-less typefalse.clipackageless-overlays.integration.test.ts, new describe over a real SQLite database: the preview lists the containercannot_declare/left;--apply --yesexits 0 withfailed: 0,moved: 0, the rows unchanged and no pair stored.Acceptance notes
sys_metadatarow withpackage_idNULL that overlays an item a code package ships becomes a declared override of that item"; D2 defines the declaration as a key "in the item's body" and never names a container. A container is not itself an item the read serves by name (it expands toOBJECT.VIEWitems), and the way out moves each of those items as D11 describes. Read as not requiring the container row itself to move; reported here for the seat rather than asneeds_decision.basedOnis not judged and the probe's value is a fixed well-formed declaration: only the key's acceptance is read.Verification
All readings on head
3413e7bb(the fix's four commits plus a merge ofmainat0837fdc0, which carries #30322's change to the sameoverlayPairFixAdmits: the shape check runs first, thenoverrideWriteVerdict(…, fix.pendingPairScope)), except the red/green legs, taken before the merge onefbca752/17adf261withmain's bytes at8b117c0e.Red leg (main's bytes) and green leg
Package suites and typechecks, on 3413e7b after the merge (under os-verify-lock.sh)
Derived gates (dispatch-gates --commands, 95 families) and lint, on 3413e7b
Dogfood: not owed (no
!in the title, no(narrowing)arm).Generated by Claude Code