Skip to content

feat(work): add inline run duration bars to Work table rows#1059

Merged
aaight merged 2 commits intodevfrom
feature/work-item-duration-bars
Mar 26, 2026
Merged

feat(work): add inline run duration bars to Work table rows#1059
aaight merged 2 commits intodevfrom
feature/work-item-duration-bars

Conversation

@aaight
Copy link
Copy Markdown
Collaborator

@aaight aaight commented Mar 26, 2026

Summary

  • Inline duration bars — Adds a horizontal stacked bar chart on each Work table row showing run durations broken down by agent type
  • Backend query — New listUnifiedWorkWithDurations repository function fetches unified work items with per-run duration breakdowns and a project-wide average for outlier detection
  • tRPC procedure — New prs.listUnifiedWithDurations endpoint exposes the data with the same auth as listUnified
  • Outlier highlighting — Rows with total duration > 2× project average get a red tint and warning label
  • Agent color legend — Compact flex row above the table shows agent type → color mapping
  • Pure CSS bars — Reuses getAgentColor()/formatDuration() from existing chart infrastructure; no recharts

What changed

File Change
src/db/repositories/prWorkItemsRepository.ts Added listUnifiedWorkWithDurations (two-query: unified items + run durations, compute project avg)
src/api/routers/prs.ts Added listUnifiedWithDurations tRPC procedure
web/src/components/projects/work-item-duration-bar.tsx New component: WorkItemDurationBar + exported buildDurationSegments pure function
web/src/components/projects/project-work-table.tsx Added Duration column, legend, WorkItemRun interface, projectAvgDurationMs prop
web/src/routes/projects/$projectId.work.tsx Switched to listUnifiedWithDurations query, passes avg to table
tests/unit/web/work-item-duration-bar.test.ts New: tests for segment logic, percentages, outlier threshold
tests/unit/api/routers/prs.test.ts Added tests for listUnifiedWithDurations procedure

Test plan

  • Unit tests for buildDurationSegments (empty, zero durations, percentages, colors, repeated agent types, outlier threshold)
  • Unit tests for listUnifiedWithDurations tRPC procedure (auth, mock repo call, empty result)
  • Full unit test suite passes (7188 tests)
  • TypeScript type check passes (backend + frontend)
  • Lint passes (biome)
  • Manual: verify bars on Work tab, tooltips, outlier red highlight, legend visibility on wider screens

Key decisions

  • Two-query approach in repository keeps SQL clean and backward-compatible with existing listUnified consumers
  • Duration hidden on mobile (hidden sm:table-cell) to avoid overflow on narrow screens; legend also hidden on mobile
  • colSpan bumped from 4 to 5 to account for the new Duration column in the empty-state row

https://trello.com/c/yySaJYFa/568-lets-put-run-durations-horizontal-stacked-bar-chart-directly-on-rows-in-work-section-of-the-project-so-at-a-glance-we-see-any-wo

🤖 Generated with Claude Code

🕵️ claude-code · claude-sonnet-4-6 · run details

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 26, 2026

Codecov Report

❌ Patch coverage is 11.53846% with 46 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/db/repositories/prWorkItemsRepository.ts 0.00% 46 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Collaborator

@nhopeatall nhopeatall left a comment

Choose a reason for hiding this comment

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

Summary

Clean, well-structured feature. The two-query approach, pure segment function, and reuse of existing getAgentColor/formatDuration/agentTypeLabel infrastructure are all solid choices. One CSS bug to fix.

Code Issues

Should Fix

  • web/src/components/projects/project-work-table.tsx:252 — CSS class conflict between flex and hidden in the legend container. Both flex (display: flex) and hidden (display: none) are applied at the base breakpoint, creating non-deterministic display behavior depending on Tailwind's CSS generation order. The legend may be visible on mobile instead of hidden.

    Fix: remove the unprefixed flex so only hidden applies at the base level, and sm:flex overrides it at the sm breakpoint:

    <div className="hidden sm:flex flex-wrap gap-3" style={{ fontSize: 12 }}>

Everything else looks good:

  • Backend listUnifiedWorkWithDurations cleanly reuses listUnifiedWorkForProject and correctly filters to completed/failed/timed_out runs with non-null durations
  • buildDurationSegments is properly extracted as a pure testable function with good test coverage
  • Auth/access control mirrors the existing listUnified pattern
  • colSpan correctly bumped from 4→5 for the new Duration column
  • Duration column and legend properly hidden on mobile (hidden sm:table-cell)
  • Segment labeling logic (#1, #2 for repeated agent types) matches the existing WorkItemDurationChart pattern
  • The as number cast on durationMs is safe since the query filters with isNotNull
  • Tests cover segment building, outlier threshold logic, and tRPC procedure auth

🕵️ claude-code · claude-opus-4-6 · run details

Remove unprefixed `flex` that conflicted with `hidden` at the base
breakpoint. Now `hidden` applies at mobile and `sm:flex` overrides at
the sm breakpoint, preventing non-deterministic display behaviour.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@nhopeatall nhopeatall left a comment

Choose a reason for hiding this comment

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

Summary

LGTM — Well-structured feature addition that follows existing codebase patterns consistently.

The two-query approach in listUnifiedWorkWithDurations cleanly extends the existing listUnifiedWorkForProject without modifying it, maintaining backward compatibility. The pure buildDurationSegments function is properly separated for testability, and the component correctly reuses getAgentColor/formatDuration/agentTypeLabel from existing chart infrastructure rather than introducing duplicates.

Auth, responsive behavior (hidden on mobile), colSpan updates, and the outlier detection logic are all correct. Test coverage is solid across the pure segment logic, tRPC procedure (auth, happy path, empty state), and outlier threshold boundaries.

🕵️ claude-code · claude-opus-4-6 · run details

@aaight aaight merged commit 487ab38 into dev Mar 26, 2026
8 of 9 checks passed
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.

2 participants