Skip to content

feat(ui): cc-deck attention feed, ATB + Telos visual redesign#311

Open
dimakis wants to merge 2 commits into
mainfrom
session/2026-05-03-f57087306036
Open

feat(ui): cc-deck attention feed, ATB + Telos visual redesign#311
dimakis wants to merge 2 commits into
mainfrom
session/2026-05-03-f57087306036

Conversation

@dimakis
Copy link
Copy Markdown
Owner

@dimakis dimakis commented May 3, 2026

Summary

  • Homepage "What's Next" feed — always-visible attention section aggregating Telos focus items, ATB blocked/review tasks, and waiting sessions into a tiered feed (replaces the session-only overview that hides when quiet)
  • ATB redesign — 2-line task cards with state-colored left borders, T1 background tint, attention-tier sorting, done-task fade logic, tree/attention sort toggle
  • Telos redesign — 3-line card layout (summary-first), urgency-as-border-color (red/amber/purple), section grouping (Focus/Active/Seen/Done), collapsible headers, status-colored icons

Completes the cc-deck-inspired UI overhaul specs from local_features/atb-redesign/spec.md and local_features/telos-redesign/spec.md.

Test plan

  • Verify "What's Next" section renders on homepage even with no active sessions
  • Verify Telos items with starred + high urgency appear in feed
  • Verify ATB tasks in pending_review/blocked/failed surface in feed
  • Verify TaskBoard shows state-colored left borders per status
  • Verify attention sort puts review/blocked tasks at top
  • Verify done tasks fade opacity after 5 minutes
  • Verify Telos view groups into Focus/Active/Seen/Done sections
  • Verify urgency border colors: red (≥0.8), amber (≥0.5), purple (≥0.2)
  • Verify swipe gestures still work on Telos cards
  • Run npm test — 664 tests passing

🤖 Generated with Claude Code

Copy link
Copy Markdown
Owner Author

@dimakis dimakis left a comment

Choose a reason for hiding this comment

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

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_BLUE is 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 checking Array.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): 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]
  • 🟡 bugs (L54): 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.

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.tree is always empty ([]), and no session_activity events are dispatched. Add tests that populate mockTasks.tree with tasks in pending_review/blocked/failed statuses, and simulate session_activity SSE 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): 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]
  • 🔵 style (L39): 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.

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): 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.

Comment thread frontend/src/hooks/useAttentionFeed.ts Outdated
const COLOR_RED = '#ff6d6d';
const COLOR_PURPLE = '#b48cff';
const COLOR_GREEN = '#4ade80';
const COLOR_BLUE = '#60a5fa';
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 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) => {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟡 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]

Comment thread frontend/src/pages/TaskBoard.tsx Outdated
// Root tasks with no parent serve as potential goals
const goals = tasks.filter((t) => !t.parentId);

const t1Count = tasks.filter(
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟡 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)),
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟡 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[] {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 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;
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 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 {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 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.

@dimakis dimakis force-pushed the session/2026-05-03-f57087306036 branch from 597ec51 to f300336 Compare May 4, 2026 09:22
Copy link
Copy Markdown
Owner Author

@dimakis dimakis left a comment

Choose a reason for hiding this comment

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

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_BLUE is declared but never used. Remove it to keep the module clean. [fixable]
  • 🔵 style (L133): 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]
  • 🔵 style (L12): 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]

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): 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]
  • 🔵 missing_tests (L14): 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]

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): 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]

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): 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]

Comment thread frontend/src/hooks/useAttentionFeed.ts Outdated
const COLOR_RED = '#ff6d6d';
const COLOR_PURPLE = '#b48cff';
const COLOR_GREEN = '#4ade80';
const COLOR_BLUE = '#60a5fa';
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 style: COLOR_BLUE is declared but never used. Remove it to keep the module clean. [fixable]

}));
}

// ─── Sort by tier then recency ─────────────────────────────────────────────
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 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]

Comment thread frontend/src/hooks/useAttentionFeed.ts Outdated
// ─── Attention item model ──────────────────────────────────────────────────

export type AttentionSource = 'telos' | 'atb' | 'session';
export type AttentionTier = 1 | 2 | 3;
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 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);
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🟡 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;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 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 {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 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;
}
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

🔵 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>
@dimakis dimakis force-pushed the session/2026-05-03-f57087306036 branch from f300336 to 250e698 Compare May 9, 2026 14:29
…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>
@dimakis dimakis force-pushed the session/2026-05-03-f57087306036 branch from 250e698 to 31ae223 Compare May 9, 2026 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant