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.
Supporting trees at the repo root:
Where to make a change
| You want to change | Package | Notes |
|---|---|---|
| A new YAML key, or validation behavior | schema | Also add the code to diagnostics and document it in the reference |
| A diagnostic message, hint, or suggestion | diagnostics | Codes are permanent — add, never renumber |
| Which archetype a topology gets | layout/classify.ts | Precedence order is documented and load-bearing |
| Node placement | layout/elk.ts, compose.ts, presets.ts | |
| Edge routing or the tidy pass | layout/router.ts, tidy.ts | |
| Port chip placement | layout/portchips.ts | L1 rule port-chip-overlap guards it |
| What fails a build | lint | Moving a rule between L1 and L3 is a breaking change in practice |
| How something looks | render-svg and runtime/client/canvas-engine.ts and site/styles.ts | All three, always |
| Interaction, chrome, keyboard | runtime/client/main.ts + site/shell.ts + site/styles.ts | Vanilla only — no React |
| Page splitting, what earns a page | site/planner.ts | Thresholds are exported constants |
| Dev server or its API | server | |
| A command or flag | cli | Update the CLI reference too |
| Icon search, ranking, fetching | icons |
Key files
| Path | Why it matters |
|---|---|
packages/diagnostics/src/codes.ts | The stable code registry. Public contract |
packages/layout/src/classify.ts | The six archetypes and their precedence |
packages/site/src/planner.ts | SITE_DEVICE_THRESHOLD = 150, SITE_ZONE_THRESHOLD = 5 |
packages/site/src/styles.ts | The complete diagram stylesheet and design system |
packages/site/src/brand.ts | Branding types and logo sanitizing |
packages/schema/src/zodSchema.ts | The validation shape |
packages/cli/src/index.ts | Usage 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.