Files
go-otel/AGENTS.md
T
matsandClaude Sonnet 5 1e9482ed21
Build and Publish / Metadata (push) Successful in 2s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 56s
Unify otel-ui on ds-* theme; restore redesigned Issues view
Restore IssuesVisualizer redesign reverted by merge 67ce419 and re-apply
Needle triage/test additions. Migrate all remaining screens off the MD3
palette, raw Tailwind colors, gradients and Material Symbols. Remove the
legacy theme block and fonts, drop the duplicate tab strip, rebuild
TelemetryFilters on FilterBar, and document status in AGENTS.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 08:56:34 +00:00

167 lines
9.3 KiB
Markdown

# Agent instructions
Instructions for AI agents (and contributors) working in this repo. Currently
covers the `otel-ui` frontend design system, since that's the active area of
work; add sections here as other parts of the repo get their own conventions.
## otel-ui: design system
### The rule
**One theme, light and dark - that's it.** `otel-ui/src/index.css` defines a
single set of `--ds-*` tokens with a dark variant (default) and a light
variant (`:root[data-theme="light"]`). Never introduce a second palette, a
third theme variant, or a new token namespace alongside `ds-*`. If a screen
needs a color, it comes from this table or it doesn't exist yet - extend the
table, don't invent a one-off.
The theme toggle lives in `TopBar.tsx`, persists to `localStorage`
(`otel.ui.theme`), and is applied before first paint by an inline script in
`index.html` so there's no flash of the wrong theme.
The old MD3-style `--color-surface-container-*` / `--color-on-surface` tokens,
the Material Symbols icon font and the Geist/Inter/JetBrains fonts have been
**deleted**. Never reintroduce them, raw Tailwind palette colors
(`slate-*`, `sky-*`, `rose-*`, `white/10`...), gradients, or shadows. Use
`lucide-react` for icons.
Documented exceptions (data-viz only, never chrome): per-service span colors
in the trace waterfall (`getSpanColor` in `otel-utils.ts`) and the muted
chart-series hues for metric lines/pods (`POD_COLORS`).
### Token reference
Source of truth is `otel-ui/src/index.css` (`:root` and `:root[data-theme="light"]`).
Tailwind utilities are generated as `bg-ds-*`, `text-ds-*`, `border-ds-*`.
| Token | Dark | Light | Used for |
|---|---|---|---|
| `ds-bg` | `#0D1014` | `#F4F2ED` | page ground |
| `ds-chrome` | `#0B0E12` | `#FFFFFF` | icon rail, top bar |
| `ds-surface` | `#14181E` | `#FFFFFF` | cards, panels |
| `ds-surface-2` | `#191E26` | `#F7F5F0` | inputs, chips, selected row |
| `ds-fill` | `#1D232B` | `#EDEAE3` | hairlines inside cards, chart bands, bar tracks |
| `ds-line` | `#242B34` | `#E3E0D8` | card/control borders |
| `ds-line-strong` | `#2A313B` | `#D6D2C8` | chip borders, neutral badges |
| `ds-text` | `#E8ECF2` | `#161A1F` | primary text |
| `ds-text-2` | `#98A3B2` | `#4F5964` | secondary text |
| `ds-text-3` | `#8B96A6` | `#6B7480` | labels, metadata, muted text - this is the accessibility floor, don't go dimmer |
| `ds-critical` / `ds-critical-bg` | `#FF6F5E` / `#2B1512` | `#C4402C` / `#FBE9E5` | breached threshold, open errors |
| `ds-warn` / `ds-warn-bg` | `#F0B24A` / `#2A2011` | `#8A5D0E` / `#F9EFDA` | degraded but not (necessarily) user-visible |
| `ds-ok` | `#4FCF9A` | `#1F7A56` | healthy, resolved, recovered |
| `ds-accent` / `ds-accent-bg` | `#7B8CFF` / `#161B33` | `#3B4FC4` / `#E9ECFB` | the one interactive accent - primary buttons, selected segment, links, focus |
| `ds-on-accent` | `ds-chrome` value | `#FFFFFF` | text on top of `ds-accent` |
**Colour is a signal, not decoration.** Chrome, borders and type are neutral.
`ds-critical` / `ds-warn` / `ds-ok` only appear where a real measurement is
out of its expected band or a real state changed (open vs resolved). A
severity pill's color must correspond to an actual state, never picked for
visual variety. Don't tint cards, headers or buttons "for interest."
### Fonts
Three families (`otel-ui/index.html` loads them; `index.css` exposes them as
`--font-display` / `--font-ui` / `--font-data`, used via Tailwind's
`font-[family-name:var(--font-display)]` arbitrary syntax):
- **Bricolage Grotesque** (`--font-display`) - page/card headings, big metric numbers.
- **Instrument Sans** (`--font-ui`) - all other UI text and body copy. Also the
new global default (`index.css`'s `@theme` overrides `--font-body`), so
unmigrated screens already inherit this typeface even before their colors move.
- **IBM Plex Mono** (`--font-data`) - anything a person would copy or grep:
ids, `key=value` attribute pairs, timestamps, durations, service names.
Never Inter, Roboto, Arial, or a new font stack.
### Layout shell (already built - reuse, don't rebuild)
- `otel-ui/src/components/layout/IconRail.tsx` - 72px fixed icon rail, global nav, one `aria-label` per icon.
- `otel-ui/src/components/layout/TopBar.tsx` - 58px fixed top bar: search, time range, theme toggle, auth.
- `otel-ui/src/components/FilterBar.tsx` - generic filter bar (optional scope segmented control, removable chips, dashed add-filter, an always-visible match readout, clear-all). Reuse this on every screen that lists things instead of building a bespoke filter row.
- `App.tsx` wires the shell globally and renders the single page-level `<main>`. Screens render inside it - see pitfall #2 below.
### Screen pattern
Established by `IssuesVisualizer.tsx` (the triage overview) and
`RumSessionsVisualizer.tsx` (real-user sessions) - follow this shape for the
remaining screens:
- `<FilterBar />` at the top of the screen's content.
- A main content column (`flex-1`) plus an optional fixed-width aside
(~340-420px) for supporting stats/panels.
- Cards: `rounded-[14px] border border-ds-line bg-ds-surface`, **no shadows,
no gradients** - depth comes from the border and ground color alone.
- No per-service rainbow badges. `getServicePalette` in `otel-utils.ts` (the
gradient hash-to-color picker) is legacy: per the original design spec, a
muted per-service palette is reserved for trace-waterfall span coloring
only, never for general UI chrome like service badges. Use a neutral
`border-ds-line-strong text-ds-text-2` badge instead.
- Real `<button>` / `<a href>` / `<input>` + `<label>` only. Never `onClick`
on a `div` or `span[role=button]`. `aria-label` on every icon-only control;
`aria-hidden="true"` on decorative SVG icons (use `lucide-react`, not the
Material Symbols icon font - see pitfall below).
### Pitfalls hit migrating Issues and RUM sessions - avoid repeating these
1. **No nested `<button>` elements.** Browsers implicitly close the outer
`<button>` the moment they hit an inner one, which silently breaks click
handling on whatever's after it in the DOM (this shipped once, undetected
until manual testing). If a row/card needs both "click to expand" and
inner action buttons, make the expandable area its own `<button>` and put
the action buttons as **siblings**, not children.
2. **No nested `<main>` landmarks.** `App.tsx` already renders one `<main>`;
a screen component must not render its own - use a `<div>`.
3. **`whitespace-nowrap` + `shrink-0` on fixed-height pills/badges.** Without
them, a longer label silently wraps onto two lines and blows out the
pill's height inside its flex row - easy to miss until you test with real
(longer) data or a narrower viewport.
4. **Never fabricate data.** Before adding a metric, column, or button, check
whether the field actually exists in the real data model - the backend API
response, or for RUM specifically, what `static/otel-rum.js` actually
sends (see the header comment in `otel-ui/src/lib/rum-utils.ts` for a
worked example of what's real vs. invented). If a design reference shows
something with no backing data - the RUM mockup invented rage clicks, geo
location, a "frustration score", and session replay, none of which this
pipeline captures - drop it or replace it with the closest real,
honestly-labeled equivalent. Never wire up a control that looks
functional but isn't backed by anything.
5. **Screenshots of a hidden Browser preview pane are unreliable** for
`position: fixed` elements (stale compositor frame, even after a full
reload). If a screenshot shows something that contradicts
`getComputedStyle`, trust the computed style, front the tab, and re-check
before concluding there's a real bug.
## Migration status
Whole UI is on `ds-*` (one theme, light + dark). The nav strip in `Tabs.tsx`
is gone - `IconRail` is the only navigation; `Tabs.tsx` just renders the
active screen. `TelemetryFilters` is built on `FilterBar`. The Material
Symbols icons were replaced with lucide.
Lessons from getting here:
- **Merges can silently revert a redesign.** `67ce419` reverted
`IssuesVisualizer.tsx` to the old styling (a parallel branch had edited the
same file). After any merge touching `otel-ui/src`, run the greps below -
they must return nothing.
- **A stale `otel-ui/dist` or un-redeployed image hides changes.** The Go
server serves `otel-ui/dist`, else `./dist`, else the legacy root
`index.html`. Rebuild (`npm --prefix otel-ui run build`) before concluding a
screen isn't wired.
- Still to polish (functional, but not yet re-laid-out to the screen
pattern): `TraceVisualization`, `MetricsVisualizer`, `StructuredLogTerminal`,
`SearchAndWatcher`, `TrendsVisualizer`, `SetupPanel`. They use `ds-*`
colors via a mechanical token swap but keep their old layout/type scale, use
`onClick` on non-buttons in places, and `CommandAndQueryBar` /
`WaveformHistogram` still sit above every non-Issues/RUM tab. Restructure
them onto `FilterBar` + the card/aside pattern when touching them.
### Finding old-pattern usages
```sh
grep -rlE "surface-container|text-on-surface|primary-container|bg-error-container|material-symbols-outlined" otel-ui/src
grep -rnE "(bg|text|border|ring|from|to|via)-(slate|zinc|gray|sky|cyan|blue|violet|indigo|purple|emerald|teal|rose|red|amber|orange)-[0-9]|text-white|border-white|bg-(white|black)/" otel-ui/src
```
Both must print nothing.