fix(objectql): expand an aggregated views container from the nested-plugin seam too (#7163) - #7452
Conversation
…-plugin seam too (#7163) ADR-0017 makes the loader dual-read: an aggregated `defineView` container is registered under the bare `<object>` key for back-compatible reads AND expanded into independent `ViewItem`s under `<object>.<viewKey>`. Only the expanded items carry `viewKind`, and `getViewsByObject()` filters on exactly that — so the expanded layer, not the container, is what `GET /meta/view?object=`, the view switcher and Studio's package attribution read. `engine.ts` reaches the registration seam from two entry points and only the manifest one expanded. One container measured through each: via manifest → ['account', 'account.all_accounts', 'account.form'] via nested plugin → ['account'] No refusal, no diagnostic — a package shipping views through `manifest.plugins[]` had no views as far as every reader of the expanded layer was concerned. The direction was measured, not assumed. ADR-0017 states the dual-read as a property of "the loader" at load time rather than of one entry point; the other loader (`MetadataPlugin`'s artifact/HMR path) expands too, which is why the shared implementation was pushed into `@objectstack/spec` "so the two loaders cannot drift"; every authored stack in the tree ships `views` at manifest top level, so removing the manifest expansion would take the switcher from all of them; and no in-tree package ships `views` through a nested plugin, so the seam that gains behaviour breaks nobody. One direction is load-bearing, the other is not. So the copies are gone rather than reconciled: both seams now run one `registerMetadataCollections()`. #7049 hoisted the shared `METADATA_ARRAY_KEYS` and recorded that the bodies still differed in a per-key `debug` line, this expansion, and a warn-on-nameless-item. Sharing the list made the seams' collection SET unanswerable-differently; sharing the body does the same for what they DO with a collection both see — a nested plugin now also emits the nameless-item warning it used to swallow. Pins: manifest-vs-nested parity for an aggregated container, the expanded per-view identities (name, `viewKind`, object, isDefault, order, config, ADR-0010 provenance stamped to the parent package), the ADR-0017 dual-read back-compat half, and a control that a non-aggregated `ViewItem` still registers identically through both seams with no expansion. `engine-nested-plugin-collections.test.ts`'s `views` exclusion row — the only one that was ever a behaviour difference rather than a retired kind — is removed with the divergence, and `views` rejoins its cross-seam property test. Refs: #7163, #7049, #6242, #5870, ADR-0017, ADR-0010. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BPVc5WY75PkeXoA5NFiEEk
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 15 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also reference the affected code. These are read-only:
|
Closes #7163. Follow-on to #7049.
The fact, re-measured on
ba63f0epackages/objectql/src/engine.tsreaches the registration seam from two entry points. Since #7049 both enumerate oneMETADATA_ARRAY_KEYS, soviewsis seen by both — but only the manifest seam expanded an aggregated view container (expandViewContainerimported atengine.ts:136, called at exactly one site,:3458). The nested-plugin loop had no equivalent. One container through each seam:"Object has-many View" (ADR-0017 §2, §3.2) makes the loader dual-read: the aggregated
defineViewcontainer is registered under the bare<object>key for back-compatible reads and expanded into independentViewItems under<object>.<viewKey>. Only the expanded items carryviewKind, andgetViewsByObject()filters on exactly that — so a container that is registered but never expanded is invisible to it, toGET /meta/view?object=, and to the view switcher and Studio's package attribution above it. No refusal and no diagnostic.Which seam is right — measured, not assumed
The card deliberately did not pick. Both readings were live: the nested seam gains the expansion, or the manifest seam's expansion is the anomaly. Four measurements, all on this base:
MetadataPlugin's artifact/HMR path expands too (packages/metadata/src/plugin.ts:643-655). That is why the implementation was pushed down into@objectstack/specin the first place —ui/view.zod.tsstates the reason inline: "so the two loaders cannot drift." Removing the expansion would putengine.tsalone against both the ADR and the sibling loader.viewsat manifest top level — ~51 files, includingexamples/app-crm,examples/app-todo,examples/app-showcase,packages/qa/downstream-contract. Removing the manifest seam's expansion takes the view switcher away from all of them.viewsthrough a nested plugin — sweptexamples/,apps/,packages/and every fixture: zero. Every in-treeplugins:array holds runtimePlugininstances or package-name strings, none carrying metadata collections.So the divergence is not load-bearing in both directions: one direction breaks every shipping consumer, the other breaks nobody in-tree. The nested seam catches up — the same direction #7049 took for the enumeration.
The change
The copies are gone rather than reconciled: both seams now run one
registerMetadataCollections(source, ownerId, sourceLabel).#7049 hoisted the shared
METADATA_ARRAY_KEYSand measured the two loop bodies on the way past, recording that they still differed in a per-keydebugline, this view expansion, and a warn-on-nameless-item. Sharing the list made "which collections does a seam see?" unanswerable-differently; it left "what does a seam do with a collection both see?" answered in two places. Both remaining differences had the same structure — a body copied, then improved on one side only — so the body is shared too, for the reason #7049 gave for the list: a divergence that cannot be written down cannot be re-introduced by the next hand patch. What legally varies is passed in (which object is read, which package id is stamped, thedebuglabel).Behaviour deltas for a nested plugin, both from body-sharing: an aggregated
viewscontainer now expands, and a nameless item now emits thewarnthe manifest seam always emitted instead of being silently skipped. Nothing is removed and no manifest-seam behaviour changes.Pins
New
packages/objectql/src/engine-nested-plugin-view-expansion.test.ts:viewKind,object,scope,isDefault,order,config, and ADR-0010 provenance stamped to the parent package;<object>container survives alongside the expansion, both seams;ViewItemstill registers exactly once through both seams, no expansion, no new keys; a container-free manifest still registers no views.engine-nested-plugin-collections.test.ts: theviewsexclusion row — the only entry ever excluded for a behaviour difference rather than a retired kind — is removed with the divergence, andviewsrejoins the cross-seam property test.Reverse-verified: with
engine.tsreverted toorigin/mainand the new tests kept, 8 pins fail with exactly the reported symptom (['account']alone); the two control pins still pass, which is what makes them controls.Gates run locally (all
success)packages/objectqlfull vitestcheck:stack-collection-mapscheck:adr-anchorscheck:nul-bytescheck:engine-double-contractcheck:durability-log-leveltsc --noEmit(objectql)check-engine-split-ratio.mjsis report-only by default (ADR-0076 OQ#5) — no threshold to trip.Changeset
patch,@objectstack/objectql. Direct precedent: #7049's own changeset (nested-plugin-collection-registration.md) shipped the same class — "this changes boot behaviour for packages that already ship today", a nested plugin beginning to register what it silently dropped — aspatch. This is additive registration on one shape (isAggregatedViewContainer), removes nothing, and changes no public type. Nopackages/spectouch, so no #6017 declaration; notmajor, so no ADR-0087 trio.Refs: #7163, #7049, #6242, #5870, ADR-0017, ADR-0010.
Generated by Claude Code