13 files reviewed with mission-control design lens. Key additions: - theme: extended indigo scale, 4-level surface hierarchy, 22 terminal tokens, transition/z-index/focus-visible token categories - All screens: keyboard shortcuts, loading/error/empty states hardened - 5 new shared components: StatusDot, SkeletonLoader, Toast, Badge, KeyboardShortcutHint - settings: expanded from 2 to 5 sub-pages (accounts, workspace, danger zone) - review-tab: 3-pane layout, inline comments, file nav, hunk controls - execution-tab: zoom, partial failure state, stale agent detection - dialogs: 2 bugs found (mutation locking, error placement) Total: 4,039 → 9,302 lines (+130% from review pass)
36 KiB
Initiatives List (v2)
Route: /initiatives
Source: packages/web/src/routes/initiatives.tsx
v1 -> v2 Changes
| Aspect | v1 | v2 |
|---|---|---|
| Card metadata | Name + status + progress only | Branch, projects, timestamp, active agent count, last activity summary |
| Sort | None | Dropdown: Recent Activity / Oldest / Name A-Z / Name Z-A / % Complete |
| Search | None | Client-side text filter on initiative name |
| Create dialog | Had Branch field | Branch removed (auto-generated on first execution dispatch) |
| Loading state | 3 skeleton cards | 5 skeleton cards with shimmer animation |
| Error state | None | AlertCircle + message + Retry button |
| Empty (filtered) | None | "No matching initiatives" + Clear filters link |
| Empty (zero) | Basic text + CTA | Centered EmptyState component |
| Card actions | View + Spawn dropdown + ... menu |
Same + enumerated ... menu actions |
| Keyboard nav | None | Arrow keys navigate cards, Enter opens, n creates new |
| Multi-select | None | Checkbox select + batch action bar |
| View toggle | None | Card / compact list toggle for 20+ initiatives |
Default State (with cards)
+=============================================================================+
| [CW] [*Initiatives*] Agents *3 Inbox (2) Settings [cmd-k] [sun] * |
+=============================================================================+
| |
| Initiatives (3) [ + New Initiative ] |
| |
| [search____________________] [All v] [Recent Activity v] |
| |
| +-----------------------------------------------------------------------+ |
| | Auth System Overhaul [ACTIVE] [REVIEW] | |
| | [git] cw/auth-overhaul [P] backend, frontend . 2h ago | |
| | [▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░] 3/7 tasks [*2 agents] [...] | |
| | Agent completed "Implement auth middleware" 2h ago | |
| +-----------------------------------------------------------------------+ |
| |
| +-----------------------------------------------------------------------+ |
| | API Redesign [DONE] [YOLO] | |
| | [git] cw/api-redesign [P] backend . 1d ago | |
| | [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 12/12 tasks [...] | |
| | All tasks completed 1d ago | |
| +-----------------------------------------------------------------------+ |
| |
| +-----------------------------------------------------------------------+ |
| | Mobile App Redesign [ACTIVE] [REVIEW] | |
| | [P] frontend . 5m ago | |
| | [▓▓░░░░░░░░░░░░░░░░░░░░░] 1/5 tasks [*1 agent] [...] | |
| | Agent started "Design mobile nav" 5m ago | |
| +-----------------------------------------------------------------------+ |
| |
+=============================================================================+
Note: Third card has no branch yet (auto-generated on first execution dispatch),
so [git] badge is absent. Only [P] project badges and timestamp shown.
Note: Total initiative count (3) shown next to heading for at-a-glance density.
Agent count badge [*2 agents] only appears when > 0 running agents on initiative.
Card Anatomy
+-------------------------------------------------------------------------+
| [checkbox] Initiative Name [STATUS] [MODE] |
| [git] cw/<branch> [P] project1, project2 . <relative-time> |
| [▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░] N/M tasks [*K agents] [...] |
| <last-activity-summary> |
+-------------------------------------------------------------------------+
Row 1: Title + badges
[checkbox]— Hidden by default. Appears on hover or when any card is selected (batch mode).- Initiative name — clickable, navigates to initiative detail.
- Status + mode badges right-aligned.
Row 2: Metadata
[git] cw/<branch>— GitBranch icon + branch name. Hidden if no branch yet.[P] project1, project2— Folder icon + comma-separated project names. Hidden if no projects linked.. <relative-time>— Dot separator + relative timestamp (e.g., "2h ago", "1d ago"). Based onupdatedAt.
Row 3: Progress + agents
- Progress bar with
N/M taskslabel. [*K agents]— Running agent count (green dot + count). Hidden when 0.[...]— More actions menu, right-aligned.
Row 4: Last activity
- Single-line activity summary in
text-muted-foreground text-sm. - Format:
"<Actor> <verb> "<target>" <relative-time>". - Examples:
Agent completed "Implement auth middleware" 2h ago,You approved task "Deploy to staging" 10m ago,All tasks completed 1d ago,Agent started "Design mobile nav" 5m ago. - Source: derived from most recent agent event or task status change on the initiative.
Priority order: agent error > agent question (waiting) > task completed > agent spawned > task created.
Falls back to
"Created <relative-time>"if no events exist yet. - Truncated with ellipsis if text overflows.
Card hover / focus states
- Hover:
bg-muted/50background transition (transition-colors duration-150),border-border/80border brightens subtly. Checkbox fades in on Row 1 (see Multi-Select). Cursor:cursor-pointer. - Keyboard focus (via
j/kor Tab):ring-2 ring-primary ring-offset-2 ring-offset-background. No background change — the ring is the only focus indicator, keeping it distinct from hover. - Active/pressed:
bg-muted/70onmousedown, releases onmouseup. - Selected (checkbox checked):
ring-2 ring-primary bg-primary/5persistent highlight. The ring distinguishes selected from focused — both can coexist (focused + selected). - Disabled: N/A — cards are never disabled. Archived initiatives are visually dimmed
(
opacity-60) but still interactive.
Status badge colors
[ACTIVE]— green[DONE]— grey[ARCHIVED]— dim grey
Mode badge colors
[YOLO]— yellow/amber[REVIEW]— blue
Progress bar
- Segmented fill: each segment represents one task. Completed segments are filled, in-progress segments pulse, pending segments are grey track.
- Segment gap: 2px between segments (rendered via
gap-0.5on a flex container, each segment is adivwithflex: 1). Minimum segment width: 4px. - Color: indigo (
--primary) fill for completed segments when initiative is in progress, green (--status-success-fg) fill for all segments when 100% complete. - In-progress segments:
--primaryat 50% opacity withanimate-pulse(1.5s ease-in-out). This is the only animation on the card — intentionally subtle, draws the eye to where work is actively happening. pending_approvalsegments: amber (--status-warning-fg) static fill — distinct from in-progress (pulsing) and completed (solid).- Grey track (
--muted) for pending/not-started segments. - At > 20 tasks, segments merge into a continuous bar (segments too narrow to distinguish). The bar becomes three colored sections: completed (solid), in-progress (pulsing), pending (grey).
- The
N/M taskslabel is always visible regardless of bar width. - Tooltip on hover shows breakdown: "3 completed, 1 in progress, 1 pending approval, 2 pending".
- Zero tasks edge case: show "No tasks yet" in
text-muted-foreground text-xsinstead of an empty bar. This happens for freshly created initiatives before planning runs.
More menu [...]
Opened via click on [...] button or . keyboard shortcut on focused card.
Uses shadcn <DropdownMenu>. Closed on Escape, click outside, or item selection.
[...]
+------------------------+
| View Enter | ← navigate to initiative detail
| ---------------------- |
| Discuss... | ← spawn discuss agent (opens dialog)
| Plan... | ← spawn plan agent (opens dialog)
| Refine... | ← spawn refine agent (opens dialog)
| ---------------------- |
| Duplicate | ← clone initiative (name, projects, phases — no tasks/agents)
| Archive a | ← sets status to archived; window.confirm, Shift+click bypasses
| ---------------------- |
| Delete d | ← destructive red text; window.confirm, Shift+click bypasses
+------------------------+
Right-side hints (Enter, a, d) are keyboard shortcut annotations in
text-muted-foreground text-xs. They match the global keyboard bindings —
these are not menu-local shortcuts, they work without opening the menu.
Actions are context-aware:
Archiveonly shown foractive/doneinitiatives.Unarchiveshown forarchived.Discuss/Plan/Refinehidden forarchivedinitiatives.Deletealways shown, always destructive-colored (text-destructive).Duplicatehidden forarchivedinitiatives (duplicating dead work is a mistake).- When all agent actions are hidden (archived), the second separator is also hidden to avoid double separators.
Search + Filter + Sort Controls
[search____________________] [All v] [Recent Activity v]
Responsive layout
>= 768px: [search_______________] [All v] [Recent Activity v]
< 768px: [search_______________________________________]
[All v] [Recent Activity v]
At mobile widths, the filter row wraps to two lines: search takes full width on the first line, dropdowns share the second line. Search is always first because it is the most-used control.
Search input
- Placeholder: "Search initiatives..."
- Client-side filter on
initiative.name(case-insensitive substring match) - Debounced 150ms
- Clear button
[x]appears when non-empty - Keyboard shortcut:
/focuses the search input (standard convention)
Status filter dropdown [All v]
[ All v]
+---------------+
| All |
| Active |
| Completed |
| Archived |
+---------------+
Sort dropdown [Recent Activity v]
[ Recent Activity v]
+----------------------+
| Recent Activity | ← updatedAt DESC (default) — most recently touched first
| Oldest | ← createdAt ASC
| Name A-Z | ← name ASC
| Name Z-A | ← name DESC
| % Complete | ← (completed tasks / total tasks) DESC, then updatedAt DESC
+----------------------+
"Recent Activity" replaces "Newest" — the label should describe the sort semantics, not the age of the record. The default sort puts the initiative you most recently interacted with at the top, which is the correct default for a workspace tool.
"% Complete" is unambiguous: it is completed / total as a percentage, with ties
broken by recent activity. An initiative at 3/7 (42.8%) sorts below 5/10 (50%).
Loading State (5 skeleton cards with shimmer)
+=============================================================================+
| |
| Initiatives [ + New Initiative ] |
| |
| [search____________________] [All v] [Recent Activity v] |
| |
| +-----------------------------------------------------------------------+ |
| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | |
| | ░░░░░░░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | |
| +-----------------------------------------------------------------------+ |
| +-----------------------------------------------------------------------+ |
| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | |
| | ░░░░░░░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | |
| +-----------------------------------------------------------------------+ |
| +-----------------------------------------------------------------------+ |
| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | |
| | ░░░░░░░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | |
| +-----------------------------------------------------------------------+ |
| +-----------------------------------------------------------------------+ |
| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | |
| | ░░░░░░░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | |
| +-----------------------------------------------------------------------+ |
| +-----------------------------------------------------------------------+ |
| | ░░░░░░░░░░░░░░░░░░░ ░░░░░░ ░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ . ░░░░░ | |
| | ░░░░░░░░░░░░░░ | |
| | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | |
| +-----------------------------------------------------------------------+ |
| |
+=============================================================================+
Skeleton cards mirror card anatomy: title line, metadata line, progress line,
activity line (4 rows matching real card height). Shimmer animation sweeps
left-to-right on a 1.5s loop via @keyframes shimmer with background: linear-gradient(...).
Skeleton height must match real card height to prevent layout shift on data load.
Skeleton count reasoning: 5 cards fills a typical viewport (900px content area at ~120px per card with gaps). Showing fewer risks the page feeling empty; showing more risks painting below the fold for nothing. This is a minor detail — the shimmer animation is what makes loading feel fast, not the count.
The search/filter bar renders immediately (not skeletonized) — the controls are static and user-operable even while data loads. If the user starts typing a search during load, the filter is applied as soon as data arrives. No race condition: the query returns all initiatives and the client filters.
Error State
+=============================================================================+
| |
| Initiatives [ + New Initiative ] |
| |
| +-----------------------------------------------------------------------+ |
| | | |
| | [AlertCircle] | |
| | Failed to load initiatives | |
| | | |
| | [ Retry ] | |
| | | |
| +-----------------------------------------------------------------------+ |
| |
+=============================================================================+
Uses <ErrorState> component (see shared-components.md).
Retry calls refetch() on the tRPC query. Retry button shows a spinner while
the refetch is in-flight (disabled state, opacity-50 pointer-events-none).
Error message is the tRPC error message, not a generic string. Examples:
- "Failed to load initiatives" (generic fallback)
- "Database connection lost" (SQLite file locked)
- "Server unreachable" (network error)
The error state preserves the filter bar — if the user had filters set before the error, they remain. This prevents the disorienting "my filters disappeared" experience on transient errors.
Empty State — No Initiatives
+=============================================================================+
| |
| Initiatives [ + New Initiative ] |
| |
| +-----------------------------------------------------------------------+ |
| | | |
| | [inbox] | |
| | No initiatives yet | |
| | Create an initiative to get started. | |
| | | |
| | [ + New Initiative ] | |
| | | |
| +-----------------------------------------------------------------------+ |
| |
+=============================================================================+
Uses <EmptyState> component (see shared-components.md).
CTA button opens the CreateInitiativeDialog.
Empty State — No Filter Match
+=============================================================================+
| |
| Initiatives [ + New Initiative ] |
| |
| [auth_____________________] [Active v] [Recent Activity v] |
| |
| +-----------------------------------------------------------------------+ |
| | | |
| | No matching initiatives | |
| | Try a different search or filter. | |
| | | |
| | [Clear filters] | |
| | | |
| +-----------------------------------------------------------------------+ |
| |
+=============================================================================+
"Clear filters" is a text link (not a button). Resets search input to empty, status filter to "All", sort to "Recent Activity".
Keyboard Navigation
| Key | Action |
|---|---|
j / Arrow Down |
Move focus to next card |
k / Arrow Up |
Move focus to previous card |
Enter |
Open focused initiative |
x |
Toggle selection on focused card (batch mode) |
Shift+x |
Select range from last selected to focused card |
n |
Open Create Initiative dialog |
/ |
Focus search input |
Escape |
Clear selection / blur search / close menu (priority order) |
. |
Open [...] menu on focused card |
d |
Quick-delete focused card (triggers confirm dialog) |
a |
Quick-archive focused card (triggers confirm dialog) |
Focus ring: ring-2 ring-primary ring-offset-2 on the focused card.
Scroll-into-view: when j/k moves focus to a card partially or fully off-screen,
scrollIntoView({ block: 'nearest', behavior: 'smooth' }) is called. This scrolls
the minimum distance needed — no jarring jumps to center.
Focus wraps: pressing j on the last card moves to the first.
Keyboard shortcuts are disabled when: any dialog is open, search input is focused,
or a dropdown is open. The useHotkeys hook should check document.activeElement
and bail if it is an input/textarea/select or inside a [role="dialog"].
No keyboard shortcut hints are shown on the page itself. They are discoverable via
the command palette (cmd-k) which lists all available actions with their bindings.
Multi-Select & Batch Operations
Checkbox appears on each card on hover, or on all cards when any card is selected.
Batch action bar (appears when >= 1 card selected)
+=============================================================================+
| [x] 3 selected [ Archive ] [ Delete ] [Deselect all] |
+=============================================================================+
- Slides down from below the filter bar (above the card list),
animate-in slide-in-from-top. Slides back up when selection is cleared (animate-out slide-out-to-top). Archive— sets all selected toarchived. Confirms unless Shift held. Confirmation message: "Archive 3 initiatives?" (count is dynamic).Delete— destructive red (text-destructive). Confirms unless Shift held. Confirmation message: "Permanently delete 3 initiatives? This cannot be undone."Deselect all— text link (text-muted-foreground underline), clears selection.[x]checkbox is tri-state: unchecked (0 selected), indeterminate (some selected), checked (all visible selected). Clicking when indeterminate selects all visible.- Selection count label:
"3 selected"or"3 of 12 selected"when filtered (so the user knows they are only operating on visible items, not all initiatives). - Selection state is cleared when filters change (prevents invisible-selection bugs).
- Bar has
bg-muted border-bstyling,h-12fixed height,z-10to layer above cards. - Bar is sticky (
sticky top-0) so it remains visible when scrolling through selected cards.
Compact List View (toggle)
For workspaces with 20+ initiatives, a compact list view is more scannable than cards.
Toggle control (right of filter bar)
[search_______________] [All v] [Recent Activity v] [card|list]
[card|list] — Two icon buttons (LayoutGrid / List lucide icons). Active state
uses bg-muted. Persisted to localStorage key cw-initiatives-view.
Compact list wireframe
+=============================================================================+
| |
| Initiatives (12) [ + New Initiative ] |
| |
| [search_______________] [All v] [Recent Activity v] [card|*list*] |
| |
| +-----------------------------------------------------------------------+ |
| | NAME STATUS PROGRESS AGENTS ACTIVITY | |
| | ------------------------------------------------------------------- | |
| | Auth System Overhaul ACTIVE 3/7 (42%) *2 2h ago | |
| | API Redesign DONE 12/12 (100%) — 1d ago | |
| | Mobile App Redesign ACTIVE 1/5 (20%) *1 5m ago | |
| | Logging Refactor ACTIVE 0/3 (0%) — 3d ago | |
| +-----------------------------------------------------------------------+ |
| |
+=============================================================================+
- Each row is clickable (navigates to initiative detail).
- Column headers are sortable by click (toggles ASC/DESC, synced with sort dropdown). Active sort column shows a chevron indicator (up/down). Click again to reverse.
- Rows are 40px height (
h-10) for density. Text istext-sm. - Hover:
bg-muted/50row highlight (transition-colors duration-100). - Focus: same
ring-2 ring-primaryas card view.j/knavigation works identically. - Selection: checkbox column appears on left (always visible in list view — no hover reveal,
because the row is too compact for hover-dependent UI).
xto toggle,Shift+xfor range. [...]menu: appears as the last column on each row (right-aligned, icon-only button). Same menu items as card view. This is better than right-click context menus because: (a) right-click is not discoverable, (b) it conflicts with browser dev tools context menu, (c) it has no mobile equivalent.- STATUS column: uses the same badge components as card view but in
text-xssize. - AGENTS column: green dot + number when > 0, em-dash when 0.
- ACTIVITY column: relative time only (no activity summary — too dense for a table row). Full activity summary available on hover via a tooltip.
Scale Behavior (50+ initiatives)
At high initiative counts, the page needs to stay responsive:
- Card view: No pagination. All cards render in a virtualized list (
@tanstack/react-virtual) when count exceeds 30. The virtual window renders ~10 cards plus 5 overscan on each side. Scroll position is preserved on navigation back from initiative detail. - List view: Same virtualization strategy. At 40px row height, 100 initiatives is only 4000px — manageable without virtualization, but virtualize anyway for consistency.
- Filter bar counts: The
(N)count next to "Initiatives" updates to reflect filtered count when filters are active:Initiatives (5 of 47). - Search performance: Client-side substring match is O(n) on initiative names. At 200 initiatives this is ~0.1ms — no performance concern. If initiatives ever hit 1000+, move to server-side search (but that is not a v2 concern).
- Auto-suggestion: When the list exceeds 20 initiatives and the user is in card view,
a one-time subtle hint appears below the view toggle:
"Tip: Switch to list view for faster scanning". Dismissable, stored inlocalStoragekeycw-list-view-hint-dismissed.
State Transitions
Transitions between loading, empty, error, and populated states should feel deliberate:
| From | To | Transition |
|---|---|---|
| Loading (skeletons) | Populated (cards) | Skeletons fade out (opacity-0 150ms), cards fade in (opacity-100 150ms). No layout jump — skeleton card dimensions match real card dimensions. |
| Loading | Empty (zero) | Skeletons fade out, empty state fades in. Container height stays stable (min-height matches 3 skeleton cards). |
| Loading | Error | Skeletons fade out, error state fades in centered. |
| Populated | Empty (filtered) | Cards slide out, empty-filter message fades in. Instant — no loading spinners for client-side filtering. |
| Error | Loading (retry) | Error fades out, skeletons fade in. Retry button shows spinner during refetch. |
Key principle: the container holding the card list has min-h-[400px] to prevent
the page from collapsing vertically during transitions. This avoids the jarring
"page jumping" effect when going from a tall state to a short empty state.
Source
packages/web/src/routes/initiatives/index.tsxpackages/web/src/components/InitiativeList.tsxpackages/web/src/components/InitiativeCard.tsxpackages/web/src/components/InitiativeCardCompact.tsx(proposed — list view row)packages/web/src/components/BatchActionBar.tsx(proposed)packages/web/src/components/EmptyState.tsx(proposed)packages/web/src/components/ErrorState.tsx(proposed)
Design Review Notes
Honest critique of the v2 spec. What was already good, what was weak, what was missing, and what was added or changed during review.
What the spec already got right
The v1-to-v2 delta table is excellent — it makes the design intent traceable. The card anatomy with 4 rows is the right density for a mission-control tool. The segmented progress bar, active agent count badge, and last-activity summary were all present before this review. The sort options are well-chosen with clear semantics. The responsive filter bar layout is properly specified. Credit where due: this spec was 80% there.
1. Card hover/focus/selected states were completely unspecified
The spec defined a focus ring in the keyboard nav section but said nothing about hover,
active (mousedown), or selected (checkbox checked) states. For a page where the primary
interaction is "scan cards and click one," this is a critical omission. A card with no
hover feedback feels dead. Added: hover (bg-muted/50), focus (ring-2 ring-primary),
active (bg-muted/70), selected (ring-2 ring-primary bg-primary/5), and the rule
that focus + selected can coexist visually.
2. Progress bar: good concept, missing edge cases
The segmented bar idea was solid. But the spec was silent on: pending_approval tasks
(a real status in this system — they need amber fill, not grey), zero-task initiatives
(shows an empty bar which looks broken), and the exact visual treatment of in-progress
segments (just saying "pulse" is not enough — added opacity and animation details).
The >20 task fallback was already there and is pragmatic.
3. Sort semantics were already clean
"Recent Activity" and "% Complete" were already well-defined with tiebreakers. No changes needed here. The original spec nailed this.
4. Batch operations: specified but the bar lacked detail
Multi-select was present but the batch action bar was a one-liner. Missing: tri-state
checkbox behavior, filtered-count awareness ("3 of 12 selected"), sticky positioning,
animation in/out, confirmation message wording, and the Shift+x range selection
keyboard shortcut. These are the details that separate "we thought about batch ops"
from "we can actually implement batch ops."
5. Compact list view: right-click context menu was a bad call
The original spec said "No [...] menu in list view — right-click context menu provides
the same actions." This is wrong for three reasons: (a) right-click is not discoverable
by any user who does not already know it exists, (b) it conflicts with browser developer
tools on right-click, (c) it does not work on mobile/touch. Changed to: [...] icon
button as the last column in every row. Consistent with card view, no discovery problem.
6. Keyboard nav: missing . for menu, d/a quick actions, Shift+x range
The original j/k/Enter/x/n///Escape set was a good start but missed: . to open the
[...] menu (standard in GitHub), d and a for quick-delete/archive (common in
email clients), and Shift+x for range selection. Also missing: scroll-into-view
behavior (if j moves focus off-screen, the card must scroll into view), and the
critical note about disabling shortcuts when dialogs/inputs are focused.
7. Skeleton cards had 3 rows, real cards have 4
The ASCII wireframe for skeleton cards showed 3 shimmer rows per card, but the real card anatomy has 4 rows (title, metadata, progress, activity). This causes a layout shift on data load — exactly the problem skeletons are supposed to prevent. Fixed: added a 4th shimmer row to each skeleton card.
8. No scale strategy beyond "list view"
The spec had card view and list view but no mention of what happens at 50+ or 100+
initiatives. Added: virtualization via @tanstack/react-virtual at 30+ items, scroll
position preservation on back-navigation, and a one-time hint nudging card-view users
toward list view when count exceeds 20.
9. Error state was too thin
"AlertCircle + message + Retry" is the skeleton of an error state, not a complete one. Missing: retry button loading state, error message source (tRPC error vs. generic), filter bar preservation during error. These are the details that determine whether a transient SQLite lock causes user confusion or is handled gracefully.
10. Last activity event priority was unspecified
The spec said "derived from most recent agent event or task status change" but did not define which events take priority. An agent error is more important than a task creation. Added priority order: agent error > agent question (waiting) > task completed > agent spawned > task created. This ensures the activity line surfaces the most actionable information, not just the most recent timestamp.
Open questions for implementation
-
Last activity data source: The
agent_log_chunkstable has raw JSONL but no structured events. The EventBus emits events but does not persist them. Two options: (a) derive from taskstatus+updatedAt(cheap, less rich), or (b) add a lightweightactivity_logtable that captures the ~5 event types needed for activity summaries. Option (b) is cleaner but adds a migration and a new repository. Recommendation: option (a) for v2, option (b) for v2.1. -
Agent count query cost:
listInitiativesneeds a LEFT JOIN onagentsWHEREstatus = 'running'GROUP BYinitiativeId. This is a single query, not N+1. The Drizzle ORMwithclause or a raw subquery in the repository adapter handles this. Not optional — the agent count badge is the highest-value addition in this spec. -
Compact list view priority: v2 can ship without it. Cards-only works for the typical 3-15 initiative range. But the toggle infrastructure (localStorage persistence, conditional render) should be stubbed even if the list component is not built yet, so adding it later is a 1-file change, not a refactor.
-
Virtualization priority: Not needed until someone actually has 30+ initiatives. Skip for v2, add when performance is measurably impacted. The hint to switch to list view is the cheap mitigation.
-
Shift+xrange selection complexity: Range selection requires tracking "last selected index" which interacts with sort order changes. If sort changes while a range is partially selected, the range anchor becomes meaningless. Decision: clear the range anchor (but not the selection) on sort change. Document this behavior.