Skip to content

feat: add source field suggestions#2436

Open
karl-power wants to merge 1 commit into
mainfrom
karl/hdx-4374-source-field-suggestions
Open

feat: add source field suggestions#2436
karl-power wants to merge 1 commit into
mainfrom
karl/hdx-4374-source-field-suggestions

Conversation

@karl-power

Copy link
Copy Markdown
Contributor

Summary

Why: Configuring a source against a custom (non-OTel) table means typing column names from memory into free-text inputs, with no schema inspection, no validation, and no warning when a config will silently break search (e.g. bodyExpression set but implicitColumnExpression empty — the HDX-4376 trap). This adds the form-side prevention to pair with HDX-4376 (runtime fallback) and HDX-4373 (row-panel cleanup).

What changed:

  • Candidate hints — after Database + Table are picked, scans columns (cached via useColumns) and suggests the likely column under each empty optional-expression field, with one-click apply. Type-aware, name-matched, resolves per source kind (logs vs traces). Suggests, never pre-fills; lists all candidates when ambiguous.
  • Live validation — filled expressions are validated against the real table via a debounced EXPLAIN query, showing valid/invalid inline.
  • Pairing warningsgetSourceConfigPairingWarnings flags body/implicit-column mismatches (log sources only) in a non-blocking save-time modal with one-click fixes or "Save anyway".

Screenshots or video

Untitled2.mov

How to test on Vercel preview

Preview routes: /team

Steps:

  1. Create a new source, or edit an existing one and check the suggestions for fields are correct.
  2. Check the warning modal displays in one of two ways when Body or Implicit Column Expression fields are unset.

References

@vercel

vercel Bot commented Jun 10, 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 Jun 10, 2026 4:37pm
hyperdx-storybook Ready Ready Preview, Comment Jun 10, 2026 4:37pm

Request Review

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

Copy link
Copy Markdown
Contributor

🔴 Tier 4 — Critical

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

Why this tier:

  • Large diff: 1150 production lines changed (threshold: 1000)

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: 5
  • Production lines changed: 1150 (+ 221 in test files, excluded from tier calculation)
  • Branch: karl/hdx-4374-source-field-suggestions
  • Author: karl-power

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 Jun 10, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds schema-aware field suggestions, live EXPLAIN-based expression validation, and a save-time pairing-warning modal to the source configuration form. The validation logic is extracted into a reusable useExpressionValidation hook; column inference lives in a well-tested sourceFieldSuggestions utility.

  • Field suggestions — after Database + Table are chosen, useColumns fetches the schema and inferSourceFieldCandidates suggests the most likely column for each empty expression field, with one-click apply and ranked alternates. Suggestions are type-aware and resolve per source kind (logs vs traces).
  • Live validation — filled expression fields are validated against the real table via a debounced EXPLAIN query rendered by ExpressionValidationStatus; the existing HighlightedAttributeRow validation is refactored onto the same hook.
  • Pairing warningsgetSourceConfigPairingWarnings detects body/implicit-column mismatches for log sources and surfaces a non-blocking modal on save with one-click fixes or a "Save anyway" escape hatch.

Confidence Score: 4/5

The feature is additive and non-destructive — existing save paths are unchanged when no warnings are triggered, and the warning modal provides a cancel path so nothing is persisted without user intent.

The new code is well-structured and the core suggestion/validation logic has solid unit-test coverage. The two flagged points — a silent key collision in the lowercase-name map and the untyped parsedData patch — are both low-probability paths today, but neither is a correctness guarantee. The rest of the change (hook extraction, component composition, modal flow) is straightforward and safe.

sourceFieldSuggestions.ts (lowercase map collision) and SourceForm.tsx (pendingSave.parsedData: any) warrant a second look before extending the warning system with additional fix types.

Important Files Changed

Filename Overview
packages/app/src/utils/sourceFieldSuggestions.ts New utility providing column-name inference and body/implicit-column pairing checks; well-tested, minor key-collision edge case in the lowercase name map
packages/app/src/utils/tests/sourceFieldSuggestions.test.ts Comprehensive tests cover canonical matching, alternates, per-kind overrides, type predicates, and pairing warnings; no issues
packages/app/src/hooks/useExpressionValidation.ts Clean extraction of the debounced EXPLAIN-based validation logic from HighlightedAttributeRow; logic is identical to the pre-existing code, functionally safe
packages/app/src/components/Sources/ExpressionValidationStatus.tsx Thin presenter component wrapping useExpressionValidation; correctly handles valid/invalid/pending states and delegates rendering to the hook
packages/app/src/components/Sources/SourceFieldCandidateHint.tsx Simple hint component that renders canonical and alternate column suggestions; returns null when no candidates, UI looks sensible
packages/app/src/components/Sources/SourceForm.tsx Adds ExpressionFormRow wrapper, pendingSave warning modal, and applyPairingFix; pendingSave.parsedData typed as any reduces type safety for patch operations

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User picks DB + Table] --> B[useColumns fetches schema]
    B --> C{Field has value?}
    C -- No --> D[inferSourceFieldCandidates\ntype + name match]
    D --> E[SourceFieldCandidateHint\nshows canonical + alternates]
    E -- click apply --> F[setValue via RHF]
    C -- Yes --> G[ExpressionValidationStatus\ndebounced EXPLAIN query]
    G --> H{Result?}
    H -- valid --> I[green 'Expression is valid.']
    H -- ClickHouseQueryError --> J[ErrorCollapse with message]
    H -- loading / stale --> K[nothing shown]
    F --> L[User clicks Save]
    L --> M[handleSubmit + Zod parse]
    M --> N[getSourceConfigPairingWarnings\nbody vs implicit-column check]
    N -- warnings present --> O[setPendingSave\nshow Modal]
    N -- no warnings --> P[persist parseResult.data]
    O --> Q{User action}
    Q -- Apply fix --> R[applyPairingFix\nsetValue + patch parsedData]
    R -- last fix resolved --> P
    Q -- Save anyway --> P
    Q -- Cancel --> S[modal closed\nno save]
Loading

Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (1): Last reviewed commit: "feat: add source field suggestions" | Re-trigger Greptile

Comment on lines +163 to +166
// 2. case-insensitive name match, ranked by `canonicalNames` order
const byLowerName = new Map(
compatible.map(c => [c.name.toLowerCase(), c.name]),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Silent key collision in column-name map

compatible.map(c => [c.name.toLowerCase(), c.name]) builds a Map from lowercased name → original name. If two type-compatible columns share the same lowercase name (e.g., a table containing both Body and BODY), the second entry in the array wins and the first column is silently dropped. The dropped column will never appear as a canonical match or an alternate, even though it exists in the table. ClickHouse column names are case-sensitive, so this is a realistic schema configuration. The result is a missed suggestion with no error or warning to the user.

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

Comment on lines +2325 to +2329
const [pendingSave, setPendingSave] = useState<{
warnings: PairingWarning[];
parsedData: any;
persist: (data: any) => void;
}>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Untyped parsedData makes the patch operation un-checkable

parsedData is typed as any, so the spread-patch in applyPairingFix{ ...pendingSave.parsedData, [field]: value } — bypasses TypeScript's schema checks entirely. Today the patched fields (bodyExpression, implicitColumnExpression) are direct top-level strings, so the manual patch is correct. But if a future warning's suggestedFix.field ever maps to a nested path (e.g., from a nested sub-schema), the patch silently creates a top-level key while leaving the nested field untouched, producing a malformed save payload with no compile-time error. Typing parsedData to match the parsed schema (e.g., z.infer<typeof SourceSchema>) would catch this class of mistake.

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!

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

@github-actions

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 199 passed • 3 skipped • 1357s

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

Tests ran across 4 shards in parallel.

View full report →

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