Skip to content

feat(web-ui): task status tooltips and valid action guidance (#481)#524

Merged
frankbria merged 2 commits into
mainfrom
feat/481-task-status-tooltips
Apr 3, 2026
Merged

feat(web-ui): task status tooltips and valid action guidance (#481)#524
frankbria merged 2 commits into
mainfrom
feat/481-task-status-tooltips

Conversation

@frankbria

@frankbria frankbria commented Apr 3, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #481

  • Status badge tooltips: Every TaskCard and TaskDetailModal status badge now shows a Radix UI tooltip on hover with the status meaning, how the task entered this state, and what to do next
  • Guidance panel: TaskDetailModal shows a "What's next?" info panel for DONE, BLOCKED, and MERGED statuses (which have no action buttons) — telling the user the next step to take
  • Last changed timestamp: TaskDetailModal shows "Last changed: [date]" in metadata when updated_at is available
  • Shared constants: taskStatusInfo.ts defines STATUS_INFO for all 7 statuses — single source of truth used by both components

Acceptance Criteria

  • Tooltip on every status badge with meaning and context
  • TaskDetailModal shows only valid next transitions (existing buttons already correct; guidance panel added for action-less states)
  • Invalid transition buttons are disabled with explanation (action-less states now have a guidance note)
  • Status transition reasons displayed when status was last changed (updated_at shown as "Last changed")
  • Consistent tooltip across all board columns (same STATUS_INFO used everywhere)

Test plan

  • 20 new unit tests in src/__tests__/components/tasks/
    • TaskCard.test.tsx: verifies tooltip content for all 7 statuses
    • TaskDetailModal.test.tsx: verifies tooltip content, guidance panel presence (DONE/BLOCKED/MERGED), action buttons (BACKLOG/READY), and last-changed display
  • No pre-existing tests broken (6 pre-existing failures unchanged)
  • Lint clean on all modified files

Summary by CodeRabbit

  • New Features

    • Status badges now show tooltips with status meanings and next steps.
    • Task detail modal displays a "Last changed" date when available and includes a "What's next?" guidance panel for Done, Blocked, and Merged tasks.
  • Tests

    • Added tests covering status badge/tooltips and task-detail status-related UI behavior (tooltips, guidance panel, and timestamp visibility).

- Add STATUS_INFO constants in taskStatusInfo.ts with meaning, entry
  condition, and next-steps for all 7 task statuses
- Wrap status badge in TaskCard with Tooltip showing meaning + next steps
- Wrap status badge in TaskDetailModal with Tooltip showing meaning +
  entry condition
- Add 'What's next?' guidance panel in TaskDetailModal footer for DONE,
  BLOCKED, and MERGED statuses (which have no action buttons)
- Show 'Last changed' date in TaskDetailModal metadata when updated_at is set
- Add 20 unit tests covering tooltip content and guidance panel rendering
@coderabbitai

coderabbitai Bot commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

A new STATUS_INFO module was added and TaskCard and TaskDetailModal were updated to show Radix tooltips around status badges using that data. TaskDetailModal now conditionally shows "Last changed" and "What's next?" guidance panels. Two new test suites validate tooltip and guidance UI behavior.

Changes

Cohort / File(s) Summary
Status Information Module
web-ui/src/lib/taskStatusInfo.ts
Adds StatusInfo interface and STATUS_INFO: Record<TaskStatus, StatusInfo> mapping each status (BACKLOG, READY, IN_PROGRESS, DONE, BLOCKED, FAILED, MERGED) to meaning, enteredWhen, and nextSteps.
Task UI Components
web-ui/src/components/tasks/TaskCard.tsx, web-ui/src/components/tasks/TaskDetailModal.tsx
Wraps status Badge in Tooltip/TooltipTrigger, sources tooltip content from STATUS_INFO, centralizes TooltipProvider, adds conditional "Last changed" display in modal, and renders a data-testid="status-next-step" guidance panel for DONE/BLOCKED/MERGED/FAILED (and adjusts FAILED content to use STATUS_INFO).
Tests
web-ui/src/__tests__/components/tasks/TaskCard.test.tsx, web-ui/src/__tests__/components/tasks/TaskDetailModal.test.tsx
Adds tests that mock Radix tooltips (and other deps in modal tests), assert tooltip text matches STATUS_INFO[status].meaning/nextSteps, validate presence/absence of guidance panel and "last changed" timestamp for various statuses.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant UI as TaskDetailModal
  participant API as tasksApi.getOne
  participant Tooltip as TooltipProvider/Tooltip

  User->>UI: open modal (taskId)
  UI->>API: fetch task (taskId)
  API-->>UI: return task data
  UI->>Tooltip: render status Badge wrapped in Tooltip
  Tooltip-->>User: show tooltip (meaning + nextSteps) on hover
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I nibble on statuses, soft and bright,
Badges now whisper in hover-light,
Meaning and next steps hop into view,
Guiding each task like morning dew —
Hooray for tooltips, fresh and true! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding task status tooltips and valid action guidance, which are the primary features introduced in this PR.
Linked Issues check ✅ Passed The PR implementation addresses all coding requirements from issue #481: tooltip content for all statuses via STATUS_INFO, guidance panels for DONE/BLOCKED/MERGED, last-changed metadata display, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are scoped to issue #481 requirements: TaskCard and TaskDetailModal tooltip implementations, STATUS_INFO constant definition, and supporting tests. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/481-task-status-tooltips

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude

claude Bot commented Apr 3, 2026

Copy link
Copy Markdown

test comment placeholder

@claude

claude Bot commented Apr 3, 2026

Copy link
Copy Markdown

PR Review: feat(web-ui): task status tooltips and valid action guidance

Good UX addition overall. The single-source-of-truth approach with taskStatusInfo.ts is well-designed, and the test coverage with Radix UI mocking is solid. A few issues worth addressing:


Issue 1 - Performance: Multiple TooltipProvider instances

TaskCard.tsx wraps each badge in its own <TooltipProvider>. For a board with 20+ tasks, this creates 20+ provider instances each managing its own tooltip delay state. The Radix UI docs recommend a single <TooltipProvider> at the app/layout level (or at least at the board level), not per-card.

Fix: Move <TooltipProvider> up to TaskBoard or the app layout, and only use <Tooltip><TooltipTrigger><TooltipContent> inside the cards.


Issue 2 - Inconsistent tooltip content between TaskCard and TaskDetailModal

  • TaskCard tooltip shows: meaning + nextSteps
  • TaskDetailModal tooltip shows: meaning + enteredWhen

Same status badge, different tooltip content depending on where you click. Users who open the modal expecting the same tooltip context they saw on the card will get different information. Decide on one consistent set of fields (probably meaning + nextSteps is more actionable), or show all three fields in both places.


Issue 3 - Guidance panel missing for FAILED status

The guidance panel (data-testid="status-next-step") is shown for DONE, BLOCKED, and MERGED but not FAILED. A failed task is arguably the most confusing state to be in -- users most need guidance there (cf work diagnose <task-id> is the next step). This seems like an accidental omission given that STATUS_INFO already has nextSteps defined for FAILED.

Fix: Add FAILED to the condition that renders the guidance panel:
['DONE', 'BLOCKED', 'FAILED', 'MERGED'].includes(task.status)


Issue 4 - Minor: toLocaleDateString locale handling

Verify whether the "Last changed" date formatting passes an explicit locale (e.g. 'en-US') or relies on the browser default. Without an explicit locale, output varies by user's browser settings. If the rest of the UI targets English-speaking users for now, hardcoding 'en-US' is fine -- just worth a comment if i18n is on the roadmap.


What's working well

  • taskStatusInfo.ts as a shared constant is the right pattern -- no duplication between components
  • Radix UI tooltip mocking strategy in tests is correct for jsdom environments
  • 20 new tests with good coverage of status-specific behavior
  • The guidance panel UX pattern (info icon + next steps) is clean and non-intrusive
  • updated_at display is a useful addition to the modal metadata

Summary: The TooltipProvider placement (issue 1) and the tooltip content inconsistency (issue 2) are the two items I'd want resolved before merge. The FAILED guidance panel gap (issue 3) is a quick fix that would make the feature complete against its own intent.

- Consolidate to single TooltipProvider per TaskCard (removes per-tooltip overhead)
- Align modal tooltip content to match card (meaning + nextSteps in both)
- Add data-testid="status-next-step" to FAILED alert panel for consistent testability
- Use 'en-US' locale in toLocaleDateString for deterministic date formatting
- Add 2 tests: FAILED guidance panel + READY has no guidance panel
@claude

claude Bot commented Apr 3, 2026

Copy link
Copy Markdown

Follow-up Review

All four issues from my previous review have been addressed:

Fixed: TooltipProvider placement — Single <TooltipProvider> now wraps all of CardContent in TaskCard.tsx, consolidating both the status badge tooltip and the dependency tooltip under one provider. This is a clear improvement over per-tooltip providers.

Fixed: Tooltip content consistency — Both TaskCard and TaskDetailModal now show meaning + nextSteps. Previously the modal was showing enteredWhen instead of nextSteps.

Fixed: FAILED guidance panel — The FAILED alert panel now carries data-testid="status-next-step" and uses STATUS_INFO.FAILED.nextSteps for its description. The new test 'shows next-step guidance for FAILED status via the alert panel' covers this correctly.

Fixed: Locale handlingtoLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) is explicit and deterministic.


One minor observation: TaskDetailModal scopes TooltipProvider only around the single status badge tooltip, while TaskCard scopes it across all card content. Both patterns are valid here (the modal only has one tooltip), so this isn't a problem — just noting the difference is intentional rather than an oversight.

This is ready to merge. The implementation is clean, test coverage is thorough, and the feature is complete across all 7 statuses.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
web-ui/src/components/tasks/TaskDetailModal.tsx (1)

158-170: Tooltip implementation is correct; consider addressing the type assertion.

The tooltip correctly displays meaning and nextSteps from STATUS_INFO. Since STATUS_INFO is typed as Record<TaskStatus, StatusInfo> and covers all seven status values, the access pattern is type-safe.

The as never type assertion on line 161 for the Badge variant is a workaround for variant typing constraints. This pattern appears elsewhere in the codebase (e.g., dependency badges at line 226), so it's consistent, but it circumvents type checking.

💡 Optional: Consider typing STATUS_BADGE_VARIANT more precisely

If the Badge component's variant prop type were extended or if STATUS_BADGE_VARIANT were typed to match the expected variant union, the as never assertion could be eliminated for better type safety. This is a broader refactor that could be addressed in a follow-up.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/components/tasks/TaskDetailModal.tsx` around lines 158 - 170, The
Badge variant is being forced with "as never"; instead derive the Badge variant
type and type STATUS_BADGE_VARIANT to that so the cast can be removed: get the
variant prop type from the Badge component (e.g., React.ComponentProps<typeof
Badge>['variant'] or the BadgeProps union) and update the declaration of
STATUS_BADGE_VARIANT to be Record<TaskStatus, ThatVariantType>, then update the
usage in TaskDetailModal (the Badge inside Tooltip/TooltipTrigger) to pass
STATUS_BADGE_VARIANT[task.status] directly without "as never"; this will remove
the unsafe assertion while keeping the existing mapping and labels
(STATUS_LABEL, STATUS_INFO) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@web-ui/src/components/tasks/TaskDetailModal.tsx`:
- Around line 158-170: The Badge variant is being forced with "as never";
instead derive the Badge variant type and type STATUS_BADGE_VARIANT to that so
the cast can be removed: get the variant prop type from the Badge component
(e.g., React.ComponentProps<typeof Badge>['variant'] or the BadgeProps union)
and update the declaration of STATUS_BADGE_VARIANT to be Record<TaskStatus,
ThatVariantType>, then update the usage in TaskDetailModal (the Badge inside
Tooltip/TooltipTrigger) to pass STATUS_BADGE_VARIANT[task.status] directly
without "as never"; this will remove the unsafe assertion while keeping the
existing mapping and labels (STATUS_LABEL, STATUS_INFO) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 18df47dc-4b9e-4228-8123-ecb80250b25b

📥 Commits

Reviewing files that changed from the base of the PR and between f6d447d and 588459d.

📒 Files selected for processing (3)
  • web-ui/src/__tests__/components/tasks/TaskDetailModal.test.tsx
  • web-ui/src/components/tasks/TaskCard.tsx
  • web-ui/src/components/tasks/TaskDetailModal.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • web-ui/src/components/tasks/TaskCard.tsx
  • web-ui/src/tests/components/tasks/TaskDetailModal.test.tsx

@frankbria frankbria merged commit 511f505 into main Apr 3, 2026
11 checks passed
@frankbria frankbria deleted the feat/481-task-status-tooltips branch April 3, 2026 17:34
frankbria pushed a commit that referenced this pull request Apr 4, 2026
The frontend-tests CI job was disabled during the Phase 2 CLI-first
refactor and never re-enabled when Phase 3 web UI became active. This
allowed component changes to silently break tests across multiple PRs.

CI change:
- Uncomment frontend-tests job in test.yml (runs npm run test:coverage)
- Add frontend-tests to test-summary needs and failure check
- Exclude e2e/ (Playwright) and test-helpers.ts from Jest via jest.config.js

Test fixes (component updated but test not):
- QuickActions: rewrite tests to exercise all 5 pipeline states
  (broke in #493 — context-aware refactor changed button labels)
- TaskCard: update dependency title to regex match Radix Tooltip text
  (broke in #524 — tooltip replaced title attribute)
- TaskDetailModal: update dep count assertion to "Dependencies (N)"
  (broke in #524 — label changed from "2 dependencies")
- BlockerCard: update success text to "answer recorded"
  (broke in #531 — success message reworded)
- TaskBoardView: update empty state to "No tasks yet" (single block)
  (broke in #499 — empty state redesigned with CTA)
frankbria added a commit that referenced this pull request Apr 4, 2026
* feat(core): WorktreeRegistry, orphan cleanup, and get_base_branch (#533)

Add atomic worktree registration and stale cleanup to complete the
worktree-per-task isolation feature for parallel batch execution.

Changes:
- codeframe/core/worktrees.py: add WorktreeRegistry (register/unregister/
  list_stale/cleanup_stale), get_base_branch(), list_worktrees()
- codeframe/core/sandbox/context.py: use get_base_branch() instead of
  hardcoded "main" when creating worktrees
- codeframe/core/conductor.py: call WorktreeRegistry.cleanup_stale() at
  _execute_parallel() start when isolation == worktree
- codeframe/cli/env_commands.py: report stale worktrees in cf env doctor
- tests/core/test_worktrees.py: 13 new tests for registry, get_base_branch,
  list_worktrees, and conductor orphan cleanup integration

Closes #533

* fix(web-ui): re-enable frontend CI and fix 5 stale tests

The frontend-tests CI job was disabled during the Phase 2 CLI-first
refactor and never re-enabled when Phase 3 web UI became active. This
allowed component changes to silently break tests across multiple PRs.

CI change:
- Uncomment frontend-tests job in test.yml (runs npm run test:coverage)
- Add frontend-tests to test-summary needs and failure check
- Exclude e2e/ (Playwright) and test-helpers.ts from Jest via jest.config.js

Test fixes (component updated but test not):
- QuickActions: rewrite tests to exercise all 5 pipeline states
  (broke in #493 — context-aware refactor changed button labels)
- TaskCard: update dependency title to regex match Radix Tooltip text
  (broke in #524 — tooltip replaced title attribute)
- TaskDetailModal: update dep count assertion to "Dependencies (N)"
  (broke in #524 — label changed from "2 dependencies")
- BlockerCard: update success text to "answer recorded"
  (broke in #531 — success message reworded)
- TaskBoardView: update empty state to "No tasks yet" (single block)
  (broke in #499 — empty state redesigned with CTA)

* fix(core): address code review feedback on WorktreeRegistry (#537)

- wire register()/unregister() into create_execution_context() — registry
  was defined but never populated, making cleanup_stale() a no-op
- fix list_stale() to catch PermissionError separately (process alive,
  different owner is not stale); previously OSError caught both cases
- guard list_worktrees() against non-list JSON (returns [] for dicts etc.)
- fix get_base_branch() to return "main" in detached HEAD state
  (git returns literal "HEAD" — now treated as fallback)
- fix conductor.py string comparison to use IsolationLevel.WORKTREE.value
- replace misleading conductor test with two focused tests that patch
  WorktreeRegistry.cleanup_stale directly and assert call/no-call
- add tests for detached HEAD fallback and non-list JSON guard

---------

Co-authored-by: Test User <test@example.com>
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.

UX: Add task status transition tooltips and valid action guidance

1 participant