Pipeline view groups phases by dependency depth with DAG visualization.
Phase detail panel with Tiptap rich content editor and auto-save.
Code review tab with diff viewer and comment threads (dummy data).
Centralized live updates hook replaces scattered subscription boilerplate.
Extract agent output parsing into shared utility. Inbox detail panel,
account cards, and agent action components.
PROBLEM:
- Agents completing with questions were incorrectly marked as "crashed"
- Race condition: polling handler AND crash handler both called handleCompletion()
- Caused database corruption and lost pending questions
SOLUTION:
- Add completion mutex in OutputHandler to prevent concurrent processing
- Remove duplicate completion call from crash handler
- Only one handler executes completion logic per agent
TESTING:
- Added mutex-completion.test.ts with 4 test cases
- Verified mutex prevents concurrent access
- Verified lock cleanup on exceptions
- Verified different agents can process concurrently
FIXES: residential-cuckoo and 12+ other agents stuck in crashed state
Replaces file completion detection with a superior approach that reads only
complete JSONL lines and tracks file position. This eliminates race conditions
without any delays or polling.
Key improvements:
- Read up to last complete line, avoiding partial lines during writes
- Track file position per agent for incremental reading
- Process only valid, complete JSON lines
- Clean up position tracking on completion/crash
- No hardcoded delays or polling required
This approach is more robust, responsive, and elegant than timing-based solutions.
The race condition where agents were marked as crashed is now completely resolved.
Fixes race condition where agents were incorrectly marked as crashed when
output files took longer than 500ms to complete writing.
Changes:
- Replace hardcoded 500ms delay with polling-based file completion detection
- Add signal file validation to ensure JSON is complete before processing
- Make status updates atomic to prevent race conditions
- Update cleanup manager to pass outputFilePath for proper timing
This resolves the issue where successful agents like "abundant-wolverine"
were marked as crashed despite producing valid output.
Tasks completed: 2/2
- Run full build verification (TypeScript, Vite, tests)
- Full integration smoke test (human-verified, approved)
SUMMARY: .planning/phases/21-polish-integration/21-06-SUMMARY.md
Add 21-05-SUMMARY.md documenting route-based code splitting results
(582 KB -> 454 KB main chunk + route-level chunks) and PlanTasksFetcher
useEffect fix. Update STATE.md to plan 5/6 with new decisions.
Replace the setState-during-render pattern in PlanTasksFetcher with a
useEffect hook. The onTasks callback was being called directly in the
render body when tasksQuery.isSuccess was true, which could cause
infinite re-render loops when the parent state update triggered a
re-render. Now data flows through useEffect with proper dependencies.
Enable autoCodeSplitting in TanStackRouterVite plugin to split the monolithic
582 KB bundle into route-level chunks. Main vendor chunk drops to 454 KB with
initiative detail (~13 KB), inbox (~13 KB), and dashboard (~65 KB) as separate
lazy-loaded chunks. Eliminates the Vite 500 KB chunk size warning.
- SpawnArchitectDropdown: toast.success on spawn, toast.error on failure
- ActionMenu: toast.success on archive, toast.error on failure
- CreateInitiativeDialog: toast.success on creation
- Inbox: toast.success/error for resumeAgent and respondToMessage mutations
- Replace all console.error-only mutation error handling with user-visible toasts
Replace plain "Loading initiative..." and "Loading phases..." text with
structured skeleton placeholders matching the page layout: header with
back arrow/title/badge, two-column grid with phase accordions and
sidebar panels.
- Hide inbox list on mobile when agent selected (drill-down pattern)
- Add "Back to list" button visible only on mobile (lg:hidden)
- Clicking back clears selection and returns to list view
- Create ErrorBoundary class component with recovery UI (reload button)
- Create Sonner Toaster wrapper (bottom-right, richColors)
- Wire ErrorBoundary around Outlet in root route to catch render errors
- Add Toaster as sibling to AppLayout in root route
- Update notFoundComponent with Back to Dashboard link button
- Remove disabled nav stubs (Agents, Tasks, Settings) from AppLayout
- Make TaskRow agent names clickable links to inbox page
- Add task ID link and "View in context" link in inbox detail panel
Create reusable Skeleton component with animate-pulse styling. Replace
plain "Loading..." text with structured skeleton placeholders matching
card layouts in InitiativeList and inbox page.
Phase 20 (Real-Time Subscriptions) is now complete. Both plans delivered:
- 20-01: SSE backend with subscription procedures
- 20-02: Frontend splitLink + subscription hooks on all 3 UI pages
Add useSubscription hooks to all three UI pages that invalidate React
Query caches on domain events:
- Dashboard: onTaskUpdate invalidates listInitiatives + listPhases
- Detail: onTaskUpdate invalidates phases/tasks/plans, onAgentUpdate
invalidates listAgents
- Inbox: onAgentUpdate invalidates listWaitingAgents + listMessages
Subscription failures are silent (onError: () => {}) so pages degrade
gracefully to manual refresh when the backend is not running.
Replace single httpBatchLink with splitLink that routes subscription
operations to httpSubscriptionLink (SSE) while keeping queries/mutations
on httpBatchLink. No new packages needed — both exports are part of
@trpc/client v11.
Add SubscriptionEvent interface for frontend consumption of SSE
subscription data in onData callbacks. Vite proxy confirmed compatible
with SSE streaming (http-proxy passes chunked responses by default).
AgentManager was never instantiated or passed to the CoordinationServer,
causing all agent-related tRPC procedures to throw "Agent manager not
available". Creates DrizzleAgentRepository, SimpleGitWorktreeManager,
and ClaudeAgentManager in startServer() and passes agentManager into
the server context deps.
listWaitingAgents, listAgents, and getAgentQuestions now return empty
results instead of throwing when agentManager is not wired into the
tRPC context. Mutation procedures (spawn, stop, resume) still throw.
Replaces stub inbox page with full implementation connecting InboxList
and QuestionForm components to tRPC backend. Two-column layout with
agent list on left and detail panel on right. Handles answer submission
via resumeAgent mutation, notification dismissal via respondToMessage,
and loading/error states following patterns from initiative detail page.
- Renders mixed question types from questions array
- Submit disabled until all questions answered
- onSubmit called with Record<string, string> mapping questionId to answer
InboxList joins agents with their latest messages, provides filter
(all/waiting/completed) and sort (newest/oldest) controls, renders
MessageCard instances, and shows empty state when no messages match.
- OptionGroup renders radio or checkbox based on multiSelect prop
- "Other" field auto-selects when user types into it
- FreeTextInput renders Input or Textarea based on multiline prop
- getAgentQuestions returns structured PendingQuestions from AgentManager
- listWaitingAgents filters agents to waiting_for_input status
- Updated JSDoc procedure list with both new procedures
MessageCard displays agent name with status, message preview (truncated
to 80 chars), relative timestamp, and response indicator (filled/empty
circle). Uses shadcn Card base with selected state highlighting.
Replace placeholder with full initiative detail page: tRPC data fetching
(getInitiative, listPhases, listPlans, listTasks), PhaseWithTasks helper
component pattern for stable hooks, two-column layout with phases on left
and progress/decisions on right, TaskDetailModal with selectedTask state,
queue actions for phases and tasks, loading/error states.