Start a campaign¶
A campaign hands a parent issue and its sub-issues to an agent, the
lead. The configured prompt tells the lead what to do with them. The
default dream:lead route shepherds each sub-issue through an
assignment and merged pull request, and asks you on the parent when it
needs a decision.
dreamcatcher gives the lead a round when the campaign starts, another for each comment you post on the parent, and another when a sub-issue changes in a way that the lead needs to hear of. It ends the campaign when you close the parent.
You need a configured campaign route and a running daemon. For a first campaign, follow the worked example in From parent issue to merged pull requests.
Configure a campaign route¶
The dreamcatcher.toml that dreamcatcher init writes routes the
dream:lead label to campaigns. Its recipes run the
dream:lead
skill from the dream plugin. The skill sets up an assignment for each
sub-issue on an assignment route, reviews the assignment's pull request
and checks its proof, gives the assignment's agent feedback, and merges
the pull request once the sub-issue is done. It asks you on the parent
before it changes the plan.
init never changes an existing dreamcatcher.toml, so a file written
before the route existed has none. Copy the [[campaign]] route from
the default configuration
into it.
The dream plugin has had dream:lead since version 43.3.0, and init
leaves an installed plugin as it is. If you installed the plugin before
then, update it with the plugin commands of each harness that runs the
route.
To give the lead other instructions, add a campaign route of your own
beside dream:lead, under a label of its own. Its prompt tells the lead
what to do with the parent and with your comments on it. For example,
this prompt makes a lead that summarises the plan and then talks with
you:
[[campaign]]
label = "summarise-plan"
[campaign.claude]
prompt = "Read GH{issue} and its sub-issues. Reply to the comments in your input in one comment on GH{issue}. If your input holds no comment, post a comment on GH{issue} that summarises the plan."
model = "opus[1m]"
effort = "high"
Run dreamcatcher init to create the label
on GitHub, then restart the daemon so that it reads the new route. The
campaign skill contract says what
dreamcatcher gives the lead and what the lead must leave to you.
Keep sub-issue pull requests from updating one another¶
A campaign can have several sub-issue pull requests open at once. If the
repository requires branches to be up to date before merging, each merge
leaves every other sub-issue behind main. Each remaining assignment's
agent then merges main, its checks run again, and the lead wakes for
the new head. Only one of those pull requests merges next, so the rest
repeat the work after the next merge.
Use
GitHub's merge queue
when it is available for the repository. It tests queued changes with
the latest main when their turn comes, without making each agent
update its pull request branch after every earlier merge.
Otherwise, leave Require branches to be up to date before merging
off for a repository that runs campaigns. A pull request that falls
behind without a conflict still merges cleanly. One that conflicts with
main still wakes its agent to bring the branch up to date.
Start the campaign¶
On GitHub:
- Keep the parent issue open.
- Assign it to the account authenticated through
gh. - Add exactly one configured campaign label, such as
dream:lead.
If open issues block the parent on GitHub, the campaign waits until every one of them is closed.
A campaign needs no comment to start. At its next update with a free
agent slot, dreamcatcher checks out current origin/main in a
worktree of the campaign's own, and starts the lead with the configured
prompt. Its input holds any comments you have already posted on the
parent, the parent's sub-issues with where each one's assignment stands,
and the daemon's assignment routes. The lead posts on GitHub itself.
dreamcatcher keeps the lead's final output in the feed and posts none
of it.
Monitor the campaign on the web home page's Campaigns tab, where its card shows how many of the parent's sub-issues are closed. Expand the count to see each sub-issue with its assignment's card and the row of each request for an assignment on it that has no open work, or the sub-issue's number and title alone when it has neither. Each assignment's card stays on the Assignments tab too. Once the campaign ends, its card moves to the ended list and shows only the campaign itself. You can also monitor the campaign in the terminal:
dreamcatcher view GH123
dreamcatcher feed GH123
Queue campaigns¶
To run one campaign after another, mark the later parent as blocked by the earlier parent on GitHub, then assign and label both parents, and leave their sub-issues for the leads to start. The later campaign waits, and its request shows as blocked by the earlier parent. The block holds back only the later lead, so a sub-issue that you label for an assignment yourself starts according to its own dependencies.
Close the earlier parent when you are satisfied with its campaign, and the later campaign starts at the next update with a free agent slot. Closing every sub-issue does not close the parent, and the lead is told never to close it, so the next campaign waits for you.
Talk to the lead¶
Comment on the parent to talk to the lead. At the next update with a
free agent slot, dreamcatcher refreshes the lead's worktree to current
origin/main and gives the lead every comment you have posted since its
last round, in the same harness session. The lead answers on GitHub
itself. Its own posts carry the agent marker, so they never wake it.
When the lead gives a sub-issue's assignment feedback on its pull request, it leaves the marker off. The assignment's agent is then given the feedback as it is given yours, and the lead hears of the agent's answer once the assignment hands back.
Changes among the sub-issues¶
At each update, dreamcatcher compares the parent's sub-issues with those it last gave the lead. A sub-issue added to or removed from the parent wakes the lead at once. A sub-issue that is closed or reopened, an assignment that finishes its turn, ends or falls into fault, a pull request that changes its head, state or draft flag, checks that finish, and a pull request that becomes mergeable wake the lead once the sub-issue's assignment has no round running or due. The lead's input names each sub-issue that changed and what changed, beside the whole current picture. Changes that arrive together, or while the lead cannot take a round, reach it in one round.
Pause or end a campaign¶
Remove the campaign's label from the parent, or unassign the parent from yourself, to pause the campaign. A round that is already running finishes. Comments you post while the campaign is paused wake nothing. Restore the label and the assignee to resume the same campaign, and the lead is given every comment you posted while it was paused, and what changed among the sub-issues meanwhile. A second campaign label on the parent is a routing conflict, and pauses the campaign in the same way until one of the labels comes off.
Close the parent to end the campaign. It ends once any running round finishes, and dreamcatcher then removes its worktree and keeps its record. Only you close the parent: the lead is told never to close it. If you reopen the parent with its label still on, a new campaign starts.
Stop or recover a round¶
To stop a lead's round, or to recover after one errors or the daemon stops, follow Stop and recover work.