---
phase: 19-agent-inbox
plan: 02
type: execute
wave: 1
depends_on: []
files_modified: [packages/web/src/components/InboxList.tsx, packages/web/src/components/MessageCard.tsx]
autonomous: true
---

<objective>
Build the InboxList and MessageCard components for the agent inbox message list view.

Purpose: These are the entry-point components for the inbox — showing a filterable, sortable list of agent messages. Following the wireframe spec from docs/wireframes/agent-inbox.md.

Output: InboxList and MessageCard components with filter/sort controls and empty state.
</objective>

<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md

@docs/wireframes/agent-inbox.md
@packages/web/src/components/InitiativeList.tsx
@packages/web/src/components/InitiativeCard.tsx
@packages/web/src/components/StatusBadge.tsx
@packages/web/src/components/ui/card.tsx
@packages/web/src/components/ui/badge.tsx
@packages/web/src/components/ui/button.tsx
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create MessageCard component</name>
  <files>packages/web/src/components/MessageCard.tsx</files>
  <action>
Create MessageCard following the wireframe spec. Props:
- `agentName: string` — agent human-readable name
- `agentStatus: string` — 'waiting_for_input' | 'running' | 'stopped' | etc.
- `preview: string` — message preview text (truncated)
- `timestamp: string` — ISO date string for relative time display
- `requiresResponse: boolean` — determines filled (●) vs empty (○) indicator
- `isSelected: boolean` — highlighted state
- `onClick: () => void`

Display:
- Status indicator: ● (filled circle) when requiresResponse=true, ○ (empty) otherwise
- Agent name with status text in parentheses (e.g., "gastown (waiting)")
- Message preview truncated to ~80 chars with ellipsis
- Relative timestamp using simple helper (e.g., "2 min ago", "1h ago"). Write an inline `formatRelativeTime(isoDate: string): string` helper at the top of the file — do NOT create a separate utils file.
- Selected state: slightly different background via className toggle

Use shadcn Card as the base, Tailwind for styling. Match the visual density of InitiativeCard.
  </action>
  <verify>npx tsc --noEmit -p packages/web/tsconfig.app.json</verify>
  <done>MessageCard renders with indicator, name, preview, timestamp, and selected state</done>
</task>

<task type="auto">
  <name>Task 2: Create InboxList component</name>
  <files>packages/web/src/components/InboxList.tsx</files>
  <action>
Create InboxList following the wireframe spec. Props:
- `agents: Array<{ id: string; name: string; status: string; taskId: string; updatedAt: string }>` — agent data
- `messages: Array<{ id: string; senderId: string | null; content: string; requiresResponse: boolean; status: string; createdAt: string }>` — message data
- `selectedAgentId: string | null` — currently selected agent
- `onSelectAgent: (agentId: string) => void` — selection handler
- `onRefresh: () => void` — refresh handler

Internal state:
- `filter: 'all' | 'waiting' | 'completed'` — default 'all'
- `sort: 'newest' | 'oldest'` — default 'newest'

Behavior:
1. Join agents with their latest message (match message.senderId to agent.id)
2. Filter: 'waiting' = requiresResponse messages, 'completed' = responded/non-requiring messages, 'all' = everything
3. Sort by message timestamp (newest or oldest)
4. Render MessageCard for each result
5. Show header with count badge: "Agent Inbox (3)" and Refresh button
6. Filter and sort as simple button groups or select elements (not dropdowns — keep it simple)
7. Empty state when no messages match filter: "No pending messages" with subtitle text

Follow the pattern from InitiativeList for the list layout structure.
  </action>
  <verify>npx tsc --noEmit -p packages/web/tsconfig.app.json</verify>
  <done>
    - InboxList renders with filter/sort controls
    - MessageCard instances rendered for each agent+message pair
    - Empty state shown when no messages
    - Filter and sort work correctly
  </done>
</task>

</tasks>

<verification>
Before declaring plan complete:
- [ ] `npx tsc --noEmit -p packages/web/tsconfig.app.json` passes
- [ ] `npx vite build` in packages/web succeeds
</verification>

<success_criteria>

- MessageCard shows indicator, agent name, preview, relative time
- InboxList filters by waiting/completed/all
- InboxList sorts by newest/oldest
- Empty state renders when no messages match
- All builds pass
  </success_criteria>

<output>
After completion, create `.planning/phases/19-agent-inbox/19-02-SUMMARY.md`
</output>
