The mesh store
The committed graph is the one thing everything in GoToMesh orbits: graph/nodes.jsonl (one
node per page that could exist) plus graph/edges.tsv (the typed relationships between them). It is
the state file — the single source of truth.
What it is
graph/nodes.jsonl is a committed, canonically-serialized list of LPG nodes — one per page
identity, each carrying a label set (page + its page type, plus any axis label a hub owns) and
minimal props: identity (id, slug, anchor), the classification-determined spec (section,
intent), the demand assignment, folded entity facts, and carried build state
(status, the built_* hashes). graph/edges.tsv carries the typed edges — MEMBER_OF,
HUB_OF, PILLAR_OF, HAS_<axis>, ALIAS_OF — every node and edge stamped with provenance.
It holds catalogued pages (recorded, no body yet) and built pages alike.
The graph is desired state expressed as a committed artifact. Because it’s canonically
serialized (id-sorted nodes, tuple-sorted edges, key-sorted props), every real change shows up as a
clean, reviewable Git diff — a rewrite whose values didn’t change produces zero diff — and Git
becomes the audit log. The derived site.manifest.json is the
denormalized projection your SSG reads (resolved links, cards, redirect table, render sets).
Why it matters
Treating the graph as the state file is what makes the whole engine trustworthy:
- The diff is graph-to-graph. GoToMesh compares the hashes stored in the graph against freshly
computed desired state — it never scans your rendered pages to figure out what changed. That is
what keeps
planfast and exact. - Re-running
applyis always safe — it only acts where a node actually differs. - Nothing drifts, because there is no second copy of the truth. Mesh links, roles, redirects, and the rest are derived from the graph, not stored on the page. A relink is a re-projection of the manifest, not a page rewrite.
Never hand-edit the committed graph. It is computed. apply, seal, and the lifecycle commands
(promote/demote/publish/…) are its only writers. To change a node, change the input — a
keyword pull, a config rule, a reference table — and re-plan.
How it works
You change an input and run gtmesh apply. The engine recomputes desired state, diffs it against the
committed graph by identity, and emits one action per node (catalogue, create, recompute,
rewrite, redirect, or noop). The actions are what update the nodes. seal later stamps the
built_* provenance hashes that make the next diff precise. A projection-only change — a moved
link, an added sibling — never rewrites a page: it re-derives the manifest, a zero-rewrite change you
review in the manifest diff.
A node’s identity is carried, not recomputed: it’s minted once (the id is the frozen first slug)
and the resolver re-attaches an incoming demand pull to the same node across re-clustering. Two pulls
that resolve to the same id are the same page, which is how the diff knows what to compare against.
Key files & commands
| Thing | What it is |
|---|---|
graph/nodes.jsonl | The committed state — one LPG node per page identity |
graph/edges.tsv | The typed mesh edges (MEMBER_OF, HUB_OF, HAS_<axis>, ALIAS_OF, …) |
site.manifest.json | The derived projection the SSG reads (resolved links, cards, redirects) |
gtmesh plan | Read-only: recompute desired state, print the diff |
gtmesh apply | The writer: enact the diff into the graph |
gtmesh graph get <id> | Read one node (labels + props + folded facts) as JSON |
Related
plan— compute the graph-to-graph diffapply— the only writer of the graph- The mental models — the graph-as-state idea in full
- Glossary — the committed graph, identity, desired state, built hashes