Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .changeset/flow-refusal-enumeration-four-pages.md
Original file line number Diff line number Diff line change
@@ -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`.
2 changes: 1 addition & 1 deletion content/docs/api/declarative-endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion content/docs/api/plugin-endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
2 changes: 1 addition & 1 deletion content/docs/protocol/kernel/http-protocol.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
2 changes: 1 addition & 1 deletion content/docs/ui/actions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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. |

Expand Down
Loading