Skip to content
commission

Technical reference · CLI · MCP · HTTP API

One core, three machine interfaces

Commission is a Bun binary and a SQLite file. Everything on this page is the same core function reached over a different transport — commission on a command line, typed tools over MCP stdio, and /v1 over HTTP. There is no second implementation, so there is no second set of rules.

Every command takes --json. Every rejected action states what is missing and the exact command that supplies it, and exits 2422 over HTTP, isError over MCP, the same body in all three. That contract is the whole reason an agent can correct itself without a human reading the output.

shell exit 2 — a refusal, not an error
commission start checkout-1checkout-1: open → in_progressbreadcrumbs: commission note checkout-1 -m "..." commission done checkout-1cannot complete checkout-1: 2 unchecked criteria; gate 'tier1' missing evidence [pr, test]  missing: criterion 0: signature verified against the endpoint secret  missing: criterion 1: replayed events are rejected  missing: evidence.pr  missing: evidence.testhint: tick with 'commission check checkout-1 <n>'; attach with 'commission evidence checkout-1 pr=<ref> test=<ref>'; or --force to override (logged)

Captured from a real run. Every terminal block on this page is: site/scripts/capture-terminal.ts drives a throwaway project through the whole loop and writes what the binary actually printed. Nothing here is typed by hand.


Execution model

Plan, dispatch, record, prove, close

Five properties, and one loop that any actor — a coding agent, a script, or a human at a terminal — drives with the same commands.

  1. 1

    A plan becomes a graph

    commission apply -f plan.json creates every task and every dependency in one transaction, so there is never a moment where half a plan exists.

  2. 2

    The frontier is derived

    A task is ready when its dependencies are satisfied and its children are closed. It is evaluated at query time, so it cannot be stale, and blocked is never a status anybody sets.

  3. 3

    Three agents claim three tasks, at once

    commission next --claim selects by the project’s policy and takes the lease in the same transaction. Run it three times in parallel and you get three different tasks, not three copies of one.

  4. 4

    The gate reads criteria and evidence

    Completion is a predicate, not a status field. A task’s gate names the evidence kinds it requires, and the criteria have to be ticked.

  5. 5

    Granted, refused, or routed to a person

    Where the gate is satisfied, done is granted. Where it is not, done exits 2 and the refusal names the missing evidence and the exact command that attaches it. Where the execution policy requires approval, it goes to a human — because that is the one thing a gate cannot decide.

Three lanes, one command each, and three different endings — because done is a predicate the gate evaluates, not a status the worker sets.

in_progress
claimed, lease held
satisfied
criterion ticked, evidence attached
missing
the gate is still waiting on it
exit 2
refused, with the fix named
One truth, derived views
Status lives in exactly one row. Boards, the ready frontier, standup narratives, and blocked state are queries over it. There is no second place to update, so there is nothing to keep in sync.
Work is dispatched, not assigned
commission next --claim picks the highest-value workable task and claims it in a single transaction. Claims are leases, so a crashed session never wedges a task, and N agents running the same command each get a different one.
Context arrives in one read
A claim returns a context bundle: the task body, acceptance criteria, dependency state, parent and children, linked documents, attached evidence, prior session notes, recent events, and the exact commands that finish the task from its current state.
Completion is verified, not asserted
A task's gate names the evidence kinds it requires — a PR, a test run, a benchmark, a rollback plan. done is refused until they exist and until every acceptance criterion is ticked, and the refusal says what is missing and the exact command that supplies it.
Parallel by default
The ready frontier is everything workable right now: dependencies satisfied, children closed. It is claimable by many actors at once, and tasks that declare the files they touch are batched so overlapping work is visible before it collides.
  1. plan commission apply -f plan.json
  2. dispatch commission next --claim
  3. record commission note <id> -m "…"
  4. prove commission evidence <id> pr=214 test=<url>
  5. close commission done <id>

The whole loop. Every command takes --json, and exits 0 on success, 2 on a refusal.

shell one transaction
commission apply -f plan.jsonapplied to 'checkout': 4 created, 0 updated  + checkout-1  (verify)  + checkout-2  (idempotent)  + checkout-3  (retry)  + checkout-4  (summary)

A graph is created in one transaction, with dependencies, so the ready frontier is accurate the moment it exists.

A cold session becomes a productive one

One command returns everything needed to work the task, and claims it on the way past — so the agent that read the context is the one holding the lease. The bundle ends with the exact commands that finish the task from the state it is actually in.

The prepared place the work happens is part of the same object: commission workspace prepare <id> materialises a worktree, environment variables from a provider chain, deterministically allocated ports, and setup hooks, and prints the descriptor. commission workspace release <id> tears it down through the same provider chain that built it.

shell a cold session, resumed
commission next --claimclaimed checkout-1routing: agent:one is holding it; autonomy autonomous (default); no actors are declared, so nothing is restricted checkout-1 · Verify Stripe webhook signatures  [open · p1 · gate:tier1]project: Checkout criteria:  [ ] 0. signature verified against the endpoint secret  [ ] 1. replayed events are rejectedblocks: checkout-2 gate 'tier1' requires: pr, test next:  commission start checkout-1  commission note checkout-1 -m "<progress>"
The record One SQLite file, append-only
Tasks, dependencies, acceptance criteria, evidence, events, notes and leases live in exactly one place. Boards, the ready frontier, the standup and blocked state are queries over it, so there is no second copy to keep in sync.
The context bundle Composed per task, returned by one claim
commission next --claim returns the task body, its acceptance criteria, dependency state, parent and children, linked documents, attached evidence, prior session notes, recent events, and the exact commands that finish the task from the state it is actually in.
The workspace Materialised per task, released the same way
commission workspace prepare <id> materialises a git worktree, environment variables resolved through a provider chain, deterministically allocated ports, and setup hooks, and prints the descriptor. commission workspace release <id> tears it down through the same chain that built it.
The session An agent, a script, or a person at a terminal
The session holds none of the above. What it learns — a note, a piece of evidence, a transition — is appended to the record, never written back over it, which is why the next bundle is better than this one and why a crashed session loses nothing but its lease.
Both are assembled from the one record, and nothing the session learns stays in the session — which is what makes the next cold start cheaper than this one.

Dispatch

The ready frontier, and claims that are leases

Parallelism is not a feature bolted onto a tracker here; it is the consequence of two decisions — readiness is computed, and claiming is atomic.

Readiness is derived, never set
A task is ready when every dependency is satisfied and every child is closed. It is evaluated at query time, so there is no blocked status to set and therefore none to forget to unset. The frontier cannot be stale, because it is not stored.
Selection is declared policy
Ordering comes from the project's configured policy — severity, sprint, due date, priority, rank, age — not from whoever shouted loudest. Two agents running the same command get the same ordering and different tasks.
Claiming is one transaction
A claim is a conditional UPDATE inside a SQLite transaction: it wins if and only if the task is unclaimed or the previous lease has expired. Selection and claim happen together, so the agent that read the context is the one holding it.
Claims are leases, not assignments
The default lease is 8h (agentDefaults.lease, or COMMISSION_LEASE). Re-claiming as the holder extends it, commission heartbeat <id> extends it without changing state, and an expired lease is reclaimable by anyone — so a crashed session releases work by not existing.
Overlap is visible before it collides
A task declares the paths it touches. commission ready groups the frontier into batches whose touch sets do not intersect, so fan-out to N agents is a read rather than a guess.
commission ready
the frontier, batched for safe fan-out
commission next --claim
select by policy and take the lease atomically
commission claim <id>
claim one specific task
commission heartbeat <id>
extend your lease without changing state
commission release <id>
release a claim — yours, or a stale one
shell derived at query time
commission readyready frontier (dispatch independent tasks in parallel):  checkout-1 · Verify Stripe webhook signatures  [open · p1 · gate:tier1]  ⚠ touches overlap: checkout-3  checkout-3 · Retry queue for failed captures  [open · p2 · gate:tier1]  ⚠ touches overlap: checkout-1  checkout-4 · Checkout summary page  [open · p2 · gate:tier1]suggested parallel batches (no touches overlap within a batch — dispatch batch 1 together, then 2):  1) checkout-1, checkout-4  2) checkout-3

Readiness and the touches overlaps are computed at query time. Nothing here was set by anyone.

shell two agents, one frontier
commission claim checkout-1checkout-1 is already claimed by agent:onehint: pick another with 'commission ready', or 'commission release checkout-1' if that claim is stale (lease expires 2026-07-27T21:40:25.657Z) commission next --claimclaimed checkout-3skipped: checkout-1 (claimed by agent:one)routing: agent:two is holding it; autonomy autonomous (default); no actors are declared, so nothing is restricted checkout-3 · Retry queue for failed captures  [open · p2 · gate:tier1]project: Checkout criteria:  [ ] 0. a failed capture retries with backoff gate 'tier1' requires: pr, test next:  commission start checkout-3  commission note checkout-3 -m "<progress>"

The second actor is not blocked by the first. It is told the task is taken, given the command that would release a stale claim, and moved on to the next workable thing.

This is the property prompting cannot add. A conditional UPDATE inside a transaction with an expiry is either in the data model or it is not.


Refusals

A refusal is the next prompt, and exit 2 is the contract

A rejected action states what is missing and the exact command that fixes it. That is the whole interface an agent needs in order to correct itself without a human reading the output.

Exit codes

0 ok
the command did what it said
1 error
something broke — a bug, a bad file, an unreachable database
2 refusal
the action is not permitted yet; the message says what is missing and how to fix it
3 not found
no such task, project, or binding

2 is deliberately distinct from 1, because "you cannot do that yet" and "something broke" require different responses from the caller.

The refusal object

error
refusal | not_found | error — the class of the failure
message
what was attempted and why it was declined, in one sentence
missing
the itemised list — criteria, evidence kinds, open children
hint
the exact command that supplies what is missing

done is a predicate

Completion is not a status write. It is evaluated at the moment of closing, and any one of these refuses the transition and leaves the task exactly where it was.

Open children
A parent cannot close over unfinished work. The refusal names each child.
Unchecked acceptance criteria
Criteria are ticked one at a time with commission check <id> <n>, and the refusal lists the ones still open by number.
Missing gate evidence
The task's gate profile names evidence kinds — pr, test, commit, benchmark, docs, rollback. Each missing kind is listed, and the hint is the commission evidence command that attaches it.
Outstanding human approval
Where a task is supervised, a recorded human approval is required and --force cannot override it.
shell completion is a predicate
commission note checkout-1 -m "Constant-time compare — the naive === leaks timing. Rejected the stripe-node helper: it pulls the whole SDK for one function."noted on checkout-1 commission check checkout-1 0checkout-1 criterion 0 ticked; 1 remaining commission check checkout-1 1checkout-1 criterion 1 ticked; 0 remaining commission done checkout-1cannot complete checkout-1: gate 'tier1' missing evidence [pr, test]  missing: evidence.pr  missing: evidence.testhint: attach with 'commission evidence checkout-1 pr=<ref> test=<ref>'; or --force to override (logged) commission evidence checkout-1 pr=214 test=https://ci.example.com/runs/8821attached pr, test to checkout-1 commission done checkout-1checkout-1: in_progress → donenext task: commission next --claim

One task, closed properly: a breadcrumb, two criteria, a refusal for the missing evidence, then done.

Overrides are expensive and legible

--force exists and refuses without -m "<why>". The reason is written to the append-only event log and surfaces in the standup. Cancellation is held to the same rule: a reason, or the task that supersedes it.

A system that cannot be overridden gets worked around invisibly — in a side channel, in a spreadsheet, in a status document nobody reconciles. One that makes overrides expensive and legible gets overridden rarely and traceably.

Refusals are prompts, not errors — the argument at length →

The same refusal, every transport

surface signal body
CLI exit 2 message, then "missing:" lines, then "hint:" on stderr
CLI --json exit 2 the JSON object above on stderr, and nothing else
HTTP 422 the same JSON object as the response body
MCP isError the same text, so the model reads the fix and retries

There is one core, so there is one refusal. Nothing about the wording is transport-specific.


CLI

The surface agents actually operate

Every command takes --json, every refusal exits 2 with a machine-readable body, and nothing requires a person to be looking at a screen.

Work the loop

commission next --claim
pick the next ready task and claim it
commission start <id>
begin work (auto-claims if unclaimed)
commission note <id> -m "…"
leave a session breadcrumb
commission check <id> <n>
tick acceptance criterion n
commission evidence <id> pr=214
attach completion evidence
commission done <id>
complete — refused until the gate is satisfied

See position

commission ready
the parallel frontier, batched for safe fan-out
commission board
in-flight, ready, blocked, recent
commission standup
a done / doing / blocked narrative
commission log --last 20
the append-only event feed
commission search "…"
full-text over titles, bodies, and notes
commission show <id>
the full context bundle for one task

Plan and operate

commission init <slug>
create a project and write .commission.json
commission apply -f plan.json
create a whole task graph atomically
commission define <slug>
the brief for turning an idea into a graph
commission prime
the agent operating manual, for your CLAUDE.md
commission doctor
self-diagnostics with exact remediation commands
commission dump > backup.json
full JSON export; restore with commission restore

Two output modes, one shape

--json switches both streams: stdout carries the result and stderr carries refusals as JSON and nothing else, so a pipeline sees identical bytes whatever the repository's configuration looks like.

--json
machine-readable output on both streams, including refusals
--project <slug>
override the project .commission.json resolved to
-m "<why>"
the reason a cancellation or an override is justified
--force
override a gate deliberately; refuses without -m

Exit codes and the refusal body are the same contract on every surface, so they are documented once — refusals and exit codes. The full command list, including bindings, imports, and migrations, is commission --help; this page is the subset an evaluating engineer needs, not a manual.

shell did we already decide this?
commission search "timing"checkout-1 [done] Verify Stripe webhook signatures    note: Constant-time compare — the naive === leaks [timing]. Rejected the stripe-node helper…

Recall is full-text search plus graph traversal over what actors actually wrote down. There is no embedding store and no inference.


MCP

The same core, as typed tools

For contexts where a shell is not available — claude.ai, Claude Desktop, any MCP client — the same core is exposed over stdio.

register once
claude mcp add commission -- commission mcp

The MCP surface is deliberately narrower than the CLI: it covers the working loop — claim, record, prove, close — and planning, not project administration or import.

Refusals come back in Commission's standard shape — message, missing, and the exact fixing command — marked isError, so a model can correct itself without a human in the loop.

commission_next
claim the next ready task and return its context bundle
commission_show
the full context bundle for one task
commission_board
in progress, ready, blocked, recently done, stale claims
commission_ready
the parallel frontier, with suggested no-overlap batches
commission_standup
the done / doing / blocked narrative
commission_transition
start, review, done, cancel, reopen
commission_claim
claim a task under a lease
commission_release
release a claim — yours or a stale one
commission_note
leave a session breadcrumb
commission_evidence
attach completion evidence
commission_check
tick or untick an acceptance criterion
commission_apply
create a whole task graph, with dry_run
commission_search
full-text: did we already decide this?
commission_log
the append-only event feed, cursor-based
commission_prime
the operating manual for agents
commission_define
the brief for turning an idea into a graph
commission_sync
observe bound externals and return the outbound plan
commission_bind
bind a task to a Jira issue or a pull request
shell isError, with the fix
# registered once with: claude mcp add commission -- commission mcp echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude","version":"1"}}}' | commission mcp 2>/dev/null | jq -c .result.serverInfo{"name":"commission","version":"0.1.0"} echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"commission_transition","arguments":{"id":"checkout-4","to":"done"}}}' | commission mcp 2>/dev/null | jq -r '.result.content[0].text'{ "error": "refusal", "message": "cannot complete checkout-4: 1 unchecked criteria; gate 'tier1' missing evidence [pr, test]", "missing": [  "criterion 0: displayed totals match the order",  "evidence.pr",  "evidence.test" ], "hint": "tick with 'commission check checkout-4 <n>'; attach with 'commission evidence checkout-4 pr=<ref> test=<ref>'; or --force to override (logged)"}

The same refusal as the CLI's, over a different transport. Nothing about it is transport-specific, because there is only one core.


HTTP API

One core, three transports

commission serve exposes the same functions the CLI calls over /v1, and renders the human web view from the same rows.

method route returns
GET /v1/version binary, schema, and config versions
GET /v1/projects every project in this database
GET /v1/projects/:slug/board the board, derived
GET /v1/projects/:slug/ready the ready frontier, with claim state
GET /v1/next preview the next task — never claims
POST /v1/next select and claim atomically
GET /v1/tasks/:id the full context bundle
POST /v1/tasks create one task
POST /v1/tasks/:id/transition move a task — refusable
POST /v1/tasks/:id/claim take a lease
POST /v1/tasks/:id/release release a lease
POST /v1/tasks/:id/notes append a note
POST /v1/tasks/:id/evidence attach evidence
POST /v1/apply create a whole graph in one transaction
GET /v1/search full-text search
GET /v1/events the event log, from a cursor
start the server agents on /v1, humans on /
commission serve
shell same core, same refusal
# commission serve --port 4401 is running in another shell curl -s localhost:4401/v1/version{"commission":"0.1.0","schema":10,"config":1} curl -s -i -X POST localhost:4401/v1/tasks/checkout-4/transition -d '{"to":"done"}'HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
Date: Mon, 27 Jul 2026 13:40:26 GMT
Content-Length: 354
 {"error":"refusal","message":"cannot complete checkout-4: 1 unchecked criteria; gate 'tier1' missing evidence [pr, test]","missing":["criterion 0: displayed totals match the order","evidence.pr","evidence.test"],"hint":"tick with 'commission check checkout-4 <n>'; attach with 'commission evidence checkout-4 pr=<ref> test=<ref>'; or --force to override (logged)"}

A refusal is 422 with the same body the CLI prints: the message, the missing list, and the hint carrying the exact fixing command.

Set COMMISSION_TOKEN and every mutation requires a bearer token, while reads stay open — which is what lets the web view be exposed to people while writes stay agent-only. The web view is read-only by design: humans observe and judge, they do not drive the loop by clicking.


Derived views

Every view is a query, and the log is append-only

There is one row that says what a task's status is. Boards, the frontier, standup narratives, and blocked state are all computed from it, so there is no second place to update and nothing to keep in sync.

commission board
Position: in flight with who holds each lease, the ready frontier, what is blocked and by what, and what closed recently.
commission standup
A done / doing / blocked narrative for a human on a call. Overrides and forced transitions surface here rather than staying in the log.
commission log --last 20
The append-only event feed, read forward from a cursor. Every entry carries the actor that caused it.
commission search "…"
Full-text over titles, bodies, and notes — the "did we already decide this?" query.
commission export / commission dump
A git-committable markdown snapshot, and a full JSON export restorable with commission restore. The database stays the truth; both are derived.
shell a query, not a meeting
commission boardCheckout (checkout) — 1/4 done, 3 open ready:  checkout-2 · Idempotent order creation  [open · p1 · gate:tier1]  checkout-3 · Retry queue for failed captures  [open · p2 · gate:tier1 · @agent:two]  checkout-4 · Checkout summary page  [open · p2 · gate:tier1] recently done:  checkout-1 · Verify Stripe webhook signatures  (2026-07-27 13:40)
shell derived from the same rows
commission standupstandup — Checkout (checkout) what to say (covers since Friday 7:00 AM):  Since Friday 7:00 AM we shipped Verify Stripe webhook signatures.  Next up: Idempotent order creation. done (since Friday 7:00 AM):  ✓ checkout-1 Verify Stripe webhook signatures  (just now)

A standup with nobody to attend it — derived from the same rows the agents were writing to all night.

shell append-only, attributed
commission log --last 102026-07-27 13:40 agent:one checkout-4 created: Checkout summary page2026-07-27 13:40 agent:one - project: {"action":"apply","created":4,"updated":0}2026-07-27 13:40 agent:one checkout-1 claim: {"via":"next"}2026-07-27 13:40 agent:two checkout-3 claim: {"via":"next"}2026-07-27 13:40 agent:one checkout-1 transition: open→in_progress2026-07-27 13:40 agent:one checkout-1 note: Constant-time compare — the naive === leaks timing. Rejected the stripe-node helper: it pulls the whole SDK for one function.2026-07-27 13:40 agent:one checkout-1 criteria: {"position":0,"text":"signature verified against the endpoint secret","done":true}2026-07-27 13:40 agent:one checkout-1 criteria: {"position":1,"text":"replayed events are rejected","done":true}2026-07-27 13:40 agent:one checkout-1 evidence: pr=214, test=https://ci.example.com/runs/88212026-07-27 13:40 agent:one checkout-1 transition: in_progress→donenext cursor: 15  (commission log --after 15)

History is appended, never rewritten. A status cannot be quietly corrected after the fact, because there is no place to correct it.

Recall, across sessions

The durable parts of a session are data attached to the task, not to the transcript: notes, criteria state, evidence with references, the dependency graph, and the events in between. A different actor arriving days later reads the decision the first one made and the evidence that closed it.

The honest limit: this is not a semantic memory system. There is no embedding store and no inference. Recall is full-text search plus graph traversal over what actors actually wrote down — its advantage is that the workflow makes writing things down the path of least resistance.

shell a different agent, days later
commission show checkout-1checkout-1 · Verify Stripe webhook signatures  [done · p1 · gate:tier1]project: Checkout criteria:  [x] 0. signature verified against the endpoint secret  [x] 1. replayed events are rejectedblocks: checkout-2 gate 'tier1' requires: pr✓, test✓evidence: pr=214, test=https://ci.example.com/runs/8821 notes:  2026-07-27 13:40 agent:one: Constant-time compare — the naive === leaks timing. Rejected the stripe-node helper: it pulls the whole SDK for one function. next:  commission reopen checkout-1

Internals

One file, five statuses, and configuration that refuses to guess

Commission is Bun + SQLite in WAL mode + Drizzle + Hono. The core is a library and the only writer; the CLI, the HTTP API, the MCP server, and the adapters are thin consumers of it.

Five statuses, moved only by a transition

Nothing can write a status except a transition, so every state change is validated, attributed, and logged. There is no settable blocked status: blocked is derived from dependencies, and a status that can be set can be wrong.

from may move to
open in_progress · done · cancelled
in_progress in_review · done · cancelled
in_review in_progress · done · cancelled
done open (reopen only — done is terminal)
cancelled open

Cancelling requires a reason or the task that supersedes it, because silent cancellation destroys the context the next session needs.

The storage engine is the boring part

One SQLite file in WAL mode. No server, no account, no daemon, and nothing to provision — the database is created on first use at ~/.commission/commission.db, or wherever COMMISSION_DB points.

The interesting parts are the invariants: readiness is derived so it cannot be stale, claiming is atomic so parallelism is safe, done is refusable so completion means something, and events are append-only so history cannot be rewritten.

Configuration is strict and explainable

.commission.json is versioned and validated against a strict schema: unknown keys are refused rather than ignored, because a typo that silently does nothing is the failure mode the file exists to prevent. A solo project's config is two keys — a version and a project slug.

Precedence — last wins

  1. defaults
  2. db
  3. .commission.json
  4. .commission.local.json
  5. env
  6. flags

commission config explain <key> prints the value, the layer that set it, and every layer it overrode. commission config validate exits 2 on an invalid file, which is what makes it usable in CI.

shell one command, one file
commission init checkout --name "Checkout" --gates '{"tier1":["pr","test"]}'created project checkout (Checkout)wrote .commission.json — bare 'commission next' now targets 'checkout' from this repoadd tasks: commission add "..." or commission apply -f plan.json

First run. Two keys in .commission.json, and every bare command in that repository targets the right project.

Environment

COMMISSION_ACTOR
who is acting — recorded on every event, claim, and note
COMMISSION_DB
the database file; defaults to ~/.commission/commission.db
COMMISSION_PROJECT
the project slug, overriding .commission.json
COMMISSION_CONFIG
an explicit config file, skipping discovery
COMMISSION_LEASE
claim lease duration, e.g. 8h
COMMISSION_TOKEN
bearer token required for mutations over HTTP

Commission contains no model, performs no inference, and writes no code. It is not an agent runtime and not a CI system: it never runs a test, it records that a test run happened and where to find it.


Install

From source today; four paths when RC1 is cut

Commission is a binary and a SQLite file — one file, no server, no account. There is nothing to sign up for.

No release published yet

No tag has ever been cut: the repository has carried the placeholder version 0.1.0 since its first commit. The four distribution paths below are built and documented but not yet publishing artifacts, so none of those commands resolves today. planned · relay-52 Building from source is the honest answer, and it is the same code.

Works today

Requires Bun, and access to the repository, which is private while the open-versus-closed-source decision is open. planned · relay-57
bun link puts commission on your PATH; the database is created on first use at ~/.commission/commission.db, or wherever COMMISSION_DB points.

1 · clone
git clone https://github.com/Row-Labs/commission.git
2 · dependencies
bun install
3 · install the commission command
bun link

Then, in your project

create a project writes .commission.json
commission init myapp --name "My App" --gates '{"tier1":["pr","test"]}'
teach your agent paste into CLAUDE.md
commission prime

When RC1 is cut planned · relay-52

Four paths, all first-class, each producing a working commission --version and passing the same smoke test on macOS, Linux, and Windows. Copy them now if you like; they will not resolve until the tag exists.

install script detects platform, verifies checksum
curl -fsSL commission.sh/install | sh
homebrew formula bumped by the release workflow
brew install row-labs/tap/commission
npm npm, pnpm, and bun
npm i -g @rowlabs/commission
github releases tagged binaries, notes, and SHA256SUMS
gh release download --repo Row-Labs/commission --pattern "commission-*"