Skip to content

feat: single-source API-method derivation contract (#3391 P1)#3498

Draft
os-zhuang wants to merge 5 commits into
mainfrom
claude/complete-scheduled-development-xqcwch
Draft

feat: single-source API-method derivation contract (#3391 P1)#3498
os-zhuang wants to merge 5 commits into
mainfrom
claude/complete-scheduled-development-xqcwch

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

背景

落地 #3391P1 契约(方案见 该 issue 评论,基线 main@69f1dfd5c)。服务端成为唯一裁决者:每个对象的有效操作集由 spec 里唯一一张派生表从 6 个原语(get/list/create/update/delete/bulk)白名单解析,REST gate、runtime dispatcher gate、/me/permissions 注解全部消费同一张表;前端只渲染下发的 effective 结果,不读原始 apiMethods

框架侧三个 PR(方案里的 PR-1/2/3)合并到本分支,按 commit 切分。objectui 侧(PR-4)属独立仓库,不在本分支范围。

改动

PR-1 — 派生真源(d3ae192)

  • @objectstack/spec/data 新增 api-derivation:resolveEffectiveApiMethods / isApiOperationAllowed / effectiveOperationsArray / API_METHOD_DERIVATION / DATA_ACTION_TO_API_OPERATION。三态语义(undefined=全开、[]=全禁、子集=派生闭包);legacy 8 值由 6 原语派生;restore/purge 不派生(enable.trash 已退役 [11.0][A2] Remove dead author-facing metadata properties (ADR-0049 enforce-or-remove) #2377)。
  • runtime api-exposure.ts 重写消费共享表:修复 dispatcher/MCP 死码(getObject() 返回嵌套 .enable,旧扁平读取从未生效)——嵌套优先、兼容扁平;[] 翻转为 deny-all。
  • objectql 注册期 legacy 值 deprecation warning;两张 affordance 表(registry MANAGED_WRITE_VERB_AFFORDANCE、plugin-security WRITE_OP_AFFORDANCE)加交叉引用注释,明确 verb→affordance(UI 意图)与 verb→primitive(API 收紧)两条正交轴。

PR-2 — REST gate 接入(209d3e5)

  • 主 gate apiAccessDenialFromEnable 换共享 resolver;405 allowed 改为 effective 集。
  • import 两段式(粗判先行 + writeMode 精判);export 列投影同 FLS 可读集;bulk 五路统一 bulk ∧ child
  • 四个 in-repo 显式白名单 identity 对象补 bulk 原语。

PR-3 — 有效操作下发(d9941b1)

  • spec EffectiveObjectPermissionSchema(ObjectPermissionSchema.extend({ apiOperations }),仅响应侧,authoring schema 不动);/me/permissions 注解每对象 effective apiOperations,超管 enumerate 补条目;seed → fold → clamp → annotate,全程 guarded。

行为变化(收紧,均属"declared ≠ enforced"缺口收口)

  1. apiMethods: [] + apiEnabled:true → 全 405(in-repo 零影响,[] 对象均配 apiEnabled:false,404 先于 405)。
  2. dispatcher/MCP 白名单由死转活。
  3. import/export 反向派生:CRUD 白名单对象放行 import(⊆create∨update)/export(⊆list);export 表头收缩为 FLS 可读列。
  4. Many/batch 要求 bulk 原语(四对象已补;三方缺 bulk 会 405)。
  5. 405 allowed 由原始白名单改为 effective 集。

测试

  • 新增:spec api-derivation.test.ts(25)、rest rest-api-derivation-gates.test.ts、hono effective-api-operations.test.ts、export FLS 列投影阻塞性两条。
  • 更新:runtime api-exposure.test.ts(翻转 []=deny-all + 嵌套回归)、rest 曝露套件(405 = effective / bulk∧child)、rest-batch-endpoint、spec permission/protocol。
  • 全仓 pnpm build 71/71 通过;涉及包全量测试通过(spec 6880 / runtime 605 / objectql 1073 / rest 372 / hono 107 / plugin-security 540 / platform-objects 215)。

后续(另开 issue,不在本 PR)

用户级 export 权限轴(接 userExportAllowed 槽)、元数据不可解析 fail-open 残余风险、detail/form 面 edit/delete 接 effective、security 服务 getReadableFields 查询面、P2 枚举收缩、objectui 侧接线(PR-4)。

Closes 部分 #3391(P1)。

🤖 Generated with Claude Code

https://claude.ai/code/session_012L8EfEa157Pe6C73qRnaJH


Generated by Claude Code

claude added 3 commits July 25, 2026 04:32
…PR-1)

Introduce the spec's one source of truth for turning an object's
`enable.apiMethods` whitelist into its effective operation set, and make the
runtime dispatcher gate consume it.

- spec: new `@objectstack/spec/data` `api-derivation` module —
  `resolveEffectiveApiMethods` / `isApiOperationAllowed` /
  `effectiveOperationsArray` / `API_METHOD_DERIVATION` /
  `DATA_ACTION_TO_API_OPERATION`. Three-state semantics (undefined =
  unrestricted, [] = deny-all, subset = derived closure); the legacy 8 verbs
  derive from the six primitives (get/list/create/update/delete/bulk).
  restore/purge never derive (enable.trash retired, #2377). liveness note
  updated.
- runtime: rewrite `api-exposure.ts` to consume the shared table. Fixes the
  silent dead dispatcher/MCP gate — `getObject()` returns the flags nested
  under `.enable`, which the flat-only reader ignored (nested-first, flat-
  compatible). Flips `[]` from fail-open to deny-all.
- objectql: registration-time deprecation warning for standalone legacy
  apiMethods values (`warnDeprecatedExplicitApiMethods`); cross-reference
  comments distinguishing the verb→affordance (UI-intent) axis from the
  verb→primitive (API-tightening) axis.
- plugin-security: cross-reference comment on the parallel WRITE_OP_AFFORDANCE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012L8EfEa157Pe6C73qRnaJH
…PR-2)

Route every per-object exposure gate through the spec's single derivation source
of truth, so the three-state whitelist and the derived verbs are resolved
identically everywhere.

- Main gate (`apiAccessDenialFromEnable`): resolver-backed; `[]` → deny-all;
  the 405 body's `allowed` array is now the EFFECTIVE operation set (enum-
  ordered), not the raw whitelist. `enforceApiAccess` forwards writeMode /
  bulkChild opts.
- Import: two-stage gate — a coarse `create ∨ update` check 405s fully-closed
  objects before the CSV parse; a writeMode-precise second stage
  (insert→create, update→update, upsert→create∧update) after prep resolves the
  mode.
- Export: reverse-derives from `list`, and the schema-derived column header is
  projected to the FLS-readable set (union of masked-row keys) so export can
  never expose a wider column set than list. Explicit `?fields=` is honored but
  masked values stay empty.
- Bulk: all five surfaces (createMany/updateMany/deleteMany, per-object /batch,
  cross-object /batch) require `bulk ∧ child`.
- platform-objects: the four in-repo explicit-whitelist identity objects gain
  the `bulk` primitive so their Many/batch surfaces keep working.

Tests: rest exposure suite (405 allowed = effective, deny-all, bulk∧child),
new rest-api-derivation-gates suite, batch endpoint bulk cases, and two
blocking export FLS column-projection tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012L8EfEa157Pe6C73qRnaJH
#3391 PR-3)

Add the response-side contract that lets the server hand the frontend the
resolved effective operation set — the single channel the UI consumes, never the
raw whitelist.

- spec: `EffectiveObjectPermissionSchema` extends `ObjectPermissionSchema` with
  an optional `apiOperations` array; `GetEffectivePermissionsResponse.objects`
  uses it. The authoring `ObjectPermissionSchema` is deliberately NOT extended,
  so a permission-set author can never declare a meaningless key.
- hono: `/me/permissions` now annotates each per-object entry with its effective
  `apiOperations` (`annotateEffectiveApiOperations`), and for a modify-all
  super-user seeds false-init entries for restricting objects absent from the
  merged map (`seedSuperUserRestrictedObjects`) so fold pulls them true.
  Sequence: seed → fold → clamp → annotate, each guarded (failure omits
  apiOperations and the client falls back to default-allow).
- client: zero code — the typed surface carries the new field via the spec
  re-export.
- changeset documenting the contract and the five behavior changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012L8EfEa157Pe6C73qRnaJH
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 25, 2026 4:46am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling size/xl labels Jul 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 7 package(s): @objectstack/objectql, @objectstack/platform-objects, @objectstack/plugin-hono-server, @objectstack/plugin-security, @objectstack/rest, @objectstack/runtime, @objectstack/spec.

116 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/rest, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/rest, @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 @objectstack/objectql, 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 packages/objectql, @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/plugin-security, @objectstack/spec)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/objectql)
  • 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/objectql, @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/plugin-hono-server, @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/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/objectql)
  • content/docs/permissions/access-recipes.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql, @objectstack/plugin-hono-server, @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via @objectstack/plugin-security, packages/runtime, @objectstack/spec)
  • content/docs/permissions/explain.mdx (via @objectstack/plugin-security)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-security, @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/plugin-security, @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/objectql, @objectstack/plugin-hono-server, @objectstack/plugin-security, @objectstack/rest, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql, @objectstack/platform-objects, @objectstack/plugin-hono-server, @objectstack/plugin-security, @objectstack/rest, @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 packages/rest, @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/objectql, @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/objectql, @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/objectql, @objectstack/plugin-hono-server, @objectstack/plugin-security, @objectstack/rest, @objectstack/runtime, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/plugin-hono-server, @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/audience-based-interfaces.mdx (via packages/plugins/plugin-security)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/plugin-security, @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/platform-objects, @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.

…3391)

Auto-generated reference doc for the new EffectiveObjectPermissionSchema
(`pnpm gen:schema && gen:docs`). Keeps content/docs/references in sync with
packages/spec — fixes the check:docs CI gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012L8EfEa157Pe6C73qRnaJH
Regenerate the public-API surface snapshot (`gen:api-surface`) for the 19 new
`@objectstack/spec` exports added by #3391 (the api-derivation module +
EffectiveObjectPermission). 0 breaking, 19 added — fixes the check:api-surface
CI gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012L8EfEa157Pe6C73qRnaJH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants