# Design System Unification — one visual language, enforced end-to-end
Establishes **one source of truth for every visual decision**, wires the
whole app to it, and **locks it in with CI** so it can't drift back.
### Foundations
- **Design-token system ("one knob per decision").** Every colour, text
size, spacing, corner radius, and shadow is a named token defined in
exactly one place — change the token, every screen updates. Collapsed
hundreds of near-duplicate definitions and removed "fake" duplicates
that pretended to be different but weren't.
- **Shared component library (O2).** Buttons, tables, dialogs, drawers,
form fields, badges, etc. already know the correct colours, spacing, and
dark-mode behaviour; new screens compose them instead of hand-styling.
Added new primitives on this branch (`OContent`, `KpiCard`) and
standardized existing ones.
- **One page structure everywhere** (`PageLayout` / `AppPageHeader` /
`AppPage` / `OContent`) — titles, breadcrumbs, spacing, and content
edges line up identically across every screen.
- **One table implementation** — all tabular data uses the same table
(sorting, density, sticky headers, empty states).
- **Styling consolidated onto Tailwind; old systems removed** —
hand-written CSS driven to the practical minimum, the legacy **Quasar**
framework fully removed, surviving stylesheets required to justify
themselves.
- **Two-tier corner radius** — a tight **4px** for controls and a soft
**12px** for surfaces (dialogs, cards, the app shell), plus a
full-circle option — replacing five radius names that all secretly meant
the same thing.
### Detailed improvements across screens
**Page structure & alignment** — unified page shell rolled across ~40+
screens (IAM, Settings, Alerts, Dashboards, Functions, Pipelines, RUM,
Metrics, Synthetics, Billing, editors, lists); one header height; one
page-edge grid driving headers, tables, rails, and tabs; unified
side-rail widths; inset rail selection/hover pills; every tab strip on
the grid line; tightened title/subtitle spacing.
**Tables (cross-screen)** — action columns consistently centered (incl.
Organizations); unified row-number column with no "#" truncation;
non-standard tables restyled to match; loading skeletons made
geometrically identical to the loaded table; symmetric edge inset; IAM
first-column grid alignment fixed.
**Traces** — flame graph + span-detail correlation panel fixes;
correlated logs/metrics placeholder fit; sticky headers; removed a
double scrollbar + a 1px sliver; aligned table fonts/badges; polished
the service detail drawer; stream-selector + correlation alignment;
aligned loading spinners; floating overlay arrows for the
disabled-services strip.
**Logs** — field-list controls aligned (focus ring no longer clipped);
detail-drawer spacing + metrics-tab overflow fixed; Run Query toolbar
tidied; listing sizing + stray border fixed.
**Metrics** — search added to the "Type" facet rail and aligned with the
other rails; proper empty states for the explorer and facet rail.
**Dashboards** — removed inconsistent card shadows; tightened
borders/resizers/field-list spacing; aligned View Dashboard chrome to
the grid; chart colours via the token helper.
**Home** — redesigned the Usage tab; added reusable KPI stat cards so
summary strips stop drifting.
**Dark mode** — new theme-switch animation; fixed a 1px content shift on
toggle and a white flash on load; fixed AI-chat text and assorted
light/dark mismatches; completed the token-driven dark migration.
**Empty states, controls & small polish** — restored 3 broken
empty-state animations; adopted the standard empty state; added the
sliding indicator to the toggle control; fixed the Quality-page inset +
drawer badge; refreshed Search History / Schedulers; fixed the
alert-history drawer scrollbar + timeline hover; removed a stray
menu-bar border.
### Dead code & cruft removal
- **Deleted the entire legacy `utilities.css`** global stylesheet (well
over a thousand lines) — evacuated in waves, roughly half of it dead
once traced.
- **Removed Quasar entirely**, including dead `.q-*` styles and ~25
phantom `--q-*` variables that were silently breaking bits of the UI.
- Stripped hundreds of **dead CSS classes and tokens that compiled to
nothing** — e.g. 46 uses of a colour class with no backing token, a
no-op shadow class on 51 elements, dead layout/utility classes, a
leftover `card-container` block, a dead table-actions token, and a
mis-registered gradient that rendered nothing.
- Cut **unused keyframe animations (19 → 3)** and removed **unused
components** + a stray build artifact.
- Net effect: a much smaller, leaner styling surface — less to maintain,
fewer dead ends to copy.
## Safety & verification
Large but low-risk: this is overwhelmingly a **styling and structure**
change, not product behaviour or data. Guarded by the
design-token/consistency/type checks above plus the existing test suite,
and reviewed screen-by-screen.
---------
Co-authored-by: Kirtan Thakkar <kirtan@kiara.tech>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: ktx-mihir <mihir.patel@kiara.tech>
## What
Adds an **Agent Graph** to AI Observability that renders AI agents,
tools, and models as first-class nodes in the service graph — unified
with the real downstream services an agent talks to. It answers "what
does my agent actually interact with?" (tools, models, services,
datastores) instead of showing GenAI spans in isolation.
## Why
LLM Insights shows plenty of per-call data but no picture of the flow —
where a request starts, where the agent sits, and everything it fans out
to. This surfaces the full agent topology, works across every framework
OpenObserve documents, and correctly attributes tools/models to the
owning agent even when the instrumentation nests them several spans
deep.
## Backend (`src/core`)
- **Agent/tool/model edge families in the service-graph processor**
(`traces/service_graph/processor.rs`): derives `service → agent`, `agent
→ model`, `agent → tool` edges from the canonical `gen_ai_*` columns,
keyed on column presence (open operation-name vocabulary), schema-gated
so streams missing a column don't error.
- **Multi-level agent inheritance.** The owning agent name usually lives
on an *ancestor* span (real Google ADK nests `generate_content(agent) →
chat → execute_tool`), not on the tool/LLM span. The `from` climbs the
parent chain up to `AGENT_INHERIT_DEPTH` (4) levels —
`COALESCE(child.agent, p1…pN, service_name)` + chained ancestor `LEFT
JOIN`s — taking the nearest ancestor with an agent name. Extracted
`build_agent_or_service` / `build_ancestor_joins` as pure, unit-tested
helpers. (Recursive CTE was tried first but panics in the search engine;
a bounded chained join is supported and shallow-tree-cheap.)
- **Extractor coverage** for the framework conventions OpenObserve
documents (gen_ai semconv, OpenInference, traceloop, CrewAI, Google ADK,
LangChain, Langfuse): `is_llm_trace` gate now admits
OpenInference/framework agent+tool spans; `extract_tool_name` reads
`tool.name` / `langchain.tool.name` / `crewai.task.tools` / Langfuse
metadata; new attribute constants. All normalize into the canonical
`gen_ai.*` columns at ingest, so the query layer reads one column set
regardless of vendor.
## Frontend (`web/src`)
- **Standalone Agent Graph page** under AI Observability
(`AgentGraphPage.vue`) with Stream/Agent selectors and a DateTime
picker, matching the LLM Insights UX; route + rail item + i18n.
- **Graph rendering** (`convertTraceData.ts`, `ServiceGraph.vue`):
agent/tool/model node kinds, vendor brand logos on model nodes (reusing
the AI-integrations catalog logos), an animated "radar-ping" halo baked
into the agent symbol SVG so it stays centered and pans/zooms with the
node, and a layered-layout depth fix so `service < agent < model/tool`
renders correctly. Agent highlighting is gated to the Agent Graph page
(the Service Graph tab is unchanged).
- **Side-panel caller consistency** (`ServiceGraphNodeSidePanel.vue`):
the Operations table now resolves a tool/model node's caller with the
*same* ancestor-agent climb the graph edge uses, so the panel and graph
agree (previously the panel showed the host app as the caller).
Refactored the identity-field builder to emit aliased columns directly
(no fragile regex post-processing).
- **Design tokens** (`theme.ts`): added `cssToken(name, fallback)` to
resolve `--color-*` tokens at runtime for chart/SVG contexts (where
`var(--x)` can't be used), and routed the agent-accent + exact-match
health colors through it instead of hardcoded hex.
## Consistency
Service classification is mirrored TS ⇄ Rust; the shared
`classification_cases.json` vector (both copies kept identical) drives
unit tests in both languages.
## Testing
- Rust: multi-level inheritance + edge-attribution unit tests; `cargo
build` clean.
- Frontend: new specs for the graph conversion, layout,
model-vendor-logo, side-panel caller climb, and `cssToken`; full traces
suite green.
- Verified end-to-end on real production traces (SRE agent) and on
synthetic fixtures for CrewAI / OpenAI-Agents / Google ADK /
genai-semconv / LangChain — every framework attributes agent →
model/tool correctly.
## Notes
- Topology is read from the pre-aggregated `_o2_service_graph` stream
(the hourly incremental job), so it scales to large trace volumes — no
per-load raw-trace scan.
- The enterprise `classify_entity` / `build_topology` changes live in
the o2-enterprise repo and are required for the enterprise build.