You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A draft row stored under a manifest-absent plural (fields, seeds, external_catalogs, translations) is promotable by publishPackageDrafts and lands ACTIVE in the second namespace #8908
Measured while verifying #8858's premise, at origin/main8664a2c99. Filed rather than fixed: the fix is a producer-side decision, not a line inside #8858's declared region.
What was measured
promoteDraftForPublish folds the stored draft's type through the manifest map (PLURAL_TO_SINGULAR[request.type] ?? request.type, protocol.ts:13198) — the map #7894 moved the /meta URL boundary OFF, because four registry types are legitimately absent from it (field, seed, external_catalog, translation; they are not stack collections). The batch route is not a URL boundary: its input is sys_metadata rows at rest, so #7894's fix never reaches it.
Two consequences, measured with the real repository and the real protocol over a stub engine:
Identical result for translations. The fold is a no-op for these four, so the lookup key equals the stored spelling and the row is found.
Contrast the manifest-PRESENT class (objects): the fold turns it into object, whereFor emits that spelling with no at-rest fallback, the promote raises NO_DRAFT and the all-or-nothing batch aborts — already pinned in protocol.publish-side-effects-canonical-type.test.ts and re-measured here. So the four manifest-absent types are exactly the class this route still lets through.
Downstream of the promote, runPublishSideEffects receives singularType = 'fields', so the registry write-through registers the item under the plural key as well — the #4432 shadowing shape, minted at publish time instead of at the URL. The ADR-0010 audit row records 'fields' too: recordMetadataAudit re-folds through PLURAL_TO_SINGULAR (protocol.ts:10577), which covers manifest-present plurals and misses these four — a fact rollbackMetaItem's own header already states.
Suggested disposition — a decision, not a one-line fold
Folding at the producer through the URL/registry map (canonicalMetaUrlType) would make these four behave like objects — i.e. the row becomes unpromotable (NO_DRAFT) and the whole batch aborts on it. That is fail-closed but it strands the residue with an unhelpful verdict. So the real question is what a legacy second-namespace row at rest should DO on publish:
(a) refuse loudly at the pre-flight, naming the row and pointing at the stored-migration path (fail-closed, actionable, batch-atomic — the shape the namespace-prefix gate already uses); or
(b) migrate it (_migrate-stored / a boot reconciliation step) so the residue is converted once rather than refused forever.
Both are producer-side and neither is a rider on another card.
Note for triage:#8858 proposes a related-looking one-line change to this method's ADR-0010 audit row (p.d.type → p.singularType). That change is a provable no-op — see the dev report on #8858 — and it does not touch this class, because p.singularType is folded through the same manifest map that misses these four. If #8858 is re-scoped toward the producer rather than closed as not-a-defect, THIS issue is the shape that re-scope should take: merge them rather than running both.
Related: #7894 (the URL door, one layer above), #8868 (diffMetaItem — same manifest-absent class, different verb), #8858 (the measurement that surfaced this).
Measured while verifying #8858's premise, at
origin/main8664a2c99. Filed rather than fixed: the fix is a producer-side decision, not a line inside #8858's declared region.What was measured
promoteDraftForPublishfolds the stored draft's type through the manifest map (PLURAL_TO_SINGULAR[request.type] ?? request.type,protocol.ts:13198) — the map #7894 moved the/metaURL boundary OFF, because four registry types are legitimately absent from it (field,seed,external_catalog,translation; they are not stack collections). The batch route is not a URL boundary: its input issys_metadatarows at rest, so #7894's fix never reaches it.Two consequences, measured with the real repository and the real protocol over a stub engine:
type = 'fields'(residue minted through the pre-meta-plural-url-bypass:PUT /meta/fields/<name>walks around the whole two-tier registry gate — 4 registry types have no entry inPLURAL_TO_SINGULAR#7894 plural URL door, which nothing rewrites on upgrade):Identical result for
translations. The fold is a no-op for these four, so the lookup key equals the stored spelling and the row is found.objects): the fold turns it intoobject,whereForemits that spelling with no at-rest fallback, the promote raisesNO_DRAFTand the all-or-nothing batch aborts — already pinned inprotocol.publish-side-effects-canonical-type.test.tsand re-measured here. So the four manifest-absent types are exactly the class this route still lets through.Downstream of the promote,
runPublishSideEffectsreceivessingularType = 'fields', so the registry write-through registers the item under the plural key as well — the #4432 shadowing shape, minted at publish time instead of at the URL. The ADR-0010 audit row records'fields'too:recordMetadataAuditre-folds throughPLURAL_TO_SINGULAR(protocol.ts:10577), which covers manifest-present plurals and misses these four — a factrollbackMetaItem's own header already states.Why it is a defect
PUT /meta/field/…answers 403 NOT_OVERRIDABLE for — the door meta-plural-url-bypass:PUT /meta/fields/<name>walks around the whole two-tier registry gate — 4 registry types have no entry inPLURAL_TO_SINGULAR#7894 closed, reopened from data at rest.fieldmisses an item the platform just reported as published.PUT /meta/fields/<name>walks around the whole two-tier registry gate — 4 registry types have no entry inPLURAL_TO_SINGULAR#7894 row can be re-promoted rather than migrated.Suggested disposition — a decision, not a one-line fold
Folding at the producer through the URL/registry map (
canonicalMetaUrlType) would make these four behave likeobjects— i.e. the row becomes unpromotable (NO_DRAFT) and the whole batch aborts on it. That is fail-closed but it strands the residue with an unhelpful verdict. So the real question is what a legacy second-namespace row at rest should DO on publish:_migrate-stored/ a boot reconciliation step) so the residue is converted once rather than refused forever.Both are producer-side and neither is a rider on another card.
Note for triage: #8858 proposes a related-looking one-line change to this method's ADR-0010 audit row (
p.d.type→p.singularType). That change is a provable no-op — see the dev report on #8858 — and it does not touch this class, becausep.singularTypeis folded through the same manifest map that misses these four. If #8858 is re-scoped toward the producer rather than closed as not-a-defect, THIS issue is the shape that re-scope should take: merge them rather than running both.Related: #7894 (the URL door, one layer above), #8868 (
diffMetaItem— same manifest-absent class, different verb), #8858 (the measurement that surfaced this).