Documentation

Get started with ad2v

This guide takes you from a fresh machine to a map of your own repository, a PR diff in CI, and an MCP host that can answer questions about your system. Every command here was run on a clean clone.

Requirements

ad2v 0.7.0.dev0 is a development pre-release and is not published on PyPI. Install from source.

Install

Option A: full install (CLI + map viewer + MCP App)

git clone https://github.com/shanglai/ad2v.git
cd ad2v
python3 -m venv .venv
source .venv/bin/activate
pip install -e .

# build the viewer and the MCP App (needs the venv active: fixtures uses Python)
cd viewer
npm ci
npm run fixtures
npm run build
npm run build:app
cd ..

ad2v --version        # ad2v 0.7.0.dev0

No git? Download the ZIP, unpack it, and run the same steps from that folder (ad2v history still needs git and a repository to read).

Prefer uv? uv sync --extra dev works too.

Option B: CLI only

pip install "git+https://github.com/shanglai/ad2v.git"

Good for CI and for the MCP tools. It does not include the built viewer, so ad2v view stops with “the viewer is not built”. Use option A on machines where you want the map.

Your first map (5 minutes)

The repository ships a small two-container system, payments-recon, with specs, tests and deliberate problems. From the ad2v folder:

ad2v extract --repo fixtures/payments-recon \
  --spec fixtures/payments-recon/specs \
  --test-results fixtures/payments-recon/test-results/junit.xml \
  --out recon.ad2v

ad2v status recon.ad2v --side spec
ad2v view recon.ad2v

view opens your browser on 127.0.0.1. Things to try:

  1. Zoom: wheel to zoom, double-click to enter a box, Esc to go up, / to search.
  2. Status: enter api; find the red dashed orphan and the risky loop in matching.py.
  3. Replay: pick POST /v1/reconciliations in Replay a workflow…, step through it and move the detail slider.
  4. Legend: the Legend button explains every status and glyph.

For an 8-epoch history with AI badges, try the demo map: ad2v view viewer/tests/fixtures/demo.ad2v (created by npm run fixtures), or open the live demo in your browser.

Your own project

Keep your specs inside the repository (so history sees them change) and the .ad2v file outside it, or in a gitignored .ad2v/: it stores code snippets.

my-project/
├── src/…                 ← Python code
├── tests/…               ← optional: test_req_001_… names link tests to requirements
├── specs/                ← OpenAPI, Gherkin, Markdown
├── ad2v.yaml             ← optional manifest
└── .gitignore            ← add: .ad2v/

1. Let ad2v look first

ad2v init reads files only. It runs nothing, calls no model, and writes only with --write. It reports spec folders, containers and their languages, id prefixes, SQL, infrastructure and test configuration, then proposes an ad2v.yaml.

ad2v init --repo ../my-project
ad2v init --repo ../my-project --write ~/ad2v-maps/my-project.yaml

Fill in any empty value it leaves (for example a route prefix that lives in a setting).

2. Extract and view

ad2v extract --repo ../my-project --manifest ~/ad2v-maps/my-project.yaml \
  --test-results ../my-project/junit.xml --out ~/ad2v-maps/my-project.ad2v
ad2v view ~/ad2v-maps/my-project.ad2v

extract prints the spec roots it used and the languages it did not read. Later, ad2v init --repo … --check my-project.yaml lists what changed since (new folders, stale excludes, missing prefixes) and exits 1 if anything did.

Spec formats

Use what you have and mix freely. Without --spec, ad2v uses spec_root from ad2v.yaml, else the first of specs/, spec/, docs/specs/, .kiro/specs/, sdd/, docs/sdd/ that holds a spec, plus a root openapi.yaml.

FormatExample
OpenAPI 3specs/openapi.yaml; pairs with FastAPI / Flask / Starlette routes. x-ad2v-refs: [REQ-001] ties an endpoint to a requirement.
Gherkinspecs/features/*.feature, scenarios tagged @REQ-001; # language: es works.
ad2v convention## Component: Reconciliation API {#CMP-001} then - [REQ-001] When …, the system shall …
spec-kitspecs/001-payments/spec.md; ids become 001-payments:FR-001.
Kiro.kiro/specs/*/requirements.md
Team formatsRequirement tables, user stories (HU-001), EARS in English or Spanish (Cuando …, el sistema deberá …), or your own regexes.

Full rules: spec Markdown convention.

Explicit links (recommended)

ad2v pairs by route and by name on its own. These make it precise:

History and diff

Extract git history into one file, one epoch per commit whose inputs changed. ad2v uses git archive: it never checks out or runs anything.

ad2v history --repo . --spec specs --commits HEAD~20..HEAD --into project.ad2v
ad2v diff project.ad2v --from parent --to head --format md
ad2v materialize project.ad2v --epochs last:5     # make epochs browsable in the viewer
ad2v pin project.ad2v head --label v1.2            # keep this one through prune

The diff reports added, removed and renamed nodes (renames follow lineage), status transitions including stale, risk changes, spec changes and workflow changes. --into on extract appends a new head epoch and reuses the parse cache.

CI: a diff comment on every pull request

Copy examples/github-action/ad2v-diff.yml to .github/workflows/. It builds two epochs (merge base and PR head), renders the diff in at most ~60 lines, and creates or updates one PR comment. It needs only the job's own GITHUB_TOKEN. While ad2v is not on PyPI, change its install step to:

- name: Install ad2v
  run: pip install "git+https://github.com/shanglai/ad2v.git"

Pin a commit or tag (…/ad2v.git@<sha>) for reproducible CI.

MCP hosts: Claude, VS Code, others

ad2v mcp is a read-only MCP server over your .ad2v files. Tools include ad2v_locate, ad2v_search, ad2v_get_node, ad2v_links, ad2v_workflow, ad2v_diff, ad2v_architecture and ad2v_show. Hosts with MCP Apps render the map inline. Materialize at least one epoch first.

Claude Code

claude mcp add ad2v -- /path/to/ad2v/.venv/bin/python -m ad2v mcp --root /path/to/maps

Add --scope project to share it through .mcp.json.

Claude Desktop

In claude_desktop_config.json, with absolute paths to the venv's Python:

{
  "mcpServers": {
    "ad2v": {
      "command": "/Users/you/projects/ad2v/.venv/bin/python",
      "args": ["-m", "ad2v", "mcp", "--root", "/Users/you/projects/maps"]
    }
  }
}

VS Code

// .vscode/mcp.json
{
  "servers": {
    "ad2v": {
      "type": "stdio",
      "command": "${workspaceFolder}/.venv/bin/python",
      "args": ["-m", "ad2v", "mcp", "--root", "${workspaceFolder}"]
    }
  }
}
ad2v mcp --http binds 127.0.0.1 only and has no authentication. Hosts that need a remote HTTPS server (such as ChatGPT connectors) would need an authenticating proxy you set up yourself; read the MCP guide checklist first.

No MCP host? ad2v view project.ad2v --chat --manifest ad2v.yaml answers questions in the viewer with your own model.

Infrastructure (Terraform / OpenTofu)

ad2v never runs Terraform. Produce a plan yourself and keep it out of the repository:

terraform init && terraform plan -refresh=false -out tfplan && terraform show -json tfplan > plan.json
ad2v extract --repo . --out app.ad2v --infra-plan prod=plan-prod.json --infra-plan dev=plan-dev.json
ad2v c4 app.ad2v --env prod --format structurizr --out workspace.dsl

Or set infra_root: [infra] in ad2v.yaml to read .tf files statically (also used by history). You get the env-var bridge (which variables each deployment provides, which the code reads but nobody provides) and the Architecture tab, per environment. GCP and AWS core resource types; only allowlisted attributes and env var names are stored. Details: infra guide.

Optional LLM layer

Bring your own model for a semantic judge, link suggestions, summaries, narrations and prose-spec extraction. Providers: vertex, openai_compatible, or the offline fake. Results are informative: they never change an epoch id, and a model can only suggest.

ad2v llm plan project.ad2v --manifest ad2v.yaml                 # no calls; token and USD estimate
ad2v llm run  project.ad2v --manifest ad2v.yaml --tasks judge,suggest --max-cost-usd 0.50 --yes
ad2v suggestions list   project.ad2v
ad2v suggestions accept project.ad2v <id> --manifest ad2v.yaml  # writes pairing.links

run refuses to start without a budget and --yes, and stops before exceeding it. Results are cached by exact input, so reruns are free. Start with judge,suggest; summarize,narrate scale with repository size.

Plug-ins

Language extractors and framework packs are plug-ins behind a stable API (ad2v.api 1.0, entry points ad2v.extractors and ad2v.frameworks). Plug-ins run with your privileges, so only built-ins are active by default; enable others per repository:

ad2v extractors list
ad2v extract --repo . --out x.ad2v --extractor routes-dsl
ad2v extractor check my-lang --fixture tests/fixture   # conformance kit

Guide and two out-of-tree examples: docs/plugins.md.

CLI reference

CommandWhat it does
initRead a repository and propose an ad2v.yaml; --check compares with an existing one.
extractWrite a fresh file (--out) or append a head epoch (--into).
historyOne epoch per commit in a revision range.
statusEffective statuses with reasons; filter by --side, --status.
diffCompare epochs; table, json or md.
viewServe the map on 127.0.0.1 (read-only); --chat for BYOM chat.
mcpRead-only MCP server over --file or --root; --http for local HTTP.
c4Export the C4 view (structurizr or json) per environment.
inspectLook at a node, a kind, or the top risks.
log · pin · unpin · pruneManage epochs; prune keeps the last N and pinned ones.
materialize · layoutPrepare epochs and their deterministic layout for the viewer.
dumpCanonical sorted JSONL; --normative-only is the determinism contract.
validateCheck a file against the format: schema, ids, hashes, roll-ups.
llm · suggestions · specsOptional model layer: plan, run, eval; review suggestions; bootstrap specs from prose.
extractors · extractor checkList plug-ins; run the conformance kit.

Every command accepts --help. An <epoch> is an epoch id, head, parent, a pin label, or a commit SHA prefix (≥ 7 chars).

ad2v.yaml

Optional. Without it, each top-level directory with pyproject.toml, setup.py or a Dockerfile is a container.

system: {name: payments-recon}
containers:
  - {name: api, paths: [api], kind: service, replicas: 2}
  - {name: worker, paths: [worker], kind: worker}
exclude: [migrations]
spec_ids: {test_name_prefixes: [REQ, CMP]}
pairing:
  components: {CMP-002: worker}        # spec component -> container
  path_prefix_map: {/api: ""}          # route normalization
  orphan_policy: auto                  # auto | strict | off
  path_constants: {"settings.API_V1_STR": "/api/v1"}
  links: []                            # human-confirmed pairs
infra_root: [infra]                    # optional: read .tf statically
extractors:
  enabled: []                          # third-party plug-ins (opt-in)
llm:                                   # optional, informative only
  provider: vertex
  project: my-gcp-project
  model: gemini-3.8-flash
  budget_usd: 1.00

Limits

Troubleshooting

Further reading