Metadata-Version: 2.4
Name: archagent
Version: 0.3.0
Summary: Keep code adherent to a described architecture; teach coding agents to reason about it.
Project-URL: Homepage, https://github.com/BenedatLLC/archagent
Project-URL: Repository, https://github.com/BenedatLLC/archagent
Project-URL: Issues, https://github.com/BenedatLLC/archagent/issues
Author-email: Jeff Fischer <jeff.fischer@benedat.com>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: architecture,ast-grep,coding-agents,conformance,import-linter,invariants,llm
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Documentation
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.13
Requires-Dist: ast-grep-cli>=0.44.0
Requires-Dist: import-linter>=2.12
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: typer>=0.26.8
Description-Content-Type: text/markdown

# archagent

Keep your codebase adherent to a described architecture — and teach your coding agent
to reason about it.

You describe the architecture as markdown in your repo (including a table of machine-checkable
**invariants**). archagent generates configs for existing tools (import-linter, dependency-cruiser,
ast-grep) from that single source and runs them, reporting adherence per invariant. The checkers
are deterministic; the LLM only ever *proposes*.

**It enforces the rules you already wrote down.** Design docs and code are full of stated invariants —
`# INVARIANT: the query set is always sorted`, "summaries must never be empty", "only the config layer
reads the environment" — that nothing checks. `archagent scan-invariants` finds them across your docs and
code, and the `describe` skill classifies each, verifies it against the code, and lifts it into the
enforceable table. Intent that was buried in prose becomes a checked rule — and a stated rule the code
*violates* is surfaced as drift (a real bug, or a stale design).

## Install

archagent installs once (from outside your repos) and scaffolds into each project (like Spec-Kit).
Install it from PyPI:

```bash
uv tool install archagent          # gives you an `archagent` command
# or run without installing:
uvx archagent init .
```

Prefer the latest unreleased code? Install from the repo instead:

```bash
uv tool install git+https://github.com/BenedatLLC/archagent
```

Then run it inside a project (see **Workflow** below).

## Upgrading

The prompts (agent skills + `architecture/AGENTS.md`) ship inside the archagent package, so upgrading is
**two steps**: update the tool, then refresh the repo.

1. **Update the archagent tool:**
   ```bash
   uv tool upgrade archagent                                   # installed from PyPI or GitHub
   # or, from a local checkout:
   git -C /path/to/archagent pull && uv tool install --force /path/to/archagent
   ```
2. **Refresh the repo's prompts:**
   ```bash
   cd your-repo
   archagent upgrade      # refreshes the skills + architecture/AGENTS.md only; leaves your
                          # archagent.toml and architecture content untouched (--agents to pick which)
   ```
3. **Restart your coding-agent session** so it reloads the updated skills (`/skills` in Claude Code to confirm).

> `archagent upgrade` alone won't help if the installed tool is stale — the prompts come from the package,
> so do step 1 first. Don't use `archagent init --force` to upgrade: it re-scaffolds everything and would
> overwrite your `invariants.md` and other authored content.

## The architecture artifact

`init` scaffolds an `architecture/` directory in your repo — plain markdown, versioned in git, the
shared source of truth that both humans and agents read and write:

| File | Tier | What it holds |
|------|------|---------------|
| `constitution.md` | hot (always loaded) | terse conventions + the handful of patterns the system relies on, and how to work here |
| `invariants.md` | hot | the **single source of truth** for checkable rules (the table archagent parses) |
| `subsystems/<name>.md` | cold (on demand) | one doc per subsystem, the narrative architecture across the six dimensions |
| `decisions/NNNN-*.md` | cold | ADRs — the *why* behind decisions, and the rejected alternatives |
| `investigations/*.md` | cold | what an `evaluate` finding turned out to mean once someone read the code, with a minor/moderate/critical rating |
| `index.md` | hot | catalog of the docs |
| `log.md` | — | append-only, chronological change log (grep/tail friendly) |
| `deployment.md` | cold | deployment view (services/runtimes/infra) + configuration (the `**Config:**` env-key manifest) |
| `AGENTS.md` | — | how to work with archagent in this repo (archagent-owned; refreshed by `upgrade`) |

Two tiers, on purpose: the **hot** files are loaded into the agent every session, so they stay terse.
The **cold** files (subsystem docs, ADRs) are retrieved only when relevant, so they can be full
narrative — written so a new engineer can learn a subsystem by reading one doc, without chasing links.

### See a real one: [`docs/architecture/`](docs/architecture/)

**archagent describes itself.** That directory is not a sample — it is this repository's own artifact,
generated by `/archagent-describe`, checked by `archagent check` on every commit, and scored by the
evaluation harness. Reading it is the fastest way to see what the output actually looks like:

- [`index.md`](docs/architecture/index.md) — the entry narrative and a generated Mermaid system map
- [`constitution.md`](docs/architecture/constitution.md) — the layering rules, in the terse always-loaded form
- [`invariants.md`](docs/architecture/invariants.md) — ten enforced rules, each verified by planting a
  violation and watching `check` fail
- [`subsystems/drift.md`](docs/architecture/subsystems/drift.md) — a cold subsystem doc, with the diagram
  and the caption saying what to notice
- [`decisions/`](docs/architecture/decisions/) — three ADRs, including one recording a dependency cycle
  the tool found in itself and has not yet fixed

That last point is the honest part: `evaluate` reports a `drift ↔ extraction` cycle in this codebase, and
[ADR 0003](docs/architecture/decisions/0003-drift-holds-shared-git-plumbing.md) records it as a known cost
rather than suppressing the finding.

## How architecture is modeled

Each subsystem is described across **six dimensions** (in `subsystems/<name>.md`):

1. **Process topology & components** — what the pieces are, how they connect, the entry points.
2. **Key abstractions & patterns** — the few patterns the system leans on, each with a concrete example.
3. **State & tiering** — what state exists and *where* it lives: in-memory, durable files, a database,
   a cache, a vector store. The storage tiers are made explicit.
4. **Lifecycles** — how components and state move through their states over time, as a **Mermaid
   `stateDiagram`** with a plain-language caption. State machines live here.
5. **Key flows** — the important end-to-end paths, as a **Mermaid `sequenceDiagram`** with a caption.
6. **System-wide invariants** — what must always hold; the checkable ones are linked to `invariants.md`.

Diagrams are text (Mermaid), so they diff cleanly and an agent can read and edit them. The *why*
behind any non-obvious choice goes in an ADR under `decisions/`, which invariants link to.

```mermaid
stateDiagram-v2
    [*] --> Created
    Created --> Active
    Active --> Retired
    Retired --> [*]
```
_A lifecycle is a state machine + a one-line caption: what it shows and the key takeaway._

**System-level view.** The six dimensions describe each subsystem in isolation; the *cross-cutting* view —
how the system is deployed and configured — lives in `deployment.md`:

- **Deployment topology** — the services / runtimes / infra the system runs as (read from
  `docker-compose` / k8s / `Procfile`), listed under a `**Services:**` manifest.
- **Configuration** — the environment keys the system reads, declared under a `**Config:**` manifest (or a
  committed `.env.example`). This is where configuration is modeled: `drift` compares the keys actually
  read in code (`os.getenv`, `process.env`) against what's declared, and a config-access boundary can be
  enforced as an invariant (e.g. only a `config` module may read the environment).

These tie back to the subsystems through a few optional metadata fields on each `subsystems/<name>.md`:
`**Covers:**` (the code it owns), `**Service:**` (which deployment service it runs as), `**Tier:**` (its
layer), and `**Connects:** … via <kind>` (its dependencies, typed by connector — `import` / `sync-call` /
`async-event` / `shared-data` / `pipe`). `drift` and `evaluate` read these to check topology, layering,
data ownership, and deployment coupling.

## Invariants are a markdown table

`architecture/invariants.md` — the first table is parsed; the prose around it is for humans:

| ID | Type | Tier | Applies-to | Rule | Severity | Why | Status |
|----|------|------|-----------|------|----------|-----|--------|
| BND-001 | BOUNDARY | structural | python | `forbid app.domain -> app.web` | error | [0007](decisions/0007-hexagonal.md) | active |
| BND-010 | BOUNDARY | structural | ts | `forbid src/domain -> src/ui` | error | [0008](decisions/0008-layers.md) | active |
| STR-002 | STRUCTURAL | structural | python | `forbid-pattern print($$$)` | warn | [0009](decisions/0009-no-io.md) | active |

- **Type** (the dimension it protects): BOUNDARY · INTERFACE · DATAFLOW · STRUCTURAL · PURPOSE.
- **Tier** (how it's enforced, cheapest first): structural · contract · pbt · model-check.
- **Rule** (compact DSL):
  - `forbid <a> -> <b>[, <c>...]` — BOUNDARY (must not import *directly*).
  - `forbid-pattern <ast-grep pattern> [in|outside <scope>]` — STRUCTURAL (a code shape that must not
    appear). `in <scope>` flags matches only there; `outside <scope>` flags everywhere *except* there
    (the "only `<scope>` may do this" case). `<scope>` is a path/glob (`src/app/domain`) or a dotted
    module (`app.domain.workflow`); omit it to scan all sources.
  - `property <path::test>` — a **behavioral / data invariant** ("all state is per-user", round-trip
    properties) checked by a **property-based test**. The target's file extension picks the framework:
    `.py` → a Hypothesis `@given` stub, a JS/TS file → a **fast-check** `fc.property` stub. `check` runs it
    in the *project's* env (`[python] test_command` / `[ts] test_command`) and reports the counterexample.
  - `property stateful <path::TestCase>` — for **stateful** systems (state machines, stores, lifecycles):
    a Hypothesis `RuleBasedStateMachine` (Python) or a fast-check `fc.commands` model-based stub (JS/TS) —
    random operation sequences checked against invariants, the right tool for state/data-layer bugs.
- **Severity**: `error` fails `check`; `warn` is reported but doesn't fail.
- **Why**: a link to the ADR with the rationale.

## How it works

```
architecture/invariants.md  ──gen──▶  checker configs  ──check──▶  per-invariant report
      (single source)            (existing tools = the diff)        (PASS / WARN / FAIL)
```

archagent doesn't reimplement architecture checking — it compiles your invariant table into configs for
tools that already do it, and maps their results back to invariant IDs. The capability matrix picks the
tool per `(invariant tier × language)`:

| Tier / invariant | Python | JS / TS |
|------------------|--------|---------|
| BOUNDARY / layering | import-linter | dependency-cruiser |
| STRUCTURAL (code shape) | ast-grep | ast-grep |
| PBT (behavioral / data) | Hypothesis | fast-check |

Adding a language is adding a column, not rewriting anything. Generated configs live under
`.archagent/generated/` and are gitignored — they're derived from the table and regenerated on every
`check`.

The one other file archagent writes is `.archagent/history-profile.json`: how *this* repo words its bug-fix
commits, learned from your commit guidelines and a sample of real subjects (`archagent history-profile
--write`). Unlike the generated configs, **commit it** — it's small, it makes the history-based `evaluate`
signals reproducible across machines and CI, and it's the file to hand-edit (or let an agent rewrite from
`--evidence`) when the inferred recognizer misreads your convention. `evaluate` reads it if present and
otherwise infers one in memory; it never writes it.

## Workflow

**Set up the architecture (once per repo):**

1. **`archagent init .`** — scaffold `archagent.toml`, the `architecture/` templates, and the phase
   skills. It **auto-detects which agents you use** (`.claude/`, `.cursor/`, `.openhands/`) and installs
   skills for those; override with `--agents claude,cursor` / `all` / `none`. It also detects languages
   and guesses `root_package` / `source_paths` — check those in `archagent.toml`. It asks **where the
   architecture docs should live** (default `architecture/`, or a combo it finds like `docs/architecture` —
   set it directly with `--arch-dir`, or `--yes` to take the default), and records it as `architecture_dir`.
   It **never creates or overwrites your top-level `CLAUDE.md` / `AGENTS.md`**; the full instructions go in
   `<arch-dir>/AGENTS.md`. Add `--wire` to append a small additive pointer to your top-level file(s).
2. **`/archagent-describe`** (in your coding agent) — document the *current* architecture: it locates your
   docs (via README/`AGENTS.md`/`CLAUDE.md` and any `designs/`/`spec/` dirs), **verifies them against the
   code**, and writes the constitution, the per-subsystem docs (the six dimensions), and an initial set of
   invariants — including ones it **mines from your existing design docs and code** (`archagent
   scan-invariants` surfaces the candidates; see below).
3. **`archagent check`** (or `/archagent-check`) — verify the code against those invariants.

**Keep it honest as you work:**

- **Every commit** — `archagent install-hook` drops a git pre-commit hook that runs `archagent check` on
  each commit (add `--skip-pbt` to run only the fast static tiers and leave the property tests to your test
  suite). `check` exits nonzero on an error-severity violation, so it also drops straight into CI.
- **Add an invariant** — **`/archagent-invariant`**, or edit `architecture/invariants.md` by hand, to
  encode a new rule (from a design decision, or lifted from a subsystem doc); `check` confirms it catches
  the right thing.
- **Mine stated invariants** — `archagent scan-invariants` surfaces rules already written in your docs and
  code (`INVARIANT:` markers, asserts/contracts, and modal prose like "must never" / "only X may"); the
  `describe` skill classifies each, verifies it, and lifts the checkable ones into the table (capturing the
  rest as cited prose rows).
- **See what drifted** — `archagent drift` reflexion-diffs the `architecture/` docs against the code:
  **dangling references**, **stale docs** (git), **undocumented modules** (via `**Covers:**`),
  **undeclared/stale subsystem dependencies** (declared `**Connects:**` `import`-kind edges vs the actual
  import graph), **undocumented entry points**, the **web-route surface** (Flask/FastAPI/Django routes vs a committed
  OpenAPI spec, else the docs), **configuration** (env keys read vs a `.env.example` / `**Config:**`
  manifest), **deployment topology** (IaC services vs a `**Services:**` list), and **connector-kind
  mismatches** (a `**Connects:** … via async-event` the code contradicts with a blocking HTTP call).
  Informational — its
  output (`--json` for tooling) is the update work-list.
- **Evaluate the architecture** — `archagent evaluate` (or **`/archagent-evaluate`**) judges the *model
  itself* for **system-level** smells and recommends fixes: **data & source-of-truth** (shared persistency,
  duplicated ownership, cross-service data intimacy, shared libraries — via `**Service:**` maps),
  **shotgun surgery** and **unstable interfaces** (from git **co-change** history), **change-prone complex
  files** and a **scattered single source of truth** (one decision re-implemented across files, ranked by
  the churn of the files involved), **God Components**,
  **circular subsystem/service dependencies** (with shape + severity), **unstable dependencies** (Martin's
  `I = Ce/(Ca+Ce)`), **leaky abstractions** (layer inversion/skip, via `**Tier:**`), **distributed monolith**
  (a synchronous service cycle — from typed `**Connects:**` edges *and* sync-call edges inferred from the
  code, so it works with no annotation) and **extraneous adjacent connectors**,
  **hard-coded endpoints**, and **cross-boundary observability** (services that call each other but can't
  trace a request across the boundary). It emits *candidate* signals (`--json`); the skill judges them in
  context, clusters to roots, and prioritizes. Advisory (not a commit gate). Runs at design-review +
  periodically.
- **Update the architecture** (a new design, or the code changed) — re-run **`/archagent-describe`**:
  it's *build-**or-update***. Start from `archagent drift` (reconcile doc-vs-code), then `archagent evaluate`
  (assess system-level health); refresh the subsystem(s) that changed and reconcile the invariants. Drift
  items are record fixes; evaluate findings are design decisions — change the structure or accept it with an
  ADR, and graduate the fixes you want to hold into `check` invariants. Do this at **design-review time**
  (does the proposed design fit — and does it introduce a smell?) and **periodically** as the code evolves;
  record decisions as ADRs in `architecture/decisions/`.
- **Upgrade the prompts** — update the tool, then `archagent upgrade` (refreshes the skills +
  `architecture/AGENTS.md` only, leaving your config and architecture content untouched). See
  [Upgrading](#upgrading).

> Cadence: `describe` + `evaluate` at design-review + periodically; `check` on every commit. archagent
> enforces *your system's* design rules and flags *system-level* smells (candidates its skill judges in
> context) — it isn't a generic metrics dashboard (cycle counts, coupling scores).

The three skills (`describe`, `check`, `invariant`) come from one neutral source and are installed per
agent — Claude Code `.claude/skills/`, Cursor `.cursor/skills/`, OpenHands `.openhands/microagents/` — plus
`architecture/AGENTS.md` (the full instructions, archagent-owned). In Claude Code, invoke a skill directly
as `/archagent-describe` (etc.) or just describe the task and Claude activates it.

## Commands

CLI:
- `archagent help` — concise overview of the lifecycle and the command/skill for each step.
- `archagent init [PATH]` — scaffold `archagent.toml` + the architecture templates + agent skills.
  Auto-detects agents (`--agents auto`); override with `--agents claude,cursor` / `all` / `none`.
  `--arch-dir docs/architecture` picks where the docs live (skips the prompt); `-y` / `--yes` is
  non-interactive throughout. `--wire` adds an additive pointer to top-level `CLAUDE.md`/`AGENTS.md`;
  `--force` re-scaffolds everything, **clobbering your edits to user-owned files** — use `upgrade` instead.
- `archagent check` — regenerate configs, run the checkers, report per invariant (exit 1 on an
  error-severity failure). `--skip-pbt` runs only the fast static tiers (BOUNDARY + STRUCTURAL).
- `archagent install-hook` — install a git pre-commit hook that runs `archagent check` on every commit
  (`--skip-pbt` for the static-only variant). Native `.git/hooks/pre-commit`, idempotent, composes with an
  existing hook.
- `archagent drift` — reflexion-diff the `architecture/` docs against the code: dangling references,
  stale docs (git), undocumented modules (via `**Covers:**`), undeclared/stale subsystem dependencies
  (declared `**Connects:**` `import`-kind edges vs the actual import graph — Python `ast` + JS/TS regex), undocumented entry
  points (`[project.scripts]` + `package.json` `bin`), the **web-route surface** (Flask/FastAPI/Django +
  Express/Fastify/NestJS, static, vs a committed OpenAPI spec if present, else the docs), and
  **configuration** (env keys read in code vs a `.env.example` / `**Config:**` manifest), and **deployment
  topology** (services from docker-compose/Procfile/k8s vs a `**Services:**` list, plus code cross-service
  dependencies vs compose `depends_on` via subsystem `**Service:**` mappings), and **connector-kind
  mismatches** (declared `via async-event` vs a synchronous HTTP call inferred from the code). Informational; `--json` for
  tooling/agents, `--exit-code` to fail CI on any drift, and `--until` / `--as-of <tag>` to bound the git
  staleness comparison to a past revision (same semantics as `evaluate`, below).
- `archagent evaluate` — judge the architecture for **system-level** smells (candidates for
  `/archagent-evaluate`): **data & source-of-truth** (shared persistency, duplicated ownership, cross-service
  data intimacy, shared libraries — from a static datastore→service map via `**Service:**`; silent on a
  single-service repo, where none of these can apply), God Components,
  circular subsystem/service dependencies (shape + severity), unstable dependencies (`I = Ce/(Ca+Ce)`,
  `DoUD ≥ 0.30`), leaky abstractions (layer inversion/skip via `**Tier:**`), **distributed monolith** +
  **extraneous adjacent connectors** (from typed `**Connects:**` edges), hard-coded service endpoints, and
  **cross-boundary observability** (no request tracing at all, and gaps in an otherwise-traced chain);
  plus **git-history** signals —
  **shotgun surgery** / implicit coupling and **unstable interface** (subsystem co-change),
  **change-prone complex files** (per-file churn × indentation complexity, both as within-repo
  percentiles), and **scattered single source of truth** — either inferred (one decision's value set
  branched on across several files, likely owner inferred, ranked by churn) or **declared** (an enum
  bypassed by comparisons against its raw member strings; the one signal here that needs no git, so it
  still runs under `--no-history`).
  `--json`, `--group A|B|C|D|E|F`, `--min-severity`, `--no-history`, `--since`, `--until`, `--as-of`,
  `--exit-code`. `--until` / `--as-of <tag>` bound the history so a run can be reproduced *as of* a past
  revision; they do not check anything out, and the run warns if your tree is newer than the window.
- `archagent investigate <finding-id>` — print an investigation brief for one `evaluate` finding: what the
  concept is, how many times it is declared, whether the copies have drifted, whether any code path
  actually misbehaves, and whether it fails loudly or silently. `evaluate`'s severity counts files and
  commits; a **minor / moderate / critical** rating requires reading the code. `--record <file.md>
  --rating <level> [--by NAME]` stores the result in the artifact under `<arch-dir>/investigations/`, so
  the next run reports the verdict instead of asking again. Pass the same `--until` the run used, so the
  brief describes the finding as it stood when it was reported.
- `archagent history-profile` — learn how *this* repo words its bug-fix commits (`Fixed #123` vs
  `fix(scope):` vs free-form), which the history signals above rely on. Prints what it inferred; `--write`
  caches it to `.archagent/history-profile.json`, `--evidence` dumps the raw facts (commit guidelines,
  leading-word frequencies, per-pattern match rates) for an agent to judge. A cached profile always wins.
- `archagent scan-invariants` — scan docs + code for **stated invariants** (explicit `INVARIANT`/
  `@invariant`/assert/contract markers, plus modal language like MUST/NEVER/"only X may") and emit them as
  candidates for `/archagent-describe` to classify, verify, and lift into `invariants.md`. `--json`,
  `--markers-only`.
- `archagent status` — repo-scale + coverage snapshot: per top-level package, how many source files a
  subsystem's `**Covers:**` claims. Use it to size a `describe` pass (a fixed "document 3 and stop" is wrong
  for a large repo) and to state coverage in `index.md` as an "N of M" count.
- `archagent graph` — generate a Mermaid system map (one node per subsystem, one edge per typed
  `**Connects:**`) from the metadata the docs already declare. `--write` splices it into `index.md` between
  the `<!-- archagent:graph -->` markers (idempotent), so the diagram stays in sync instead of being
  hand-redrawn.
- `archagent lint-docs` — lint the Mermaid diagrams in the architecture docs for syntax errors (a stray
  second `:` in a `stateDiagram-v2` label, an unclosed/empty block, an unknown diagram type) — deterministic,
  no Node required. `--json`, `--exit-code`.
- `archagent modules` — diagnostic: how each Python source file resolves to an import module, flagging
  top-level **name collisions** (two packages that install under the same name, which quietly breaks
  import-linter scoping).
- `archagent gen` — regenerate only the checker configs from `architecture/invariants.md` (`check` does
  this for you).
- `archagent upgrade` — refresh the archagent-owned prompts (skills + the artifact's `AGENTS.md`) to the
  latest; leaves your config and architecture content untouched. `--agents` scopes which are refreshed.

Every command takes `--project PATH` (default `.`) to run against a repo other than the current directory.

Agent skills (invoke in your coding agent; Claude Code slash form shown):
- `/archagent-describe` — build or update the architecture artifact.
- `/archagent-check` — run `archagent check` and resolve violations.
- `/archagent-invariant` — add or change a checkable invariant.
- `/archagent-evaluate` — judge the architecture for system-level smells and recommend fixes.
- `/archagent-help` — overview of the lifecycle and which command/skill to use at each step.

## Configuration

A small `archagent.toml` at the repo root tells archagent where the code is:

```toml
[project]
languages = ["python", "ts"]
architecture_dir = "architecture"   # where the architecture docs live (default; e.g. "docs/architecture")

[python]
root_package = "app"
source_paths = ["src"]

[ts]
source_paths = ["src"]
```

`architecture_dir` is set at `init` time and used everywhere the artifact is read or referenced (drift,
check, evaluate, and the top-level wiring). Choose it non-interactively with `archagent init --arch-dir
docs/architecture`; otherwise `init` finds your `docs/`/`design/`/`spec/` dirs and offers them (pass
`--yes` to skip the prompt and take the default).

## Try it

```bash
uv run archagent check --project examples/sample_py    # Python (import-linter + ast-grep)
uv run archagent check --project examples/sample_ts    # TS (dependency-cruiser + ast-grep)
```

## What it composes

import-linter (Python boundaries) · dependency-cruiser (JS/TS boundaries) · ast-grep (structural,
any language) · grep/git for retrieval and history. archagent is the thin layer that turns one
markdown table into those tools' configs and reports results per invariant.

## Repository layout

The layout of this source repository (distinct from the `architecture/` artifact archagent *generates* in
a target repo, described above):

```
archagent/
├── README.md                 this file
├── pyproject.toml            package metadata + dependencies (uv)
├── docs/
│   ├── architecture/         archagent's own artifact — it describes itself (archagent.toml points here)
│   ├── designs/              one design doc per feature, with `status:` frontmatter
│   ├── evaluations/          what the evaluation runs concluded (the data lives in a separate repo)
│   ├── ROADMAP.md            planned future work, grouped by theme (checkable)
│   ├── ADL-SPEC.md           the architecture-artifact format, as a standards-style spec
│   └── RELEASING.md          how to cut a new release to PyPI
├── src/archagent/
│   ├── cli.py                the `archagent` CLI (init · gen · check · drift · evaluate · status · graph …)
│   ├── config.py             archagent.toml loading (languages, source paths, test commands)
│   ├── invariants.py         parse the invariants.md table  ·  rules.py — the Rule DSL
│   ├── generate.py           compile invariants → checker configs  ·  check.py — run them, map results
│   ├── init.py               scaffold the artifact + per-agent skills; upgrade prompts
│   ├── drift.py              the reflexion-diff (docs vs code): the `drift` + `modules` commands
│   ├── evaluate.py           system-level architecture smells: the `evaluate` command
│   ├── history.py            learn this repo's bug-fix commit wording: the `history-profile` command
│   ├── hotspots.py           churn × indentation-complexity: the change-prone-file check
│   ├── dupdecide.py          duplicated branch-value sets: the scattered-source-of-truth check
│   ├── investigations.py     recorded verdicts on findings, stored in the artifact
│   ├── status.py             per-package coverage snapshot: the `status` command
│   ├── graph.py              Mermaid system map from metadata: the `graph` command
│   ├── docscan.py            Mermaid diagram linter: the `lint-docs` command
│   ├── <extraction scanners> configscan · deployscan · webapi · datamap · cochange · connscan · obsscan
│   │                         (static, no-execution extractors: env keys, IaC, routes, datastores,
│   │                          git co-change + per-file churn, connector kinds, observability)
│   └── templates/
│       ├── architecture/     the artifact scaffold (constitution, invariants, subsystems, deployment…)
│       └── agent/phases/     the neutral skill prompts (describe · check · invariant · evaluate)
├── examples/                 sample_py, sample_ts — end-to-end fixtures
├── scripts/                  evaluation CLIs: selfeval · defect_study · spotcheck · blindcomp
│                             (evalhome.py resolves where their output goes)
├── tests/                    the pytest suite, and the evaluation harness it exercises
│   ├── rubric.py             the deterministic half of the artifact rubric
│   ├── rubric_judged.py      the judged half: anchored criteria, resolved citations
│   ├── defect_study.py       rate ratios, bootstrap intervals, churn-decile stratification
│   ├── corpus.py             pinned-repo regression  ·  spotcheck.py · blindcomp.py
│   ├── golden/               projected `evaluate` output for the built-in fixture repos
│   └── corpus/               … and for real repositories pinned to a tag (`pytest -m corpus`)
└── .github/workflows/ci.yml  CI (runs the suite on every push/PR)
```

**The evaluation harness ships in `tests/`, not in the package.** Nothing under `scripts/` or the harness
modules is installed by `pip install archagent`; they exist to measure the tool, not to run it. The data
those runs produce lives in a separate private repository — see
[`docs/evaluations/README.md`](docs/evaluations/README.md) for what was concluded and where the evidence
sits.

## Development

```bash
uv sync --group dev
uv run pytest            # unit tests (DSL + table parsing, config generation, init/upgrade logic)
                         # + an end-to-end check on examples/sample_py (real import-linter + ast-grep)
```

Tests run in CI on every push/PR (`.github/workflows/ci.yml`). The TS/PBT paths need Node / a target
test env, so they're validated via the examples rather than in the unit suite.
