Skip to content

feat(apm): editor latency gutter markers + popover#2839

Open
jonmcwest wants to merge 1 commit into
06-22-feat_apm_editor_apm_enrichment_service_trpc_routefrom
06-22-feat_apm_editor_latency_gutter_markers_popover
Open

feat(apm): editor latency gutter markers + popover#2839
jonmcwest wants to merge 1 commit into
06-22-feat_apm_editor_apm_enrichment_service_trpc_routefrom
06-22-feat_apm_editor_latency_gutter_markers_popover

Conversation

@jonmcwest

@jonmcwest jonmcwest commented Jun 22, 2026

Copy link
Copy Markdown

Problem

When reviewing code in the editor, engineers have no visibility into how that code performs in production. Switching to PostHog tracing to correlate spans with source lines requires manual cross-referencing. This adds inline APM (Application Performance Monitoring) enrichment to the code editor, surfacing real production latency and span data directly in the gutter alongside the relevant source lines.

Changes

APM eligibility and marker building (@posthog/core)

  • Added isApmEnrichmentEligible to gate enrichment by file extension, delegating to the shared apmLangForFile so the editor and agent paths share one supported-language list.
  • Added buildApmLineMarkers to convert SerializedApmEnrichment stats into per-line gutter marker objects, each carrying the underlying SpanLineStat and a single-line tooltip summary (p95, p50, span count, error count).
  • Re-exported formatPercentDelta from @posthog/shared through enrichmentPresenters so the editor popover's existing import path stays stable while the agent path can also use it without importing @posthog/core.

CodeMirror extension (@posthog/ui)

  • Added postHogApmEnrichmentExtension, a CodeMirror state field + gutter that renders a fixed-colour purple presence marker on every instrumented line. Clicking a marker opens the APM popover. Markers re-anchor on doc changes to stay correct if the view becomes editable.

APM popover

  • Added ApmEnrichmentPopover, a fixed-position portal card showing p50/p95/p99 latency, span count, error rate, and percentage deltas vs. the prior window. Deltas are colour-coded (red when latency increases, green when it decreases). A "View in PostHog →" link deep-links to the tracing explorer. The popover closes on outside click or Escape.
  • Added apmPopoverStore (Zustand) to manage open/close state and the anchor rect independently of the existing enrichment popover store.

Data fetching

  • Added useFileApmEnrichment hook that calls trpc.apmEnrichment.enrichFile, gated behind the APM_ENRICHMENT_FLAG feature flag (auto-enabled in dev) and file-type eligibility. Returns undefined when the gutter extension should be skipped entirely, and null while data is loading.
  • Wired apmEnrichment into CodeEditorPanel and CodeMirrorEditor. The APM popover is dismissed automatically when switching files.

How did you test this?

  • Added unit tests for isApmEnrichmentEligible covering Rust, Go, Python, TypeScript, Java source files, and non-source files (Markdown, JSON, CSS, PNG).
  • Added unit tests for buildApmLineMarkers covering null enrichment, marker-per-line production, stat pass-through, and tooltip summary formatting.
  • Added unit tests for formatPercentDelta covering null/undefined input, signed rounding, and sub-1% noise suppression.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Copy link
Copy Markdown
Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

React Doctor found 2 issues in 1 file · 2 warnings.

2 warnings

src/features/code-editor/components/CodeEditorPanel.tsx

Reviewed by React Doctor for commit 8ffd7e0.

@jonmcwest jonmcwest marked this pull request as ready for review June 22, 2026 14:41
@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "feat(apm): editor latency gutter markers..." | Re-trigger Greptile

Comment on lines +10 to +21
it("signs and rounds a meaningful delta", () => {
expect(formatPercentDelta(186.2)).toBe("+186%");
expect(formatPercentDelta(-5.2)).toBe("-5%");
expect(formatPercentDelta(1.43)).toBe("+1%");
expect(formatPercentDelta(-0.9)).toBe("-1%");
});

it("hides sub-1% noise rather than rendering a meaningless +0%/-0%", () => {
expect(formatPercentDelta(0)).toBeNull();
expect(formatPercentDelta(0.4)).toBeNull();
expect(formatPercentDelta(-0.4)).toBeNull();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The two it blocks each bundle multiple independent input/expected pairs as sequential assertions. Per the team's preference, these should use it.each so each pair gets its own test row — a single assertion failure won't shadow the rest, and the test name itself documents the input.

Suggested change
it("signs and rounds a meaningful delta", () => {
expect(formatPercentDelta(186.2)).toBe("+186%");
expect(formatPercentDelta(-5.2)).toBe("-5%");
expect(formatPercentDelta(1.43)).toBe("+1%");
expect(formatPercentDelta(-0.9)).toBe("-1%");
});
it("hides sub-1% noise rather than rendering a meaningless +0%/-0%", () => {
expect(formatPercentDelta(0)).toBeNull();
expect(formatPercentDelta(0.4)).toBeNull();
expect(formatPercentDelta(-0.4)).toBeNull();
});
it.each([
[186.2, "+186%"],
[-5.2, "-5%"],
[1.43, "+1%"],
[-0.9, "-1%"],
] as const)("signs and rounds %s → %s", (input, expected) => {
expect(formatPercentDelta(input)).toBe(expected);
});
it.each([0, 0.4, -0.4])(
"hides sub-1%% noise: formatPercentDelta(%s) → null",
(input) => {
expect(formatPercentDelta(input)).toBeNull();
},
);

Context Used: Do not attempt to comment on incorrect alphabetica... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

{ filePath },
{
enabled,
staleTime: Number.POSITIVE_INFINITY,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 staleTime: Number.POSITIVE_INFINITY caches the APM data for the entire session with no automatic refresh. After a deployment or any production change, engineers will continue seeing the old latency numbers with no indication they are stale. A bounded stale time (e.g. 5 minutes) lets React Query silently background-refresh without causing visible loading flickers.

Suggested change
staleTime: Number.POSITIVE_INFINITY,
staleTime: 5 * 60 * 1000, // 5 minutes – APM data is cheap to refresh and should not go stale silently

@jonmcwest jonmcwest force-pushed the 06-22-feat_apm_editor_apm_enrichment_service_trpc_route branch from 214b340 to df9dcd6 Compare June 22, 2026 15:29
@jonmcwest jonmcwest force-pushed the 06-22-feat_apm_editor_latency_gutter_markers_popover branch from 0cfe05e to 8ffd7e0 Compare June 22, 2026 15:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant