You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
A second client class also traverses the metadata save door and still discards its advisory findings #4237
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):
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.
Findings triage (first grading): HOLD as finding — coverage-gap observation on the just-landed #4133 advisory rendering; nothing regressed and no wrong result reaches a user.
Premise verified on origin/main @ 5e2e9fa: useNavigationSync.ts:216 still awaits client.meta.saveItem('app', …) and discards the return; the reusable sink (saveAdvisoryToast.ts) is on main, so the gap is live-but-benign exactly as filed. Restart condition: promote when the adapter-emitter shape the card sketches (reusing onWriteWarning's seam) is taken up, or when a user-visible "my save warning disappeared" report lands on any of the listed ObjectStackClient.meta.saveItem callers. Filer's dedup (only #4133 adjacent) re-confirmed.
CLAIM — promoted from the triage hold (objectui whole-repo seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3) — dispatching now. The hold's own restart condition ("promote when the adapter-emitter shape the card sketches is taken up") is satisfied by taking it up: the maintainer has directed acceleration, the fix shape is recorded and reuses existing seams (onWriteWarning subscription + the already-reusable emitSaveAdvisories/readSaveAdvisories), and the gap means Studio authors on the ACTIVE-mode save door of the second client class get no advisory rendering today — feature-completeness of a just-landed surface, not speculative work.
Scope per the card's own sketch: the emitter rides ObjectStackAdapter's existing onWriteWarning subscription — NOT a copy of feat(studio): render the runtime authoring gate's advisory findings after a save #4236's config-callback wiring; the enumerated callers (useNavigationSync, MetadataService ×5, data-objectstack index ×4, plugin-designer Create/EditAppPage) get coverage through the shared seam, not per-site edits.
ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4258, CI fully green.
The seam decision is the review's centerpiece, and the dev's deviation from the dispatch's literal wording is CORRECT and endorsed: pushing advisories down the literal onWriteWarning listener set would have either forced every existing subscriber to grow a branch — violating the dispatch's own stated control — or made WriteWarningEvent lie (its required droppedFields means "fields the write legally stripped"), and would have orphaned the reusable emitSaveAdvisories renderer. Instead the seam's SHAPE is reused (one constructor-installed interceptor on the adapter's long-lived client, a new onSaveAdvisory subscription wired once in AdapterProvider beside the write-warning one), which is what #4236's own type declaration says the shape is FOR — with a pin asserting metadata saves never leak onto the write-warning channel. The response-shape work is measured, not assumed: the two clients' envelopes coincide (unwrapResponse returns verbatim absent a data key), pinned by driving a REAL SDK client through a fake fetch, plus a tripwire so a future SDK wrapping change goes red instead of silently emitting nothing. All 12 enumerated callers covered with zero per-site edits (the getClient() instance identity itself pinned), and the draft-door question resolved by measurement — this client class HAS no draft door, so mode derives from the response's own state, both directions pinned. The reverse verification's honest split (the 7 greens are the pins that CANNOT observe the emitter) closes it. Flipping ready + auto-merge.
Activity
os-zhuang commentedon Aug 11, 2026
Findings triage (first grading): HOLD as
finding— coverage-gap observation on the just-landed #4133 advisory rendering; nothing regressed and no wrong result reaches a user.Premise verified on
origin/main@5e2e9fa:useNavigationSync.ts:216still awaitsclient.meta.saveItem('app', …)and discards the return; the reusable sink (saveAdvisoryToast.ts) is on main, so the gap is live-but-benign exactly as filed. Restart condition: promote when the adapter-emitter shape the card sketches (reusingonWriteWarning's seam) is taken up, or when a user-visible "my save warning disappeared" report lands on any of the listedObjectStackClient.meta.saveItemcallers. Filer's dedup (only #4133 adjacent) re-confirmed.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
yinlianghui commentedon Aug 11, 2026
CLAIM — promoted from the triage hold (objectui whole-repo seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3) — dispatching now. The hold's own restart condition ("promote when the adapter-emitter shape the card sketches is taken up") is satisfied by taking it up: the maintainer has directed acceleration, the fix shape is recorded and reuses existing seams (onWriteWarningsubscription + the already-reusableemitSaveAdvisories/readSaveAdvisories), and the gap means Studio authors on the ACTIVE-mode save door of the second client class get no advisory rendering today — feature-completeness of a just-landed surface, not speculative work.claude/issue-4237-second-client-advisories, worktreeobjectui-issue-4237(M container)ObjectStackAdapter's existingonWriteWarningsubscription — NOT a copy of feat(studio): render the runtime authoring gate's advisory findings after a save #4236's config-callback wiring; the enumerated callers (useNavigationSync, MetadataService ×5, data-objectstack index ×4, plugin-designer Create/EditAppPage) get coverage through the shared seam, not per-site edits.Generated by Claude Code
yinlianghui commentedon Aug 11, 2026
ACCEPT (objectui seat PM, session
session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4258, CI fully green.The seam decision is the review's centerpiece, and the dev's deviation from the dispatch's literal wording is CORRECT and endorsed: pushing advisories down the literal
onWriteWarninglistener set would have either forced every existing subscriber to grow a branch — violating the dispatch's own stated control — or madeWriteWarningEventlie (its requireddroppedFieldsmeans "fields the write legally stripped"), and would have orphaned the reusableemitSaveAdvisoriesrenderer. Instead the seam's SHAPE is reused (one constructor-installed interceptor on the adapter's long-lived client, a newonSaveAdvisorysubscription wired once inAdapterProviderbeside the write-warning one), which is what #4236's own type declaration says the shape is FOR — with a pin asserting metadata saves never leak onto the write-warning channel. The response-shape work is measured, not assumed: the two clients' envelopes coincide (unwrapResponsereturns verbatim absent adatakey), pinned by driving a REAL SDK client through a fakefetch, plus a tripwire so a future SDK wrapping change goes red instead of silently emitting nothing. All 12 enumerated callers covered with zero per-site edits (thegetClient()instance identity itself pinned), and the draft-door question resolved by measurement — this client class HAS no draft door, somodederives from the response's ownstate, both directions pinned. The reverse verification's honest split (the 7 greens are the pins that CANNOT observe the emitter) closes it. Flipping ready + auto-merge.Generated by Claude Code
feat(data): surface save advisories from the second metadata client c…