Found while implementing #9415 (the #9378 ruling's remaining two rows). Filed unassigned for triage — not fixed there: #9378 ruled the two trigger routes, and what these two doors should answer is a contract decision of its own, not a mechanical extension.
Three doors dispatch a flow through IAutomationService.execute. Only one of them classifies the result.
1. POST /api/v1/automation/:name/trigger (+ the legacy spelling) — CORRECT
respondToFlowTrigger (packages/runtime/src/domains/automation.ts) answers the full four-row table after #9413 + #9415:
| engine exit | answer |
|---|
| flow not found | 404 |
| flow disabled | 409FLOW_DISABLED |
| flow has no start node | 422FLOW_NO_START_NODE |
| ran and failed | 400FLOW_FAILED |
2. POST /api/v1/actions/:object/:action with type: 'flow' — MISLABELS two of the four
packages/runtime/src/action-execution.ts maps anysuccess: false result to one code:
if(result&&typeofresult==='object'&&'success'inresult&&result.success===false){consterr: any=newError(`Flow '${action.target}' failed: ${result.error??'unknown error'}`);err.status=400;err.code='FLOW_FAILED';throwerr;}Its comment says "The flow RAN and rejected" — which is now false for two of the exits it catches. A disabled flow invoked through an action answers 400 FLOW_FAILED, telling the caller a run failed when no node ever executed. The producer's result.code is available and ignored.
3. Declared endpoints (type: 'flow') — still fully pre-#9378
packages/runtime/src/endpoint-executor.ts ends the flow branch with:
returnsuccessAnswer(awaitautomation.execute(plan.flow,automationContext));
Unconditional 200. Every failure — including a run that ran and failed — still rides the double envelope #3962 ruled out for /actions: HTTP 200 {"success":true,"data":{"success":false,...}}. This door was not in #9413's scope and nothing has flipped it since.
Consequence for the docs: content/docs/protocol/kernel/http-protocol.mdx describes a type: 'flow' endpoint as "delegated to the same automation pipeline as POST /api/v1/automation/{name}/trigger". True of the context builder and the service method, no longer true of the response. #9415 corrects that page's wording to state the boundary; it does not change the behaviour.
The decision this needs
Whether the #9378 table is a property of the flow-dispatch contract (all three doors converge) or of the trigger route (the others keep their own envelope conventions deliberately). Door 3 additionally carries the endpoint policy chain and outputMapping, so converging it is not a copy of the mapper.
Note both doors also answer for FLOW_FAILED today, so this is not created by #9415 — that card only makes the divergence wider and easier to see, because the producer now says which refusal it is.
Refs: #9378 (the ruling) - #9413 (rows 1-2) - #9415 (rows 3-4) - #3962 (the /actions double-envelope ruling).
Found while implementing #9415 (the #9378 ruling's remaining two rows). Filed unassigned for triage — not fixed there: #9378 ruled the two
triggerroutes, and what these two doors should answer is a contract decision of its own, not a mechanical extension.Three doors dispatch a flow through
IAutomationService.execute. Only one of them classifies the result.1.
POST /api/v1/automation/:name/trigger(+ the legacy spelling) — CORRECTrespondToFlowTrigger(packages/runtime/src/domains/automation.ts) answers the full four-row table after #9413 + #9415:404409FLOW_DISABLED422FLOW_NO_START_NODE400FLOW_FAILED2.
POST /api/v1/actions/:object/:actionwithtype: 'flow'— MISLABELS two of the fourpackages/runtime/src/action-execution.tsmaps anysuccess: falseresult to one code:Its comment says "The flow RAN and rejected" — which is now false for two of the exits it catches. A disabled flow invoked through an action answers
400 FLOW_FAILED, telling the caller a run failed when no node ever executed. The producer'sresult.codeis available and ignored.3. Declared endpoints (
type: 'flow') — still fully pre-#9378packages/runtime/src/endpoint-executor.tsends the flow branch with:Unconditional 200. Every failure — including a run that ran and failed — still rides the double envelope #3962 ruled out for
/actions:HTTP 200 {"success":true,"data":{"success":false,...}}. This door was not in #9413's scope and nothing has flipped it since.Consequence for the docs:
content/docs/protocol/kernel/http-protocol.mdxdescribes atype: 'flow'endpoint as "delegated to the same automation pipeline asPOST /api/v1/automation/{name}/trigger". True of the context builder and the service method, no longer true of the response. #9415 corrects that page's wording to state the boundary; it does not change the behaviour.The decision this needs
Whether the #9378 table is a property of the flow-dispatch contract (all three doors converge) or of the trigger route (the others keep their own envelope conventions deliberately). Door 3 additionally carries the endpoint policy chain and
outputMapping, so converging it is not a copy of the mapper.Note both doors also answer for
FLOW_FAILEDtoday, so this is not created by #9415 — that card only makes the divergence wider and easier to see, because the producer now says which refusal it is.Refs: #9378 (the ruling) - #9413 (rows 1-2) - #9415 (rows 3-4) - #3962 (the
/actionsdouble-envelope ruling).