Skip to content

Client edit gate reports served bodies as invalid: the read-time _diagnostics decoration reaches it through the pending-draft merge #7603

Description

@claude

What

ResourceEditPage's edit door hands the client Zod gate a draft that still carries the server's own read-time decoration _diagnostics. Every wired metadata type whose schema is .strict() — 14 of the 15, by the count already recorded in ResourceEditPage.tsx — then reports a body the server accepts as unrecognized_keys.

Found while implementing #6982 (out of that card's scope, which is clientValidation.ts's two named cures). Filed unassigned for PM triage.

The ingress, measured

ResourceEditPage.tsx builds the edit baseline from the layered read and then merges the pending draft over it:

const baseline   = (lay.effective ?? lay.code ?? {}) as Record...;
const rawInitial = draftReal ? { ...baseline, ...draftReal } : baseline;
  • lay.effective is safe: getMetaItemLayered serves three RAW layers and decoration never reaches it (protocol.ts states this in its own words).
  • draftReal is client.getDraft(...)'s resp.item, taken verbatim by extractDraftBody. That item is decorated — the strict draft branch returns item: decorateMetadataItem(type, ...), and decorateMetadataItem attaches _diagnostics whenever the type has a registered Zod schema.

Nothing on the objectui side strips it: validateMetadataDraft(type, draft, ...) receives draft as-is.

So the misfire needs one condition: the item has a pending draft. Without one, draftReal is null and the baseline alone is clean — which is why this has stayed invisible.

Measured, on the real read path

Driving the real protocol over a real engine (save a sharing_rule, read it back):

GET  /meta/sharing_rule/NAME                 item keys += _diagnostics
GET  /meta/sharing_rule/NAME?state=draft     item keys += _diagnostics
layered `effective` (raw, undecorated)       clean

And through objectui's own gate, on flow (a type that gates BOTH doors today):

flow, clean body                    ACCEPT
flow, body + _diagnostics           REJECT  unrecognized_keys: `_diagnostics`

Why the server is the one that is right

The spec names _diagnostics a read decoration on purpose, and says a served body "is therefore NOT a valid input to the schema that produced it until these are removed" (kernel/metadata-read-decorations.ts, METADATA_READ_DECORATIONS = ['_diagnostics', '_draft']). The write path strips them, so the same body PUTs back byte-identical. The envelope keys are deliberately excluded from that list — they are allowlisted by the closed schemas precisely so provenance survives a re-parse. The decorations are not.

The framework has already paid for this class twice: a served _diagnostics 400ing every saved dataset, and the cold-boot flow bind (cloud#971) throwing unrecognized_keys: ["_diagnostics"] for every flow.

Blast radius

Not a Save blocker and nothing is exploitable — schema issues are advisory on this page (ResourceEditPage.schemaAdvisory.test.tsx), and the server has the last word. The cost is a false "this item is invalid" banner plus inline field errors on a legitimate item, on exactly the workflow where an author is mid-edit. _draft: true is the second decoration and reaches the same place through the preview-draft branch.

sharing_rule is accidentally the only type shielded, because AUTHOR_SHAPE_ONLY_TYPES switches its edit gate off for an unrelated reason — see #6982, where that opt-out is kept for exactly this measurement.

Suggested shape, not a decision

Strip the read decorations where the draft is assembled, deriving the key list from the spec's exported METADATA_READ_DECORATIONS rather than restating it — a second hand-maintained copy of that list in objectui is the thing the spec's own header warns about. ⛔ Not by loosening any schema.


Generated by Claude Code

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 seatfindingpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions