| name | system-flow-mapper |
| description | Produces three grounded deliverables for ANY codebase by reading it (source unchanged), written into the project under docs/system-flow/: SYSTEM_FLOW.md (deep technical reference), system-flow.html (jargon-free offline companion for non-technical readers), and system-flow-technical.html (interactive offline reference for engineers: searchable endpoint cards, actor sidebar, sequence diagrams, light/dark toggle). Detects project type first and routes to the right playbook (backend/API, frontend/SPA, full-stack/monorepo, Pulumi, Flux/GitOps, or generic IaC). Use this whenever the user wants to "map the flow", "understand how this project works", "document the system", "draw the request/response flow", "diagram the architecture", "trace how data moves", "explain the request lifecycle", "show what each user/role can do", or "onboard me to this codebase" — across frontend, backend, full-stack, or infrastructure-as-code repositories, even if they don't name these files explicitly. Prefer this skill over ad-hoc exploration whenever the goal is a durable architecture/flow document plus a plain-language companion. Not for modifying code (it is read-only — it only writes docs under docs/system-flow/) and not a substitute for runtime profiling or live debugging. |
System Flow Mapper
Reproduce, on any project, a three-part deliverable:
$DOCS_DIR/SYSTEM_FLOW.md — the source-of-truth
technical map (how data moves, what each actor can do, the data model, async paths,
and a tech-debt list).
$DOCS_DIR/system-flow.html — a self-contained,
fully-offline, jargon-free visual companion for non-technical readers.
$DOCS_DIR/system-flow-technical.html — a
self-contained, fully-offline, interactive technical reference: searchable endpoint
cards with method badges and role chips, actor-scoped sidebar navigation, sequence
diagrams, and a light/dark toggle. For engineers and reviewers.
$DOCS_DIR is docs/system-flow/ inside the analyzed project (computed in Step 0c).
This is a read-only analysis of the project's source — never change
application, config, or source files. The only files you create are this skill's
own deliverables under $DOCS_DIR/ (the three docs, per-section fragments, and
state.json). The one project file you may touch is .gitignore, and only with
the user's say-so (see Step 0c).
The bar: deep, grounded (every claim cites a real path:line), and honest
(anything you can't confirm is tagged VERIFY, never guessed). The companion HTML
must read like a friendly explainer with zero jargon.
Data sources: files by default; live infra reads only on opt-in
Default — and ALWAYS in non-interactive / benchmark / automated runs: work
strictly from committed files, read-only. Derive everything from what's in the
repo. Do not invoke project CLIs (pulumi, flux, kubectl, etc.), and do not
contact any live cluster, cloud account, or state backend. Any live-only state that
can't be read from files is tagged VERIFY rather than guessed.
Optional live-read mode (infra projects only, opt-in per run): if — and only
if — the user explicitly opts in for this run (e.g. "run in live mode" / "you can
use the cluster"), the Pulumi/Flux/IaC playbooks may run read-only introspection
commands to fill in live state. The infra playbooks offer this during Phase 1 and
list the prerequisites; never assume it. Live mode is governed by these rules
(details and exact command allow/deny lists live in the infra playbooks):
- Read-only commands ONLY. Never run anything that mutates infrastructure, state,
or the repo (no
apply/create/delete/patch/edit/scale/exec/port-forward,
reconcile/suspend/resume/bootstrap, up/destroy/import/config set/state,
git commit/push/checkout/clean).
- Confirm the target before any cluster command. Print the active kube context +
namespace (or Pulumi stack) and confirm with the user it's the intended
environment, so production is never touched by accident.
- Fail-stop, never fall back blindly. If a command fails on access (wrong/no
context, not authenticated, missing passphrase), STOP and tell the user exactly
what to set up. Don't retry blindly or silently revert.
- Never reveal secret VALUES. Even in live mode, never retrieve, decrypt, or
display plaintext secret material, and never write a secret value into either
deliverable. No
kubectl get secret -o yaml|json (existence + metadata only), no
pulumi stack export of secret material, no --show-secrets flags. Prefer
non-secret reads. Record that a secret exists and where it's referenced —
never its plaintext.
- Anything still unreadable stays
VERIFY. Live mode never modifies the repo or
the infrastructure — it only reads.
The workflow (every run)
Phase 0 Read intent docs → detect project type → announce the playbook
Phase 1 Read-only INVENTORY → STOP, present it, wait for confirmation ← mandatory gate
Phase 2 Build all three deliverables, sliced by the Phase-0 axis
Phase 3 Self-verify (spot-check citations + headless-render both HTMLs)
Track these as TODOs so the gate isn't skipped.
Phase 0 — Detect the project type
Step 0a — Read intent & convention docs first (when present)
Before scanning for project-type markers, read any of the following files that
exist in the repo. They provide intent and convention context — domain
vocabulary, module boundaries, known decisions — that sharpens the inventory in
Phase 1 and the diagrams in Phase 2.
Files to look for (not all will exist — skip gracefully if absent):
| File / path | What to extract |
|---|
CLAUDE.md, AGENTS.md, .cursorrules | Coding conventions, off-limit areas, project vocabulary |
README.md (root or per-package) | Purpose, entry points, how to run |
ARCHITECTURE.md | Module boundaries, design decisions |
CONTRIBUTING.md | Workflow, naming conventions |
docs/** (any .md) | ADRs, runbooks, guides |
Files named ADR-*.md, adr-*.md, or inside docs/decisions/, docs/adr/ | Architectural decision records |
Treat these as SECONDARY to code. They guide where to look and provide
vocabulary; they do not override what the code actually does. Apply this rule
consistently:
- If a doc says something that matches the code → use it freely as context.
- If a doc contradicts the code → trust the code. Flag the gap as:
VERIFY — CLAUDE.md says X; code at path:line shows Y
Collect all such flags in the VERIFY list (§12 of SYSTEM_FLOW.md).
- If a doc claims something you cannot confirm in the code → tag it
VERIFY.
- If no doc files exist → proceed directly to marker detection below.
Do not change your grounding discipline: every claim in the deliverables still
cites a real path:line. Docs add vocabulary, not citations.
Step 0b — Detect the project type
Inspect manifests and signature files, scanning recursively (key marker files
are often nested in subdirectories, e.g. Pulumi.yaml under tenants/<x>/, or
Flux kustomization.yaml under clusters/<env>/). Do not judge from the repo
root alone.
Check, in this order (IaC markers win over language detection — a Pulumi
program is TypeScript but is not a backend):
| Look for | → Playbook | Load |
|---|
Pulumi.yaml / Pulumi.<stack>.yaml, or @pulumi/* in package.json/requirements | Pulumi | references/pulumi.md |
flux-system/, gotk-components.yaml, HelmRelease/Kustomization CRDs, clusters/<env>/ overlays | Flux / GitOps | references/flux.md |
*.tf / .terraform/, CloudFormation *.template.{json,yaml}, serverless.yml, Helm Chart.yaml (standalone), raw k8s manifests | Generic IaC | references/iac.md |
| A server framework + routes/handlers: Express/Nest/Fastify (Node), FastAPI/Flask/Django (Python), Gin/net-http (Go), Spring (Java), Rails (Ruby), etc. — and no significant client app | Backend / API | references/backend.md |
| A client app: React/Vue/Svelte/Angular + a bundler (Vite/Webpack/Next), routes + components + API client — and no significant server | Frontend / SPA | references/frontend.md |
Both a server app AND a client app (monorepo, or apps/+libs/, or frontend/+backend/) | Full-stack | references/fullstack.md |
Detection tips:
- Read
package.json dependencies, not just file extensions — @pulumi/aws
means Pulumi even though the code is .ts; react+vite means frontend;
@nestjs/*/express/fastify means backend.
- For Python:
pyproject.toml/requirements.txt deps (fastapi, flask,
django, celery) classify the backend. The backend playbook is
framework-agnostic — never assume a specific framework's constructs.
- A repo can be ambiguous. If two playbooks plausibly apply (e.g. a Next.js app
with API routes), say so, pick the dominant one, and note that you'll fold in the
secondary axis. When genuinely unsure, ask the user before continuing.
Announce the result, e.g.: "Detected a Flux GitOps repo (clusters/ with
staging + production overlays). Using the Flux playbook — I'll slice by
environment." Include a one-line note on any doc files read in Step 0a and
whether any doc↔code contradictions were found.
Step 0c — Determine the output directory and load prior state
Compute DOCS_DIR (every run, before any other action):
$SKILL_DIR — throughout this skill, this is the absolute path of this
skill's own directory (the folder containing scripts/, assets/, and this
SKILL.md). Resolve it once, then substitute it in every command below:
- Claude Code plugin install:
SKILL_DIR="${CLAUDE_PLUGIN_ROOT}/skills/system-flow-mapper"
- Otherwise (
npx skills, manual copy, or any other agent): it's the
folder this SKILL.md lives in. If you don't already know that path, discover
it from a bundled marker file:
SKILL_DIR="$(cd "$(dirname "$(find "$HOME" -type f -path '*system-flow-mapper/scripts/get_docs_dir.py' 2>/dev/null | head -1)")/.." && pwd)"
DOCS_DIR=$(python3 $SKILL_DIR/scripts/get_docs_dir.py .)
This returns docs/system-flow/ inside the analyzed project — a stable location,
so re-runs reuse state.json for incremental updates. Announce:
"Docs directory: <DOCS_DIR>".
Because these generated files live inside the repo, ask the user once:
"Add docs/system-flow/ to .gitignore so the generated docs aren't committed,
or keep them tracked?" If they choose to ignore, append docs/system-flow/ to
the project's .gitignore (create it only if they want). This is the only project
file this skill may modify — never touch source.
All deliverables write to this directory:
${DOCS_DIR}/SYSTEM_FLOW.md
${DOCS_DIR}/system-flow.html
${DOCS_DIR}/system-flow-technical.html
Per-section fragment files persist in ${DOCS_DIR}/fragments/ (written during
Phase 2, read on incremental runs):
fragments/<name>-md.md — SYSTEM_FLOW.md content for this section
fragments/<name>-plain.html — plain-language HTML section body
fragments/<name>-tech.html — technical HTML actor/reference panel body
Where <name> is the section's short identifier (e.g. public, admin,
traces, data-model). The section order is the order they appear in the
final documents.
Check for prior state — look for ${DOCS_DIR}/state.json:
Case A — No state.json (first run, or file missing/corrupt): proceed normally
through Phases 1–3.
Case B — state.json found (incremental run): read commit from it, then:
git diff --name-only <state.commit>..HEAD
If the diff is empty (no new commits since last run), tell the user the docs are
already up to date and stop.
Otherwise, classify each section in state.json["sections"] as stale or
clean by checking whether any changed file path has a prefix that appears in
that section's paths list (literal prefix match, not glob):
- A section with an empty
paths list ([]) is always stale when anything else
changed — this covers cross-cutting reference sections like traces and data-model.
- A section is clean only if none of its prefixes match any changed file.
Announce: "Incremental run — stale: [names], clean: [names]. Skipping Phase 1
inventory confirmation for clean sections."
In Phase 2 (incremental run):
- Stale sections: re-read their source files, regenerate all three fragment files.
- Clean sections: read existing fragment files from
${DOCS_DIR}/fragments/ — do
not re-read their source files.
- Rebuild both HTML files in full from all fragments via
build_html.py — never
surgically edit the 3.3 MB assembled HTML.
Phase 1 — Inventory, then STOP (mandatory gate)
Before building anything, do a quick read-only pass to produce an inventory,
then stop and present it to the user for confirmation. The per-type reference
file lists exactly what to inventory for that playbook; in general it is:
- the slice axis (actors/roles for apps; environments+components for IaC) and
the concrete list of values on that axis;
- the surface: routes/endpoints, pages, or resources/modules — counted and
grouped;
- data stores and the data model entities;
- async / out-of-band paths (queues, events, websockets, schedulers, CI/CD,
reconciliation loops);
- entry points and how a request/change enters and flows through;
- a first pass at security / tech-debt smells to chase down.
Then present a short summary: "Here's what I found and how I'd slice the
docs — X actors/environments, Y endpoints/resources, these diagrams. Confirm and
I'll build both files, or tell me what to adjust."
Why this gate matters: the whole value is slicing the documentation along the
axis that matches how people actually reason about this system. Slicing wrong (by
the wrong roles, or per-resource when the user thinks per-environment) wastes a
large build and produces a doc nobody uses. A 30-second confirmation prevents
building the wrong thing. Do not skip it in interactive use. (In a
non-interactive / automated run where no human can answer, record the inventory in
the doc and proceed.)
Phase 2 — Build the two deliverables
Open the routed reference file and follow it — it defines the section structure,
the required diagrams, and the slice axis for that project type. The references
share these rules:
Grounding discipline (non-negotiable)
- Every claim cites a real
path:line. If you state that an endpoint exists,
a resource is public, or two tables relate — point at the file.
- Never fabricate. No invented endpoints, resources, env vars, or relations.
If you didn't read it, you don't claim it.
- Tag uncertainty
VERIFY with what you'd need to confirm it, and collect
these in an "Open questions / VERIFY list" section. Honest gaps beat confident
fiction.
Diagrams are Mermaid only
sequenceDiagram for request/response and event flows. Match the gold-standard
arrow-payload style: put the method/path/payload on the arrow, e.g.
FE->>API: POST /things {CreateThingDto}<br/>Authorization: Bearer {token} and
API-->>FE: { data, errors }. Use participant aliases and Note over for
context. In sequence-arrow text, avoid bare <...> and unmatched () —
Mermaid's sequence parser reads <...> as markup and errors out (<br/> is the
one safe tag); use {token}, [id], or plain words instead.
flowchart TD / graph TD for capability maps and architecture/topology.
erDiagram for data models (with cardinality and key attributes per entity).
- Keep each diagram focused; prefer several readable diagrams over one giant one.
Mandatory "Known issues / tech debt" section
A dedicated section in the .md collecting security-relevant and correctness
findings — the do-not-carry-over list. Rate severity, cite the file, explain
the impact. For IaC, this is where public buckets, open ingress, broad IAM/RBAC,
hardcoded secrets, and missing encryption go.
Fragment discipline (required for incremental updates)
As you generate each section, write it to three persistent fragment files in
${DOCS_DIR}/fragments/:
<name>-md.md — this section's SYSTEM_FLOW.md content
<name>-plain.html — this section's body content for system-flow.html
<name>-tech.html — this actor/reference panel for system-flow-technical.html
Where <name> is the section's short identifier (e.g. public, admin, traces,
data-model). On a first run, write all fragments. On an incremental run, re-write
only stale-section fragments and read clean-section fragments from disk.
Before assembling each HTML, concatenate all *-plain.html fragments (in section
order) into ${DOCS_DIR}/fragments/plain-body.html, and all *-tech.html fragments
into ${DOCS_DIR}/fragments/tech-body.html.
HTML deliverable 1 — plain-language companion
Build the .md first, then translate it into the plain-language companion. The HTML
is for someone non-technical: no routes, DTOs, HTTP verbs, resource ARNs, class
names, or file paths. Translate everything to everyday terms. See
references/html-template.md for the section pattern, the diagram color palette,
and how to assemble the file.
Assembling the plain HTML:
python3 $SKILL_DIR/scripts/build_html.py \
--content "${DOCS_DIR}/fragments/plain-body.html" \
--output "${DOCS_DIR}/system-flow.html" \
--title "How <Project> Works, In Plain Language"
HTML deliverable 2 — technical interactive reference
After the plain companion, produce the technical interactive HTML from the same
SYSTEM_FLOW.md findings. This is for engineers: searchable endpoint cards with
color-coded HTTP method badges and role chips, actor-scoped sidebar navigation,
rendered sequence diagrams, and a light/dark toggle — all offline. See
references/html-technical.md for the fragment structure, the JS selector contract,
and endpoint-card rules.
Assembling the technical HTML (pass --shell to use the interactive shell):
python3 $SKILL_DIR/scripts/build_html.py \
--content "${DOCS_DIR}/fragments/tech-body.html" \
--output "${DOCS_DIR}/system-flow-technical.html" \
--title "<Project> — Technical Reference" \
--shell $SKILL_DIR/assets/html-technical-shell.html
Do not try to write either HTML by hand — the Mermaid bundle is ~3.3MB.
Both resulting files are ~3.3MB by design (the price of true offline capability).
Phase 3 — Self-verify before declaring done
- Spot-check 3–5 claims against source — open the cited files and confirm the
endpoint/resource/relation actually says what the doc says. Fix or
VERIFY-tag
anything that doesn't hold.
- Headless-render both HTML files offline. Use the Playwright MCP:
- Navigate to
file://<abs path> for each file (no network/CDN dependency).
If your headless tool blocks the file: protocol, serve $DOCS_DIR with a
localhost static server (python3 -m http.server from that dir) and open
http://127.0.0.1:<port>/<file> — still fully offline, no CDN.
- Wait for
svg elements inside .mermaid / .diagram-wrap to appear.
- Check the console for Mermaid parse errors.
- For the technical HTML: also verify that panel switching works (click a nav
item, confirm the correct
.actor-panel becomes visible).
- If a diagram fails, the usual cause is a Mermaid syntax slip — fix the
fragment and rebuild.
- Confirm all three files cross-link each other (relative
./ links work because
they all live in the same ${DOCS_DIR}), and the .md has its full
table-of-contents and the tech-debt + VERIFY sections.
- Write
${DOCS_DIR}/state.json with the current HEAD commit, today's date,
and a per-section entry for every section generated (stale or clean):
{
"commit": "<git rev-parse --short HEAD>",
"mappedAt": "<YYYY-MM-DD>",
"sections": [
{ "name": "public", "paths": ["src/auth", "app/api/auth"] },
{ "name": "admin", "paths": ["src/admin"] },
{ "name": "traces", "paths": [] },
{ "name": "data-model", "paths": ["src/models", "src/entities", "prisma"] }
]
}
paths contains the directory prefixes in the project repo that, when changed,
should cause this section to be regenerated on the next run. Use your Phase 1
module-boundary knowledge to fill these in. Sections that span everything (traces,
ER diagrams, tech-debt lists) should have "paths": [] so they always rebuild
when any sibling section rebuilds. Section order in the array must match the order
of the actual document sections.
Reference files (load the one Phase 0 routed to)
| File | When |
|---|
references/backend.md | Backend / API services (any language/framework) |
references/frontend.md | Client SPAs / web frontends |
references/fullstack.md | Monorepos / repos with both a backend and a frontend |
references/pulumi.md | Pulumi infrastructure programs |
references/flux.md | Flux / GitOps Kubernetes config repos |
references/iac.md | Generic IaC: Terraform, CloudFormation, serverless, Helm |
references/html-template.md | Always — plain-language companion (HTML deliverable 1) |
references/html-technical.md | Always — interactive technical reference (HTML deliverable 2) |
A worked, gold-standard example of both deliverables (a NestJS + React monorepo)
is the reference for depth and style. The structure and discipline generalize;
the content does not — never carry a previous project's specifics into a new one.