How Kiwi decomposes a task into parallel agent workers
The Planner is the Control Plane component (pkg/planner) that turns a high-level task into a DAG of worker specs, persists it as an immutable manifest, and enqueues one unit of work per worker onto the lease queue.
From a task to a plan
You submit a PlanRequest — a task description, a repo URL and ref, an optional target file (or files), the worker model, and a test command that defines "done". The Planner asks a frontier model to decompose that task into a Plan:
// pkg/planner/planner.go
type Plan struct {
Summary string // one-line description of the whole plan
Workers []PlannedWorker // the DAG nodes
}
type PlannedWorker struct {
ID string // node id, unique within the plan
Task string // the natural-language goal for this worker
File string // target file this worker edits
Files []string // optional: multiple target files
Model string // the model this worker runs on (customer's key)
TestCmd string // per-worker "definition of done" (optional)
DependsOn []string // ids of workers that must SUCCEED first
}
Two seams keep this flexible:
- The
Plannerinterface has two implementations: a deterministic HeuristicPlanner (used by default and in tests) and a frontier-model-backed LLMPlanner (production). Both produce the samePlanshape. - The planner model that does the decomposition is distinct from the worker model that does the edits. The planner runs on the Control Plane's own planning key; the workers run on your provider key. An optional
planner_modeloverrides the decomposition model, falling back to the platform default when empty.
What a worker spec contains
Each planned worker is expanded into a worker-spec payload — the self-contained unit a daemon leases and executes:
| Field | Meaning |
|---|---|
id | <job_id>-<worker_id> — the queued task id |
task | the worker's natural-language goal |
job_task | the original top-level task (for context) |
file / files | the target file(s) to edit |
model | the worker model (runs on the customer's key) |
test_cmd | the command that must pass — the definition of done |
depends_on | ids of sibling workers that must SUCCEED first |
repo_url / ref | where to get the code |
job_id | links the worker back to its job and manifest |
The test_cmd resolves per-worker first, then falls back to the plan-wide command from the request. If neither is set, the daemon cannot run a verifying loop for that worker.
Decomposition flow
The manifest is immutable and content-addressed
Once the Planner produces a Plan, the service canonicalizes the task, repo, ref, summary, and workers into a JSON document and hashes it with SHA-256 — that hash is the manifest id. The manifest and all of its worker tasks are then written in a single database transaction: if any worker fails to enqueue, the manifest is rolled back too, so there are never partial plans. Submissions can be made idempotent with an Idempotency-Key header, which dedupes retried submissions to the same job.
Shared context (opt-in)
At submit time the planner can optionally draw on your org's prior jobs so related work informs the new decomposition:
- Auto — semantic nearest-neighbour over past jobs using pgvector embeddings.
- Manual — you pick specific prior jobs to reference.
It is off by default, strictly org-scoped (you can only ever reference your own jobs), and injected at plan time only. Auto mode may use extra tokens for the embedding lookup.
Next: DAGs — what the depends_on edges buy you, and how they are scheduled.