feat(spec)!: shrink ApiMethod enum to the six primitives (#3543, P2 of #3391)#3581
Merged
Merged
Conversation
…#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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 5 package(s): 113 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…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
marked this pull request as ready for review
July 27, 2026 08:27
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>
This was referenced Jul 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3543 (P2 of #3391).
BREAKING: the authored
enable.apiMethodsenum 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 aminorbump 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
ApiMethodz.enum → six primitives. NewstripLegacyApiMethodsz.preprocesscompat 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_GUIDANCEis the value-level tombstone map (AGENTS.md Post-Task Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3).ApiOperationtype /ApiOperationSchema/API_OPERATION_ORDER(14 values, byte-stable pre-shrink wire order) carry the gate/wire vocabulary. The wire contract is unchanged: the 405allowedarray and/me/permissionsapiOperationsstill serialize derived verbs (export,search, …) — objectui's Export-button gating keeps working with zero frontend changes (verified: objectui imports noApiMethod, consumesapiOperationsasstring[]).EffectiveObjectPermissionSchema.apiOperationsnow validates againstApiOperationSchema.API_METHOD_ORDERstays as a deprecated alias.EffectiveApiMethods.explicitLegacyand 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.apiMethodsnow resolves todeny-all(fails closed) instead of unrestricted.api-surface.json(additive),json-schema/(publishedApiMethod.jsonis now the strict six-value enum — deliberate divergence from the tolerant zod parse, documented in the changeset), reference docs, skill refs, liveness note.objectql
warnDeprecatedExplicitApiMethods→warnStrippedLegacyApiMethods: 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.MANAGED_WRITE_VERB_AFFORDANCE"unification deferred to P2" comment resolved: the three affordance tables stay deliberately separate (ADR-0103);upsert/purgekeys survive for un-parsed whitelists.platform-objects
sys_business_unit/sys_business_unit_member: P1's explicitimport/exportreclaimed; the remaining whitelist named all six primitives, so theapiMethodskey is dropped entirely (equivalent to default-open, and it tracks future primitives). Same forsys_user_preference. Tests now pinapiMethodsabsent + the derivation granting import/export.[]declarations (identity/oauth domain, all withapiEnabled: false) are kept, not deleted —[]is the second gate ifapiEnabledever flips totrue(defense-in-depth; 跟踪:UI 操作按钮与 apiMethods 白名单一致性契约落地(#3026 设计定稿) #3391 made[]fail closed precisely for this). Flagged in the changeset.sys_secretcomment fixed ("empty array would fail OPEN" — it fails CLOSED since 跟踪:UI 操作按钮与 apiMethods 白名单一致性契约落地(#3026 设计定稿) #3391).Migration
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).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
ApiMethod/ legacy enum-value imports before the release train cuts.Tests
check:api-surface/check:docs/check:skill-refsgates verified locally🤖 Generated with Claude Code
https://claude.ai/code/session_018V7nzG1x7nuqkwZQTowtBo