Measured while implementing #11391 (giving @objectstack/client's meta.saveItem the query-string options its route reads). Separate surface, filed rather than folded — it is a server-side route defect and #11391's ruling is client-side only.
What was measured
packages/rest/src/rest-server.ts registers two PUT doors onto one generic saveMetaItem:
- single-segment
PUT /meta/:type/:name reads three query parameters and threads all three:
if (refuseRepeatedQueryParams(req, res, ['force', 'package', 'mode'])) return;
...
...(force ? { force: true } : {}),
...(packageId ? { packageId } : {}),
...((typeof req.query?.mode === 'string'
&& req.query.mode.toLowerCase() === 'draft')
? { mode: 'draft' } : {}),
- compound-name
PUT /meta/:type/:section/:name reads two:
if (refuseRepeatedQueryParams(req, res, ['force', 'package'])) return;
...
...(force ? { force: true } : {}),
...(packageId ? { packageId } : {}),
There is no mode read and no mode in the request object it builds.
So PUT /api/v1/meta/object/views/all_leads?mode=draft is accepted and published live, while the byte-identical intent against a single-segment name stages a draft. The parameter is not refused — it is silently ignored, and the caller gets a 200 saying the save succeeded.
mode is also absent from the compound door's refuseRepeatedQueryParams list, so the #6877 repeated-parameter guard does not cover it there either.
Why this looks like the same class the twin pair keeps closing
The compound route's own comments describe it as "word for word the same operation" as its single-segment twin — one generic saveMetaItem reached by a name spelled in two segments — and record four divergences closed on that reason:
?mode is the same shape, one field along: one door reading a parameter and its literal twin ignoring it. #11095's comment explicitly scopes its own ruling ("⛔ NOT a licence for every door that reaches this gate") to the dispatcher, which has no query string at all — that carve-out does not describe this door, which has a query string and already reads two parameters off it.
Not asserted here
Whether the compound door should gain mode is a contract question, not folded into this report — the draft/publish door may have its own reason to be single-segment only. What is measured is only that the two doors disagree and that the disagreement is silent.
Where it would land
packages/rest/src/rest-server.ts (the compound PUT handler), plus the parity pins in packages/rest/src/meta-compound-save-force-parity.test.ts, which already pins the force half of exactly this parity.
Related: #11095 (threaded ?force on this same door), #11391 (the client-side half — that card is not addressed by this one), #7019 (the twin-parity ruling).
Measured on origin/main at 4ceae8ab0.
Generated by Claude Code
Generated by Claude Code
Measured while implementing #11391 (giving
@objectstack/client'smeta.saveItemthe query-string options its route reads). Separate surface, filed rather than folded — it is a server-side route defect and #11391's ruling is client-side only.What was measured
packages/rest/src/rest-server.tsregisters twoPUTdoors onto one genericsaveMetaItem:PUT /meta/:type/:namereads three query parameters and threads all three:PUT /meta/:type/:section/:namereads two:moderead and nomodein the request object it builds.So
PUT /api/v1/meta/object/views/all_leads?mode=draftis accepted and published live, while the byte-identical intent against a single-segment name stages a draft. The parameter is not refused — it is silently ignored, and the caller gets a 200 saying the save succeeded.modeis also absent from the compound door'srefuseRepeatedQueryParamslist, so the #6877 repeated-parameter guard does not cover it there either.Why this looks like the same class the twin pair keeps closing
The compound route's own comments describe it as "word for word the same operation" as its single-segment twin — one generic
saveMetaItemreached by a name spelled in two segments — and record four divergences closed on that reason:manage_metadatacapability gaterest-server.ts里三个相邻/metahandler 的错误信封是三种不同形状,其中两种不符合 ADR-0112 #7035 — the 501 envelope?force=trueon routes that never thread it — the compound-namePUT /meta/:type/:a/:band the runtime dispatcherPUT /meta#11095 —?force?modeis the same shape, one field along: one door reading a parameter and its literal twin ignoring it. #11095's comment explicitly scopes its own ruling ("⛔ NOT a licence for every door that reaches this gate") to the dispatcher, which has no query string at all — that carve-out does not describe this door, which has a query string and already reads two parameters off it.Not asserted here
Whether the compound door should gain
modeis a contract question, not folded into this report — the draft/publish door may have its own reason to be single-segment only. What is measured is only that the two doors disagree and that the disagreement is silent.Where it would land
packages/rest/src/rest-server.ts(the compoundPUThandler), plus the parity pins inpackages/rest/src/meta-compound-save-force-parity.test.ts, which already pins theforcehalf of exactly this parity.Related: #11095 (threaded
?forceon this same door), #11391 (the client-side half — that card is not addressed by this one), #7019 (the twin-parity ruling).Measured on
origin/mainat4ceae8ab0.Generated by Claude Code
Generated by Claude Code