Enterprise adoption — incremental, and reversible at every stage
You will be able to introduce commission into an organization that already has a system of record, without migrating anything, without asking anyone to stop using Jira, and without a stage you cannot back out of — and answer procurement’s questions about where the data lives, who may write to what, and what the licence permits.
Audience: the person who has to sign off on commission being used at all. Assumes: install, and that Jira (or Linear, or Azure DevOps) is staying.
The premise is narrow and worth stating first: Commission is not replacing your
tracker. Your tracker answers “what is the organization committed to, and who
is accountable” — a reporting and planning question, asked by people who are not
in the repository. Commission answers “what may this agent work on right now, and how
do we know it was verified” — an execution question, asked hundreds of times a
day by processes. Those are different jobs, and the second one is currently not
being done by anything. See
positioning.md §2.
Why “reversible” is the first section and not the last
A rollout nobody can back out of is a rollout nobody starts. Every stage below states its exit before it states its benefit, and the exits are real because of how Commission stores things:
- The data is one SQLite file. Deleting it removes commission from the machine. There is no server holding state, no account to close, no tenant to decommission.
commission dumpis unconditional and complete. Full JSON, every project, at any stage, free forever — see business model §2docs/business-model.md.- Bindings are opt-in and individually removable. No binding means no interop machinery runs at all.
- Commission never writes to an external system unattended. Pushes are explicit. Undoing a stage is therefore a matter of stopping, not of repairing.
Stage 0 — one engineer, one repository, nothing bound
What changes. One person installs commission and runs it against a repository they already work in. No Jira credentials, no bindings, no server. Commission is a private execution surface for that person’s agents.
Who has to agree. Nobody. This is the same permission surface as installing
any developer tool that writes files under $HOME.
What proves it worked. They stop hand-writing task lists for their agents,
and an agent that finishes work cannot close it without attaching the test and
commit references. Ask them for commission standup at the end of the week.
The exit. rm -rf ~/.commission. Nothing outside that directory was touched.
Start here even if you are certain you want stage 3. The whole model — refusals, gates, claims, derived views — is legible in an afternoon on one repository, and every later stage assumes people already understand it. The solo founder guide is the walkthrough; it is written for one person, which is what stage 0 is.
Stage 1 — one team, committed configuration, still nothing bound
What changes. The team commits .commission.json at the repository root and
declares who is who. Commission now knows which humans exist, which agents exist, and
what each may do. Jira is untouched and does not know commission exists.
commission init payments --name "Payments platform"cat > .commission.json <<'CONFIG'
{
"$schema": "https://commission.sh/schema/commission-config.schema.json",
"version": 1,
"project": "payments",
"actors": [
{ "name": "priya", "kind": "human", "roles": ["tech-lead", "approver"] },
{ "name": "sam", "kind": "human", "roles": ["security"] },
{ "name": "claude:impl", "kind": "agent", "capabilities": ["implement", "test"] }
],
"execution": {
"defaultAutonomy": "supervised",
"requireApproval": [
{ "role": "approver", "kind": "approval", "reason": "the team's existing PR review, recorded as data" }
],
"reviewPolicy": {
"released": [{ "role": "security", "kind": "verification", "reason": "change control: a named human confirms the rollback was exercised" }]
}
},
"gates": { "shipped": ["pr", "test"], "released": ["pr", "test", "changelog"] },
"integrations": {
"jira": { "baseUrl": "https://acme.atlassian.net", "projects": ["PAY"] },
"github": { "repos": ["acme/payments"], "autoCloseOnMerge": false }
}
}
CONFIG
commission actors
commission gatesclaude:impl agent autonomous may: claim
can: implement, test
priya human autonomous may: claim, approve, verify
roles: tech-lead, approver
sam human autonomous may: claim, approve, verify
roles: securityNote what the integrations block does at this stage: nothing. It declares
what this project is allowed to bind to later. Writing it now, in the same
review as everything else, is how the allowlist gets read by the people who care
about it rather than added under time pressure in stage 2.
Who has to agree. Whoever reviews changes to the repository. .commission.json is
code and goes through code review.
What proves it worked. commission inbox is short and the team stops asking each
other what state something is in.
The exit. Delete the file, delete the database. The repository is byte- identical to before.
Who owns the configuration
Three layers, and the split is a rule rather than a habit:
| layer | file | owns | committed |
|---|---|---|---|
file | .commission.json | anything that constrains other people: actors, roles, autonomy, approval routing, gates, integration allowlists | yes |
local | .commission.local.json | one machine’s preferences: lease length, workspace strategy, context budget | never |
env | COMMISSION_* | one session: COMMISSION_ACTOR, COMMISSION_DB, credentials | n/a |
Higher layers win, and .commission.local.json cannot declare project — a laptop
cannot quietly re-point a repository at a different graph. When someone asks why
a setting is what it is, the answer is a command rather than an investigation:
commission config explain execution.defaultAutonomyexecution.defaultAutonomy = "supervised"
set by: file (.commission.json)
overrode: defaults = "autonomous"
precedence: defaults < db < file < local < env < flagsTwo practical rules. Put .commission.json under CODEOWNERS alongside the CI
configuration — it is the same class of file, and for the same reason. And put
.commission.local.json in .gitignore on day one, because the first time it is
committed it will be committed with someone’s lease preference and stay there for
a year.
The full key list is Reference → config; worked examples are config examples.
How approvals map to the review process you already have
This is the question that decides whether an organization adopts commission, so it gets its own section rather than a bullet in a stage.
Commission does not replace code review. The pull request still happens, in GitHub, reviewed by people, under whatever rules you already enforce. What commission adds is that the outcome of that review becomes a fact the system can check, rather than a state of mind that someone remembers to act on.
Three mechanisms, mapping onto three things you probably already do:
| you already have | commission mechanism | what it does |
|---|---|---|
| “a PR must be merged, CI must be green” | gate + evidence | done is refused until the required evidence kinds are attached |
| “a senior engineer signs off” | approval (requireApproval by role) | a named human accepts the work; no agent can satisfy it and --force cannot override it |
| “security/QA confirms something is actually true” | verification | the sibling of approval: a human confirming a fact, not expressing a preference |
The distinction between approval and verification is not decoration. “I am happy with this” and “I ran the rollback and it worked” are different claims, they are made by different people, and an audit that cannot tell them apart is not an audit.
In practice an agent finishes the work, attaches its evidence, and is refused:
commission add "Rotate the card-vault signing key" -c "old key revoked" --gate released
COMMISSION_ACTOR=claude:impl commission start payments-1
COMMISSION_ACTOR=claude:impl commission check payments-1 0
COMMISSION_ACTOR=claude:impl commission evidence payments-1 pr=https://github.com/acme/payments/pull/812 test=ci-4471 changelog=CHANGELOG.md
COMMISSION_ACTOR=claude:impl commission done payments-1cannot complete payments-1: 3 outstanding approval(s)
missing: approval from a human — autonomy is supervised, so a human must accept the work
missing: approval from anyone with the 'approver' role — the team's existing PR review, recorded as data
missing: verification from anyone with the 'security' role — change control: a named human confirms the rollback was exercised
hint: a person has to accept this — priya or sam: COMMISSION_ACTOR=priya commission approve payments-1 -m "<why it is acceptable>"Each missing item carries the reason from the configuration, so the requirement
explains itself to the person who meets it. The agent puts the work up for review,
and the humans see it in the one place they look:
COMMISSION_ACTOR=claude:impl commission review payments-1
commission inbox --all1. [approve] payments-1 · Rotate the card-vault signing key
what an agent finished it and put it up for review; 3 outstanding approvals
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 payments-1 -m "<why it is acceptable>"commission review is what puts an item in the inbox, and nothing tells the agent to
run it — the refusal names commission approve, which the agent cannot run, and
commission prime does not connect review to approvals. An agent that meets the
refusal and simply stops therefore leaves work that is finished, unaccepted, and
invisible. Put the rule in your agent runbooks explicitly, and have whoever owns
the queue read commission inbox --all rather than only their own.
COMMISSION_ACTOR=priya commission approve payments-1 -m "Reviewed PR 812; key rotation is behind a feature flag."
COMMISSION_ACTOR=sam commission verify payments-1 -m "Ran the rollback in staging against a production snapshot."
COMMISSION_ACTOR=claude:impl commission done payments-1Three properties worth taking to whoever owns your change process:
- An agent cannot approve. Commission treats any actor with
COMMISSION_ACTORset as a process, and refuses human judgement from it. The conservative direction is deliberate — a wrongly-declared human dissolves the boundary the whole mechanism exists to hold. --forcecannot override an approval. It overrides commission’s own checks — unchecked criteria, missing evidence — with a mandatory reason that is logged. It has never overridden a person’s sign-off.- An approval is bound to the work it accepted. Edit the task afterwards and the approval goes stale, rather than silently carrying forward onto changed work.
Details and the full refusal catalogue: execution policies.
Stage 2 — bind a deliberate subset, pull only
What changes. A named set of Jira issues — one team, one epic, one sprint — is bound to commission tasks. Commission observes them; Jira remains the system of record and nothing is written back. Divergences are recorded and surfaced, not resolved.
commission bind payments-1 --jira PAY-4412
commission sync
commission serve --sync 5mCredentials come from the environment (JIRA_BASE_URL, JIRA_EMAIL,
JIRA_API_TOKEN) and are never written to the database or to .commission.json. The
committed file holds the site URL, the custom-field ids, and the key-prefix
allowlist — the things that are configuration, not secrets.
The allowlist is enforced, not advisory: a project that has not declared a Jira
site, a repository, or a key prefix cannot bind to it, and a mismatch between
JIRA_BASE_URL and the declared site is refused on observe, push, and import. One
project can never write to another project’s systems.
Who has to agree. Whoever owns the Jira project, and whoever issues API tokens. Ask for a token scoped to the smallest possible set of projects; commission never needs more than read access at this stage.
What proves it worked. commission sync reports divergences that are real — a
Jira status claiming “In Progress” for work whose PR merged last Tuesday. That
list is the value of the stage: it is the drift you already had and could not see.
The exit. commission unbind <id> on each binding, or stop running commission sync.
Jira has no record that any of it happened, because nothing was written.
Expect a per-instance configuration pass. “Works with Jira” is never generic — every instance is a snowflake of custom fields, workflows, and status names, and the adapter is configurable precisely because it has to be. See interop for the honest caveat and the field mapping.
Stage 3 — supervised outbound writes
What changes. Commission starts proposing changes to Jira. It still does not make them unattended.
commission sync --plan
commission sync --push--plan emits the outbound plan as JSON for a human to read or an agent to
execute through its own credentials. --push has commission make the transitions
itself. Both are explicit invocations; the background poller
(commission serve --sync 5m) is pull-only and logs plans it will never execute.
Two rules keep this from becoming a synchronization mess:
- Signals beat claims. Observable pull-request reality sets commission’s status. A Jira status field is a claim, and auto-applies only where Commission has not been moved by an agent.
- Plans compare meaning, not names. Same-category workflow states — Triaged, Groomed, Awaiting QA — are the external system’s business and are left alone.
Who has to agree. Whoever is accountable for Jira hygiene, since Commission is now
a writer. Run stage 3 with --plan only for two weeks before enabling --push;
the plans are readable and the disagreement surfaces early.
What proves it worked. The Jira board stops being wrong without anyone updating it by hand.
The exit. Stop passing --push. You are back at stage 2 with no cleanup,
because everything commission wrote is an ordinary Jira transition attributed to the
token’s user.
Stage 4 — the team’s default execution surface
What changes. New work for that team is planned in commission and reflected
outward for the people who need it in Jira. Agents get their work from
commission next --claim; humans get theirs from commission inbox. Jira stays the system
of record for the organization’s commitments.
Who has to agree. The team, and whoever reads the reports. Nobody else’s workflow changed.
What proves it worked. Two numbers: how much of the team’s completed work has evidence attached, and how long the inbox is. If the second is growing, the autonomy settings are wrong — not the tool.
The exit. commission dump > payments.json and commission export > STATUS.md. Every
bound issue’s state is already in Jira, which is the point of having kept it
there. The team goes back to working the Jira board directly, with a JSON archive
of the execution history nobody else was recording anyway.
Do not run stages 2–4 forever and call it done. Two systems that both believe they are authoritative is worse than either alone. Either Commission is the execution layer and Jira is the reporting surface, or you finish the migration and stop syncing. The bridge is a bridge.
Security posture
Written for the person who has to fill in a questionnaire.
Where the data is
One SQLite file, on the machine that runs commission: ~/.commission/commission.db by default,
COMMISSION_DB to move it. WAL journaling, so readers never block the writer. There
is no commission-operated service, no account, no telemetry, no licence check, and no
phone-home — a machine with no network runs the entire product. Data residency is
“wherever you put the file”, and backup is copying it.
Schema migrations ship inside the binary and run on open, each step in its own transaction. A database written by a newer Commission is refused rather than guessed at, and any database holding data is copied before the first migration with the restore command printed. See upgrades.
Who can talk to it
| surface | authentication | notes |
|---|---|---|
| CLI | the OS user | whoever can read the file can read the graph |
MCP (commission mcp) | stdio, local process | no network listener |
HTTP (commission serve) | COMMISSION_TOKEN bearer, mutations only | reads are unauthenticated |
| web view | the same COMMISSION_TOKEN rule, as an HttpOnly SameSite=Strict cookie holding a SHA-256 of the token | a browser form cannot send a bearer header; reads never need it |
The honest limitation is in that table: COMMISSION_TOKEN gates writes, not reads. A
commission serve exposed beyond localhost is a readable board for anyone who can
reach the port. Treat the port as internal, put your own proxy in front of it if
it must be shared, and do not put a secret in a task body on the assumption that
the web view is private.
Human judgement over HTTP is safe for a narrower reason than it looks: approve
resolves the actor from the server process, never from the request, so a caller
cannot name itself into a person.
Secrets
Commission stores none. The workspace layer exists to put environment variables in
front of a task’s work, and its provider contract deliberately has no field for a
value: a provider that materializes secrets writes them to a file and reports the
path and the key names. Values never enter a workspace descriptor, so they
cannot reach --json, the event log, or the persisted record. Hook output is
masked on the same principle.
External credentials — Jira, GitHub — are read from the environment at the moment of use. They are not stored in the database, and the committed configuration holds only URLs, field ids, and allowlists.
The residual risk is the one every tracker has: a task body is stored in
plaintext, and someone will eventually paste a key into one. The database is a
file with file permissions; treat it as you treat a .env.
What is attributable
Every claim, transition, note, evidence attachment, approval, question, and
answer is an event with an actor and a timestamp, appended to a log that is never
rewritten. --force requires a reason and is recorded as an override. Reopening a
task destroys nothing. “Who did this, under what authority, and how do we know it
was verified” is a query, not an investigation:
commission log --last 20
commission boardLicence and supply chain
Commission is proprietary. The repository is private, all rights reserved, and
distribution is by compiled binary under a commercial agreement — see
LICENSE LICENSE and licensing. It is not open source
and not source-available, so a source-code review is a contractual conversation
rather than a link.
What a security team can inspect without one: the binary and its behaviour, the
SQLite file (an open, documented format — commission dump renders it as JSON), the
HTTP surface, and the network traffic, of which there is none unless you
configure an integration.
Being free to use is a separate claim from being open source. Commission makes the
first and not the second: the local product is free and unlimited, with no
account and no cap on agents or concurrency
(business model §2 docs/business-model.md).
What to say to the four people who will ask
- The engineering manager: nothing about your Jira process changes at any stage. What changes is that “done” becomes checkable and agent work becomes attributable.
- The security reviewer: local file, no network by default, secrets never stored, every action attributed, append-only log. The read side of the HTTP surface is unauthenticated; keep the port internal.
- Procurement: proprietary licence, binary distribution, no hosted component today, complete data export at any moment.
- The engineer who thinks this is more process: it is less. There is no status to update, no board to groom, and no estimate to give. The only thing Commission makes you do that you were not doing is attach the evidence you already produced.
The whole sequence
commission init payments --name "Payments platform"
commission actors
commission gates
commission config explain execution.defaultAutonomy
commission add "Rotate the card-vault signing key" -c "old key revoked" --gate released
COMMISSION_ACTOR=claude:impl commission start payments-1
COMMISSION_ACTOR=claude:impl commission check payments-1 0
COMMISSION_ACTOR=claude:impl commission evidence payments-1 pr=https://github.com/acme/payments/pull/812 test=ci-4471 changelog=CHANGELOG.md
COMMISSION_ACTOR=claude:impl commission done payments-1
COMMISSION_ACTOR=claude:impl commission review payments-1
commission inbox --all
COMMISSION_ACTOR=priya commission approve payments-1 -m "Reviewed PR 812; key rotation is behind a feature flag."
COMMISSION_ACTOR=sam commission verify payments-1 -m "Ran the rollback in staging against a production snapshot."
COMMISSION_ACTOR=claude:impl commission done payments-1
commission log --last 20The commands on this page that do not need a live Jira site, a GitHub token, or a
long-running server are executed by tests/docs.test.ts against a scratch
database. The ones that do are named in that test with the reason they are
skipped, and are checked against the live command tree so a renamed flag still
fails the suite.
Related
- Interop — bindings, divergences, and the per-instance Jira caveat.
- Migration — if you are leaving the tracker rather than living beside it.
- Execution policies — autonomy, eligibility, and approval as data.
- Upgrades — migrating a database that holds real work.
- Architecture — why the security posture is a consequence of the storage decisions rather than a feature.
Part of Guides.
This page is docs/guides/enterprise.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.