Repository navigation
fix(metadata-protocol): the customizations read lists a moved edit under the package that serves it - #31668
Conversation
…der the package that serves it After a suite move (ADR-0130 D4) a tenant's edit is served at the package that ships the item now, but listServedOverrides took the package from the pair override's declaration: listed under the former owner after a split, under neither after a rollback. The list now picks each item's pair row through the served reads' own candidates and predicates (servedOverlayRowCandidates, holdsServedOverlayCandidate, isMovedOverrideRowOf), so a package lists the override its address serves, and only there. The item lock already reads every stored row of the item; a pin holds that it answers the same for a moved edit. Claude-Session: https://claude.ai/code/session_01ADHkmPHUhgip3YbMfQh27s Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmPHUhgip3YbMfQh27s Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check6 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 — 10 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 5846748681cb45e2bfb3d088015283f64b6b777c && git checkout 5846748681cb45e2bfb3d088015283f64b6b777c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8c9260f79c5d060dfed58a92cc8f9b10a1762a5b a135e658c194682da5333f893760a07f2f5ce45d && git checkout -B drift-repro 8c9260f79c5d060dfed58a92cc8f9b10a1762a5b && git merge --no-ff a135e658c194682da5333f893760a07f2f5ce45d
node scripts/docs-audit/affected-docs.mjs --json 8c9260f79c5d060dfed58a92cc8f9b10a1762a5b |
|
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM 维护者速读
席位核对 — ACCEPT · domain:server#2 · Head-sha
|
Requested by Steve · project thread
Fixes #31656
Clause-②: no — what the customizations list holds for a moved edit; no accept set changes
Metadata-protocol: none
维护者速读
GET /packages/:id/customizations)现在把租户的修改列在实际生效的新包下,且只列在那里。appOf)判定。条目锁本就读取该条目的全部存储行,实测对挪动的修改与在新包下保存的修改给出相同答案,未改,只加了钉。回滚即还原本 PR。packages/metadata-protocol/src/protocol-served-overrides.ts(逐条目按候选顺序选行,以及"旧包不再列"的排除)。pnpm --filter @objectstack/runtime exec vitest run src/domains/meta-overlay-follows-suite.test.ts(需先构建 metadata-protocol),预期 12 个全绿。What changes
As measured on
origin/mainafter #31653. The card read "listed under neither". Measured, it depends on the direction: after a flat → composed split the pair override declaring the App was still listed under the App (which no longer ships the item) and not under the module that serves it; after a composed → flat rollback it was listed under neither. Both are fixed.The list reads the served reads' own candidates, one reading (seat ruling 1).
listServedOverridesgroups the pair's ACTIVE rows by item (folded type, name, bundle member) and picks each item's row throughservedOverlayRowCandidatesandholdsServedOverlayCandidate, in their order, with the item's shipping packages read the way the by-name read reads them (shippingPackageIds(shippedArtifactsOf(...))). So a package lists the pair's declared override of its item first, else the pair's override thatisMovedOverrideRowOfholds as a moved edit. Same App is read only inside the predicates (appOfover installed manifests); no key list and no same-App test of its own.Listed only where it is served (ruling 2). A declared override whose package no longer ships the item, which a package shipping it now holds as its moved edit (
isMovedOverrideRowOf), is that package's and is not also listed under the former owner. A former owner that still ships the item keeps its edit, listed as today.The list stays the pair's rows. A row bound to a package of the App itself (a Regime O save from before the ADR-0146 D11 close) is served at the new owner through
isMovedBaseRowOf's candidate, but it is not a pair row: it is listed under no package, before or after the move, as such a row bound to its still-shipping owner is not listed today. Named in the report's open questions. Since the list reads no base-bound rows, "two former owners' rows, list neither" has no listed case; the served reads already serve neither.Item lock (ruling 3): measured, unchanged. The lock's overlay layer reads every ACTIVE row of the item whichever package it is bound to (
overlayLockLayerAt, no package filter), so a moved edit was already in its scope. A pin holds that a moved edit carrying a stamped lock answers at the module exactly what the module's own edit answers: the read'slock/editable/deletable/lockReasonand the save door'sITEM_LOCKED. No save door refusal changes.Files:
protocol-served-overrides.ts,protocol.ts(one host member,shippedArtifactsOf, passed to the list), the test, a patch changeset.protocol.tsis outside the delta's named surface: the host-split pattern needs the member wired there, as #31653 did for the by-name read. Nopackages/spec/**, noui/**, nodocs/adr/**;served-override.tsanditem-lock.tsare not edited. No forced test edits.Tests
Six cases added to
packages/runtime/src/domains/meta-overlay-follows-suite.test.ts(the suite-move harness #31653 added: one SQLite tenant DB, a fresh real kernel per version), as a top-leveldescribe:Red on main first (529e535, fix sources restored from main, tests kept): 2 failed | 10 passed (12)
The lock pin passes on main: the lock half of the premise does not hold.
Ablations (fix committed first; scripts/ablation-replace.mjs, dist preflight, restore proven blob == HEAD each leg)
The runtime test reads
@objectstack/metadata-protocolthrough itsdist/, so each leg rebuilt it;ablation-dist-preflight.mjsfound the marker in 2 built files before the run and in none of 24 after the restore build, with the tree clean. The legs ran on a local commit over #31653's branch head0fad163d(identical to its squash on main for every file it touched) whoseprotocol-served-overrides.tsblob0b23b796is the one at this PR's head.0b23b796→2f6f269e):Tests 2 failed | 10 passed (12), cases 1 and 2. Restored0b23b796== HEAD.a53e1f98):Tests 1 failed | 11 passed (12), case 1, the App lists the moved edit too. Restored == HEAD.7dfc0eba):Tests 2 failed | 10 passed (12), cases 3 and 4, the module lists the App's still-shipped edit and the pair row declaring the partner App. Restored == HEAD.Case 5 and the lock pin hold on main and guard behaviour this diff does not change, so no ablation is owed for them.
Acceptance notes
listOverridesin@objectstack/objectql, feat(objectql): the package upgrade plan lists, as advice, each .custom override that hides a change of the upgrade #31627) reads this list, so it follows the same rule; its tests pass unchanged (package-upgrade-environment.test.ts).Verification
Head
a135e658(branchclaude/project-thread-o96evq): fix28e7e921cut fromorigin/main529e5351after #31653 merged, thenorigin/mainmerged in ata135e658(merge, no rebase; the two incoming commits touchplugin-authandmetadata, not this surface).pnpm install0;pnpm build119/119 (on28e7e921);turbo build --filter ./packages/**81/81 after the merge.packages-customizations,dispatcher-plugin.package-customizations-mountandmeta-legacy-pair-served: 4 files / 41 tests passed ona135e658.@objectstack/metadata-protocol328 files / 22595 tests passed (4 files / 26 tests skipped) on28e7e921.@objectstack/objectqlpackage-upgrade-environmentandprotocol-meta101 passed. Heavy runs throughos-verify-lock.sh(VERDICT command-exit 0).check:test-typecheckfirst reportedorganization-ownership-boot-guard.turso-remote.kernel.test.ts, a known prerequisite: the optional peerdriver-tursohad no dist; built, re-run 0).--no-inline-configover the 2 changed sources and the test, exit 0.dispatch-gates --commandsderived 72 families; all 72 run ona135e658, each exit 0 (also on28e7e921, where two first exited 3 on container prerequisites: the teardown self-test's fixture commit, fetched;dual-build-cjs-loadsneeds every dist, afterpnpm build0).dispatch-gates --ran: 72 derived, 72 run, 0 NOT-MEASURED, 0 UNRUN.check-platform-checklist --changed-from origin/main: OK, 0 lost.!in the title, no narrowing arm).test:repo: declared to CI, not run locally.Generated by Claude Code