Skip to content

Frequently asked questions

Practical answers for evaluating, installing, and operating anvil. For positioning ("why is this different from X"), see the comparison table in the README and _positioning.md. For architectural depth, see architecture.md; for design rationale, see design.md.


Getting started

Do I need a GitHub account or repository?

No. Canonical state lives locally (SQLite + JSONL) — by default in a per-project workspace under your home directory, ~/.anvil/workspaces/<dir>-<hash8>/.anvil/, not inside your repo. Run anvil status to see the exact resolved path, or set ANVIL_STATE_LAYOUT=local to keep state in-repo at ./.anvil/ instead. GitHub Issues is an opt-in sync target via the bidirectional sync engine — never the source of truth. The CLI works fully offline; init, plan, claim, submit, and apply make zero network calls.

If you do want GitHub Issues as an external projection (so non-developers can read and comment in a familiar surface), set GITHUB_REPOSITORY and either authenticate gh or export GITHUB_TOKEN, then run anvil sync github. The mappings flow both ways and conflicts are labeled rather than auto-resolved.

See design.md § Why local-first for the rationale, how-to/state-location.md for exactly where state lives and how to override it, and how-to/syncing-with-github.md for the sync setup walkthrough.

Do I need an Anthropic, OpenAI, or other LLM API key?

No, not for the core flow. The PRD parser, six-dimension scorer, and dependency inferencer are deterministic and rule-based — they ship as Python in bin/src/anvil/planning/ and run with no network.

The three optional --use-llm augmentations do not require an API key either: by default they run through the Claude Agent SDK subscription path (no API key — it uses your existing claude CLI login), with ANTHROPIC_API_KEY, Bedrock, and OpenAI-compatible custom endpoints as configurable alternatives. plan --use-llm extends short task descriptions, score --use-llm adds a trade-off paragraph to the explanation, and expand --use-llm proposes sub-tasks for tasks with complexity >= 4. The numeric scores, task IDs, dependencies, and status transitions are never touched by the model — the LLM layer is strictly additive.

expand is the only command that requires --use-llm; everything else has a deterministic baseline. For the API-key providers, the default model is claude-sonnet-4-6 with ephemeral prompt caching on by default.

See llm.md for the full augmentation contract.

Which agent runtimes does anvil work with?

Any MCP-compatible runtime. The plugin is built first for Claude Code (where the skills, agents, and hooks compose natively), but the CLI and FastMCP stdio server are runtime-neutral. anvil is documented as working with Claude Code, Codex, Cursor, OpenHands, and Copilot.

Two surfaces are always available:

  • CLI (anvil <cmd>) — runtime-agnostic; any shell-capable agent can call it via Bash, and humans use it directly.
  • MCP server — 36 registered tools over FastMCP stdio; the lean execution default serves 24 on the wire (set ANVIL_MCP_PLANNING=1 to add the 12 planning tools). Any MCP client connects; tool responses are structured JSON with explicit error envelopes.

When a runtime cannot speak MCP (Cursor has no shell, some Copilot modes have no stdio), the CLI surface is the fallback. Hooks are Claude Code-specific because they use the SessionStart / PreToolUse / PostToolUse contract — agents in other runtimes still get full coordination via claims + leases, just without the editor-time warning.

See architecture.md § CLI / MCP / hooks surface and _positioning.md.


Day-to-day operation

My claim lease expired — what happens to my task?

The next mutating CLI or MCP call invokes detect_and_release_stale(), which releases your expired lease with release_reason="stale". The task returns to ready and becomes available to other actors. The audit event preserves the original claimant, so the history is not lost.

To resume work yourself, run anvil claim T001 again. If another actor claimed it in the meantime, the call exits non-zero with task already claimed by <actor>; pass --force to take it over (logged in the audit trail), or run anvil next to pick a different ready task.

Default lease is 240 minutes (configurable in the resolved config.yaml); the lease is extended by anvil renew <claim-id> or by the MCP renew_claim tool. Long-running work should heartbeat every few minutes — see architecture.md § Concurrency model for the four layered mechanisms (SQLite WAL + BEGIN IMMEDIATE, leases, heartbeats, stale reaping). For work performed outside Anvil's file-change hooks, submit a canonical claim-bound artifact with anvil progress <task> <phase> --attestation-file <path> (or MCP submit_progress.attestation_base64) before renewing. The artifact is consumed once; an ordinary progress note never extends the lease.

Full walkthrough: how-to/claiming-and-shipping-a-task.md.

Two agents want to work on the same task — what happens?

First to call claim wins. The claim transaction runs inside SQLite's BEGIN IMMEDIATE mode, so concurrent claimers serialize at the database layer. The losing call exits non-zero with task already claimed by <actor> and prints the active claim id and lease expiry.

The losing actor's options:

  • Run anvil next to get the next ready task with no conflict.
  • Wait for the lease to expire or for the holder to release voluntarily.
  • Pass --force to override (logged as a claim.force_released event so the takeover is auditable).

A second safety layer catches overlap before the claim attempt: if the target task shares a ConflictGroup with an already-claimed task, anvil next will not surface it and claim will warn via pre-claim conflict check. See architecture.md § Concurrency model.

Does --use-llm cost money?

Yes — Anthropic charges per token. The deterministic path is free and always available. The LLM layer is opt-in per command.

Cost-shaping defaults are in place: temperature=0.0 for repeatability, and prompt caching is on by default (every Anthropic call sends the system block with cache_control: {"type": "ephemeral"}). A typical score --use-llm run against a 20-task batch hits the 5-minute ephemeral cache on tasks 2–20 and pays only for the cold system block plus per-task user and output tokens.

Per-call output ceilings are bounded by named constants: _SCORE_EXPLAIN_MAX_TOKENS (300), _DESCRIPTION_ENRICH_MAX_TOKENS (400), and _EXPAND_MAX_TOKENS (2000). expand is the heaviest call but is gated on complexity >= 4 and invoked one task at a time.

If the LLM call fails mid-operation, the engine falls back to the deterministic baseline and emits a stderr warning — the operation never aborts mid-batch. See llm.md § Cost notes.

How do I migrate from agent-*-status.md markdown files?

There is no shipped migration tool. The recommended path is:

  1. anvil init --name "<project>" — scaffolds .anvil/.
  2. Author .anvil/prd.md against the schema in prd-template.md. Existing intent / acceptance criteria from your markdown status files map cleanly into PRD task blocks.
  3. anvil prd parse then prd review --approve to promote the PRD to approved.
  4. anvil plan then score to materialize tasks and dependencies.
  5. Once tasks exist in the database, the old agent-*-status.md files can be deleted. Their role (per-agent status notes) is replaced by claim rows, evidence, and the events.jsonl audit log.

An automated importer is not on the roadmap — PRD authoring is a thinking exercise as much as a data-entry one, and copy-pasting forces the author to revisit intent. Community contributions for a migrator would be welcome; open an issue describing your source format.

See how-to/getting-started.md and how-to/authoring-a-prd.md.

I upgraded Anvil, but SessionStart or MCP still reports the old version. What now?

A harness can keep an already-running MCP process alive after the executable on disk is upgraded, and a plugin can be newer or older than the independently installed CLI on PATH. Do not delete state.db to repair that skew.

Follow the upgrade matrix and verification runbook: resolve the actual anvil and anvil-mcp executables, compare engine and database schema, refresh the harness plugin/config, fully restart the harness, and inspect the new MCP initialize serverInfo.version. If the database is newer than the engine, upgrade the stale engine/plugin and restart; preserve the state directory so the compatible engine can open it.


Hooks, storage, and concurrency

How do I temporarily disable a hook?

The five hooks are wired in hooks/hooks.json at the SessionStart, PreToolUse, and PostToolUse events — including heartbeat, which fires at PostToolUse on Edit/Write/NotebookEdit and Bash and renews the active lease. Every entry's command runs the shell-free dispatcher — uv run --project bin python -m anvil.cli hook dispatch <name> — there is no .sh script file for the manifest to resolve.

To disable one hook without uninstalling the plugin, delete or comment out its block in hooks.json and restart your Claude Code session. To turn every hook off at once, disable or uninstall the anvil plugin through your harness's plugin configuration; every hook also fast-paths to a silent no-op if it can't resolve any anvil state for the project.

All five hooks are non-blocking by design: each anvil hook dispatch ... call wraps its body in try/except Exception: pass and always exits 0, regardless of internal failure. A hook that errors out internally already behaves like a disabled hook: it warns once to stderr and gets out of the way. See design.md § Why hooks are non-blocking.

To debug a hook that is misbehaving, run the dispatcher directly with a sample payload on stdin to inspect its stderr: echo '{}' | uv run --project bin python -m anvil.cli hook dispatch <name>. A ANVIL_HOOK_DEBUG=1 env var that redirects hook stderr to .anvil/.hook-debug.log is tracked as a Phase 11 backlog item (P11-HK-C2) but does not ship today.

Where does my data live, and should I commit it to git?

By default, nowhere near your repo. State lives in a per-project workspace under your home directory, keyed by the project's canonical git repo:

~/.anvil/workspaces/<dir>-<hash8>/.anvil/
├── config.yaml         # project-level config (sync providers, lease defaults)
├── state.db            # SQLite, WAL mode — the canonical state
├── events.jsonl        # append-only audit log (replay source)
├── prd.md              # PRD source (you edit this)
└── packets/            # generated work packets (per-task markdown / json)

<dir>-<hash8> is the repo basename plus a short hash of its absolute path, so two projects that share a folder name never collide, and every git worktree of the same repo resolves to the same workspace. anvil status prints the exact resolved path on its Path: line. See how-to/state-location.md for the full resolution order (including ANVIL_ROOT and ANVIL_STATE_LAYOUT).

Because the default layout lives outside the repo, there is nothing under .anvil/ for git clone to carry — the "commit it to git" question only applies if you opt back into the old in-repo layout. Two ways to keep your state durable:

  • Back it up out-of-band (default layout). Use anvil backup / anvil restore (see Backup and recovery below), or cp -R the workspace directory yourself. Nothing here touches git.
  • Pin an in-repo state dir and commit it. Set ANVIL_STATE_LAYOUT=local so state resolves to <repo>/.anvil/ again, then pick one of two commit policies:
  • Commit everything. State, audit log, and packets all survive git clone. Simplest for solo work or small teams. Beware: state.db is binary and merge conflicts are unrecoverable manually (use replay instead).
  • Gitignore state.db (and *.wal, *.shm) but commit events.jsonl. The replay guarantee means state.db is regenerable from the event log; this avoids binary merge conflicts while preserving audit history across clones.

See architecture.md § Storage layout and design.md § Why local-first.

Can I inspect state with sqlite3 or SQLite Browser?

Yes. state.db is a standard SQLite file in WAL mode — any SQLite tool works. Resolve its path without opening the database (by default ~/.anvil/workspaces/<dir>-<hash8>/.anvil/state.db, not a path inside your repo):

STATE_DIR=$(anvil status --path-only)
sqlite3 "$STATE_DIR/state.db" .schema
sqlite3 "$STATE_DIR/state.db" "SELECT id, status, title FROM tasks;"

The engine's current schema version is reported by anvil --version (v22 in this release). Supported historical databases are auto-upgraded through the ordered, additive migration chain when the backend opens (anvil migrate state is dry-run by default; pass --yes to apply it deliberately). Pydantic models in bin/src/anvil/state/models.py define every entity; the SQLite implementation lives in bin/src/anvil/state/sqlite.py.

Read-only inspection is safe and concurrent — WAL mode lets readers proceed without blocking the CLI's writers. Do not edit rows directly: state mutations should go through the CLI or MCP server so the corresponding event lands in events.jsonl (the replay guarantee depends on every mutation being represented in the log).


Backup and recovery

How do I back up .anvil/?

Copy the state directory wholesale — resolve its path without opening the database (by default ~/.anvil/workspaces/<dir>-<hash8>/.anvil/, not a path inside your repo):

STATE_DIR=$(anvil status --path-only)
cp -R "$STATE_DIR" "/backup/location/anvil-$(date +%Y-%m-%d)"

That captures state.db, events.jsonl, prd.md, config.yaml, and any generated packets. Quiesce every Anvil writer before a file-level copy or restore, and preserve the complete state directory as one unit, including any *.wal and *.shm sidecars. Do not copy only the live database or discard its sidecars.

The replay guarantee (see next question) means events.jsonl alone is enough to reconstruct state.db, so the audit log is the minimum you must preserve. Because the default layout keeps state outside your repo, git commit doesn't help here unless you've pinned an in-repo state dir with ANVIL_STATE_LAYOUT=local — otherwise back the audit log up the same way as the rest of the state directory, or use anvil backup below.

Shipping today: anvil backup pushes events.jsonl (and optionally state.db, with --include-db) to a configured durable_store: s3, and anvil restore pulls it back and rebuilds state via replay. cp -R remains the fully-local flow. A native anvil snapshot subcommand (a local sqlite3 .backup wrapper with retention) is on the roadmap — see roadmap.md § Snapshot / replay, item P9B-7 — but is not yet shipped; anvil backup / restore and the anvil replay command (see the next question) are the supported recovery paths today.

What if state.db gets corrupted?

First quiesce every Anvil writer and preserve the complete resolved state directory for rollback and forensics. If a configured S3 backup exists, use the supported anvil restore flow. Otherwise retain the local snapshot and treat any live replacement as a separately authorized managed recovery. Use the shipped anvil replay command to build a separate projection for inspection:

STATE_DIR=$(anvil status --path-only)
anvil replay \
  --from-events "$STATE_DIR/events.jsonl" \
  --into "/safe/scratch/rebuilt-state.db"

anvil replay --from-events <path> --into <path> reads every event from the source JSONL and replays it into a fresh SQLite database at --into. It refuses to target the live state.db directly. Inspect and compare that scratch projection; replay itself stops there. Replacing live state is a separate, explicitly authorized recovery operation that must keep writers quiesced and retain the complete original state directory as a verified rollback. See the migration replay audit.

The replay guarantee is the central audit property of the engine: replaying every event from events.jsonl against an empty database reconstructs canonical SQLite state exactly. That property is what makes events.jsonl the minimum you must preserve. Since the default layout keeps it outside your repo, back it up the same way as the rest of the state directory (or push it with anvil backup, see above). If you've pinned an in-repo state dir with ANVIL_STATE_LAYOUT=local, committing events.jsonl to git also gives you a distributed audit log recoverable from any clone even if every local state.db is lost.

Event ids are assigned inside the mutating transaction, not before it, so the JSONL ordering is consistent with the SQLite commit order. See architecture.md § Event log and JSONL replay.


Roadmap and contributing

When will Linear, Monday, or Jira support land?

None yet — github_issues is the only sync provider that ships today. On the roadmap, per roadmap.md:

  • LinearIssuesProvider (GraphQL transport, item P9B-1) and MondayBoardsProvider (REST + JSON with people-columns, item P9B-2). Both are OPEN and in development. Webhook-based sync (P9B-5) is SPEC-FIRST alongside them.
  • JiraIssuesProvider (per-project workflow discovery, P9B-3) and GitHubProjectsProvider (Projects v2 board surface, P9B-4). Both OPEN, as follow-on work after Linear/Monday land.

The SyncProvider Protocol has already shipped and is deliberately registry-driven so contributors can add providers without engine changes. If you want to add one now rather than wait, see the next question.

How do I write my own sync provider?

Implement the SyncProvider Protocol from bin/src/anvil/sync/provider.py and register it in registry.py. The GitHubIssuesProvider at sync/providers/github_issues.py is the reference implementation — read it alongside the contributor guide at sync-providers.md, which walks through the Linear case step by step.

Per-provider acceptance criteria (from roadmap.md § Next → Theme: Sync providers): provider module + transport (GraphQL or REST) + full-lifecycle respx tests + live nightly workflow gated on the provider's API key secret + anvil sync <provider_id> --health works.

Provider config schemas in config.yaml (item P9B-9) are co-required with the first new provider — that work is SPEC-FIRST and tracked in the same Next bucket.

How do I contribute?

Open a pull request against github.com/fakoli/anvil. No dedicated CONTRIBUTING.md currently ships; use the README's "Status" section and the roadmap.md item taxonomy (Phase 11 backlog items prefixed P11-XX-XN, Phase 9 carry-forward items prefixed P9B-N).

Three contribution shapes most appreciated right now:

  • Sync providers. Linear / Monday providers are on the roadmap but community implementations are welcome. Follow sync-providers.md.
  • Phase 11 backlog batches. 56 SHOULD FIX / CONSIDER / NIT items tracked in phase-11-backlog.md; the cross-cutting themes in roadmap.md indicate which items batch cleanly.
  • Test coverage. Keep the local pytest suite and live-test documentation current when adding a surface or provider; live-tests.md describes the nightly workflow.

For new architectural choices (a second backend, a daemon, a webhook listener), write a SPEC-FIRST design doc under docs/specs/ before opening a PR — the SPEC-FIRST roadmap items are the precedents to mirror.