Package map

What lives in each of the thirteen packages, and where to make a given change.

A TypeScript monorepo under packages/*, built with project references (tsc -b) plus an esbuild bundle for the runtime client.

packages/
├─ diagnostics   error/warning format, codes (MRW-Exxx), snippets, suggestions
├─ model         the normalized in-memory topology every module speaks
├─ scene         the geometric contract (boxes, polylines) + geometry helpers
├─ metrics       deterministic text measurement (static glyph tables)
├─ icons         icon packs: manifest, generator, search, suggestions
├─ schema        YAML parse → strict validation (line/col, did-you-mean) → model
├─ layout        classify → place (ELK / compose / polar / circle) → route (libavoid)
├─ lint          geometry linter: L1 fails builds, L2 auto-repairs, L3 advises
├─ render-svg    scene → SVG (hero tier) or canvas host (scale tier)
├─ runtime       the ~20 KB client: camera, inspector, spotlight, minimap, export
├─ site          collapse, page planner, single-file + multi-page compiler
├─ server        dev server: watch, rebuild, SSE live reload, /api
└─ cli           the marrow command

Supporting trees at the repo root:

icon-packs/   drop-in packs (manifest.json + svg/) — cisco, affinity, k8s, generic
              committed; aws, azure, gcp fetched per-clone
fixtures/     valid / invalid / corpus / v1 topologies driving the test suite
input/        topology YAML (yours)
output/       generated HTML (gitignored)
docs/         this site
legacy-v1/    archived Python v1, untracked, reference only

Where to make a change

You want to changePackageNotes
A new YAML key, or validation behaviorschemaAlso add the code to diagnostics and document it in the reference
A diagnostic message, hint, or suggestiondiagnosticsCodes are permanent — add, never renumber
Which archetype a topology getslayout/classify.tsPrecedence order is documented and load-bearing
Node placementlayout/elk.ts, compose.ts, presets.ts
Edge routing or the tidy passlayout/router.ts, tidy.ts
Port chip placementlayout/portchips.tsL1 rule port-chip-overlap guards it
What fails a buildlintMoving a rule between L1 and L3 is a breaking change in practice
How something looksrender-svg and runtime/client/canvas-engine.ts and site/styles.tsAll three, always
Interaction, chrome, keyboardruntime/client/main.ts + site/shell.ts + site/styles.tsVanilla only — no React
Page splitting, what earns a pagesite/planner.tsThresholds are exported constants
Dev server or its APIserver
A command or flagcliUpdate the CLI reference too
Icon search, ranking, fetchingicons

Key files

PathWhy it matters
packages/diagnostics/src/codes.tsThe stable code registry. Public contract
packages/layout/src/classify.tsThe six archetypes and their precedence
packages/site/src/planner.tsSITE_DEVICE_THRESHOLD = 150, SITE_ZONE_THRESHOLD = 5
packages/site/src/styles.tsThe complete diagram stylesheet and design system
packages/site/src/brand.tsBranding types and logo sanitizing
packages/schema/src/zodSchema.tsThe validation shape
packages/cli/src/index.tsUsage strings and exit codes

Cross-cutting gotchas

Port chips carry data-edge and data-node. Runtime element maps must use class-qualified selectors — .m-node[data-node], .m-edge[data-edge] — or they match chips as well as the things they're meant to match.

Text measurement is static. @marrow/metrics measures at the exact pixel size and letter-spacing the renderers use. Don't reach for a DOM measurement; it breaks determinism across machines.

Sorting. Anywhere order would otherwise be arbitrary, sort by id. Determinism is a hard requirement, not a nice-to-have.