diff --git a/docs/superpowers/2026-05-29-MASTER-PLAN.md b/docs/superpowers/2026-05-29-MASTER-PLAN.md index 4200f84..f1a5543 100644 --- a/docs/superpowers/2026-05-29-MASTER-PLAN.md +++ b/docs/superpowers/2026-05-29-MASTER-PLAN.md @@ -27,6 +27,12 @@ 18. Open decisions & deferred work 19. Glossary 20. Document index +- Appendix A — Data dictionary (entity schemas) +- Appendix B — The 10 scenarios, concrete data +- Appendix C — Component & file map +- Appendix D — Demo walkthrough clickstreams (sales + investor) +- Appendix E — Naming conventions +- Appendix F — Verification & QA detail --- @@ -309,4 +315,158 @@ Role-specific **dashboards/landing stay distinct**: `/owner`, `/admin`, `/agent` - **Execution protocol:** `docs/superpowers/plans/2026-05-29-execution-protocol.md` - **Plan 1 — Data Foundation (done):** `docs/superpowers/plans/2026-05-29-demo-data-foundation.md` - **Plan 2 — Unify + Routing (written):** `docs/superpowers/plans/2026-05-29-plan2-unify-routing.md` -- **Plans 3–7:** authored from this master doc as each prior plan lands. +- **Plans 3–8:** authored from this master doc as each prior plan lands. + +--- + +## Appendix A — Data dictionary (entity schemas) + +Field-level reference for the core entities. "Phase" = where the field is introduced/populated. Types are illustrative (mock data, no DB). + +### A.1 Project (unified job) — `projects[]`, id `PRJ-2026-###` +| Field | Type | Notes / Phase | +|-------|------|---------------| +| `id` | string | `PRJ-2026-###` (Phase 2) | +| `sourceLeadId` | string? | `kl_###` if converted from a won lead (Phase 2) | +| `ownerId` | string | → owner (own_001/own_002) | +| `customer` | object | `{ name, phone, email, address }` | +| `address`, `propertyId` | string | site address | +| `projectType` | string | Roof Replacement, HVAC, Siding, … | +| `status` | enum | active / completed / on_hold / disputed | +| `lifecycleStage` | enum | the §9 state machine (Phase 4) | +| `phase` | string | Structural / MEP / Finishing / Handover | +| `completionPercentage` | number | tied to `lifecycleStage` (Phase 4) | +| `healthScore` | number | 0–100 | +| `budget` / `approvedBudget` | number | contract value | +| `committedCost`, `actualCost`, `spent` | number | `spent == actualCost`; `committed ≥ actual` | +| `variancePercent` | number | `round((actualCost−budget)/budget×100,1)` | +| `margin` | number | gross margin | +| `commissionType` / `commissionRate` / `commission` | — | flat / percent_gross / percent_net / custom | +| `budgetBreakdown[]` | array | `{ name, category, allocated, committed, actual }` — Σ each column ties to totals | +| `milestones[]` | array | `{ id, name, dueDate, status, assignedTo }` | +| `invoices[]` | array | `{ id, amount, submittedBy, status, dueDate, datePaid }` — Σpaid ≤ actualCost | +| `paymentSchedule[]` | array | `{ id, milestone, amount, dueDate, status }` | +| `changeOrders[]` | array | `{ id, title, amount, status, dateSubmitted, description }` | +| `rfis[]` | array | `{ id, subject, status, submittedBy, dateOpened, dateClosed }` | +| `riskLog[]` / `issueLog[]` | array | risk & issue tracking | +| `activityTimeline[]` | array | `{ date, action, user, details }` | +| `documents[]` | array | `{ id, name, uploadedBy, uploadedDate, size, notes }` | +| `teamMembers[]` | array | `{ id, userId, name, orgRole, jobRole, status, commissionEnabled, jobCommission }` | +| `contractorId`, `subcontractorIds[]` | FK | → con_001 / sub_### | +| `estimateVersions[]` | array | Phase 4 — `{ version, date, total, note, status, lineItems/templateRef }` | +| `reportedScope` / `verifiedScope` | object | Phase 4 — claimed vs verified | +| `scopeVariance` | object? | Phase 4 — `{ reported, verified, flagged }` | +| `inspections[]` | array | Phase 4 — see A.5 | +| `openRFIs`, `changeOrderCount`, `pendingInvoiceCount` | number | computed counts | + +### A.2 Pre-sale lead — `kanbanLeads[]` (`KANBAN_LEADS_INITIAL`), id `kl_###` +`{ id, name, address, phone, email, jobType, insuranceType, value, stormSource?, assignedAgentId, assignedAgentName, columnId, bucketId?, projectId?(if won), notes, createdDate, activity[{from,to,by,date,note}] }`. Stages via `columnId` (kc_new…kc_complete) + buckets (kc_stuck/kc_followup). + +### A.3 Person & Profile — `users`/`personnel`/`owners`/`subcontractors`; profile (Phase 7) +- **Person (login/identity):** `{ id, name, email, empId(LUP-####), username, password, role, type, companyName?, tradeType?, phone? }`. +- **Profile.self (Phase 7):** `{ preferredName, photo, phone, altContact, address, emergencyContact, nickname?, pronouns?, gender?, sex?, bio, yearsExperience, languages[], specialties[], equipment[], availability, serviceAreas[], (sub:) license, insurance{coi,expiry}, w9Status }`. +- **Profile.company (owner-only, Phase 7):** `{ internalNotes, statusTags[], skillScores[{skillId,score|'N/A'}], payTerms, hireDate, managerId }`. +- **Achievements (employees, read-only):** from ProCanvas gamification — `{ level, xp, streak, badges[], achievementTiers[] }`. +- **Skill catalog (org, Phase 7):** `skillCatalog[] = { id, name, description }`. + +### A.4 Subcontractor task — `MOCK_SUBCONTRACTOR_TASKS[]`, id `sct_###` +`{ id, projectId(→PRJ after Phase 2), subcontractorId, assignedBy, assignedByName, title, category(Phase 5 column), location, description, status(§10 machine), priority, dueDate, assignedDate(Phase 5 column), photos[], statusHistory[], thread[{senderId,senderName,...}], activities[], expenses[], fees[], paymentStatus, paidAt?, stageNotes[](Phase 5) }`. Notifications: `MOCK_NOTIFICATIONS[] = { id, recipientUserId, taskId, type, message, read, timestamp }`. + +### A.5 Inspection record (Phase 4) — `project.inspections[]` +`{ id, kind: 'damage'|'final', round, inspectorName, inspectorRole?(damage), inspectorContact?{phone,email}(final/external), inspectionDate, result: 'pass'|'fail', feedbackNote, issues[{ id, description, note, photos[], status:'open'|'fixed' }] }`. + +### A.6 Estimate version (Phase 4) — `project.estimateVersions[]` +`{ version, date, total, note, status:'sent'|'revised'|'signed', templateId?, lineItems[] }`. Renders via the existing estimate/template view. + +### A.7 Dispatch (LynkDispatch) +- **`DISPATCH_REPS[]`** (map to canonical people in Phase 7): `{ id, repId→personId, initials, name, status:'available'|'en_route'|'busy', rating, closeRate, todayAppointments, maxDaily, performanceScore }`. +- **`DISPATCH_RECOMMENDATIONS[leadId][]`:** `{ repId, score, factors{proximity,driveTime,calendarFit,weather,skillMatch}, reasons[], stormReasons[], aiInsight, distanceMi, slotStart, slotEnd, conflictFlag }`. Phase 7 authors `skillMatch`/`reasons`/`aiInsight` to cite real profile facts. + +### A.8 Access control — Org Settings +- `orgPermissions[moduleKey][roleKey] = [actions]` (role defaults). +- `userPermissionOverrides[] = { userId, moduleKey, action, effect:'grant'|'deny' }` (per-person wins over role default). +- `PERMISSION_MODULES[moduleKey] = { label, actions[] }` — expanded in Phase 8 to cover every gated route. + +--- + +## Appendix B — The 10 scenarios, concrete data + +Each is a full thread (lead → project → subcontractor task → invoices → commission) with consistent roster identities. Addresses are Plano/DFW. (Existing `proj_001…012` migrate to `PRJ-2026-001…012`; converted won-leads become `PRJ-2026-013…028` — the table below assigns each scenario a representative id; exact figures are authored to satisfy the financial invariants.) + +| # | Scenario | Project | Customer / Address | Type · Value | Owner · Rep · Sub | Lifecycle stage | The beat | +|---|----------|---------|--------------------|--------------|-------------------|-----------------|----------| +| 1 | Storm-driven win, on track | PRJ-2026-001 | Marcus & Tara Whitfield · 2604 Dunwick Dr | Roof Replacement · $45k | Justin Johnson · Jesus Gonzales · Carlos Mendoza | Work In Progress (42%) | Cody Tatum canvassed after the hail event → signed → Carlos on electrical | +| 2 | Insurance-backed up-scope → approved | PRJ-2026-013 | Brenda Ochoa · 3917 Arizona Pl | Roof Replacement · $200→$15.4k | Diana Reeves · Sarah Calloway · Maya Patel | Contract Signed | "Small leak" self-report → inspection finds major hail damage → supplement → approved | +| 3 | Up-scope → declined (lost) | (lead, lost) | Greg Lindfors · 6601 Phoenix Pl | Roof · quoted $18k | — · Jesus Gonzales · — | Lead (lost) | Revised estimate too high; lead marked "declined after inspection" | +| 4 | Smaller/different than claimed | PRJ-2026-014 | Nadia Okafor · 2516 Dunwick Dr | Gutter Repair · $1.9k | Justin Johnson · Sarah Calloway · — | Completed | Claimed full replacement; inspection down-scopes to a quick repair | +| 5 | Multiple revisions / negotiation | PRJ-2026-015 | The Bauer Trust · 7224 Independence Pkwy | Window Replacement · $42k | Diana Reeves · Jesus Gonzales · Greg Alston | Scope Approved | v1→v2→v3 estimate history before agreement | +| 6 | Over-budget / margin loss | PRJ-2026-004 | Hollis Property Group · 2612 Dunwick Dr | Interior Reno · $55k (over) | Justin Johnson · Sarah Calloway · Carlos Mendoza | Work In Progress (78%) | Scope-creep change orders push committed > budget; negative margin | +| 7 | Large commercial, multi-phase | PRJ-2026-007 | Custer Rd Retail LLC · 6909 Custer Rd | Commercial Roof · $125k | Diana Reeves · Jesus Gonzales · Maya Patel + Carlos | Work In Progress (32%) | Phased payment schedule, multiple subs, big commission | +| 8 | Near-complete, healthy | PRJ-2026-008 | Winslow Family · 2112 Winslow Dr | Foundation · $75k | Diana Reeves · Sarah Calloway · Jennifer Castillo | Work Complete — Awaiting Inspection (88%) | Most milestones done; final invoices pending | +| 9 | Completed, full lifecycle, paid | PRJ-2026-010 | Legacy Plaza HOA · 2100 Legacy Dr | Parking Lot Resurface · $35k | Diana Reeves · Jesus Gonzales · — | Final Payment Received (100%) | Closed-won; all invoices paid; commission paid out | +| 10 | Disputed / on-hold | PRJ-2026-011 | Cedar Ridge Owners · Legacy Town Center | Facade Restoration · $95k | Diana Reeves · Sarah Calloway · Dwayne Boudreaux | On Hold | Dispute timeline, paused payments, health score ~30 | + +--- + +## Appendix C — Component & file map + +| File | Responsibility | +|------|----------------| +| `src/data/mockStore.jsx` | The entire mock data store + mutators + `resolvePerson` + (Phase 3) selectors hook-ins. ~7,000 lines. The hot file — most data tasks touch it (edit sequentially). | +| `src/data/selectors.js` *(new, Phase 3)* | Pure functions deriving analytics (pipeline value/funnel, revenue/profit, commission, sub performance, storm attribution) from the store arrays. | +| `src/App.jsx` | Route table + `ProtectedRoute`. Phase 8 rewrites this to unified routes + the permission-aware guard. | +| `src/pages/owner/OwnerProjectDetail.jsx` | The rich project detail page (10 tabs, commission/profit engine). Becomes the single job page (Phase 2); gains lifecycle/inspection/estimate UI (Phase 4). | +| `src/pages/LeadProjectPage.jsx` | Pre-sale lead detail (lighter). Won leads stop using it after Phase 2. | +| `src/pages/KanbanPage.jsx` + `src/components/kanban/KanbanCard.jsx`, `LeadInfoDrawer.jsx` | Pipeline board, cards, side drawer. Drawer "More Details" routes won leads → project page (Phase 2). | +| `src/pages/owner/OwnerProjectList.jsx` | Lists projects + pipeline leads; links both (relinked Phase 2). | +| `src/pages/Maps.jsx` | Territory map; owners can create entries (fixed). | +| `src/components/Layout.jsx` | Sidebar/nav; Phase 8 hides items a person can't `view`. | +| `src/hooks/usePermissions.js`, `src/components/PermissionGate.jsx`, `src/utils/permissions.js` | RBAC core (`can`, role resolution, component gating). Phase 8 adds the route guard + Access Restricted page. | +| `src/pages/owner/OrgSettings.jsx` | Edits roles, permissions, per-user overrides, commission. | +| `src/components/dispatch/DispatchRecommendationDrawer.jsx` | LynkDispatch recommendation UI. Phase 7 wires profile-cited rationale. | +| `src/pages/ProCanvas.jsx` + `components/ProCanvas/gamification/*` | Gamified canvassing (XP/levels/badges) — source of profile achievements. | +| `src/pages/Login.jsx`, `src/context/AuthContext.jsx` | Per-role login picker (done); auth matching by empId/username. | +| `src/pages/subcontractor/*`, `src/components/subcontractor/*` | Subcontractor portal + task modals (Phases 5–6). | + +--- + +## Appendix D — Demo walkthrough clickstreams + +**Sales demo (the continuous story):** +1. Log in as **Justin Johnson (Owner)** → Owner dashboard: pipeline value, revenue, profit KPIs. +2. **Storm Intel** → a recent Plano hail event; **ProCanvas/leaderboard** → Cody Tatum's door-knocks that day. +3. **Pipeline** → the storm-sourced lead (Scenario 2); open it → reported scope "small leak ~$200". +4. Project detail → **Damage Verified** with inspection photos; **scope-variance badge** ("$200 → $15.4k"); **estimate versions** (v1 claim → v2 verified, signed); insurance supplement. +5. **Lifecycle** advances → Work In Progress; **Team** tab shows Carlos Mendoza's subcontractor task; open **Subcontractor Tasks** → his stage timeline. +6. **Invoices/Budget** reconcile; **commission** for Sarah Calloway computed. Close as **Scenario 9** (completed, paid). + +**Investor demo (breadth + sophistication):** +1. Owner dashboard → computed KPIs; drill a KPI into the exact projects that compose it (coherence proof). +2. **LynkDispatch** → assign an incoming lead; the AI cites real profile facts ("Carlos — 92 Emergency Tarp · carries a tarp"). +3. **Leaderboard** → animated podium; **ProCanvas** → gamification depth. +4. **Org Settings** → roles, granular per-person permissions (configure "deny one admin a feature" live); navigate as that admin → **Access Restricted**. +5. **Profiles / skill catalog** → the structured context behind the AI. +6. Breadth tour: 28 projects across 10 scenario types, storm attribution, subcontractor performance. + +--- + +## Appendix E — Naming conventions + +- **Person ids:** owners `own_00#`, admins `a#`, sales reps/personnel `p#`, field agents `e#`, contractor `con_00#`, subcontractors `sub_00#`. Employee id (login + display): `LUP-####` (1xxx employees/owners, 2xxx subs, 3xxx contractor). +- **Job ids:** projects `PRJ-2026-###`; pre-sale leads `kl_###`; subcontractor tasks `sct_###`. +- **Routes:** kebab-case feature routes, no role prefix (`/territory-map`, `/pipeline`, `/projects/:id`); role landings `/owner`, `/admin`, `/agent`, `/contractor`, `/vendor`, `/subcontractor`. +- **Dates:** all relative to `DEMO_TODAY` (2026-05-29); never future-dated. +- **Commits:** conventional prefixes (`feat`/`fix`/`chore`/`docs`/`refactor`); **no AI/Claude attribution**; author Satyam Rastogi. +- **Files:** pages in `src/pages/**`, shared UI in `src/components/**`, hooks in `src/hooks/**`, pure logic in `src/utils/**`, data/selectors in `src/data/**`. + +--- + +## Appendix F — Verification & QA detail + +No unit-test framework exists, so verification is layered: +1. **Build gate:** `npm run build` must end `✓ built` with zero errors after every task — this catches import errors, duplicate declarations, and broken references (it caught both a merge duplicate-declaration bug and a Phase-1 copy-paste bug). +2. **Grep/invariant checks per task:** e.g. `git grep` for stale ids/names returns none; project financial invariants (Appendix A.1) hold; every FK resolves. +3. **Reconciliation review:** for data-heavy tasks, an independent read-only reviewer re-sums breakdowns/invoices and confirms dashboard KPIs equal the underlying records. +4. **Cross-login coherence:** the same job viewed as owner vs subcontractor agrees (Phase 6); no "Unknown" person labels. +5. **Dev-run spot checks:** Leads page non-empty; each role login lands correctly; denied routes show Access Restricted (Phase 8); lifecycle progress % matches stage (Phase 4). +6. **Final whole-implementation review** per plan, then `superpowers:finishing-a-development-branch`.