System diagram.

As agents make shipping faster, map your GTM system as one architecture diagram, so the team stays grounded and builds it to hold together.

AmdahlGTM engineering1 install
SKILL.md

Turns a written source into one interactive page that a non-engineer can read in a minute and an engineer can build from. The engine and build script are fixed. Your job is the data file: choosing the steps, the rows and the boxes, and making every sentence true to the source.

Files: build.py (checks and builds), engine/ (page shell and script), themes/neutral.json (system fonts, works anywhere), tools/check.mjs (clicks everything, takes screenshots), references/schema.md (every field), references/judge.md (the prompt for an independent source check), examples/skeleton/ (short, made up, every feature: start here).

Needs only python3 to build. The checker (step 5) also needs Node 18+ with npm; if node is not found, look for it under your version manager (nvm, fnm, volta, pnpm) and put it on PATH in the same command. Nothing else is needed: no fonts, no network, no design system.

Steps

  1. Read the whole source. A Notion page or another web doc: use a connector for it if one is available, otherwise ask the person to paste or export it. Files: Read. Note the time you read it. Read the pages it depends on, or name what you did not read in meta.source ("Source: holds-spec.md and returns.md, as of 22 Oct 14:05 UTC; the code was not read").
  2. Pick the reader and their journey. Ask who opens this page and what they want to know. The columns (stages) are what the subject goes through, in order: a new user's path, a deal's path, a request's path. 4 to 7 of them. The rows (lanes) are who does each part. 2 to 4 of them. Then list every decision a person makes in the source that changes the result (approve a request, answer a question, turn a setting on or off). Each one gets a box or a note in that person's row.
  3. Write the data file in a folder that outlives the session; the session scratchpad gets wiped. For example ~/system-diagrams/<slug>/data.json. Copy the shape of examples/skeleton/data.json, look up fields in references/schema.md, and follow the content rules and source rules below.
  4. Build: python3 <skill>/build.py data.json --out index.html (the neutral theme is the default; pass --theme <path> for your own). Fix every error. Act on every warning: merge boxes, add the missing field, or decide that the warning does not apply here.
  5. Check: node <skill>/tools/check.mjs index.html <scratchpad>/check. It must report no problems. Then look at desktop-light-<first tab>.png, phone-light-<first tab>.png and the panel-<tab>.png files (the longest open panel on each tab). Setup, once per machine: cd <skill>/tools && npm ci && npx playwright-core install chromium (on Linux, install --with-deps chromium). If Node cannot be installed, build with --standalone, open the page in a browser and click through every tab yourself instead.
  6. Check it against the source. Run the source rules' self-check below. For a diagram other people will read, also start one independent agent with the prompt in references/judge.md, and fix what it finds.
  7. Publish. Where the claude.ai Artifact tool is available, publish index.html with it (private by default, icon: "flow"). Tell the person it is private and that they share it from the page's Share menu. Anywhere else, build with --standalone and give the person the file: it opens in any browser with no server, and any static host can serve it.
  8. Record the data folder and the page's link or path where the next session will find them (a memory note, or the project's docs).

Content rules

  • Organize by the reader's journey, not by how the system is built. Test each box: would the reader see this happen, or do it? If not, it is plumbing. Scheduled passes, queues, locks, keys and the internal stages of one decision (filter, model call, checks, second check) stay out of the grid: fold them into one box's parts or where. Internal signals and stored records go in the footnote row (foot). Staff dashboards and reports never take a journey column; give them their own tab.
  • Name rows by role. "The reader", "Desk staff", "The catalogue". When several automatic actors take part, name each by the job it does ("The overdue check", "The pick-up reminder"). When two systems split the work and the reader cares which one acts, split the row or name the system in each box's text.
  • A box sits in a role's row only when the source gives that action to that role. When the source does not say who does it, write that it is not covered, never a guess. When the source gives one action to "a person or an agent", say both in the row's sub, or give agents a row. A person's setup step that the source asks for (connect an account, turn a setting on) is a box in that person's row, not a callout on a system box.
  • Plain sentence first, detail second. text and plain use the reader's words. Code names, routes, tables, flags, character budgets and rollback steps go in where, notes and parts. Explain internal shorthand (a codename, a flag) the first time a reader-facing sentence uses it.
  • Only warnings sit on a box. At most one tag: wait (blocked on other work) or gap (needed and not built; for a running system, a known limit the reader will hit). optional only when the source calls the step optional. Build pieces and owners go in refs, shown in the panel.
  • About 20 boxes, 3 or fewer in a cell. Over that, merge boxes in the same row and step. A merged box keeps every fact of the boxes it replaced, one parts section each.
  • Flags are for disagreements and open questions. When two parts of the source disagree, or a stated fact cannot be true, add a flag with src: "Found while drawing this" and quote both sides. Before writing it, check each case it lists against both sides; cut cases that do not disagree. A limit the source states plainly is not a flag: it is a gap tag or callout on the box it limits. A source's own "open" list becomes flags with src naming that section. A disagreement the source resolves itself ("this reverses the note above") is not a flag: use the later statement, and add a note on the box if readers may meet the older one.
  • Each item sits on the tab it affects. Flags about the grid go in the journey tab's flags, a pricing question on the pricing tab. No separate "open items" tab.
  • Give each extra tab one job: the build plan (graph + checklist), the numbers (table, meter, stats), a catalogue (a table with clickable rows). Prefer a table of numbers to a chart that needs a legend.
  • Plain ASCII punctuation. No em dashes, curly quotes, arrows or middle dots (the build warns). Refer to people by name or "they"; never guess pronouns.

Source rules

Every independent check of a test diagram found the same kind of error: a sentence that says more, or less, than the source. These rules prevent it.

  • Every fact comes from the source. No invented dates, numbers, owners, statuses, file locations or capabilities. A value you work out (a weekday from "next Tuesday", a total, a percentage) says how you got it.
  • Never widen a scoped fact. When the source limits a rule ("on both paths", "only when it has a line", "in strict mode"), no heading or summary may say "every", "always", "never" or "all" for more than the source does.
  • Keep every exception, trigger and condition that changes the result. A list of triggers keeps them all or says "among others". Keep the source's numbers ("at least 80%", not "most"). A fallback or deadline keeps its condition, above all in meta.stats, which is read with no context.
  • Modes and switches. When behaviour depends on a mode or switch, each headline, stat and box holds in every mode the source names, or names its mode.
  • Samples are not populations. A fact about a test set, a trial or one day of data stays about that set.
  • Examples stay examples. Text in parentheses or after "for example" never becomes the rule for every case.
  • Instructions stay instructions. "Never set X on staging" is an instruction to operators, not a claim about how the system behaves.
  • Keep each sentence's subject. When splitting a paragraph across boxes, a sentence keeps its own subject; resolve "it" to the noun the source means. Name a component as a component (a weight inside a score is not the score).
  • Copy tables faithfully. Keep the column headers word for word. If you rename one, re-check every cell against the new header.
  • where names only what the source ties to that step. A constant is tied to a file only when the source says so; a route the source lists elsewhere is not evidence that this step uses it.
  • Check negative claims against every surface. "Nothing reaches you by text" must hold for every message the source describes, including those in other sections.
  • Two orderings, name which. When the source gives an order shown and an order of priority, a summary says which one it means.
  • Same name, two things. Tell them apart on every mention, and flag it.

Self-check before publishing. Run python3 <skill>/build.py data.json --scope-words. It lists every "only", "never", "always", "every", "all", "any", "nothing", "except", "first", "most" and "each" with its place in the file. Check each one against the whole source: does the source say this, for exactly this set? Then re-read every plain sentence and every stat against the source line it came from. In testing, this self-check caught many overstatements that a reread missed, and an independent check still found more, which is why step 6 also asks for one for anything others will read.

Updating a diagram when the source changes

  1. Fetch the source again. List what changed against the data file, item by item.
  2. Save the current file as data.v<N>.json, then edit data.json. Remove flags the source has resolved, and tell the person which ones.
  3. Update meta.source with the new read time. Build, check, and publish to the same place (for an Artifact: the same file path in the same session, or its url from another session).

When asked to simplify

Run this pass, then propose the specific cuts before applying them:

  • Tags that are not warnings: move them to the panel.
  • Cells with more than 3 boxes, or rows of internal steps: merge into one box with parts.
  • Rows about plumbing or the team's own monitoring: turn them into the footnote row or a tab.
  • Tabs that only list open items: move each item to the tab it affects.
  • Charts that need a legend: replace them with a table. Afterwards, check that the box count went down and that no fact disappeared: every merged fact is in a parts section.

Themes

neutral is the default and the only theme shipped. It uses each reader's system fonts, so it needs no font files, no network and no design system, and looks the same on any machine.

For a brand look, copy themes/neutral.json anywhere, change the colors, and pass its path to --theme. To use brand fonts, put the font files next to the theme file and list them in fontFiles with relative paths; the build inlines them into the page. A font file it cannot find is skipped with a warning and the page falls back to system fonts, so a missing file never blocks a build. Only inline fonts whose licence allows it. The format is in references/schema.md.

Limits

  • The graph layout is by hand: you give each node a lane and row.
  • The data is static. For new numbers, rebuild and republish.
  • On a phone the journey grid scrolls sideways inside its box, by design.