## Summary
Adds a new Progress view to the Buildout dashboard (/dashboards?tab=buildout) for P2 members to track pre-launch Work Unit completion per campus.
Rows are non-cancelled sites whose current derived milestone is in the pre-launch buildout window (milestones 4-8: construction permits through ready-to-open). Columns are all 13 Quality Bars. Each populated cell is a percentage bar of pre-launch catalog WU completion for that QB; QBs with no pre-launch WUs for that site render as empty cells. Clicking a populated cell opens a side panel that drills into the QB -> its Work Unit Groups -> pre-launch Work Units, where eligible users can edit each WU's status, start/due/completed dates, and assignee. The panel also exposes read-only, paginated notes for WUs that have notes.
The existing FTO Pipeline matrix remains the default; the new grid is a second mode selectable from the "Sort & display" popover, mirroring how the Diligence tab switches between Progress and Outcomes. The only DB schema change is a new optional launchPhase ("pre" | "post") field on work units. Everything else the dashboard edits (title, status, dates, assignee) already exists on the workUnits table.
### Screenshots
<img width="2294" height="1220" alt="Screenshot 2026-07-06 at 1 49 28 PM" src="https://github.com/user-attachments/assets/6bbc3628-741c-4b04-a48f-94154213c903" />
<img width="2022" height="1212" alt="Screenshot 2026-07-06 at 1 49 56 PM" src="https://github.com/user-attachments/assets/4cac28e1-d415-4983-9690-15dc8906438c" />
<img width="968" height="478" alt="Screenshot 2026-07-06 at 12 02 43 PM" src="https://github.com/user-attachments/assets/0befac43-6aab-49d8-acb3-391e07b470bf" />
### Changes
Schema & data
- chat/convex/rhodes/schema.ts — adds launchPhaseValidator and an optional launchPhase field on the workUnits table.
- chat/convex/rhodes/p2WorkUnitDefinitions.ts — adds launchPhase: "pre" to the P2WorkUnitDefinition type and to all 60 P2 definitions (P2 has no post-launch WUs yet).
- chat/convex/rhodes/p2WorkUnits.ts — provisioning now stamps launchPhase on inserted WUs, and adds backfillWorkUnitLaunchPhase to set launchPhase: "pre" on existing P2 pre-launch catalog rows that lack it.
Backend queries & API
- chat/convex/rhodes/dashboard.ts — adds p2BuildoutDashboard (grid payload), p2BuildoutSiteQualityBarDetail (panel payload), p2BuildoutWorkUnitNotes (paginated WU notes), and editP2BuildoutWorkUnit. Reads are gated on operations.buildout.read; edits also require canEditSite. The bar numerator counts WUs whose status is completed or skipped; only pre-launch WUs count (missing launchPhase is treated as "pre" defensively). All 13 QB columns are emitted; per-row cells are only present for QBs with >=1 pre-launch WU.
- chat/lib/p2-buildout-contract.ts *(new)* — shared payload types + zod schemas for grid, detail, and notes payloads; shared edit types/status values. The edit request body schema lives in the API route.
- chat/lib/rhodes-p2-buildout-server.ts *(new)* — token-injecting fetch/edit helpers over the Convex functions, including the notes feed helper.
- chat/app/api/p2-buildout/route.ts *(new)* — GET grid payload.
- chat/app/api/p2-buildout/[siteId]/quality-bars/[qb]/route.ts *(new)* — GET QB detail.
- chat/app/api/p2-buildout/[siteId]/work-units/route.ts *(new)* — POST single-WU edit (status, dates, assignee).
- chat/app/api/p2-buildout/[siteId]/work-units/[workUnitId]/notes/route.ts *(new)* — GET paginated read-only WU notes.
Frontend
- chat/components/dashboards/buildout/buildout-view.tsx *(new)* — parent that holds the persisted Pipeline/Progress view mode and renders both panes (Pipeline = existing FtoPipelineView, Progress = new grid).
- chat/components/dashboards/buildout/buildout-view-mode-toggle.tsx *(new)* — the Pipeline/Progress segmented control, injected as the rootPrefix of the Sort & display popover.
- chat/components/dashboards/buildout/p2-buildout-progress-view.tsx *(new)* — fetches the grid, owns search/sort + panel selection state, and mounts the side panel.
- chat/components/dashboards/buildout/p2-buildout-matrix.tsx *(new)* — sticky Site column + one column per QB; populated cells show percentage bars, empty cells show no WU data.
- chat/components/dashboards/buildout/p2-buildout-detail-panel.tsx *(new)* — QB detail panel: header count, WUGs -> pre-launch WUs, per-WU editor (status, dates, assignee), compact navigation, close control, and read-only notes view.
- chat/components/dashboards/shared/note-card.tsx *(new)* — shared display component for note feed entries.
- chat/components/dashboards/dashboards-layout.tsx — the buildout tab now renders <BuildoutView /> instead of <FtoPipelineView />.
- chat/components/dashboards/fto/fto-pipeline-view.tsx — threads an optional, backward-compatible renderViewModeToggle prop so the toggle can be injected into the pipeline's Sort & display popover; existing behavior is unchanged.
Tests, docs, and related hardening
- chat/convex/rhodesDashboardP2Buildout.test.ts, chat/convex/p2WorkUnits.test.ts, chat/app/api/p2-buildout/__tests__/route.test.ts, chat/components/dashboards/buildout/__tests__/p2-buildout-matrix.test.tsx — coverage for dashboard semantics, backfill behavior, API route behavior, notes pagination, and matrix rendering.
- chat/lib/platform-error-coverage-inventory.ts and docs/platform-error-smoke.md — registers the new P2 Buildout routes in platform error coverage.
- chat/lib/operating-sites.ts and tests — accepts nullable split DRI refs returned by Rhodes listSites.
- chat/convex/rhodesDashboardParity.test.ts — tightens utilities parity assertions.
- features/dashboards/p2-buildout-dashboard/plan.md *(new)* — the implementation plan this PR was built from.
### Design Decisions
- Milestone-driven site selection — the Progress grid uses the same active-milestone model as the rest of the dashboard: sites are included when their current milestone is one of milestones 4-8, not merely because their stored stage is buildout or operating.
- All 13 QB columns are always visible — rows remain sparse, but the matrix shape is stable across campuses.
- launchPhase on WUs only — WUGs can hold both pre- and post-launch WUs, so the pre/post distinction lives on the WU. Adding the field to workUnits alone keeps the model minimal; the dashboard's pre-launch filter reads it directly.
- Status and completedDate are independent — toggling a WU's status never auto-stamps or clears completedDate (and vice versa). P2 members set the completion date explicitly.
- "Done" counts completed or skipped — matches the existing dashboard rollups so a legitimately skipped WU doesn't drag a campus's bar down.
- Missing launchPhase treated as "pre" — bars render correctly before the backfill runs, so the rollout is not gated on the migration.
## Backfill Runbook
This PR adds launchPhase to P2 work units. Existing P2 pre-launch catalog workUnits should be backfilled after deploy so rows missing launchPhase are explicitly patched with launchPhase: "pre".
The backfill is paginated by site. With scheduleNext: true, the command response reports only the current page's counts; later pages are scheduled and are not aggregated into the CLI output.
### Dev
First, confirm which dev deployment you are pointing at:
pnpm --dir chat exec convex dev --once
Then dry run against that dev deployment:
CONVEX_DEPLOY_KEY= pnpm --dir chat exec convex run rhodes/p2WorkUnits:backfillWorkUnitLaunchPhase '{"batchSize":5,"scheduleNext":true,"dryRun":true}'Then run the live dev backfill:
CONVEX_DEPLOY_KEY= pnpm --dir chat exec convex run rhodes/p2WorkUnits:backfillWorkUnitLaunchPhase '{"batchSize":5,"scheduleNext":true,"dryRun":false}'### Production
Dry run production first:
CONVEX_DEPLOY_KEY= pnpm --dir chat exec convex run --deployment oceanic-pika-463 rhodes/p2WorkUnits:backfillWorkUnitLaunchPhase '{"batchSize":5,"scheduleNext":true,"dryRun":true}'Then run the live production backfill:
CONVEX_DEPLOY_KEY= pnpm --dir chat exec convex run --deployment oceanic-pika-463 rhodes/p2WorkUnits:backfillWorkUnitLaunchPhase '{"batchSize":5,"scheduleNext":true,"dryRun":false}'Expected result: existing P2 pre-launch catalog workUnits missing launchPhase are patched with launchPhase: "pre". Unrelated/manual WUs are left alone, and existing launchPhase: "post" rows are preserved. The dashboard has a defensive fallback before this runs, but the backfill should still be part of the PR rollout.
## Test Plan
- [x] pnpm typecheck — clean (exit 0)
- [x] pnpm biome check — clean
- [ ] Manual: run rhodes/p2WorkUnits:backfillWorkUnitLaunchPhase with {"dryRun": true}, review counts, then {"dryRun": false} to materialize launchPhase: "pre" on existing WUs
- [ ] Manual: confirm operations.buildout.read is granted to the roles that should see the Progress grid
- [ ] Manual: on /dashboards?tab=buildout, switch to Progress, verify all 13 QB columns, per-QB bars/empty cells, side-panel navigation, WU notes, and WU edits for status/dates/assignee