Forester
Grove is the ground and Dryad seats one worker on it. Forester decides which
work exists, in what order, and how much of it this machine runs at once.
The allocator is a pure function of its inputs: the plan, the Dryad seats,
the budget, and the baseline's git facts; the same inputs give the same
assignment every time. The runner around it (assign --apply, serve) is
stateful on purpose: it reserves slots, seats items, and launches tools,
and everything it did is readable from status.
This file owns the pattern. The fields, the allocation rule, and the CLI are in references/plan.md. Dryad pattern: dryad.
Specify first
A plan needs four things settled: the goal, the scope (what may change and what may not), the verification (what proves each item), and the permissions (what may be spent, destroyed, or sent outside). When the person hands you a brief that already settles them, use it: do not interview them again about what they wrote down, and do not stop a safe, well-specified task because a question could be asked. When something is not settled, interview until the two of you hold the same picture. Work it as a design tree: every decision branches into the decisions that hang off it. The frontier is every question whose prerequisites are already answered. Ask the whole frontier in one round, numbered, each with your recommended answer, then wait. A round with one question is a round; a round that guesses at an answer it has not heard is not. Destructive, costly, or irreversible choices, and real ambiguity in the ask, are always questions; a preference you could recommend and revise is not.
Q1 <title>: <the question, with the choices if there are some>
→ <your recommended answer, and why in one line>
Stop when the frontier is empty: no open question remains whose answer
would change an item, a claim, or an edge. A locked decision is a fact
worth keeping: propose it to Mycelium as a decision when the project has
the log (Call the Skill tool with "mycelium").
Analyse the work
You write the plan. The CLI reads it. When the grilling is done:
Split it into items a single worker can finish alone, each with a one-line title and, unless the title says it all, a
brieffile that a stranger could start from (outcome, boundary, verification, report, forbidden: the shape in Dryad's seat brief). Name the checks inverify; the handoff carries both. An item is a vertical slice: a narrow but complete path through every layer it touches, so a finished item is demoable or verifiable on its own, never one layer of something bigger. Size each to one fresh context window. An item is too big when its task line needs "and". It is too small when its worktree would hold one trivial change. A wide refactor (one mechanical change whose blast radius is the whole codebase) is the exception: sequence it as expand, then migrate in batches sized by blast radius, then contract, each batch an item blocked by the expand.Name what each item will touch in
owns, as paths or globs. This is a claim, not a lock: the allocator refuses to run two items whose claims intersect, and nothing else enforces it. Claim what you honestly expect the worker to edit. Two items that must edit the same file are one item, or one depends on the other.Declare
depends_ononly where the second item cannot start until the first is done, and say what it needs. A bare id means the second item starts from a base that holds the first one's result, so it waits until a person has merged that branch (or recorded the squash withdryad integrate).{ item: <id>, needs: order }means only that the first has reported done. A shared reference that both read is a result dependency on the item that writes it. Sharing an opinion is not.Order matters. Items are walked in declared order and ties are broken by it. Put the item that unblocks the most first. Do not encode priority any other way; there is no estimate field on purpose.
Give
retry.max_attemptsabove one only to an item whose failure is likely to be the worker's, not the plan's.Show the split before writing the file. Show the items as a numbered list: title, blocked by, what it delivers end to end. When the person gave you the items, confirm the edges and claims you added and move on. When you cut them yourself, ask three things: is the granularity right, does each edge gate what it says it gates, should any item be merged or split. Iterate until they say yes.
Then write .agents/forester-plan.yml, validate it with
de-novo skills forester plan, and show the person the printed graph before
anything is assigned. The first evidence that a plan is good is that a
person can read that output and know what is happening.
Set the budget
parallel is how many items may be active at once. It is a property of the
machine more than the project: a laptop running three shared engines seats
fewer workers than a workstation. So it lives in .agents/forester.local.yml,
which is gitignored. A project may set parallel in the plan itself, and
then the project's value is followed and the local file is ignored. No
budget anywhere is an error, never a silent default. Above every project
sits one machine cap, forester machine --parallel N --apply: the sum of
what Forester seats on this machine, taken as reservations under one
lock. Seats planned by hand are shown as unmanaged and not counted.
Keep the slots full
de-novo skills forester plan # every item and why it is in its state
de-novo skills forester next # what assign would seat now; changes nothing
de-novo skills forester assign --apply # seat the chosen items through Dryad
de-novo skills forester status # slots filled, items waiting
assign --apply creates one Dryad seat per chosen item, with the item id as
the seat id, a generated handoff as the seat's task, and the item's
revision, scope, and dependency inputs recorded on the seat. From there
Dryad's rules apply: the worker reports its own state, and report --status done for this revision is the only thing that makes an item done. A done
report is a report, not an integration: an item whose result another item
needs stays waiting until a person merges its branch or records the
squash, and plan says which. Rerun assign --apply after a done report
or a merge to fill the freed slot. A seat that is finished without a done
report spends one of the item's attempts; the next attempt gets a branch of
its own. Editing an item's task, claims, or brief gives it a new revision,
and no earlier done is inherited.
forester serve, run in a terminal of its own, does the refill by itself
and starts each seated item's tool as a real interactive session that it
holds. Nothing is headless: every approval, the trust dialog included, is
still the tool's own prompt, and status shows which session needs a
person. serve grants no trust on its own; a machine that wants seat
worktrees pre-trusted says so in the local file. A session that could not
start says which thing is missing, a pending env is retried with backoff,
and forester restart <id> launches a failed or exited item again once
you have fixed it. One serve runs per project. forester attach <id>
opens that session; Ctrl-] leaves it running. The tools and their launch
lines are machine facts and live in the local file, never in the plan.
Run forester hooks --apply once on a machine so Codex, Grok, Cursor, and
OpenCode sessions report their state the way Claude Code does; --remove
takes it back.
Invariants — not weakenable
- No model call in the CLI. You analyse; the file is the interface.
servestarting the tool a person configured as a session is a launch, not a judgement: the CLI still decides nothing an allocation would not. - Forester creates no environment or worktree itself. It asks Dryad, which asks Grove.
- A claim is not a lock.
ownsconstrains allocation, not the filesystem. - Order is declared, not inferred. No estimate-based scheduling.
- The project's budget wins; the local one is the fallback; none is an error.
- A done report is not an integration. A result reaches a dependent item only through the baseline, by a person's merge or recorded integration. Forester never merges.
- A done belongs to a revision. An item edited after its seat was done is not done.
Not this skill
- Verifying that an item's work is good enough to land. That is a separate gate.
- A screen. Canopy shows seats;
forester plan --jsonis shaped for it to read. - Merging, rebasing, or deciding who yields on an overlap.