Skip to content

[P3] Read decorations (_diagnostics, _draft) round-trip into persisted sys_metadata bodies #4326

Description

@os-zhuang

Found while wiring #3903 (stored-metadata conversion, PR #4317). Filed per Prime Directive #10 rather than widening that PR.

What happens

getMetaItem / getMetaItems decorate every served item with _diagnostics (decorateMetadataItem, metadata-diagnostics.ts), and draft reads add _draft: true. saveMetaItem deliberately persists the request body verbatim (ADR-0005 §Validation — parsed.data would strip Studio-only auxiliary fields), and it strips neither decoration. So the standard Studio round-trip

GET (body + _diagnostics) → edit → PUT the whole body → persisted verbatim

writes the stale read-time verdict into the stored row: into sys_metadata.metadata, into the checksum, into sys_metadata_history diffs.

Verified against the built spec: the type schemas are strip-mode, so the save gate does not reject the key (view/object/action all safeParse successfully with _diagnostics present) — it just persists.

Why it stays invisible (and why it still matters)

  • Reads always recompute: decorateMetadataItem spreads { ...item, _diagnostics }, so the fresh verdict shadows the persisted stale one — users never see wrong diagnostics.
  • But the row body, its checksum, and every history diff carry diagnostic noise; a body-equality or checksum comparison across a GET→PUT round-trip is polluted; and computeMetadataDiagnostics already has to strip its own key defensively before validating (metadata-diagnostics.ts stripDiagnostics) — the write path never got the matching half.

Suggested fix

saveMetaItem strips the read-only decorations (_diagnostics, and _draft unless the write is itself the draft-mode marker) from request.item before the destructive-change diff / validation / persistence — mirroring the existing layered-envelope rejection, but as a silent strip since these keys are our own decoration, not author data. One place, both decorations, plus a pin test that a GET→PUT round-trip persists a byte-identical body.

Refs #3903, PR #4317, ADR-0005 §Validation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions