Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
1242694
wip(automation,metadata-protocol,spec): the engine's verdict on a sto…
claude Oct 13, 2026
66e6bb7
test(service-automation): a stored flow the engine refuses at boot re…
claude Oct 13, 2026
0de2ff5
test(metadata-protocol,spec): the verdict is the engine's answer or n…
claude Oct 13, 2026
c3b976f
chore(spec): regenerate the projections of the flow registration verdict
claude Oct 13, 2026
1313739
docs(automation): a refused flow's verdict on the meta read; changeset
claude Oct 13, 2026
bdae792
Merge remote-tracking branch 'origin/main' into claude/issue-23316-re…
claude Oct 13, 2026
7c71647
test(metadata-protocol): the verdict cases ride the pinned by-name en…
claude Oct 13, 2026
1254cb8
chore(spec): bind the flow registration verdict's states to their doc
claude Oct 13, 2026
8f39c94
test(spec): pin the flow registration verdict's two schemas isomorphi…
claude Oct 13, 2026
e9367e3
feat(rest,metadata-protocol,spec): the default cached flow read carri…
claude Oct 13, 2026
87937d4
test(rest,metadata-protocol,service-automation): pin the verdict on t…
claude Oct 13, 2026
7da6ada
docs(spec,automation): the verdict rides the default cached read too;…
claude Oct 13, 2026
9c498cd
chore(spec): regenerate the projections of the cached read's verdict
claude Oct 13, 2026
2b84fe5
Merge remote-tracking branch 'origin/main' into claude/issue-23316-re…
claude Oct 14, 2026
ba00d59
Merge remote-tracking branch 'origin/main' into claude/issue-23316-re…
claude Oct 14, 2026
93a7272
chore(spec): regenerate the reference index over both schema families
claude Oct 14, 2026
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
15 changes: 15 additions & 0 deletions .changeset/23316-refused-flow-registration-verdict.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@objectstack/spec': minor
'@objectstack/metadata-protocol': minor
'@objectstack/service-automation': minor
'@objectstack/rest': minor
---

A stored flow the automation engine refuses is no longer served as running: the flow meta read serves the engine's verdict beside the item as `registration`, an envelope sibling of `lock`, and the flow list serves it as `registrations` keyed by flow name, so a refused flow reads `{ state: 'refused', reason: { code?, message } }` beside its stored `status`, which stays as authored.

Clause-②: yes (widening)

- **The shape** (`@objectstack/spec`). `FlowRegistrationSchema` is a closed set: `registered` (the engine holds the flow), `refused` (it refused the stored flow and holds nothing under the name; `reason` carries the engine's own `message` and, when the thrown error set one, its `code`, verbatim), and `unregistered` (it holds nothing and refused nothing: a read during boot, between a save and the engine following it, or after a deliberate unregister). `GetMetaItemResponseSchema` gains `registration` and `GetMetaItemsResponseSchema` gains `registrations`, both optional. New exports: `FlowRegistrationSchema`, `FlowRegistrationRefusalSchema` and the types `FlowRegistration` and `FlowRegistrationRefusal`. `IAutomationService` gains the optional member `getFlowRegistration(name)`.
- **Where the verdict comes from** (`@objectstack/service-automation`). The `kernel:ready` cold-boot bind records each flow it refuses, with what the engine threw, after it withdraws the flow; the `metadata:reloaded` re-sync and the metadata-change sync record a refusal that leaves the name unregistered. A later successful registration of the name, or its withdrawal, ends the record. `AutomationEngine` answers `getFlowRegistration(name)` and takes `recordFlowRefusal(name, error)`. The refusal's warn line, its level and the withdrawal are unchanged.
- **Where it is served** (`@objectstack/metadata-protocol`). The protocol reads the engine through the kernel's service table by name, as it already reads the engine's stored-flow canonicalizer; it does not import the automation service. The field is absent where no automation service declares the member, on every type other than `flow`, on a draft read and on the execution face the engine binds from: absence means no engine judged the flow, never `registered`. The verdict is the engine of the process that answered the read.
- **The default door** (`@objectstack/rest`, `@objectstack/metadata-protocol`, `@objectstack/spec`). `GET /api/v1/meta/flow/:name` on a default deployment (`metadata.enableCache: true`) is answered by the cached item branch, which now carries `registration` too. `getMetaItemCached` returns it beside `data`, read live from its delegate read on every call, and `GetMetaItemCachedResponseSchema` declares it. It is never part of the ETag, so a cached read that carries it is never answered `304 Not Modified`: a judged flow is always served in full. The cached branch's other envelope keys are unchanged.
18 changes: 18 additions & 0 deletions content/docs/automation/flows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2133,6 +2133,24 @@ The plugin is a **soft dependency** on `metadata` — it tolerates running
without `MetadataPlugin` and it logs (not throws) on per-flow registration
failures so one broken flow does not abort startup.

A flow the engine refuses is not served as running. The metadata read keeps
the stored `status` as you wrote it, and serves the engine's verdict beside
the item: `registration` on `GET /api/v1/meta/flow/:name`, and `registrations`,
keyed by flow name, on `GET /api/v1/meta/flow`. The verdict is one of three:

| `state` | Meaning |
|:---|:---|
| `registered` | The engine holds the flow. Whether its trigger is bound is `GET /api/v1/automation/_status`'s answer. |
| `refused` | The engine refused the stored flow and holds nothing under its name, so it never fires. `reason` is what the engine threw, verbatim: its `message`, and its `code` when it set one. |
| `unregistered` | The engine holds nothing under the name and refused nothing: the read came during boot, or between a save and the engine following it, or the flow was unregistered on purpose. |

A save, a publish or a reload that registers a corrected body ends the
`refused` state. The field is absent where no automation service answered the
read. The item read carries it on the default cached read as well as the
uncached one: it is read live on every call and kept out of the ETag, so a
read that carries it is always answered in full, never with `304 Not
Modified`. It is the verdict of the engine in the process that answered.

## Expressions in flows

A flow computes with **one expression dialect, CEL**: every condition is bare
Expand Down
Loading
Loading