docs(18): create initiative detail phase plan

Phase 18: Initiative Detail
- 4 plans in 2 waves
- 3 parallel (wave 1), 1 sequential (wave 2)
- Ready for execution
This commit is contained in:
Lukas May
2026-02-04 21:28:33 +01:00
parent cbf0ed28cb
commit 305809f0b2
4 changed files with 568 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
---
phase: 18-initiative-detail
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- packages/web/src/components/InitiativeHeader.tsx
- packages/web/src/components/ProgressPanel.tsx
autonomous: true
---
<objective>
Create InitiativeHeader and ProgressPanel components for the initiative detail page.
Purpose: The header provides navigation back to the dashboard, displays initiative metadata (name, status, dates), and the progress panel shows phase/task completion stats with a progress bar.
Output: Two reusable components ready for page assembly in Plan 04.
</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
# Wireframe spec
@docs/wireframes/initiative-detail.md
# Prior phase components to follow patterns
@packages/web/src/components/StatusBadge.tsx
@packages/web/src/components/ProgressBar.tsx
@packages/web/src/components/InitiativeCard.tsx
# tRPC client
@packages/web/src/lib/trpc.ts
# Existing route placeholder
@packages/web/src/routes/initiatives/$id.tsx
# Shared types
@packages/shared/src/types.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Create InitiativeHeader component</name>
<files>packages/web/src/components/InitiativeHeader.tsx</files>
<action>
Create a header component for the initiative detail page.
Props interface:
- `initiative`: object with `{ id: string; name: string; status: string; createdAt: string; updatedAt: string }` (serialized initiative from tRPC — dates are ISO strings)
- `onBack`: `() => void` callback for the back button
Layout (matching wireframe):
- Top bar: left-aligned "← Back to Dashboard" button (use `ChevronLeft` from lucide-react), right-aligned "[Actions]" placeholder (just a disabled Button for now — actions come in Plan 04)
- Below: Card containing initiative name as h1, status badge (reuse existing StatusBadge component), and metadata line showing created/updated dates formatted as readable strings (use `new Date(iso).toLocaleDateString()`)
Use Tailwind classes. Follow same component structure as InitiativeCard (functional component, named export).
Do NOT fetch data — this is a presentational component that receives props.
</action>
<verify>npx tsc --noEmit -p packages/web/tsconfig.app.json passes with no errors</verify>
<done>InitiativeHeader renders initiative name, status badge, dates, and back button. TypeScript compiles cleanly.</done>
</task>
<task type="auto">
<name>Task 2: Create ProgressPanel component</name>
<files>packages/web/src/components/ProgressPanel.tsx</files>
<action>
Create a progress panel component matching the wireframe's PROGRESS section.
Props interface:
- `phasesComplete`: number
- `phasesTotal`: number
- `tasksComplete`: number
- `tasksTotal`: number
Layout:
- Card with "Progress" heading
- Reuse existing ProgressBar component for overall task percentage (`tasksComplete / tasksTotal * 100`, handle 0/0 as 0%)
- Below the bar: two lines of text: "Phases: {phasesComplete}/{phasesTotal} complete" and "Tasks: {tasksComplete}/{tasksTotal} complete"
Use Tailwind. Follow Card component from shadcn (CardHeader, CardContent pattern).
This is a pure presentational component — no data fetching.
</action>
<verify>npx tsc --noEmit -p packages/web/tsconfig.app.json passes with no errors</verify>
<done>ProgressPanel displays progress bar with phase/task completion stats. TypeScript compiles cleanly.</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
- [ ] Both components export correctly and follow existing patterns
</verification>
<success_criteria>
- InitiativeHeader component created with back button, name, status badge, dates
- ProgressPanel component created with progress bar and phase/task counts
- Both components are presentational (no data fetching)
- TypeScript and Vite build pass
</success_criteria>
<output>
After completion, create `.planning/phases/18-initiative-detail/18-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,148 @@
---
phase: 18-initiative-detail
plan: 02
type: execute
wave: 1
depends_on: []
files_modified:
- packages/web/src/components/PhaseAccordion.tsx
- packages/web/src/components/TaskRow.tsx
- packages/web/src/components/DependencyIndicator.tsx
autonomous: true
---
<objective>
Create PhaseAccordion, TaskRow, and DependencyIndicator components for the initiative detail hierarchy view.
Purpose: The core of the detail page — renders the phase → task tree with expand/collapse, status indicators, agent assignments, and dependency annotations. The UI flattens the Phase → Plan → Task hierarchy by hiding the Plan layer (per decision 15-02).
Output: Three interconnected components ready for page assembly in Plan 04.
</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
# Wireframe spec — the primary reference for this plan
@docs/wireframes/initiative-detail.md
# Prior phase components to follow patterns
@packages/web/src/components/StatusBadge.tsx
@packages/web/src/components/ProgressBar.tsx
@packages/web/src/components/InitiativeCard.tsx
# tRPC client and existing types
@packages/web/src/lib/trpc.ts
@packages/shared/src/types.ts
# tRPC router — need to understand listPlans, listTasks, getPhaseDependencies signatures
@src/trpc/router.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Create DependencyIndicator component</name>
<files>packages/web/src/components/DependencyIndicator.tsx</files>
<action>
Create a component that renders dependency annotations for blocked tasks/phases.
Props interface:
- `blockedBy`: `Array<{ name: string; status: string }>` — the items blocking this one
- `type`: `'task' | 'phase'` — for display text
Layout (matching wireframe):
- Renders a line with `^` prefix and text: "blocked by: {name1}, {name2}"
- Text is muted/orange color (`text-orange-500` or `text-amber-600`)
- Indented to align with parent tree structure (use `pl-8` or similar)
- If `blockedBy` is empty, render nothing (return null)
This is a pure presentational component.
</action>
<verify>npx tsc --noEmit -p packages/web/tsconfig.app.json passes</verify>
<done>DependencyIndicator renders blocked-by annotations with ^ prefix. Compiles cleanly.</done>
</task>
<task type="auto">
<name>Task 2: Create TaskRow component</name>
<files>packages/web/src/components/TaskRow.tsx</files>
<action>
Create a component for a single task row in the phase tree.
Props interface:
- `task`: serialized Task object from tRPC (`{ id, name, status, type, priority, order }` — dates are strings)
- `agentName`: `string | null` — agent assigned to this task (if any)
- `blockedBy`: `Array<{ name: string; status: string }>` — blocking dependencies
- `isLast`: `boolean` — whether this is the last sibling (affects tree connector)
- `onClick`: `() => void` — opens task detail modal
Layout (matching wireframe):
- Row with tree connector: `|--` for siblings, `+--` for last item (use `├──` and `└──` Unicode)
- Task name as main text
- StatusBadge showing task status (reuse existing component)
- If `agentName` is set, show `[agentName]` inline in blue/muted text
- If `blockedBy` has items, render DependencyIndicator below the row (indented)
- Entire row is clickable (calls `onClick`)
- Use hover state: `hover:bg-accent` for clickability feedback
Tree connectors: Use a left-side border or pseudo-element pattern. Keep it simple — a `border-l-2` on a wrapper div with connector characters as inline text.
Do NOT add "Spawn Agent" button yet — that comes in Plan 04 with queue actions.
</action>
<verify>npx tsc --noEmit -p packages/web/tsconfig.app.json passes</verify>
<done>TaskRow renders task with tree connectors, status badge, agent name, and dependency indicator. Compiles cleanly.</done>
</task>
<task type="auto">
<name>Task 3: Create PhaseAccordion component</name>
<files>packages/web/src/components/PhaseAccordion.tsx</files>
<action>
Create an expandable/collapsible phase container that holds TaskRow components.
Props interface:
- `phase`: serialized Phase object (`{ id, number, name, status, description }`)
- `tasks`: `Array<{ task: SerializedTask; agentName: string | null; blockedBy: Array<{ name: string; status: string }> }>` — tasks with their metadata, already fetched and assembled by parent
- `defaultExpanded`: `boolean` — whether to start expanded
- `onTaskClick`: `(taskId: string) => void` — callback when a task row is clicked
Behavior (matching wireframe):
- Header row: `v`/`>` expand indicator (use `ChevronDown`/`ChevronRight` from lucide-react), phase number + name, task count `(completed/total)`, and StatusBadge for phase status
- Click header to toggle expand/collapse (internal state with `useState`, initialized from `defaultExpanded`)
- When expanded, render all tasks as TaskRow components
- Phase with status 'blocked' shows DependencyIndicator below header (needs additional prop `phaseDependencies`: `Array<{ name: string; status: string }>`)
Add `phaseDependencies` prop: `Array<{ name: string; status: string }>` — for phase-level blocked-by display.
Use Tailwind for styling. Border-bottom between phases for visual separation.
</action>
<verify>npx tsc --noEmit -p packages/web/tsconfig.app.json passes</verify>
<done>PhaseAccordion renders expandable phase with task list, status, and dependency indicators. Compiles cleanly.</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
- [ ] All three components export correctly
- [ ] Components follow existing codebase patterns (functional components, named exports, Tailwind)
</verification>
<success_criteria>
- PhaseAccordion renders expandable phases with task counts and status
- TaskRow renders tasks with tree connectors, status, agent names
- DependencyIndicator renders blocked-by annotations
- All components are presentational (data assembled by parent)
- TypeScript and Vite build pass
</success_criteria>
<output>
After completion, create `.planning/phases/18-initiative-detail/18-02-SUMMARY.md`
</output>

View File

@@ -0,0 +1,136 @@
---
phase: 18-initiative-detail
plan: 03
type: execute
wave: 1
depends_on: []
files_modified:
- packages/web/src/components/DecisionList.tsx
- packages/web/src/components/TaskDetailModal.tsx
autonomous: true
---
<objective>
Create DecisionList and TaskDetailModal components for the initiative detail page.
Purpose: DecisionList shows key decisions made during initiative planning (collapsible). TaskDetailModal opens when clicking a task row to show full task details, dependencies, agent assignment, and action buttons.
Output: Two components ready for page assembly in Plan 04.
</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
# Wireframe spec
@docs/wireframes/initiative-detail.md
# Existing shadcn components
@packages/web/src/components/ui/dialog.tsx
@packages/web/src/components/ui/button.tsx
@packages/web/src/components/ui/badge.tsx
@packages/web/src/components/ui/card.tsx
# Existing feature components for patterns
@packages/web/src/components/StatusBadge.tsx
@packages/web/src/components/InitiativeCard.tsx
# tRPC client
@packages/web/src/lib/trpc.ts
@packages/shared/src/types.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Create DecisionList component</name>
<files>packages/web/src/components/DecisionList.tsx</files>
<action>
Create a collapsible list of key decisions matching the wireframe's KEY DECISIONS panel.
Props interface:
- `decisions`: `Array<{ topic: string; decision: string; reason: string }>` — decisions to display
- `maxVisible`: `number` (default 3) — how many to show before "show more"
Behavior (matching wireframe):
- Each decision is individually collapsible: topic as header, decision + reason expand on click
- Use `ChevronDown`/`ChevronRight` from lucide-react for expand indicators
- Internal state: `expandedIndices` as `Set<number>` tracking which decisions are expanded
- "Show more" / "Show less" button if decisions exceed `maxVisible` — uses another state toggle
Layout:
- Wrap in Card (CardHeader with "Key Decisions" title, CardContent with list)
- Each decision item: clickable topic row, expandable detail below with decision text and "Reason: {reason}" in muted text
- If `decisions` array is empty, render Card with "No decisions recorded" placeholder text
NOTE: There is currently no `listDecisions` tRPC procedure. The parent page will pass an empty array for now. This component is built for the future when decisions are persisted. This is intentional — the component is ready, the data source comes later.
</action>
<verify>npx tsc --noEmit -p packages/web/tsconfig.app.json passes</verify>
<done>DecisionList renders collapsible decision items with show more/less. Compiles cleanly.</done>
</task>
<task type="auto">
<name>Task 2: Create TaskDetailModal component</name>
<files>packages/web/src/components/TaskDetailModal.tsx</files>
<action>
Create a modal dialog that shows full task details when clicking a task row.
Props interface:
- `task`: serialized Task object or `null` (null = modal closed)
- `phaseName`: `string` — name of the phase this task belongs to
- `agentName`: `string | null` — assigned agent
- `dependencies`: `Array<{ name: string; status: string }>` — tasks this one depends on
- `dependents`: `Array<{ name: string; status: string }>` — tasks blocked by this one
- `onClose`: `() => void` — close the modal
- `onQueueTask`: `(taskId: string) => void` — callback to queue this task for dispatch
- `onStopTask`: `(taskId: string) => void` — callback to stop this task (placeholder)
Layout (matching wireframe TaskDetailModal):
- Use shadcn Dialog component (controlled: `open={task !== null}`, `onOpenChange` calls `onClose`)
- DialogHeader: Task name + close button (built into Dialog)
- Content grid:
- Status: StatusBadge
- Priority: text display
- Phase: phaseName
- Type: task.type
- Agent: agentName or "Unassigned"
- Description section: task.description or "No description"
- Dependencies section: list of dependency names with their status badges. If empty, "No dependencies"
- Blocks section: list of dependent names. If empty, "None"
- DialogFooter: action buttons
- "Queue Task" button (enabled only if task status is 'pending' and dependencies array has all 'completed' items)
- "Stop Task" button (enabled only if task status is 'in_progress')
- Both call their respective callbacks with task.id
Use shadcn Dialog, Button, Badge components. Follow controlled dialog pattern from CreateInitiativeDialog.
</action>
<verify>npx tsc --noEmit -p packages/web/tsconfig.app.json passes</verify>
<done>TaskDetailModal renders full task details with dependencies, dependents, and action buttons. Compiles cleanly.</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
- [ ] Both components export correctly
- [ ] Dialog follows controlled pattern matching CreateInitiativeDialog
</verification>
<success_criteria>
- DecisionList renders collapsible decisions with show more/less
- TaskDetailModal renders full task details in a dialog with action buttons
- Both components are presentational with callback props for actions
- TypeScript and Vite build pass
</success_criteria>
<output>
After completion, create `.planning/phases/18-initiative-detail/18-03-SUMMARY.md`
</output>

View File

@@ -0,0 +1,170 @@
---
phase: 18-initiative-detail
plan: 04
type: execute
wave: 2
depends_on: ["18-01", "18-02", "18-03"]
files_modified:
- packages/web/src/routes/initiatives/$id.tsx
autonomous: true
---
<objective>
Assemble the Initiative Detail page by wiring all Phase 18 components into the $id.tsx route with tRPC data fetching, queue actions, and build verification.
Purpose: Replace the placeholder detail page with a fully functional initiative detail view that fetches data from the backend, renders the phase/task hierarchy, and provides queue action buttons.
Output: Working Initiative Detail page matching the wireframe specification.
</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
# Prior plan summaries — need to know what components were created
@.planning/phases/18-initiative-detail/18-01-SUMMARY.md
@.planning/phases/18-initiative-detail/18-02-SUMMARY.md
@.planning/phases/18-initiative-detail/18-03-SUMMARY.md
# Wireframe spec
@docs/wireframes/initiative-detail.md
# Existing page pattern to follow
@packages/web/src/routes/initiatives/index.tsx
# Components created in plans 01-03
@packages/web/src/components/InitiativeHeader.tsx
@packages/web/src/components/ProgressPanel.tsx
@packages/web/src/components/PhaseAccordion.tsx
@packages/web/src/components/TaskRow.tsx
@packages/web/src/components/DependencyIndicator.tsx
@packages/web/src/components/DecisionList.tsx
@packages/web/src/components/TaskDetailModal.tsx
# tRPC client and types
@packages/web/src/lib/trpc.ts
@packages/shared/src/types.ts
# tRPC router — need exact procedure signatures
@src/trpc/router.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Wire Initiative Detail page with data fetching and all components</name>
<files>packages/web/src/routes/initiatives/$id.tsx</files>
<action>
Replace the placeholder content with the full initiative detail page.
**Data fetching strategy:**
The page needs to assemble a nested data structure (initiative → phases → tasks) from multiple tRPC calls. Use React Query's `useQuery` hooks with proper `enabled` chaining.
1. `trpc.getInitiative.useQuery({ id })` — fetch initiative by route param `$id`
2. `trpc.listPhases.useQuery({ initiativeId: id })` — fetch all phases
3. For each phase, fetch plans: `trpc.listPlans.useQuery({ phaseId })` — but since hooks can't be called conditionally, use a pattern where the page fetches all plans for all phases. Use `trpc.useQueries()` if available, or fetch plans in a child component.
**Recommended approach for nested fetching:**
Create a helper component `PhaseWithTasks` that:
- Receives a phase
- Calls `trpc.listPlans.useQuery({ phaseId: phase.id })` to get plans
- For each plan, calls `trpc.listTasks.useQuery({ planId: plan.id })` to get tasks
- Flattens all tasks from all plans into a single array (hiding the Plan layer per decision 15-02)
- Calls `trpc.getPhaseDependencies.useQuery({ phaseId: phase.id })` for phase-level deps
- Renders PhaseAccordion with the assembled task data
This avoids the conditional hooks problem — each `PhaseWithTasks` instance has a stable hook call count.
**Page layout (matching wireframe):**
```
InitiativeHeader (back button, name, status, dates)
Two-column layout on desktop (grid-cols-1 lg:grid-cols-[1fr_340px]):
Left: PHASES section with PhaseWithTasks for each phase
Right: ProgressPanel + DecisionList (stacked)
TaskDetailModal (controlled by selectedTask state)
```
**State management:**
- `selectedTask`: `string | null` — ID of task with open modal
- For the modal, when a task is selected, look it up from the fetched data
**Back button:**
Use `useNavigate` from TanStack Router to navigate to `/initiatives`.
**Queue actions:**
- "Queue All" button in the PHASES section header — calls `trpc.queuePhase.useMutation()` for each pending phase (disabled if no pending phases)
- In TaskDetailModal, "Queue Task" calls `trpc.queueTask.useMutation()` with task ID
- After successful queue mutation, invalidate relevant queries
**Loading/error states:**
- Show skeleton/spinner while initiative loads (follow InitiativeList pattern)
- Show "Initiative not found" if getInitiative returns 404/error
- Individual PhaseWithTasks components show their own loading states
**DecisionList:**
Pass empty array for decisions: `decisions={[]}` — no backend endpoint exists yet.
**Progress calculation:**
Compute from phases data:
- `phasesComplete`: count phases with status 'completed'
- `phasesTotal`: phases.length
- `tasksComplete/tasksTotal`: sum across all PhaseWithTasks (or compute at page level by fetching all)
Since task counts require fetching all tasks (which happens in PhaseWithTasks children), lift the counts up via a callback or compute from the phase-level data. Simplest approach: use the phase statuses for the progress panel and skip exact task counts initially. Or pass task count via a state aggregation pattern.
Pragmatic approach: ProgressPanel shows phase-level progress only (`phasesComplete/phasesTotal`). Task counts show as "—" until the phase components have loaded their tasks. This avoids overcomplicating the initial implementation. If you find a clean way to aggregate task counts, do it, but don't over-engineer.
</action>
<verify>npx tsc --noEmit -p packages/web/tsconfig.app.json passes AND npx vite build in packages/web succeeds</verify>
<done>Initiative Detail page renders with header, progress, decisions placeholder, phase accordion with tasks, and task detail modal. Build passes.</done>
</task>
<task type="auto">
<name>Task 2: Verify full build and integration</name>
<files>packages/web/src/routes/initiatives/$id.tsx</files>
<action>
Run full verification:
1. `npx tsc --noEmit -p packages/web/tsconfig.app.json` — TypeScript check
2. `npx vite build` in packages/web — production build
3. `npm run build` in root — backend build (ensure no regressions)
4. Verify the route is accessible by checking routeTree.gen.ts includes the $id route
Fix any TypeScript errors or build failures discovered.
No new code unless fixing build issues.
</action>
<verify>All three build commands pass with zero errors</verify>
<done>Full project builds cleanly. Initiative detail page compiles and bundles 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
- [ ] `npm run build` in root succeeds
- [ ] Navigating from dashboard card to /initiatives/$id renders the detail page
- [ ] Phase accordion expand/collapse works
- [ ] Task detail modal opens on task click
</verification>
<success_criteria>
- Initiative Detail page replaces placeholder with full implementation
- Data fetched from backend via tRPC (getInitiative, listPhases, listPlans, listTasks)
- Phase hierarchy renders with expand/collapse
- Task detail modal functional
- Queue actions wired to tRPC mutations
- All builds pass
</success_criteria>
<output>
After completion, create `.planning/phases/18-initiative-detail/18-04-SUMMARY.md`
</output>