Graph Core Refactors & Architecture Doctrine
Doctrine: Graph as exposure layer, not universal store
The graph is an exposure/adaptor layer — it does not own the objects it exposes. Domain data lives wherever it naturally belongs: entity data in entity stores (entities/string/, entities/code/, etc.), activity-owned world surface state in activity sessions, LLM conversations in the LLM store (llm/), filesystem nodes on the real filesystem, kernel instances in the kernel system. The graph creates lightweight adapter nodes that project these domain objects into a uniform navigable topology.
Graph core persists only user-materialized topology — which visible node keys exist and which visible edge connections exist (graph:capture-state / graph:restore-state). Owning systems persist the actual object data. There is no "full graph state backup" that captures domain data; capture-state stores node keys and edge source/target keys only.
Related objects become graph-visible only through explicit preview, view, search-row, or action controls. A node may offer actions such as Show Code, Show Details, Start, or a searchable picker that loads a selected key into the current graph map and adds any display edge needed for that user-visible relationship. Graph code should not rely on hidden relationship expansion hooks to bulk-materialize neighboring records.
Canonical terminology
| Use | Avoid |
|---|---|
| graph-exposed object, graph-visible object | graph-backed object, graph object |
| graph node adapter, projection | first-class graph object |
| entity-store-backed node, world-state-backed node, LLM-store-backed node | backed by the graph |
| graph topology state (node keys + edge keys) | full graph state, graph data, graph node data |
| exposed through the graph, visible in the graph | lives in the graph, stored in the graph |
| graph as universal interface / exposure layer | graph as universal model |
| domain object | graph-native object |
Key architectural facts
graph/core.fnl: nodes are lightweight records (key, label, color, view ref, graph ref).capture-statestores node keys and edge source/target keys only — no domain data.graph/key-loaders.fnl: each loader adapts its owning store/system into a graph node on demand viaload-by-key. Entity loaders adapt entity stores; LLM loaders adapt the LLM store; world activity and surface loaders adaptworld-managerandWorldData.graph/map.fnl: graph maps hold the visible topology a user has materialized in that interaction context. Preview/search/action code loads keys through the activeGraphMapand inserts explicit display edges when the user asks to reveal related records.graph/world-data.fnl: activity-owned scene/HUD/canvas state is resolved fromworld.state.activity.sessions.<activity-id>throughWorldDatahelpers. Activity-owned graph keys include bothworld-idandactivity-id(for exampleactivity-scene:<world-id>:<activity-id>,activity-background:<world-id>:<activity-id>, andactivity-terrain:<world-id>:<activity-id>:<terrain-id>). Updates mutate the owning activity surface state, then sync to the active surface and persist world. Graph nodes are projections, not the source of truth.- Activity hierarchy keys expose
world:<world-id>→world-activities:<world-id>→world-activity:<world-id>:<activity-id>→activity-surfaces:<world-id>:<activity-id>before reaching concrete surface nodes such as scene, HUD, or canvas. graph/nodes/*.fnl: node constructors receive stores/world-manager, resolve domain records from them, and emit signals when underlying data changes.graph/view/: owns visual/interactive systems (ForceLayout, points, labels, selection, movables, persistence metadata). Graph nodes do not track view instances.
Extracted
- Graph model:
graph/coreowns nodes/edges, add/remove/replace, and emits signals (node-added,node-removed,node-replaced,edge-added,edge-removed). - Graph view:
graph/view(GraphView) owns ForceLayout, points, labels, selection, movables, persistence, and node dialogs. It listens to graph signals and can be dropped/recreated without touching the graph model. - Persistence:
graph/view/persistencehandles metadata load/save and scheduled writes. - Labels and LOD:
graph/view/labels(GraphViewLabels) manages label creation, LOD switching, camera-distance debounce, positioning, and cleanup. - Selection:
graph/view/selection(GraphViewSelection) owns selector wiring, selected-node bookkeeping, pruning, and signals. - Node view wiring:
graph/view/node-views(GraphViewNodeViews) builds/drops node dialogs or HUD embeds. - Movables:
graph/view/movables(GraphViewMovables) builds drag targets, registers/unregisters withapp.movables, forwards position updates, and schedules persistence on drag end. - Layout and edges:
graph/view/layout(GraphViewLayout) wrapsForceLayout, line updates, node positioning, and rebuilds. - View registry:
graph/view/registry(GraphViewRegistry) centralizes view-only bookkeeping (indices, node/point maps, edge map), replacement, and deduping. - Utils split:
graph/core/utilsholds core-only helpers (glm vec coercion).graph/view/utilsadds view helpers (label text truncation/wrapping) and re-exports the core glm helpers.
Label and LOD handling
- Core now forwards node→point maps to
graph/view/labelsto build spans and reposition them based on camera distance; tune LOD thresholds and label styling there instead of in core. - Benefits: keeps HUD text concerns and camera logic out of core; makes it easier to tune LOD thresholds separately.
Selection and node views
- Selection is owned by
graph/view+graph/view/selection; update behavior there when changing selector policies or selection logging. - Node dialogs are handled by
graph/view/node-views; tweak dialog builders or HUD embedding there without touching the graph model. - Graph nodes do not track view instances. Nodes expose a plain
:viewconstructor (e.g.:view HackerNewsStoryView) and emit state via signals (rows-changed,items-changed,targets-changed, etc.). Views subscribe to those signals (or call node methods likefetch,open-entry,add-target) and stay UI-only so multiple views can hang off the same node.
What stays in core
- Graph model: adding/removing/replacing visible nodes/edges and lifecycle (
drop). - No visual state: layout, labels, selection, movables, points, or persistence belong in
graph/view.
