Skip to content

feat: Task Board page (rich Kanban, filtering, dependency viz)#871

Merged
Aureliolo merged 7 commits intomainfrom
feat/task-board-page
Mar 27, 2026
Merged

feat: Task Board page (rich Kanban, filtering, dependency viz)#871
Aureliolo merged 7 commits intomainfrom
feat/task-board-page

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Rich Kanban board with drag-and-drop between status columns using @dnd-kit, optimistic updates with rollback, and spring physics settle animations
  • Multi-criteria filter bar (status, priority, assignee, task type, text search) with URL search param persistence and active filter pills
  • Slide-in task detail panel with inline editing (title, description, priority), status transition buttons, cancel/delete with confirmation dialogs, and Escape key close
  • Task creation dialog with form validation, required/optional fields, and error handling
  • List view toggle (Kanban vs table) with sortable columns and responsive layout
  • WebSocket real-time updates via tasks channel with flash animations on card updates, polling fallback at 30s
  • New shared UI components: TaskStatusIndicator (9 task statuses) and PriorityBadge (4 priority levels)
  • Zustand store with full CRUD, WS event handling (with payload validation), and optimistic transition helpers
  • 9 task statuses grouped into 6 visible Kanban columns + toggleable Terminal column

Test plan

  • 790 tests passing across 70 files (25 new tests added during review)
  • Property-based tests (fast-check) for filterTasks and groupTasksByColumn invariants
  • Unit tests for: utilities (status colors, labels, column grouping, filtering, transitions), Zustand store (CRUD, WS events, optimistic updates), shared components, all page sub-components, data hook, main pages
  • TypeScript type-check passes (0 errors)
  • ESLint passes (0 errors, 1 pre-existing warning)
  • Production build succeeds
  • Storybook stories for all 7 page sub-components + shared UI component
  • Pre-reviewed by 4 agents (docs-consistency, frontend-reviewer, test-quality, issue-resolution-verifier) -- 18 findings addressed

Closes #780

Aureliolo and others added 2 commits March 27, 2026 11:36
Implement the Task Board page (#780) replacing the placeholder with a
full Kanban board featuring drag-and-drop, multi-criteria filtering,
task detail side panel, task creation dialog, and list view toggle.

Key components:
- TaskCard: rich cards with status, priority, assignee, dependencies
- TaskColumn: droppable Kanban columns with @dnd-kit integration
- TaskFilterBar: status/priority/assignee/type/search with URL sync
- TaskDetailPanel: slide-in panel with inline editing and transitions
- TaskCreateDialog: form with validation for new task creation
- TaskListView: table alternative with sortable columns
- TaskStatusIndicator/PriorityBadge: shared UI components

Infrastructure:
- Zustand store with API actions, WS handler, optimistic updates
- useTaskBoardData hook (API + WebSocket + polling)
- Task utilities (status colors, column grouping, filtering, transitions)
- 9 statuses grouped into 6+1 Kanban columns (terminal toggle)

Tests: 60+ new tests including property-based tests with fast-check
Stories: Storybook stories for all page sub-components

Closes #780

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 4 agents (docs-consistency, frontend-reviewer,
test-quality, issue-resolution-verifier), 18 findings addressed:

- Validate WS payload fields before unsafe cast in task store
- Add try/catch error handling to handleDelete/handleCancel in both
  TaskDetailPage and TaskDetailPanel
- Add Escape key handler to TaskDetailPanel for keyboard accessibility
- Fix error state unreachable in TaskDetailPage (check error before null)
- Extract getTaskTypeLabel utility, replacing fragile replace('_',' ')
- Remove unused serverFilters param from useTaskBoardData hook
- Update CLAUDE.md: add TaskStatusIndicator/PriorityBadge to component
  table, @dnd-kit to deps, tasks/ to package structure
- Add missing test files: TaskColumn, TaskDetailPage
- Add tests: WS event with non-object payload, transition button click,
  Escape key close, filterTasks with empty description, cost/deadline
  rendering, dialog close on success

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 27, 2026

Walkthrough

Replaces placeholders with a complete Task Board feature: adds TaskBoardPage (board/list views) and TaskDetailPage, a fully implemented useTasksStore with API actions, WebSocket handling, optimistic transitions, and a new useTaskBoardData hook. Introduces task utilities (status/priority mappings, kanban columns, filtering), UI components (TaskColumn, TaskCard, TaskDetailPanel, TaskCreateDialog, TaskFilterBar, TaskListView, TaskDependencyGraph, TaskBoardSkeleton, TaskStatusIndicator, PriorityBadge), drag-and-drop via @dnd-kit, many Storybook stories, extensive unit/property tests, test factories, and documentation updates.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.00% which is insufficient. The required threshold is 40.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly describes the main implementation: a rich Kanban-based Task Board page with drag-and-drop, filtering, and dependency visualization—all key deliverables from issue #780.
Description check ✅ Passed The PR description comprehensively details the implementation including Kanban features, filtering, panels, dialogs, WebSocket updates, components, store changes, and test coverage—all directly related to the changeset.
Linked Issues check ✅ Passed The implementation directly addresses all primary objectives from issue #780: rich Kanban cards, drag-drop with optimistic updates, multi-criteria filtering with URL persistence, task detail panel with inline editing, creation dialog, dependency visualization, list view toggle, WebSocket real-time updates, new UI components, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are scoped to the Task Board page implementation. Minor additions (ConfirmDialog children prop, documentation updates, test factories) are supporting changes directly necessary for the feature. No unrelated changes detected.

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


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

@socket-security
Copy link
Copy Markdown

socket-security bot commented Mar 27, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​dnd-kit/​utilities@​3.2.21001007180100
Addednpm/​@​dnd-kit/​core@​6.3.1981007980100
Addednpm/​@​dnd-kit/​sortable@​10.0.01001007980100
Addednpm/​wrangler@​4.77.0981009296100

View full report

@socket-security
Copy link
Copy Markdown

socket-security bot commented Mar 27, 2026

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 27, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA c358be9.
Ensure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice.

OpenSSF Scorecard

PackageVersionScoreDetails
npm/@dnd-kit/accessibility 3.1.1 🟢 3.9
Details
CheckScoreReason
Code-Review⚠️ 1Found 2/15 approved changesets -- score normalized to 1
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained🟢 1030 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@dnd-kit/core 6.3.1 🟢 3.9
Details
CheckScoreReason
Code-Review⚠️ 1Found 2/15 approved changesets -- score normalized to 1
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained🟢 1030 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@dnd-kit/sortable 10.0.0 🟢 3.9
Details
CheckScoreReason
Code-Review⚠️ 1Found 2/15 approved changesets -- score normalized to 1
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained🟢 1030 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/@dnd-kit/utilities 3.2.2 🟢 3.9
Details
CheckScoreReason
Code-Review⚠️ 1Found 2/15 approved changesets -- score normalized to 1
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained🟢 1030 commit(s) and 12 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Binary-Artifacts🟢 10no binaries found in the repo
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Security-Policy⚠️ 0security policy file not detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0

Scanned Files

  • web/package-lock.json

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive Task Board feature, including Kanban and list views, drag-and-drop support via @dnd-kit, and real-time synchronization with WebSockets. It adds a new Zustand store for task management, custom hooks for data fetching, and several reusable UI components. Feedback focuses on adhering to React hook best practices in TaskCreateDialog, correcting component paths in the documentation, and simplifying redundant asynchronous function wrappers in TaskBoardPage.

Comment on lines +107 to +108
// eslint-disable-next-line @eslint-react/exhaustive-deps -- validate reads form which is in deps
}, [form, onCreate, onOpenChange])
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.

high

The exhaustive-deps ESLint rule is disabled for the handleSubmit useCallback. The dependency array is [form, onCreate, onOpenChange], but the function also uses validate, which is defined outside the callback and depends on form. This means validate will capture a stale form state if it's not included or if handleSubmit isn't correctly memoized.

To fix this, you should include validate in the dependency array. Since validate is redefined on every render, you should also wrap validate in a useCallback.

| `AnimatedPresence` | `@/components/ui/animated-presence` | Page transition wrapper (Framer Motion AnimatePresence keyed by route) |
| `StaggerGroup` / `StaggerItem` | `@/components/ui/stagger-group` | Card entrance stagger container with configurable delay |
| `TaskStatusIndicator` | `@/components/ui/task-status-indicator` | Task status dot with optional label and pulse animation (accepts `TaskStatus`) |
| `PriorityBadge` | `@/components/ui/task-status-indicator` | Task priority colored pill badge (critical/high/medium/low) |
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.

medium

There seems to be a copy-paste error in the path for the PriorityBadge component. It's currently pointing to @/components/ui/task-status-indicator, which is the same as TaskStatusIndicator. While both components are in the same file, for clarity and correctness in documentation, it should likely have its own distinct path or be clarified. I suggest updating the path to avoid confusion.

Suggested change
| `PriorityBadge` | `@/components/ui/task-status-indicator` | Task priority colored pill badge (critical/high/medium/low) |
| `PriorityBadge` | `@/components/ui/priority-badge` | Task priority colored pill badge (critical/high/medium/low) |

Comment on lines +278 to +281
onUpdate={async (id, data) => { await updateTask(id, data) }}
onTransition={async (id, data) => { await transitionTask(id, data) }}
onCancel={async (id, data) => { await cancelTask(id, data) }}
onDelete={async (id) => { await deleteTask(id); handleClosePanel() }}
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.

medium

The onUpdate, onTransition, onCancel, and onDelete props are defined with async functions that simply await another async function call. This is redundant. You can pass the function references directly.

Suggested change
onUpdate={async (id, data) => { await updateTask(id, data) }}
onTransition={async (id, data) => { await transitionTask(id, data) }}
onCancel={async (id, data) => { await cancelTask(id, data) }}
onDelete={async (id) => { await deleteTask(id); handleClosePanel() }}
onUpdate={updateTask}
onTransition={transitionTask}
onCancel={cancelTask}
onDelete={async (id) => { await deleteTask(id); handleClosePanel() }}

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 26

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/__tests__/components/ui/task-status-indicator.test.tsx`:
- Around line 10-20: The test defines a duplicate STATUS_LABELS map — replace
this duplication by importing and using the production helper getTaskStatusLabel
from '@/utils/tasks' (or derive expected labels via getTaskStatusLabel) so the
tests use the same source of truth; locate usages of STATUS_LABELS in the test
file (the constant named STATUS_LABELS and any assertions referencing it) and
update them to call getTaskStatusLabel(taskStatus) or remove STATUS_LABELS and
import getTaskStatusLabel at the top of the file.

In `@web/src/__tests__/hooks/useTaskBoardData.test.ts`:
- Around line 44-52: The test uses a fragile empty act (await act(async () =>
{})) to wait for useTaskBoardData's initial fetch; replace that with a
deterministic waitFor that waits for the hook to reach the expected settled
state. Specifically, in the test that renders useTaskBoardData, remove the empty
act and instead import and use waitFor to await a condition such as waitFor(()
=> expect(result.current.loading).toBe(false)) or waitFor(() =>
!result.current.loading) before asserting result.current.tasks, total and error;
this ensures asynchronous work inside fetchTasks (and any internal effects in
useTaskBoardData) completes reliably.
- Around line 71-77: The test is flaky because spying on a snapshot from
useTasksStore.getState() can miss calls made via Zustand selectors; before
calling renderHook(() => useTaskBoardData()) create the spy on the store action
(e.g., create the spy for fetchTasks from useTasksStore.getState() or spy on
useTasksStore.getState to return a mocked fetchTasks) so the spy is installed
before the hook mounts, or alternatively change the assertion to observe the
resulting state change from useTasksStore (e.g., assert tasks or loading state
via useTasksStore.getState() or waitFor) instead of spying on fetchTasks;
reference useTaskBoardData, useTasksStore, and fetchTasks when applying the
change.

In `@web/src/__tests__/pages/TaskBoardPage.test.tsx`:
- Around line 6-31: The test helper makeTask is missing required fields used
elsewhere (version, created_at, updated_at); update the makeTask function to
include default values for these fields (e.g., version: 1 and ISO timestamps for
created_at/updated_at) and still allow overrides via the existing overrides
parameter so tests can set different values when needed; locate the makeTask
helper in TaskBoardPage.test.tsx and add the three fields to the returned Task
object while preserving the spread of ...overrides so existing tests (and
TaskDetailPanel.test.tsx) can override them.

In `@web/src/__tests__/pages/TaskDetailPage.test.tsx`:
- Around line 89-132: Replace the empty await act(async () => {}) calls in
TaskDetailPage.test.tsx with DOM-aware waits (e.g., use findByText, findByRole,
or waitFor) so tests wait on rendered output instead of draining the microtask
queue; update the tests that call renderDetailPage() (including the cases that
mockGetTask rejects/resolves and the assertions for 'Task not found', 'Test
task', 'Test description', 'Back to Board', 'In Review', 'Delete', and 'Cancel
Task') to await the appropriate findBy... or wrap assertions in waitFor so they
reliably wait for fetchTask-driven updates rather than relying on act(). Ensure
you reference the existing mockGetTask and renderDetailPage helpers when
adjusting each test.

In `@web/src/__tests__/pages/tasks/TaskColumn.test.tsx`:
- Around line 7-32: The makeTask factory function is duplicated across tests;
extract it into a shared test utility (e.g., create a new factories module that
exports makeTask) and replace the inline definitions in TaskColumn.test.tsx and
TaskListView.test.tsx with imports from that module; keep the same function
signature and default fields (id, title, overrides: Partial<Task>) and ensure
tests import the new exported makeTask, update any relative import paths, and
run the test suite to confirm no behavioral changes.

In `@web/src/components/ui/task-status-indicator.stories.tsx`:
- Around line 46-55: The story PriorityBadges is incorrectly typed as
StatusStory (from TaskStatusIndicator meta) while its render returns
PriorityBadge instances and ignores args; either create a separate Storybook
meta for PriorityBadge and change PriorityBadges to use that meta (so
typing/autodocs match PriorityBadge), or keep it here but remove the unused
args: { status: 'created' } and add a short comment above PriorityBadges
explaining it uses a custom render that ignores args; update the story type from
StatusStory to the correct PriorityBadge story type if you add the new meta.

In `@web/src/pages/TaskBoardPage.tsx`:
- Around line 61-63: The page currently reads selectedTaskId from searchParams
but only fetches task data via handleSelectTask on clicks, so external changes
to ?selected=... leave the panel stale; update TaskBoardPage to react to changes
in selectedTaskId (from searchParams) by invoking the same fetch logic used by
handleSelectTask (or centralize fetch into a loadSelectedTask function) whenever
selectedTaskId changes, and change rendering so the panel only displays when
selectedTask?.id === selectedTaskId to avoid transient mismatches; apply the
same pattern where selectedTaskId is used (including the other occurrences
around the blocks referenced) so both query-param and click selection stay in
sync.
- Around line 64-70: The TaskBoard filters currently only read status, priority,
assignee, type, and search from searchParams; update the filters useMemo (the
filters constant / TaskBoardFilters) to also parse and include the department
and date range keys you use in the UI (e.g., department and startDate/endDate or
dateFrom/dateTo depending on your model) from searchParams so they survive
reloads, and then update the code paths that write/update the URL (the code
around lines ~95-105 that syncs filter changes to searchParams) to include those
same department and date parameters when setting searchParams; ensure the same
parameter names are used in both reading (useMemo filters) and writing so
shareable links and reloads restore department and date filters.
- Around line 178-181: executeOptimistic(...) is called but its return value is
ignored; when the optimistic transition fails it returns null and the UI simply
snaps back with no feedback. Capture the result of executeOptimistic( () =>
optimisticTransition(taskId, targetStatus), () => transitionTask(taskId, {
target_status: targetStatus, expected_version: sourceTask.version }) ) and, if
the result is null, surface an error (e.g., set an error state, dispatch a
toast, or call an existing notify function) tied to this taskId so users see why
the drag reverted; ensure you reference executeOptimistic, optimisticTransition,
transitionTask, taskId, targetStatus and sourceTask.version when locating the
code to update.

In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 102-120: Replace the hand-built card div ("rounded-lg border
border-border bg-card p-6 space-y-6") with the shared SectionCard component from
web/src/components/ui/SectionCard: keep the existing ErrorBoundary and move the
header block (TaskStatusIndicator, PriorityBadge, and the InlineEdit title) into
SectionCard's header/children as appropriate so padding, border and header
styling come from SectionCard; preserve the InlineEdit onSave logic
(useTasksStore.getState().updateTask(...)) and validate handler and ensure you
add the SectionCard import and remove the manual card classes.
- Around line 172-173: formatCurrency is defaulting to EUR so the USD-backed
field task.cost_usd is displayed with the wrong currency; update the usage in
TaskDetailPage where task.cost_usd is rendered (the JSX block containing
formatCurrency(task.cost_usd)) to pass the correct currency code by calling
formatCurrency(task.cost_usd, 'USD') so the value is formatted as USD.
- Around line 32-38: The page is using selectedTask directly which can show
stale data when taskId changes; update the code so the local task variable only
uses the store value when selectedTask?.id === taskId (e.g. replace "const task
= selectedTask" with a guarded assignment such as "const task = selectedTask?.id
=== taskId ? selectedTask : undefined") so the component treats the store value
as loaded only when it matches the current taskId; keep the existing
useEffect(fetchTask) logic unchanged.
- Around line 138-143: The select's onChange handler currently awaits
useTasksStore.getState().updateTask(task.id, { priority: e.target.value as
Priority, expected_version: task.version }) without error handling; wrap that
await in a try/catch inside the onChange callback (the select change handler)
and call the app's toast/error notificaton utility in the catch block to surface
the failure to the user (and optionally revert any UI/optimistic change by
reloading task data or resetting value to task.priority if needed).

In `@web/src/pages/tasks/TaskColumn.stories.tsx`:
- Around line 7-32: The makeTask helper is missing required Task fields version,
created_at, and updated_at; update the makeTask function to include these keys
(e.g., version: 1 and timestamps like created_at/new Date().toISOString() and
updated_at/new Date().toISOString()) while still allowing overrides via the
existing ...overrides spread so tests can supply alternate values; reference the
makeTask function and the Task type to locate and modify the helper.
- Line 44: The non-null assertion on KANBAN_COLUMNS.find(...) for
inProgressColumn can hide configuration issues; update the code that computes
inProgressColumn to handle the undefined case instead of using "!" by checking
the find result and either providing a sensible fallback column or throwing an
explicit, descriptive error. Locate the expression where inProgressColumn is
assigned (the find on KANBAN_COLUMNS) and replace the forced assertion with a
runtime guard that logs/throws a clear error mentioning 'in_progress' (or
returns a default column object) so missing configuration is detected
immediately.

In `@web/src/pages/tasks/TaskCreateDialog.stories.tsx`:
- Around line 14-20: The Story's onCreate handler uses console.log; import
action from 'storybook/actions' and replace the console.log usage in the Open
story's args with Storybook's action so events appear in the Actions panel;
specifically update the TaskCreateDialog.stories.tsx Open story (args.onCreate)
to call action('onCreate') (or wrap it as async: async (data) =>
action('onCreate')(data)) and add the corresponding import for action at the top
of the file.

In `@web/src/pages/tasks/TaskCreateDialog.tsx`:
- Line 97: The budget field conversion in TaskCreateDialog (the object property
budget_limit: form.budget_limit ? Number(form.budget_limit) : undefined) can
produce NaN for non-numeric input; update the TaskCreateDialog form handling to
validate/parse the value robustly (e.g., use parseFloat or
Number(form.budget_limit) then check Number.isFinite or isNaN) and only set
budget_limit when the parsed value is a valid number, otherwise set it to
undefined or surface a validation error to the user; locate the conversion in
the component where the payload is built (budget_limit) and replace it with a
guarded parse + numeric check before sending to the API.

In `@web/src/pages/tasks/TaskDetailPanel.stories.tsx`:
- Around line 26-34: The story fixture uses Date.now()/new Date() for deadline,
created_at and updated_at which causes drifting snapshots; replace those
expressions in TaskDetailPanel story (the object fields deadline, created_at,
updated_at) with fixed ISO 8601 string literals (e.g., a chosen deterministic
timestamp like '2025-01-01T00:00:00.000Z') so the rendered metadata and deadline
are stable across test runs and visual diffs.

In `@web/src/pages/tasks/TaskDetailPanel.tsx`:
- Around line 153-165: The select's async onChange awaiting onUpdate can cause
state updates after unmount; in TaskDetailPanel avoid awaiting directly in the
inline handler: either call onUpdate without await or add an isMounted
guard—create an isMounted ref in TaskDetailPanel (set true in useEffect and
false in cleanup) and in the async handler wrap the await in try/catch and check
isMounted before allowing any follow-up state logic; reference the onUpdate call
in the select change handler and the onUpdate function to locate where to apply
the fix.
- Around line 271-280: The Cancel dialog currently uses ConfirmDialog which has
no input support, so the cancelReason state (cancelReason) passed to
onCancel/from handleCancel will always be empty; fix by replacing ConfirmDialog
with a modal that accepts custom content (or extend ConfirmDialog to accept
children/reason prop) so you can render a TextArea/TextInput bound to
cancelReason and pass its value into handleCancel/onCancel, or alternatively
render a separate input outside the dialog and block confirming until
cancelReason is provided; update usages of ConfirmDialog (the JSX block that
renders ConfirmDialog) and ensure handleCancel reads the updated cancelReason
state when confirming.

In `@web/src/pages/tasks/TaskFilterBar.tsx`:
- Around line 16-23: Remove the duplicated TASK_TYPE_LABELS constant and replace
its usages with the already imported getTaskTypeLabel from '@/utils/tasks';
specifically, delete the TASK_TYPE_LABELS declaration and update any code that
reads TASK_TYPE_LABELS[taskType] (e.g., usages referenced near the TaskFilterBar
component, formerly at the two spots noted) to call getTaskTypeLabel(taskType)
instead so the shared utility is the single source of truth.

In `@web/src/pages/tasks/TaskListView.tsx`:
- Around line 12-25: The headers are inert and TaskListView can't change sort
order; add sorting support by extending TaskListViewProps to include current
sort key and direction plus an onChangeSort callback (e.g., props: sortKey:
keyof Task | null, sortDirection: 'asc'|'desc', onChangeSort: (key: keyof Task)
=> void), mark sortable columns in COLUMNS (add a sortable boolean or omit for
non-sortable keys) and update the header rendering in the TaskListView component
(around the current header span code) to render interactive elements (button or
role="button") that call onChangeSort with the column key and toggle direction
when clicked, and visually indicate the active sort and direction; ensure sort
state is used when rendering rows to display tasks in the requested order.
- Around line 94-95: The currency display for task.cost_usd is using
formatCurrency(task.cost_usd) which defaults to EUR; update the call in
TaskListView (the span rendering task.cost_usd) to pass 'USD' explicitly: use
formatCurrency(task.cost_usd, 'USD') so USD amounts show the correct currency.
Ensure you only change the rendering expression that references task.cost_usd
and keep the null check/placeholder ('--') intact.
- Around line 54-99: The repeated JSX inside tasks.map should be extracted into
a separate row component (e.g., TaskListRow or TaskRow) to keep .map() bodies
small; create a new functional component that accepts the task object and the
onSelectTask handler (and any helpers it needs) and move the StaggerItem wrapper
and the clickable div with its onClick/onKeyDown logic, aria-label, and all cell
children (TaskStatusIndicator, Avatar, PriorityBadge, getTaskTypeLabel,
formatRelativeTime, formatCurrency) into that component; update the .map() to
return <StaggerItem key={task.id}><TaskListRow task={task}
onSelectTask={onSelectTask} /></StaggerItem> (or have TaskListRow include
StaggerItem) and ensure keyboard handling, tabIndex, classes, and prop types are
preserved.

In `@web/src/stores/tasks.ts`:
- Around line 101-121: The WebSocket handler can stomp optimistic updates; add a
pendingTransitions Set<string> to the same store and mark taskId as pending
inside optimisticTransition (add id to pendingTransitions before set and return
a rollback that removes the id and restores prev tasks), then update
handleWsEvent to ignore/upsertTask only when payload.task.id is not in
pendingTransitions; also ensure the real transition flow (transitionTask or its
caller) clears the id from pendingTransitions when the API call resolves or
rejects so server updates resume applying.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d949eb45-1326-4ca4-a591-29645bada091

📥 Commits

Reviewing files that changed from the base of the PR and between 7d519d5 and 3a8e679.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (35)
  • CLAUDE.md
  • web/package.json
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/hooks/useTaskBoardData.ts
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskColumn.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/stores/tasks.ts
  • web/src/utils/tasks.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Dashboard Test
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (10)
web/**

📄 CodeRabbit inference engine (CLAUDE.md)

Use Node.js 22+ and npm for web dashboard; all dependencies in web/package.json (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, Recharts, Framer Motion, Axios, Lucide React, Geist fonts, Storybook 10, Vitest, ESLint, fast-check, etc.)

Files:

  • web/package.json
  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/hooks/useTaskBoardData.ts
  • web/src/pages/TaskDetailPage.tsx
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/utils/tasks.ts
  • web/src/pages/tasks/TaskColumn.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/stores/tasks.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript 6+ with paths for module resolution (remove deprecated baseUrl); esModuleInterop is always true
In TypeScript, strict defaults to true and types defaults to []; do not auto-discover @types/* — must explicitly list needed types

Files:

  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/hooks/useTaskBoardData.ts
  • web/src/pages/TaskDetailPage.tsx
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/utils/tasks.ts
  • web/src/pages/tasks/TaskColumn.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/stores/tasks.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from web/src/components/ui/
Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones
Use design tokens exclusively in web components: Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)); never hardcode hex values or pixel spacing
Use font-sans or font-mono (Geist tokens) in web components; never set fontFamily directly
Use density-aware spacing tokens in web components (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacing
Use shadow and border token variables in web components (var(--so-shadow-card-hover), border-border, border-bright); never hardcode shadow or border styles
In React components, import cn from @/lib/utils for conditional class merging; never concatenate class strings directly
Do not recreate status dots inline; use <StatusBadge> component
Do not build card-with-header layouts from scratch; use <SectionCard> component
Do not create metric displays with text-metric font-bold; use <MetricCard> component
Do not render initials circles manually; use <Avatar> component
Do not create complex (>8 line) JSX inside .map() blocks; extract to a shared component

Files:

  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/hooks/useTaskBoardData.ts
  • web/src/pages/TaskDetailPage.tsx
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/utils/tasks.ts
  • web/src/pages/tasks/TaskColumn.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/stores/tasks.ts
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in pages/<page-name>/ subdirectories

Files:

  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/pages/tasks/TaskColumn.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @eslint-react/eslint-plugin and eslint-plugin-security in web dashboard

Files:

  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/hooks/useTaskBoardData.ts
  • web/src/pages/TaskDetailPage.tsx
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/utils/tasks.ts
  • web/src/pages/tasks/TaskColumn.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/stores/tasks.ts
web/src/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/__tests__/**/*.{ts,tsx}: Use fast-check for property-based testing in React; use fc.assert and fc.property
Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Files:

  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
web/**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/**/*.stories.{ts,tsx}: In Storybook, use parameters.backgrounds.options (object keyed by name) and initialGlobals.backgrounds.value for background configuration
In Storybook, use parameters.a11y.test: 'error' | 'todo' | 'off' for a11y testing configuration (replaces old .element and .manual)

Files:

  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
web/src/components/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When creating new shared React components, place them in web/src/components/ui/ with kebab-case filename, create accompanying .stories.tsx Storybook file with all states, export TypeScript interface for props, and use design tokens exclusively

Files:

  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/components/ui/task-status-indicator.tsx
web/src/components/**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Storybook 10 (ESM-only) for React component documentation; import from storybook/test, storybook/actions; use definePreview from @storybook/react-vite

Files:

  • web/src/components/ui/task-status-indicator.stories.tsx
web/src/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)

Files:

  • web/src/stores/tasks.ts
🧠 Learnings (32)
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)

Applied to files:

  • web/package.json
  • CLAUDE.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/** : Use Node.js 22+ and npm for web dashboard; all dependencies in `web/package.json` (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, Recharts, Framer Motion, Axios, Lucide React, Geist fonts, Storybook 10, Vitest, ESLint, fast-check, etc.)

Applied to files:

  • web/package.json
  • CLAUDE.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).

Applied to files:

  • web/package.json
  • CLAUDE.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/**/*.stories.{ts,tsx} : Use Storybook 10 (ESM-only) for React component documentation; import from `storybook/test`, `storybook/actions`; use `definePreview` from `storybook/react-vite`

Applied to files:

  • web/package.json
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • CLAUDE.md
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/.storybook/**/*.{ts,tsx} : In Storybook, use type-safe config with `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite`; include explicit `framework` field

Applied to files:

  • web/package.json
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not build card-with-header layouts from scratch; use `<SectionCard>` component

Applied to files:

  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones

Applied to files:

  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • CLAUDE.md
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from `web/src/components/ui/`

Applied to files:

  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • CLAUDE.md
  • web/src/hooks/useTaskBoardData.ts
  • web/src/pages/TaskDetailPage.tsx
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/utils/tasks.ts
  • web/src/pages/tasks/TaskColumn.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared React components, place them in `web/src/components/ui/` with kebab-case filename, create accompanying `.stories.tsx` Storybook file with all states, export TypeScript interface for props, and use design tokens exclusively

Applied to files:

  • web/src/pages/tasks/TaskBoardSkeleton.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • CLAUDE.md
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/utils/tasks.ts
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Applied to files:

  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use fast-check for property-based testing in React; use `fc.assert` and `fc.property`

Applied to files:

  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/__tests__/utils/tasks.property.test.ts
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)

Applied to files:

  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • CLAUDE.md
  • web/src/hooks/useTaskBoardData.ts
  • web/src/__tests__/stores/tasks.test.ts
  • web/src/stores/tasks.ts
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/**/*.stories.{ts,tsx} : In Storybook, use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing configuration (replaces old `.element` and `.manual`)

Applied to files:

  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCreateDialog.test.tsx
  • web/src/__tests__/pages/tasks/TaskFilterBar.test.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create metric displays with `text-metric font-bold`; use `<MetricCard>` component

Applied to files:

  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • CLAUDE.md
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/**/*.stories.{ts,tsx} : In Storybook, use `parameters.backgrounds.options` (object keyed by name) and `initialGlobals.backgrounds.value` for background configuration

Applied to files:

  • web/src/pages/tasks/TaskListView.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in `pages/<page-name>/` subdirectories

Applied to files:

  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • CLAUDE.md
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not recreate status dots inline; use `<StatusBadge>` component

Applied to files:

  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • CLAUDE.md
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/pages/tasks/TaskCard.tsx
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design tokens exclusively in web components: Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`, etc.) or CSS variables (`var(--so-accent)`); never hardcode hex values or pixel spacing

Applied to files:

  • CLAUDE.md
  • web/src/components/ui/task-status-indicator.tsx
  • web/src/pages/tasks/TaskCard.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use density-aware spacing tokens in web components (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing; never hardcode pixel values for layout spacing

Applied to files:

  • CLAUDE.md
  • web/src/pages/tasks/TaskCard.tsx
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to pyproject.toml : Dependencies: all versions use == in pyproject.toml. Groups: test (pytest + plugins, hypothesis), dev (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Required: mem0ai (Mem0 memory backend — the default and currently only backend). Install: uv sync installs everything (dev group is default).

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to docker/{Dockerfile*,compose.yml} : Docker: Backend uses 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened. Web uses nginxinc/nginx-unprivileged, Vue 3 SPA with PrimeVue + Tailwind CSS, SPA routing, API/WebSocket proxy to backend.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to go.mod : Maintain Go 1.26+ requirement. Dependencies: Cobra (CLI framework), charmbracelet/huh and charmbracelet/lipgloss (UI), sigstore-go (code signing), go-containerregistry (container image verification), go-tuf (TUF client for Sigstore).

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/go.mod : CLI uses Go 1.26+. Dependencies: Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use shadow and border token variables in web components (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`); never hardcode shadow or border styles

Applied to files:

  • web/src/pages/tasks/TaskCard.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create complex (>8 line) JSX inside `.map()` blocks; extract to a shared component

Applied to files:

  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskListView.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/.storybook/preview.tsx : In Storybook, set a11y testing globally in `preview.tsx` with `parameters.a11y.test: 'error'` to enforce WCAG compliance on all stories

Applied to files:

  • web/src/pages/tasks/TaskFilterBar.stories.tsx
  • web/src/pages/tasks/TaskCard.stories.tsx

Comment on lines +6 to +31
function makeTask(id: string, overrides: Partial<Task> = {}): Task {
return {
id,
title: `Task ${id}`,
description: 'Description',
type: 'development',
status: 'assigned',
priority: 'medium',
project: 'test-project',
created_by: 'agent-cto',
assigned_to: 'agent-eng',
reviewers: [],
dependencies: [],
artifacts_expected: [],
acceptance_criteria: [],
estimated_complexity: 'medium',
budget_limit: 10,
deadline: null,
max_retries: 3,
parent_task_id: null,
delegation_chain: [],
task_structure: null,
coordination_topology: 'auto',
...overrides,
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add missing version, created_at, updated_at fields to makeTask.

Same issue as in the stories file—this helper is missing fields that may be required by the Task type and are used elsewhere in the codebase (e.g., TaskDetailPanel.test.tsx uses version: 2).

🛠️ Proposed fix
     task_structure: null,
     coordination_topology: 'auto',
+    version: 1,
+    created_at: new Date().toISOString(),
+    updated_at: new Date().toISOString(),
     ...overrides,
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function makeTask(id: string, overrides: Partial<Task> = {}): Task {
return {
id,
title: `Task ${id}`,
description: 'Description',
type: 'development',
status: 'assigned',
priority: 'medium',
project: 'test-project',
created_by: 'agent-cto',
assigned_to: 'agent-eng',
reviewers: [],
dependencies: [],
artifacts_expected: [],
acceptance_criteria: [],
estimated_complexity: 'medium',
budget_limit: 10,
deadline: null,
max_retries: 3,
parent_task_id: null,
delegation_chain: [],
task_structure: null,
coordination_topology: 'auto',
...overrides,
}
}
function makeTask(id: string, overrides: Partial<Task> = {}): Task {
return {
id,
title: `Task ${id}`,
description: 'Description',
type: 'development',
status: 'assigned',
priority: 'medium',
project: 'test-project',
created_by: 'agent-cto',
assigned_to: 'agent-eng',
reviewers: [],
dependencies: [],
artifacts_expected: [],
acceptance_criteria: [],
estimated_complexity: 'medium',
budget_limit: 10,
deadline: null,
max_retries: 3,
parent_task_id: null,
delegation_chain: [],
task_structure: null,
coordination_topology: 'auto',
version: 1,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
...overrides,
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/__tests__/pages/TaskBoardPage.test.tsx` around lines 6 - 31, The test
helper makeTask is missing required fields used elsewhere (version, created_at,
updated_at); update the makeTask function to include default values for these
fields (e.g., version: 1 and ISO timestamps for created_at/updated_at) and still
allow overrides via the existing overrides parameter so tests can set different
values when needed; locate the makeTask helper in TaskBoardPage.test.tsx and add
the three fields to the returned Task object while preserving the spread of
...overrides so existing tests (and TaskDetailPanel.test.tsx) can override them.

Comment on lines +12 to +25
export interface TaskListViewProps {
tasks: Task[]
onSelectTask: (taskId: string) => void
}

const COLUMNS = [
{ key: 'status', label: 'Status', width: 'w-20' },
{ key: 'title', label: 'Title', width: 'flex-1' },
{ key: 'assignee', label: 'Assignee', width: 'w-32' },
{ key: 'priority', label: 'Priority', width: 'w-24' },
{ key: 'type', label: 'Type', width: 'w-24' },
{ key: 'deadline', label: 'Deadline', width: 'w-24' },
{ key: 'cost', label: 'Cost', width: 'w-20' },
] as const
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The list view still isn't sortable.

These headers are inert spans and TaskListViewProps has no sort state/callbacks, so users cannot change sort order from the list view.

Also applies to: 41-49

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

In `@web/src/pages/tasks/TaskListView.tsx` around lines 12 - 25, The headers are
inert and TaskListView can't change sort order; add sorting support by extending
TaskListViewProps to include current sort key and direction plus an onChangeSort
callback (e.g., props: sortKey: keyof Task | null, sortDirection: 'asc'|'desc',
onChangeSort: (key: keyof Task) => void), mark sortable columns in COLUMNS (add
a sortable boolean or omit for non-sortable keys) and update the header
rendering in the TaskListView component (around the current header span code) to
render interactive elements (button or role="button") that call onChangeSort
with the column key and toggle direction when clicked, and visually indicate the
active sort and direction; ensure sort state is used when rendering rows to
display tasks in the requested order.

Error handling:
- Add catch blocks to TaskDetailPanel transition/cancel/delete handlers
- Add try/catch with toast to all inline async handlers in TaskBoardPage
- Add error handling to InlineEdit/select onChange in TaskDetailPage and TaskDetailPanel
- Surface executeOptimistic failures as toast on DnD rollback
- Use getErrorMessage in TaskCreateDialog catch block

Feature gaps (#780):
- Add dependency visualization using @xyflow/react with toggle
- Add date range filter (dateFrom/dateTo) in TaskFilterBar and filterTasks
- Add assignee inline editing in TaskDetailPanel
- Add template suggestions in TaskCreateDialog

Data safety:
- Strengthen WS handleWsEvent validation (check title, priority, type, arrays)
- Guard TaskDetailPage against stale selectedTask when taskId changes
- Sync selectedTaskId URL param with fetchTask on direct navigation
- Validate budget_limit for NaN/Infinity/negative in TaskCreateDialog
- Fix formatCurrency to pass 'USD' for task.cost_usd (4 files)

Code quality:
- Remove duplicated TASK_TYPE_LABELS from TaskFilterBar, use getTaskTypeLabel
- Extract TaskListRow from TaskListView .map() (CLAUDE.md >8 line rule)
- Fix TaskCard 4-space indentation to 2-space
- Add children prop to ConfirmDialog for cancel reason textarea
- Add cancel reason textarea to TaskDetailPanel cancel dialog

Tests:
- Create shared makeTask factory in __tests__/helpers/factories.ts
- Replace duplicated makeTask in 5 test files with shared import
- Replace fragile empty act(async () => {}) with waitFor/findBy patterns
- Add onDelete/onCancel callback invocation tests to TaskDetailPanel
- Use getTaskStatusLabel in indicator test instead of duplicate map

Stories:
- Add comment clarifying PriorityBadges story type mismatch
- Replace console.log with Storybook action() in TaskCreateDialog story
- Use fixed timestamps in TaskDetailPanel story
- Add version/created_at/updated_at to TaskColumn story factory
- Replace non-null assertion with runtime guard in TaskColumn stories/tests
- Add TaskDependencyGraph story

Documentation:
- Add TaskStatusIndicator, PriorityBadge to brand-and-ux.md component inventory
- Add task utility functions and types to brand-and-ux.md
- Fix ux-guidelines.md DnD version (v0.6+ -> v0.5.0)
- Clarify dual routing pattern in page-structure.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 11:39 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

♻️ Duplicate comments (5)
web/src/components/ui/task-status-indicator.stories.tsx (1)

47-56: ⚠️ Potential issue | 🟡 Minor

PriorityBadges is typed against the wrong component meta.

This story is declared as StatusStory but renders PriorityBadge, so controls/autodocs typing is inaccurate and args.status is dead. Move this into a PriorityBadge-scoped stories file (or a PriorityBadge-scoped meta) so the story type matches the rendered component.

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

In `@web/src/components/ui/task-status-indicator.stories.tsx` around lines 47 -
56, The story PriorityBadges is using the wrong meta type (StatusStory) and has
a dead args.status while rendering PriorityBadge; change the story to use a
PriorityBadge-scoped meta/type (or move the story into a PriorityBadge stories
file) so its type matches the rendered component, remove or replace the unused
args.status, and ensure the story's props and controls target PriorityBadge
(referencing PriorityBadges, StatusStory, PriorityBadge, and args.status to
locate and update the code).
web/src/pages/tasks/TaskListView.tsx (1)

17-25: ⚠️ Potential issue | 🟠 Major

List headers are still non-interactive, so table sorting is missing.

Columns render as inert <span> elements and TaskListViewProps has no sort key/direction change API, so users cannot sort from list view.

Proposed direction
 export interface TaskListViewProps {
   tasks: Task[]
   onSelectTask: (taskId: string) => void
+  sortKey: 'status' | 'title' | 'assignee' | 'priority' | 'type' | 'deadline' | 'cost'
+  sortDirection: 'asc' | 'desc'
+  onChangeSort: (key: TaskListViewProps['sortKey']) => void
 }
-<span key={col.key} className={cn(...)}>{col.label}</span>
+<button
+  key={col.key}
+  type="button"
+  onClick={() => onChangeSort(col.key)}
+  className={cn(...)}
+>
+  {col.label}
+</button>

Also applies to: 41-49

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

In `@web/src/pages/tasks/TaskListView.tsx` around lines 17 - 25, The column
headers are rendered inertly (COLUMNS) and TaskListViewProps lacks any sort
state or callback, so make headers interactive by replacing the inert header
elements in TaskListView with clickable elements (e.g., button or role="button")
that call a new sort handler; add sortKey and sortDirection (or
sortBy/sortOrder) to TaskListViewProps and expose an onSortChange callback so
parent components can control sorting, update TaskListView to keep a local sort
state if uncontrolled, toggle direction when the same key is clicked, and add
appropriate ARIA attributes (aria-sort) and visual indicators for the active
sorted column to the rendering logic for COLUMNS and the header renderer.
web/src/__tests__/hooks/useTaskBoardData.test.ts (1)

69-74: 🧹 Nitpick | 🔵 Trivial

Spy approach works but could be more robust.

The vi.spyOn(useTasksStore.getState(), 'fetchTasks') pattern works here because useTaskBoardData calls useTasksStore.getState().fetchTasks() directly. However, this creates a coupling between the test and the hook's internal implementation detail.

A more resilient approach would be to assert on the observable outcome (store state change or API call) rather than spying on the store method:

♻️ Alternative using API spy
+import { listTasks } from '@/api/endpoints/tasks'
...
 it('triggers initial fetch on mount', async () => {
-  const fetchTasks = vi.spyOn(useTasksStore.getState(), 'fetchTasks')
   renderHook(() => useTaskBoardData())
-  await waitFor(() => expect(fetchTasks).toHaveBeenCalled())
-  expect(fetchTasks).toHaveBeenCalledWith({ limit: 200 })
+  await waitFor(() => expect(listTasks).toHaveBeenCalled())
+  expect(listTasks).toHaveBeenCalledWith({ limit: 200 })
 })

This asserts the same behavior but at the API boundary, which is more stable if the store internals change.

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

In `@web/src/__tests__/hooks/useTaskBoardData.test.ts` around lines 69 - 74, The
test couples to the hook's internals by spying on
useTasksStore.getState().fetchTasks; instead, make the assertion at the
observable boundary: either spy on the API layer that fetchTasks calls (e.g.,
the HTTP client or fetch/axios mock) or assert the store/resulting state change
after renderHook (useTaskBoardData) — reference useTaskBoardData,
useTasksStore.getState(), and fetchTasks — replace
vi.spyOn(useTasksStore.getState(), 'fetchTasks') with a spy on the API call or
an assertion like expecting the tasks slice/state to be populated after waitFor.
web/src/pages/TaskDetailPage.tsx (1)

103-126: 🧹 Nitpick | 🔵 Trivial

Consider using SectionCard for the card layout.

This hand-built card layout (rounded-lg border border-border bg-card p-6 space-y-6) could drift from the shared dashboard styling. As per coding guidelines, do not build card-with-header layouts from scratch; use <SectionCard> component.

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

In `@web/src/pages/TaskDetailPage.tsx` around lines 103 - 126, Replace the
hand-built card wrapper around the task header with the shared SectionCard
component to ensure consistent dashboard styling: import and use SectionCard in
place of the outer div with classes "rounded-lg border border-border bg-card p-6
space-y-6", keeping the ErrorBoundary, the inner structure (TaskStatusIndicator,
PriorityBadge, InlineEdit) and their handlers (the InlineEdit onSave that calls
useTasksStore.getState().updateTask and error handling via useToastStore)
unchanged; ensure any className on InlineEdit (text-xl font-semibold) is
preserved or moved into SectionCard props if required.
web/src/pages/TaskBoardPage.tsx (1)

310-334: ⚠️ Potential issue | 🟡 Minor

Guard selectedTask by verifying ID match to prevent stale data.

The condition selectedTaskId && selectedTask can briefly render stale task data when navigating between tasks or when the URL param changes externally. Ensure the panel only renders when selectedTask?.id === selectedTaskId.

🛡️ Proposed fix
       {/* Detail panel overlay */}
       <AnimatePresence>
-        {selectedTaskId && selectedTask && (
+        {selectedTaskId && selectedTask?.id === selectedTaskId && (
           <TaskDetailPanel
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/TaskBoardPage.tsx` around lines 310 - 334, The current render
condition for TaskDetailPanel uses selectedTaskId && selectedTask which can
briefly show stale data; change the guard to only render when the loaded task
matches the selected ID by checking selectedTask?.id === selectedTaskId (e.g.,
replace the condition with selectedTaskId && selectedTask && selectedTask.id ===
selectedTaskId or the optional chaining variant) so TaskDetailPanel is only
mounted when the task's id equals selectedTaskId.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/__tests__/pages/TaskBoardPage.test.tsx`:
- Around line 31-34: Replace the unusual computed property mock with a direct
object literal: in the vi.mock call for '@/hooks/useTaskBoardData' return an
object that directly defines useTaskBoardData instead of using [hookName]; keep
the implementation returning getTaskBoardData() and retain the existing vi.mock
structure to preserve hoisting behavior.

In `@web/src/__tests__/pages/TaskDetailPage.test.tsx`:
- Around line 76-87: The loading-state tests may race with TaskDetailPage's
mount fetch; to fix, make the tests deterministic by either mocking the fetch to
never resolve (e.g., adjust mockGetTask to return a pending Promise before
calling renderDetailPage) or by asserting on a concrete loading indicator
element instead of absence of content; update the tests that call resetStore and
renderDetailPage so they either (a) stub/mock mockGetTask/fetchTask to a pending
promise before mounting, or (b) query for the loading spinner element (used by
TaskDetailPage) and assert it is present when loadingDetail is true or
selectedTask is null.

In `@web/src/__tests__/pages/tasks/TaskCard.test.tsx`:
- Around line 85-90: The test it('renders deadline when set') uses a dynamic
futureDate computed with Date.now(); replace that dynamic date with a stable
fixed ISO string (e.g. '2024-01-01T00:00:00.000Z') when calling makeTask so the
test is deterministic; update the TaskCard render invocation in
TaskCard.test.tsx (the render(<TaskCard task={makeTask({ deadline: ... })} ...
/>) line) to pass the fixed ISO deadline and keep the existing assertion
(screen.getByTitle(/Deadline/)) unchanged.

In `@web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx`:
- Around line 135-145: The test "calls onDelete after confirm dialog
confirmation" in TaskDetailPanel.test.tsx is fragile because getByRole('button',
{ name: 'Delete' }) can match the panel's footer button instead of the dialog
confirm; update the test to target the dialog confirm button explicitly (for
example, use screen.getAllByRole('button', { name: 'Delete' }) and pick the
last/expected index, or scope the query to the dialog element returned by
screen.getByRole('dialog') or within(getByRole('dialog')).getByRole) so the
click is performed on the confirm button rendered by the ConfirmDialog component
and then assert onDelete('task-1') was called.

In `@web/src/__tests__/pages/tasks/TaskListView.test.tsx`:
- Around line 31-37: Add a keyboard interaction test in TaskListView.test.tsx
that verifies TaskListRow activation via Enter/Space calls the provided
onSelectTask handler: render <TaskListView tasks={tasks} onSelectTask={onSelect}
/>, focus or tab to the row element (e.g., locate by text "First task"),
simulate keyboard activation with userEvent.keyboard('{Enter}') and/or
userEvent.keyboard(' '), and assert the mock onSelect was called with 't1';
reference TaskListView and TaskListRow to locate the right element for
focus/keyboard events.

In `@web/src/pages/TaskBoardPage.tsx`:
- Around line 206-209: The handleCreateTask callback should catch errors from
createTask and show an error toast instead of letting them bubble unhandled:
wrap the await createTask(data) call in a try/catch inside handleCreateTask,
keep the existing success toast on success, and in the catch call
useToastStore.getState().add({ variant: 'error', title: 'Failed to create task',
description: error?.message ?? String(error) }) (or similar) to surface the
failure to the user; refer to handleCreateTask, createTask, and useToastStore to
locate and update the code.

In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 68-76: The cancel flow in handleCancel currently sends a hardcoded
reason; update the cancel dialog (the ConfirmDialog used on this page) to
include a textarea input for the cancellation reason, wire that textarea's state
into the component (e.g., cancelReason state) and change handleCancel to call
useTasksStore.getState().cancelTask(task.id, { reason: cancelReason ||
'Cancelled by user' }) then close the dialog via setCancelOpen(false); ensure
the same pattern mirrors TaskDetailPanel's behavior and that validation/trim is
applied before submitting so empty input falls back to the default.
- Around line 112-126: The upsertTask mutator currently updates only the tasks
array which leaves selectedTask stale and causes optimistic concurrency 409s;
modify upsertTask so when the incoming task.id matches state.selectedTask?.id it
also replaces state.selectedTask with the new task (in addition to updating or
inserting the task in tasks and adjusting total), ensuring selectedTask used by
TaskDetailPage and updateTask carries the fresh version after API responses.

In `@web/src/pages/tasks/TaskCreateDialog.tsx`:
- Line 131: The inline redundant validation for budget_limit should be removed
and replaced with a simple conversion since validate() (called by handleSubmit)
already enforces valid values; change the payload construction that currently
uses the long Number.isFinite(...) check to simply set budget_limit to
form.budget_limit ? Number(form.budget_limit) : undefined, referencing the same
form variable and budget_limit field in the payload so the code relies on
validate() for correctness.

In `@web/src/pages/tasks/TaskDependencyGraph.stories.tsx`:
- Around line 45-54: Replace the no-op onSelectTask in the WithDependencies
story with Storybook's action so clicks appear in the Actions panel: update the
WithDependencies.args.onSelectTask to use action('onSelectTask') and add the
appropriate import for action (e.g., import { action } from 'storybook/actions')
near the top of the file; keep the rest of the story and makeTask usage
unchanged.
- Around line 5-33: The file defines a duplicate local makeTask factory; remove
this local function and import and reuse the shared makeTask factory from the
test helpers module instead. Replace the local declaration with an import of
makeTask (the factory exported by the shared factories helper) and update any
local overrides usage to call that imported makeTask, ensuring the override
signature (makeTask(id, title, overrides)) remains consistent.

In `@web/src/pages/tasks/TaskDependencyGraph.tsx`:
- Around line 90-106: The container currently uses a fixed Tailwind class
h-[400px]; make height configurable by adding an optional prop (e.g., height:
string | number) to the TaskDependencyGraph component and use that prop to set
the container height (fallback to "400px" or a tailwind class if not provided).
Update where the component renders the outer div to apply the dynamic height
(via inline style height or computed className) so nodes/edges and controls
adapt to different screen sizes and task counts; keep existing props like nodes,
edges, and onSelectTask unchanged.

In `@web/src/pages/tasks/TaskDetailPanel.stories.tsx`:
- Around line 39-47: Add an a11y test parameter to the Storybook meta for
TaskDetailPanel by updating the exported meta object (meta) to include
parameters.a11y.test set to the appropriate value ('error' | 'todo' | 'off') so
WCAG checks run for this story; modify the meta object near the TaskDetailPanel
declaration (used by Story/StoryObj) to add parameters: { layout: 'fullscreen',
a11y: { test: 'error' } } (or your chosen level) so accessibility testing is
enabled.

In `@web/src/pages/tasks/TaskDetailPanel.tsx`:
- Around line 52-59: The global Escape handler in the useEffect (handleKeyDown
-> onClose) should skip closing the panel when a confirmation dialog is open;
modify handleKeyDown to early-return if a ConfirmDialog is present or an active
modal should handle the key (e.g., detect presence of the ConfirmDialog DOM
marker or modal attributes such as role="dialog" or aria-modal="true" or a data
attribute the ConfirmDialog sets) before calling onClose; update detection logic
to check document.activeElement or
document.querySelector('[data-confirm-dialog], [role="dialog"],
[aria-modal="true"]') (or the specific class used by ConfirmDialog) so the panel
only closes on Escape when no modal/confirm dialog is open.

In `@web/src/pages/tasks/TaskListView.tsx`:
- Around line 38-50: Replace the hand-rolled card+header in TaskListView with
the shared SectionCard primitive: import SectionCard, wrap the current outer
<div className="rounded-lg border border-border"> with <SectionCard>, move the
header block (the div with "flex items-center..." that maps COLUMNS) into the
SectionCard's header slot (e.g., SectionCard.Header) preserving the COLUMNS.map
rendering and its className logic, and put the remaining table/body content into
SectionCard.Content so styling/structure come from the shared component rather
than custom markup.

In `@web/src/stores/tasks.ts`:
- Around line 125-135: optimisticTransition currently applies optimistic changes
but doesn't mark the task as "in-flight", allowing incoming WebSocket upserts to
overwrite it; update optimisticTransition to add taskId to a shared pending
Set<string> (e.g., pendingTransitions) when applying the optimistic update and
return a cleanup function that removes the taskId from that Set when the real
API completes or is rolled back; then update the WebSocket upsert/merge logic to
skip applying updates for any id present in pendingTransitions so optimistic UI
isn't clobbered (remember the caller/useOptimisticUpdate must call the returned
cleanup to clear the pending id on success/failure).

In `@web/src/utils/tasks.ts`:
- Around line 176-184: The dateTo handling currently concatenates
'T23:59:59.999Z' blindly which breaks when filters.dateTo is already a full ISO
timestamp; update the logic in the date range filter (the block using
filters.dateTo and result filter) to normalize/validate the input first: if
filters.dateTo contains a time component (e.g., contains 'T' or parses to a Date
with time != 00:00:00), use it as-is (or parse and use its exact end timestamp),
otherwise treat it as a date-only string and construct the end-of-day timestamp
(23:59:59.999) before filtering; ensure the comparison compares parsed ISO
strings or Date values consistently so the filter on result (t.deadline) works
for both date-only and full-timestamp inputs.

---

Duplicate comments:
In `@web/src/__tests__/hooks/useTaskBoardData.test.ts`:
- Around line 69-74: The test couples to the hook's internals by spying on
useTasksStore.getState().fetchTasks; instead, make the assertion at the
observable boundary: either spy on the API layer that fetchTasks calls (e.g.,
the HTTP client or fetch/axios mock) or assert the store/resulting state change
after renderHook (useTaskBoardData) — reference useTaskBoardData,
useTasksStore.getState(), and fetchTasks — replace
vi.spyOn(useTasksStore.getState(), 'fetchTasks') with a spy on the API call or
an assertion like expecting the tasks slice/state to be populated after waitFor.

In `@web/src/components/ui/task-status-indicator.stories.tsx`:
- Around line 47-56: The story PriorityBadges is using the wrong meta type
(StatusStory) and has a dead args.status while rendering PriorityBadge; change
the story to use a PriorityBadge-scoped meta/type (or move the story into a
PriorityBadge stories file) so its type matches the rendered component, remove
or replace the unused args.status, and ensure the story's props and controls
target PriorityBadge (referencing PriorityBadges, StatusStory, PriorityBadge,
and args.status to locate and update the code).

In `@web/src/pages/TaskBoardPage.tsx`:
- Around line 310-334: The current render condition for TaskDetailPanel uses
selectedTaskId && selectedTask which can briefly show stale data; change the
guard to only render when the loaded task matches the selected ID by checking
selectedTask?.id === selectedTaskId (e.g., replace the condition with
selectedTaskId && selectedTask && selectedTask.id === selectedTaskId or the
optional chaining variant) so TaskDetailPanel is only mounted when the task's id
equals selectedTaskId.

In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 103-126: Replace the hand-built card wrapper around the task
header with the shared SectionCard component to ensure consistent dashboard
styling: import and use SectionCard in place of the outer div with classes
"rounded-lg border border-border bg-card p-6 space-y-6", keeping the
ErrorBoundary, the inner structure (TaskStatusIndicator, PriorityBadge,
InlineEdit) and their handlers (the InlineEdit onSave that calls
useTasksStore.getState().updateTask and error handling via useToastStore)
unchanged; ensure any className on InlineEdit (text-xl font-semibold) is
preserved or moved into SectionCard props if required.

In `@web/src/pages/tasks/TaskListView.tsx`:
- Around line 17-25: The column headers are rendered inertly (COLUMNS) and
TaskListViewProps lacks any sort state or callback, so make headers interactive
by replacing the inert header elements in TaskListView with clickable elements
(e.g., button or role="button") that call a new sort handler; add sortKey and
sortDirection (or sortBy/sortOrder) to TaskListViewProps and expose an
onSortChange callback so parent components can control sorting, update
TaskListView to keep a local sort state if uncontrolled, toggle direction when
the same key is clicked, and add appropriate ARIA attributes (aria-sort) and
visual indicators for the active sorted column to the rendering logic for
COLUMNS and the header renderer.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: caadfbe6-eff0-4614-9a93-cb6405d3dbaa

📥 Commits

Reviewing files that changed from the base of the PR and between 3a8e679 and 63466bf.

📒 Files selected for processing (29)
  • docs/design/brand-and-ux.md
  • docs/design/page-structure.md
  • docs/design/ux-guidelines.md
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/helpers/factories.ts
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/components/ui/confirm-dialog.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/stores/tasks.ts
  • web/src/utils/tasks.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (11)
docs/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Use Zensical for Markdown documentation build with mkdocs.yml config; docs in docs/ directory; design spec in docs/design/ (11 pages)

Files:

  • docs/design/page-structure.md
  • docs/design/ux-guidelines.md
  • docs/design/brand-and-ux.md
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript 6+ with paths for module resolution (remove deprecated baseUrl); esModuleInterop is always true
In TypeScript, strict defaults to true and types defaults to []; do not auto-discover @types/* — must explicitly list needed types

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/components/ui/confirm-dialog.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/helpers/factories.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/utils/tasks.ts
  • web/src/stores/tasks.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from web/src/components/ui/
Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones
Use design tokens exclusively in web components: Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)); never hardcode hex values or pixel spacing
Use font-sans or font-mono (Geist tokens) in web components; never set fontFamily directly
Use density-aware spacing tokens in web components (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacing
Use shadow and border token variables in web components (var(--so-shadow-card-hover), border-border, border-bright); never hardcode shadow or border styles
In React components, import cn from @/lib/utils for conditional class merging; never concatenate class strings directly
Do not recreate status dots inline; use <StatusBadge> component
Do not build card-with-header layouts from scratch; use <SectionCard> component
Do not create metric displays with text-metric font-bold; use <MetricCard> component
Do not render initials circles manually; use <Avatar> component
Do not create complex (>8 line) JSX inside .map() blocks; extract to a shared component

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/components/ui/confirm-dialog.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/helpers/factories.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/utils/tasks.ts
  • web/src/stores/tasks.ts
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in pages/<page-name>/ subdirectories

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
web/**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/**/*.stories.{ts,tsx}: In Storybook, use parameters.backgrounds.options (object keyed by name) and initialGlobals.backgrounds.value for background configuration
In Storybook, use parameters.a11y.test: 'error' | 'todo' | 'off' for a11y testing configuration (replaces old .element and .manual)

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @eslint-react/eslint-plugin and eslint-plugin-security in web dashboard

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/components/ui/confirm-dialog.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/helpers/factories.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/utils/tasks.ts
  • web/src/stores/tasks.ts
web/**

📄 CodeRabbit inference engine (CLAUDE.md)

Use Node.js 22+ and npm for web dashboard; all dependencies in web/package.json (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, Recharts, Framer Motion, Axios, Lucide React, Geist fonts, Storybook 10, Vitest, ESLint, fast-check, etc.)

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/components/ui/confirm-dialog.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/helpers/factories.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/__tests__/utils/tasks.test.ts
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/utils/tasks.ts
  • web/src/stores/tasks.ts
web/src/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/__tests__/**/*.{ts,tsx}: Use fast-check for property-based testing in React; use fc.assert and fc.property
Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Files:

  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/helpers/factories.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/utils/tasks.test.ts
web/src/components/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When creating new shared React components, place them in web/src/components/ui/ with kebab-case filename, create accompanying .stories.tsx Storybook file with all states, export TypeScript interface for props, and use design tokens exclusively

Files:

  • web/src/components/ui/confirm-dialog.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
web/src/components/**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Storybook 10 (ESM-only) for React component documentation; import from storybook/test, storybook/actions; use definePreview from @storybook/react-vite

Files:

  • web/src/components/ui/task-status-indicator.stories.tsx
web/src/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)

Files:

  • web/src/stores/tasks.ts
🧠 Learnings (27)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/**/*.stories.{ts,tsx} : Use Storybook 10 (ESM-only) for React component documentation; import from `storybook/test`, `storybook/actions`; use `definePreview` from `storybook/react-vite`
📚 Learning: 2026-03-18T08:23:08.912Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T08:23:08.912Z
Learning: When approved deviations occur, update the relevant `docs/design/` page to reflect the new reality.

Applied to files:

  • docs/design/ux-guidelines.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/**/*.stories.{ts,tsx} : Use Storybook 10 (ESM-only) for React component documentation; import from `storybook/test`, `storybook/actions`; use `definePreview` from `storybook/react-vite`

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared React components, place them in `web/src/components/ui/` with kebab-case filename, create accompanying `.stories.tsx` Storybook file with all states, export TypeScript interface for props, and use design tokens exclusively

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/utils/tasks.ts
  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/**/*.stories.{ts,tsx} : In Storybook, use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing configuration (replaces old `.element` and `.manual`)

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/__tests__/utils/tasks.test.ts
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/**/*.stories.{ts,tsx} : In Storybook, use `parameters.backgrounds.options` (object keyed by name) and `initialGlobals.backgrounds.value` for background configuration

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/.storybook/**/*.{ts,tsx} : In Storybook, use type-safe config with `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite`; include explicit `framework` field

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskColumn.stories.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not recreate status dots inline; use `<StatusBadge>` component

Applied to files:

  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Applied to files:

  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/utils/tasks.test.ts
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use fast-check for property-based testing in React; use `fc.assert` and `fc.property`

Applied to files:

  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/utils/tasks.test.ts
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones

Applied to files:

  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/components/ui/task-status-indicator.stories.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/utils/tasks.ts
  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/helpers/factories.ts
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/utils/tasks.test.ts
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from `web/src/components/ui/`

Applied to files:

  • web/src/__tests__/components/ui/task-status-indicator.test.tsx
  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/tasks/TaskFilterBar.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/utils/tasks.ts
  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create complex (>8 line) JSX inside `.map()` blocks; extract to a shared component

Applied to files:

  • web/src/__tests__/pages/tasks/TaskColumn.test.tsx
  • web/src/pages/tasks/TaskListView.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)

Applied to files:

  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/stores/tasks.ts
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/.storybook/preview.tsx : In Storybook, set a11y testing globally in `preview.tsx` with `parameters.a11y.test: 'error'` to enforce WCAG compliance on all stories

Applied to files:

  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not build card-with-header layouts from scratch; use `<SectionCard>` component

Applied to files:

  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in `pages/<page-name>/` subdirectories

Applied to files:

  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create metric displays with `text-metric font-bold`; use `<MetricCard>` component

Applied to files:

  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/TaskDetailPage.tsx
  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use shadow and border token variables in web components (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`); never hardcode shadow or border styles

Applied to files:

  • web/src/pages/tasks/TaskCard.tsx
  • web/src/pages/TaskDetailPage.tsx
  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use density-aware spacing tokens in web components (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing; never hardcode pixel values for layout spacing

Applied to files:

  • web/src/pages/tasks/TaskCard.tsx
  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).

Applied to files:

  • web/src/pages/TaskBoardPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/** : Use Node.js 22+ and npm for web dashboard; all dependencies in `web/package.json` (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, Recharts, Framer Motion, Axios, Lucide React, Geist fonts, Storybook 10, Vitest, ESLint, fast-check, etc.)

Applied to files:

  • web/src/pages/TaskBoardPage.tsx
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).

Applied to files:

  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : In React components, import `cn` from `@/lib/utils` for conditional class merging; never concatenate class strings directly

Applied to files:

  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design tokens exclusively in web components: Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`, etc.) or CSS variables (`var(--so-accent)`); never hardcode hex values or pixel spacing

Applied to files:

  • docs/design/brand-and-ux.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)

Applied to files:

  • docs/design/brand-and-ux.md

Comment on lines +38 to +50
return (
<div className="rounded-lg border border-border">
{/* Table header */}
<div className="flex items-center gap-4 border-b border-border bg-surface px-4 py-2">
{COLUMNS.map((col) => (
<span
key={col.key}
className={cn('text-[11px] font-semibold uppercase tracking-wider text-text-muted', col.width)}
>
{col.label}
</span>
))}
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use SectionCard for this card-with-header container instead of hand-rolling it.

This file builds a custom bordered card + header shell that should be composed from the shared UI primitive.

As per coding guidelines, "Do not build card-with-header layouts from scratch; use <SectionCard> component."

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

In `@web/src/pages/tasks/TaskListView.tsx` around lines 38 - 50, Replace the
hand-rolled card+header in TaskListView with the shared SectionCard primitive:
import SectionCard, wrap the current outer <div className="rounded-lg border
border-border"> with <SectionCard>, move the header block (the div with "flex
items-center..." that maps COLUMNS) into the SectionCard's header slot (e.g.,
SectionCard.Header) preserving the COLUMNS.map rendering and its className
logic, and put the remaining table/body content into SectionCard.Content so
styling/structure come from the shared component rather than custom markup.

Data integrity:
- upsertTask now also updates selectedTask when IDs match (prevents stale version 409s)
- pendingTransitions Set prevents WS from overwriting optimistic updates
- dateTo filter handles both date-only and full ISO timestamp inputs

Error handling:
- handleCreateTask catches errors with toast (re-throws for dialog)
- TaskDetailPage cancel dialog now has cancelReason textarea
- TaskDetailPanel Escape handler skips when confirm dialog is open

Sortable list view:
- TaskListView headers are now interactive buttons with sort state
- Sort by status, title, assignee, priority, type, deadline, cost
- aria-sort attributes and visual indicators (arrow icons)

Code quality:
- TaskBoardPage panel guard checks selectedTask.id === selectedTaskId
- TaskDependencyGraph height prop (default 400px)
- Simplified budget_limit conversion (validate() handles correctness)
- Direct vi.mock object literal (no computed property)
- TaskDependencyGraph stories use action() and shared makeTask

Tests:
- TaskDetailPage loading tests use pending promise (deterministic)
- TaskCard deadline test uses fixed ISO string
- TaskDetailPanel delete test scopes to alertdialog with within()
- TaskListView keyboard activation tests (Enter + Space)
- useTaskBoardData test spies on API layer instead of store internals

Stories:
- TaskDetailPanel a11y test parameter
- TaskDependencyGraph uses shared factory + action()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 12:03 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (4)
web/src/pages/tasks/TaskListView.tsx (1)

74-109: 🧹 Nitpick | 🔵 Trivial

Consider the SectionCard guideline for this layout.

A previous review suggested using the shared SectionCard component. While this is a table-like structure rather than a typical content card, evaluate whether SectionCard could provide consistent styling. If the table structure is intentional and distinct from card layouts, the current implementation with design tokens (border-border, bg-surface) is acceptable.

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

In `@web/src/pages/tasks/TaskListView.tsx` around lines 74 - 109, The outer
container in TaskListView uses a plain div with design tokens but should use the
shared SectionCard for consistent styling; replace the top-level <div
className="rounded-lg border border-border"> with the SectionCard component
(import SectionCard) and move the header and StaggerGroup contents as its
children, preserving existing className props (border, bg-surface, etc.) on the
header wrapper and keeping COLUMNS, handleSort, sortKey, sortDir logic and the
TaskListRow/StaggerGroup structure intact; if the table semantics are
intentionally different, add a brief comment in TaskListView explaining why the
plain div is required instead of SectionCard.
web/src/pages/TaskBoardPage.tsx (1)

77-85: ⚠️ Potential issue | 🟠 Major

Department is still missing from the URL-backed filter state.

Phase 4.4 requires department filter persistence, but this page only round-trips status, priority, assignee, type, search, dateFrom, and dateTo. A department selection still won’t survive reloads or shared links.

Also applies to: 103-124

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

In `@web/src/pages/TaskBoardPage.tsx` around lines 77 - 85, The TaskBoard
URL-backed filters are missing the department key: update the TaskBoardFilters
creation inside the useMemo where `filters` is built to include `department:
searchParams.get('department') || undefined` (or cast to the appropriate
Department type) so the department selection round-trips in the URL; ensure the
`TaskBoardFilters` type/interface includes `department` and update any related
places that read/write `filters` (the useMemo block building `filters` and any
code that reads `searchParams` for other filter keys).
web/src/pages/TaskDetailPage.tsx (1)

105-128: 🛠️ Refactor suggestion | 🟠 Major

Reuse SectionCard for the main detail shell.

This is a hand-built card-with-header layout, so its padding, border, and header treatment can drift from the shared dashboard shell. Please move this block onto the shared SectionCard instead of styling the container manually.

As per coding guidelines: "Do not build card-with-header layouts from scratch; use <SectionCard> component".

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

In `@web/src/pages/TaskDetailPage.tsx` around lines 105 - 128, Replace the
hand-built card container with the shared SectionCard component: remove the
outer div that has className="rounded-lg border border-border bg-card p-6
space-y-6" and wrap the existing header and InlineEdit block inside
<SectionCard> (preserving the ErrorBoundary wrapper and children), passing any
needed header props or children (keep TaskStatusIndicator, PriorityBadge,
InlineEdit as-is). Ensure the InlineEdit onSave still calls
useTasksStore.getState().updateTask(task.id, { title: value, expected_version:
task.version }) and that toast/error handling using useToastStore.getState().add
and getErrorMessage remains unchanged. Verify styling/spacing by relying on
SectionCard’s API instead of manual classes so the header layout matches the
shared dashboard shell.
web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx (1)

152-158: ⚠️ Potential issue | 🟡 Minor

Scope the cancel confirmation click to the dialog.

After the modal opens there are two “Cancel Task” buttons in the tree, so the unscoped screen.getByRole(...) can become ambiguous. Query within the alertdialog, like the delete test already does.

🧪 Suggested fix
     await user.click(screen.getByRole('button', { name: 'Cancel Task' }))
-    // Fill in reason
-    const reasonInput = screen.getByLabelText('Cancellation reason')
+    const dialog = screen.getByRole('alertdialog')
+    const reasonInput = within(dialog).getByLabelText('Cancellation reason')
     await user.type(reasonInput, 'No longer needed')
-    // Confirm
-    await user.click(screen.getByRole('button', { name: 'Cancel Task' }))
+    await user.click(within(dialog).getByRole('button', { name: 'Cancel Task' }))
     expect(onCancel).toHaveBeenCalledWith('task-1', { reason: 'No longer needed' })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx` around lines 152 -
158, The test clicks the confirmation "Cancel Task" button with an unscoped
screen.getByRole which is ambiguous because there are two matching buttons;
update the confirmation click to query within the opened dialog (use the
alertdialog container returned by screen.getByRole('alertdialog') or similar)
and call user.click on the dialog-scoped getByRole to target the modal's "Cancel
Task" button (keep the existing reason input and final expect using onCancel).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/pages/TaskBoardPage.tsx`:
- Around line 68-75: handleSelectTask is triggering fetchTask immediately and
the selectedTaskId useEffect calls fetchTask again when the URL/store updates;
fix by adding a skip-next-fetch flag: introduce a ref (e.g., skipNextFetchRef)
that handleSelectTask sets true before it updates the search param/store, and
update the selectedTaskId effect to check skipNextFetchRef.current — if true,
clear it and skip calling fetchTask; apply the same pattern to the other select
handler mentioned (the block referenced around handleSelectTask) so only one
fetchTask runs per selection.
- Around line 324-339: The wrapper callbacks onUpdate, onTransition, onCancel
and onDelete in the TaskDetailPanel invocation are duplicating error toasts and
prematurely closing the drawer; remove the try/catch/toast logic and the
handleClosePanel call so the panel's own handlers control UX. Replace the
current bodies for onUpdate/onTransition/onCancel with direct async calls (e.g.,
await updateTask(id, data)) without catching or calling useToastStore, and for
onDelete call await deleteTask(id) only (no handleClosePanel or toast), leaving
TaskDetailPanel to display toasts and close itself.

In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 1-19: The acceptance-criteria marker classes in TaskDetailPage.tsx
are built via template literals instead of the shared class-merging helper;
import cn from '@/lib/utils' at the top and replace string
concatenation/template literal usages that build the marker classes (see the
acceptance-criteria marker usage around lines referenced by the reviewer and the
TaskDetailPage component) with cn(...) calls to conditionally merge classes;
ensure any conditional class fragments (e.g., status-based or visibility
classes) are passed as separate arguments or objects to cn and remove manual '+'
or template interpolation so the component uses the unified class-merging
pattern.

In `@web/src/pages/tasks/TaskDetailPanel.tsx`:
- Around line 75-83: The cancel handler (handleCancel) currently calls onCancel
even when cancelReason is empty; add a guard that validates cancelReason.trim()
is non-empty before invoking onCancel, and if empty call
useToastStore.getState().add(...) with an error message and return early; apply
the same guard to the duplicate cancel flow further down (the other handler
around the 305-320 logic) and only clear setCancelReason('') and close the
dialog (setCancelOpen(false)) after a successful onCancel call.

---

Duplicate comments:
In `@web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx`:
- Around line 152-158: The test clicks the confirmation "Cancel Task" button
with an unscoped screen.getByRole which is ambiguous because there are two
matching buttons; update the confirmation click to query within the opened
dialog (use the alertdialog container returned by
screen.getByRole('alertdialog') or similar) and call user.click on the
dialog-scoped getByRole to target the modal's "Cancel Task" button (keep the
existing reason input and final expect using onCancel).

In `@web/src/pages/TaskBoardPage.tsx`:
- Around line 77-85: The TaskBoard URL-backed filters are missing the department
key: update the TaskBoardFilters creation inside the useMemo where `filters` is
built to include `department: searchParams.get('department') || undefined` (or
cast to the appropriate Department type) so the department selection round-trips
in the URL; ensure the `TaskBoardFilters` type/interface includes `department`
and update any related places that read/write `filters` (the useMemo block
building `filters` and any code that reads `searchParams` for other filter
keys).

In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 105-128: Replace the hand-built card container with the shared
SectionCard component: remove the outer div that has className="rounded-lg
border border-border bg-card p-6 space-y-6" and wrap the existing header and
InlineEdit block inside <SectionCard> (preserving the ErrorBoundary wrapper and
children), passing any needed header props or children (keep
TaskStatusIndicator, PriorityBadge, InlineEdit as-is). Ensure the InlineEdit
onSave still calls useTasksStore.getState().updateTask(task.id, { title: value,
expected_version: task.version }) and that toast/error handling using
useToastStore.getState().add and getErrorMessage remains unchanged. Verify
styling/spacing by relying on SectionCard’s API instead of manual classes so the
header layout matches the shared dashboard shell.

In `@web/src/pages/tasks/TaskListView.tsx`:
- Around line 74-109: The outer container in TaskListView uses a plain div with
design tokens but should use the shared SectionCard for consistent styling;
replace the top-level <div className="rounded-lg border border-border"> with the
SectionCard component (import SectionCard) and move the header and StaggerGroup
contents as its children, preserving existing className props (border,
bg-surface, etc.) on the header wrapper and keeping COLUMNS, handleSort,
sortKey, sortDir logic and the TaskListRow/StaggerGroup structure intact; if the
table semantics are intentionally different, add a brief comment in TaskListView
explaining why the plain div is required instead of SectionCard.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 89e24853-732f-4411-87d9-8d1879a8df61

📥 Commits

Reviewing files that changed from the base of the PR and between 63466bf and 5fa7d3f.

📒 Files selected for processing (16)
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/stores/tasks.ts
  • web/src/utils/tasks.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript 6+ with paths for module resolution (remove deprecated baseUrl); esModuleInterop is always true
In TypeScript, strict defaults to true and types defaults to []; do not auto-discover @types/* — must explicitly list needed types

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/stores/tasks.ts
  • web/src/utils/tasks.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from web/src/components/ui/
Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones
Use design tokens exclusively in web components: Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)); never hardcode hex values or pixel spacing
Use font-sans or font-mono (Geist tokens) in web components; never set fontFamily directly
Use density-aware spacing tokens in web components (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacing
Use shadow and border token variables in web components (var(--so-shadow-card-hover), border-border, border-bright); never hardcode shadow or border styles
In React components, import cn from @/lib/utils for conditional class merging; never concatenate class strings directly
Do not recreate status dots inline; use <StatusBadge> component
Do not build card-with-header layouts from scratch; use <SectionCard> component
Do not create metric displays with text-metric font-bold; use <MetricCard> component
Do not render initials circles manually; use <Avatar> component
Do not create complex (>8 line) JSX inside .map() blocks; extract to a shared component

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/stores/tasks.ts
  • web/src/utils/tasks.ts
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in pages/<page-name>/ subdirectories

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/pages/tasks/TaskListView.tsx
web/**/*.stories.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/**/*.stories.{ts,tsx}: In Storybook, use parameters.backgrounds.options (object keyed by name) and initialGlobals.backgrounds.value for background configuration
In Storybook, use parameters.a11y.test: 'error' | 'todo' | 'off' for a11y testing configuration (replaces old .element and .manual)

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @eslint-react/eslint-plugin and eslint-plugin-security in web dashboard

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/stores/tasks.ts
  • web/src/utils/tasks.ts
web/**

📄 CodeRabbit inference engine (CLAUDE.md)

Use Node.js 22+ and npm for web dashboard; all dependencies in web/package.json (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, Recharts, Framer Motion, Axios, Lucide React, Geist fonts, Storybook 10, Vitest, ESLint, fast-check, etc.)

Files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/stores/tasks.ts
  • web/src/utils/tasks.ts
web/src/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/__tests__/**/*.{ts,tsx}: Use fast-check for property-based testing in React; use fc.assert and fc.property
Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Files:

  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
web/src/stores/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)

Files:

  • web/src/stores/tasks.ts
🧠 Learnings (19)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/**/*.stories.{ts,tsx} : Use Storybook 10 (ESM-only) for React component documentation; import from `storybook/test`, `storybook/actions`; use `definePreview` from `storybook/react-vite`
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/**/*.stories.{ts,tsx} : Use Storybook 10 (ESM-only) for React component documentation; import from `storybook/test`, `storybook/actions`; use `definePreview` from `storybook/react-vite`

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/**/*.stories.{ts,tsx} : In Storybook, use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing configuration (replaces old `.element` and `.manual`)

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared React components, place them in `web/src/components/ui/` with kebab-case filename, create accompanying `.stories.tsx` Storybook file with all states, export TypeScript interface for props, and use design tokens exclusively

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/**/*.stories.{ts,tsx} : In Storybook, use `parameters.backgrounds.options` (object keyed by name) and `initialGlobals.backgrounds.value` for background configuration

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/.storybook/**/*.{ts,tsx} : In Storybook, use type-safe config with `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite`; include explicit `framework` field

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.stories.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskListView.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Applied to files:

  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use fast-check for property-based testing in React; use `fc.assert` and `fc.property`

Applied to files:

  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/pages/tasks/TaskListView.test.tsx
  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)

Applied to files:

  • web/src/__tests__/hooks/useTaskBoardData.test.ts
  • web/src/pages/TaskBoardPage.tsx
  • web/src/stores/tasks.ts
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/.storybook/preview.tsx : In Storybook, set a11y testing globally in `preview.tsx` with `parameters.a11y.test: 'error'` to enforce WCAG compliance on all stories

Applied to files:

  • web/src/pages/tasks/TaskDetailPanel.stories.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not build card-with-header layouts from scratch; use `<SectionCard>` component

Applied to files:

  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskListView.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create metric displays with `text-metric font-bold`; use `<MetricCard>` component

Applied to files:

  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from `web/src/components/ui/`

Applied to files:

  • web/src/__tests__/pages/tasks/TaskCard.test.tsx
  • web/src/pages/tasks/TaskDependencyGraph.tsx
  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskCreateDialog.tsx
  • web/src/__tests__/pages/TaskBoardPage.test.tsx
  • web/src/pages/tasks/TaskListView.tsx
  • web/src/utils/tasks.ts
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use density-aware spacing tokens in web components (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing; never hardcode pixel values for layout spacing

Applied to files:

  • web/src/pages/tasks/TaskDependencyGraph.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in `pages/<page-name>/` subdirectories

Applied to files:

  • web/src/__tests__/pages/TaskDetailPage.test.tsx
  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use shadow and border token variables in web components (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`); never hardcode shadow or border styles

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskListView.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create complex (>8 line) JSX inside `.map()` blocks; extract to a shared component

Applied to files:

  • web/src/pages/tasks/TaskListView.tsx
🔇 Additional comments (17)
web/src/__tests__/pages/tasks/TaskCard.test.tsx (4)

1-13: Strong fixture setup and typing.

The test factory wrapper is clean, deterministic, and keeps per-test overrides focused while preserving type safety.


15-49: Rendering and conditional-state coverage looks solid.

These cases exercise the key visible variants (title/description, assignee, priority, dependency presence/absence) effectively.


51-72: Interaction and accessibility checks are well covered.

Click + keyboard activation and the accessible task label assertion provide good confidence for core card interaction behavior.


74-89: Edge-case display tests are clear and stable.

Cost visibility boundaries and fixed-date deadline rendering checks are deterministic and appropriately scoped.

web/src/__tests__/pages/tasks/TaskListView.test.tsx (1)

1-63: LGTM!

The test suite comprehensively covers the TaskListView component: header rendering, row display, null assignee handling, click and keyboard interactions (Enter/Space), and empty state. The keyboard accessibility tests at lines 39-57 properly address the earlier review feedback.

web/src/pages/tasks/TaskCreateDialog.tsx (2)

103-115: LGTM! Budget validation properly implemented.

The validate() function now correctly checks for non-negative finite numbers when budget_limit is provided, addressing the earlier review feedback about potential NaN values.


117-142: LGTM! Submit handler is well-structured.

The handleSubmit correctly validates before submission, builds a clean payload with trimmed strings and proper type conversions, handles errors gracefully, and resets form state on success. The simplified budget_limit conversion at line 131 appropriately relies on the upstream validation.

web/src/stores/tasks.ts (2)

45-54: Pending transition tracking correctly implemented.

The pendingTransitions Set at the module level with the state reference at line 54 correctly prevents WebSocket events from overwriting optimistic updates. The guard at line 118 (if (pendingTransitions.has(candidate.id as string)) return) and cleanup in upsertTask at line 147 address the race condition flagged in previous reviews.

Note: The caller of optimisticTransition must invoke the returned rollback function or call the actual transition API (which calls upsertTask) to ensure pending IDs are cleared, allowing future WS updates to apply.


146-159: LGTM! upsertTask properly updates selectedTask when IDs match.

Line 152 ensures the detail panel stays in sync when a task is updated. The total increment logic at line 156 correctly only increments for new insertions.

web/src/__tests__/hooks/useTaskBoardData.test.ts (1)

1-75: LGTM!

The test suite properly addresses previous review feedback:

  • Uses waitFor instead of fragile empty act() calls (line 46)
  • Uses dynamic import for the API spy to ensure it's set up correctly before the hook runs (lines 70-71)

The mock setup and store reset pattern are appropriate for testing this hook.

web/src/utils/tasks.ts (2)

181-184: LGTM! Date range filtering properly handles both date-only and ISO timestamp inputs.

The dateTo normalization at line 182 correctly checks for an existing time component before appending the end-of-day timestamp, addressing the earlier review feedback.


1-209: Well-structured utility module.

The centralized mappings for status/priority colors and labels, Kanban column definitions, filtering logic, and transition validation provide a clean single source of truth for task-board logic. The derived STATUS_TO_COLUMN mapping ensures consistency with KANBAN_COLUMNS.

web/src/pages/tasks/TaskDependencyGraph.tsx (2)

7-11: LGTM! Height is now configurable.

The optional height prop with a sensible default (line 10, 80) and proper number-to-pixel conversion (line 92) addresses the earlier review feedback for responsive height handling.

Also applies to: 80-80, 92-92


13-78: Well-implemented graph building logic.

The buildGraph function correctly filters to tasks with dependency relationships, handles the case of no relevant tasks (line 32), and builds nodes/edges with design token CSS variables for consistent styling. The non-null assertion at line 70 is safe due to the taskMap.has(depId) guard at line 63.

web/src/pages/tasks/TaskDependencyGraph.stories.tsx (1)

1-43: LGTM!

The stories file properly addresses previous review feedback:

  • Uses the shared makeTask factory from @/__tests__/helpers/factories (line 4)
  • Uses action('onSelectTask') from storybook/actions for Actions panel integration (lines 24, 34, 41)

The three story variants (WithDependencies, NoDependencies, Empty) provide good coverage of the component's states.

web/src/pages/tasks/TaskListView.tsx (2)

23-31: LGTM! Sorting implementation is complete and accessible.

The sortable columns with state management (lines 48-58), compareTasks function (lines 33-45), and interactive header buttons with aria-sort attributes (line 88) fully address the earlier review feedback about missing sort functionality. The accessibility attributes are properly applied.

Also applies to: 47-62


112-156: LGTM! Row component properly extracted.

TaskListRow cleanly encapsulates row rendering with keyboard accessibility (role="button", tabIndex, onKeyDown handler for Enter/Space), addressing the earlier feedback about complex JSX in .map() blocks. The formatCurrency(task.cost_usd, 'USD') at line 153 correctly passes the currency code.

Comment on lines +75 to +83
const handleCancel = useCallback(async () => {
try {
await onCancel(task.id, { reason: cancelReason })
setCancelOpen(false)
setCancelReason('')
} catch {
useToastStore.getState().add({ variant: 'error', title: 'Failed to cancel task' })
}
}, [task.id, cancelReason, onCancel])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Block empty cancellation reasons before calling onCancel.

The dialog says a reason is required, but this path will happily submit '' on the first confirm click. That loses the audit trail and can also trip backend validation if empty reasons are rejected.

🛠️ Suggested fix
   const handleCancel = useCallback(async () => {
+    const reason = cancelReason.trim()
+    if (!reason) {
+      useToastStore.getState().add({ variant: 'error', title: 'Cancellation reason is required' })
+      return
+    }
     try {
-      await onCancel(task.id, { reason: cancelReason })
+      await onCancel(task.id, { reason })
       setCancelOpen(false)
       setCancelReason('')
     } catch {

Also applies to: 305-320

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

In `@web/src/pages/tasks/TaskDetailPanel.tsx` around lines 75 - 83, The cancel
handler (handleCancel) currently calls onCancel even when cancelReason is empty;
add a guard that validates cancelReason.trim() is non-empty before invoking
onCancel, and if empty call useToastStore.getState().add(...) with an error
message and return early; apply the same guard to the duplicate cancel flow
further down (the other handler around the 305-320 logic) and only clear
setCancelReason('') and close the dialog (setCancelOpen(false)) after a
successful onCancel call.

- Eliminate double fetchTask on task selection (skip ref prevents
  useEffect from re-fetching after handleSelectTask already fetched)
- Simplify panel wrapper callbacks to plain pass-through (panel's own
  handlers control toasts and close, removing duplicate error toasts)
- Use cn() for acceptance criteria marker classes in TaskDetailPage
- Validate cancelReason is non-empty before submitting in TaskDetailPanel
- Scope cancel confirmation test to alertdialog with within()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 12:16 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
web/src/pages/TaskDetailPage.tsx (1)

106-129: 🧹 Nitpick | 🔵 Trivial

Reuse SectionCard for the main details shell.

This hand-built card-with-header layout duplicates styling that SectionCard provides. As per coding guidelines, prefer reusing existing components from web/src/components/ui/.

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

In `@web/src/pages/TaskDetailPage.tsx` around lines 106 - 129, The current
TaskDetailPage hand-builds the card/header UI around the task details (the <div
className="rounded-lg ..."> containing TaskStatusIndicator, PriorityBadge and
InlineEdit) which duplicates styling provided by the existing SectionCard
component; replace that outer div with the SectionCard from
web/src/components/ui/, moving the header content (TaskStatusIndicator,
PriorityBadge, and the InlineEdit for task.title) into SectionCard's header
slot/props while preserving the surrounding ErrorBoundary and the InlineEdit
onSave logic that calls useTasksStore.getState().updateTask(task.id, { title:
value, expected_version: task.version }) and error toast handling via
useToastStore.getState().add(...); ensure className/spacing and accessibility
are preserved and that other children of the original card remain as SectionCard
body children.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx`:
- Around line 39-161: Add a test that verifies validation when cancelling:
render TaskDetailPanel with a mocked onCancel, open the cancel confirmation by
clicking the "Cancel Task" button, do not fill the "Cancellation reason" input,
then click the confirm button inside the alertdialog and assert that the
onCancel mock was NOT called and (if your toast store is mockable) that an error
toast was shown; use userEvent.setup(), query the dialog via
getByRole('alertdialog'), and target the confirm button via
within(dialog).getByRole('button', { name: 'Cancel Task' }) to avoid footer
ambiguity.

In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 70-79: The handler handleCancel currently falls back to 'Cancelled
by user' when cancelReason is empty; change it to require a non-empty reason
(matching TaskDetailPanel.handleCancel) by checking cancelReason.trim() and, if
empty, call useToastStore.getState().add(...) with a validation error and return
early; only call useTasksStore.getState().cancelTask(task.id, { reason:
cancelReason.trim() }) when the reason is non-empty, and keep the existing
setCancelOpen(false) and setCancelReason('') behavior after a successful cancel.

In `@web/src/pages/tasks/TaskDetailPanel.tsx`:
- Around line 259-264: The list mapping over task.acceptance_criteria uses
criterion.description as the React key which is unstable when descriptions
repeat; update the key prop in the map inside TaskDetailPanel (the mapping that
renders <li key={...} ...>) to use a stable identifier: prefer criterion.id if
the API provides it, otherwise fall back to the array index (e.g.,
key={criterion.id ?? index}) so keys remain unique and stable across renders.

---

Duplicate comments:
In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 106-129: The current TaskDetailPage hand-builds the card/header UI
around the task details (the <div className="rounded-lg ..."> containing
TaskStatusIndicator, PriorityBadge and InlineEdit) which duplicates styling
provided by the existing SectionCard component; replace that outer div with the
SectionCard from web/src/components/ui/, moving the header content
(TaskStatusIndicator, PriorityBadge, and the InlineEdit for task.title) into
SectionCard's header slot/props while preserving the surrounding ErrorBoundary
and the InlineEdit onSave logic that calls
useTasksStore.getState().updateTask(task.id, { title: value, expected_version:
task.version }) and error toast handling via useToastStore.getState().add(...);
ensure className/spacing and accessibility are preserved and that other children
of the original card remain as SectionCard body children.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 62523149-5417-4b12-a3b3-1fadea212084

📥 Commits

Reviewing files that changed from the base of the PR and between 5fa7d3f and a749c3d.

📒 Files selected for processing (4)
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript 6+ with paths for module resolution (remove deprecated baseUrl); esModuleInterop is always true
In TypeScript, strict defaults to true and types defaults to []; do not auto-discover @types/* — must explicitly list needed types

Files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from web/src/components/ui/
Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones
Use design tokens exclusively in web components: Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)); never hardcode hex values or pixel spacing
Use font-sans or font-mono (Geist tokens) in web components; never set fontFamily directly
Use density-aware spacing tokens in web components (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacing
Use shadow and border token variables in web components (var(--so-shadow-card-hover), border-border, border-bright); never hardcode shadow or border styles
In React components, import cn from @/lib/utils for conditional class merging; never concatenate class strings directly
Do not recreate status dots inline; use <StatusBadge> component
Do not build card-with-header layouts from scratch; use <SectionCard> component
Do not create metric displays with text-metric font-bold; use <MetricCard> component
Do not render initials circles manually; use <Avatar> component
Do not create complex (>8 line) JSX inside .map() blocks; extract to a shared component

Files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
web/src/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/__tests__/**/*.{ts,tsx}: Use fast-check for property-based testing in React; use fc.assert and fc.property
Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @eslint-react/eslint-plugin and eslint-plugin-security in web dashboard

Files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
web/**

📄 CodeRabbit inference engine (CLAUDE.md)

Use Node.js 22+ and npm for web dashboard; all dependencies in web/package.json (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, Recharts, Framer Motion, Axios, Lucide React, Geist fonts, Storybook 10, Vitest, ESLint, fast-check, etc.)

Files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in pages/<page-name>/ subdirectories

Files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/**/*.stories.{ts,tsx} : Use Storybook 10 (ESM-only) for React component documentation; import from `storybook/test`, `storybook/actions`; use `definePreview` from `storybook/react-vite`
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use fast-check for property-based testing in React; use `fc.assert` and `fc.property`

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/**/*.stories.{ts,tsx} : In Storybook, use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing configuration (replaces old `.element` and `.manual`)

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from `web/src/components/ui/`

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/TaskBoardPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in `pages/<page-name>/` subdirectories

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not build card-with-header layouts from scratch; use `<SectionCard>` component

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/TaskBoardPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create metric displays with `text-metric font-bold`; use `<MetricCard>` component

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use shadow and border token variables in web components (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`); never hardcode shadow or border styles

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : In React components, import `cn` from `@/lib/utils` for conditional class merging; never concatenate class strings directly

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design tokens exclusively in web components: Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`, etc.) or CSS variables (`var(--so-accent)`); never hardcode hex values or pixel spacing

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create complex (>8 line) JSX inside `.map()` blocks; extract to a shared component

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
🔇 Additional comments (21)
web/src/pages/tasks/TaskDetailPanel.tsx (6)

1-16: LGTM! Clean imports and proper use of shared UI components.

The component correctly reuses existing UI components (Button, InlineEdit, ConfirmDialog, TaskStatusIndicator, PriorityBadge, Avatar) from @/components/ui/ and imports cn for class merging, following project guidelines.


52-62: LGTM! Escape handler properly guards against open dialogs.

The implementation correctly checks for [role="alertdialog"] or [role="dialog"][aria-modal="true"] before closing the panel, preventing conflicts with confirmation dialogs.


75-87: LGTM! Cancel reason validation implemented correctly.

The handleCancel callback properly validates that cancelReason.trim() is non-empty before proceeding, showing an error toast and returning early if validation fails. This addresses the requirement for capturing audit-trail reasons.


178-194: Priority select error handling is adequate.

The async onChange handler wraps the onUpdate call in try/catch with toast feedback. While unmounting during the await could theoretically cause issues, the error is caught and the store manages the actual state, making this acceptable.


308-325: LGTM! Cancel dialog correctly implements reason input.

The ConfirmDialog now includes a textarea child for capturing the cancellation reason, and the onOpenChange handler properly clears the reason when the dialog closes. This aligns with the ConfirmDialog API that supports children prop for custom content.


341-351: LGTM! Clean helper component with proper accessibility.

The MetaField component correctly uses design tokens and sets aria-hidden="true" on decorative icons.

web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx (4)

1-5: LGTM! Clean test imports.

The test file properly imports from @testing-library/react and userEvent for interaction testing.


6-35: LGTM! Comprehensive test fixture.

The mockTask object covers all Task fields needed for the component tests, including version: 2 which is correctly asserted in the transition test.


135-146: LGTM! Delete test correctly scopes button selection.

The test uses within(dialog).getByRole('button', { name: 'Delete' }) to avoid ambiguity between the footer button and the dialog's confirm button.


148-160: LGTM! Cancel test validates reason input flow.

The test correctly types into the reason textarea, scopes the confirm button to the dialog, and asserts onCancel receives the expected payload with the typed reason.

web/src/pages/TaskDetailPage.tsx (4)

1-19: LGTM! Imports are well-organized.

The component correctly imports cn from @/lib/utils and reuses shared UI components. Note that useState and useCallback are imported on line 19, separate from the line 1 import of useEffect — consider consolidating for cleaner imports.


35-41: LGTM! Task ID gating prevents stale data.

The implementation correctly derives task only when selectedTask?.id === taskId, preventing display of stale data during route transitions.


217-225: LGTM! Uses cn() for conditional classes.

The acceptance criteria marker correctly uses cn() for conditional class merging instead of template literals, following project guidelines.


265-281: LGTM! Cancel dialog correctly implements reason capture.

The ConfirmDialog includes the textarea child for the cancellation reason and properly manages state via onOpenChange.

web/src/pages/TaskBoardPage.tsx (7)

1-36: LGTM! Well-organized imports with proper lazy loading.

The imports are cleanly organized, and TaskDependencyGraph is correctly lazy-loaded with a proper Suspense boundary later in the component.


68-80: LGTM! Skip-fetch pattern prevents duplicate network requests.

The skipNextFetchRef pattern correctly prevents the double-fetch that would otherwise occur when handleSelectTask both sets the URL param and calls fetchTask.


82-90: LGTM! Date filters now persist to URL.

The filters memo correctly parses dateFrom and dateTo from search params, enabling URL persistence for date range filters.


178-210: LGTM! Drag-and-drop with proper validation and error feedback.

The handler validates transitions via canTransitionTo, shows a warning toast for invalid moves, and properly surfaces failed optimistic updates with an error toast.


324-336: LGTM! Panel callbacks are clean passthroughs.

The callbacks correctly delegate to store actions without adding extra error handling, allowing TaskDetailPanel to own its UX (addresses the past review about duplicate toasts).


148-157: LGTM! Task selection correctly coordinates fetch and URL update.

Setting skipNextFetchRef.current = true before updating search params ensures the effect won't trigger a duplicate fetch, while the explicit fetchTask(taskId) call ensures the task is fetched immediately on user click.


237-255: 🧹 Nitpick | 🔵 Trivial

Consider using a shared Checkbox component if available.

The native <input type="checkbox"> works but may not match the design system styling. If a Checkbox component exists in @/components/ui/, consider using it for visual consistency.

⛔ Skipped due to learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared React components, place them in `web/src/components/ui/` with kebab-case filename, create accompanying `.stories.tsx` Storybook file with all states, export TypeScript interface for props, and use design tokens exclusively

Comment on lines +259 to +264
{task.acceptance_criteria.map((criterion) => (
<li key={criterion.description} className="flex items-start gap-2 text-xs text-text-secondary">
<span className={cn('mt-0.5 size-3.5 shrink-0 rounded border', criterion.met ? 'border-success bg-success/20' : 'border-border')} />
{criterion.description}
</li>
))}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider using a stable key instead of description.

Using criterion.description as the key could cause rendering issues if two criteria have identical descriptions. If the API provides an id field for criteria, prefer that. Otherwise, consider using the index as a fallback.

💡 Suggested change
-                    {task.acceptance_criteria.map((criterion) => (
-                      <li key={criterion.description} className="flex items-start gap-2 text-xs text-text-secondary">
+                    {task.acceptance_criteria.map((criterion, idx) => (
+                      <li key={criterion.id ?? idx} className="flex items-start gap-2 text-xs text-text-secondary">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/tasks/TaskDetailPanel.tsx` around lines 259 - 264, The list
mapping over task.acceptance_criteria uses criterion.description as the React
key which is unstable when descriptions repeat; update the key prop in the map
inside TaskDetailPanel (the mapping that renders <li key={...} ...>) to use a
stable identifier: prefer criterion.id if the API provides it, otherwise fall
back to the array index (e.g., key={criterion.id ?? index}) so keys remain
unique and stable across renders.

- TaskDetailPage handleCancel validates non-empty reason (matches panel)
- Use index key for acceptance_criteria map (no id on AcceptanceCriterion)
- Add test for cancel rejection when reason is empty

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 12:28 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
web/src/pages/TaskDetailPage.tsx (1)

110-133: 🧹 Nitpick | 🔵 Trivial

Consider using SectionCard for the main details shell.

This hand-built card layout (rounded-lg border border-border bg-card p-6 space-y-6) replicates what SectionCard provides. Per coding guidelines: "Do not build card-with-header layouts from scratch; use <SectionCard> component."

♻️ Suggested refactor using SectionCard
+import { SectionCard } from '@/components/ui/section-card'
 ...
       <ErrorBoundary level="section">
-        <div className="rounded-lg border border-border bg-card p-6 space-y-6">
+        <SectionCard>
           {/* Header */}
           <div className="flex items-start justify-between">
             ...
           </div>
           ...
-        </div>
+        </SectionCard>
       </ErrorBoundary>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/TaskDetailPage.tsx` around lines 110 - 133, The TaskDetailPage
renders a custom container for the main task details (the div with classes
"rounded-lg border border-border bg-card p-6 space-y-6") — replace that manual
shell with the existing SectionCard component to follow the guideline; update
the JSX to wrap the header and InlineEdit/other children inside <SectionCard>
(keeping TaskStatusIndicator, PriorityBadge, InlineEdit and their props
unchanged) and remove the manual utility classes so the layout uses
SectionCard's built-in styling and header structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 222-228: TaskDetailPage currently maps task.acceptance_criteria
using the array index as the React key (in the JSX inside the component where
acceptance_criteria.map is rendered); because AcceptanceCriterion has no stable
id this is acceptable for read-only rendering but inconsistent with lint
rules—add a lint suppression comment (e.g. eslint-disable-next-line
react/no-array-index-key) immediately above the acceptance_criteria.map
expression to silence the rule and document the intent, referencing the
TaskDetailPage component and the acceptance_criteria.map usage so reviewers know
this is intentional.
- Around line 1-19: Consolidate duplicate React hook imports by replacing the
separate imports of useEffect and the later useState/useCallback with a single
import from 'react' (e.g. import { useEffect, useState, useCallback } from
'react'); update the top of the file to remove the separate import that declares
useState/useCallback and keep only the unified import so symbols useEffect,
useState, and useCallback are all imported from the same statement.

In `@web/src/pages/tasks/TaskDetailPanel.tsx`:
- Around line 259-265: The acceptance criteria list in TaskDetailPanel uses the
array index as the React key (task.acceptance_criteria.map((criterion, idx) =>
... key={idx}) which is intentional because AcceptanceCriterion has no id and
the list is read-only; add an inline lint suppression comment above the map to
document and silence the rule (e.g., eslint-disable-next-line
react/no-array-index-key) and include a short comment explaining why using the
index is safe here (read-only, no reordering/deletes) so future readers know
this is intentional.

---

Duplicate comments:
In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 110-133: The TaskDetailPage renders a custom container for the
main task details (the div with classes "rounded-lg border border-border bg-card
p-6 space-y-6") — replace that manual shell with the existing SectionCard
component to follow the guideline; update the JSX to wrap the header and
InlineEdit/other children inside <SectionCard> (keeping TaskStatusIndicator,
PriorityBadge, InlineEdit and their props unchanged) and remove the manual
utility classes so the layout uses SectionCard's built-in styling and header
structure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 16f08095-b2e7-4c99-b125-814e435f8f91

📥 Commits

Reviewing files that changed from the base of the PR and between a749c3d and 79921d5.

📒 Files selected for processing (3)
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript 6+ with paths for module resolution (remove deprecated baseUrl); esModuleInterop is always true
In TypeScript, strict defaults to true and types defaults to []; do not auto-discover @types/* — must explicitly list needed types

Files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from web/src/components/ui/
Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones
Use design tokens exclusively in web components: Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)); never hardcode hex values or pixel spacing
Use font-sans or font-mono (Geist tokens) in web components; never set fontFamily directly
Use density-aware spacing tokens in web components (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacing
Use shadow and border token variables in web components (var(--so-shadow-card-hover), border-border, border-bright); never hardcode shadow or border styles
In React components, import cn from @/lib/utils for conditional class merging; never concatenate class strings directly
Do not recreate status dots inline; use <StatusBadge> component
Do not build card-with-header layouts from scratch; use <SectionCard> component
Do not create metric displays with text-metric font-bold; use <MetricCard> component
Do not render initials circles manually; use <Avatar> component
Do not create complex (>8 line) JSX inside .map() blocks; extract to a shared component

Files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in pages/<page-name>/ subdirectories

Files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @eslint-react/eslint-plugin and eslint-plugin-security in web dashboard

Files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
web/**

📄 CodeRabbit inference engine (CLAUDE.md)

Use Node.js 22+ and npm for web dashboard; all dependencies in web/package.json (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, Recharts, Framer Motion, Axios, Lucide React, Geist fonts, Storybook 10, Vitest, ESLint, fast-check, etc.)

Files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
web/src/__tests__/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/__tests__/**/*.{ts,tsx}: Use fast-check for property-based testing in React; use fc.assert and fc.property
Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/**/*.stories.{ts,tsx} : Use Storybook 10 (ESM-only) for React component documentation; import from `storybook/test`, `storybook/actions`; use `definePreview` from `storybook/react-vite`
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from `web/src/components/ui/`

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in `pages/<page-name>/` subdirectories

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not build card-with-header layouts from scratch; use `<SectionCard>` component

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
  • web/src/pages/tasks/TaskDetailPanel.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create metric displays with `text-metric font-bold`; use `<MetricCard>` component

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use shadow and border token variables in web components (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`); never hardcode shadow or border styles

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : In React components, import `cn` from `@/lib/utils` for conditional class merging; never concatenate class strings directly

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design tokens exclusively in web components: Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`, etc.) or CSS variables (`var(--so-accent)`); never hardcode hex values or pixel spacing

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create complex (>8 line) JSX inside `.map()` blocks; extract to a shared component

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use Vitest for React unit testing with coverage scoped to files changed vs origin/main

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/__tests__/**/*.{ts,tsx} : Use fast-check for property-based testing in React; use `fc.assert` and `fc.property`

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/**/*.stories.{ts,tsx} : In Storybook, use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing configuration (replaces old `.element` and `.manual`)

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/**/*.stories.{ts,tsx} : Use Storybook 10 (ESM-only) for React component documentation; import from `storybook/test`, `storybook/actions`; use `definePreview` from `storybook/react-vite`

Applied to files:

  • web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx
🪛 GitHub Check: Dashboard Lint
web/src/pages/TaskDetailPage.tsx

[warning] 223-223:
Do not use item index in the array as its key

web/src/pages/tasks/TaskDetailPanel.tsx

[warning] 260-260:
Do not use item index in the array as its key

🔇 Additional comments (5)
web/src/pages/tasks/TaskDetailPanel.tsx (2)

36-97: LGTM — Panel logic and error handling are well-structured.

The component correctly:

  • Guards Escape key closure when dialogs are open (lines 55-56)
  • Validates non-empty cancel reason before submission (lines 76-79)
  • Uses proper try-catch with toast error feedback for all async handlers
  • Manages transition loading state to disable buttons during in-flight operations

308-325: LGTM — Cancel dialog correctly uses ConfirmDialog with children for reason input.

The textarea is properly wired to cancelReason state and the onOpenChange handler clears the reason when the dialog closes. The handleCancel guard (lines 76-79) prevents confirmation with empty input.

web/src/__tests__/pages/tasks/TaskDetailPanel.test.tsx (1)

39-172: LGTM — Comprehensive test coverage with proper query scoping.

The test suite effectively covers:

  • Rendering of all task fields (title, description, status, priority, assignee, dependencies, criteria)
  • Conditional UI visibility (transitions for non-terminal tasks, Cancel Task button visibility)
  • User interactions (close button, Escape key, transition clicks, dialog confirmations)
  • Validation behavior (rejects cancel with empty reason at lines 148-157)
  • Loading state content suppression

The within(dialog).getByRole() pattern correctly addresses prior fragile selector concerns.

web/src/pages/TaskDetailPage.tsx (2)

41-41: LGTM — Task gated by taskId prevents stale data display.

This addresses the prior concern about showing stale selectedTask when navigating between tasks or after failed fetches.


70-83: LGTM — Cancel handler validates non-empty reason consistently with TaskDetailPanel.

The validation logic (lines 72-75) matches the panel's behavior, ensuring audit trail consistency.

Comment on lines +222 to +228
{task.acceptance_criteria.map((criterion, idx) => (
<li key={idx} className="flex items-start gap-2 text-sm text-text-secondary">
<span className={cn('mt-0.5 size-4 shrink-0 rounded border', criterion.met ? 'border-success bg-success/20' : 'border-border')} />
{criterion.description}
</li>
))}
</ul>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Index key for acceptance criteria — same intentional pattern as TaskDetailPanel.

Per the commit message, index keys are used because AcceptanceCriterion lacks a stable id field. The criteria are read-only here, so this is acceptable.

Consider adding a lint suppression comment for consistency:

💡 Optional: Add lint suppression
               <ul className="mt-1.5 space-y-1">
+                {/* eslint-disable-next-line `@eslint-react/no-array-index-key` -- criteria have no stable id */}
                 {task.acceptance_criteria.map((criterion, idx) => (
🧰 Tools
🪛 GitHub Check: Dashboard Lint

[warning] 223-223:
Do not use item index in the array as its key

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

In `@web/src/pages/TaskDetailPage.tsx` around lines 222 - 228, TaskDetailPage
currently maps task.acceptance_criteria using the array index as the React key
(in the JSX inside the component where acceptance_criteria.map is rendered);
because AcceptanceCriterion has no stable id this is acceptable for read-only
rendering but inconsistent with lint rules—add a lint suppression comment (e.g.
eslint-disable-next-line react/no-array-index-key) immediately above the
acceptance_criteria.map expression to silence the rule and document the intent,
referencing the TaskDetailPage component and the acceptance_criteria.map usage
so reviewers know this is intentional.

Comment on lines +259 to +265
{task.acceptance_criteria.map((criterion, idx) => (
<li key={idx} className="flex items-start gap-2 text-xs text-text-secondary">
<span className={cn('mt-0.5 size-3.5 shrink-0 rounded border', criterion.met ? 'border-success bg-success/20' : 'border-border')} />
{criterion.description}
</li>
))}
</ul>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Index key for acceptance criteria is acceptable given no stable ID exists.

The static analysis warning about using index as key is valid in general, but per the commit message this is intentional since AcceptanceCriterion has no id field. Since acceptance criteria are read-only in this panel (no reordering/deletion), using the index is safe.

Consider adding a lint suppression comment to document this decision:

💡 Optional: Add lint suppression
                   <ul className="mt-1.5 space-y-1">
+                    {/* eslint-disable-next-line `@eslint-react/no-array-index-key` -- criteria have no stable id */}
                     {task.acceptance_criteria.map((criterion, idx) => (
                       <li key={idx} className="flex items-start gap-2 text-xs text-text-secondary">
🧰 Tools
🪛 GitHub Check: Dashboard Lint

[warning] 260-260:
Do not use item index in the array as its key

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

In `@web/src/pages/tasks/TaskDetailPanel.tsx` around lines 259 - 265, The
acceptance criteria list in TaskDetailPanel uses the array index as the React
key (task.acceptance_criteria.map((criterion, idx) => ... key={idx}) which is
intentional because AcceptanceCriterion has no id and the list is read-only; add
an inline lint suppression comment above the map to document and silence the
rule (e.g., eslint-disable-next-line react/no-array-index-key) and include a
short comment explaining why using the index is safe here (read-only, no
reordering/deletes) so future readers know this is intentional.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 12:34 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
web/src/pages/TaskDetailPage.tsx (1)

220-227: 🧹 Nitpick | 🔵 Trivial

Index key usage is acceptable; consider adding lint suppression.

Per prior review discussion, using array index as key for acceptance criteria is acceptable because AcceptanceCriterion lacks a stable id field and the list is read-only. Consider adding a lint suppression comment to document this intentional choice and silence the static analysis warning.

Optional: Add lint suppression
               <ul className="mt-1.5 space-y-1">
+                {/* eslint-disable-next-line `@eslint-react/no-array-index-key` -- criteria have no stable id */}
                 {task.acceptance_criteria.map((criterion, idx) => (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/TaskDetailPage.tsx` around lines 220 - 227, The map over
task.acceptance_criteria uses the array index as the React key (key={idx})
intentionally because AcceptanceCriterion has no stable id and the list is
read-only; add a one-line lint suppression comment (e.g., //
eslint-disable-next-line react/no-array-index-key) immediately above the JSX
line that renders the <li> inside the map to document and silence the warning,
referencing the task.acceptance_criteria.map callback and the key={idx} usage so
reviewers can see this is an intentional, safe choice.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 155-170: The priority select is showing the user's chosen value
even when useTasksStore.getState().updateTask fails because the DOM value is
tied only to task.priority; change the select to use a local state (e.g.,
selectedPriority) initialized from task.priority, update selectedPriority
optimistically onChange, call updateTask, and on catch rollback selectedPriority
back to task.priority and show the existing toast via
useToastStore.getState().add(getErrorMessage(err)); also add an effect to sync
selectedPriority whenever task.priority changes so the control stays in sync
with external updates (references: updateTask, task.priority, PRIORITIES,
getPriorityLabel, useToastStore.getState().add, getErrorMessage).

---

Duplicate comments:
In `@web/src/pages/TaskDetailPage.tsx`:
- Around line 220-227: The map over task.acceptance_criteria uses the array
index as the React key (key={idx}) intentionally because AcceptanceCriterion has
no stable id and the list is read-only; add a one-line lint suppression comment
(e.g., // eslint-disable-next-line react/no-array-index-key) immediately above
the JSX line that renders the <li> inside the map to document and silence the
warning, referencing the task.acceptance_criteria.map callback and the key={idx}
usage so reviewers can see this is an intentional, safe choice.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 93704778-ec44-4463-ab8d-c78fbbb8f40b

📥 Commits

Reviewing files that changed from the base of the PR and between 79921d5 and c358be9.

📒 Files selected for processing (1)
  • web/src/pages/TaskDetailPage.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript 6+ with paths for module resolution (remove deprecated baseUrl); esModuleInterop is always true
In TypeScript, strict defaults to true and types defaults to []; do not auto-discover @types/* — must explicitly list needed types

Files:

  • web/src/pages/TaskDetailPage.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from web/src/components/ui/
Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones
Use design tokens exclusively in web components: Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-accent)); never hardcode hex values or pixel spacing
Use font-sans or font-mono (Geist tokens) in web components; never set fontFamily directly
Use density-aware spacing tokens in web components (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; never hardcode pixel values for layout spacing
Use shadow and border token variables in web components (var(--so-shadow-card-hover), border-border, border-bright); never hardcode shadow or border styles
In React components, import cn from @/lib/utils for conditional class merging; never concatenate class strings directly
Do not recreate status dots inline; use <StatusBadge> component
Do not build card-with-header layouts from scratch; use <SectionCard> component
Do not create metric displays with text-metric font-bold; use <MetricCard> component
Do not render initials circles manually; use <Avatar> component
Do not create complex (>8 line) JSX inside .map() blocks; extract to a shared component

Files:

  • web/src/pages/TaskDetailPage.tsx
web/src/pages/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in pages/<page-name>/ subdirectories

Files:

  • web/src/pages/TaskDetailPage.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use ESLint with @eslint-react/eslint-plugin and eslint-plugin-security in web dashboard

Files:

  • web/src/pages/TaskDetailPage.tsx
web/**

📄 CodeRabbit inference engine (CLAUDE.md)

Use Node.js 22+ and npm for web dashboard; all dependencies in web/package.json (React 19, react-router, shadcn/ui, Radix UI, Tailwind CSS 4, Zustand, Recharts, Framer Motion, Axios, Lucide React, Geist fonts, Storybook 10, Vitest, ESLint, fast-check, etc.)

Files:

  • web/src/pages/TaskDetailPage.tsx
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/components/**/*.stories.{ts,tsx} : Use Storybook 10 (ESM-only) for React component documentation; import from `storybook/test`, `storybook/actions`; use `definePreview` from `storybook/react-vite`
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from `web/src/components/ui/`
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19 with TypeScript 6+ in web dashboard; enforce design tokens (no hardcoded colors, fonts, spacing) and reuse components from `web/src/components/ui/`

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/pages/**/*.{ts,tsx} : Use React Router with lazy-loaded page components (one per route); place page-scoped sub-components in `pages/<page-name>/` subdirectories

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not build card-with-header layouts from scratch; use `<SectionCard>` component

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create metric displays with `text-metric font-bold`; use `<MetricCard>` component

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use shadow and border token variables in web components (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`); never hardcode shadow or border styles

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : In React components, import `cn` from `@/lib/utils` for conditional class merging; never concatenate class strings directly

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use design tokens exclusively in web components: Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`, etc.) or CSS variables (`var(--so-accent)`); never hardcode hex values or pixel spacing

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
📚 Learning: 2026-03-27T09:44:40.059Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T09:44:40.059Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do not create complex (>8 line) JSX inside `.map()` blocks; extract to a shared component

Applied to files:

  • web/src/pages/TaskDetailPage.tsx
🪛 GitHub Check: Dashboard Lint
web/src/pages/TaskDetailPage.tsx

[warning] 222-222:
Do not use item index in the array as its key

🔇 Additional comments (6)
web/src/pages/TaskDetailPage.tsx (6)

109-132: Reuse SectionCard for the main details shell.

This is a hand-built card-with-header layout that should use the shared <SectionCard> component to ensure consistent padding, border treatment, and header styling across the dashboard. As per coding guidelines: "Do not build card-with-header layouts from scratch; use <SectionCard> component."


1-20: LGTM!

Imports are well-organized with React hooks consolidated, cn imported for class merging, and appropriate reuse of existing UI components from @/components/ui/.


34-40: LGTM!

The task derivation now correctly gates selectedTask by matching taskId, preventing stale task display when navigating between routes.


42-82: LGTM!

All handlers implement proper error handling with toasts, use optimistic concurrency via expected_version, and the cancel flow now validates that a reason is provided before proceeding.


186-265: LGTM!

Metadata grid, dependencies, transitions, and action buttons are well-implemented. Currency formatting correctly passes 'USD', transitions use expected_version for optimistic concurrency with proper disabled state during in-flight operations, and the Cancel Task button is appropriately hidden for terminal states.


268-295: LGTM!

Both confirm dialogs are properly configured. The cancel dialog includes the reason textarea with state reset on close, and the delete dialog clearly warns about permanent deletion.

Comment on lines +155 to +170
<select
value={task.priority}
onChange={async (e) => {
try {
await useTasksStore.getState().updateTask(task.id, { priority: e.target.value as Priority, expected_version: task.version })
} catch (err) {
useToastStore.getState().add({ variant: 'error', title: 'Failed to update priority', description: getErrorMessage(err) })
}
}}
className="h-8 rounded-md border border-border bg-surface px-2 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent"
>
{PRIORITIES.map((p) => (
<option key={p} value={p}>{getPriorityLabel(p)}</option>
))}
</select>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Priority select shows stale value after failed update.

When updateTask fails, the select's DOM value remains at the user's selection while task.priority is unchanged. The UI stays out of sync until something else triggers a re-render. Consider using local state with rollback on failure.

Suggested fix using local state with rollback
+  const [localPriority, setLocalPriority] = useState(task.priority)
+
+  useEffect(() => {
+    setLocalPriority(task.priority)
+  }, [task.priority])
+
   ...
               <select
-                value={task.priority}
+                value={localPriority}
                 onChange={async (e) => {
+                  const newPriority = e.target.value as Priority
+                  const previousPriority = localPriority
+                  setLocalPriority(newPriority)
                   try {
-                    await useTasksStore.getState().updateTask(task.id, { priority: e.target.value as Priority, expected_version: task.version })
+                    await useTasksStore.getState().updateTask(task.id, { priority: newPriority, expected_version: task.version })
                   } catch (err) {
+                    setLocalPriority(previousPriority)
                     useToastStore.getState().add({ variant: 'error', title: 'Failed to update priority', description: getErrorMessage(err) })
                   }
                 }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/TaskDetailPage.tsx` around lines 155 - 170, The priority select
is showing the user's chosen value even when useTasksStore.getState().updateTask
fails because the DOM value is tied only to task.priority; change the select to
use a local state (e.g., selectedPriority) initialized from task.priority,
update selectedPriority optimistically onChange, call updateTask, and on catch
rollback selectedPriority back to task.priority and show the existing toast via
useToastStore.getState().add(getErrorMessage(err)); also add an effect to sync
selectedPriority whenever task.priority changes so the control stays in sync
with external updates (references: updateTask, task.priority, PRIORITIES,
getPriorityLabel, useToastStore.getState().add, getErrorMessage).

@Aureliolo Aureliolo merged commit 04a19b0 into main Mar 27, 2026
32 checks passed
@Aureliolo Aureliolo deleted the feat/task-board-page branch March 27, 2026 12:40
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 27, 2026 12:40 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Mar 30, 2026
🤖 I have created a release *beep* *boop*
---
#MAJOR CHANGES; We got a somewhat working webui :)

##
[0.5.0](v0.4.9...v0.5.0)
(2026-03-30)


### Features

* add analytics trends and budget forecast API endpoints
([#798](#798))
([16b61f5](16b61f5))
* add department policies to default templates
([#852](#852))
([7a41548](7a41548))
* add remaining activity event types (task_started, tool_used,
delegation, cost_incurred)
([#832](#832))
([4252fac](4252fac))
* agent performance, activity, and history API endpoints
([#811](#811))
([9b75c1d](9b75c1d))
* Agent Profiles and Detail pages (biography, career, performance)
([#874](#874))
([62d7880](62d7880))
* app shell, Storybook, and CI/CD pipeline
([#819](#819))
([d4dde90](d4dde90))
* Approvals page with risk grouping, urgency indicators, batch actions
([#889](#889))
([4e9673d](4e9673d))
* Budget Panel page (P&L dashboard, breakdown charts, forecast)
([#890](#890))
([b63b0f1](b63b0f1))
* build infrastructure layer (API client, auth, WebSocket)
([#815](#815))
([9f01d3e](9f01d3e))
* CLI global options infrastructure, UI modes, exit codes, env vars
([#891](#891))
([fef4fc5](fef4fc5))
* CodeMirror editor and theme preferences toggle
([#905](#905),
[#807](#807))
([#909](#909))
([41fbedc](41fbedc))
* Company page (department/agent management)
([#888](#888))
([cfb88b0](cfb88b0))
* comprehensive hint coverage across all CLI commands
([#900](#900))
([937974e](937974e))
* config system extensions, per-command flags for
init/start/stop/status/logs
([#895](#895))
([32f83fe](32f83fe))
* configurable currency system replacing hardcoded USD
([#854](#854))
([b372551](b372551))
* Dashboard page (metric cards, activity feed, budget burn)
([#861](#861))
([7d519d5](7d519d5))
* department health, provider status, and activity feed endpoints
([#818](#818))
([6d5f196](6d5f196))
* design tokens and core UI components
([#833](#833))
([ed887f2](ed887f2))
* extend approval, meeting, and budget API responses
([#834](#834))
([31472bf](31472bf))
* frontend polish -- real-time UX, accessibility, responsive,
performance ([#790](#790),
[#792](#792),
[#791](#791),
[#793](#793))
([#917](#917))
([f04a537](f04a537))
* implement human roles and access control levels
([#856](#856))
([d6d8a06](d6d8a06))
* implement semantic conflict detection in workspace merge
([#860](#860))
([d97283b](d97283b))
* interaction components and animation patterns
([#853](#853))
([82d4b01](82d4b01))
* Login page + first-run bootstrap + Company page
([#789](#789),
[#888](#888))
([#896](#896))
([8758e8d](8758e8d))
* Meetings page with timeline viz, token bars, contribution formatting
([#788](#788))
([#904](#904))
([b207f46](b207f46))
* Messages page with threading, channel badges, sender indicators
([#787](#787))
([#903](#903))
([28293ad](28293ad))
* Org Chart force-directed view and drag-drop reassignment
([#872](#872),
[#873](#873))
([#912](#912))
([a68a938](a68a938))
* Org Chart page (living nodes, status, CRUD, department health)
([#870](#870))
([0acbdae](0acbdae))
* per-command flags for remaining commands, auto-behavior wiring,
help/discoverability
([#897](#897))
([3f7afa2](3f7afa2))
* Providers page with backend rework -- health, CRUD, subscription auth
([#893](#893))
([9f8dd98](9f8dd98))
* scaffold React + Vite + TypeScript + Tailwind project
([#799](#799))
([bd151aa](bd151aa))
* Settings page with search, dependency indicators, grouped rendering
([#784](#784))
([#902](#902))
([a7b9870](a7b9870))
* Setup Wizard rebuild with template comparison, cost estimator, theme
customization ([#879](#879))
([ae8b50b](ae8b50b))
* setup wizard UX -- template filters, card metadata, provider form
reuse ([#910](#910))
([7f04676](7f04676))
* setup wizard UX overhaul -- mode choice, step reorder, provider fixes
([#907](#907))
([ee964c4](ee964c4))
* structured ModelRequirement in template agent configs
([#795](#795))
([7433548](7433548))
* Task Board page (rich Kanban, filtering, dependency viz)
([#871](#871))
([04a19b0](04a19b0))


### Bug Fixes

* align frontend types with backend and debounce WS refetches
([#916](#916))
([134c11b](134c11b))
* auto-cleanup targets newly pulled images instead of old ones
([#884](#884))
([50e6591](50e6591))
* correct wipe backup-skip flow and harden error handling
([#808](#808))
([c05860f](c05860f))
* improve provider setup in wizard, subscription auth, dashboard bugs
([#914](#914))
([87bf8e6](87bf8e6))
* improve update channel detection and add config get command
([#814](#814))
([6b137f0](6b137f0))
* resolve all ESLint warnings, add zero-warnings enforcement
([#899](#899))
([079b46a](079b46a))
* subscription auth uses api_key, base URL optional for cloud providers
([#915](#915))
([f0098dd](f0098dd))


### Refactoring

* semantic analyzer cleanup -- shared filtering, concurrency, extraction
([#908](#908))
([81372bf](81372bf))


### Documentation

* brand identity and UX design system from
[#765](#765) exploration
([#804](#804))
([389a9f4](389a9f4))
* page structure and information architecture for v0.5.0 dashboard
([#809](#809))
([f8d6d4a](f8d6d4a))
* write UX design guidelines with WCAG-verified color system
([#816](#816))
([4a4594e](4a4594e))


### Tests

* add unit tests for agent hooks and page components
([#875](#875))
([#901](#901))
([1d81546](1d81546))


### CI/CD

* bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group
([#831](#831))
([01c19de](01c19de))
* bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in
/.github/actions/setup-python-uv in the all group
([#920](#920))
([5f6ba54](5f6ba54))
* bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group
([#868](#868))
([f22a181](f22a181))
* bump github/codeql-action from 4.34.1 to 4.35.0 in the all group
([#883](#883))
([87a4890](87a4890))
* bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the
minor-and-patch group
([#830](#830))
([7a69050](7a69050))
* bump the all group with 3 updates
([#923](#923))
([ff27c8e](ff27c8e))
* bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch
group ([#822](#822))
([07d43eb](07d43eb))
* bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group
([#882](#882))
([f84118d](f84118d))


### Maintenance

* add design system enforcement hook and component inventory
([#846](#846))
([15abc43](15abc43))
* add dev-only auth bypass for frontend testing
([#885](#885))
([6cdcd8a](6cdcd8a))
* add pre-push rebase check hook
([#855](#855))
([b637a04](b637a04))
* backend hardening -- eviction/size-caps and model validation
([#911](#911))
([81253d9](81253d9))
* bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1
directory ([#922](#922))
([b1b0232](b1b0232))
* bump brace-expansion from 5.0.4 to 5.0.5 in /web
([#862](#862))
([ba4a565](ba4a565))
* bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web
([#801](#801))
([7574bb5](7574bb5))
* bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group
([#803](#803))
([14d322e](14d322e))
* bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to
0.15.8 ([#864](#864))
([f52901e](f52901e))
* bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in
/docker/web in the all group
([#919](#919))
([df85e4f](df85e4f))
* bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in
/docker/web ([#800](#800))
([f4e9450](f4e9450))
* bump node from `44bcbf4` to `71be405` in /docker/sandbox
([#827](#827))
([91bec67](91bec67))
* bump node from `5209bca` to `cf38e1f` in /docker/web
([#863](#863))
([66d6043](66d6043))
* bump picomatch in /site
([#842](#842))
([5f20bcc](5f20bcc))
* bump recharts 2-&gt;3 and @types/node 22-&gt;25 in /web
([#802](#802))
([a908800](a908800))
* Bump requests from 2.32.5 to 2.33.0
([#843](#843))
([41daf69](41daf69))
* bump smol-toml from 1.6.0 to 1.6.1 in /site
([#826](#826))
([3e5dbe4](3e5dbe4))
* bump the all group with 3 updates
([#921](#921))
([7bace0b](7bace0b))
* bump the minor-and-patch group across 1 directory with 2 updates
([#829](#829))
([93e611f](93e611f))
* bump the minor-and-patch group across 1 directory with 3 updates
([#841](#841))
([7010c8e](7010c8e))
* bump the minor-and-patch group across 1 directory with 3 updates
([#869](#869))
([548cee5](548cee5))
* bump the minor-and-patch group in /site with 2 updates
([#865](#865))
([9558101](9558101))
* bump the minor-and-patch group with 2 updates
([#867](#867))
([4830706](4830706))
* consolidate Dependabot groups to 1 PR per ecosystem
([06d2556](06d2556))
* consolidate Dependabot groups to 1 PR per ecosystem
([#881](#881))
([06d2556](06d2556))
* improve worktree skill with full dep sync and status enhancements
([#906](#906))
([772c625](772c625))
* remove Vue remnants and document framework decision
([#851](#851))
([bf2adf6](bf2adf6))
* update web dependencies and fix brace-expansion CVE
([#880](#880))
([a7a0ed6](a7a0ed6))
* upgrade to Storybook 10 and TypeScript 6
([#845](#845))
([52d95f2](52d95f2))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.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.

feat: Task Board page (rich Kanban, filtering, dependency viz)

1 participant