feat(ui): cc-deck attention feed, ATB + Telos visual redesign#311
feat(ui): cc-deck attention feed, ATB + Telos visual redesign#311dimakis wants to merge 2 commits into
Conversation
dimakis
left a comment
There was a problem hiding this comment.
Centaur Review
Found 8 issue(s) (3 warning).
frontend/src/hooks/useAttentionFeed.ts
Well-structured UI feature with good test coverage for the new hook, though ATB/session attention paths are untested, t1Count undercounts nested tasks, and one SSE type assertion is unsafe.
- 🔵 style (L32):
COLOR_BLUEis declared but never used. Remove to avoid dead code.[fixable] - 🟡 unsafe_assumptions (L195):
data as SessionActivity[]is an unsafe type assertion on SSE event data. If the event payload shape changes or the event carries a wrapper object (e.g.{ activities: [...] }), this will silently produce wrong data. Consider validating or at least checkingArray.isArray(data)before casting.[fixable]
frontend/src/pages/TaskBoard.tsx
Well-structured UI feature with good test coverage for the new hook, though ATB/session attention paths are untested, t1Count undercounts nested tasks, and one SSE type assertion is unsafe.
- 🟡 bugs (L78):
t1Countonly counts root-level tasks, but children can also bepending_review/blocked/failed. The badge could undercount urgent items. The AttentionFeed usesflattenTasks()to include children — consider the same approach here, or accept the inconsistency as intentional.[fixable] - 🟡 bugs (L54):
sortByAttentionflattens the tier ordering of root tasks but doesn't recurse into children. A root task that ispending(tier 4) but has afailedchild won't bubble up. Since TaskNode renders children inline, a tier-1 child could be buried under a low-priority parent. This may be acceptable given the tree UI, but it's worth noting the sort only affects root ordering.
frontend/src/hooks/__tests__/useAttentionFeed.test.ts
Well-structured UI feature with good test coverage for the new hook, though ATB/session attention paths are untested, t1Count undercounts nested tasks, and one SSE type assertion is unsafe.
- 🔵 missing_tests: Tests cover Telos items well but never exercise the ATB (task) or session activity code paths. The mock
tasks.treeis always empty ([]), and no session_activity events are dispatched. Add tests that populatemockTasks.treewith tasks inpending_review/blocked/failedstatuses, and simulatesession_activitySSE events to verify those attention items appear correctly.[fixable]
frontend/src/pages/TodoView.tsx
Well-structured UI feature with good test coverage for the new hook, though ATB/session attention paths are untested, t1Count undercounts nested tasks, and one SSE type assertion is unsafe.
- 🔵 missing_tests (L20):
groupIntoSectionscontains non-trivial bucketing and sorting logic (focus vs active, urgency tiebreaker, snoozed-falls-to-active). It's a pure function that could be exported and unit-tested directly, rather than relying solely on integration tests through the rendered view.[fixable] - 🔵 style (L39): The
byUrgencyDesccomparator usesa.ageDays - b.ageDaysas a tiebreaker, which sorts newer items first. Thedonesection sortsb.ageDays - a.ageDays(newest first). Theseensection sortsa.ageDays - b.ageDays(oldest first). These orderings are reasonable but the inconsistency in tiebreaker direction withinbyUrgencyDesc(newer first) vsseen(older first) could confuse future maintainers.
frontend/src/components/TaskNode.tsx
Well-structured UI feature with good test coverage for the new hook, though ATB/session attention paths are untested, t1Count undercounts nested tasks, and one SSE type assertion is unsafe.
- 🔵 style (L69):
doneOpacitycallsDate.now()during render, making its output non-deterministic. The opacity won't update as time passes unless something else triggers a re-render. If the fade effect should be live, a timer-based approach is needed; otherwise this is fine as a render-time snapshot — but the docstring implies continuous fading.
| const COLOR_RED = '#ff6d6d'; | ||
| const COLOR_PURPLE = '#b48cff'; | ||
| const COLOR_GREEN = '#4ade80'; | ||
| const COLOR_BLUE = '#60a5fa'; |
There was a problem hiding this comment.
🔵 style: COLOR_BLUE is declared but never used. Remove to avoid dead code. [fixable]
| // Session activities from SSE | ||
| const [activities, setActivities] = useState<SessionActivity[]>([]); | ||
| useEffect(() => { | ||
| const unsub = eventBus.on('session_activity', (data) => { |
There was a problem hiding this comment.
🟡 unsafe_assumptions: data as SessionActivity[] is an unsafe type assertion on SSE event data. If the event payload shape changes or the event carries a wrapper object (e.g. { activities: [...] }), this will silently produce wrong data. Consider validating or at least checking Array.isArray(data) before casting. [fixable]
| // Root tasks with no parent serve as potential goals | ||
| const goals = tasks.filter((t) => !t.parentId); | ||
|
|
||
| const t1Count = tasks.filter( |
There was a problem hiding this comment.
🟡 bugs: t1Count only counts root-level tasks, but children can also be pending_review/blocked/failed. The badge could undercount urgent items. The AttentionFeed uses flattenTasks() to include children — consider the same approach here, or accept the inconsistency as intentional. [fixable]
| const [showAll, setShowAll] = useState(false); | ||
|
|
||
| const sortedTasks = useMemo( | ||
| () => (showAll ? tasks : sortByAttention(tasks)), |
There was a problem hiding this comment.
🟡 bugs: sortByAttention flattens the tier ordering of root tasks but doesn't recurse into children. A root task that is pending (tier 4) but has a failed child won't bubble up. Since TaskNode renders children inline, a tier-1 child could be buried under a low-priority parent. This may be acceptable given the tree UI, but it's worth noting the sort only affects root ordering.
| defaultCollapsed: boolean; | ||
| } | ||
|
|
||
| function groupIntoSections(items: TodoItem[]): TodoSection[] { |
There was a problem hiding this comment.
🔵 missing_tests: groupIntoSections contains non-trivial bucketing and sorting logic (focus vs active, urgency tiebreaker, snoozed-falls-to-active). It's a pure function that could be exported and unit-tested directly, rather than relying solely on integration tests through the rendered view. [fixable]
| } | ||
|
|
||
| // Sort within sections | ||
| const byUrgencyDesc = (a: TodoItem, b: TodoItem) => b.urgency - a.urgency || a.ageDays - b.ageDays; |
There was a problem hiding this comment.
🔵 style: The byUrgencyDesc comparator uses a.ageDays - b.ageDays as a tiebreaker, which sorts newer items first. The done section sorts b.ageDays - a.ageDays (newest first). The seen section sorts a.ageDays - b.ageDays (oldest first). These orderings are reasonable but the inconsistency in tiebreaker direction within byUrgencyDesc (newer first) vs seen (older first) could confuse future maintainers.
| } | ||
|
|
||
| /** Opacity for done tasks: fade after 5min, near-invisible after 30min */ | ||
| function doneOpacity(task: Task): number { |
There was a problem hiding this comment.
🔵 style: doneOpacity calls Date.now() during render, making its output non-deterministic. The opacity won't update as time passes unless something else triggers a re-render. If the fade effect should be live, a timer-based approach is needed; otherwise this is fine as a render-time snapshot — but the docstring implies continuous fading.
597ec51 to
f300336
Compare
dimakis
left a comment
There was a problem hiding this comment.
Centaur Review
Found 7 issue(s) (1 warning).
frontend/src/hooks/useAttentionFeed.ts
Solid UI redesign with good test coverage for the new hook. Main concern is sortByAttention ignoring child task statuses, which could hide failed/blocked subtasks. Several new grouping/sorting functions lack dedicated unit tests.
- 🔵 style (L32):
COLOR_BLUEis declared but never used. Remove it to keep the module clean.[fixable] - 🔵 style (L133): Comment says
Sort by tier then recencybut the sort function only sorts by tier — there is no recency tiebreaker. Either add a tiebreaker (e.g., by a timestamp field) or fix the comment.[fixable] - 🔵 style (L12):
AttentionTierincludes3but no code path ever produces a tier-3 item (telos emits 1 or 2, ATB emits only 1, sessions emit 1 or 2). The type is wider than what's actually used.[fixable]
frontend/src/pages/TaskBoard.tsx
Solid UI redesign with good test coverage for the new hook. Main concern is sortByAttention ignoring child task statuses, which could hide failed/blocked subtasks. Several new grouping/sorting functions lack dedicated unit tests.
- 🟡 bugs (L25):
sortByAttentionsorts only top-level tasks by their own status, ignoring children. Apendingparent with afailedchild won't surface. Consider checking if any descendant is T1 before assigning the parent's tier, or flattening first.[fixable] - 🔵 missing_tests (L14):
getTaskTierandsortByAttentionare new sorting logic with specific tier assignments but have no unit tests. Consider extracting them and adding tests, especially for the tier ordering and theupdatedAttiebreaker.[fixable]
frontend/src/components/TaskNode.tsx
Solid UI redesign with good test coverage for the new hook. Main concern is sortByAttention ignoring child task statuses, which could hide failed/blocked subtasks. Several new grouping/sorting functions lack dedicated unit tests.
- 🔵 unsafe_assumptions (L69):
doneOpacityandformatElapsedcallDate.now()during render but there's no timer to trigger re-renders, so the opacity and elapsed label become stale until the next external re-render. This is acceptable if the task list refreshes frequently via SSE, but if not, the fade effect won't animate smoothly.[fixable]
frontend/src/pages/TodoView.tsx
Solid UI redesign with good test coverage for the new hook. Main concern is sortByAttention ignoring child task statuses, which could hide failed/blocked subtasks. Several new grouping/sorting functions lack dedicated unit tests.
- 🔵 missing_tests (L18):
groupIntoSectionsis new logic (Focus/Active/Seen/Done grouping with within-section sorting) but has no unit tests. The boundary condition (starred && urgency >= 0.5for Focus vs Active) should be covered.[fixable]
| const COLOR_RED = '#ff6d6d'; | ||
| const COLOR_PURPLE = '#b48cff'; | ||
| const COLOR_GREEN = '#4ade80'; | ||
| const COLOR_BLUE = '#60a5fa'; |
There was a problem hiding this comment.
🔵 style: COLOR_BLUE is declared but never used. Remove it to keep the module clean. [fixable]
| })); | ||
| } | ||
|
|
||
| // ─── Sort by tier then recency ───────────────────────────────────────────── |
There was a problem hiding this comment.
🔵 style: Comment says Sort by tier then recency but the sort function only sorts by tier — there is no recency tiebreaker. Either add a tiebreaker (e.g., by a timestamp field) or fix the comment. [fixable]
| // ─── Attention item model ────────────────────────────────────────────────── | ||
|
|
||
| export type AttentionSource = 'telos' | 'atb' | 'session'; | ||
| export type AttentionTier = 1 | 2 | 3; |
There was a problem hiding this comment.
🔵 style: AttentionTier includes 3 but no code path ever produces a tier-3 item (telos emits 1 or 2, ATB emits only 1, sessions emit 1 or 2). The type is wider than what's actually used. [fixable]
|
|
||
| function sortByAttention(tasks: Task[]): Task[] { | ||
| return [...tasks].sort((a, b) => { | ||
| const tierDiff = getTaskTier(a) - getTaskTier(b); |
There was a problem hiding this comment.
🟡 bugs: sortByAttention sorts only top-level tasks by their own status, ignoring children. A pending parent with a failed child won't surface. Consider checking if any descendant is T1 before assigning the parent's tier, or flattening first. [fixable]
| // ─── Attention-tier sorting ──────────────────────────────────────────────── | ||
|
|
||
| type AttendTier = 1 | 2 | 3 | 4; | ||
|
|
There was a problem hiding this comment.
🔵 missing_tests: getTaskTier and sortByAttention are new sorting logic with specific tier assignments but have no unit tests. Consider extracting them and adding tests, especially for the tier ordering and the updatedAt tiebreaker. [fixable]
| } | ||
|
|
||
| /** Opacity for done tasks: fade after 5min, near-invisible after 30min */ | ||
| function doneOpacity(task: Task): number { |
There was a problem hiding this comment.
🔵 unsafe_assumptions: doneOpacity and formatElapsed call Date.now() during render but there's no timer to trigger re-renders, so the opacity and elapsed label become stale until the next external re-render. This is acceptable if the task list refreshes frequently via SSE, but if not, the fade effect won't animate smoothly. [fixable]
| label: string; | ||
| items: TodoItem[]; | ||
| defaultCollapsed: boolean; | ||
| } |
There was a problem hiding this comment.
🔵 missing_tests: groupIntoSections is new logic (Focus/Active/Seen/Done grouping with within-section sorting) but has no unit tests. The boundary condition (starred && urgency >= 0.5 for Focus vs Active) should be covered. [fixable]
…ctions Homepage "What's Next" attention feed that aggregates Telos focus items, ATB blocked/review tasks, and waiting sessions into a tiered feed — always visible, not just when sessions are active. ATB redesign: 2-line task cards with state-colored left borders, T1 background tint for review/blocked/failed, attention-tier sorting, done-task fade logic (5min→30min opacity decay), sort toggle. Telos redesign: 3-line card layout (summary-first), urgency-as-border- color (red/amber/purple intensity), section grouping (Focus/Active/ Seen/Done with collapsible headers), status-colored icons. Telos: 76732123c7c5, ae3ead922dc8 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
f300336 to
250e698
Compare
…recursion - Remove unused AttentionTier 3 and COLOR_BLUE - Add updatedAt for recency tiebreaker in attention sorting - Fix sortByAttention to consider children status for tier calculation - Fix t1Count to recurse into children Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
250e698 to
31ae223
Compare
Summary
Completes the cc-deck-inspired UI overhaul specs from
local_features/atb-redesign/spec.mdandlocal_features/telos-redesign/spec.md.Test plan
pending_review/blocked/failedsurface in feednpm test— 664 tests passing🤖 Generated with Claude Code