Skip to content

Three other draft-envelope consumers take getDraft().item verbatim, with no read-decoration strip #8181

Description

@os-justin

What

objectui#7603 fixed ResourceEditPage by stripping the spec's METADATA_READ_DECORATIONS
(_diagnostics, _draft) in extractDraftBody — the chokepoint its three merge sites share.
Three other modules read the same decorated envelope and strip nothing. Filed unassigned
for PM triage; found while implementing objectui#7603 and deliberately left untouched there.

client.getDraft() serves a decorated body: the strict draft branch returns
item: decorateMetadataItem(type, ...), which attaches _diagnostics whenever the type has a
registered Zod schema. Measured on the bundled spec, PageSchema.safeParse on a body plus
_diagnostics returns unrecognized_keys.

The three sites, measured on origin/main 9bfd61848

site line shape
packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx 445, 453-455 ((pendingDraft as any).item ?? pendingDraft) becomes effective, which seeds draft
packages/app-shell/src/views/studio-design/ObjectHooksPanel.tsx 42-47, 139 its own local draftBody() helper, same shape as the pre-fix extractDraftBody
packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx 1730, 1856, 2845, 3881 four getDraft reads

Each is the pre-objectui#7603 extractDraftBody shape: presence check, emptiness check, body
returned verbatim.

What is and is not measured

Measured: none of the three passes the body through stripReadDecorations, and none has a
local equivalent — grep -c stripReadDecorations is 0 in all three files, while the same grep
returns 2 in ResourceEditPage.tsx (post-fix) and 3 in MetadataService.ts. The zero is
backed by a firing control: grep -c client on the same three files returns 20 / 12 / 55, so
the grep shape does reach these files.

NOT measured — this is why the card is filed rather than fixed: whether a decorated body
actually reaches a gate or a write on any of these paths. One narrow leak is visible by reading
PermissionMatrixEditor.doSave: under the package door it builds
base = fresh?.effective ?? payload and then merged = { ...base }, so decorations are dropped
whenever the fresh layered read succeeds (effective is a RAW layer) — but the .catch(() => null) arm falls back to payload, which is the decorated draft, and spreads it into the save.
That is a failure-path-only leak and it has not been exercised.

Why it is worth a card anyway

The framework has now paid for this class three times (a served _diagnostics 400ing every
saved dataset; the cold-boot flow bind in cloud#971; and objectui#7603 itself). The remaining
value here is that a fourth site is one copy-paste away: ObjectHooksPanel already carries a
hand-rolled duplicate of the helper objectui#7603 just fixed, so the next author who copies it
inherits the defect and not the cure.

Suggested shape, not a decision

Consider one shared draft-envelope reader that all consumers call, deriving the key list from
the spec's exported stripReadDecorations rather than restating it. Triage should decide
whether that is worth the refactor or whether the three sites should simply be measured and
left alone. Not urgent, and expressly not a request to loosen any schema.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpackage: app-shellpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions