Governance¶
BoundFlow applies guardrails in three layers — from a hard per-run cap to self-healing version rollback. Runtime limits are enforced during a run (SDK-side); lifecycle policies are evaluated after runs on aggregated metrics (server-side).
1. Runtime policy — hard caps during a run¶
from boundflow import RuntimePolicy
# Halt the agent when a single run's cost crosses the budget:
await cp.set_agent_runtime_policy(wf.id, "analyst", RuntimePolicy(max_cost_usd=0.25))
Runtime policies also cover max_llm_calls, max_tokens_per_call, and per-tool
call limits — the knobs that stop an output blowup or a runaway loop mid-run.
Caps BoundFlow carries but doesn't enforce¶
custom holds limits only you can enforce — a harness's own vocabulary, or a rule your
handler applies itself. BoundFlow stores it, ships it with the operation and hands it
back; it never validates or acts on it.
await cp.set_agent_runtime_policy(wf.id, "analyst", RuntimePolicy(
max_cost_usd=0.25, # BoundFlow enforces this
custom={"max_total_subagents": 4}, # you enforce this
))
@worker.workflow("research", version=1)
async def entry(ctx):
cap = ctx.policy("analyst").custom.get("max_total_subagents", 0)
...
The point is that the limit is declared in one place and readable from the control plane,
rather than hard-coded wherever the agent is built. Nothing in custom binds unless you
make it bind.
2. Agent lifecycle — adapt the model after runs¶
from boundflow import AgentRule, AgentMetric, Op, SetModel
# After runs, downgrade the model if cost trends high:
await cp.set_agent_lifecycle_policy(wf.id, "analyst", [
AgentRule(metric=AgentMetric.COST_USD, op=Op.GT, threshold=0.20, window=5,
action=SetModel(value="claude-haiku-4-5")),
])
3. Workflow lifecycle — self-heal the whole workflow¶
from boundflow import WorkflowRule, WorkflowMetric, SetVersion
# After repeated failures, roll back to a known-good version automatically:
await cp.set_workflow_lifecycle_policy(wf.id, [
WorkflowRule(metric=WorkflowMetric.NUM_FAILURES, threshold=3,
action=SetVersion(target=1)),
])
Workflow rules can also Pause a workflow or put it on Cooldown instead of
rolling back.
Approval gates¶
A workflow can pause mid-execution for a human to approve or reject a proposed
action before continuing. While parked, the workflow reports
awaiting_approval; the decision is recorded in the governance audit log (see
Observability).
See sdk/python/boundflow/examples/approval_gate.py
for a runnable example.
Finding a workflow's agents¶
Every agent policy call takes an agent name. To discover the names — which is what anyone operating a workflow they didn't write needs:
for agent in await cp.list_agents(workflow_id):
print(agent.agent_name, agent.runtime_policy, agent.lifecycle_policy)
An agent appears once it has run or had a policy set. An empty policy means none is armed, which is different from the agent not existing at all.
Metrics¶
Every run collects per-invocation metrics — cost, tokens, LLM calls, and per-tool counts/failures — computed from real token usage with cache-aware, per-tenant pricing. These are what the lifecycle policies above evaluate against.