Noticed while implementing #5026 (PR #6961). Not fixed there: it is a different defect class from that card's advisory rendering, and the correct shape needs a ruling rather than a guess.
The asymmetry
Both methods issue POST /api/v1/meta/:type/:name/publish. They disagree about what the response may look like.
MetadataClient.publishDraft tolerates a dispatcher envelope and returns the inner object:
constbody=(awaitres.json().catch(()=>({})))asRecord<string,unknown>;// Tolerate the dispatcher's `{ success, data: {...}}` envelope.constinner=(bodyasany)?.data&&typeof(bodyasany).data==='object' ? (bodyasany).data : body;returninnerasany;MetadataClient.publish, a few hundred lines down, does not:
constbody=awaitres.json();returnbodyasT;
Same route, same declared response, two different beliefs about the wire. One of them is wrong, and which one is not something a reader can settle from the code.
Why it matters rather than being cosmetic
If the dispatcher never envelopes this route, publishDraft's branch is dead tolerance — and under the repo's contract-first commandment (#0.1) that is exactly the "lenient fallback that fossilizes a second de-facto contract" the rule exists to reject. PublishMetaItemResponseSchema declares success / version / seq at the body's TOP level, with no envelope.
If the dispatcher does envelope it under some configuration, then publish is the broken one, and it is the more consequential half: it is the method Studio's designer actually takes (via publishRuntimeMetadata) and the one ResourceEditPage's Publish button calls. It would hand callers { success, data } where they expect the response, and version — the ADR-0008 optimistic-concurrency token the next write echoes back as If-Match — would silently read as undefined.
What this needs
A measurement of what the route actually returns, then ONE spelling for both methods:
- A — the route is never enveloped: delete the tolerance from
publishDraft, matching the declared schema. Contract-first, and it removes a dialect. - B — the route can be enveloped: give
publish the same unwrap, and say in the spec where the envelope is declared, since today it is nowhere.
A is the likely answer given the schema, but that is an inference from the declaration, not a measurement of the server, and I did not make the measurement.
Not affected by this
PR #6961's advisory reporting reads from whatever each method already treats as its response object, so it is correct under either answer — publishDraft reads the unwrapped inner, publish reads the parsed body. Whichever way this is ruled, the advisory wiring follows it rather than needing a second decision.
Source: packages/data-objectstack/src/metadata-client.ts (publishDraft, publish).
Noticed while implementing #5026 (PR #6961). Not fixed there: it is a different defect class from that card's advisory rendering, and the correct shape needs a ruling rather than a guess.
The asymmetry
Both methods issue
POST /api/v1/meta/:type/:name/publish. They disagree about what the response may look like.MetadataClient.publishDrafttolerates a dispatcher envelope and returns the inner object:MetadataClient.publish, a few hundred lines down, does not:Same route, same declared response, two different beliefs about the wire. One of them is wrong, and which one is not something a reader can settle from the code.
Why it matters rather than being cosmetic
If the dispatcher never envelopes this route,
publishDraft's branch is dead tolerance — and under the repo's contract-first commandment (#0.1) that is exactly the "lenient fallback that fossilizes a second de-facto contract" the rule exists to reject.PublishMetaItemResponseSchemadeclaressuccess/version/seqat the body's TOP level, with no envelope.If the dispatcher does envelope it under some configuration, then
publishis the broken one, and it is the more consequential half: it is the method Studio's designer actually takes (viapublishRuntimeMetadata) and the oneResourceEditPage's Publish button calls. It would hand callers{ success, data }where they expect the response, andversion— the ADR-0008 optimistic-concurrency token the next write echoes back asIf-Match— would silently read asundefined.What this needs
A measurement of what the route actually returns, then ONE spelling for both methods:
publishDraft, matching the declared schema. Contract-first, and it removes a dialect.publishthe same unwrap, and say in the spec where the envelope is declared, since today it is nowhere.A is the likely answer given the schema, but that is an inference from the declaration, not a measurement of the server, and I did not make the measurement.
Not affected by this
PR #6961's advisory reporting reads from whatever each method already treats as its response object, so it is correct under either answer —
publishDraftreads the unwrapped inner,publishreads the parsed body. Whichever way this is ruled, the advisory wiring follows it rather than needing a second decision.Source:
packages/data-objectstack/src/metadata-client.ts(publishDraft,publish).