Orbital Graph Data Model
meridian intermediate 7 min read
ELI5
The Observatory paints the protocol as a solar system: each council is a star, each agent a planet around it, each capability a moon every planet shares, doctrines are asteroids on weak orbits, and units are debris pulled toward their domain’s star. Bigger things have more connections.
Technical Deep Dive
src/lib/orbital.ts builds a single force-directed dataset by merging councils, agents, capabilities, doctrines, and units.
Class Diagram
classDiagram class OrbitalNode { +string id +string label +CelestialType celestialType +EntityType entityType +number radius +string color +string glowColor +string? orbitParent +string? domain +string? council +boolean? planned +string? unitType +string? status +string? url +Record meta } class OrbitalEdge { +string source +string target +EdgeRelationship relationship +number strength +number? forceStrength } class OrbitalData { +OrbitalNode[] nodes +OrbitalEdge[] edges +Stats stats } OrbitalData "1" *-- "*" OrbitalNode OrbitalData "1" *-- "*" OrbitalEdge OrbitalNode <.. OrbitalEdge : source OrbitalNode <.. OrbitalEdge : targetType Mapping
| Source | celestialType | entityType | id prefix |
|---|---|---|---|
| Council (active or planned) | star | council | council: |
| Agent | planet | agent | agent: |
Capability (from CAPABILITIES constant) | moon | capability | cap: |
| Doctrine | asteroid | doctrine | doctrine: |
| Unit | debris | unit | unit: |
Edge Relationships
| Relationship | From → To | strength | forceStrength override |
|---|---|---|---|
roster | council → agent | 0.9 | — |
has | agent → capability | 0.5 | 0 (visual-only) |
enhances | doctrine → capability | 0.4 | — |
imports | unit → unit | 0.6 | — |
belongs_to | unit → council | 0.3 | 0 (visual-only) |
forceStrength: 0 is the d3-force escape hatch: the edge renders for hover/click but exerts no physics pull, so capabilities don’t get yanked toward every agent that uses them and units don’t get sucked into their council star.
Radius Formula
radius = BASE_RADII[celestialType] + log2(1 + edgeCount) * 2.5BASE_RADII = { star: 22, planet: 12, moon: 9, asteroid: 7, debris: 4 }. Edge count is computed during the build pass via countEdge. Visual-only edges (forceStrength: 0) still increment the count, so a high-traffic capability still grows.
Capability Filter
When walking agent.capabilities, any value not in the canonical CAPABILITIES constant is dropped (e.g. free-text strings like "search" or "summarisation"). This is enforced silently — agents may declare capabilities that are aspirational but not yet first-class moons.
Key Terms
- Celestial type → Visual category (star/planet/moon/asteroid/debris); drives base radius and palette.
- Entity type → Logical category (council/agent/capability/doctrine/unit); drives URL routing and meta payload.
forceStrength→ Override for d3-force link strength independent ofstrength(which governs visual weight).orbitParent→ Hint to the renderer that a node should orbit a specific star/planet.
Q&A
Q: Why is the unit→council edge visual-only?
A: Units inherit their council via domain → council lookup, and pulling every debris node toward a star would collapse the layout. The edge exists so the UI can highlight membership on hover without distorting the layout.
Q: What happens to a unit whose domain has no matching council?
A: councilId is undefined, no belongs_to edge is emitted, and orbitParent is left unset — the debris floats free in the layout.
Q: Why is the radius formula logarithmic in edge count?
A: A handful of high-degree nodes (popular capabilities, frequently imported units) would otherwise dominate the canvas. log2(1 + n) keeps growth visible but bounded.
Examples
A new council crucible (planned) is added with one agent and three protected capabilities. After build:
- 1 star (
council:crucible) withplanned=trueand the burnt-sienna palette entry. - 1 planet under it.
- The three capabilities already exist as moons (added once globally, not per agent).
- Edges: 1 roster + 3 has (visual-only). Star radius increases by
log2(1+1)*2.5 = 2.5from one outgoing roster edge.
neighbors on the map
- Graph Topology Snapshot authoring a new graph.json
- LORE+CAIRNET Shared Graph Primitive understanding how LORE and CAIRNET share the graph component
- CLI Command Surface wiring STRATT into a CI pipeline