# features/ — Feature CRUD & Completion

See @../../CLAUDE.md for project-wide patterns.

## Status Lifecycle
`planned → in_progress → done` (also `cancelled`). No transition rules enforced — you can go `planned → done` directly.

## complete_feature Workflow
1. Validates ALL tasks with this feature_id are done or cancelled — fails with 400 listing incomplete task IDs
2. If no `pr_url` provided: auto-creates PR via `gh` CLI (`create_feature_pr()`)
3. Sets status = 'done', stores PR URL
4. Fires webhook (FEATURE_COMPLETED) and auto-merge as `asyncio.create_task` — response returns BEFORE these complete

## Gotchas
- **Branch auto-generation**: If branch not provided on create, generates `feature/{slug-from-name}` (max 50 char slug)
- **UNIQUE(project_id, branch)**: Duplicate branches per project → 409 Conflict
- **complete_feature accepts empty POST body**: `Body(default=None)` pattern — `None` body is valid
- **Deleting a feature is blocked** if any tasks reference it (FK check in routes)
- **Task counts in list response**: LEFT JOIN aggregation, only includes statuses with count > 0

## MCP Tools (3 only)
`list_features`, `get_feature`, `complete_feature` — CRUD/delete/resolve-conflicts are REST-API-only per tool policy.

## Conflict Resolution
`POST /api/features/{id}/resolve-conflicts` creates a temporary detached worktree at `.peon-tmp/conflict-resolve-{id}`, merges origin/main, pushes back. Returns conflict list on failure (graceful, no exception).
