{
  "schema": "leumas.docs.page/1",
  "id": "pkg:@leumas/architecture-graph",
  "slug": "packages/architecture-graph",
  "kind": "capabilities",
  "bucket": "package",
  "title": "@leumas/architecture-graph",
  "name": "@leumas/architecture-graph",
  "eyebrow": null,
  "chip": null,
  "summary": "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...",
  "keywords": [
    "architecture-graph",
    "serializer",
    "node-only",
    "graphs",
    "control-plane",
    "leumas architecture graph",
    "how to use architecture graph",
    "architecture"
  ],
  "audience": "both",
  "funnel": {
    "product": null,
    "cta": null
  },
  "body": "# @leumas/architecture-graph\n\nThe document format behind the architecture graphs in `ops/architecture/*.graph.json`. Node-only, no\ndependencies, no React — three very different callers read it and one of them is a guard running in\nbare Node.\n\n| Import | What it is |\n|---|---|\n| `@leumas/architecture-graph` | `serializeGraph` · `isCanonical` · `revOf` |\n| `@leumas/architecture-graph/kinds` | `UNIT_KINDS` · `unitsOfKind` · `allUnits` · `readManifestDomains` · `resolveRef` |\n\n## Why a canonical serializer is the whole point\n\nThe architecture graphs are edited two ways: in the Leumas Admin editor, and by a coding agent with a\ntext editor. The agent's loop is what makes the second one cheap:\n\n```bash\ngrep -n '\"id\":\"studio\"' ops/architecture/ecosystem.graph.json   # -> one line number\n```\n\n…then read that line and edit it. A couple of hundred tokens, whatever the graph's size.\n\nThat works **only** while every node and every edge is exactly one line. The first save that writes\n`JSON.stringify(doc, null, 2)` reflows a 60-line file to 350, and in that one commit every line anchor\nis wrong, every one-node change becomes a whole-file diff, and `grep` stops answering the question.\nThe JSON is still valid and the app still renders, so nothing else in the repo can see it happen.\n\nSo `serializeGraph()` is the **only** writer — in the control-plane PUT handler, in\n`ops/architecture/build.mjs`, and in `check-architecture --fix` — and `pnpm check:architecture` hard-fails\na committed file that is not byte-identical to what it would produce. Canonical form is an invariant,\nnot a convention.\n\nThe shape: header keys pretty-printed (a person hand-edits `lanes`, `kinds` and `classes`), lookup\ntables one entry per line, and `regions` / `labels` / `panels` / `nodes` / `edges` compact with a fixed\nkey order. Fixed order matters as much as one-line-ness — without it two writers that disagree about\nkey order produce a diff on every record of a file where nothing changed.\n\n## `revOf` — why every write carries one\n\nTwo writers means a page left open while an agent works would, on the next node drag, write back the\ndocument it loaded an hour ago and silently revert everything in between. `revOf` hashes the bytes on\ndisk; the store returns it with the document and refuses a `PUT` carrying a stale one with a 409.\n\n## `kinds` — binding a drawing to the tree\n\n`UNIT_KINDS` says, per family of thing Leumas contains, where the real ones live and how to recognise\none. That is what lets the guard answer *is this still true?* — a node's `refs` that no longer resolve\nis a hard failure, because a map asserting paths that moved is the failure binding exists to prevent.\n\n[critical] **`readManifestDomains` reads a generated index, not the manifests.** Grepping the three nav\nmanifests for `id:` finds the control plane's domains and **none** of Studio's, because Studio does not\nwrite its ids literally — `DOMAINS` is built from `{ ...app('graphs'), pillar: 'ai' }`, so the id is a\nfunction argument. A reader that silently misses most of the tree is worse than no reader, since every\ncoverage number it feeds looks plausible. The source is therefore\n`.claude/skills/leumas-capabilities/reference/domains.md`, which `pnpm skills:sync` builds by\n*importing* those manifests and which `pnpm check:skills` fails the build over when it drifts.\n\n## Guards\n\n`pnpm check:architecture` — canonical form, ref resolution, the `hot` ownership tree, region coverage.\n`pnpm check:architecture --self-test` also asserts that the tidy in `@leumas/ui/graph/tidy` is\nidempotent, which is what makes a tidied drawing committable.\n",
  "source": {
    "path": "shared/packages/architecture-graph/README.md",
    "blobSha": "",
    "commit": "",
    "committedAt": "",
    "provenance": "no-git",
    "bytes": 3833,
    "hash": "0c0757996284a35f9016e7c813b3b84b23d8a5ec"
  },
  "urls": {
    "html": "/p/packages/architecture-graph",
    "json": "/docs/packages/architecture-graph.json",
    "md": "/docs/packages/architecture-graph.md"
  },
  "links": {
    "composes": [],
    "usedBy": [
      "pkg:@leumas/control"
    ],
    "product": [],
    "howTo": [],
    "skills": []
  },
  "exports": {
    "total": 11,
    "component": 1,
    "hook": 0,
    "helper": 10,
    "names": [
      {
        "n": "UNIT_KINDS",
        "k": "component"
      },
      {
        "n": "allUnits",
        "k": "helper"
      },
      {
        "n": "familyOf",
        "k": "helper"
      },
      {
        "n": "inferNode",
        "k": "helper"
      },
      {
        "n": "isCanonical",
        "k": "helper"
      },
      {
        "n": "normalizeGraph",
        "k": "helper"
      },
      {
        "n": "readManifestDomains",
        "k": "helper"
      },
      {
        "n": "resolveRef",
        "k": "helper"
      },
      {
        "n": "revOf",
        "k": "helper"
      },
      {
        "n": "serializeGraph",
        "k": "helper"
      },
      {
        "n": "unitsOfKind",
        "k": "helper"
      }
    ]
  }
}
