Skip to content

feat(spec)!: shrink ApiMethod enum to the six primitives (#3543, P2 of #3391)#3581

Merged
os-zhuang merged 2 commits into
mainfrom
claude/apimethod-enum-shrink-risk-utzm24
Jul 27, 2026
Merged

feat(spec)!: shrink ApiMethod enum to the six primitives (#3543, P2 of #3391)#3581
os-zhuang merged 2 commits into
mainfrom
claude/apimethod-enum-shrink-risk-utzm24

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Closes #3543 (P2 of #3391).

BREAKING: the authored enable.apiMethods enum shrinks to the six primitives (get/list/create/update/delete/bulk). The eight legacy values (upsert/aggregate/history/search/restore/purge/import/export) are derived effective operations only — no longer authorable. Ships as a minor bump per the lockstep launch-window policy (check-changeset-no-major): every package versions together, so the ! marker + changeset are the breaking-change record, matching the pending breaking changesets already on main.

What changed

spec

  • ApiMethod z.enum → six primitives. New stripLegacyApiMethods z.preprocess compat layer: a stored legacy value is stripped at parse with a FROM→TO warning (canonicalize-and-warn, never a hard failure) — permanent tolerance, since real metadata does not upgrade in lockstep with the spec. Unknown values (typos) still hard-fail. The deny-all cliff (a pure-legacy whitelist strips to [] = deny-all) gets a dedicated loud warning. LEGACY_API_METHOD_GUIDANCE is the value-level tombstone map (AGENTS.md Post-Task Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3).
  • Type split — authored vs effective vocabulary: new ApiOperation type / ApiOperationSchema / API_OPERATION_ORDER (14 values, byte-stable pre-shrink wire order) carry the gate/wire vocabulary. The wire contract is unchanged: the 405 allowed array and /me/permissions apiOperations still serialize derived verbs (export, search, …) — objectui's Export-button gating keeps working with zero frontend changes (verified: objectui imports no ApiMethod, consumes apiOperations as string[]). EffectiveObjectPermissionSchema.apiOperations now validates against ApiOperationSchema. API_METHOD_ORDER stays as a deprecated alias.
  • EffectiveApiMethods.explicitLegacy and the "explicit wins" honoring are removed; the derivation resolver ignores legacy/unknown strings on un-parsed inputs, so the zod path and the raw path converge on the same effective set.
  • 元数据不可解析时的 fail-open 残余风险评估(api-exposure / rest-server) #3545 deferred tightening landed: a present-but-non-array apiMethods now resolves to deny-all (fails closed) instead of unrestricted.
  • Regenerated: api-surface.json (additive), json-schema/ (published ApiMethod.json is now the strict six-value enum — deliberate divergence from the tolerant zod parse, documented in the changeset), reference docs, skill refs, liveness note.

objectql

  • warnDeprecatedExplicitApiMethodswarnStrippedLegacyApiMethods: a permanent per-object registration diagnostic (the parse-time strip warning carries no object name; this covers raw schemas that never pass through Zod). Escalates on the deny-all case.
  • The MANAGED_WRITE_VERB_AFFORDANCE "unification deferred to P2" comment resolved: the three affordance tables stay deliberately separate (ADR-0103); upsert/purge keys survive for un-parsed whitelists.

platform-objects

Migration

  • Reporter codemod: node scripts/codemod/apimethods-legacy-to-primitives.mjs (scans, prints exact replacement per site, flags whitelists the mapping would widen — deliberately not an auto-rewriter: silently widening an API-exposure whitelist is a security hazard, and the runtime strip already keeps stored metadata working).
  • The P1 changeset's "honored one release" sentence is amended (both phases ship in the same release train, so that promise would be false); the new changeset carries the full FROM→TO table.

Release sequencing note

P1 (#3498) and this P2 ship in the same release train — per discussion, the deprecation window is replaced structurally by the permanent parse-time strip layer + merged migration docs + codemod, which serve skip-version upgraders better than a one-week transitional release would.

Cross-repo

  • objectui: no changes needed (verified — consumes the effective set as strings; server wire vocabulary unchanged).
  • cloud: not accessible from this session — needs a grep for ApiMethod / legacy enum-value imports before the release train cuts.

Tests

  • spec: 6733 passed (includes new strip/deny-all/vocabulary-split suites)
  • objectql: registry 78 passed (diagnostic reworked)
  • platform-objects: 215 passed (assertions flipped to derivation-based)
  • rest: explicit-legacy gate test flipped to strip semantics
  • full monorepo turbo run: 131/131 tasks green locally
  • check:api-surface / check:docs / check:skill-refs gates verified locally

🤖 Generated with Claude Code

https://claude.ai/code/session_018V7nzG1x7nuqkwZQTowtBo

…#3391)

The authored enable.apiMethods enum is now exactly the six primitives
(get/list/create/update/delete/bulk); the eight legacy values are DERIVED
effective operations only. Stored metadata keeps parsing: a legacy value is
stripped at parse by the new stripLegacyApiMethods z.preprocess layer
(canonicalize-and-warn, permanent tolerance — real metadata does not upgrade
in lockstep), with a loud dedicated warning on the deny-all cliff (a
pure-legacy whitelist strips to [] = fully closed). Unknown values still
hard-fail. LEGACY_API_METHOD_GUIDANCE carries the FROM→TO tombstones.

Type split — authored vs effective vocabulary: new ApiOperation /
ApiOperationSchema / API_OPERATION_ORDER (14 values, byte-stable pre-shrink
wire order) carry the gate/wire vocabulary, so the 405 allowed array and
/me/permissions apiOperations still serialize derived verbs and the frontend
needs zero changes. EffectiveObjectPermissionSchema.apiOperations validates
against ApiOperationSchema; API_METHOD_ORDER remains as a deprecated alias.
EffectiveApiMethods.explicitLegacy and the P1 "explicit wins" honoring are
removed — the resolver ignores legacy/unknown strings on un-parsed inputs so
both paths converge. A present-but-non-array apiMethods now fails CLOSED
(deny-all), landing the #3545 deferred tightening.

objectql: warnDeprecatedExplicitApiMethods → warnStrippedLegacyApiMethods, a
permanent per-object registration diagnostic (parse-time strip carries no
object name); escalates on the deny-all case. The affordance-table
"unification deferred to P2" comment is resolved: tables stay separate
(ADR-0103).

platform-objects: sys_business_unit(-member) reclaim P1's explicit
import/export and, with sys_user_preference, drop apiMethods entirely (each
named all six primitives = default-open that also tracks future primitives).
The seven [] declarations are deliberately KEPT as defense-in-depth beside
apiEnabled:false (deviation from the issue checklist, documented). Stale
sys_secret fail-OPEN comment fixed.

Migration: reporter codemod scripts/codemod/apimethods-legacy-to-primitives.mjs
(reports exact replacements, flags widening — deliberately not an
auto-rewriter). P1 changeset's "honored one release" sentence amended (both
phases ship in one release train); the new major changeset carries the full
FROM→TO table. Docs/skills/api-surface/json-schema/liveness regenerated.

Closes #3543

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018V7nzG1x7nuqkwZQTowtBo
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Jul 27, 2026 8:11am

Request Review

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

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/objectql, @objectstack/platform-objects, @objectstack/rest, @objectstack/runtime, @objectstack/spec.

113 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/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/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/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/authentication.mdx (via @objectstack/objectql, @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/objectql, @objectstack/rest, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql, @objectstack/platform-objects, @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/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/spec)
  • content/docs/releases/v9.mdx (via @objectstack/objectql, @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/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.

…policy

The repo's fixed (lockstep) Changesets group versions every publishable
package together, and the launch-window CI gate (check-changeset-no-major)
rejects majors — breaking changes ship as minor with the ! marker as the
record, matching every pending breaking changeset on main (GraphQL-surface
removal, ADR-0104 write cutover). The changeset body still carries the full
BREAKING migration guide.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018V7nzG1x7nuqkwZQTowtBo
@os-zhuang
os-zhuang marked this pull request as ready for review July 27, 2026 08:27
@os-zhuang
os-zhuang merged commit d44dbfa into main Jul 27, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/apimethod-enum-shrink-risk-utzm24 branch July 27, 2026 08:28
os-zhuang added a commit that referenced this pull request Jul 27, 2026
…xture debris (#3586) (#3595)

The const described a route surface that never existed (/workflow,
/realtime listed; eight real prefixes missing) and was consumed by
nothing in the runtime — only its own tests and api-surface.json. Its
one real-world effect was underwriting CLIENT_SPEC_COMPLIANCE.md's false
"FULLY COMPLIANT" verdict (retired in #3571). The maintained,
guard-enforced source of truth is packages/runtime/src/route-ledger.ts.

Since the major train is loading now (#3562, #3581), the removal ships
directly instead of the deprecate-first interim step.

Also swept what #3562 left behind: registry test fixtures renamed from
graphql_api//graphql to honest OData naming, the tautological
config.graphql assertions dropped, the stale '"type": "graphql"'
JSDoc example corrected. Client's bare tsc -p is verified clean (the
TS2741 acceptance item — resolved by #3562's schema change, confirmed
here).

Closes #3586


Claude-Session: https://claude.ai/code/session_01LX9ut3MK3KykE11S9bJmv5

Co-authored-by: Claude <noreply@anthropic.com>
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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P2:ApiMethod 枚举收缩至 6 原语(breaking,独立版本)

2 participants