Skip to content

anvil architecture

Condensed reference for the current v0.6.13 standalone state. For the original v0 vision and aspirational items, see specs/2026-05-24-anvil-v0.md. For what is planned but not yet shipped, see roadmap.md.

This document is intentionally short — readable in ten minutes — and describes only what exists on disk today. Every entity, command, tool, and hook listed here has a file pointer; if it isn't pointed at, it doesn't ship.


Mental model

anvil is to agentic software work what Terraform is to infrastructure: a canonical state file holds the project record, derived views (work packets, markdown plans, dependency graphs) are projected from it, and the plan-then-apply rhythm gates execution behind review. The PRD is the configuration; the SQLite database is the state; anvil apply is the commit point that records evidence and transitions a task to done. Drift (stale claims, orphan branches, sync conflicts) is detected and reconciled, not papered over.

A single project holds several release-scoped PRDs in one state.db and one events.jsonl. Each PRD is a separately-gated, revisable plan that carries a target version/tag (a release or milestone); its requirements, features, and tasks are partitioned by an owning prd_id. The single-PRD project is just the degenerate case — one default PRD owning every row. Because each PRD gates independently, the effective claim gate keys on the task's owning PRD: a task is claimable only after its PRD is approved for its exact current source/material lineage, even while a sibling PRD is still draft. Conflict detection, by contrast, spans all PRDs — two tasks in different PRDs that touch the same file land in one conflict group, so the single-winner guarantee holds across the whole project, not per-PRD. See _positioning.md for the per-PRD-as-scoped-stack framing.

The full positioning (the five differentiators and the Terraform analogy) is maintained in _positioning.md; this document does not duplicate that material.

Behaviour-first PRD readiness

The PRD is an executable contract across five layers: PRD intent → user behaviour → technical design → task proof → outcome evidence. Its optional ## Assumptions section stores typed records (A### ID, statement, rationale, and optional requirement references) in the canonical PRD projection. Parse and revision events carry those records, SQLite persists them as JSON, and missing fields default safely for old event logs.

anvil prd assess and the planning-tagged assess_prd MCP tool run a pure, deterministic, read-only assessment over the parsed PRD. Findings include a source location, explanation, and challenge question. They are advisory: neither surface changes parsing, status gates, claims, approval, or evidence. The parser’s optional EARS/Gherkin recognition informs the assessment; it does not introduce a required grammar.

The planner receives all active assumptions. Packet generation then filters them to global assumptions plus those whose requirement references intersect the task’s feature, keeping autonomous defaults visible to implementation and review without leaking unrelated product context.


Component layers

graph TD
    User["Human developer"]
    Agent["AI coding agent<br/>(Claude Code, Codex, Cursor, ...)"]

    subgraph Entry["Entry surfaces"]
        CLI["CLI<br/>anvil &lt;cmd&gt;"]
        MCP["MCP server<br/>FastMCP stdio<br/>36 tools (24 on default surface)"]
        Hooks["Hooks<br/>SessionStart / PreToolUse / PostToolUse"]
    end

    subgraph Plugin["Plugin layer (operating discipline)"]
        Skills["Skills<br/>start-prd, prd, plan, claim,<br/>execute, finish, state-ops,<br/>resolve-decisions"]
        Agents["Plugin agents<br/>planner, critic, sentinel,<br/>state-keeper, docs-scribe"]
    end

    subgraph Engine["State engine"]
        Claims["Claims manager<br/>lease + heartbeat + stale reap"]
        Transitions["Transitions<br/>pure state machine"]
        Planning["Planning engine<br/>template parser + LLM helpers"]
        Context["Context engine<br/>work-packet renderer"]
        Review["Review engine<br/>gates: PRD, readiness, evidence"]
        GitOps["Git ops<br/>agent/&lt;task&gt;-&lt;slug&gt; branches"]
    end

    subgraph Storage["Storage layer"]
        Backend["Backend Protocol"]
        SQLite[("SQLite<br/>state.db<br/>WAL + BEGIN IMMEDIATE")]
        Events[("Events<br/>events.jsonl<br/>append-only")]
    end

    subgraph Sync["Sync engine"]
        SyncProto["SyncProvider Protocol<br/>+ registry"]
        GitHub["GitHubIssuesProvider<br/>gh CLI + httpx"]
    end

    User --> CLI
    User --> Skills
    Agent --> MCP
    Agent --> Skills
    Agent --> Hooks

    CLI --> Engine
    MCP --> Engine
    Hooks --> CLI
    Skills --> CLI
    Skills --> Agents
    Agents --> CLI

    Claims --> Backend
    Transitions --> Backend
    Planning --> Backend
    Context --> Backend
    Review --> Backend
    GitOps --> Backend

    Backend --> SQLite
    Backend --> Events

    CLI --> SyncProto
    SyncProto --> GitHub
    GitHub --> Backend

Source: assets/diagrams/component.mmd.

Isometric diagram: agents claim tasks from the anvil surface, execute, and submit evidence — claim → execute → evidence → done

Per-layer responsibilities

Layer What it owns Key files
Plugin manifest Discoverability, version, keywords .claude-plugin/plugin.json
CLI Pure state operations — CRUD, scoring, packet generation, sync. No workflow choreography. bin/src/anvil/cli/__init__.py
MCP server Runtime-neutral capability surface — 36 registered stdio tools; the default execution surface serves 24 on the wire, and the 12 planning-tagged tools (including assess_prd) require ANVIL_MCP_PLANNING=1 bin/src/anvil/mcp_server.py
Hooks Non-blocking enforcement the model would otherwise forget hooks/hooks.json, bin/src/anvil/cli/hooks.py
Skills Workflow choreography — one-question-at-a-time, propose approaches, gate transitions skills/*/SKILL.md
Plugin agents Specialist roles owned by this plugin agents/*.md
Backend protocol The seam between state-engine logic and storage; SqliteBackend is the only impl that ships bin/src/anvil/state/backend.py, bin/src/anvil/state/sqlite.py
Transitions Pure state machine — no I/O, no DB, no side-effects beyond model_copy() bin/src/anvil/state/transitions.py
Claims manager Atomic lease + heartbeat; stale detection at coordination entry points bin/src/anvil/claims/manager.py, bin/src/anvil/claims/stale.py
Planning engine Template-first PRD parser; deterministic behavioural-readiness assessor; optional LLM task generation; deterministic six-dim scorer bin/src/anvil/planning/
Context engine Renders work packets (markdown + JSON) from canonical state, including relevant typed PRD assumptions bin/src/anvil/context/packets.py
Review engine Pure transition-gate functions (readiness, evidence) bin/src/anvil/review/gates.py
Git ops Auto-create agent/<task>-<slug> branch on claim; optional worktree bin/src/anvil/git_ops/
Sync engine Bidirectional GitHub Issues projection via the SyncProvider Protocol bin/src/anvil/sync/

The two iron rules of the layering:

  1. The state engine is the only mutation authority. CLI and MCP are public state surfaces that resolve the same project state and call the same engine contracts; neither hand-writes storage. Active hooks delegate through the shell-free CLI dispatcher. The two provider reads are intentionally CLI-only execution transports, with their versioned contracts discoverable through describe_surface.
  2. Transitions are pure. Every status change is a function from (entity, context) -> new entity. Persisting the result is the backend's job, not the transition's. This is what makes the JSONL replay possible.

Data model

The full type system lives in bin/src/anvil/state/models.py — Pydantic v2 models and enums validated at every transition (extra="forbid", validate_assignment=True); all timestamps are UTC-required. The tables below intentionally summarize the core set. The generated CLI/MCP schemas and the source model file are authoritative for the complete public shape.

Core enums

Enum Values Purpose
PRDStatus draft, reviewed, approved, rejected Gates task claimability
FeatureStatus proposed, ready, in_progress, done Coarse-grain status on a Feature
TaskStatus proposed, drafted, reviewed, ready, claimed, in_progress, blocked, needs_review, accepted, done, rejected The 11-status task lifecycle (see Task lifecycle)
TaskPriority low, medium, high, critical Sort key for anvil next
ClaimType task, feature, file_scope, exploratory Distinguishes whole-task vs partial leases
ClaimStatus active, released, stale, force_released Lease lifecycle
ReviewTargetKind prd, task, feature What a Review row points at
ReviewDecision approve, reject, needs_changes Reviewer verdict
ExternalSystem github_issues Canonical first-party provider ids (extensible via registry)
SyncState in_sync, local_ahead, remote_ahead, conflict, external_deleted, remote_unknown Per-mapping conflict / health label
ConflictResolutionStrategy local_wins, remote_wins, prompt, manual_merge How to resolve a divergence

Selected embedded value objects

Model Purpose
Score Six-dimension scoring on a Task: complexity, parallelizability, context_load, blast_radius, review_risk, agent_suitability (each 1-5 or null)
Verification Embedded on Task: commands, manual_steps, required_evidence — the contract the evidence gate checks against

Core top-level entities

Entity Purpose
Project Root entity that owns all other entities in the database — including several PRDs
PRD A release/milestone-scoped, separately-gated, revisable plan carrying a target version/tag; gates claimability of the tasks it owns. A project holds one or more PRDs (a default PRD plus any named release PRDs), all in the same state.db/events.jsonl
Requirement A single atomic requirement derived from a section of a PRD; partitioned by its owning prd_id
Feature A logical grouping of tasks that delivers a user-observable capability; partitioned by prd_id
Task The primary unit of work — claimable, scoreable, evidence-backed; carries the owning prd_id that its claim gate keys on
Claim An exclusive lease that an agent holds on a Task while working on it
Evidence Completion evidence submitted by an agent after finishing a Task
Decision An architectural or design decision recorded for audit and context
Review A human or agent review verdict on a PRD, Task, or Feature
Event An immutable append-only log entry; monotonic id E000001, E000002, ...
SyncMapping Tracks a Task's relationship to an issue in an external system
ConflictGroup A named set of tasks whose expected_files overlap

Type aliases (TaskID, FeatureID, RequirementID, ClaimID, EvidenceID, DecisionID, ReviewID, EventID) are plain str newtypes — no runtime overhead but grep-able at every call site.

Most entities use the shared mutable model config:

_MODEL_CONFIG = ConfigDict(
    frozen=False,             # mutable for state transitions
    validate_assignment=True, # but assignment-validated end-to-end
    extra="forbid",           # unknown fields are an error
)

PRD is the exception: exact-source provenance makes it a frozen value object. PRD lifecycle transitions construct a fully revalidated copy rather than using assignment or Pydantic's non-validating model_copy(update=...) path.


Task lifecycle

stateDiagram-v2
    [*] --> proposed: plan / expand

    proposed --> drafted: task_proposed_to_drafted
    drafted --> reviewed: task_drafted_to_reviewed<br/>gate: acceptance_criteria<br/>+ verification.commands
    reviewed --> ready: task_reviewed_to_ready
    ready --> claimed: task_ready_to_claimed<br/>gate: exact current PRD approval

    claimed --> in_progress: task_claimed_to_in_progress
    in_progress --> blocked: task_in_progress_to_blocked<br/>(reason required)
    blocked --> in_progress: task_blocked_to_in_progress

    in_progress --> needs_review: task_in_progress_to_needs_review<br/>(evidence submitted)

    needs_review --> accepted: task_needs_review_to_accepted<br/>gate: required_evidence present
    needs_review --> rejected: task_needs_review_to_rejected<br/>(reason required)

    accepted --> done: task_accepted_to_done
    rejected --> drafted: task_rejected_to_drafted

    done --> [*]

    note right of claimed
        Claim row holds lease +
        heartbeat. Stale leases
        are reaped by queue,
        claim, renewal, progress,
        submission, and summary paths.
    end note

    note right of needs_review
        Human or critic agent
        verdict. Rejection loops
        back through drafted.
    end note

Source: assets/diagrams/lifecycle.mmd.

Note: the evidence gate on needs_review → accepted is computed and reported at apply time but enforced only when strict_evidence resolves true; the default path approves regardless.

All 11 statuses are defined in TaskStatus and the allowed transitions are the public functions in bin/src/anvil/state/transitions.py. The module is pure (no I/O); each function returns a new Task via model_copy(update=...).

Gates on the lifecycle

Three named gates appear in the transition module; each raises TransitionError(gate_name=...) with a structured error envelope:

Gate Where it fires What it checks
readiness_gate drafted → reviewed task.acceptance_criteria and task.verification.commands must both be non-empty
prd_status_gate plus canonical claim binding ready → claimed The task's owning PRD (resolved via task.prd_id) must be approved, and that approval must bind the exact persisted revision, source digest, canonical material digest, and content event. A material reparse demotes the PRD to draft; active claims are not revoked.
evidence_gate needs_review → accepted Every item in task.verification.required_evidence must appear as a substring of at least one Evidence field. Computed and reported at apply time, but enforced only when strict_evidence resolves true — the default path approves regardless

Who drives each transition

Transition Typical driver CLI verb
proposed → drafted → reviewed → ready Planner agent or human via plan / review anvil plan, anvil review tasks
ready → claimed Coding agent (or human) anvil claim T012
claimed → in_progress Auto on first heartbeat or file change (implicit)
in_progress ↔ blocked Agent or human anvil hook ... block
in_progress → needs_review Coding agent submitting evidence anvil submit T012 ...
needs_review → accepted or rejected Human reviewer or critic agent anvil apply T012 --approve / --reject
accepted → done Auto on apply --approve (implicit)
rejected → drafted Author revises and re-submits anvil plan (re-edit)

Only drafted ↔ ready and the blocked toggle are exposed via the update_task_status MCP tool; all other transitions require a more specific CLI verb (claim, submit, apply) so that the necessary side-effects (lease creation, evidence write, claim release) happen atomically.


Event log and JSONL replay

Every accepted state mutation records one Event in a locked, log-first critical section:

  1. Assign the monotonic id (E000001, E000002, ...) from log authority, append the materialized event to events.jsonl, and fsync first when strict durability is configured.
  2. Run BEGIN IMMEDIATE, mutate the SQLite projection, insert the same event into the events table, and commit.

If SQLite fails after the append, it rolls back while the append-only log line remains. The failure is audited and forward catch-up on the next initialization applies the logged event to SQLite. The log is never truncated to disguise a post-append projection failure.

The replay guarantee is the central audit property of the engine: replaying events.jsonl from an empty database must reconstruct canonical SQLite state exactly. This is what makes the engine safe to back up by copying the resolved state directory and what makes a corrupted database recoverable.

A native anvil replay --from-events <jsonl> --into <db> subcommand ships today (bin/src/anvil/cli/replay.py — it refuses to target the live database) and rebuilds state from the event log; a CI equivalence test (tests/test_replay_equivalence.py) verifies the guarantee. anvil backup / anvil restore also ship — S3 push/pull of events.jsonl plus a replay-based restore. Only the anvil snapshot subcommand (item P9B-7, a local sqlite .backup wrapper — see roadmap.md § Snapshot / replay) remains open. Copying the resolved state directory wholesale stays as the fully-local fallback; the replay guarantee makes that safe and minimal:

# Back up before destructive work.
ANVIL_STATE_DIR="$(anvil status --path-only)"
cp -R "$ANVIL_STATE_DIR" "/backup/location/anvil-$(date +%Y-%m-%d)"

events.jsonl is the durable audit log even without replay tooling. In the opt-in in-repo layout it may be committed alongside the repository; with the default HOME workspace, back it up with the rest of the resolved state directory or use anvil backup.

Event ids are assigned inside the lock, not before it, to eliminate a read-before-lock race surfaced in PR #41 (Critic-3). The Event.id validator accepts a "PENDING" sentinel so callers can defer id assignment to the backend's apply_event method.


Storage layout

anvil init scaffolds this layout in the resolved state directory (not inside the plugin). By default that is a per-project HOME workspace shared by every Git worktree; ANVIL_STATE_LAYOUT=local opts into the legacy in-repo layout, and ANVIL_ROOT supplies an explicit root:

<resolved-state-dir>/
├── config.yaml         # project-level config (sync providers, lease defaults, ...)
├── state.db            # SQLite — the canonical state for ALL PRDs (WAL mode)
├── events.jsonl        # append-only audit / event log for ALL PRDs (replay source)
├── prd.md              # the default PRD source (edited by hand; `prd parse`)
├── prds/               # named release-scoped PRD sources
│   ├── v0.2.md         #   portable source filename — one per named PRD
│   └── v0.3.md
└── packets/            # generated work packets (per-task markdown / json)

One state.db and one events.jsonl hold every PRD's rows, partitioned by prd_id; there is no per-PRD database. The default PRD keeps its source at <resolved-state-dir>/prd.md; each named release PRD has a portable markdown source under <resolved-state-dir>/prds/ (resolved by prd_source_path()cli/_helpers.py). Re-parsing one PRD replaces only that PRD's rows and leaves the others untouched.

A snapshots/ subdirectory was originally planned (and is shown in the v0 spec) but the anvil snapshot subcommand has not yet shipped — see Roadmap → Snapshot / replay. Backups today are done with anvil backup / anvil restore (S3 push/pull of events.jsonl plus a replay-based restore) or by copying the resolved state directory wholesale (cp -R); the replay guarantee makes that safe. Run anvil status --path-only before filesystem operations; see Where anvil stores its state for the complete resolution order shared by CLI, hooks, and MCP.


Concurrency model

Multiple humans and multiple agents must coordinate on the same canonical state without overlapping each other's work. anvil achieves this with four mechanisms layered together:

  1. SQLite WAL + BEGIN IMMEDIATE. Every mutating operation runs inside an immediate-mode transaction, so concurrent writers serialize at the SQLite layer. Reads use WAL snapshots and do not block writers.
  2. Claim leases with heartbeats. A Claim row carries lease_expires_at and last_heartbeat_at. The CLI's renew command (and the MCP renew_claim tool) extends the lease. Default lease is 240 minutes (configurable via the resolved config.yaml); the in-code default lives at claims/manager.py. A renewal requires new hook-observed file progress or a pending verified claim-bound attestation. Accepted attestations are generation-bound and consumed once; audit-only free-text progress does not extend a lease.
  3. Stale-claim reaping. CLI next, claim/release/renew, packet, submit/apply, and bundle-lease paths call detect_and_release_stale() before coordinated work. MCP additionally reaps on its documented progress, status-summary, and task-status entry points (the exact list is below). CLI progress remains an audit-only note that can be recorded without an active claim, so it does not reap. Expired leases are released with release_reason="stale"; read-only listers skip reaping for latency.
  4. Conflict groups. A ConflictGroup row names a set of tasks whose expected_files overlap. anvil next and the get_next_task MCP tool refuse to surface a task whose conflict group already has an active claim — preventing two agents from being routed to overlapping work even when neither task is itself claimed.

The shell-free PreToolUse hook dispatch check-claim path adds a final layer of safety at the Claude Code editor surface: before an Edit / Write / NotebookEdit fires, the dispatcher checks active claim scopes and surfaces a warning (non-blocking, per the hook contract) only when the target file overlaps another actor's expected_files. It stays silent when no claim holds the file.


CLI / MCP / hooks surface

CLI commands

Full reference is available at docs/cli-reference.md. The command surface assembled in bin/src/anvil/cli/__init__.py:

  • Lifecycle setup and inspection: init, status, describe, doctor
  • PRD authoring: prd list, prd source-name, prd show, prd parse, prd assess, prd find-decisions, prd resolve-decision, prd review
  • Planning: plan, score, assumptions, expand, deps, review tasks (sub-app)
  • Listing / inspecting: list, show, scan, drift, graph, conflicts
  • Provider reads: project snapshot, prd show (bounded JSON-only contracts)
  • Task claiming and delivery: next, claim, release, renew, progress, packet, submit, apply, claim-guard, gate-check
  • Coordinator bundles: bundle create, list, show, status, claim, renew, release, packet, progress, complete, review, finalize-review, checkpoint, reconcile, supersede
  • Workflows and proofs: run-workflow, merge-check, proof verify
  • Notifications: notify-digest
  • Harness config: mcp-config, install
  • Backup / restore: backup, restore
  • Migration / replay: migrate state, migrate-events, migrate-workspace, replay
  • Hooks: hook ... (sub-app — the active manifest uses hook dispatch)
  • Sync: sync ... (sub-app — sync github, sync github --health, ...)

MCP tools (36)

Full reference is at docs/mcp.md. Source: bin/src/anvil/mcp_server.py.

All 36 tools are registered, but the default execution surface serves 24 on the wire; the 12 planning-tagged tools (parse_prd, assess_prd, plan_tasks, score_tasks, ...) require ANVIL_MCP_PLANNING=1 (mcp_server.py tag-disables them at startup).

  • Default execution surface (24): task/project reads, claim/evidence mutation, dependency/conflict reads, and the complete coordinator-bundle execution/review/reconciliation loop.
  • Planning surface (12): init_project, parse_prd, assess_prd, review_prd, plan_tasks, score_tasks, review_tasks, apply_review_decision, edit_dependencies, find_decisions, describe_surface, and create_bundle.
  • Stale-sweep entry points: get_next_task, claim_task, claim_bundle, release_task, renew_claim, submit_progress, submit_completion_evidence, update_task_status, and get_project_summary.

Sync tools (sync_run, sync_health, sync_status, sync_reconcile) are not yet on the MCP surface — agents that want sync today shell out via Bash to anvil sync. See Roadmap → MCP sync tools.

Hooks (5)

Wired in hooks/hooks.json. The manifest calls the shell-free anvil hook dispatch <name> path via uv run --quiet --project ..., so Codex on Windows never depends on a bare bash. The legacy shell scripts remain as compatibility/test wrappers. All five hooks are non-blocking: they must exit 0 regardless of internal failure and must complete in well under their declared timeout.

Hook Trigger Active path / legacy wrapper Purpose
detect-state SessionStart anvil hook dispatch detect-state / detect-state.sh Surface project state info into the session context
check-claim PreToolUse on Edit / Write / NotebookEdit anvil hook dispatch check-claim / check-claim.sh Warn (non-blocking) when the file overlaps another actor's active claim scope
record-file-change PostToolUse on Edit / Write / NotebookEdit anvil hook dispatch record-file-change / record-file-change.sh Record the change against the active claim for orphan detection
capture-evidence PostToolUse on Bash anvil hook dispatch capture-evidence / capture-evidence.sh When the command matches a verification pattern, buffer it as evidence for the active claim
heartbeat PostToolUse on Edit / Write / NotebookEdit and on Bash anvil hook dispatch heartbeat / heartbeat.sh Renew the active claim's lease

Skills (8)

Workflow choreography lives in skills/*/SKILL.md — start-prd, prd, plan, claim, execute, finish, state-ops, resolve-decisions.

Skill frontmatter is always loaded into the model's context (it is the plugin's command surface), so the combined skill footprint is kept under an explicit token budget enforced in CI — see context-budget.md and tests/test_token_budget.py.

Plugin agents (5)

Defined in agents/*.md:

  • planner — drafts feature / task decomposition from a parsed PRD
  • critic — reviews PRD or task drafts; produces an approve / reject / needs_changes verdict
  • sentinel — pre-merge verification
  • state-keeper — operational hygiene: orphan claims, drift, schema migrations
  • docs-scribe — keeps docs/ synchronised with shipped behaviour

Each agent's frontmatter pins tools: (least privilege).


Where to read the code

Map from architectural layer to source file. Every layer of this document points at a file you can grep.

Layer File(s)
Entry: CLI assembly bin/src/anvil/cli/__init__.py
Entry: MCP server (36 tools) bin/src/anvil/mcp_server.py
Entry: hooks manifest hooks/hooks.json
Type system bin/src/anvil/state/models.py
Transitions (pure) bin/src/anvil/state/transitions.py
Backend Protocol bin/src/anvil/state/backend.py
SQLite impl + schema bin/src/anvil/state/sqlite.py, schema.py
Event payloads bin/src/anvil/state/payloads.py
Canonical-state snapshot (replay-equivalence) bin/src/anvil/state/snapshot.py
Hash-chained event ids (git-backed events) bin/src/anvil/state/hashing.py
Durable storage seam (push/pull events.jsonl to a remote store) bin/src/anvil/state/durable.py
Per-PRD status rollup bin/src/anvil/state/rollup.py
Claims manager bin/src/anvil/claims/manager.py
Stale reaping bin/src/anvil/claims/stale.py
Planning (template + LLM + scoring) bin/src/anvil/planning/
Context (work packets) bin/src/anvil/context/packets.py
Review gates bin/src/anvil/review/gates.py
Git ops bin/src/anvil/git_ops/
Brownfield scan / ingest (<resolved-state-dir>/scan.db → draft PRD + task graph) bin/src/anvil/scan/
Declarative workflows (<resolved-state-dir>/workflows/*.yaml parse + run) bin/src/anvil/workflows/
Ed25519 proof signing bin/src/anvil/signing.py
Task-id → safe path/branch component bin/src/anvil/naming.py
Sync Protocol + registry bin/src/anvil/sync/provider.py, registry.py
GitHub provider bin/src/anvil/sync/providers/github_issues.py
Reconciliation bin/src/anvil/sync/reconciliation.py
Plugin config bin/src/anvil/config.py
Clock abstraction bin/src/anvil/clock.py

What is NOT here yet

This document describes the current shipped behaviour only. The full backlog of planned-but-not-yet-shipped items is in roadmap.md; the notable open themes:

  • additional workflow-aware sync providers and provider-specific configuration;
  • webhook-based sync and remaining conflict-resolution wiring;
  • the local SQLite anvil snapshot backup/retention command (distinct from the shipped read-only anvil project snapshot provider operation);
  • an MCP sync surface; and
  • focused agent, skill, hook, and documentation hygiene.

Each item carries a backlog id (P9B-N from the Phase 9 backlog or P11-XX-XN from the Phase 11 backlog) preserved across audits — see roadmap.md for per-item acceptance criteria, file pointers, and welder-effort estimates.


Further reading

  • _positioning.md — differentiators and the Terraform analogy (internal positioning reference)
  • specs/2026-05-24-anvil-v0.md — the original 358-line v0 build spec (this document is its condensed shipped sibling)
  • mcp.md — full 36-tool MCP reference with error envelope contract
  • github-sync.md — bidirectional GitHub Issues sync reference
  • sync-providers.md — contributor guide for new sync providers
  • prd-template.md — PRD authoring schema and worked example
  • llm.md--use-llm augmentation, prompt caching, RecordedLLMProvider test pattern
  • roadmap.md — what is planned next