A flat tool list is a bag of loaded guns
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 on every single call.
Praxec doesn’t hand the model a list. It hands the model the current legal moves — and nothing else.
Every response carries its own next steps
The model searches once, then follows the links the kernel hands back. Each response tells it what’s legal next; it never memorizes a catalog. (If you want the lineage: this is HATEOAS, the oldest idea in web APIs, pointed at agents.)
← { "state": "tests_green",
"version": 7,
"links": [
{ "rel": "open_pr", "method": "praxec.command",
"args": { "workflowId": "wf_…", "expectedVersion": 7 } }
] }
One state, one set of legal moves. The model follows a link; it doesn’t guess from a menu.
A wrong move doesn’t crash the run
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.
← { "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