Skip to content
commission

Planning a task graph

You will be able to turn an idea — or an existing codebase — into a task graph that commission can dispatch, and re-apply that plan safely as it changes, without ever creating a half-written graph.

Audience: whoever is deciding what gets built. Assumes: your first task.

One task, when one task is the answer

commission add "Rotate the signing key" -c "old key revoked" -c "runbook updated" --gate shipped

commission add is right for a thing you thought of while doing something else. It is the wrong tool for a quarter’s work, because you will end up with thirty tasks and no dependencies between them, which is a list rather than a graph.

The plan document

Everything real goes through commission apply:

cat > plan.json <<'PLAN'
{
  "project": "myapp",
  "tasks": [
    { "key": "schema", "title": "Design the events table",
      "criteria": ["migration reviewed", "indexes justified"],
      "touches": ["src/db"] },
    { "key": "ingest", "title": "Webhook ingest endpoint",
      "deps": ["schema"], "gate": "shipped", "touches": ["src/http"],
      "criteria": ["signature verified", "replays rejected"] },
    { "key": "feed", "title": "Event feed in the web view",
      "deps": ["schema"], "touches": ["src/web"] }
  ]
}
PLAN
commission apply -f plan.json
applied to 'myapp': 3 created, 0 updated
  + myapp-1  (schema)
  + myapp-2  (ingest)
  + myapp-3  (feed)

The full field list — parents, links, priority, autonomy, external keys — is in the generated apply reference, and the JSON Schema itself is one command away:

commission apply --schema

Four properties worth knowing

It is atomic. Local keys resolve to real ids inside one transaction. A plan that names a gate you have not defined, or a dependency that does not exist, creates nothing:

unknown gate 'tier1' for project 'myapp'
hint: 'myapp' has no gate profiles yet — define one: commission gates --set tier1=test,commit

You never have to clean up after a rejected plan.

It upserts by key. Re-applying the same document does not duplicate anything; it updates in place and creates whatever is new:

applied to 'myapp': 1 created, 2 updated
  + myapp-3  (c)
  ~ myapp-1  (a)
  ~ myapp-2  (b)

So the plan file can live in the repository and grow with the work. It is not a one-shot import.

It previews. --dry-run prints the exact result and writes nothing:

commission apply -f plan.json --dry-run
dry-run for 'myapp': 2 created, 0 updated
  + myapp-1  (a)
  + myapp-2  (b)
nothing written — land it with: commission apply -f <file>

It takes stdin. commission apply -f - reads the document from a pipe, which is how an agent lands a plan it just wrote without touching the filesystem.

Designing the graph

Three rules, and they all point the same way: keep the ready frontier wide.

  1. deps only for real ordering. A dependency that encodes a preference serialises work for no reason. If B could start before A finishes, B does not depend on A.
  2. Split by area, not by phase. “Implement the endpoint” then “test the endpoint” is a chain of two. “The ingest endpoint” and “the event feed” is two parallel tasks. Phases are how a plan becomes a queue.
  3. Label touches honestly. It is what lets commission suggest which ready tasks can be dispatched together — see parallel work for exactly how much that promises.

Use parent for containment, not for ordering. A parent task cannot close while any child is open, and that refusal is not forceable: a parent’s exit criterion is its children.

Check the shape before you dispatch:

commission ready --project myapp
commission board --project myapp

If blocked is long and ready is one item, the graph is a queue and the agents will run one at a time.

Criteria, gates, and evidence

  • Criteria are the task’s own definition of finished, ticked one at a time. Write them as checkable facts — “replays rejected”, not “handle replays properly”.
  • Gates are the evidence class the task belongs to, defined once for the project and referenced by name.
commission gates --set shipped=pr,test
commission gates

Declare gates before applying a plan that references them; the whole plan is refused otherwise. Gates live on the project row: commission gates --set and commission init --gates write them, and the gates block in .commission.json is not yet applied.

Letting an agent do the planning

commission define myapp
commission define myapp --repo-path .

define prints a brief — not a plan. It walks the agent through interrogating the idea, writing a project vision, designing the graph, and ending in a reviewed commission apply, with an explicit instruction not to skip the dry run. --repo-path grounds it in a codebase that already exists, so the plan describes the system you have rather than the one the model imagined.

The vision it produces is stored on the project and injected into every task bundle, so a session six weeks later inherits the principles that settled the arguments rather than re-litigating them.

Before planning anything, check whether it was already decided:

commission search "rate limit"

Full-text search covers task titles, bodies, notes, questions, and answers. “Did we already try this?” is answerable in one command, and the answer is usually a note somebody left at 2am.

The whole sequence

commission gates --set shipped=pr,test
commission search "rate limit"
commission apply -f plan.json --dry-run
commission apply -f plan.json
commission ready --project myapp
commission board --project myapp

Every command on this page is executed by tests/docs.test.ts against a scratch database.


Part of Guides.

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