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
- Specs
- Spec Types
- Prompts
- Spec IDs
- Spec Groups
- Dependencies
- Autonomous Workflows
- Data Lifecycle
- Skills
Architecture
Guides
- Prompt Guide
- Research Workflows Guide
- OSS Maintainer Workflow
- Examples
- Approval Workflow
- Recovery & Resume
Reference
- CLI Reference
- Configuration Reference
- Provider Configuration
- Errors
- Search Syntax
- Git Integration
- Templates
- Schema & Validation
- Export
- Initialization
- MCP Server
- Versioning
- Output & Progress
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
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
| Platform | Status | Architecture | Package Manager |
|---|---|---|---|
| Linux | ✅ Supported | x86_64 | Homebrew, Cargo |
| macOS | ✅ Supported | x86_64, aarch64 (Apple Silicon) | Homebrew, Cargo |
| Windows | ✅ Supported | x86_64 | Direct 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
- Read the Quickstart guide
- Explore the Philosophy behind Chant
- Check out the CLI Commands reference
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 forwork - 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
-
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 -
Check your shell’s completion system is enabled:
- Bash: Ensure
bash-completionpackage is installed - Zsh: Ensure
compinitis called in~/.zshrc - Fish: Completions load automatically
- Bash: Ensure
-
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
- chant installed
- One of these AI coding CLIs:
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:
- Project name - accept the default
- Provider - choose
claudeorkirocli - Model - choose
sonnet(fast and capable) - 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:
- Read the spec and acceptance criteria
- Create
hello.shwith the required content - Make it executable
- 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?
- chant init - Set up project config and MCP integration
- chant add - Created a spec (work intention)
- chant lint - Validated spec quality
- MCP tools - Let the agent discover and execute the spec
- 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 specs | chant add "your task" |
| Run from CLI | chant work 001 |
| Run multiple specs | chant work --chain |
| See all commands | chant --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:
- Write specs in
.chant/specs/ - Execute with
chant work - 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.
| Approach | Source of Truth | Problem |
|---|---|---|
| Documentation-first | Docs | Rots as code changes |
| Code-first | Code | Intent buried in implementation |
| Ticket-first | Tickets | Closed and forgotten |
| Intent-first | Specs | Execute, 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 origin —
origin: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 --silentkeeps.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
| Before | After |
|---|---|
| Mental tracking | .chant/specs/ |
| Copy-paste context | Spec file IS the context |
| Manual branch management | Worktrees + chant merge |
| Hope for the best | Acceptance criteria + linting |
| Lost work on crash | PID locks + recovery |
Specs
Spec Types at a Glance
| Type | Use For | Example |
|---|---|---|
code | Features, bugs, refactoring | Implement JWT auth |
task | Manual work, prompts, config | Create documentation prompt |
driver | Coordinate multiple specs | Auth system (with .1, .2, .3 members) |
group | Alias for driver | Same as driver |
documentation | Generate docs from code | Document auth module |
research | Analysis, synthesis | Analyze 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
| Type | Field | Drifts When |
|---|---|---|
code | — | Acceptance criteria fail |
documentation | tracks: | Tracked source code changes |
research | origin:, 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:
- Status is
pending - All
depends_onspecs arecompleted - 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)
- Spec status changed to
Cancelledin frontmatter - File is preserved in
.chant/specs/ - Cancelled specs excluded from
chant listandchant work - Can still view with
chant showorchant list --status cancelled - All git history preserved
What Happens When Deleted (Hard Delete with --delete)
- Permanently removes spec file from
.chant/specs/ - Removes associated log file from
.chant/logs/ - Removes worktree artifacts
- With
--cascade: deletes driver and all member specs - With
--delete-branch: removes associated git branch
Cancelled State
---
status: cancelled
---
Difference from Delete
cancel: Changes status to Cancelled, preserves files and historydelete: 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:
CHANT_MODEL- chant-specific overrideANTHROPIC_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.5claude-sonnet-4- Claude Sonnet 4claude-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
| Type | Purpose | Drift Trigger |
|---|---|---|
code | Implement features, fix bugs | Acceptance criteria fail |
task | Manual work, prompts, config | Acceptance criteria fail |
driver | Coordinate multiple specs | Members incomplete |
group | Alias for driver | Members incomplete |
documentation | Generate docs from source | tracks: files change |
research | Analysis, synthesis, findings | origin: or informed_by: files change |
Field Reference
| Field | Type(s) | Triggers Drift? | Purpose |
|---|---|---|---|
context: | all | No | Background reading for the agent |
tracks: | documentation | Yes | Source code being documented |
informed_by: | research | Yes | Materials to synthesize |
origin: | research | Yes | Input data for analysis |
target_files: | all | No | Output 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
| Aspect | code | task |
|---|---|---|
| Output | Source code, tests | Prompts, config, docs |
| Tests | Usually runs tests | Usually no tests |
| Build | May require build | No 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 (
.1before.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 Case | tracks: | target_files: |
|---|---|---|
| API reference | src/api/**/*.rs | docs/api.md |
| Architecture docs | src/, Cargo.toml | docs/architecture.md |
| Module docs | src/auth/*.rs | docs/auth.md |
| README | src/lib.rs | README.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 Type | Trigger | Detection |
|---|---|---|
| Data drift | origin: files changed | File modification detected |
| Source drift | informed_by: files changed | File modification detected |
| Reproducibility drift | Can’t replicate results | chant verify fails |
Prompt Selection by Type
Override the default prompt per-spec:
---
type: research
prompt: research-analysis
---
Summary
| Concept | Code | Task | Driver/Group | Documentation | Research |
|---|---|---|---|---|---|
| Purpose | Implement features | Manual work | Coordinate specs | Document code | Analyze/synthesize |
| Work input | Criteria | Criteria | Members | tracks: | informed_by: / origin: |
| Drift trigger | Criteria fail | Criteria fail | Members incomplete | tracks: changes | Input files change |
| Default prompt | standard | standard | — | documentation | research-* |
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/:
| Prompt | Type | Purpose |
|---|---|---|
documentation | documentation | Generate/update docs from tracked code |
research-synthesis | research | Synthesize materials into findings |
research-analysis | research | Analyze data, generate reports |
Auto-selection:
type: documentation→documentationprompttype: researchwithorigin:→research-analysisprompttype: researchwithoutorigin:→research-synthesisprompt
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.
| Variable | Description |
|---|---|
{{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
| Prompt | Purpose |
|---|---|
bootstrap | (Default) Minimal prompt that delegates to chant prep |
standard | Read → Plan → Implement → Verify → Commit |
split | Split driver into group members |
document | Write user-facing documentation |
research | Deep root cause analysis for issues |
merge-conflict | Resolve 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
--promptCLI flag (highest priority)prompt:in spec frontmatterdefaults.promptfrom 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-x7mchant(2026-01-22-001-x7m): Add authentication
Format
Base Format
YYYY-MM-DD-SSS-XXX
└────┬────┘ └┬┘ └┬┘
date seq random
| Component | Purpose |
|---|---|
YYYY-MM-DD | Creation date, sortable |
SSS | Sequence within day (base36: 001-zzz) |
XXX | Random suffix (3 chars, base36) |
Example: 2026-01-22-001-x7m
Sequence Format (Base36)
Sequence uses base36 for scalability:
001through999- familiar numerica00throughzzz- 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→ matches2026-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:
- Exact match - Full ID matches exactly
- Suffix match -
x7muniquely identifies across all time - Today first -
001checks today before searching history - Date-qualified -
22-001or01-22-001for specific dates - 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:
chant lint- Validates all specschant add --depends-on- Checks before adding- 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
- Split creates members (.1, .2, .3, …)
- Members without deps become ready
- Ready members execute in parallel
- As members complete, blocked ones become ready
- 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:
- Dependency tracking bug - Spec shows as blocked but dependencies are actually completed
- Manual override - You verified dependencies are satisfied despite their status
- Emergency work - Need to proceed despite dependency chain issues
- 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 workinvokes an agent to implement the spec - Verify:
chant verifyre-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
| Level | Description | Approval Required |
|---|---|---|
| Supervised | Agent works, human reviews before merge | PR review |
| Trusted | Agent works, auto-merge low-risk | High-risk only |
| Autonomous | Agent works, auto-merge, human notified | Exceptions 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:
- Week 1:
autonomy.level: supervised- review all PRs - Week 2: Auto-merge
[trivial]labels - Week 3: Auto-merge
[trivial, deps, docs] - Week 4+: Full autonomous with guardrails
Goal: More done, less human time, acceptable risk, intent preserved.
Data Lifecycle
What Data Exists
| Data | Location | Tracked | Retention |
|---|---|---|---|
| Specs | .chant/specs/*.md | Git | Forever (git history) |
| Prompts | .chant/prompts/*.md | Git | Forever |
| Config | .chant/config.md | Git | Forever |
| Locks | .chant/.locks/*.pid | No | Until released |
| Index | .chant/.store/ | No | Rebuilt on demand |
| Logs | .chant/logs/ | Optional | Configurable |
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_onto complete - Status automatically applied when spec is loaded if dependencies incomplete
- Excluded from
chant workuntil dependencies complete - Use
chant list --summaryto 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.jsonin 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 workwhen 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 listandchant work - Can still be viewed with
chant showor filtered withchant list --status cancelled - Use
chant cancel {spec-id} --deleteif 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:
- Status automatically changes to
Blocked - Spec excluded from
chant work(can’t execute) - Spec excluded from
chant list(hidden by default) - When all dependencies complete, status reverts to
Pending - 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:
chant workchecks if watch is running via PID file (.chant/watch.pid)- If not running, spawns
chant watchas a detached background process - Watch writes its PID to
.chant/watch.pidon startup - 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.pidon 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 workingdone- Agent completed successfullyfailed- Agent encountered an error
Startup Recovery
On startup, watch recovers from previous crashes:
- Scans for existing worktrees with
.chant-status.json donestatus but not merged → queued for mergeworkingstatus but stale (>1 hour) → marked failed- 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
| Field | Constraints |
|---|---|
name | Max 64 chars. Lowercase letters, numbers, hyphens. Must match directory name. |
description | Max 1024 chars. Describes what the skill does and when to activate it. |
Optional Fields
| Field | Purpose |
|---|---|
license | License name or reference to bundled file |
compatibility | Environment requirements (tools, network, etc.) |
metadata | Arbitrary key-value pairs (author, version) |
allowed-tools | Pre-approved tools the skill may use (experimental) |
Progressive Disclosure
Skills are designed for efficient context usage:
- Discovery (~100 tokens): Only
nameanddescriptionload at startup for all skills - Activation (< 5000 tokens): Full
SKILL.mdbody loads when the agent matches a request - 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:
| Provider | Skills 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:
| Aspect | Skills | Prompts |
|---|---|---|
| Standard | Agent Skills (open) | Chant-specific |
| Location | Provider’s skills dir | .chant/prompts/ |
| Loaded by | IDE/agent at startup | chant work at execution time |
| Scope | Interactive sessions | Single spec execution |
| Purpose | General chant awareness | Specific agent behavior |
| Examples | chant/SKILL.md | standard.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:
| Scope | Location | Purpose |
|---|---|---|
| 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
- Keep SKILL.md under 500 lines — move detailed docs to
references/ - Write a good description — this determines when the skill activates
- Include keywords — the agent matches descriptions against user requests
- Be specific — “Review pull requests for security issues and test coverage” beats “Helps with PRs”
- 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
- Agent Skills Specification
- Anthropic Skills Repository
- Agent Skills Community
- Claude Code Skills Docs
- Kiro Skills Docs
Architecture Overview
Living Intent Infrastructure
The composable primitives that make self-driving specs possible:
| Primitive | Purpose | Self-Driving Role |
|---|---|---|
| Specs | Executable specifications | The intent to execute |
| Prompts | Agent behavior definitions | How agents interpret intent |
| Triggers | Event-based activation | When specs activate |
| Dependencies | Execution ordering | Sequencing of intent |
| Verification | Ongoing truth-checking | Detecting drift |
| Replay | Re-execution mechanism | Restoring 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
| Category | Crate | Purpose |
|---|---|---|
| Parsing | pulldown-cmark | Markdown parsing |
serde, serde_yaml | YAML frontmatter | |
| CLI | clap | Argument parsing |
| Async | rayon | Parallel parsing |
External Dependencies
| Dependency | Integration |
|---|---|
| Git | Shell out (uses user’s config/auth) |
| AI Agent | Shell out (provider CLI invocation) |
Delivery
brew install chantcargo 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:
- Terminal (streamed)
- Spec file
progressfield (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
| Component | Responsibility |
|---|---|
| CLI | User interface, command dispatch |
| Parser | Read/write markdown + YAML frontmatter |
| Queue | Track ready specs, priority ordering |
| Locks | Prevent double-work, crash recovery |
| Executor | Invoke agent, manage lifecycle |
| Daemon | Persistent services (optional) |
| Index | Fast search via Tantivy |
| Providers | Agent 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:
| Variable | Description |
|---|---|
CHANT_SPEC_ID | Current spec ID |
CHANT_SPEC_FILE | Absolute path to spec markdown |
CHANT_PROJECT | Project prefix (if any) |
CHANT_PROMPT | Prompt name being used |
CHANT_PROMPT_FILE | Absolute path to prompt markdown |
CHANT_WORKTREE | Worktree path (if isolated) |
CHANT_ATTEMPT | Attempt number (1, 2, 3…) |
CHANT_TIMEOUT | Remaining timeout in seconds |
Input
Agent receives:
- System prompt - The prompt markdown (agent behavior)
- 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:
- Working directory - Code changes
- Spec file - Progress updates (output section)
- Git - Commits (if prompt instructs)
Exit Handling
| Agent Exit | Chant Action |
|---|---|
| 0 | Mark spec completed |
| Non-zero | Mark spec failed, capture error |
| Timeout | Kill agent, mark failed |
| Signal | Mark 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:
- Accept spec content (stdin or file)
- Accept prompt/system instruction (env var or flag)
- Write changes to working directory
- 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
- Spec frontmatter
agent.provider(highest) - Config
agent.provider - Environment
CHANT_PROVIDER - 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
| Module | Description |
|---|---|
archive.rs | Move completed specs to archive directory |
cancel.rs | Cancel specs and mark them as cancelled |
commits.rs | Auto-detect and associate git commits with specs |
create.rs | Create new specs with ID generation and template application |
finalize.rs | Mark specs as completed with validation and state checks |
model.rs | Update model configuration for specs |
pause.rs | Pause running work processes for specs |
reset.rs | Reset failed or in-progress specs back to pending |
update.rs | Update spec frontmatter fields and append output |
verify.rs | Verify 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_attimestamp, 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
failedorin_progressstate - Transition to
pendingvia 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
failedandin_progressspecs 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_statusfor 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\nheader - 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):
-
Create
src/operations/{operation}.rs- Define an
Optionsstruct 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
- Define an
-
Export from
src/operations/mod.rs#![allow(unused)] fn main() { pub mod archive; pub use archive::{archive_spec, ArchiveOptions}; } -
Add CLI command in
src/cmd/- Parse arguments with clap
- Load spec and dependencies
- Call operation function
- Handle errors and output
-
Add MCP handler in
src/server/handlers/- Parse JSON-RPC parameters
- Load spec and dependencies
- Call the same operation function
- Return JSON response
-
Write tests in
tests/operations/- Test the operation directly (not via CLI/MCP)
- Cover validation, state transitions, edge cases
- Use
TestHarnessandSpecFactoryfor 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
| Pattern | Component | Location | Purpose |
|---|---|---|---|
| State Machine + Builder | TransitionBuilder | spec/state_machine.rs | Composable preconditions for lifecycle transitions |
| Template Method + Strategy | Execution Engine | cmd/work/executor.rs | Common validation flow, pluggable execution modes |
| Sandbox + Mutex | Worktree Isolation | worktree/mod.rs | Isolated execution environments, serialized creation |
| DAG | Dependency Graph | deps.rs | Cross-repo dependencies, cycle detection, topological sort |
| Rules Engine | Merge Driver | merge.rs | Declarative conflict resolution by field |
| Command | MCP Interface | mcp/server.rs | Self-describing tools for AI orchestration |
| Strategy | Provider Abstraction | provider.rs | Pluggable AI backends |
| Composite | Spec Groups | spec_group.rs | Driver/member hierarchy with auto-completion |
| Observer | Watch Service | cmd/watch.rs | Polling-based lifecycle orchestration |
| Facade/Service Layer | Operations | operations/mod.rs | Shared business logic for CLI and MCP |
| Layered Config | Config Merge | config/mod.rs | Global → project → local override semantics |
| Strategy + Adapter | Output | ui.rs, formatters.rs | Human/JSON/Quiet output modes |
| Composite | Score/Lint | scoring.rs, score/mod.rs | Independent metric aggregation, traffic light |
Navigation Tips
When exploring the codebase:
- Start with patterns, not files — Identify which pattern you need to understand, then read the relevant module
- Follow the data — Specs flow through: parse → validate → execute → finalize → merge
- Check tests — Most patterns have comprehensive test coverage showing usage examples
- Read module docs — Each module has header comments explaining its role and architectural decisions
- Use
docs/architecture/architecture.md— For high-level component overview and storage layout
Additional Resources
- Architecture Overview - High-level system design
- Agent Protocol - How agents interact with chant
- Concepts - Core concepts like specs, dependencies, groups
- CLI Reference - Command-line interface documentation
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:
.cursorrulesfile with AI instructions for Cursor.cursor/mcp.jsonfor 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.
- Quit Cursor completely
- Reopen Cursor
- Open your project folder
Step 3: Verify MCP is Working
Open Cursor’s AI chat and check that chant tools are available:
- Open the Cursor chat panel
- Look for MCP tools in the tool list (if available in UI)
- Or ask Cursor: “What chant tools are available?”
Cursor should respond with a list of chant_* tools like:
chant_spec_listchant_spec_getchant_addchant_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 mcpprocess - 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:
| Tool | Purpose |
|---|---|
chant_spec_list | List all specs, optionally filtered by status |
chant_spec_get | Get full details of a spec |
chant_ready | List specs ready to work (no unmet dependencies) |
chant_status | Get project summary with spec counts |
chant_add | Create a new spec |
chant_spec_update | Update a spec’s status or add output |
chant_finalize | Mark a spec as completed |
chant_search | Search specs by title and content |
chant_diagnose | Diagnose issues with a spec |
chant_log | Read 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
.cursorrulesfor the complete AI behavior guide
Troubleshooting
MCP Tools Not Showing Up
Symptoms: Cursor doesn’t recognize chant_* tools
Solutions:
- Restart Cursor completely - Quit and reopen
- Verify
.cursor/mcp.jsonexists - Should be in your project root - Check chant is in PATH - Run
which chantin terminal - 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:
- Check spec exists - Run
chant listto verify - Verify model provider - Check
.chant/config.mdfor provider/model settings - Check logs - Look in
.chant/specs/2026-01-30-001-abc.logfor 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
- Forgetting to restart Cursor after running
chant init --agent cursor - Trying to run
chant workfrom Cursor’s AI - Use the terminal instead - Editing specs directly while an agent is working them
- Not committing changes before switching specs
Tips for Success
- Write clear acceptance criteria in your specs
- Keep specs focused - One logical unit of work per spec
- Review agent output before merging to main
- Use Cursor’s AI for exploration and spec management, not execution
- Customize
.cursorrulesto match your project’s standards
Additional Resources
- Chant Documentation
- MCP Protocol Specification
- Cursor Documentation
- GitHub Issues - For bugs or feature requests
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 Case | informed_by: | origin: | schedule: |
|---|---|---|---|
| Literature review | papers, docs | — | — |
| Log analysis | — | log files | daily |
| Codebase health | src/**/*.rs | — | weekly |
| Performance report | prior reports | metrics CSV | weekly |
| Bug investigation | related code | error logs | — |
| Library comparison | library docs | — | — |
| Survey analysis | methodology docs | survey 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
- Research Workflow Example — Example demonstrating synthesis and analysis
- spec-types.md — Overview of spec types including research
- prompts.md — research-synthesis and research-analysis prompts
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
| Principle | How chant implements it |
|---|---|
| Never lose data | Git tracks everything — branches, commits, worktree state |
| Make recovery explicit | reset is deliberate, not automatic |
| Preserve partial work | Branches survive agent crashes; inspect before cleanup |
| Fail fast in parallel | Chain mode stops on first failure |
| Track attempts | Retry counter in frontmatter lets agents adapt |
Key Commands
| Command | When to use |
|---|---|
chant reset <id> | Reset a failed/stuck spec to pending |
chant reset <id> --work | Reset and immediately re-execute |
chant cleanup | Remove orphan worktrees and stale artifacts |
chant cleanup --dry-run | Preview 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
- Lifecycle — State transitions including failed and recovery
- CLI Reference — Full command documentation
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:
-
Diagnose the issue:
chant diagnose <spec-id> -
If agent is running but stuck:
chant takeover <spec-id> # Analyze partial work -
If agent crashed:
chant reset <spec-id> # Clear state and start fresh -
If worktree is corrupted:
rm -rf /tmp/chant-<spec-id> git worktree prune chant reset <spec-id> -
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
- Lifecycle — State transitions including approval gates
- CLI Reference — Full command documentation
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:
- Setup – Configure silent mode for working on a shared repo
- Comprehension – Understand what the issue is about
- Reproduction – Create a failing test that proves the bug
- Root Cause – Find out why data is lost
- Impact Map – Discover what else is affected
- Fork Fix – Implement the fix and create a staging PR
- Upstream PR – Human reviews and submits upstream
- Advanced Patterns – Single-spec mode, pausing, takeover (file is
08-advanced.mdbecause 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
- Lifecycle Walkthrough – Core spec lifecycle concepts
- Recovery & Resume – Handling failed specs
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
- Adds
.chant/to.git/info/exclude(local-only gitignore) - Suppresses warnings about untracked spec files
- All chant functionality works normally
- Nothing from
.chant/appears ingit statusor 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
| Role | Person | Responsibility |
|---|---|---|
| VP Product | Sarah | Sets OKRs, approves specs |
| Data Analyst | Mike | Gathers 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
- The Business Context — Acme’s product, churn problem, and Q1 OKR
- Data Ingestion — Week 1: Human investigation and data gathering
- Research Phase — Week 2: Chant agent analyzes churn drivers
- Approval Gate — Week 2: Team reviews and approves findings
- Implementation — Week 3: Parallel execution of fixes
- 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
- KPI/OKR Workflow Example — Working example demonstrating this workflow
The Business Context
Acme SaaS Corp
Acme SaaS Corp runs a B2B project management platform. Key numbers:
| Metric | Value |
|---|---|
| Customers | 5,000 |
| MRR | $2.5M |
| Monthly churn | 8% (400 customers/month) |
| Revenue at risk | $200K/month |
| Customer segments | Startup, 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:
| Metric | Baseline | Target | How Measured |
|---|---|---|---|
| Monthly churn rate | 8% | 5% | Billing system exports |
| 30-day activation rate | 62% | 75% | Product analytics |
| Support ticket volume | 340/week | <250/week | Zendesk |
| NPS score | 32 | 40+ | Quarterly survey |
Churn Breakdown (Current State)
Mike (Data Analyst) pulls initial numbers by segment:
| Segment | Customers | Churn Rate | Lost/Month |
|---|---|---|---|
| Startup (<50 seats) | 3,200 | 11% | 352 |
| Mid-Market (50-500) | 1,400 | 4% | 56 |
| Enterprise (500+) | 400 | 1% | 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:
- Data Ingestion — Mike gathers metrics, support data, and survey results
- Research — Chant agent analyzes the data for churn drivers
- Approval — Sarah reviews and approves the analysis
- 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:
| Source | What | Format |
|---|---|---|
| Datadog | Churn metrics, cohort analysis | Dashboard exports |
| Zendesk | Support ticket patterns | Ticket exports, tags |
| Typeform | User exit survey responses | Survey 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
- Research Workflows — Research spec patterns
- Approval Workflow — Approval gates and discussion
- Enterprise Features — Derived fields, required fields, audit
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
| Team | Focus | Engineers | Test Coverage | Test Flakiness |
|---|---|---|---|---|
| Auth | Authentication & SSO | 5 | 85% | 8% |
| Payments | Billing & subscriptions | 4 | 45% | 18% |
| Analytics | Reporting & dashboards | 6 | 92% | 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
- The TDD Challenge — Acme’s testing problems across 3 teams
- Chant Meets TDD — How spec-driven development aligns with TDD
- Specs as Test Plans — Using acceptance criteria to define tests
- Writing Tests with Chant — Agent-assisted test implementation
- Ensuring Quality — Enforcing test standards across teams
- 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
- TDD Workflow Example — Working example demonstrating this workflow
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.
| Metric | Current | Target |
|---|---|---|
| Average test coverage | 74% | 80%+ |
| Flaky test rate | 12% | <5% |
| Test drift | 23% | <5% |
| Tests written before code | 35% | 90%+ |
Three Teams, Three Approaches
Auth Team (5 engineers)
The Auth team follows TDD religiously. Every feature starts with failing tests.
| Metric | Value | Assessment |
|---|---|---|
| Coverage | 85% | Strong |
| Flaky tests | 8% | Needs work |
| Test drift | 12% | Moderate |
| TDD adoption | 95% | 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.
| Metric | Value | Assessment |
|---|---|---|
| Coverage | 45% | Critical |
| Flaky tests | 18% | Critical |
| Test drift | 35% | Critical |
| TDD adoption | 15% | 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.
| Metric | Value | Assessment |
|---|---|---|
| Coverage | 92% | Excellent |
| Flaky tests | 5% | Good |
| Test drift | 28% | Needs work |
| TDD adoption | 40% | 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:
- Chant Meets TDD — How specs naturally support test-first development
- Test Planning — Using acceptance criteria as test cases
- Execution — Agents writing tests before implementation
- 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 Practice | With Chant |
|---|---|
| Jira tickets | Tickets reference spec IDs |
| Pull requests | PRs link to specs |
| Code review | Reviewers check acceptance criteria |
| Test coverage | Coverage 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
- Include expected values — “returns 400” not “returns error”
- Specify boundaries — “after 3 attempts” not “rate limits requests”
- Name error codes — “REFUND_EXCEEDS_ORIGINAL” not “appropriate error”
- 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:
| Metric | Before | After | Target |
|---|---|---|---|
| Payment coverage | 45% | 86% | 85% ✓ |
| Refund module | 38% | 87% | 85% ✓ |
| Flaky tests | 18% | 4% | <5% ✓ |
| Test count | 42 | 69 | — |
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:
| Team | Test Naming | Fixture Pattern | Assertions/Test | Coverage Threshold |
|---|---|---|---|---|
| Auth | test_* | Factories | 3-5 | 80% |
| Payments | it_should_* | Raw data | 1-2 | None |
| Analytics | test_* | Fixtures | 2-4 | 90% |
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:
| Metric | Before | After |
|---|---|---|
| Consistent naming | 65% | 98% |
| Factory usage | 40% | 95% |
| Docstring coverage | 30% | 94% |
| Meeting coverage target | 1/3 teams | 3/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 Type | Count | Risk |
|---|---|---|
| Tests asserting removed behavior | 45 | High — false positives |
| Tests missing new parameters | 32 | Medium — incomplete coverage |
| Tests using deprecated mocks | 28 | Low — maintenance burden |
| Tests with outdated docstrings | 89 | Low — 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:
| Metric | Before | After | Target |
|---|---|---|---|
| Test drift rate | 23% | 4% | <5% ✓ |
| Tests asserting removed behavior | 45 | 3 | — |
| Missing parameter tests | 32 | 5 | — |
| Outdated docstrings | 89 | 12 | — |
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 Workflows — Using research specs for coverage analysis
- Enterprise Features — Validation and required fields
- KPI/OKR Workflow — Related enterprise workflow guide
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:
- Investigate — Gather and synthesize sources
- Analyze — Process data to generate findings
- Document — Capture insights for others
- Implement — Act on findings
- 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:
| Path | Protagonist | Research Goal |
|---|---|---|
| Academic | Dr. Sarah Chen, PhD student | Analyze 30 years of Arctic temperature data |
| Developer | Alex Torres, Staff Engineer | Evaluate 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:
| Phase | Academic Example | Developer Example | Spec Types |
|---|---|---|---|
| Investigation | Literature review (25 papers) | Codebase analysis (src/**) | research with informed_by: |
| Analysis | Statistical analysis of climate data | Performance metrics analysis | research with origin: |
| Documentation | Write methodology section | Document architecture decisions | documentation with tracks: |
| Implementation | Data processing pipeline | POC microservice extraction | code, driver, depends_on: |
| Pipeline | Phase 1→2→3 dependencies | Service-by-service rollout | driver with member specs |
| Maintenance | New data triggers re-analysis | Code changes trigger doc updates | drift detection |
Spec Type Reference
| Spec Type | Purpose | Key Fields | Example |
|---|---|---|---|
research | Investigation and analysis | informed_by:, origin: | Synthesize papers, analyze data |
documentation | Capture and communicate | tracks: | Architecture docs, methodology |
code | Implement changes | target_files: | Scripts, services, utilities |
driver | Coordinate phases | members: | Multi-step pipelines |
task | Non-code work | target_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. Analysis —
informed_by:for reading sources,origin:for processing data - Dependency chains —
depends_on:for phase ordering - Driver coordination — Decomposing complex work into parallel member specs
- Documentation tracking —
tracks:keeps docs synchronized with source - Drift detection — Know when inputs change, trigger re-verification
Prerequisites
Familiarity with:
See Also
- Research workflow examples — Concrete examples demonstrating these patterns
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.
| Attribute | Value |
|---|---|
| Program | PhD, Climate Science |
| Year | Third year |
| Advisor | Prof. James Morrison |
| Funding | NSF Arctic Research Grant |
| Timeline | Dissertation 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:
| Issue | Prevalence |
|---|---|
| Methods under-specified | 67% of papers |
| Data not preserved | 45% of papers |
| Analysis steps not documented | 72% of papers |
| Results not reproducible | 38% 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:
| Challenge | Chant Solution |
|---|---|
| Scattered notes | informed_by: links findings to sources |
| Unclear dependencies | depends_on: chains analysis phases |
| Data versioning | origin: tracks input data files |
| Result staleness | Drift detection alerts when inputs change |
| Method documentation | Spec 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:
| Week | Spec Type | Purpose |
|---|---|---|
| 1 | research with informed_by: | Synthesize 25 papers into themes |
| 2 | research with origin: | Analyze temperature data |
| 3 | driver with members | Coordinate multi-step pipeline |
| 4+ | Drift detection | Alert 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:
- Reads all CSV files via
origin: - Reads the literature review via
informed_by:for context - Runs the statistical methodology
- 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 Identified | How Analysis Addressed |
|---|---|
| Post-2015 acceleration | Extended analysis to 2024 |
| Station-level variability | Analyzed 12 stations individually |
| Seasonal patterns | Decomposed 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:
| Step | Input | Output | Depends On |
|---|---|---|---|
| Data Cleaning | Raw CSVs | Cleaned CSVs | None |
| Statistical Analysis | Cleaned CSVs | Results | Cleaning |
| Sensitivity Analysis | Cleaned CSVs, Results | Robustness tests | Cleaning, Analysis |
| Figure Generation | Results | Publication figures | Analysis |
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:
- Creates worktrees for each member spec
- Executes .1 (cleaning) first
- Waits for .1 to complete
- Executes .2 (analysis)
- Waits for .2 to complete
- 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
| Benefit | How It Helps Sarah |
|---|---|
| Reproducibility | Pipeline can be re-run identically |
| Provenance | Each output traces to specific inputs |
| Parallelism | Independent steps run concurrently |
| Documentation | Pipeline 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 Type | Trigger | Example | Response |
|---|---|---|---|
| origin | Data files change | New temperature readings | Re-run analysis |
| informed_by | Source materials change | New paper published | Re-run synthesis |
| tracks | Tracked code changes | Analysis script updated | Re-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:
- Reproducibility: Every analysis has a spec that can be re-run
- Provenance: Every finding traces to specific data files and versions
- Currency: Drift detection ensures findings reflect latest data
- 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.
| Attribute | Value |
|---|---|
| Title | Staff Engineer |
| Company | TechCorp |
| Team Size | 12 engineers across 3 teams |
| Tenure | 4 years |
| Timeline | 6-week evaluation |
The Codebase
TechCorp’s backend is a Python monolith that has grown over 7 years:
| Metric | Value |
|---|---|
| Lines of code | 80,000 |
| Python packages | 42 |
| Database tables | 156 |
| API endpoints | 234 |
| Test coverage | 68% |
| Average deployment | Weekly |
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:
| Symptom | Impact |
|---|---|
| Slow CI/CD | Full test suite takes 45 minutes |
| Deployment coupling | Team A’s changes require Team B’s review |
| Scaling bottlenecks | Auth service needs 10x capacity of reporting |
| Onboarding time | New engineers take 3 months to be productive |
| Bug blast radius | One 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:
| Week | Spec Type | Purpose |
|---|---|---|
| 1 | research with informed_by: | Analyze codebase coupling |
| 2 | documentation with tracks: | Document architecture decisions |
| 3 | driver with members | Coordinate POC extraction |
| 4+ | Drift detection | Keep 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:
| Problem | Traditional Docs | Documentation Spec |
|---|---|---|
| Stale immediately | Updated manually, if at all | Drift detection alerts |
| Unclear source | “Where did this come from?” | tracks: links to code |
| Hard to verify | No way to check accuracy | Re-run spec to regenerate |
| Scattered ownership | Anyone can edit | Spec 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:
- Reads the coupling analysis (via
informed_by:) - Reads tracked source files
- 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
| Benefit | How It Helps |
|---|---|
| Always current | Drift detection catches stale docs |
| Traceable | Every statement links to source code |
| Reproducible | Re-run spec to regenerate from scratch |
| Verified | Agent 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
| Benefit | How It Helps |
|---|---|
| Proactive alerts | Know when docs are stale before customers complain |
| Traceable changes | See exactly which file changes caused drift |
| Cascade awareness | Understand how changes ripple through specs |
| Automated monitoring | Weekly 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
| Flag | Description |
|---|---|
--ready | Show only ready specs (equivalent to --status ready) |
--status STATUS | Filter by status (pending, ready, in_progress, completed, failed, blocked, cancelled) |
--type TYPE | Filter by spec type (code, task, driver, documentation, research, group) |
--label LABEL | Filter by label (can be specified multiple times for OR logic) |
--approval STATUS | Filter by approval status (pending, approved, rejected) |
--created-by NAME | Filter by spec creator name (case-insensitive) |
--mentions NAME | Filter specs mentioning a person in approval discussion |
--activity-since DURATION | Filter by recent activity (e.g., “2h”, “1d”, “1w”) |
--count | Show only count of matching specs instead of listing them |
--global | List specs from all configured repos in global config |
--repo PATH | Filter to specific repository path (implies --global) |
--project NAME | Filter to specific project within repository |
--main-only | Skip branch resolution for in_progress specs (debug option) |
--summary | Show project status summary (replaces chant status) |
--watch | Watch mode - refresh every 5 seconds (requires --summary) |
--brief | Brief single-line output (requires --summary) |
--json | JSON 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):
- Spec status changed to
Cancelledin frontmatter - File is preserved in
.chant/specs/ - Cancelled specs excluded from
chant listandchant work - Can still view with
chant showorchant list --status cancelled - All git history preserved
Hard Delete (--delete):
- Permanently removes spec file from
.chant/specs/ - Removes associated log file from
.chant/logs/ - Removes worktree artifacts
- With
--cascade: deletes driver and all member specs - 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:
- Shows all ready specs with multi-select
- Asks whether to use parallel execution
- Lets you choose a prompt (defaults to spec’s prompt or type-based default)
- Asks about branch creation (if
defaults.branchnot set) - 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:
| Command | Behavior |
|---|---|
chant work --chain | Chain through ALL ready specs |
chant work --chain spec1 | Start from spec1, then continue through any specs unblocked by completions |
chant work --chain spec1 spec2 spec3 | Run 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
--labelfilter is ignored for the specified IDs but applies to discovery--chain-maxlimit 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
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:
- Enter a search query (keyword, or
label:name,status:name,type:name) - Filter results by status
- Filter results by type
- 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_fieldslist
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: Missingtracksortarget_filesfieldsresearch: Missing bothinformed_byANDoriginfields
Complexity Warnings:
- More than 5 acceptance criteria
- More than 5 target files
- More than 500 words in description
- Suggests using
chant splitif too complex
Coupling Warnings:
- Detecting spec ID references in body (outside code blocks)
- Suggests using
depends_onfor 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:
- Reads the spec’s acceptance criteria
- Checks the current codebase against each criterion
- 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
| Flag | Description |
|---|---|
--brief | Compact single-line output showing key metrics |
--json | Output status as JSON for programmatic parsing |
--global | Show status across all configured repositories |
--repo <path> | Filter to specific repository path (implies --global) |
--watch | Watch 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
| Level | Description |
|---|---|
minimal | Show only spec IDs |
titles | Show IDs and titles |
full | Show 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:
- Reloads all specs fresh from disk (no caching)
- Recalculates ready/blocked status based on current dependencies
- Reports summary counts (completed, ready, in-progress, pending, blocked)
- 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:
- Loads all completed specs that have associated branches
- Shows a multi-select list with spec ID, title, and branch name
- Prompts for rebase strategy (default: no)
- Prompts for branch deletion (default: yes)
- 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:
- Have
status: completed - 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:
| Flag | What it merges |
|---|---|
--all | All completed specs (including those completed without branches) |
--all-completed | Only 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:
- Reads the conflicting files
- Analyzes the conflict markers
- Edits files to resolve conflicts
- Stages resolved files
- 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:
- Validates the spec is in
failedorin_progressstatus - Resets status to
pending - 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:
- Validates all acceptance criteria are checked
- Records commit SHAs from git history
- Sets
completed_attimestamp andmodelin frontmatter - 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:
tracksfield: Source files being documentedoriginfield: Research spec originsinformed_byfield: 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:
- Lets you choose export format (JSON, CSV, or Markdown)
- Allows selecting multiple status filters
- Lets you filter by type
- Asks where to save (stdout or file)
- 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.promptfile exists at.chant/prompts/{name}.md - Checks
parallel.cleanup.promptfile 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_concurrentvalues)
Recommended Fields (warnings):
- Warns if
defaults.modelnot 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
| Type | Color | Description |
|---|---|---|
CREATED | Cyan | Spec was created |
APPROVED | Green | Spec was approved |
REJECTED | Red | Spec was rejected |
WORKED | Yellow | Commit with chant(<id>): pattern |
COMPLETED | Green (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:
| Field | Description |
|---|---|
| Path | Absolute path to the worktree directory |
| Branch | Git branch checked out in the worktree (format: chant/<spec-id>) |
| HEAD | Short commit hash (7 characters) of current HEAD |
| Spec | Associated spec ID extracted from branch or path |
| Title | Spec title (if spec file exists) |
| Status | Spec status: pending, ready, in_progress, completed, failed, blocked, cancelled |
| Size | Disk space used by the worktree |
| Age | Time since worktree was last modified |
Flags:
| Flag | Meaning |
|---|---|
[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:
| Variable | Description | Example |
|---|---|---|
CHANT_WORKTREE | Set to 1 when running in a worktree | 1 |
CHANT_WORKTREE_PATH | Absolute path to the worktree directory | /tmp/chant-2026-01-27-001-abc |
CHANT_BRANCH | Git branch name for this spec | chant/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:
- Auto-start:
chant workcommands automatically start watch if not running - PID file: Watch writes PID to
.chant/watch.pidon startup - Idle timeout: Exits automatically after configurable idle period (default: 5 min)
- 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:
- Writes PID to
.chant/watch.pid - Monitors worktrees for agent status changes (
.chant-status.json) - When agent writes
status: done, watch:- Merges the worktree branch to main
- Finalizes the spec (records commits, timestamp)
- Cleans up the worktree and branch
- When agent writes
status: failed, watch handles failure appropriately - Exits after idle timeout when no work remains
Flags
| Flag | Description |
|---|---|
--once | Run only one iteration then exit (useful for testing) |
--dry-run | Show what would be done without actually doing it |
--poll-interval MS | Set 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:
- Scans for existing worktrees with
.chant-status.json donestatus but not merged → queued for mergeworkingstatus but stale (>1 hour) → marked failed- 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:
- Creates
.chant/site/theme/directory - Copies default templates for customization:
index.html- Homepage templatespec.html- Individual spec page templatestyles.css- Site stylesheet- Other theme assets
- Lists created files with descriptions
- 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:
- Loads all specs from
.chant/specs/ - Uses custom theme from
.chant/site/theme/if available - Falls back to embedded default theme
- Generates static HTML files
- 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:
- Checks that the site has been built
- Starts an HTTP server on the specified port
- Serves static files from the output directory
- Logs requests to stdout
- 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?
- Consistency - Same format as specs and prompts
- Self-documenting - Body explains the config
- Editable anywhere - Any text editor works
- 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 (
claudecommand) - Best for: Full feature support, Claude-specific capabilities
- Requires:
claudecommand 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_KEYenvironment 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:
- Gather available capacity from all configured agents
- Respect per-agent
max_concurrentlimits - Distribute to agents with most remaining capacity first
- Stop when total capacity is reached (or
--maxlimit 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:
| Issue | Detection | Severity |
|---|---|---|
| API errors (429, rate limit) | Exit code, stderr | High |
| Merge conflicts | Git status on branches | High |
| Partial failures | Some specs failed | Medium |
| Stale worktrees | Worktrees not cleaned up | Low |
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 namepath- Spec file path relative to repository rootenv- 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_usersource does not use regex — pattern must be the literal string"name"or"email"
Validation:
type: enumwithvalues: [...]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 lintvalidates 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:
- During spec completion - Auto-populates fields, tracked in
derived_fieldslist - Manual re-derivation - Use
chant deriveto update existing specs - 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-approvalflag 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
rejectedstatus - 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
drivertype - 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
- Spec frontmatter (highest)
- Environment variables
- Project config (
.chant/config.md) - Global config (
~/.config/chant/config.md) - 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:
claudecommand 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_KEYenvironment 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-chatcommand 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
- Configuration Reference - Full configuration schema
- CLI Reference - Command reference
Errors
Error Categories
| Category | Examples | Severity |
|---|---|---|
| Parse | Invalid YAML, missing fields | Blocking |
| State | Locked, blocked, wrong status | Blocking |
| Execution | Agent failed, tests failed | Spec fails |
| Git | Merge conflict, dirty worktree | Blocking |
| System | Disk full, permissions | Fatal |
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
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Parse error |
| 3 | State error (locked, blocked) |
| 4 | Execution failed |
| 5 | Git error |
| 10 | Lint 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
| Error | Recovery |
|---|---|
| Stale lock | chant unlock <id> |
| Failed spec | chant retry <id> |
| Merge conflict | chant resolve <id> |
| Cycle | chant deps --check |
Search Command
Status: Implemented - The
chant searchcommand 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
Case-Sensitive Search
chant search "Auth" --case-sensitive # Matches exact case
By default, search is case-insensitive.
Title-Only Search
chant search "authentication" --title-only
Searches only the spec title (first # heading in the spec body).
Body-Only Search
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 workprocesses in separate terminals can cause merge conflicts when specs finish concurrently. Both processes attemptgit checkout main && git mergein 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
-
Add to
.gitattributesin your repository root:.chant/specs/*.md merge=chant-spec -
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
| Variable | Description |
|---|---|
{{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
| Variable | Description |
|---|---|
{{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
| Issue | Auto-fixable | Fix |
|---|---|---|
Missing status | Yes | Default to pending |
Invalid status value | No | Error, human decides |
Missing id | Yes | Generate from filename |
| Trailing whitespace | Yes | Trim |
| Inconsistent indentation | Yes | Normalize to 2 spaces |
| Missing newline at EOF | Yes | Add 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
- Agents write most specs - they follow the prompt, which includes validation
- Humans can still edit - lint catches mistakes before commit
- Parse errors are rare - YAML frontmatter is simple, well-supported
- 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
-
Prompt Injection: When
output_schemais present, chant automatically injects an “Output Format” section into the agent prompt with the schema definition, required fields, and an example. -
Post-Execution Validation: After the agent completes, chant extracts JSON from the agent output and validates it against the schema.
-
Linter Integration:
chant lintvalidates output for completed specs that haveoutput_schemadefined.
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:
- Code blocks:
```json ... ```or``` ... ``` - Bare JSON: Entire output is valid JSON
- Embedded JSON:
{...}or[...]patterns in text
Example Workflow
-
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 -
Create spec with schema reference:
chant add "Research bug #123" # Edit spec to add: output_schema: .chant/schemas/research.json -
Work the spec - agent sees schema in prompt
-
Validation runs automatically on completion
-
Check all specs:
chant lint
Export
Status: Implemented ✅
The
chant exportcommand 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
| Field | Description |
|---|---|
id | Spec ID |
title | First heading from spec body |
status | Current status |
created_at | Creation timestamp |
completed_at | Completion timestamp (if completed) |
labels | Labels array |
project | Project prefix (if set) |
commit | Git commit hash (if completed) |
branch | Git branch (if created) |
cost.tokens | Token count (if tracked) |
cost.usd | Cost in USD (if tracked) |
duration_s | Execution duration in seconds |
agent | Agent/model used |
prompt | Prompt 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:
- Highly variable - Every team wants different formats
- Better done by agents - LLMs excel at summarization
- Already solved - jq, csvkit, pandas, etc.
Export gives you the raw data. Use the right tool for presentation.
Initialization
Interactive Setup Wizard (Recommended)
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 specsCLAUDE.mdwith agent instructions (rules/steering).claude/skills/chant/SKILL.mdwith chant workflow skill (Agent Skills standard).mcp.jsonfor 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:
| Source | Field |
|---|---|
package.json → name | project.name |
Cargo.toml → [package] name | project.name |
go.mod → module path | project.name |
| Directory name | project.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:
- Adds
.chant/to.git/info/exclude(local gitignore, not committed) - Specs stay on your machine only
- No trace in shared repo
- 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)
| Tool | Description | Parameters |
|---|---|---|
chant_spec_list | List all specs | status, limit (optional, default 50) |
chant_spec_get | Get spec details including body content | id (required, partial match supported) |
chant_ready | List specs ready to be worked (no unmet dependencies) | limit (optional, default 50) |
chant_status | Get project status summary with spec counts | brief, include_activity (optional) |
chant_log | Read execution log for a spec | id (required), lines (optional, default: 100), offset (optional), since (optional, ISO timestamp) |
chant_search | Search specs by title and body content | query (required), status (optional) |
chant_diagnose | Diagnose issues with a spec | id (required) |
chant_lint | Lint specs for quality issues | id (optional, lints all if not provided) |
chant_verify | Verify a spec meets its acceptance criteria | id (required) |
Mutating Tools
| Tool | Description | Parameters |
|---|---|---|
chant_spec_update | Update spec status/output | id (required), status, output (optional) |
chant_add | Create a new spec | description (required), prompt (optional) |
chant_finalize | Mark a spec as completed | id (required) |
chant_reset | Reset a failed spec to pending | id (required) |
chant_cancel | Cancel a spec | id (required) |
chant_archive | Move a completed spec to archive | id (required) |
chant_work_start | Start working on a spec asynchronously | id (required), chain, parallel, skip_criteria (optional) |
chant_work_list | List running work processes | process_id (optional), include_completed (optional) |
chant_pause | Pause a running work process for a spec | id (required) |
chant_takeover | Take over a running spec, stopping the agent and analyzing progress | id (required), force (optional) |
chant_watch_status | Get watch status and active worktrees | (none) |
chant_watch_start | Start watch in background if not running | (none) |
chant_watch_stop | Stop running watch process | (none) |
chant_split | Split a complex spec into smaller member specs using AI analysis | id (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,failedlimit(optional): Maximum number of specs to return (default: 50)
Response includes:
specs: Array of spec objectstotal: Total count of matching specs (before limit applied)limit: The limit that was appliedreturned: 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 objectstotal: Total count of ready specs (before limit applied)limit: The limit that was appliedreturned: 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 JSONinclude_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,failedoutput(optional): Output text to append to spec body (or replace ifreplace_bodyis true)replace_body(optional, boolean): Replace spec body withoutputinstead 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 onlabels(optional): Array of labels to assign to the spectarget_files(optional): Array of target file paths for the specmodel(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
## Outputmarkdown 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 messagesuggestion: 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 texttype: Invalid or missing spec typemodel_waste: Using expensive model on simple specapproval: Approval schema inconsistenciesoutput: Output schema validation issuesdependency: Missing or invalid dependency referencesrequired: Missing required enterprise fieldstitle: Missing spec titleparse: 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:
- First call:
chant_log(id)→ returns content +byte_offset - 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 promptsrecursive(optional, boolean): Recursively split member specs that are still too complexmax_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 processinclude_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 completionsparallel(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_logwithoffsetorsinceparameters to monitor progress - Use
chant_statuswithinclude_activityto 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 specdone- Agent completed successfully, awaiting watch to merge and finalizefailed- Agent encountered an errorunknown- 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 watchas 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:
| Code | Message | Description | When It Occurs |
|---|---|---|---|
-32700 | Parse error | Request JSON is malformed or not valid JSON | Invalid JSON sent to stdin |
-32600 | Invalid JSON-RPC version | Request has jsonrpc field != “2.0” | Version mismatch in request |
-32603 | Server error | Internal server error during tool execution | Tool 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 codeerror.message: Human-readable error messageerror.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:
| Error | Condition | Tool(s) |
|---|---|---|
| “Chant not initialized” | .chant/specs directory doesn’t exist | All tools |
| “Missing required parameter: id” | id parameter not provided | chant_spec_get, chant_spec_update |
| “Missing required parameter: name” | name parameter not provided | tools/call |
| “Missing tool name” | Tool name is not a string or missing | tools/call |
| “Missing arguments” | arguments not provided to tools/call | tools/call |
| “Method not found” | Unknown method requested | Protocol level |
| “Unknown tool” | Tool name doesn’t match available tools | tools/call |
| “Invalid status” | Status string not in [pending, in_progress, completed, failed] | chant_spec_update |
| “No updates specified” | Neither status nor output parameter provided | chant_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 objectscontent[].type: Currently always"text"content[].text: The response data as formatted textid: 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
-
Check
jsonrpcanderrorfields: Distinguish between protocol errors and tool errors- If
erroris present, it’s a protocol-level error - If
resultcontainsisError: true, it’s a tool-level error
- If
-
Handle missing initialization: Always check for “Chant not initialized” before using tools
-
Validate parameters: Tools will return descriptive errors for missing/invalid parameters
-
Parse tool output: Tool responses have JSON in the
textfield - 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
| Component | Where | Purpose |
|---|---|---|
| Chant CLI | Binary | Feature compatibility |
| Config schema | config.md | Configuration format |
| Spec schema | Validated by linter | Spec 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
| Trigger | Automatic | Purpose |
|---|---|---|
chant lint | No | Explicit validation |
chant work | Yes | Pre-execution check |
chant add | Yes | Validate new spec |
| After agent writes | Yes | Validate 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
| Version | Changes |
|---|---|
| 1 | Initial 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
- Announcement: Deprecation notice in release notes and docs
- Warning period: CLI shows warnings when deprecated features are used
- Removal: Feature removed in next major version
Minimum Support Period
| Component | Support Period |
|---|---|
| Config fields | 2 major versions |
| CLI commands | 2 major versions |
| Spec schema fields | 2 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
---
progress → log 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:
--quietmode- Non-TTY environments
--jsonoutput 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
| Phase | Key Tests |
|---|---|
| 0 → 1 | Self-hosting: chant executes spec on itself |
| 1 → 2 | Branching, isolation |
| 2 → 3 | MCP server tools, provider config |
| 3 → 4 | Cross-repo dependencies |
| 4 → 5 | Labels, triggers, spec types, groups |
| 5 → 6 | Cost tracking, linting, error catalog, reports |
| 6 → 7 | Search, locks, queue, pools, daemon |
| 7 → 8 | Drift 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
| Component | Target |
|---|---|
| Core workflow | 90% |
| Parser | 95% |
| MCP server | 85% |
| Search | 80% |
| CLI | 70% |
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:
- Silent mode - Personal use on shared repos
- Derived frontmatter - Auto-populate fields from conventions
- 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:
| Source | Description | Example | How to Use |
|---|---|---|---|
branch | Current git branch name | sprint/2026-Q1-W4/PROJ-123 | Extract from branch naming conventions |
path | Spec file path | .chant/specs/teams/platform/task.md | Extract from directory structure |
env | Environment variable | TEAM_NAME=platform | Extract from shell environment |
git_user | Git user.name or user.email | alice@company.com | Pattern 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:
| Pattern | Source | Value | Notes |
|---|---|---|---|
sprint/(\d{4}-Q\d-W\d) | sprint/2026-Q1-W4/task | 2026-Q1-W4 | Sprint format |
([A-Z]+-\d+) | PROJ-123-auth | PROJ-123 | Jira ticket |
teams/(\w+)/ | .chant/specs/teams/platform/task.md | platform | Team directory |
(\w+)\.md | dashboard.md | dashboard | Filename |
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_usersource does not use regex patterns. The pattern must be the literal string"name"(foruser.name) or"email"(foruser.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 lintcommand - 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 completionmodel: 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:
| Feature | Tracked .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.