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
Four SDK metadata methods build compound-name URLs against routes mounted in one arity only — measured 404, and getHistory/deleteItem encode while getAudit/getReferences/diffItem/rollbackItem do not #12106
Measured while mounting the compound-name promotion door (#11932 / PR #12105). Filed rather than folded: #11932 is one route; this is four SDK methods aimed at an arity the server does not mount, and the split that produces it.
2026-08-25 maintainer ruling (recorded in the comments): the option-1/option-2 fork below is SUPERSEDED — metadata item names are ruled to not contain /, so the compound-name convention itself is being retired via #12176's staged plan. This card waits on that card; the defect dissolves at its re-addressing stage.
What was measured
Driven through the real registered server — new RestServer(new HonoHttpServer(0), …).registerRoutes() — and asked of the LIVE router which registration would answer each path (IHttpServer.resolveMountedRoute, the same observation the #7526 dogfood parity gate reads). views/all_leads is the compound name the SDK's own doc comments use.
GET /api/v1/meta/view/views/all_leads/references -> undefined client.meta.getReferences
GET /api/v1/meta/view/views/all_leads/audit -> undefined client.meta.getAudit
GET /api/v1/meta/view/views/all_leads/diff -> undefined client.meta.diffItem
POST /api/v1/meta/view/views/all_leads/rollback -> undefined client.meta.rollbackItem
GET /api/v1/meta/view/views%2Fall_leads/history -> GET /api/v1/meta/:type/:name/history client.meta.getHistory
DELETE /api/v1/meta/view/views%2Fall_leads -> DELETE /api/v1/meta/:type/:name client.meta.deleteItem
GET /api/v1/meta/view/views/all_leads/published -> GET /api/v1/meta/:type/:section/:name/published
POST /api/v1/meta/view/views/all_leads/publish -> POST /api/v1/meta/:type/:section/:name/publish
Live, through the real Hono app:
POST /api/v1/meta/view/views/all_leads/rollback -> status 404, body "404 Not Found"
GET /api/v1/meta/view/views/all_leads/audit -> status 404, body "404 Not Found"
That is Hono's notFound, byte-identical to an unmounted path — not a handler answer.
The root cause is a split inside the SDK, not four separate oversights
packages/client/src/index.ts builds these URLs two different ways, and nothing states which is the rule:
works — the server mounts the compound arity for these four
So the unencoded spelling is correct exactly where a compound arity is mounted, and silently wrong everywhere else. Two methods that reach the same family of doors (getHistory vs getAudit) sit on opposite sides of the split, one line apart in the same file.
meta.publishItem's own doc comment says "Compound names pass through unencoded, like getItem" — that sentence was true of the URL and false of the server until #11932 mounted the route. The same sentence is implied by the four methods above and is still false for them.
Why the existing guards cannot see this
#3642's reverse guard matches the URL a client method CONSTRUCTS against the ledger patterns. A compound name is interpolated into the same ${name} slot the single-arity pattern declares, so the guard sees /meta/:type/:name/audit, finds the row, and passes. The extra segment only exists at call time. #7526's live-mount parity gate probes ledger rows against the router and would catch an unmounted declared route — but no row declares the compound arity of these four, so there is nothing for it to probe. Both guards are green on this class by construction, which is the same shape #7526 was filed about.
Two dispositions, not one — the fork belongs to a maintainer
Encode in the SDK (getHistory's spelling) — four one-line changes, no new public surface, and it makes these methods work for compound names today. But it makes the transport spelling inconsistent with getItem / saveItem / getPublished / publishItem, which deliberately pass through unencoded because the server mounts a compound arity for them.
Not proposing which. What is not in doubt is that the current state is neither: an SDK method that constructs a URL nothing serves, answering 404 with no diagnostic.
Scope note
GET /api/v1/meta/:type/:name/layers is server-only in rest-route-ledger.ts and has no SDK method, so it is listed for completeness rather than as part of the defect.
Measured on origin/main at 1e79aa4f8 plus PR #12105 (efd007b73); the two mounted-compound controls in the first block are /published (#7526) and /publish (that PR).
Blocked-by: #12176
Measured while mounting the compound-name promotion door (#11932 / PR #12105). Filed rather than folded: #11932 is one route; this is four SDK methods aimed at an arity the server does not mount, and the split that produces it.
2026-08-25 maintainer ruling (recorded in the comments): the option-1/option-2 fork below is SUPERSEDED — metadata item names are ruled to not contain
/, so the compound-name convention itself is being retired via #12176's staged plan. This card waits on that card; the defect dissolves at its re-addressing stage.What was measured
Driven through the real registered server —
new RestServer(new HonoHttpServer(0), …).registerRoutes()— and asked of the LIVE router which registration would answer each path (IHttpServer.resolveMountedRoute, the same observation the #7526 dogfood parity gate reads).views/all_leadsis the compound name the SDK's own doc comments use.Live, through the real Hono app:
That is Hono's
notFound, byte-identical to an unmounted path — not a handler answer.The root cause is a split inside the SDK, not four separate oversights
packages/client/src/index.tsbuilds these URLs two different ways, and nothing states which is the rule:getReferences${route}/${type}/${name}/referencesgetAudit${route}/${type}/${name}/auditdiffItem${route}/${type}/${name}/diffrollbackItem${route}/${type}/${name}/rollbackgetHistory${route}/${encodeURIComponent(type)}/${encodeURIComponent(name)}/historydeleteItem${route}/${encodeURIComponent(type)}/${encodeURIComponent(name)}getItem/saveItem/getPublished/publishItemSo the unencoded spelling is correct exactly where a compound arity is mounted, and silently wrong everywhere else. Two methods that reach the same family of doors (
getHistoryvsgetAudit) sit on opposite sides of the split, one line apart in the same file.meta.publishItem's own doc comment says "Compound names pass through unencoded, likegetItem" — that sentence was true of the URL and false of the server until #11932 mounted the route. The same sentence is implied by the four methods above and is still false for them.Why the existing guards cannot see this
#3642's reverse guard matches the URL a client method CONSTRUCTS against the ledger patterns. A compound name is interpolated into the same
${name}slot the single-arity pattern declares, so the guard sees/meta/:type/:name/audit, finds the row, and passes. The extra segment only exists at call time. #7526's live-mount parity gate probes ledger rows against the router and would catch an unmounted declared route — but no row declares the compound arity of these four, so there is nothing for it to probe. Both guards are green on this class by construction, which is the same shape #7526 was filed about.Two dispositions, not one — the fork belongs to a maintainer
getHistory's spelling) — four one-line changes, no new public surface, and it makes these methods work for compound names today. But it makes the transport spelling inconsistent withgetItem/saveItem/getPublished/publishItem, which deliberately pass through unencoded because the server mounts a compound arity for them./references,/audit,/diffand/rollback(and decide about/layers, which isserver-only) — consistent with the direction Three ledgered /meta routes are never mounted and die in the/meta/:typecatch-all — the route audit can't see this class because it treats the ledger as ground truth for what's mounted #7526 and A compound-name draft has no REST promotion door —POST /meta/:type/:name/publishis mounted in one arity while its read twin/publishedis mounted in both #11932 took, and it makes one spelling correct everywhere. But it is four new public routes, so it is a Clause-② surface expansion and wants the contract-review tier.Not proposing which. What is not in doubt is that the current state is neither: an SDK method that constructs a URL nothing serves, answering 404 with no diagnostic.
Scope note
GET /api/v1/meta/:type/:name/layersisserver-onlyinrest-route-ledger.tsand has no SDK method, so it is listed for completeness rather than as part of the defect.Measured on
origin/mainat1e79aa4f8plus PR #12105 (efd007b73); the two mounted-compound controls in the first block are/published(#7526) and/publish(that PR).Generated by Claude Code