From 1974fb160f2d82b0243413370680548e7739b5e8 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 31 Aug 2026 13:32:54 +0000 Subject: [PATCH] docs: name FLOW_INPUT_SCHEMA_INVALID in the four flow refusal-code enumerations Four pages enumerate the flow trigger-refusal status contract and each still listed exactly the three pre-widening codes, stating the enumeration as complete while `FLOW_INPUT_SCHEMA_INVALID` (422, never-dispatched) was missing. Placement was read off `packages/runtime/src/flow-dispatch-status.ts`: `classifyFlowRefusal` tests the code in the never-dispatched arm block, above the `status === 'failed'` arm that answers 400 FLOW_FAILED. `ui/actions.mdx` is the one page that groups its enumeration by that distinction, so the code joins its "a dispatch that never happened" group there. Every entry spells the status together with its code, so the two codes now sharing 422 each stay a self-contained pair. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC --- .../flow-refusal-enumeration-four-pages.md | 37 +++++++++++++++++++ content/docs/api/declarative-endpoints.mdx | 2 +- content/docs/api/plugin-endpoints.mdx | 2 +- .../docs/protocol/kernel/http-protocol.mdx | 2 +- content/docs/ui/actions.mdx | 2 +- 5 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 .changeset/flow-refusal-enumeration-four-pages.md diff --git a/.changeset/flow-refusal-enumeration-four-pages.md b/.changeset/flow-refusal-enumeration-four-pages.md new file mode 100644 index 0000000000..028ea4bf6e --- /dev/null +++ b/.changeset/flow-refusal-enumeration-four-pages.md @@ -0,0 +1,37 @@ +--- +"@objectstack/docs": patch +--- + +fix(docs): four pages enumerating the flow refusal codes now name `FLOW_INPUT_SCHEMA_INVALID` (#13720) + +`FlowRefusalCode` gained a fourth member in `packages/runtime/src/flow-dispatch-status.ts` +(`b6d3d76b5`), answered `422` and classified never-dispatched. Three pages were updated with +it; four others enumerate the same union and were not, so each stated the enumeration as +**complete** while it was one code short — a teaching surface telling a reader that a status +they will really receive does not exist. + +| page | the row that was short | +|:---|:---| +| `content/docs/api/declarative-endpoints.mdx` | the `type: 'flow'` delegation row | +| `content/docs/api/plugin-endpoints.mdx` | `POST /automation/:name/trigger` | +| `content/docs/protocol/kernel/http-protocol.mdx` | the declared-endpoint `type: 'flow'` answer row | +| `content/docs/ui/actions.mdx` | the `type: 'flow'` over-REST row | + +Prose only — no schema, no runtime behaviour and no generated artifact moves. The two +generated reference pages (`references/api/contract.mdx`, +`references/api/error-code-ledger.mdx`) already carried the code, which is why the +generator needed nothing here. + +**Which group the new code joins was read off the source, not inferred from the status.** +`classifyFlowRefusal` tests `FLOW_INPUT_SCHEMA_INVALID` inside the +`── never dispatched: the producer says WHICH refusal ──` arm block, above the +`result.status === 'failed'` arm that answers `400 FLOW_FAILED`. `ui/actions.mdx` is the +one page that splits its enumeration into "a run that ran and was rejected" versus "a +dispatch that never happened", so the code is placed in the second group there; putting it +beside `FLOW_FAILED` would have said the run started. + +`422` is now carried by two codes (`FLOW_NO_START_NODE` and `FLOW_INPUT_SCHEMA_INVALID`). +Each page spells the status together with its code, so every entry stays a self-contained +pair rather than a claim about what `422` alone means — the discriminator is `error.code`, +which is what `http-protocol.mdx` already tells readers to branch on. The full table with +per-code guidance stays where it is, in `content/docs/automation/flows.mdx`. diff --git a/content/docs/api/declarative-endpoints.mdx b/content/docs/api/declarative-endpoints.mdx index 1552a8e159..4d59c14167 100644 --- a/content/docs/api/declarative-endpoints.mdx +++ b/content/docs/api/declarative-endpoints.mdx @@ -100,7 +100,7 @@ declaration to shadow a built-in one. Match → policy chain (`rateLimit` → `a | `type` | Delegates to | Request shape | |:---|:---|:---| | `object_operation` | the same `callData` binding that serves `/api/v1/data/{object}` | `find` reads its criteria from the query string; `get` / `update` / `delete` take the record id from `query.id`; `create` / `update` take the body. `create` answers `201`, the rest `200` | -| `flow` | the same automation pipeline as `POST /api/v1/automation/{name}/trigger` | the request body is the flow input. A refused or failed run answers a real status: `404` unknown flow, `409` `FLOW_DISABLED`, `422` `FLOW_NO_START_NODE`, `400` `FLOW_FAILED` | +| `flow` | the same automation pipeline as `POST /api/v1/automation/{name}/trigger` | the request body is the flow input. A refused or failed run answers a real status: `404` unknown flow, `409` `FLOW_DISABLED`, `422` `FLOW_NO_START_NODE`, `422` `FLOW_INPUT_SCHEMA_INVALID`, `400` `FLOW_FAILED` | The frozen vocabulary has **no path-template syntax**, so an endpoint cannot express `/leads/{id}` — a record id travels as `?id=…`. A method that no declaration claims diff --git a/content/docs/api/plugin-endpoints.mdx b/content/docs/api/plugin-endpoints.mdx index f28d949c20..e653c572e2 100644 --- a/content/docs/api/plugin-endpoints.mdx +++ b/content/docs/api/plugin-endpoints.mdx @@ -48,7 +48,7 @@ Approve/reject were never workflow routes (ADR-0019): approval is a flow node, a | Method | Endpoint | Description | |:-------|:---------|:------------| -| POST | `/automation/:name/trigger` | Trigger an automation flow by name (legacy alias: `/automation/trigger/:name`). Failures answer real status codes, not a `200` wrapping an inner failure: **404** unknown flow, **409** `FLOW_DISABLED`, **422** `FLOW_NO_START_NODE`, **400** `FLOW_FAILED` for a run that ran and was rejected — see [Run a flow via API](/docs/automation/flows#run-a-flow-via-api) | +| POST | `/automation/:name/trigger` | Trigger an automation flow by name (legacy alias: `/automation/trigger/:name`). Failures answer real status codes, not a `200` wrapping an inner failure: **404** unknown flow, **409** `FLOW_DISABLED`, **422** `FLOW_NO_START_NODE`, **422** `FLOW_INPUT_SCHEMA_INVALID`, **400** `FLOW_FAILED` for a run that ran and was rejected — see [Run a flow via API](/docs/automation/flows#run-a-flow-via-api) | The automation dispatcher also exposes flow CRUD (`GET`/`POST /automation`, `GET`/`PUT`/`DELETE /automation/:name`) and run observability/resume routes — see [Durable pause & resume](/docs/automation/flows#durable-pause--resume-adr-0019). diff --git a/content/docs/protocol/kernel/http-protocol.mdx b/content/docs/protocol/kernel/http-protocol.mdx index 925c1fbf48..1cf488689c 100644 --- a/content/docs/protocol/kernel/http-protocol.mdx +++ b/content/docs/protocol/kernel/http-protocol.mdx @@ -1225,7 +1225,7 @@ declaration to shadow a built-in route: | Endpoint declares | Answer | |:---|:---| | `type: 'object_operation'` | delegated to the same `callData` binding that serves `/api/v1/data/{object}` — byte-identical `data` | -| `type: 'flow'` | delegated to the same automation pipeline as `POST /api/v1/automation/{name}/trigger` — the same execution context builder, the same `execute` call, and **the same response contract**: a refused or failed run is classified into the same real status codes (404 / 409 `FLOW_DISABLED` / 422 `FLOW_NO_START_NODE` / 400 `FLOW_FAILED`), from one shared definition all three flow doors read. Branch on the status and `error.code`, never on an inner success flag | +| `type: 'flow'` | delegated to the same automation pipeline as `POST /api/v1/automation/{name}/trigger` — the same execution context builder, the same `execute` call, and **the same response contract**: a refused or failed run is classified into the same real status codes (404 / 409 `FLOW_DISABLED` / 422 `FLOW_NO_START_NODE` / 422 `FLOW_INPUT_SCHEMA_INVALID` / 400 `FLOW_FAILED`), from one shared definition all three flow doors read. Branch on the status and `error.code`, never on an inner success flag | | `authRequired: true` (or omitted) + anonymous caller | `401` `UNAUTHENTICATED`, the same envelope every seam answers | | `rateLimit` armed and exhausted | `429` + `Retry-After`, never with a cache directive | | `cacheTtl: 30` on a successful GET | `Cache-Control: private, max-age=30` — `private` is a security rule, not tuning: any response can be RLS-trimmed | diff --git a/content/docs/ui/actions.mdx b/content/docs/ui/actions.mdx index 0ad92acebf..fc4efb999b 100644 --- a/content/docs/ui/actions.mdx +++ b/content/docs/ui/actions.mdx @@ -346,7 +346,7 @@ The endpoint dispatches on the **declared `type`**, exactly like the MCP | `type` | Over REST | |:---|:---| | `script` | Runs the registered handler / inline body. | -| `flow` | Runs `target` on the automation engine, with your identity forwarded (a `runAs: 'user'` flow enforces RLS as you). Dispatches the same flow as `POST /api/v1/automation/:target/trigger`, without having to know the flow name — **and answers the same way**: a run that ran and was rejected is **400** `FLOW_FAILED`, while a dispatch that never happened is separated out (**404** unknown flow / **409** `FLOW_DISABLED` / **422** `FLOW_NO_START_NODE`). See [Run a flow via API](/docs/automation/flows#run-a-flow-via-api) for the full table — it is one table, read by both doors. | +| `flow` | Runs `target` on the automation engine, with your identity forwarded (a `runAs: 'user'` flow enforces RLS as you). Dispatches the same flow as `POST /api/v1/automation/:target/trigger`, without having to know the flow name — **and answers the same way**: a run that ran and was rejected is **400** `FLOW_FAILED`, while a dispatch that never happened is separated out (**404** unknown flow / **409** `FLOW_DISABLED` / **422** `FLOW_NO_START_NODE` / **422** `FLOW_INPUT_SCHEMA_INVALID`). See [Run a flow via API](/docs/automation/flows#run-a-flow-via-api) for the full table — it is one table, read by both doors. | | `api` | **400** — it dispatches on `target`; call that endpoint directly. | | `url` / `modal` / `form` | **400** — client-side navigation; there is nothing for the server to run. |