Skip to content

Comments

feat(dashboard): traces timeline ui#4056

Closed
jog1t wants to merge 1 commit intomainfrom
01-28-feat_dashboard_traces_timeline_ui
Closed

feat(dashboard): traces timeline ui#4056
jog1t wants to merge 1 commit intomainfrom
01-28-feat_dashboard_traces_timeline_ui

Conversation

@jog1t
Copy link
Contributor

@jog1t jog1t commented Jan 28, 2026

No description provided.

Copy link
Contributor Author

jog1t commented Jan 28, 2026

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


How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

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

@claude
Copy link

claude bot commented Jan 28, 2026

PR Review: Traces Timeline UI

This PR adds a timeline visualization view for traces in the dashboard, complementing the existing list view. The implementation includes gap detection/collapsing, zoom/pan controls, and interactive span selection.


🟢 Strengths

Code Organization

  • Clean separation of concerns with dedicated files for sidebar, timeline, types, and utilities
  • Good use of React hooks (useMemo, useCallback, useRef) for performance optimization
  • Comprehensive gap detection algorithm that handles edge cases

UX Features

  • Toggle between list and timeline views provides flexibility
  • Gap collapsing with visual indicators improves readability for sparse traces
  • Zoom/pan controls enhance navigation

🔴 Critical Issues

1. Broken Timeline View Integration (actor-traces.tsx:149-167)

The timeline view is integrated but with placeholder implementations that throw errors:

onSelectSpan={(spanId: string | null): void => {
    throw new Error("Function not implemented.");
}}
onSelectEvent={(spanId: string, eventIndex: number): void => {
    throw new Error("Function not implemented.");
}}

Impact: The timeline view will crash when users click on any span or event.

Fix: Implement proper state management.

2. Type Safety Issues (types.ts)

Multiple any types that should be specific:

export type SpanNode = {
    endTimeUnixNano: any;  // Should be: bigint | null
    startTimeUnixNano: any; // Should be: bigint
    name: ReactNode;  // Should probably be: string
    spanId: string | null;  // Can spanId actually be null?
}

Impact: Runtime type errors, loss of IDE autocomplete, harder debugging.

3. Inconsistent Property Usage

The SpanNode type has duplicate/inconsistent timestamp properties:

  • startNs vs startTimeUnixNano
  • endNs vs endTimeUnixNano
  • timeUnixNano (unclear purpose)

Used inconsistently throughout:

  • span-sidebar.tsx:92 uses startNs/endNs
  • traces-timeline.tsx:467-470 uses startTimeUnixNano/endTimeUnixNano
  • utils.ts:32-59 assumes properties like parentSpanId that don't exist in the type

Impact: Potential runtime errors, confusing codebase.

4. Missing Null Checks (utils.ts:38)

If spanId is null per the type definition, using it as a Map key will cause bugs.

5. Incorrect Duration Check (utils.ts:5)

export function isSpanInProgress(span: SpanNode): boolean {
    return span.endNs === undefined;
}

Per the type definition, endNs is bigint | null, not bigint | undefined. Should check for both.


🟡 Major Issues

6. BigInt Arithmetic (traces-timeline.tsx:467-470)

If timestamps are bigints, you need to convert to Number before division.

7. Performance: Multiple Date.now() Calls

Date.now() is called repeatedly in tight loops (lines 70, 290, 331). Store once using useMemo.

8. Unused SpanSidebar Component

span-sidebar.tsx is imported but never used. The timeline view renders its own sidebar.

9. Missing Event Handlers

Timeline view references span.events but no details panel implementation exists unlike the old inline code that was removed.

10. Accessibility Issues

  • No keyboard navigation support
  • No ARIA labels on interactive elements
  • Button elements missing proper focus indicators
  • No screen reader support for timeline visualization

🟢 Minor Issues

11. Magic Numbers

Constants lack explanation:

const GAP_THRESHOLD_MS = 50; // Why 50ms?
const GAP_FIXED_WIDTH_PX = 48; // Why 48px?
const MIN_ACTIVE_SEGMENT_WIDTH_PX = 8; // Why 8px?

12. Missing Error Handling

No error boundaries or try-catch blocks. If span data is malformed, the entire component will crash.

13. Sorting Assumption (utils.ts:52-53)

If timestamps are bigints, subtraction won't work for sorting. Use comparison operators instead.


📋 Recommendations

High Priority

  1. Fix the broken onclick handlers - this breaks the feature entirely
  2. Fix type definitions - remove any types and make properties consistent
  3. Add null checks for spanId and other nullable fields
  4. Reconcile property naming - use either *Ns or *TimeUnixNano consistently

Medium Priority

  1. Add error boundaries around timeline components
  2. Implement proper event selection state management
  3. Add comprehensive tests for gap detection algorithm
  4. Optimize Date.now() calls
  5. Add keyboard navigation support

Low Priority

  1. Add JSDoc comments for complex algorithms
  2. Extract magic numbers to named constants with explanations
  3. Consider adding loading states

🎯 Test Coverage

Missing tests for:

  • Gap detection edge cases
  • Time mapper with various segment configurations
  • Zoom/pan boundary conditions
  • Null/undefined span data
  • BigInt arithmetic edge cases

Summary

This is a solid foundation for a timeline visualization feature with good architecture and performance considerations. However, it cannot be merged in its current state due to the broken click handlers and type safety issues. The core algorithms look sound, but need type fixes and null handling before they're production-ready.

Recommendation: Request changes - Fix critical issues #1-5 before merging.

@jog1t jog1t force-pushed the 01-27-refactor_rivetkit_make_traces_server_only_lib branch from 63d40ba to d77a4fc Compare January 30, 2026 21:29
@jog1t jog1t force-pushed the 01-28-feat_dashboard_traces_timeline_ui branch from 2a858f0 to d559f28 Compare January 30, 2026 21:29
@jog1t jog1t changed the base branch from 01-27-refactor_rivetkit_make_traces_server_only_lib to graphite-base/4056 February 4, 2026 22:11
@jog1t jog1t force-pushed the 01-28-feat_dashboard_traces_timeline_ui branch from d559f28 to dff6754 Compare February 4, 2026 22:11
@jog1t jog1t force-pushed the graphite-base/4056 branch from d77a4fc to 828baf3 Compare February 4, 2026 22:11
@jog1t jog1t changed the base branch from graphite-base/4056 to 02-04-fix_traces_separate_entrypoints February 4, 2026 22:11
@jog1t jog1t mentioned this pull request Feb 4, 2026
11 tasks
@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 4, 2026

More templates

@rivetkit/cloudflare-workers

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/cloudflare-workers@4056

@rivetkit/db

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/db@4056

@rivetkit/framework-base

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/framework-base@4056

@rivetkit/next-js

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/next-js@4056

@rivetkit/react

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/react@4056

rivetkit

pnpm add https://pkg.pr.new/rivet-dev/rivet/rivetkit@4056

@rivetkit/sql-loader

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sql-loader@4056

@rivetkit/traces

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/traces@4056

@rivetkit/workflow-engine

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/workflow-engine@4056

@rivetkit/virtual-websocket

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/virtual-websocket@4056

@rivetkit/engine-runner

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner@4056

@rivetkit/engine-runner-protocol

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner-protocol@4056

commit: c2e901e

@jog1t jog1t force-pushed the 02-04-fix_traces_separate_entrypoints branch from 828baf3 to a316801 Compare February 4, 2026 22:17
@jog1t jog1t force-pushed the 01-28-feat_dashboard_traces_timeline_ui branch 2 times, most recently from e4ac15a to a18121c Compare February 4, 2026 22:57
@jog1t jog1t force-pushed the 02-04-fix_traces_separate_entrypoints branch from a316801 to 3d621b9 Compare February 4, 2026 22:57
@graphite-app graphite-app bot force-pushed the 02-04-fix_traces_separate_entrypoints branch 2 times, most recently from 940cd33 to 9c70402 Compare February 5, 2026 00:40
@graphite-app graphite-app bot force-pushed the 01-28-feat_dashboard_traces_timeline_ui branch from a18121c to f49653b Compare February 5, 2026 00:40
@NathanFlurry NathanFlurry marked this pull request as ready for review February 5, 2026 08:35
@graphite-app
Copy link
Contributor

graphite-app bot commented Feb 5, 2026

Graphite Automations

"Test" took an action on this PR • (02/05/26)

1 assignee was added to this PR based on Kacper Wojciechowski's automation.

@jog1t jog1t force-pushed the 02-04-fix_traces_separate_entrypoints branch from 9c70402 to 0ad0c4d Compare February 5, 2026 20:34
@jog1t jog1t mentioned this pull request Feb 5, 2026
11 tasks
Comment on lines 2 to 12
import type {
OtlpAnyValue,
OtlpExportTraceServiceRequestJson,
OtlpKeyValue,
OtlpSpan,
OtlpSpanEvent,
} from "@rivetkit/traces";
import { readRangeWireToOtlp } from "@rivetkit/traces/otlp";
import { useQuery } from "@tanstack/react-query";
import { format } from "date-fns";
import {
useMemo,
useState,
type ReactElement,
} from "react";
import { type ReactElement, useMemo, useState } from "react";
Copy link
Contributor

Choose a reason for hiding this comment

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

Imports are not sorted correctly according to Biome's rules. React imports should come first, followed by third-party imports in alphabetical order, then local imports. Consider reorganizing these imports.

Spotted by Graphite Agent (based on CI logs)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@jog1t jog1t force-pushed the 02-04-fix_traces_separate_entrypoints branch from 0ad0c4d to df3efc4 Compare February 5, 2026 21:48
Comment on lines +169 to +188
<button
key={`${span.spanId}-event-${idx}`}
onClick={() =>
onSelectEvent(span.spanId, idx)
}
className={cn(
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-left text-sm transition-colors",
isEventSelected
? "bg-accent text-accent-foreground"
: "hover:bg-accent/50 text-muted-foreground",
)}
>
<Icon
icon={faCircle}
className="size-2 shrink-0 fill-current"
/>
<span className="truncate">
{event.name}
</span>
</button>
Copy link
Contributor

Choose a reason for hiding this comment

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

This button element is missing a type attribute. Add type="button" to prevent potential form submission issues.

Spotted by Graphite Agent (based on CI logs)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +634 to +665
{ticks.map((tick, i) => (
<div
key={i}
className={cn(
"absolute top-0 h-full flex flex-col items-center",
tick.isGapLabel && "z-10",
)}
style={{ left: tick.position }}
>
{tick.isGapLabel ? (
<>
<div
className="h-full w-px bg-muted-foreground/30"
style={{
backgroundImage:
"repeating-linear-gradient(to bottom, transparent, transparent 2px, hsl(var(--muted-foreground) / 0.3) 2px, hsl(var(--muted-foreground) / 0.3) 4px)",
}}
/>
<span className="absolute top-1/2 -translate-y-1/2 text-[10px] text-muted-foreground bg-background/95 px-1.5 py-0.5 rounded border border-border whitespace-nowrap">
{tick.label} skipped
</span>
</>
) : (
<>
<div className="h-2 w-px bg-border" />
<span className="text-[10px] text-muted-foreground mt-0.5">
{tick.label}
</span>
</>
)}
</div>
))}
Copy link
Contributor

Choose a reason for hiding this comment

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

Using array index 'i' as a React key can cause rendering issues when items are reordered. Use a unique identifier from the tick object or create a compound key like tick.position-${i}.

Spotted by Graphite Agent (based on CI logs)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +553 to +576
<Button
variant="ghost"
size="icon"
className="size-8"
onClick={handleZoomOut}
>
<Icon icon={faMagnifyingGlassMinus} className="size-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="size-8"
onClick={handleZoomReset}
>
<Icon icon={faMinusLarge} className="size-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="size-8"
onClick={handleZoomIn}
>
<Icon icon={faMagnifyingGlassPlus} className="size-4" />
</Button>
Copy link
Contributor

Choose a reason for hiding this comment

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

These Button components may need explicit type="button" attributes if they're not already provided by the Button component implementation.

Spotted by Graphite Agent (based on CI logs)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

Comment on lines +117 to +138
<button
onClick={() => onSelectSpan(span.spanId)}
className={cn(
"flex-1 flex items-center gap-2 px-2 py-1.5 rounded text-left text-sm transition-colors min-w-0",
isSelected
? "bg-accent text-accent-foreground"
: "hover:bg-accent/50",
)}
>
{inProgress && (
<Icon
icon={faSpinnerThird}
className="size-3 shrink-0 animate-spin"
/>
)}
<span className="truncate flex-1">{span.span.name}</span>
{durationMs !== null && (
<span className="text-xs text-muted-foreground shrink-0">
{formatDuration(Number(durationMs))}
</span>
)}
</button>
Copy link
Contributor

Choose a reason for hiding this comment

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

This button element is missing a type attribute. Add type="button" to prevent potential form submission issues.

Spotted by Graphite Agent (based on CI logs)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@graphite-app graphite-app bot changed the base branch from 02-04-fix_traces_separate_entrypoints to graphite-base/4056 February 5, 2026 22:07
@NathanFlurry NathanFlurry force-pushed the 01-28-feat_dashboard_traces_timeline_ui branch from f49653b to c2e901e Compare February 6, 2026 07:58
@NathanFlurry NathanFlurry changed the base branch from graphite-base/4056 to main February 6, 2026 07:58
@graphite-app
Copy link
Contributor

graphite-app bot commented Feb 6, 2026

Merge activity

  • Feb 6, 8:22 AM UTC: NathanFlurry added this pull request to the Graphite merge queue.
  • Feb 6, 8:23 AM UTC: CI is running for this pull request on a draft pull request (#4146) due to your merge queue CI optimization settings.
  • Feb 6, 8:24 AM UTC: Merged by the Graphite merge queue via draft PR: #4146.

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