Concept 2 of 10
You gave a model a bag of tools and hoped it would call them in the right order. Flowgate hands it only the legal next moves — as links it follows, one at a time.
Register your tools with a model and it can fire any of them, any time, in any order — deploy before tests, delete before backup, skip the approval entirely. A flat list encodes no order and no preconditions. You're trusting the model to remember the rules every single call.
Flowgate doesn't hand the model a list. It hands the model the current legal moves — and nothing else.
The model searches once, then follows the links the server hands back. Each response tells it what's legal next — it never memorizes a catalog, it carries one current response and follows its links. (If you want the lineage: this is HATEOAS, the oldest idea in web APIs, pointed at agents.)
→ flowgate.command { "definitionId": "ship_guard", "input": {...} }
← { "state": "tests_green",
"version": 7,
"links": [
{ "rel": "open_pr", "method": "flowgate.command",
"args": { /* prefilled */ } }
] } One state, one set of legal moves. The model follows a link; it doesn't guess from a menu.
If the model tries an illegal transition, it doesn't get a dead end. It
gets the current legal links back and recovers in place — and the
rejection is still recorded as a transition.rejected audit
event, so you see it even when the model fixes itself silently.
→ flowgate.command { "transition": "deploy", ... }
← { "error": { "code": "INVALID_TRANSITION",
"message": "Transition 'deploy' is not valid from state 'tests_green'." },
"state": "tests_green",
"links": [ { "rel": "open_pr", ... } ] } // here's what you CAN do