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, seeroadmap.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 <cmd>"]
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/<task>-<slug> 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.
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:
- 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. - 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 → acceptedis computed and reported at apply time but enforced only whenstrict_evidenceresolves 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:
- Assign the monotonic id (
E000001,E000002, ...) from log authority, append the materialized event toevents.jsonl, andfsyncfirst when strict durability is configured. - Run
BEGIN IMMEDIATE, mutate the SQLite projection, insert the same event into theeventstable, 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:
- 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. - Claim leases with heartbeats. A
Claimrow carrieslease_expires_atandlast_heartbeat_at. The CLI'srenewcommand (and the MCPrenew_claimtool) extends the lease. Default lease is 240 minutes (configurable via the resolvedconfig.yaml); the in-code default lives atclaims/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. - Stale-claim reaping. CLI
next, claim/release/renew, packet, submit/apply, and bundle-lease paths calldetect_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). CLIprogressremains an audit-only note that can be recorded without an active claim, so it does not reap. Expired leases are released withrelease_reason="stale"; read-only listers skip reaping for latency. - Conflict groups. A
ConflictGrouprow names a set of tasks whoseexpected_filesoverlap.anvil nextand theget_next_taskMCP 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 useshook 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, andcreate_bundle. - Stale-sweep entry points:
get_next_task,claim_task,claim_bundle,release_task,renew_claim,submit_progress,submit_completion_evidence,update_task_status, andget_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 PRDcritic— reviews PRD or task drafts; produces an approve / reject / needs_changes verdictsentinel— pre-merge verificationstate-keeper— operational hygiene: orphan claims, drift, schema migrationsdocs-scribe— keepsdocs/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 snapshotbackup/retention command (distinct from the shipped read-onlyanvil project snapshotprovider 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 contractgithub-sync.md— bidirectional GitHub Issues sync referencesync-providers.md— contributor guide for new sync providersprd-template.md— PRD authoring schema and worked examplellm.md—--use-llmaugmentation, prompt caching,RecordedLLMProvidertest patternroadmap.md— what is planned next