Repository navigation
fix(objectql, metadata-protocol, runtime): a package's export carries the objectExtensions it was installed with, and the owner's export folds no other package's extension - #29948
Conversation
…ts manifest objectExtensions, and an owner export folds only its own extensions Claude-Session: https://claude.ai/code/session_01WGufzebqeaeGkaTWGnN9bm Co-authored-by: Claude <noreply@anthropic.com>
…s on both hops (#29872) Claude-Session: https://claude.ai/code/session_01WGufzebqeaeGkaTWGnN9bm Co-authored-by: Claude <noreply@anthropic.com>
…nlyExtensionsOf; changeset (#29872) Claude-Session: https://claude.ai/code/session_01WGufzebqeaeGkaTWGnN9bm Co-authored-by: Claude <noreply@anthropic.com>
…29872) Claude-Session: https://claude.ai/code/session_01WGufzebqeaeGkaTWGnN9bm Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzebqeaeGkaTWGnN9bm Co-authored-by: Claude <noreply@anthropic.com>
|
domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5 Contract reviewServed-tier: Face: declaration ( ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
|
domain:server#6 · os-support-ai · session_01WGufzibcN9oHa8kDbTTnL5 席位核对Verdict: ACCEPT at head
|
📓 Docs Drift CheckThis PR changes 3 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 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 73273c8fd7765fd5e94580032b831b6c40df38c9 && git checkout 73273c8fd7765fd5e94580032b831b6c40df38c9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6a4737abae46e1d6847d7abfe430e5d62cd927f0 7b1145ab6d56f676c27faefc9405b4c8df5cfdab && git checkout -B drift-repro 6a4737abae46e1d6847d7abfe430e5d62cd927f0 && git merge --no-ff 7b1145ab6d56f676c27faefc9405b4c8df5cfdab
node scripts/docs-audit/affected-docs.mjs --json 6a4737abae46e1d6847d7abfe430e5d62cd927f0
|
Requested by Yibo Wang · project thread
Fixes #29872
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
objectExtensions;被扩展对象的拥有者(基础包)导出时,不再把任何其他包的扩展字段并进自己的对象(此前只排除了它自己的.custom配对包)。/meta/object、控制台和数据接口读到的对象不变,仍是合并后的。withoutExtensionsOf语义不变。回滚即 revert 本 PR。packages/runtime/src/domains/packages.ts(registeredExtensionsOf,按目标去重)、packages/objectql/src/registry.ts(getObjectExtensionsOf与isLeftOutExtension)、packages/metadata-protocol/src/protocol-meta-list-reads.ts(per-package 层改传onlyExtensionsOf)。pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/domains/packages-export-base-showcase-boot.integration.test.ts -t 29872,预期 2 passed:扩展包导出带objectExtensions,基础包导出与安装扩展包之前逐字节相同,第二跳再导出不变。What changes
The defect (measured on
origin/main21510bb).assemblePackageManifestbuilt a package'sobjectExtensionsfrom theobject_extensionitems of the protocol's per-package layer. install-local (and a code package) registers a manifest'sobjectExtensionsstraight into the registry as'extend'contributors (ObjectQL.registerApp) and files no item, so such a package exported no extension. Its fields were folded into the OWNER's export instead (the per-package layer left out only the owner's.custompair since #29930). A deployment restored from an export therefore lost every extension field on its next export, and a.custompair installed from its export no longer re-exported its extension.Half 1: the extender's export carries its extensions, however they were installed.
SchemaRegistry.getObjectExtensionsOf(packageId)(new,@objectstack/objectql): everyextendcontribution the package makes, read back as theobjectExtensionsentry it was registered from, the inverse ofregisterObjectExtension.extendis the key it is filed under;priorityis written only when it is not the default200(the value an entry without one parses to), so a second hop exports the same bytes as the first;nameand every envelope key (_packageId,_provenance, ...) are left out.assemblePackageManifest(@objectstack/runtime) appends those entries (registeredExtensionsOf) to the per-package layer'sobject_extensionitems, skipping any target an item already names: a published extension keeps exporting as it is stored (metadata-protocol + objectql: an object extension is an authorable item of a writable package — saved and published through the existing doors, registered as 'extend', exported as objectExtensions #28574, packages export: a read-only base's own export carries its .custom pair's object extension merged into the object, where ADR-0146 D8 item 3 says the base exports unchanged #29903 pins unchanged). A registry double without the method adds nothing, as before.Half 2: the owner's export carries its object without ANY other package's extension (the seat's carrier note 6605292329).
listItems/getAllObjects/foldObjectExtendersOntotake anonlyExtensionsOfoption: only that package's ownextendcontributions fold, every other package's is left out. Both options live in oneObjectExtensionFoldOptionsand one predicate (isLeftOutExtension).readFlattenedMetaItems,layer: 'per-package') passesonlyExtensionsOfnaming the listed package to both the registry list and the overlay-row fold, in place ofwithoutExtensionsOfnaming its pair.withoutExtensionsOfis kept with its meaning: it shipped in the published@objectstack/objectql@18.3.0tarball (5 hits indist/index.js, measured withnpm pack), so removing it would be a narrowing of a published surface. No in-repo caller passes it any more; its unit pin stays.Served and effective reads are untouched: neither option is passed outside the per-package layer, and the controls in every new pin read
GET /meta/object/NAMEandGET /meta/objectwith every extension folded in.Tests
packages/objectql/src/registry-extension-field-patches.test.ts: the packages export: a read-only base's own export carries its .custom pair's object extension merged into the object, where ADR-0146 D8 item 3 says the base exports unchanged #29903 describe now pinsonlyExtensionsOf(owner listed with neither the pair's nor a third package's extension; an extender folds its own only; an un-extended object is the cached object; byte-equal to a registry with no extender; the stored-body fold) and keeps awithoutExtensionsOfcase. A new describe pinsgetObjectExtensionsOf: a manifest entry throughObjectQL.registerAppcomes back as written and parses under the strictObjectExtensionSchema; a stored extension throughregisterObjectExtensioncomes back with its own priority and re-registers on a fresh registry to a byte-equal served object.packages/runtime/src/domains/packages-export-base-showcase-boot.integration.test.ts(the shared showcase-boot harness; new top-level describe): an extender registered the way install-local registers it exports its entry; the base's export is byte-identical to its export before the extender was installed, with the pair's extension published beside it, and after a restart; the pair exports its own item only; an extender installed from its own export re-exports the same bytes (the second hop).packages/qa/dogfood/test/package-export-bundle-round-trip.dogfood.test.ts: the first describe asserts the install-local extender'sobjectExtensionsby name and in full on the source bundle AND the target bundle, and that the base's object carries the extender's field on neither; the third describe asserts the pair installed on the target from its entry re-exports its extension. The same-inventory pin stays and is green.Acceptance notes
extendspelling against the registry key. A stored row spelling a short name that the registry files under a legacy folded key (objectql: a manifest objectExtensions entry or hydrated row whose extend folds to a legacy key opens a second, owner-less object entry #28858) would export twice; the save door refuses that spelling (metadata-protocol: an object_extension item's refusal sentences, row comment and folded target spelling misfit the item door #28834), so no stored row carries it today. Noted, not filed..changeset/29903-base-export-without-pair-extension.mdstill says an extension from any other installed package "is still folded into the base's object in the base's export, as before", which this PR makes false. I first corrected it, andcheck-empty-changesetrefused (it guards another PR's pending note); I restored it byte-for-byte from the merge base instead, and this PR's changeset carries a "Supersedes, in the same release" paragraph naming that sentence. The seat may still prefer to correct the packages export: a read-only base's own export carries its .custom pair's object extension merged into the object, where ADR-0146 D8 item 3 says the base exports unchanged #29903 note on the release side.source: 'code'rows) now also carries its manifestobjectExtensions, and a platform-owned object's export no longer folds in plugin extensions. Both follow from the same rule.Verification
Ablations (each: mutation through scripts/ablation-replace.mjs, anchor 1 → 0, blob restored == HEAD, `git diff HEAD` empty)
packages.ts: drop theregisteredExtensionsOfappend) against the runtime integration file-t 29872: 2 failed.the extender's export carries its manifest entry: expected undefined to deeply equal [ { extend: 'showcase_task', …(1) } ], and the second-hop test the same. Restored.source: com.dogfood.exportext: expected { extends: [], objectCarries: false } to deeply equal { …(2) }and[#29872] target: the pair installed from its entry re-exports its extension: expected undefined to deeply equal [ { …(3) } ]. Restored.protocol-meta-list-reads.ts:onlyExtensionsOfnever set; markerablated-29872-b; metadata-protocol rebuilt,ablation-dist-preflightfound the marker indist) against the runtime integration file-t '29872|29903': 2 failed / 1 passed.expected [ 'organization_id', …(11) ] to not include 'acme_billing_code'and the packages export: a read-only base's own export carries its .custom pair's object extension merged into the object, where ADR-0146 D8 item 3 says the base exports unchanged #29903the base's object with the extension liveequality. Restored, rebuilt, preflight--absentexit 0.isLeftOutExtension'sonlyExtensionsOfclause →false) againstregistry-extension-field-patches.test.ts -t 29872: 4 failed / 4 passed (the fouronlyExtensionsOfcases). Restored.Local runs (code head 2e498fb; final head 7b1145a changes only this PR's changeset)
pnpm --filter '@objectstack/dogfood^...' build(closure), then objectql + metadata-protocol + runtime rebuilt: VERDICT command-exit 0.pnpm --filter @objectstack/objectql test: 547 files / 10827 tests passed.pnpm --filter @objectstack/metadata-protocol test: 311 passed, 4 skipped files / 22320 passed, 26 skipped tests.pnpm --filter @objectstack/runtime test: 538 files / 8599 passed, 19 skipped tests.typecheckfor objectql, metadata-protocol, runtime (incl. eachcheck:test-typecheck): exit 0.package-export-bundle-round-trip.dogfood.test.ts: 13/13.--no-inline-configover the 8 changed source/test files: exit 0 (the repo-widepnpm lintis CI's Lint & Repo Gates).dispatch-gates --commands(67 families), each run and its exit recorded: 67 exit 0 after three reruns.check-plugin-teardown-shape --self-testfirst exited 3 (shallow clone; its pinned fixture commit fetched, then 48 cases pass);check:dual-build-cjs-loadsfirst exited 3 (seven unbuilt dists: client-react, embedder-openai, knowledge-memory, knowledge-ragflow, service-cluster-redis, service-knowledge, studio; built, then exit 0);check-empty-changesetfirst exited 1 on my edit of the packages export: a read-only base's own export carries its .custom pair's object extension merged into the object, where ADR-0146 D8 item 3 says the base exports unchanged #29903 note (see Acceptance notes; restored, then exit 0). The changeset gates (check-empty-changeset,check-changeset-no-major,check-adr-0087-registration) were rerun on 7b1145a: exit 0.dispatch-gates --ran: 67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN.🤖 Generated with Claude Code
https://claude.ai/code/session_01WGufzebqeaeGkaTWGnN9bm