Bounded autonomy: deterministic workflows and metrics for coding agents
Agents can work autonomously, but guarantees must live outside the model: in programmatic workflows, deterministic gates, and verifiable metrics.
Share this article
Yesterday I read an idea that neatly captures where agent-assisted development should be heading: programmatic, deterministic workflows reinforced by quantifiable metrics.
The idea also appears in the video Pi Extensible Workflows: Full Guide. The goal is not to launch more agents or write longer prompts, but to stop trusting a model to remember every condition and know when it has finished correctly.
That control does not exist in the model. It must exist around it.
Autonomy is not the problem
An agent can read a repository, modify files, run commands, fix errors, and coordinate other agents. But it remains a probabilistic system: it can skip a check, narrow the solution’s scope, lose context, or declare a task complete because one specific test passes.
Asking it to “continue until everything is correct” delegates the definition of both “everything” and “correct” to the model.
Autonomy is not the problem. The problem is making the agent simultaneously the executor, judge, and sole source of evidence.
Workflows as executable boundaries
A programmatic workflow changes how responsibilities are divided. The agent decides how to solve a task; the workflow decides:
- which phases exist and which can run in parallel;
- what information each agent receives;
- which commands and checks are mandatory;
- which conditions allow progress;
- how many retries are available;
- when a run fails permanently.
A prompt can ask:
Implement the feature and make sure all tests pass.
A workflow can enforce:
1. Analyze requirements.
2. Implement in an isolated branch.
3. Run tests and static analysis.
4. Measure coverage, duplication, and complexity.
5. Run an independent review.
6. Accept only if every gate passes.
The agent keeps freedom during execution, but it cannot redefine the success criteria. That is the right boundary: autonomy in execution, determinism in validation.
Not everything should be another agent
Analyzing requirements, proposing a solution, or reviewing changes requires judgment. Checking a command’s exit code, cleaning a worktree, or verifying that a commit was integrated does not require generative intelligence.
Agent → proposes and modifies
Tool → executes and measures
Workflow → decides whether progress is allowed
Agent → fixes failures
Workflow → measures again
Using deterministic code for deterministic tasks reduces cost, latency, and the error surface. It also avoids spending context on work that a shell, compiler, or analyzer already performs better.
Metrics as a quality contract
Whenever possible, gates should rely on verifiable evidence:
- Coverage: measure branches and new code, not just total lines. Executing a line does not mean its result was verified.
- Mutation score: checks whether tests detect real behavioral changes. It is more expensive, so it can be reserved for domain logic, permissions, critical calculations, or modified code.
- Duplication: preventing an increase in new code is more useful than chasing a perfect global percentage.
- Cyclomatic complexity: highlights functions that are difficult to understand and test, although the number never replaces judgment.
- Security: critical vulnerabilities, secrets, or unsafe permissions should block delivery rather than remain optional recommendations.
Metrics are not automatic guarantees either. If we require only coverage, we will get tests that execute lines without checking behavior. If we require only low complexity, logic can be split into small functions that remain equally difficult to understand.
They should therefore measure different risks and, whenever possible, compare the change with the base branch. New code should not make the system worse even if the global average still looks acceptable.
Closing the loop
Measurement is useless if the result does not change execution. A minimal pipeline could be:
analyze → plan → implement
↓
tests + analysis + metrics
↓
independent review
↓
quality gate
↙ ↘
fix and repeat deliver
The gate should not interpret the agent’s prose. It should consume structured results:
{
"tests": { "passed": true },
"coverage": { "branchDelta": 1.2 },
"mutation": { "score": 82.1 },
"duplication": { "newCode": 0.4 },
"complexity": { "delta": 0 },
"security": { "critical": 0 }
}
Limits should be defined before execution and kept under version control. The specific numbers depend on the repository and the risk of each module; what matters is that the rules are explicit and reproducible.
Pi Taskflow and the next step
This is why I am configuring pi-taskflow in Pi. Its phases separate analysis, planning, execution, review, and verification. The most capable models can be reserved for higher-risk decisions, while faster models handle exploration or mechanical checks.
The important part is not having many agents. It is giving each one a bounded responsibility and passing the result through gates outside the model.
Workflows with parallelism or retries also need limits on time, tokens, cost, and attempt count. An impossible condition should not turn into an endless chain of runs.
The rule is simple: no autonomy loop without a verifiable exit condition and a safety limit.
Real control is not in the prompt
More context, rules, and reminders help, but they do not solve the fundamental problem: the model still interprets the instructions and decides whether it has followed them.
Real control must move outward: workflows defined as software, reproducible commands, verifiable metrics, cost limits, isolated changes, and independent review.
The goal is not to eliminate autonomy, but to make it operate inside a system that can say no.
Agents can decide how to reach a solution. They should not decide alone whether the solution is acceptable.