Repository navigation
fix(metadata-protocol): os migrate packageless-overlays also moves overlay rows bound to the read-only base itself into its .custom pair - #29477
Conversation
…bound to a read-only base Claude-Session: https://claude.ai/code/session_01ADHkmQZSg4zWtKMdRnKmKS Co-authored-by: Claude <noreply@anthropic.com>
… member Claude-Session: https://claude.ai/code/session_01ADHkmQZSg4zWtKMdRnKmKS Co-authored-by: Claude <noreply@anthropic.com>
…ckage on SQLite Claude-Session: https://claude.ai/code/session_01ADHkmQZSg4zWtKMdRnKmKS Co-authored-by: Claude <noreply@anthropic.com>
…y base Claude-Session: https://claude.ai/code/session_01ADHkmQZSg4zWtKMdRnKmKS Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
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 8c1b6b2c072a3a080e90dfec044e183bb5f25a36 && git checkout 8c1b6b2c072a3a080e90dfec044e183bb5f25a36
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 186a83ab5d59986698a5b6d975d9e950286d05fc 30bfafa6245d488642029d837000a7a31f18c138 && git checkout -B drift-repro 186a83ab5d59986698a5b6d975d9e950286d05fc && git merge --no-ff 30bfafa6245d488642029d837000a7a31f18c138
node scripts/docs-audit/affected-docs.mjs --json 186a83ab5d59986698a5b6d975d9e950286d05fc
|
|
domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67 席位核对 · ACCEPT · PR #29477 · 2026-11-02T16:06ZHead-sha: 维护者速读(席位终稿)
核对六项
|
Requested by Steve · project thread
Fixes #29392
Clause-②: no
Metadata-protocol: none
维护者速读
os migrate packageless-overlays的盘点现在也列出「绑定在只读基础包本身」的视图、仪表盘、报表、翻译、邮件模板行(metadata-protocol: a save naming a read-only code package itself (?package=<base>) for an overlay type is still accepted and lands an overlay bound to the base #28905 关门前用?package=基础包保存留下的),--apply把它们像无包行一样搬进.custom配对包并声明overrides(或作为配对包自己的语言成员),结果词不变。packages/metadata-protocol/src/protocol-packageless-overlay-migration.ts的listPackagelessOverlays(binding判定)与migratePackagelessOverlays(先后顺序与movedFrom防覆盖);packages/cli/src/commands/migrate/packageless-overlays.ts的渲染。pnpm --filter @objectstack/objectql exec vitest run src/protocol-packageless-overlay-migration.test.ts -t 29392(需先 build metadata-protocol),应见 6 个用例全过。What changes
The census (
listPackagelessOverlays,protocol-packageless-overlay-migration.ts). Besides the package-less rows, every row of the five overlay types whosepackage_idis a READ-ONLY code package that ships the name is now in the population: a row bound to the base itself, as a save naming the base stored it before #28905 closed that write. "Read-only package that ships the name" is the reading the #28905 close calls packaged (overlayBaseShippers=readOnlyShippingPackagesovershippedArtifactsOf), the same function the census already reads for a package-less row. Such a row:classifyPackagelessRow, with its binding as the one shipper, so a name several packages ship is nevermulti_shippedhere (the binding names the base);boundToBase: true(a new optional field on the census row and in the cli--jsonrows);addedMemberexactly as a package-less row does (an email template locale the base does not ship).A row bound to a writable package (the pair included) or to a read-only package that does not ship the name is not listed. Package-less rows are classified exactly as before.
The move (
migratePackagelessOverlays). Unchanged steps: ensure the pair, save the declared body (or the undeclared own member) into the pair through the save door, read it back, then delete the source row by its own address, nowpackageId: BASEfor a bound row. Same outcomes (movedwithbasedOn, ormovedwithaddedMember); no new outcome word.One item, two bindings. A package-less row and a row bound to the base of the same item would both land on the pair's one row. The move takes the package-less rows first; a row of an item this run already moved a row of the other binding into is kept, reported
failed("'PAIR' now holds this item … kept beside it, never merged"), and the next preview lists itshadowedwith that disposition's two ways out. Nothing is overwritten.The cli (
packageless-overlays.ts, render only): the preview names a bound row ("bound to PACKAGE → PAIR"),--jsonrows carryboundToBase, and a boundforeign_declarationrow's remedy names the delete naming the base and the save into the pair (a save naming the base is closed now, so the package-less row's "remove the key in place" remedy does not apply). Help text and three wording spots no longer say "package-less" where the row may be bound.Changeset:
.changeset/29392-packageless-overlays-base-bound.md,patchfor@objectstack/metadata-protocoland@objectstack/cli.Tests
packages/objectql/src/protocol-packageless-overlay-migration.test.ts, new describe[#29392](6 tests, in-memory topology, rows laid down through the engine as an older release stored them):movable, with base and pair, writing nothing;--applymoves both withoverrides: { package: BASE, basedOn: '1.2.0' }in their own state; the layered read at the base shows the base's document as the code layer and the customization as effective; the five reads serve the customization; a re-run moves 0;movableinto that base's pair, notmulti_shipped;foreign_declarationand a boundshadowedrow: listed and left;failed, nothing overwritten, next census lists itshadowed;packages/objectql/src/environment-overlay-email-locale-restart.test.ts, one new case in the migrate: os migrate packageless-overlays fails on an email template row of a locale the base does not ship, so after #28576 that row can be neither written nor migrated #28904 describe: azh-CNrow of a template the package shipsen-USonly, bound to the package: listedmovable+addedMember+boundToBase, moved as the pair's own member with nooverrides, still sent inzh-CNat once and after a restart.packages/cli/src/commands/migrate/packageless-overlays.integration.test.ts(SQLite topology): a second template database with a row bound to the package; the preview lists it and writes nothing;--applymoves it with the declaration, keeps the package-less control, a fresh boot serves the customization at all four reads, a re-run moves 0. The seeding moved into oneinsertRowshelper (both templates use it); the existing three cases are unchanged.Acceptance notes
namedBaseCloseSubjectis not the census's predicate and is not called: its first step (a read-only package amongoverlayBaseShippers) is the population test here, read through the samereadOnlyShippingPackagesthe census host already carries. Its remaining steps (pair id, ensure door, base version, the D2 gate admitting a declared body) would, as a census filter, hide again a bound row whose fix cannot be taken, and drop the unshipped-locale rows this card moves as the pair's own member (the gate refuses their declared body, so the close answersnull). The census keeps its package-less precedent: classify, then let the move's own steps answerfailedwith the reason. The two close predicates are not folded here (metadata-protocol: fold the two overlay-write close predicates (namedBaseCloseSubject,packagelessCloseSubject) into one fix-can-be-taken predicate #29397).namedBaseCloseSubject,packagelessCloseSubject) into one fix-can-be-taken predicate #29397, which edits them next):protocol.tsnamedBaseOverlayWriteRefusaldocblock ("its census reads package-less rows only", "the only way to clear such a row … the ceremony moves package-less rows only") and thelistPackagelessOverlaysdocblock ("bound to no package");protocol-packaged-base-refusal.tsNamedBaseWriteOperationand refusal docblocks;protocol.named-base-overlay-write.test.tsheader.boundToBaseis a new optional field in the census rows and--json; additive.Verification
Red, green and ablations measured on
d74ab15d..7ebc8019(the implementation is unchanged since); suites, typecheck, gates and lint re-run on the final head30bfafa62(main merged ata64416366), treeobjectstack-ai/objectstack.Red on main, then green
protocol-packageless-overlay-migration.tsrestored fromorigin/main7f277a8 in the worktree, metadata-protocol rebuilt (boundToBasein dist: 0), objectql file run: the 5 bound-row pins red, the dry run answering[]for the bound rows (+ []against the two expectedmovable … boundlines). The CONTROL case was red in that run too, from an expected-order slip in the test itself ([OTHER, WORKSPACE]sorted), fixed before the green run; the email bound case was written after. Restored to HEAD, rebuilt (dist hits 3):protocol-packageless-overlay-migration.test.ts+environment-overlay-email-locale-restart.test.ts68/68.Ablations (rebuild + dist preflight each leg)
if (binding !== undefined && !shippers.includes(binding)) continue;→ every bound row skipped), viascripts/ablation-replace.mjs, marker in both dist files: objectql 6 failed / 63 passed (the 5 bound-row pins plus the email bound case; CONTROL stays green); cli integration 2 failed / 3 passed (both bound-row cases). Restored: blob == HEAD,git diff HEADempty, rebuilt,--absentpreflight clean.movedFromcheck → never true): objectql 1 failed / 68 passed, the two-bindings case (AssertionError … ['package-less','moved'], …second outcome no longerfailed). Restored the same way,--absentclean.Suites and typecheck (under os-verify-lock, after a full `./packages/**` + `./examples/**` build)
Final head
30bfafa62, afterpnpm install --frozen-lockfileandturbo run build --filter='./packages/**' --filter='./examples/**'(VERDICT command-exit 0, held 41m42s for the whole round, shared box):pnpm --filter @objectstack/metadata-protocol typecheckexit 0;@objectstack/objectqlexit 0;@objectstack/cliexit 0vitest run: 22161 passed, 26 skipped (22187)vitest run: 10326 passed (10326)--project unit: 5267 passed (5267)--project integration src/commands/migrate/packageless-overlays.integration.test.ts: 5 passed (5). The rest of the integration tier is declared to CI.Derived gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon30bfafa62: 62 commands, every one exit 0 (the same list as before the merge).--ranreconciliation: "62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED".check:type-check-debtre-measured 0 raw tsc errors, none above its record.check-plugin-teardown-shape --self-testneeds its pinned fixture commit fetched first (exit 3 before, 48 cases pass after).pnpm lint(repo-wide eslint,--no-inline-config): exit 0.Dogfood: not owed (no
!, no narrowing arm).Generated by Claude Code