Skip to content

feat: add event patterns as a first-class dashboard tile type#2604

Open
brandon-pereira wants to merge 2 commits into
mainfrom
brandon/brandon-event-patterns-dashboard-tile
Open

feat: add event patterns as a first-class dashboard tile type#2604
brandon-pereira wants to merge 2 commits into
mainfrom
brandon/brandon-event-patterns-dashboard-tile

Conversation

@brandon-pereira

@brandon-pereira brandon-pereira commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

Event patterns can now be created, edited, and saved as dashboard tiles — with full support across the UI, MCP server, and External API v2.

Previously, pattern mining was only available on the search page. Dashboard tiles had no way to persist a custom pattern expression, and the PatternColumnSelector used internal component state that was lost on reload. The MCP and External API schemas didn't include event_patterns at all, so agents and API consumers couldn't create or round-trip pattern tiles.

What changed

Tile edit UI — EventPatterns gets its own editor section with a "Pattern Expression" input (labeled, with the source's default body expression as placeholder) and a WHERE filter. This replaces the generic SELECT input that Search uses, which was semantically wrong for patterns.

Config persistence fixconvertFormStateToSavedChartConfig and convertFormStateToChartConfig now correctly persist select as a string for EventPatterns (it was falling through to the form.series array path, discarding the user's input). A new isStringSelectDisplayType guard consolidates the repeated Search || EventPatterns checks across 4 call sites.

Source-switching fix — The preview panel now overrides from, connection, timestampValueExpression, and select from the live tableSource (matching how the Search preview already works). This prevents stale columns from a previous source (e.g. SeverityText from a log source) from leaking into a trace-table query. The bodyValueExpression fallback also rejects multi-column strings (containing commas) to guard against pre-fix saved tiles that stored defaultTableSelectExpression as the pattern expression.

MCP — New mcpEventPatternsTileSchema added to both the save and patch tile unions. Agents can create pattern tiles with displayType: "event_patterns", sourceId, optional select (pattern expression), where, and whereLanguage.

External API v2 — New Zod schema + bidirectional conversion. Pattern tiles now round-trip through GET/PUT without being silently downgraded to line charts.

PatternTable cleanup — Removed the variant prop (parent containers own the background). The PatternColumnSelector is now only shown when external handlers are provided (search page); dashboard/preview tiles configure the expression in the edit UI instead.

Screenshots

Screenshot 2026-07-07 at 4 13 42 PM Screenshot 2026-07-07 at 4 14 06 PM

Event patterns can now be created, edited, and saved as dashboard tiles
with full parity across the UI, MCP, and External API v2.

- Add EventPatterns to the DisplayType enum and all exhaustive
  switches (rawSqlParams, constants, external API conversions)
- Dedicated Pattern Expression input in the tile edit UI with the
  sources default body expression as placeholder
- Fix config persistence: select is now correctly saved as a string
  for EventPatterns (was falling through to the series array path)
- Fix source switching: override from/connection/select from the
  live source so stale columns dont leak into queries
- Add isStringSelectDisplayType guard to consolidate the repeated
  Search/EventPatterns display-type checks
- Add MCP tile schema to both save and patch unions so agents can
  create pattern tiles
- Add External API v2 Zod schema and bidirectional conversion so
  pattern tiles round-trip through GET/PUT without downgrade
- Remove variant prop from PatternTable - parent containers own bg
- Hide PatternColumnSelector in dashboard/preview contexts
@changeset-bot

changeset-bot Bot commented Jul 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a45ea36

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Minor
@hyperdx/api Minor
@hyperdx/otel-collector Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Jul 7, 2026 11:01pm
hyperdx-storybook Ready Ready Preview, Comment Jul 7, 2026 11:01pm

Request Review

@github-actions github-actions Bot added the review/tier-4 Critical — deep review + domain expert sign-off label Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🔴 Tier 4 — Critical

Touches auth, data models, config, tasks, OTel pipeline, ClickHouse, or CI/CD.

Why this tier:

  • Critical-path files (1):
    • packages/api/src/routers/external-api/v2/utils/dashboards.ts
  • Cross-layer change: touches frontend (packages/app) + backend (packages/api) + shared utils (packages/common-utils)

Review process: Deep review from a domain expert. Synchronous walkthrough may be required.
SLA: Schedule synchronous review within 2 business days.

Stats
  • Production files changed: 15
  • Production lines changed: 344 (+ 9 in test files, excluded from tier calculation)
  • Branch: brandon/brandon-event-patterns-dashboard-tile
  • Author: brandon-pereira

To override this classification, remove the review/tier-4 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@greptile-apps

greptile-apps Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR promotes event pattern mining to a first-class dashboard tile type by wiring up a new EventPatterns display type end-to-end — from the DisplayType enum and UI tab, through chart form state serialization and preview rendering, to the External API v2 schema and MCP tool schemas.

  • UI editor: Adds a dedicated "Pattern Expression" + WHERE filter control block in ChartEditorControls, replaces the generic series array editor, and ensures select is persisted as a plain string (not a DerivedColumn[]) for both save and preview paths via the new isStringSelectDisplayType guard.
  • Config persistence: convertFormStateToSavedChartConfig and convertFormStateToChartConfig now correctly handle EventPatterns select as a string, and a bracket-aware isSingleExpression helper guards against stale multi-column defaultTableSelectExpression values leaking into the pattern body field for previously-saved tiles.
  • API round-trip: externalDashboardEventPatternsChartConfigSchema + the new convertToExternalTileChartConfig/convertToInternalTileConfig arms ensure GET→PUT round-trips preserve the pattern config without silent downgrade; the MCP schemas add the new tile type to both create and patch unions.

Confidence Score: 5/5

Safe to merge — the change is a well-scoped additive feature with no modifications to existing display-type paths.

All changed code paths are additive: the new EventPatterns enum value, the isStringSelectDisplayType guard, the API schema, and the MCP schema are all opt-in and leave existing tile types untouched. The two issues flagged in previous review threads (comma heuristic and missing stringValueOrDefault on where) have both been addressed in this version. The bracket-aware isSingleExpression helper correctly handles function-call expressions with commas. Serialization round-trips through both the External API and MCP are symmetrical and covered by satisfies constraints.

No files require special attention.

Important Files Changed

Filename Overview
packages/app/src/components/ChartEditor/utils.ts Introduces isStringSelectDisplayType guard and updates convertFormStateToSavedChartConfig / convertFormStateToChartConfig / validateChartForm to handle EventPatterns select as a plain string; logic is correct and well-commented.
packages/app/src/components/DBEditTimeChartForm/ChartPreviewPanel.tsx Renders PatternTable in preview when activeTab === 'event_patterns'; properly overrides from/connection/timestampValueExpression from live tableSource to prevent stale-source leakage during source switching.
packages/app/src/DBDashboardPage.tsx Adds the EventPatterns tile render branch using isSingleExpression to guard against legacy multi-column select values; dashboard tiles don't override from/connection from source directly but effectiveQueriedConfig is already source-resolved at this point.
packages/api/src/routers/external-api/v2/utils/dashboards.ts Adds EventPatterns to both the builder convertToExternalTileChartConfig switch and convertToInternalTileConfig switch; both select and where are normalized via stringValueOrDefault, enabling clean GET/PUT round-trips.
packages/api/src/utils/zod.ts New externalDashboardEventPatternsChartConfigSchema added with correct defaults and added to the discriminated union; mirrors the search schema closely.
packages/api/src/mcp/tools/query/helpers.ts runConfigTile correctly delegates EventPatterns to runEventPatterns before the generic SQL path, preventing the pattern-mining query from being mis-rendered as a standard chart query.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant EditForm as EditTimeChartForm
    participant Controls as ChartEditorControls
    participant Utils as ChartEditor/utils
    participant Preview as ChartPreviewPanel
    participant PatternTable
    participant API as External API v2
    participant MCP as MCP Tool

    User->>EditForm: Select "Patterns" tab
    EditForm->>Utils: isStringSelectDisplayType(EventPatterns) → true
    EditForm->>EditForm: setValue('select', '') if not string
    EditForm->>Controls: render EventPatterns branch
    Controls->>Controls: SQLInlineEditor (Pattern Expression) + SearchWhereInput

    User->>EditForm: Enter pattern expression + submit
    EditForm->>Utils: convertFormStateToChartConfig(form, dateRange, source)
    Utils->>Utils: "mergedSelect = form.select (string)"
    Utils->>Preview: "queriedConfig (select='', displayType=EventPatterns)"
    Preview->>PatternTable: "config{select:'', displayType:Table} + bodyValueExpression"

    User->>EditForm: Save tile
    EditForm->>Utils: convertFormStateToSavedChartConfig(form, source)
    Utils->>Utils: isStringSelectDisplayType → persist select as string

    Note over API: External API GET/PUT
    API->>API: convertToExternalTileChartConfig → EventPatterns case
    API->>API: convertToInternalTileConfig → 'event_patterns' case

    Note over MCP: MCP create/patch tile
    MCP->>MCP: mcpEventPatternsTileSchema validates config
    MCP->>MCP: runConfigTile → runEventPatterns(teamId, source, ...)

    Note over PatternTable: Dashboard render
    PatternTable->>PatternTable: isSingleExpression(select) → bodyValueExpression
    PatternTable->>PatternTable: useGroupedPatterns(config, bodyValueExpression)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant EditForm as EditTimeChartForm
    participant Controls as ChartEditorControls
    participant Utils as ChartEditor/utils
    participant Preview as ChartPreviewPanel
    participant PatternTable
    participant API as External API v2
    participant MCP as MCP Tool

    User->>EditForm: Select "Patterns" tab
    EditForm->>Utils: isStringSelectDisplayType(EventPatterns) → true
    EditForm->>EditForm: setValue('select', '') if not string
    EditForm->>Controls: render EventPatterns branch
    Controls->>Controls: SQLInlineEditor (Pattern Expression) + SearchWhereInput

    User->>EditForm: Enter pattern expression + submit
    EditForm->>Utils: convertFormStateToChartConfig(form, dateRange, source)
    Utils->>Utils: "mergedSelect = form.select (string)"
    Utils->>Preview: "queriedConfig (select='', displayType=EventPatterns)"
    Preview->>PatternTable: "config{select:'', displayType:Table} + bodyValueExpression"

    User->>EditForm: Save tile
    EditForm->>Utils: convertFormStateToSavedChartConfig(form, source)
    Utils->>Utils: isStringSelectDisplayType → persist select as string

    Note over API: External API GET/PUT
    API->>API: convertToExternalTileChartConfig → EventPatterns case
    API->>API: convertToInternalTileConfig → 'event_patterns' case

    Note over MCP: MCP create/patch tile
    MCP->>MCP: mcpEventPatternsTileSchema validates config
    MCP->>MCP: runConfigTile → runEventPatterns(teamId, source, ...)

    Note over PatternTable: Dashboard render
    PatternTable->>PatternTable: isSingleExpression(select) → bodyValueExpression
    PatternTable->>PatternTable: useGroupedPatterns(config, bodyValueExpression)
Loading

Reviews (2): Last reviewed commit: "fix: address PR review feedback" | Re-trigger Greptile

Comment thread packages/app/src/DBDashboardPage.tsx
Comment thread packages/api/src/routers/external-api/v2/utils/dashboards.ts
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 225 passed • 3 skipped • 1545s

Status Count
✅ Passed 225
❌ Failed 0
⚠️ Flaky 3
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Deep Review

Scope note: The base SHA passed to this review (a34b7fb) is a shallow/divergent commit with no common ancestor to the PR head, so git diff a34b7fb HEAD fabricates phantom "removals" (collapsed tile toolbar, multi-line title ellipsis from #2596, display-settings unsaved-changes tracking, a deleted e2e test) that are not part of this PR. This review was scoped against the true branch point (803f824f..HEAD), where the change is purely additive: a new DisplayType.EventPatterns tile type wired through the tile editor UI, chart preview, dashboard render, the MCP server, and External API v2. Any automation or reviewer diffing against a34b7fb will see false regressions — see the branch-hygiene note below.

No critical issues found. The feature is cleanly implemented. Server-side pattern execution sanitizes the caller-supplied expression via the SAFE_BODY_EXPR_CHARS allowlist plus a single-expression check, wraps ClickHouse and mining errors into structured mcpUserError/mcpServerError results, and resolves source/connection defensively; the External API and MCP schemas mirror the existing Search tile and round-trip without data loss; and the frontend (getEventBody) and backend (resolveBodyExpression) resolve the same default body column, so the empty-select fallback is consistent across surfaces.

🟡 P2 — recommended

  • packages/app/src/components/ChartEditor/utils.ts:139 — New conversion and guard logic (isStringSelectDisplayType, the EventPatterns empty-select fallback that diverges from Search, and the External API v2 bidirectional event_patterns conversion in dashboards.ts) ships with no unit tests; a future refactor could silently break tile persistence or API round-trip.
    • Fix: Add unit tests covering the EventPatterns branch of convertFormStateToChartConfig/convertFormStateToSavedChartConfig, isSingleExpression edge cases (empty, multi-column, bracketed like COALESCE(a, b)), and an External API v2 GET→PUT round-trip for an event_patterns tile.
  • branch base — The PR branch is missing recent main commits (e.g. fix(charts): use multi-line ellipsis for dashboard tile titles #2596, Polish dashboard tile styling and header layout #2592) and has no shared ancestor with the provided base, which will surface phantom conflicts/regressions in diffs and can cause a messy merge.
    • Fix: Rebase the branch onto current main before merge and re-diff to confirm the change stays additive.
🔵 P3 nitpicks (2)
  • packages/app/src/components/DBEditTimeChartForm/ChartPreviewPanel.tsx:298 — The PatternTable config/bodyValueExpression/totalCountConfig construction (including the isSingleExpression guard and the select: '' reset comment) is duplicated near-verbatim in DBDashboardPage.tsx; the two copies can drift.
    • Fix: Extract a shared helper (e.g. buildPatternTableProps(config, source, dateRange)) reused by both the preview and dashboard render paths.
  • packages/app/src/source.ts:90getEventBody uses || for the log body fallback while the equivalent backend resolveBodyExpression uses ??; harmless today but an empty-string bodyExpression would resolve differently across the two.
    • Fix: Align both on the same nullish/falsy operator so the frontend and backend default-column resolution stay provably identical.

Reviewers: The compound-engineering fan-out (correctness, adversarial, api-contract, kieran-typescript, julik-frontend-races, reliability, testing, maintainability, project-standards, agent-native) was dispatched against the corrected scope; at synthesis time only the learnings researcher had returned (no relevant prior learnings in docs/solutions/). The findings above are grounded in the orchestrator's own verified read of the full diff and the key referenced code (runEventPatterns.ts, the MCP and External API v2 schemas, and the bidirectional conversion utilities).

Testing gaps:

  • No unit coverage for External API v2 event_patterns round-trip conversion.
  • No coverage for isSingleExpression / isStringSelectDisplayType or the EventPatterns empty-select fallback divergence.

- Replace naive comma heuristic with bracket-aware isSingleExpression()
  helper so expressions like COALESCE(a, b) are not silently discarded
  as multi-column strings
- Normalize where field via stringValueOrDefault in external API
  EventPatterns conversion, matching the Search arm
- Add changeset for the feature
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/tier-4 Critical — deep review + domain expert sign-off

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant