# @leumas/architecture-graph

The document format behind the Leumas architecture graphs. One canonical serializer that keeps every node and edge on its own line so an agent can grep-and-edit a single record, a content hash for...


The document format behind the architecture graphs in `ops/architecture/*.graph.json`. Node-only, no
dependencies, no React — three very different callers read it and one of them is a guard running in
bare Node.

| Import | What it is |
|---|---|
| `@leumas/architecture-graph` | `serializeGraph` · `isCanonical` · `revOf` |
| `@leumas/architecture-graph/kinds` | `UNIT_KINDS` · `unitsOfKind` · `allUnits` · `readManifestDomains` · `resolveRef` |

## Why a canonical serializer is the whole point

The architecture graphs are edited two ways: in the Leumas Admin editor, and by a coding agent with a
text editor. The agent's loop is what makes the second one cheap:

```bash
grep -n '"id":"studio"' ops/architecture/ecosystem.graph.json   # -> one line number
```

…then read that line and edit it. A couple of hundred tokens, whatever the graph's size.

That works **only** while every node and every edge is exactly one line. The first save that writes
`JSON.stringify(doc, null, 2)` reflows a 60-line file to 350, and in that one commit every line anchor
is wrong, every one-node change becomes a whole-file diff, and `grep` stops answering the question.
The JSON is still valid and the app still renders, so nothing else in the repo can see it happen.

So `serializeGraph()` is the **only** writer — in the control-plane PUT handler, in
`ops/architecture/build.mjs`, and in `check-architecture --fix` — and `pnpm check:architecture` hard-fails
a committed file that is not byte-identical to what it would produce. Canonical form is an invariant,
not a convention.

The shape: header keys pretty-printed (a person hand-edits `lanes`, `kinds` and `classes`), lookup
tables one entry per line, and `regions` / `labels` / `panels` / `nodes` / `edges` compact with a fixed
key order. Fixed order matters as much as one-line-ness — without it two writers that disagree about
key order produce a diff on every record of a file where nothing changed.

## `revOf` — why every write carries one

Two writers means a page left open while an agent works would, on the next node drag, write back the
document it loaded an hour ago and silently revert everything in between. `revOf` hashes the bytes on
disk; the store returns it with the document and refuses a `PUT` carrying a stale one with a 409.

## `kinds` — binding a drawing to the tree

`UNIT_KINDS` says, per family of thing Leumas contains, where the real ones live and how to recognise
one. That is what lets the guard answer *is this still true?* — a node's `refs` that no longer resolve
is a hard failure, because a map asserting paths that moved is the failure binding exists to prevent.

[critical] **`readManifestDomains` reads a generated index, not the manifests.** Grepping the three nav
manifests for `id:` finds the control plane's domains and **none** of Studio's, because Studio does not
write its ids literally — `DOMAINS` is built from `{ ...app('graphs'), pillar: 'ai' }`, so the id is a
function argument. A reader that silently misses most of the tree is worse than no reader, since every
coverage number it feeds looks plausible. The source is therefore
`.claude/skills/leumas-capabilities/reference/domains.md`, which `pnpm skills:sync` builds by
*importing* those manifests and which `pnpm check:skills` fails the build over when it drifts.

## Guards

`pnpm check:architecture` — canonical form, ref resolution, the `hot` ownership tree, region coverage.
`pnpm check:architecture --self-test` also asserts that the tidy in `@leumas/ui/graph/tidy` is
idempotent, which is what makes a tidied drawing committable.


---
Source: shared/packages/architecture-graph/README.md
Canonical: https://docs.leumas.tech/p/packages/architecture-graph
