Configuration reference¶
dreamcatcher reads dreamcatcher.toml from the root of the main
checkout where dreamcatcher run starts. The file defines which issue
labels dispatch assignments, campaigns or issue conversations, and the
dispatch recipes available for each label. It does not configure daemon
controls such as the interval or agent capacity; those are
run options.
The file is required and must be UTF-8 TOML. run reports a missing,
unreadable or invalid file rather than supplying a default
configuration. init writes the complete
example below when the file is missing.
The daemon reads the file when it starts. Restart it to apply a change. Commit the file when everyone watching a repository should use the same routes.
Complete example¶
[[assignment]]
label = "agent-assignment"
[assignment.claude]
prompt = "/dream:smith GH{issue}"
model = "opus[1m]"
effort = "high"
[assignment.codex]
prompt = "$dream:smith GH{issue}"
model = "gpt-5.6-sol"
effort = "high"
config = { model_context_window = 1000000, model_auto_compact_token_limit = 900000 }
[[assignment]]
label = "agent-assignment-easy"
[assignment.claude]
prompt = "/dream:less GH{issue}"
model = "sonnet"
effort = "high"
[assignment.codex]
prompt = "$dream:less GH{issue}"
model = "gpt-5.6-terra"
effort = "high"
[[campaign]]
label = "agent-campaign"
[campaign.claude]
prompt = "/dream:big GH{issue}"
model = "opus[1m]"
effort = "high"
[campaign.codex]
prompt = "$dream:big GH{issue}"
model = "gpt-5.6-sol"
effort = "high"
config = { model_context_window = 1000000, model_auto_compact_token_limit = 900000 }
[[conversation]]
label = "agent-conversation"
[conversation.claude]
prompt = "/dream:scout GH{issue}"
model = "opus"
effort = "high"
[conversation.codex]
prompt = "$dream:scout GH{issue}"
model = "gpt-5.6-sol"
effort = "high"
This file offers both supported harnesses for every route. A route may
instead contain only its claude or only its codex block. When init
writes the file, it comments out the blocks of a harness that is not
installed.
Top-level settings¶
| Setting | Value | Required | Default | Constraint |
|---|---|---|---|---|
assignment |
Array of route tables, written as [[assignment]] |
No | Empty array | Each entry defines a separate assignment route. |
campaign |
Array of route tables, written as [[campaign]] |
No | Empty array | Each entry defines a separate campaign route. |
conversation |
Array of route tables, written as [[conversation]] |
No | Empty array | Each entry defines a separate conversation route. |
No other top-level settings are accepted. In particular, assignee,
interval and max_agents are not configuration-file settings.
dreamcatcher obtains the account from authenticated gh; use
run --interval and run --max-agents for the two daemon controls.
Assignment, campaign and conversation routes¶
An [[assignment]], [[campaign]] or [[conversation]] entry has this
schema:
| Setting | Value | Required | Default | Constraint |
|---|---|---|---|---|
label |
String | Yes | None | Identifies this route, matched case-insensitively against GitHub issue labels. |
claude |
Dispatch recipe | No | Absent | Configures Claude for this route. |
codex |
Dispatch recipe | No | Absent | Configures Codex for this route. |
Every route must contain at least one dispatch recipe. claude and
codex are the only accepted harness-table names.
A label is a case-insensitive identity. No two routes may share a label, whether they are of one kind or of different kinds. Route order gives no route precedence. Issue labels are also matched case-insensitively.
If an issue matches more than one assignment route, that assignment work has a routing conflict. The same is independently true of campaign routes and of conversation routes. Labels of different kinds may coexist on an issue. An assignment conflict prevents a new assignment but does not stop an existing assignment's rounds. A conversation or campaign conflict prevents new rounds of that kind, though a round already running may finish.
Dispatch recipes¶
Each [assignment.claude], [assignment.codex], [campaign.claude],
[campaign.codex], [conversation.claude] or [conversation.codex]
table requires three settings, which have no defaults. Each table also
accepts a config table, the harness config, which holds settings for
the harness beyond its model and effort. Codex takes the settings
described in Codex config. Claude takes none, so a
Claude table's config must be empty.
| Setting | Value | Effect and constraint |
|---|---|---|
prompt |
String | The prompt template for first contact with the harness. Every literal {issue} is replaced by the issue number. Other text in braces is left unchanged. Newlines and percent signs are allowed. |
model |
String | Passed to the selected harness as its model. It may not contain %, a line feed or a carriage return. |
effort |
String | Passed to the selected harness as its reasoning-effort value. It has the same character restrictions as model. |
dreamcatcher does not prescribe the harness-specific model and effort
names; the selected harness must accept the configured strings. Unknown
settings at any level are errors, so a misspelling is reported rather
than ignored. The keys of a Codex config table are the exception,
because they belong to Codex.
Assignment prompts normally invoke an assignment skill that follows the assignment skill contract. Campaign prompts normally invoke a campaign skill, and must follow the campaign skill contract. Conversation prompts may be plain instructions or invoke a conversation skill, but they must follow the conversation skill contract. dreamcatcher adds its operational instructions to every prompt.
Codex config¶
A Codex recipe's config table gives Codex settings of its own, such as
a larger context window. dreamcatcher passes each entry to every round
of the work, first and resumed, as -c key=value, with the value
written as TOML. The table is optional, and an absent table passes
nothing.
| Part | Constraint |
|---|---|
| Key | A Codex setting's name, as codex -c takes it. A name is ASCII letters, digits, _ and -, and a dot separates the parts of a nested name. |
| Value | A boolean, an integer or a string. A string may not contain %. dreamcatcher refuses a float, an array and a table. |
Write a nested setting as one quoted key, such as
"features.web_search_request" = true. A dotted key without quotes
makes a table in TOML, and dreamcatcher refuses a table as a value.
dreamcatcher keeps these settings for itself, so that every round runs with the recipe's model and effort and with the permissions that unattended work needs:
modelandmodel_reasoning_effort;sandbox_mode,approval_policy,approvals_reviewerandsandbox_workspace_write.network_access;default_permissionsand thepermissionstable, which would choose a permissions profile in place of the sandbox settings.
A config table that sets one of these, or a key inside one, is an
error. dreamcatcher does not check that Codex knows a setting, so a
misspelt key in a config table is not reported.
Harness selection¶
dreamcatcher run --harness claude or --harness codex supplies the
preferred harness for newly created work:
- When a route has a recipe for the preferred harness, dreamcatcher uses it.
- When a route has only the other harness's recipe, dreamcatcher uses that harness instead.
The option is therefore a preference, not a repository-wide pin. At
startup, dreamcatcher requires the named harness and every harness
present in any route to be installed on PATH. Existing agent work of
any kind keeps the label, harness, prompt, model, effort and harness
config chosen when it was created; later configuration changes do not
rewrite that record.
A conversation belongs to the conversation label that started it. Another conversation label starts a conversation of its own, with that label's route and recipe, once it is the only conversation label on the issue. While both are on the issue, the labels conflict and nothing starts. A campaign belongs to its campaign label in the same way.
A matching label selects a route but does not by itself make an issue ready for dispatch. From issue to pull request explains how to prepare an assignment issue; Start a campaign explains how to start and end a campaign, and Discuss an issue explains how to start and continue a conversation.