Repository navigation
GET /analytics/meta 的已声明响应契约与实际响应不是同一个形状(data: { cubes: CubeSchema[] } vs data: CubeMeta[]) #6442
Description
Activity
Triage —
needs-user-decision·domain:specDecision box, not the queue. The defect is established, but the two routes are not interchangeable: (a) narrows a published response contract to match the implementation, (b) widens what
GET /analytics/metaemits — and (b) puts each cube'ssqlon the wire, which is a capability expansion and an information-exposure change, not a bug fix. Picking between "the endpoint only ever emits a projection" and "the endpoint emits full cube definitions" is a product-semantics call on a public contract, which is exactly theneeds-user-decisionclass. The seat does not pre-empt it, and per the label's contract this card is not dispatchable until it is answered.Routing.
domain:spec, notspec-surface: route (a) rewritesAnalyticsMetadataResponseSchema(packages/spec/src/api/analytics.zod.ts:94-99) so that a payload legal today ({ cubes: [...] }) stops validating and today's actual response starts validating — the accept face moves, which is the explicit red line for the surface seat (precedents #6245 / #6235). Anything touchingpackages/specalso has one owner regardless of who needs it. If the maintainer picks route (b) instead, the work lands inpackages/services/service-analytics/src/analytics-service.ts:1184(plus thedriver-memorytwin atmemory-analytics.ts:504) and this card should be re-labelleddomain:servicesat that point — flagged here so the re-route is a deliberate act rather than a rediscovery.Stale-premise check against
origin/main(fetched,26b72e0), all four anchors still true:plugin-rest-api.zod.ts:1230still namesAnalyticsMetadataResponseSchema; that schema atanalytics.zod.ts:94still declaresdata: z.object({ cubes: z.array(CubeSchema) });analytics-service.ts:1184is stillasync getMeta(cubeName?: string): Promise<CubeMeta[]>; andpackages/spec/src/contracts/analytics-service.ts:208declares the same bare-array return. So the mismatch is not just prose drift — the contracts package states both shapes itself, in two files.One correction to the severity framing, offered as input to the ruling. The body says only the generated reference page misleads while the hand-written
data-api.mdxis closer to reality. There is a third reader:AnalyticsMetadataResponseSchemais exported frompackages/specandanalytics.test.tsparses payloads with it, so anyone validating a live response against the published schema fails today — that is a machine-checkable break, not only a documentation one, and it is the strongest argument that route (a)'s "zero runtime change" is still worth shipping quickly.Release board. Deliberately not boarded (
target:*withheld): the card already sits in the maintainer's decision inbox vianeeds-user-decision, and boarding it too would surface one item in two places while the answer — which route, and whether it is even a defect fix — is still open. Once ruled, whichever half becomes a defect card is a boarding candidate under criterion ② (published contract that lies).Duplicate search across all three repos (
AnalyticsMetadataResponseSchema,CubeMeta,analytics/meta, cube metadata format/label): no other open card. #6369 / PR #6441 is the neighbouring/analytics/querydocs card and is correctly kept separate — its review surface is a response example, this one's fix is inpackages/**. The second consequence noted in the body (MetricSchema.formatreaching no read endpoint, dropped by theCubeMetaprojection) is in scope of this same ruling — route (b) delivers it, route (a) confirms it stays unreachable — so it stays on this card rather than becoming a third one.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
os-project-manager commented
on Aug 8, 2026 CollaboratorMore actionsMaintainer ruling — 2026-08-08. The maintainer reviewed the PM's three-axis analysis of the decision inbox and accepted the recommendations (「按照你的建议继续」). Recorded by the PM session;
needs-user-decisioncomes off with this comment.Decision: Option (a) — narrow the declaration to the shape actually served.
AnalyticsMetadataResponseSchema.databecomes theCubeMeta[]projection. Zero runtime change; the published contract stops describing a shape that has never existed.Rationale: the declared
{ cubes: CubeSchema[] }has never worked, so narrowing breaks no live consumer — and no consumer was measured pulling full cube definitions. Option (b) would additionally pushsqlto clients, which is capability expansion without demonstrated pull (and a mild disclosure widening) — the startup-focus default says no. A schema that fails to parse against its own endpoint is worse than no schema for an AI-written integration, which is the axis that makes this urgent rather than cosmetic.Recorded return path (so this is not read as closing the door): if a dashboard surface ever needs
format(ordescription), add the key to theCubeMetaprojection — additive, backwards-compatible, and it does not turn/analytics/metainto a full definition dump. Do not revisit (b) for that need.Note the generated artifact:
content/docs/references/api/analytics.mdx:49is generated from this schema and currently publishes the wrong shape — it corrects itself viagen:docsin the same PR. The hand-writtendata-api.mdx:393-395already describes the array form correctly and needs no change.Bundled: this card now rides sweep card #6487 (undeclared/misdeclared response shapes) as its third member — same admission criterion, and the direction ruled here is the same one that card prescribes. It is excluded from batch selection while #6487 is open.
Generated by Claude Code
- added a commit that references this issue
on Sep 29, 2026 - added a commit that references this issue
on Oct 7, 2026
在实现 #6369(
/analytics/query响应示例的fields[]键集)时,为核实「display name / format 由GET /analytics/meta暴露」这句话而顺带查到。按 Prime Directive #10 独立立单,不搭 #6369 那个 docs PR(#6441)的车 —— 那张卡的评审面是/analytics/query的响应示例,本条是/analytics/meta的响应形状,两件事;而且本条的修复面在packages/**,#6369 明确不动。未自我认领。
事实面(实测,file:line)
声明侧 —— 路由表为该端点指定的响应 schema:
packages/spec/src/api/plugin-rest-api.zod.ts:1230—GET /meta的responseSchema: 'AnalyticsMetadataResponseSchema'packages/spec/src/api/analytics.zod.ts:94-99— 该 schema 声明data: z.object({ cubes: z.array(CubeSchema) }),即 一个对象,带cubes键,元素是完整的CubeSchemacontent/docs/references/api/analytics.mdx:49— 由上面这个 schema 生成并已发布的参考页照印:data为{ cubes: { name: string; title?: string; description?: string; sql: string; … }[] }实际侧 —— 运行时真正返回的:
packages/runtime/src/domains/analytics.ts:110-117—subPath === 'meta' && m === 'GET'⇒const result = await analyticsService.getMeta(cube); return deps.success(result)packages/services/service-analytics/src/analytics-service.ts:1184-1204—getMeta返回CubeMeta[],一个裸数组packages/spec/src/contracts/analytics-service.ts:89-99—CubeMeta是更窄的投影:{ name, title?, measures: Array< { name, type, title? } >, dimensions: Array< { name, type, title? } > }packages/drivers/driver-memory/src/memory-analytics.ts:504-522也就是说,按已发布契约写的客户端读
data.cubes拿到的是undefined(实际data本身就是数组),拿AnalyticsMetadataResponseSchema去 parse 会直接失败。顺带的第二个后果:metric 的
format没有任何读端点暴露MetricSchema.format(packages/spec/src/data/analytics.zod.ts:72)被packages/services/service-analytics/src/dataset-compiler.ts:381写入编译出的 cube metric,但getMeta的CubeMeta投影只保留{ name, type, title }——format(以及sql/filters/description)在投影里被丢弃。这正是 #6441 里那处「对分诊措辞的实测修正」的来源:分诊原句说 display name 与 format 都「由
GET /analytics/meta暴露」,实测只有 label(映射为title)到得了线上,format 到不了。#6441 因此只对 label 那半句写进了文档。影响 / 修法方向(不自行选)
两个方向对称度不高,需要裁定:
AnalyticsMetadataResponseSchema改成实际形状(data: CubeMeta[])。零运行时改动,契约照实描述;代价是承认/analytics/meta只发投影,format等键继续不可达。getMeta按已声明的{ cubes: CubeSchema[] }返回完整 cube 定义。这会扩大该端点的输出面(把sql也发到客户端),属能力扩张,按启动期聚焦原则应先问有没有真实消费方拉动。data-api.mdx:393-395的手写描述其实贴近实际形状(说data是数组),只有 spec schema 和由它生成的references/参考页是错的;一个只读手写页的读者不会撞上,一个照生成参考页或照 schema 写类型的读者会。这两种读者哪个是今天的真实用户,由分诊判定。查重
搜过本仓 open issue:
analytics meta cubes response schema/AnalyticsMetadataResponseSchema/CubeMeta/getMeta cube metadata format label/"analytics/meta"—— 除 #6369 本身外无同题单。