---
phase: 21-polish-integration
plan: 05
type: execute
wave: 2
depends_on: ["21-01", "21-02", "21-03"]
files_modified: [packages/web/vite.config.ts, packages/web/src/routes/initiatives/$id.tsx, packages/web/src/components/InitiativeCard.tsx]
autonomous: true
---

<objective>
Code-split route chunks and optimize data-fetching performance.

Purpose: The production bundle is 544 KB (a single chunk) — everything loads upfront. Route-based code splitting reduces initial load. The InitiativeCard N+1 query pattern (fetching phases per card) is acceptable for v1 but should be acknowledged with a note for future batching.
Output: Lazy-loaded routes and reduced bundle size warning.
</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

@packages/web/vite.config.ts
@packages/web/src/routes/__root.tsx
@packages/web/src/routes/initiatives/index.tsx
@packages/web/src/routes/initiatives/$id.tsx
@packages/web/src/routes/inbox.tsx
@packages/web/src/components/InitiativeCard.tsx
</context>

<tasks>

<task type="auto">
  <name>Task 1: Add route-based code splitting via TanStack Router lazy routes</name>
  <files>packages/web/src/routes/initiatives/index.tsx, packages/web/src/routes/initiatives/$id.tsx, packages/web/src/routes/inbox.tsx</files>
  <action>
  TanStack Router file-based routing with the Vite plugin already supports automatic code splitting. The plugin generates lazy routes for files in the routes/ directory.

  Check if the TanStack Router Vite plugin is configured with `autoCodeSplitting: true` in `packages/web/vite.config.ts`. If not, add it to the TanStackRouterVite plugin options.

  If autoCodeSplitting is already enabled or the plugin handles it by default, verify by building and checking if the output has multiple chunks instead of one monolithic JS file.

  If the plugin does NOT support autoCodeSplitting in this version, manually split using TanStack Router's `.lazy.tsx` file convention:
  - Create `packages/web/src/routes/initiatives/index.lazy.tsx` — move the DashboardPage component there, export via `createLazyFileRoute('/initiatives/')({ component: DashboardPage })`
  - Create `packages/web/src/routes/initiatives/$id.lazy.tsx` — move InitiativeDetailPage and all helper components there
  - Create `packages/web/src/routes/inbox.lazy.tsx` — move InboxPage there
  - Keep the original files with just the route definition (params, loaders) and remove the component export

  The goal: separate chunks for dashboard, detail, and inbox routes.
  </action>
  <verify>
  - `npm run --workspace=packages/web build` succeeds
  - Build output shows multiple JS chunks (not just one index-*.js file)
  - No chunk exceeds 500 KB warning, or the warning is reduced
  </verify>
  <done>Routes are code-split into separate chunks. Initial load only fetches the active route's code.</done>
</task>

<task type="auto">
  <name>Task 2: Optimize PlanTasksFetcher render loop in initiative detail</name>
  <files>packages/web/src/routes/initiatives/$id.tsx</files>
  <action>
  The PlanTasksFetcher component currently calls `onTasks(planId, tasks)` on every render when `tasksQuery.isSuccess` is true (lines 195-199). This is called inside the render body, not in a useEffect, causing potential infinite re-render loops when the parent's state update triggers a re-render.

  Fix:
  1. Move the `onTasks` call into a `useEffect` with `[tasksQuery.data, planId, onTasks]` as dependencies.
  2. Inside the useEffect, only call `onTasks` when `tasksQuery.data` is defined.
  3. This prevents the setState-during-render issue and aligns with React best practices.

  The pattern should be:
  ```
  useEffect(() => {
    if (tasksQuery.data) {
      onTasks(planId, tasksQuery.data as unknown as SerializedTask[]);
    }
  }, [tasksQuery.data, planId, onTasks]);
  ```
  </action>
  <verify>
  - `npx tsc --noEmit -p packages/web/tsconfig.app.json` passes
  - `npm run --workspace=packages/web build` succeeds
  </verify>
  <done>PlanTasksFetcher no longer triggers setState during render. Data flows through useEffect.</done>
</task>

</tasks>

<verification>
Before declaring plan complete:
- [ ] `npx tsc --noEmit -p packages/web/tsconfig.app.json` passes
- [ ] `npm run --workspace=packages/web build` succeeds
- [ ] Build output shows multiple chunks (code splitting working)
- [ ] PlanTasksFetcher uses useEffect for data callback
</verification>

<success_criteria>

- All tasks completed
- All verification checks pass
- Bundle split into route-level chunks
- No setState-during-render warnings in PlanTasksFetcher
  </success_criteria>

<output>
After completion, create `.planning/phases/21-polish-integration/21-05-SUMMARY.md`
</output>
