RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Configs/AGENTS.md/grafana/grafana

Critical User Journeys

public/app/core/journeys/AGENTS.md

How to add and modify CUJ instrumentation in Grafana

AGENTS.md

Quality

73/100

Scores the file, not the repository.

Length

999 words

13 headings · 5 code blocks

Repository

76k

— · pushed 0 days ago

Last changed

3 days ago

First indexed 3 days ago.
grafana/grafana/public/app/core/journeys/AGENTS.mdRawGitHub
1---
2title: Critical User Journeys
3description: How to add and modify CUJ instrumentation in Grafana
4globs:
5 - 'public/app/core/journeys/**'
6---
7 
8# CUJ instrumentation - Agent Configuration
9 
10This directory holds the runtime wirings for Critical User Journeys (CUJs) — multi-step user workflows tracked end-to-end as OTel traces + Faro measurements behind the `cujTracking` feature toggle.
11 
12## Required Reading
13 
14Always read these before adding or modifying a journey:
15 
161. **`./journey-tracking.md`** - the canonical reference. It covers architecture, telemetry shape, the registry, parent journeys, debug logging, and a full worked example.
172. **`./searchToResource.ts`** - canonical wiring file; copy this shape for new journeys.
183. **`./searchToResource.test.ts`** - canonical test shape.
194. **`./__test-utils__/journeyTestHarness.ts`** - the only legitimate way to mock the tracker in unit tests.
20 
21Public framework types live in **`@grafana/runtime`** (`packages/grafana-runtime/src/services/JourneyTracker.ts`):
22 
23- `registerJourneyTriggers` - registers the start condition (called once at module import).
24- `onJourneyInstance` - registers the per-instance end-condition handler (also called once at module import).
25- `JourneyMeta` - registry entry (type, description, owner, timeoutMs, optional `parents`).
26- `JourneyHandle` - per-instance handle: `recordEvent`, `startStep`, `setAttributes`, `end(outcome)`.
27 
28Never import from `JourneyTrackerImpl` or `JourneyRegistryImpl` directly - those are internal.
29 
30## Adding a New Journey: Recipe
31 
32> **Fast path:** `yarn cuj:new <type> [--with-smoke]` scaffolds the wiring file, test file, optional smoke driver, registry entry, and bootstrap import in one shot. Run `yarn cuj:new --help` for flags (`--owner`, `--description`, `--timeout-ms`, `--parent`, `--dry-run`). After scaffolding, fill in the TODOs marked in the generated files. The steps below describe what the script generates and why.
33 
34This is the short version of `journey-tracking.md` Steps 0-7. Read the full version if anything's unclear.
35 
36### 1. Decide the journey shape
37 
38- **Type name**: `snake_case` verb-object (`alert_rule_save`, `panel_edit`).
39- **Owner**: the squad whose telemetry this is (`grafana-dashboards`, `grafana-alerting`, …).
40- **Timeout**: how long is "still going" plausible for? Default 5 min; multi-hour flows (datasource setup) use longer.
41- **Parents** (optional): other journey types that should nest under (set `parents: ['parent_type']`). When the parent is active at start, the child's span nests in the parent's trace.
42 
43### 2. Identify or add interactions
44 
45The framework subscribes to `reportInteraction` events via `onInteraction(name, callback)`. Check what the relevant code already emits:
46 
47```bash
48grep -rn 'reportInteraction(' public/app/features/&lt;your-area&gt;/
49```
50 
51If the events you need don't exist, add them. **Use `silent: true`** for new pure-CUJ events that shouldn't pollute analytics:
52 
53```ts
54reportInteraction('grafana_<area>_<verb>', { ...attrs }, { silent: true });
55```
56 
57### 3. Register metadata
58 
59Add an entry to `journeyRegistry.ts`:
60 
61```ts
62{
63 type: 'alert_rule_save',
64 description: 'User edits and saves an alert rule',
65 owner: 'grafana-alerting',
66 timeoutMs: 10 * 60_000,
67 // parents: ['some_parent_type'], // optional
68},
69```
70 
71### 4. Create the wiring file
72 
73Path: `public/app/core/journeys/<camelCase>.ts`. Follow `searchToResource.ts` exactly:
74 
75```ts
76import { onInteraction, registerJourneyTriggers, onJourneyInstance } from '@grafana/runtime';
77import { collectUnsubs, str } from './utils';
78 
79/**
80 * Journey: <type>
81 * <one-line description of what the journey covers>
82 *
83 * Start triggers: <which interaction(s) start it>
84 * Steps (duration): <list> // optional
85 * Events (point-in-time): <list> // optional
86 * End conditions:
87 * - success: <which interaction>
88 * - discarded / canceled / abandoned: <which interaction>
89 * - timeout: 60s / 5min / etc.
90 */
91 
92registerJourneyTriggers('<type>', (tracker) => {
93 return onInteraction('<start_event>', (props) => {
94 if (!tracker.getActiveJourney('<type>')) {
95 tracker.startJourney('<type>', { attributes: { ... } });
96 }
97 });
98});
99 
100onJourneyInstance('<type>', (handle) => {
101 const { add, cleanup } = collectUnsubs();
102 // wire steps + end conditions; each onInteraction handler call goes through `add(...)`
103 return cleanup;
104});
105```
106 
107Use the `str(value)` helper for any value going into attributes - it coerces undefined / objects to a safe string.
108 
109### 5. Import at bootstrap
110 
111Add the import to `public/app/app.ts`:
112 
113```ts
114await Promise.all([
115 // ...existing imports...
116 import('./core/journeys/<camelCase>'),
117]);
118```
119 
120### 6. Write tests
121 
122Path: `public/app/core/journeys/<camelCase>.test.ts`. Copy the shape of `searchToResource.test.ts`. Cover:
123 
124- start condition fires with right attributes
125- each step / event handler fires correctly
126- each end condition (success, discarded, etc.) ends the journey with the right outcome
127- doesn't double-start when journey is active
128- ignores irrelevant interactions
129 
130Run: `yarn jest --no-watch <camelCase>.test.ts`.
131 
132### 7. Verify locally
133 
134Enable the toggle + Faro (see `journey-tracking.md` Configuration section). Walk the workflow with `localStorage.setItem('grafana.debug.journeyTracker', 'true')` set. Confirm in console: `startJourney`, step events, `end` with right outcome.
135 
136For automated load: see `./searchToResource.smoke.ts` for the optional smoke driver pattern (runs the journey N times via Playwright).
137 
138## Pre-merge Checklist
139 
140- [ ] Registry entry has owner, description, sensible `timeoutMs`.
141- [ ] Wiring file follows the `searchToResource.ts` shape (no module-scope `StepHandle` that outlives a journey - keep duration-step bookkeeping inside `onJourneyInstance`'s closure).
142- [ ] All `onInteraction` subscriptions inside `onJourneyInstance` are tracked through `collectUnsubs` so cleanup runs on journey end.
143- [ ] Tests cover start, every end condition, and at least one negative case.
144- [ ] Bootstrap import added to `app.ts`.
145- [ ] If you added new `reportInteraction` calls purely for CUJ purposes, they pass `{ silent: true }`.
146- [ ] If parent nesting is intended, parent's `type` listed in `parents: [...]`.
147- [ ] PR titles + commits scoped to the squad area, not pan-CUJ.
148 
149## Common Mistakes
150 
151- **Module-scope step handles**. Causes step leak across journey instances. Always store `StepHandle` in the `onJourneyInstance` closure.
152- **Forgetting `add(...)` around an `onInteraction` subscription**. The unsubscribe is lost; subscriptions outlive the journey.
153- **Using `recordEvent` for things that have a duration**. Use `startStep` + `step.end()` for measured operations; `recordEvent` is point-in-time.
154- **Not handling the discarded path**. A journey that only ends on success will time out for the abandoned case - explicitly map "user closed without selecting" to `handle.end('discarded')`.
155- **Polluting analytics**. New CUJ-only events should be `silent: true`. Existing analytics events (`command_palette_action_selected`, etc.) stay un-silent because they have independent value.
156 
157## Smoke Driver (optional)
158 
159Each journey can ship a Playwright smoke driver that exercises it against local Grafana. Pattern: a `<camelCase>.smoke.ts` file alongside the wiring that exports a `JourneyDriver`. Shared helpers (typing patterns, activation styles, palette open) live in `./__smoke__/`. The orchestrator in `scripts/cuj-smoke.ts` imports and registers each driver. See `searchToResource.smoke.ts` for the canonical example.
160 
161Smoke files import each other with explicit `.ts` extensions (Node ESM requirement) and live under their own `tsconfig.smoke.json`. Validate with `yarn typecheck:smoke` (also runs in CI + lefthook on relevant file changes).
162 

Commands it names

  • yarn cuj:new <type> [--with-smoke]
  • yarn cuj:new --help
  • yarn jest --no-watch <camelCase>.test.ts
  • yarn typecheck:smoke

Sections

  • CUJ instrumentation - Agent Configuration
  • Required Reading
  • Adding a New Journey: Recipe
  • 1. Decide the journey shape
  • 2. Identify or add interactions
  • 3. Register metadata
  • 4. Create the wiring file
  • 5. Import at bootstrap
  • 6. Write tests
  • 7. Verify locally
  • Pre-merge Checklist
  • Common Mistakes
  • Smoke Driver (optional)

What it covers

testtesting-strategygit-pragent-behaviour

Stack — with the evidence

typescript

(1.00)

go

(1.00)

node

(0.85)

jest

(0.85)

playwright

(0.85)

javascript

(0.60)

nx

(0.60)

monorepo

(0.60)

eslint

(0.60)

docker

(0.60)

github-actions

(0.60)

Glob targeting

  • public/app/core/journeys/**

Format

AGENTS.md

A plain-markdown README for coding agents, deliberately unopinionated: no frontmatter, no globs, no vendor keys. That minimalism is why it became the one file a dozen different agents will read, and why it carries the least per-file targeting power of any format here.

What the corpus says about it

Repository

Owner
grafana
Language
—
License
—
Archived
no

All configs in this repo

Also in grafana/grafana

Diff this repo’s formats

One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?

The other instruction files in this repository
RepositoryFormatStackCoversScoreChanged
grafana/grafanaAGENTS.md · 76kAGENTS.mdtypescriptgo+9setupbuildtestlint-format+689/1003 days ago
grafana/grafanae2e-playwright/alerting-suite/AGENTS.md · 76kAGENTS.mdtypescriptgo+9teststylearchtesting-strategy+381/1003 days ago
grafana/grafanae2e-playwright/dashboard-new-layouts/AGENTS.md · 76kAGENTS.mdtypescriptgo+9teststyletesting-strategydatabase+162/100today
grafana/grafanae2e-playwright/plugin-e2e/plugin-e2e-api-tests/AGENTS.md · 76kAGENTS.mdtypescriptgo+9teststyletesting-strategygit+470/1003 days ago
grafana/grafanapackages/grafana-ui/AGENTS.md · 76kAGENTS.mdtypescriptgo+9uiagent-behaviour16/1003 days ago
grafana/grafanapkg/storage/unified/AGENTS.md · 76kAGENTS.mdtypescriptgo+9do-not46/1003 days ago
grafana/grafanapublic/app/features/AGENTS.md · 76kAGENTS.mdtypescriptgo+9agent-behaviour16/1003 days ago
grafana/grafanapublic/app/features/alerting/unified/AGENTS.md · 76kAGENTS.mdtypescriptgo+9setuptestlint-formatstyle+1176/1003 days ago
grafana/grafanapublic/app/features/expressions/components/SqlExpressions/SqlEditor/AGENTS.md · 76kAGENTS.mdtypescriptgo+9styleagent-behaviour43/1003 days ago
grafana/grafanapublic/app/plugins/panel/AGENTS.md · 76kAGENTS.mdtypescriptgo+9agent-behaviour16/1003 days ago
Diff against AGENTS.md Diff against e2e-playwright/alerting-suite/AGENTS.md Diff against e2e-playwright/dashboard-new-layouts/AGENTS.md Diff against e2e-playwright/plugin-e2e/plugin-e2e-api-tests/AGENTS.md Diff against packages/grafana-ui/AGENTS.md Diff against pkg/storage/unified/AGENTS.md Diff against public/app/features/AGENTS.md Diff against public/app/features/alerting/unified/AGENTS.md Diff against public/app/features/expressions/components/SqlExpressions/SqlEditor/AGENTS.md Diff against public/app/plugins/panel/AGENTS.md

Similar configs

Same format, overlapping stack, ranked by quality.

Same format, overlapping stack, ranked by quality
RepositoryFormatStackCoversScoreChanged
TryGhost/Ghoste2e/AGENTS.md · 55kAGENTS.mdtypescriptjavascript+12setupteststylearch+2100/1003 days ago
aaif-goose/gooseAGENTS.md · 52kAGENTS.mdrusttypescript+2setupbuildtestlint-format+6100/1003 days ago
SkeneTechnologies/skene-cookbookAGENTS.md · 51AGENTS.mdpythoneslint+4setupbuildtestlint-format+7100/1002 days ago
n8n-io/n8npackages/@n8n/agents/AGENTS.md · 199kAGENTS.mdtypescriptlangchain+16buildteststylearch+3100/1003 days ago
mui/material-uiAGENTS.md · 99kAGENTS.mdtypescriptjavascript+13setupbuildtestlint-format+9100/1003 days ago
duckduckgo/content-scope-scriptsspecial-pages/AGENTS.md · 70AGENTS.mdtypescriptjavascript+5buildteststylearch+3100/1003 days ago
trick77/agents-md-syncAGENTS.md · 2AGENTS.mdtypescriptnode+4setupbuildteststyle+5100/1003 days ago
code-yeongyu/oh-my-openagentpackages/web/AGENTS.md · 67kAGENTS.mdtypescriptbun+10setupbuildtestlint-format+6100/1002 days ago
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack