Skip to content
commission

Solo founder — install to "here are this quarter's deliverables"

You will be able to take a repository you already have, turn a quarter’s worth of intent into a task graph, run several agents against it at once, answer only the questions that are genuinely yours, and end the week with a derived list of what actually shipped — without keeping any of it in your head.

Audience: one person, several agents, no process. Assumes: install and your first task. Skip: approvals-at-scale, roles, org policy — see enterprise adoption if that is your world.

Every command below was run, in this order, against a real git repository — a small double-entry ledger service — and the transcripts on this page are that run’s output, copied verbatim. Where something went wrong, it is on the page, because the wrong turns are the part you will actually hit.

The shape of the week

you docommission does
say what the product isstores it once, and puts it in every agent’s context
write the graphderives what is ready, what is blocked, what can run together
fan agents outhands each one a distinct task, atomically
answer questions, approve what you asked to seerefuses completion until the evidence exists
read one commandderives the deliverables from what happened

Nothing on the left is administration. There is no board to groom and no status to update, because status is not a thing you write.

1. Bind the repository to a project

Run this at the root of the repository the work happens in. commission init writes .commission.json, and from then on every bare commission command in this tree targets this project — you never pass --project again.

commission init ledger --name "Ledger"
commission vision --set "A double-entry ledger service that never loses a cent. Money is integer minor units; the journal is append-only; every balance is derived from it and none is stored."
commission gates --set shipped=test,commit

The vision is not decoration. It is stored on the project and injected into every context bundle, which means a fresh agent session six weeks from now argues from your principles instead of inventing its own. Write the three or four sentences that settle design arguments in advance.

The gate is the evidence class your work belongs to. shipped=test,commit means a task carrying that gate cannot close until a test reference and a commit reference are attached. Define it now: commission init seeds no gate profile, so the first apply that names one is otherwise refused with “project has no gate profiles”.

2. Write the quarter as a graph, not a list

An agent writes this better than you do. commission define ledger prints a brief — interrogate the idea, write the vision, design the graph, dry-run it — that you hand to a coding agent along with commission apply --schema. What comes back is one JSON document:

cat > plan.json <<'PLAN'
{ "project": "ledger", "tasks": [
  { "key": "money", "title": "Exact money arithmetic in minor units", "gate": "shipped",
    "touches": ["src/money.ts"],
    "criteria": ["cents(1.005) returns 101", "no float arithmetic remains in src/money.ts"] },
  { "key": "journal", "title": "Append-only journal with balanced entries", "gate": "shipped",
    "deps": ["money"], "touches": ["src/journal.ts"],
    "criteria": ["an unbalanced entry is rejected", "posted entries are frozen"] },
  { "key": "api", "title": "HTTP API for posting and reading entries", "gate": "shipped",
    "deps": ["journal"], "touches": ["src/api.ts"],
    "criteria": ["POST /entries returns 422 when debits != credits"] },
  { "key": "reconcile", "title": "Reconciliation report by account", "gate": "shipped",
    "deps": ["journal"], "touches": ["src/reconcile.ts"],
    "criteria": ["balances are derived from the journal, never stored"] },
  { "key": "docs", "title": "Bookkeeper-facing README", "deps": ["api", "reconcile"],
    "touches": ["README.md"],
    "criteria": ["a bookkeeper can post and reconcile one entry from the README alone"] } ] }
PLAN
commission apply --dry-run -f plan.json
commission apply -f plan.json

--dry-run writes nothing and prints exactly what landing it would do. The real apply is one transaction: a plan naming an undefined gate or a missing dependency creates nothing, so there is never a half-landed graph to clean up.

applied to 'ledger': 5 created, 0 updated
  + ledger-1  (money)
  + ledger-2  (journal)
  + ledger-3  (api)
  + ledger-4  (reconcile)
  + ledger-5  (docs)

The full document format is Reference → apply; the design rules are planning.

3. Check the frontier before you dispatch — this is where it went wrong

commission ready
ready frontier (dispatch independent tasks in parallel):
  ledger-1 · Exact money arithmetic in minor units  [open · p2 · gate:shipped]

One task. Five tasks planned, five agents available, and a frontier of one. That plan is a pipeline: everything hangs off money, and docs waits on both api and reconcile. Fanning agents out against it would have produced four idle sessions and one working one.

Two moves fix it, and both are worth doing before any agent starts.

Cut the dependencies that were preference, not order. The README was made to wait on the API and the reconciler because it felt later. It is not: a bookkeeper-facing README can be written against the design, and the criterion — “a bookkeeper can post and reconcile one entry from the README alone” — is what catches it if the design changes underneath.

commission undep ledger-5 --on ledger-3
commission undep ledger-5 --on ledger-4

Use undep rather than editing plan.json and re-applying. commission apply is additive for dependencies: re-applying a document with a shorter deps list updates every other field in place but leaves the dropped edge exactly where it was, and the frontier does not widen. Discovered the hard way on this walk; the upsert-by-key behaviour documented in planning is about fields, not edges.

Add the work that was genuinely independent all along. The chart of accounts touches nothing else in the graph, so it never needed to be in the pipeline:

cat > accounts.json <<'PLAN'
{ "project": "ledger", "tasks": [
  { "key": "accounts", "title": "Chart of accounts loader", "gate": "shipped",
    "touches": ["src/accounts.ts"],
    "criteria": ["an unknown account code is rejected at load time"] } ] }
PLAN
commission apply -f accounts.json
commission ready
ready frontier (dispatch independent tasks in parallel):
  ledger-1 · Exact money arithmetic in minor units  [open · p2 · gate:shipped]
  ledger-5 · Bookkeeper-facing README  [open · p2]
  ledger-6 · Chart of accounts loader  [open · p2 · gate:shipped]
safe to fan out together (no touches overlap): ledger-1, ledger-5, ledger-6

Three wide, and commission says which three are safe together because their touches globs do not overlap. That last line is advisory, not a lock — see parallel work for exactly how much it promises.

4. Declare yourself a human before you need to be one

This is the step that is easy to skip and expensive to skip.

Commission decides whether an actor is a person conservatively: anything with COMMISSION_ACTOR set is treated as an agent, because a process that could name itself into a human could approve its own work.

That has a consequence worth catching before it catches you. Run commission init from an ordinary shell and it declares you on the way past — “declared you (blaze) as the human who approves work here” — writing an actors block with your OS username. Run it with COMMISSION_ACTOR exported, which is what install tells you to do and what every agent session does, and commission cannot tell a person from a process, so it declares nobody. The config it writes is two keys, and the first time an agent escalates a decision to you, you get this:

blaze cannot answer ledger-6: answering is a human judgement
  missing: a human actor
hint: no humans are declared on this project — add one under "actors" in .commission.json, then: COMMISSION_ACTOR=<their-name> commission answer ledger-6 -m "<the decision, and why>"

The refusal is correct and it names the fix, but there is no reason to meet it mid-flow. Declare the whole cast up front — you, and the agent names you intend to run. This replaces whatever commission init wrote, and it is the file you would have ended up with anyway once a second agent existed:

cat > .commission.json <<'CONFIG'
{
  "$schema": "https://commission.sh/schema/commission-config.schema.json",
  "version": 1,
  "project": "ledger",
  "actors": [
    { "name": "blaze", "kind": "human", "roles": ["owner"], "default": true },
    { "name": "agent:a", "kind": "agent", "capabilities": ["implement", "test"] },
    { "name": "agent:b", "kind": "agent", "capabilities": ["implement", "test"] }
  ]
}
CONFIG
commission actors
agent:a              agent  autonomous  may: claim
  can: implement, test
agent:b              agent  autonomous  may: claim
  can: implement, test
blaze                human  autonomous  may: claim, approve, verify
  roles: owner

may: claim, approve, verify on your line is the thing you came for. This file is committed, so the answer to “who may accept this work” travels with the repository rather than living in one machine’s environment. Every key is in Reference → config.

5. Fan the agents out

Each agent session exports its own COMMISSION_ACTOR and asks for work. The claim is atomic, so it does not matter whether they run a second apart or the same millisecond:

COMMISSION_ACTOR=agent:a commission next --claim
COMMISSION_ACTOR=agent:b commission next --claim

Run genuinely concurrently — two shells, same instant — the loser of the race is told so and takes the next task instead of colliding:

claimed ledger-5
skipped: ledger-1 (claimed concurrently by another agent)

What each agent gets back is not a task id. It is the whole context bundle: the body, the vision, the criteria, what this task blocks, the gate it must satisfy, prior notes, and the commands that finish it from here. That is the property that makes a fresh session cheap — one command, no re-briefing.

In practice you do not type these two lines; you start two coding-agent sessions whose CLAUDE.md contains commission prime output, and they run this themselves. See your first agent.

6. What an agent’s task looks like from the inside

The agent claims, starts, writes the code, runs the tests, commits — and then meets the gate:

COMMISSION_ACTOR=agent:a commission start ledger-1
COMMISSION_ACTOR=agent:a commission note ledger-1 -m "Math.round on a float was the bug: 1.005 * 100 is 100.49999999999999. Parsing the decimal string into thousandths and rounding half-up there keeps it exact."
COMMISSION_ACTOR=agent:a commission done ledger-1
cannot complete ledger-1: 2 unchecked criteria; gate 'shipped' missing evidence [test, commit]
  missing: criterion 0: cents(1.005) returns 101
  missing: criterion 1: no float arithmetic remains in src/money.ts
  missing: evidence.test
  missing: evidence.commit
hint: tick each with 'commission check ledger-1 0', 'commission check ledger-1 1' — only once it is actually true; attach with 'commission evidence ledger-1 test=<ref> commit=<ref>'; or --force to override (logged)

Exit code 2, and the message is a to-do list. The agent satisfies it and closes:

COMMISSION_ACTOR=agent:a commission check ledger-1 0
COMMISSION_ACTOR=agent:a commission check ledger-1 1
COMMISSION_ACTOR=agent:a commission evidence ledger-1 test="bun test — 1 pass, 0 fail" commit=6feaa66
COMMISSION_ACTOR=agent:a commission done ledger-1

The note is the part you cannot buy back later. Six weeks on, “why is this parsing a string instead of multiplying” is answered by the task itself rather than by re-deriving the floating-point bug from scratch.

7. Answer only what is actually yours

An agent that hits a judgement call escalates instead of guessing:

COMMISSION_ACTOR=agent:b commission ask ledger-5 -m "The sample books use both Accounts Receivable and Trade Debtors. Should the README teach one name or both?" --options "one name,both names" --blocking

--blocking parks the task: no agent may work it until it is answered, which is better than an agent picking a house style you disagree with three files later. --options is a single comma-separated list, so a choice whose text contains a comma silently becomes two choices — keep them short.

Your side of it is one command, and it is the only queue you have:

commission inbox
1 thing needs your judgment in ledger — 1 escalated decision
scoped to blaze (--all shows every actor's) · 0 in flight · 2 ready to pick up

1. [decide] ledger-5 · Bookkeeper-facing README
   just now
   what       agent:b asked: "The sample books use both Accounts Receivable and Trade Debtors. Should the README teach one name or both?" (one name / both names)
   needs you  no agent may work ledger-5 until this is decided — and deciding it is not theirs to do
   if ignored ledger-5 stays parked
   →  commission answer ledger-5 -m "<the decision, and why>"

Every item states what it is, why it needs you specifically, what happens if you ignore it, and the one command that clears it. Answer it and the agents move:

commission answer ledger-5 -m "One name: Accounts Receivable. Trade Debtors is the UK synonym and the loader aliases it, so the README teaches the canonical name only."

The answer is recorded on the task and appears in every later context bundle as a decision. It is not a Slack message that scrolls away.

8. Watch the one thing you actually want to see

You do not want to review everything, and reviewing nothing is how something lands that you would have vetoed. Mark the tasks you want a look at, and only those:

commission edit ledger-6 --autonomy supervised

The agent works it exactly as before, and is refused at the end:

COMMISSION_ACTOR=agent:a commission claim ledger-6
COMMISSION_ACTOR=agent:a commission start ledger-6
COMMISSION_ACTOR=agent:a commission check ledger-6 0
COMMISSION_ACTOR=agent:a commission evidence ledger-6 test="bun test — 3 pass, 0 fail" commit=1d0a4f2
COMMISSION_ACTOR=agent:a commission done ledger-6
cannot complete ledger-6: 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: COMMISSION_ACTOR=blaze commission approve ledger-6 -m "<why it is acceptable>"

The agent must put it up for review, or you will not see it. This walk found the gap the hard way: at the moment of that refusal, commission inbox said nothing needs you. The approval item is keyed on the task being in_review, so an agent that meets the refusal and stops leaves the work invisible to the only person who can release it.

Nothing currently closes that loop for you. The refusal names commission approve — a command the agent cannot run — and commission prime, the operating manual agents are handed, explains approvals without mentioning review in connection with them. Until it does, put one line in your own agent instructions: if done is refused for an approval, run commission review <id> and stop.

COMMISSION_ACTOR=agent:a commission review ledger-6
commission inbox
1 thing needs your judgment in ledger — 1 approval
scoped to blaze (--all shows every actor's) · 0 in flight · 1 ready to pick up

1. [approve] ledger-6 · Chart of accounts loader
   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 ledger-6 -m "<why it is acceptable>"
commission approve ledger-6 -m "Read the diff: unknown codes throw at load, and the UK alias resolves to 1100. Ship it."
COMMISSION_ACTOR=agent:a commission done ledger-6

An approval is a separate fact from technical completion, stored against the version of the work you accepted, and --force cannot substitute for it. An agent that could force past a human approval would make “human approval” a naming convention. See execution policies.

9. Here are this quarter’s deliverables

Three reads, no reporting:

commission board
commission standup
commission export > DELIVERABLES.md

board is your position — in flight, ready, blocked, recently done. standup is the same truth as prose, for the call where someone asks how it is going:

what to say (covers since Friday 7:00 AM):
  Since Friday 7:00 AM we shipped 2 things: Chart of accounts loader and Exact money arithmetic in minor units.
  Blocked: HTTP API for posting and reading entries — waiting on Append-only journal with balanced entries (open).
  Next up: Append-only journal with balanced entries.

export writes a git-committable markdown snapshot — the artifact you send to an investor, a customer, or your future self:

# Ledger — status

`2/6` done · 2 ready · 2 blocked · generated 2026-07-27T16:30Z

## Done (last 7 days)

- **ledger-6** Chart of accounts loader (just now)
- **ledger-1** Exact money arithmetic in minor units (just now)

It opens with DO NOT EDIT, and it means it: the database is the truth and this file is a derivation of it. Commit it if you like the diff; never edit it, and never let it become the place you look.

To catch up on a morning you missed, read the log forward instead:

commission log --last 5
2026-07-27 16:30 agent:a ledger-6 evidence: test=bun test — 3 pass, 0 fail, commit=1d0a4f2
2026-07-27 16:30 blaze ledger-6 approval: {"kind":"approval","by":"blaze","note":"Read the diff: unknown codes throw at load, and the UK alias resolves to 1100. Ship it.","remaining":0}
2026-07-27 16:30 agent:a ledger-6 transition: in_review→done
next cursor: 28  (commission log --after 28)

Append-only, with a cursor, so “what happened since I last looked” is a real question with an exact answer.

Habits that survive the first month

  • Keep the frontier wide. Run commission ready after every plan change. If it shows one item, you have written a queue, and the agents will run one at a time no matter how many you start.
  • Mark supervised sparingly. Two or three tasks a quarter — the public surface, the migration, the thing customers see. Marking everything supervised rebuilds the review bottleneck you left a job to escape.
  • Notes over memory. A note costs eight seconds and buys back the hour spent re-deriving a decision. It is also what commission search finds.
  • Never edit an exported file. The database is the truth; everything else is a view. See architecture.
  • Let the refusals teach the agents. Exit code 2 always names the fixing command. An agent that reads them needs no process documentation from you.

The whole sequence

commission init ledger --name "Ledger"
commission gates --set shipped=test,commit
commission apply -f plan.json
commission ready
commission undep ledger-5 --on ledger-3
commission undep ledger-5 --on ledger-4
commission actors
COMMISSION_ACTOR=agent:a commission next --claim
COMMISSION_ACTOR=agent:a commission start ledger-1
COMMISSION_ACTOR=agent:a commission check ledger-1 0
COMMISSION_ACTOR=agent:a commission evidence ledger-1 test="bun test — 1 pass, 0 fail" commit=6feaa66
COMMISSION_ACTOR=agent:a commission done ledger-1
commission inbox
commission answer ledger-5 -m "One name: Accounts Receivable. Trade Debtors is the UK synonym and the loader aliases it, so the README teaches the canonical name only."
commission edit ledger-6 --autonomy supervised
COMMISSION_ACTOR=agent:a commission review ledger-6
commission approve ledger-6 -m "Read the diff: unknown codes throw at load, and the UK alias resolves to 1100. Ship it."
commission board
commission export > DELIVERABLES.md

Every command on this page is executed by tests/docs.test.ts against a scratch database in a real git repository, in this order.

  • Planning — designing the graph, and why the frontier stays wide.
  • Parallel worktouches, batches, and what “safe to fan out” promises.
  • Task lifecycle — every transition and every refusal.
  • Workspaces — when two agents need two checkouts and two ports.
  • Enterprise adoption — the same product where other people have opinions.

Part of Guides.

This page is docs/guides/solo-founder.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.