62 KiB
LynkedUp Pro Roofing — Demo Build: Master Plan, Scope & Rationale
Date: 2026-05-29 · Branch: revamp · App: React 18 + Vite 7 SPA, mock React-context data store, pnpm.
What this document is. The single, authoritative "what we are building and why." It paints the whole picture: the vision, the product as it stands, the canonical company, the current vs. target data model, every phase of work with rationale and acceptance criteria, the state machines, the demo scenarios, and how we execute safely. Detailed step-by-step plans live in
docs/superpowers/plans/; the formal design spec indocs/superpowers/specs/2026-05-29-demo-data-coherence-design.md; the subagent rules indocs/superpowers/plans/2026-05-29-execution-protocol.md. When those and this doc disagree, this doc is the intent; the others are the mechanics.
Table of contents
- Vision & audiences
- Guiding principles
- The product at a glance (roles, modules, screens)
- The canonical company (roster & org)
- Data architecture — current state
- Data architecture — target unified model
- Status: what's already done (Phase 1)
- The phases (2–8) in depth
- The project lifecycle state machine
- The subcontractor task state machine
- The estimate & inspection models
- The 10 demo-case scenarios
- Profiles, skills & AI dispatch context
- Cross-cutting coherency requirements
- Routing map (current → target)
- Execution model (subagent-driven, parallel-safe)
- Risks & how we mitigate them
- Open decisions & deferred work
- Glossary
- 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
- Appendix G — Plan-by-plan roadmap (what · why · how)
1. Vision & audiences
Turn this CRM's mock data and screens into one coherent fake roofing company — "LynkedUp Pro Roofing," based in Plano, Texas — convincing enough to carry two very different conversations:
- The live sales demo. A salesperson sits with a roofing-company prospect and walks the app. Nothing may be empty or obviously fake. Numbers must feel real at a glance. There must be a continuous story: a storm hits, a canvasser knocks a door, a lead is created, an inspector verifies major damage, an insurance-backed estimate is signed, the job runs with subcontractors, invoices are paid, commission is calculated. Every screen the salesperson clicks into must reinforce that story.
- The investor / stakeholder pitch. Here breadth and sophistication matter: dashboards that summarize a busy pipeline, profit and commission analytics, storm-driven lead attribution, AI-assisted dispatch, gamified field teams. The product should look like a mature, data-rich platform — not a prototype with three rows of sample data.
These two audiences pull in the same direction if — and only if — the underlying data is deeply consistent. A flashy dashboard that contradicts the records behind it is worse than no dashboard. So the whole effort optimizes for end-to-end coherence first, and surface polish second.
2. Guiding principles
- Coherence is the wow. The single most impressive thing in a live demo is drilling from a summary number into the exact records that compose it and finding they tie out — dashboard revenue equals the sum of project contract values; a project's budget breakdown sums to its committed and actual costs; the subcontractor working a job sees the same job the owner sees. We engineer for that.
- Compute analytics from data, never hardcode. Dashboard KPIs are derived via selectors over the live
projects/leads/tasksarrays. This guarantees they can never drift from the detail screens, and every data fix automatically flows into the charts. The only exception is purely-visual long-range trend lines (e.g. a 12-month revenue sparkline) where no daily source data exists — those are clearly illustrative. - One canonical identity per person. Every employee, owner, contractor, and subcontractor has exactly one id, one name, one set of attributes, referenced consistently everywhere. No placeholder names, no parallel id schemes, no "Unknown" labels.
- A job is one entity. A lead that is won becomes a project — the same record, not a parallel copy. This is the architectural spine that lets the rich detail UI, lifecycle, estimates, and analytics apply everywhere a job appears.
- No empty or broken views. Every tab, list, and modal a demo might open is populated with believable data. Dates are anchored to a fixed
DEMO_TODAY(2026-05-29) so nothing goes stale or lands in the future. - Texan realism. Names, addresses, companies, and phone numbers read like a real Plano/DFW roofing operation, not
test/asdf/Admin One.
3. The product at a glance
Roles (login types): Customer, Field Agent (canvasser), Admin/Manager, Owner, Contractor, Vendor, Subcontractor. Each lands on a role-specific dashboard after login.
Major modules / screens:
- Maps / Territory (
/…/maps) — pin properties, reverse-geocode, create entries. (Owners can now create entries — fixed.) - Pipeline / Kanban (
/…/kanban) — leads flow New → Contacted → Appt → Estimate → Signed → In-Progress → Complete, plus Stuck / Follow-up buckets. - Leads list (
/…/leads) and Lead detail (/…/leads/:id). - Projects (owner) — list (
/owner/projects) and rich detail (/owner/projects/:id) with 10 tabs (Overview, Budget & Costs, Team, Change Orders, RFIs, Milestones, Invoices, Risk & Issues, Activity, Docs). - LynkDispatch (
/…/dispatch) — AI-scored rep recommendations for incoming leads. - Storm Intel (
/…/storm-intel) and Field Storm Zones — hail/storm data, attribution. - Leaderboard (
/admin/leaderboard) — sales & canvasser rankings (animated podium). - ProCanvas (
/…/pro-canvas) — gamified canvassing (XP, levels, badges, achievement tiers). - Estimates / EstimateBuilder, Owner Snapshot dashboard, Org Settings (roles, permissions, commission), Subcontractor Tasks (owner) and the Subcontractor portal (dashboard, task detail).
Data store: a single large React-context module (src/data/mockStore.jsx, ~7,000 lines) exposing arrays (projects, kanbanLeads, leads, users, personnel, owners, subcontractors, orgMembers, MOCK_SUBCONTRACTOR_TASKS, dispatch data, storm data, estimates/templates) and mutators via useMockStore(). There is no backend and no test framework — everything is in-memory mock data, and verification is by production build + reconciliation checks + running the dev server.
4. The canonical company (roster & org)
LynkedUp Pro Roofing — Plano, TX. Owners run the business; admins manage operations; sales reps close; field agents (canvassers) source storm leads door-to-door; a contractor and several subcontractors execute the work.
Canonical roster (single source of truth — names, ids, employee ids, login):
| id | Role | Name | empId | Login type | Login id | Password |
|---|---|---|---|---|---|---|
| own_001 | Owner | Justin Johnson | LUP-1001 | owner | justin | password |
| own_002 | Owner | Diana Reeves | LUP-1002 | owner | diana | password |
| a1 | Admin | Wade Hollis | LUP-1010 | employee | LUP-1010 | password |
| a2 | Admin | Darlene Brooks | LUP-1011 | employee | LUP-1011 | password |
| a3 | Admin | Roy Schaefer | LUP-1012 | employee | LUP-1012 | password |
| p1 | Sales Rep | Jesus Gonzales | LUP-1030 | employee | LUP-1030 | password |
| p2 | Sales Rep | Sarah Calloway | LUP-1031 | employee | LUP-1031 | password |
| e1 | Field Agent | Cody Tatum | LUP-1040 | employee | LUP-1040 | password |
| e2 | Field Agent | Hannah Reyes | LUP-1041 | employee | LUP-1041 | password |
| e3 | Field Agent | Travis Boone | LUP-1042 | employee | LUP-1042 | password |
| e4 | Field Agent | Shelby Greer | LUP-1043 | employee | LUP-1043 | password |
| e5 | Field Agent | Dalton Pruitt | LUP-1044 | employee | LUP-1044 | password |
| con_001 | Contractor | Mike Brennan | LUP-3001 | contractor | mike | password |
| sub_001 | Subcontractor | Carlos Mendoza (Electric Pro Services) | LUP-2001 | subcontractor | carlos | password |
| sub_002 | Subcontractor | Maya Patel (Skyline Roof Crew) | LUP-2002 | subcontractor | maya | password |
| sub_003 | Subcontractor | Dwayne Boudreaux (Holland Painting Co.) | LUP-2003 | subcontractor | dwayne | password |
| sub_004 | Subcontractor | Jennifer Castillo (Wu Plumbing) | LUP-2004 | subcontractor | jennifer | password |
| sub_005 | Subcontractor | Greg Alston (Alston Window Pros) | LUP-2005 | subcontractor | greg | password |
Org-role taxonomy used by Org Settings / permissions: OWNER, ADMIN, SALES_REP, CANVASSER, SUBCONTRACTOR. Login identity for employees/admins is the LUP-#### employee id; owners/contractors/subs log in by username; all share the demo password password (the login page's per-role picker auto-fills it).
5. Data architecture — current state
The single most important structural fact (confirmed by code exploration) is that a "job" is currently modeled twice:
projectsarray (proj_001…proj_012, now migrating toPRJ-2026-###). Rich schema:budget/approvedBudget,committedCost,actualCost,spent,variancePercent,margin,commissionType/Rate/commission,budgetBreakdown[],changeOrders[],rfis[],riskLog[],issueLog[],paymentSchedule[],activityTimeline[],milestones[],invoices[],documents[],teamMembers[],contractorId,subcontractorIds[],phase,healthScore,completionPercentage. Rendered byOwnerProjectDetail.jsx(2,086 lines, 10 tabs, owner-only) with a full commission/profit engine.KANBAN_LEADS_INITIAL(kl_001…kl_044) +KANBAN_PROJECT_DATA(keyed by the 16 won lead ids kl_023–kl_038). Thinner schema with different field names (estimatedAmountvsbudget,completionPctvscompletionPercentage,workTimelinevsactivityTimeline, divergent milestone/invoice/RFI/CO shapes) and denormalized contractor strings instead of FKs. Rendered byLeadProjectPage.jsx(1,159 lines, simple/full tab switch).
The two share no join key — no common id, address, or foreign key. They describe overlapping real-world jobs but cannot be cross-referenced. This duplication is the root cause of "lead pages don't match project pages," inconsistent routes, and the maintenance burden of keeping two datasets coherent. Resolving it is Phase 2.
Other key collections: users/personnel/owners/subcontractors (people, now canonical), orgMembers (org roster + roles), MOCK_SUBCONTRACTOR_TASKS + MOCK_NOTIFICATIONS (subcontractor work), DISPATCH_REPS + DISPATCH_RECOMMENDATIONS (LynkDispatch — note: a separate REP-0x namespace), MOCK_ESTIMATES_INITIAL + MASTER_TEMPLATES_INITIAL (estimates, not yet linked to projects), storm/hail data, and ProCanvas gamification state.
6. Data architecture — target unified model
Principle: a won lead and a project are the same entity. After Phase 2:
- One
projectsarray holds all jobs, keyed byPRJ-2026-###. The 12 existing projects migrate ids (with their inbound FKs from tasks/schedule/subcontractor data). The 16 won kanban leads (kl_023–038) are converted into full project records (PRJ-2026-013…028), withKANBAN_PROJECT_DATAfield names normalized to the project schema and missing project fields back-filled. - Bidirectional link: each converted project carries
sourceLeadId: 'kl_0xx'; each won kanban lead carriesprojectId: 'PRJ-2026-0xx'. The kanban board still shows these leads in Signed/In-Progress/Complete columns, but "More Details" routes to the unified project page. - Pre-sale leads (kl_001–022, 039–044 — New/Contacted/Appt/Estimate + Stuck/Follow-up) remain leads with no project, shown on the lighter
LeadProjectPageat/…/leads/:id. - One rich detail page (
OwnerProjectDetail, made viewable by owner/admin/field-agent) serves every won job at/…/projects/:id. Lead-side extras it currently lacks (pipeline progression bar, stage mover, hail history) are added onto it in Phase 4.
Field normalization map (kanban → project): estimatedAmount→budget/approvedBudget; completionPct→completionPercentage; workTimeline→activityTimeline; milestone date→dueDate; invoice {description,paidDate}→{submittedBy,datePaid}; rfi {openedDate,closedDate}→{dateOpened,dateClosed}; changeOrder {requestedBy,date}→{dateSubmitted}; contractorName/Phone→contractorId. Back-fill: teamMembers, riskLog/issueLog, paymentSchedule, commission*, phase, budgetBreakdown (Plan-1 invariants), computed openRFIs/changeOrderCount/pendingInvoiceCount.
7. Status: what's already done (Phase 1 — Data Foundation)
Complete, independently reviewed APPROVED, 14 commits, build clean. Delivered:
- Canonical identity — unified id scheme;
resolvePerson(id)selector as the single people lookup;orgMembersand subcontractor-task actor ids remapped; no stale ids anywhere. - Texan roster +
LUP-####empIds — all placeholder names ("Frank Agent", "Admin One", "Carlos Subcontractor", …) replaced repo-wide; emails reconciled. - Financial coherence — all 12 projects'
budgetBreakdowncolumns sum to their totals,committed ≥ actual,spent == actualCost, variance signs correct; paid invoices ≤ actual cost; 6 missing breakdowns added. (106 id/name pairings cross-checked, zero mismatches.) - No empty/broken views — Leads list seeded (35 leads);
sub_002–005can log in (company/trade aligned to source records); subcontractor tasks address-matched to real projects;On Holdstatus styled. - Pipeline data — every kanban lead has a
$ value; Complete-stage leads read finished; 18 storm leads flagged. - Login — per-role person picker (expand role → pick person → one-click in); all role paths verified.
- Earlier fixes — Vite
/api/*dev stub (no more@vercel/kvcrash); owners can create map entries; animated leaderboard podium (count-up + staggered bottom-up bars); the_Demo_Oldfeature branch merged in (subcontractor, org-settings, commission features) with conflicts resolved.
8. The phases (2–8) in depth
Phase 2 — Unify lead↔project + routing/naming (planned; plan written)
- Current: two data models, two detail pages, two route families, no join key (see §5).
- Target: one
projectsmodel withPRJ-2026-###ids; won leads converted and linked; one rich detail page served under/owner|admin|emp-fa/projects/:id; pre-sale leads stay on the lead page; kanban + project-list links updated (see §6). - Why: eliminates the duplication that makes lead pages thinner than project pages and routes inconsistent; once a job is one entity, lifecycle/estimates/analytics/subcontractor-parity all apply everywhere for free.
- Key decisions: Unify (won = project).
PRJ-2026-###. Pre-sale leads = lighter subset of the same page. - Obstacles handled in the plan: no join key → generate
projectIdon conversion + back-link; field-name mismatches → normalization map; missing project fields on won leads → back-fill with Plan-1 invariants; owner-only detail lookup → relax for admin/field-agent; 4 navigation call-sites → conditional links;proj_*FK blast-radius → migrate definitions + FKs together, verified by grep. - Acceptance: no
proj_###left; 16 new linked projects; all ~28 projects satisfy financial invariants; build clean; won leads route to the rich page for all three roles; pre-sale leads route to the lead page. - Demo beat: "Here's the pipeline. This signed job? Click in — full budget, team, change orders, invoices, the same screen the owner manages it on."
Phase 3 — Kanban/pipeline depth + analytics (next plan)
- Target: (1) realistic pre-sale lead details (customer, address, estimate value, activity notes, real dates — no sequential fake phones); (2) rich per-lead/project data so any job's detail is deep; (3) coherent stage distribution with a working pipeline $ total and conversion funnel; (4) storm attribution on leads, traceable to storm events; the 10 curated demo-case scenarios (see §12); and a computed
src/data/selectors.jsanalytics layer wired into the owner/admin dashboards (pipeline value/funnel/win-rate, revenue/gross/net profit, commission by rep, subcontractor performance, storm attribution). - Why: the pipeline must read like a busy real company (investor breadth) and let a salesperson demo any scenario (sales depth); computed analytics guarantee the dashboards never contradict the records.
- Acceptance: dashboard headline numbers equal the sums of the underlying records; pipeline shows a real $ total; every lead has believable details.
Phase 4 — Project lifecycle + inspections + estimates (plan after 3)
- Current:
OwnerProjectDetailalready renders a "PROJECT PROGRESSION" bar (Lead → Inspection Scheduled → Damage Verified → Scope Approved → Project Completed) but it is static and its highlighted stage disagrees with the completion % (e.g. proj_003 shows "Inspection Scheduled" at 42% complete). - Target: make it an interactive state machine with progress % correctly tied to the stage (see §9); the inspection model with two touchpoints and an issues/rework loop (see §11); a claimed-vs-verified scope-variance badge; and an estimate-versions section per project (v1 from the customer's claim → v2 verified → signed; each opens the full rendered estimate document).
- Why: the stage/percent mismatch is a visible defect; interactivity + the inspection loop + scope-variance is the heart of the sales narrative (a professional inspection turns a $200 self-report into an insurance-backed job). Uploads (issue photos/notes) persist for the session; data is seeded.
- Acceptance: advancing a stage updates progress %; logging an inspection issue (with photo + note) shows on the project and loops to rework then re-inspection; estimate versions list with a Signed badge and full document view.
Phase 5 — Subcontractor task state machine (plan)
- Target: replace flat task statuses with the stage machine in §10 (Not Assigned → Assigned → Pre-Work Inspection → Work In Progress → On Hold ↔ → Post-Work Review → Pass→Completed / Fail→Rework). Every transition carries an optional note + timestamp + actor; pre/post inspections record who performed them; the owner table gains Task Category + Assigned Date columns; the task modal gets a richer progress timeline + issues list with photos.
- Why: mirrors the real field workflow and gives the subcontractor an interactive inbox; the review/rework loop is a credible operational story that also feeds owner-side visibility.
Phase 6 — Subcontractor portal data + project parity (plan)
- Target: the
/subcontractor/pages (dashboard, projects, task detail) get realistic data and, where they show a job, the same project details the owner sees — a permitted subset of the unified project (address, scope, schedule, the subcontractor's slice of budget/payments). Enabled by Phase 2's single project entity. - Why: coherence across logins is the demo's credibility test — the same job must look like the same job whether the owner or the subcontractor is viewing it.
Phase 7 — Profiles, skill catalog & AI dispatch context (plan)
- Target: a two-layer profile per person + a self-service User Details page + an owner Employee/Subcontractor Details view + a shared org skill catalog, all feeding LynkDispatch (see §13).
- Why: richer structured context yields more believable AI dispatch recommendations (investor wow), and the profile depth is what a prospect expects from a mature CRM.
Phase 8 — ABAC access control + unified routing (plan)
- Current: the existing framework is effectively RBAC with per-person exceptions —
orgPermissions(role × module → actions) +userPermissionOverrides(per-person grant/deny) +usePermissions().can(module, action)+PermissionGate. The routing layer ignores it and is triplicated: feature components are mounted under/owner/…,/admin/…,/emp/fa/…with only<ProtectedRoute allowedRoles={[...]}>and abasePathhelper; denied users bounce to/; segments are terse (maps,kanban). - Target — ABAC (attribute-based access control) + Option-B unified routes:
- Policy engine over attributes. Access decisions evaluate policies against attributes of: the subject (user:
id,role/orgRole,team,territory/regions,skills,employmentType, seniority), the resource (e.g. project:ownerId,assignedUserIds,subcontractorIds,status,region,sensitivity,value), the action (view/edit/delete/approve/assign/export), and optional context (time). Policies are ordered{ effect: 'permit'|'deny', when: <condition over attributes> }; explicit deny wins; default deny. - The existing data folds in:
roleis one subject attribute; eachuserPermissionOverridebecomes asubject.id-scoped policy; the role×module matrix compiles to baseline permit policies. So nothing is thrown away — ABAC is a superset. - New API:
can(action, resource, ctx?)(resource-aware) replaces the module-onlycan(module, action); a thin module-level form (can('view', { type: module })) backs route guards.PermissionGateand a new route guard both call it. - Expressive rules this enables (examples): sales rep may
edita project only ifsubject.id ∈ resource.assignedUserIds; a subcontractor mayviewonly projects wheresubject.id ∈ resource.subcontractorIds(and only their slice); a canvasser mayeditleads they sourced andviewothers in theirterritory; "all admins except one get feature X" = role-permit + adeny when subject.id == that admin. - Unified routes (Option B): one human-readable route per feature, no role prefix —
/territory-map,/pipeline,/projects/:id,/leads/:id,/dispatch,/storm-intel,/estimates,/leaderboard,/pro-canvas,/org-settings,/subcontractor-tasks. The route guard evaluates the policy for that route's resource-type +view. Role-specific dashboards/landing stay distinct (/owner,/admin,/agent,/contractor,/vendor,/subcontractor). Removes the 3× triplication andbasePath. - UX: nav hides what a subject can't
view; direct-URL hits to denied routes show a styled Access Restricted page; edit/delete gated inside pages viaPermissionGate. No seeded override examples — capability built, configured live in Org Settings (which becomes the policy/attribute editor, ideally keeping the friendly matrix as a front-end that compiles to policies).
- Policy engine over attributes. Access decisions evaluate policies against attributes of: the subject (user:
- Why: ABAC is a materially more impressive and realistic enterprise-access story than role gates — it expresses ownership/assignment/territory rules RBAC can't — while still cleanly covering the simple "deny one admin / one agent read-only" asks. Folded with the route cleanup, it also gives one obvious URL per feature.
- Decisions: ABAC policy engine (attributes + ordered permit/deny policies, deny-wins, default-deny) generalizing the current role/override data;
can(action, resource, ctx)API; Option-B unified routes folded in; hide-nav + Access Restricted page; role dashboards kept; no seeded overrides. - Acceptance: access decisions consider resource attributes (e.g. a rep blocked from editing a project they're not on; a sub sees only their projects); the route guard blocks denied subjects (Access Restricted) and nav hides denied routes; owner sees all; one route per feature; no
basePath/role-prefix duplication; every gated route maps to a resource-type/module. - Note on Plan 2: Plan 2 lands the final unified
/projects/:idroute with a coarse interimallowedRolesguard; Phase 8 swaps that for the ABAC policy guard and consolidates the remaining feature routes (removing the legacy/owner/projects/:id).
9. The project lifecycle state machine
A lifecycleStage field per project drives a state machine; the coarse kanban stage (In-Progress/Complete) derives from it. Each stage maps to a project progress %:
| Stage | Progress | Meaning |
|---|---|---|
| Lead | 5% | Customer's reported claim captured |
| Damage Inspection Scheduled | 10% | Inspection booked |
| Damage Verified | 20% | Real scope captured (findings + photos) |
| Scope Approved | 30% | Customer accepts the verified scope/estimate |
| Contract Signed | 40% | Signed estimate; becomes the deal |
| Work In Progress | 40–70% | Milestone-driven within this band |
| Work Complete — Awaiting Inspection | 80% | Build done, pending final inspection |
| Final Inspection (Scheduled → In Progress) | 85–90% | External examiner reviewing |
| Rework In Progress | regresses | If issues found; loops back to Final Inspection |
| Project Completed | 95% | Passed inspection |
| Final Payment Received — Closed | 100% | Paid in full |
Transitions are interactive (owner/admin advance them, each with an optional note + timestamp + actor). (Stage names are the proposed set; minor renames pending from the user.)
10. The subcontractor task state machine
Not Assigned → Assigned → Pre-Work Inspection → Work In Progress → (On Hold ↔) → Post-Work Review → then either Pass → Completed or Fail → Rework Needed (capture what's wrong + note + photo) → back to Work In Progress → re-review. Every transition carries an optional note, timestamp, and who set it. Pre-Work Inspection and Post-Work Review each record who performed them. Surfaced on the owner Subcontractor Tasks page (with Task Category + Assigned Date columns and a rich detail modal) and in the subcontractor's own login.
11. The estimate & inspection models
Estimate versions (per project): estimateVersions[] = { version, date, total, note, status: sent|revised|signed, lineItems/templateRef }. The list shows date/note/total and a Signed badge; clicking a version opens the full rendered estimate document (reusing the existing estimate/template rendering). v1 reflects the customer's claim; later versions reflect the verified scope.
Inspections (two project-level touchpoints, each records who did it):
- Damage Verification (pre-sale): performed by a field agent or sales rep — inspector recorded per project (name + role) — producing the verified scope, findings, and photos.
- Final Inspection (post-work): an external examiner brought by the customer — no login/role; we store
inspectorName,inspectorContact(phone + email, so they can be re-contacted),inspectionDate,result(pass/fail),feedbackNote, andissues[](each{ description, note, photos[], status: open|fixed }). Re-inspection after rework creates a new round; all rounds remain visible.
Claimed-vs-verified scope variance: the lead stores reportedScope (the customer's claim); Damage Verification produces verifiedScope; when they differ materially, the project carries a scope-variance flag shown as a visible badge (e.g. "Reported ~$200 / 1 day → Verified $15,400 / 5 days"). This feeds the estimate versioning and a decision branch: approved (often via an insurance supplement) or declined (lead lost).
12. The 10 demo-case scenarios
Curated jobs, each threaded end-to-end (lead → project → subcontractor task → invoices → commission) with consistent identities, addresses, and dates, chosen to let a salesperson demonstrate any real-world situation and to show breadth:
- Storm-driven win, on track — canvasser sources it, healthy active job.
- Insurance-backed up-scope → approved — small self-report; inspection finds major damage; revised estimate; insurance supplement; approved. (The hero discrepancy story.)
- Up-scope → customer declines (lost) — sticker shock; lead lost "after inspection."
- Smaller/different than claimed — over-stated; inspection down-scopes to a quick job.
- Multiple revisions / negotiation — several estimate versions before agreement.
- Over-budget / margin loss — scope creep; negative margin (the problem job).
- Large commercial, multi-phase — phased payment schedule, multiple subs.
- Near-complete, healthy — most milestones done, final invoices pending.
- Completed, full lifecycle, all paid — closed-won.
- Disputed / on-hold — dispute timeline, paused payments, low health score.
13. Profiles, skills & AI dispatch context
Profile (per employee & subcontractor), two layers:
- Self section (the person edits on a self-service User Details page; owner can view): preferred name, photo, phone, alt contact, address, emergency contact; optional nickname / pronouns / gender / sex; short bio, years of experience, languages; self-declared specialties + equipment carried (e.g. "tarp, ladder rack, drone"); availability/service areas. Subcontractors also: company name, license #, insurance/COI + expiry, W-9 status.
- Company section (owner/admin only — private to the company): internal notes ("reliable in storms, keeps a tarp on the truck"), status/tags (preferred/probation/do-not-use), skill scores, pay/commission terms, hire date, manager.
- Achievements & levels (employees): read-only, pulled from ProCanvas gamification (level, XP, streak, badges, achievement tiers).
Skill catalog (shared, org-level): the owner defines a skill once (name + required description); each person carries a 0–100 score or N/A per skill. Comparable across people.
AI dispatch context: LynkDispatch scores reps per lead with factors proximity / driveTime / calendarFit / weather / skillMatch, plus reasons[] chips and a natural-language aiInsight. DISPATCH_REPS map to canonical people; each recommendation is authored to cite real profile facts (equipment + skill scores) — e.g. "Carlos — 92 Emergency Tarp · carries a tarp on the truck" — so "the AI read their profile" is demonstrably true on screen. (Today's recommendations are pre-authored mock; the profile data makes them coherent and explainable.)
14. Cross-cutting coherency requirements
These hold across all phases and are the acceptance backbone:
- One person, one identity. Same id → same name/role everywhere; no old ids or placeholder names. (Verified by grep gates.)
- One job, one entity. Lead → project → subcontractor task → invoices → commission all reference the same job and tie out. (Phase 2.)
- Dashboards reconcile. KPI numbers equal the sums of the records they summarize. (Phase 3 selectors; verified by a reviewer re-summing.)
- Same job across logins. Owner, pipeline, and subcontractor views of a job agree. (Phases 2 & 6.)
- Financial invariants. For every project: Σbreakdown.allocated == budget; Σcommitted == committedCost; Σactual == actualCost; committed ≥ actual; spent == actualCost; variancePercent sign correct; paid invoices ≤ actualCost.
- Profiles cited by dispatch. Equipment/skills referenced in recommendation rationale. (Phase 7.)
- No future-dated mock; no empty tabs. Anchored to
DEMO_TODAY= 2026-05-29.
15. Routing map (current → target)
Current (relevant routes): /owner/projects + /owner/projects/:projectId (OwnerProjectDetail, owner-only); /owner|admin|emp-fa/kanban (KanbanPage); /owner|admin|emp-fa/leads/:leadId (LeadProjectPage); /emp/fa/leads, /emp/fa/leads/new. Kanban cards open a side drawer; the drawer's "More Details" is the only kanban→detail navigation, and it always goes to the lead page.
Plan-2 target: introduce the final unified /projects/:id route (Option B) now, gated by a coarse interim allowedRoles (OWNER/ADMIN/FIELD_AGENT). Won leads' "More Details" routes to /projects/PRJ-2026-###; pre-sale leads keep /…/leads/:id (role-prefixed until Phase 8). Project ids become PRJ-2026-### throughout. The legacy /owner/projects/:id stays until Phase 8 removes the duplicate. (No interim role-prefixed project routes — Plan 2 lands the real URL; Phase 8 only swaps the coarse guard for the ABAC policy guard and consolidates the other feature routes.)
Final target (Phase 8 — Option B, unified): one human-readable route per feature, no role prefix, access decided by the permission guard:
| Feature | Final route | Old (triplicated) |
|---|---|---|
| Territory map | /territory-map |
/owner/maps, /admin/maps, /emp/fa/maps |
| Pipeline (kanban) | /pipeline |
/…/kanban |
| Project detail | /projects/:id |
/owner/projects/:id (+ interim admin/fa) |
| Lead detail (pre-sale) | /leads/:id |
/…/leads/:id |
| LynkDispatch | /dispatch |
/…/dispatch |
| Storm Intel | /storm-intel |
/…/storm-intel |
| Estimates | /estimates |
/…/estimates |
| Leaderboard | /leaderboard |
/admin/leaderboard |
| ProCanvas | /pro-canvas |
/…/pro-canvas |
| Org settings | /org-settings |
/owner/settings, /admin/settings |
| Subcontractor tasks | /subcontractor-tasks |
`/owner |
| User details / profile | /profile |
(new, Phase 7) |
Role-specific dashboards/landing stay distinct: /owner, /admin, /agent (replacing /emp/fa), /contractor, /vendor, /subcontractor. The permission-aware guard (can(module,'view') + per-person overrides) replaces allowedRoles-only checks; nav hides what a person can't view; denied direct-URL hits show a styled Access Restricted page; edit/delete stay gated inside pages via PermissionGate. The basePath triplication helper is removed (links become absolute feature paths).
16. Execution model (subagent-driven, parallel-safe)
- Controller + fresh subagents. The controller (orchestrating agent) extracts each task's full text and context, dispatches a fresh implementer subagent per task, then runs spec-compliance and code-quality review; the implementer commits per task. Subagents never read the plan file — the controller hands them exactly what they need.
- Verification gates (no test harness). Each task must end with a green
npm run build, its specific grep/reconciliation check, and a committed clean tree. High-value/judgment tasks (e.g. financial reconciliation) also get an independent read-only reviewer; the whole plan ends with a final reviewer. - Parallel where lanes are disjoint. Every plan ships a lane/ownership map assigning each task an exclusive file set. Tasks whose lanes don't intersect may run in parallel; tasks that share a file (especially
mockStore.jsx) run sequentially. The controller never lets two in-flight agents write the same file, and commits between dependent tasks. (Example: Plan 2's Phase A — id migration then lead→project conversion — is sequential onmockStore.jsx; Phase B — detail-page access, routes, kanban relink, project-list relink — runs as four parallel agents on four different files.) - Model tiers. Cheap/fast model for mechanical edits; standard for judgment/integration; most-capable for architecture and final review.
- Invariants every task preserves: the canonical roster (§4) and the coherency requirements (§14).
17. Risks & how we mitigate them
- Large shared file (
mockStore.jsx). Mitigation: sequential edits with a commit between each; the build gate caught a real duplicate-declaration bug during the original merge and a copy-paste coherence bug during Phase 1. - Id migration breaking FKs (Phase 2).
proj_*is a foreign key in tasks/schedule/subcontractor data. Mitigation: migrate definitions + FKs together; grep gate (proj_0→ none) proves no dangling references. - Lead→project field drift. Mitigation: explicit normalization map + Plan-1 financial-invariant reconciliation on the new projects.
- Analytics drifting from records. Mitigation: compute from the live arrays (selectors), never hardcode.
- Demo data going stale. Mitigation: single
DEMO_TODAYanchor. - Scope sprawl. Mitigation: phased plans, each producing working/verifiable software; this master doc as the stable reference.
18. Open decisions & deferred work
- Stage-name wording — user said "I'll tweak a few"; proposed names in §9/§10 stand until provided.
- Full convergence of
LeadProjectPageinto the unified page — deferred; Phase 4 adds the lead-side extras (stage mover, hail history, progression) onto the project page. - Pre-sale leads getting
PRJids — only on conversion to a project; pre-sale staykl_*. - Per-plan lane maps — authored with each plan to enable safe parallel execution.
- Distinct per-person passwords — declined for demo simplicity (shared
password, auto-filled by the picker).
19. Glossary
- Lead — a potential job in the pipeline (kanban). Pre-sale leads are not yet projects.
- Project / Job — a won job with full execution data; after Phase 2, a won lead is a project.
- Canonical id / roster — the single agreed id + name + empId per person (§4).
DEMO_TODAY— fixed demo date (2026-05-29) anchoring all relative dates.resolvePerson(id)— store selector returning a person's{id,name,email,role}from any collection.- Lane — the exclusive set of files a task may edit; the basis for safe parallel execution.
- Scope variance — the gap between a customer's reported claim and the inspection-verified scope.
- LynkDispatch — the AI rep-recommendation module.
- ProCanvas — the gamified canvassing module (XP/levels/badges).
- ABAC — Attribute-Based Access Control: access decided by policies over subject/resource/action/context attributes (Phase 8), generalizing the prior role-based model.
- Policy — an ordered
{ effect: permit|deny, when }rule; explicit deny wins, default deny.
20. Document index
- This master plan:
docs/superpowers/2026-05-29-MASTER-PLAN.md - Design spec:
docs/superpowers/specs/2026-05-29-demo-data-coherence-design.md - 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–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 authorsskillMatch/reasons/aiInsightto cite real profile facts.
A.8 Access control — ABAC (Phase 8)
ABAC = decisions from policies over attributes. The existing RBAC data folds in as inputs.
- Subject attributes (user):
id,role/orgRole,team,territory/regions,skills,employmentType, seniority. - Resource attributes (e.g. project):
ownerId,assignedUserIds,subcontractorIds,status,region,sensitivity,value,type. - Action:
view/edit/delete/approve/assign/export. Context (optional): time. - Policy: ordered
{ effect:'permit'|'deny', when:<condition over attributes> }; explicit deny wins; default deny. - Engine API:
can(action, resource, ctx?)(resource-aware) + module-level formcan('view', { type: module })for route guards. - Folded-in legacy data:
role→ a subject attribute;orgPermissions[module][role]→ baseline permit policies; eachuserPermissionOverridesentry → asubject.id-scoped permit/deny policy. Org Settings becomes the policy/attribute editor (the friendly role×module matrix may remain as a UI that compiles to policies).
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):
- Log in as Justin Johnson (Owner) → Owner dashboard: pipeline value, revenue, profit KPIs.
- Storm Intel → a recent Plano hail event; ProCanvas/leaderboard → Cody Tatum's door-knocks that day.
- Pipeline → the storm-sourced lead (Scenario 2); open it → reported scope "small leak ~$200".
- Project detail → Damage Verified with inspection photos; scope-variance badge ("$200 → $15.4k"); estimate versions (v1 claim → v2 verified, signed); insurance supplement.
- Lifecycle advances → Work In Progress; Team tab shows Carlos Mendoza's subcontractor task; open Subcontractor Tasks → his stage timeline.
- Invoices/Budget reconcile; commission for Sarah Calloway computed. Close as Scenario 9 (completed, paid).
Investor demo (breadth + sophistication):
- Owner dashboard → computed KPIs; drill a KPI into the exact projects that compose it (coherence proof).
- LynkDispatch → assign an incoming lead; the AI cites real profile facts ("Carlos — 92 Emergency Tarp · carries a tarp").
- Leaderboard → animated podium; ProCanvas → gamification depth.
- Org Settings → roles, granular per-person permissions (configure "deny one admin a feature" live); navigate as that admin → Access Restricted.
- Profiles / skill catalog → the structured context behind the AI.
- Breadth tour: 28 projects across 10 scenario types, storm attribution, subcontractor performance.
Appendix E — Naming conventions
- Person ids: owners
own_00#, adminsa#, sales reps/personnelp#, field agentse#, contractorcon_00#, subcontractorssub_00#. Employee id (login + display):LUP-####(1xxx employees/owners, 2xxx subs, 3xxx contractor). - Job ids: projects
PRJ-2026-###; pre-sale leadskl_###; subcontractor taskssct_###. - 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 insrc/components/**, hooks insrc/hooks/**, pure logic insrc/utils/**, data/selectors insrc/data/**.
Appendix F — Verification & QA detail
No unit-test framework exists, so verification is layered:
- Build gate:
npm run buildmust end✓ builtwith 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). - Grep/invariant checks per task: e.g.
git grepfor stale ids/names returns none; project financial invariants (Appendix A.1) hold; every FK resolves. - Reconciliation review: for data-heavy tasks, an independent read-only reviewer re-sums breakdowns/invoices and confirms dashboard KPIs equal the underlying records.
- Cross-login coherence: the same job viewed as owner vs subcontractor agrees (Phase 6); no "Unknown" person labels.
- 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).
- Final whole-implementation review per plan, then
superpowers:finishing-a-development-branch.
Appendix G — Plan-by-plan roadmap (what · why · how)
Each plan = one phase, produces working/verifiable software on its own, and is executed via the subagent protocol. "How" lists the approach and the main tasks; full step-by-step lives in each plan file.
Plan 1 — Data Foundation ✅ DONE
- What: canonical identity +
resolvePerson; Texan roster +LUP-####empIds; reconciling project financials + 6 missing breakdowns; seeded Leads page;sub_002–005logins; address-matched subcontractor tasks + On-Hold styling; kanban lead values + storm flags; per-role login picker. - Why: nothing else is trustworthy until identities are consistent, money ties out, and no view is empty — this is the bedrock the demo and every later phase stand on.
- How: 11 sequential subagent tasks on
mockStore.jsx(one writer at a time), each with anpm run buildgate + grep/reconciliation checks; an independent financial-reconciliation review; final whole-implementation review. (Delivered in 14 commits, APPROVED.)
Plan 2 — Unify Lead↔Project + Routing (written; next to execute)
- What: migrate
proj_###→PRJ-2026-###(+ inbound FKs); convert the 16 won kanban leads into full project records (normalize fields, back-fill, link both ways); make the rich detail page viewable by owner/admin/field-agent; introduce the unified/projects/:idroute; relink the kanban drawer + project list. - Why: a won lead and a project are the same job — collapsing the two parallel data models into one entity + one page is the prerequisite that makes the rich detail, lifecycle, estimates, analytics, and subcontractor parity apply everywhere for free.
- How: Phase A sequential on
mockStore.jsx(T1 id migration → T2 conversion); Phase B parallel across 4 disjoint files (detail-page access, route, kanban relink, list relink); Phase C verify (reviewer re-checks financial invariants + nav). Field-normalization map + back-fill rules in the plan; coarseallowedRolesguard interim (ABAC arrives in Plan 8).
Plan 3 — Kanban/pipeline depth + analytics
- What: realistic pre-sale lead details (customers,
values, notes, real dates — kill the sequential fake phones); the **10 concrete demo-case scenarios** (Appendix B) authored as coherent threads; a working pipelinetotal + conversion funnel; storm attribution on leads; andsrc/data/selectors.jsderiving dashboard analytics (pipeline value/funnel/win-rate, revenue/gross/net profit, commission by rep, subcontractor performance, storm attribution) wired into the owner/admin dashboards. - Why: the pipeline must read like a busy real company (investor breadth) and let a salesperson demo any scenario (sales depth); computed selectors guarantee dashboards never contradict the records a prospect drills into.
- How: data-authoring subagent tasks on
mockStore.jsx(sequential) for lead/scenario data; then a newselectors.js(its own file → parallelizable) of pure functions over the store arrays; then wire dashboards (OwnerSnapshot, admin dashboard) to the selectors (per-dashboard files → parallel). Verify: a reviewer confirms each dashboard KPI equals the sum of the underlying records; pipeline total equals Σ lead values; clicking a pre-sale lead opens its page. (Note: an earlier-suspectedApp.jsxbackslash-route bug turned out to be a Windows grep display artifact — the committed routes are already correct/…/leads/:leadId; no fix needed.) Scope boundary: inspection/estimate-version/on-hold mechanics in scenarios #2/#5/#10 are Phase 4 — Plan 3 authors only their present-tense data. - Plan file:
docs/superpowers/plans/2026-05-29-plan3-pipeline-depth-analytics.md.
Plan 4 — Project lifecycle + inspections + estimates
- What: make the existing PROJECT PROGRESSION bar an interactive state machine with progress % tied to the stage (§9); the two-touchpoint inspection model with an issues/rework loop + session photo/note uploads (§11, A.5); the claimed-vs-verified scope-variance badge; and the estimate-versions section (v1 claim → v2 verified → signed; full document view).
- Why: the static progression bar already disagrees with completion % (a visible defect); interactivity + the inspection loop + scope-variance is the core sales narrative (a professional inspection turns a $200 self-report into an insurance-backed job).
- How: add the lifecycle/inspection/estimate fields to projects in
mockStore.jsx(sequential) per Appendix A; add store mutators (advance stage, add inspection/issue, upload photo→object URL, add estimate version); build the UI inOwnerProjectDetail.jsx(progression control, inspection panel + issues list, scope-variance badge, estimate-versions tab) — these UI pieces in the one file run sequentially, but are independent of the data tasks. Verify: advancing a stage updates %, logging an issue loops to rework then re-inspection, estimate versions render.
Plan 5 — Subcontractor task state machine
- What: the §10 stage machine (Not Assigned → Assigned → Pre-Work Inspection → Work In Progress → On Hold ↔ → Post-Work Review → Pass→Completed / Fail→Rework), per-transition notes + actor + timestamp, pre/post-inspection attribution; owner table gains Task Category + Assigned Date; richer task modal (stage timeline + issues with photos).
- Why: mirrors the real field workflow and gives the subcontractor an interactive inbox; the review/rework loop is a credible operational story feeding owner-side visibility.
- How: extend
MOCK_SUBCONTRACTOR_TASKS+ status constants + mutators inmockStore.jsx(sequential); update the ownerSubcontractorTasksPage(columns) and the task modal/detail components (parallel — different files). Verify: stages advance with notes in both owner and subcontractor views.
Plan 6 — Subcontractor portal data + project parity
- What: realistic data on
/subcontractor/*; a subcontractor viewing their task sees the same project the owner sees (permitted subset — address, scope, schedule, their slice of budget/payments). - Why: coherence across logins is the demo's credibility test — the same job must look like the same job to owner and subcontractor.
- How: reuse the unified project entity (Plan 2) + the upcoming ABAC visibility (Plan 8) to render a scoped project view in the subcontractor pages; seed/enrich subcontractor-facing data in
mockStore.jsx; build the portal views (component files → parallelizable). Verify: a sub's task links to the real project; the owner and sub views agree.
Plan 7 — Profiles, skill catalog & AI dispatch context
- What: two-layer profiles (self + company) per person; the self-service User Details page (all roles); the owner Employee/Subcontractor Details view; the org skill catalog; ProCanvas achievements surfaced;
DISPATCH_REPSmapped to canonical people; dispatchreasons/aiInsight/skillMatchauthored to cite real profile facts. - Why: richer structured context yields more believable AI dispatch (investor wow), and profile depth is what a prospect expects from a mature CRM.
- How: add
profile/skillCatalog/skillScoresto people + dispatch-rep↔person mapping inmockStore.jsx(sequential); build the User Details page + owner Details view (new component files → parallel); rewrite the pre-authoredDISPATCH_RECOMMENDATIONSto reference profile facts. Verify: a recommendation's rationale cites a real skill/equipment value from the cited rep's profile.
Plan 8 — ABAC access control + unified routing
- What: an attribute-based policy engine (subject/resource/action/context attributes; ordered permit/deny; deny-wins; default-deny) generalizing the role/override data;
can(action, resource, ctx)API; a permission-aware route guard; the Option-B unified route scheme (one human-readable route per feature, role landings only); nav hiding + styled Access Restricted page; Org Settings as policy/attribute editor. - Why: ABAC expresses ownership/assignment/territory rules RBAC can't (a rep edits only their projects; a sub sees only theirs) while still covering the simple "deny one admin / one agent read-only" asks — a credible enterprise-access story; folded with the route cleanup it also yields one obvious URL per feature.
- How: define attributes + a policy evaluator in
src/utils/permissions.js(+ a policies data source); extendusePermissionsto resource-awarecan; add a route-guard component; consolidateApp.jsxto unified routes and stripbasePathacross components (the largest cross-file change — sequence carefully, lane-map per file); add the Access Restricted page; makeLayouthide denied nav; update Org Settings. Verify: resource-aware decisions (rep blocked from a project they're not on; sub sees only their projects); denied direct-URL → Access Restricted; one route per feature; nobasePathleft.