Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

OtelContext is a self-hosted OTLP observability platform. Single Go binary with embedded React frontend.
- **Backend:** Go 1.25, native `net/http` (no frameworks), GORM ORM, gRPC + HTTP for OTLP ingestion
- **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)
- **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)
- **Ports:** gRPC `:4317` (OTLP), HTTP `:8080` (API + HTTP OTLP + WebSocket + UI)

## Strict Rules
Expand Down Expand Up @@ -114,6 +114,8 @@ LogsServer.Export() → DB persist → logCallback → GraphRAG.OnLogIngested(
MetricsServer.Export() → TSDB → metricCallback → GraphRAG.OnMetricIngested()
```

**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.

## MCP Server — 7-Tool Triage Surface

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