Skip to content

feat(spec)!: 声明式 apis: 翻转 —— 硬拒收窄为逐端点门(#5040 E7) - #5188

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-5111-publish-flip
Aug 4, 2026
Merged

feat(spec)!: 声明式 apis: 翻转 —— 硬拒收窄为逐端点门(#5040 E7)#5188
os-zhuang merged 1 commit into
mainfrom
claude/issue-5111-publish-flip

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#5111
Part of #5040(E7 —— 本程序唯一改变行为的一单)

这一单做了什么

#4936 对非空 apis:整面硬拒packages/spec/src/stack.zod.ts 上是一条 .max(0),理由是当时端点面全链路零执行(无挂载、无匹配器、每个键——包括 authRequired——解析通过而不生效)。E1–E6 把执行器建成之后,这条理由不复存在,继续拒绝就变成了反方向的谎:一条能跑的能力被挡在门外。

本 PR 把它收窄为逐端点门:过门的端点在 publish 之后真实挂载、真实服务流量。门挂在 ObjectStackDefinitionSchema 上(不是挂在 defineStack 里),因此 defineStackos validate、lint 评分、metadata 插件的 artifact 摄入、EnvironmentArtifactSchema.metadata 这五条路径没有一条能绕过它

五道门(每道自带处方,点名端点、点名键)

拒绝的形状运行时对偶
命名空间(ADR-0121 D1/D2)path 不是 /api/v1/apps/{manifest.namespace}/{subpath};或声明了 apis: 却没有显式 manifest.namespace(Q1 = A,不做 deriveNamespaceFromPackageId 回落——对外 URL 契约不应因为改了 package id 而漂移)isAppEndpointPath(路由候选判据)
支持子集type: 'script' / 'proxy';object_operationobjectParams.object.operation;flowtarget 为空planEndpointTarget
映射任何 transform;不可用的 source/target 路径(空串、空段 a..b、原型键);互撞的 target(同路径 / 一条写进另一条内部);外加 PM 裁决:inputMapping 写在 find/get/delete 上(不读 body,声明必然惰性,与非 GET 的 cacheTtl 同类同判)mappingDeclarationRejection
策略(ADR-0121 D6 + E4 四条)authRequired: false 而无已装配限流(判据 rateLimit?.enabled === true,不是键存在——enabled 的 schema 缺省是 false,写了窗口和配额却不写 enabled 会得到一个「匿名且完全不计量」的端点);已装配但不可用的预算(maxRequests/windowMs ≤ 0);负数 cacheTtl;非 GET 上的 cacheTtlendpointRateLimiterRegistry / cacheControlHeader
唯一性同栈内两条声明认领同一 METHOD + 规整后 path(裁掉一个尾斜杠,与匹配器同规则),拒绝文案点名两条endpointIndexKey

每一道门的判据都是照读运行时得出的,不是凭记忆复述:接受的集合 = 执行器服务的集合。运行时侧的 501 拒绝保留不动——绕过 publish 直写 metadata.register() 的条目仍需要那道兜底。

翻转的阳性断言(#4936 之后第一次)

新增测试里第一组就是正面用例:命名空间下的 object_operation 端点、flow 端点、装配了限流的匿名端点、body 型操作上的映射键——全部通过校验。回归钉子同时保留:空 apis: / 缺省 apis: 依然合法,没有 namespace 但也没有端点的 stack 依然可发布。

升级文档 = 安全承载件(维护者裁决:不加激活开关)

生成机制只从 ADR-0087 registry 取料,所以指令写在 registry 里:

  • packages/spec/src/migrations/registry.ts 新增 semantic 条目 declarative-apis-endpoints-live(surface / replacement / reason / acceptanceCriteria),外加 step17 rationale 的一段 ⚠️ 前置说明;
  • 二者经 gen:upgrade-guide / gen:spec-changes 落进 docs/protocol-upgrade-guide.mdspec-changes.json(本 PR 已重生成)。

内容明确指令升级者(通常是 AI 维护者):升级前审视每一处历史 apis: 配置;过门的端点在 v17 publish 后即为在线;特别注意显式 authRequired: false——schema 缺省是 true,漏写是安全的,只有显式 false 才打开匿名面,且 D6 要求它配一条已装配的限流。未触碰 content/docs/releases/

其它

  • 词表冻结:ApiEndpointSchema 零改动——门是校验逻辑,不是新键;
  • normalizeEndpointPath 上移到 @objectstack/spec/api,packages/metadata 的匹配器改为再导出。唯一性门与匹配器索引键从此不可能对「规范形式」产生分歧(否则可以发布一对匹配器只会留一条的重复声明);
  • changeset:@objectstack/specmajor(与一期 feat(spec,core,runtime)!: 声明式 apis: 响亮拒绝 + ApiRegistry 整面退役 (#4936, #4939) #5065 同级,同属 v17 破坏面),正文含 FROM → TO 与升级前的安全审视说明。

验证(真实输出)

spec test Test Files 305 passed (305) / Tests 7794 passed (7794)
spec typecheck tsc --noEmit (clean)
spec check:generated ✗ 3 stale → --fix → spec-changes / upgrade-guide / api-surface(仅本改动)
check:exported-any ✅ 1843 types + 1594 schemas
check:dual-source ✅ 0 accepted dual-source
metadata test 17 passed / 384 tests runtime test 89 passed / 1312 tests
rest test 40 passed / 608 tests cli test 69 passed / 612 tests
turbo typecheck runtime + rest + cli:55 tasks successful
eslint clean(六个改动文件)

🤖 Generated with Claude Code

https://claude.ai/code/session_01EYGdmvWP1ieZSLqvAW6uyd


Generated by Claude Code

…ates (#5111, #5040 E7)
THE FLIP. #4936 refused a non-empty `apis:` wholesale because the declarative
endpoint surface executed nothing — no route mounted, no matcher, every key
including `authRequired` parsed green and gated nothing. The #5040 E-series
built the executor, so that premise is gone; keeping the refusal would be the
lie in the other direction. This replaces the blanket `.max(0)` with a
per-endpoint gate on `ObjectStackDefinitionSchema`, and an endpoint that passes
it is MOUNTED and serves traffic on publish.
Gates, each rejecting with a prescription naming the endpoint and the key:
- namespace (ADR-0121 D1/D2): `path` must be
`/api/v1/apps/<manifest.namespace>/<subpath>`; `manifest.namespace` must be
declared explicitly (#5040 Q1 = A — no `deriveNamespaceFromPackageId`
fallback for an outward URL contract);
- supported subset (mirrors `planEndpointTarget`): `script` / `proxy`, an
`object_operation` missing `objectParams.object|operation`, a `flow` with an
empty `target`;
- mapping (mirrors `mappingDeclarationRejection`): any `transform`, an unusable
`source`/`target` path (empty, empty segment, prototype keys), colliding
targets — plus `inputMapping` on `find`/`get`/`delete`, which never read a
body (PM ruling: same category as `cacheTtl` on a non-GET);
- policy (ADR-0121 D6 + the E4 refusals): `authRequired: false` requires
`rateLimit.enabled === true` (presence is NOT armed — `enabled` defaults to
`false`), an armed budget must be usable, `cacheTtl` non-negative and GET-only;
- uniqueness: one claim per METHOD + normalized path inside a stack.
The gate lives on the schema, not in `defineStack`, so every publish/validate
seam runs it: `defineStack`, `os validate`, the lint scorer, the metadata
plugin's artifact ingestion and `EnvironmentArtifactSchema.metadata`.
`normalizeEndpointPath` moves to `@objectstack/spec/api` and the endpoint
matcher re-exports it, so the uniqueness gate and the matcher's index key can
never disagree about the canonical path form.
Upgrade documentation is the security deliverable (maintainer ruling: no
activation switch): a `declarative-apis-endpoints-live` semantic migration entry
plus a step-17 rationale paragraph instruct the upgrading (AI) maintainer to
review every historical `apis:` block before upgrading and to pay particular
attention to explicit `authRequired: false`. Both reach
`docs/protocol-upgrade-guide.md` and `spec-changes.json` through the ADR-0087
generators. Vocabulary frozen: no key added, removed or renamed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYGdmvWP1ieZSLqvAW6uyd
@vercel

vercelBot commented Aug 4, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 4, 2026 8:18am

Request Review

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 4, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

108 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 @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/spec)
  • content/docs/automation/approvals.mdx(via @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via 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 @objectstack/metadata, packages/spec)
  • content/docs/concepts/north-star.mdx(via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @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/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • 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/spec)
  • content/docs/kernel/cluster.mdx(via packages/metadata, @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/metadata, @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @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/metadata, @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/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/metadata-service.mdx(via @objectstack/metadata)
  • content/docs/protocol/kernel/plugin-spec.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/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/metadata, @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/metadata, @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.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xlteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

E7(#5040 执行器):翻转 —— publish 硬拒收窄为「不支持子集 + 命名空间门」,声明式端点随 v17 放行执行

2 participants

@os-zhuang@claude