You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(api): declarative apis: endpoints (ADR-0121) have no developer page — and api/plugin-endpoints.mdx is a different thing with a near-identical name #10215
Notation note (2026-08-20): placeholders below are written in {braces}, not angle brackets. GitHub's body sanitizer on this repo eats angle-bracket-delimited text, and it silently ate this card's namespace rule when it was first filed — gate 1 read /api/v1/apps//. Corrected and read back.
The gap
api is a metadata kind (ApiEndpointSchema, ADR-0121), authorable as defineStack({ apis }), and live from protocol 17 — the stack describe string says so: 'API Endpoints — declared endpoints are live from protocol 17; each is gated at publish (ADR-0121, #5040)'. An entry that passes its gates serves requests as soon as it is published.
Documentation for a developer who wants to expose an endpoint:
references/api/endpoint.mdx — 69 generated lines;
protocol/kernel/http-protocol.mdx — normative implementer text, the only place ADR-0121 is named in the hand-written tree;
and that is all.
Meanwhile content/docs/api/plugin-endpoints.mdx — the page whose title a developer will click — is a catalog of built-in plugin routes (/auth, /workflow, /automation, /ui, /realtime, /notifications, /ai, /i18n, /graphql, /storage). Nothing on it concerns authoring an endpoint. The naming collision actively sends the reader to the wrong page.
This one carries real risk, not just inconvenience: authRequired: false on an entry is, in ADR-0121's own words, an anonymous, internet-reachable execution entry point. Undocumented is the wrong state for that.
File surface
content/docs/api/declarative-endpoints.mdx — new
content/docs/api/index.mdx — edit (Cards + the sentence that disambiguates the two pages)
content/docs/api/plugin-endpoints.mdx — edit, intro paragraph and "See also" only: state in one sentence what this page is not, and link the new page. ⛔ Do not restructure or re-title it.
content/docs/api/meta.json — one added entry
⛔ Nothing outside content/docs/api/. Do not edit protocol/kernel/http-protocol.mdx — cross-link it.
What to write
Read ApiEndpointSchema (packages/spec/src/api/endpoint.zod.ts), the apis: docblock in packages/spec/src/stack.zod.ts, and the publish-time judge (validateApiEndpointDeclarations / identityFreeEndpointGateFailure). Cover:
Which channel (ADR-0121 D3) — a caller inside the platform (session, UI, AI/MCP, SDK) invokes an action; a caller outside it (a partner system, an inbound webhook) reaches an apis: endpoint. Lead with this; it is the decision the reader actually has to make.
The four gates, because publish/validate fails naming the endpoint and the key, and a developer needs to recognise each failure:
Namespace (D1/D2) — path must be /api/v1/apps/{manifest.namespace}/{subpath}, where {manifest.namespace} is the package's own declared namespace. ⚠️ Take the exact spelling from stack.zod.ts / the validator rather than from this line: no built-in domain lives under apps/, and two packages cannot collide because their namespaces differ, so route ownership is structural rather than a maintained list.
Supported subset — only object_operation (with both objectParams.object and .operation) and flow (with a target) execute in 17.x; script and proxy are refused pending their own rulings, and mapping transform is refused because no transformation registry exists;
Policy — authRequired: false requires rateLimit.enabled: true (D6 makes an armed budget the paired obligation of anonymity), an armed budget must be usable, cacheTtl must be non-negative and GET-only;
Uniqueness — one claim per METHOD + path within a stack.
Anonymous endpoints — give this its own section with the security framing above, not a bullet.
api is code-only (api 注册表条目声明 allowRuntimeCreate: true,但运行时创建的端点匹配器永远看不见 —— 声明的能力运行时不兑现(真实 boot 实测) #5488): allowRuntimeCreate: false, allowOrgOverride: false. PUT /meta/api/:name is refused with 403 NOT_CREATABLE before any body validation; endpoints are validated on the artifact route (stack compile, loader ingest, publishPackage). Say this, because a developer who tries the runtime metadata API will otherwise not understand the 403.
A worked example end to end, and where /openapi.json describes the result.
⛔ Non-goals
Do not document script or proxy targets as available. They are refused today; a doc that presents them as coming soon is a promise this card has no authority to make.
Do not touch packages/spec/**.
Gates (derived at dispatch time via node scripts/pm/dispatch-gates.mjs, not recalled)
check:doc-anchors, check:docs-audit-scope, check:docs-redirects, check:published-readme-links, check:role-word, check:cross-package-test-inputs, and the spec liveness set (check:empty-state, check:liveness, check:strictness-ledger, check:variant-docs). Re-derive against your actual diff; check:adr-anchors / check:adr-links may also apply once you cite ADR-0121 decision letters — cite only letters that are real headings of that ADR.
⚠️ Adding a hand-written page turns check:docs-audit-scope RED until you run node scripts/docs-audit/check-audit-scope.mjs --write, which writes one line into .claude/workflows/docs-accuracy-audit.js. That is expected and required (see #9866). Commit exactly what --write produces, name it as a declared deviation in the PR body, and do not flip the PR ready or enqueue it — .claude/** is a governed surface, so the PR is human-merge-only.
Acceptance
A developer who wants a partner system to call into their app finds the page from the API sidebar, and is not misdirected by plugin-endpoints.
Each of the four gates is stated in terms the reader can match against the error they will see.
The anonymous-endpoint obligation is impossible to miss.
Part of #10206
The gap
apiis a metadata kind (ApiEndpointSchema, ADR-0121), authorable asdefineStack({ apis }), and live from protocol 17 — the stack describe string says so:'API Endpoints — declared endpoints are live from protocol 17; each is gated at publish (ADR-0121, #5040)'. An entry that passes its gates serves requests as soon as it is published.Documentation for a developer who wants to expose an endpoint:
references/api/endpoint.mdx— 69 generated lines;protocol/kernel/http-protocol.mdx— normative implementer text, the only place ADR-0121 is named in the hand-written tree;Meanwhile
content/docs/api/plugin-endpoints.mdx— the page whose title a developer will click — is a catalog of built-in plugin routes (/auth,/workflow,/automation,/ui,/realtime,/notifications,/ai,/i18n,/graphql,/storage). Nothing on it concerns authoring an endpoint. The naming collision actively sends the reader to the wrong page.This one carries real risk, not just inconvenience:
authRequired: falseon an entry is, in ADR-0121's own words, an anonymous, internet-reachable execution entry point. Undocumented is the wrong state for that.File surface
content/docs/api/declarative-endpoints.mdx— newcontent/docs/api/index.mdx— edit (Cards + the sentence that disambiguates the two pages)content/docs/api/plugin-endpoints.mdx— edit, intro paragraph and "See also" only: state in one sentence what this page is not, and link the new page. ⛔ Do not restructure or re-title it.content/docs/api/meta.json— one added entry⛔ Nothing outside
content/docs/api/. Do not editprotocol/kernel/http-protocol.mdx— cross-link it.What to write
Read
ApiEndpointSchema(packages/spec/src/api/endpoint.zod.ts), theapis:docblock inpackages/spec/src/stack.zod.ts, and the publish-time judge (validateApiEndpointDeclarations/identityFreeEndpointGateFailure). Cover:action; a caller outside it (a partner system, an inbound webhook) reaches anapis:endpoint. Lead with this; it is the decision the reader actually has to make.pathmust be/api/v1/apps/{manifest.namespace}/{subpath}, where{manifest.namespace}is the package's own declared namespace.stack.zod.ts/ the validator rather than from this line: no built-in domain lives underapps/, and two packages cannot collide because their namespaces differ, so route ownership is structural rather than a maintained list.object_operation(with bothobjectParams.objectand.operation) andflow(with atarget) execute in 17.x;scriptandproxyare refused pending their own rulings, and mappingtransformis refused because no transformation registry exists;authRequired: falserequiresrateLimit.enabled: true(D6 makes an armed budget the paired obligation of anonymity), an armed budget must be usable,cacheTtlmust be non-negative and GET-only;apiis code-only (api注册表条目声明 allowRuntimeCreate: true,但运行时创建的端点匹配器永远看不见 —— 声明的能力运行时不兑现(真实 boot 实测) #5488):allowRuntimeCreate: false,allowOrgOverride: false.PUT /meta/api/:nameis refused with 403NOT_CREATABLEbefore any body validation; endpoints are validated on the artifact route (stack compile, loader ingest,publishPackage). Say this, because a developer who tries the runtime metadata API will otherwise not understand the 403./openapi.jsondescribes the result.⛔ Non-goals
scriptorproxytargets as available. They are refused today; a doc that presents them as coming soon is a promise this card has no authority to make.packages/spec/**.Gates (derived at dispatch time via
node scripts/pm/dispatch-gates.mjs, not recalled)check:doc-anchors,check:docs-audit-scope,check:docs-redirects,check:published-readme-links,check:role-word,check:cross-package-test-inputs, and the spec liveness set (check:empty-state,check:liveness,check:strictness-ledger,check:variant-docs). Re-derive against your actual diff;check:adr-anchors/check:adr-linksmay also apply once you cite ADR-0121 decision letters — cite only letters that are real headings of that ADR.check:docs-audit-scopeRED until you runnode scripts/docs-audit/check-audit-scope.mjs --write, which writes one line into.claude/workflows/docs-accuracy-audit.js. That is expected and required (see #9866). Commit exactly what--writeproduces, name it as a declared deviation in the PR body, and do not flip the PR ready or enqueue it —.claude/**is a governed surface, so the PR is human-merge-only.Acceptance
plugin-endpoints.