Epic → task decomposition
Milestone 4 introduces the first review-first mutation workflow in A2C:
plan → propose → validate → review → apply
Given an Epic, A2C proposes a set of draft Tasks, validates them through a2c_core,
lets a human review the summary and details, and only writes files after an explicit apply.
Commands
a2c plan decompose-epic <EPIC_ID>
a2c show draft-task <EPIC_ID> <INDEX>
a2c apply tasks-from-epic <EPIC_ID>
Use --path / -C when not running from the repository root.
Plan
a2c plan decompose-epic loads the Epic via a2c_core, extracts front matter and key
Markdown sections (Summary, Problem, Goals, Acceptance criteria, Notes), and asks
the configured provider for a structured task list.
Each proposed task includes:
- front matter conforming to the Task schema (
type,id,title,status,epic_id, …) - a Markdown body with at least
SummaryandAcceptance criteriasections
The CLI validates every draft before caching. Invalid proposals are reported with file-level
reasons and are not written into planning/tasks/.
Review
After a successful plan, the CLI prints a one-line summary per draft (id, title,
status, optional rationale and summary line).
Inspect a single draft:
a2c show draft-task epic-onboarding 1
Drafts are stored under .a2c/cache/decompose-epic/<epic-id>/ as Markdown files that
mirror normal Task artifacts.
Apply
a2c apply tasks-from-epic loads the cached proposal, re-validates all drafts,
checks for ID and filename conflicts, then writes new files under planning/tasks/ and
updates the Epic task_ids list.
a2c apply tasks-from-epic epic-onboarding
Use --force only when you intentionally want to overwrite an existing task file with the
same id.
Safety defaults
| Rule | Behaviour |
|---|---|
| No silent writes | plan never touches planning/tasks/ |
| Untrusted AI output | Parsed and validated before caching or apply |
| ID conflicts | Apply fails if a task id already exists (unless --force) |
| Duplicate draft ids | Proposal fails validation |
| Re-run plan | Replaces the cached proposal; does not apply |
Providers (Milestone 5)
Provider choice: CLI --provider > decomposition.provider in config > mock.
| Provider | Purpose |
|---|---|
mock | Default; deterministic output or --fixture JSON (Cursor-as-model path) |
openai | Hosted OpenAI-compatible API |
local | Generic OpenAI-compatible endpoint (LM Studio, vLLM, gateways) |
ollama | Ollama preset (http://127.0.0.1:11434/v1, reachability probe) |
Configuration (.a2c/config.yaml)
decomposition:
provider: mock # mock | openai | local | ollama
model: gpt-4o-mini # provider-specific
endpoint: null # local/openai override; ollama uses OLLAMA_HOST when null
timeout_seconds: 120
max_tokens: 4096
temperature: 0.2
Environment variables
| Provider | Variables |
|---|---|
openai | OPENAI_API_KEY (required), OPENAI_BASE_URL, OPENAI_MODEL (optional) |
local | A2C_LOCAL_LLM_ENDPOINT (required if config endpoint unset), LOCAL_LLM_MODEL, LOCAL_LLM_API_KEY (optional) |
ollama | OLLAMA_HOST (optional, default http://127.0.0.1:11434), OLLAMA_MODEL, OLLAMA_API_KEY (optional) |
Examples
# Default mock (no API keys)
a2c plan decompose-epic epic-onboarding
a2c plan decompose-epic epic-onboarding --fixture path/to/response.json
# Hosted OpenAI
a2c plan decompose-epic epic-onboarding --provider openai
# Local OpenAI-compatible server
a2c plan decompose-epic epic-onboarding --provider local --endpoint http://localhost:8080/v1 --model llama3.2
# Ollama preset (assumes default listen address; not OS auto-discovery)
a2c plan decompose-epic epic-onboarding --provider ollama --model llama3.2
Provider failures are actionable and never write to planning/. The CLI does not silently
fall back to mock when a real provider is selected.
Future: native Ollama SDK via optional a2c-core[ollama] — see pathname:///a2c-assets/plans/milestone-ollama-native-sdk.md.
Optional live Ollama smoke on a self-hosted GPU runner: testing-ollama-ci.md.
Package boundaries
| Layer | Responsibility |
|---|---|
a2c_core | Parsing, validation, proposal cache, apply writes |
a2c_cli | Provider selection, human output, exit codes |
a2c_core does not depend on a2c_cli.
Limitations
- One Epic per command; no batch decomposition.
- Task drafts only — no Sprint creation or assignment.
- No automatic owner/assignee fields.
- No interactive TUI or editor integration yet.