# codebase-cartography (Unified Skill)

## Core Instructions (SKILL.md)

#### Core Instructions (content/distilled/codebase-cartography/SKILL.md)

# Codebase Cartography

Map a codebase's structure as text-based architecture diagrams at three zoom levels, with optional structural-health triage.

You are a cartographer, not a critic: you document topology, dependencies, and boundaries as they exist. You do not recommend changes unless an explicit diagnostic pass was requested.

## Zoom Levels

- **macro** — whole-system topology: modules/domains, dependency direction, layering.
- **meso** — one module or feature: internal components, wiring, integration contracts.
- **micro** — one functionality: call chain, data flow, state mutation, I/O boundaries.

Pick the entry level from the request:

| Request shape | Entry level | Typical ask |
| :---- | :---- | :---- |
| "map this repo", onboarding | macro | lay of the land |
| "how does <module> work", wiring | meso | structure of one district |
| "trace <functionality>", "where does X happen" | micro | end-to-end path |

## Procedure

1. Clarify the target if ambiguous. State the entry zoom level explicitly in the output.
2. Gather evidence by searching — imports, entry points, directory layout, call sites. Never infer structure from names alone.
3. Render the entry-level map plus one adjacent level for context (parent overview when starting micro/meso; child detail when starting macro).
4. Run the health check only if requested.
5. Produce the report using the template.

**Reference loading protocol — read on demand:**

- Before gathering evidence for any map (and again before rendering), read `references/zoom-levels.md` — per-level evidence collection commands and text rendering formats.
- Before any health check or when the user asks about "health", "smells", or refactoring targets, read `references/smell-signatures.md` — anti-pattern proxies, evidence requirements, severity ratings.
- Before writing the final output, read `references/report-template.md` — output structure per mode.
- When the user asks for an HTML export or a shareable version of the report, read the "Optional HTML Export" section at the end of `references/report-template.md`; when the request spans multiple reports (batch), also read its "Batch export" section.

## Zoom Discipline

- Offer adjacent levels as follow-ups; never dump all three maps unrequested. An explicit batch request ("all reports", "map every module") is a user request — the offer-don't-dump rule applies to unprompted follow-ups only (batch procedure in `references/report-template.md`, "Batch export" section).
- Re-verify every embedded count and fan-in/fan-out number against a fresh command run immediately before writing each report (one consolidated pass per batch is fine) — counts gathered early in a long session drift.
- Every claim carries a file reference (`path`, or `path:line` for specific symbols).
- State dependency direction explicitly (A → B); never "related to".
- Distinguish observed evidence from inference; label inference explicitly.
- If the target is too large for one pass, map boundaries first and ask which module or area to enter.

## Rules

- Describe structure as it exists; no refactor advice outside an explicit diagnostic request.
- Cycles, hotspots, and boundary violations are facts to report, not flaws to fix.
- Prefer structural facts over narrative: dependency direction, counts, boundaries.
- Health-check findings follow the evidence format in `references/smell-signatures.md` (pattern, evidence, quantity, severity) — never unanchored opinions.
- Maps are diff-able artifacts: tables and Mermaid, not prose walls.
- HTML export is derived output, produced only on request; the markdown report remains the artifact of record. Exports live in `docs/cartography/` as a self-contained directory (index + shared assets); see the "Optional HTML Export" section of `references/report-template.md`.

---

#### Reference: references/report-template.md

# Report Template

Structure the final output by mode. All modes share the closing sections. Keep maps as tables/Mermaid — diff-able artifacts.

## Header (all modes)

```
## Cartography — <target>
**Entry zoom level:** macro | meso | micro
**Scope covered:** <paths examined>
```

## Mode: macro

1. **Module inventory** — table (module, path, responsibility, weight bar, deps out, fan-in)
2. **Dependency matrix** — text DSM with ↑ marks for layer violations
3. **Module graph** — Mermaid, edges labeled with counts
4. **Composition** — disk-reality breakdown by file kind (source / test / generated / vendored / config), excluded code included; distinct from the inventory weight column (analysis code, exclusions applied)
5. Structural observations

## Mode: meso

1. **Parent context** — 2-3 lines: where this module sits in the macro map
2. **Component table** — components, paths, roles, internal links, external ports
3. **Wiring graph** — Mermaid with mechanism-labeled edges
4. Structural observations

## Mode: micro

1. **Entry point** — symbol and file:line
2. **Call chain** — numbered hops with file:line and purity/I-O markers
3. **Data flow** — transformation list from input to outputs/side effects
4. Structural observations

## Mode: health check (additive to any level)

One finding block per pattern (format in `smell-signatures.md`), ordered by severity. If nothing found, state the patterns checked and the evidence gathered — absence of findings without evidence of search is not a result.

## Closing sections (all modes)

**Key Relationships** — top 3-5 dependencies that explain the structure:

```
- web → api (all requests funnel here; 34 call sites)
- ledger ← webhooks (cycle with billing; see observations)
```

**Structural Observations** — facts only, no fixes:

```
- Cycle: billing → webhooks → ledger → billing (3 modules)
- Hotspot: core/ledger.ts imported by 14 modules
- Layer skip: web/cli imports infra/db directly (5 files)
```

**Open Questions** — what the cartographer could not resolve, for human follow-up:

```
- Is the billing → ledger edge intentional or legacy? (no test covers it)
```

**Adjacent levels offered** — one line each, e.g. "meso map of ledger available on request".

## Batch export

An explicit batch request ("generate all the reports", "map every module") is a user request — Zoom Discipline's offer-don't-dump rule applies to unprompted follow-ups only.

- Enumerate targets from the module inventory. Confirm the list once when it exceeds 3 targets or the scope is ambiguous; proceed unless corrected.
- One markdown per target in `docs/cartography/` — each the artifact of record for its target. For each, re-verify counts and re-confirm weight/composition shares immediately before writing — one consolidated pass per batch is fine (Zoom Discipline).
- Include a health report only if the batch request names health/smells.
- A target too large for one pass: skip it, mark it as "offered — too large for one pass" in the final summary and, when HTML export is part of the batch, in `index.html`. Do not stop the batch to ask.
- Superseded artifacts from older exports (old-format HTML pages, render scripts): regenerate in place, recommend deleting the superseded script/pages, and never index stale-format files.
- When HTML export is part of the batch request or separately requested, HTML pages and `index.html` follow the Optional HTML Export section (regenerate the index once at the end, covering all reports).

## Optional HTML Export

Produce only on request ("export as HTML", "shareable report"). The markdown report is the source of truth; the HTML files are a derived, regenerable view — never hand-edit them, regenerate them from the markdown.

The export is a **self-contained directory**: all pages plus shared assets under one folder, opening correctly via `file://`, offline, with no server, CDN at view time, or build step. JavaScript is permitted only as a local vendored asset (see Diagrams).

### Output directory

- All exports live in `docs/cartography/` (create directories as needed). If `docs/` does not already exist in the repo, confirm once with the user before creating it.
- Pages reference assets with relative paths (`assets/...`), never absolute or remote URLs.

### Files

- One page per report: `cartography-<level>-<target>.html` (e.g. `cartography-meso-payments-billing.html`; slugify the target with hyphens, no dots or spaces). The zoom level in the name prevents exports of different levels overwriting each other. Re-exporting the same level+target overwrites the previous file — intended; there is no history.
- `index.html` — regenerated on every export; never hand-maintained.
- `assets/mermaid.min.js` — vendored Mermaid v9.4.3 IIFE build, shared by all pages.

### Index

Regenerate `index.html` on every export from what is on disk plus what is known this session:

- Scan `docs/cartography/*.html` (excluding `index.html`), group entries by zoom level (macro / meso / micro / health), sorted alphabetically.
- Each entry links to the page and shows its target plus a one-line scope taken from the report header.
- Also list markdown reports known to this session (the artifact of record), linked with a repo-relative path from the HTML location. Do not invent paths for reports you cannot locate.
- The template must handle N=1 (a single entry) without looking broken.
- Deterministic: sorted, no timestamps, no generator metadata.

### Navigation

- Every page (including `index.html`) has a plain nav header: `← Index` plus links to parent-context and adjacent-level pages when they exist (meso → its macro page; micro → its macro/meso parent). Relative links only.
- Static links only — no interactivity. Do not evolve the export into an application — no servers, frameworks, or interactive controls. If live interaction is requested, note it is out of scope for this skill.

### Diagrams

- Render Mermaid client-side with the vendored local asset: `<script src="assets/mermaid.min.js"></script>` plus `<pre class="mermaid">` blocks. Use only the v9.x IIFE build (global `mermaid`); v10+ is ESM-only and is blocked by CORS under `file://`.
- Asset acquisition: on the first export, download `mermaid.min.js` v9.4.3 into `assets/` if absent (e.g. from jsDelivr); reuse thereafter. If the download fails (offline machine), still emit all pages — diagrams degrade to their source in a styled `<pre>` and the report footer notes "diagrams show source; rerun the export with network access to enable rendering". Never fail the whole export because of assets.
- Every diagram keeps its Mermaid source in `<details><pre class="mermaid-source">` as the no-JS fallback, so nothing is ever blank.
- Prose stays pre-rendered HTML — do not client-render markdown. The markdown report remains the artifact of record.

### Handoff

- Final step of any export (including the first): tell the user to open `docs/cartography/index.html` directly in a browser — `file://` works with no server; diagram rendering is guaranteed by the Diagrams section.
- Never start a server for static reports unless the user explicitly asks.

### Invariants

- **Write HTML directly:** no render scripts, build pipelines, or new dependencies (pandoc etc.) — even in batch mode. The regeneration path is re-running this skill, not a committed pipeline; exception only on explicit user request (note the dependency caveat in the reply).
- **Mirror, don't redesign:** same sections, same order, same content as the markdown report. The HTML adds presentation only (readable typography; `prefers-color-scheme` support optional).
- **Deterministic:** stable section order, no timestamps, no generator metadata.
- **Size guard:** render large reports as-is; never paginate, collapse, or add interactivity.
- **Commit-able:** reports and `assets/` are self-contained and offline-safe; suggest committing them, never silently gitignore.

Decision record: diagrams use a vendored local Mermaid v9.4.3 IIFE; prose is pre-rendered. Rationale: offline rendering, `file://` compatibility, no ESM/CORS issues. Render scripts and build pipelines are banned because agent-regeneration keeps the navigation/diagram/index guarantees that build scripts historically dropped (wai cartography batch, 2026-09-01: a pandoc pipeline produced 12 pages with zero cross-links and unrendered diagrams).

---

#### Reference: references/smell-signatures.md

# Anti-Pattern Signatures — Agent-Computable Proxies

Structural health check. Each anti-pattern from the visual framework is reduced to evidence an agent can gather by search and counting. Report findings; do not propose fixes unless explicitly asked.

## Report Format (per finding)

```
[PATTERN] severity: high|medium|low
Evidence: file:line list or matrix marks
Quantity: loop members / fan-out count / depth / ratio
```

Severity scales with blast radius: fan-out count, number of affected modules, loop size.

## Signature Catalog

### Circular Dependencies

- **Proxy:** closed loops in the module or component graph. Build the dependency list (see `zoom-levels.md` macro), then trace: if A → B → … → A exists, report the full loop.
- **Quick scan:** mutual imports between two files (`rg` for A importing B, then B importing A); module-pair matrix marks both ways.
- **Severity:** loop spanning multiple top-level modules > intra-module pair.

### God Object

- **Proxy:** one file/class with extreme fan-in AND fan-out, plus size. Check: file length (`wc -l`), count of exported methods/attributes, count of distinct importers (`rg -l "from .*<module>" | wc -l`), count of its own imports.
- **Thresholds:** relative to the repo baseline — flag when a file is ~3× the median size AND importers are ~3× the median. State the baseline numbers used. In tiny repos (< ~10 files of the same kind), the median is meaningless — fall back to absolute judgment (e.g., one file dominating all coupling) and label the weak baseline.
- **Severity:** grows with importer count (every change touches all of them).

### Shotgun Surgery

- **Proxy:** one concept scattered across many modules. Pick a domain concept from the target question (e.g., "invoice", "permissions") and count modules containing references to it: `rg -l "<concept>" | cut -d/ -f1-2 | sort -u | wc -l`.
- **Severity:** grows with module count and with coupling between the scattered sites (do they import each other or duplicate?).

### Feature Envy

- **Proxy:** a module imports another module far more than its own internals. Count both sides and compare — imports of the foreign module: `rg -c "from .*<moduleX>" moduleA/`; imports resolving inside moduleA (relative `./` imports): `rg -c "from '\./" moduleA/`. Flag when the foreign count exceeds the local count by ~2× or more.
- **Severity:** medium when it is one direction; high when the envying module is otherwise nearly disconnected from its own parent.

### Deep Inheritance

- **Proxy:** chains of `extends`/subclassing beyond ~3 levels: `rg "extends "` then follow each chain. Report max depth and the chain.
- **Severity:** medium; flag deepest chain only, not every chain.

### Pure/Impure Entanglement (functional codebases)

- **Proxy:** business-logic functions directly performing I/O (network, DB, filesystem, clock, randomness) instead of receiving results as parameters. Verify by reading the call chain at micro level: mark the first impure hop and whether logic continues after it.
- **Severity:** high when the same file mixes domain rules and I/O calls repeatedly (report count).

### Broken Composition (layer-skipping)

- **Proxy:** dependencies that bypass an architectural interface layer — e.g., web module importing infra directly instead of going through the api/service layer. Visible as ↑ marks in the macro matrix (a dependency pointing against the repo's declared or evident layering).
- **Severity:** scales with how many files take the shortcut.

## Interpretation Rules

- One finding per pattern per target; list all instances under Evidence.
- If the pattern is suspected but evidence is thin, say "suspected, evidence: <what's missing>" instead of asserting.
- Findings are facts about structure. The user decides what to do with them.

---

#### Reference: references/zoom-levels.md

# Zoom Levels — Evidence Collection & Rendering

Per-level collection strategy and text rendering formats. Match the rendering to what the level must show; the paradigm-scale principle applies: module-level relations read best as matrices/graphs, functionality reads best as ordered chains.

## Macro — System Topology

**Collect:**

Adapt import syntax to the language (see language rule below).

0. **Language rule:** use the repo's import syntax for scans. JS/TS: `from './x'`; Python: `import x` / `from x import`; Rust: `use x::`; Go: `go list -deps`; Java: `import x.y;`. If the repo is multi-language, run the scan per language.
1. Top-level directory layout (`ls`, tree to depth 1–2). Identify likely module boundaries (packages, workspaces, domains). Exclude vendored/generated code (`node_modules`, `dist`, `target`, `vendor`, generated committed sources) from all counts — `rg` respects `.gitignore` by default, but generated-but-committed code must be excluded with explicit globs (`-g '!dist'`).
2. Inter-module imports: scan import statements crossing module boundaries. Quoting-safe scan (works in POSIX shells), e.g. JS/TS: `rg 'from ["'"'"']' --type js --type ts` or simply `rg 'import|require'`; then filter by path.
3. Count edges per pair of modules to get a weighted dependency list.
4. Determine layering for violation flags: use the repo's declared layering (README/ARCHITECTURE/docs) if present; otherwise infer it from the majority dependency direction and label that inference in the report.
5. **Weight & composition:** size each module by LOC (default) or file count (mixed-language repos, where LOC is not comparable across languages). Tool: `tokei` (apply the step-1 globs via `tokei -e ...`, or `cloc`); fallback: `rg --files -0 <exclusion globs> | xargs -0 wc -l`, summed per module. Two distinct metrics:
   - **Weight** (inventory column) — analysis code only, same exclusions as all counts (step 1).
   - **Composition** (report block) — disk reality: all code grouped by kind (source / test / generated / vendored / config), excluded code included. Classify by path/name conventions (`tests/`, `test_*.py`, `*_test.go`, `*.spec.ts` → test; step-1 exclusion-glob hits → generated/vendored; lockfiles, manifests, dotfiles → config). Works without modules too (flat/script repos): group by top-level directory or language.
   If neither tool nor fallback works, degrade to file-count-only weight and note it in the report.

**Render:**

Module inventory table:

| Module | Path | Responsibility (one line, from evidence) | Weight | Deps out → | Fan-in |
| :---- | :---- | :---- | :---- | :---- | :---- |

Weight bar: 10-cell `▓/░` bar proportional to the module's share of total weight, rounded to nearest cell, minimum 1 filled cell when weight > 0, followed by an integer percentage — e.g. `▓▓▓▓▓▓▓░░░ 67%`. Weight flags candidate targets for deeper levels; size ≠ complexity (a big cohesive module can be cheaper to map than a small hotspot).

Text dependency matrix (DSM-style). Rows depend on columns; mark `↑` for a dependency that skips a layer or points "backward" against the declared — or inferred (see collect step 4) — layering:

|  | core | api | web | jobs |
| :---- | :---- | :---- | :---- | :---- |
| core | — |  |  |  |
| api | ✦ | — |  |  |
| web | ✦ | ✦ | — |  |
| jobs | ✦ |  | ↑ | — |

Module graph (Mermaid), one node per module, edges labeled with edge counts:

```mermaid
graph TD
  web --> api
  api --> core
  jobs --> core
```

**Facts to surface:** layering violations (↑ marks), cycles, hub modules (highest fan-in/out), orphan modules (zero fan-in), bulk distribution (largest modules by weight; share of total).

## Meso — Module / Feature Wiring

**Collect:**

1. Entry points of the module: exported symbols, route registrations, message handlers, CLI commands.
2. Internal components: classes/functions by responsibility; group by subdirectory.
3. Integration contracts: interfaces implemented, events published/consumed, external clients (HTTP, DB, queues).
4. Which other modules call in, and which the module calls out to.

**Render:**

Component table:

| Component | Path | Role | Talks to (internal) | External ports |
| :---- | :---- | :---- | :---- | :---- |

Wiring graph (Mermaid). Show direction and label edges with the mechanism (calls, publishes, reads):

```mermaid
graph LR
  routes --> service
  service --> repo
  service -- "publishes InvoicePaid" --> bus
  repo -- SQL --> [(db)]
```

**Facts to surface:** single points of fan-in, components with no callers, cross-boundary calls that bypass the module's public interface.

## Micro — Functionality Trace

**Collect:**

1. Locate the entry symbol (`rg "symbolName"`); read it fully.
2. Follow the call chain depth-first through the request's relevant path. Record each hop: `caller → callee (path:line)`.
3. Mark at each hop: pure logic, state mutation, I/O side effect (network, disk, DB, clock, randomness).

**Render:**

Ordered call chain:

```
1. POST /refunds        routes/payments.ts:42
2. refundInvoice()      services/refund.ts:17      [pure until step 4]
3. validateAmount()     domain/money.ts:88         [pure]
4. gateway.refund()     infra/stripe.ts:120        [I/O: network]
5. ledger.record()      infra/ledger.ts:55         [I/O: DB write]
6. bus.publish()        infra/events.ts:30         [I/O: queue]
```

Data flow list — what enters, how it transforms, what leaves:

```
RefundRequest{invoiceId, amount} → validated Amount → gateway response → LedgerEntry → InvoicePaid event
```

**Facts to surface:** where purity ends (first I/O hop), total hops, state mutated, error paths encountered (report; do not evaluate their quality).

Decision record: macro maps encode scale as a proportional weight bar in the inventory table (treemap area-encoding, phronemophobic "Treemaps are awesome!") plus a Composition block, not as literal treemap graphics — the vendored Mermaid v9.4.3 has no treemap diagram (v11.5+ is ESM-only, CORS-blocked under `file://`) and an ASCII treemap is less diff-able than the bar column. (treemap evaluation, 2026-09-05)
