Skip to content

fix(spec,runtime)!: AnalyticsQueryRequest 即裸 AnalyticsQuery;/analytics 入口 Zod 校验,畸形请求 400(#3878 建议 1) - #4010

Merged
os-zhuang merged 1 commit into
mainfrom
claude/analytics-shim-degradation-xmx6iu
Jul 30, 2026
Merged

fix(spec,runtime)!: AnalyticsQueryRequest 即裸 AnalyticsQuery;/analytics 入口 Zod 校验,畸形请求 400(#3878 建议 1)#4010
os-zhuang merged 1 commit into
mainfrom
claude/analytics-shim-degradation-xmx6iu

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#3878 建议 1 的落地(#3891/#3989 退役 shim 后,信封方言已无服务端消费者,规范形状之争消失)。PR A of 2 —— PR B(路由挂载与服务安装绑定)随后。

Spec:AnalyticsQueryRequestSchema 信封 → 裸形状

原 schema 描述的是 { cube, query: {...}, format }信封——已退役降级 shim 的方言,真引擎从不认识:信封 body 推断出无列 cube,死在驱动层 SQL 语法错误(SELECT FROM …),而不是形状错误(#3867 第一次实证被它带偏)。现在:

  • schema = AnalyticsQuery(顶层 cube+measures 必填,dimensions/where/timeDimensions/order/limit/offset/timezone 并列),.strict()
  • query/formatretiredKey 墓碑(tsc never + 解析期即迁移指引);format 从未实现(declared ≠ enforced,响应永远是 JSON 包络);
  • 按守卫链要求登记为两条 step-17 semantic migration(HTTP wire 变更,无存储元数据可改写);authorable-surface / spec-changes / 升级指南 / JSON schemas / OpenAPI / references 全部重新生成,gen:schema 守卫通过。

Runtime:入口校验

POST /analytics/query/analytics/sql 在入口用该 schema 校验:

测试

  • 新增:5 条入口校验用例(http-dispatcher.test.ts)+ 2 条 wire 层 400/200 端到端(dispatcher-validation-error.real.test.ts);
  • 修正 9 处旧 fixture 的不合契约 body(它们各自测的是服务解析/错误路径,与校验正交);
  • 本地全量 pnpm test132/132 绿

影响面

发对形状的调用方(objectui dashboard、client SDK 透传)零影响。发信封或 filters 的调用方从"500 SQL 语法错误 / 数字静默变大"变为 400 + 指出改法@objectstack/spec major(schema 形状变更),@objectstack/runtime minor。

关联:#3878#3891#3989#3867#3918

🤖 Generated with Claude Code

https://claude.ai/code/session_017WZBR9XyhXoqKCU6qQVyjx


Generated by Claude Code

… validate /analytics bodies at the entry (#3878)
The spec's AnalyticsQueryRequestSchema described a {cube, query, format}
ENVELOPE — the dialect of the retired degraded shim (#3891) that the
real engine never understood: an envelope body inferred a column-less
cube and died as a driver SQL syntax error (SELECT FROM ...) instead of
a shape error, and a non-contract `filters` key was silently ignored.
That mismatch sent #3867's first investigation to a wrong conclusion.
Spec:
- AnalyticsQueryRequestSchema = bare AnalyticsQuery + required top-level
`cube`, `.strict()`. `query` and `format` are retiredKey-tombstoned
(tsc `never` + parse-time prescription); `format` was never
implemented (declared != enforced).
- Registered as two step-17 semantic migrations (an HTTP-wire change
with no stored metadata to rewrite); spec-changes.json, upgrade
guide, authorable-surface, JSON schemas, OpenAPI, reference docs
regenerated.
Runtime:
- POST /analytics/query and /analytics/sql validate the body at the
entry and raise the duck-typed VALIDATION_FAILED shape both
dispatcher error exits already map to 400 + fields[] (#3918) — the
envelope answers with the tombstone's migration text, `filters` gets
a bespoke hint at `where`. The domain throws through, matching its
existing error contract; the wire-level 400 is pinned in
dispatcher-validation-error.real.test.ts.
- A valid body is forwarded byte-identical (validation only): parsing
would inject the schema's timezone 'UTC' default and override
org-timezone resolution (#1982/#2018).
- Service-absent still answers 404 before body inspection (#3891).
Tests: 5 new entry-validation cases + wire-level 400/200 pins; 9 legacy
fixtures updated to contract-valid bodies.
Refs #3878, #3891, #3867, #3918. Part 1 of the #3878 follow-through;
route-mount conditionality is the next PR.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017WZBR9XyhXoqKCU6qQVyjx
@vercel

vercelBot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredJul 30, 2026 2:40am

Request Review

@github-actionsgithub-actionsBot added size/l documentation Improvements or additions to documentation tests tooling labels Jul 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/runtime, @objectstack/spec.

112 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via packages/runtime, @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/api/wire-format.mdx(via @objectstack/runtime)
  • content/docs/automation/approvals.mdx(via packages/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via @objectstack/runtime, packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via packages/runtime, packages/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/index.mdx(via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx(via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx(via @objectstack/runtime)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/deployment/vercel.mdx(via @objectstack/runtime)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via packages/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via packages/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/permissions/authentication.mdx(via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx(via packages/runtime, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/runtime)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx(via @objectstack/runtime, @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v16.mdx(via @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)
  • content/docs/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 30, 2026 02:56
@os-zhuang
os-zhuang merged commit 9b6fe7c into mainJul 30, 2026
18 checks passed
@os-zhuang
os-zhuang deleted the claude/analytics-shim-degradation-xmx6iu branch July 30, 2026 02:57
os-zhuang added a commit that referenced this pull request Jul 30, 2026
#3891 follow-through, ADR-0076 D11) (#4019)
* feat(runtime): mount /analytics routes only when the capability exists (#3891 follow-through, ADR-0076 D11)
The dispatcher plugin mounted POST /analytics/query, GET /analytics/meta
and POST /analytics/sql unconditionally, so a deployment without
@objectstack/service-analytics kept the routes in its table: PUT answered
405 + Allow: POST, advertising a method on an API that isn't there (the
POST itself has answered 404 since #3989 emptied the slot).
Mounting is now capability-conditional. Plugin start() runs after the
kernel's Phase-1 init, so service presence is authoritative:
- single-kernel, `analytics` absent: routes NOT mounted — every method
answers the adapter's shared not-found contract, and a boot log names
the fix (install @objectstack/service-analytics);
- single-kernel, `analytics` registered: unchanged;
- multi-tenant host (kernel-resolver wired): mounted unconditionally —
mounts are host-global while the service lives per-project kernel, so
presence is a per-request question answered by the analytics domain's
existing handled:false → 404. New public
HttpDispatcher.isMultiTenantHost() exposes the mode.
Tests: three conditional-mount cases in dispatcher-plugin.routes.test.ts
(absent / registered / multi-tenant); conformance boots gain an
analytics stub so the 405 + parity contracts keep their subject, plus a
capability-absent boot pinning "no mounts, shared 404 for every method,
sibling routes unaffected". Route-ledger row annotated (a row is an
SDK-expressibility claim, not a mount guarantee).
Completes the #3891 arc: #3989 emptied the slot, #4010 made the body
contract strict at the entry, this removes the last wire-level residue
of the uninstalled API. The hono standalone static discovery that still
hardcodes the full routes table is tracked as #4018.
Refs #3891, #3989, #4010, #4018.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017WZBR9XyhXoqKCU6qQVyjx
* docs(api): discovery sample reflects the capability-conditional analytics surface
The /api/v1 discovery example still showed the pre-#3989 picture:
analytics hardcoded in `routes` and reported available with
provider objectql on a minimal install. Show the honest minimal-install
shape instead — analytics unavailable with the install hint and omitted
from routes — which also makes the sample demonstrate the
omitted-when-uninstalled rule the paragraph below it explains.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017WZBR9XyhXoqKCU6qQVyjx
---------
Co-authored-by: Claude <noreply@anthropic.com>
os-zhuang added a commit that referenced this pull request Jul 30, 2026
…l inert-key removal, ADR-0113 (Proposed) (#3998)
Three parts, closing the #3896 security-audit line for the v17 window.
1. v17 release notes carry the whole security-hardening arc: a Highlights bullet (empty-criteria sharing + disabled RLS both fail closed now), two breaking-change sections with migration guidance, dead-cluster rows for DynamicLoadingConfig and rls.priority, Spec/kernel bullets for the 44-entry security-subset re-verification and the check:empty-state gate, and three upgrade-checklist items. implementation-status updated to match.
2. The four inert tool authoring keys are removed (major): category/permissions/active/builtIn parsed and did nothing — permissions promised an invocation gate nothing enforced, and active:false read as "withdrawn" while the tool kept reaching the LLM set and /execute kept running it (the rls.enabled shape). Full retirement kit per #3715: strict-rejection prescriptions (TOOL_RETIRED_KEY_GUIDANCE, 6 new tests), D2 conversion + D3 chain step (tool-inert-authoring-keys-removed), ToolCategorySchema removed with the key it typed, Studio tool form drops the retired inputs, the published AI skill's Tool section rewritten (it was teaching the removed keys, os:check'd), ledger entries deleted, api-surface + authorable-surface + json-schema.manifest + form-translation bundles updated deliberately, and the CLI authorWarn expectation retired with its ledger entry.
3. ADR-0113 merges as PROPOSED (not implemented): required as a write-time contract vs the column constraint as its own explicitly-authored axis — the tri-binding cited at its exact sites (record-validator; sql-driver.ts:4901; schema-drift.ts:249), criteria_json as first consumer. Q1 (spelling) / Q2 (new-field default) / Q3 (requiredWhen) remain open for adjudication.
spec: 6895 tests green; all gates green; 198 prose examples type-check; dogfood green. Union-merged over #4010's step-17 rationale (chain-replay gate verified the composition).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude