Skip to content
commission

Working on commission, with commission

You will be able to take a piece of work on commission from “what should I do” to “closed, with evidence”, using the same loop the project uses on itself — and know which tests your change has to satisfy, which files you must never edit by hand, and what a release costs.

Before anything else: this repository is private and the software is proprietary. LICENSE LICENSE grants no rights, and it is explicit about contribution — the repository is not open to public contribution, every contribution is assigned to the copyright holder, and contributors must have a written agreement in place before contributing. Access to the repository is not that agreement. If you do not have one, stop here; the rest of this page is operational detail for people who do. Context and consequences: licensing.

Commission tracks commission

There is no second place where work on Commission is tracked. No status document, no markdown board, no issue tracker — the project’s own CLAUDE.md says it first and it is meant literally: “Commission is the single source of truth for tasks in this project. Never track status in markdown files; query commission instead.”

That is not dogfooding for its own sake. It is the only way the refusals stay honest: every rough edge in commission’s own loop is met by whoever is building it, that same day.

Get work

commission next --project commission --claim

The claim is atomic, so several agents can run this at once and each gets a distinct task. What comes back is the whole context bundle — the body, the acceptance criteria, the dependency state, the linked documents to read, the recent notes, and the commands that finish it from here. A fresh session needs that one command and nothing else.

If you want position rather than an assignment:

commission board --project commission
commission log --project commission --last 20

board is where things stand; log is what happened since you last looked. Neither is a report anyone writes.

Search before you plan

commission search "generated docs"

Commission’s own history is full-text searchable, notes included. A decision that was already argued out six weeks ago is cheaper to find than to re-derive, and re-deriving it usually produces a different answer, which is worse.

Leave breadcrumbs as you go, not at the end

commission note commission-1 -m "the drift test regenerates every artifact in memory and diffs it, so a generator change with no doc change fails the suite — which is the point"

Notes are written for the next session, not for a reader today. Decisions, dead ends, discoveries. They are carried in every future context bundle and are what commission search finds.

Finish with evidence

Commission’s documentation tasks carry the documented gate — test, commit, docs — so closing one without them is refused:

commission done commission-1
cannot complete commission-1: 1 unchecked criteria; gate 'documented' missing evidence [test, commit, docs]

Satisfy it, then close:

commission check commission-1 0
commission evidence commission-1 test="bun test — 0 fail" commit=300e680 docs=docs/contributing/working-with-commission.md
commission done commission-1

Never fabricate evidence. A benchmark number nobody ran is worse than an unfinished task, because it closes the loop that was supposed to catch it. And do not --force past a gate unless a human asked you to: the override is recorded, requires a reason, and shows up in the log and the standup.

Plan new work as a graph

commission apply --schema

Whole task graphs land atomically from one JSON document (commission apply -f plan.json). Use deps so the ready frontier stays accurate, and keep it narrow — every false dependency serialises work that could have run in parallel. See planning.

Setting up

bun install
bun test
bun run typecheck

Bun 1.1 or newer. There is no build step for development: bun run src/cli/index.ts is the CLI, and bun link puts commission on your PATH from the bin entry in package.json. bun run build compiles the standalone binary when you want to test what users actually get.

The layout, and why it is that shape, is architecture. The short version: src/core/ is the only writer, src/cli/, src/server/, and src/mcp/ are thin consumers, src/db/ owns storage and migrations, src/workspace/ prepares where work happens, and src/adapters/ talks to external systems.

Test conventions

bun test runs everything. The suite is the specification, and four of its conventions are worth internalising before you write your first test.

Tests are written against the surface a caller meets. tests/version.test.ts spawns the real CLI and performs a real MCP initialize handshake over stdio rather than importing the version constant, and says why: “Unit-importing the constant would prove nothing: the drift being guarded against lives at the call sites, not in the module.” When the risk is a call site, test the call site.

Every test file opens with what it is defending. Not what it covers — what would go wrong without it. tests/cli-contract.test.ts opens with “The CLI is an API: agents parse its output, branch on its exit codes, and act on its refusals unsupervised.” If you cannot write that sentence, the test is probably asserting an implementation detail.

A skip states its reason, in the file. The pattern is everywhere: the docs generator pushes { path, reason } onto a blocked list rather than throwing; tests/docs.test.ts requires every skipped command and every page routed to another suite to carry a reason longer than twenty characters. A silent skip lets a page or an artifact rot invisibly, which is the failure these tests exist to prevent.

Scratch worlds, never your real database. Tests build a temp directory with its own COMMISSION_DB, and often a commission shim on PATH so a documented pipeline or VAR=x commission … prefix runs exactly as written. Nothing in the suite may touch ~/.commission/commission.db.

Where the interesting suites live:

suitedefends
tests/core.test.tsthe library surface: transitions, claims, gates, apply, bundles
tests/cli-contract.test.tsthe CLI as an API — output shape, exit codes, refusal wording
tests/docs.test.tsevery documented command actually runs; generated pages are current
tests/version.test.tsone version, reported identically by CLI, MCP, HTTP, and manifest
tests/migrations.test.tsa v1 database walks to current, and the backup restores
tests/config.test.tsthe config schema and its generated reference agree

Generated versus written

The rule that matters most. Some pages under docs/ are produced from src/. Hand-editing one looks like it worked, is reverted by the next generator run, and in the meantime makes the documentation disagree with the binary. Every generated page opens with a comment naming its generator; if you see that line, close the file and edit the source.

bun run generate:docs
bun run generate:config

Both are idempotent, and both take --check so CI can assert the same thing at the terminal. The drift tests regenerate every artifact in memory and diff it against disk, which has one consequence worth stating plainly: you cannot change the CLI, the apply schema, the MCP tool list, or the config schema without the documentation change landing in the same commit.

Everything else under docs/ is prose. Its authoring conventions — the opening sentence, the tested command blocks, one definition in one place — are writing commission’s documentation, and a new page carrying a ```sh block must be registered in that suite or the build fails by design.

Before you open a change

bun test
bun run typecheck
bun run generate:docs
bun run generate:config

If either generator produces a diff you did not expect, that diff is the documentation change your source change implied. Commit it.

Then close the loop in commission: tick the criteria that are actually met, attach the evidence the gate asks for, and let commission done check you.

The release process

The full step-by-step is the release checklist, and it is written to be run top to bottom by a human or an agent with no step that says “verify things look right”. The shape of it:

  1. Preconditions — clean tree, on main, nothing for this release still in flight in commission board.
  2. Decide the number — from the changelog, under compatibility.md. A schema bump alone does not force a major.
  3. Green buildbun test, tsc --noEmit, bun run build, and ./dist/commission doctor on a scratch database.
  4. Bump the version in exactly one place — the version field in package.json. src/version.ts derives from it and every surface derives from that; tests/version.test.ts fails if a literal appears anywhere else.
  5. Close the changelog section, adding the Schema version N · config version M line.
  6. Upgrade rehearsal — this is the step the test suite cannot replace. Take a database created by the previous release, run commission migrate --check against a copy, migrate it, confirm the printed backup restores it, and check that any new migration was appended rather than edited in place.
  7. Documentation — regenerate, and reflect any surface change in compatibility.md.
  8. Tag it. Pushing the tag is what builds and publishes: the workflow builds every platform binary on its own runner and refuses to continue if any of them cannot run init through done on that platform. See release engineering.
  9. Verify against the published artifact, not your working tree.
  10. Close the release task with the tag as evidence, and note anything that went wrong. A checklist that never changes is one nobody is following.

Two rules that are not steps. Released migrations are immutable — append, never edit; a released migration’s text changing is how somebody’s database becomes unreadable. And the version lives in one place, for the same reason LATEST_SCHEMA is the migration array’s length: a second copy of a number can only drift.

The rules that are not negotiable

  • Never track status in a markdown file. Query commission. A file that says what is done is a second truth, and the second truth is always the wrong one.
  • Never hand-edit a generated page.
  • Never fabricate evidence.
  • Do not --force past a gate unless a human asked you to.
  • Nothing points at a public repository. No “view on GitHub”, no “edit this page”, no issue-tracker URL. The repository is private, and a 404 reads as broken rather than deliberate (licensing).
  • Nothing is described in the present tense unless it has shipped. Check positioning.md §8 and commission show <id> before you write a claim onto a page.

Part of Contributing.

This page is docs/contributing/working-with-commission.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.