Files
Codewalkers/.planning/phases/21-polish-integration/21-05-PLAN.md
Lukas May 81814ac213 docs(21): create phase plan for Polish & Integration
Phase 21: Polish & Integration
- 6 plans in 3 waves
- 3 parallel (Wave 1), 2 parallel (Wave 2), 1 sequential (Wave 3)
- Ready for execution
2026-02-05 07:46:36 +01:00

4.7 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous
phase plan type wave depends_on files_modified autonomous
21-polish-integration 05 execute 2
21-01
21-02
21-03
packages/web/vite.config.ts
packages/web/src/routes/initiatives/$id.tsx
packages/web/src/components/InitiativeCard.tsx
true
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.

<execution_context> @/.claude/get-shit-done/workflows/execute-plan.md @/.claude/get-shit-done/templates/summary.md </execution_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

Task 1: Add route-based code splitting via TanStack Router lazy routes packages/web/src/routes/initiatives/index.tsx, packages/web/src/routes/initiatives/$id.tsx, packages/web/src/routes/inbox.tsx 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.

  • 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 Routes are code-split into separate chunks. Initial load only fetches the active route's code.
Task 2: Optimize PlanTasksFetcher render loop in initiative detail packages/web/src/routes/initiatives/$id.tsx 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]);
- `npx tsc --noEmit -p packages/web/tsconfig.app.json` passes - `npm run --workspace=packages/web build` succeeds PlanTasksFetcher no longer triggers setState during render. Data flows through useEffect. 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

<success_criteria>

  • All tasks completed
  • All verification checks pass
  • Bundle split into route-level chunks
  • No setState-during-render warnings in PlanTasksFetcher </success_criteria>
After completion, create `.planning/phases/21-polish-integration/21-05-SUMMARY.md`