Execution policies and approval
You will be able to say which work an agent may do unsupervised, which work only a person may touch, and which work an agent may do but only a person may accept — and to explain to anyone who asks why a particular actor got a particular task.
Audience: whoever is accountable for what agents are allowed to do. Assumes: task lifecycle.
Declare who is who
Nothing below means anything until Commission knows which actors are people. Declare
them in .commission.json:
{
"version": 1,
"project": "myapp",
"actors": [
{ "name": "priya", "kind": "human", "roles": ["owner", "security"] },
{ "name": "blaze", "kind": "human", "roles": ["owner"] },
{ "name": "claude:api", "kind": "agent", "capabilities": ["implement", "test"] }
]
}actors is one of the two blocks reconciled from the file into the project on
every command, so committing it is enough — there is no separate registration
step.
commission actorsblaze human autonomous may: claim, approve, verify
roles: owner
claude:api agent autonomous may: claim
can: implement, test
priya human autonomous may: claim, approve, verify
roles: owner, securityAn actor whose kind is agent may claim and work. It may not approve, and
it may not answer a blocking question. That is not a permission you can grant.
With no actors declared at all, nothing is restricted — Commission does not invent policy for a project that has not asked for any.
Three fields, per task
| field | question it answers | command |
|---|---|---|
autonomy | how far may this be taken unsupervised? | commission edit <id> --autonomy supervised |
mayExecute | which roles or capabilities may claim it at all? | commission edit <id> --may-execute security |
preferredActor | who should do it? | commission edit <id> --preferred-actor claude:api |
autonomy has three values:
| value | agents may claim | closing needs a human |
|---|---|---|
autonomous (default) | yes | no |
supervised | yes | yes |
human_only | no | yes |
Eligibility refuses; preference does not
mayExecute and human_only are permission. An actor that fails one is
refused, by name, with the lever that changes it:
claude:api cannot claim myapp-1: requires one of [security] and claude:api has none of them
missing: mayExecute: security
hint: priya can: COMMISSION_ACTOR=priya commission claim myapp-1 — or drop the requirement: commission edit myapp-1 --may-execute ""claude:api cannot claim myapp-1: autonomy is human_only and claude:api is an agent
missing: autonomy: human_only
hint: blaze or priya can: COMMISSION_ACTOR=blaze commission claim myapp-1 — or change the policy: commission edit myapp-1 --autonomy supervisedpreferredActor is bias, and this is the distinction that surprises people:
a non-preferred actor is fully eligible. Preference sorts a task earlier for the
actor it names; it does not reserve it. If the preferred actor is busy and
someone else claims it first, that is the system working, and the explanation
says so:
preferred actor is claude:api, but claude:web claimed it first — preference biases selection, it does not reserveIneligible tasks are filtered before ordering, so commission ready and
commission next simply do not offer an agent work it could never claim. An empty
frontier for an agent and a non-empty one for a human is a correct answer, not a
bug.
Why did this actor get this task?
Every automatic routing decision can answer it. commission next prints one line;
--json carries the structure:
{
"why": "unclaimed; autonomy autonomous (default); no actors are declared, so nothing is restricted",
"eligible": [],
"ineligible": [],
"wouldChange": [
{
"change": "autonomy is autonomous, so agents may claim it; human_only would reserve it for people",
"command": "commission edit myapp-2 --autonomy human_only"
}
]
}wouldChange is the part worth using. Every lever is stated as the fact it
currently is, plus the command that flips it — an explanation nobody can act on
is trivia.
Approval is not completion
This is the distinction the whole feature exists for, and it is worth stating plainly:
A gate says WHAT must be attached before a task can close. An approval says WHO accepted it. No amount of evidence substitutes for a person’s judgement, and no person’s judgement substitutes for the evidence.
A manager will recognise the failure this prevents. “It’s done — the tests pass and the PR is merged” is a claim about technical completion. “It’s done — I looked at it and I’m happy” is a claim about acceptance. Teams routinely conflate them, and the conflation is invisible until an incident, when it turns out that everything was green and nobody had actually decided anything.
A worked example
A key rotation. The agent can do the work; a human has to accept that it was done safely.
commission edit myapp-1 --autonomy supervisedThe agent claims it, works it, and ticks the criterion:
COMMISSION_ACTOR=claude:api commission start myapp-1
COMMISSION_ACTOR=claude:api commission check myapp-1 0
COMMISSION_ACTOR=claude:api commission done myapp-1cannot complete myapp-1: 1 outstanding approval(s)
missing: approval from a human — autonomy is supervised, so a human must accept the work
hint: a person has to accept this — blaze or priya: COMMISSION_ACTOR=blaze commission approve myapp-1 -m "<why it is acceptable>"Every criterion is ticked and every gate artifact is attached. The task is
technically complete and still cannot close. The agent cannot fix this by trying
harder, and cannot fix it with --force:
COMMISSION_ACTOR=claude:api commission approve myapp-1 -m "lgtm"claude:api cannot approve myapp-1: approval is a human judgement
missing: a human actor
hint: a human runs this — blaze or priya: COMMISSION_ACTOR=blaze commission approve myapp-1 -m "<why>"So the agent hands it over instead of closing it:
COMMISSION_ACTOR=claude:api commission review myapp-1And that is the step that puts it in front of a person. An outstanding
approval on an in_progress task is not in anybody’s inbox — the work is still
being done, and interrupting a human about it would be noise. Moving it to
in_review is the request:
COMMISSION_ACTOR=priya commission inbox1 thing needs your judgment in myapp — 1 approval
scoped to priya (--all shows every actor's) · 0 in flight · 0 ready to pick up
1. [approve] myapp-1 · Rotate the signing key
just now
what an agent finished it and put it up for review; 1 outstanding approval
needs you autonomy is supervised, so a human must accept the work; no agent can satisfy it
if ignored it stays finished but unaccepted, and cannot close
→ commission approve myapp-1 -m "<why it is acceptable>"Four questions and one command. The person answers:
COMMISSION_ACTOR=priya commission approve myapp-1 -m "audit log confirms the old key is revoked"myapp-1 approved by priya
nothing else is waiting on judgement: commission done myapp-1And the agent closes it.
Approval is bound to a version of the work
Approve a task, then change what was approved, and the approval goes stale:
cannot complete myapp-1: 1 outstanding approval(s)
missing: approval from a human (priya approved an earlier version — the task changed since) — autonomy is supervised, so a human must accept the work“Changed” is deliberately narrow: creation, edits, criteria changes, and dependency changes invalidate an approval. Notes and claims do not. Leaving a breadcrumb is not a change to the work, and if it invalidated sign-off people would learn to stop leaving breadcrumbs — or to stop approving. The trigger is an event id, not a timestamp, so an edit landing in the same millisecond as an approval cannot slip through looking unchanged.
Verification, when it is not a judgement call
verification is the second kind of sign-off: a human confirming something is
true rather than deciding whether it is acceptable.
commission verify myapp-1 -m "ran the migration against a production snapshot"Unlike approval, verification can be delegated to a non-human actor that has
been given the verify capability explicitly. Approval cannot — that is the
whole difference between the two.
Note the current limit: commission verify refuses unless there is an outstanding
verification requirement, and the only way to declare one is
execution.reviewPolicy / execution.requireApproval with
"kind": "verification" — which is the config block that is not yet applied
(below). Until that closes, verification is reachable in the data model and not
from a config file.
Escalation: asking instead of guessing
An agent that hits a judgement call has three options, and two of them are bad. It can invent an answer, which is how a task graph fills with assumptions nobody can trace. It can stop, and leave a human wondering why nothing is moving. Or it can ask:
commission ask myapp-1 -m "Reject unsigned webhooks, or log and continue?" --options "reject,log" --blockingasked on myapp-1 (blocking — agents will not work it until answered)
a human answers with: commission answer myapp-1 -m "<the decision, and why>"--blocking is a control, not a label. While the question is open, an agent’s
claim is refused and the refusal quotes the question. Humans are unaffected —
the person who can answer it is exactly the person who might want to work on it.
The question appears in the inbox as a [decide] item, and the answer is the
valuable part:
commission answer myapp-1 -m "reject — a logged-and-continued forgery is worse than a dropped event"The answer is attached to the task, indexed for search, and carried in every future context bundle as a decision. The reasoning behind a decision is normally the first thing lost between sessions; this is where it stops being lost.
What is not enforced yet
The execution block in .commission.json — defaultAutonomy, requireApproval,
reviewPolicy, project-wide mayExecute — validates, appears in
commission config show, and is attributed correctly by commission config explain, but
does not change behaviour. Execution policy is read from the project row,
and only integrations and actors are reconciled into it from the file.
Set policy per task until that closes:
commission edit myapp-1 --autonomy supervised
commission edit myapp-1 --may-execute security
commission edit myapp-1 --preferred-actor claude:apiStated here rather than left to be discovered, because a policy file that validates and does nothing is worse than no policy file. See worked configurations.
The whole sequence
commission actors
commission edit myapp-1 --autonomy supervised
COMMISSION_ACTOR=claude:api commission start myapp-1
COMMISSION_ACTOR=claude:api commission check myapp-1 0
COMMISSION_ACTOR=claude:api commission done myapp-1
COMMISSION_ACTOR=claude:api commission review myapp-1
COMMISSION_ACTOR=priya commission inbox
COMMISSION_ACTOR=priya commission approve myapp-1 -m "audit log confirms the old key is revoked"
COMMISSION_ACTOR=claude:api commission done myapp-1Every command on this page is executed by tests/docs.test.ts against a scratch
database.
Related
- Task lifecycle — the transitions and what refuses them.
- Worked configurations — the
actorsblock in context. positioning.md§6 — actor, approval, execution policy.
Part of Guides.
This page is docs/guides/execution-policies.md in the Commission
repository, rendered in place — the site keeps no copy of it. The repository is private, so there is no edit link to follow.