# Configuration

Huskies is configured via three TOML files in your `.huskies/` directory. All files are created by `huskies init` with sensible defaults.

## project.toml

Project-wide settings. Lives at `.huskies/project.toml`.

Key | Type | Default | Description
--- | --- | --- | ---
default_qa | string | `"server"` | Default QA mode. One of `"server"` (automated gate run), `"agent"` (spawn a QA agent), or `"human"` (manual approval).
default_coder_model | string | `"sonnet"` | Default model for coder agents. Only agents matching this model are auto-assigned. Use `"opus"` on individual stories for complex tasks.
max_coders | integer | `3` | Maximum concurrent coder agents. Stories wait in `2_current/` when all slots are full.
max_retries | integer | `3` | Maximum retries per story per pipeline stage before marking it as blocked. Set to `0` to disable.
base_branch | string | auto-detected | Base branch for merges and agent prompts. When unset, huskies reads the current HEAD branch.
rate_limit_notifications | bool | `false` | Send chat notifications when API soft rate limits are hit. Hard blocks and story-blocked notifications are always sent.
timezone | string | `"UTC"` | IANA timezone for timer scheduling (e.g. `"Europe/London"`, `"America/New_York"`). Timer HH:MM inputs are interpreted in this timezone.

### Component setup

The `[[component]]` sections define how to build and verify each part of your project. The server runs setup commands before accepting a story's QA and teardown commands after merging.

    [[component]]
    name = "frontend"
    path = "frontend"
    setup = ["npm ci", "npm run build"]
    teardown = []

    [[component]]
    name = "server"
    path = "."
    setup = ["mkdir -p frontend/dist", "cargo check"]
    teardown = []

### Story front matter overrides

Individual stories can override project defaults using YAML front matter:

    ---
    name: "My Complex Story"
    qa: agent        # override default_qa
    agent: opus      # use the opus coder agent
    ---

## agents.toml

Agent definitions. Lives at `.huskies/agents.toml`. Each `[[agent]]` block defines one agent slot.

Key | Description
--- | ---
name | Unique identifier for this agent slot (e.g. `"coder-1"`, `"qa"`).
stage | Pipeline stage this agent handles. One of `"coder"`, `"qa"`, or `"mergemaster"`.
role | Human-readable description of the agent's responsibilities (shown in status output).
model | Claude model to use. One of `"sonnet"` (claude-sonnet-4-6) or `"opus"` (claude-opus-4-6).
max_turns | Maximum conversation turns before the agent is forcefully stopped.
max_budget_usd | Maximum API spend in USD before the agent is stopped.
prompt | The initial user-turn prompt sent to the agent. Supports template variables (see below).
system_prompt | The system prompt sent to the agent session.

### Template variables

The following variables are interpolated into `prompt` and `system_prompt` at agent start time:

Variable | Description
--- | ---
{{story_id}} | The story's ID slug (e.g. `42_story_add_login`).
{{worktree_path}} | Absolute path to the agent's git worktree.
{{base_branch}} | The base branch name from `project.toml`.

### Example: adding an opus coder

    [[agent]]
    name = "coder-opus"
    stage = "coder"
    role = "Senior engineer for complex tasks."
    model = "opus"
    max_turns = 80
    max_budget_usd = 20.00
    prompt = "You are working on story {{story_id}} ..."
    system_prompt = "You are a senior full-stack engineer ..."

To use this agent for a specific story, add `agent: opus` to the story's front matter, or run `start <number> opus` in chat.

## Project-local agent prompt (`.huskies/AGENT.md`)

Place a file at `.huskies/AGENT.md` in your project root to append project-specific guidance to every agent's initial prompt at spawn time.

### How it works

- Huskies reads `.huskies/AGENT.md` each time an agent is spawned — no caching, no restart required.
- The file content is appended after the baked-in agent prompt, so project guidance refines core instructions without overriding them.
- Applies to all agent roles: coder, QA, mergemaster, and supervisor.
- If the file is missing or empty, agents spawn normally — no warnings, no errors.
- When the file exists and is non-empty, a single `INFO` log line is emitted showing the file path and byte count.

### Ordering

1. Baked-in agent prompt (from `agents.toml` or `project.toml`)
2. Project-local content from `.huskies/AGENT.md`
3. Resume context (only on agent restart after a gate failure)

### Example

    # .huskies/AGENT.md

    ## Documentation
    Docs live in `website/docs/*.html`, not Markdown files.
    Edit the relevant .html file when a story asks for documentation.

    ## Quality gates
    Run `cargo clippy -- -D warnings` before committing. Zero warnings allowed.

Edit the file at any time — the next agent spawn picks up the latest content automatically.

## bot.toml

Chat transport configuration. Lives at `.huskies/bot.toml`. This file is gitignored as it contains credentials. Copy the appropriate example file to get started:

    cp .huskies/bot.toml.matrix.example .huskies/bot.toml

Only one transport can be active at a time. See the Chat transports guide for setup instructions for each platform.

### Common fields

Key | Description
--- | ---
enabled | Set to `true` to activate the bot. Set to `false` to disable without removing the file.
transport | Transport type: `"matrix"`, `"whatsapp"`, `"slack"`, or `"discord"`.
display_name | Optional. Bot display name in chat messages.
history_size | Optional. Maximum conversation turns to remember per room/user (default: 20).

## Gateway: aggregated chat stream

When running `huskies --gateway`, you can configure a single bot that receives pipeline notifications from all registered projects. Events are prefixed with `[project-name]` so you can tell them apart in one shared room.

The aggregated stream is configured entirely in the gateway's `.huskies/bot.toml` — no per-project bot config is required and no per-project files need to change when you add a new project to `projects.toml`.

### Enabling the aggregated stream

Add or edit `<gateway-config-dir>/.huskies/bot.toml` and set `enabled = true`. The gateway bot will automatically poll every project listed in `projects.toml` and forward events to the configured rooms.

    # <gateway-config-dir>/.huskies/bot.toml
    enabled = true
    transport = "matrix"
    homeserver = "https://matrix.example.com"
    username = "@gateway-bot:example.com"
    password = "secret"
    room_ids = ["!gateway-room:example.com"]
    allowed_users = ["@you:example.com"]

    # Gateway-specific: poll interval and on/off switch
    aggregated_notifications_poll_interval_secs = 5   # default
    aggregated_notifications_enabled = true            # default

### Aggregated stream settings

Key | Type | Default | Description
--- | --- | --- | ---
aggregated_notifications_enabled | bool | `true` | Set to `false` to disable the aggregated stream without disabling the gateway bot entirely. Per-project configs are never consulted.
aggregated_notifications_poll_interval_secs | integer | `5` | How often (in seconds) the gateway polls each project's `/api/events` endpoint. Lower values reduce notification latency.

### No-duplicate guarantee

Per-project bots and the gateway aggregated stream send to different rooms — they are independent. Events from a per-project bot go to that project's rooms; events from the gateway stream go to the gateway rooms. The same event will never appear twice in either room.

### Unreachable projects

If a per-project server is temporarily unreachable, the gateway logs a warning and skips that project for the current poll cycle. All other projects continue to deliver notifications normally. No configuration change is required — the poller retries on the next interval.

### Supported event types

The aggregated stream delivers the following event types, each prefixed with the project name:

- Stage transitions — story created, agent started, QA requested, QA approved/rejected, merge succeeded (all pipeline stage moves)
- Merge failures — merge failed with a reason
- Story blocked — story blocked after exceeding retry limit