Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chant
intent driven development

Introduction

Write specs in markdown. Agents execute them. Everything is git-tracked.

chant add "Add user authentication"
chant work 001
# Agent implements the spec
# Changes committed automatically

Documentation

Getting Started

Core Concepts

Architecture

Guides

Reference

Enterprise

Building Documentation

This documentation is built using mdbook.

To build the documentation locally:

# Install mdbook if needed
cargo install mdbook

# Build the documentation
mdbook build docs

# Or serve with live reload
mdbook serve docs

The built documentation will be in docs/book/.

Installation

See the Installation Guide for detailed instructions.

Quick options:

  • Homebrew: brew tap lex00/tap && brew install chant
  • Cargo: cargo install --git https://github.com/lex00/chant
  • Direct download: Visit the Releases page

Chant
Intent Driven Development

Installation

Chant is available through multiple installation methods. Choose the one that works best for your environment.

Quick Install (Linux/macOS)

The fastest way to get started:

curl -fsSL https://github.com/lex00/chant/releases/latest/download/chant-linux-x86_64 -o chant
chmod +x chant
sudo mv chant /usr/local/bin/

For macOS, download the appropriate architecture:

# Intel/x86_64
curl -fsSL https://github.com/lex00/chant/releases/latest/download/chant-macos-x86_64 -o chant

# Apple Silicon (aarch64)
curl -fsSL https://github.com/lex00/chant/releases/latest/download/chant-macos-aarch64 -o chant

chmod +x chant
sudo mv chant /usr/local/bin/
codesign -f -s - /usr/local/bin/chant

Homebrew (macOS/Linux)

If you have Homebrew installed:

brew install lex00/tap/chant

Cargo (from source)

If you have Rust and Cargo installed:

cargo install --git https://github.com/lex00/chant

Download from Releases

Visit the Releases page to download pre-built binaries for your platform:

  • Linux x86_64 - chant-linux-x86_64
  • macOS Intel - chant-macos-x86_64
  • macOS Apple Silicon - chant-macos-aarch64
  • Windows - chant-windows-x86_64.exe

After downloading, make it executable and move it to your PATH:

chmod +x chant
sudo mv chant /usr/local/bin/
# On macOS, re-sign the binary to prevent SIGKILL
codesign -f -s - /usr/local/bin/chant

Build from Source

To build Chant from source, you’ll need Rust and Git:

git clone https://github.com/lex00/chant
cd chant
cargo build --release

The binary will be available at target/release/chant. You can then move it to your PATH:

sudo mv target/release/chant /usr/local/bin/
# On macOS, re-sign the binary to prevent SIGKILL
codesign -f -s - /usr/local/bin/chant

Verify Installation

After installation, verify that chant is working:

chant --version

You should see the version number printed.

Getting Started After Installation

Once installed, initialize chant in your project:

chant init

Then proceed to the Quickstart guide to learn how to create and execute your first spec.

Platform Support

PlatformStatusArchitecturePackage Manager
Linux✅ Supportedx86_64Homebrew, Cargo
macOS✅ Supportedx86_64, aarch64 (Apple Silicon)Homebrew, Cargo
Windows✅ Supportedx86_64Direct Download

Troubleshooting

Binary not found after installation

If you get “command not found: chant” after installing, ensure that /usr/local/bin is in your PATH:

echo $PATH

If /usr/local/bin is not listed, you may need to add it to your shell configuration (.bashrc, .zshrc, etc.):

export PATH="/usr/local/bin:$PATH"

Permission denied

If you get a “Permission denied” error when running chant, ensure the binary is executable:

chmod +x /usr/local/bin/chant

macOS process killed with SIGKILL (exit 137)

On macOS, if chant is killed immediately after running (exit code 137), the binary needs to be code-signed. This happens when macOS strips the code signature after copying the binary. Re-sign with an ad-hoc signature:

codesign -f -s - /usr/local/bin/chant

This is automatically handled by Homebrew and should be done after any manual installation or binary copy operation on macOS.

Cargo installation fails

If cargo install --git fails, ensure you have Rust installed:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Then try the installation again.

Next Steps

Shell Completion

Chant provides shell completion for bash, zsh, fish, and PowerShell. Tab completion helps you discover commands, flags, and options without memorizing them.

Quick Setup

Bash

# System-wide (requires root)
chant completion bash | sudo tee /etc/bash_completion.d/chant > /dev/null

# User-local
mkdir -p ~/.local/share/bash-completion/completions
chant completion bash > ~/.local/share/bash-completion/completions/chant

Then restart your shell or run:

source ~/.local/share/bash-completion/completions/chant

Zsh

# If using Oh My Zsh
chant completion zsh > ~/.oh-my-zsh/completions/_chant

# Standard Zsh (ensure completions dir is in fpath)
mkdir -p ~/.zsh/completions
chant completion zsh > ~/.zsh/completions/_chant

Add to your ~/.zshrc if the completions directory is not already in fpath:

fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit && compinit

Then restart your shell or run:

source ~/.zshrc

Fish

# Create completions directory if needed
mkdir -p ~/.config/fish/completions

# Generate completions
chant completion fish > ~/.config/fish/completions/chant.fish

Fish will automatically load the completions on next shell start.

PowerShell

# Add to your PowerShell profile
chant completion powershell >> $PROFILE

# Or create a separate file and source it
chant completion powershell > ~/.config/powershell/chant.ps1

Add to your $PROFILE if using a separate file:

. ~/.config/powershell/chant.ps1

What Gets Completed

Once installed, you can use tab completion for:

  • Commands: chant <TAB> shows all available commands
  • Flags: chant work --<TAB> shows available options for work
  • Subcommands: chant init <TAB> shows subcommand options

Verifying Installation

Test that completions are working:

# Type 'chant ' and press Tab
chant <TAB>

# Should show: add, approve, archive, cancel, cleanup, ...

Updating Completions

After upgrading chant, regenerate your completions to pick up new commands:

# Example for bash
chant completion bash > ~/.local/share/bash-completion/completions/chant

Troubleshooting

Completions not loading

  1. Verify the completion file exists:

    ls -la ~/.local/share/bash-completion/completions/chant  # bash
    ls -la ~/.zsh/completions/_chant                          # zsh
    ls -la ~/.config/fish/completions/chant.fish              # fish
    
  2. Check your shell’s completion system is enabled:

    • Bash: Ensure bash-completion package is installed
    • Zsh: Ensure compinit is called in ~/.zshrc
    • Fish: Completions load automatically
  3. Restart your shell completely (not just source the profile)

Zsh: “compdef: unknown command or service”

Add this to your ~/.zshrc before loading completions:

autoload -Uz compinit && compinit

Bash: completion script errors

Ensure you have bash-completion installed:

# Debian/Ubuntu
sudo apt install bash-completion

# macOS (Homebrew)
brew install bash-completion@2

Quick Start

Get chant working in 5 minutes. By the end, you’ll have an AI agent create a script for you.

Prerequisites

Step 1: Initialize a Project

Create a test project:

mkdir chant-quickstart && cd chant-quickstart
git init

Run the wizard:

chant init

The wizard asks:

  1. Project name - accept the default
  2. Provider - choose claude or kirocli
  3. Model - choose sonnet (fast and capable)
  4. Agent config - choose your CLI (Claude Code or Kiro)

Quick setup: Skip the wizard with flags:

# For Claude Code
chant init --provider claude --model sonnet --agent claude

# For Kiro CLI
chant init --provider kirocli --model sonnet --agent kiro

Step 2: Create a Spec

Add a simple task:

chant add "Create a hello.sh script that prints Hello World"

This creates a minimal spec. Check what lint thinks:

chant lint

You’ll see warnings like:

⚠ No acceptance criteria found
⚠ Description is too brief

Step 3: Improve the Spec

Open the spec file (shown in the chant add output) and add acceptance criteria:

chant show 001  # View the spec

Edit .chant/specs/[your-spec-id].md to look like this:

---
status: pending
---

# Create a hello.sh script that prints Hello World

Create a bash script that outputs a greeting.

## Acceptance Criteria

- [ ] Creates `hello.sh` in the project root
- [ ] Script is executable (`chmod +x`)
- [ ] Running `./hello.sh` prints "Hello World"
- [ ] Script includes a shebang line (`#!/bin/bash`)

Run lint again:

chant lint

Now it passes. The spec is ready for execution.

Step 4: Start Your Agent CLI

Open a new terminal in the same directory and start your AI CLI:

Claude Code:

claude

Kiro CLI:

kiro-cli-chat chat

Step 5: Use MCP Tools to Execute

Inside your agent CLI, the chant MCP tools are available. Try these commands:

Check project status:

Use chant_status to show the project status

You’ll see: 1 pending | 0 in_progress | 0 completed

View the spec:

Use chant_spec_get to show spec 001

This displays your spec with its acceptance criteria.

Start working on it:

Use chant_work_start to begin working on spec 001

The agent will:

  1. Read the spec and acceptance criteria
  2. Create hello.sh with the required content
  3. Make it executable
  4. Commit with message: chant(001): Create hello.sh script

Monitor progress:

Use chant_status to check progress

You’ll see: 0 pending | 0 in_progress | 1 completed

Step 6: Verify the Result

Back in your original terminal:

# Check the script exists
ls -la hello.sh

# Run it
./hello.sh
# Output: Hello World

# View the commit
git log -1 --oneline

# Check spec status
chant list

What Just Happened?

  1. chant init - Set up project config and MCP integration
  2. chant add - Created a spec (work intention)
  3. chant lint - Validated spec quality
  4. MCP tools - Let the agent discover and execute the spec
  5. Agent - Read the spec, wrote code, committed changes

The key insight: you defined the goal, the agent figured out how to achieve it.

Next Steps

Want to…Do this…
Add more specschant add "your task"
Run from CLIchant work 001
Run multiple specschant work --chain
See all commandschant --help

Learn More

  • Specs - How to write effective specs
  • Prompts - Customize agent behavior
  • Skills - Agent Skills open standard and custom skills
  • Providers - Configure different AI providers
  • MCP Tools - All available MCP operations

Philosophy

Intent based. Spec driven. Self bootstrapping.

What is Chant?

Chant is a spec execution platform for AI-assisted work. Write specs in markdown. Agents execute them.

Specs can drive:

  • Code — Implementation, configuration, infrastructure
  • Documentation — Docs that track the code they describe
  • Research — Analysis, synthesis, experiments

The Problem

Specifications don’t drive themselves:

  • Docs describe intent but can’t enforce it
  • Tickets track work but forget after closure
  • Code comments explain but don’t verify
  • AI agents execute but don’t persist

The result: Intent exists for a moment, then decays.

AI coding agents are powerful but stateless. Each session starts fresh:

  • No memory of previous work
  • No awareness of what’s done vs pending
  • No coordination between sessions
  • No discipline enforced

Developers end up:

  • Re-explaining context every session
  • Manually tracking what’s done
  • Copy-pasting between sessions
  • Getting inconsistent results

Self-Driving Specs

Specs that:

  • Execute — Agent invocation
  • Verify — Continuous checking
  • Detect drift — When reality diverges
  • Replay — Restore intent automatically

See how it works →

Development Workflow

Chant’s development follows the same pattern available to all users:

  1. Write specs in .chant/specs/
  2. Execute with chant work
  3. Changes are committed and tracked

The CLI binary is built on the same spec execution model used for any project.

Intent-First Development

Specifications are the source of truth.

ApproachSource of TruthProblem
Documentation-firstDocsRots as code changes
Code-firstCodeIntent buried in implementation
Ticket-firstTicketsClosed and forgotten
Intent-firstSpecsExecute, verify, persist

In intent-first development:

  • Specs are executable, not just readable
  • Completion means “verified”, not “closed”
  • Drift from intent is detected, not ignored
  • Replay restores intent without manual work

The spec IS the work.

Core Value

Markdown IS the UI

Specs are markdown files with YAML frontmatter. No special viewer needed.

# .chant/specs/2026-01-22-001-x7m.md
---
status: pending
depends_on: []
---

# Add authentication

## Acceptance Criteria
- [ ] JWT tokens work
- [ ] 401 on invalid token

Filename is the ID.

  • Edit in any editor (VS Code, vim, GitHub web UI)
  • Git diffs are the changelog
  • PRs are the review interface
  • Search with grep

Value: Zero friction to view, edit, understand.

Index is Optimization

The index makes queries fast. Delete and rebuild from markdown anytime.

.chant/
  specs/*.md      ← Source of truth (git-tracked)
  .store/         ← Derived index (gitignored)

Specs Drive Agents

Each spec is complete:

  • Title (what)
  • Description (context)
  • Acceptance criteria (done when)
  • Target files (where)

Agent prompt: “Implement this spec.”

Value: Consistent, reproducible agent behavior.

Parallel Execution

Split a spec into a group, execute members in parallel:

chant split 001
chant work 001 --parallel

Each agent in isolated worktree. No conflicts.

Value: Faster completion of complex work.

Git-Native

  • Branches per spec (optional)
  • Commits tracked in frontmatter
  • PRs created automatically (optional)

Value: Fits existing workflow.

Crash Recovery

PID locks track running agents. Stale locks detected.

$ chant work 001
Warning: Stale lock from crashed session
Recover and continue? [y/N]

Value: Resilient to failures.

Prompts are Configuration

Agent behavior defined in markdown, not code.

.chant/prompts/
  standard.md     ← Default execution
  minimal.md      ← Quick fixes
  tdd.md          ← Test-driven
  split.md        ← Decompose specs

Customize to match your workflow.

Prompts are Universal

The same prompts work for:

  • Chant building itself
  • Customers building their projects
  • Code, documentation, and research specs

This universality is why self-bootstrapping works. The prompts don’t know they’re building chant—they just implement specs.

Core Principles

Unix Philosophy

Do one thing well. Compose with standard tools.

  • Is: Spec tracking + execution discipline
  • Is not: Project management, visualization, reporting
  • Uses: Git for sync, editors for viewing, grep for search

Who It’s For

Solo Developers / Small Teams

  • Persistent spec tracking
  • Execution discipline
  • Simple tooling

Documentation Maintainers

The problem: Docs rot as code changes.

With Chant:

  • Doc specs have originorigin: field links docs to source code
  • Drift detection — Know when code changed since docs were written
  • Replay — Re-run spec to update docs automatically
---
type: documentation
origin: [src/auth/*.go]
target_files: [docs/auth.md]
---

Researchers

The problem: Analysis goes stale when data changes.

With Chant:

  • Research specs have origin — Analysis linked to data files
  • Reproducibility — Every analysis step recorded
  • Provenance — Know exactly what data produced what findings
---
type: research
origin: [data/survey.csv]
target_files: [findings/analysis.md]
---

Enterprise Developers (Silent Mode)

Working in rigid environments:

  • Personal AI workflow without changing shared repo
  • chant init --silent keeps .chant/ local only
  • No trace in official codebase

Enterprise Teams

  • Derived frontmatter — Auto-populate fields from branch/path patterns
  • Integration with existing conventions
# config.yaml
enterprise:
  derived:
    sprint:
      from: branch
      pattern: "sprint/(\\d{4}-Q\\d-W\\d)"
    team:
      from: path
      pattern: "teams/(\\w+)/"

Not for:

  • Non-technical users
  • Teams wanting GUI/dashboards

What Chant Replaces

BeforeAfter
Mental tracking.chant/specs/
Copy-paste contextSpec file IS the context
Manual branch managementWorktrees + chant merge
Hope for the bestAcceptance criteria + linting
Lost work on crashPID locks + recovery

Specs

Spec Types at a Glance

TypeUse ForExample
codeFeatures, bugs, refactoringImplement JWT auth
taskManual work, prompts, configCreate documentation prompt
driverCoordinate multiple specsAuth system (with .1, .2, .3 members)
groupAlias for driverSame as driver
documentationGenerate docs from codeDocument auth module
researchAnalysis, synthesisAnalyze survey data
# Code spec - implement something
---
type: code
target_files: [src/auth.rs]
---

# Task spec - manual/config work
---
type: task
target_files: [.chant/prompts/doc.md]
---

# Driver spec - coordinates members
---
type: driver
---
# (has 001.1.md, 001.2.md members)

# Documentation spec - docs from code
---
type: documentation
tracks: [src/auth/*.rs]
target_files: [docs/auth.md]
---

# Research spec - analysis/synthesis
---
type: research
origin: [data/metrics.csv]
target_files: [analysis/report.md]
---

See spec-types.md for detailed documentation of each type.

Unified Model

No separate “epic” type. Specs can be split into groups. A spec with group members is a driver.

.chant/specs/
  2026-01-22-001-x7m.md       ← Driver (has members)
  2026-01-22-001-x7m.1.md     ← Member
  2026-01-22-001-x7m.2.md     ← Member
  2026-01-22-001-x7m.2.1.md   ← Nested group member

Filename is the ID

No id field in frontmatter. The filename (without .md) is the identifier.

See ids.md for format details.

Frontmatter Schema

See Schema & Validation for validation rules and linting.

---
# No id field - filename is the ID

# Type (determines behavior)
type: code                     # code | documentation | research

# State
status: pending                # pending | in_progress | completed | failed
depends_on:                    # Spec IDs that must complete first
  - 2026-01-22-001-x7m

# Organization
labels: [auth, feature]        # Free-form tags
target_files:                  # Files this spec creates/modifies
  - src/auth/middleware.go

# Context (reference docs for any type)
context:                       # Docs injected as background for agent
  - docs/api-design.md

# Type-specific fields (see spec-types.md)
tracks:                        # documentation: source code to monitor
informed_by:                   # research: materials to synthesize
origin:                        # research: input data (triggers drift)

# Git (populated on completion)
branch: chant/2026-01-22-002-q2n
commit: a1b2c3d4
completed_at: 2026-01-22T15:30:00Z
model: claude-opus-4-5            # AI model that executed the spec

# Execution
prompt: standard               # Optional, defaults to config

# Verification
last_verified: 2026-01-22T15:00:00Z  # Timestamp of last verification
verification_status: passed   # passed | partial | failed (after verify)
verification_failures:        # List of failed acceptance criteria
  - "Criterion description"

# Re-execution tracking
replayed_at: 2026-01-22T16:00:00Z    # Timestamp of last re-execution
replay_count: 1               # Number of times re-executed
original_completed_at: 2026-01-15T14:30:00Z  # Preserved from first completion
---

See spec-types.md for field usage by type.

Spec States

pending → in_progress → completed
                      ↘ failed
blocked
cancelled

See Lifecycle for detailed status documentation, transitions, and approval workflow.

Drift Detection

Documentation and research specs declare their input files. When these change after completion, drift is detected.

# Documentation: tracks source code
---
type: documentation
tracks:
  - src/auth/*.rs
target_files:
  - docs/auth.md
---

# Research: origin data + informed_by materials
---
type: research
origin:
  - data/metrics.csv
informed_by:
  - docs/methodology.md
target_files:
  - analysis/report.md
---

Drift by Type

TypeFieldDrifts When
codeAcceptance criteria fail
documentationtracks:Tracked source code changes
researchorigin:, informed_by:Input files change

Checking for Drift

Use chant verify to re-check acceptance criteria and detect drift:

$ chant verify 001
Verifying spec 001: Add rate limiting

Checking acceptance criteria...
  ✓ Rate limiter middleware exists
  ✓ Returns 429 with Retry-After header
  ✓ Tests verify rate limiting works

Spec 001: VERIFIED

For documentation and research specs, use chant drift to detect input file changes:

$ chant drift
⚠ Drifted Specs (inputs changed)
  2026-01-24-005-abc (documentation)
    src/api/handler.rs (modified: 2026-01-25)

$ chant work 005 --skip-criteria  # Re-run to update documentation

See autonomy.md for more on drift detection.

Readiness

A spec is ready when:

  1. Status is pending
  2. All depends_on specs are completed
  3. No group members exist OR all members are completed

Spec Groups

Determined by filename, not frontmatter:

2026-01-22-001-x7m.md      ← Driver
2026-01-22-001-x7m.1.md    ← Member (driver = 2026-01-22-001-x7m)

No driver field needed. The .N suffix establishes group membership.

A driver with incomplete members cannot be marked complete. See groups.md.

Spec Cancellation

Cancel a spec by marking it cancelled (soft-delete), or permanently delete it with --delete.

Cancelling a Spec

$ chant cancel 001                       # Cancel with confirmation (soft-delete)
$ chant cancel 001 --yes                 # Skip confirmation
$ chant cancel 001 --dry-run             # Preview what would be cancelled
$ chant cancel 001 --skip-checks         # Skip safety checks
$ chant cancel 001 --delete              # Permanently delete (hard delete)
$ chant cancel 001 --delete --cascade    # Delete driver and all members
$ chant cancel 001 --delete --delete-branch  # Delete spec and associated branch

Safety Checks (default, skipped with --skip-checks):

  • Cannot cancel specs that are in-progress or failed
  • Cannot cancel member specs (cancel the driver instead)
  • Cannot cancel already-cancelled specs
  • Warns if other specs depend on this spec

What Happens When Cancelled (Soft-Delete)

  1. Spec status changed to Cancelled in frontmatter
  2. File is preserved in .chant/specs/
  3. Cancelled specs excluded from chant list and chant work
  4. Can still view with chant show or chant list --status cancelled
  5. All git history preserved

What Happens When Deleted (Hard Delete with --delete)

  1. Permanently removes spec file from .chant/specs/
  2. Removes associated log file from .chant/logs/
  3. Removes worktree artifacts
  4. With --cascade: deletes driver and all member specs
  5. With --delete-branch: removes associated git branch

Cancelled State

---
status: cancelled
---

Difference from Delete

  • cancel: Changes status to Cancelled, preserves files and history
  • delete: Removes spec file, logs, and worktree artifacts

Re-opening Cancelled Specs

To resume work on a cancelled spec, manually edit the status back to pending:

# Edit the spec file and change status: cancelled to status: pending
chant work 001  # Resume execution

Spec Amendments

Specs are append-only by default. Prefer:

  • Cancel and create new spec
  • Add member specs for new requirements
  • Create follow-up spec

Editing Specs

Edit spec files directly in your text editor - they’re just markdown files.

Safe edits (always allowed):

  • Description clarification
  • Labels
  • Notes

Risky edits (use caution for in-progress/completed specs):

  • Target files (may not match actual work)
  • Dependencies (may invalidate completion)

Amendment Log

Track changes to spec after creation:

---
status: completed
amendments:
  - at: 2026-01-22T14:00:00Z
    by: alex
    field: description
    reason: "Clarified scope"
---

Splitting Specs

If a spec grows too large, use chant split to break it into member specs:

$ chant split 001
Creating member specs from 001...

Found sections:
  1. "Implement auth middleware"
  2. "Add JWT validation"
  3. "Write tests"

Create as members? [y/N]

Created:
  001.1 - Implement auth middleware
  001.2 - Add JWT validation
  001.3 - Write tests

Driver 001 status: pending (waiting for members)

Acceptance Criteria Validation

Chant validates that all acceptance criteria checkboxes are checked before marking a spec complete. This validation happens after the agent exits.

## Acceptance Criteria

- [x] Implement login endpoint
- [ ] Add rate limiting        <- Unchecked!
- [x] Write tests

If unchecked boxes exist, chant shows a warning and fails:

⚠ Found 1 unchecked acceptance criterion.
Use --force to skip this validation.
error: Cannot complete spec with 1 unchecked acceptance criteria

The spec is marked as failed until all criteria are checked.

Skipping Validation

Use --force to complete despite unchecked boxes:

chant work 001 --force

Best Practice

Agents should check off criteria as they complete each item:

  • Change - [ ] to - [x] in the spec file
  • This creates a clear record of completion

Agent Output

After successful completion, chant appends the agent’s output to the spec file. This creates an audit trail of agent work.

Format

The output is appended as a new section with timestamp:

## Agent Output

2026-01-24T15:30:00Z

\`\`\`
Done! I've implemented the authentication middleware.

Summary:
- Added JWT validation in src/auth/middleware.go
- Added tests in src/auth/middleware_test.go
- All 5 tests pass
\`\`\`

Multiple Runs

Each replay with --force appends a new output section:

## Agent Output

2026-01-24T15:30:00Z

\`\`\`
[first run output]
\`\`\`

## Agent Output

2026-01-24T16:00:00Z

\`\`\`
[replay output - agent detected implementation exists]
\`\`\`

This allows tracking how the agent behaved across multiple executions.

Truncation

Outputs longer than 5000 characters are truncated with a note indicating the truncation. This prevents spec files from growing excessively large while still capturing the essential information about what the agent accomplished.

Model Tagging

When a spec completes, chant records the AI model used in the frontmatter:

---
status: completed
commit: abc1234
model: claude-opus-4-5
---

How Model is Detected

The model is detected from environment variables at execution time. Chant checks these variables in order:

  1. CHANT_MODEL - chant-specific override
  2. ANTHROPIC_MODEL - standard Anthropic environment variable

The first non-empty value found is recorded. If neither is set, the model field is omitted from the frontmatter.

Possible Values

The model field contains whatever value is in the environment variable. Common values include:

  • claude-opus-4-5 - Claude Opus 4.5
  • claude-sonnet-4 - Claude Sonnet 4
  • claude-haiku-3-5 - Claude Haiku 3.5

The value is recorded as-is without validation, so it may also contain version suffixes or custom identifiers depending on your setup.

Use Cases

  • Cost tracking: See which models completed which specs to understand costs
  • Debugging: Identify model-specific behavior differences when issues arise
  • Auditing: Know which AI version produced each change for compliance or review

Spec Types

Chant supports six spec types, each with different behaviors for execution, verification, and drift detection.

Overview

TypePurposeDrift Trigger
codeImplement features, fix bugsAcceptance criteria fail
taskManual work, prompts, configAcceptance criteria fail
driverCoordinate multiple specsMembers incomplete
groupAlias for driverMembers incomplete
documentationGenerate docs from sourcetracks: files change
researchAnalysis, synthesis, findingsorigin: or informed_by: files change

Field Reference

FieldType(s)Triggers Drift?Purpose
context:allNoBackground reading for the agent
tracks:documentationYesSource code being documented
informed_by:researchYesMaterials to synthesize
origin:researchYesInput data for analysis
target_files:allNoOutput files to create/modify

Code Specs

The default type. Creates or modifies code, configuration, infrastructure.

---
type: code
context:                          # Reference docs for background
  - docs/api-design.md
target_files:
  - src/auth/middleware.rs
  - src/auth/jwt.rs
---
# Add JWT authentication

## Acceptance Criteria
- [ ] JWT tokens validated
- [ ] 401 on invalid token
- [ ] Token refresh works

Execution: Agent reads context, implements code changes. Verification: Acceptance criteria checked. Drift: When criteria no longer pass.

Task Specs

For manual work, creating prompts, configuration, or anything that doesn’t produce code.

---
type: task
target_files:
  - .chant/prompts/documentation.md
---
# Create documentation prompt

Create a prompt that guides agents to generate documentation from tracked source files.

## Acceptance Criteria
- [ ] Prompt file created
- [ ] Prompt explains process
- [ ] Prompt is actionable

Execution: Agent performs the task, creates artifacts. Verification: Acceptance criteria checked. Drift: When criteria no longer pass.

Task vs Code

Aspectcodetask
OutputSource code, testsPrompts, config, docs
TestsUsually runs testsUsually no tests
BuildMay require buildNo build needed

Driver Specs

Coordinate multiple related specs. A spec becomes a driver when it has member specs (files with .N suffix).

---
type: driver
status: pending
---
# Implement authentication system

This driver coordinates the auth implementation.

## Members
- `.1` - Add JWT middleware
- `.2` - Add login endpoint
- `.3` - Add tests

File structure:

2026-01-22-001-x7m.md      ← Driver
2026-01-22-001-x7m.1.md    ← Member 1
2026-01-22-001-x7m.2.md    ← Member 2
2026-01-22-001-x7m.3.md    ← Member 3

Execution: Driver waits for all members to complete, then auto-completes. Verification: All members must be completed. Drift: When any member becomes incomplete.

Driver Behavior

  • Driver cannot complete until all members complete
  • Members execute in order (.1 before .2)
  • Starting a member marks driver as in_progress
  • Driver auto-completes when last member completes

Group Specs

group is an alias for driver. Use whichever term feels more natural.

---
type: group
---
# Feature: User profiles

## Members
- `.1` - Add profile model
- `.2` - Add profile API
- `.3` - Add profile UI

The context: Field

For code specs, context: provides reference material:

---
type: code
context:
  - docs/api-design.md#error-handling
  - docs/security-policy.md
---
# Add error responses

The agent sees the referenced doc content while implementing. This is background information, not work input.

Documentation Specs

Creates or updates documentation based on source code.

---
type: documentation
tracks:                           # Source code to document and monitor
  - src/auth/*.rs
target_files:
  - docs/authentication.md
---
# Document authentication module

## Scope
- All auth endpoints
- JWT flow
- Error codes

## Acceptance Criteria
- [ ] All public functions documented
- [ ] Usage examples included
- [ ] Architecture diagram current

Execution: Agent reads tracked files, writes documentation. Verification: Tracked files haven’t changed since completion. Drift: When tracked source code changes after doc is complete.

The tracks: Field

The tracks: field creates a living link between docs and code:

Code changes → triggers → Doc spec drift → re-verify → update docs

When src/auth/*.rs changes after doc completion:

$ chant verify 001
Spec 001 (documentation): DRIFT

Tracked files changed since completion:
  - src/auth/middleware.rs (modified 2026-01-25)
  - src/auth/token.rs (added 2026-01-25)

Recommendation: Re-run spec to update docs

Documentation Use Cases

Use Casetracks:target_files:
API referencesrc/api/**/*.rsdocs/api.md
Architecture docssrc/, Cargo.tomldocs/architecture.md
Module docssrc/auth/*.rsdocs/auth.md
READMEsrc/lib.rsREADME.md

Research Specs

Creates analysis, synthesis, or findings. Supports two patterns: synthesis (reading materials) and analysis (processing data). Both can be combined.

Pattern: Synthesis

Read and synthesize materials into findings:

---
type: research
prompt: research-synthesis
informed_by:                      # Materials to read and synthesize
  - papers/smith2025.pdf
  - papers/jones2024.pdf
  - docs/prior-analysis.md
target_files:
  - findings/lit-review.md
---
# Synthesize ML efficiency papers

## Research Questions
- [ ] What are the main efficiency approaches?
- [ ] Which show >50% improvement?
- [ ] What are the trade-offs?

## Acceptance Criteria
- [ ] All papers summarized
- [ ] Comparison table created
- [ ] Research gaps identified

Pattern: Analysis

Process data files into reports:

---
type: research
prompt: research-analysis
origin:                           # Input data to analyze
  - data/survey-2026.csv
target_files:
  - analysis/survey-results.md
  - analysis/figures/correlation.png
---
# Analyze survey responses

## Methodology
- Descriptive statistics
- Correlation analysis
- Theme coding for open responses

## Acceptance Criteria
- [ ] Analysis script runs without error
- [ ] All columns analyzed
- [ ] Statistical significance noted
- [ ] Visualizations generated

Combined Pattern

Research specs can use both informed_by: AND origin::

---
type: research
informed_by:                      # Prior work to build on
  - papers/methodology.pdf
  - docs/previous-analysis.md
origin:                           # Data to analyze
  - data/experiment-results.csv
target_files:
  - findings/analysis.md
---
# Analyze experiment using established methodology

Execution: Agent reads informed_by: and origin: files, performs analysis/synthesis, writes findings. Verification: Input files haven’t changed since completion. Drift: When origin: OR informed_by: files change after completion.

Research Drift

Drift TypeTriggerDetection
Data driftorigin: files changedFile modification detected
Source driftinformed_by: files changedFile modification detected
Reproducibility driftCan’t replicate resultschant verify fails

Prompt Selection by Type

Override the default prompt per-spec:

---
type: research
prompt: research-analysis
---

Summary

ConceptCodeTaskDriver/GroupDocumentationResearch
PurposeImplement featuresManual workCoordinate specsDocument codeAnalyze/synthesize
Work inputCriteriaCriteriaMemberstracks:informed_by: / origin:
Drift triggerCriteria failCriteria failMembers incompletetracks: changesInput files change
Default promptstandardstandarddocumentationresearch-*

Implementation Status

The documentation and research spec types are implemented with:

  • Frontmatter fields: tracks:, informed_by:, origin:
  • Lint validation warnings for missing fields
  • Prompts: documentation, research-analysis, research-synthesis

Prompts

These prompts are available in .chant/prompts/:

PromptTypePurpose
documentationdocumentationGenerate/update docs from tracked code
research-synthesisresearchSynthesize materials into findings
research-analysisresearchAnalyze data, generate reports

Auto-selection:

  • type: documentationdocumentation prompt
  • type: research with origin:research-analysis prompt
  • type: research without origin:research-synthesis prompt

Prompts

A markdown file that defines agent behavior. Lives in .chant/prompts/.

# .chant/prompts/standard.md
---
name: standard
purpose: Default execution prompt
---

# Execute Spec

You are implementing a spec for {{project.name}}.

## Your Spec

**{{spec.title}}**

{{spec.description}}

## Instructions

1. **Read** the relevant code first
2. **Plan** your approach before coding
3. **Implement** the changes
4. **Verify** each acceptance criterion
5. **Commit** with message: `chant({{spec.id}}): <description>`

Template Variables

Prompts support variable substitution using {{variable}} syntax.

VariableDescription
{{project.name}}Project name from config
{{spec.id}}Full spec identifier
{{spec.title}}Spec title (first # heading)
{{spec.description}}Full spec body content
{{spec.target_files}}Target files from frontmatter
{{worktree.path}}Worktree path (parallel execution)
{{worktree.branch}}Branch name (parallel execution)

Note: Advanced templating ({{#if}}, {{#each}}) is not implemented. Only simple {{variable}} substitution is supported.

Built-in Prompts

PromptPurpose
bootstrap(Default) Minimal prompt that delegates to chant prep
standardRead → Plan → Implement → Verify → Commit
splitSplit driver into group members
documentWrite user-facing documentation
researchDeep root cause analysis for issues
merge-conflictResolve git conflicts during rebase

See Prompt Guide: Built-in Prompts for full examples and detailed explanations.

Prompt Selection

chant work 001                # Uses default prompt (bootstrap)
chant work 001 --prompt tdd   # Uses TDD prompt

Selection Order

  1. --prompt CLI flag (highest priority)
  2. prompt: in spec frontmatter
  3. defaults.prompt from config

Custom Prompts

Create custom prompts in .chant/prompts/:

# .chant/prompts/security-review.md
---
name: security-review
purpose: Security-focused code review
---

# Security Review

Review {{spec.target_files}} for security issues.

Use with: chant work 001 --prompt security-review


For comprehensive guidance, examples, and advanced techniques, see the Prompt Guide.

Spec IDs

Filename is the ID

No separate ID field. The filename (without .md) is the identifier.

.chant/specs/2026-01-22-001-x7m.md
            └────────┬────────┘
                   ID

Referenced as:

  • depends_on: [2026-01-22-001-x7m]
  • chant work 2026-01-22-001-x7m
  • chant(2026-01-22-001-x7m): Add authentication

Format

Base Format

YYYY-MM-DD-SSS-XXX
└────┬────┘ └┬┘ └┬┘
   date    seq  random
ComponentPurpose
YYYY-MM-DDCreation date, sortable
SSSSequence within day (base36: 001-zzz)
XXXRandom suffix (3 chars, base36)

Example: 2026-01-22-001-x7m

Sequence Format (Base36)

Sequence uses base36 for scalability:

  • 001 through 999 - familiar numeric
  • a00 through zzz - extends to 46,656/day

Most users never see beyond 999. Research workflows at massive scale can exceed it.

Project-Prefixed Format (Scale)

For monorepos with multiple projects (prefixed to spec ID):

PROJECT-YYYY-MM-DD-NNN-XXX
└──┬──┘ └────┬────┘ └┬┘ └┬┘
 prefix    date    seq  random

Example: auth-2026-01-22-001-x7m

Config:

# config.md
project:
  prefix: auth    # Explicit prefix

Or auto-detect from path:

scale:
  id_prefix:
    from: path
    pattern: "packages/([^/]+)/"   # packages/auth/ → auth-

Project prefix is optional. Solo/team deployments use base format.

Why This Format

Scannable

Glance at a directory listing, immediately see chronology:

2026-01-20-001-a3k.md
2026-01-20-002-f8p.md
2026-01-22-001-x7m.md   ← today
2026-01-22-002-q2n.md   ← today

Sortable

ls and find return chronological order by default.

Parallel-Safe

Random suffix prevents collision when:

  • Multiple agents create specs simultaneously
  • Multiple humans create specs on same day
  • Distributed teams across timezones

Stable

ID never changes. Title can change freely:

---
# No id field needed - filename is the ID
status: pending
---

# Add authentication   ← Can rename this anytime

Typeability

Not optimized for typing. That’s fine.

  • Tab completion handles long IDs
  • CLI can offer fuzzy matching: chant work 22-001 → matches 2026-01-22-001-x7m
  • Agents write most references anyway
  • Copy-paste works

Group Members

Members append .N suffix to driver ID:

2026-01-22-001-x7m.md       ← Driver
2026-01-22-001-x7m.1.md     ← Member 1
2026-01-22-001-x7m.2.md     ← Member 2
2026-01-22-001-x7m.2.1.md   ← Nested member

See groups.md for group semantics.

ID Generation

#![allow(unused)]
fn main() {
fn generate_id() -> String {
    let date = Local::now().format("%Y-%m-%d");
    let seq = next_sequence_for_date(&date);  // 1, 2, ... 46656
    let rand = random_base36(3);              // x7m, a3k, ...
    format!("{}-{}-{}", date, format_base36(seq, 3), rand)
}

fn format_base36(n: u32, width: usize) -> String {
    // 0 → "000", 999 → "999", 1000 → "a00", 46655 → "zzz"
    let chars = "0123456789abcdefghijklmnopqrstuvwxyz";
    // ... base36 encoding with zero-padding
}
}

Sequence resets daily. Random suffix ensures uniqueness even if sequence logic fails.

Short ID Resolution

CLI accepts partial IDs with smart resolution:

chant work 001           # Today's 001 (if exists)
chant work 22-001        # Jan 22's 001 (this year)
chant work 01-22-001     # Jan 22's 001 (explicit month)
chant work x7m           # Suffix match (globally unique)

Resolution order:

  1. Exact match - Full ID matches exactly
  2. Suffix match - x7m uniquely identifies across all time
  3. Today first - 001 checks today before searching history
  4. Date-qualified - 22-001 or 01-22-001 for specific dates
  5. Prompt on ambiguity - Multiple matches shows selection

The random suffix is the true unique identifier. Use it for archived specs:

chant show x7m           # Works even for old specs

Spec Groups

Specs can be split into groups. One spec drives the group - it completes when all members complete.

The Model

Driver spec
├── Member 1: completed  ─┐
├── Member 2: completed   ├─→ Driver auto-completes
└── Member 3: completed  ─┘

This is status aggregation, not hierarchy:

  • Members report status back
  • Members can depend on each other
  • Members don’t inherit driver properties
  • Driver completes when all members complete

Group Structure from Filenames

Group membership is determined by filename suffix:

2026-01-22-001-x7m.md       ← Driver
2026-01-22-001-x7m.1.md     ← Member 1
2026-01-22-001-x7m.2.md     ← Member 2
2026-01-22-001-x7m.2.1.md   ← Subgroup (member 2 is also a driver)

The .N suffix establishes group membership. No group field needed.

Creating Groups

Via Split

chant split 2026-01-22-001-x7m

Agent analyzes the driver spec and creates members:

2026-01-22-001-x7m.1.md
2026-01-22-001-x7m.2.md
2026-01-22-001-x7m.3.md

Manually

chant add "Subspec description" --group 2026-01-22-001-x7m
# Creates: 2026-01-22-001-x7m.4.md

Driver Auto-Completion

When all members are completed, the driver automatically completes:

Driver: pending
├── Member 1: completed
├── Member 2: completed
└── Member 3: completed
              ↓
Driver: completed (auto)
#![allow(unused)]
fn main() {
fn check_group_completion(driver_id: &str, specs: &[Spec]) -> bool {
    let members = find_group_members(specs, driver_id);

    if members.is_empty() {
        return false;  // No members, no auto-complete
    }

    members.iter().all(|m| m.status == "completed")
}

fn maybe_complete_driver(driver_id: &str, specs: &mut [Spec]) {
    if check_group_completion(driver_id, specs) {
        let driver = find_spec_mut(specs, driver_id);
        driver.status = "completed";
        driver.completed_at = Some(Utc::now());
        driver.auto_completed = true;  // Flag for audit
    }
}
}

Working on Groups

Driver with no members

Works like any spec:

chant work 2026-01-22-001-x7m  # Executes directly

Driver with members

Working a driver automatically works its members:

chant work 2026-01-22-001-x7m             # Sequential (one at a time)
chant work 2026-01-22-001-x7m --parallel   # Parallel execution
chant work 2026-01-22-001-x7m --max 3      # Parallel with concurrency limit

Or work a specific member:

chant work 2026-01-22-001-x7m.2   # Just this member

Member Dependencies

Members can depend on other members:

# 2026-01-22-001-x7m.2.md
---
status: pending
depends_on:
  - 2026-01-22-001-x7m.1   # Must complete first
---

Execution order respects dependencies:

chant work 2026-01-22-001-x7m --parallel
# Runs .1 first, then .2 after .1 completes (respects depends_on)

Nested Groups

A member can also be a driver for its own subgroup:

2026-01-22-001-x7m.md           ← Driver (top)
2026-01-22-001-x7m.2.md         ← Member of top, driver of subgroup
2026-01-22-001-x7m.2.1.md       ← Member of subgroup
2026-01-22-001-x7m.2.2.md       ← Member of subgroup

When .2.1 and .2.2 complete → .2 auto-completes → when all .N complete → top driver auto-completes.

Listing Group Members

$ chant group 2026-01-22-001-x7m
2026-01-22-001-x7m.1  [completed]  Setup database schema
2026-01-22-001-x7m.2  [pending]    Implement API endpoints
2026-01-22-001-x7m.3  [pending]    Add authentication

Orphan Detection

Members without a valid driver are flagged:

$ chant lint
Warning: 2026-01-22-001-x7m.5.md references non-existent driver

What Groups Are NOT

Groups are not:

  • Inheritance - Members don’t inherit driver properties (labels, prompt, priority)
  • Hierarchy - It’s a flat group with a designated driver
  • Ownership - Members are independent specs that happen to report status

If you need shared properties, set them explicitly on each member or use a project-level prompt.

Dependencies

Basics

Specs can depend on other specs:

---
status: pending
depends_on:
  - 2026-01-22-001-x7m
  - 2026-01-22-002-q2n
---

A spec is blocked if any dependency is not completed.

Dependency Graph

    ┌─────┐
    │  A  │ (no deps, ready)
    └──┬──┘
       │
    ┌──▼──┐
    │  B  │ (depends on A, blocked)
    └──┬──┘
       │
    ┌──▼──┐
    │  C  │ (depends on B, blocked)
    └─────┘

When A completes → B becomes ready → when B completes → C becomes ready.

Cycle Detection

Cycles are invalid:

A depends on B
B depends on C
C depends on A  ← cycle!

Detected at:

  1. chant lint - Validates all specs
  2. chant add --depends-on - Checks before adding
  3. Spec file save - Editor integration
#![allow(unused)]
fn main() {
fn detect_cycles(specs: &[Spec]) -> Vec<Cycle> {
    let graph = build_graph(specs);
    petgraph::algo::tarjan_scc(&graph)
        .into_iter()
        .filter(|scc| scc.len() > 1)
        .collect()
}
}

Parallel Execution

Independent specs can run in parallel:

    ┌─────┐     ┌─────┐
    │  A  │     │  B  │  ← Both ready, can run in parallel
    └──┬──┘     └──┬──┘
       │           │
       └─────┬─────┘
             │
          ┌──▼──┐
          │  C  │  ← Blocked until both A and B complete
          └─────┘
chant work --parallel          # Run all ready specs in parallel
chant work --parallel --max 3  # Limit concurrent agents

Split and Continue

For epic decomposition with parallel execution:

# Split the spec into members
chant split 2026-01-22-001-x7m

# Then work on ready members in parallel
chant work --parallel
  1. Split creates members (.1, .2, .3, …)
  2. Members without deps become ready
  3. Ready members execute in parallel
  4. As members complete, blocked ones become ready
  5. Continue until all members done

Ready Calculation

#![allow(unused)]
fn main() {
fn is_ready(spec: &Spec, all_specs: &[Spec]) -> bool {
    // Must be pending
    if spec.status != "pending" {
        return false;
    }

    // All dependencies must be completed
    for dep_id in &spec.depends_on {
        let dep = find_spec(all_specs, dep_id);
        if dep.status != "completed" {
            return false;
        }
    }

    // If has group members, all must be completed
    let members = find_group_members(all_specs, &spec.id);
    if !members.is_empty() {
        for member in members {
            if member.status != "completed" {
                return false;
            }
        }
    }

    true
}
}

Blocked Specs

Attempting to work on a blocked spec:

$ chant work 2026-01-22-003-abc
✗ Spec has unsatisfied dependencies.
Blocked by: 2026-01-22-001-x7m (in_progress), 2026-01-22-002-q2n (pending)
Use --force to bypass dependency checks.

Forcing Work on Blocked Specs

Use --force to bypass dependency checks when you know dependencies are satisfied despite their status:

$ chant work 2026-01-22-003-abc --force
⚠ Warning: Forcing work on spec (skipping dependency checks)
  Skipping dependencies: 2026-01-22-001-x7m (in_progress), 2026-01-22-002-q2n (pending)
→ Working 2026-01-22-003-abc with prompt 'standard'
...

Use Cases for --force:

  1. Dependency tracking bug - Spec shows as blocked but dependencies are actually completed
  2. Manual override - You verified dependencies are satisfied despite their status
  3. Emergency work - Need to proceed despite dependency chain issues
  4. Testing - Want to work on spec to test dependency resolution

Warning: Forcing work on a truly blocked spec may lead to conflicts or incomplete work if dependencies aren’t actually satisfied.

Cross-Spec References

Specs can reference each other in body text:

See also: [[2026-01-22-001-x7m]]

This continues work from [[2026-01-22-002-q2n]].

[[id]] syntax is for documentation only. Not enforced as dependencies.

Use depends_on for actual blocking relationships.

Autonomous Workflows

Self-Driving Specs

Specs that execute, verify, and correct themselves:

  • Execute: chant work invokes an agent to implement the spec
  • Verify: chant verify re-checks acceptance criteria
  • Detect Drift: Find when reality diverges from intent

Intent Durability

Specs persist after completion and can be re-verified. When reality diverges from the specification (code changes, tests fail, files deleted), the spec detects this drift and can be re-executed.

Traditional: Spec → Work → Done → Forget
Chant:       Spec → Work → Done → Verify → Drift? → Re-work → ...

Autonomy Spectrum

LevelDescriptionApproval Required
SupervisedAgent works, human reviews before mergePR review
TrustedAgent works, auto-merge low-riskHigh-risk only
AutonomousAgent works, auto-merge, human notifiedExceptions only

Decision Handling

The spec is the decision point. Decisions happen when writing the spec, not during execution.

Manual Intervention

You can interrupt and take control of running work:

  • chant takeover <spec-id> - Pause the agent, analyze progress, and update the spec for manual continuation.

See the Advanced Patterns guide for detailed usage examples.

Spec Design for Autonomy

Good Specs

# ✓ Clear, verifiable, bounded
---
labels: [autonomous, trivial]
---
# Update copyright year
Change 2025 to 2026 in LICENSE file.
## Acceptance Criteria
- [ ] LICENSE shows 2026

Bad Specs

# ✗ Ambiguous, unbounded
---
# Improve the API
Make the API better.

Decomposition

Use chant split to break down large specs:

$ chant split 001
Suggested breakdown:
  001.1 - Add User model
  001.2 - Implement registration
  001.3 - Add middleware

Good member specs are: independent, testable, small (<30 min), specific.

Verification

# Verify a completed spec
$ chant verify 001
  ✓ Rate limiter middleware exists
  ✓ Returns 429 with Retry-After header
  ✓ Tests passing
Spec 001: VERIFIED

# Verify all completed specs
$ chant verify --all

Drift Detection

Drift is when reality diverges from intent (feature removed, tests disabled, etc.).

$ chant drift
Drift Report:
  023: Rate limiting - middleware removed
  089: Auth middleware - file deleted

Patterns

Nightly Batch

# Cron: 0 2 * * *
chant work --chain --label autonomous --max 50

Verification Gate

# CI/CD
deploy:
  requires:
    - chant verify --all --exit-code

Audit Trail

Specs track verification:

---
status: completed
completed_at: 2026-01-10T15:30:00Z
last_verified: 2026-01-15T00:00:00Z
---

Use git to trace history:

$ git log --oneline -- .chant/specs/001.md

Trust Building

Start supervised, graduate to autonomous:

  1. Week 1: autonomy.level: supervised - review all PRs
  2. Week 2: Auto-merge [trivial] labels
  3. Week 3: Auto-merge [trivial, deps, docs]
  4. Week 4+: Full autonomous with guardrails

Goal: More done, less human time, acceptable risk, intent preserved.

Data Lifecycle

What Data Exists

DataLocationTrackedRetention
Specs.chant/specs/*.mdGitForever (git history)
Prompts.chant/prompts/*.mdGitForever
Config.chant/config.mdGitForever
Locks.chant/.locks/*.pidNoUntil released
Index.chant/.store/NoRebuilt on demand
Logs.chant/logs/OptionalConfigurable

Spec Lifecycle

┌──────────┐
│ Created  │  chant add "Fix bug"
└────┬─────┘
     │
     ▼
┌──────────┐
│ Pending  │  Waiting for work or dependencies
└────┬─────────────────────┐
     │                     │
     │                     │ has unmet
     │                     │ dependencies
     │                     ▼
     │                ┌──────────┐
     │                │ Blocked  │  Waiting for depends_on
     │                └────┬─────┘
     │                     │
     │                     │ dependencies
     │                     │ complete
     │                     ▼
     │                ┌──────────┐
     │                │ Pending  │
     │                └──────────┘
     │
     │ (if --needs-approval)
     │
     ├──────────────────────────────┐
     │                              │
     │                              ▼
     │                    ┌───────────────────┐
     │                    │ Approval Required │
     │                    └────┬──────────┬───┘
     │                         │          │
     │              approved   │          │ rejected
     │                         ▼          ▼
     │                    ┌────────┐ ┌──────────┐
     │                    │Approved│ │ Rejected │
     │                    └────┬───┘ └──────────┘
     │                         │
     │◄────────────────────────┘
     │
     ▼
┌──────────┐
│In Progress│  Agent working
└────┬─────┘
     │
     ├──────────────┐
     ▼              ▼
┌──────────┐  ┌──────────┐
│Completed │  │  Failed  │
└────┬─────┘  └────┬─────┘
     │              │
     │              │ retry
     │              ▼
     │        ┌──────────┐
     │        │ Pending  │
     │        └──────────┘
     │
     ▼
┌──────────┐
│ Archived │  (optional, after retention period)
└──────────┘

Cancelled (any status)  ← chant cancel
     │
     └─────► Excluded from lists and work

Approval Gate

When a spec is created with --needs-approval, it must be approved before work can begin. See the Approval Workflow Guide for details.

Spec Statuses

Pending

  • Spec created and ready for work
  • Or failed spec that has been retried
  • Can have unmet dependencies

Blocked

  • Pending spec with unmet dependencies
  • Waiting for one or more specs in depends_on to complete
  • Status automatically applied when spec is loaded if dependencies incomplete
  • Excluded from chant work until dependencies complete
  • Use chant list --summary to see which specs are blocked

In Progress

  • Spec is currently being executed by an agent
  • Lock file created in .chant/.locks/{spec-id}.pid
  • Agent writes status to .chant-status.json in worktree (parallel mode)

Completed

  • Spec execution completed successfully
  • All acceptance criteria were checked
  • Commit hash, timestamp, and model recorded in frontmatter
  • Auto-finalized by chant work when criteria pass

Failed

  • Spec execution failed (agent error or acceptance criteria unchecked)
  • Can be retried with chant resume {spec-id} --work
  • Can be manually finalized with chant finalize {spec-id} if work was done
  • Use chant log {spec-id} to view agent output

Cancelled

  • Spec soft-deleted with chant cancel {spec-id}
  • Status changed to Cancelled, file preserved
  • Excluded from chant list and chant work
  • Can still be viewed with chant show or filtered with chant list --status cancelled
  • Use chant cancel {spec-id} --delete if you want to permanently remove the spec file

Dependency Blocking

Specs can declare dependencies using depends_on:

---
type: code
status: pending
depends_on:
  - 2026-01-26-001-auth     # This spec depends on 001
---

When a spec has unmet dependencies:

  1. Status automatically changes to Blocked
  2. Spec excluded from chant work (can’t execute)
  3. Spec excluded from chant list (hidden by default)
  4. When all dependencies complete, status reverts to Pending
  5. Spec becomes available for work

View blocked specs:

chant list --status blocked          # Show all blocked specs
chant list --summary                 # See overview with block reasons

Retention Policies

Spec Files

Specs stay in .chant/specs/ forever by default. Git history is the archive.

# config.md
lifecycle:
  specs:
    retention: forever         # forever | duration
    # retention: 90d           # Archive after 90 days

Archival (Optional)

If retention is set, completed specs can be archived:

lifecycle:
  specs:
    retention: 90d
    archive:
      enabled: true
      location: .chant/archive/
      compress: true           # Gzip archived specs
.chant/
├── specs/                    # Active specs
│   └── 2026-01-22-001-x7m.md
└── archive/                  # Archived (gitignored or separate branch)
    └── 2025/
        └── 12/
            └── 2025-12-15-042-abc.md.gz

Note: Archival changes spec IDs (moved file). References break. Use sparingly.

Data Flow Diagram

                    Human
                      │
                      │ chant add
                      ▼
┌─────────────────────────────────────────────────────────────┐
│                     Spec File Created                        │
│                                                              │
│  .chant/specs/2026-01-22-001-x7m.md                         │
│  status: pending                                             │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           │ git add, git commit
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                     Git Repository                           │
│                                                              │
│  Permanent history of all spec changes                       │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           │ chant work
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                     Execution                                │
│                                                              │
│  Worktree created: /tmp/chant-{spec-id}/ (parallel only)    │
│  Lock created:  .chant/.locks/{spec-id}.pid                 │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           │ completion
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                     Cleanup                                  │
│                                                              │
│  Lock released (immediate)                                   │
│  Worktree removed (immediate, after merge)                   │
│  Spec updated (status: completed)                            │
│  Branch merged and deleted                                   │
└──────────────────────────┬──────────────────────────────────┘
                           │
                           │ long-term
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                     Long-term                                │
│                                                              │
│  Spec: stays in git history forever                          │
│  Logs: rotated, old logs deleted                             │
│  Branch: deleted after merge                                 │
└─────────────────────────────────────────────────────────────┘

Watch Coordinator

Watch is the unified lifecycle coordinator for all work execution. It runs as an ephemeral daemon that handles state transitions, merging, and cleanup.

Architecture

Agent (worktree)                    Watch (main)
────────────────                    ────────────
create worktree + branch
write status: "working"       →     see working → update spec: InProgress
do work, commit to branch
write status: "done"          →     see done → merge → finalize → cleanup
  (or "failed" on error)            (or handle failure)
exit

Auto-Start Behavior

chant work commands automatically start watch if not running:

  1. chant work checks if watch is running via PID file (.chant/watch.pid)
  2. If not running, spawns chant watch as a detached background process
  3. Watch writes its PID to .chant/watch.pid on startup
  4. Watch exits gracefully after idle timeout (default: 5 minutes)

Use --no-watch flag to disable auto-start (useful for testing).

PID Management

  • Watch writes PID to .chant/watch.pid on startup
  • Watch removes PID file on graceful exit (SIGINT, idle timeout, --once)
  • is_watch_running() checks: PID file exists AND process alive AND is chant
  • Stale PID file (process dead or wrong process) is automatically cleaned up

Agent Status File

Agents communicate with watch via .chant-status.json in the worktree root:

{
  "spec_id": "2026-02-03-02p-ydf",
  "status": "working",
  "updated_at": "2026-02-03T14:30:00Z",
  "error": null,
  "commits": ["abc123"]
}

Status values:

  • working - Agent is actively working
  • done - Agent completed successfully
  • failed - Agent encountered an error

Startup Recovery

On startup, watch recovers from previous crashes:

  1. Scans for existing worktrees with .chant-status.json
  2. done status but not merged → queued for merge
  3. working status but stale (>1 hour) → marked failed
  4. Orphaned worktrees (no status file, old) → cleaned up

Idle Timeout

Watch automatically exits when idle (configurable):

# config.md
watch:
  idle_timeout_minutes: 5  # Exit after 5 minutes of no activity

Activity is detected when:

  • In-progress specs exist
  • Active worktrees with agents are found
  • Status file changes are observed

Git Branch Lifecycle

lifecycle:
  branches:
    delete_after_merge: true   # Delete chant/* branches after merge
    retain_unmerged: 30d       # Keep unmerged branches for 30d

Branches are cleaned up automatically when specs are completed.

Export

Export spec data in various formats using chant export. See CLI Reference for full details.

# Export to JSON
chant export --format json > specs.json

# Export to CSV
chant export --format csv > specs.csv

# Export with filters
chant export --status completed --from 2026-01-01

Backup

Git IS the Backup

Spec files are git-tracked. Normal git backup applies:

  • Push to remote
  • Mirror to backup location
  • Standard git disaster recovery

Note: Local state (index, logs, clones) can be rebuilt. Git history is the permanent archive.

Configuration Reference

# config.md
lifecycle:
  specs:
    retention: forever         # forever | 30d | 90d | 1y
    archive:
      enabled: false
      location: .chant/archive/
      compress: true

  branches:
    delete_after_merge: true
    retain_unmerged: 30d

Skills

Overview

Skills are portable instruction packages that teach AI agents how to perform specific tasks. They follow the Agent Skills open standard — an industry-wide format adopted by Claude Code, Kiro, Cursor, GitHub Copilot, Codex, and others.

Chant uses skills to give your IDE’s agent context about your project’s chant workflow, spec structure, and conventions. When you run chant init --agent <provider>, chant deposits a skill into the provider’s skills directory so the agent automatically knows how to work with specs.

The Agent Skills Standard

The Agent Skills format is an open standard published at agentskills.io. It defines a simple, portable structure for packaging agent instructions.

Directory Structure

Every skill is a directory containing at minimum a SKILL.md file:

skill-name/
├── SKILL.md          # Required — instructions and metadata
├── scripts/          # Optional — executable code (Python, Bash, etc.)
├── references/       # Optional — additional documentation
└── assets/           # Optional — templates, schemas, static files

SKILL.md Format

The SKILL.md file uses YAML frontmatter followed by markdown instructions:

---
name: my-skill
description: What this skill does and when to use it.
---

## Instructions

Step-by-step guidance for the agent...

Required Fields

FieldConstraints
nameMax 64 chars. Lowercase letters, numbers, hyphens. Must match directory name.
descriptionMax 1024 chars. Describes what the skill does and when to activate it.

Optional Fields

FieldPurpose
licenseLicense name or reference to bundled file
compatibilityEnvironment requirements (tools, network, etc.)
metadataArbitrary key-value pairs (author, version)
allowed-toolsPre-approved tools the skill may use (experimental)

Progressive Disclosure

Skills are designed for efficient context usage:

  1. Discovery (~100 tokens): Only name and description load at startup for all skills
  2. Activation (< 5000 tokens): Full SKILL.md body loads when the agent matches a request
  3. Resources (as needed): Files in scripts/, references/, assets/ load on demand

This means you can have many skills installed without overwhelming the agent’s context window.

Skills in Chant

How Chant Uses Skills

When you run chant init --agent <provider>, chant creates a skill in your provider’s skills directory:

ProviderSkills Directory
Claude Code.claude/skills/chant/SKILL.md
Kiro.kiro/skills/chant/SKILL.md
Cursor.cursor/skills/chant/SKILL.md

The chant skill teaches the agent about:

  • Spec structure and lifecycle
  • How to read and execute specs
  • Commit message conventions
  • Acceptance criteria workflow

Because the format is identical across providers, chant uses a single skill template that works everywhere — only the destination directory differs.

Skills vs Prompts

Chant has two distinct instruction systems:

AspectSkillsPrompts
StandardAgent Skills (open)Chant-specific
LocationProvider’s skills dir.chant/prompts/
Loaded byIDE/agent at startupchant work at execution time
ScopeInteractive sessionsSingle spec execution
PurposeGeneral chant awarenessSpecific agent behavior
Exampleschant/SKILL.mdstandard.md, bugfix.md

Skills give the agent ambient knowledge about chant — activated when the user mentions specs, acceptance criteria, or chant workflows during interactive sessions.

Prompts are injected by chant work to control agent behavior during spec execution — they define the execution loop, constraints, and output format.

Skills vs Rules

Some providers have a separate “rules” or “steering” concept (e.g., .kiro/rules.md, CLAUDE.md, .cursorrules). These are always-loaded project instructions that apply to every interaction.

Skills differ from rules:

  • Rules are always loaded — every token counts against context
  • Skills are selectively activated — only loaded when relevant
  • Rules are project-wide — apply to all tasks
  • Skills are task-specific — activated by matching description

Chant may write both: rules for essential project conventions, and skills for chant-specific workflows that only activate when needed.

Provider Skills Directories

Workspace vs Global

Most providers support two skill scopes:

ScopeLocationPurpose
Workspace.{provider}/skills/Project-specific skills
Global~/.{provider}/skills/Personal skills across all projects

Workspace skills override global skills when names conflict.

chant init writes workspace skills so they’re scoped to the project and version-controlled with the codebase.

Per-Provider Details

Claude Code: Skills in .claude/skills/. Supports slash commands, MCP tools, and the full Agent Skills spec.

Kiro: Skills in .kiro/skills/. Shipped in Kiro v0.9.0. Supports progressive disclosure and auto-activation.

Cursor: Skills support in progress. Expected at .cursor/skills/.

GitHub Copilot / VS Code: Supports Agent Skills via the VS Code extensions API.

Creating Custom Skills

You can create project-specific skills alongside the chant skill:

.claude/skills/
├── chant/
│   └── SKILL.md          # Created by chant init
├── deploy/
│   ├── SKILL.md           # Your custom deployment skill
│   └── scripts/
│       └── deploy.sh
└── code-review/
    ├── SKILL.md           # Your code review standards
    └── references/
        └── style-guide.md

Skill Authoring Tips

  1. Keep SKILL.md under 500 lines — move detailed docs to references/
  2. Write a good description — this determines when the skill activates
  3. Include keywords — the agent matches descriptions against user requests
  4. Be specific — “Review pull requests for security issues and test coverage” beats “Helps with PRs”
  5. Use scripts for automation — put executable logic in scripts/, not inline

Example: Custom Skill

---
name: api-conventions
description: Enforce API design conventions including REST patterns,
  error response formats, and pagination. Use when creating or
  modifying API endpoints.
metadata:
  author: my-team
  version: "1.0"
---

## API Design Rules

All endpoints must follow these conventions:

### URL Structure
- Use plural nouns: `/users`, `/orders`
- Nest for relationships: `/users/{id}/orders`
...

Advanced: The Open Standard

Specification

The full Agent Skills specification is at agentskills.io/specification. Key design principles:

  • Portability: Same format works across all supporting agents
  • Progressive disclosure: Metadata loads first, instructions on activation, resources on demand
  • Composability: Skills are independent units that can be mixed and matched
  • Simplicity: A directory with a SKILL.md is a valid skill

Validation

Use the reference library to validate skills:

# Install the validator
npm install -g @agentskills/skills-ref

# Validate a skill
skills-ref validate ./my-skill

Ecosystem

The Agent Skills standard is supported by:

  • Anthropic: Claude Code, Claude Desktop
  • Amazon: Kiro
  • Microsoft: GitHub Copilot, VS Code
  • OpenAI: Codex, ChatGPT
  • Cursor: Cursor IDE
  • Community: Antigravity, OpenCode, and others

Skills can be shared via GitHub repositories and imported into any supporting agent.

Resources

Architecture Overview

Living Intent Infrastructure

The composable primitives that make self-driving specs possible:

PrimitivePurposeSelf-Driving Role
SpecsExecutable specificationsThe intent to execute
PromptsAgent behavior definitionsHow agents interpret intent
TriggersEvent-based activationWhen specs activate
DependenciesExecution orderingSequencing of intent
VerificationOngoing truth-checkingDetecting drift
ReplayRe-execution mechanismRestoring intent

The Big Picture

┌─────────────────────────────────────────────────────────────────────┐
│                         Human / CI                                  │
│                              │                                      │
│                         chant CLI                                   │
└──────────────────────────────┬──────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│                                                                     │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────────────────┐ │
│  │   Prompts   │    │    Specs    │    │        Config           │ │
│  │             │    │             │    │                         │ │
│  │ .chant/     │    │ .chant/     │    │ .chant/config.md        │ │
│  │ prompts/    │    │ specs/      │    │                         │ │
│  │ *.md        │    │ *.md        │    │ defaults, providers     │ │
│  │             │    │             │    │                         │ │
│  └─────────────┘    └─────────────┘    └─────────────────────────┘ │
│                                                                     │
│                    MARKDOWN IS THE UI                               │
│                    (git-tracked, human-readable)                    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│                         Execution                                   │
│                                                                     │
│  1. Load spec      ─→  .chant/specs/001.md                         │
│  2. Load prompt    ─→  .chant/prompts/standard.md                  │
│  3. Acquire lock   ─→  .chant/.locks/001.pid                       │
│  4. Create branch  ─→  git checkout -b chant/001                   │
│  5. Invoke agent   ─→  configured provider                         │
│  6. Agent works    ─→  reads, edits, tests, commits                │
│  7. Update spec    ─→  status: completed, commit: abc123           │
│  8. Release lock   ─→  remove .locks/001.pid                       │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Layer Diagram

┌─────────────────────────────────────────────────────────────────────┐
│                        Interface Layer                              │
│   CLI ◄──────────────────────────────────────────────► Daemon API   │
└─────────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│                        Core Layer                                   │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐            │
│  │  Parser  │  │  Queue   │  │  Locks   │  │ Executor │            │
│  │ YAML +   │  │ Ready    │  │ PID      │  │ Agent    │            │
│  │ Markdown │  │ specs    │  │ files    │  │ invoke   │            │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘            │
└─────────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│                        Storage Layer                                │
│  .chant/                                                            │
│    config.md          (project config)        git-tracked          │
│    prompts/*.md       (agent behavior)        git-tracked          │
│    specs/*.md         (work items)            git-tracked          │
│    .locks/*.pid       (who's working)         gitignored           │
└─────────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│                        Provider Layer                               │
│  AI providers (pluggable) · SCM adapters (pluggable)               │
└─────────────────────────────────────────────────────────────────────┘

Technology Stack

Implementation: Rust - Single native binary. No runtime dependencies.

Core Dependencies

CategoryCratePurpose
Parsingpulldown-cmarkMarkdown parsing
serde, serde_yamlYAML frontmatter
CLIclapArgument parsing
AsyncrayonParallel parsing

External Dependencies

DependencyIntegration
GitShell out (uses user’s config/auth)
AI AgentShell out (provider CLI invocation)

Delivery

  • brew install chant
  • cargo install chant
  • GitHub releases (cargo-dist)

Agent Invocation

The Spec IS the Prompt

Specs are granular, have acceptance criteria, and are linted. The agent prompt is simple:

Implement this spec.

{spec file contents}

The spec file contains everything the agent needs:

  • Title (what to do)
  • Description (context)
  • Acceptance criteria (definition of done)
  • Target files (where to look)

Default Prompt

# .chant/prompts/standard.md
---
name: standard
---

Implement this spec. Follow the acceptance criteria exactly.

When complete, commit with message: `chant({{spec.id}}): <description>`

---

{{spec}}

Output Capture

Agent output goes to:

  1. Terminal (streamed)
  2. Spec file progress field (appended)

Storage & Indexing

Directory Structure

.chant/
├── config.md             # Project config (git-tracked)
├── prompts/              # Prompt files (git-tracked)
│   ├── standard.md
│   └── tdd.md
├── specs/                # All specs (git-tracked)
│   ├── 2026-01-22-001-x7m.md
│   └── 2026-01-22-002-q2n.md
├── .locks/               # PID files (gitignored)
└── .store/               # Index cache (gitignored)

No Archive Folder

Specs stay in specs/ forever. Completed specs have status: completed.

Why:

  • Git history preserves everything
  • Moving files changes IDs (breaks references)
  • Simpler mental model

Filter by status instead:

chant list                  # Active (pending, in_progress, failed)
chant list --all            # Everything
chant list --completed      # Just completed

Active Specs: In-Memory

For <50 active specs, parse files in parallel on each CLI invocation (~50-100ms).


Deployment Modes

Mode 1: Solo (No Daemon)

Developer Machine
  CLI → .chant/ → Agent

No daemon. CLI reads files directly. Simple. Works offline.

Mode 2: Team (No Daemon)

Alice           Bob
  ↓               ↓
  └─────┬─────────┘
        ↓
    Git Remote (.chant/)

No daemon. Git coordinates. PID locks prevent double-work.

Mode 3: Team (With Daemon)

Shared Server
  Daemon (Index + Queue)
     ↓
  Worker 1, Worker 2, Worker 3

Daemon provides fast queries, coordination. Workers poll queue, execute specs.

Mode 4: Scale (K8s)

Kubernetes Cluster
  Daemon Pod (Index, Queue, API, Metrics)
     ↓
  Worker Pods (auto-scaled)
     ↓
  Prometheus → Grafana

Component Responsibilities

ComponentResponsibility
CLIUser interface, command dispatch
ParserRead/write markdown + YAML frontmatter
QueueTrack ready specs, priority ordering
LocksPrevent double-work, crash recovery
ExecutorInvoke agent, manage lifecycle
DaemonPersistent services (optional)
IndexFast search via Tantivy
ProvidersAgent adapters (pluggable)

File Ownership

.chant/
├── config.md             # Human + Chant
├── prompts/*.md          # Human (or community)
├── specs/*.md            # Human creates, Chant updates
├── .locks/               # Chant only (gitignored)
└── .store/               # Chant only (gitignored)

For detailed agent protocol specification, see protocol.md.

Agent Protocol

Overview

How chant invokes AI agents and exchanges information.

Invocation

# Primary: CLI with environment context
CHANT_SPEC_ID=2026-01-22-001-x7m \
CHANT_PROJECT=auth \
CHANT_PROMPT=standard \
claude --print "$(cat .chant/prompts/standard.md)" < spec.md

Environment Variables

Chant sets these before invoking the agent:

VariableDescription
CHANT_SPEC_IDCurrent spec ID
CHANT_SPEC_FILEAbsolute path to spec markdown
CHANT_PROJECTProject prefix (if any)
CHANT_PROMPTPrompt name being used
CHANT_PROMPT_FILEAbsolute path to prompt markdown
CHANT_WORKTREEWorktree path (if isolated)
CHANT_ATTEMPTAttempt number (1, 2, 3…)
CHANT_TIMEOUTRemaining timeout in seconds

Input

Agent receives:

  1. System prompt - The prompt markdown (agent behavior)
  2. User prompt - The spec markdown (what to do)
# Conceptual
claude \
  --system-prompt .chant/prompts/standard.md \
  --user-prompt .chant/specs/2026-01-22-001-x7m.md

Output

Agent writes directly to:

  1. Working directory - Code changes
  2. Spec file - Progress updates (output section)
  3. Git - Commits (if prompt instructs)

Exit Handling

Agent ExitChant Action
0Mark spec completed
Non-zeroMark spec failed, capture error
TimeoutKill agent, mark failed
SignalMark failed, note signal

MCP Server

Chant exposes an MCP server (chant-mcp) for tool integration with agents.

Required for: Some agents (only way to provide tools) Optional for: CLI-based agents (enhancement for structured tool access)

See mcp.md for full design.

Invocation Example

# Non-interactive execution (provider-specific)
CHANT_SPEC_ID=2026-01-22-001-x7m \
CHANT_SPEC_FILE=/path/to/spec.md \
CHANT_PROMPT_FILE=/path/to/prompt.md \
agent-cli --autonomous --spec $CHANT_SPEC_FILE

Each provider has its own CLI flags. Chant abstracts this through the provider interface.

Providers

Chant supports multiple AI providers via adapters:

Built-in Providers

Chant supports multiple AI providers via adapters. Providers are pluggable - see the protocol specification for details on adding new ones.

# config.md
agent:
  provider: default       # Built-in provider

Provider Configuration

# config.md
agent:
  provider: my-provider
  model: model-name
  api_key: ${PROVIDER_API_KEY}

Custom Providers (Plugins)

For unsupported agents, define custom command:

# config.md
agent:
  provider: custom
  command: "my-agent --execute"
  # Chant sets env vars, pipes spec to stdin

Provider Interface

Any provider must:

  1. Accept spec content (stdin or file)
  2. Accept prompt/system instruction (env var or flag)
  3. Write changes to working directory
  4. Exit 0 on success, non-zero on failure
# Provider receives:
CHANT_SPEC_ID=2026-01-22-001-x7m
CHANT_SPEC_FILE=/path/to/spec.md
CHANT_PROMPT_FILE=/path/to/prompt.md
CHANT_WORKTREE=/path/to/worktree

# Provider must:
# 1. Read spec and prompt
# 2. Execute changes
# 3. Exit with status

Provider Plugins

Plugins live in .chant/providers/:

.chant/providers/
  my-provider.md       # Plugin definition
# .chant/providers/my-provider.md
---
name: my-provider
command: my-agent
args:
  - "--autonomous"
  - "--spec-file"
  - "{{spec_file}}"
env:
  MY_AGENT_KEY: ${MY_AGENT_KEY}
---

# My Provider

Custom agent for specialized work.

## Setup

1. Install my-agent: `brew install my-agent`
2. Set MY_AGENT_KEY environment variable

## Capabilities

- Supports TypeScript
- Runs tests automatically

Per-Spec Provider Override

# spec frontmatter
---
status: pending
agent:
  provider: alternate-provider
  model: model-name
---

Provider Selection Logic

  1. Spec frontmatter agent.provider (highest)
  2. Config agent.provider
  3. Environment CHANT_PROVIDER
  4. Built-in default

Custom Command (Legacy)

Simple command override (deprecated, use providers):

# config.md
agent:
  command: "my-agent --autonomous"

Spec and prompt are passed via stdin/env. Agent writes to filesystem.

Operations Layer

The operations layer provides canonical business logic for spec manipulation, preventing the common anti-pattern where CLI and MCP re-implement the same logic with subtle differences.

The Problem

Before the operations layer existed, chant’s MCP server duplicated CLI command logic. When we fixed a bug in chant finalize, the same bug persisted in the MCP handler. When we added validation to chant reset, the MCP endpoint had different behavior. Every feature required two implementations that inevitably diverged.

This violated DRY and created maintenance burden: bug fixes didn’t propagate, validation rules were inconsistent, and behavior differed depending on whether you used the CLI or MCP.

Architecture

The operations layer sits between interface handlers (CLI/MCP) and the domain layer (spec files, state machine, repository):

CLI (clap)  ──┐
               ├──▶ operations/ ──▶ domain layer
MCP (JSON-RPC)─┘

Both CLI commands and MCP handlers route through the same operations functions. This ensures:

  • Single source of truth: Business logic lives in one place
  • Consistent validation: All interfaces enforce the same rules
  • Unified behavior: CLI and MCP behave identically
  • Easier testing: Test operations once instead of per-interface

Current Operations

ModuleDescription
archive.rsMove completed specs to archive directory
cancel.rsCancel specs and mark them as cancelled
commits.rsAuto-detect and associate git commits with specs
create.rsCreate new specs with ID generation and template application
finalize.rsMark specs as completed with validation and state checks
model.rsUpdate model configuration for specs
pause.rsPause running work processes for specs
reset.rsReset failed or in-progress specs back to pending
update.rsUpdate spec frontmatter fields and append output
verify.rsVerify specs meet their acceptance criteria

create.rs — Spec Creation

Creates new specs with ID generation, template application, derivation, and git auto-commit.

Key responsibilities:

  • Generate unique spec ID based on current date
  • Split long descriptions into title + body
  • Apply prompt templates if specified
  • Run derivation engine for enterprise fields
  • Auto-commit to git (unless disabled or .chant/ is gitignored)

Usage:

#![allow(unused)]
fn main() {
use chant::operations::create::{create_spec, CreateOptions};

let (spec, path) = create_spec(
    "Add user authentication",
    &specs_dir,
    &config,
    CreateOptions {
        prompt: Some("feature".to_string()),
        needs_approval: false,
        auto_commit: true,
    },
)?;
}

finalize.rs — Spec Completion

Marks specs as completed with full validation and state consistency checks.

Key responsibilities:

  • Check for uncommitted changes in worktree
  • Validate driver/member relationships (drivers can’t complete with incomplete members)
  • Auto-detect commits (or accept provided list)
  • Check for agent co-authorship and set approval requirements
  • Update status, completed_at timestamp, model field
  • Verify persistence (reload and validate saved state)

Usage:

#![allow(unused)]
fn main() {
use chant::operations::finalize::{finalize_spec, FinalizeOptions};

finalize_spec(
    &mut spec,
    &spec_repo,
    &config,
    &all_specs,
    FinalizeOptions {
        allow_no_commits: false,
        commits: Some(vec!["abc123".to_string()]),
    },
)?;
}

Validation performed:

  • Uncommitted changes block finalization
  • Driver specs require all members completed first
  • Completed specs must have valid ISO timestamps
  • Persistence is verified by reloading from disk

reset.rs — Failure Recovery

Resets failed or in-progress specs back to pending status.

Key responsibilities:

  • Validate spec is in failed or in_progress state
  • Transition to pending via state machine
  • Persist the status change
  • Optionally re-execute (parameter exists but not yet implemented)

Usage:

#![allow(unused)]
fn main() {
use chant::operations::reset::{reset_spec, ResetOptions};

reset_spec(
    &mut spec,
    &spec_path,
    ResetOptions {
        re_execute: false,
        prompt: None,
        branch: None,
    },
)?;
}

Constraints:

  • Only failed and in_progress specs can be reset
  • State transitions are validated by the spec state machine

update.rs — Field Mutations

Updates spec frontmatter fields with selective preservation.

Key responsibilities:

  • Update status (using force_status for MCP compatibility)
  • Set dependencies, labels, target files, model
  • Append output text to spec body
  • Persist changes

Usage:

#![allow(unused)]
fn main() {
use chant::operations::update::{update_spec, UpdateOptions};

update_spec(
    &mut spec,
    &spec_path,
    UpdateOptions {
        status: Some(SpecStatus::InProgress),
        labels: Some(vec!["bug".to_string(), "p0".to_string()]),
        output: Some("Progress update: completed phase 1".to_string()),
        ..Default::default()
    },
)?;
}

Output handling:

  • Appends text to spec body with ## Output\n\n header
  • Preserves existing body content
  • Ensures proper newline spacing

How to Add a New Operation

When you need a new spec operation (e.g., archive, split, merge):

  1. Create src/operations/{operation}.rs

    • Define an Options struct for parameters
    • Implement the operation function taking &mut Spec, options, and dependencies
    • Use the spec state machine for status transitions
    • Perform all validation and business logic here
  2. Export from src/operations/mod.rs

    #![allow(unused)]
    fn main() {
    pub mod archive;
    pub use archive::{archive_spec, ArchiveOptions};
    }
  3. Add CLI command in src/cmd/

    • Parse arguments with clap
    • Load spec and dependencies
    • Call operation function
    • Handle errors and output
  4. Add MCP handler in src/server/handlers/

    • Parse JSON-RPC parameters
    • Load spec and dependencies
    • Call the same operation function
    • Return JSON response
  5. Write tests in tests/operations/

    • Test the operation directly (not via CLI/MCP)
    • Cover validation, state transitions, edge cases
    • Use TestHarness and SpecFactory for setup

What Goes Where

Operations layer (src/operations/):

  • Spec manipulation business logic
  • Validation rules
  • State transitions and persistence
  • Anything that should behave identically across interfaces

CLI layer (src/cmd/):

  • Argument parsing (clap)
  • Terminal output formatting
  • Interactive prompts
  • Shell-specific concerns (exit codes, colored output)

MCP layer (src/server/handlers/):

  • JSON-RPC request/response handling
  • Parameter deserialization
  • Error formatting for MCP protocol
  • MCP-specific features (notifications, progress)

Domain layer (src/spec/, src/repository/, etc.):

  • Core data structures (Spec, SpecStatus)
  • File I/O and parsing
  • State machine transitions
  • Low-level primitives

Rule of thumb: If CLI and MCP need to do it the same way, it belongs in operations. If it’s interface-specific (formatting, protocol details), it belongs in the handler.

Examples

Adding an Archive Operation

#![allow(unused)]
fn main() {
// src/operations/archive.rs
use anyhow::Result;
use std::path::Path;
use crate::spec::{Spec, SpecStatus};

pub struct ArchiveOptions {
    pub archive_dir: PathBuf,
}

pub fn archive_spec(
    spec: &Spec,
    spec_path: &Path,
    options: ArchiveOptions,
) -> Result<()> {
    // Validation
    if spec.frontmatter.status != SpecStatus::Completed {
        anyhow::bail!("Only completed specs can be archived");
    }

    // Business logic
    let archive_path = options.archive_dir.join(spec_path.file_name().unwrap());
    std::fs::rename(spec_path, &archive_path)?;

    Ok(())
}
}

Using from CLI

#![allow(unused)]
fn main() {
// src/cmd/archive.rs
use clap::Args;
use chant::operations::archive::{archive_spec, ArchiveOptions};

#[derive(Args)]
pub struct ArchiveArgs {
    id: String,
}

pub fn run(args: ArchiveArgs, config: &Config) -> Result<()> {
    let spec = load_spec(&args.id)?;
    let spec_path = get_spec_path(&args.id)?;

    archive_spec(
        &spec,
        &spec_path,
        ArchiveOptions {
            archive_dir: config.archive_dir.clone(),
        },
    )?;

    println!("Archived spec {}", args.id);
    Ok(())
}
}

Using from MCP

#![allow(unused)]
fn main() {
// src/server/handlers/archive.rs
use serde::{Deserialize, Serialize};
use chant::operations::archive::{archive_spec, ArchiveOptions};

#[derive(Deserialize)]
struct ArchiveRequest {
    id: String,
}

pub fn handle_archive(req: ArchiveRequest, config: &Config) -> Result<Value> {
    let spec = load_spec(&req.id)?;
    let spec_path = get_spec_path(&req.id)?;

    archive_spec(
        &spec,
        &spec_path,
        ArchiveOptions {
            archive_dir: config.archive_dir.clone(),
        },
    )?;

    Ok(json!({ "success": true, "id": req.id }))
}
}

Notice how both CLI and MCP call the same archive_spec function with identical parameters. Validation, business logic, and file operations happen once in the operations layer.

Architecture Patterns

This document maps chant’s architecture to well-known software design patterns. Understanding these patterns helps contributors navigate the ~18K LOC codebase and see how different components fit together.


1. State Machine (TransitionBuilder)

Pattern: Builder + State Machine Location: src/spec/state_machine.rs

The TransitionBuilder provides composable, validated state transitions for spec lifecycle management. It uses the Builder pattern to accumulate preconditions, then validates them before executing the state transition.

┌─────────────────────────────────────────────────┐
│  TransitionBuilder (Builder Pattern)           │
│                                                 │
│  .require_clean_tree()                          │
│  .require_dependencies_met()                    │
│  .require_all_criteria_checked()                │
│  .require_commits_exist()                       │
│  .to(SpecStatus::Completed)                    │
│                                                 │
│  ┌──────────────────────────────┐               │
│  │  Valid Transitions (FSM)     │               │
│  │                              │               │
│  │  Pending → InProgress        │               │
│  │  InProgress → Completed      │               │
│  │  InProgress → Failed         │               │
│  │  Failed → Pending            │               │
│  │  ...                         │               │
│  └──────────────────────────────┘               │
└─────────────────────────────────────────────────┘

Key insight: Preconditions are composable — you can require different combinations of checks depending on the transition context.


2. Execution Engine (Template Method + Strategy)

Pattern: Template Method + Strategy Location: src/cmd/work/executor.rs, src/cmd/work/single.rs, src/cmd/work/chain.rs, src/cmd/work/parallel.rs

The execution engine uses Template Method to define the common validation/agent invocation/finalization flow, while Strategy pattern allows for three execution modes (single, chain, parallel).

┌──────────────────────────────────────────────────┐
│  Executor (Template Method)                      │
│                                                  │
│  1. validate_spec()                              │
│  2. invoke_agent()     ← Strategy implementation │
│  3. finalize_spec()                              │
│                                                  │
│  ┌─────────────┐  ┌─────────────┐  ┌──────────┐ │
│  │   Single    │  │    Chain    │  │ Parallel │ │
│  │   (one)     │  │ (sequence)  │  │ (N-way)  │ │
│  └─────────────┘  └─────────────┘  └──────────┘ │
└──────────────────────────────────────────────────┘

Key insight: Common validation logic lives in executor.rs, while each mode implements its own scheduling strategy.


3. Worktree Isolation (Sandbox + Mutex)

Pattern: Sandbox + Mutex Location: src/worktree/mod.rs, src/worktree/git_ops.rs

Git worktrees provide isolated execution environments for each spec. Mutex-protected creation prevents race conditions during parallel worktree creation.

┌──────────────────────────────────────────────────┐
│  Main Repository                                 │
│  (/path/to/project)                              │
│                                                  │
│  ┌──────────────────────────────────┐            │
│  │  Worktree Lock (Mutex)           │            │
│  │  Serializes create operations    │            │
│  └──────────────────────────────────┘            │
│                                                  │
│  ┌───────────────────┐  ┌───────────────────┐   │
│  │ Worktree A        │  │ Worktree B        │   │
│  │ /tmp/chant-001    │  │ /tmp/chant-002    │   │
│  │ branch: chant/001 │  │ branch: chant/002 │   │
│  │                   │  │                   │   │
│  │ .chant-status.json│  │ .chant-status.json│   │
│  └───────────────────┘  └───────────────────┘   │
└──────────────────────────────────────────────────┘

Key insight: Each agent works in an isolated filesystem and git branch, preventing conflicts between concurrent executions.


4. Dependency Graph (DAG + Topological Sort)

Pattern: Directed Acyclic Graph (DAG) Location: src/deps.rs, src/spec.rs

Spec dependencies form a DAG with cycle detection and topological sort for determining execution order.

┌──────────────────────────────────────────────────┐
│  Dependency Resolution                           │
│                                                  │
│     spec-001                                     │
│       ↓                                          │
│     spec-002 ──→ spec-004                        │
│       ↓                                          │
│     spec-003                                     │
│                                                  │
│  Functions:                                      │
│  • resolve_dependency()                          │
│  • check_circular_dependencies()                 │
│  • is_blocked_by_dependencies()                  │
│  • is_ready()                                    │
└──────────────────────────────────────────────────┘

Key insight: Cross-repo dependencies supported via repo:spec-id syntax, resolved through config-defined repo paths.


5. Merge Driver (Rules Engine)

Pattern: Rules Engine (Declarative Conflict Resolution) Location: src/merge.rs, src/merge_driver.rs

The merge driver uses a declarative rules table to map spec fields to merge strategies (ours, theirs, union, newest).

┌──────────────────────────────────────────────────┐
│  Merge Strategy Table                            │
│                                                  │
│  Field          Strategy      Reason             │
│  ─────────────  ────────────  ─────────────────  │
│  status         theirs        Agent's final      │
│  completed_at   theirs        Agent sets         │
│  model          theirs        Agent context      │
│  depends_on     union         Merge deps         │
│  target_files   union         Combine files      │
│  body           theirs        Agent output       │
│                                                  │
│  Strategies:                                     │
│  • ours: Keep base branch value                  │
│  • theirs: Take incoming branch value            │
│  • union: Merge both (deduplicate)               │
│  • newest: Pick most recent timestamp            │
└──────────────────────────────────────────────────┘

Key insight: Spec merges from work branches back to main are fully automated using field-specific merge strategies.


6. MCP Interface (Command Pattern)

Pattern: Command + Self-Describing Tools Location: src/mcp/server.rs, src/mcp/handlers.rs, src/mcp/tools/mod.rs

The Model Context Protocol (MCP) interface exposes chant operations as self-describing tools with JSON Schema definitions.

┌──────────────────────────────────────────────────┐
│  MCP Server                                      │
│                                                  │
│  Tool Registry:                                  │
│  ┌────────────────────────────────────┐          │
│  │ chant_spec_list                    │          │
│  │ chant_spec_get                     │          │
│  │ chant_spec_update                  │          │
│  │ chant_work_start                   │          │
│  │ chant_finalize                     │          │
│  │ chant_watch_status                 │          │
│  │ ...                                │          │
│  └────────────────────────────────────┘          │
│                                                  │
│  Each tool:                                      │
│  • JSON Schema for parameters                    │
│  • Handler function in operations layer          │
│  • Structured JSON response                      │
└──────────────────────────────────────────────────┘

Key insight: Tools provide Claude-friendly interface to chant operations, enabling AI orchestration.


7. Provider Abstraction (Strategy Pattern)

Pattern: Strategy (Pluggable Backends) Location: src/provider.rs

The ModelProvider trait defines a common interface for AI provider backends, enabling easy swapping between Claude, OpenAI, Ollama, etc.

┌──────────────────────────────────────────────────┐
│  ModelProvider Trait                             │
│                                                  │
│  trait ModelProvider {                           │
│    fn invoke_agent(...) -> Result<Output>        │
│  }                                               │
│                                                  │
│  ┌──────────────┐  ┌──────────────┐  ┌────────┐ │
│  │ ClaudeCliProvider                              │
│  │ └─→ claude code                  │            │
│  │                                  │            │
│  │ OpenaiProvider                   │            │
│  │ └─→ OpenAI API                   │            │
│  │                                  │            │
│  │ OllamaProvider                   │            │
│  │ └─→ Local Ollama                 │            │
│  └──────────────────────────────────┘            │
│                                                  │
│  Configured in: .chant/config.md                 │
│  providers.default_provider = "claude" | ...     │
└──────────────────────────────────────────────────┘

Key insight: Providers are configured declaratively and selected at runtime, no code changes needed.


8. Spec Groups (Composite Pattern)

Pattern: Composite (Driver/Member Hierarchy) Location: src/spec_group.rs, src/merge.rs

Driver specs can have member specs in a hierarchical relationship. Members are identified by numeric suffixes (.1, .2, .3).

┌──────────────────────────────────────────────────┐
│  Spec Groups (Composite Pattern)                 │
│                                                  │
│  driver-spec                                     │
│    ├─→ driver-spec.1 (member)                    │
│    ├─→ driver-spec.2 (member)                    │
│    └─→ driver-spec.3 (member)                    │
│                                                  │
│  Functions:                                      │
│  • is_member_of()                                │
│  • get_members()                                 │
│  • all_members_completed()                       │
│  • auto_complete_driver_if_ready()               │
│                                                  │
│  Behavior:                                       │
│  • Driver marks in_progress when first member    │
│    starts                                        │
│  • Driver auto-completes when all members done   │
│  • Members can execute sequentially or in DAG    │
│    order via depends_on                          │
└──────────────────────────────────────────────────┘

Key insight: Drivers provide grouping/orchestration without executing themselves — they’re auto-completed when members finish.


9. Watch Service (Observer Pattern)

Pattern: Observer (Polling-Based) Location: src/cmd/watch.rs, src/worktree/status.rs

The watch service polls worktree status files and spec status, triggering lifecycle operations (finalize, merge, failure handling) when state changes are detected.

┌──────────────────────────────────────────────────┐
│  Watch Service (Observer via Polling)            │
│                                                  │
│  ┌─────────────────────────────────────┐         │
│  │  Poll Loop (configurable interval)  │         │
│  │                                     │         │
│  │  1. Load all specs                  │         │
│  │  2. Discover active worktrees       │         │
│  │  3. Read .chant-status.json files   │         │
│  │  4. Detect state changes            │         │
│  │  5. Trigger lifecycle handlers      │         │
│  │     • finalize + merge              │         │
│  │     • failure recovery              │         │
│  │  6. Sleep for poll_interval_ms      │         │
│  └─────────────────────────────────────┘         │
│                                                  │
│  Crash Recovery:                                 │
│  • Detects stale worktrees (>1hr working)        │
│  • Marks specs as failed                         │
│  • Cleans up orphaned worktrees                  │
└──────────────────────────────────────────────────┘

Key insight: Watch provides hands-off orchestration — start agents and let watch handle completion/merge/cleanup.


10. Operations Layer (Facade/Service Layer)

Pattern: Facade + Service Layer Location: src/operations/mod.rs, src/operations/create.rs, src/operations/finalize.rs, etc.

The operations layer provides a stable API for spec operations, shared by both CLI commands and MCP handlers. This ensures consistency and simplifies testing.

┌──────────────────────────────────────────────────┐
│  Operations Layer (Facade)                       │
│                                                  │
│  ┌────────────────┐      ┌──────────────────┐   │
│  │  CLI Commands  │      │  MCP Handlers    │   │
│  └────────┬───────┘      └─────────┬────────┘   │
│           │                        │            │
│           └────────┬───────────────┘            │
│                    ↓                            │
│  ┌──────────────────────────────────────┐       │
│  │  Operations Layer                    │       │
│  │  • create_spec()                     │       │
│  │  • update_spec()                     │       │
│  │  • finalize_spec()                   │       │
│  │  • reset_spec()                      │       │
│  └──────────────────────────────────────┘       │
│                    ↓                            │
│  ┌──────────────────────────────────────┐       │
│  │  Domain Layer                        │       │
│  │  • Spec                              │       │
│  │  • SpecRepository                    │       │
│  │  • State Machine                     │       │
│  └──────────────────────────────────────┘       │
└──────────────────────────────────────────────────┘

Key insight: Operations layer provides canonical business logic, preventing drift between CLI and MCP implementations.


11. Config (Layered Configuration)

Pattern: Layered Configuration Merge Location: src/config/mod.rs

Configuration is loaded from multiple sources with explicit merge order: global config, project config, and project-local agents config.

┌──────────────────────────────────────────────────┐
│  Configuration Merge (Layered Override)          │
│                                                  │
│  1. Global Config                                │
│     ~/.config/chant/config.md                    │
│     (user preferences, provider keys)            │
│                                                  │
│  2. Project Config  ← overrides                  │
│     .chant/config.md                             │
│     (git-tracked, team defaults)                 │
│                                                  │
│  3. Agents Config  ← overrides parallel.agents   │
│     .chant/agents.md                             │
│     (gitignored, local agent overrides)          │
│                                                  │
│  Result: Merged Config                           │
│  • Later layers override earlier                 │
│  • Allows user/project/local customization       │
└──────────────────────────────────────────────────┘

Key insight: Configuration merge allows personal defaults globally while projects define team standards and individuals override locally.


12. Output (Strategy + Adapter)

Pattern: Strategy + Adapter Location: src/ui.rs, src/formatters.rs

The output system supports multiple output modes (human-readable colored output, JSON, quiet mode) via strategy pattern, with adapters for different contexts (CLI, MCP, test).

┌──────────────────────────────────────────────────┐
│  Output Strategies                               │
│                                                  │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────┐  │
│  │   Human     │  │    JSON     │  │  Quiet  │  │
│  │  (colored)  │  │ (machine)   │  │ (none)  │  │
│  └─────────────┘  └─────────────┘  └─────────┘  │
│                                                  │
│  Adapters:                                       │
│  • status_icon() - colored icons per status      │
│  • colors::success/warning/error                 │
│  • is_quiet() - check for silent mode            │
│  • Formatters for status displays                │
│                                                  │
│  Configuration:                                  │
│  • project.silent = true  ← suppresses output    │
│  • CHANT_QUIET env var                           │
└──────────────────────────────────────────────────┘

Key insight: Output strategies enable machine-readable JSON for CI/automation while preserving human-friendly terminal output.


13. Score/Lint (Composite Scorer)

Pattern: Composite (Aggregated Metrics) Location: src/scoring.rs, src/score/mod.rs, src/score/complexity.rs, etc.

The quality scoring system composes multiple independent metrics (complexity, confidence, splittability, isolation, AC quality) into a unified score with traffic-light status.

┌──────────────────────────────────────────────────┐
│  Scoring System (Composite)                      │
│                                                  │
│  SpecScore {                                     │
│    complexity:      ComplexityGrade,             │
│    confidence:      ConfidenceGrade,             │
│    splittability:   SplittabilityGrade,          │
│    isolation:       Option<IsolationGrade>,      │
│    ac_quality:      ACQualityGrade,              │
│    traffic_light:   TrafficLight,                │
│  }                                               │
│                                                  │
│  Scorers (Independent):                          │
│  • Complexity (criteria count, file count, size) │
│  • Confidence (structure, vague language)        │
│  • Splittability (decomposability)               │
│  • Isolation (group member independence)         │
│  • AC Quality (acceptance criteria clarity)      │
│                                                  │
│  Traffic Light:                                  │
│  • Ready (green): Safe to execute                │
│  • Warning (yellow): Consider splitting          │
│  • Stop (red): Needs rework                      │
└──────────────────────────────────────────────────┘

Key insight: Each scorer is independent and tests are isolated. Traffic light provides quick go/no-go decision.


Pattern Summary Table

PatternComponentLocationPurpose
State Machine + BuilderTransitionBuilderspec/state_machine.rsComposable preconditions for lifecycle transitions
Template Method + StrategyExecution Enginecmd/work/executor.rsCommon validation flow, pluggable execution modes
Sandbox + MutexWorktree Isolationworktree/mod.rsIsolated execution environments, serialized creation
DAGDependency Graphdeps.rsCross-repo dependencies, cycle detection, topological sort
Rules EngineMerge Drivermerge.rsDeclarative conflict resolution by field
CommandMCP Interfacemcp/server.rsSelf-describing tools for AI orchestration
StrategyProvider Abstractionprovider.rsPluggable AI backends
CompositeSpec Groupsspec_group.rsDriver/member hierarchy with auto-completion
ObserverWatch Servicecmd/watch.rsPolling-based lifecycle orchestration
Facade/Service LayerOperationsoperations/mod.rsShared business logic for CLI and MCP
Layered ConfigConfig Mergeconfig/mod.rsGlobal → project → local override semantics
Strategy + AdapterOutputui.rs, formatters.rsHuman/JSON/Quiet output modes
CompositeScore/Lintscoring.rs, score/mod.rsIndependent metric aggregation, traffic light

When exploring the codebase:

  1. Start with patterns, not files — Identify which pattern you need to understand, then read the relevant module
  2. Follow the data — Specs flow through: parse → validate → execute → finalize → merge
  3. Check tests — Most patterns have comprehensive test coverage showing usage examples
  4. Read module docs — Each module has header comments explaining its role and architectural decisions
  5. Use docs/architecture/architecture.md — For high-level component overview and storage layout

Additional Resources

Cursor Quick Start Guide

A step-by-step guide for using Cursor with chant for spec-driven development.

Prerequisites

  • Cursor IDE: Download and install from cursor.com
  • Chant installed: See the Quick Start Guide for installation instructions
  • Git: Required for chant’s spec tracking

Step 1: Initialize Cursor Integration

Navigate to your project directory and run:

chant init --agent cursor

This creates:

  • .cursorrules file with AI instructions for Cursor
  • .cursor/mcp.json for MCP (Model Context Protocol) integration

What is MCP? MCP lets Cursor’s AI discover and use chant commands directly as tools, making spec management seamless and structured.

Step 2: Restart Cursor

Important: After running chant init --agent cursor, you must restart Cursor to load the MCP configuration.

  1. Quit Cursor completely
  2. Reopen Cursor
  3. Open your project folder

Step 3: Verify MCP is Working

Open Cursor’s AI chat and check that chant tools are available:

  1. Open the Cursor chat panel
  2. Look for MCP tools in the tool list (if available in UI)
  3. Or ask Cursor: “What chant tools are available?”

Cursor should respond with a list of chant_* tools like:

  • chant_spec_list
  • chant_spec_get
  • chant_add
  • chant_status

If you don’t see these tools, see Troubleshooting below.

Using Cursor with Chant

Cursor works with chant in two ways:

Terminal Commands: Execute Specs

Run agents from the terminal:

chant work 001

This is the recommended approach for spec execution. The agent runs autonomously and commits when done.

Cursor’s AI: Manage Specs via MCP

Use Cursor’s chat with MCP tools to:

  • Browse specs: “Show me all pending specs”
  • Create specs: “Create a spec for adding pagination to the user list”
  • Check status: “What’s the status of the project?”
  • Search specs: “Find specs related to authentication”

Important: chant work is not available via MCP (it spawns an agent process). Use the terminal for running work.

Understanding the Cursor Integration

.cursorrules

This file tells Cursor’s AI how to work with chant. It contains instructions for:

  • Understanding the chant workflow
  • Working within specs
  • Using proper commit messages
  • Testing and code quality standards

You can customize this file to match your project’s specific needs (linting tools, test commands, coding standards).

.cursor/mcp.json

This file connects Cursor to the chant MCP server:

{
  "mcpServers": {
    "chant": {
      "type": "stdio",
      "command": "chant",
      "args": ["mcp"]
    }
  }
}

How it works:

  • Cursor starts the chant mcp process
  • Chant exposes spec management tools
  • Cursor’s AI can query specs, create specs, update status, etc.

MCP Tools Reference

When using Cursor’s AI, these tools are available:

ToolPurpose
chant_spec_listList all specs, optionally filtered by status
chant_spec_getGet full details of a spec
chant_readyList specs ready to work (no unmet dependencies)
chant_statusGet project summary with spec counts
chant_addCreate a new spec
chant_spec_updateUpdate a spec’s status or add output
chant_finalizeMark a spec as completed
chant_searchSearch specs by title and content
chant_diagnoseDiagnose issues with a spec
chant_logRead execution log for a spec

For full details, see MCP Reference.

What’s Next

  • Learn the basics: See Quick Start Guide for core concepts and workflows
  • Explore prompts: Read Prompts to customize agent behavior
  • Advanced workflows: Check Examples for real-world usage patterns
  • Cursor rules reference: See .cursorrules for the complete AI behavior guide

Troubleshooting

MCP Tools Not Showing Up

Symptoms: Cursor doesn’t recognize chant_* tools

Solutions:

  1. Restart Cursor completely - Quit and reopen
  2. Verify .cursor/mcp.json exists - Should be in your project root
  3. Check chant is in PATH - Run which chant in terminal
  4. Check Cursor logs - Look for MCP-related errors in Cursor’s output

“Chant not initialized” Error

Symptoms: MCP tools return “Chant not initialized”

Solution: Run chant init --agent cursor in your project directory

Agent Fails to Start with chant work

Symptoms: chant work 001 fails immediately

Solutions:

  1. Check spec exists - Run chant list to verify
  2. Verify model provider - Check .chant/config.md for provider/model settings
  3. Check logs - Look in .chant/specs/2026-01-30-001-abc.log for errors

Cursor Doesn’t Follow Spec Guidelines

Symptoms: Cursor makes changes outside of specs or doesn’t commit properly

Solution: Ensure .cursorrules exists and is up to date. You can regenerate it:

# Backup existing file if you've customized it
cp .cursorrules .cursorrules.backup

# Regenerate
chant init --agent cursor --force

Git Commits Don’t Have Proper Format

Symptoms: Commits don’t follow chant(SPEC-ID): description pattern

Solution: Remind Cursor of the commit format in chat, or check that .cursorrules has the commit guidelines.

Common Mistakes

  1. Forgetting to restart Cursor after running chant init --agent cursor
  2. Trying to run chant work from Cursor’s AI - Use the terminal instead
  3. Editing specs directly while an agent is working them
  4. Not committing changes before switching specs

Tips for Success

  1. Write clear acceptance criteria in your specs
  2. Keep specs focused - One logical unit of work per spec
  3. Review agent output before merging to main
  4. Use Cursor’s AI for exploration and spec management, not execution
  5. Customize .cursorrules to match your project’s standards

Additional Resources

Prompt Guide

This guide covers prompt authoring, examples, and advanced techniques.

See also: Prompts Concept for basic usage, template variables reference, and built-in prompts overview.

Why Prompts Matter

Prompts are the core of Chant. Everything else is infrastructure.

Good prompt + mediocre spec = Good result
Bad prompt + perfect spec = Bad result

Anatomy of a Prompt

<!-- .chant/prompts/standard.md -->
---
name: standard
purpose: Default prompt for general tasks
---

# Role

You are a senior developer implementing a spec.

# Context

{{spec.description}}

# Instructions

1. Read relevant code first
2. Make minimal changes
3. Verify before committing

# Constraints

- Don't modify unrelated files
- Don't add dependencies without asking
- Keep changes focused

# Output

Commit your changes with message: chant({{spec.id}}): {{spec.title}}

The Five Sections

Every effective prompt has these sections:

1. Role

Who is the agent? This sets behavior and expertise level.

# Role

You are a senior developer with 10 years of experience.
You write clean, tested, maintainable code.
You prefer simplicity over cleverness.

2. Context

What does the agent need to know? Injected from spec.

# Context

## Spec
{{spec.description}}

## Target Files
{{spec.target_files}}

3. Instructions

What should the agent do? Step-by-step process.

# Instructions

## Phase 1: Understand
1. Read the target files
2. Understand the existing patterns

## Phase 2: Implement
3. Make changes
4. Follow existing code style

## Phase 3: Verify
5. Run tests
6. Check for lint errors

## Phase 4: Commit
7. Commit: git commit -m "chant({{spec.id}}): {{spec.title}}"

4. Constraints

What should the agent NOT do? Boundaries and guardrails.

# Constraints

- Only modify files related to the spec
- Don't refactor unrelated code
- Don't add new dependencies without approval
- Don't commit secrets

5. Output

What does “done” look like?

# Output

When complete:
1. All acceptance criteria met
2. Tests passing
3. Changes committed with proper message format

If you cannot complete:
1. Commit any partial progress
2. Document what's blocking
3. Exit with error

Template Variables

Prompts use {{variable}} syntax. See the template variables reference for the complete list of available variables and their descriptions.

Prompt Inheritance

Prompts can extend other prompts:

---
name: tdd
extends: standard
---

{{> parent}}

## TDD Requirements

Write tests before implementation.

The {{> parent}} marker indicates where the parent prompt content should be injected.


Built-in Prompts

See Prompts Concept: Built-in Prompts for a quick reference table of all built-in prompts.

bootstrap.md

The default prompt. Minimal and efficient - delegates to chant prep for instructions.

---
name: bootstrap
purpose: Minimal bootstrap prompt that defers to prep command
---

You are implementing spec {{spec.id}} for {{project.name}}.

Run this command to get your instructions:

chant prep {{spec.id}}


Follow the instructions returned by that command.

standard.md

Balanced approach for most tasks. Read → Plan → Implement → Verify → Commit.

---
name: standard
purpose: Default execution prompt
---

# Execute Spec

You are implementing a spec for {{project.name}}.

## Your Spec

**{{spec.title}}**

{{spec.description}}

## Instructions

1. **Read** the relevant code first
2. **Plan** your approach before coding
3. **Implement** the changes
4. **Verify** each acceptance criterion
5. **Commit** with message: `chant({{spec.id}}): <description>`

## Constraints

- Only modify files related to this spec
- Follow existing code patterns
- Do not refactor unrelated code

split.md

Break down large specs into members.

---
name: split
purpose: Break down specs into smaller pieces
---

# Split Spec

You are breaking down a spec into smaller, executable pieces.

## Driver Spec

**{{spec.title}}**

{{spec.description}}

## Instructions

1. Analyze the spec scope
2. Identify independent pieces of work
3. Create member specs that are:
   - Small enough to complete in one session
   - Clear acceptance criteria
   - Specific target files

tdd

Test-driven development workflow.

---
name: tdd
extends: standard
---

{{> parent}}

## TDD Cycle

### 1. RED - Write Failing Test
Write a test that fails because the feature doesn't exist.

### 2. GREEN - Make It Pass
Write minimum code to make the test pass.

### 3. REFACTOR - Clean Up
Improve code quality while keeping tests passing.

Prompt Patterns

The Loop

The fundamental execution pattern:

Repeat until done:
1. **Read** - Understand current state
2. **Plan** - Decide next action
3. **Change** - Make one change
4. **Verify** - Check it works
5. **Commit** - Save progress

Fail Fast

If you encounter a blocker:
1. Stop immediately
2. Commit any safe partial work
3. Document the blocker
4. Mark spec as failed

Don't guess. Don't work around. Fail clearly.

Minimal Change

Make the smallest change that satisfies the spec.

- Don't refactor adjacent code
- Don't fix unrelated issues
- Don't add "nice to have" features

Common Mistakes

Too Vague

# Bad
Do the spec well.

# Good
1. Read the target files
2. Implement the change
3. Write tests
4. Run existing tests
5. Commit with format: chant(ID): description

Too Rigid

# Bad
Always use exactly 4 spaces for indentation.

# Good
Follow the existing code style in the file.

No Failure Path

# Bad
Complete the spec.

# Good
Complete the spec.

If you cannot complete:
1. Commit partial progress
2. Document what's blocking
3. Exit with non-zero status

Scope Creep Invitation

# Bad
Improve the code quality while you're there.

# Good
Only modify code directly related to the spec.
Note other improvements as potential follow-up specs.

Prompt Library Organization

.chant/prompts/
├── standard.md          # Default for most tasks
├── tdd.md               # Test-driven development
├── security.md          # Security-sensitive changes
├── docs.md              # Documentation tasks
└── domain/
    ├── api.md           # API-specific
    └── frontend.md      # UI changes

Checklist: Is Your Prompt Ready?

  • Role defined
  • Context injection (spec body, target files)
  • Instructions numbered and phased
  • Verification step before completion
  • Constraints on scope and behavior
  • Failure handling documented
  • Commit format specified

Research Workflows Guide

Using chant for research: investigation, synthesis, data analysis, and reproducible findings.

Why Chant for Research?

Research has a reproducibility crisis:

  • Methods under-specified
  • Data not preserved
  • Analysis steps lost
  • Conclusions go stale

Chant addresses this:

  • Spec IS the method — Not a paper written after the fact
  • Execution is recorded — Agent logs every step
  • Verification is built-in — Re-run and compare
  • Drift detection — Know when inputs change

Quick Start

# Initialize chant
chant init

# Create a research spec
chant add "Analyze Q1 survey data"

Edit the spec:

---
type: research
prompt: research-analysis
origin:
  - data/q1-survey.csv
target_files:
  - analysis/q1-results.md
  - analysis/figures/q1-correlation.png
---
# Analyze Q1 survey data

## Methodology
- Descriptive statistics for all numeric columns
- Correlation analysis between satisfaction and tenure
- Theme coding for open-ended responses

## Research Questions
- [ ] What is the average satisfaction score?
- [ ] Does tenure correlate with satisfaction?
- [ ] What themes emerge from comments?

## Acceptance Criteria
- [ ] Analysis script runs without error
- [ ] All questions answered with data
- [ ] Visualizations generated
- [ ] Statistical significance noted

Run:

chant work 001

Research Patterns

Pattern: Synthesis

Synthesize multiple sources into findings:

---
type: research
prompt: research-synthesis
informed_by:
  - papers/smith2025.pdf
  - papers/jones2024.pdf
  - arxiv:2401.12345
target_files:
  - findings/lit-review.md
---
# Synthesize transformer efficiency research

## Research Questions
- [ ] What are the main efficiency approaches?
- [ ] Which show >50% improvement on benchmarks?
- [ ] What are the trade-offs?

## Acceptance Criteria
- [ ] 10+ papers reviewed
- [ ] Comparison table created
- [ ] Gaps in research identified

The informed_by: field lists materials to synthesize. Changes to these files trigger drift detection.

Pattern: Data Analysis

Analyze data files to generate findings:

---
type: research
prompt: research-analysis
origin:
  - data/experiment-results.csv
target_files:
  - analysis/experiment-findings.md
---
# Analyze experiment results

## Methodology
- Two-sample t-test for treatment vs control
- Effect size calculation (Cohen's d)
- Confidence intervals

## Acceptance Criteria
- [ ] p-value calculated
- [ ] Effect size reported
- [ ] 95% CI for mean difference
- [ ] Visualization of distributions

The origin: field declares input data. When data changes after analysis, drift is detected.

Pattern: Codebase Investigation

Research specs work for code analysis too:

---
type: research
prompt: research-analysis
informed_by:
  - src/**/*.rs
target_files:
  - analysis/tech-debt.md
---
# Investigate technical debt

## Research Questions
- [ ] Where are the TODO/FIXME comments?
- [ ] Which modules have highest complexity?
- [ ] What patterns are inconsistently applied?

## Acceptance Criteria
- [ ] All modules scanned
- [ ] Issues prioritized by impact
- [ ] Recommendations provided

Pattern: Library Comparison

Before choosing a dependency:

---
type: research
prompt: research-synthesis
informed_by:
  - https://docs.rs/serde
  - https://docs.rs/rkyv
target_files:
  - findings/serialization-comparison.md
---
# Compare serialization libraries

## Research Questions
- [ ] Performance characteristics?
- [ ] API ergonomics?
- [ ] Ecosystem support?

## Acceptance Criteria
- [ ] Both libraries evaluated
- [ ] Benchmarks compared
- [ ] Recommendation with rationale

Recurring Research

For recurring analysis, use the schedule: field to track cadence:

---
type: research
prompt: research-analysis
schedule: weekly
origin:
  - logs/production-*.json
target_files:
  - reports/weekly-errors.md
---
# Weekly error analysis

## Methodology
- Aggregate errors by type
- Identify new patterns
- Compare to previous week

## Acceptance Criteria
- [ ] All error types categorized
- [ ] Trends identified
- [ ] Actionable recommendations

The schedule: field documents intended recurrence (e.g., daily, weekly, monthly) but does not trigger automated execution.

Drift Detection

Data Drift

When origin: data changes:

$ chant verify 001
Spec 001 (research): DRIFT

Origin files changed since completion:
  - data/q1-survey.csv (modified 2026-01-25)

New data rows: 47 added since analysis

Recommendation: Re-run spec to update analysis

Source Drift

When informed_by: materials change:

$ chant verify 002
Spec 002 (research): DRIFT

Informed-by files changed since completion:
  - papers/smith2025.pdf (modified 2026-01-25)

Recommendation: Re-run spec to incorporate updates

Re-running Analysis

When drift is detected, re-run the spec to update findings:

$ chant reset 001
$ chant work 001

Compare the new results to the original to see what changed.

Verification Strategies

Objective Verification

Use acceptance criteria that can be checked programmatically:

## Acceptance Criteria
- [ ] Analysis script runs without error
- [ ] p-value < 0.05 for primary hypothesis
- [ ] All required visualizations exist
- [ ] Output matches expected schema

The agent can write validation scripts:

# Agent writes this during execution
import json

with open('analysis/results.json') as f:
    results = json.load(f)

assert results['p_value'] < 0.05, f"p-value {results['p_value']} not significant"
assert 'effect_size' in results, "Effect size not calculated"
print("Validation passed")

Subjective Verification

For qualitative research, criteria are inherently subjective:

## Acceptance Criteria
- [ ] All interviews coded
- [ ] Themes supported by 3+ quotes
- [ ] Negative cases discussed

These require human review, but drift detection still works.

Provenance

Every finding traces back to:

---
status: completed
completed_at: 2026-01-22T15:00:00Z
commit: abc123
origin:
  - data/survey.csv
informed_by:
  - papers/methodology.pdf
---

The spec itself is the audit trail.

Research Pipelines

Experiment Pipeline

# 001.md - Data collection
---
type: research
target_files: [data/experiment.csv]
---

# 002.md - Analysis (depends on collection)
---
type: research
depends_on: [001]
origin: [data/experiment.csv]
target_files: [analysis/results.md]
---

# 003.md - Write-up (depends on analysis)
---
type: research
depends_on: [002]
informed_by: [analysis/results.md]
target_files: [paper/methodology.md]
---

Reproducibility Check

---
type: research
origin:
  - data/original-study.csv
informed_by:
  - papers/original-paper.pdf
---
# Replicate Smith 2025 findings

Attempt to reproduce results from original paper.

## Acceptance Criteria
- [ ] Same methodology applied
- [ ] Results compared to original
- [ ] Discrepancies documented

Use Cases Summary

Use Caseinformed_by:origin:schedule:
Literature reviewpapers, docs
Log analysislog filesdaily
Codebase healthsrc/**/*.rsweekly
Performance reportprior reportsmetrics CSVweekly
Bug investigationrelated codeerror logs
Library comparisonlibrary docs
Survey analysismethodology docssurvey data

Limitations

Chant helps with:

  • Computational research
  • Data analysis
  • Literature synthesis
  • Code investigation

Chant doesn’t help with:

  • Wet lab work (but can analyze results)
  • Creative insight (but can synthesize)
  • Peer review (but can prepare submissions)
  • Data collection (but can analyze collected data)

See Also

Examples

Overview

Complete example configurations for different deployment scenarios.

Solo Developer

Single developer, simple workflow.

Directory Structure

my-project/
├── .chant/
│   ├── config.md
│   ├── specs/
│   │   └── 2026-01-22-001-x7m.md
│   └── prompts/
│       └── standard.md
└── src/

Config

# .chant/config.md
---
project:
  name: my-project

defaults:
  prompt: standard
---

# My Project

Simple workflow using worktrees for isolation.

Sample Spec

# .chant/specs/2026-01-22-001-x7m.md
---
status: pending
created: 2026-01-22
---

# Add user settings page

Create a settings page where users can update their profile.

## Acceptance Criteria

- [ ] Settings page accessible at /settings
- [ ] Users can update display name
- [ ] Users can change email
- [ ] Form validates input
- [ ] Changes persist to database

## Target Files

- src/pages/settings.tsx
- src/api/settings.ts

Workflow

chant add "Add user settings page"
chant work 2026-01-22-001-x7m
# Agent works, commits directly to main

Team Collaboration

Multiple developers with notifications.

Directory Structure

team-project/
├── .chant/
│   ├── config.md
│   ├── specs/
│   └── prompts/
│       ├── standard.md
│       └── tdd.md
└── src/

Config

# .chant/config.md
---
project:
  name: team-project

defaults:
  prompt: standard
---

# Team Project

Worktrees for isolation.

Workflow

# Developer creates spec
chant add "Implement OAuth login"

# Execute - creates branch
chant work 2026-01-22-001-x7m

# Team reviews changes

Enterprise Silent

Personal chant on corporate repo with derived fields.

Directory Structure

corporate-monorepo/
├── .gitignore                    # Includes .chant-local/
├── .chant-local/                 # Gitignored, personal only
│   ├── config.md
│   ├── specs/
│   └── prompts/
└── packages/
    ├── auth/
    └── payments/

Config

# .chant-local/config.md
---
project:
  name: corporate-silent

defaults:
  prompt: standard
  branch_prefix: "alex/"      # Personal prefix for worktrees

enterprise:
  derived:
    jira_key:
      from: branch
      pattern: "([A-Z]+-\\d+)"
    sprint:
      from: branch
      pattern: "sprint/(\\d{4}-Q\\d-W\\d)"
---

# Personal Chant Setup

Silent mode - not visible to team.
Branch naming extracts Jira keys automatically.

Workflow

# Initialize in silent mode
chant init --silent

# Work normally
chant add "AUTH-123: Fix token refresh"
chant work 2026-01-22-001-x7m
# Creates branch: alex/AUTH-123-fix-token-refresh
# Spec frontmatter auto-populated with jira_key: AUTH-123

Scale: Monorepo with K8s

Large monorepo with daemon, workers, metrics.

Directory Structure

monorepo/
├── .chant/
│   ├── config.md
│   ├── specs/
│   │   ├── auth-2026-01-22-001-x7m.md
│   │   └── payments-2026-01-22-001-abc.md
│   ├── prompts/
│   └── providers/
└── packages/
    ├── auth/
    ├── payments/
    └── frontend/

Config

# .chant/config.md
---
project:
  name: monorepo

defaults:
  prompt: standard

scale:
  id_prefix:
    from: path
    pattern: "packages/([^/]+)/"

  daemon:
    enabled: true
    metrics_port: 9090
    api_port: 8080

  worktree:
    sparse: true
    pool_size: 10

  limits:
    max_agents: 50
    max_per_project: 10
    spec_timeout: 30m

  costs:
    daily_limit_usd: 500.00
    auth:
      daily_limit_usd: 100.00
    payments:
      daily_limit_usd: 150.00
---

# Monorepo Configuration

Daemon mode enabled. Per-project limits.

K8s Deployment

# k8s/daemon.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chant-daemon
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: daemon
          image: chant:latest
          command: ["chant", "daemon", "start"]
          ports:
            - containerPort: 8080
            - containerPort: 9090
          volumeMounts:
            - name: repo
              mountPath: /repo
      volumes:
        - name: repo
          persistentVolumeClaim:
            claimName: repo-pvc
---
# k8s/worker.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: chant-worker-auth
spec:
  replicas: 3
  template:
    spec:
      containers:
        - name: worker
          image: chant:latest
          command: ["chant", "agent", "worker", "--project", "auth"]
          env:
            - name: CHANT_DAEMON
              value: "http://chant-daemon:8080"
            - name: AGENT_API_KEY
              valueFrom:
                secretKeyRef:
                  name: chant-secrets
                  key: api-key

Grafana Dashboard

# grafana/chant-dashboard.yaml
panels:
  - title: "Specs by Status"
    type: piechart
    query: "chant_specs_total"

  - title: "Active Workers"
    type: gauge
    query: "chant_agents_active"

  - title: "Completions/Hour"
    type: graph
    query: "rate(chant_agent_completions_total[1h]) * 3600"

  - title: "Cost Today"
    type: stat
    query: "chant_costs_usd_total"

Workflow

# Daemon running in K8s

# Add specs (from CI or developer)
chant add "Implement new auth flow" --project auth
chant add "Fix payment processing" --project payments

# Workers automatically pick up and execute
# Monitor in Grafana dashboard
# Notifications via Slack

Approval-Gated Team Development

Team with approval requirements for spec execution. See the Approval Workflow guide for complete documentation on the approval process, rejection handling modes, and workflow examples.


Self-Bootstrap

Building chant using chant.

Bootstrap Sequence

# 1. Manual init (chicken-egg)
mkdir -p .chant/tasks .chant/prompts
cat > .chant/config.md << 'EOF'
---
project:
  name: chant
---
# Chant
EOF

# 2. Create first spec manually
cat > .chant/specs/2026-01-22-001-x7m.md << 'EOF'
---
status: pending
---
# Implement chant init command
EOF

# 3. Implement init command (manually or with other agent)

# 4. Now use chant for everything
chant add "Implement chant add command"
chant work 2026-01-22-002-abc

chant add "Implement chant work command"
chant work 2026-01-22-003-def

# 5. Chant is now self-sustaining

Dogfooding Specs

chant add "Add spec parser"
chant add "Implement Tantivy search"
chant add "Add worktree isolation"
chant add "Implement daemon mode"
chant add "Add Prometheus metrics"
# ... all features as specs

Recovery

Things go wrong. Agents crash, tests fail, merges conflict. This guide walks through common failure scenarios and how to recover from each.

Scenario: Agent Fails a Spec

The most common failure. An agent runs, encounters a problem (tests fail, can’t meet acceptance criteria), and the spec ends up in failed status:

$ chant show 001

ID:     2026-02-08-001-xyz
Status: failed
Title:  Add CSV export handler

Check the log to understand what went wrong:

$ chant log 001

[2026-02-08 14:32:00] Running tests...
[2026-02-08 14:32:15] ✗ 2 tests failed
[2026-02-08 14:32:15] Agent exiting with failure

If the problem is in the spec (unclear requirements, wrong approach), edit it first:

$ chant edit 001

Then reset and retry:

$ chant reset 001
Spec 001-xyz reset to pending

$ chant work 001
Working 001-xyz: Add CSV export handler
...
✓ Completed in 1m 30s (attempt 2)

Or do both in one step:

$ chant reset 001 --work

The retry counter increments each time — the agent sees it’s on attempt 2 and can try a different approach.

Scenario: Agent Killed (OOM, SIGKILL)

Sometimes the agent process gets killed by the OS (memory pressure, too many Claude processes). The spec stays in_progress with no agent running, and a stale worktree may be left behind.

Diagnose the state:

$ chant diagnose 001

Spec: 001-xyz
Status: in_progress
Lock: stale (PID 12345 not running)
Worktree: exists at /tmp/chant-001-xyz
  Uncommitted changes: 3 files
Branch: chant/2026-02-08-001-xyz (ahead of main by 2 commits)

The worktree may contain useful partial work. If you want to preserve it, check the branch manually before cleanup:

$ git -C /tmp/chant-001-xyz log --oneline main..HEAD
abc1234 Add CSV formatter
def5678 Update export module

Reset the spec and retry — the agent gets a fresh worktree:

$ chant reset 001 --work

Clean up the orphaned worktree:

$ chant cleanup
Found 1 orphan worktree:
  /tmp/chant-001-xyz (spec: 001-xyz, stale)

Remove? [y/N] y
Cleaned 1 worktree

Scenario: Merge Conflict

After parallel execution, merging branches back to main can conflict if two specs touched the same files:

$ chant merge --all-completed

Merging 001-xyz... ✓
Merging 002-xyz... ✗ conflict in src/lib.rs

Resolve manually:
  cd /tmp/chant-002-xyz
  # fix conflicts
  git add src/lib.rs
  git commit
  # then retry merge

Or use rebase to replay on top of the first merge:

$ git -C /tmp/chant-002-xyz rebase main
# resolve conflicts
$ chant merge 002

The --no-merge flag on chant work --parallel skips auto-merge entirely, leaving branches for manual review:

$ chant work --parallel --no-merge

Scenario: Stale State After Reboot

Your machine reboots mid-work. Lock files, worktrees, and process records are left behind. On your next session:

$ chant cleanup --dry-run
Would remove:
  Worktree: /tmp/chant-001-xyz (stale, no running process)
  Worktree: /tmp/chant-003-xyz (stale, no running process)

$ chant cleanup --yes
Cleaned 2 worktrees

Any specs left in_progress need manual reset:

$ chant list --status in_progress

ID          Type  Status       Title
001-xyz     code  in_progress  Add CSV export handler
003-xyz     code  in_progress  Fix edge case

$ chant reset 001
$ chant reset 003

Then re-execute:

$ chant work --chain

Scenario: Wrong Approach

The agent completed work, but the approach is wrong — you want to redo it differently. Edit the spec with guidance and reset:

$ chant edit 001
# Add: "Use pessimistic locking, not optimistic. See src/lock.rs for the existing Lock module."

$ chant reset 001 --work

The agent sees the updated spec and takes the new direction.

Recovery Principles

PrincipleHow chant implements it
Never lose dataGit tracks everything — branches, commits, worktree state
Make recovery explicitreset is deliberate, not automatic
Preserve partial workBranches survive agent crashes; inspect before cleanup
Fail fast in parallelChain mode stops on first failure
Track attemptsRetry counter in frontmatter lets agents adapt

Key Commands

CommandWhen to use
chant reset <id>Reset a failed/stuck spec to pending
chant reset <id> --workReset and immediately re-execute
chant cleanupRemove orphan worktrees and stale artifacts
chant cleanup --dry-runPreview what would be cleaned
chant diagnose <id>Inspect a spec’s state (lock, worktree, branch)
chant log <id>Read the agent’s execution log

Further Reading

Troubleshooting

Common issues with chant and their solutions.

Installation & Setup

macOS code signing: cargo install builds get killed

Symptom: After running cargo install chant, the binary gets killed when you try to run it.

Cause: macOS code signing verification fails for unsigned binaries built from source.

Fix:

codesign -f -s - $(which chant)

chant init fails in non-git repo

Symptom: Running chant init produces an error about not being in a git repository.

Cause: Chant requires a git repository to track spec history and branches.

Fix:

git init
git commit --allow-empty -m "Initial commit"
chant init

Shell completion not loading

Symptom: Tab completion doesn’t work for chant commands.

Cause: Shell completion script not installed or not sourced in your shell config.

Fix:

For bash:

chant completions bash >> ~/.bashrc
source ~/.bashrc

For zsh:

chant completions zsh >> ~/.zshrc
source ~/.zshrc

For fish:

chant completions fish > ~/.config/fish/completions/chant.fish

Spec Execution

Agent exits with code 137 (SIGKILL)

Symptom: chant work fails with exit code 137, or agent process disappears without error.

Cause: System killed the process due to memory pressure. This often happens when too many Claude processes are running in parallel.

Fix:

# config.md
parallel:
  max_workers: 2  # Reduce from default

Or run specs sequentially:

chant work <id>  # Don't use --parallel

Spec stuck in in_progress with no running agent

Symptom: chant list shows a spec as in_progress, but no agent is actually running.

Cause: Agent crashed or was killed without cleaning up its lock file.

Fix:

Check if an agent is actually running:

chant diagnose <id>

If no agent is running, either take over or reset:

# Take over to analyze partial work
chant takeover <id>

# Or reset to start fresh
chant reset <id>

Chain mode stops after first spec

Symptom: Running chant work <id> --chain completes one spec but doesn’t continue to the next ready spec.

Cause: Known issue with finalization not triggering chain continuation in some cases.

Fix:

Check if the work actually completed:

chant list
chant diagnose <id>

If spec is completed, manually start the next one:

chant ready  # Find next ready spec
chant work <next-id> --chain

chant work fails with “not a git repository” in worktree

Symptom: Agent starts but immediately fails with git-related errors, or can’t find .git directory.

Cause: Stale worktree from previous run that wasn’t cleaned up properly.

Fix:

# Manually remove stale worktree
rm -rf /tmp/chant-<spec-id>
git worktree prune

Parallel Mode

Specs fail immediately with no log/branch/error

Symptom: When running chant work --parallel, some specs transition to failed instantly with no error message, log file, or git branch.

Cause: Race condition in worktree creation (fixed in chant 0.18.2+).

Fix:

Upgrade to chant 0.18.2 or later:

cargo install chant --force

Or run specs sequentially instead:

chant work <id>  # Without --parallel

API rate limiting errors in parallel mode

Symptom: Multiple specs fail with rate limit errors when running in parallel.

Cause: Too many concurrent API requests to Claude API.

Fix:

# config.md
parallel:
  max_workers: 3  # Reduce from default (5)

Or add delays between worker starts:

parallel:
  stagger_delay: 5s  # Wait 5s between starting workers

Git & Merge

Merge conflicts in spec frontmatter

Symptom: Git merge conflicts in .chant/specs/*.md files, particularly in the YAML frontmatter.

Cause: Multiple specs modifying the same spec file simultaneously (common in parallel mode).

Fix:

Install the chant merge driver:

chant init --merge-driver

This configures git to use chant’s custom merge strategy for spec files.

If conflicts still occur, resolve manually:

git mergetool
# Or edit the file directly, keeping the most recent status/metadata

git mv fails for .chant/ files

Symptom: Running git mv on files in .chant/ directory produces an error.

Cause: Some files in .chant/ (like logs, status files) are not tracked by git.

Fix:

Use regular mv instead:

mv .chant/specs/old-id.md .chant/specs/new-id.md
git add .chant/specs/new-id.md
git rm .chant/specs/old-id.md

Archive fails

Symptom: chant archive <partial-id> fails to find the spec or produces an error.

Cause: Ambiguous partial ID matching multiple specs, or trying to archive non-completed spec.

Fix:

Use the full spec ID:

chant archive 2026-02-07-00x-x5v  # Full ID, not just "00x"

Verify spec is completed first:

chant list | grep <id>

Watch & Recovery

Watch not detecting completed specs

Symptom: chant watch is running, but doesn’t detect when a spec completes in its worktree.

Cause: Spec didn’t write .chant-status.json in the worktree, or watch process isn’t polling.

Fix:

Check if status file exists:

cat .chant/.worktrees/<spec-id>/.chant-status.json

If missing, the agent may have crashed. Check logs:

chant log <spec-id>

Restart watch:

chant watch stop
chant watch start

Stale worktrees accumulating

Symptom: /tmp/chant-* directories accumulate from failed specs.

Cause: Worktrees are only cleaned up after successful merge. Failed specs preserve worktrees for debugging.

Fix:

# Manually remove worktree for failed spec
rm -rf /tmp/chant-<spec-id>
git worktree prune

Debugging Steps

How to check agent logs

Agent logs are written to .chant/logs/<spec-id>.log:

# View full log
cat .chant/logs/2026-02-07-00x-x5v.log

# View last 50 lines
tail -n 50 .chant/logs/2026-02-07-00x-x5v.log

# Follow log in real-time
tail -f .chant/logs/2026-02-07-00x-x5v.log

# Or use chant command
chant log 00x --lines 50

How to check spec status

# List all specs with status
chant list

# Get summary
chant status

# Diagnose specific spec
chant diagnose <spec-id>

The diagnose command checks:

  • Spec file validity
  • Lock file status
  • Log file existence and activity
  • Git branch status
  • Acceptance criteria

How to inspect worktree state

# List all worktrees
git worktree list

# Check status in specific worktree
git -C .chant/.worktrees/<spec-id> status

# View worktree commits
git -C .chant/.worktrees/<spec-id> log

How to recover from broken state

For a spec stuck in a bad state:

  1. Diagnose the issue:

    chant diagnose <spec-id>
    
  2. If agent is running but stuck:

    chant takeover <spec-id>  # Analyze partial work
    
  3. If agent crashed:

    chant reset <spec-id>  # Clear state and start fresh
    
  4. If worktree is corrupted:

    rm -rf /tmp/chant-<spec-id>
    git worktree prune
    chant reset <spec-id>
    
  5. If spec file is corrupted:

    # Edit manually
    vim .chant/specs/<spec-id>.md
    
    # Validate
    chant diagnose <spec-id>
    

For general repository issues:

# Verify all completed specs
chant verify --all

# Check for recovery-needed specs
chant recover --check

Approval Workflow

Your team is building a payments integration. Sarah creates a spec to refactor the auth system — a change that touches security-critical code. Before an agent touches anything, someone needs to sign off. This is what the approval workflow is for.

Adding an Approval Gate

Sarah creates the spec with --needs-approval:

$ chant add "Refactor authentication to support OAuth2" --needs-approval
Created spec: 2026-01-28-001-abc

The spec is created with approval.required: true and approval.status: pending in frontmatter:

---
type: code
status: pending
approval:
  required: true
  status: pending
---

If she tries to execute it now, chant blocks:

$ chant work 001
Error: Spec requires approval before work can begin

  Approval status: pending

  To approve:  chant approve 001 --by <name>
  To bypass:   chant work 001 --skip-approval

Review and Approve

The tech lead reviews the spec and approves:

$ chant approve 001-abc --by marcos

Chant validates the approver name against git committers (warns if not found), updates the frontmatter, and appends a timestamped entry to the spec’s “## Approval Discussion” section:

## Approval Discussion

**marcos** - 2026-01-28 14:30 - APPROVED

The frontmatter now reads:

approval:
  required: true
  status: approved
  by: marcos
  at: 2026-01-28T14:30:45Z

Now chant work 001 proceeds normally.

Rejection

But sometimes the spec isn’t ready. Marcos reads the spec and sees it’s too broad:

$ chant reject 001-abc --by marcos --reason "Scope too large — split auth token handling from session management"

The rejection is recorded in the approval discussion:

## Approval Discussion

**marcos** - 2026-01-28 10:15 - REJECTED
Scope too large — split auth token handling from session management

The spec can’t be worked until the issues are addressed. Sarah edits the spec, narrows the scope, and Marcos approves the revised version:

$ chant edit 001
# Narrow scope to just OAuth2 token handling

$ chant approve 001-abc --by marcos

The discussion section now has the full history — rejection reason, then approval — all tracked in git.

Rejection Handling Modes

By default, rejection is manual — the author reads the feedback and edits the spec. But you can configure automatic responses to rejection:

Dependency Mode

# .chant/config.md
approval:
  rejection_action: dependency

When a spec is rejected, chant automatically creates a fix spec and blocks the original on it. Useful when rejection identifies prerequisite work — “you need to add the token refresh endpoint first.”

Group Mode

approval:
  rejection_action: group

When a spec is rejected, chant converts it to a driver with member specs, distributing the acceptance criteria. Useful when the rejection is “this is too big, split it up.”

Finding Specs by Approval Status

# Specs waiting for review
$ chant list --approval pending

# Rejected specs that need attention
$ chant list --approval rejected

# Find specs where marcos participated
$ chant list --mentions marcos

# Specs with recent activity
$ chant list --activity-since 2h

In list output, approval status shows as colored indicators:

✓ 001-abc [approved]       Refactor auth           👤 sarah ↩ 1h 💬 2 ✓ marcos
⚠ 002-def [needs approval] Add rate limiting       👤 sarah ↩ 30m
✗ 003-ghi [rejected]       Rewrite session layer   👤 dave  ↩ 2h 💬 4

Agent Co-Authorship Detection

When require_approval_for_agent_work is enabled, chant automatically adds an approval gate to any spec where the agent’s commits include co-authorship signatures (Co-Authored-By: Claude, etc.):

# .chant/config.md
approval:
  require_approval_for_agent_work: true

The workflow becomes:

$ chant work 001
# Agent completes work, commits include Co-Authored-By: Claude
# During finalization: "⚠ Agent co-authorship detected. Approval required before merge."

$ chant approve 001 --by marcos
# Now merge proceeds

This ensures a human reviews all agent-written code before it reaches main. Detected agents include Claude, GPT/ChatGPT, Copilot, and Gemini.

Solo Developer Self-Review

Approval gates aren’t just for teams. For risky changes, use them as a forced thinking checkpoint:

$ chant add "Migrate database schema" --needs-approval

# Come back after coffee, review with fresh eyes
$ chant show 001
# Think about rollback plan, edge cases, data loss risk

$ chant approve 001 --by me
$ chant work 001

Emergency Bypass

When the approval process would cause unacceptable delays:

$ chant work 001 --skip-approval

Use sparingly. The bypass is visible in the execution log.

Further Reading

Open Source Maintainer Workflow

Command examples and output are illustrative – your exact output will differ.

The Scenario

You maintain kvstore, a Rust CLI tool for key-value storage. A user files issue #1234: concurrent writes to the same key silently lose data. Two CLI processes writing simultaneously should both succeed, but one write vanishes without an error.

This is the kind of issue that tempts you to grep for the write path and add a mutex. But a hasty fix without proper investigation leads to incomplete solutions, regressions, and poor documentation for future maintainers. Instead, you’ll use chant to work through the issue systematically – understanding before acting.

The Research-First Approach

The workflow moves through six phases, each producing a spec that feeds the next:

Comprehension --> Reproduction --> Root Cause --> Impact Map --> Fork Fix --> Upstream PR
   (research)       (task)        (research)    (research)     (code)     (human gate)

Each phase is a separate spec. Research specs produce documents that inform later phases. The chain creates an auditable trail from issue report to merged fix. (Spec IDs in this guide aren’t sequential — 001, 004, 005, 008, 010 — because real investigations create additional specs along the way: decomposition experiments, exploratory tangents, discarded approaches. The gaps are normal.)

Why separate phases?

A single “fix the bug” spec would leave no record of what was investigated, what was ruled out, or why this approach was chosen over alternatives. Separate specs mean:

  • Auditability. When someone asks “why was this fixed with locking instead of CAS?”, the root cause spec explains the reasoning.
  • Resumability. If the agent fails during root cause analysis, you reset that one spec – not the entire investigation.
  • Collaboration. One maintainer can do comprehension, another can pick up root cause, each with full context.

When to skip phases

For trivial bugs – typos, obvious one-liners, clear documentation errors – go straight to implementation:

$ chant add "Fix typo in README storage section"
Created spec: 2026-02-08-001-abc
$ chant work 001

If comprehension reveals the report is not a bug or won’t be fixed, document the finding and stop. Not every issue needs all six phases.

The Investigation

The rest of this guide follows the kvstore concurrent write bug through each phase:

  1. Setup – Configure silent mode for working on a shared repo
  2. Comprehension – Understand what the issue is about
  3. Reproduction – Create a failing test that proves the bug
  4. Root Cause – Find out why data is lost
  5. Impact Map – Discover what else is affected
  6. Fork Fix – Implement the fix and create a staging PR
  7. Upstream PR – Human reviews and submits upstream
  8. Advanced Patterns – Single-spec mode, pausing, takeover (file is 08-advanced.md because an earlier draft phase was removed)

How Specs Connect

Research specs pass knowledge forward through informed_by and target_files:

# Comprehension produces a research document
target_files:
  - .chant/research/issue-1234-comprehension.md

# Root cause reads the comprehension output
informed_by:
  - .chant/research/issue-1234-comprehension.md
target_files:
  - .chant/research/issue-1234-root-cause.md

# Implementation reads all research
informed_by:
  - .chant/research/issue-1234-root-cause.md
  - .chant/research/issue-1234-impact-map.md

Each spec is a self-contained unit of work with its own acceptance criteria, but the informed_by chain ensures nothing is lost between phases.

See Also

Phase 0: Setup

Before starting the investigation, you configure chant for working on a shared open source repository. The key decision: keep your specs local so they don’t clutter the project’s git history.

Silent Mode

$ chant init
Initialized .chant/ directory

$ chant silent
Silent mode enabled. .chant/ added to .git/info/exclude.

Silent mode adds .chant/ to your local git exclude file. Your specs, research documents, and execution logs stay on your machine. When you eventually submit a pull request, it contains only the code changes – no .chant/ artifacts.

This matters because your investigation trail is personal workflow. The upstream project doesn’t need to see your hypothesis elimination table or your three failed reproduction attempts. They need a clean fix with tests.

What Silent Mode Does

  1. Adds .chant/ to .git/info/exclude (local-only gitignore)
  2. Suppresses warnings about untracked spec files
  3. All chant functionality works normally
  4. Nothing from .chant/ appears in git status or commits

When Not to Use Silent Mode

If you’re the sole maintainer and want specs tracked in the repository, or if your team has agreed to share specs, skip chant silent. The default behavior tracks specs in git, which is useful for team workflows where investigation history should be shared.

Configuration

Your .chant/config.md for this workflow:

defaults:
  silent: true
  main_branch: "main"

If you’re working on a fix branch instead of main, set main_branch to your branch name so worktree merges land in the right place.

With setup complete, you’re ready to start investigating issue #1234.

Next: Comprehension

Phase 1: Comprehension

Before reproducing the bug or guessing at fixes, you need to understand what the user is actually experiencing. Comprehension is about mapping the territory – identifying where to look, not diagnosing what’s broken.

Starting the Investigation

You create a research spec for the comprehension phase:

$ chant add "Comprehension: issue #1234 data loss on concurrent writes"
Created spec: 2026-02-08-001-r4x

You edit the spec to set it up as a research task. The target_files field tells the agent where to write its findings:

---
type: research
labels: [comprehension, issue-1234]
informed_by:
  - https://github.com/yourproject/kvstore/issues/1234
target_files:
  - .chant/research/issue-1234-comprehension.md
---

Then you run it:

$ chant work 001
Working 001-r4x: Comprehension: issue #1234 data loss on concurrent writes
> Agent working in worktree /tmp/chant-001-r4x
...
Completed in 1m 30s

What the Agent Produces

The agent reads the issue thread, skims the relevant source files, checks for prior related issues, and writes a comprehension document:

# Comprehension: Issue #1234

## Issue Type
Bug - User reports data loss during concurrent write operations

## Observable Symptom
When two CLI processes write to the same key simultaneously,
one write is silently lost. No error messages. The key contains
the value from one writer, but the other writer's data vanishes.

## Affected Components
| Component | Files | Relevance |
|-----------|-------|-----------|
| Storage write path | src/storage/store.rs | Primary write logic |
| Concurrency handling | src/storage/concurrent.rs | Lock mechanisms |
| CLI write command | src/cli/write.rs | Entry point |

## Missing Information
- Exact sequence of CLI commands that triggers it
- Whether data is completely lost or partially corrupted
- Frequency (every time? intermittent?)

At this point you know what to investigate, but not why it happens. That distinction matters. Comprehension gives you the map; root cause analysis is the expedition.

The Decomposition Gate

Sometimes comprehension reveals that a single issue report contains multiple distinct bugs. The user filed one issue about “data loss in concurrent scenarios,” but the agent’s review finds three separate problems: a race condition in the storage layer, missing input validation in the CLI, and incorrect retry logic.

If these are truly independent bugs with different root causes, you decompose:

$ chant add "Comprehension: #1234 race condition in storage"
Created spec: 2026-02-08-002-abc

$ chant add "Comprehension: #1234 CLI input validation"
Created spec: 2026-02-08-003-def

Each gets its own investigation chain. You pursue one at a time, starting with the most severe.

If the symptoms look different but stem from the same root cause – say, three write failure modes all caused by the same missing lock – that’s one bug with multiple symptoms. Don’t decompose; keep it as a single investigation.

When to Stop Early

Comprehension may reveal there’s nothing to fix:

  • Not a bug. The reported behavior is working as designed. Document the finding, close the issue with an explanation.
  • Can’t action. The fix would require breaking changes that conflict with stability guarantees. Document the trade-off, suggest a workaround.

In either case, the comprehension spec still has value as a record of what was investigated and why the decision was made.

Collapsing Later Phases

After comprehension, if the root cause is already obvious – say, the agent found an unlocked read-modify-write cycle in plain sight – you can combine reproduction and root cause into a single research spec. But when in doubt, keep phases separate. Thorough research is easier to build on than incomplete research.

Next: Reproduction

Phase 2: Reproduction

With the comprehension document identifying the affected code areas, you now need to prove the bug exists in your environment. A failing test serves as both confirmation and as the success criterion for the eventual fix.

Creating the Reproduction Spec

$ chant add "Reproduce issue #1234: data loss on concurrent writes"
Created spec: 2026-02-08-004-m2p

You edit the spec as a task that references the comprehension output:

---
type: task
prompt: reproduce
labels: [reproduction, issue-1234]
informed_by:
  - .chant/specs/2026-02-08-001-r4x.md
  - .chant/research/issue-1234-comprehension.md
target_files:
  - tests/regression/issue_1234_test.rs
---

The informed_by chain is important here. The agent reads the comprehension document to know which files and components to focus on, rather than starting from scratch.

$ chant work 004
Working 004-m2p: Reproduce issue #1234: data loss on concurrent writes
> Agent working in worktree /tmp/chant-004-m2p
...
Completed in 2m 10s

What the Agent Produces

The agent writes a minimal failing test:

#![allow(unused)]
fn main() {
#[test]
fn issue_1234_concurrent_write_loses_data() {
    // Reproduction for: https://github.com/yourproject/kvstore/issues/1234
    // Environment: macOS 14.2, version 0.5.2
    let store = Store::new_temp();
    store.write("key", "initial").unwrap();

    let handle1 = store.write_async("key", "value1");
    let handle2 = store.write_async("key", "value2");

    handle1.join().unwrap();
    handle2.join().unwrap();

    let result = store.read("key").unwrap();
    assert!(
        result == "value1" || result == "value2",
        "Expected one of the written values, got: {result}"
    );
}
}

Running it confirms the bug:

running 1 test
test regression::issue_1234_concurrent_write_loses_data ... FAILED

failures:
    Expected one of the written values, got: valu

The truncated value valu is telling. This isn’t just a last-write-wins ordering issue – data is being partially overwritten. The write operation is not atomic.

Automatic vs Assisted Reproduction

The approach above is automatic reproduction: the agent writes and runs a test. For bugs that can’t be captured in an automated test – UI issues, environment-specific problems, hardware-dependent behavior – the agent instead produces reproduction instructions that a human follows.

When Reproduction Fails

Three outcomes when you can’t reproduce:

Environment mismatch. The bug depends on a specific OS, filesystem, or configuration you don’t have. Ask the user for more details or debug logs.

Flaky reproduction. The bug appears intermittently. For a race condition like this one, a stress test with multiple iterations often catches it:

#![allow(unused)]
fn main() {
#[test]
fn issue_1234_race_condition_stress() {
    for _ in 0..100 {
        let store = Store::new_temp();
        let handles: Vec<_> = (0..10)
            .map(|i| {
                let s = store.clone();
                std::thread::spawn(move || s.write("key", &format!("v{i}")))
            })
            .collect();
        for h in handles { h.join().unwrap().unwrap(); }
        let result = store.read("key").unwrap();
        assert!(result.starts_with("v"), "Data corrupted: {result}");
    }
}
}

User error. The reported behavior is caused by misconfiguration or outdated software. Document the finding, suggest the fix, and consider improving error messages or documentation.

Documenting Incrementally

As the agent works through reproduction, it should update its findings as they emerge rather than writing everything at the end. This creates an accurate paper trail and prevents loss of observations if the agent’s context is interrupted. The reproduction spec captures not just the final test, but what was tried along the way.

Next: Root Cause

Phase 3: Root Cause

You have a failing test that proves the bug exists. Now you need to find out why it happens. This is the most demanding phase of the investigation – and the most valuable. A thorough root cause analysis prevents fixing symptoms while the real bug remains.

Starting Root Cause Research

$ chant add "Root cause: issue #1234 concurrent write data loss"
Created spec: 2026-02-08-005-k9w

The spec references everything learned so far:

---
type: research
labels: [root-cause, issue-1234]
informed_by:
  - .chant/specs/2026-02-08-001-r4x.md
  - .chant/specs/2026-02-08-004-m2p.md
  - tests/regression/issue_1234_test.rs
  - src/storage/store.rs
  - src/storage/concurrent.rs
target_files:
  - .chant/research/issue-1234-root-cause.md
---
$ chant work 005
Working 005-k9w: Root cause: issue #1234 concurrent write data loss
> Agent working in worktree /tmp/chant-005-k9w
...
Completed in 3m 45s

The Investigation

Most of the agent’s time is spent forming, testing, and eliminating hypotheses. This loop – hypothesize, test, record, iterate – is where the real detective work happens. Each eliminated hypothesis narrows the search and documents what doesn’t cause the issue, which is as valuable as finding what does.

The agent’s root cause document captures this process:

# Root Cause Analysis: Issue #1234

## Hypotheses Tested

| Hypothesis | Evidence | Result |
|------------|----------|--------|
| Filesystem cache coherency | Tested with direct I/O, disabled caching | Eliminated |
| Buffer overflow in write path | Buffer size checks, memory sanitizer | Eliminated |
| Lock timeout causing skip | Instrumented lock acquisition timing | Eliminated |
| Version counter race | Added logging for version assignment | Confirmed |

Four hypotheses tested, three eliminated. The version counter race is the culprit.

The Root Cause

The agent traces the exact failure sequence:

## What Happens

1. Thread A calls write("key", "value1")
2. Thread A reads current value, gets version 5
3. Thread B calls write("key", "value2")
4. Thread B reads current value, also gets version 5
5. Thread A writes with version 6
6. Thread B writes with version 6 (should be 7)
7. Thread B's write overwrites Thread A's without detection

## Why It Happens

The write() method in src/storage/store.rs:145 uses optimistic locking
that assumes writes are serialized at the filesystem level. This
assumption breaks under buffered I/O with concurrent writers.

The relevant code:

#![allow(unused)]
fn main() {
// src/storage/store.rs:145-150
fn write(&self, key: &str, value: &str) -> Result<()> {
    let current = self.read(key)?;      // Not locked
    let version = current.version + 1;
    // <-- Window where another write can interleave
    self.persist(key, value, version)    // May conflict
}
}

Proposed Approaches

A good root cause analysis doesn’t just find the bug – it evaluates multiple fix strategies:

## Approach 1: Pessimistic Locking
Acquire exclusive lock before read-modify-write.
+ Simple, uses existing Lock module
+ Guarantees correctness
- Reduces write throughput
Estimated: ~10 lines changed in store.rs

## Approach 2: Compare-and-Swap
Atomic CAS at the persistence layer.
+ Higher throughput, no deadlock risk
- More complex, needs retry logic
Estimated: ~50 lines across store.rs and persist.rs

## Approach 3: Write-Ahead Log
Log all writes, apply in order.
+ Preserves all writes, enables recovery
- Significant complexity, overkill for this use case
Estimated: ~200+ lines, new module

## Recommendation: Approach 1

Pessimistic locking is the simplest correct solution. It uses
the existing Lock module, and write throughput is not a critical
requirement per docs/architecture/storage.md. Can migrate to CAS
later if performance becomes an issue.

The recommendation is justified by the analysis, not by gut feeling. Future maintainers can read this document and understand why locking was chosen over CAS.

When to Pivot

Investigation can hit dead ends. If you’ve tested several hypotheses and none are narrowing the search, step back:

  • Re-read the issue thread with fresh eyes. Details you dismissed initially may be critical.
  • Broaden the search to adjacent modules, callers, or dependencies.
  • Verify the reproduction test actually matches the reported symptoms.
  • Check environmental factors: configuration, platform differences, timing.

The goal is deliberate re-orientation when progress stalls, not endless persistence on an unproductive path.

When Research Reveals Complexity

Sometimes root cause analysis shows the fix is bigger than expected. If the bug exists in multiple places or requires foundational changes first, split the implementation:

$ chant add "Refactor Lock module for reentrant locking"
Created spec: 2026-02-08-006-abc

$ chant add "Fix issue #1234 using reentrant locks"
Created spec: 2026-02-08-007-def

The second spec depends on the first. Chant’s dependency tracking ensures they execute in order.

Next: Impact Map

Codebase Sprawl Research

Phase 5: Fork Fix

Four phases of research have produced a clear picture: the bug, the fix strategy, and every file that needs to change. Now you implement.

Creating the Implementation Spec

$ chant add "Fix issue #1234: add locking to concurrent writes"
Created spec: 2026-02-08-010-v7b

The spec references all research outputs:

---
type: code
labels: [fix, issue-1234]
depends_on: [008-j3n]
informed_by:
  - .chant/research/issue-1234-root-cause.md
  - .chant/research/issue-1234-impact-map.md
target_files:
  - src/storage/store.rs
  - src/storage/batch.rs
  - tests/storage/concurrent_test.rs
  - docs/architecture/storage.md
---
$ chant work 010
Working 010-v7b: Fix issue #1234: add locking to concurrent writes
> Agent working in worktree /tmp/chant-010-v7b
...
Completed in 2m 30s

What the Agent Implements

The agent reads the root cause analysis, which recommended pessimistic locking. It follows the recommendation:

#![allow(unused)]
fn main() {
fn write(&self, key: &str, value: &str) -> Result<()> {
    // Pessimistic lock prevents data loss during concurrent writes.
    // See: .chant/research/issue-1234-root-cause.md
    let _guard = self.lock.acquire(key)?;
    let current = self.read(key)?;
    let version = current.version + 1;
    self.persist(key, value, version)
}
}

The same fix goes into batch.rs, the second location identified by the impact map. The agent also adds concurrency tests beyond the original reproduction test, covering edge cases the research identified: lock timeouts, partial failures, and cross-process writes.

The reproduction test now passes:

running 3 tests
test regression::issue_1234_concurrent_write_loses_data ... ok
test storage::concurrent_write_stress ... ok
test storage::concurrent_batch_write ... ok

Why Fork-Internal Staging?

For open source work, the agent’s changes land in your fork, not upstream. After the agent finishes and its worktree merges to your local main, you manually create a staging PR within your fork (yourfork:fix/issue-1234 to yourfork:main) to review the changes before exposing them upstream:

$ gh pr create \
  --repo yourusername/kvstore \
  --base main \
  --head fix/issue-1234 \
  --title "Fix #1234: Data loss on concurrent writes" \
  --body "$(cat <<'EOF'
## Summary
Research-backed fix using pessimistic locking for concurrent write safety.

## Changes
- Added locking to write path in store.rs and batch.rs
- Added concurrency stress tests
- Updated architecture documentation

## Testing
- Regression test passes
- New concurrency tests pass
- All existing tests pass
EOF
)"

This staging PR lets you:

  • Run CI in your fork before going upstream
  • Iterate on the fix without upstream visibility
  • Review agent output before it becomes a public contribution

Following the Research

The implementation should match what the research recommended. If the agent discovers the recommended approach won’t work during implementation – say, the Lock module doesn’t support reentrant locking for nested writes – the right response is to stop, document the finding, and create a new research spec:

$ chant add "Re-research #1234: pessimistic locking insufficient"
Created spec: 2026-02-08-011-abc

Don’t improvise a different approach mid-implementation. The research exists for a reason.

Keeping Changes Focused

The diff should contain only what the research identified. No unrelated refactoring, no “while I’m here” improvements. A focused PR is easier to review, easier to revert if needed, and easier for upstream maintainers to understand.

Next: Upstream PR

Phase 6: Upstream PR

The staging PR in your fork looks good. CI passes, the fix is clean, the tests are comprehensive. Now a human – you – creates the real pull request to the upstream project.

The Human Gate

This is the one phase the agent doesn’t do. You review the staging PR, decide the timing is right, and create the upstream PR:

$ gh pr create \
  --repo upstream-org/kvstore \
  --base main \
  --head yourusername:fix/issue-1234 \
  --title "Fix #1234: Data loss on concurrent writes" \
  --body "$(cat <<'EOF'
## Summary

Fixes #1234. Concurrent writes to the same key could silently lose data
because the storage layer's optimistic locking didn't serialize the
read-modify-write cycle. Added pessimistic locking to both the single-write
and batch-write paths.

## Changes

- `src/storage/store.rs` -- Lock acquired before write operation
- `src/storage/batch.rs` -- Same fix for batch writes
- `tests/storage/concurrent_test.rs` -- Concurrency stress tests
- `tests/regression/issue_1234_test.rs` -- Regression test
- `docs/architecture/storage.md` -- Updated concurrency model

## Testing

All existing tests pass. Added regression test and concurrency stress tests.
EOF
)"

Why a Human Gate?

The agent did the investigation and implementation, but humans make better decisions about:

  • Timing. Don’t submit during a code freeze or right before a release.
  • Communication. Write the PR description in terms upstream maintainers understand, not in terms of your internal research process.
  • Scope. Decide whether to bundle related fixes or submit them separately.
  • Relationship. Maintain your standing with the upstream project.

After Submission

Monitor the upstream PR. Address reviewer feedback by updating the staging PR first, then pushing to the upstream branch. If the upstream maintainers request a fundamentally different approach, that’s a new research cycle.

Archiving the Investigation

Once the upstream PR is merged, archive the specs:

$ chant archive 001
$ chant archive 004
$ chant archive 005
$ chant archive 008
$ chant archive 010

The investigation trail moves to .chant/archive/ but remains available if a similar issue surfaces later.

Next: Advanced Patterns

Advanced Patterns

Working on Fix Branches

If you’re developing the fix on a dedicated branch instead of main, configure chant to merge worktree results there:

# .chant/config.md
defaults:
  main_branch: "fix/issue-1234"

Now chant work merges completed specs into your fix branch rather than main.

Pausing and Taking Over

Pausing Work

If you need to stop an agent mid-investigation – say, you realize it needs information you haven’t provided – pause it:

$ chant pause 005
Paused spec 005-k9w. Agent stopped, progress preserved.

The spec moves to paused status. Resume later with chant work 005.

Taking Over

If the agent is heading in the wrong direction, take over the spec entirely:

$ chant takeover 005
Stopping agent for 005-k9w...
Analyzing execution log...

Progress summary:
- Tested 3 hypotheses (all eliminated)
- Currently investigating filesystem cache coherency
- No root cause identified yet

Spec updated with progress notes and suggested next steps.

Takeover stops the agent, reads its log, and updates the spec body with a summary of what was accomplished and what remains. You can then fix the approach manually or edit the spec and re-run chant work 005.

Single-Spec Investigation Mode

The full six-phase workflow provides excellent auditability and enables handoffs between people. But for a solo investigator working one issue in a single session, six specs can feel ceremonial.

Single-spec mode consolidates the research phases into one document with stage markers:

$ chant add "Investigation: issue #1234 concurrent write data loss"
Created spec: 2026-02-08-001-abc
---
type: research
labels: [investigation, issue-1234]
target_files:
  - .chant/research/issue-1234-investigation.md
---

The target file uses stage markers to organize findings:

# Investigation: Issue #1234

## Stage 1: Comprehension
[Issue summary, affected components, initial observations]

## Stage 2: Reproduction
[Failing test, reproduction steps, environment details]

## Stage 3: Root Cause
[Hypothesis table, root cause identification, evidence]

## Stage 4: Impact Map
[Affected components, similar patterns, test gaps]

## Summary
Root Cause: Unprotected read-modify-write in store.rs:145
Fix Strategy: Pessimistic locking using existing Lock module
Target Files: src/storage/store.rs, src/storage/batch.rs

The agent completes all four research stages in one pass. When it finishes, you create a single implementation spec referencing the investigation output:

$ chant add "Fix issue #1234: add locking to concurrent writes"
Created spec: 2026-02-08-002-def

When to Use Each Mode

Use the full six-spec workflow when:

  • Multiple people will work the issue (one person does comprehension, another picks up root cause)
  • Investigation spans multiple days or sessions (each spec is a resumable checkpoint)
  • The issue is complex: multiple hypotheses, more than two affected files, or unclear reproduction steps

Use single-spec mode when:

  • You’re the only person working the issue, in a single sitting
  • You can describe the bug and likely fix direction in a paragraph
  • The fix will touch one or two files with a clear test strategy

Investigation Heuristics

Across both modes, watch for signs that your investigation approach needs adjustment:

  • Hypotheses aren’t converging. Multiple theories tested, all eliminated, and new ones don’t build on previous findings. Broaden your search to adjacent modules.
  • Stuck in one file. Re-reading the same code repeatedly. Look at callers, dependencies, and configuration instead.
  • Reproduction keeps failing. Your test may not match the actual reported symptoms. Re-read the issue with fresh eyes.

The goal is deliberate re-orientation when progress stalls – not premature abandonment or endless persistence on an unproductive path.

Enterprise KPI/OKR Workflow

A complete walkthrough showing how chant drives a real business OKR from data analysis through implementation.

OKR vs KPI — What’s the difference?

  • KPI (Key Performance Indicator) = An ongoing metric being tracked (e.g., churn rate, NPS score)
  • OKR (Objectives and Key Results) = A time-bound goal framework targeting improvement
    • Objective: Qualitative goal (“Improve customer retention”)
    • Key Result: Quantitative target (“Reduce churn from 8% to 5%”)

In this guide, churn rate is the KPI being tracked. The Q1 OKR sets a target to improve that KPI.

The Scenario

Acme SaaS Corp is a B2B platform with 5,000 customers. Their Q1 OKR targets improving customer retention by reducing churn. This guide follows their team through the full workflow — from gathering data to shipping fixes to tracking results.

Team

RolePersonResponsibility
VP ProductSarahSets OKRs, approves specs
Data AnalystMikeGathers data, creates digests
Engineers(managed by chant)Implement approved changes

Q1 OKR

Objective: Improve customer retention

Key Result: Reduce monthly churn rate from 8% to 5%

KPI tracked:  Monthly customer churn rate
Baseline:     8% (December 2025)
Target:       5% by end of Q1 2026
Timeline:     4 weeks

Workflow Phases

Week 1          Week 2              Week 2            Week 3         Week 4
┌──────────┐   ┌───────────────┐   ┌──────────┐   ┌───────────┐   ┌──────────┐
│  Human    │   │    Chant      │   │  Human   │   │   Chant   │   │  Track   │
│  Data     │──>│   Research    │──>│ Approval │──>│  Execute  │──>│ Results  │
│ Ingestion │   │   Phase       │   │  Gate    │   │  Parallel │   │  Daily   │
└──────────┘   └───────────────┘   └──────────┘   └───────────┘   └──────────┘

Guide Pages

  1. The Business Context — Acme’s product, churn problem, and Q1 OKR
  2. Data Ingestion — Week 1: Human investigation and data gathering
  3. Research Phase — Week 2: Chant agent analyzes churn drivers
  4. Approval Gate — Week 2: Team reviews and approves findings
  5. Implementation — Week 3: Parallel execution of fixes
  6. Reporting — Week 4: Daily tracking and dashboards

Key Concepts Demonstrated

  • Context directories for ingesting external data
  • Research specs for AI-driven analysis
  • Approval workflow with reject/approve cycle
  • Driver specs that decompose into parallel member specs
  • Activity tracking and reporting for stakeholder visibility

Prerequisites

Familiarity with core concepts, research workflows, and approval workflows.

See Also

The Business Context

Acme SaaS Corp

Acme SaaS Corp runs a B2B project management platform. Key numbers:

MetricValue
Customers5,000
MRR$2.5M
Monthly churn8% (400 customers/month)
Revenue at risk$200K/month
Customer segmentsStartup, Mid-Market, Enterprise

The 8% churn rate is unsustainable. Each lost customer costs roughly $500/month in MRR, and acquiring a replacement costs 5x more than retention.

Q1 OKR

Objective: Improve customer retention

Key Result: Reduce monthly churn from 8% to 5%

Target delta:  -3 percentage points
Revenue saved: ~$75K/month at target
Timeline:      End of Q1 2026 (4 weeks)

KPIs Being Tracked

Sarah (VP Product) defines the key performance indicators that the Q1 OKR targets:

MetricBaselineTargetHow Measured
Monthly churn rate8%5%Billing system exports
30-day activation rate62%75%Product analytics
Support ticket volume340/week<250/weekZendesk
NPS score3240+Quarterly survey

Churn Breakdown (Current State)

Mike (Data Analyst) pulls initial numbers by segment:

SegmentCustomersChurn RateLost/Month
Startup (<50 seats)3,20011%352
Mid-Market (50-500)1,4004%56
Enterprise (500+)4001%4

The problem is concentrated in the Startup segment. Mid-Market and Enterprise are healthy.

Team Structure

The project follows chant’s orchestrator pattern:

  • Sarah creates specs, reviews findings, approves work
  • Mike gathers external data, creates context digests for chant
  • Chant agents analyze data (research specs) and implement fixes (code specs)
  • CI/CD runs daily activity reports and KPI tracking

Project Setup

# Initialize chant with enterprise features
chant init --agent claude

# Create context directory for KPI data
mkdir -p .chant/context/kpi-churn-q1

The .chant/context/ directory holds human-curated data that agents can reference during research and implementation.

What’s Next

With the OKR defined and project initialized, the team begins the four-phase workflow:

  1. Data Ingestion — Mike gathers metrics, support data, and survey results
  2. Research — Chant agent analyzes the data for churn drivers
  3. Approval — Sarah reviews and approves the analysis
  4. Implementation — Chant executes approved fixes in parallel

Week 1: Data Ingestion

The first phase is entirely human-driven. Mike (Data Analyst) gathers data from external systems and creates markdown digests that chant agents can read.

Why Human-First?

Chant agents don’t have access to Datadog, Zendesk, or survey tools. The human-in-the-loop pattern is:

External Systems ──> Human Digest ──> .chant/context/ ──> Agent Analysis
(Datadog, Zendesk)    (Markdown)       (Git-tracked)      (Research spec)

Mike translates raw data into structured markdown. This is intentional — it forces a human to curate what matters before handing off to an agent.

Data Sources

Mike pulls from three systems:

SourceWhatFormat
DatadogChurn metrics, cohort analysisDashboard exports
ZendeskSupport ticket patternsTicket exports, tags
TypeformUser exit survey responsesSurvey results

Digest 1: Churn Metrics

Mike exports Datadog dashboards and distills them into a markdown digest:

# Create the digest file
vim .chant/context/kpi-churn-q1/datadog-churn-metrics-2026-01.md

File: .chant/context/kpi-churn-q1/datadog-churn-metrics-2026-01.md

# Churn Metrics - January 2026

Source: Datadog dashboard "Customer Health"
Exported: 2026-01-06

## Monthly Churn by Segment

| Segment | Oct | Nov | Dec | Trend |
|---------|-----|-----|-----|-------|
| Startup | 9% | 10% | 11% | Rising |
| Mid-Market | 5% | 4% | 4% | Stable |
| Enterprise | 1% | 1% | 1% | Stable |
| Overall | 7% | 7.5% | 8% | Rising |

## Churn Timing (Days After Signup)

- 0-7 days: 35% of all churns
- 8-30 days: 28% of all churns
- 31-90 days: 22% of all churns
- 90+ days: 15% of all churns

Key insight: 63% of churn happens in the first 30 days.

## Feature Adoption at Churn

Customers who churned in Dec had NOT used:
- Project templates: 78% never used
- Team invites: 65% never invited a teammate
- Integrations: 82% had zero integrations

Customers retained 90+ days had used:
- Project templates: 91% used in first week
- Team invites: 88% invited 2+ teammates
- Integrations: 74% connected at least one

Digest 2: Support Patterns

Mike reviews Zendesk ticket trends for churned customers:

File: .chant/context/kpi-churn-q1/zendesk-support-patterns.md

# Support Ticket Patterns - Churned Customers

Source: Zendesk export, Dec 2025 churns (n=400)
Exported: 2026-01-07

## Top Ticket Categories (Churned Customers)

| Category | Count | % of Churns | Avg Response Time |
|----------|-------|------------|-------------------|
| Onboarding confusion | 142 | 36% | 4.2 hours |
| Missing features | 89 | 22% | 6.1 hours |
| Billing questions | 67 | 17% | 2.8 hours |
| Performance issues | 54 | 14% | 3.5 hours |
| Other | 48 | 12% | 5.0 hours |

## Common Phrases in Tickets (Pre-Churn)

- "How do I set up my first project?" (87 tickets)
- "Can I import from Trello/Asana?" (45 tickets)
- "Where is the dashboard?" (38 tickets)
- "My team can't see my projects" (29 tickets)

## Ticket-to-Churn Correlation

- Customers with 3+ tickets in first week: 42% churn rate
- Customers with 0 tickets in first week: 6% churn rate
- Unresolved tickets at churn: 68% had at least one open ticket

Digest 3: Exit Survey

Mike summarizes exit survey responses:

File: .chant/context/kpi-churn-q1/user-survey-summary.md

# Exit Survey Summary - Q4 2025

Source: Typeform exit survey (n=156 responses, 39% response rate)
Exported: 2026-01-08

## Primary Reason for Leaving

| Reason | Count | % |
|--------|-------|---|
| Too hard to get started | 52 | 33% |
| Missing key feature | 34 | 22% |
| Switched to competitor | 28 | 18% |
| Price too high | 22 | 14% |
| Other | 20 | 13% |

## Verbatim Highlights

> "I spent 2 hours trying to set up my first project and gave up."

> "Couldn't figure out how to invite my team. The settings menu is buried."

> "No Slack integration was a dealbreaker for us."

> "Love the concept but the onboarding wizard just drops you on a blank page."

Commit the Context

Mike commits all digests to the repo:

git add .chant/context/kpi-churn-q1/
git commit -m "Add Q1 churn KPI context digests"

The data is now git-tracked and available for chant agents to reference.

What’s Next

With context digested, Sarah creates a research spec for chant to analyze the data:

Research Phase — Chant agent reads these digests and identifies actionable churn drivers.

Week 2: Research Phase

With context digests committed, Sarah creates a research spec for chant to analyze the churn data and identify actionable drivers.

Creating the Research Spec

chant add "Analyze Q1 churn drivers from support, metrics, and survey data"

This creates spec 2026-01-13-001-xyz. Sarah edits it to reference the context digests and define clear research questions.

Complete Research Spec

File: .chant/specs/2026-01-13-001-xyz.md

---
type: research
status: ready
labels:
  - kpi-churn
  - q1-2026
approval:
  required: true
  status: pending
informed_by:
  - .chant/context/kpi-churn-q1/datadog-churn-metrics-2026-01.md
  - .chant/context/kpi-churn-q1/zendesk-support-patterns.md
  - .chant/context/kpi-churn-q1/user-survey-summary.md
target_files:
  - .chant/context/kpi-churn-q1/research-findings.md
---

# Analyze Q1 churn drivers

## Problem

Acme SaaS Corp has 8% monthly churn, concentrated in the Startup segment
(11%). 63% of churn happens in the first 30 days. We need to identify the
top actionable drivers and recommend specific interventions.

## Research Questions

- [ ] What are the top 3 churn drivers by impact?
- [ ] Which drivers are addressable through product changes?
- [ ] What is the expected churn reduction for each intervention?
- [ ] What is the recommended implementation priority?

## Methodology

1. Cross-reference churn timing data with support ticket patterns
2. Map feature adoption gaps to exit survey reasons
3. Quantify impact of each driver using available metrics
4. Rank interventions by expected churn reduction and implementation effort

## Acceptance Criteria

- [ ] Top 3 churn drivers identified with supporting data
- [ ] Each driver linked to specific metrics from context files
- [ ] Recommended interventions with expected impact estimates
- [ ] Priority ranking based on effort vs. impact
- [ ] Findings written to research-findings.md

Executing the Research

chant work 2026-01-13-001-xyz

The agent reads the three context digests, cross-references the data, and produces findings.

Research Output

After execution, the agent creates the findings document:

File: .chant/context/kpi-churn-q1/research-findings.md

# Q1 Churn Analysis — Research Findings

Spec: 2026-01-13-001-xyz
Completed: 2026-01-14

## Executive Summary

Three drivers account for an estimated 6.2 percentage points of the 8%
monthly churn. Addressing all three could reduce churn to approximately
3.5%, exceeding the 5% target.

## Top 3 Churn Drivers

### 1. Failed Onboarding (Impact: ~3.5pp)

**Evidence:**
- 63% of churn occurs in the first 30 days (Datadog)
- 36% of churned customers filed onboarding tickets (Zendesk)
- 33% of exit survey cited "too hard to get started" (Survey)
- 78% of churned users never used project templates (Datadog)

**Root cause:** New users land on a blank page after signup. No guided
setup, no templates suggested, no team invite prompt.

**Recommended intervention:** Onboarding wizard with step-by-step setup
(create project from template, invite team, connect integration).

**Expected impact:** Reducing first-30-day churn by 50% = ~3.5pp reduction.

### 2. Missing Integrations (Impact: ~1.5pp)

**Evidence:**
- 82% of churned users had zero integrations (Datadog)
- "No Slack integration" cited as dealbreaker (Survey)
- 45 tickets asking about Trello/Asana import (Zendesk)

**Root cause:** No Slack integration exists. Import from competitors
requires manual CSV export.

**Recommended intervention:** Slack notification integration and one-click
import from Trello/Asana.

**Expected impact:** ~1.5pp churn reduction based on integration adoption
correlation.

### 3. Team Discovery Friction (Impact: ~1.2pp)

**Evidence:**
- 65% of churned users never invited a teammate (Datadog)
- "Can't figure out how to invite my team" — 29 tickets (Zendesk)
- Team invite setting "buried" in settings (Survey)

**Root cause:** Team invite is nested under Settings > Organization >
Members. Not surfaced during onboarding or in main navigation.

**Recommended intervention:** Surface team invite in onboarding wizard
and add persistent "Invite Team" button to sidebar.

**Expected impact:** ~1.2pp churn reduction based on team-size retention
correlation.

## Priority Matrix

| # | Intervention | Impact | Effort | Priority |
|---|-------------|--------|--------|----------|
| 1 | Onboarding wizard | ~3.5pp | Medium | P0 |
| 2 | Slack + import integrations | ~1.5pp | Medium | P1 |
| 3 | Team invite UX | ~1.2pp | Low | P1 |

## Recommendation

Implement all three. Start with the onboarding wizard (highest impact)
and team invite UX (lowest effort) in parallel. Follow with integrations.

Combined expected impact: 8% → ~3.5% (exceeds 5% target).

Spec Status After Research

The agent marks all research questions and acceptance criteria as checked:

---
type: research
status: completed
labels:
  - kpi-churn
  - q1-2026
approval:
  required: true
  status: pending
completed_at: 2026-01-14T16:45:00Z
model: claude-haiku-4-5-20251001
target_files:
  - .chant/context/kpi-churn-q1/research-findings.md
---

The research is complete, but approval is still pending. The agent has done its analysis — now the humans decide whether to act on it.

What’s Next

Sarah and the team review the findings and decide whether to approve, reject, or refine:

Approval Gate — Human review of research findings before implementation begins.

Week 2: Approval Gate

The research spec is complete. Before any code is written, Sarah and the team review the findings.

Review the Research

chant show 2026-01-13-001-xyz
ID:      2026-01-13-001-xyz
Title:   Analyze Q1 churn drivers
Type:    research
Status:  completed

Approval: PENDING
Labels:   kpi-churn, q1-2026

Acceptance Criteria:
  [x] Top 3 churn drivers identified with supporting data
  [x] Each driver linked to specific metrics from context files
  [x] Recommended interventions with expected impact estimates
  [x] Priority ranking based on effort vs. impact
  [x] Findings written to research-findings.md

Sarah reads the full findings in .chant/context/kpi-churn-q1/research-findings.md and shares with the team.

First Review: Rejection

After discussion, Sarah rejects the spec. The integration estimates seem optimistic without more data:

chant reject 2026-01-13-001-xyz --by sarah \
  --reason "Integration impact estimate (1.5pp) needs validation. \
We have integration usage data from the beta program — agent should \
factor that in. Also need cost estimate for import work."
Spec 2026-01-13-001-xyz REJECTED by sarah

Reason: Integration impact estimate (1.5pp) needs validation.
We have integration usage data from the beta program — agent should
factor that in. Also need cost estimate for import work.

The spec file now shows the rejection in the discussion section:

## Approval Discussion

**sarah** - 2026-01-15 09:30 - REJECTED
Integration impact estimate (1.5pp) needs validation. We have integration
usage data from the beta program — agent should factor that in. Also
need cost estimate for import work.

Adding More Context

Mike adds the integration beta data to the context directory:

vim .chant/context/kpi-churn-q1/integration-beta-usage.md
git add .chant/context/kpi-churn-q1/integration-beta-usage.md
git commit -m "Add integration beta usage data for churn research"

File: .chant/context/kpi-churn-q1/integration-beta-usage.md

# Integration Beta - Usage Data

Source: Internal beta program (n=200 customers, Oct-Dec 2025)

## Adoption

- 200 customers invited to beta
- 134 enabled integration (67% adoption)
- 98 still active after 30 days (73% retention of adopters)

## Churn Comparison (Beta Period)

| Group | Churn Rate | n |
|-------|-----------|---|
| Integration beta (enabled) | 4.5% | 134 |
| Integration beta (not enabled) | 9.2% | 66 |
| Non-beta (control) | 10.8% | 3,000 |

## Key Insight

Integration correlates with 6.3pp lower churn, but self-selection
bias is likely. Conservative estimate: 2-3pp attributable to integration.

Re-executing Research

Sarah resumes the spec with the new context:

chant reset 2026-01-13-001-xyz --work

The agent re-reads all context files (including the new integration beta data) and updates the findings. The integration section now reflects validated data:

### 2. Missing Integrations (Impact: ~2.0pp, revised)

**Updated with integration beta data:**
- Beta users with integration enabled: 4.5% churn vs 10.8% control
- Conservative attributable impact: 2-3pp (accounting for self-selection)
- Revised estimate: ~2.0pp (midpoint of conservative range)

**Cost note:** Additional integrations require API integration work.
Existing integration already exists in beta — promotion to GA is low effort.

Second Review: Approval

Sarah reviews the updated findings. The integration data validates the thesis, and the revised estimates are more credible:

chant approve 2026-01-13-001-xyz --by sarah
Spec 2026-01-13-001-xyz APPROVED by sarah

The discussion section now shows the full history:

## Approval Discussion

**sarah** - 2026-01-15 09:30 - REJECTED
Integration impact estimate (1.5pp) needs validation. We have Slack
usage data from the beta program — agent should factor that in. Also
need cost estimate for Trello/Asana import.

**sarah** - 2026-01-16 14:15 - APPROVED

Why This Matters

The reject-revise-approve cycle is the key value of approval gates:

  • No wasted implementation work — Code isn’t written until the analysis is validated
  • Human judgment preserved — Sarah caught an optimistic estimate before it drove engineering decisions
  • Audit trail — The spec file records who rejected, why, and when it was eventually approved
  • Data-driven iteration — The rejection surfaced the Slack beta data, making the final analysis stronger

What’s Next

With approved findings, Sarah creates implementation specs:

Implementation — Converting research into a driver spec with parallel member specs.

Week 3: Implementation

The research is approved. Sarah now converts the three recommended interventions into a driver spec with member specs for parallel execution.

Creating the Driver Spec

chant add "Reduce Q1 churn: implement approved interventions" --type driver

This creates spec 2026-01-16-002-abc. Sarah edits it to define the members:

File: .chant/specs/2026-01-16-002-abc.md

---
type: driver
status: ready
labels:
  - kpi-churn
  - q1-2026
depends_on:
  - 2026-01-13-001-xyz
members:
  - 2026-01-16-002-abc-1
  - 2026-01-16-002-abc-2
  - 2026-01-16-002-abc-3
---

# Reduce Q1 churn: implement approved interventions

Based on research findings in spec 2026-01-13-001-xyz.

## Interventions

1. **Onboarding wizard** — Step-by-step setup flow for new users (P0)
2. **Integration GA** — Promote beta to general availability (P1)
3. **Team invite UX** — Surface invite flow in onboarding and sidebar (P1)

## Acceptance Criteria

- [ ] All member specs completed
- [ ] Combined interventions deployed to staging
- [ ] Churn tracking baseline established

Member Specs

Sarah creates three focused member specs. Each one targets a single intervention.

Member 1: Onboarding Wizard

chant add "Add onboarding wizard for new user setup"

File: .chant/specs/2026-01-16-002-abc-1.md

---
type: code
status: ready
labels:
  - kpi-churn
  - q1-2026
  - onboarding
parent: 2026-01-16-002-abc
informed_by:
  - .chant/context/kpi-churn-q1/research-findings.md
target_files:
  - src/components/onboarding/wizard.tsx
  - src/components/onboarding/steps.tsx
  - tests/onboarding/wizard.test.tsx
---

# Add onboarding wizard for new user setup

## Problem

78% of churned users never used project templates. New users land on a
blank page after signup with no guidance.

## Solution

Add a multi-step onboarding wizard that appears on first login:
1. Create first project (from template)
2. Invite team members
3. Connect an integration

## Acceptance Criteria

- [ ] Wizard appears on first login for new accounts
- [ ] Step 1: Template selection with 3 starter templates
- [ ] Step 2: Team invite with email input
- [ ] Step 3: Integration connection
- [ ] "Skip" option available on each step
- [ ] Wizard state persisted (resume if closed early)
- [ ] Tests passing

Member 2: Integration GA

chant add "Promote integration from beta to GA"

File: .chant/specs/2026-01-16-002-abc-2.md

---
type: code
status: ready
labels:
  - kpi-churn
  - q1-2026
  - integrations
parent: 2026-01-16-002-abc
target_files:
  - src/integrations/config.ts
  - src/integrations/feature-flag.ts
  - tests/integrations/integration.test.ts
---

# Promote integration from beta to GA

## Problem

Integration beta users show 4.5% churn vs 10.8% control. Integration exists
but is gated behind a beta flag.

## Solution

Remove the beta feature flag and enable integration for all users.
Add integration card to the onboarding wizard and settings page.

## Acceptance Criteria

- [ ] Beta feature flag removed
- [ ] Integration visible to all users in settings
- [ ] Integration card added to integrations page
- [ ] Existing beta users unaffected (no re-setup required)
- [ ] Tests passing

Member 3: Team Invite UX

chant add "Surface team invite in sidebar and onboarding"

File: .chant/specs/2026-01-16-002-abc-3.md

---
type: code
status: ready
labels:
  - kpi-churn
  - q1-2026
  - team-ux
parent: 2026-01-16-002-abc
target_files:
  - src/components/sidebar/invite-button.tsx
  - src/components/onboarding/team-step.tsx
  - tests/sidebar/invite.test.tsx
---

# Surface team invite in sidebar and onboarding

## Problem

65% of churned users never invited a teammate. The invite flow is buried
under Settings > Organization > Members.

## Solution

Add a persistent "Invite Team" button to the sidebar and integrate
the invite step into the onboarding wizard.

## Acceptance Criteria

- [ ] "Invite Team" button visible in sidebar for accounts with <3 members
- [ ] Button opens invite modal with email input
- [ ] Invite step integrated into onboarding wizard (Step 2)
- [ ] Button hides after team reaches 3+ members
- [ ] Tests passing

Parallel Execution

All three member specs share the kpi-churn label. Sarah executes them in parallel:

chant work --parallel --label kpi-churn
Starting parallel execution (3 specs, label: kpi-churn)

[002-abc-1] Starting: Add onboarding wizard...
[002-abc-2] Starting: Promote integration...
[002-abc-3] Starting: Surface team invite...

[002-abc-3] Completed (4 files changed)
[002-abc-2] Completed (3 files changed)
[002-abc-1] Completed (6 files changed)

All 3 specs completed successfully.

Each spec executes in its own worktree, so there are no conflicts between agents.

Monitoring Progress

While agents run, Sarah checks progress:

chant log 2026-01-16-002-abc-1
[14:02] Reading research findings...
[14:03] Analyzing existing component structure...
[14:05] Creating wizard component with 3 steps...
[14:08] Writing tests...
[14:10] All tests passing. Committing changes.

Merging Results

After all member specs complete, Sarah merges the worktrees back to main:

chant merge --all --rebase
Merging 3 completed specs...

  002-abc-1 (onboarding wizard): Merged ✓
  002-abc-2 (integration):       Merged ✓
  002-abc-3 (team invite UX):    Merged ✓

All specs merged to main.

The driver spec 002-abc auto-completes when all its members are merged.

What’s Next

With all interventions merged, the team sets up tracking to measure impact:

Reporting — Daily KPI tracking, activity feeds, and dashboards.

Week 4: Reporting

With interventions shipped, the team tracks whether churn actually drops. This phase combines chant’s activity tracking with a lightweight cron setup for daily KPI reports.

Activity Tracking

Chant tracks all spec activity. Sarah uses this to monitor the OKR initiative:

chant activity --label kpi-churn --since 7d
2026-01-22 14:10  002-abc-1  COMPLETED  Add onboarding wizard
2026-01-22 14:08  002-abc-2  COMPLETED  Promote Slack integration
2026-01-22 14:06  002-abc-3  COMPLETED  Surface team invite
2026-01-22 14:00  002-abc    WORKED     Reduce Q1 churn (parallel)
2026-01-16 14:15  001-xyz    APPROVED   Analyze Q1 churn drivers
2026-01-15 09:30  001-xyz    REJECTED   Analyze Q1 churn drivers
2026-01-14 16:45  001-xyz    COMPLETED  Analyze Q1 churn drivers
2026-01-13 10:00  001-xyz    CREATED    Analyze Q1 churn drivers

This gives a complete audit trail from research through implementation.

Daily KPI Tracking Spec

Sarah creates a recurring spec to track results:

chant add "Daily churn KPI report" --type task

File: .chant/specs/2026-01-22-003-def.md

---
type: task
status: ready
labels:
  - kpi-churn
  - q1-2026
  - reporting
schedule: daily  # Metadata field - documents intended frequency, not a trigger
informed_by:
  - .chant/context/kpi-churn-q1/research-findings.md
target_files:
  - reports/kpi-churn-daily.md
---

# Daily churn KPI report

Generate daily snapshot of churn KPI progress.

## Acceptance Criteria

- [ ] Current churn rate calculated from billing data
- [ ] Comparison to 8% baseline and 5% target
- [ ] Feature adoption metrics (wizard completion, Slack, team invites)
- [ ] Report written to reports/kpi-churn-daily.md

Scheduling with External Tools

Chant does not have built-in scheduling. Instead, you trigger chant commands from your existing automation infrastructure — cron, CI/CD pipelines, or task schedulers. The schedule: field in specs is metadata that documents your intended frequency for human readers; it doesn’t trigger execution automatically.

Mike sets up automated daily runs using the team’s existing tools:

Standard Crontab

# Edit crontab with: crontab -e
# Daily KPI report at 8am UTC
0 8 * * * cd /path/to/repo && chant work --parallel --label reporting

GitHub Actions

# .github/workflows/kpi-report.yml
name: Daily KPI Report
on:
  schedule:
    - cron: '0 8 * * *'

jobs:
  report:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run KPI report
        run: chant work --parallel --label reporting
      - name: Post update
        run: |
          chant activity --since 1d

Example Daily Report

After the first daily run, the agent produces:

File: reports/kpi-churn-daily.md

# Churn KPI Report — 2026-01-28

## Summary

| Metric | Baseline | Current | Target | Status |
|--------|----------|---------|--------|--------|
| Monthly churn | 8.0% | 6.2% | 5.0% | On track |
| 30-day activation | 62% | 68% | 75% | Improving |
| Support tickets/wk | 340 | 285 | <250 | Improving |

## Intervention Adoption (First Week)

| Intervention | Metric | Value |
|-------------|--------|-------|
| Onboarding wizard | Completion rate | 71% of new signups |
| Integration | GA activations | 312 new connections |
| Team invite | Invites sent via sidebar | 189 teams |

## Trend

Week 1 post-launch shows early positive signals. Churn rate dropped
1.8pp from 8.0% to 6.2%. The onboarding wizard has the highest
engagement (71% completion). Need 2-3 more weeks of data to confirm
the trend is sustained.

## Spec Activity (Last 24h)

- No new specs created
- 003-def (this report) completed

Dashboard View

For a quick summary across all KPI-labeled specs:

chant list --label kpi-churn
ID           Type      Status     Title
───────────  ────────  ─────────  ─────────────────────────────────────
001-xyz      research  completed  Analyze Q1 churn drivers
002-abc      driver    completed  Reduce Q1 churn (driver)
002-abc-1    code      completed  Add onboarding wizard
002-abc-2    code      completed  Promote Slack integration
002-abc-3    code      completed  Surface team invite
003-def      task      completed  Daily churn KPI report
chant activity --label kpi-churn --since 30d
2026-01-22 14:10  002-abc-1  COMPLETED  Add onboarding wizard
2026-01-22 14:08  002-abc-2  COMPLETED  Promote Slack integration
2026-01-22 14:06  002-abc-3  COMPLETED  Surface team invite
2026-01-22 14:00  002-abc    WORKED     Reduce Q1 churn (parallel)
2026-01-16 14:15  001-xyz    APPROVED   Analyze Q1 churn drivers
2026-01-15 09:30  001-xyz    REJECTED   Analyze Q1 churn drivers
2026-01-14 16:45  001-xyz    COMPLETED  Analyze Q1 churn drivers
2026-01-13 10:00  001-xyz    CREATED    Analyze Q1 churn drivers

End-to-End Recap

The full workflow from OKR to results:

Week 1: Mike creates data digests          → .chant/context/
Week 2: Chant analyzes churn drivers       → research spec (rejected, revised, approved)
Week 3: Chant implements 3 fixes in parallel → 3 code specs merged
Week 4: Daily reports track KPI progress   → 8% → 6.2% (on track for 5%)

All artifacts — data digests, research findings, approval discussions, implementation specs, and daily reports — are tracked in git through chant specs. Any team member can reconstruct the full decision chain from OKR to code change.

See Also

Enterprise TDD Workflow

A complete walkthrough showing how chant improves Test-Driven Development (TDD) across multiple teams in an enterprise environment.

The Scenario

Acme SaaS Corp (the same B2B platform from the KPI/OKR guide) faces inconsistent testing practices across their 3 product teams. This guide follows their journey to standardize TDD using chant’s spec-driven approach.

Teams

TeamFocusEngineersTest CoverageTest Flakiness
AuthAuthentication & SSO585%8%
PaymentsBilling & subscriptions445%18%
AnalyticsReporting & dashboards692%5%

TDD Challenges

Current State                    Target State
┌─────────────────────┐          ┌─────────────────────┐
│ Inconsistent coverage│          │ 80%+ all teams     │
│ Tests after code     │    →     │ Tests first (TDD)  │
│ 12% flaky tests     │          │ <5% flaky tests    │
│ 23% test drift      │          │ <5% drift          │
└─────────────────────┘          └─────────────────────┘

How Chant Helps

Chant’s spec-driven model naturally aligns with TDD:

  • Acceptance criteria = test cases — Each checkbox becomes a test
  • Specs before code — Tests are planned before implementation
  • Research specs — Analyze coverage gaps before writing tests
  • Drift detection — Keep tests synchronized with behavior

Guide Pages

  1. The TDD Challenge — Acme’s testing problems across 3 teams
  2. Chant Meets TDD — How spec-driven development aligns with TDD
  3. Specs as Test Plans — Using acceptance criteria to define tests
  4. Writing Tests with Chant — Agent-assisted test implementation
  5. Ensuring Quality — Enforcing test standards across teams
  6. Keeping Tests Current — Detecting and fixing test drift

Key Concepts Demonstrated

  • Spec-first testing — Write acceptance criteria before tests before code
  • Research specs for test coverage analysis
  • Parallel test execution across feature areas
  • Config validation for test quality standards
  • Drift detection for test maintenance

Prerequisites

Familiarity with core concepts and the KPI/OKR workflow.

See Also

The TDD Challenge

Acme SaaS Corp — Testing Reality

Acme SaaS Corp runs the same B2B project management platform from the KPI/OKR guide. After successfully reducing churn from 8% to 5%, engineering leadership turns to a persistent problem: inconsistent testing practices.

MetricCurrentTarget
Average test coverage74%80%+
Flaky test rate12%<5%
Test drift23%<5%
Tests written before code35%90%+

Three Teams, Three Approaches

Auth Team (5 engineers)

The Auth team follows TDD religiously. Every feature starts with failing tests.

MetricValueAssessment
Coverage85%Strong
Flaky tests8%Needs work
Test drift12%Moderate
TDD adoption95%Excellent

Pain point: Flaky tests in OAuth integration tests. External service mocks don’t match production behavior.

Payments Team (4 engineers)

The Payments team writes tests after implementation. High bug rate in production.

MetricValueAssessment
Coverage45%Critical
Flaky tests18%Critical
Test drift35%Critical
TDD adoption15%Poor

Pain point: Tests are an afterthought. Engineers say “no time for tests” under delivery pressure.

Analytics Team (6 engineers)

The Analytics team has the highest coverage but struggles with test maintenance.

MetricValueAssessment
Coverage92%Excellent
Flaky tests5%Good
Test drift28%Needs work
TDD adoption40%Moderate

Pain point: Tests don’t match current behavior. The codebase evolved, tests didn’t.

The Root Causes

Engineering leadership identifies five systemic issues:

1. Inconsistent Test Planning

No standard process for deciding what to test. Each engineer makes ad-hoc decisions.

Engineer A: "I'll test the happy path"
Engineer B: "I'll test all edge cases"
Engineer C: "I'll test what seems important"

2. Tests Written Too Late

When tests come after code, they test implementation rather than behavior. They become brittle and don’t catch real bugs.

Traditional Flow (common at Acme):
  Code → Tests → Review → Ship

TDD Flow (goal):
  Spec → Tests → Code → Review → Ship

3. No Test Quality Gates

No validation that tests meet standards. Missing assertions pass CI. Low-value tests inflate coverage numbers.

4. Test Drift

Tests describe behavior that no longer exists. 23% of tests are out of sync with current code behavior.

5. Slow Test Authoring

Writing tests is seen as slow. Engineers estimate 30-40% of implementation time goes to test writing.

Q1 OKR: TDD Transformation

Objective: Establish consistent TDD practices across all teams

Key Results:

  • All teams at 80%+ coverage by end of Q1
  • Flaky test rate below 5%
  • Test drift below 5%
  • 90%+ of new features start with test specs

Team Structure

The transformation follows chant’s orchestrator pattern:

  • Marcus (Engineering Manager) creates standards, reviews test specs
  • Team leads review and approve test planning specs
  • Chant agents analyze coverage gaps and write tests
  • CI/CD runs test quality reports daily

Project Setup

# Initialize chant with test-focused configuration
chant init --agent claude

# Create context directory for test standards
mkdir -p .chant/context/tdd-standards

The .chant/context/tdd-standards/ directory holds team test guidelines that agents reference when writing tests.

What’s Next

With the problem defined, Marcus explores how chant’s spec model aligns with TDD:

  1. Chant Meets TDD — How specs naturally support test-first development
  2. Test Planning — Using acceptance criteria as test cases
  3. Execution — Agents writing tests before implementation
  4. Consistency — Enforcing standards across teams

Chant Meets TDD

Chant’s spec-driven model naturally aligns with Test-Driven Development. This page shows how the workflow fits together without changing existing practices.

The Natural Alignment

TDD and spec-driven development share the same approach: define expected behavior before implementation. See philosophy for chant’s broader design principles.

Traditional TDD:
  Red → Green → Refactor
  (Write failing test → Make it pass → Clean up)

Spec-Driven TDD:
  Spec → Tests → Code → Verify
  (Define behavior → Write tests → Implement → Confirm)

The spec’s acceptance criteria become the test plan. Each criterion maps to one or more test cases.

Spec = Test Plan

When a developer writes acceptance criteria, they’re defining test cases:

## Acceptance Criteria

- [ ] Returns 401 for invalid tokens
- [ ] Returns 403 for expired tokens
- [ ] Returns 200 with user data for valid tokens
- [ ] Logs authentication failures with request ID
- [ ] Rate limits failed attempts (max 5 per minute)

Each line becomes a test:

def test_returns_401_for_invalid_tokens(): ...
def test_returns_403_for_expired_tokens(): ...
def test_returns_200_with_user_data_for_valid_tokens(): ...
def test_logs_authentication_failures_with_request_id(): ...
def test_rate_limits_failed_attempts(): ...

Workflow Comparison

Before Chant

1. Developer receives feature request
2. Developer writes code
3. Developer writes tests (maybe)
4. Code review
5. Ship

Test coverage depends on individual discipline. No visibility into what should be tested.

With Chant

1. Developer creates spec with acceptance criteria
2. Spec defines test cases explicitly
3. Agent writes tests first (or developer does)
4. Tests fail (red)
5. Implementation makes tests pass (green)
6. Code review with spec as reference
7. Ship

Test coverage is determined at planning time, not implementation time.

No Process Changes Required

Chant doesn’t require teams to change their existing workflow. It layers on top:

Existing PracticeWith Chant
Jira ticketsTickets reference spec IDs
Pull requestsPRs link to specs
Code reviewReviewers check acceptance criteria
Test coverageCoverage targets in spec config

The key insight: acceptance criteria already exist in most teams’ workflows. Chant makes them actionable by connecting them to test execution.

Types of Test Specs

Chant supports different spec types for different testing needs:

Research Specs: Coverage Analysis

Before writing tests, analyze what’s missing:

---
type: research
status: ready
target_files:
  - .chant/context/coverage-analysis.md
---

# Analyze payment service test coverage gaps

Identify untested paths in the payment service.

## Acceptance Criteria

- [ ] List all public methods with <50% coverage
- [ ] Identify untested error paths
- [ ] Prioritize gaps by risk (payment failures = critical)

Code Specs: Test Implementation

Write tests for specific functionality:

---
type: code
status: ready
target_files:
  - tests/payments/test_refund_flow.py
---

# Add tests for refund flow

## Acceptance Criteria

- [ ] Test successful refund under $1000
- [ ] Test refund requiring manager approval (>$1000)
- [ ] Test refund on disputed transaction (should fail)
- [ ] Test partial refund calculation

Driver Specs: Test Suites

Organize multiple test specs under a single initiative:

---
type: driver
members:
  - 2026-01-20-001-abc  # Unit tests
  - 2026-01-20-002-def  # Integration tests
  - 2026-01-20-003-ghi  # E2E tests
---

# Payment service test suite expansion

Coordinate test improvements across unit, integration, and E2E layers.

Benefits for Each Team

Auth Team (already doing TDD)

  • Specs formalize their existing practice
  • Coverage gaps become visible in research specs
  • Flaky tests get dedicated fix specs

Payments Team (minimal testing)

  • Specs force test planning before code
  • Acceptance criteria can’t be skipped
  • Coverage requirements enforced via config

Analytics Team (test drift)

  • Documentation specs track test-to-behavior mapping
  • Drift detection catches mismatches
  • Update specs keep tests current

Example: New Feature Flow

Marcus (Engineering Manager) walks through a new feature:

Feature: Add two-factor authentication (2FA) to login

Step 1: Create spec with test-focused acceptance criteria

chant add "Add 2FA to login flow"

Step 2: Edit spec to define test cases

---
type: code
status: ready
labels:
  - auth
  - tdd
target_files:
  - src/auth/two_factor.py
  - tests/auth/test_two_factor.py
---

# Add 2FA to login flow

## Test Cases

- [ ] Valid TOTP code returns success
- [ ] Invalid TOTP code returns 401
- [ ] Expired TOTP code returns 401 with "code expired" message
- [ ] Missing 2FA setup prompts enrollment
- [ ] Backup codes work when TOTP unavailable
- [ ] Rate limiting after 3 failed attempts
- [ ] Audit log entry created for each 2FA attempt

## Acceptance Criteria

- [ ] All test cases passing
- [ ] 2FA toggle in user settings
- [ ] TOTP QR code generation
- [ ] Backup code generation and storage

Step 3: Agent writes tests first, then implements

The spec makes it impossible to ship without tests — the tests are the first acceptance criterion.

What’s Next

With the model understood, see how to structure specs as detailed test plans:

Specs as Test Plans — Deep dive into test-focused acceptance criteria.

Specs as Test Plans

Specs become test plans when acceptance criteria are written as testable assertions. This page shows how to structure specs for effective TDD.

The Test Planning Spec

The Payments team needs to improve coverage for their refund processing flow. Marcus creates a comprehensive test planning spec.

Creating the Spec

chant add "Add comprehensive tests for payment refund flow"

Complete Test Planning Spec

File: .chant/specs/2026-01-20-001-rfn.md

---
type: code
status: ready
labels:
  - payments
  - tdd
  - coverage
target_files:
  - tests/payments/test_refund_flow.py
  - tests/payments/test_refund_edge_cases.py
  - tests/payments/conftest.py
---

# Add comprehensive tests for payment refund flow

## Problem

The refund flow has 38% test coverage. Production incidents in Q4 revealed
untested edge cases: partial refunds, currency conversion, and fraud holds.

## Test Categories

### Happy Path Tests

- [ ] Full refund on completed transaction
- [ ] Partial refund with correct remaining balance
- [ ] Refund to original payment method
- [ ] Refund confirmation email triggered

### Authorization Tests

- [ ] Refund under $100: auto-approved
- [ ] Refund $100-$1000: requires team lead approval
- [ ] Refund over $1000: requires manager approval
- [ ] Refund on flagged account: requires fraud review

### Edge Cases

- [ ] Refund on transaction older than 90 days (policy limit)
- [ ] Refund exceeds original transaction amount (reject)
- [ ] Multiple partial refunds totaling more than original (reject)
- [ ] Refund during payment processor outage (queue for retry)
- [ ] Currency conversion on international refund
- [ ] Refund on disputed transaction (blocked)

### Error Handling

- [ ] Invalid transaction ID returns 404
- [ ] Insufficient balance returns 400 with clear message
- [ ] Payment processor timeout triggers retry with backoff
- [ ] Database error triggers rollback, no partial state

## Acceptance Criteria

- [ ] All 16 test cases implemented and passing
- [ ] Test fixtures created in conftest.py
- [ ] Coverage of refund module reaches 85%+
- [ ] No flaky tests (verified with 10 consecutive runs)

This single spec defines 16 test cases organized by category. Each checkbox becomes a test function.

Research Specs for Coverage Analysis

Before writing tests, understand what’s missing. Research specs analyze existing coverage.

Coverage Analysis Spec

chant add "Analyze payment service test coverage gaps"

File: .chant/specs/2026-01-18-001-cov.md

---
type: research
status: ready
labels:
  - payments
  - tdd
informed_by:
  - .chant/context/tdd-standards/coverage-requirements.md
target_files:
  - .chant/context/payments-coverage-analysis.md
---

# Analyze payment service test coverage gaps

## Research Questions

- [ ] Which modules have <50% coverage?
- [ ] Which error paths are untested?
- [ ] Which edge cases appear in production logs but lack tests?
- [ ] What's the coverage by risk level (critical/high/medium)?

## Methodology

1. Parse coverage report for payments module
2. Cross-reference with production error logs (last 30 days)
3. Identify untested paths that caused incidents
4. Prioritize by business impact

## Acceptance Criteria

- [ ] Coverage gaps documented with line-level detail
- [ ] Gaps prioritized by risk (payment failures = P0)
- [ ] Recommended test additions with estimated effort
- [ ] Findings in .chant/context/payments-coverage-analysis.md

Research Output

After execution, the agent produces a coverage analysis:

File: .chant/context/payments-coverage-analysis.md

# Payment Service Coverage Analysis

Spec: 2026-01-18-001-cov
Completed: 2026-01-19

## Executive Summary

Payment service has 45% overall coverage. Critical paths (transaction
processing) have 72% coverage, but error handling paths have only 18%.

## Coverage by Module

| Module | Coverage | Risk Level | Gap Priority |
|--------|----------|------------|--------------|
| refund.py | 38% | Critical | P0 |
| transaction.py | 72% | Critical | P1 |
| subscription.py | 51% | High | P1 |
| reporting.py | 89% | Low | P3 |

## Critical Gaps (P0)

### refund.py (38% → target 85%)

| Line Range | Description | Production Incidents |
|------------|-------------|---------------------|
| 45-67 | Partial refund calculation | 3 in Dec |
| 102-118 | Currency conversion | 1 in Dec |
| 145-160 | Fraud hold handling | 2 in Nov |
| 189-205 | Retry on processor failure | 4 in Dec |

### Recommended Test Additions

1. **Partial refund flow** (8 tests, ~2 hours)
2. **Currency conversion** (4 tests, ~1 hour)
3. **Fraud hold scenarios** (3 tests, ~1 hour)
4. **Retry logic** (4 tests, ~1.5 hours)

Total: 19 new tests, estimated 5.5 hours

Driver Specs for Test Suites

Large testing initiatives use driver specs to organize multiple test specs.

Test Suite Driver

---
type: driver
status: ready
labels:
  - payments
  - tdd
  - q1-coverage
members:
  - 2026-01-20-001-rfn  # Refund flow tests
  - 2026-01-20-002-cur  # Currency conversion tests
  - 2026-01-20-003-frd  # Fraud handling tests
  - 2026-01-20-004-rty  # Retry logic tests
---

# Payment service test coverage expansion

Based on coverage analysis in 2026-01-18-001-cov.

## Goal

Increase payment service coverage from 45% to 85%.

## Member Specs

| Spec | Focus | Tests | Status |
|------|-------|-------|--------|
| 001-rfn | Refund flow | 16 | Ready |
| 002-cur | Currency | 4 | Ready |
| 003-frd | Fraud | 3 | Ready |
| 004-rty | Retry | 4 | Ready |

## Acceptance Criteria

- [ ] All member specs completed
- [ ] Combined coverage reaches 85%
- [ ] No new flaky tests introduced

Writing Effective Test Criteria

Good: Specific and Testable

- [ ] Refund exceeds original amount returns 400 with error code REFUND_EXCEEDS_ORIGINAL
- [ ] Rate limiting triggers after 3 failed attempts within 60 seconds
- [ ] Audit log contains user_id, action, timestamp, and result for each refund

Bad: Vague or Untestable

- [ ] Refund flow works correctly
- [ ] Error handling is robust
- [ ] Performance is acceptable

Guidelines

  1. Include expected values — “returns 400” not “returns error”
  2. Specify boundaries — “after 3 attempts” not “rate limits requests”
  3. Name error codes — “REFUND_EXCEEDS_ORIGINAL” not “appropriate error”
  4. Define data requirements — “contains user_id, action, timestamp”

Context Files for Test Standards

Store team test standards in context files:

File: .chant/context/tdd-standards/test-patterns.md

# Acme Test Patterns

## Naming Convention

Tests follow: `test_<action>_<condition>_<expected_result>`

Examples:
- `test_refund_exceeds_original_returns_400`
- `test_login_invalid_password_increments_failure_count`
- `test_subscription_expired_blocks_api_access`

## Required Assertions

Every test must assert:
1. Return value or side effect
2. State change (if applicable)
3. Audit/logging (for security-sensitive operations)

## Fixture Requirements

- Use factories, not raw fixtures
- Mock external services at the client boundary
- Clean up database state after each test

Agents reference these standards when writing tests.

What’s Next

With test plans defined, see how agents execute them:

Writing Tests with Chant — Agent-driven test implementation.

Writing Tests with Chant

With test plans defined in specs, agents write tests before implementation code. This page shows the execution workflow.

Executing a Test Spec

Marcus triggers the refund flow test spec:

chant work 2026-01-20-001-rfn

The agent reads the spec’s acceptance criteria and writes tests for each case.

Agent Execution Flow

1. Agent reads spec acceptance criteria
2. Agent reads context files (test patterns, fixtures)
3. Agent reads existing code to understand interfaces
4. Agent writes tests (all should fail initially)
5. Agent runs tests to confirm they fail (red)
6. Agent implements code to make tests pass (green)
7. Agent marks criteria as complete

Execution Log

chant log 2026-01-20-001-rfn
[10:02] Reading spec 2026-01-20-001-rfn...
[10:02] Reading context: tdd-standards/test-patterns.md
[10:03] Analyzing refund.py interface...
[10:04] Creating test file: tests/payments/test_refund_flow.py
[10:05] Writing test: test_full_refund_on_completed_transaction
[10:05] Writing test: test_partial_refund_correct_remaining_balance
[10:06] Writing test: test_refund_to_original_payment_method
[10:06] Writing test: test_refund_confirmation_email_triggered
[10:07] Writing test: test_refund_under_100_auto_approved
[10:07] Writing test: test_refund_100_to_1000_requires_team_lead
[10:08] Writing test: test_refund_over_1000_requires_manager
[10:08] Writing test: test_refund_flagged_account_requires_fraud_review
[10:09] Creating test file: tests/payments/test_refund_edge_cases.py
[10:10] Writing 8 edge case tests...
[10:12] Running tests... 16 passed, 0 failed
[10:12] Checking coverage... refund.py: 87% (target: 85%) ✓
[10:13] Running flakiness check (10 iterations)...
[10:15] All iterations passed. No flaky tests detected.
[10:15] Marking acceptance criteria complete.
[10:16] Committing changes.

Generated Test File

The agent produces a complete test file:

File: tests/payments/test_refund_flow.py

"""Tests for payment refund flow.

Spec: 2026-01-20-001-rfn
"""

import pytest
from decimal import Decimal
from unittest.mock import Mock, patch

from payments.refund import RefundService
from payments.models import Transaction, RefundRequest
from tests.factories import TransactionFactory, UserFactory


class TestRefundHappyPath:
    """Happy path tests for refund processing."""

    def test_full_refund_on_completed_transaction(self, refund_service, completed_transaction):
        """Full refund on completed transaction succeeds."""
        result = refund_service.process_refund(
            transaction_id=completed_transaction.id,
            amount=completed_transaction.amount,
        )

        assert result.status == "completed"
        assert result.refunded_amount == completed_transaction.amount
        assert completed_transaction.remaining_balance == Decimal("0.00")

    def test_partial_refund_correct_remaining_balance(self, refund_service, completed_transaction):
        """Partial refund calculates remaining balance correctly."""
        original_amount = completed_transaction.amount
        refund_amount = Decimal("50.00")

        result = refund_service.process_refund(
            transaction_id=completed_transaction.id,
            amount=refund_amount,
        )

        assert result.status == "completed"
        assert result.refunded_amount == refund_amount
        assert completed_transaction.remaining_balance == original_amount - refund_amount

    def test_refund_to_original_payment_method(self, refund_service, completed_transaction):
        """Refund goes to original payment method."""
        result = refund_service.process_refund(
            transaction_id=completed_transaction.id,
            amount=completed_transaction.amount,
        )

        assert result.payment_method_id == completed_transaction.payment_method_id

    @patch("payments.notifications.send_refund_confirmation")
    def test_refund_confirmation_email_triggered(
        self, mock_send_email, refund_service, completed_transaction
    ):
        """Refund triggers confirmation email."""
        refund_service.process_refund(
            transaction_id=completed_transaction.id,
            amount=completed_transaction.amount,
        )

        mock_send_email.assert_called_once_with(
            user_id=completed_transaction.user_id,
            refund_amount=completed_transaction.amount,
            transaction_id=completed_transaction.id,
        )


class TestRefundAuthorization:
    """Authorization level tests for refunds."""

    def test_refund_under_100_auto_approved(self, refund_service, small_transaction):
        """Refunds under $100 are auto-approved."""
        result = refund_service.process_refund(
            transaction_id=small_transaction.id,
            amount=Decimal("50.00"),
        )

        assert result.status == "completed"
        assert result.approval_level == "auto"

    def test_refund_100_to_1000_requires_team_lead(self, refund_service, medium_transaction):
        """Refunds $100-$1000 require team lead approval."""
        result = refund_service.process_refund(
            transaction_id=medium_transaction.id,
            amount=Decimal("500.00"),
        )

        assert result.status == "pending_approval"
        assert result.required_approval_level == "team_lead"

    def test_refund_over_1000_requires_manager(self, refund_service, large_transaction):
        """Refunds over $1000 require manager approval."""
        result = refund_service.process_refund(
            transaction_id=large_transaction.id,
            amount=Decimal("1500.00"),
        )

        assert result.status == "pending_approval"
        assert result.required_approval_level == "manager"

    def test_refund_flagged_account_requires_fraud_review(
        self, refund_service, flagged_user_transaction
    ):
        """Refunds on flagged accounts require fraud review."""
        result = refund_service.process_refund(
            transaction_id=flagged_user_transaction.id,
            amount=Decimal("50.00"),
        )

        assert result.status == "pending_fraud_review"


# ... additional tests in test_refund_edge_cases.py

Parallel Test Execution

For the full payment coverage initiative, Marcus runs all test specs in parallel:

# Note: work in parallel by passing multiple spec IDs or using --chain
chant work 001-rfn 002-cur 003-frd 004-rty
Starting parallel execution (4 specs)

[001-rfn] Starting: Refund flow tests...
[002-cur] Starting: Currency conversion tests...
[003-frd] Starting: Fraud handling tests...
[004-rty] Starting: Retry logic tests...

[003-frd] Completed (3 tests added)
[004-rty] Completed (4 tests added)
[002-cur] Completed (4 tests added)
[001-rfn] Completed (16 tests added)

All 4 specs completed. 27 tests added.
Payment service coverage: 45% → 86%

Monitoring Progress

While agents run, Marcus monitors progress:

# Watch live execution
chant log 2026-01-20-001-rfn

# Check all TDD specs
chant list --label tdd
ID           Type    Status       Title
───────────  ──────  ───────────  ────────────────────────────────
001-rfn      code    in_progress  Refund flow tests
002-cur      code    in_progress  Currency conversion tests
003-frd      code    completed    Fraud handling tests
004-rty      code    completed    Retry logic tests

Merging Test Changes

After completion, merge all test changes:

chant merge --all-completed --rebase --auto
Merging 4 completed specs...

  001-rfn (refund tests):    Merged ✓
  002-cur (currency tests):  Merged ✓
  003-frd (fraud tests):     Merged ✓
  004-rty (retry tests):     Merged ✓

All specs merged to main.

Before/After Metrics

After the test suite expansion:

MetricBeforeAfterTarget
Payment coverage45%86%85% ✓
Refund module38%87%85% ✓
Flaky tests18%4%<5% ✓
Test count4269

What’s Next

With tests written, see how to ensure consistency across teams:

Ensuring Quality — Enforcing test standards via configuration.

Ensuring Quality Across Teams

With three teams writing tests differently, Marcus needs to enforce consistent standards. Chant’s configuration and validation features help maintain quality without micromanagement.

The Consistency Problem

Before standardization:

TeamTest NamingFixture PatternAssertions/TestCoverage Threshold
Authtest_*Factories3-580%
Paymentsit_should_*Raw data1-2None
Analyticstest_*Fixtures2-490%

Different patterns make tests harder to review, maintain, and debug.

Config-Based Standards

Marcus creates a shared configuration that enforces test requirements:

File: .chant/config.md

## spec_defaults

### code
- target_files: required
- test_coverage_target: 80

### required_labels
- One of: auth, payments, analytics (team identifier)
- One of: unit, integration, e2e (test level)

## validation

### coverage
- minimum: 80
- warn_below: 85
- modules:
  - path: "payments/*"
    minimum: 85
  - path: "auth/*"
    minimum: 80

### test_quality
- min_assertions_per_test: 2
- max_test_file_length: 500
- require_docstrings: true

Required Fields

Spec validation catches incomplete test specs before execution:

chant lint
Linting 12 specs...

WARN  2026-01-21-001-xyz: Missing required label (team identifier)
ERROR 2026-01-21-002-abc: target_files is required for code specs
ERROR 2026-01-21-003-def: Test coverage target not specified

2 errors, 1 warning

Specs with errors can’t be executed:

chant work 2026-01-21-002-abc
Error: Spec 2026-01-21-002-abc failed validation
  - target_files is required for code specs

Fix validation errors before executing.

Test Spec Template

Marcus creates a template that pre-fills required fields:

File: .chant/templates/test-spec.md

---
type: code
status: pending
labels:
  - {{team}}
  - {{test_level}}
target_files:
  - tests/{{module}}/test_{{feature}}.py
---

# Add tests for {{feature}}

## Problem

Brief description of what needs testing and why.

## Test Categories

### Happy Path
- [ ] Test case 1
- [ ] Test case 2

### Edge Cases
- [ ] Edge case 1
- [ ] Edge case 2

### Error Handling
- [ ] Error case 1
- [ ] Error case 2

## Acceptance Criteria

- [ ] All test cases passing
- [ ] Coverage target met (80%+)
- [ ] No flaky tests (verified with 10 runs)
- [ ] Tests follow naming convention

Creating a test spec with the template:

chant add --template test-spec \
  --var team=payments \
  --var test_level=unit \
  --var module=refund \
  --var feature=partial_refund \
  "Add unit tests for partial refund"

Context Files for Standards

Store team-wide test patterns in context files:

File: .chant/context/tdd-standards/naming-conventions.md

# Test Naming Conventions

## Test Functions

Pattern: `test_<action>_<condition>_<expected_result>`

Good:
- `test_refund_exceeds_original_returns_400`
- `test_login_expired_token_returns_401`
- `test_subscription_canceled_stops_billing`

Bad:
- `test_refund` (too vague)
- `test_it_works` (meaningless)
- `testRefundFlow` (wrong style)

## Test Classes

Pattern: `Test<Component><Category>`

Examples:
- `TestRefundHappyPath`
- `TestRefundEdgeCases`
- `TestRefundAuthorization`

File: .chant/context/tdd-standards/fixture-patterns.md

# Fixture Patterns

## Use Factories Over Raw Data

Good:
```python
@pytest.fixture
def completed_transaction():
    return TransactionFactory(status="completed", amount=Decimal("100.00"))

Bad:

@pytest.fixture
def completed_transaction():
    return {"id": 1, "status": "completed", "amount": 100}

Mock External Services at Client Boundary

Good:

@patch("payments.stripe_client.StripeClient.charge")
def test_payment_processed(mock_charge):
    mock_charge.return_value = ChargeResult(success=True)

Bad:

@patch("stripe.Charge.create")  # Too deep
def test_payment_processed(mock_create):

## Automated Quality Checks

Marcus sets up a CI job that runs quality checks:

**File: `.github/workflows/test-quality.yml`**

```yaml
name: Test Quality

on: [push, pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Lint specs
        run: chant lint --strict

      - name: Check test coverage thresholds
        run: |
          pytest --cov=payments --cov=auth --cov=analytics \
            --cov-fail-under=80

      - name: Check for flaky tests
        run: pytest --count=5 -x

      - name: Validate test patterns
        run: |
          # Check naming conventions
          grep -r "def test_" tests/ | \
            grep -v "def test_[a-z_]*_[a-z_]*" && \
            echo "ERROR: Tests not following naming convention" && exit 1 || true

Team-Specific Overrides

While enforcing minimums, teams can exceed them:

File: .chant/context/tdd-standards/team-overrides.md

# Team-Specific Standards

## Auth Team

Higher coverage required due to security sensitivity:
- Minimum coverage: 85% (vs 80% baseline)
- All auth flows require integration tests
- Session handling requires E2E tests

## Payments Team

Critical path coverage:
- Transaction processing: 90% minimum
- Refund flow: 85% minimum
- Error handling: explicit tests for all error codes

## Analytics Team

Performance-sensitive tests:
- Query tests must assert execution time
- Dashboard tests must verify caching

Validation Report

Regular validation reports track compliance:

# Note: Use activity and export commands for reporting
chant activity --since 30d
chant export --format json
Test Quality Report — Last 30 Days

Team Coverage Compliance:
  Auth:      87% (target: 85%) ✓
  Payments:  86% (target: 85%) ✓
  Analytics: 91% (target: 80%) ✓

Flaky Test Rate:
  Auth:      3% (target: <5%) ✓
  Payments:  4% (target: <5%) ✓
  Analytics: 2% (target: <5%) ✓

Test Specs Created: 23
Test Specs Completed: 21
Tests Added: 156

Standards Compliance:
  Naming convention: 98% (3 violations)
  Min assertions: 100%
  Docstrings: 94% (9 missing)

Before/After: Team Consistency

After implementing standards:

MetricBeforeAfter
Consistent naming65%98%
Factory usage40%95%
Docstring coverage30%94%
Meeting coverage target1/3 teams3/3 teams

What’s Next

With standards enforced, see how to keep tests current as code evolves:

Keeping Tests Current — Detecting and fixing test drift.

Keeping Tests Current

Tests that don’t match current behavior are worse than no tests — they provide false confidence. This page shows how chant detects and fixes test drift.

The Drift Problem

At Acme, 23% of tests are out of sync with current behavior:

Drift TypeCountRisk
Tests asserting removed behavior45High — false positives
Tests missing new parameters32Medium — incomplete coverage
Tests using deprecated mocks28Low — maintenance burden
Tests with outdated docstrings89Low — documentation confusion

Documentation Specs for Test Mapping

Documentation specs track the relationship between tests and behavior:

File: .chant/specs/2026-01-22-001-doc.md

---
type: doc
status: ready
labels:
  - payments
  - test-mapping
watches:
  - src/payments/refund.py
  - tests/payments/test_refund_flow.py
target_files:
  - docs/testing/refund-test-mapping.md
---

# Document refund test-to-behavior mapping

## Purpose

Track which tests cover which behaviors in the refund module.
This mapping enables drift detection when code changes.

## Acceptance Criteria

- [ ] Each public method mapped to its tests
- [ ] Each error code mapped to its test
- [ ] Coverage gaps identified
- [ ] Document written to target file

Generated Mapping Document

File: docs/testing/refund-test-mapping.md

# Refund Module Test Mapping

Generated by spec 2026-01-22-001-doc

## Method: `RefundService.process_refund()`

| Behavior | Test | Status |
|----------|------|--------|
| Full refund succeeds | `test_full_refund_on_completed_transaction` | ✓ |
| Partial refund calculates balance | `test_partial_refund_correct_remaining_balance` | ✓ |
| Returns to original payment method | `test_refund_to_original_payment_method` | ✓ |
| Triggers confirmation email | `test_refund_confirmation_email_triggered` | ✓ |

## Method: `RefundService.validate_refund_request()`

| Behavior | Test | Status |
|----------|------|--------|
| Rejects amount > original | `test_refund_exceeds_original_returns_400` | ✓ |
| Rejects 90+ day old transactions | `test_refund_older_than_90_days_rejected` | ✓ |
| Blocks disputed transactions | `test_refund_on_disputed_transaction_blocked` | ✓ |

## Error Codes

| Code | Description | Test |
|------|-------------|------|
| REFUND_EXCEEDS_ORIGINAL | Amount > transaction | ✓ Covered |
| TRANSACTION_TOO_OLD | > 90 days | ✓ Covered |
| DISPUTED_TRANSACTION | Active dispute | ✓ Covered |
| INSUFFICIENT_BALANCE | Already refunded | ✗ Missing |

## Coverage Gaps

- `INSUFFICIENT_BALANCE` error code has no dedicated test

Drift Detection

The chant drift command compares current code against documentation specs:

chant drift 2026-01-22-001-doc
Drift Detection Report — 2026-01-22-001-doc

Watched files changed since last check:
  src/payments/refund.py: 3 changes detected

Drift Analysis:

1. NEW PARAMETER: `reason` added to process_refund()
   - Added in commit abc123 (2026-01-25)
   - No tests verify reason parameter
   - Recommendation: Add test for reason validation

2. BEHAVIOR CHANGE: Authorization thresholds updated
   - $100 → $150 for auto-approval
   - $1000 → $1500 for manager approval
   - Tests still assert old thresholds
   - Recommendation: Update authorization tests

3. NEW ERROR CODE: REFUND_ALREADY_PROCESSED
   - Added for idempotency check
   - No test coverage
   - Recommendation: Add idempotency test

Summary: 3 drift issues detected
  - 1 missing parameter test
  - 1 outdated assertion
  - 1 missing error test

Automated Drift Checks

Marcus sets up a weekly drift report:

File: .github/workflows/drift-check.yml

name: Weekly Drift Check

on:
  schedule:
    - cron: '0 9 * * 1'  # Monday 9am

jobs:
  drift:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Check all doc specs for drift
        run: |
          chant drift > drift-report.txt

      - name: Notify on drift
        if: ${{ steps.drift.outputs.drift_count > 0 }}
        run: |
          echo "Test drift detected - see drift-report.json for details"

Fixing Drift with Update Specs

When drift is detected, create a spec to fix it:

chant add "Fix test drift in refund module"

File: .chant/specs/2026-01-27-001-fix.md

---
type: code
status: ready
labels:
  - payments
  - tdd
  - drift-fix
depends_on:
  - 2026-01-22-001-doc
target_files:
  - tests/payments/test_refund_flow.py
  - tests/payments/test_refund_edge_cases.py
---

# Fix test drift in refund module

## Problem

Drift detection found 3 issues (spec 2026-01-22-001-doc):
1. Missing test for `reason` parameter
2. Outdated authorization thresholds
3. Missing test for REFUND_ALREADY_PROCESSED error

## Acceptance Criteria

- [ ] Add test for reason parameter validation
- [ ] Update authorization threshold tests ($100→$150, $1000→$1500)
- [ ] Add test for REFUND_ALREADY_PROCESSED error
- [ ] Update test-to-behavior mapping document
- [ ] All tests passing
- [ ] Drift check passes with no issues

Drift Detection Output

After fixing drift:

chant drift 2026-01-22-001-doc
Drift Detection Report — 2026-01-22-001-doc

Watched files changed since last check:
  src/payments/refund.py: No new changes

Drift Analysis:
  No drift detected ✓

All tests aligned with current behavior.

Continuous Drift Prevention

To prevent drift from accumulating, Marcus adds a drift check to PR validation:

# .github/workflows/pr-check.yml
- name: Check for drift
  run: |
    # Check drift on all doc specs
    chant drift

This fails the PR if code changes would cause test drift without updating tests.

Before/After: Drift Metrics

After implementing drift detection:

MetricBeforeAfterTarget
Test drift rate23%4%<5% ✓
Tests asserting removed behavior453
Missing parameter tests325
Outdated docstrings8912

End-to-End Recap

The full TDD transformation journey:

Week 1: Identify testing problems        → 3 teams, 3 different approaches
Week 2: Align chant with TDD workflow    → Specs = test plans
Week 3: Write test specs, execute        → 27 new tests, coverage 45% → 86%
Week 4: Enforce standards, detect drift  → Consistent quality, <5% drift

All test artifacts — specs, coverage reports, drift analyses — are tracked in git. Any engineer can trace from a test back to its spec, and from a spec to the business requirement that drove it.

See Also

Research Workflow Guide

A complete walkthrough showing how chant orchestrates research workflows from investigation through implementation. Two parallel paths demonstrate the same patterns in different contexts.

Why This Guide?

Research workflows share common patterns regardless of domain:

  1. Investigate — Gather and synthesize sources
  2. Analyze — Process data to generate findings
  3. Document — Capture insights for others
  4. Implement — Act on findings
  5. Maintain — Keep findings current as inputs change

Chant’s spec types map directly to these phases. This guide shows how.

Two Paths, Same Patterns

This guide follows two researchers using identical chant patterns in different domains:

PathProtagonistResearch Goal
AcademicDr. Sarah Chen, PhD studentAnalyze 30 years of Arctic temperature data
DeveloperAlex Torres, Staff EngineerEvaluate microservices migration for 80K LOC monolith

Both paths demonstrate:

  • Research specs with informed_by: (synthesis)
  • Research specs with origin: (analysis)
  • Documentation specs with tracks:
  • Driver specs for multi-phase coordination
  • Drift detection and re-verification

The Universal Research Pattern

┌──────────────────────────────────────────────────────────────────┐
│                    Research Workflow Phases                       │
└──────────────────────────────────────────────────────────────────┘

Phase 1: Investigation
┌─────────────────────┐
│   Research Spec     │  informed_by: [papers|code|docs]
│    (synthesis)      │  → Synthesize sources, identify patterns
└──────────┬──────────┘
           │
           ▼
Phase 2: Analysis
┌─────────────────────┐
│   Research Spec     │  origin: [data|logs|metrics]
│    (analysis)       │  → Analyze data, generate findings
└──────────┬──────────┘
           │
           ├────────────────────────────┐
           ▼                            ▼
Phase 3: Documentation              Implementation
┌─────────────────────┐         ┌─────────────────────┐
│  Documentation      │         │    Driver Spec      │
│      Spec           │         │   (coordinate)      │
│                     │         └──────────┬──────────┘
│  tracks: [code]     │              ┌─────┴─────┬─────────┐
│  → Capture          │              ▼           ▼         ▼
│    findings         │           Code        Code      Code
└─────────────────────┘           (.1)        (.2)      (.3)
           │                       │           │         │
           │                       └───────────┴─────────┘
           │                                │
           ▼                                ▼
Phase 4: Maintenance
┌──────────────────────────────────────────────────────────────────┐
│              Drift Detection & Re-verification                    │
│                                                                   │
│   • origin: files change → re-analyze                            │
│   • tracks: code changes → update docs                           │
│   • informed_by: sources change → review                         │
└──────────────────────────────────────────────────────────────────┘

How the Paths Overlap

Both paths use the same spec types in the same order. The table below maps each phase:

PhaseAcademic ExampleDeveloper ExampleSpec Types
InvestigationLiterature review (25 papers)Codebase analysis (src/**)research with informed_by:
AnalysisStatistical analysis of climate dataPerformance metrics analysisresearch with origin:
DocumentationWrite methodology sectionDocument architecture decisionsdocumentation with tracks:
ImplementationData processing pipelinePOC microservice extractioncode, driver, depends_on:
PipelinePhase 1→2→3 dependenciesService-by-service rolloutdriver with member specs
MaintenanceNew data triggers re-analysisCode changes trigger doc updatesdrift detection

Spec Type Reference

Spec TypePurposeKey FieldsExample
researchInvestigation and analysisinformed_by:, origin:Synthesize papers, analyze data
documentationCapture and communicatetracks:Architecture docs, methodology
codeImplement changestarget_files:Scripts, services, utilities
driverCoordinate phasesmembers:Multi-step pipelines
taskNon-code worktarget_files:Reports, presentations

Choose Your Path

Academic Path — Follow Dr. Sarah Chen through:

  • Literature review of 25 climate science papers
  • Statistical analysis of Arctic temperature datasets
  • Multi-phase data pipeline with dependencies
  • Drift detection when new data is published

Developer Path — Follow Alex Torres through:

  • Codebase coupling analysis of 80K LOC monolith
  • Performance metrics analysis from production logs
  • Architecture documentation that tracks source code
  • Drift detection when code or metrics change

Key Concepts Demonstrated

  • Synthesis vs. Analysisinformed_by: for reading sources, origin: for processing data
  • Dependency chainsdepends_on: for phase ordering
  • Driver coordination — Decomposing complex work into parallel member specs
  • Documentation trackingtracks: keeps docs synchronized with source
  • Drift detection — Know when inputs change, trigger re-verification

Prerequisites

Familiarity with:

See Also

The Research Context

Dr. Sarah Chen

Dr. Sarah Chen is a third-year PhD student in Climate Science at Northern University. Her dissertation focuses on Arctic temperature acceleration patterns over the past three decades.

AttributeValue
ProgramPhD, Climate Science
YearThird year
AdvisorProf. James Morrison
FundingNSF Arctic Research Grant
TimelineDissertation defense in 18 months

Research Goal

Thesis Question: Have Arctic temperature increases accelerated since 1995, and what factors drive the acceleration?

Sarah’s analysis requires:

  • Synthesizing 25+ peer-reviewed papers on Arctic warming
  • Analyzing 30 years of temperature data from 12 monitoring stations
  • Processing satellite imagery datasets
  • Producing reproducible statistical analysis

The Reproducibility Challenge

Sarah’s field has a reproducibility problem. A 2024 meta-analysis found:

IssuePrevalence
Methods under-specified67% of papers
Data not preserved45% of papers
Analysis steps not documented72% of papers
Results not reproducible38% when attempted

Her advisor emphasizes: “Every finding must trace back to specific data and methodology. Your dissertation defense will include a reproducibility audit.”

Current Research State

Sarah has:

  • Downloaded 30 years of temperature data (CSV files, ~2GB)
  • Collected 25 papers on Arctic warming patterns
  • Rough notes on initial observations
  • No systematic approach to tracking analysis

Her pain points:

  • Literature notes scattered across Notion, PDFs, and text files
  • Analysis scripts in various Jupyter notebooks, unclear dependencies
  • Uncertain which findings came from which data version
  • No way to know when new data invalidates old conclusions

Why Chant?

Chant addresses each challenge:

ChallengeChant Solution
Scattered notesinformed_by: links findings to sources
Unclear dependenciesdepends_on: chains analysis phases
Data versioningorigin: tracks input data files
Result stalenessDrift detection alerts when inputs change
Method documentationSpec IS the methodology

Project Setup

Sarah initializes chant in her dissertation repository:

# Initialize chant
chant init --agent claude

# Create directories for research data
mkdir -p data/temperature
mkdir -p data/satellite
mkdir -p papers
mkdir -p analysis

# Create context directory for literature synthesis
mkdir -p .chant/context/arctic-research

Directory structure:

dissertation/
├── .chant/
│   ├── specs/           # Research specs live here
│   ├── context/         # Human-curated summaries
│   │   └── arctic-research/
│   └── config.md
├── data/
│   ├── temperature/     # 30 years of station data
│   └── satellite/       # Imagery datasets
├── papers/              # PDF collection
└── analysis/            # Output: findings, figures

Research Timeline

Sarah plans a four-week research phase:

Week 1          Week 2              Week 3            Week 4
┌──────────┐   ┌───────────────┐   ┌──────────────┐   ┌──────────────┐
│Literature│   │    Data       │   │   Pipeline   │   │  Write-up &  │
│  Review  │──>│   Analysis    │──>│ Coordination │──>│   Ongoing    │
│ (Papers) │   │ (Statistics)  │   │  (Driver)    │   │   Drift      │
└──────────┘   └───────────────┘   └──────────────┘   └──────────────┘

Spec Workflow Preview

Sarah’s research will use these spec types:

WeekSpec TypePurpose
1research with informed_by:Synthesize 25 papers into themes
2research with origin:Analyze temperature data
3driver with membersCoordinate multi-step pipeline
4+Drift detectionAlert when new data arrives

Team Structure

Unlike enterprise scenarios, Sarah works largely alone, but chant’s orchestrator pattern still applies:

  • Sarah creates specs, reviews findings, validates methodology
  • Chant agents synthesize literature, run statistical analysis
  • Git provides version control and audit trail
  • Drift detection runs when data files change

What’s Next

With the project initialized, Sarah begins the literature review phase:

Literature Review — Synthesizing 25 papers using research specs with informed_by:

Week 1: Literature Review

Sarah begins with a systematic literature review. This phase synthesizes 25 peer-reviewed papers to identify themes, gaps, and the current state of Arctic warming research.

The Synthesis Pattern

Literature review uses the synthesis pattern — reading multiple sources to extract themes:

┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│  Paper 1    │   │  Paper 2    │   │  Paper N    │
│  (PDF)      │   │  (PDF)      │   │  (PDF)      │
└──────┬──────┘   └──────┬──────┘   └──────┬──────┘
       │                 │                 │
       └────────────────┬┴─────────────────┘
                        │
                        ▼
              ┌─────────────────────┐
              │   Research Spec     │
              │  informed_by:       │
              │   - papers/*.pdf    │
              └──────────┬──────────┘
                         │
                         ▼
              ┌─────────────────────┐
              │  Literature Review  │
              │  (Themes & Gaps)    │
              └─────────────────────┘

The informed_by: field tells the agent what to read. The agent synthesizes patterns, not raw data.

Preparing the Papers

Sarah organizes her paper collection:

# Papers organized by topic
papers/
├── acceleration/
│   ├── chen-2024-arctic-amplification.pdf
│   ├── morrison-2023-feedback-loops.pdf
│   └── wang-2022-ice-albedo.pdf
├── methodology/
│   ├── ipcc-2023-data-standards.pdf
│   └── noaa-2024-station-calibration.pdf
└── regional/
    ├── greenland-2023-ice-sheet.pdf
    └── siberia-2024-permafrost.pdf

Sarah creates a markdown index for the agent:

File: .chant/context/arctic-research/paper-index.md

# Arctic Warming Paper Index

## Acceleration Studies

| Paper | Year | Key Finding |
|-------|------|-------------|
| Chen et al. | 2024 | Arctic amplification 2.5x global average |
| Morrison & Lee | 2023 | Ice-albedo feedback accelerating since 2010 |
| Wang et al. | 2022 | Ocean heat transport increasing |

## Methodology

| Paper | Year | Focus |
|-------|------|-------|
| IPCC WG1 | 2023 | Data quality standards for temperature records |
| NOAA Technical | 2024 | Station calibration procedures |

## Regional Studies

| Paper | Year | Region |
|-------|------|--------|
| Jensen et al. | 2023 | Greenland ice sheet dynamics |
| Petrov et al. | 2024 | Siberian permafrost thaw |

Total papers: 25

Creating the Literature Review Spec

Sarah creates a research spec for the synthesis:

chant add "Synthesize Arctic warming literature" --type research

She edits the spec to add detailed structure:

File: .chant/specs/2026-01-15-001-lit.md

---
type: research
status: pending
prompt: research-synthesis
informed_by:
  - papers/**/*.pdf
  - .chant/context/arctic-research/paper-index.md
target_files:
  - analysis/literature-review.md
  - analysis/research-gaps.md
---
# Synthesize Arctic warming literature

## Problem

25 papers on Arctic warming need systematic synthesis. I need to identify:
- Consensus findings on acceleration patterns
- Methodological approaches for temperature analysis
- Gaps in current research that my dissertation can address

## Research Questions

- [ ] What is the consensus on Arctic amplification magnitude?
- [ ] Which feedback mechanisms are well-established vs. debated?
- [ ] What data sources and methodologies are standard?
- [ ] What temporal patterns (seasonal, decadal) are documented?
- [ ] What gaps exist that my research can address?

## Methodology

1. Read all papers in `papers/` directory
2. Extract key findings on acceleration patterns
3. Identify methodological standards from IPCC/NOAA papers
4. Map areas of consensus vs. debate
5. Document research gaps relevant to my thesis question

## Acceptance Criteria

- [ ] All 25 papers reviewed and cited
- [ ] Themes organized by: acceleration, feedback, methodology
- [ ] Consensus vs. debate clearly distinguished
- [ ] 3+ research gaps identified with supporting citations
- [ ] literature-review.md written with proper citations
- [ ] research-gaps.md identifies dissertation contribution

Running the Synthesis

Sarah executes the spec:

chant work 001-lit

The agent reads all papers (via informed_by:), extracts themes, and produces the literature review.

Agent Output

After execution, Sarah finds two new files:

File: analysis/literature-review.md (generated)

# Arctic Warming Literature Review

## 1. Arctic Amplification

### Consensus Findings

All 25 papers agree on core amplification:
- Arctic warming 2-4x faster than global average (Chen 2024, Morrison 2023)
- Amplification factor increasing over time (Wang 2022)
- Sea ice loss is primary driver (IPCC 2023)

### Magnitude Estimates

| Study | Amplification Factor | Period |
|-------|---------------------|--------|
| Chen et al. 2024 | 2.5x | 1990-2023 |
| Morrison & Lee 2023 | 3.1x | 2000-2023 |
| IPCC AR6 WG1 | 2.0-4.0x | 1979-2021 |

### Areas of Debate

The exact contribution of each feedback mechanism remains contested:
- Ice-albedo feedback: 40-60% of amplification (range across studies)
- Ocean heat transport: 20-35% contribution
- Atmospheric circulation: 10-25% contribution

## 2. Feedback Mechanisms

### Well-Established

1. **Ice-Albedo Feedback** — Surface reflectivity decreases as ice melts
   - Supported by satellite observations (Jensen 2023)
   - Quantified in all modeling studies reviewed

2. **Lapse Rate Feedback** — Arctic troposphere warms faster
   - Observational evidence from radiosonde data
   - Consistent across all climate models

### Under Investigation

1. **Cloud Feedback** — Direction and magnitude uncertain
   - Petrov 2024: Clouds may amplify OR dampen warming
   - Key uncertainty in projections

## 3. Methodological Standards

### Data Sources

Papers use consistent data sources:
- NOAA Global Historical Climatology Network (12 papers)
- ERA5 Reanalysis (8 papers)
- MODIS satellite observations (15 papers)

### Quality Requirements

IPCC 2023 and NOAA 2024 establish standards:
- Minimum 30-year record for trend detection
- Station calibration requirements
- Homogenization procedures for historical data

## 4. Citations

[Full citation list with 25 entries...]

File: analysis/research-gaps.md (generated)

# Research Gaps and Dissertation Contribution

## Identified Gaps

### Gap 1: Acceleration Quantification Post-2015

**Current State:** Most acceleration studies end at 2015-2020.
**Gap:** Limited analysis of whether acceleration itself is accelerating.
**My Contribution:** Extend analysis to 2024 with focus on post-Paris Agreement period.

**Supporting Citations:**
- Chen 2024 notes "further investigation needed for most recent decade"
- Morrison 2023 calls for "updated acceleration estimates"

### Gap 2: Station-Level Variability

**Current State:** Most studies use gridded/reanalysis data.
**Gap:** Individual station behavior not well characterized.
**My Contribution:** Analyze 12 individual monitoring stations for local patterns.

**Supporting Citations:**
- NOAA 2024 recommends "station-level analysis to validate gridded products"

### Gap 3: Seasonal Acceleration Patterns

**Current State:** Annual averages dominate the literature.
**Gap:** Seasonal differences in acceleration rates.
**My Contribution:** Decompose acceleration by season (DJF, MAM, JJA, SON).

**Supporting Citations:**
- Wang 2022 notes "winter amplification may differ from annual"

## Dissertation Positioning

These gaps support my thesis question:
> Have Arctic temperature increases accelerated since 1995, and what factors drive the acceleration?

My analysis will address all three gaps by:
1. Including data through 2024
2. Analyzing individual stations (not just gridded data)
3. Examining seasonal patterns in acceleration

Verifying the Synthesis

Sarah reviews the output and checks the spec:

chant show 001-lit
Spec: 2026-01-15-001-lit
Type: research
Status: completed

Research Questions:
  [x] What is the consensus on Arctic amplification magnitude?
  [x] Which feedback mechanisms are well-established vs. debated?
  [x] What data sources and methodologies are standard?
  [x] What temporal patterns (seasonal, decadal) are documented?
  [x] What gaps exist that my research can address?

Acceptance Criteria:
  [x] All 25 papers reviewed and cited
  [x] Themes organized by: acceleration, feedback, methodology
  [x] Consensus vs. debate clearly distinguished
  [x] 3+ research gaps identified with supporting citations
  [x] literature-review.md written with proper citations
  [x] research-gaps.md identifies dissertation contribution

Informed by: papers/**/*.pdf (25 files)
Generated: analysis/literature-review.md, analysis/research-gaps.md

The Provenance Trail

The completed spec now documents:

  • Exactly which papers were synthesized
  • The research questions asked
  • The methodology used
  • When the synthesis was done

If Sarah’s advisor asks “How did you identify this gap?”, she can point to the spec and its informed_by: sources.

What’s Next

With the literature synthesized, Sarah moves to data analysis:

Data Analysis — Analyzing 30 years of temperature data using research specs with origin:

Week 2: Data Analysis

With the literature synthesized, Sarah moves to data analysis. This phase processes 30 years of temperature data to generate statistical findings.

The Analysis Pattern

Data analysis uses the analysis pattern — processing source data to extract findings:

┌─────────────────────────────────────────────────────────────┐
│                     Origin Data Files                        │
├─────────────┬─────────────┬─────────────┬─────────────────────┤
│ station-01  │ station-02  │ station-03  │      ...            │
│   .csv      │   .csv      │   .csv      │   (12 stations)     │
└──────┬──────┴──────┬──────┴──────┬──────┴──────────┬──────────┘
       │             │             │                 │
       └─────────────┴─────────────┴─────────────────┘
                              │
                              ▼
                 ┌─────────────────────────┐
                 │     Research Spec       │
                 │    origin:              │
                 │     - data/temp/*.csv   │
                 └────────────┬────────────┘
                              │
                              ▼
                 ┌─────────────────────────┐
                 │   Statistical Analysis  │
                 │   (Findings & Figures)  │
                 └─────────────────────────┘

The origin: field tells the agent what data to analyze. Changes to origin files trigger drift detection.

Data Overview

Sarah’s temperature data spans 12 Arctic monitoring stations:

data/temperature/
├── barrow-alaska.csv          # 1994-2024, 10,957 daily readings
├── resolute-canada.csv        # 1994-2024, 10,957 daily readings
├── longyearbyen-svalbard.csv  # 1994-2024, 10,957 daily readings
├── tiksi-russia.csv           # 1994-2024, 10,957 daily readings
├── alert-canada.csv           # 1994-2024, 10,957 daily readings
├── thule-greenland.csv        # 1994-2024, 10,957 daily readings
├── nord-greenland.csv         # 1994-2024, 10,957 daily readings
├── dikson-russia.csv          # 1994-2024, 10,957 daily readings
├── eureka-canada.csv          # 1994-2024, 10,957 daily readings
├── jan-mayen-norway.csv       # 1994-2024, 10,957 daily readings
├── bear-island-norway.csv     # 1994-2024, 10,957 daily readings
└── ny-alesund-svalbard.csv    # 1994-2024, 10,957 daily readings

Each CSV contains:

date,temp_c,temp_anomaly,quality_flag
1994-01-01,-28.3,-2.1,A
1994-01-02,-29.1,-2.9,A
...

Creating the Analysis Spec

Sarah creates a research spec for statistical analysis:

chant add "Analyze Arctic temperature acceleration" --type research

She edits the spec with detailed methodology:

File: .chant/specs/2026-01-16-001-ana.md

---
type: research
status: pending
prompt: research-analysis
depends_on:
  - 2026-01-15-001-lit
origin:
  - data/temperature/*.csv
informed_by:
  - analysis/literature-review.md
  - analysis/research-gaps.md
target_files:
  - analysis/temperature-findings.md
  - analysis/figures/trend-acceleration.png
  - analysis/figures/seasonal-patterns.png
  - analysis/figures/station-comparison.png
---
# Analyze Arctic temperature acceleration

## Problem

30 years of temperature data from 12 Arctic stations needs statistical analysis
to answer the thesis question: Have Arctic temperature increases accelerated
since 1995?

This analysis builds on the literature review findings that identified gaps in:
- Post-2015 acceleration quantification
- Station-level variability analysis
- Seasonal acceleration patterns

## Research Questions

- [ ] What is the overall warming trend (1994-2024)?
- [ ] Is there evidence of acceleration (nonlinear trend)?
- [ ] Do stations show consistent or divergent patterns?
- [ ] How do seasonal patterns differ?
- [ ] When did acceleration begin (if present)?

## Methodology

### Data Preparation
1. Load all CSV files from `data/temperature/`
2. Handle quality flags (exclude 'X' flagged observations)
3. Convert to annual and seasonal means
4. Calculate anomalies from 1994-2000 baseline

### Trend Analysis
1. Linear regression for overall trend
2. Quadratic regression to test for acceleration
3. Piecewise regression to find breakpoints
4. Mann-Kendall test for monotonic trends

### Station Comparison
1. Calculate trends for each station
2. Test for homogeneity across stations
3. Identify outlier stations
4. Map spatial patterns

### Seasonal Decomposition
1. Separate annual means into DJF, MAM, JJA, SON
2. Calculate trends by season
3. Test for acceleration by season
4. Identify which seasons drive overall acceleration

## Acceptance Criteria

- [ ] All 12 stations analyzed with consistent methodology
- [ ] Quality-flagged data excluded
- [ ] Linear and quadratic trend estimates with confidence intervals
- [ ] Breakpoint analysis with statistical tests
- [ ] Station-level trends with heterogeneity test
- [ ] Seasonal trends with acceleration testing
- [ ] Three figures generated (trend, seasonal, station comparison)
- [ ] temperature-findings.md with all results and statistics

Running the Analysis

Sarah executes the spec:

chant work 001-ana

The agent:

  1. Reads all CSV files via origin:
  2. Reads the literature review via informed_by: for context
  3. Runs the statistical methodology
  4. Generates findings and figures

Agent Output

After execution, Sarah finds new analysis files:

File: analysis/temperature-findings.md (generated)

# Arctic Temperature Acceleration Analysis

## Executive Summary

Analysis of 12 Arctic monitoring stations (1994-2024) confirms acceleration:
- **Overall warming:** +0.67°C per decade (p < 0.001)
- **Acceleration detected:** Quadratic term significant (p = 0.003)
- **Breakpoint:** Acceleration begins ~2005 (95% CI: 2003-2008)
- **Seasonal pattern:** Winter (DJF) acceleration 2x stronger than summer

## 1. Data Summary

| Metric | Value |
|--------|-------|
| Stations analyzed | 12 |
| Total observations | 131,484 |
| Quality-excluded | 1,247 (0.95%) |
| Period | 1994-01-01 to 2024-12-31 |

## 2. Overall Trend

### Linear Model

Temperature = -0.89 + 0.067 × Year


| Parameter | Estimate | SE | 95% CI | p-value |
|-----------|----------|-----|--------|---------|
| Intercept | -0.89°C | 0.12 | [-1.12, -0.66] | < 0.001 |
| Trend | +0.067°C/yr | 0.008 | [0.051, 0.083] | < 0.001 |

Interpretation: +0.67°C warming per decade.

### Quadratic Model (Acceleration Test)

Temperature = -0.72 + 0.042 × Year + 0.0018 × Year²


| Parameter | Estimate | SE | p-value |
|-----------|----------|-----|---------|
| Quadratic term | +0.0018 | 0.0006 | 0.003 |

Interpretation: Statistically significant acceleration. Rate of warming is increasing.

### Breakpoint Analysis

Piecewise regression identified a breakpoint at **2005** (95% CI: 2003-2008):

| Period | Trend (°C/decade) | SE |
|--------|-------------------|-----|
| 1994-2005 | +0.42 | 0.11 |
| 2005-2024 | +0.89 | 0.09 |

Warming rate doubled after 2005.

## 3. Station Comparison

| Station | Trend (°C/decade) | Rank |
|---------|-------------------|------|
| Tiksi, Russia | +0.92 | 1 |
| Longyearbyen | +0.84 | 2 |
| Barrow, Alaska | +0.78 | 3 |
| Resolute, Canada | +0.72 | 4 |
| Ny-Alesund | +0.68 | 5 |
| Thule, Greenland | +0.65 | 6 |
| Nord, Greenland | +0.61 | 7 |
| Dikson, Russia | +0.59 | 8 |
| Alert, Canada | +0.58 | 9 |
| Eureka, Canada | +0.55 | 10 |
| Jan Mayen, Norway | +0.51 | 11 |
| Bear Island, Norway | +0.44 | 12 |

Heterogeneity test: Q = 45.2, p < 0.001 (significant station variation)

## 4. Seasonal Patterns

| Season | Trend (°C/decade) | Acceleration (°C/decade²) | p-value |
|--------|-------------------|---------------------------|---------|
| Winter (DJF) | +1.12 | +0.048 | 0.001 |
| Spring (MAM) | +0.71 | +0.022 | 0.018 |
| Summer (JJA) | +0.34 | +0.008 | 0.241 |
| Autumn (SON) | +0.52 | +0.015 | 0.067 |

Winter acceleration is 2x the overall average. Summer shows no significant acceleration.

## 5. Figures

- `figures/trend-acceleration.png` — Time series with linear and quadratic fits
- `figures/seasonal-patterns.png` — Seasonal decomposition with trend lines
- `figures/station-comparison.png` — Map of station-level trends

## 6. Conclusions

1. Arctic warming is accelerating (quadratic trend significant)
2. Acceleration began around 2005 (±3 years)
3. Post-2005 warming rate (0.89°C/decade) is 2x pre-2005 rate
4. Winter drives acceleration; summer shows no acceleration
5. Stations vary significantly, with Siberian stations warming fastest

## 7. Statistical Notes

- All tests use α = 0.05
- Confidence intervals are 95%
- Autocorrelation addressed via Newey-West standard errors
- Missing data handled via pairwise deletion

Verifying the Analysis

Sarah checks the completed spec:

chant show 001-ana
Spec: 2026-01-16-001-ana
Type: research
Status: completed

Depends on: 2026-01-15-001-lit (completed)
Origin: data/temperature/*.csv (12 files)
Informed by: analysis/literature-review.md, analysis/research-gaps.md

Research Questions:
  [x] What is the overall warming trend (1994-2024)?
  [x] Is there evidence of acceleration (nonlinear trend)?
  [x] Do stations show consistent or divergent patterns?
  [x] How do seasonal patterns differ?
  [x] When did acceleration begin (if present)?

Acceptance Criteria:
  [x] All 12 stations analyzed with consistent methodology
  [x] Quality-flagged data excluded
  [x] Linear and quadratic trend estimates with confidence intervals
  [x] Breakpoint analysis with statistical tests
  [x] Station-level trends with heterogeneity test
  [x] Seasonal trends with acceleration testing
  [x] Three figures generated (trend, seasonal, station comparison)
  [x] temperature-findings.md with all results and statistics

Origin Tracking

The spec records exactly which data files were analyzed:

origin:
  - data/temperature/barrow-alaska.csv (sha256: a1b2c3...)
  - data/temperature/resolute-canada.csv (sha256: d4e5f6...)
  # ... 10 more files

If any of these files change, drift detection will flag the analysis as stale.

Connecting Literature and Data

The informed_by: field ensured the analysis addressed the gaps identified in the literature review:

Gap IdentifiedHow Analysis Addressed
Post-2015 accelerationExtended analysis to 2024
Station-level variabilityAnalyzed 12 stations individually
Seasonal patternsDecomposed by DJF, MAM, JJA, SON

The depends_on: field ensured the literature review completed before analysis began.

What’s Next

With individual analyses complete, Sarah coordinates a multi-step pipeline:

Pipeline Coordination — Using driver specs to coordinate data cleaning, analysis, and visualization

Week 3: Pipeline Coordination

Sarah’s analysis requires a multi-step pipeline. Each step must complete before the next begins. This phase uses driver specs to coordinate the workflow.

The Pipeline Pattern

Complex research often requires coordinated phases:

┌─────────────┐   ┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│ Data Clean  │──>│ Statistical │──>│ Sensitivity │──>│ Figure      │
│  (.1)       │   │ Analysis    │   │  Analysis   │   │ Generation  │
│             │   │  (.2)       │   │   (.3)      │   │   (.4)      │
└─────────────┘   └─────────────┘   └─────────────┘   └─────────────┘
       │                 │                 │                 │
       └─────────────────┴─────────────────┴─────────────────┘
                                   │
                                   ▼
                      ┌─────────────────────────┐
                      │      Driver Spec        │
                      │   members: [.1-.4]      │
                      │   Coordinates all steps │
                      └─────────────────────────┘

A driver spec coordinates member specs, ensuring proper sequencing and parallel execution where possible.

Why a Pipeline?

Sarah’s analysis has dependencies:

StepInputOutputDepends On
Data CleaningRaw CSVsCleaned CSVsNone
Statistical AnalysisCleaned CSVsResultsCleaning
Sensitivity AnalysisCleaned CSVs, ResultsRobustness testsCleaning, Analysis
Figure GenerationResultsPublication figuresAnalysis

Some steps can run in parallel (Sensitivity + Figures), but all depend on earlier steps.

Creating the Driver Spec

Sarah creates a driver spec to coordinate the pipeline:

chant add "Arctic analysis pipeline" --type driver

File: .chant/specs/2026-01-20-001-drv.md

---
type: driver
status: pending
prompt: driver
members:
  - 2026-01-20-001-drv.1  # Data cleaning
  - 2026-01-20-001-drv.2  # Statistical analysis
  - 2026-01-20-001-drv.3  # Sensitivity analysis
  - 2026-01-20-001-drv.4  # Figure generation
target_files:
  - analysis/pipeline-report.md
---
# Arctic analysis pipeline

## Problem

The full analysis requires four coordinated steps. Manual execution risks:
- Running steps out of order
- Missing dependencies
- Inconsistent methodology across steps

## Pipeline Steps

### Step 1: Data Cleaning (.1)
- Validate all CSV files
- Apply quality flag filters
- Standardize date formats
- Output: cleaned data files

### Step 2: Statistical Analysis (.2)
- Run trend analysis on cleaned data
- Calculate confidence intervals
- Output: statistical results

### Step 3: Sensitivity Analysis (.3)
- Test alternative methodologies
- Vary quality thresholds
- Output: robustness report

### Step 4: Figure Generation (.4)
- Generate publication-ready figures
- Apply consistent styling
- Output: PNG files for dissertation

## Execution Order

.1 (cleaning) │ ▼ .2 (analysis) │ ├─────────┐ ▼ ▼ .3 (sens) .4 (figs)


.3 and .4 can run in parallel after .2 completes.

## Acceptance Criteria

- [ ] All four member specs created
- [ ] Dependencies correctly specified
- [ ] All members complete successfully
- [ ] Pipeline report summarizing all steps

Creating Member Specs

Sarah creates each member spec. The driver spec doesn’t execute them directly — it coordinates their creation and execution.

Member .1: Data Cleaning

File: .chant/specs/2026-01-20-001-drv.1.md

---
type: code
status: pending
parent: 2026-01-20-001-drv
origin:
  - data/temperature/*.csv
target_files:
  - data/cleaned/*.csv
  - analysis/cleaning-report.md
---
# Data cleaning

## Problem

Raw temperature CSV files need standardization before analysis.

## Tasks

- [ ] Validate date formats (ISO 8601)
- [ ] Apply quality flag filtering (exclude 'X' flags)
- [ ] Handle missing values (document, don't interpolate)
- [ ] Standardize column names
- [ ] Output cleaned files to data/cleaned/

## Acceptance Criteria

- [ ] All 12 stations processed
- [ ] Quality flags applied correctly
- [ ] Cleaning report documents excluded observations
- [ ] Cleaned files ready for analysis

Member .2: Statistical Analysis

File: .chant/specs/2026-01-20-001-drv.2.md

---
type: research
status: pending
parent: 2026-01-20-001-drv
depends_on:
  - 2026-01-20-001-drv.1
origin:
  - data/cleaned/*.csv
target_files:
  - analysis/statistical-results.json
  - analysis/statistical-report.md
---
# Statistical analysis

## Problem

Run core statistical analysis on cleaned data.

## Methodology

- Linear trend estimation
- Quadratic (acceleration) test
- Breakpoint detection
- Seasonal decomposition

## Acceptance Criteria

- [ ] All statistical tests documented
- [ ] Results in JSON for downstream use
- [ ] Report with interpretation

Member .3: Sensitivity Analysis

File: .chant/specs/2026-01-20-001-drv.3.md

---
type: research
status: pending
parent: 2026-01-20-001-drv
depends_on:
  - 2026-01-20-001-drv.1
  - 2026-01-20-001-drv.2
origin:
  - data/cleaned/*.csv
informed_by:
  - analysis/statistical-results.json
target_files:
  - analysis/sensitivity-report.md
---
# Sensitivity analysis

## Problem

Test robustness of findings to methodological choices.

## Tests

1. **Quality threshold**: Vary from strict (A only) to permissive (A, B, C)
2. **Baseline period**: 1994-2000 vs 1994-2005
3. **Trend method**: OLS vs Theil-Sen (robust)
4. **Missing data**: Exclude vs interpolate

## Acceptance Criteria

- [ ] All four sensitivity tests run
- [ ] Results compared to primary analysis
- [ ] Robustness assessment documented

Member .4: Figure Generation

File: .chant/specs/2026-01-20-001-drv.4.md

---
type: code
status: pending
parent: 2026-01-20-001-drv
depends_on:
  - 2026-01-20-001-drv.2
informed_by:
  - analysis/statistical-results.json
target_files:
  - analysis/figures/main-trend.png
  - analysis/figures/seasonal.png
  - analysis/figures/station-map.png
  - analysis/figures/sensitivity.png
---
# Figure generation

## Problem

Generate publication-ready figures for dissertation.

## Figures

1. **main-trend.png**: Time series with trend and uncertainty
2. **seasonal.png**: Four-panel seasonal decomposition
3. **station-map.png**: Geographic map with station trends
4. **sensitivity.png**: Sensitivity analysis comparison

## Style Requirements

- Font: Times New Roman, 12pt
- Colors: Publication-ready palette
- Resolution: 300 DPI
- Format: PNG with transparency

## Acceptance Criteria

- [ ] All four figures generated
- [ ] Consistent styling applied
- [ ] Ready for dissertation inclusion

Running the Pipeline

Sarah executes the driver:

chant work 001-drv

Chant automatically:

  1. Creates worktrees for each member spec
  2. Executes .1 (cleaning) first
  3. Waits for .1 to complete
  4. Executes .2 (analysis)
  5. Waits for .2 to complete
  6. Executes .3 and .4 in parallel
$ chant work 001-drv
Working: 2026-01-20-001-drv (Arctic analysis pipeline)

Phase 1: Sequential
  [✓] 2026-01-20-001-drv.1 (Data cleaning) - 2m 15s

Phase 2: Sequential
  [✓] 2026-01-20-001-drv.2 (Statistical analysis) - 4m 32s

Phase 3: Parallel
  [✓] 2026-01-20-001-drv.3 (Sensitivity analysis) - 3m 45s
  [✓] 2026-01-20-001-drv.4 (Figure generation) - 1m 58s

Driver complete. All 4 members succeeded.

Pipeline Report

The driver spec generates a summary:

File: analysis/pipeline-report.md (generated)

# Arctic Analysis Pipeline Report

## Execution Summary

| Step | Spec ID | Duration | Status |
|------|---------|----------|--------|
| Data Cleaning | drv.1 | 2m 15s | Success |
| Statistical Analysis | drv.2 | 4m 32s | Success |
| Sensitivity Analysis | drv.3 | 3m 45s | Success |
| Figure Generation | drv.4 | 1m 58s | Success |

Total pipeline duration: 12m 30s

## Data Flow

Raw CSVs (12 files) │ ▼ [drv.1] Cleaned CSVs (12 files, 1,247 observations excluded) │ ▼ [drv.2] Statistical Results (JSON + report) │ ├──────────────────┬───────────────────┐ ▼ [drv.3] ▼ [drv.4] │ Sensitivity Report 4 Figures │ │ │ │ └───────────────────┴──────────────────┘ │ ▼ Pipeline Complete


## Key Findings

From drv.2 (Statistical Analysis):
- Warming trend: +0.67°C/decade
- Acceleration: Significant (p = 0.003)
- Breakpoint: 2005

From drv.3 (Sensitivity Analysis):
- Results robust to quality threshold variations
- Baseline period has minor effect (<5% change)
- Theil-Sen gives similar results to OLS

## Generated Outputs

- `data/cleaned/*.csv` (12 files)
- `analysis/cleaning-report.md`
- `analysis/statistical-results.json`
- `analysis/statistical-report.md`
- `analysis/sensitivity-report.md`
- `analysis/figures/*.png` (4 files)

Verifying the Pipeline

chant show 001-drv
Spec: 2026-01-20-001-drv
Type: driver
Status: completed

Members:
  [x] 2026-01-20-001-drv.1 (Data cleaning) - completed
  [x] 2026-01-20-001-drv.2 (Statistical analysis) - completed
  [x] 2026-01-20-001-drv.3 (Sensitivity analysis) - completed
  [x] 2026-01-20-001-drv.4 (Figure generation) - completed

Acceptance Criteria:
  [x] All four member specs created
  [x] Dependencies correctly specified
  [x] All members complete successfully
  [x] Pipeline report summarizing all steps

Pipeline Benefits

BenefitHow It Helps Sarah
ReproducibilityPipeline can be re-run identically
ProvenanceEach output traces to specific inputs
ParallelismIndependent steps run concurrently
DocumentationPipeline report captures the whole workflow

What’s Next

With the pipeline complete, Sarah needs to handle ongoing changes:

Drift Detection — Detecting when new data or papers arrive and re-verifying analysis

Week 4+: Drift Detection

Research doesn’t end when analysis completes. New data arrives, papers are published, and findings may become stale. This phase shows how chant detects drift and triggers re-verification.

The Maintenance Pattern

Drift detection monitors three types of changes:

┌─────────────────────────────────────────────────────────────────┐
│                    Drift Detection Types                         │
└─────────────────────────────────────────────────────────────────┘

     origin:                  informed_by:              tracks:
   ┌──────────┐             ┌──────────────┐         ┌──────────┐
   │  Data    │             │   Sources    │         │  Code    │
   │  Files   │             │  (Papers)    │         │ (Scripts)│
   └────┬─────┘             └──────┬───────┘         └────┬─────┘
        │                          │                      │
        ▼                          ▼                      ▼
   ┌──────────┐             ┌──────────────┐         ┌──────────┐
   │  New     │             │   Updated    │         │   Doc    │
   │  Data    │             │   Research   │         │ Changes  │
   │  Rows    │             │              │         │          │
   └────┬─────┘             └──────┬───────┘         └────┬─────┘
        │                          │                      │
        └──────────────────────────┴──────────────────────┘
                                   │
                                   ▼
                        ┌─────────────────────┐
                        │   chant drift       │
                        │  Detects changes,   │
                        │  suggests re-run    │
                        └─────────────────────┘

Scenario: New Data Arrives

Two months after completing her analysis, Sarah receives updated temperature data. The monitoring stations added January and February 2025 observations.

# New data added to existing files
data/temperature/barrow-alaska.csv       # +59 rows
data/temperature/resolute-canada.csv     # +59 rows
# ... 10 more stations

Sarah runs drift detection:

$ chant drift

Output:

Drift Report
============

Spec: 2026-01-16-001-ana (Analyze Arctic temperature acceleration)
Status: DRIFT DETECTED

Origin files changed since completion (2026-01-16):
  - data/temperature/barrow-alaska.csv
      Modified: 2026-03-15
      Rows added: 59
  - data/temperature/resolute-canada.csv
      Modified: 2026-03-15
      Rows added: 59
  [... 10 more files ...]

Total new observations: 708

Recommendation: Re-run spec to incorporate new data
  $ chant reset 001-ana && chant work 001-ana

---

Spec: 2026-01-20-001-drv (Arctic analysis pipeline)
Status: DRIFT DETECTED (via member)

Member 2026-01-20-001-drv.1 has upstream drift
Cascade: drv.1 → drv.2 → drv.3, drv.4

Recommendation: Re-run entire pipeline
  $ chant reset 001-drv && chant work 001-drv

Re-running Analysis

Sarah re-runs the pipeline to incorporate new data:

$ chant reset 001-drv
$ chant work 001-drv

The agent re-executes the pipeline with the new data, updating all analysis outputs.

Comparison Report

After replay, chant generates a comparison:

File: analysis/replay-comparison.md (generated)

# Replay Comparison Report

## Summary

| Metric | Original (Jan 2026) | Updated (Mar 2026) | Change |
|--------|---------------------|---------------------|--------|
| Observations | 131,484 | 132,180 | +696 |
| Data end date | 2024-12-31 | 2025-02-28 | +59 days |

## Key Finding Changes

### Overall Trend

| Metric | Original | Updated | Change |
|--------|----------|---------|--------|
| Warming rate | +0.67°C/decade | +0.69°C/decade | +0.02 |
| Acceleration p-value | 0.003 | 0.002 | More significant |
| Breakpoint | 2005 | 2005 | Unchanged |

Interpretation: Additional data strengthens the acceleration finding.

### Station-Level Changes

| Station | Original Trend | Updated Trend | Change |
|---------|---------------|---------------|--------|
| Barrow | +0.78 | +0.81 | +0.03 |
| Tiksi | +0.92 | +0.94 | +0.02 |
| [others] | ... | ... | ... |

### Seasonal Pattern

Winter 2025 data shows continued strong warming:
- DJF 2024-2025 anomaly: +2.3°C (above 1994-2000 baseline)
- Consistent with acceleration trend

## Conclusion

New data strengthens original findings. No methodological changes required.

Scenario: New Paper Published

A month later, a relevant paper is published. Sarah adds it to her collection:

# New paper added
papers/acceleration/petrov-2026-siberian-warming.pdf

She updates the paper index and runs drift:

$ chant drift 001-lit
Drift Report
============

Spec: 2026-01-15-001-lit (Synthesize Arctic warming literature)
Status: DRIFT DETECTED

Informed-by files changed since completion (2026-01-15):
  - papers/acceleration/petrov-2026-siberian-warming.pdf
      Added: 2026-04-10
      Status: New file

Recommendation: Re-run spec to incorporate new paper
  $ chant reset 001-lit && chant work 001-lit

Re-running Selective Analysis

Sarah can re-run just the affected spec:

$ chant reset 001-lit
$ chant work 001-lit

The agent re-synthesizes the literature review incorporating the new paper.

Periodic Drift Checks

Sarah runs drift detection periodically to check for stale findings:

$ chant drift

This shows all specs with detected drift, allowing her to decide which to re-run.

Drift Types Summary

Drift TypeTriggerExampleResponse
originData files changeNew temperature readingsRe-run analysis
informed_bySource materials changeNew paper publishedRe-run synthesis
tracksTracked code changesAnalysis script updatedRe-run documentation

Recurring Research

For ongoing monitoring, Sarah documents the intended cadence:

File: .chant/specs/2026-03-01-001-mon.md

---
type: research
status: pending
schedule: monthly
origin:
  - data/temperature/*.csv
target_files:
  - reports/monthly/temperature-update.md
---
# Monthly temperature update

Generate monthly update on Arctic temperature trends.

## Acceptance Criteria

- [ ] New data incorporated
- [ ] Trend recalculated
- [ ] Comparison to previous month
- [ ] Alert if significant change detected

The schedule: monthly field documents the intended recurrence. Sarah manually runs chant work 001-mon each month when new data arrives.

Audit Trail

Git commits track the history of each spec execution:

$ git log --oneline --grep="001-ana"
abc1234 chant(001-ana): update analysis with May data
def5678 chant(001-ana): update analysis with March data
9ab0cde chant(001-ana): initial Arctic temperature analysis

Each re-run creates a new commit, preserving the full audit trail.

Dissertation Defense

When Sarah defends her dissertation, she can demonstrate:

  1. Reproducibility: Every analysis has a spec that can be re-run
  2. Provenance: Every finding traces to specific data files and versions
  3. Currency: Drift detection ensures findings reflect latest data
  4. Methodology: Specs document the exact procedures used

Her advisor’s reproducibility audit is straightforward:

# Show all specs with their completion dates
$ chant list

# Verify no drift in final results
$ chant drift

# Re-run entire pipeline to verify reproducibility
$ chant reset 001-drv && chant work 001-drv

What’s Next

This completes the academic path. See the artifacts directory for complete spec examples.

To explore the parallel developer path, start at:

Developer Scenario — Follow Alex Torres investigating a microservices migration

The Engineering Context

Alex Torres

Alex Torres is a Staff Engineer at TechCorp, a mid-sized SaaS company. They’ve been asked to evaluate migrating the company’s monolithic backend to microservices.

AttributeValue
TitleStaff Engineer
CompanyTechCorp
Team Size12 engineers across 3 teams
Tenure4 years
Timeline6-week evaluation

The Codebase

TechCorp’s backend is a Python monolith that has grown over 7 years:

MetricValue
Lines of code80,000
Python packages42
Database tables156
API endpoints234
Test coverage68%
Average deploymentWeekly

Migration Question

Leadership wants to know: Should we migrate to microservices, and if so, how?

The question has layers:

  • Which components should be extracted first?
  • What are the coupling hotspots?
  • How will this affect the 3 existing teams?
  • What’s the risk profile?

Alex needs to research the codebase systematically, not just skim and guess.

Current Pain Points

The monolith has symptoms that suggest extraction might help:

SymptomImpact
Slow CI/CDFull test suite takes 45 minutes
Deployment couplingTeam A’s changes require Team B’s review
Scaling bottlenecksAuth service needs 10x capacity of reporting
Onboarding timeNew engineers take 3 months to be productive
Bug blast radiusOne module failure can crash everything

Why Research First?

Alex has seen migrations fail when teams skip investigation:

“We extracted the auth service first because it felt obvious. Turns out it had deep coupling to user preferences, billing, and audit logging. Should have mapped the dependencies first.”

Chant provides structured investigation before implementation.

Project Setup

Alex initializes chant in the monolith repository:

# Initialize chant
chant init --agent claude

# Create directories for investigation outputs
mkdir -p docs/architecture
mkdir -p analysis/coupling
mkdir -p analysis/metrics

# Create context directory for external inputs
mkdir -p .chant/context/migration-research

Directory structure:

techcorp-backend/
├── .chant/
│   ├── specs/              # Research and implementation specs
│   ├── context/            # External data (metrics, diagrams)
│   │   └── migration-research/
│   └── config.md
├── src/                    # 80K LOC Python monolith
│   ├── auth/
│   ├── billing/
│   ├── reporting/
│   ├── users/
│   └── ...
├── tests/                  # Test suite
├── docs/                   # Documentation outputs
│   └── architecture/
└── analysis/              # Analysis outputs

Investigation Timeline

Alex plans a four-week research phase:

Week 1          Week 2              Week 3            Week 4
┌──────────┐   ┌───────────────┐   ┌──────────────┐   ┌──────────────┐
│ Codebase │   │ Architecture  │   │    POC       │   │   Ongoing    │
│ Analysis │──>│ Documentation │──>│ Extraction   │──>│ Maintenance  │
│(Coupling)│   │  (Decisions)  │   │  (Driver)    │   │   (Drift)    │
└──────────┘   └───────────────┘   └──────────────┘   └──────────────┘

Spec Workflow Preview

Alex’s investigation will use these spec types:

WeekSpec TypePurpose
1research with informed_by:Analyze codebase coupling
2documentation with tracks:Document architecture decisions
3driver with membersCoordinate POC extraction
4+Drift detectionKeep docs current as code evolves

Team Structure

Unlike solo academic work, Alex coordinates with teams:

  • Alex creates specs, reviews findings, presents to leadership
  • Chant agents analyze coupling, generate documentation
  • Team leads review architecture decisions
  • CI/CD runs weekly coupling reports

Metrics Context

Alex gathers baseline metrics from production:

File: .chant/context/migration-research/production-metrics.md

# Production Metrics Baseline

Source: Datadog APM, January 2026
Period: Last 30 days

## Request Volume by Module

| Module | Requests/day | P99 Latency | Error Rate |
|--------|-------------|-------------|------------|
| auth | 2,400,000 | 45ms | 0.01% |
| users | 890,000 | 120ms | 0.05% |
| billing | 340,000 | 380ms | 0.12% |
| reporting | 45,000 | 2,400ms | 0.08% |
| notifications | 180,000 | 89ms | 0.02% |

## Resource Utilization

| Module | CPU % | Memory % | DB Queries/req |
|--------|-------|----------|----------------|
| auth | 15% | 8% | 2.1 |
| users | 22% | 18% | 4.7 |
| billing | 45% | 35% | 12.3 |
| reporting | 78% | 52% | 28.6 |
| notifications | 8% | 5% | 1.4 |

## Observations

- Reporting consumes disproportionate resources
- Auth has highest volume but lowest resource use
- Billing has highest error rate and query count

What’s Next

With the project initialized and metrics gathered, Alex begins codebase investigation:

Investigation — Analyzing coupling and dependencies using research specs with informed_by:

Week 1: Codebase Investigation

Alex begins with systematic codebase analysis. This phase uses research specs to understand coupling, dependencies, and extraction candidates.

The Investigation Pattern

Codebase investigation uses the same pattern as literature review — synthesizing multiple sources:

┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│ src/auth/   │   │src/billing/ │   │src/users/   │
│   *.py      │   │   *.py      │   │   *.py      │
└──────┬──────┘   └──────┬──────┘   └──────┬──────┘
       │                 │                 │
       └────────────────┬┴─────────────────┘
                        │
                        ▼
              ┌─────────────────────┐
              │   Research Spec     │
              │  informed_by:       │
              │   - src/**/*.py     │
              └──────────┬──────────┘
                         │
                         ▼
              ┌─────────────────────┐
              │  Coupling Analysis  │
              │  (Dependencies)     │
              └─────────────────────┘

The informed_by: field tells the agent what code to analyze. Instead of papers, it reads source files.

Creating the Investigation Spec

Alex creates a research spec for coupling analysis:

chant add "Analyze codebase coupling for microservices evaluation" --type research

File: .chant/specs/2026-01-27-001-cpl.md

---
type: research
status: pending
prompt: research-analysis
informed_by:
  - src/**/*.py
  - .chant/context/migration-research/production-metrics.md
target_files:
  - analysis/coupling/dependency-matrix.md
  - analysis/coupling/extraction-candidates.md
  - analysis/coupling/risk-assessment.md
---
# Analyze codebase coupling for microservices evaluation

## Problem

TechCorp's 80K LOC Python monolith needs evaluation for microservices migration.
Before recommending extraction candidates, I need to understand:
- Which modules are tightly coupled?
- Which modules are loosely coupled and easy to extract?
- What shared state exists between modules?
- Where are the database coupling hotspots?

## Research Questions

- [ ] What are the import dependencies between modules?
- [ ] Which modules share database tables?
- [ ] What is the cyclomatic complexity distribution?
- [ ] Which modules have the most external callers?
- [ ] What shared utilities create implicit coupling?

## Methodology

### Import Analysis
1. Parse all Python files for import statements
2. Build module-to-module dependency graph
3. Calculate coupling metrics (fan-in, fan-out)
4. Identify circular dependencies

### Database Coupling
1. Find all ORM model definitions
2. Map models to modules
3. Identify cross-module foreign keys
4. Analyze query patterns in each module

### Complexity Analysis
1. Calculate cyclomatic complexity per module
2. Identify high-complexity functions
3. Map complexity to test coverage
4. Flag under-tested complex code

### Extraction Scoring
1. Score each module on extractability (1-10)
2. Consider: coupling, complexity, test coverage, team ownership
3. Recommend extraction order

## Acceptance Criteria

- [ ] Import dependency matrix generated
- [ ] Database coupling analysis complete
- [ ] Complexity metrics calculated
- [ ] Extraction candidates ranked with rationale
- [ ] Risk assessment for top 3 candidates
- [ ] All findings written to target files

Running the Investigation

Alex executes the spec:

chant work 001-cpl

The agent reads all Python files (via informed_by:), analyzes patterns, and generates findings.

Agent Output

File: analysis/coupling/dependency-matrix.md (generated)

# Module Dependency Matrix

## Import Dependencies

Rows: importing module, Columns: imported module

|              | auth | users | billing | reporting | notifications | utils |
|--------------|------|-------|---------|-----------|---------------|-------|
| auth         | -    | 3     | 0       | 0         | 0             | 12    |
| users        | 5    | -     | 2       | 0         | 1             | 18    |
| billing      | 3    | 8     | -       | 1         | 4             | 22    |
| reporting    | 2    | 6     | 5       | -         | 0             | 31    |
| notifications| 1    | 3     | 2       | 0         | -             | 9     |

## Coupling Metrics

| Module | Fan-In | Fan-Out | Instability | Notes |
|--------|--------|---------|-------------|-------|
| auth | 11 | 15 | 0.58 | Core dependency, moderate coupling |
| users | 20 | 26 | 0.57 | Most imported, hub module |
| billing | 6 | 40 | 0.87 | High instability, many outgoing deps |
| reporting | 0 | 44 | 1.00 | Pure consumer, no dependents |
| notifications | 3 | 15 | 0.83 | Leaf node, few dependents |
| utils | 92 | 0 | 0.00 | Shared utilities, imported everywhere |

## Circular Dependencies

Found 3 circular dependency chains:

1. `auth → users → auth` (via auth.permissions)
2. `billing → users → billing` (via billing.subscriptions)
3. `users → billing → notifications → users` (via user preferences)

## Interpretation

- **users** is a hub — everything depends on it
- **reporting** is a leaf — depends on others, nothing depends on it
- **utils** is problematic — 92 incoming dependencies creates implicit coupling
- Circular dependencies complicate extraction ordering

File: analysis/coupling/extraction-candidates.md (generated)

# Extraction Candidates

## Ranking

| Rank | Module | Score | Rationale |
|------|--------|-------|-----------|
| 1 | reporting | 8.5/10 | Leaf node, no dependents, resource-heavy |
| 2 | notifications | 7.2/10 | Few dependents, clear boundaries |
| 3 | billing | 5.8/10 | Important, but coupled to users |
| 4 | auth | 4.2/10 | Core dependency, high risk |
| 5 | users | 2.1/10 | Hub module, extract last |

## Detailed Analysis

### 1. Reporting (Score: 8.5)

**Pros:**
- Zero incoming dependencies (pure consumer)
- Clear API boundary (5 endpoints)
- Distinct resource profile (heavy CPU/memory)
- Team C owns entirely

**Cons:**
- Heavy database access (28.6 queries/request)
- Uses 12 models from other modules

**Recommendation:** Extract first. Create read replicas for data access.

### 2. Notifications (Score: 7.2)

**Pros:**
- Only 3 incoming dependencies
- Async by nature (good for separate service)
- Clear business boundary

**Cons:**
- Depends on user preferences from users module
- Circular dependency with users

**Recommendation:** Extract second after resolving user preference coupling.

### 3. Billing (Score: 5.8)

**Pros:**
- Important business domain
- Would benefit from isolated scaling

**Cons:**
- 8 dependencies on users module
- Circular dependency chain
- Highest error rate (0.12%)

**Recommendation:** Extract third. Requires event-driven decoupling.

## Extraction Order

Phase 1: reporting (standalone) Phase 2: notifications (after preference refactor) Phase 3: billing (after event bus setup) Phase 4: auth (after users interface stabilizes) Phase 5: users (becomes core service)

File: analysis/coupling/risk-assessment.md (generated)

# Risk Assessment

## Top 3 Candidates

### 1. Reporting Extraction

| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Data consistency | Medium | High | Read replicas with eventual consistency |
| Query performance | High | Medium | Dedicated database with denormalized views |
| Team coordination | Low | Low | Single team ownership |

**Overall Risk: MEDIUM**

### 2. Notifications Extraction

| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Preference coupling | High | Medium | Extract preferences as shared service |
| Message ordering | Medium | High | Use ordered message queue |
| Delivery guarantees | Medium | High | Implement idempotency |

**Overall Risk: MEDIUM-HIGH**

### 3. Billing Extraction

| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Transaction consistency | High | Critical | Saga pattern for distributed transactions |
| User data access | High | High | Event-driven sync |
| Revenue impact | Medium | Critical | Extensive parallel running |

**Overall Risk: HIGH**

## Recommendations

1. Start with reporting (lowest risk, highest standalone value)
2. Build event infrastructure before billing extraction
3. Consider extracting user preferences as shared service first
4. Plan 6-month timeline for first two extractions

Verifying the Investigation

Alex reviews the completed spec:

chant show 001-cpl
Spec: 2026-01-27-001-cpl
Type: research
Status: completed

Informed by: src/**/*.py (342 files)

Research Questions:
  [x] What are the import dependencies between modules?
  [x] Which modules share database tables?
  [x] What is the cyclomatic complexity distribution?
  [x] Which modules have the most external callers?
  [x] What shared utilities create implicit coupling?

Acceptance Criteria:
  [x] Import dependency matrix generated
  [x] Database coupling analysis complete
  [x] Complexity metrics calculated
  [x] Extraction candidates ranked with rationale
  [x] Risk assessment for top 3 candidates
  [x] All findings written to target files

The Provenance Trail

The spec records exactly what was analyzed:

  • 342 Python files read
  • Production metrics incorporated
  • Clear methodology documented

When leadership asks “How did you conclude reporting should be first?”, Alex points to the spec and its analysis.

What’s Next

With investigation complete, Alex documents the architecture:

Documentation — Creating architecture documentation that tracks source code with tracks:

Week 2: Architecture Documentation

With investigation complete, Alex creates architecture documentation that stays synchronized with the codebase. This phase uses documentation specs with the tracks: field.

The Documentation Pattern

Documentation specs create a linkage between docs and source code:

┌─────────────────────────────────────────────────────────────────┐
│                    Documentation Spec                            │
└─────────────────────────────────────────────────────────────────┘

     informed_by:                tracks:                  target:
   ┌──────────────┐         ┌──────────────┐         ┌──────────────┐
   │  Analysis    │         │   Source     │         │   Output     │
   │  Results     │    +    │    Code      │    =    │    Docs      │
   │              │         │              │         │              │
   └──────────────┘         └──────────────┘         └──────────────┘

   Previous research        Code being               Generated
   findings                  documented               documentation

The tracks: field creates a bidirectional link:

  • Docs are generated FROM the tracked code
  • When tracked code changes, docs become stale (drift)

Why Documentation Specs?

Traditional documentation has problems:

ProblemTraditional DocsDocumentation Spec
Stale immediatelyUpdated manually, if at allDrift detection alerts
Unclear source“Where did this come from?”tracks: links to code
Hard to verifyNo way to check accuracyRe-run spec to regenerate
Scattered ownershipAnyone can editSpec owns the doc

Creating the Documentation Spec

Alex creates a documentation spec for architecture:

chant add "Document system architecture"

File: .chant/specs/2026-01-28-001-doc.md

---
type: documentation
status: pending
prompt: documentation
depends_on:
  - 2026-01-27-001-cpl  # Coupling analysis
informed_by:
  - analysis/coupling/dependency-matrix.md
  - analysis/coupling/extraction-candidates.md
tracks:
  - src/auth/**/*.py
  - src/users/**/*.py
  - src/billing/**/*.py
  - src/reporting/**/*.py
  - src/notifications/**/*.py
target_files:
  - docs/architecture/overview.md
  - docs/architecture/modules.md
  - docs/architecture/data-flow.md
  - docs/architecture/decisions.md
---
# Document system architecture

## Problem

The codebase lacks up-to-date architecture documentation. New engineers
take 3 months to become productive, partly because there's no map.

This documentation should:
- Provide a high-level overview of the system
- Document each module's responsibilities and interfaces
- Show data flow between modules
- Capture architecture decisions

## Tracked Files

This spec tracks `src/{auth,users,billing,reporting,notifications}/**/*.py`.
When these files change, the documentation may become stale.

## Documentation Sections

### 1. Overview (overview.md)
- System purpose and boundaries
- High-level architecture diagram (ASCII)
- Technology stack
- Deployment topology

### 2. Modules (modules.md)
- Each module's purpose
- Public interfaces
- Internal structure
- Team ownership

### 3. Data Flow (data-flow.md)
- Request flow through the system
- Data dependencies between modules
- Database access patterns
- External service integrations

### 4. Decisions (decisions.md)
- Key architecture decisions (ADRs)
- Rationale for current structure
- Known limitations and tech debt

## Acceptance Criteria

- [ ] Overview with architecture diagram
- [ ] All 5 modules documented
- [ ] Data flow diagram created
- [ ] 3+ architecture decisions documented
- [ ] All docs reference specific source locations

Running the Documentation Spec

Alex executes the spec:

chant work 001-doc

The agent:

  1. Reads the coupling analysis (via informed_by:)
  2. Reads tracked source files
  3. Generates documentation that maps to the code

Agent Output

File: docs/architecture/overview.md (generated)

# System Architecture Overview

## Purpose

TechCorp Backend is a B2B SaaS platform providing project management,
billing, and reporting services. It handles 3.8M requests/day across
5,000 customer accounts.

## Architecture Diagram

                ┌─────────────────────────────────────┐
                │           Load Balancer             │
                └──────────────────┬──────────────────┘
                                   │
                ┌──────────────────┴──────────────────┐
                │                                      │
        ┌───────┴───────┐                  ┌──────────┴──────────┐
        │   API Layer   │                  │     Web Layer       │
        │   (REST)      │                  │    (GraphQL)        │
        └───────┬───────┘                  └──────────┬──────────┘
                │                                      │
                └──────────────────┬───────────────────┘
                                   │
┌──────────┬──────────┬────────────┼────────────┬──────────────────┐
│          │          │            │            │                  │

┌───┴───┐ ┌────┴────┐ ┌───┴────┐ ┌─────┴─────┐ ┌────┴─────┐ ┌─────────┴──────┐ │ Auth │ │ Users │ │Billing │ │ Reporting │ │ Notifs │ │ Utils │ │ │ │ │ │ │ │ │ │ │ │ (Shared) │ └───┬───┘ └────┬────┘ └───┬────┘ └─────┬─────┘ └────┬─────┘ └────────────────┘ │ │ │ │ │ └──────────┴──────────┴────────────┴────────────┘ │ ┌────────────┴────────────┐ │ │ ┌────┴────┐ ┌─────┴─────┐ │ Primary │ │ Redis │ │ (PG) │ │ Cache │ └─────────┘ └───────────┘


## Technology Stack

| Layer | Technology |
|-------|------------|
| Language | Python 3.11 |
| Framework | FastAPI |
| Database | PostgreSQL 15 |
| Cache | Redis 7 |
| Queue | RabbitMQ |
| Container | Docker |
| Orchestration | Kubernetes |

## Deployment Topology

| Environment | Instances | Database | Purpose |
|-------------|-----------|----------|---------|
| Production | 12 pods | Primary + 2 replicas | Live traffic |
| Staging | 3 pods | Single instance | Pre-release testing |
| Development | 1 pod | Docker compose | Local development |

File: docs/architecture/modules.md (generated)

# Module Documentation

## Auth Module

**Location:** `src/auth/`
**Owner:** Platform Team
**LOC:** 8,200

### Purpose

Handles authentication, authorization, and session management.

### Public Interface

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/auth/login` | POST | User login |
| `/auth/logout` | POST | Session termination |
| `/auth/refresh` | POST | Token refresh |
| `/auth/permissions` | GET | Get user permissions |

### Key Files

- `src/auth/routes.py:1-150` — API endpoints
- `src/auth/services.py:1-280` — Business logic
- `src/auth/models.py:1-95` — ORM models
- `src/auth/permissions.py:1-180` — Permission system

### Dependencies

- Imports from: users (3), utils (12)
- Imported by: users (5), billing (3), reporting (2), notifications (1)

---

## Users Module

**Location:** `src/users/`
**Owner:** Platform Team
**LOC:** 12,400

### Purpose

Manages user accounts, profiles, preferences, and team relationships.

### Public Interface

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/users` | GET | List users |
| `/users/{id}` | GET | Get user details |
| `/users/{id}` | PATCH | Update user |
| `/users/{id}/preferences` | GET/PUT | User preferences |
| `/users/{id}/teams` | GET | User's teams |

### Key Files

- `src/users/routes.py:1-320` — API endpoints
- `src/users/services.py:1-450` — Business logic
- `src/users/models.py:1-180` — ORM models (User, Team, Preference)

### Dependencies

- Imports from: auth (5), billing (2), notifications (1), utils (18)
- Imported by: auth (3), billing (8), reporting (6), notifications (3)

---

[... billing, reporting, notifications modules ...]

File: docs/architecture/decisions.md (generated)

# Architecture Decision Records

## ADR-001: Monolithic Architecture

**Date:** 2017-03-15
**Status:** Active (but under evaluation)

### Context

When TechCorp was founded, team size was 3 engineers. A monolithic
architecture provided:
- Simpler deployment
- Easier debugging
- Faster initial development

### Decision

Build as a single deployable unit with module-based organization.

### Consequences

- Positive: Fast time-to-market in years 1-3
- Negative: Scaling and deployment coupling issues at current size

### Source Evidence

Found in `src/config/settings.py:1-15` (original architecture notes)

---

## ADR-002: Shared Utils Pattern

**Date:** 2018-06-20
**Status:** Active (identified as tech debt)

### Context

Common functionality needed across modules: logging, caching, database
connections.

### Decision

Create `src/utils/` package imported by all modules.

### Consequences

- Positive: Code reuse, consistent patterns
- Negative: 92 incoming dependencies create implicit coupling

### Source Evidence

Found in `src/utils/__init__.py:1-50` (utility exports)

### Recommendation

Consider splitting utils into:
- `utils/core` — Database, config (always needed)
- `utils/cache` — Caching (needed by some)
- `utils/logging` — Logging (standalone)

---

## ADR-003: PostgreSQL for All Data

**Date:** 2017-03-20
**Status:** Active

### Context

Needed a reliable, scalable database for all application data.

### Decision

Use PostgreSQL for all persistent data, no polyglot persistence.

### Consequences

- Positive: Single database to manage, strong consistency
- Negative: Reporting queries compete with OLTP workloads

### Source Evidence

Found in `src/config/database.py:1-30` (single connection pool)

Tracking and Drift

The tracks: field creates a linkage. When tracked files change, documentation becomes stale:

$ chant drift 001-doc
Drift Report
============

Spec: 2026-01-28-001-doc (Document system architecture)
Status: UP TO DATE

Tracked files (152) unchanged since completion (2026-01-28)

Next check scheduled: 2026-02-04

Later, when a developer modifies src/auth/routes.py:

$ chant drift 001-doc
Drift Report
============

Spec: 2026-01-28-001-doc (Document system architecture)
Status: DRIFT DETECTED

Tracked files changed since completion (2026-01-28):
  - src/auth/routes.py
      Modified: 2026-02-15
      Lines changed: +45, -12
      New endpoint: /auth/mfa/setup

Recommendation: Re-run spec to update documentation
  $ chant reset 001-doc && chant work 001-doc

Documentation Benefits

BenefitHow It Helps
Always currentDrift detection catches stale docs
TraceableEvery statement links to source code
ReproducibleRe-run spec to regenerate from scratch
VerifiedAgent validates docs match code

What’s Next

With architecture documented, Alex coordinates a POC extraction:

Implementation — Using driver specs to coordinate the reporting service extraction

Week 3: Implementation

Based on the investigation and documentation, Alex coordinates a proof-of-concept extraction of the reporting service. This phase uses driver specs to coordinate multiple implementation steps.

The Implementation Pattern

Service extraction requires coordinated changes:

┌─────────────────────────────────────────────────────────────────┐
│                     Driver Spec                                  │
│              Reporting Service Extraction                        │
└─────────────────────────────────────────────────────────────────┘
                              │
        ┌─────────────────────┼─────────────────────┐
        │                     │                     │
        ▼                     ▼                     ▼
   ┌─────────┐          ┌─────────┐          ┌─────────┐
   │  API    │          │Database │          │  Tests  │
   │ Extract │          │ Migrate │          │  Suite  │
   │  (.1)   │          │  (.2)   │          │  (.3)   │
   └────┬────┘          └────┬────┘          └────┬────┘
        │                     │                     │
        └─────────────────────┴─────────────────────┘
                              │
                              ▼
                    ┌─────────────────┐
                    │  Integration    │
                    │   Testing       │
                    │    (.4)         │
                    └─────────────────┘

Creating the Driver Spec

Alex creates a driver spec for the extraction:

chant add "Extract reporting service POC" --type driver

File: .chant/specs/2026-02-01-001-ext.md

---
type: driver
status: pending
prompt: driver
depends_on:
  - 2026-01-27-001-cpl  # Coupling analysis
  - 2026-01-28-001-doc  # Architecture docs
informed_by:
  - analysis/coupling/extraction-candidates.md
  - analysis/coupling/risk-assessment.md
members:
  - 2026-02-01-001-ext.1  # API extraction
  - 2026-02-01-001-ext.2  # Database migration
  - 2026-02-01-001-ext.3  # Test suite
  - 2026-02-01-001-ext.4  # Integration testing
target_files:
  - services/reporting/README.md
---
# Extract reporting service POC

## Problem

The reporting module is the best extraction candidate (score: 8.5/10):
- Zero incoming dependencies
- Clear API boundary
- Distinct resource profile
- Single team ownership

This POC extracts it as a standalone service to validate the approach.

## Extraction Steps

### Step 1: API Extraction (.1)
- Create new service repository structure
- Extract reporting endpoints
- Implement API gateway routing
- Output: Standalone reporting service

### Step 2: Database Migration (.2)
- Create read replica for reporting
- Set up denormalized views
- Configure connection pooling
- Output: Isolated data access

### Step 3: Test Suite (.3)
- Migrate existing reporting tests
- Add integration tests
- Create contract tests for API
- Output: Comprehensive test coverage

### Step 4: Integration Testing (.4)
- Test with monolith in parallel
- Verify data consistency
- Load test the new service
- Output: Validation report

## Execution Order

.1 (API) ──────────┐ │ .2 (Database) ─────┼───> .4 (Integration) │ .3 (Tests) ────────┘


.1, .2, .3 can run in parallel; .4 depends on all three.

## Acceptance Criteria

- [ ] All four member specs created
- [ ] Standalone service deployable
- [ ] All existing tests passing
- [ ] Integration tests passing
- [ ] Performance meets baseline

Member Specs

Member .1: API Extraction

File: .chant/specs/2026-02-01-001-ext.1.md

---
type: code
status: pending
parent: 2026-02-01-001-ext
informed_by:
  - src/reporting/**/*.py
  - docs/architecture/modules.md
target_files:
  - services/reporting/src/main.py
  - services/reporting/src/routes.py
  - services/reporting/src/models.py
  - services/reporting/Dockerfile
  - services/reporting/pyproject.toml
---
# API extraction

## Problem

Extract reporting endpoints from monolith to standalone service.

## Tasks

- [ ] Create service structure
- [ ] Extract route handlers from `src/reporting/routes.py`
- [ ] Extract models (read-only copies)
- [ ] Configure FastAPI application
- [ ] Create Dockerfile for deployment
- [ ] Set up pyproject.toml with dependencies

## Acceptance Criteria

- [ ] Service starts and responds to health check
- [ ] All 5 reporting endpoints functional
- [ ] Same API contract as monolith
- [ ] Docker image builds successfully

Member .2: Database Migration

File: .chant/specs/2026-02-01-001-ext.2.md

---
type: code
status: pending
parent: 2026-02-01-001-ext
informed_by:
  - src/reporting/models.py
  - analysis/coupling/dependency-matrix.md
target_files:
  - services/reporting/src/database.py
  - services/reporting/migrations/001_initial.py
  - infrastructure/reporting-replica.tf
---
# Database migration

## Problem

Reporting needs isolated data access without affecting monolith.

## Tasks

- [ ] Create read replica configuration
- [ ] Set up denormalized views for reporting queries
- [ ] Configure connection pooling (separate from monolith)
- [ ] Create Terraform for replica infrastructure

## Acceptance Criteria

- [ ] Read replica provisioned
- [ ] Reporting queries use replica (not primary)
- [ ] Query performance equal or better than baseline
- [ ] Zero impact on monolith database

Member .3: Test Suite

File: .chant/specs/2026-02-01-001-ext.3.md

---
type: code
status: pending
parent: 2026-02-01-001-ext
informed_by:
  - tests/reporting/**/*.py
target_files:
  - services/reporting/tests/test_routes.py
  - services/reporting/tests/test_models.py
  - services/reporting/tests/conftest.py
  - services/reporting/tests/contracts/reporting_api.yaml
---
# Test suite

## Problem

Extracted service needs comprehensive tests.

## Tasks

- [ ] Migrate unit tests from `tests/reporting/`
- [ ] Create integration tests for new service
- [ ] Define contract tests (Pact-style)
- [ ] Set up test fixtures and conftest

## Acceptance Criteria

- [ ] All existing tests pass in new service
- [ ] Coverage >= 80%
- [ ] Contract tests verify API compatibility
- [ ] CI/CD pipeline runs tests

Member .4: Integration Testing

File: .chant/specs/2026-02-01-001-ext.4.md

---
type: task
status: pending
parent: 2026-02-01-001-ext
depends_on:
  - 2026-02-01-001-ext.1
  - 2026-02-01-001-ext.2
  - 2026-02-01-001-ext.3
target_files:
  - analysis/extraction/validation-report.md
---
# Integration testing

## Problem

Validate extracted service works correctly with rest of system.

## Tasks

- [ ] Deploy service alongside monolith
- [ ] Route 10% of reporting traffic to new service
- [ ] Compare response accuracy
- [ ] Measure latency and throughput
- [ ] Document findings

## Acceptance Criteria

- [ ] 100% response accuracy (vs monolith)
- [ ] P99 latency within 10% of baseline
- [ ] No errors under normal load
- [ ] Validation report complete

Running the Extraction

Alex executes the driver:

chant work 001-ext
Working: 2026-02-01-001-ext (Extract reporting service POC)

Phase 1: Parallel
  [✓] 2026-02-01-001-ext.1 (API extraction) - 8m 22s
  [✓] 2026-02-01-001-ext.2 (Database migration) - 12m 45s
  [✓] 2026-02-01-001-ext.3 (Test suite) - 6m 18s

Phase 2: Sequential (depends on all above)
  [✓] 2026-02-01-001-ext.4 (Integration testing) - 18m 30s

Driver complete. All 4 members succeeded.

Validation Report

File: analysis/extraction/validation-report.md (generated)

# Reporting Service Extraction - Validation Report

## Summary

| Metric | Baseline (Monolith) | Extracted Service | Status |
|--------|--------------------|--------------------|--------|
| P99 Latency | 2,400ms | 1,890ms | ✓ 21% improvement |
| Throughput | 45K req/day | 52K req/day capacity | ✓ Headroom |
| Error Rate | 0.08% | 0.06% | ✓ Slight improvement |
| Response Accuracy | — | 100% | ✓ All responses match |

## Test Results

| Test Suite | Pass | Fail | Skip |
|------------|------|------|------|
| Unit tests | 147 | 0 | 3 |
| Integration tests | 28 | 0 | 0 |
| Contract tests | 12 | 0 | 0 |
| Load tests | 5 | 0 | 0 |

Total: 192 passed, 0 failed, 3 skipped

## Performance Comparison

### P99 Latency by Endpoint

| Endpoint | Monolith | Extracted | Change |
|----------|----------|-----------|--------|
| /reports/daily | 1,850ms | 1,420ms | -23% |
| /reports/weekly | 2,400ms | 1,890ms | -21% |
| /reports/custom | 3,200ms | 2,650ms | -17% |
| /reports/export | 4,100ms | 3,200ms | -22% |
| /reports/schedule | 280ms | 245ms | -12% |

### Resource Usage

| Resource | Monolith (reporting) | Extracted | Change |
|----------|---------------------|-----------|--------|
| CPU | 78% | 65% | -17% |
| Memory | 52% | 48% | -8% |
| DB Queries | 28.6/req | 24.2/req | -15% |

## Parallel Running

Traffic split: 90% monolith, 10% extracted service

| Hour | Monolith Requests | Extracted Requests | Discrepancies |
|------|-------------------|-------------------|---------------|
| 00:00 | 1,890 | 210 | 0 |
| 01:00 | 1,620 | 180 | 0 |
| ... | ... | ... | 0 |
| 23:00 | 2,340 | 260 | 0 |

Total: 0 discrepancies in 24-hour parallel run

## Conclusion

Extraction successful. The POC validates:
1. Reporting can run as isolated service
2. Performance improves with dedicated resources
3. Zero data consistency issues
4. Test coverage is sufficient

### Recommendation

Proceed with production migration using gradual traffic shift:
- Week 1: 10% → 25%
- Week 2: 25% → 50%
- Week 3: 50% → 75%
- Week 4: 75% → 100%

Code Structure Created

The extraction created a new service structure:

services/
└── reporting/
    ├── src/
    │   ├── main.py           # FastAPI application
    │   ├── routes.py         # Extracted endpoints
    │   ├── models.py         # Read-only model copies
    │   └── database.py       # Replica connection
    ├── tests/
    │   ├── test_routes.py    # Unit tests
    │   ├── test_models.py    # Model tests
    │   ├── conftest.py       # Fixtures
    │   └── contracts/
    │       └── reporting_api.yaml
    ├── migrations/
    │   └── 001_initial.py    # Schema setup
    ├── Dockerfile
    ├── pyproject.toml
    └── README.md

What’s Next

With the POC complete, Alex sets up ongoing maintenance:

Maintenance — Drift detection on documentation and metrics as code evolves

Week 4+: Maintenance

With the POC complete, Alex sets up ongoing maintenance. This phase shows how drift detection keeps documentation and analysis current as the codebase evolves.

The Maintenance Pattern

Code changes constantly. Documentation and analysis must keep up:

┌──────────────────────────────────────────────────────────────────┐
│                    Drift Detection Types                          │
└──────────────────────────────────────────────────────────────────┘

     tracks:                  origin:                  informed_by:
   ┌──────────┐            ┌──────────────┐         ┌──────────────┐
   │  Source  │            │   Metrics    │         │   Analysis   │
   │   Code   │            │    Data      │         │   Results    │
   └────┬─────┘            └──────┬───────┘         └──────┬───────┘
        │                         │                        │
        ▼                         ▼                        ▼
   ┌──────────┐            ┌──────────────┐         ┌──────────────┐
   │  Code    │            │ New Metrics  │         │  Upstream    │
   │ Changes  │            │  Collected   │         │  Findings    │
   │          │            │              │         │   Updated    │
   └────┬─────┘            └──────┬───────┘         └──────┬───────┘
        │                         │                        │
        └─────────────────────────┴────────────────────────┘
                                  │
                                  ▼
                       ┌─────────────────────┐
                       │    chant drift      │
                       │  Detects changes,   │
                       │  suggests re-run    │
                       └─────────────────────┘

Three Types of Drift

1. Documentation Drift (tracks:)

When tracked source code changes, documentation becomes stale.

Scenario: A developer adds a new endpoint to the reporting service:

# services/reporting/src/routes.py - new endpoint added
@router.get("/reports/realtime")
async def get_realtime_report(request: Request):
    """New endpoint not in docs"""
    ...

Running drift detection:

$ chant drift 001-doc
Drift Report
============

Spec: 2026-01-28-001-doc (Document system architecture)
Status: DRIFT DETECTED

Tracked files changed since completion (2026-01-28):
  - services/reporting/src/routes.py
      Modified: 2026-03-10
      Lines changed: +35, -0
      New function: get_realtime_report

Documentation may be outdated for:
  - docs/architecture/modules.md (Reporting Module section)

Recommendation: Re-run spec to update documentation
  $ chant reset 001-doc && chant work 001-doc

2. Analysis Drift (origin:)

When origin data files change, analysis may be outdated.

Scenario: New production metrics collected:

# Updated metrics file
.chant/context/migration-research/production-metrics.md (modified)

Running drift detection:

$ chant drift 001-cpl
Drift Report
============

Spec: 2026-01-27-001-cpl (Analyze codebase coupling)
Status: DRIFT DETECTED

Informed-by files changed since completion (2026-01-27):
  - .chant/context/migration-research/production-metrics.md
      Modified: 2026-03-15
      Content: New Q1 metrics added

Analysis may need updating for:
  - analysis/coupling/extraction-candidates.md (rankings may shift)

Recommendation: Re-run spec with updated metrics
  $ chant reset 001-cpl && chant work 001-cpl

3. Cascade Drift (depends_on:)

When upstream specs drift, downstream specs may also be affected.

$ chant drift --all
Drift Report
============

Cascade detected:

2026-01-27-001-cpl (coupling analysis)
  └─ DRIFT: production-metrics.md changed
      │
      ├─> 2026-01-28-001-doc (documentation)
      │     Status: CASCADE DRIFT (upstream stale)
      │
      └─> 2026-02-01-001-ext (extraction)
            Status: CASCADE DRIFT (upstream stale)

Recommendation: Re-run upstream spec first
  1. `chant reset 001-cpl && chant work 001-cpl` — Refresh coupling analysis
  2. `chant reset 001-doc && chant work 001-doc` — Update architecture docs

Periodic Drift Monitoring

Alex runs drift checks periodically to catch stale specs:

$ chant drift

This shows all specs with detected drift, helping decide which to re-run.

Selective Re-runs

Alex can re-run just what’s needed:

$ chant reset 001-doc
$ chant work 001-doc

The agent regenerates documentation incorporating recent code changes.

Weekly Coupling Reports

For ongoing monitoring, Alex creates a scheduled research spec:

File: .chant/specs/2026-03-01-001-wkly.md

---
type: research
status: pending
schedule: weekly
informed_by:
  - src/**/*.py
origin:
  - .chant/context/migration-research/production-metrics.md
target_files:
  - reports/weekly/coupling-status.md
---
# Weekly coupling status

## Purpose

Track coupling changes week-over-week as the codebase evolves.

## Methodology

1. Recalculate import dependency matrix
2. Compare to previous week
3. Flag any new circular dependencies
4. Track progress on extraction candidates

## Output Format

```markdown
# Coupling Status - Week of [DATE]

## Changes Since Last Week
- New dependencies: N
- Removed dependencies: N
- New circular: Y/N

## Extraction Progress
| Candidate | Last Week Score | This Week | Trend |
|-----------|-----------------|-----------|-------|
| reporting | 8.5 | 8.7 | ↑ |
| notifications | 7.2 | 7.2 | → |
| billing | 5.8 | 5.6 | ↓ |

## Alerts
[Any coupling concerns]

Acceptance Criteria

  • Dependency matrix recalculated
  • Comparison to previous week
  • Extraction scores updated
  • Report generated

## Metrics Dashboard Integration

Alex integrates drift status with the team dashboard:

**File: `.chant/context/migration-research/drift-status.md`** (auto-updated)

```markdown
# Drift Status Dashboard

Last updated: 2026-03-18T09:00:00Z

## Spec Health

| Spec | Type | Last Run | Status | Days Since |
|------|------|----------|--------|------------|
| 001-cpl | research | 2026-01-27 | DRIFT | 50 |
| 001-doc | documentation | 2026-01-28 | DRIFT | 49 |
| 001-ext | driver | 2026-02-01 | OK | 45 |
| 001-wkly | research | 2026-03-18 | OK | 0 |

## Tracked File Changes

| Path | Last Modified | Tracked By | Needs Replay |
|------|--------------|------------|--------------|
| src/auth/routes.py | 2026-03-10 | 001-doc | Yes |
| src/reporting/routes.py | 2026-03-08 | 001-doc | Yes |
| production-metrics.md | 2026-03-15 | 001-cpl | Yes |

## Recommended Actions

1. `chant replay 001-cpl` — Refresh coupling analysis
2. `chant replay 001-doc` — Update architecture docs

Maintenance Benefits

BenefitHow It Helps
Proactive alertsKnow when docs are stale before customers complain
Traceable changesSee exactly which file changes caused drift
Cascade awarenessUnderstand how changes ripple through specs
Automated monitoringWeekly reports without manual checking

Complete Workflow Summary

Alex’s research workflow used all spec types:

Week 1: Investigation
  └─ research spec (informed_by: src/**) → Coupling analysis

Week 2: Documentation
  └─ documentation spec (tracks: src/**) → Architecture docs

Week 3: Implementation
  └─ driver spec (members: .1-.4) → POC extraction

Week 4+: Maintenance
  └─ scheduled specs + drift detection → Ongoing updates

This completes the developer path. See the artifacts directory for complete spec examples.

What’s Next

Return to the main guide:

Research Workflow Overview — Compare both paths and see the universal pattern

CLI Reference

Global Flags

Quiet Mode

Suppress all non-essential output (applies to all commands):

chant --quiet list                      # Quiet list output
chant --quiet work 2026-01-22-001-x7m    # No terminal output, exit code only
chant -q status                         # Short form

The --quiet / -q flag is a global flag that can be used with any command to minimize output. When used with chant work, the spec file is still updated during execution.

Initialization

Initialize chant in a new project. For detailed setup instructions, see the Initialization Guide.

chant init                                 # Interactive wizard (recommended)
chant init --name my-project               # Direct mode with project name
chant init --minimal                       # Only create config.md (no templates)
chant init --silent                        # Keep .chant/ local-only (gitignored)
chant init --agent claude                  # Create CLAUDE.md for AI instructions

See Initialization Guide for details on:

  • Interactive wizard setup
  • Direct mode flags
  • Agent configuration options
  • Silent mode for enterprise environments

Spec Management

Create and List

chant add                                    # Interactive wizard
chant add "Fix authentication bug"           # Create spec with description
chant add "Risky refactor" --needs-approval  # Create spec requiring approval
chant list                                   # List all specs
chant show 2026-01-22-001-x7m                # Show spec details
chant edit 001                               # Edit spec in $EDITOR

Interactive Wizard for Add

When you run chant add without a description, you’ll be guided through spec creation interactively:

? Spec title: Fix authentication bug
? Spec type: code
? Brief description: Add JWT token validation to API endpoints
? Acceptance criteria (one per line, end with empty line):
  - [ ] JWT validation middleware implemented
  - [ ] All tests passing
  - [ ] Code linted
?
? Target files (optional):
  - src/auth/middleware.rs
  - src/auth/tokens.rs
?

Needs-Approval Flag

When creating a spec with --needs-approval, the spec requires explicit approval before work can begin:

chant add "Add authentication" --needs-approval

This sets approval.required: true in the spec’s frontmatter. The spec cannot be worked on until approved. See the Approval Workflow Guide for details.

List Specs

chant list                                   # List all specs
chant list --ready                           # List ready specs (shortcut for --status ready)
chant list --label auth                      # Filter by label
chant list --label auth --label api          # Multiple labels (OR logic)
chant list --ready --label feature           # Combine filters

Available Flags

FlagDescription
--readyShow only ready specs (equivalent to --status ready)
--status STATUSFilter by status (pending, ready, in_progress, completed, failed, blocked, cancelled)
--type TYPEFilter by spec type (code, task, driver, documentation, research, group)
--label LABELFilter by label (can be specified multiple times for OR logic)
--approval STATUSFilter by approval status (pending, approved, rejected)
--created-by NAMEFilter by spec creator name (case-insensitive)
--mentions NAMEFilter specs mentioning a person in approval discussion
--activity-since DURATIONFilter by recent activity (e.g., “2h”, “1d”, “1w”)
--countShow only count of matching specs instead of listing them
--globalList specs from all configured repos in global config
--repo PATHFilter to specific repository path (implies --global)
--project NAMEFilter to specific project within repository
--main-onlySkip branch resolution for in_progress specs (debug option)
--summaryShow project status summary (replaces chant status)
--watchWatch mode - refresh every 5 seconds (requires --summary)
--briefBrief single-line output (requires --summary)
--jsonJSON output (requires --summary)

Type Filtering

Filter specs by type:

chant list --type code                       # Code specs only
chant list --type documentation              # Documentation specs
chant list --type task                       # Task specs
chant list --type research                   # Research specs
chant list --type driver                     # Driver/group specs

# Supported types: code, task, driver, documentation, research, group

Status Filtering

Filter specs by status:

chant list --status pending                  # Pending specs
chant list --status ready                    # Ready specs (shortcut: --ready)
chant list --status in_progress              # In-progress specs
chant list --status completed                # Completed specs
chant list --status failed                   # Failed specs
chant list --status blocked                  # Blocked specs (waiting on dependencies or approval)
chant list --status cancelled                # Cancelled specs

# Combine filters
chant list --type code --status pending      # Pending code specs
chant list --status completed --label auth   # Completed auth specs

Label Filtering

chant list --label auth                      # Specs with 'auth' label
chant list --label auth --label api          # Specs with 'auth' OR 'api' label
chant list --label feature --label urgent    # Combine multiple labels

Approval Filtering

Filter specs by approval status. See the Approval Workflow Guide for details.

chant list --approval pending                # Specs awaiting approval
chant list --approval approved               # Approved specs
chant list --approval rejected               # Rejected specs

People and Activity Filtering

chant list --created-by alice                # Specs created by alice (case-insensitive)
chant list --mentions bob                    # Specs where bob is mentioned in approval discussion
chant list --activity-since 2h               # Specs modified in the last 2 hours
chant list --activity-since 1d               # Modified in the last day
chant list --activity-since 1w               # Modified in the last week

Duration formats: 2h (hours), 1d (days), 1w (weeks), 2mo (months).

Count Flag

Show only the count of matching specs instead of listing them:

chant list --count                           # Total spec count
chant list --approval pending --count        # Count of specs awaiting approval
chant list --status ready --count            # Count of ready specs

Cross-Repository Filtering

List specs across multiple repositories configured in your global config:

chant list --global                          # All specs from all configured repos
chant list --global --status ready           # Ready specs across all repos
chant list --repo /path/to/repo              # Specs from specific repository
chant list --project my-api                  # Specs from specific project
chant list --global --project frontend       # Project filter across all repos

When using --global, the output includes the repository path for each spec.

Flag Combinations

Combine flags for powerful filtering:

# Multi-dimensional filtering
chant list --type code --status ready --label feature
chant list --approval approved --created-by alice --activity-since 1w
chant list --global --status in_progress --project backend

# Counting filtered results
chant list --type documentation --status completed --count
chant list --global --approval pending --count

# Cross-repo queries with filters
chant list --global --status ready --type code --label urgent

Edit Spec

Open a spec in your default editor:

chant edit 2026-01-22-001-x7m              # Open spec in $EDITOR
chant edit 001                             # Partial ID matching works

Uses the $EDITOR environment variable. Falls back to vi on Unix or notepad on Windows if not set.

Cancel Spec

Cancel a spec by marking it cancelled (soft-delete), or permanently delete it with --delete:

chant cancel 2026-01-22-001-x7m                # Cancel a spec (soft-delete, confirms)
chant cancel 2026-01-22-001-x7m --yes          # Skip confirmation
chant cancel 2026-01-22-001-x7m --dry-run      # Preview changes
chant cancel 2026-01-22-001-x7m --skip-checks  # Skip safety checks
chant cancel 2026-01-22-001-x7m --delete       # Permanently delete spec (hard delete)
chant cancel 2026-01-22-001-x7m --delete --cascade         # Delete driver and all members
chant cancel 2026-01-22-001-x7m --delete --delete-branch   # Delete spec and associated branch

Safety Checks (default, skipped with --skip-checks):

  • Cannot cancel specs that are in-progress or failed
  • Cannot cancel member specs (cancel the driver instead)
  • Cannot cancel already-cancelled specs
  • Warns if other specs depend on this spec

Soft Cancel (default):

  1. Spec status changed to Cancelled in frontmatter
  2. File is preserved in .chant/specs/
  3. Cancelled specs excluded from chant list and chant work
  4. Can still view with chant show or chant list --status cancelled
  5. All git history preserved

Hard Delete (--delete):

  1. Permanently removes spec file from .chant/specs/
  2. Removes associated log file from .chant/logs/
  3. Removes worktree artifacts
  4. With --cascade: deletes driver and all member specs
  5. With --delete-branch: removes associated git branch

Execution

chant work                                 # Interactive wizard to select specs
chant work 2026-01-22-001-x7m              # Execute single spec
chant work 2026-01-22-001-x7m --prompt tdd # Execute with specific prompt
chant work 2026-01-22-001-x7m --skip-deps  # Override dependency checks
chant work 2026-01-22-001-x7m --skip-approval  # Bypass approval check
chant work 2026-01-22-001-x7m --no-watch   # Disable watch auto-start (for testing)
chant work --parallel                      # Execute all ready specs in parallel
chant work --parallel --label auth         # Execute ready specs with label
chant work 001 002 003 --parallel          # Execute specific specs in parallel

Approval Check

When a spec has approval.required: true, chant work checks the approval status before proceeding. Use --skip-approval for emergency bypasses only. See the Approval Workflow Guide for details.

Interactive Wizard for Work

When you run chant work without a spec ID, an interactive wizard guides you through selection:

? Select specs to execute:
  [x] 2026-01-26-001-abc  Fix login bug
  [ ] 2026-01-26-002-def  Add API logging
  [ ] 2026-01-26-003-ghi  Update docs
  [Select all]
? Use parallel execution? No
? Select prompt: standard (auto-detected for code)
? Create feature branch? No

The wizard:

  1. Shows all ready specs with multi-select
  2. Asks whether to use parallel execution
  3. Lets you choose a prompt (defaults to spec’s prompt or type-based default)
  4. Asks about branch creation (if defaults.branch not set)
  5. Executes the selected specs

Split Spec

Split a spec into member specs using AI analysis:

chant split 2026-01-22-001-x7m             # Split into group members
chant split 001 --force                    # Force split even if not pending
chant split 001 --model claude-opus-4-5    # Use specific model for analysis

The split command analyzes the spec content and creates numbered member specs (.1, .2, etc.) that break down the work into smaller pieces.

Prep

Output cleaned spec content for agent preparation:

chant prep 2026-01-22-001-x7m              # Output spec content
chant prep 001 --clean                     # Strip agent conversation sections

The prep command is useful for:

  • Getting the spec content ready for the agent to process
  • Removing stale agent conversation sections from re-executed specs (with --clean)
  • Preparing specs for the bootstrap prompt workflow

By default, chant prep outputs the spec body. With the --clean flag, it removes any “## Agent Conversation”, “## Execution Result”, or similar sections that may have been added during previous runs, ensuring a clean slate for re-execution.

No-Watch Flag

The --no-watch flag disables automatic watch startup:

chant work 001 --no-watch    # Work without starting watch

When to use:

  • Testing agent behavior in isolation
  • Debugging lifecycle issues
  • CI/CD environments where watch is managed separately
  • When you want to manually control the lifecycle

Without watch running, agents will complete and write their status file, but lifecycle operations (merge, finalize, cleanup) won’t happen until watch is started.

Skip Flags

The --skip-deps and --skip-criteria flags for chant work allow overriding safety checks:

# Work on blocked spec (bypasses dependency checks)
chant work 001 --skip-deps

# Complete spec without all criteria checked
chant work 001 --skip-criteria

Overriding Dependency Checks

When a spec is blocked due to unsatisfied dependencies, --skip-deps allows you to proceed anyway:

$ chant work 2026-01-22-003-abc --skip-deps
⚠ Warning: Forcing work on spec (skipping dependency checks)
  Skipping dependencies: 2026-01-22-001-x7m (in_progress)
→ Working 2026-01-22-003-abc with prompt 'standard'

Use cases:

  • Dependency tracking has a bug or false positive
  • You’ve manually verified dependencies are satisfied
  • Emergency work needed despite dependency chain issues

Skipping Acceptance Criteria Validation

The --skip-criteria flag allows completing a spec even if some acceptance criteria checkboxes are not marked as complete. Use this when:

  • Requirements changed after spec was created
  • A criterion is no longer applicable
  • You want to complete with manual verification

Acceptance Criteria Validation

After the agent exits, chant validates that all acceptance criteria checkboxes are checked:

⚠ Found 1 unchecked acceptance criterion.
Use --force to skip this validation.
error: Cannot complete spec with 1 unchecked acceptance criteria

If unchecked boxes exist, the spec is marked as failed. Use --force to skip this validation and complete the spec anyway.

Parallel Execution

Execute multiple ready specs concurrently:

# Execute all ready specs in parallel
chant work --parallel

# Execute specific specs in parallel (selective)
chant work 001 002 003 --parallel

# Filter by label
chant work --parallel --label auth
chant work --parallel --label feature --label urgent

# Specify prompt for all specs
chant work --parallel --prompt tdd

# Override maximum concurrent agents
chant work --parallel --max 4

# Skip cleanup prompt after execution
chant work --parallel --no-cleanup

# Force cleanup prompt even on success
chant work --parallel --cleanup

Selective Parallel Execution:

When you specify multiple spec IDs, only those specs are executed in parallel (regardless of their ready status):

# Run exactly these 4 specs in parallel
chant work 00e 00i 00j 00k --parallel

# Combine with other options
chant work 001 002 --parallel --prompt tdd --max 2

This is useful when you want to control exactly which specs run together, rather than running all ready specs.

Multi-Account Support:

Configure multiple Claude accounts in .chant/config.md for distributed execution:

parallel:
  agents:
    - name: main
      command: claude
      max_concurrent: 2
    - name: alt1
      command: claude-alt1
      max_concurrent: 3

Example output:

→ Starting 5 specs in parallel...

  • main: 2 specs
  • alt1: 3 specs

[00m-khh] Working with prompt 'standard' via main
[00n-1nl] Working with prompt 'standard' via alt1
[00o-6w7] Working with prompt 'standard' via alt1

[00m-khh] ✓ Completed (commit: abc1234)
[00n-1nl] ✓ Completed (commit: def5678)
[00o-6w7] ✓ Completed (commit: ghi9012)

════════════════════════════════════════════════════════════
Parallel execution complete:
  ✓ 5 specs completed work
  ✓ 5 branches merged to main
════════════════════════════════════════════════════════════

Pitfall Detection:

After parallel execution, chant detects and reports issues:

→ Issues detected:
  ✗ [spec-002] API concurrency error (retryable): Error 429
  ⚠ [spec-003] Worktree not cleaned up: /path/to/worktree

Chain Execution

Execute specs sequentially, one after another:

# Chain through all ready specs until none remain or failure
chant work --chain

# Start from a specific spec, then continue through unblocked specs
chant work --chain spec1

# Chain through specific specs in order, then continue through unblocked specs
chant work --chain spec1 spec2 spec3

# Limit number of specs to chain
chant work --chain --chain-max 5

# Chain through labeled specs only
chant work --chain --label auth

Two Modes of Operation:

CommandBehavior
chant work --chainChain through ALL ready specs
chant work --chain spec1Start from spec1, then continue through any specs unblocked by completions
chant work --chain spec1 spec2 spec3Run specified specs first, then continue through unblocked specs

Specific ID Behavior:

When spec IDs are provided:

  • Runs the specified IDs first in the order given
  • After exhausting the list, continues discovering newly-ready specs (e.g., specs unblocked by completions)
  • Invalid spec IDs fail fast with clear error before execution starts
  • Non-ready specs are skipped with warning, chain continues
  • --label filter is ignored for the specified IDs but applies to discovery
  • --chain-max limit still applies

Example output:

→ Starting chain execution...

[1/?] Working 001-abc with prompt 'standard'
[1/?] ✓ Completed in 45s

[2/?] Working 002-def with prompt 'standard'
[2/?] ✓ Completed in 32s

[3/?] ⚠ Skipping 003-ghi: not ready (blocked by: 004-jkl)

[4/?] Working 005-mno with prompt 'standard'
[4/?] ✗ Failed: Tests did not pass

════════════════════════════════════════════════════════════
Chain stopped (spec failure):
  ✓ Completed 3 spec(s) in 135.0s
  ⚠ Skipped 1 spec(s)
  ✗ Failed 1 spec(s):
    ✗ 005-mno: Tests did not pass
  → 2 spec(s) still ready to work
════════════════════════════════════════════════════════════

Use cases:

  • Overnight processing: Run all ready specs while you sleep
  • CI/CD integration: Process specs in automated pipelines
  • Targeted batch processing: Chain through specific specs in a defined order
  • Dependency-aware execution: Specs become ready as their dependencies complete

Search and filter specs interactively or with direct queries:

chant search                          # Interactive wizard
chant search "auth"                   # Search by keyword
chant search "label:feature"          # Search by label
chant search "status:ready"           # Search by status

Interactive Wizard

When you run chant search without arguments, an interactive wizard guides you through filtering:

? Search query (keyword, label, status, type):
auth

? Filter by status:
  [x] Pending
  [x] Ready
  [x] In Progress
  [x] Completed
  [ ] Failed
  [ ] Blocked
  [ ] Cancelled

? Filter by type:
  [x] All types
  [ ] Code
  [ ] Task
  [ ] Documentation
  [ ] Driver
  [ ] Research

Results: 15 specs matching "auth"

The wizard lets you:

  1. Enter a search query (keyword, or label:name, status:name, type:name)
  2. Filter results by status
  3. Filter results by type
  4. View and select specs from results

Derive

Manually trigger field derivation for existing specs:

chant derive 2026-01-22-001-x7m   # Derive fields for one spec
chant derive --all                 # Derive fields for all specs
chant derive --all --dry-run       # Preview without modifying

What Derivation Does

Automatically extracts and populates fields from enterprise configuration:

  • Reads derivation rules from .chant/config.md
  • Applies patterns to sources (branch, path, env, git_user)
  • Validates values against enum rules (warnings only)
  • Updates spec frontmatter with derived values
  • Tracks which fields were derived in derived_fields list

Use Cases

  • Backfill - Add derivation rules and re-populate existing specs
  • Update values - Re-derive if branch names or paths changed
  • Fix invalid values - Update specs with incorrect derived values

Examples

Add derivation rules to .chant/config.md:

enterprise:
  derived:
    team:
      from: path
      pattern: "teams/(\\w+)/"
    jira_key:
      from: branch
      pattern: "([A-Z]+-\\d+)"

Then backfill all specs:

$ chant derive --all --dry-run
Preview: Would update 12 specs with derived fields

2026-01-22-001-x7m: team=platform, jira_key=PROJ-123
2026-01-22-002-y8n: team=frontend, jira_key=PROJ-124
...

$ chant derive --all
Derived 12 specs successfully

Dry-Run Mode

Preview changes without modifying files:

$ chant derive --all --dry-run
Preview: Would update 12 specs
  • 2026-01-22-001-x7m: +team, +jira_key
  • 2026-01-22-002-y8n: +team, +jira_key
  (showing first 10 of 12)

Dry-run is useful for:

  • Testing new derivation rules
  • Validating patterns before updating
  • Verifying the impact of changes

Lint

Validate specs for structural issues and best practices:

chant lint                            # Validate all specs
chant lint 001                        # Validate specific spec

Validation Rules

Lint checks are organized into categories:

Hard Errors (fail validation):

  • Missing title in spec
  • Unknown spec IDs in depends_on (broken dependencies)
  • Invalid YAML frontmatter

Type-Specific Warnings:

  • documentation: Missing tracks or target_files fields
  • research: Missing both informed_by AND origin fields

Complexity Warnings:

  • More than 5 acceptance criteria
  • More than 5 target files
  • More than 500 words in description
  • Suggests using chant split if too complex

Coupling Warnings:

  • Detecting spec ID references in body (outside code blocks)
  • Suggests using depends_on for explicit dependencies
  • Skipped for drivers/groups (allowed to reference members)

Model Waste Warnings:

  • Using expensive models (opus/sonnet) on simple specs
  • Simple spec definition: ≤3 criteria, ≤2 files, ≤200 words
  • Suggests using haiku for simple work

Output

✓ 2026-01-26-001-abc          (all valid)
✗ 2026-01-26-002-def: Missing title
⚠ 2026-01-26-003-ghi: Spec has 8 acceptance criteria (>5)
  Consider: chant split 2026-01-26-003-ghi
⚠ 2026-01-26-004-jkl: Spec references 001-abc without depends_on
  Suggestion: Use depends_on to explicit document dependency

Validation Summary:
  Errors: 1
  Warnings: 3

Exit code: 0 (all valid) or 1 (errors found)

Verify

Re-check acceptance criteria on completed specs to detect drift:

chant verify                          # Interactive wizard
chant verify 001                      # Verify single spec
chant verify --all                    # Verify all completed specs
chant verify --all --exit-code        # Exit with code if verification fails (CI/CD)
chant verify --all --dry-run          # Preview verification without updating
chant verify --label auth             # Verify by label
chant verify --prompt custom          # Use custom prompt

Interactive Wizard

When you run chant verify without arguments, an interactive wizard guides you:

? Verify which specs:
  [x] All completed specs
  [ ] Specific spec ID(s)
  [ ] By label

? Exit with code on failure? No
? Dry run (no updates)? No

→ Verifying 12 completed specs...

✓ 2026-01-24-001-abc: Rate limiting - PASSED
✓ 2026-01-24-002-def: Auth middleware - PASSED
⚠ 2026-01-24-003-ghi: API docs - PARTIAL (1 criterion skipped)
✗ 2026-01-24-004-jkl: Logging - FAILED (2 criteria failed)

Verification Summary:
  Passed: 10
  Partial: 1
  Failed: 1

What Verification Does

For each spec, the agent:

  1. Reads the spec’s acceptance criteria
  2. Checks the current codebase against each criterion
  3. Reports: ✓ PASS, ⚠ SKIP (not applicable), ✗ FAIL

Output Stored in Frontmatter

After verification, the spec’s frontmatter is updated:

---
status: completed
last_verified: 2026-01-22T15:00:00Z
verification_status: passed          # passed | partial | failed
verification_failures:
  - "Rate limiter tests disabled"
  - "Configuration file missing"
---

Exit Code for CI/CD

Use --exit-code to integrate with CI pipelines:

# In GitHub Actions or other CI
chant verify --all --exit-code
# Exit code: 0 (all passed), 1 (any failed)

Use Cases

  • Verify Before Deploy: Ensure acceptance criteria still hold before shipping
  • Detect Drift: Find when reality diverges from original intent
  • Scheduled Checks: Run nightly to catch regressions
  • Continuous Verification: Gate deployments on spec verification

Logs

View agent output logs for a spec:

chant log 001                         # Show last 50 lines and follow (default)
chant log 001 --no-follow             # Show last 50 lines without following
chant log 001 -n 100                  # Show last 100 lines and follow
chant log 001 -n 100 --no-follow      # Show last 100 lines without following
chant log 001 --run latest            # Show only the most recent run's output
chant log 001 --run 2                 # Show output from the 2nd run

Logs are stored in .chant/logs/{spec-id}.log and are created when a spec is executed with chant work. The log contains the full agent output including timestamp and prompt used.

When a spec is retried, subsequent runs are appended to the same log file with visual run separators (========). Use --run to isolate output from a specific run.

Use cases:

  • Monitor spec execution in real-time (follows by default)
  • Review agent output after execution with --no-follow
  • Debug failed specs
  • Isolate output from a specific run with --run

Real-time Log Streaming

Logs are streamed to the log file in real-time as the agent produces output, not buffered until completion. This enables monitoring spec execution as it happens:

Terminal 1:

chant work 001    # Agent runs, streams to stdout AND log file

Terminal 2 (simultaneously):

chant log 001     # See output in real-time as agent works (follows by default)

The log file header (spec ID, timestamp, prompt name) is written before the agent starts, so chant log will begin showing content immediately.

Status

Show project status summary with spec counts:

chant list --summary                  # Project overview (replaces 'chant status')
chant list --summary --brief          # Compact one-line summary
chant list --summary --json           # JSON output format
chant list --summary --global         # Status across all configured repos
chant list --summary --repo /path     # Specific repository (implies --global)
chant list --summary --watch          # Watch mode - refresh every 5 seconds
chant list --ready                    # Show ready specs

Note: The chant status command has been replaced by chant list --summary. The --summary flag provides the same functionality for displaying project status summaries.

Flags

FlagDescription
--briefCompact single-line output showing key metrics
--jsonOutput status as JSON for programmatic parsing
--globalShow status across all configured repositories
--repo <path>Filter to specific repository path (implies --global)
--watchWatch mode - automatically refresh status every 5 seconds

Brief Mode

Displays a compact single-line summary:

$ chant list --summary --brief
5 ready, 3 in-progress, 12 pending, 2 blocked, 45 completed

JSON Mode

Outputs structured data for parsing:

$ chant list --summary --json
{
  "specs": {
    "ready": 5,
    "in_progress": 3,
    "pending": 12,
    "blocked": 2,
    "completed": 45,
    "failed": 0,
    "cancelled": 1
  }
}

Watch Mode

Continuously monitors project status, refreshing every 5 seconds:

$ chant list --summary --watch
Spec Status (auto-refresh every 5s, press Ctrl+C to stop)

Ready: 5
In Progress: 3
Pending: 12
Blocked: 2
Completed: 45
Failed: 0
Cancelled: 1

[Last updated: 2026-01-28 14:30:15]

Press Ctrl+C to exit watch mode.

Man Pages

Chant includes Unix-style manual pages generated using clap_mangen. Man pages provide comprehensive command documentation accessible via the man command.

Generating Man Pages

Generate the man page during development:

chant man --out-dir ./target

This creates chant.1 in the specified directory.

Installation

To install the man page system-wide:

# Generate the man page
chant man --out-dir /tmp

# Copy to system man directory (requires sudo)
sudo cp /tmp/chant.1 /usr/share/man/man1/

# Update man database (Linux)
sudo mandb

# View the installed man page
man chant

On macOS, the man database updates automatically when man pages are added to /usr/share/man/.

Accessing Documentation

Once installed, access chant documentation via:

man chant              # View the full manual
man chant | grep -A 5 "chant work"  # Search for specific commands

The man page includes all commands, flags, and usage examples from the CLI help system.

Visualization

Dependency Graph

Show spec dependencies as an ASCII graph:

chant dag                              # Full graph with all details
chant dag --detail minimal             # IDs only
chant dag --detail titles              # IDs and titles
chant dag --status pending             # Filter by status
chant dag --label auth                 # Filter by label
chant dag --type code                  # Filter by type

Detail Levels

LevelDescription
minimalShow only spec IDs
titlesShow IDs and titles
fullShow IDs, titles, status, and labels (default)

Example Output

$ chant dag --detail titles
2026-02-01-001-abc  Add authentication
├── 2026-02-01-002-def  Add login endpoint
│   └── 2026-02-01-003-ghi  Add session management
└── 2026-02-01-004-jkl  Add logout endpoint

Refresh

Reload all specs from disk and recalculate dependency status:

chant refresh                         # Quick summary of spec counts
chant refresh --verbose               # Show detailed ready/blocked lists
chant refresh -v                      # Short form of --verbose

The refresh command:

  1. Reloads all specs fresh from disk (no caching)
  2. Recalculates ready/blocked status based on current dependencies
  3. Reports summary counts (completed, ready, in-progress, pending, blocked)
  4. With --verbose: lists ready specs and blocked specs with their blockers

Example output:

Checking dependency status...
✓ Refreshed 142 specs
  Completed: 45
  Ready: 18
  In Progress: 3
  Pending: 52
  Blocked: 24

Verbose output:

Checking dependency status...
✓ Refreshed 142 specs
  Completed: 45
  Ready: 18
  In Progress: 3
  Pending: 52
  Blocked: 24

Ready specs:
  ○ 2026-01-27-00t-bfs Validate nodes.edn coverage
  ○ 2026-01-27-01a-xyz Generate AST types
  ...

Blocked specs:
  ⊗ 2026-01-27-02b-abc Implement parser (blocked by: 2026-01-27-01a-xyz (pending))
  ⊗ 2026-01-27-03c-def Add tests (blocked by: 2026-01-27-02b-abc (pending))
  ...

Use cases:

  • Verify dependency status after completing specs manually
  • Debug why a spec isn’t showing as ready
  • Get a quick overview of project spec health
  • Check what’s unblocked after a dependency completes

Merge

Merge completed spec branches back to main:

chant merge                           # Interactive wizard to select specs
chant merge 001                       # Merge single spec branch
chant merge 001 002 003               # Merge multiple specs
chant merge --all                     # Merge all completed spec branches
chant merge --all-completed           # Merge completed specs with branches (post-parallel)
chant merge --all --dry-run           # Preview what would be merged
chant merge --all --delete-branch     # Delete branches after merge
chant merge --all --yes               # Skip confirmation prompt

Interactive Wizard

When you run chant merge without arguments, an interactive wizard guides you through the merge process:

? Select specs to merge:
  [x] 2026-01-26-001-abc  Add user authentication (chant/001-abc)
  [x] 2026-01-26-002-def  Fix login bug (chant/002-def)
  [ ] 2026-01-26-003-ghi  Update API docs (chant/003-ghi)
  [Select all]
? Use rebase strategy? No
? Delete branches after merge? Yes

→ Will merge 2 spec(s):
  · chant/001-abc → main Add user authentication
  · chant/002-def → main Fix login bug

The wizard:

  1. Loads all completed specs that have associated branches
  2. Shows a multi-select list with spec ID, title, and branch name
  3. Prompts for rebase strategy (default: no)
  4. Prompts for branch deletion (default: yes)
  5. Executes the merge with your selections

Post-Parallel Convenience: --all-completed

After running chant work --parallel, use --all-completed as a convenience flag to merge all specs that:

  1. Have status: completed
  2. Have an associated branch (e.g., chant/spec-id)

This is perfect for post-parallel workflows where you want to merge all successfully completed work:

# After parallel execution
chant work --parallel --max 5

# Merge all completed specs that have branches
chant merge --all-completed --delete-branch --yes

# Preview what would be merged
chant merge --all-completed --dry-run

--all-completed vs --all:

FlagWhat it merges
--allAll completed specs (including those completed without branches)
--all-completedOnly completed specs that have corresponding branches

Use --all-completed when you’ve run parallel execution and want to merge only the specs that were worked on with feature branches, ignoring specs that may have been manually completed on main.

Rebase Before Merge

When multiple specs run in parallel, their branches diverge from main. Use --rebase to rebase each branch onto current main before the fast-forward merge:

chant merge --all --rebase            # Rebase each branch before ff-merge
chant merge --all --rebase --yes      # Skip confirmation
chant merge 001 002 --rebase          # Rebase specific specs

Auto-Resolve Conflicts

Use --auto with --rebase for agent-assisted conflict resolution:

chant merge --all --rebase --auto     # Auto-resolve conflicts with agent

When conflicts occur during rebase, chant invokes an agent with the merge-conflict prompt to resolve them. The agent:

  1. Reads the conflicting files
  2. Analyzes the conflict markers
  3. Edits files to resolve conflicts
  4. Stages resolved files
  5. Continues the rebase

If --auto is not specified and conflicts occur, the rebase is aborted and the spec is skipped.

Resume

Resume failed or stuck specs by resetting them to pending:

chant resume 001                      # Reset spec to pending
chant resume 001 --work               # Reset and immediately re-execute
chant resume 001 --work --prompt tdd  # Reset and re-execute with specific prompt

The resume command:

  1. Validates the spec is in failed or in_progress status
  2. Resets status to pending
  3. Optionally re-executes with --work

Use cases:

  • Retry after agent failure
  • Resume specs stuck in in_progress (e.g., agent crashed)
  • Re-attempt with different prompt or branch strategy

Finalize

Manually finalize specs that weren’t properly completed:

chant finalize 001                    # Finalize spec (records commits, timestamp, model)

The finalize command:

  1. Validates all acceptance criteria are checked
  2. Records commit SHAs from git history
  3. Sets completed_at timestamp and model in frontmatter
  4. Changes status to completed

When to use:

  • Agent exited without calling finalize
  • Spec marked failed but work was actually done
  • Manual intervention needed after auto-finalize failure

Note: chant work now auto-finalizes specs when all acceptance criteria are checked. Manual finalize is only needed for recovery scenarios.

Drift

Detect when documentation and research specs have stale inputs:

chant drift                           # Check all completed specs for drift
chant drift 001                       # Check specific spec

Drift detection checks:

  • tracks field: Source files being documented
  • origin field: Research spec origins
  • informed_by field: Reference materials

A spec has “drifted” when any tracked file was modified after the spec was completed. This indicates the documentation or research may be outdated.

Example output:

⚠ Drifted Specs (inputs changed since completion)
──────────────────────────────────────────────────
● 2026-01-24-005-abc (documentation)
  Completed: 2026-01-24
  Changed files:
    - src/api/handler.rs (modified: 2026-01-25)

✓ Up-to-date Specs (no input changes)
──────────────────────────────────────────────────
● 2026-01-24-003-xyz (research)

Export

Export spec data in various formats:

chant export                          # Interactive wizard
chant export --format json            # Export all specs as JSON
chant export --format csv             # Export as CSV
chant export --format markdown        # Export as Markdown table
chant export --output specs.json      # Write to file instead of stdout

Interactive Wizard for Export

When you run chant export without format or filters, an interactive wizard guides you:

? Export format:
  JSON
  CSV
  Markdown
? Filter by status (select multiple):
  [x] Ready
  [ ] Completed
  [ ] Pending
  [ ] Failed
  [ ] All statuses
? Filter by type:
  (none)
  code
  task
  documentation
  driver
? Output destination:
  Print to stdout
  Save to file
? Output filename: specs.json

The wizard:

  1. Lets you choose export format (JSON, CSV, or Markdown)
  2. Allows selecting multiple status filters
  3. Lets you filter by type
  4. Asks where to save (stdout or file)
  5. Prompts for filename if saving to file

Direct Mode

Use flags to skip the wizard:

chant export --status completed       # Filter by status
chant export --status pending --status ready  # Multiple statuses (OR)
chant export --type code              # Filter by spec type
chant export --label feature          # Filter by label
chant export --ready                  # Only ready specs
chant export --from 2026-01-20        # Specs from date
chant export --to 2026-01-25          # Specs until date

Field Selection

chant export --fields id,status,title # Select specific fields
chant export --fields all             # Include all fields

Default fields: id, type, status, title, labels, model, completed_at

Config Validation

Validate configuration semantically:

chant config --validate                     # Check configuration for issues

Validation Checks

The config --validate command performs these checks:

Agent Commands (errors):

  • Verifies each agent command exists in PATH (using which)
  • Example: claude, claude-alt1, etc.
  • Error if command not found

Prompt Files (errors):

  • Checks defaults.prompt file exists at .chant/prompts/{name}.md
  • Checks parallel.cleanup.prompt file exists (if cleanup enabled)
  • Error if prompt file not found

Parallel Configuration (informational):

  • Shows number of configured agents
  • Shows total capacity (sum of all max_concurrent values)

Recommended Fields (warnings):

  • Warns if defaults.model not set (will default to haiku)

Output

→ Checking configuration...

Checking parallel agents...
  ✓ main (claude) - found in PATH
  ✓ alt1 (claude-alt1) - found in PATH
  ✗ alt2 (claude-alt2) - not found in PATH

Checking prompt files...
  ✓ standard (.chant/prompts/standard.md)
  ✓ parallel-cleanup (.chant/prompts/parallel-cleanup.md)

Parallel Configuration:
  Agents: 2
  Total capacity: 5 concurrent

Recommended Fields:
  ⚠ defaults.model not set (will use haiku)

✓ Configuration valid with 1 warning

Exit code: 0 (valid) or 1 (errors found)

Execution Flow

chant work 2026-01-22-001-x7m
       │
       ▼
┌──────────────────────────────────────┐
│  1. Load spec from 2026-01-22-001-x7m.md │
│  2. Check if ready (deps satisfied)  │
│  3. Resolve prompt (spec → config)   │
│  4. Load prompt from prompts/{name}.md │
└──────────────────────────────────────┘
       │
       ▼
┌──────────────────────────────────────┐
│  5. Create branch (if enabled)       │
│  6. Build message (prompt + spec)    │
│  7. Spawn agent with prompt + spec   │
│  8. Stream output                    │
└──────────────────────────────────────┘
       │
       ▼
   ┌───┴───┐
   │       │
success  failure
   │       │
   ▼       ▼
┌────────┐ ┌────────┐
│complete│ │ failed │
│spec    │ │ spec   │
└────────┘ └────────┘
       │
       ▼
┌──────────────────────────────────────┐
│  9. Update frontmatter (commit hash) │
│  10. Check if driver complete        │
└──────────────────────────────────────┘

Approve / Reject

Approve or reject specs before work begins. See the Approval Workflow Guide for detailed usage.

Approve

chant approve 001 --by alice                 # Approve spec by name

Updates approval status to approved and records approver information.

Reject

chant reject 001 --by bob --reason "Scope too large, split first"

Updates approval status to rejected, records reason, and applies the configured rejection action (manual, dependency, or group mode).

Activity

View recent activity across specs:

chant activity                               # Show all recent activity
chant activity --by alice                    # Filter by person
chant activity --since 2h                    # Activity in the last 2 hours
chant activity --spec 001                    # Activity for a specific spec
chant activity --by alice --since 1d         # Combine filters

Activity Types

TypeColorDescription
CREATEDCyanSpec was created
APPROVEDGreenSpec was approved
REJECTEDRedSpec was rejected
WORKEDYellowCommit with chant(<id>): pattern
COMPLETEDGreen (bold)Spec status changed to completed

Filters

  • --by <name> - Filter by person (case-insensitive substring match)
  • --since <duration> - Show activity from the last N time (e.g., 2h, 1d, 1w, 1m)
  • --spec <id> - Filter by spec ID (substring match)

Output

2026-01-28 14:30  alice    APPROVED    001-abc  Implement feature X
2026-01-28 12:00  bob      CREATED     002-def  Fix authentication
2026-01-27 16:45  alice    COMPLETED   003-ghi  Update docs
2026-01-27 10:15  charlie  WORKED      004-jkl  Add logging

Activity is deduplicated, showing the first occurrence of each (spec_id, activity_type) pair.


Worktree

Inspect and manage chant worktrees used during parallel and isolated execution.

Status

Display status information about active chant worktrees:

chant worktree status                 # Show all chant worktrees

Example output:

Found 3 chant worktrees:

  /tmp/chant-2026-01-27-001-abc
    Branch: chant/2026-01-27-001-abc
    HEAD:   a1b2c3d
    Spec:   2026-01-27-001-abc
    Title:  Add user authentication
    Status: in_progress
    Size:   234 MB  Age: 2h

  /tmp/chant-2026-01-27-002-def [prunable]
    Branch: chant/2026-01-27-002-def
    HEAD:   e4f5g6h
    Spec:   2026-01-27-002-def
    Title:  Fix login bug
    Status: completed
    Size:   189 MB  Age: 1d
    Reason: gitdir file points to non-existent location

  /tmp/chant-2026-01-27-003-ghi
    Branch: chant/2026-01-27-003-ghi
    HEAD:   i7j8k9l
    Spec:   2026-01-27-003-ghi
    Status: unknown (spec not found)
    Size:   156 MB  Age: 3d

⚠ 1 prunable worktree (run git worktree prune to clean up)
Total disk usage: 579 MB

Output columns:

FieldDescription
PathAbsolute path to the worktree directory
BranchGit branch checked out in the worktree (format: chant/<spec-id>)
HEADShort commit hash (7 characters) of current HEAD
SpecAssociated spec ID extracted from branch or path
TitleSpec title (if spec file exists)
StatusSpec status: pending, ready, in_progress, completed, failed, blocked, cancelled
SizeDisk space used by the worktree
AgeTime since worktree was last modified

Flags:

FlagMeaning
[prunable] (red)Worktree can be safely removed (orphaned or corrupted)

Use cases:

  • Debug issues with worktree state during parallel execution
  • Identify orphaned worktrees that can be cleaned up
  • Monitor disk usage of active worktrees
  • Check which specs have active worktrees

Environment Variables for Agents

When agents run in worktree mode (during parallel execution), chant sets environment variables to help agents understand their execution context:

VariableDescriptionExample
CHANT_WORKTREESet to 1 when running in a worktree1
CHANT_WORKTREE_PATHAbsolute path to the worktree directory/tmp/chant-2026-01-27-001-abc
CHANT_BRANCHGit branch name for this specchant/2026-01-27-001-abc

These variables are only set when the agent is invoked with a working directory parameter, which indicates worktree mode. Agents can use these variables to:

  • Detect they’re running in an isolated environment
  • Understand their changes won’t affect main until merged
  • Log or reference the worktree path for debugging
  • Check the branch name for commit messages

Example agent check:

if [ -n "$CHANT_WORKTREE" ]; then
    echo "Running in worktree: $CHANT_WORKTREE_PATH"
    echo "Branch: $CHANT_BRANCH"
fi

Watch

The unified lifecycle coordinator for all work execution. Watch runs as an ephemeral daemon that handles state transitions, merging, and cleanup.

chant watch                           # Start watch (normally auto-started)
chant watch --once                    # Run one iteration then exit
chant watch --dry-run                 # Preview actions without executing
chant watch --poll-interval 10        # Set poll interval to 10ms (overrides config)

Ephemeral Daemon Behavior

Watch behaves as an ephemeral daemon:

  1. Auto-start: chant work commands automatically start watch if not running
  2. PID file: Watch writes PID to .chant/watch.pid on startup
  3. Idle timeout: Exits automatically after configurable idle period (default: 5 min)
  4. Graceful shutdown: Removes PID file on exit (SIGINT, idle timeout, --once)

You typically don’t need to start watch manually - it’s started automatically when you run chant work.

How It Works

The watch command:

  1. Writes PID to .chant/watch.pid
  2. Monitors worktrees for agent status changes (.chant-status.json)
  3. When agent writes status: done, watch:
    • Merges the worktree branch to main
    • Finalizes the spec (records commits, timestamp)
    • Cleans up the worktree and branch
  4. When agent writes status: failed, watch handles failure appropriately
  5. Exits after idle timeout when no work remains

Flags

FlagDescription
--onceRun only one iteration then exit (useful for testing)
--dry-runShow what would be done without actually doing it
--poll-interval MSSet poll interval in milliseconds (overrides config setting)

PID Management

.chant/watch.pid    # Contains watch process ID
  • Watch writes PID on startup
  • PID file removed on graceful exit
  • is_watch_running() validates: file exists AND process alive AND is chant
  • Stale PID files (dead process) are automatically cleaned up

Configuration

# config.md
watch:
  poll_interval: 1000        # Poll every 1000ms (1 second)
  idle_timeout_minutes: 5    # Exit after 5 minutes idle

Examples

Normal operation (auto-started):

chant work --chain           # Watch is auto-started

Watch is started in the background automatically.

Manual start:

chant watch                  # Start watch manually (runs in foreground)

Useful for debugging or when you want to see watch output directly.

Disable auto-start (for testing):

chant work 001 --no-watch    # Work without starting watch

Agent will write status file, but lifecycle won’t be processed until watch runs.

One-shot mode for testing:

chant watch --once

Process one iteration of lifecycle operations and exit.

Preview mode:

chant watch --dry-run --once

Shows what would be done without making changes.

Startup Recovery

On startup, watch recovers from previous crashes:

  1. Scans for existing worktrees with .chant-status.json
  2. done status but not merged → queued for merge
  3. working status but stale (>1 hour) → marked failed
  4. Orphaned worktrees → cleaned up

Use Cases

  • Ephemeral coordinator: Auto-starts with work, exits when idle
  • Crash recovery: Resumes lifecycle operations after watch/agent crashes
  • Parallel execution: Handles merging from multiple concurrent agents
  • Background automation: Run explicitly for longer-running sessions

Site Generation

Generate static documentation sites from specs:

chant site init                       # Initialize theme directory
chant site init --force-overwrite     # Overwrite existing theme files
chant site build                      # Build static site
chant site build --output ./dist      # Build to custom directory
chant site serve                      # Serve site locally (default port: 3000)
chant site serve --port 8080          # Serve on custom port
chant site serve --output ./dist      # Serve from custom directory

Site Init

Initialize the theme directory with customizable templates:

chant site init                       # Create theme files at .chant/site/theme
chant site init --force-overwrite     # Overwrite existing theme files

The init command:

  1. Creates .chant/site/theme/ directory
  2. Copies default templates for customization:
    • index.html - Homepage template
    • spec.html - Individual spec page template
    • styles.css - Site stylesheet
    • Other theme assets
  3. Lists created files with descriptions
  4. Provides next steps for customization

Flags:

  • --force-overwrite - Overwrite existing theme files without prompting

Example output:

✓ Theme initialized at .chant/site/theme

Created files:
  index.html - Homepage template
  spec.html - Individual spec page template
  styles.css - Site stylesheet

Next steps:
  1. Edit templates in .chant/site/theme
  2. Run chant site build to generate the site
  3. Run chant site serve to preview locally

Site Build

Build a static site from all specs:

chant site build                      # Build to configured output directory
chant site build --output ./dist      # Build to custom directory
chant site build -o ./docs            # Short form

The build command:

  1. Loads all specs from .chant/specs/
  2. Uses custom theme from .chant/site/theme/ if available
  3. Falls back to embedded default theme
  4. Generates static HTML files
  5. Writes to output directory (default: .chant/site/dist)

Flags:

  • --output DIR, -o DIR - Output directory (overrides config)

Example output:

→ Building site to .chant/site/dist
  Found 142 specs
  Using custom theme from .chant/site/theme

✓ Site built successfully
  142 specs included
  285 files written
  Output: .chant/site/dist

Next steps:
  Preview locally: chant site serve --port 3000
  Deploy: Copy .chant/site/dist to your web server

Site Serve

Start a local HTTP server to preview the site:

chant site serve                      # Serve on port 3000 (default)
chant site serve --port 8080          # Serve on custom port
chant site serve -p 8080              # Short form
chant site serve --output ./dist      # Serve from custom directory
chant site serve -o ./dist            # Short form

The serve command:

  1. Checks that the site has been built
  2. Starts an HTTP server on the specified port
  3. Serves static files from the output directory
  4. Logs requests to stdout
  5. Runs until Ctrl+C

Flags:

  • --port PORT, -p PORT - Port to serve on (default: 3000)
  • --output DIR, -o DIR - Output directory to serve (default: from config)

Example output:

→ Serving .chant/site/dist at http://127.0.0.1:3000
Press Ctrl+C to stop
  200 / .chant/site/dist/index.html
  200 /styles.css .chant/site/dist/styles.css
  200 /specs/2026-01-27-001-abc.html .chant/site/dist/specs/2026-01-27-001-abc.html

Configuration

Configure site generation in .chant/config.md:

site:
  output_dir: .chant/site/dist        # Output directory for build
  title: "Project Specs"              # Site title
  description: "Documentation site"   # Site description

Use cases:

  • Documentation: Generate a browsable spec catalog for your team
  • Publishing: Create a public documentation site from specs
  • Archiving: Build a static snapshot of all specs
  • Review: Share specs with stakeholders via a web interface

Configuration Reference

Config as Markdown

Configuration follows the same pattern as specs: markdown with YAML frontmatter.

.chant/config.md    ← Not config.yaml

Frontmatter is the config. Body is documentation.

Example

# .chant/config.md
---
project:
  name: my-app

defaults:
  prompt: bootstrap
  branch_prefix: "chant/"
  model: claude-opus-4
  provider: claude

schema:
  spec:
    required: [status]
    status:
      enum: [pending, in_progress, completed, failed]
---

# Project Configuration

## Prompts

- `bootstrap` - Default (minimal prompt that fetches spec via chant prep)
- `standard` - Full spec context upfront
- `tdd` - Use for anything touching auth
- `security-review` - Required for external API changes

## Team Notes

Run `chant lint` before pushing.

Why Markdown?

  1. Consistency - Same format as specs and prompts
  2. Self-documenting - Body explains the config
  3. Editable anywhere - Any text editor works
  4. Git-friendly - Readable diffs

Minimal Config

# .chant/config.md
---
project:
  name: my-app
---

# Config

Using all defaults.

Full Schema

---
# Required
project:
  name: string              # Project name (used for worktree namespacing)
  prefix: string            # Optional: ID prefix for scale deployments

# Optional - defaults shown
defaults:
  prompt: bootstrap         # Default prompt (bootstrap for minimal API concurrency)
  branch_prefix: "chant/"   # Worktree branch name prefix (use unique prefix in monorepos)
  provider: claude          # Model provider: claude, ollama, openai
  model: null               # Model name (e.g. "claude-opus-4", "llama2")
  split_model: null         # Model for split operations (defaults to sonnet)
  main_branch: "main"       # Default main branch for merges
  rotation_strategy: "none" # Agent rotation: none, random, round-robin

# Optional - model provider endpoints
providers:
  ollama:
    endpoint: http://localhost:11434/v1  # Ollama API endpoint
  openai:
    endpoint: https://api.openai.com/v1  # OpenAI API endpoint

# Optional - parallel execution settings
parallel:
  agents:                   # List of available agents
    - name: main            # Display name
      command: claude       # Shell command
      max_concurrent: 2     # Max concurrent for this agent
  stagger_delay_ms: 1000    # Delay between spawning agents (ms), default 1000
  stagger_jitter_ms: 200    # Jitter for spawn delays (ms), default 200 (20% of delay)
  cleanup:
    enabled: true           # Offer cleanup after parallel execution
    prompt: parallel-cleanup # Cleanup prompt to use
    auto_run: false         # Run cleanup automatically

# Optional - approval settings
approval:
  rejection_action: manual              # manual | dependency | group
  require_approval_for_agent_work: true # Auto-require approval for agent commits

# Optional - schema validation
schema:
  spec:
    required: [status]      # Required frontmatter fields (id comes from filename)
    status:
      enum: [pending, in_progress, completed, failed]

---

Global Configuration

Chant supports a global config file for user-wide defaults:

~/.config/chant/config.md

Merge Behavior

Configuration is merged from three sources (later overrides earlier):

~/.config/chant/config.md    <- Global defaults
.chant/config.md             <- Project overrides
.chant/agents.md             <- Agent overrides (parallel.agents only)

Values are merged at the key level. The agents.md file only overrides the parallel.agents section.

Example Global Config

The global config is the recommended place for agent definitions since they often contain account-specific settings:

# ~/.config/chant/config.md
---
defaults:
  model: claude-opus-4
  provider: claude
  rotation_strategy: round-robin

parallel:
  stagger_delay_ms: 1000
  agents:
    - name: main
      command: claude
      max_concurrent: 2
    - name: worker1
      command: claude-alt1
      max_concurrent: 3

providers:
  openai:
    endpoint: https://api.openai.com/v1
---

# Global Chant Settings

My default settings and agent configuration for all projects.

Example Project Override

# .chant/config.md
---
project:
  name: quick-prototype

defaults:
  model: sonnet   # Override: use faster model for this project
---

Project config overrides global defaults.

Note: Agent definitions should NOT be in project config since they often contain sensitive information. Use global config or .chant/agents.md instead.

Project Agents Override

For project-specific agent overrides (rare case), create .chant/agents.md. This file is gitignored by default:

# .chant/agents.md
---
parallel:
  agents:
    - name: project-specific
      command: claude-project
      max_concurrent: 2
---

This file only overrides the parallel.agents section. Other parallel settings (like stagger_delay_ms) come from global or project config.

Model Providers

Chant supports multiple AI model providers. Choose the provider that works best for your workflow.

Provider Types

Claude (Default)

  • Uses the Anthropic Claude CLI (claude command)
  • Best for: Full feature support, Claude-specific capabilities
  • Requires: claude command installed and available in PATH

Ollama

  • OpenAI-compatible API
  • Best for: Local models, offline execution, cost control
  • Requires: Ollama running locally (or accessible via network)
  • Models: Llama, Mistral, and other open-source models

OpenAI

  • OpenAI API (GPT-4, GPT-3.5, etc.)
  • Best for: Production deployments, advanced reasoning
  • Requires: OpenAI API key (OPENAI_API_KEY environment variable)

Configuration

Set the default provider in defaults.provider:

# .chant/config.md
---
project:
  name: my-project

defaults:
  provider: ollama
---

Configure provider endpoints in the providers section:

---
project:
  name: my-project

defaults:
  provider: ollama

providers:
  ollama:
    endpoint: http://localhost:11434/v1
  openai:
    endpoint: https://api.openai.com/v1
---

Provider Configuration Details

Claude Provider

No additional configuration needed. Ensure claude CLI is installed:

pip install anthropic-cli

Ollama Provider

Default endpoint: http://localhost:11434/v1

To use a remote Ollama instance:

---
defaults:
  provider: ollama

providers:
  ollama:
    endpoint: http://ollama-server.example.com:11434/v1
---

Start Ollama:

ollama serve

Pull a model:

ollama pull llama2

OpenAI Provider

Default endpoint: https://api.openai.com/v1

Requires OPENAI_API_KEY environment variable:

export OPENAI_API_KEY=sk-...

To use a custom OpenAI-compatible endpoint (e.g., Azure OpenAI):

---
defaults:
  provider: openai

providers:
  openai:
    endpoint: https://your-instance.openai.azure.com/openai
---

Provider-Specific Models

After choosing a provider, specify the model name:

Claude:

defaults:
  provider: claude
  model: claude-opus-4-5

Ollama:

defaults:
  provider: ollama
  model: llama2

OpenAI:

defaults:
  provider: openai
  model: gpt-4

Split Operations

For the chant split command, specify a separate model:

defaults:
  provider: ollama
  model: llama2
  split_model: mistral

If split_model is not specified, it defaults to sonnet (for Claude).

Parallel Execution

Configure multiple Claude agents for parallel spec execution. Useful when you have multiple Claude accounts to distribute work across.

Important: Agent definitions should be in global config (~/.config/chant/config.md) or .chant/agents.md, not in project config. This keeps sensitive account information out of git.

Example Configuration

Configure agents in your global config:

# ~/.config/chant/config.md
---
parallel:
  stagger_delay_ms: 1000
  agents:
    - name: main
      command: claude           # Shell command or wrapper script
      max_concurrent: 2         # Limited - may have active session
    - name: alt1
      command: claude-alt1      # Wrapper for alternate account
      max_concurrent: 3
    - name: alt2
      command: claude-alt2
      max_concurrent: 3
  cleanup:
    enabled: true
    prompt: parallel-cleanup    # Prompt for agent-assisted recovery
    auto_run: false             # Require confirmation before cleanup
---

Or for project-specific overrides (rare), use .chant/agents.md (gitignored):

# .chant/agents.md
---
parallel:
  agents:
    - name: project-worker
      command: claude-project
      max_concurrent: 2
---

Configuration Options

agents - List of available agents (Claude accounts/commands)

  • name: Display name for the agent (used in logs and attribution)
  • command: Shell command to invoke the agent (default: claude)
  • max_concurrent: Maximum concurrent instances for this agent (default: 2)

Total capacity is the sum of all agent max_concurrent values. Use the --max flag to limit below capacity.

cleanup - Post-execution cleanup settings

  • enabled: Whether to offer cleanup after parallel execution (default: true)
  • prompt: Prompt to use for cleanup agent (default: parallel-cleanup)
  • auto_run: Run cleanup automatically without confirmation (default: false)

Setting Up Multiple Accounts

Create shell aliases for each Claude account:

# ~/.bashrc or ~/.zshrc

# Main account (default)
alias claude='ANTHROPIC_API_KEY=sk-ant-xxx... claude'

# Alternate accounts
alias claude-alt1='ANTHROPIC_API_KEY=sk-ant-yyy... claude'
alias claude-alt2='ANTHROPIC_API_KEY=sk-ant-zzz... claude'

Distribution Strategy

When chant work --parallel runs, specs are distributed using a least-loaded-first strategy:

  1. Gather available capacity from all configured agents
  2. Respect per-agent max_concurrent limits
  3. Distribute to agents with most remaining capacity first
  4. Stop when total capacity is reached (or --max limit if specified)

Example distribution with 5 specs:

main:  spec-001, spec-004
alt1:  spec-002, spec-005
alt2:  spec-003

Pitfall Detection

After parallel execution, chant detects common issues:

IssueDetectionSeverity
API errors (429, rate limit)Exit code, stderrHigh
Merge conflictsGit status on branchesHigh
Partial failuresSome specs failedMedium
Stale worktreesWorktrees not cleaned upLow

Issues are reported in the execution summary, and cleanup can be offered if enabled.

Tuning Limits

The max_concurrent values are user-configurable. There are no universally “correct” values - the right settings depend on your specific setup.

Factors to consider:

  • API rate limits: Different accounts may have different rate limits
  • System resources: More concurrent agents means more CPU, memory, network
  • Account usage: Leave headroom if you also use accounts interactively
  • Experimentation: Start conservative, increase based on observed behavior

Example configurations:

# Conservative - single account, shared with manual use
parallel:
  agents:
    - name: main
      command: claude
      max_concurrent: 1

# Moderate - dedicated accounts for parallel work
parallel:
  agents:
    - name: worker1
      command: claude1
      max_concurrent: 3
    - name: worker2
      command: claude2
      max_concurrent: 3

# Aggressive - maximize throughput
parallel:
  agents:
    - name: worker1
      command: claude1
      max_concurrent: 5
    - name: worker2
      command: claude2
      max_concurrent: 5
    - name: worker3
      command: claude3
      max_concurrent: 5

Use --max N flag to limit below your total capacity when needed.

Run chant config --validate to verify your configuration.

Agent Rotation

When executing a single spec, distribute work across multiple configured agents using rotation strategies. This is useful for load balancing across multiple accounts or distributing expensive computations.

Configuration

# .chant/config.md
---
defaults:
  rotation_strategy: round-robin  # none, random, round-robin

parallel:
  agents:
    - name: main
      command: claude
      weight: 1           # Selection weight (default: 1)
    - name: alt1
      command: claude-alt1
      weight: 2           # Picked 2x as often as 'main'
    - name: alt2
      command: claude-alt2
      weight: 1
---

Strategies

none (default) - Always use first agent

  • Most conservative approach
  • Consistent behavior
  • Single point of contact

random - Weighted random selection

  • Each agent selected with probability proportional to weight
  • Unpredictable agent assignment
  • Load distributed randomly across agents

round-robin - Sequential rotation with weights

  • Agents selected in rotating order: main → alt1 → alt2 → main → …
  • Agents with higher weights appear more frequently in rotation
  • Selection state persists in .chant/store/rotation.json
  • Ensures even distribution over time

Weight Configuration

The weight field controls selection probability:

agents:
  - name: main
    command: claude
    weight: 1      # Default
  - name: worker1
    command: claude-1
    weight: 2      # Selected 2x as often as main
  - name: worker2
    command: claude-2
    weight: 3      # Selected 3x as often as main

In the above example, the rotation list would be: [main, worker1, worker1, worker2, worker2, worker2]

With round-robin, specs are executed in sequence using agents from this list, ensuring worker2 gets picked 3 times per 6-spec cycle.

Use Cases

  • Load balancing: Distribute work across multiple accounts with different rate limits
  • Account rotation: Rotate through multiple Claude accounts to avoid session conflicts
  • Capacity planning: Weight agents based on their availability/capacity

Enterprise Configuration

Configure automatic field derivation and enforcement for enterprise workflows.

Derived Fields

Automatically extract metadata from conventions (branch names, paths, environment):

---
enterprise:
  derived:
    # Extract sprint from branch name
    sprint:
      from: branch
      pattern: "sprint/(\\d{4}-Q\\d-W\\d)"

    # Extract Jira ticket from branch
    jira_key:
      from: branch
      pattern: "([A-Z]+-\\d+)"

    # Extract team from spec path
    team:
      from: path
      pattern: "teams/(\\w+)/"
      validate:
        type: enum
        values: [platform, frontend, backend, infra]

    # Extract from environment variable
    environment:
      from: env
      pattern: DEPLOY_ENV

    # Extract from git user (pattern must be literal "name" or "email", not regex)
    author_email:
      from: git_user
      pattern: "email"           # Extracts git config user.email
---

Derivation Sources:

  • branch - Current git branch name
  • path - Spec file path relative to repository root
  • env - Environment variable name (omits $)
  • git_user - Git user name or email (pattern must be literal "name" or "email", not regex)

Pattern Syntax:

  • Standard regex with capture groups
  • First capture group becomes the field value
  • If pattern doesn’t match → field omitted (graceful failure)
  • Exception: git_user source does not use regex — pattern must be the literal string "name" or "email"

Validation:

  • type: enum with values: [...] list
  • Case-sensitive matching
  • Invalid values: field included but warning logged
  • Validation never blocks derivation

Required Fields

Enforce presence of fields for compliance:

---
enterprise:
  required:
    - team
    - jira_key
    - environment
---

When enforced:

  • chant lint validates all specs have these fields
  • Fields can be derived or explicitly set
  • Failure blocks spec operations
  • Shows enterprise policy in error messages

How Derivation Works

Derivation runs automatically:

  1. During spec completion - Auto-populates fields, tracked in derived_fields list
  2. Manual re-derivation - Use chant derive to update existing specs
  3. Conflict handling - Existing explicit values are preserved

Example flow:

# Before completion
---
status: pending
---

# After completion (with derivation rules configured)
---
status: completed
completed_at: 2026-01-22T15:30:00Z

# Auto-populated from branch: sprint/2026-Q1-W4/PROJ-123-task
sprint: 2026-Q1-W4    [derived]
jira_key: PROJ-123    [derived]

# Tracking
derived_fields: [sprint, jira_key]
---

Common Patterns

Jira Integration:

jira_key:
  from: branch
  pattern: "([A-Z]+-\\d+)"
  validate:
    type: enum
    values: [PROJ, AUTH, API, WEB]  # Your project prefixes

Team Ownership:

team:
  from: path
  pattern: "teams/(\\w+)/"

Component Tracking:

component:
  from: path
  pattern: "src/(\\w+)/"

Multi-Environment:

environment:
  from: branch
  pattern: "^(dev|staging|prod)"

Approval Configuration

Configure how the approval workflow behaves when specs are rejected and how agent work is handled.

Configuration Fields

approval:
  rejection_action: manual              # manual | dependency | group
  require_approval_for_agent_work: true # Auto-require approval for agent commits

Agent Work Approval

When enabled, require_approval_for_agent_work automatically sets approval.required: true for specs where agent co-authorship is detected in commits (via Co-Authored-By signatures).

auto_approve concept:

  • Detection occurs during chant finalize
  • Triggers when commit messages contain AI assistant signatures (Claude, GPT, Copilot, Gemini)
  • Prevents merge until human approval via chant approve
  • Provides safety checkpoint for AI-generated code

on_ambiguity concept:

  • When agent work is detected, approval becomes required regardless of initial spec settings
  • User must explicitly approve via chant approve <spec-id> --by <name> before merge
  • Emergency bypass available via --skip-approval flag for urgent situations

See Approval Workflow Guide for detailed examples.

Rejection Action

The approval.rejection_action setting controls what happens after a spec is rejected with chant reject:

manual (default):

  • Spec remains in rejected status
  • User must manually resolve issues and re-submit for approval
  • No automatic changes to spec structure

dependency:

  • Automatically creates a new “fix spec” for the rejection issues
  • Original spec status changes to blocked
  • Fix spec added to original spec’s depends_on
  • Fix spec title: “Fix rejection issues for <spec-id>
  • Includes context from the rejection reason

group:

  • Converts the rejected spec to a driver type
  • Creates numbered member specs (.1, .2, .3, etc.)
  • Distributes acceptance criteria across member specs
  • Each member depends on the previous one (sequential execution)
  • Includes context from the rejection reason

Example Configuration

# .chant/config.md
---
project:
  name: my-project

approval:
  rejection_action: dependency
  require_approval_for_agent_work: true
---

# Project Config

When specs are rejected, automatically create a fix spec
and block the original until the fix is complete.

Agent-written code requires human approval before merge.

Approval Frontmatter Schema

Specs that require approval have an approval: section in their frontmatter:

approval:
  required: true              # Whether approval is required
  status: pending             # pending | approved | rejected
  by: alice                   # Name of the approver/rejector
  at: 2026-01-28T14:30:45Z   # ISO8601 timestamp of approval/rejection

This section is added automatically when using chant add --needs-approval, or can be added manually to any spec.

Members Frontmatter Field

Driver and group specs can list their member specs in the members: frontmatter field:

---
type: driver
status: pending
members:
  - 2026-01-28-001-abc.1
  - 2026-01-28-001-abc.2
  - 2026-01-28-001-abc.3
---

This field is automatically populated when using chant split or the group rejection action. It tracks which specs belong to a driver for status tracking and merge ordering.

Monorepo Support

Chant supports multiple .chant/ directories in a monorepo. Each subdirectory with its own .chant/ directory is treated as an independent project.

Project Name

The project.name field is used to namespace worktree directories in /tmp/. This prevents collisions when running multiple projects:

# frontend/.chant/config.md
---
project:
  name: frontend
---

Worktrees will be created at /tmp/chant-frontend-{spec-id} instead of /tmp/chant-{spec-id}.

Branch Prefix

The defaults.branch_prefix field controls branch naming. Use unique prefixes to isolate branches between projects:

# frontend/.chant/config.md
---
defaults:
  branch_prefix: "chant/frontend/"
---

This creates branches like chant/frontend/2026-02-06-001-abc instead of chant/2026-02-06-001-abc.

Complete Example

monorepo/
├── frontend/.chant/config.md  (project.name: frontend, branch_prefix: chant/frontend/)
├── backend/.chant/config.md   (project.name: backend, branch_prefix: chant/backend/)
└── infra/.chant/config.md     (project.name: infra, branch_prefix: chant/infra/)

See the Monorepo Guide for detailed setup instructions, MCP configuration, and cross-project workflows.

Precedence

  1. Spec frontmatter (highest)
  2. Environment variables
  3. Project config (.chant/config.md)
  4. Global config (~/.config/chant/config.md)
  5. Built-in defaults (lowest)

Provider Configuration

Chant supports multiple AI model providers. Configure providers in .chant/config.md to control which model APIs chant uses for spec execution.

Supported Providers

Claude (Default)

Uses the Anthropic Claude CLI (claude command).

Best for:

  • Full feature support
  • Claude-specific capabilities
  • Interactive development

Requirements:

  • claude command installed and available in PATH
  • Anthropic API key configured

Installation:

pip install anthropic-cli

Configuration:

defaults:
  provider: claude
  model: claude-opus-4-5

No additional endpoint configuration needed.

Ollama

OpenAI-compatible API for running local models.

Best for:

  • Local execution
  • Offline development
  • Cost control
  • Privacy-sensitive workloads

Requirements:

  • Ollama running locally or accessible via network
  • Supported models: Llama, Mistral, and other open-source models

Installation:

# Install Ollama
curl https://ollama.ai/install.sh | sh

# Start Ollama
ollama serve

# Pull a model
ollama pull llama2

Configuration:

defaults:
  provider: ollama
  model: llama2

providers:
  ollama:
    endpoint: http://localhost:11434/v1

Remote Ollama:

providers:
  ollama:
    endpoint: http://ollama-server.example.com:11434/v1

OpenAI

OpenAI API (GPT-4, GPT-3.5, etc.)

Best for:

  • Production deployments
  • Advanced reasoning tasks
  • GPT-specific capabilities

Requirements:

  • OpenAI API key (OPENAI_API_KEY environment variable)
  • API access

Configuration:

defaults:
  provider: openai
  model: gpt-4

providers:
  openai:
    endpoint: https://api.openai.com/v1

Environment setup:

export OPENAI_API_KEY=sk-...

Azure OpenAI:

defaults:
  provider: openai
  model: gpt-4

providers:
  openai:
    endpoint: https://your-instance.openai.azure.com/openai

Kiro CLI

Kiro CLI (kiro-cli-chat) for MCP-based agent execution.

Best for:

  • MCP server integration
  • Teams using Kiro ecosystem

Requirements:

  • kiro-cli-chat command installed and available in PATH
  • MCP servers configured via kiro-cli-chat mcp add

Installation:

# See https://kiro.dev/docs/cli for installation

Configuration:

defaults:
  provider: kirocli
  model: sonnet  # shorthand names: sonnet, opus, haiku

MCP Server Setup: Kiro CLI requires MCP servers to be configured separately:

# Add chant MCP server (command and args must be separate)
kiro-cli-chat mcp add --name chant --command "$(which chant)" --args mcp --scope global

# Verify configuration
kiro-cli-chat mcp list

# Should show under global:
#   • chant        /path/to/chant

Verify tools are available:

# Start interactive chat
kiro-cli-chat chat

# In the chat, type /tools to see available tools
# Should list: chant_spec_list, chant_status, chant_add, etc.

Note: Uses kiro-cli-chat chat --no-interactive --trust-all-tools --model <model> for automated execution.

Configuration Reference

Basic Provider Setup

Set the default provider in .chant/config.md:

# .chant/config.md
---
project:
  name: my-project

defaults:
  provider: claude    # claude | ollama | openai | kirocli
  model: claude-opus-4-5
---

Provider Endpoints

Configure provider-specific endpoints in the providers section:

---
defaults:
  provider: ollama
  model: llama2

providers:
  ollama:
    endpoint: http://localhost:11434/v1
  openai:
    endpoint: https://api.openai.com/v1
---

Model Selection

Specify models for each provider:

Claude:

defaults:
  provider: claude
  model: claude-opus-4-5

Ollama:

defaults:
  provider: ollama
  model: llama2          # or: mistral, mixtral, etc.

OpenAI:

defaults:
  provider: openai
  model: gpt-4           # or: gpt-3.5-turbo, etc.

Split Model Configuration

For chant split operations, specify a separate model:

defaults:
  provider: ollama
  model: llama2
  split_model: mistral   # Used for split operations

If split_model is not specified, it defaults to sonnet (for Claude).

Per-Spec Provider Override

Override the provider for specific specs using frontmatter:

---
status: pending
provider: openai
model: gpt-4
---

# Spec title

This spec will use GPT-4 instead of the default provider.

Global vs. Project Configuration

Global config (~/.config/chant/config.md):

defaults:
  provider: claude
  model: claude-opus-4-5

providers:
  openai:
    endpoint: https://api.openai.com/v1

Project config (.chant/config.md):

project:
  name: my-project

defaults:
  provider: ollama      # Override global default
  model: llama2

Project settings override global settings.

Validation

Verify your provider configuration:

chant config --validate

This checks:

  • Provider is supported
  • Required fields are present
  • Endpoint URLs are valid
  • Model names are specified

See Also

Errors

Error Categories

CategoryExamplesSeverity
ParseInvalid YAML, missing fieldsBlocking
StateLocked, blocked, wrong statusBlocking
ExecutionAgent failed, tests failedSpec fails
GitMerge conflict, dirty worktreeBlocking
SystemDisk full, permissionsFatal

Error Catalog

Parse Errors

PARSE_INVALID_YAML

Error: Invalid YAML in spec frontmatter
File: 2026-01-22-001-x7m.md
Line: 3
  status pending   ← Missing colon

Fix: Add colon after 'status'

PARSE_MISSING_FIELD

Error: Missing required field 'status'
File: 2026-01-22-001-x7m.md

Fix: Add 'status: pending' to frontmatter

PARSE_INVALID_VALUE

Error: Invalid status value 'open'
File: 2026-01-22-001-x7m.md
Allowed: pending, in_progress, completed, failed

Fix: Change status to valid value

State Errors

STATE_LOCKED

Error: Spec is locked
Spec: 2026-01-22-001-x7m
Locked by: PID 12345 (alex@macbook.local)
Since: 2026-01-22T15:30:00Z

Options:
  - Wait for completion
  - chant unlock 2026-01-22-001-x7m (if stale)

STATE_BLOCKED

Error: Spec is blocked by dependencies
Spec: 2026-01-22-001-x7m

Waiting on:
  ✗ 2026-01-22-002-q2n (pending)
  ✓ 2026-01-22-003-abc (completed)

Fix: Complete pending dependencies first

STATE_HAS_MEMBERS

Error: Driver spec has pending members
Spec: 2026-01-22-001-x7m

Members:
  - 2026-01-22-001-x7m.1 (pending)
  - 2026-01-22-001-x7m.2 (pending)

Options:
  - chant work 2026-01-22-001-x7m.1
  - chant work 2026-01-22-001-x7m --parallel

STATE_ALREADY_COMPLETE

Error: Spec is already completed
Spec: 2026-01-22-001-x7m
Completed: 2026-01-22T15:30:00Z

Use --force to re-run (not recommended)

Execution Errors

EXEC_AGENT_FAILED

Error: Agent execution failed
Spec: 2026-01-22-001-x7m
Exit code: 1

Last output:
  [15:30:45] Running tests...
  [15:30:52] FAILED: 2 assertions failed

Spec marked as 'failed'. See spec file for details.

EXEC_TIMEOUT

Error: Agent execution timed out
Spec: 2026-01-22-001-x7m
Timeout: 30m

Spec marked as 'failed'.
Options:
  - Increase timeout: chant work --timeout 60m
  - Break into smaller specs

Git Errors

GIT_DIRTY

Error: Working directory has uncommitted changes
Files:
  M src/api/handler.go
  ? src/api/new.go

Options:
  - git stash
  - git commit
  - chant work --allow-dirty (not recommended)

GIT_CONFLICT

Error: Merge conflict after spec completion
Spec: 2026-01-22-001-x7m
Branch: chant/2026-01-22-001-x7m

Conflicts:
  - src/api/handler.go

Options:
  - Resolve manually and commit
  - chant retry 2026-01-22-001-x7m (re-run on current main)

GIT_WORKTREE_FAILED

Error: Failed to create worktree
Spec: 2026-01-22-001-x7m
Path: .chant/.worktrees/2026-01-22-001-x7m

Cause: Path already exists

Fix: rm -rf .chant/.worktrees/2026-01-22-001-x7m

Dependency Errors

DEP_CYCLE

Error: Dependency cycle detected

Cycle:
  2026-01-22-001-x7m
  → 2026-01-22-002-q2n
  → 2026-01-22-003-abc
  → 2026-01-22-001-x7m

Fix: Remove one dependency to break cycle

DEP_NOT_FOUND

Error: Dependency not found
Spec: 2026-01-22-001-x7m
Missing: 2026-01-22-999-zzz

Fix: Remove invalid dependency or create missing spec

Exit Codes

CodeMeaning
0Success
1General error
2Parse error
3State error (locked, blocked)
4Execution failed
5Git error
10Lint errors found

Error in Spec File

On failure, error details saved to spec:

---
status: failed
failed_at: 2026-01-22T15:31:02Z
error: "EXEC_AGENT_FAILED: 2 test assertions failed"
log: |
  [15:30:01] Starting execution
  [15:30:45] Running tests...
  [15:30:52] FAILED: 2 assertions failed
---

JSON Output

For tooling:

chant work 2026-01-22-001-x7m --json 2>&1
{
  "error": {
    "code": "STATE_BLOCKED",
    "message": "Spec is blocked by dependencies",
    "spec": "2026-01-22-001-x7m",
    "details": {
      "waiting_on": ["2026-01-22-002-q2n"]
    }
  }
}

Recovery Commands

ErrorRecovery
Stale lockchant unlock <id>
Failed specchant retry <id>
Merge conflictchant resolve <id>
Cyclechant deps --check

Search Command

Status: Implemented - The chant search command performs simple text search across spec titles and body content.

Overview

Search specs by title and body content with optional filters for date, status, type, and labels. Supports both interactive wizard mode (when run without arguments) and direct command-line search.

Interactive Wizard Mode

Run without arguments to launch an interactive search wizard:

chant search

The wizard prompts for:

  • Search query (required)
  • Search scope: Title + Body, Title only, or Body only
  • Date range: Any time, Last 7 days, Last 2 weeks, Last month, or Custom
  • Archive scope: Include archived specs or not
  • Status filter: Any, pending, ready, in_progress, completed, failed
  • Type filter: Any, code, task, documentation, research

Direct Search Mode

Search from the command line with optional filters:

chant search "authentication"           # Search title + body
chant search "auth" --title-only        # Search title only
chant search "TODO" --body-only         # Search body only
chant search "Auth" --case-sensitive    # Case-sensitive match

Filtering

By Status

chant search "api" --status pending      # Filter by status
chant search "auth" --status completed
chant search "fix" --status in_progress

Supported statuses: pending, ready, in_progress, completed, failed

By Type

chant search "auth" --type code          # Filter by type
chant search "doc" --type documentation

Supported types: code, task, documentation, research

By Labels

chant search "api" --label feature       # Filter by label
chant search "bug" --label urgent --label critical

Labels use OR logic - specs with any matching label are included.

By Date Range

Use relative dates or absolute dates:

# Relative dates
chant search "bug" --since 7d            # Last 7 days
chant search "feature" --since 2w        # Last 2 weeks
chant search "api" --since 1m            # Last month
chant search "auth" --since 1w           # Last week

# Absolute dates
chant search "auth" --since 2026-01-20   # Since specific date
chant search "fix" --until 2026-01-15    # Until specific date

# Date ranges
chant search "api" --since 1w --until 3d # Between dates

Date is based on the spec ID date component (YYYY-MM-DD prefix).

Archive Scope

By default, search includes both active and archived specs:

chant search "auth"                     # Both active and archived
chant search "auth" --active-only       # Only .chant/specs/
chant search "auth" --archived-only     # Only .chant/archive/

Combined Filters

Combine multiple filters with AND logic:

# Pending code specs from last 2 weeks
chant search "auth" --status pending --type code --since 2w

# Failed API tasks with urgent label
chant search "api" --status failed --label urgent

# Recently completed documentation
chant search "doc" --status completed --type documentation --since 1w

Text Matching Options

chant search "Auth" --case-sensitive     # Matches exact case

By default, search is case-insensitive.

chant search "authentication" --title-only

Searches only the spec title (first # heading in the spec body).

chant search "TODO" --body-only

Searches only the spec body (everything after frontmatter).

Output Format

Results show:

  • Status icon (● for active, ◌ for archived)
  • Spec ID (in cyan)
  • Spec title
  • Archive indicator [archived] for archived specs
● 2026-01-24-001-abc Add user authentication
● 2026-01-24-005-xyz Fix auth token refresh
◌ 2026-01-20-003-def [archived] Old auth implementation

Found 3 specs matching "auth"

Examples

Find pending authentication tasks

chant search "auth" --status pending

Find recently completed code specs

chant search "" --status completed --type code --since 1w

Find failed API tasks

chant search "api" --status failed

Find critical bugs added this week

chant search "bug" --label critical --since 7d

Search only active specs

chant search "refactor" --active-only

Case-sensitive search for specific term

chant search "TODO" --body-only --case-sensitive

Git Integration

Configuration

Git worktree settings in .chant/config.md:

defaults:
  branch_prefix: "chant/"   # Prefix for worktree branches
  main_branch: "main"       # Target branch for merges

Worktree Mode

Chant uses git worktrees for isolation. Each spec executes in its own worktree with a dedicated branch:

  • Branch name: {branch_prefix}{spec_id} (e.g., chant/2026-01-22-001-x7m)
  • Worktree location: .chant/worktrees/{spec_id}/
  • Changes merged back to main branch after completion

Note: Specs serve as the review primitive in chant. Each spec has a title, acceptance criteria, branch, commits, and review workflow — fulfilling the same role as a pull request but working offline and without external dependencies.

Commit Flow

1. Agent commits work
   └── git commit -m "chant(2026-01-22-001-x7m): Add authentication"

2. CLI captures hash
   └── git rev-parse HEAD → a1b2c3d4

3. CLI updates frontmatter
   └── Adds: commit, completed_at, branch (if applicable)

4. CLI commits metadata update
   └── git commit -m "chant: mark 2026-01-22-001-x7m complete"

Concurrency

Worktree isolation ensures each spec executes in its own branch without conflicts. However, merge races can occur when multiple specs finish simultaneously and attempt to merge back to main:

Terminal 1: chant work spec-a   →  git checkout main && git merge chant/spec-a
Terminal 2: chant work spec-b   →  git checkout main && git merge chant/spec-b
                                    ⚠️  Race: both merge to main at the same time

Warning: Running multiple chant work processes in separate terminals can cause merge conflicts when specs finish concurrently. Both processes attempt git checkout main && git merge in the main repository, leading to race conditions.

Safe Approach: Use --parallel

chant work --parallel 3    # Sequences all merges safely

The --parallel flag coordinates merge-back operations across workers, ensuring only one spec merges at a time.

Watch Mode

Watch mode (chant watch) uses a PID lock to ensure only one instance runs, preventing concurrent merges by design.


Custom Merge Driver

Chant includes a custom git merge driver that automatically resolves frontmatter conflicts in spec files.

What It Does

When merging spec branches back to main, frontmatter conflicts commonly occur. The merge driver:

  • Intelligently merges status, completed_at, and model fields
  • Preserves implementation content
  • Prefers “completed” status over “in_progress”
  • Merges lists (commits, labels, target_files) with deduplication

Installation

  1. Add to .gitattributes in your repository root:

    .chant/specs/*.md merge=chant-spec
    
  2. Configure git:

    git config merge.chant-spec.driver "chant merge-driver %O %A %B"
    git config merge.chant-spec.name "Chant spec merge driver"
    

Verification

git config --get merge.chant-spec.driver
grep chant-spec .gitattributes

Templates

Chant uses markdown templates for specs and prompts with simple variable substitution.

Template Syntax

{{variable}}     # Variable substitution
${ENV_VAR}       # Environment variable

Spec Templates

Default spec template for chant add:

# .chant/templates/spec.md
---
status: pending
---

# {{description}}

## Context

<!-- Why is this spec needed? -->

## Acceptance Criteria

- [ ]

## Notes

<!-- Optional: additional context -->

Usage:

chant add "Fix authentication bug"
# Creates spec with {{description}} = "Fix authentication bug"

Spec Template Variables

VariableDescription
{{description}}From chant add argument
{{date}}Current date (YYYY-MM-DD)
{{project}}Project name from config
{{id}}Generated spec ID

Prompt Templates

Prompts use the same substitution:

# .chant/prompts/standard.md
---
name: standard
---

You are implementing a spec for {{project.name}}.

**{{spec.title}}**

{{spec.description}}

Prompt Template Variables

VariableDescription
{{project.name}}Project name from config
{{spec.id}}Full spec identifier
{{spec.title}}Spec title (first heading)
{{spec.description}}Full spec body content
{{spec.target_files}}Target files from frontmatter
{{worktree.path}}Worktree path (parallel execution)
{{worktree.branch}}Branch name (parallel execution)

Custom Templates

Override defaults in config:

# config.md
templates:
  spec: .chant/templates/my-spec.md

Or use --template flag:

chant add "Add feature" --template spec-tdd

Schema & Validation

The “Messy Markdown” Problem

Criticism: Markdown is messy, JSONL is clean.

Counter: Messiness is a validation problem, not a format problem. Agents can lint.

Validation Layers

1. Schema Definition

Config defines required fields and valid values:

# In config.md frontmatter
schema:
  spec:
    required: [status]    # id comes from filename
    fields:
      status:
        type: string
        enum: [pending, in_progress, completed, failed]
      depends_on:
        type: array
        items: string
      labels:
        type: array
        items: string

2. Lint on Write

Prompt instructs agent to validate before commit:

# In prompt file
Before committing, verify the spec file:
- [ ] Frontmatter has required fields: id, status
- [ ] Status is one of: pending, in_progress, completed, failed
- [ ] All depends_on IDs exist
- [ ] YAML is valid

3. Lint on Read

Parser validates and normalizes:

#![allow(unused)]
fn main() {
fn parse_spec(path: &Path) -> Result<Spec, ValidationError> {
    let content = read_file(path)?;
    let (frontmatter, body) = split_frontmatter(&content)?;
    let spec: Spec = serde_yaml::from_str(&frontmatter)?;

    // Validate
    validate_required(&spec)?;
    validate_status(&spec)?;
    validate_deps_exist(&spec)?;

    Ok(spec)
}
}

4. Pre-commit Hook

CLI provides lint command:

chant lint                    # Lint all specs
chant lint 2026-01-22-001-x7m            # Lint specific spec
chant lint --fix              # Auto-fix where possible

Auto-Fix Capabilities

IssueAuto-fixableFix
Missing statusYesDefault to pending
Invalid status valueNoError, human decides
Missing idYesGenerate from filename
Trailing whitespaceYesTrim
Inconsistent indentationYesNormalize to 2 spaces
Missing newline at EOFYesAdd newline

Validation Errors

$ chant lint
2026-01-22-001-x7m.md:
  error: status "open" not in enum [pending, in_progress, completed, failed]
  error: depends_on "2026-01-22-999-zzz" does not exist

2026-01-22-002-q2n.md:
  warning: missing optional field "labels"

2 errors, 1 warning

Agent-Friendly Validation

Agents get structured feedback:

$ chant lint --json
{
  "valid": false,
  "errors": [
    {
      "file": "2026-01-22-001-x7m.md",
      "field": "status",
      "message": "value 'open' not in enum",
      "allowed": ["pending", "in_progress", "completed", "failed"]
    }
  ]
}

Agent can then fix and retry.

Why This Works

  1. Agents write most specs - they follow the prompt, which includes validation
  2. Humans can still edit - lint catches mistakes before commit
  3. Parse errors are rare - YAML frontmatter is simple, well-supported
  4. Recovery is easy - fix the text file, re-run lint

The format is human-readable AND machine-validatable. Chant chooses human-first with machine validation.

Output Schema Validation

For task specs that produce structured output (research reports, analysis results, etc.), you can enforce a JSON Schema on agent output.

Defining an Output Schema

Add output_schema to spec frontmatter pointing to a JSON Schema file:

---
type: task
status: ready
output_schema: .chant/schemas/research-report.json
---

# Research issue #1234

Investigate root cause and produce structured report.

Creating Schema Files

Create JSON Schema files in .chant/schemas/:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["spec_id", "findings", "recommendation"],
  "properties": {
    "spec_id": {
      "type": "string",
      "pattern": "^[A-Z]\\.[0-9]+\\.[0-9]+$"
    },
    "findings": {
      "type": "array",
      "items": {"type": "string"},
      "minItems": 1
    },
    "recommendation": {
      "type": "string"
    }
  }
}

How It Works

  1. Prompt Injection: When output_schema is present, chant automatically injects an “Output Format” section into the agent prompt with the schema definition, required fields, and an example.

  2. Post-Execution Validation: After the agent completes, chant extracts JSON from the agent output and validates it against the schema.

  3. Linter Integration: chant lint validates output for completed specs that have output_schema defined.

Configuration

Control validation strictness in config.md:

---
validation:
  strict_output_validation: false  # Default: warn but allow
---

When strict_output_validation: true:

  • Specs fail if output doesn’t match schema
  • Status is set to needs_attention

When strict_output_validation: false (default):

  • Warning is shown but spec proceeds to completion
  • Useful for gradual adoption

Validation Output

Success:

✓ Output validation passed (schema: .chant/schemas/research-report.json)

Failure:

✗ Output validation failed (schema: .chant/schemas/research-report.json)
  - missing required field 'spec_id'
  - at '/findings': expected array, got string
  → Review .chant/logs/2026-01-29-001-abc.log for details

JSON Extraction

Chant uses multiple strategies to extract JSON from agent output:

  1. Code blocks: ```json ... ``` or ``` ... ```
  2. Bare JSON: Entire output is valid JSON
  3. Embedded JSON: {...} or [...] patterns in text

Example Workflow

  1. Create schema:

    mkdir -p .chant/schemas
    cat > .chant/schemas/research.json << 'EOF'
    {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "type": "object",
      "required": ["spec_id", "root_cause"],
      "properties": {
        "spec_id": {"type": "string"},
        "root_cause": {"type": "string"},
        "affected_files": {"type": "array", "items": {"type": "string"}}
      }
    }
    EOF
    
  2. Create spec with schema reference:

    chant add "Research bug #123"
    # Edit spec to add: output_schema: .chant/schemas/research.json
    
  3. Work the spec - agent sees schema in prompt

  4. Validation runs automatically on completion

  5. Check all specs: chant lint

Export

Status: Implemented

The chant export command is fully implemented as of v0.3.0. You can export specs to JSON, CSV, and Markdown formats with filtering options.

Design Principle

Chant exports spec data. External tools make reports.

Chant exports raw data → Agents/tools process → Human-readable reports

This keeps chant focused on spec execution, not report generation. See philosophy for the broader approach.

Basic Export

chant export                          # All specs, JSON
chant export --format json            # Explicit JSON
chant export --format csv             # Spreadsheet-friendly
chant export --format markdown        # Simple list

Date Filtering

# Completed in date range
chant export --from 2026-01-01 --to 2026-01-31

# Last N days
chant export --last 30d

# Since specific date
chant export --since 2026-01-01

# Completed this week
chant export --last 7d --status completed

Search Filtering

Use standard search syntax:

chant export --search "status:completed"
chant export --search "label:security"
chant export --search "project:auth"
chant export --search "label:security completed_at:>2026-01-01"

Output Formats

JSON (Default)

$ chant export --last 7d --format json
{
  "exported_at": "2026-01-22T15:00:00Z",
  "query": "completed_at:>2026-01-15",
  "count": 12,
  "specs": [
    {
      "id": "2026-01-22-001-x7m",
      "title": "Add authentication",
      "status": "completed",
      "created_at": "2026-01-20T10:00:00Z",
      "completed_at": "2026-01-20T11:30:00Z",
      "labels": ["feature", "auth"],
      "commit": "abc123",
      "cost": {
        "tokens": 15432,
        "usd": 1.23
      }
    }
  ]
}

CSV

$ chant export --last 7d --format csv
id,title,status,created_at,completed_at,labels,commit,tokens,cost_usd
2026-01-22-001-x7m,Add authentication,completed,2026-01-20T10:00:00Z,2026-01-20T11:30:00Z,"feature,auth",abc123,15432,1.23

Markdown

$ chant export --last 7d --format markdown
# Spec Export
Query: completed_at:>2026-01-15
Count: 12

## 2026-01-22-001-x7m: Add authentication
- Status: completed
- Completed: 2026-01-20
- Labels: feature, auth
- Commit: abc123

## 2026-01-22-002-q2n: Fix payment bug
- Status: completed
- Completed: 2026-01-21
- Labels: bug, payments
- Commit: def456

What Gets Exported

FieldDescription
idSpec ID
titleFirst heading from spec body
statusCurrent status
created_atCreation timestamp
completed_atCompletion timestamp (if completed)
labelsLabels array
projectProject prefix (if set)
commitGit commit hash (if completed)
branchGit branch (if created)
cost.tokensToken count (if tracked)
cost.usdCost in USD (if tracked)
duration_sExecution duration in seconds
agentAgent/model used
promptPrompt used

Selective Fields

# Only specific fields
chant export --fields id,title,status,completed_at

# Exclude cost data
chant export --exclude cost

Piping to Tools

# Count by status
chant export --format json | jq '.specs | group_by(.status) | map({status: .[0].status, count: length})'

# Total cost
chant export --format json | jq '[.specs[].cost.usd] | add'

# Have agent summarize
chant export --last 7d | claude "Summarize this week's completed specs for a standup"

# Generate release notes
chant export --search "label:feature" --from 2026-01-01 | claude "Write release notes from these specs"

Audit Export

For compliance, include full spec bodies:

chant export --full                   # Include spec body content
chant export --full --include-output  # Include agent output too
{
  "specs": [
    {
      "id": "2026-01-22-001-x7m",
      "title": "Add authentication",
      "body": "# Add authentication\n\n## Acceptance Criteria\n...",
      "output": "[10:15:32] Reading src/auth/handler.go\n..."
    }
  ]
}

Git Integration

Export includes git metadata:

chant export --git-details
{
  "specs": [
    {
      "id": "2026-01-22-001-x7m",
      "git": {
        "commit": "abc123",
        "branch": "chant/2026-01-22-001-x7m",
        "author": "alice@example.com",
        "files_changed": ["src/auth/handler.go", "src/auth/jwt.go"],
        "insertions": 145,
        "deletions": 12
      }
    }
  ]
}

Configuration

# config.md
export:
  default_format: json
  include_cost: true
  include_git: false

Examples

Weekly Summary for Standup

chant export --last 7d --status completed --format json | \
  claude "Create a bullet-point summary of what was accomplished this week"

Security Audit Data

chant export --search "label:security" --full --git-details > audit-data.json

Cost Tracking

chant export --last 30d --format csv --fields id,title,tokens,cost_usd > monthly-costs.csv

Release Notes Input

chant export --from 2026-01-01 --to 2026-01-31 --search "label:feature OR label:bugfix" | \
  claude "Write customer-facing release notes from these specs"

Why Not Built-in Reports?

Chant focuses on spec execution. Report generation is:

  1. Highly variable - Every team wants different formats
  2. Better done by agents - LLMs excel at summarization
  3. Already solved - jq, csvkit, pandas, etc.

Export gives you the raw data. Use the right tool for presentation.

Initialization

For first-time setup, run chant init with no arguments:

chant init

The wizard guides you through all configuration options:

  • Project name: Auto-detected from package.json, Cargo.toml, go.mod, or directory name
  • Prompt templates: Include ready-to-use prompts (recommended) or skip for minimal setup
  • Silent mode: Keep .chant/ local only (gitignored) for enterprise environments
  • Model provider: Choose your AI provider (see Provider Configuration)
  • Default model: opus, sonnet, haiku, or custom model name
  • Agent configuration: Claude Code (CLAUDE.md), Cursor, Amazon Q, Generic, or all

The wizard is the best path for new users because it:

  • Asks all the right questions in order
  • Explains each option with clear prompts
  • Automatically creates MCP config when Claude is selected
  • Provides sensible defaults
  • Prevents configuration mistakes

What the Wizard Creates

When you select Claude agent configuration, the wizard creates:

  • .chant/ directory with config, prompts, and specs
  • CLAUDE.md with agent instructions (rules/steering)
  • .claude/skills/chant/SKILL.md with chant workflow skill (Agent Skills standard)
  • .mcp.json for MCP server integration

Direct Configuration (for scripts/automation)

For CI/CD pipelines or scripted setups, use flags directly:

chant init --agent claude --provider claude --model opus

This creates the .chant/ directory structure in current repo.

What Gets Created

.chant/
├── config.md             # Project configuration
├── prompts/              # Prompt files
│   └── standard.md       # Default prompt
├── specs/                # Spec files (empty)
├── .locks/               # PID files (gitignored)
├── .store/               # Index cache (gitignored)
└── .gitignore            # Ignores local state

Generated Files

config.md

---
project:
  name: {detected from package.json, Cargo.toml, or dirname}

defaults:
  prompt: standard
---

# Chant Configuration

Project initialized on {date}.

prompts/standard.md

---
name: standard
purpose: Default execution prompt
---

# Execute Spec

You are implementing a spec for {{project.name}}.

## Your Spec

**{{spec.title}}**

{{spec.description}}

## Acceptance Criteria

{{#each spec.acceptance}}
- [ ] {{this}}
{{/each}}

## Instructions

1. **Read** the relevant code first
2. **Plan** your approach before coding
3. **Implement** the changes
4. **Verify** each acceptance criterion
5. **Commit** with message: `chant({{spec.id}}): <description>`

## Constraints

- Only modify files related to this spec
- Follow existing code patterns
- Do not refactor unrelated code

.gitignore

# Local state (not shared)
.locks/
.store/

Detection

chant init detects project info:

SourceField
package.jsonnameproject.name
Cargo.toml[package] nameproject.name
go.mod → module pathproject.name
Directory nameproject.name (fallback)

Idempotent

Running chant init twice is safe:

  • Existing files are not overwritten
  • Missing files are created
  • Reports what was created/skipped
$ chant init
Created .chant/config.md
Created .chant/prompts/standard.md
Skipped .chant/specs/ (exists)

Flags

chant init --force         # Overwrite existing files
chant init --minimal       # Only config.md, no prompts
chant init --name foo      # Override detected name
chant init --silent        # Local only, not committed
chant init --provider claude  # Set model provider (see providers.md)
chant init --model opus    # Set default model (opus, sonnet, haiku, or custom)
chant init --agent claude  # Create agent config + skill files

The --agent flag creates both rules files (always-loaded) and skills (contextually activated) per the Agent Skills open standard. Provider is automatically inferred from the agent name if --provider is not specified.

Silent Mode

For enterprise users on shared repos they don’t control:

chant init --silent

Creates .chant/ but keeps it local:

  1. Adds .chant/ to .git/info/exclude (local gitignore, not committed)
  2. Specs stay on your machine only
  3. No trace in shared repo
  4. Personal AI workflow within rigid enterprise environment
$ chant init --silent
Created .chant/config.md
Created .chant/prompts/standard.md
Added .chant/ to .git/info/exclude (silent mode)

Note: Specs will not be committed. Local use only.

Trade-offs:

  • No git history for specs
  • No collaboration via git
  • Lost if machine fails

Use case: Enterprise developer using AI assistance on a project that doesn’t officially support it. Personal productivity without changing shared repo.

Post-Init

After init, typical flow:

chant init
chant add "First spec"
chant work 2026-01-22-001-x7m

MCP Server

Overview

Chant exposes an MCP (Model Context Protocol) server for tool integration with AI agents.

Role: Chant is an MCP server, not client. Agents connect to Chant for tools.

Setup

The easiest way to configure MCP is through the interactive wizard:

chant init

When you select Claude agent configuration, the wizard automatically creates .mcp.json:

{
  "mcpServers": {
    "chant": {
      "type": "stdio",
      "command": "chant",
      "args": ["mcp"]
    }
  }
}

For direct setup: chant init --agent claude

Usage

# Start MCP server (reads from stdin, writes to stdout)
chant mcp

The server reads JSON-RPC 2.0 requests from stdin and writes responses to stdout.

Testing Manually

# List available tools
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | chant mcp

# Initialize the server
echo '{"jsonrpc":"2.0","method":"initialize","id":1}' | chant mcp

# List specs
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"chant_spec_list","arguments":{}},"id":1}' | chant mcp

# Get a specific spec (partial ID match)
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"chant_spec_get","arguments":{"id":"001"}},"id":1}' | chant mcp

Why MCP

MCP provides a standardized way to expose tools to AI agents.

  • Some providers require MCP - their only tool interface
  • Others benefit from MCP - structured tool access vs text parsing
  • Some use native formats - their own tool schemas

Architecture

┌─────────────────────────────────────────────────────────────┐
│                        AI Agent                             │
│                                                             │
│   Discovers tools via MCP protocol                          │
└─────────────────────────────────────────────────────────────┘
                              │
                              │ MCP (stdio JSON-RPC)
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                        chant mcp                            │
│                                                             │
│   Exposes chant operations as MCP tools                     │
└─────────────────────────────────────────────────────────────┘
                              │
                              │ Internal
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                        Chant Core                           │
│                                                             │
│   Specs, state machine, git operations                      │
└─────────────────────────────────────────────────────────────┘

Tools

The MCP server exposes 23 tools organized into query (read-only) and mutating categories.

Query Tools (read-only)

ToolDescriptionParameters
chant_spec_listList all specsstatus, limit (optional, default 50)
chant_spec_getGet spec details including body contentid (required, partial match supported)
chant_readyList specs ready to be worked (no unmet dependencies)limit (optional, default 50)
chant_statusGet project status summary with spec countsbrief, include_activity (optional)
chant_logRead execution log for a specid (required), lines (optional, default: 100), offset (optional), since (optional, ISO timestamp)
chant_searchSearch specs by title and body contentquery (required), status (optional)
chant_diagnoseDiagnose issues with a specid (required)
chant_lintLint specs for quality issuesid (optional, lints all if not provided)
chant_verifyVerify a spec meets its acceptance criteriaid (required)

Mutating Tools

ToolDescriptionParameters
chant_spec_updateUpdate spec status/outputid (required), status, output (optional)
chant_addCreate a new specdescription (required), prompt (optional)
chant_finalizeMark a spec as completedid (required)
chant_resetReset a failed spec to pendingid (required)
chant_cancelCancel a specid (required)
chant_archiveMove a completed spec to archiveid (required)
chant_work_startStart working on a spec asynchronouslyid (required), chain, parallel, skip_criteria (optional)
chant_work_listList running work processesprocess_id (optional), include_completed (optional)
chant_pausePause a running work process for a specid (required)
chant_takeoverTake over a running spec, stopping the agent and analyzing progressid (required), force (optional)
chant_watch_statusGet watch status and active worktrees(none)
chant_watch_startStart watch in background if not running(none)
chant_watch_stopStop running watch process(none)
chant_splitSplit a complex spec into smaller member specs using AI analysisid (required), force, recursive, max_depth (optional)

chant_spec_list

List all chant specs in the current project.

Parameters:

  • status (optional): Filter by status - pending, in_progress, completed, failed
  • limit (optional): Maximum number of specs to return (default: 50)

Response includes:

  • specs: Array of spec objects
  • total: Total count of matching specs (before limit applied)
  • limit: The limit that was applied
  • returned: Number of specs actually returned

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_spec_list",
    "arguments": {
      "status": "in_progress"
    }
  },
  "id": 1
}

Example Response:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "[\n  {\n    \"id\": \"2026-01-22-001-x7m\",\n    \"title\": \"Add user authentication\",\n    \"status\": \"in_progress\",\n    \"type\": \"feature\"\n  }\n]"
      }
    ]
  },
  "id": 1
}

chant_spec_get

Get details of a specific chant spec.

Parameters:

  • id (required): Spec ID (full or partial match)

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_spec_get",
    "arguments": {
      "id": "001"
    }
  },
  "id": 1
}

Example Response:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"id\": \"2026-01-22-001-x7m\",\n  \"title\": \"Add user authentication\",\n  \"status\": \"in_progress\",\n  \"type\": \"feature\",\n  \"body\": \"## Description\\n\\nImplement user auth...\"\n}"
      }
    ]
  },
  "id": 1
}

chant_ready

List all specs that are ready to be worked (no unmet dependencies).

Parameters:

  • limit (optional): Maximum number of specs to return (default: 50)

Response includes:

  • specs: Array of ready spec objects
  • total: Total count of ready specs (before limit applied)
  • limit: The limit that was applied
  • returned: Number of specs actually returned

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_ready",
    "arguments": {
      "limit": 10
    }
  },
  "id": 1
}

chant_status

Get project status summary with spec counts.

Parameters:

  • brief (optional, boolean): Return brief single-line output instead of full JSON
  • include_activity (optional, boolean): Include activity timestamps for in_progress specs

Example Request (default):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_status",
    "arguments": {}
  },
  "id": 1
}

Example Response (default):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"total\": 20,\n  \"pending\": 3,\n  \"in_progress\": 2,\n  \"completed\": 15,\n  \"failed\": 0,\n  \"blocked\": 0,\n  \"cancelled\": 0,\n  \"needs_attention\": 0\n}"
      }
    ]
  },
  "id": 1
}

Example Request (brief mode):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_status",
    "arguments": {
      "brief": true
    }
  },
  "id": 1
}

Example Response (brief mode):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "3 pending | 2 in_progress | 15 completed"
      }
    ]
  },
  "id": 1
}

Example Request (with activity):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_status",
    "arguments": {
      "include_activity": true
    }
  },
  "id": 1
}

Example Response (with activity):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"total\": 20,\n  \"pending\": 3,\n  \"in_progress\": 2,\n  ...\n  \"in_progress_activity\": [\n    {\n      \"id\": \"2026-01-22-001-x7m\",\n      \"title\": \"Add user auth\",\n      \"spec_modified\": \"2026-01-22 14:30:00\",\n      \"log_modified\": \"2026-01-22 14:35:00\",\n      \"has_log\": true\n    }\n  ]\n}"
      }
    ]
  },
  "id": 1
}

chant_spec_update

Update a chant spec status or append output.

Parameters:

  • id (required): Spec ID (full or partial match)
  • status (optional): New status - pending, in_progress, completed, failed
  • output (optional): Output text to append to spec body (or replace if replace_body is true)
  • replace_body (optional, boolean): Replace spec body with output instead of appending. The original title heading (# Title) is preserved automatically if the new output doesn’t include one.
  • depends_on (optional): Array of spec IDs this spec depends on
  • labels (optional): Array of labels to assign to the spec
  • target_files (optional): Array of target file paths for the spec
  • model (optional): Model name to use for the spec

Output Append Behavior

When output is provided, the text is appended to the spec body under an ## Output section. Important characteristics:

  • No timestamp: Unlike agent-driven workflow outputs, MCP appended output does not include automatic timestamps
  • No truncation: Long output strings are not automatically truncated (the caller is responsible for managing output size)
  • Section header: Output is placed under an ## Output markdown header for organization
  • Formatting: Output is appended as plain text without automatic code block wrapping

This differs from the standard append_agent_output function used in regular spec execution, which includes timestamps, truncation logic, and automatic code block formatting.

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_spec_update",
    "arguments": {
      "id": "001",
      "status": "completed",
      "output": "Implementation complete. All tests passing."
    }
  },
  "id": 1
}

Example Response:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Updated spec: 2026-01-22-001-x7m"
      }
    ]
  },
  "id": 1
}

Example Resulting Spec Body:

# Feature Implementation

Some initial content...

## Output

Implementation complete. All tests passing.

chant_add

Create a new spec with description.

Parameters:

  • description (required): Description of work to be done (becomes spec title)
  • prompt (optional): Optional prompt template name to use

Response Format:

The response includes the spec ID and linting diagnostics (if any). Each diagnostic has:

  • severity: “error” or “warning”
  • rule: The lint rule name (e.g., “complexity”, “coupling”, “type”, “model_waste”)
  • message: Human-readable diagnostic message
  • suggestion: Optional suggestion for fixing the issue

Linting Diagnostics:

When a spec is created, it is automatically linted. Diagnostics are appended to the response text in the format:

[SEVERITY] rule_name: Message
  → Suggestion (if present)

Common lint rules:

  • complexity: Spec exceeds complexity thresholds (criteria count, file count, word count)
  • coupling: Spec references other spec IDs in body text
  • type: Invalid or missing spec type
  • model_waste: Using expensive model on simple spec
  • approval: Approval schema inconsistencies
  • output: Output schema validation issues
  • dependency: Missing or invalid dependency references
  • required: Missing required enterprise fields
  • title: Missing spec title
  • parse: YAML frontmatter parse errors

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_add",
    "arguments": {
      "description": "Add user authentication",
      "prompt": "feature"
    }
  },
  "id": 1
}

Example Response (success, no diagnostics):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Created spec: 2026-01-22-001-x7m"
      }
    ]
  },
  "id": 1
}

Example Response (with linting diagnostics):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Created spec: 2026-01-22-001-x7m\n\nLint diagnostics:\n  [WARNING] complexity: Spec has 15 acceptance criteria (threshold: 10)\n    → Consider splitting into smaller, focused specs\n  [ERROR] type: Invalid spec type 'feature'. Must be one of: code, docs, fix"
      }
    ]
  },
  "id": 1
}

chant_log

Read execution log for a spec.

Parameters:

  • id (required): Spec ID (full or partial match)
  • lines (optional): Number of lines to return (default: 100)
  • offset (optional): Start from byte offset (for incremental reads)
  • since (optional): ISO timestamp - only return lines after this time

Response format:

{
  "content": "log content...",
  "byte_offset": 15234,
  "line_count": 50,
  "has_more": true
}

Polling pattern for incremental reads:

  1. First call: chant_log(id) → returns content + byte_offset
  2. Subsequent calls: chant_log(id, offset=15234) → only new content since that offset

Example Request (basic):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_log",
    "arguments": {
      "id": "001"
    }
  },
  "id": 1
}

Example Response:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"content\": \"Log line 1\\nLog line 2\\n...\",\n  \"byte_offset\": 15234,\n  \"line_count\": 100,\n  \"has_more\": false\n}"
      }
    ]
  },
  "id": 1
}

Example Request (incremental polling):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_log",
    "arguments": {
      "id": "001",
      "offset": 15234
    }
  },
  "id": 2
}

Example Request (filter by timestamp):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_log",
    "arguments": {
      "id": "001",
      "since": "2026-02-02T10:00:00Z"
    }
  },
  "id": 3
}

chant_verify

Verify a spec meets its acceptance criteria.

Parameters:

  • id (required): Spec ID (full or partial match)

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_verify",
    "arguments": {
      "id": "001"
    }
  },
  "id": 1
}

Example Response (success):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"spec_id\": \"2026-02-02-001-xyz\",\n  \"verified\": true,\n  \"criteria\": {\n    \"total\": 5,\n    \"checked\": 5,\n    \"unchecked\": 0\n  },\n  \"unchecked_items\": [],\n  \"verification_notes\": \"All acceptance criteria met\"\n}"
      }
    ]
  },
  "id": 1
}

Example Response (failure):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"spec_id\": \"2026-02-02-001-xyz\",\n  \"verified\": false,\n  \"criteria\": {\n    \"total\": 5,\n    \"checked\": 3,\n    \"unchecked\": 2\n  },\n  \"unchecked_items\": [\n    \"- [ ] Tests added\",\n    \"- [ ] Documentation updated\"\n  ],\n  \"verification_notes\": \"2 criteria not yet checked\"\n}"
      }
    ]
  },
  "id": 1
}

chant_lint

Lint specs to check for quality issues (complexity, missing criteria, etc.).

Parameters:

  • id (optional): Spec ID to lint. If not provided, lints all specs.

Example Request (single spec):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_lint",
    "arguments": {
      "id": "001"
    }
  },
  "id": 1
}

Example Request (all specs):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_lint",
    "arguments": {}
  },
  "id": 1
}

chant_split

Split a complex spec into smaller member specs using AI analysis.

Parameters:

  • id (required): Spec ID (full or partial match)
  • force (optional, boolean): Skip confirmation prompts
  • recursive (optional, boolean): Recursively split member specs that are still too complex
  • max_depth (optional, integer): Maximum recursion depth (default: 3)

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_split",
    "arguments": {
      "id": "001",
      "force": true
    }
  },
  "id": 1
}

chant_work_list

List running work processes.

Parameters:

  • process_id (optional): Filter to specific process
  • include_completed (optional, boolean): Include recently completed processes

Response format:

{
  "processes": [
    {
      "process_id": "2026-02-02-001-xyz-12345",
      "spec_id": "2026-02-02-001-xyz",
      "pid": 12345,
      "status": "running|completed|failed",
      "started_at": "2026-02-02T10:30:00Z",
      "completed_at": null,
      "mode": "single"
    }
  ],
  "summary": {
    "running": 2,
    "completed": 5,
    "failed": 0
  }
}

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_work_list",
    "arguments": {}
  },
  "id": 1
}

Example Response:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"processes\": [...],\n  \"summary\": {\n    \"running\": 2,\n    \"completed\": 5,\n    \"failed\": 0\n  }\n}"
      }
    ]
  },
  "id": 1
}

Example Request (with filter):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_work_list",
    "arguments": {
      "process_id": "001",
      "include_completed": true
    }
  },
  "id": 2
}

chant_work_start

Start working on a spec asynchronously (spawns background process and returns immediately).

Parameters:

  • id (required): Spec ID (full or partial match)
  • chain (optional, boolean): After completing the specified spec, continue discovering and executing newly-ready specs unblocked by completions
  • parallel (optional, integer): Number of parallel workers (requires multiple ready specs)
  • skip_criteria (optional, boolean): Skip acceptance criteria validation before starting work

Response format:

{
  "process_id": "2026-02-02-001-xyz-12345",
  "spec_id": "2026-02-02-001-xyz",
  "pid": 12345,
  "started_at": "2026-02-02T10:30:00Z",
  "mode": "single|chain|parallel(N)"
}

Process tracking:

  • Process info is stored in .chant/processes/<process_id>.json
  • Use chant_log with offset or since parameters to monitor progress
  • Use chant_status with include_activity to see if the spec is being worked

Example Request (single spec):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_work_start",
    "arguments": {
      "id": "001"
    }
  },
  "id": 1
}

Example Response:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"process_id\": \"2026-02-02-001-xyz-12345\",\n  \"spec_id\": \"2026-02-02-001-xyz\",\n  \"pid\": 12345,\n  \"started_at\": \"2026-02-02T10:30:00Z\",\n  \"mode\": \"single\"\n}"
      }
    ]
  },
  "id": 1
}

Example Request (chain mode):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_work_start",
    "arguments": {
      "id": "001",
      "chain": true
    }
  },
  "id": 2
}

Example Request (parallel mode):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_work_start",
    "arguments": {
      "id": "001",
      "parallel": 3
    }
  },
  "id": 3
}

chant_pause

Pause a running work process for a spec.

Parameters:

  • id (required): Spec ID (full or partial match)

Behavior:

  • Stops the running agent process for the spec
  • Updates the spec status back to in_progress (preserving work)
  • Allows manual intervention or corrections before resuming
  • Does not fail the spec - it remains ready for continuation

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_pause",
    "arguments": {
      "id": "001"
    }
  },
  "id": 1
}

Example Response (success):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Successfully paused work for spec '2026-02-02-001-xyz'"
      }
    ]
  },
  "id": 1
}

Example Response (error - no process running):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Failed to pause work for spec '2026-02-02-001-xyz': No running process found"
      }
    ],
    "isError": true
  },
  "id": 1
}

chant_takeover

Take over a running spec, stopping the agent and analyzing progress.

Parameters:

  • id (required): Spec ID (full or partial match)
  • force (optional, boolean): Force takeover even if no active process detected (default: false)

Behavior:

  • Stops the running agent process (if any)
  • Analyzes the spec’s current state and execution log
  • Provides a summary of progress and recommendations
  • Returns structured analysis including:
    • Current state of the spec
    • Recent log activity (tail)
    • Suggested next steps

Response format:

{
  "spec_id": "2026-02-02-001-xyz",
  "analysis": "Agent was implementing feature X. Made progress on 3/5 acceptance criteria.",
  "log_tail": "Last 20 lines of execution log...",
  "suggestion": "Continue by completing the remaining 2 acceptance criteria.",
  "worktree_path": "/tmp/chant-2026-02-02-001-xyz"
}

The worktree_path field contains the path to the spec’s worktree directory. After takeover, use this path as your working directory to continue the agent’s work. If the worktree no longer exists, the field will be null.

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_takeover",
    "arguments": {
      "id": "001"
    }
  },
  "id": 1
}

Example Response (success):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n  \"spec_id\": \"2026-02-02-001-xyz\",\n  \"analysis\": \"Agent was implementing authentication feature. Completed user model and login endpoint.\",\n  \"log_tail\": \"[2026-02-02 14:30:00] Created UserModel\\n[2026-02-02 14:32:15] Implemented login endpoint\\n[2026-02-02 14:35:00] Running tests...\",\n  \"suggestion\": \"Complete remaining acceptance criteria: session management and logout endpoint.\"\n}"
      }
    ]
  },
  "id": 1
}

Example Request (with force):

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_takeover",
    "arguments": {
      "id": "001",
      "force": true
    }
  },
  "id": 2
}

Example Response (error):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Failed to take over spec '2026-02-02-001-xyz': Spec not currently being worked"
      }
    ],
    "isError": true
  },
  "id": 1
}

chant_watch_status

Get watch status and list active worktrees with their agent status.

Parameters: None

Response format:

{
  "watch_running": true,
  "worktrees": [
    {
      "spec_id": "2026-02-02-001-xyz",
      "path": "/tmp/chant-2026-02-02-001-xyz",
      "status": "working",
      "updated_at": "2026-02-02T10:30:00Z",
      "error": null,
      "commits": []
    }
  ],
  "worktree_count": 1
}

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_watch_status",
    "arguments": {}
  },
  "id": 1
}

Worktree Status Values:

  • working - Agent is actively working on the spec
  • done - Agent completed successfully, awaiting watch to merge and finalize
  • failed - Agent encountered an error
  • unknown - No status file found (agent may not have started yet)

chant_watch_start

Start watch in background if not already running.

Parameters: None

Behavior:

  • Checks if watch is already running via PID file
  • If not running, spawns chant watch as a background process
  • Returns immediately with the new process PID

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_watch_start",
    "arguments": {}
  },
  "id": 1
}

Example Response (success):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Started watch process (PID: 12345)"
      }
    ]
  },
  "id": 1
}

Example Response (already running):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Watch is already running"
      }
    ],
    "isError": true
  },
  "id": 1
}

chant_watch_stop

Stop a running watch process.

Parameters: None

Behavior:

  • Reads PID from .chant/watch.pid
  • Sends SIGTERM to the watch process (Unix) or uses taskkill (Windows)
  • Watch process handles graceful shutdown (removes PID file, cleans up)

Example Request:

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "chant_watch_stop",
    "arguments": {}
  },
  "id": 1
}

Example Response (success):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Sent stop signal to watch process (PID: 12345)"
      }
    ]
  },
  "id": 1
}

Example Response (not running):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Watch is not running"
      }
    ],
    "isError": true
  },
  "id": 1
}

Tool Schemas

Full JSON schemas as returned by tools/list. Only showing key tools; run echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | chant mcp for the complete list.

{
  "tools": [
    {
      "name": "chant_spec_list",
      "description": "List all chant specs in the current project",
      "inputSchema": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Filter by status (pending, in_progress, completed, failed, ready, blocked)"
          },
          "limit": {
            "type": "integer",
            "description": "Maximum number of specs to return (default: 50)"
          }
        }
      }
    },
    {
      "name": "chant_spec_get",
      "description": "Get details of a chant spec including full body content",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_ready",
      "description": "List all specs that are ready to be worked (no unmet dependencies)",
      "inputSchema": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer",
            "description": "Maximum number of specs to return (default: 50)"
          }
        }
      }
    },
    {
      "name": "chant_status",
      "description": "Get project status summary with spec counts by status",
      "inputSchema": {
        "type": "object",
        "properties": {
          "brief": {
            "type": "boolean",
            "description": "Return brief single-line output (e.g., '3 pending | 2 in_progress | 15 completed')"
          },
          "include_activity": {
            "type": "boolean",
            "description": "Include activity info for in_progress specs (last modified time, log activity)"
          }
        }
      }
    },
    {
      "name": "chant_log",
      "description": "Read execution log for a spec",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          },
          "lines": {
            "type": "integer",
            "description": "Number of lines to return (default: 100)"
          },
          "offset": {
            "type": "integer",
            "description": "Start from byte offset (for incremental reads)"
          },
          "since": {
            "type": "string",
            "description": "ISO timestamp - only lines after this time"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_search",
      "description": "Search specs by title and body content",
      "inputSchema": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "Search query (case-insensitive substring match)"
          },
          "status": {
            "type": "string",
            "description": "Filter by status"
          }
        },
        "required": ["query"]
      }
    },
    {
      "name": "chant_diagnose",
      "description": "Diagnose issues with a spec (check file, log, locks, commits, criteria)",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_spec_update",
      "description": "Update a chant spec status, frontmatter fields, or add output",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          },
          "status": {
            "type": "string",
            "description": "New status (pending, in_progress, completed, failed)"
          },
          "output": {
            "type": "string",
            "description": "Output text to append to spec body"
          },
          "depends_on": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Spec IDs this spec depends on"
          },
          "labels": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Labels to assign to the spec"
          },
          "target_files": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Target file paths for the spec"
          },
          "model": {
            "type": "string",
            "description": "Model name to use for the spec"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_add",
      "description": "Create a new spec with description",
      "inputSchema": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "description": "Description of work to be done (becomes spec title)"
          },
          "prompt": {
            "type": "string",
            "description": "Optional prompt template name to use"
          }
        },
        "required": ["description"]
      }
    },
    {
      "name": "chant_finalize",
      "description": "Mark a spec as completed (validates all criteria are checked)",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_reset",
      "description": "Reset a failed spec to pending status so it can be reworked",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_cancel",
      "description": "Cancel a spec (sets status to cancelled)",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_archive",
      "description": "Move a completed spec to the archive directory",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_verify",
      "description": "Verify a spec meets its acceptance criteria",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_work_start",
      "description": "Start working on a spec asynchronously (returns immediately)",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          },
          "chain": {
            "type": "boolean",
            "description": "Continue to next ready spec after completion"
          },
          "parallel": {
            "type": "integer",
            "description": "Number of parallel workers (requires multiple ready specs)"
          },
          "skip_criteria": {
            "type": "boolean",
            "description": "Skip acceptance criteria validation"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_pause",
      "description": "Pause a running work process for a spec",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_takeover",
      "description": "Take over a running spec, stopping the agent and analyzing progress",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          },
          "force": {
            "type": "boolean",
            "description": "Force takeover even if no active process detected (default: false)"
          }
        },
        "required": ["id"]
      }
    },
    {
      "name": "chant_watch_status",
      "description": "Get watch status and active worktrees",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "chant_watch_start",
      "description": "Start watch in background if not running",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "chant_watch_stop",
      "description": "Stop running watch process",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "chant_lint",
      "description": "Lint specs to check for quality issues (complexity, missing criteria, etc.)",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID to lint (optional, lints all if not provided)"
          }
        }
      }
    },
    {
      "name": "chant_split",
      "description": "Split a complex spec into smaller member specs using AI analysis",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Spec ID (full or partial)"
          },
          "force": {
            "type": "boolean",
            "description": "Skip confirmation prompts"
          },
          "recursive": {
            "type": "boolean",
            "description": "Recursively split member specs that are still too complex"
          },
          "max_depth": {
            "type": "integer",
            "description": "Maximum recursion depth (default: 3)"
          }
        },
        "required": ["id"]
      }
    }
  ]
}

Protocol

Standard MCP over stdio:

  • JSON-RPC 2.0
  • Tools advertised via tools/list
  • Tool calls via tools/call
  • Server info via initialize

Initialize

{
  "jsonrpc": "2.0",
  "method": "initialize",
  "id": 1
}

Response:

{
  "jsonrpc": "2.0",
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "tools": {}
    },
    "serverInfo": {
      "name": "chant",
      "version": "0.1.0"
    }
  },
  "id": 1
}

Error Codes and Response Structures

JSON-RPC 2.0 Error Codes

The chant MCP server uses standard JSON-RPC 2.0 error codes for protocol-level errors:

CodeMessageDescriptionWhen It Occurs
-32700Parse errorRequest JSON is malformed or not valid JSONInvalid JSON sent to stdin
-32600Invalid JSON-RPC versionRequest has jsonrpc field != “2.0”Version mismatch in request
-32603Server errorInternal server error during tool executionTool function throws an exception or returns Err

Error Response Structure

All error responses follow the JSON-RPC 2.0 error format:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32603,
    "message": "Error description",
    "data": null
  },
  "id": <request-id>
}

Fields:

  • jsonrpc: Always "2.0"
  • error.code: Integer error code
  • error.message: Human-readable error message
  • error.data: Optional additional error context (currently unused)
  • id: Echo of the request ID

Tool-Level Error Responses

Tools return structured error responses as MCP tool results (not JSON-RPC errors). Tool errors are wrapped in content objects:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Error description"
      }
    ],
    "isError": true
  },
  "id": <request-id>
}

Common Tool-Level Errors:

ErrorConditionTool(s)
“Chant not initialized”.chant/specs directory doesn’t existAll tools
“Missing required parameter: id”id parameter not providedchant_spec_get, chant_spec_update
“Missing required parameter: name”name parameter not providedtools/call
“Missing tool name”Tool name is not a string or missingtools/call
“Missing arguments”arguments not provided to tools/calltools/call
“Method not found”Unknown method requestedProtocol level
“Unknown tool”Tool name doesn’t match available toolstools/call
“Invalid status”Status string not in [pending, in_progress, completed, failed]chant_spec_update
“No updates specified”Neither status nor output parameter providedchant_spec_update

Success Response Structure

Successful tool results use this format:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Success message or data"
      }
    ]
  },
  "id": <request-id>
}

Fields:

  • jsonrpc: Always "2.0"
  • result.content: Array of content objects
  • content[].type: Currently always "text"
  • content[].text: The response data as formatted text
  • id: Echo of the request ID

Notifications (No Response)

Requests without an id field are notifications and receive no response:

{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

Currently supported notifications:

  • notifications/initialized: Client notifies server it’s ready (no action taken)

Response Content Types

The content[].type field in responses can be:

  • "text": Plain text or JSON-formatted data (current implementation)
  • Future: "tool_result", "resource" (per MCP spec)

Error Handling Best Practices

  1. Check jsonrpc and error fields: Distinguish between protocol errors and tool errors

    • If error is present, it’s a protocol-level error
    • If result contains isError: true, it’s a tool-level error
  2. Handle missing initialization: Always check for “Chant not initialized” before using tools

  3. Validate parameters: Tools will return descriptive errors for missing/invalid parameters

  4. Parse tool output: Tool responses have JSON in the text field - parse it accordingly

Example Error Scenarios

Scenario 1: Parse Error

echo 'invalid json' | chant mcp

Response:

{
  "jsonrpc": "2.0",
  "error": {
    "code": -32700,
    "message": "Parse error: expected value at line 1 column 1"
  },
  "id": null
}

Scenario 2: Missing Required Parameter

echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"chant_spec_get","arguments":{}},"id":1}' | chant mcp

Response:

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Missing required parameter: id"
      }
    ],
    "isError": true
  },
  "id": 1
}

Scenario 3: Chant Not Initialized

echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"chant_spec_list","arguments":{}},"id":1}' | chant mcp

Response (when .chant/specs doesn’t exist):

{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Chant not initialized. Run `chant init` first."
      }
    ],
    "isError": true
  },
  "id": 1
}

Provider Integration

Chant generates provider-specific MCP config files before invocation.

Invocation Flow

1. chant work <id>
2. Chant writes MCP config files
3. Chant invokes provider CLI
4. Agent connects to chant mcp for tools
5. Agent executes, uses chant tools
6. Chant captures output, updates spec

Benefits with MCP

  • Structured spec data (no markdown parsing)
  • Direct status updates via tool calls
  • Better error handling

Security

  • MCP server runs locally (no network exposure)
  • Inherits filesystem permissions from parent process
  • Spec access limited to current project
  • No credential exposure via MCP

Versioning

What Gets Versioned

ComponentWherePurpose
Chant CLIBinaryFeature compatibility
Config schemaconfig.mdConfiguration format
Spec schemaValidated by linterSpec file format

Config Version

# config.md frontmatter
---
version: 1                    # Schema version

project:
  name: my-app
# ...
---

CLI checks version on load:

  • Same version → proceed
  • Older version → warn, suggest migration
  • Newer version → error, update CLI

Spec Schema Version

Specs don’t have explicit version. Schema is defined in config:

# config.md
schema:
  version: 1
  spec:
    required: [status]
    status:
      enum: [pending, in_progress, completed, failed]

Linter validates against config’s schema definition.

When Linter Runs

TriggerAutomaticPurpose
chant lintNoExplicit validation
chant workYesPre-execution check
chant addYesValidate new spec
After agent writesYesValidate output

Before Execution

#![allow(unused)]
fn main() {
fn work(spec_id: &str) -> Result<()> {
    // Lint before starting
    let errors = lint_spec(spec_id)?;
    if !errors.is_empty() {
        return Err(Error::LintFailed(errors));
    }

    // Proceed with execution
    execute(spec_id)
}
}

After Agent Writes

Agent may create/modify specs. Validate after:

#![allow(unused)]
fn main() {
fn post_execution_lint(spec_id: &str) -> Result<()> {
    // Find all specs modified by agent
    let modified = git_diff_specs()?;

    for spec in modified {
        let errors = lint_spec(&spec)?;
        if !errors.is_empty() {
            warn!("Agent produced invalid spec: {}", spec);
            // Auto-fix if possible
            if let Err(e) = auto_fix(&spec) {
                return Err(Error::LintFailed(errors));
            }
        }
    }

    Ok(())
}
}

Schema Migration

When schema changes between versions:

$ chant lint
Warning: Config schema version 1, current is 2

Migration available:
  - 'status: open' → 'status: pending' (v2 renamed)

Run 'chant migrate' to update.

Migration Command

chant migrate              # Dry run, show changes
chant migrate --apply      # Apply changes
$ chant migrate
Schema migration v1 → v2

Changes:
  config.md:
    - Add 'version: 2'

  2026-01-22-001-x7m.md:
    - 'status: open' → 'status: pending'

  2026-01-22-002-q2n.md:
    - No changes needed

Run 'chant migrate --apply' to apply.

Backwards Compatibility

Reading Old Specs

CLI should read specs from older schema versions:

#![allow(unused)]
fn main() {
fn parse_spec(content: &str) -> Result<Spec> {
    let raw: RawSpec = parse_frontmatter(content)?;

    // Handle old field names
    let status = raw.status
        .or(raw.state)           // v0 used 'state'
        .unwrap_or("pending");

    // Normalize old values
    let status = match status {
        "open" => "pending",     // v1 used 'open'
        "done" => "completed",   // v1 used 'done'
        s => s,
    };

    Ok(Spec { status, ... })
}
}

Writing Current Version

Always write current schema:

#![allow(unused)]
fn main() {
fn save_spec(spec: &Spec) -> Result<()> {
    // Always use current field names
    let frontmatter = format!(
        "status: {}\n",
        spec.status  // Not 'state', not 'open'
    );
    // ...
}
}

Version History

VersionChanges
1Initial schema

CLI Version Check

$ chant version
chant 2.0.0
config schema: 1
rust: 1.75.0

Deprecation Policy

When Features Are Deprecated

Features may be deprecated when:

  • Better alternatives exist
  • Security or stability concerns arise
  • Usage data shows minimal adoption
  • Maintenance burden outweighs value

Deprecation Process

  1. Announcement: Deprecation notice in release notes and docs
  2. Warning period: CLI shows warnings when deprecated features are used
  3. Removal: Feature removed in next major version

Minimum Support Period

ComponentSupport Period
Config fields2 major versions
CLI commands2 major versions
Spec schema fields2 major versions

Example timeline:

v2.0: Feature deprecated, warnings shown
v3.0: Feature still works, warnings shown
v4.0: Feature removed

Deprecation Warnings

When using deprecated features:

$ chant work 001
Warning: 'state' field is deprecated, use 'status' instead
Will be removed in v4.0

Config check on load:

$ chant list --summary
Warning: Deprecated config fields detected:
  - 'workers' → use 'parallelism'

Run 'chant migrate' to update.

Migration Path

All deprecations include:

  • Clear warning messages
  • Documentation of replacement
  • Automated migration via chant migrate
  • Version where removal occurs

Output & Progress

Live Markdown Updates

During execution, the spec file updates in real-time:

# 2026-01-22-001-x7m.md
---
status: in_progress
started_at: 2026-01-22T15:30:00Z
progress: |
  [15:30:01] Reading src/auth/middleware.go
  [15:30:03] Found 3 relevant files
  [15:30:05] Planning approach...
  [15:30:15] Implementing JWT validation
  [15:30:45] Running tests...
---

# Add authentication

...

Why Markdown?

Consistent with chant’s philosophy: markdown IS the UI.

  • Watch with tail -f .chant/specs/2026-01-22-001-x7m.md
  • View in any editor with auto-reload
  • Git diff shows exactly what happened
  • No separate log files

Progress Field

The progress field is a multi-line string:

progress: |
  [HH:MM:SS] Message
  [HH:MM:SS] Message
  ...

Appended to as work proceeds. Cleared on completion.

Terminal Output

chant work also streams to terminal:

$ chant work 2026-01-22-001-x7m
[15:30:01] Reading src/auth/middleware.go
[15:30:03] Found 3 relevant files
[15:30:05] Planning approach...
[15:30:15] Implementing JWT validation
[15:30:45] Running tests...
[15:31:02] ✓ Complete

Commit: a1b2c3d4

Both terminal and file get same updates.

Watch Command

For background execution:

chant work 2026-01-22-001-x7m --background
chant watch 2026-01-22-001-x7m   # Stream progress

Or watch any spec:

chant watch 2026-01-22-001-x7m
# Streams progress field updates until completion

Completion

On success, progress is moved to a log field:

---
status: completed
completed_at: 2026-01-22T15:31:02Z
commit: a1b2c3d4
log: |
  [15:30:01] Reading src/auth/middleware.go
  [15:30:03] Found 3 relevant files
  [15:30:05] Planning approach...
  [15:30:15] Implementing JWT validation
  [15:30:45] Running tests...
  [15:31:02] Complete
---

progresslog rename signals completion.

Failure

On failure:

---
status: failed
failed_at: 2026-01-22T15:31:02Z
error: "Test suite failed: 2 assertions"
log: |
  [15:30:01] Reading src/auth/middleware.go
  [15:30:15] Implementing JWT validation
  [15:30:45] Running tests...
  [15:31:02] ERROR: Test suite failed
---

error field captures the failure reason.

Quiet Mode

For scripting:

chant --quiet work 2026-01-22-001-x7m
chant -q work 2026-01-22-001-x7m
# No terminal output, exit code only
# Spec file still updated

The --quiet / -q flag is a global flag that can be used with any command to suppress non-essential output.

Verbosity

chant work 2026-01-22-001-x7m           # Normal
chant work 2026-01-22-001-x7m -v        # Verbose (more detail)
chant work 2026-01-22-001-x7m -vv       # Debug (agent prompts visible)

Progress Bars

Chain Execution

chant work --chain displays a progress bar tracking completion across the chain:

$ chant work --chain

→ Starting chain execution (5 specs)...

⠋ [=====>---------------------------------] 2/5 Working on 2026-01-22-002-x7n

The progress bar shows:

  • Current position / total specs
  • Current spec being worked on
  • Visual progress indicator

Progress updates as each spec completes, providing real-time visibility into chain execution.

Parallel Execution

chant work --parallel shows a multi-progress display with:

  • Overall completion across all specs
  • Individual progress bars for each worker (optional, when verbose)
$ chant work --parallel 3

→ Starting parallel execution...
  • agent-1: 3 specs
  • agent-2: 2 specs
  • agent-3: 2 specs

⠋ [===============>----------------------] 4/7 specs completed

The main progress bar tracks overall completion, updating as workers finish their assigned specs.

Display Details

Progress bars use the indicatif library with:

  • Spinner animation (⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏)
  • Bar width: 40 characters
  • Progress chars: =>
  • Colors: cyan/blue bar, green spinner

Progress bars are automatically hidden in:

  • --quiet mode
  • Non-TTY environments
  • --json output mode

Structured Output

For tooling:

chant work 2026-01-22-001-x7m --json
{"time": "15:30:01", "event": "read", "file": "src/auth/middleware.go"}
{"time": "15:30:15", "event": "change", "file": "src/auth/middleware.go"}
{"time": "15:31:02", "event": "complete", "commit": "a1b2c3d4"}

Testing Strategy

Approach

Design integration tests first. Unit tests emerge from implementation. Tests exercise the complete system.

See philosophy for chant’s broader design principles.

Test Hierarchy

Integration Tests (high-level, design first)
    ↓
Component Tests (boundaries)
    ↓
Unit Tests (implementation details)

Bootstrap Testing

Chant builds itself. Each phase has gate tests.

Phase Gate Example

#![allow(unused)]
fn main() {
#[test]
fn test_phase0_self_hosting() {
    let repo = ChantRepo::open(".");
    let spec_id = repo.add_spec("Add test comment to main.go");

    repo.work_with_mock(&spec_id, |files| {
        files.append("cmd/main.go", "// test comment");
    });

    let spec = repo.read_spec(&spec_id);
    assert_eq!(spec.status, "completed");
    assert!(repo.file_contains("cmd/main.go", "// test comment"));

    repo.revert_last_commit();
}
}

Phase Gates Summary

PhaseKey Tests
0 → 1Self-hosting: chant executes spec on itself
1 → 2Branching, isolation
2 → 3MCP server tools, provider config
3 → 4Cross-repo dependencies
4 → 5Labels, triggers, spec types, groups
5 → 6Cost tracking, linting, error catalog, reports
6 → 7Search, locks, queue, pools, daemon
7 → 8Drift detection, replay, verification
8 → ✓Approvals, templates

Integration Tests

End-to-end tests that exercise complete workflows.

Core Workflow

#![allow(unused)]
fn main() {
#[test]
fn test_basic_workflow() {
    let repo = TempRepo::new();
    repo.run("chant init").success();

    let output = repo.run("chant add 'Fix bug'").success();
    let spec_id = output.spec_id();

    repo.run(&format!("chant work {} --agent mock", spec_id)).success();

    let spec = repo.read_spec(&spec_id);
    assert_eq!(spec.status, "completed");
}
}

Parallel Execution

#![allow(unused)]
fn main() {
#[test]
fn test_parallel_members() {
    let repo = TempRepo::new();
    repo.init();

    let driver = repo.add_spec("Driver spec");
    repo.split(&driver);

    repo.run(&format!("chant work {} --parallel --max 3", driver)).success();

    for member in repo.members(&driver) {
        assert_eq!(member.status, "completed");
    }
    assert_eq!(repo.read_spec(&driver).status, "completed");
}
}

Dependency Chain

#![allow(unused)]
fn main() {
#[test]
fn test_dependency_chain() {
    let repo = TempRepo::new();
    repo.init();

    let spec_a = repo.add_spec("Spec A");
    let spec_b = repo.add_spec_with_dep("Spec B", &spec_a);
    let spec_c = repo.add_spec_with_dep("Spec C", &spec_b);

    assert!(repo.is_blocked(&spec_c));

    repo.work(&spec_a);
    assert!(repo.is_ready(&spec_b));
    assert!(repo.is_blocked(&spec_c));

    repo.work(&spec_b);
    repo.work(&spec_c);
    assert_eq!(repo.read_spec(&spec_c).status, "completed");
}
}

Lock Contention

#![allow(unused)]
fn main() {
#[test]
fn test_lock_contention() {
    let repo = TempRepo::new();
    repo.init();

    let spec = repo.add_spec("Contested spec");
    let handle1 = repo.work_async(&spec);
    thread::sleep(Duration::from_millis(100));

    let result = repo.run(&format!("chant work {}", spec));
    assert!(result.stderr.contains("locked"));

    handle1.wait();
}
}

Error Recovery

#![allow(unused)]
fn main() {
#[test]
fn test_crash_recovery() {
    let repo = TempRepo::new();
    repo.init();

    let spec = repo.add_spec("Crash test");
    repo.create_stale_lock(&spec, 99999);

    assert!(repo.is_locked(&spec));
    repo.run(&format!("chant unlock {}", spec)).success();
    repo.work(&spec);
}
}

Component Tests

Parser Tests

#![allow(unused)]
fn main() {
#[test]
fn test_spec_parser() {
    let content = r#"---
status: pending
labels: [urgent, bug]
---

Fix authentication

Login fails on Safari.
"#;

    let spec = parse_spec(content).unwrap();
    assert_eq!(spec.status, "pending");
    assert_eq!(spec.labels, vec!["urgent", "bug"]);
}
}

Search Index Tests

#![allow(unused)]
fn main() {
#[test]
fn test_search_index() {
    let index = TantivyIndex::new_temp();

    index.add(Spec { id: "001", status: "pending", body: "Fix authentication bug" });

    assert_eq!(index.search("status:pending").len(), 1);
    assert_eq!(index.search("authentication").len(), 1);
    assert_eq!(index.search("payment").len(), 0);
}
}

Test Fixtures

Mock Agent

#![allow(unused)]
fn main() {
struct MockAgent {
    should_succeed: bool,
    delay: Duration,
}

impl Agent for MockAgent {
    fn execute(&self, spec: &Spec) -> Result<()> {
        thread::sleep(self.delay);
        if self.should_succeed { Ok(()) }
        else { Err(Error::AgentFailed("Mock failure")) }
    }
}
}

Temp Repository

#![allow(unused)]
fn main() {
struct TempRepo {
    dir: TempDir,
}

impl TempRepo {
    fn new() -> Self { ... }
    fn init(&self) { ... }
    fn add_spec(&self, desc: &str) -> String { ... }
    fn work(&self, spec_id: &str) { ... }
    fn read_spec(&self, spec_id: &str) -> Spec { ... }
}
}

CI Configuration

# .github/workflows/test.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions-rs/toolchain@v1
        with:
          toolchain: stable

      - run: cargo test --lib           # Unit tests
      - run: cargo test --test integration
      - run: cargo test --test slow -- --test-threads=1

Coverage Goals

ComponentTarget
Core workflow90%
Parser95%
MCP server85%
Search80%
CLI70%

Test Categories

cargo test                           # All tests
cargo test --lib                     # Fast (unit + component)
cargo test --test integration        # Integration only
cargo test --test gates              # Phase gate tests
cargo test --test real_agent --ignored  # With real agent (slow)

Enterprise Features

Overview

Chant supports enterprise environments through:

  1. Silent mode - Personal use on shared repos
  2. Derived frontmatter - Auto-populate fields from conventions
  3. Schema enforcement - Required fields, validation

Silent Mode

For developers on projects that don’t officially use Chant:

chant init --silent
  • .chant/ added to .git/info/exclude (local only)
  • No files committed to shared repo
  • Personal AI workflow, invisible to team
  • See init.md for details

Derived Frontmatter

Enterprise teams have conventions: branch naming, path structure, ticket systems. Chant extracts metadata automatically during spec completion.

How It Works

Spec created on branch sprint/2026-Q1-W4/PROJ-123-add-auth:

# Before completion (user writes)
---
status: pending
---

# Add authentication

After completion, derived fields are auto-populated:

# After completion (auto-populated)
---
status: completed
completed_at: 2026-01-22T15:30:00Z
commit: a1b2c3d4

# Derived fields (auto-populated)
sprint: 2026-Q1-W4      [derived]
jira_key: PROJ-123      [derived]
team: platform          [derived]

derived_fields: [sprint, jira_key, team]
---

The [derived] indicator shows which fields were auto-populated. Derived field names are tracked in the derived_fields list for auditing.

Configuration

Configure derivation rules in .chant/config.md:

---
enterprise:
  derived:
    # Extract sprint from branch name
    sprint:
      from: branch
      pattern: "sprint/(\\d{4}-Q\\d-W\\d)"

    # Extract Jira key from branch name
    jira_key:
      from: branch
      pattern: "([A-Z]+-\\d+)"

    # Extract team from spec file path
    team:
      from: path
      pattern: "teams/(\\w+)/"

    # Extract component from path
    component:
      from: path
      pattern: "src/(\\w+)/"
      validate:
        type: enum
        values: [api, auth, web, mobile]
---

Derivation Sources

Chant supports 4 derivation sources:

SourceDescriptionExampleHow to Use
branchCurrent git branch namesprint/2026-Q1-W4/PROJ-123Extract from branch naming conventions
pathSpec file path.chant/specs/teams/platform/task.mdExtract from directory structure
envEnvironment variableTEAM_NAME=platformExtract from shell environment
git_userGit user.name or user.emailalice@company.comPattern must be literal "name" or "email" (not regex)

Pattern Syntax

Patterns are standard regex with capture groups. The first capture group becomes the field value:

pattern: "prefix/([^/]+)/suffix"
#                 ^^^^^^^^^^^
#            First capture group → field value

Examples:

PatternSourceValueNotes
sprint/(\d{4}-Q\d-W\d)sprint/2026-Q1-W4/task2026-Q1-W4Sprint format
([A-Z]+-\d+)PROJ-123-authPROJ-123Jira ticket
teams/(\w+)/.chant/specs/teams/platform/task.mdplatformTeam directory
(\w+)\.mddashboard.mddashboardFilename

Pattern Matching Rules:

  • If pattern doesn’t match source → field is omitted (graceful failure)
  • Invalid regex pattern → field is omitted (graceful failure)
  • Multi-line sources (env vars, git config) are matched as single lines
  • All matches are case-sensitive

Note: The git_user source does not use regex patterns. The pattern must be the literal string "name" (for user.name) or "email" (for user.email). Any other value returns no result.

Validation Rules

Apply validation after successful extraction:

enterprise:
  derived:
    team:
      from: path
      pattern: "teams/(\\w+)/"
      validate:
        type: enum
        values: [platform, frontend, backend, infra]

Current Validation Types:

  • enum: Value must be in allowed list (case-sensitive)
    • Failure: Field still included but warning logged to stderr
    • Use to catch naming convention violations

Behavior:

  • Valid value → field included, no warning
  • Invalid value → field included, warning logged
  • Validation never blocks derivation (graceful degradation)

Manual Derivation

Re-run derivation on existing specs:

chant derive 2026-01-22-001-x7m   # Derive fields for one spec
chant derive --all                  # Derive for all specs
chant derive --all --dry-run        # Preview without modifying

Use cases:

  • Add derivation rules and backfill existing specs
  • Update derived values if branch names changed
  • Fix invalid derived values

Common Patterns

Jira Tickets:

jira_key:
  from: branch
  pattern: "([A-Z]+-\\d+)"    # PROJ-123, AUTH-456, etc.

Sprint Cycles:

sprint:
  from: branch
  pattern: "sprint/(\\d{4}-Q\\d-W\\d)"  # 2026-Q1-W1, 2026-Q1-W2, etc.

Team Organization:

team:
  from: path
  pattern: "teams/(\\w+)/"     # Extract from directory structure
  validate:
    type: enum
    values: [platform, frontend, backend, infra]

Multiple Derivation Sources:

team:
  from: env
  pattern: TEAM_NAME           # Fallback to environment variable

Troubleshooting

“Field not derived - pattern didn’t match”

  • Verify the pattern is correct regex syntax
  • Test pattern on actual values using online regex tools
  • Ensure capture group () is present in pattern
  • Check if source contains the expected value

“Field has unexpected value”

  • Check if a more specific pattern should be used earlier
  • Add validation rules to catch invalid formats
  • Review the pattern with team to align on conventions

“Validation warning for valid value”

  • Verify enum values are spelled exactly (case-sensitive)
  • Check if all valid options are in the enum list
  • Update enum list if new values are introduced

UI Display

When viewing specs with chant show:

ID:            2026-01-22-001-x7m
Title:         Add authentication
Type:          code
Status:        completed

Team [derived]: platform
Jira_Key [derived]: PROJ-123
Custom_Field: manual_value

The [derived] indicator distinguishes automatically-populated fields from user-entered values.

Required Fields

Enforce required fields for compliance and audit:

---
enterprise:
  required:
    - team
    - component
    - jira_key
---

When a spec is missing any required field, chant lint reports an error:

$ chant lint
Error: Missing required field 'jira_key'
File: 2026-01-22-001-x7m.md

ℹ Enterprise policy requires: team, component, jira_key

Validation:

  • Required fields can be either derived or explicitly set in frontmatter
  • Checked by chant lint command
  • Blocks spec operations if missing
  • Works with any frontmatter field name (standard or custom)

Combined with Derivation:

Most commonly, derivation rules populate required fields automatically:

---
enterprise:
  derived:
    team:
      from: path
      pattern: "teams/(\\w+)/"
    jira_key:
      from: branch
      pattern: "([A-Z]+-\\d+)"

  required:        # These fields are enforced
    - team
    - jira_key
---

When a spec completes, derived fields are auto-populated, and chant lint verifies all required fields are present.

Audit Trail

Track when and with what model a spec was completed:

# Auto-populated on completion
---
status: completed
completed_at: 2026-01-22T15:30:00Z
model: claude-haiku-4-5-20251001  # Agent model used
---

These fields are automatically populated by chant finalize or auto-finalize when a spec completes:

  • completed_at: ISO 8601 timestamp of completion
  • model: Model ID of agent that completed the spec

Combined with git history (commit author, timestamp, and message), this provides a complete audit trail of spec execution. The git commit records who performed the work and when, while the spec frontmatter records which model was used to complete it.

Feature Compatibility

All enterprise features work with both tracked and silent mode:

FeatureTracked .chant/Silent .chant/
Silent mode-
Derived fields
Required fields
Audit trail

Silent mode + enterprise features: You can use derived fields, required fields, and audit trails even when .chant/ is gitignored (silent mode). This is useful when working on corporate repos that don’t officially support chant—you get enterprise metadata extraction while keeping your workflow private.

All enterprise features are opt-in via the enterprise: config block in .chant/config.md.