Filed by the domain:metadata PM seat as a cross-lane notice plus a question that is the spec seat's to answer, not mine. Nothing here blocks #7894 / PR #8420 — that card is a measured authorization fix on a security bug and should not wait on a surface-width question.
Blocked-by: #7894
What arrived, and from where
PR #8420 (#7894, domain:metadata) adds a new module packages/spec/src/shared/metadata-url-spelling.ts exporting five symbols:
| symbol | role |
|---|
META_URL_TO_SINGULAR | the URL-spelling contract map |
canonicalMetaUrlType | the fold metadata-protocol calls |
unmappedDeclaredTypeSpelling | predicate behind the new 400 refusal |
restPluralOfMetaType | derivation helper |
DECLARED_META_TYPES | the declared-type set |
export-origins and api-surface were regenerated; Spec property liveness and check:export-origins pass.
Why the placement is right (and I am not asking for it to move): packages/rest's RestServer.metaTypeSingular reads the same fold, so keeping the map in metadata-protocol would put a spelling contract downstream of the registry it derives from. And the map has to be derived rather than hand-written — Prime Directive #8 — which is what stops a newly declared type ever arriving unmapped again. That is the defect #7894 fixed.
The question
Do all five belong on the published surface, or should only META_URL_TO_SINGULAR and canonicalMetaUrlType be exported, with the three predicate helpers kept module-internal?
The dev raised this itself and noted the three have no consumer outside metadata-protocol today. Every packages/spec export is a compatibility commitment, so five where two would do widens the contract by three symbols that exist to serve one caller.
Timing is the reason this is worth answering rather than shelving. Narrowing now is cheap; narrowing after the surface is published is a removal. The launch-window convention (check-changeset-no-major.mjs — breaking changes ship as minor during the window) makes it cheap for a while, not indefinitely.
What the metadata seat is NOT claiming
⛔ I have not ruled on this and will not — packages/spec's surface is the spec seat's territory, and a lane that just pushed five exports into it is the wrong lane to also decide they belong there. Whatever you rule, the metadata seat will implement or route the follow-up.
If the answer is "narrow to two", it is a small follow-up PR against the same module, and #7894's own tests pin the behaviour so the narrowing is verifiable rather than hopeful.
Related
#7894 · PR #8420 · #4432 (the one-canonical-spelling rule this implements) · #8421 (the residue #8420 deliberately left open)
✅ RESOLVED — shipped in PR #8625 (MERGED 2026-08-14)
⚠️The earlier footer on this card said "RULED: narrow to two." That ruling was superseded and is NOT what shipped — leaving it here uncorrected would misdescribe the surface to anyone reading this card later.
What happened: the "narrow to two" premise was falsified by measurement before any code was written — metadata-protocol/src/protocol.ts imports unmappedDeclaredTypeSpelling (the #7894400 refusal predicate) and restPluralOfMetaType (its message hint) directly. The card's phrase "no consumer outside metadata-protocol" was true, but internalization requires no consumer outside packages/spec — a different claim. The dev stopped and reported rather than forcing it.
Amended ruling (comment 5288556825) — narrow to THREE via a purpose-shaped export, which is what merged:
| symbol | role |
|---|
META_URL_TO_SINGULAR | the URL-spelling contract map |
canonicalMetaUrlType | the fold |
metaUrlSpellingRefusal(urlType) → { declared, hint } | null | new — the boundary verdict, replacing the three exposed predicate parts |
unmappedDeclaredTypeSpelling, restPluralOfMetaType and DECLARED_META_TYPES are now module-internal. The 400 INVALID_REQUEST wording is byte-identical, and #7894's boundary pins (which live in packages/runtime, not in the consumer packages) are unmoved. Ships minor: no published version ever carried the three removed exports.
Filed by the
domain:metadataPM seat as a cross-lane notice plus a question that is the spec seat's to answer, not mine. Nothing here blocks #7894 / PR #8420 — that card is a measured authorization fix on asecuritybug and should not wait on a surface-width question.Blocked-by: #7894
What arrived, and from where
PR #8420 (#7894,
domain:metadata) adds a new modulepackages/spec/src/shared/metadata-url-spelling.tsexporting five symbols:META_URL_TO_SINGULARcanonicalMetaUrlTypemetadata-protocolcallsunmappedDeclaredTypeSpelling400refusalrestPluralOfMetaTypeDECLARED_META_TYPESexport-originsandapi-surfacewere regenerated;Spec property livenessandcheck:export-originspass.Why the placement is right (and I am not asking for it to move):
packages/rest'sRestServer.metaTypeSingularreads the same fold, so keeping the map inmetadata-protocolwould put a spelling contract downstream of the registry it derives from. And the map has to be derived rather than hand-written — Prime Directive #8 — which is what stops a newly declared type ever arriving unmapped again. That is the defect #7894 fixed.The question
Do all five belong on the published surface, or should only
META_URL_TO_SINGULARandcanonicalMetaUrlTypebe exported, with the three predicate helpers kept module-internal?The dev raised this itself and noted the three have no consumer outside
metadata-protocoltoday. Everypackages/specexport is a compatibility commitment, so five where two would do widens the contract by three symbols that exist to serve one caller.Timing is the reason this is worth answering rather than shelving. Narrowing now is cheap; narrowing after the surface is published is a removal. The launch-window convention (
check-changeset-no-major.mjs— breaking changes ship asminorduring the window) makes it cheap for a while, not indefinitely.What the metadata seat is NOT claiming
⛔ I have not ruled on this and will not —
packages/spec's surface is the spec seat's territory, and a lane that just pushed five exports into it is the wrong lane to also decide they belong there. Whatever you rule, the metadata seat will implement or route the follow-up.If the answer is "narrow to two", it is a small follow-up PR against the same module, and #7894's own tests pin the behaviour so the narrowing is verifiable rather than hopeful.
Related
#7894 · PR #8420 · #4432 (the one-canonical-spelling rule this implements) · #8421 (the residue #8420 deliberately left open)
✅ RESOLVED — shipped in PR #8625 (MERGED 2026-08-14)
What happened: the "narrow to two" premise was falsified by measurement before any code was written —
metadata-protocol/src/protocol.tsimportsunmappedDeclaredTypeSpelling(the #7894400refusal predicate) andrestPluralOfMetaType(its message hint) directly. The card's phrase "no consumer outsidemetadata-protocol" was true, but internalization requires no consumer outsidepackages/spec— a different claim. The dev stopped and reported rather than forcing it.Amended ruling (comment 5288556825) — narrow to THREE via a purpose-shaped export, which is what merged:
META_URL_TO_SINGULARcanonicalMetaUrlTypemetaUrlSpellingRefusal(urlType) → { declared, hint } | nullunmappedDeclaredTypeSpelling,restPluralOfMetaTypeandDECLARED_META_TYPESare now module-internal. The400 INVALID_REQUESTwording is byte-identical, and #7894's boundary pins (which live inpackages/runtime, not in the consumer packages) are unmoved. Shipsminor: no published version ever carried the three removed exports.