Filed while implementing #4133 (PR #4236). Unassigned — observation-class, recorded rather than fixed, and deliberately not folded into that PR's scope.
What was measured
#4133 renders the runtime authoring gate's advisory findings after a successful save. The wiring lifts once at useMetadataClient, which is the single point every app-shell write path takes its MetadataClient from — so ResourceEditPage, StudioDesignSurface, EmbeddedItemEditor, DatasourceResourcePage and ObjectHooksPanel are all covered by one sink.
There is a second, unrelated client class that writes through the same REST door and is not covered by it: ObjectStackClient.meta.saveItem (the SDK client hanging off ObjectStackAdapter), which is a different class from MetadataClient despite hitting the same PUT /api/v1/meta/:type/:name. Its callers, all writing in active mode (so the gate does run — this is not the D1 draft case):
packages/app-shell/src/hooks/useNavigationSync.ts:216 — saveItem('app', appName, schema)
packages/app-shell/src/services/MetadataService.ts:139,149,164,178,210 — object saves
packages/data-objectstack/src/index.ts:2878,3048,3139,3226 — updateViewConfig and the dashboard save paths
packages/plugin-designer/src/pages/CreateAppPage.tsx:56, EditAppPage.tsx:68
Each awaits the call and discards the response value, so an advisories[] the server attaches is parsed off the wire and dropped — the same client-side discard #4133 closed for the other client.
Why this is observation-class, not a defect
Nothing regressed and no user hits a wrong result: these paths behaved this way before #4133 and still do. It is a coverage gap in a feature that just landed, not a break. Severity is deliberately left for triage rather than judged here.
Worth noting for whoever picks it up: the two clients have genuinely different shapes — MetadataClient is constructed per-component through one factory (which is what made a config-level sink work), whereas ObjectStackAdapter is a single long-lived instance that already carries an event-emitter seam (onWriteWarning, #3431/#3455). So the natural fix here is probably the emitter shape rather than the config-callback shape, reusing the AdapterProvider subscription that already exists — not a copy of #4133's wiring.
The renderer itself is already reusable: emitSaveAdvisories in packages/app-shell/src/providers/saveAdvisoryToast.ts is a pure builder taking a caller-owned sink, and readSaveAdvisories is exported from @object-ui/data-objectstack. Neither would need changing.
Dedup
Searched open objectui issues for the saveItem / advisories / second-client framing: only #4133 itself and two closed unrelated items. No twin.
Generated by Claude Code
Filed while implementing #4133 (PR #4236). Unassigned — observation-class, recorded rather than fixed, and deliberately not folded into that PR's scope.
What was measured
#4133 renders the runtime authoring gate's advisory findings after a successful save. The wiring lifts once at
useMetadataClient, which is the single point every app-shell write path takes itsMetadataClientfrom — soResourceEditPage,StudioDesignSurface,EmbeddedItemEditor,DatasourceResourcePageandObjectHooksPanelare all covered by one sink.There is a second, unrelated client class that writes through the same REST door and is not covered by it:
ObjectStackClient.meta.saveItem(the SDK client hanging offObjectStackAdapter), which is a different class fromMetadataClientdespite hitting the samePUT /api/v1/meta/:type/:name. Its callers, all writing in active mode (so the gate does run — this is not the D1 draft case):packages/app-shell/src/hooks/useNavigationSync.ts:216—saveItem('app', appName, schema)packages/app-shell/src/services/MetadataService.ts:139,149,164,178,210— object savespackages/data-objectstack/src/index.ts:2878,3048,3139,3226—updateViewConfigand the dashboard save pathspackages/plugin-designer/src/pages/CreateAppPage.tsx:56,EditAppPage.tsx:68Each awaits the call and discards the response value, so an
advisories[]the server attaches is parsed off the wire and dropped — the same client-side discard #4133 closed for the other client.Why this is observation-class, not a defect
Nothing regressed and no user hits a wrong result: these paths behaved this way before #4133 and still do. It is a coverage gap in a feature that just landed, not a break. Severity is deliberately left for triage rather than judged here.
Worth noting for whoever picks it up: the two clients have genuinely different shapes —
MetadataClientis constructed per-component through one factory (which is what made a config-level sink work), whereasObjectStackAdapteris a single long-lived instance that already carries an event-emitter seam (onWriteWarning, #3431/#3455). So the natural fix here is probably the emitter shape rather than the config-callback shape, reusing theAdapterProvidersubscription that already exists — not a copy of #4133's wiring.The renderer itself is already reusable:
emitSaveAdvisoriesinpackages/app-shell/src/providers/saveAdvisoryToast.tsis a pure builder taking a caller-owned sink, andreadSaveAdvisoriesis exported from@object-ui/data-objectstack. Neither would need changing.Dedup
Searched open objectui issues for the
saveItem/ advisories / second-client framing: only #4133 itself and two closed unrelated items. No twin.Generated by Claude Code