Skip to content

Commit aed92e9

Browse files
docs: name FLOW_INPUT_SCHEMA_INVALID in the four flow refusal-code enumerations (#13824)
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. Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC Co-authored-by: Claude <noreply@anthropic.com>
1 parent b9be867 commit aed92e9

5 files changed

Lines changed: 41 additions & 4 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
"@objectstack/docs": patch
3+
---
4+
5+
fix(docs): four pages enumerating the flow refusal codes now name `FLOW_INPUT_SCHEMA_INVALID` (#13720)
6+
7+
`FlowRefusalCode` gained a fourth member in `packages/runtime/src/flow-dispatch-status.ts`
8+
(`b6d3d76b5`), answered `422` and classified never-dispatched. Three pages were updated with
9+
it; four others enumerate the same union and were not, so each stated the enumeration as
10+
**complete** while it was one code short — a teaching surface telling a reader that a status
11+
they will really receive does not exist.
12+
13+
| page | the row that was short |
14+
|:---|:---|
15+
| `content/docs/api/declarative-endpoints.mdx` | the `type: 'flow'` delegation row |
16+
| `content/docs/api/plugin-endpoints.mdx` | `POST /automation/:name/trigger` |
17+
| `content/docs/protocol/kernel/http-protocol.mdx` | the declared-endpoint `type: 'flow'` answer row |
18+
| `content/docs/ui/actions.mdx` | the `type: 'flow'` over-REST row |
19+
20+
Prose only — no schema, no runtime behaviour and no generated artifact moves. The two
21+
generated reference pages (`references/api/contract.mdx`,
22+
`references/api/error-code-ledger.mdx`) already carried the code, which is why the
23+
generator needed nothing here.
24+
25+
**Which group the new code joins was read off the source, not inferred from the status.**
26+
`classifyFlowRefusal` tests `FLOW_INPUT_SCHEMA_INVALID` inside the
27+
`── never dispatched: the producer says WHICH refusal ──` arm block, above the
28+
`result.status === 'failed'` arm that answers `400 FLOW_FAILED`. `ui/actions.mdx` is the
29+
one page that splits its enumeration into "a run that ran and was rejected" versus "a
30+
dispatch that never happened", so the code is placed in the second group there; putting it
31+
beside `FLOW_FAILED` would have said the run started.
32+
33+
`422` is now carried by two codes (`FLOW_NO_START_NODE` and `FLOW_INPUT_SCHEMA_INVALID`).
34+
Each page spells the status together with its code, so every entry stays a self-contained
35+
pair rather than a claim about what `422` alone means — the discriminator is `error.code`,
36+
which is what `http-protocol.mdx` already tells readers to branch on. The full table with
37+
per-code guidance stays where it is, in `content/docs/automation/flows.mdx`.

content/docs/api/declarative-endpoints.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ declaration to shadow a built-in one. Match → policy chain (`rateLimit` → `a
100100
| `type` | Delegates to | Request shape |
101101
|:---|:---|:---|
102102
| `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` |
103-
| `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` |
103+
| `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` |
104104

105105
The frozen vocabulary has **no path-template syntax**, so an endpoint cannot express
106106
`/leads/{id}` — a record id travels as `?id=…`. A method that no declaration claims

content/docs/api/plugin-endpoints.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Approve/reject were never workflow routes (ADR-0019): approval is a flow node, a
4848

4949
| Method | Endpoint | Description |
5050
|:-------|:---------|:------------|
51-
| 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) |
51+
| 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) |
5252

5353
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).
5454

content/docs/protocol/kernel/http-protocol.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1225,7 +1225,7 @@ declaration to shadow a built-in route:
12251225
| Endpoint declares | Answer |
12261226
|:---|:---|
12271227
| `type: 'object_operation'` | delegated to the same `callData` binding that serves `/api/v1/data/{object}` — byte-identical `data` |
1228-
| `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 |
1228+
| `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 |
12291229
| `authRequired: true` (or omitted) + anonymous caller | `401` `UNAUTHENTICATED`, the same envelope every seam answers |
12301230
| `rateLimit` armed and exhausted | `429` + `Retry-After`, never with a cache directive |
12311231
| `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 |

content/docs/ui/actions.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ The endpoint dispatches on the **declared `type`**, exactly like the MCP
346346
| `type` | Over REST |
347347
|:---|:---|
348348
| `script` | Runs the registered handler / inline body. |
349-
| `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. |
349+
| `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. |
350350
| `api` | **400** — it dispatches on `target`; call that endpoint directly. |
351351
| `url` / `modal` / `form` | **400** — client-side navigation; there is nothing for the server to run. |
352352

0 commit comments

Comments
 (0)