Skip to content

Commit abc2ae7

Browse files
aksOpsclaude
andauthored
docs(claude): service map is the sunflower layout + pre-sample topology observer (#118)
Update the project instructions to match the code after #117: - The service map uses the deterministic phyllotaxis ("sunflower") layout (radialLayout.ts), not the (now-removed) dagre layered layout; edges are border-pinned chords with direction arrowheads; selection accents the active path (nodes + edges). @dagrejs/dagre is removed. - Document the pre-sample topology observer (topology_observer.go) that records cross-service CALLS edges independent of SAMPLING_RATE, why it exists (joint-survival starvation at low sampling), and its existence-only + bounded design so sampled aggregates and OOM bounds are unaffected. Claude-Session: https://claude.ai/code/session_01LDQVJrixs2nJoea67a8pEG Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f1030b7 commit abc2ae7

1 file changed

Lines changed: 3 additions & 1 deletion

File tree

CLAUDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
OtelContext is a self-hosted OTLP observability platform. Single Go binary with embedded React frontend.
66
- **Backend:** Go 1.25, native `net/http` (no frameworks), GORM ORM, gRPC + HTTP for OTLP ingestion
7-
- **Frontend:** React 19 + TypeScript + TanStack Query/Virtual + wouter + Radix primitives + cmdk palette + hand-rolled token CSS (`ui/src/styles/tokens.css`) with CSS Modules. The **service map renders via React Flow (`@xyflow/react`, MIT)** over a dagre layered layout (`ui/src/lib/dagreLayout.ts``@dagrejs/dagre`, MIT: crossing-minimised, left→right, with edge-routing waypoints) computed synchronously. React Flow owns pan/zoom/fit/minimap/grid/a11y; nodes are token-themed React components — draggable, and they collapse to status **dots** below a zoom threshold (level-of-detail) so they stop occluding edges at scale; edges route through the dagre waypoints and meet node borders. No UI framework: `@ossrandom/design-system`, cytoscape, uplot remain removed (cytoscape's physics hairball → deterministic layered layout + React Flow; the prior hand-rolled-SVG map was replaced by React Flow on 2026-06-18, then moved to the dagre layered layout)
7+
- **Frontend:** React 19 + TypeScript + TanStack Query/Virtual + wouter + Radix primitives + cmdk palette + hand-rolled token CSS (`ui/src/styles/tokens.css`) with CSS Modules. The **service map renders via React Flow (`@xyflow/react`, MIT)** over a deterministic phyllotaxis ("sunflower") layout (`ui/src/lib/radialLayout.ts` — most-critical service near the disc centre, golden-angle spiral) computed synchronously. Unlike a layered DAG, the sunflower fills a disc evenly and never collapses sparse/disconnected graphs into a single column, so the map stays navigable from 7 to 120+ services. React Flow owns pan/zoom/fit/minimap/grid/a11y; nodes are token-themed React components — draggable, and they collapse to status **dots** below a zoom threshold (level-of-detail) so they stop occluding edges at scale. Edges are straight chords pinned to node borders (centre-to-centre at dot zoom) carrying a direction **arrowhead** (`markerEnd`) so flow is legible statically — at any zoom and under `prefers-reduced-motion`; selecting a node accents the active path (both nodes **and** edges) and dims the rest. No UI framework: `@ossrandom/design-system`, cytoscape, uplot, **and `@dagrejs/dagre`** remain removed (cytoscape's physics hairball → React Flow; the prior hand-rolled-SVG map was replaced by React Flow on 2026-06-18; the dagre layered layout it briefly used was itself replaced by the sunflower layout on 2026-06-18 because a layered DAG stacked sparse 120-service graphs into an unreadable single column)
88
- **Ports:** gRPC `:4317` (OTLP), HTTP `:8080` (API + HTTP OTLP + WebSocket + UI)
99

1010
## Strict Rules
@@ -114,6 +114,8 @@ LogsServer.Export() → DB persist → logCallback → GraphRAG.OnLogIngested(
114114
MetricsServer.Export() → TSDB → metricCallback → GraphRAG.OnMetricIngested()
115115
```
116116

117+
**Pre-sample topology observer** (`internal/graphrag/topology_observer.go`): `TraceServer.Export()` also calls `GraphRAG.ObserveSpanTopology()` for **every** received span *before* the sampler's keep/drop decision, so cross-service CALLS edges are recorded independent of `SAMPLING_RATE`. Without it, an edge formed only when both the caller and callee spans survived sampling (~`rate²` joint probability), so at low sampling the service map showed nodes but almost no edges/flow. The observer is existence-only (`ServiceStore.EnsureService`/`EnsureCallEdge` create with zeroed aggregates, no-op if present) so the sampled path stays the **sole** source of CallCount/latency/error-rate; it's bounded by a per-tenant spanID→service LRU (cap 100k) + per-pair dedup (memory ≈ #service-pairs) and never touches TraceStore/eventCh, so the SQLite OOM-survival bounds are unaffected.
118+
117119
## MCP Server — 7-Tool Triage Surface
118120

119121
The MCP server (`internal/mcp/`) exposes a focused 7-tool triage surface via

0 commit comments

Comments
 (0)