Skip to content

Configuration

autoducks is configured via .autoducks/autoducks.json at the root of your repository.

{
"command": "",
"providers": {
"its": "github",
"git": "github",
"llm": "claude"
},
"defaults": {
"model": "claude-sonnet-5",
"effort": "high",
"max_turns": 50,
"base_branch": "main",
"integration_branch": "main",
"merge_method": "auto"
},
"orchestrator": {
"mode": "waves"
},
"reviewer": {
"required_check": false,
"check_name": "Autoducks: Reviewer"
},
"checks": {
"enabled": false,
"setup": "",
"commands": [],
"git_hooks": false,
"max_iterations": 3
},
"product": {
"enabled": true,
"schedule": "0 9 * * *",
"priority_backend": "auto",
"project_number": null,
"priority_field": "Priority",
"auto_merge_duplicates": true,
"max_closes_per_run": 5,
"confidence_threshold": "high",
"max_issues_per_run": 100,
"provisional_classification": true
},
"triggers": {
"architect": [],
"engineer": [],
"execute": [],
"fix": [],
"revert": [],
"close": [],
"review": [],
"triage": [],
"merge": []
},
"context": {
"developer": {
"parts": ["issue_title", "issue_description", "prior_feedback"]
},
"reviewer": {
"parts": [
"issue_title",
"design.problem_statement",
"design.proposed_solution",
"design.constraints",
"design.out_of_scope",
"pr_diff",
"pr_meta",
"security_guidelines"
]
}
},
"review": {
"security_guidelines": ".autoducks/security-guidelines.md",
"auto_rework": true,
"max_iterations": 3
},
"security": {
"trusted_associations": ["OWNER", "MEMBER", "COLLABORATOR"],
"allow": [],
"deny": [],
"codeowners": false,
"per_agent": {
"revert": { "trusted_associations": ["OWNER", "MEMBER"] },
"close": { "trusted_associations": ["OWNER", "MEMBER"] },
"product": { "trusted_associations": ["OWNER", "MEMBER", "COLLABORATOR"] },
"merge": { "trusted_associations": ["OWNER", "MEMBER"] }
}
}
}

An optional namespace word. Commands are short-form by default — a bare verb right after the slash (/execute, /architect) — and command is the escape hatch for teams that want (or need) a namespaced prefix instead (/quack execute, /quack architect), e.g. to avoid colliding with another bot’s slash commands on the same repository.

Default: "" (empty — short-form commands, no namespace word)

Validation is /-tolerant: ^$ (empty) or ^/?[a-z0-9-]+$ (a lowercase word, with or without a leading slash). "quack" and "/quack" are equivalent — both normalize to the namespace word quack and produce /quack <verb> triggers. Anything else falls back to the default (empty).

{ "command": "" } // short-form (default): /execute, /architect, /review...
{ "command": "quack" } // namespaced: /quack execute, /quack architect...
{ "command": "/quack" } // identical to above — the leading slash is optional

GitHub Actions evaluates workflow if: guards without reading repository files, so the namespace (like custom trigger aliases) is baked into the workflow YAML. After changing it, regenerate and commit the guards:

Terminal window
bash scripts/update-triggers.sh

A bare verb (/execute) is more likely to appear at the start of an unrelated comment than a namespaced one (/quack execute) — that’s the tradeoff for the shorter surface. Two independent safeguards bound the consequences of an off-target fire:

  • The Authorization Gate runs before anything else — an accidental match from an untrusted commenter is denied before any LLM call, comment, branch, or PR.
  • Agents are safe, state-based no-ops when there’s nothing to do — they recompute from visible ITS/git state rather than blindly acting, so a stray trigger on an issue with no matching state (e.g. /revert on an issue that was never planned) does nothing destructive.

If your repository’s comment traffic makes even a gated, no-op accidental match undesirable, set command to a namespace word.

Selects the implementation for each provider interface.

KeyValueStatusDescription
itsgithub✅ SupportedGitHub Issues via the gh CLI
gitgithub✅ SupportedGit CLI with GitHub repository operations
llmclaude✅ SupportedAnthropic Claude via the Claude Code action

Default runtime parameters for all agents, unless overridden by a directive argument or a per-agent defaults.json.

The Claude model ID for LLM agents. Default: claude-sonnet-5

ModelUse case
claude-sonnet-5Default — good balance of speed and quality
claude-opus-4-8Best quality for design/planning work
claude-haiku-4-5Fastest, lowest cost — simple execution tasks

LLM effort level: off, low, medium, high, max. Default: high

Maps to the provider’s native reasoning-effort control, not a prompt instruction — for Claude, the CLI’s --effort flag, forwarded via claude_args (“effort” follows the cross-provider convention — OpenAI reasoning_effort, Anthropic output_config.effort). On adaptive-reasoning models, off degrades to the model’s minimum reasoning effort rather than fully disabling thinking. max meaningfully increases per-run token spend.

Maximum agentic turns per run. Optional at every tier; when unset everywhere, the Claude provider action falls back to 50.

The cut-point: the branch that pipeline branches are created from. Default: main

When integration_branch is unset, base_branch also serves as the PR target.

The PR target: where the final pipeline PR is opened. Default: unset (falls back to base_branch).

Exists for fork workflows: cut from the upstream base_branch, land PRs on a fork-local branch. The invariant merge-base(pipeline-branch, integration_branch) == base_branch always holds.

Method for auto-merging task/fix PRs into the pipeline branch. Default: auto

ValueDescription
autoProbe the repository’s allowed methods, preferring mergesquashrebase
merge / squash / rebaseForce one method

auto keeps autoducks working on squash-only or rebase-only repos without configuration.

Controls how the Maestro dispatches tasks from the tactical plan.

The dispatch topology: waves or sequential. Default: waves

ValueBehavior
wavesDispatch every unfinished task in the current wave to its own Developer in parallel; advance to the next wave only once the whole wave is done
sequentialDispatch one task at a time, in dependency order — wave grouping in the tactical plan is ignored, and only one Developer runs at a time

This is the lowest-priority tier in the topology resolution order — a mode: directive on the triggering comment or a Mode:* label on the issue both take precedence over it. See Maestro → Topology for the full resolution order.

Controls the Reviewer’s GitHub Check-run and whether it gates merges. See Reviewer → Required check.

When true, scripts/setup.sh (run with repo-admin gh credentials) creates a branch ruleset requiring the reviewer’s check on the integration/base branch, so a request-changes verdict blocks the merge. Default: false (the Reviewer still emits the check on final PRs, but nothing is enforced).

The Check-run name emitted on final PRs and required by the ruleset — kept in one place so the two never drift. Default: Autoducks: Reviewer.

Configures the Developer’s build-layer verification loop: automated checks (lint, build, test, or anything else scriptable) that run against the agent’s own changes before a task PR ever opens. Optional — the whole block, and every field in it, can be omitted; checks are disabled by default. See Developer → Verification loop for the runtime behavior this config drives.

Turns the loop on. Default: false

Checks only actually run when enabled is true and there’s something to run — at least one entry in commands, or git_hooks: true with a discoverable hook. enabled: true with neither is a no-op.

An optional shell command that prepares the toolchain before any check runs (installing dependencies, building, starting a service, etc.). Default: "" (no setup step)

An ordered array of { "name": "...", "run": "..." } checks, each run from the repository root via bash -c "<run>". They run in array order; the first non-zero exit stops the loop right there (later commands don’t run). Default: []

{
"checks": {
"commands": [
{ "name": "lint", "run": "npm run lint" },
{ "name": "test", "run": "npm test" }
]
}
}

name is optional (falls back to "check") and is only used to label the failure in the feedback comment; run is required.

When true, probes the repository for an existing pre-commit setup and, if one is found, runs it as an implicit first check (before commands, in this order): pre-commit run --all-files (when .pre-commit-config.yaml exists and pre-commit is on PATH), else an executable .githooks/pre-commit, else an executable .git/hooks/pre-commit. Default: false

If none of those are discoverable, git_hooks: true degrades to a no-op (with a workflow warning) rather than failing the run — a missing hook config never wedges every task.

Caps how many additional LLM attempts the loop will dispatch against a failing check before giving up. Default: 3. Clamped to 110; a non-numeric or out-of-range value falls back to the default.

The loop runs after the Developer’s LLM step has committed its changes, and before the task PR is opened:

  1. setup runs once (if set); a non-zero exit is an infra failure — see the caveat above.
  2. The git_hooks-derived check (if any) runs, then commands in order. The first failure stops the loop.
  3. All checks pass → the loop clears any stale feedback comment and the run proceeds to push + open the PR as normal.
  4. A check fails and iterations remain (iteration < max_iterations) → the branch is pushed as WIP (resumable, no PR yet), a marker-anchored comment on the task issue is created or updated in place with the failing output, and the Developer re-dispatches itself on the same task with the iteration counter incremented — the same LLM prompt, plus the prior failure appended as context.
  5. A check fails and iterations are exhausted (iteration >= max_iterations) → the loop gives up: the branch is pushed with its last attempt preserved, the run fails with category check_failed, and /fix on the task resumes from that branch.

A setup (infra) failure never consumes an iteration — only commands/git_hooks failures do.

Configuration for the Product Owner (/triage and /merge) — the only agent that also runs unattended, on a schedule.

Whether the schedule sweep fires at all. Default: true

Setting this to false removes the schedule: trigger from the baked workflow entirely — /triage, /merge, and workflow_dispatch keep working, only the timer is gone. Like the namespace and custom trigger aliases, this is baked into the workflow YAML and requires bash scripts/update-triggers.sh after changing it.

The cron expression for the backlog sweep. Default: "0 9 * * *" (09:00 UTC daily)

Also baked into the workflow by scripts/update-triggers.sh — GitHub Actions’ on.schedule cron is static YAML and can’t read config at run time.

Which storage /triage uses for priorities: auto, project, labels, or off. Default: auto

ValueBehavior
autoProbes for a reachable Projects v2 priority field; uses project if found, else falls back to labels
projectTrusts the config — always targets the repo’s Projects v2 board, no probing
labelsTrusts the config — always uses the named Priority:* labels, no probing
offPriority assignment disabled entirely; /triage still runs duplicate detection

Pins the project backend to a specific Projects v2 board by number, when a repository has more than one linked. Default: null (use the first project linked to the repository)

The Projects v2 single-select field name that represents priority, matched case-insensitively. Default: "Priority"

Whether /triage proposes and applies duplicate-issue closes at all. Default: true

Only takes effect on a full backlog sweep — a scoped single-issue /triage never has anything to compare against, regardless of this setting.

Caps how many duplicates a single /triage sweep can close, to bound the blast radius of a bad LLM proposal. Default: 5

Groups are kept in the order proposed, greedily, until the next group would exceed the cap — the rest are dropped and reported in the job summary, not silently discarded.

The minimum confidence (high, medium, low) a proposed duplicate group must carry to be applied. Default: "high"

Caps how many open issues a full backlog sweep pulls into the inbox. Default: 100

When the open backlog exceeds this, the run is bounded and the excess is reported in the job summary rather than silently dropped.

Whether /triage classifies un-classified issues by applying the Bug/Feature label. The Architect remains the authoritative classifier and overrides the label if its design concludes differently. false disables triage classification entirely; /triage still assigns priorities and proposes duplicates.

Per-team alias verbs, keyed by agent (architect, engineer, execute, fix, revert, close, review, triage, merge):

{
"triggers": {
"engineer": ["blueprint"],
"execute": ["ship"]
}
}

Rules: lowercase [a-z0-9-]+, no collision with built-in verbs/aliases, unique across agents. Aliases resolve to the canonical verb everywhere — routing, #auto: chains, and cascade included.

Like the namespace, aliases must be baked into the workflow guards:

Terminal window
bash scripts/update-triggers.sh

The script validates the whole block first (a bad alias is a hard error, never baked), regenerates each guard wholesale (idempotent — byte-identical when nothing changed), and mirrors .github/workflows/.autoducks/runtimes/github-actions/.

Each agent can have its own .autoducks/agents/<agent>/defaults.json:

{
"model": "claude-opus-4-8",
"effort": "high"
}

Agent directories: architect, engineer, maestro, developer, fix, revert, close, reviewer, product.

Priority (highest to lowest):

  1. Directive argument in the comment (model:, effort:, turns:)
  2. Agent-level defaults.json
  3. Global .autoducks/autoducks.json
  4. Provider action default — applies only to max_turns (50)

Controls which pieces of issue/PR/design context each agent is fed, keyed by agent (architect, engineer, developer, reviewer). Optional — omit the whole block, or any agent within it, to use that agent’s built-in default manifest (see Default manifests below).

{
"context": {
"developer": {
"parts": ["issue_title", "issue_description", "prior_feedback", "issue_metadata"]
}
}
}

An allow-list, not an additive list: when parts is present, it entirely replaces that agent’s default manifest rather than adding to it. An explicit "parts": [] is a deliberate “select nothing” and is honored as-is — that’s distinct from omitting parts altogether, which falls back to the default manifest.

Every requested part is validated against the availability matrix for that agent. An unknown part ID, or one not available to that agent, aborts the run (exit 1) with a fix hint pointing back at this config — never a silent no-op.

Part IDSourceArchitectEngineerDeveloperReviewer
issue_titleIssue title
issue_descriptionIssue body
issue_commentsLast 20 issue comments
issue_metadataLabels, type, author
design.problem_statementDesign-section marker extract
design.proposed_solutionDesign-section marker extract
design.technical_designDesign-section marker extract
design.dependenciesDesign-section marker extract
design.constraintsDesign-section marker extract
design.out_of_scopeDesign-section marker extract
design.fullWhole design zone of the issue body
planTactical zone (plan YAML) of the issue body
task_titleTask issue’s title
task_descriptionTask issue’s body
prior_feedbackMarker-anchored check-failure comment (only materializes when ITERATION > 1)
task_criteriaAcceptance criteria of every task in the feature’s wave plan
pr_diffUnified diff of the PR
pr_metaPR title/base/head/state plus changed-file list
security_guidelinesRepository security guidelines file (see review.security_guidelines)

task_title/task_description are shaped identically to issue_title/issue_description — they’re distinct catalog IDs because the Developer resolves them against the task issue rather than the parent feature/bug issue.

The parts injected when context.<agent> (or its parts key) is absent — chosen to reproduce each agent’s pre-existing /tmp context files byte-for-byte:

AgentDefault parts
architectissue_title, issue_description, issue_comments
engineerissue_title, issue_description, issue_comments, design.full
developerissue_title, issue_description, prior_feedback
reviewerissue_title, issue_description, task_criteria, design.full, pr_diff, pr_meta, security_guidelines

Unlike every other part, the Architect’s issue_comments is turn-agnostic: whenever it’s selected, it’s materialized on the first pass and every revision — there’s no gate on iteration count or on whether feedback exists yet. In practice this means a reviewer’s feedback comment is visible to the Architect’s very next turn, not just a later resumed revision.

To opt out, list context.architect.parts explicitly without issue_comments:

{
"context": {
"architect": { "parts": ["issue_title", "issue_description"] }
}
}

The default context.reviewer manifest (see Default manifests) hands the Reviewer the entire design zone via design.full, plus task_criteria. Repositories that want the Reviewer judging strictly against the proposed solution and constraints — narrower context, fewer tokens, less surface for the LLM to over-index on unrelated design prose — can select the individual design.<section> parts instead:

{
"context": {
"reviewer": {
"parts": [
"issue_title",
"design.problem_statement",
"design.proposed_solution",
"design.constraints",
"design.out_of_scope",
"pr_diff",
"pr_meta",
"security_guidelines"
]
}
}
}

This is the manifest that ships in this repository’s own autoducks.json. Dropping issue_description and task_criteria from the selection means the Reviewer’s Plan conformance section (see Reviewer) judges the diff against the design’s proposed solution and constraints directly, rather than against a separately-tracked list of task acceptance criteria.

Design-section markers & the legacy full-design fallback

Section titled “Design-section markers & the legacy full-design fallback”

When the Architect finalizes a design, its issue body is wrapped in wire-format HTML-comment markers around each of the six canonical sections:

<!-- autoducks:design:problem_statement:begin -->
...
<!-- autoducks:design:problem_statement:end -->

(likewise for proposed_solution, technical_design, dependencies, constraints, and out_of_scope)

Downstream agents can select individual design.<section> parts to receive only the relevant slice of the design, or design.full for the entire design zone. Two precedence rules resolve conflicts between them, each logged as an ::notice:: when triggered:

  • design.full wins. Selecting both design.full and one or more design.<section> parts drops the individual sections — only the full zone is materialized.
  • Legacy fallback. If the issue body carries none of the section markers at all (e.g. a design authored before this feature existed), any selected design.<section> part degrades to the full design zone instead. No agent ever runs with an empty design because of a missing marker.

Settings for the Reviewer (/review). Optional — the whole block, and every field in it, can be omitted.

Path to a repository-specific security guidelines file, relative to the repository root. The Reviewer reads this file (via /tmp/security-guidelines.md, staged by pre.sh) and applies it with priority over its built-in baseline security checklist.

Default: .autoducks/security-guidelines.md

The file is entirely optional: if it’s absent at the configured path, the Reviewer degrades gracefully and falls back to the baseline checklist alone — this is not treated as an error. A copyable starter template ships at the default path; see the Reviewer’s security review section for what it covers.

This field is unrelated to the security block below — that block controls who can trigger agents, this one supplies review context for judging what the code does. See Security review vs. the Authorization Gate.

Whether a request-changes verdict automatically dispatches a headless /rework round instead of waiting for a human to run /rework//defer. Default: true.

{ "review": { "auto_rework": false } }

Caps how many automatic Review → Rework rounds auto_rework will run for a single PR before handing off to a human. Default: 3. Clamped to 110; a non-numeric or out-of-range value falls back to the default. Has no effect when auto_rework is false.

See Reviewer → Bounded auto-rework loop for the round-tracking and hand-off mechanics this drives.

Every trigger passes the Authorization Gate before any agent runs — before any reaction or status comment. The security block controls trusted author associations, allow/deny lists, per-agent overrides (keys: architect, engineer, execute, fix, revert, close, review, product, merge — the Maestro and Developer share the execute policy), and optional CODEOWNERS integration.

See the Security reference for the full threat model and schema.