/arch-map
What it does
Maintains architecture.yml at the repo root — a hand-curated,
machine-readable map of top-level modules. Complements
/repo-map, which generates a human-readable
mermaid diagram; this skill produces the structured-metadata layer an
agent can load and query in one parse.
The point: agents proposing refactors regularly ask "is this module
stable or experimental?" / "what's the public surface I shouldn't
silently rename?" / "what module does this pair with?". Re-deriving the
answer from prose AGENTS.md + directory tree walks is wasted tokens
every session. architecture.yml answers in one YAML load.
This skill drafts the file (from directory walk + AGENTS.md hints).
The user confirms role and stability per module. Auto-generated
guesses get a <TODO> placeholder until the user fills them.
See ADR 0005 for the rationale (why YAML over JSON, why root over
docs/, why not per-folder).
When to invoke
- First setup of an AI-first repo (after
/init-repoand ideally after/repo-map). - After major directory restructuring (modules added, renamed, removed).
- When an agent is about to propose a refactor and you'd rather it
read
architecture.ymlfirst than guess at module roles.
Do NOT invoke for:
- Per-file documentation (use AGENTS.md per directory, or docstrings in the code).
- Auto-generated dep graphs from imports (that's
/repo-map'smadge/pydepsblock). - Tracking current commit / branch / version (this file is architectural, not point-in-time).
File format
# claude-leverage:architecture-map v1
# Hand-curated structured architecture metadata. Update via /arch-map
# or by hand-editing this file. Schema: claude-leverage v1.
# See: https://github.com/Filip-Podstavec/claude-leverage/blob/main/skills/arch-map/SKILL.md
repo:
name: "<repo-name>"
primary_lang: "<lang or lang+lang+lang>"
one_line: "<one-sentence concrete description: what + for whom>"
modules:
- path: "<dir-or-file>/"
role: "<one-line purpose>"
stability: stable | evolving | experimental | deprecated
public_surface:
- "<name agents must not silently rename>"
- "<another>"
depends_on:
- "<other module path>"
paired_with: "<module that mirrors this one>"
owners:
- "<name or @handle>"
Required fields
repo.name,repo.primary_lang,repo.one_line- Each module:
path,role,stability
Optional fields
modules[].public_surface— names/files agents must not casually rename (the rename will break callers outside the module). Strings.modules[].depends_on— array of other modulepathvalues this module depends on at the architectural level (not import-level).modules[].paired_with— single module path that mirrors this one, e.g.agents/paired with.codex/agents/.modules[].owners— array of names or @handles.
Stability levels
| Value | Meaning |
|---|---|
stable | Public surface is load-bearing; renames need an ADR + caller migration. |
evolving | Public surface may change; callers should pin imports. |
experimental | May be deleted entirely. Don't depend on this module from outside. |
deprecated | Scheduled for removal. New code should not reference it. |
Workflow
-
Resolve repo root.
git rev-parse --show-toplevel. If not in a git repo, STOP and report. -
Detect mode.
--validate→ parse existingarchitecture.yml, check required fields, checkpathvalues exist, report findings, write nothing.- No existing file → bootstrap mode.
- Existing file + no
--validate→ extend / refresh mode: re-walk directories, suggest entries for new modules; preserve existing module entries unchanged.
-
Walk top-level directories (depth 1 by default; pass
--depth Nfor 1–3). Skip:- Dotfiles except
.codex/.claude*(those are tooling). - Standard noise:
node_modules,__pycache__,dist,build,.next,.pytest_cache,target,vendor,bench/archive-*. - Empty directories.
- Dotfiles except
-
For each non-trivial directory, infer:
path— the directory path relative to repo root, with trailing slash.role(draft) — read the directory'sAGENTS.mdif present; extract the first H1 heading + first paragraph. If no AGENTS.md, emit"<TODO: one-line purpose>".stability(draft) — emit<TODO: stable|evolving|experimental|deprecated>. The skill won't guess this; the user knows.public_surface(draft) — best-effort: list top-level filenames without leading_, plus any obvious exports (Python__all__, TS named exports, Go exported identifiers). Cap at 10 entries. If unclear, omit the field.depends_on(draft) — omit; the user fills if architecturally meaningful.paired_with(draft) — heuristic: if there's a.codex/<name>/mirror of<name>/, propose that pairing.owners(draft) — omit by default. The user can fill or skip.
-
Read existing
architecture.ymlif present. Parse YAML (stdlib-only viapython -c 'import yaml'if available, else a conservative line-based parser — see "Parser fallback" below). Compute set ofmodules[].pathvalues already documented. New module entries proposed only for paths not in that set. -
Show the user the draft (interactive mode):
I'll write architecture.yml at <repo-root>/. Confirm or edit: repo: name: "claude-leverage" primary_lang: "shell+python+markdown" one_line: "<TODO: one-sentence concrete description: what + for whom>" modules (proposed): - path: "scripts/hooks/" role: "<TODO: one-line purpose>" <-- AGENTS.md present at this path? no stability: <TODO> - path: "skills/" role: "Cross-tool on-demand skills (agentskills.io spec)" <-- from skills/README.md stability: <TODO> - path: "agents/" role: "Claude Code subagents" <-- from agents/README.md stability: <TODO> paired_with: ".codex/agents/" ... Proceed? (y / n / edit-each)With
edit-each, walk modules and prompt forrole+stabilityper module. Withy, write as-shown (<TODO>placeholders intact). In--noninteractive, write as-shown without prompting. -
Write the YAML. Use a deterministic key order (the order in the schema above). Quote strings that contain
:or start with-. Use 2-space indentation. End with a single newline. -
Validate the written file. Run the YAML parser on it; check:
- Top marker comment is exactly
# claude-leverage:architecture-map v1. repo.name,repo.primary_lang,repo.one_lineare present and non-empty (a<TODO>placeholder counts as "present").modulesis a non-empty list.- Each module has
path,role,stability. - Each
pathvalue exists on disk (warn if not — could be an intentional placeholder for a planned module). stabilityis one of the four valid values OR a<TODO>placeholder. If validation fails, print the error with line number and stop before writing the final version.
- Top marker comment is exactly
-
Update AGENTS.md (idempotent, ask before write). If
AGENTS.mdexists and doesn't already mentionarchitecture.yml, offer to append:### Architecture metadata Machine-readable module map: [`architecture.yml`](architecture.yml). When proposing refactors that cross module boundaries, **read this first** for `stability` and `public_surface` constraints.Skip if
--no-update-agents-mdis passed. -
Report. Print path, count of modules total, count of
<TODO>placeholders remaining, suggested next step (/arch-map --validateafter filling).
Validate mode (--validate)
Read architecture.yml, run the same checks as step 8, exit. Useful in
CI: