anvil v0 — Build Spec¶
Historical design record (2026-05-24). This document preserves Anvil's original v0 implementation target for provenance; it is not a current operational reference. Commands, defaults, hook counts, and lifecycle rules below may be superseded. Use the current architecture, CLI reference, MCP reference, hooks reference, and migration guide when operating v0.6.5 or later.
Context¶
anvil is the third pillar of the Fakoli plugin ecosystem (fakoli-flow = how work moves, fakoli-crew = who does the work, anvil = what is true). It's a Claude Code plugin that provides a local-first, backend-neutral, LLM-optimized project state layer that humans and multiple coding agents can coordinate around. It turns rough ideas and PRDs into reviewed, lockable, agent-ready work packets.
The product is informed by three design documents:
- agentic_project_state_design_brief.md — the original vision (PRD authoring, task decomposition, claims/locks, work packets, evidence-based completion, MCP interface).
- competitive_gap_analysis_agentic_project_state.md — competitive positioning, especially against CCPM and against issue-tracker-as-state patterns.
- fakoli_plugin_primer_for_agentic_workflows.md — the operating model: plugin-first, with skills encoding workflow choreography, hooks enforcing rules, CLI handling pure state ops, MCP as the external surface.
Two distinct wedges define the product:
Against CCPM and issue-tracker-as-state — the "5 must-do-better" list: 1. Richer canonical state than issue text (Pydantic models + SQLite vs. issue body markdown). 2. Explicit claim/lock/lease model (not implied by assignment/labels). 3. Better LLM work packets (compact, task-specific, with constraints and non-goals). 4. Six-dimension scoring (complexity, parallelizability, context load, blast radius, review risk, agent suitability). 5. Runtime-neutral integration (Claude Code, Codex, Cursor, OpenHands, Copilot, local agents — via CLI + MCP).
Against the current Fakoli markdown-status-file model — the operating discipline:
- Canonical state moves from scattered markdown into a durable SQLite database with append-only event log.
- Claims are no longer conventions in agent-*-status.md files; they are enforced rows with leases and heartbeats.
- File ownership becomes a real check, not a status-file comment.
- Evidence is required and validated, not just claimed.
The intended outcome of v0 is a usable open-source plugin that demonstrates both wedges and integrates cleanly with fakoli-flow and fakoli-crew.
Goals¶
- Plugin-first packaging following the primer's "maximum use of plugins" reference architecture.
- Local-first by default; canonical state in SQLite under
.anvil/, never in an issue tracker. - Skills for workflow choreography: start-prd, prd, plan, claim, execute, verify, finish, state-ops.
- CLI binary in
bin/anvilfor pure state operations — called by skills, hooks, agents, humans, and external tools. - MCP server (FastMCP, stdio) wired via
.mcp.json, exposing 13 agent-facing tools. - Hooks that enforce claim discipline, record file changes, and capture evidence — rules the model would otherwise forget.
- Plugin-owned agents for state-specific roles (planner, critic, sentinel, state-keeper) that defer to fakoli-crew when it's installed.
- Bidirectional GitHub Issues sync as an opt-in projection of canonical state.
- Auto-generate
agent/<task>-<slug>branches (optional worktrees) onclaim; recorded on the Claim. - Soft review gate: tasks generate freely from any PRD draft;
claim_taskrefuses while PRD isdraft. - Hybrid LLM model: deterministic template-based PRD parser always available; LLM helpers (Anthropic provider in v0) augment.
- Replayability: full event log in append-only JSONL; replaying from empty reconstructs canonical SQLite state exactly.
- Integration:
fakoli-flowskills read/write anvil as workflow proceeds;fakoli-crewagents consume work packets and submit evidence.
Non-Goals (v0)¶
- Hosted SaaS / web dashboard.
- Real-time collaborative editing.
- Multi-backend sync targets beyond GitHub Issues (GitLab, Jira, Linear deferred to v0.2).
- Multi-provider LLM abstraction beyond Anthropic (provider interface present; only one impl ships).
- Webhook-based GitHub sync (polling-only in v0).
- Daemon / long-running service (MCP server is the only long-running process and only per-agent-session).
- Performance benchmarks / regression gates.
- Bundling a single static binary (Python source ships in
bin/, uv resolves deps on first run).
Architecture¶
Plugin layout¶
The plugin lives at ~/ai-code/claude-env/fakoli-plugins/plugins/anvil/:
anvil/
├── .claude-plugin/
│ └── plugin.json # name, version, description, author, repo, license, keywords
├── README.md # positions against CCPM, links docs, install instructions
├── CHANGELOG.md
├── LICENSE
├── .mcp.json # wires bin/anvil-mcp as the MCP server
├── settings.json # plugin defaults (e.g. default lease duration, llm provider)
├── docs/
│ ├── prd-template.md # the structured PRD template users author against
│ ├── architecture.md
│ ├── mcp.md
│ ├── hooks.md
│ ├── github-sync.md
│ └── integration-flow-crew.md # how anvil plugs into fakoli-flow + fakoli-crew
├── skills/ # workflow choreography
│ ├── start-prd/SKILL.md # rough idea → PRD (with optional fakoli-flow:brainstorm bridge)
│ ├── prd/SKILL.md # author/review/approve PRD
│ ├── plan/SKILL.md # PRD → features → tasks → scores → expand → ready
│ ├── claim/SKILL.md # agent-facing claim flow (called from execute)
│ ├── execute/SKILL.md # claim → packet → work → submit (or hand to fakoli-flow:execute)
│ ├── verify/SKILL.md # sentinel verification on evidence
│ ├── finish/SKILL.md # apply + ship decision (merge/PR/keep/discard)
│ └── state-ops/SKILL.md # general inspection: list, show, next, status, conflicts, sync
├── agents/ # plugin-owned specialists; defer to fakoli-crew when installed
│ ├── planner.md # PRD → features/tasks (uses LLM if configured)
│ ├── critic.md # reviews work packets, plans, and code (fallback when fakoli-crew:critic absent)
│ ├── sentinel.md # validates evidence (fallback when fakoli-crew:sentinel absent)
│ └── state-keeper.md # state reconciliation, sync, audit
├── hooks/
│ ├── hooks.json # event mappings
│ ├── detect-state.sh # SessionStart: detect plugins + project state
│ ├── check-claim.sh # PreToolUse on Edit/Write: warn if editing files outside claimed scope
│ ├── record-file-change.sh # PostToolUse on Edit/Write: append to events.jsonl
│ └── capture-evidence.sh # PostToolUse on Bash: capture test output for active claims
├── monitors/ # optional — defer to v0.2
└── bin/
├── anvil # bash wrapper → uv run python -m anvil.cli
├── anvil-mcp # bash wrapper → uv run python -m anvil.mcp_server
├── pyproject.toml # uv-managed Python project (Hatchling)
├── uv.lock # locked dependencies
└── src/anvil/ # Python source
├── __init__.py
├── cli.py # Typer app — ~15 state-op commands
├── mcp_server.py # FastMCP server — 13 agent-facing tools
├── config.py
├── clock.py # Clock protocol + SystemClock + FrozenClock (test)
├── state/ # backend, sqlite, schema, models, transitions
├── planning/ # template, llm, scoring, inference
├── context/ # packets
├── review/ # gates
├── claims/ # manager, stale
├── git_ops/ # branch, worktree
└── sync/ # github, client, mapping
(Full file tree is in the approved plan at ~/.claude/plans/i-need-evaulate-the-ticklish-cherny.md — this spec is the canonical reference, that plan was the brainstorm output.)
Per-project state directory¶
anvil init creates this inside the user's project (NOT inside the plugin):
<user-project>/.anvil/
├── config.yaml # project-level config
├── state.db # SQLite — canonical state
├── events.jsonl # append-only audit/event log
├── prd.md # the PRD source
├── packets/ # generated work packets
└── snapshots/ # opt-in periodic snapshots (created on first `anvil snapshot`)
Component responsibilities¶
| Layer | What it does |
|---|---|
| Plugin manifest | Discoverability, versioning, metadata |
| Skills | Workflow choreography — one-question-at-a-time, propose approaches, gate transitions |
| Agents | Specialized workers; defer to fakoli-crew when installed |
| Hooks | Enforcement the model would forget |
| MCP | External capability surface for any agent runtime |
| CLI | Pure state operations — CRUD + computation, no choreography |
| State engine | Backend protocol; SQLite + JSONL impl in v0 |
| Planning engine | Template-first PRD parser; LLM helpers always optional |
| Context engine | Renders work packets (markdown + JSON) |
| Review engine | Pure functions enforcing transition gates |
| Claims manager | Atomic SQLite transactions; stale detection on every op |
| Git ops | Auto-create agent/<task>-<slug> branch on claim |
| Sync engine | Bidirectional GitHub Issues sync (polling only) |
CLI command set¶
anvil init # scaffold .anvil/ in cwd
anvil prd parse # re-parse prd.md into state
anvil prd review --approve # transition PRD draft → reviewed → approved
anvil plan # generate features + tasks from parsed requirements
anvil score [TASK_ID] # populate six-dim scores; --use-llm to augment
anvil expand TASK_ID # break into subtasks
anvil review tasks # promote drafted → reviewed → ready
anvil list [--status X]
anvil show TASK_ID
anvil next # pick highest-priority claimable task
anvil claim TASK_ID [--worktree]
anvil release TASK_ID|--force
anvil renew TASK_ID
anvil packet TASK_ID [--format md|json]
anvil submit TASK_ID --commands ... --files-changed ...
anvil apply TASK_ID # human review → accepted → done
anvil status # active claims, blockers, sync state
anvil conflicts # show conflict groups + overlapping claims
anvil sync [github] [--watch] [--fix]
anvil replay --from-events events.jsonl
start-prd, full review, verify, compact from the brief move into skills; the CLI keeps only the underlying state ops.
Distribution: contribute via PR to the fakoli-plugins repo; users install via /plugin install anvil from the fakoli marketplace. Python source ships in bin/src/; uv resolves deps on first invocation. Wrapper scripts shell out to uv run.
Data Model¶
Pydantic v2 models in bin/src/anvil/state/models.py are the single source of truth. SQLite DDL is generated from them at startup (or via anvil migrate).
ID formats: T001, F001, R001, T001.1 (subtask), C001, D001, E000001, V001, EV001.
Entities (each = Pydantic model + SQLite table): Project, PRD, Requirement, Feature, Task, Score (embedded on Task), Verification (embedded on Task), Claim, Evidence, Decision, Review, Event, SyncMapping, ConflictGroup.
Task lifecycle:
proposed → drafted → reviewed → ready → claimed → in_progress
├─→ blocked → in_progress
└─→ needs_review → accepted → done
reject path: needs_review → rejected → drafted
stale claim: claim.stale event returns task to ready
(claim goes stale; task status does NOT)
Scoring scale (1-5 per dimension):
- complexity, parallelizability, context_load, blast_radius, review_risk, agent_suitability.
- complexity ≥ 4 triggers expand recommendation.
- agent_suitability drives orchestration routing.
Work packets are derived views, regenerated from canonical state. Two forms: markdown (in packets/T001.md) and JSON (from MCP generate_work_packet).
Data Flows (summary)¶
- PRD authoring —
/anvil:start-prdskill drives dialogue (can bridge tofakoli-flow:brainstorm), writesprd.md→anvil prd parse→/anvil:prd reviewgates draft → reviewed → approved. - Planning —
/anvil:planskill: optionally dispatchesplanneragent (orfakoli-crew:guido);anvil plancommits skeleton;score [--use-llm]populates dimensions;expandforcomplexity ≥ 4;review taskspromotes drafted → reviewed → ready. - Claim and execute —
/anvil:execute(orfakoli-flow:execute):next→claim T012(gate: PRD reviewed) → auto-create branch/worktree →packet T012→ agent works → heartbeat every 5 min viarenew T012→submit T012(auto-releases claim) →/anvil:finishdrives apply + ship decision. - Conflict detection — pre-claim warns (not blocks) on
expected_filesoverlap with active claims;--forceto override; logged.check-claim.shhook ALSO warns on Edit/Write outside claimed scope. - Stale claim recovery — on every CLI/MCP op, scan claims with expired leases → mark stale, return tasks to pool.
release --forcefor manual override. - GitHub sync (bidirectional) —
sync github: create issues for new tasks; reconcile changes; conflict resolution per configured strategy. Polling only. - Reconciliation (
syncwithout target) — cross-check SQLite with file system + git; report orphans;--fixwith prompt.
Hooks (the enforcement layer)¶
hooks/hooks.json wires four hooks:
| Event | Script | What it enforces |
|---|---|---|
SessionStart |
detect-state.sh |
Prints one-line summary: language, fakoli-crew/fakoli-flow availability, active claims, ready tasks, blockers |
PreToolUse (Edit, Write, NotebookEdit) |
check-claim.sh |
Warns if file modified is outside expected_files of any active claim; non-blocking |
PostToolUse (Edit, Write, NotebookEdit) |
record-file-change.sh |
Appends file_changed event to events.jsonl |
PostToolUse (Bash) |
capture-evidence.sh |
Captures stdout/stderr/exit code of registered verification commands into the claim's pending evidence buffer |
All hooks shell out to ${CLAUDE_PLUGIN_ROOT}/bin/anvil. None block; they warn, log, capture. Follow fakoli-flow/hooks/ patterns (no piped grep, no set -e, proper ${CLAUDE_PLUGIN_ROOT} usage).
MCP Server¶
.mcp.json wires bin/anvil-mcp as a stdio MCP server. Thirteen agent-facing tools:
get_project_summary list_tasks get_task
get_next_task claim_task release_task
renew_claim generate_work_packet submit_progress
submit_completion_evidence check_conflicts get_dependency_graph
update_task_status
Errors return structured {code, message, target_id, payload}.
Error Handling¶
Hard rules:
- No silent fallback on parse errors. Surface; leave last-good state intact.
- No automatic destructive recovery. --fix, --force, force-release require explicit flags.
- Events log before mutation. Aborted mutations become error.transaction_aborted; replay ignores aborted events.
Categories: transition errors (Review engine), concurrency (SQLite BEGIN IMMEDIATE + WAL), schema validation (Pydantic), git failures, GitHub sync, LLM provider failures, recovery via sync --fix, hook failures (logged, non-blocking).
--dry-run and --verbose global flags on every mutating CLI command. All errors logged to events.jsonl as error.* actions.
Testing¶
Layers: unit (<100ms total), component (per-engine with real temp SQLite, <2s/test), CLI integration (Typer CliRunner, <5s/test), MCP integration (FastMCP test client, <5s/test), end-to-end (multi-command scenarios, <15s/scenario), hook smoke (bats-style real shell). Full suite under 90s locally.
Hard rules:
- Real SQLite in every test that touches state.
- No time.sleep for lease/heartbeat tests. Clock protocol with FrozenClock.
- LLM is mocked. LLMProvider protocol with RecordedLLMProvider.
- GitHub sync via responses HTTP mocks. Nightly @pytest.mark.live_github against a real test repo.
- Git ops use a real local git repo (tmp git init per test).
- Hooks tested as real shell scripts.
Coverage targets: 85% overall; 95% on state/, 95% on claims/, 90% on review/, 80% on CLI.
CI matches fakoli-plugins conventions. Steps for anvil: uv sync → ruff → mypy → pytest with coverage gate → hook smoke → scripts/generate-index.sh --check → marketplace.json regen check. Nightly cron for live_github.
Integration with fakoli-flow and fakoli-crew¶
fakoli-flow:
- flow:execute detects anvil; when both installed, dispatches by reading anvil next and calling anvil claim before each wave. Status files replaced by anvil submit.
- flow:verify calls anvil status and dispatches sentinel only on tasks with submitted evidence.
- flow:finish calls anvil apply per accepted task before merge/PR.
fakoli-crew:
- All crew agents gain access to anvil-mcp MCP tools when anvil is installed.
- Plugin-owned agents/critic.md and agents/sentinel.md defer to fakoli-crew when its agents are detected.
When anvil is absent, fakoli-flow + fakoli-crew continue to work via existing markdown-status conventions. Integration is opt-in.
Critical Files¶
.claude-plugin/plugin.json— plugin manifest; must validate against marketplace schema. Build first (smith).bin/src/anvil/state/models.py— Pydantic models; all other modules import from here. Second.bin/src/anvil/state/sqlite.py— Backend impl. Must be correct before any engine work.bin/src/anvil/state/transitions.py— Pure transition table. Drives Review engine.bin/src/anvil/claims/manager.py— Concurrency-critical; test extensively with real SQLite.bin/src/anvil/planning/template.py— Deterministic PRD parser; defines user-facing template contract.bin/anvilandbin/anvil-mcp— bash wrappers thatuv runPython modules.skills/state-ops/SKILL.md— first skill; validates the skill structure.hooks/detect-state.sh— SessionStart hook; validates hook plumbing.docs/prd-template.md— user-facing contract for what the template parser accepts.
External utilities to reuse:
- uv for env + tool install + dependency management.
- Typer for CLI.
- Pydantic v2 for models + validation.
- FastMCP for the MCP server.
- responses library for HTTP-mocked GitHub tests.
- gh CLI for GitHub sync auth.
- fakoli-plugins infrastructure: scripts/generate-index.sh, update-index.yml GitHub Action, marketplace.json conventions.
Verification¶
End-to-end smoke (first public demo per primer):
/plugin install anvil
anvil init --name "Test Project"
$EDITOR .anvil/prd.md
anvil prd parse && anvil prd review --approve
anvil plan && anvil score && anvil expand T001 && anvil review tasks
anvil next
anvil claim T001 # auto-creates branch agent/t001-<slug>
anvil packet T001
anvil renew T001
anvil submit T001 --commands "pytest" --output-file out.log --files-changed src/foo.py
anvil apply T001
# Integration
/flow:execute "docs/plans/test-plan.md"
# GitHub sync
gh auth status && anvil sync github
# Reconciliation
anvil sync --fix --yes
Test suite + plugin validation:
uv run --project bin ruff check bin/src
uv run --project bin pytest tests/test_marketplace.py tests/test_install_manifests.py -q
Historical note: the original validation plan also called for full-suite coverage and full-tree strict mypy. Those unbounded baselines are not currently advertised as passing commands; strict mypy remains owned by its remediation milestone.
Historical replay acceptance target: rebuilding an empty projection from the
event log had to reproduce canonical state. The original destructive in-place
example has been removed because current state may use the shared HOME layout
and replay requires a separate --into destination. Follow the current,
scratch-only migration replay audit instead.
Phasing (8 phases, each its own PR into fakoli-plugins)¶
- Plugin skeleton:
.claude-plugin/plugin.json+ README + LICENSE + CHANGELOG + initial CI wiring +bin/wrappers +pyproject.toml+uv.lock+ minimalcli.py(--versiononly). - State engine: models + SQLite backend + JSONL event log +
init/statusCLI + state-ops skill +detect-state.shhook + tests. - Planning engine (template path):
prd parse/prd review/plan/score/expand/review tasks/list/showCLI + prd/plan skills + planner agent + tests. - Claims manager:
claim/release/renew/nextCLI + git_ops + claim skill + check-claim.sh + record-file-change.sh hooks + tests. - Context engine:
packet/submit/applyCLI + Review engine apply gate + execute/finish skills + capture-evidence.sh hook + critic + sentinel agents + tests. - MCP server (13 tools) +
.mcp.json+bin/anvil-mcpwrapper + tests + docs/mcp.md. - LLM augmentation: Anthropic provider +
--use-llmflags + RecordedLLMProvider tests + start-prd skill bridges to fakoli-flow:brainstorm. - GitHub sync (bidirectional) +
syncCLI + sync engine + state-keeper agent + tests + docs/github-sync.md + reconciliation + release prep + marketplace.json regen + CHANGELOG for 1.0.0.