diff --git a/docs/superpowers/2026-05-29-MASTER-PLAN.md b/docs/superpowers/2026-05-29-MASTER-PLAN.md index c70cb92..1d7b38e 100644 --- a/docs/superpowers/2026-05-29-MASTER-PLAN.md +++ b/docs/superpowers/2026-05-29-MASTER-PLAN.md @@ -515,7 +515,8 @@ Each plan = one phase, produces working/verifiable software on its own, and is e ### 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 in `mockStore.jsx` (sequential); update the owner `SubcontractorTasksPage` (columns) and the task modal/detail components (parallel — different files). Verify: stages advance with notes in both owner and subcontractor views. +- **How:** extend `MOCK_SUBCONTRACTOR_TASKS` + status constants + mutators in `mockStore.jsx` (sequential); update the owner `SubcontractorTasksPage` (columns) and the task modal/detail components (parallel — different files). Verify: stages advance with notes in both owner and subcontractor views. (Note: much of the machine already exists — `setSubcontractorTaskStatus` writes history+activities+notifications for any status, and the subcontractor detail page already has a status picker/history/timeline; Plan 5 mainly adds the §10 stages — Pre-Work Inspection / Post-Work Review / Rework Needed — the owner review gate, the Category/Assigned-Date columns, and the owner modal stage timeline.) +- **Plan file:** `docs/superpowers/plans/2026-05-29-plan5-subcontractor-state-machine.md`. ### 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). diff --git a/docs/superpowers/plans/2026-05-29-plan5-subcontractor-state-machine.md b/docs/superpowers/plans/2026-05-29-plan5-subcontractor-state-machine.md new file mode 100644 index 0000000..a8befc2 --- /dev/null +++ b/docs/superpowers/plans/2026-05-29-plan5-subcontractor-state-machine.md @@ -0,0 +1,149 @@ +# Plan 5 — Subcontractor Task State Machine + +> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:subagent-driven-development. Steps use checkbox (`- [ ]`). Follow the execution protocol (`2026-05-29-execution-protocol.md`) — sequential on shared files, parallel only where lanes are disjoint. + +**Goal:** Upgrade subcontractor tasks from a flat 5-status list to the full §10 **state machine** — `Assigned → Pre-Work Inspection → In Progress → (On Hold ↔) → Post-Work Review → Pass→Completed / Fail→Rework Needed → In Progress → re-review` — with per-transition note + actor + timestamp (already present) plus **pre/post-inspection attribution**, a **review→rework loop**, owner-table **Category + Assigned Date** columns, and a **richer owner task modal** (stage timeline + photos). + +**Architecture:** Most of the infrastructure already exists. `setSubcontractorTaskStatus(taskId, status, comment, actor, photos)` already writes `statusHistory` + `activities` + fires a notification for *any* status string, and the subcontractor's `SubcontractorTaskDetailPage` already renders a status picker, history, activity timeline, thread, and photo upload. So Plan 5 = (1) expand the status constants + add the missing `category`/`assignedDate` fields + re-stage the 10 demo tasks across the richer machine in `mockStore.jsx`; (2) add the pass/fail review gate (owner-only Completed/Rework Needed) and an inspection-capture activity; (3) surface it in the UI — owner table columns, owner modal stage timeline, subcontractor picker expanded to the new self-stages. + +**Tech stack:** React 18, Vite 7, mock React-context store, pnpm. No test harness → verify via `npm run build` + grep + dev-run of stage transitions in both owner and subcontractor views. + +**Spec/master:** `docs/superpowers/2026-05-29-MASTER-PLAN.md` §10 (sub-task machine), Appendix A.4 (sub-task schema), Appendix G Plan 5. + +--- + +## Current state (verified) + +- **`MOCK_SUBCONTRACTOR_TASKS`** (`src/data/mockStore.jsx`, ~line 6934): **10 tasks** `sct_001..sct_010`. Per-task fields include `status`, `priority`, `dueDate`, `projectId` (→ `PRJ-2026-###`; `sct_009` is `null`), `subcontractorId`, `assignedBy`, `photos[]`, `statusHistory[{id,status,actorId,actorName,comment,at}]`, `thread[]`, `activities[{id,type,status,note,photos[],actorId,actorName,actorRole,at}]`, `expenses[]`, `fees[]`, `paymentStatus`, `createdAt`, `updatedAt`. **Missing: `category`, `assignedDate`.** `activities` absent on `sct_004/006/010`. +- **Constants** (~line 6830): `SUBCONTRACTOR_TASK_STATUSES = ['Assigned','In Progress','On Hold','Completed','Cancelled']`; `SUBCONTRACTOR_SELF_STATUSES = ['Assigned','In Progress','On Hold','Completed']`. +- **Mutators** (on context value): `addSubcontractorTask` (creates at 'Assigned', seeds history, notifies), `setSubcontractorTaskStatus(taskId, nextStatus, comment, actor, photos)` (writes `statusHistory` + `activities` + `status_changed` notification — works for ANY status string), `addSubcontractorTaskActivity` (progress update + photos), `addSubcontractorTaskMessage`, `addSubcontractorTaskExpense/Fee`, `cancelSubcontractorTask` (**bug: does NOT write a statusHistory entry**), `reassignSubcontractorTask`. +- **Owner page** `src/pages/owner/SubcontractorTasksPage.jsx`: table columns = Task · Subcontractor · Location · Due · Priority · Status · Created · Actions. **No Category / Assigned Date columns.** Opens `TaskViewModal` (portal modal) on view. +- **Owner modal** `src/components/subcontractor/TaskViewModal.jsx` (~133 lines): **read-only** — renders title/badges/meta/description/photos only. **No status history, no stage timeline, no advance controls.** +- **Subcontractor side** `src/pages/subcontractor/SubcontractorTaskDetailPage.jsx`: already rich — `StatusManagementCard` (buttons from `SUBCONTRACTOR_SELF_STATUSES` + confirm panel with note + photo upload), `StatusHistoryPanel`, `ActivityTimelinePanel`, `TaskThreadCard`, expenses/fees. Inbox: `SubContractorDashboard` filters `subcontractorTasks` by `subcontractorId` → routes to the detail page. +- **Notifications** `MOCK_NOTIFICATIONS`: types `task_assigned`/`message_received`/`task_updated`/`status_changed`; `addNotification` helper fired inside mutators; subcontractor consumes via `NotificationsPanel`. + +## The §10 state machine (target) + +Ordered statuses (keeping the existing `In Progress` label rather than renaming to avoid breaking status-color maps — §10's "Work In Progress" ≡ `In Progress`): + +``` +Assigned → Pre-Work Inspection → In Progress → (On Hold ↔ In Progress) → Post-Work Review + → [Pass] Completed + → [Fail] Rework Needed → In Progress → Post-Work Review (re-review) +Cancelled (owner/admin only, from any non-terminal stage) +``` + +- **Subcontractor self-transitions** (`SUBCONTRACTOR_SELF_STATUSES`): `Assigned`, `Pre-Work Inspection`, `In Progress`, `On Hold`, `Post-Work Review`. The sub advances their own work *up to submitting for review*. +- **Owner/admin-only transitions** (the review gate): from `Post-Work Review` → `Completed` (pass) or `Rework Needed` (fail, with note + issue photos); and `Cancelled` from any stage. +- **Attribution:** `Pre-Work Inspection` and `Post-Work Review` transitions record who performed them via the existing `actor` on `setSubcontractorTaskStatus` (actorName/actorRole land in `statusHistory` + `activities`). The pass/fail note + photos ride the same call. + +--- + +## Lane / ownership map + +| Task | Lane (exclusive files) | Phase | +|------|------------------------|-------| +| T1 Constants + schema fields + cancel fix | `src/data/mockStore.jsx` | A (seq) | +| T2 Re-stage 10 demo tasks across the machine | `src/data/mockStore.jsx` | A (seq, after T1) | +| T3 Owner table: Category + Assigned Date columns | `src/pages/owner/SubcontractorTasksPage.jsx` | B (parallel) | +| T4 Owner modal: stage timeline + photos | `src/components/subcontractor/TaskViewModal.jsx` | B (parallel) | +| T5 Subcontractor picker: new stages + review gate | `src/pages/subcontractor/SubcontractorTaskDetailPage.jsx` | B (parallel) | +| T6 Verify | (read-only) | C | + +**Phase A (T1, T2) sequential** — both edit `mockStore.jsx`. **Phase B (T3, T4, T5) parallel** — three disjoint files, all depend only on Phase A. **T6** last. + +--- + +## Task 1: Expand status constants, add `category`/`assignedDate`, fix cancel + +**Files:** `src/data/mockStore.jsx` only. + +- [ ] **Step 1 — status constants** (~line 6836). Expand: + +```js +export const SUBCONTRACTOR_TASK_STATUSES = [ + 'Assigned', 'Pre-Work Inspection', 'In Progress', 'On Hold', + 'Post-Work Review', 'Rework Needed', 'Completed', 'Cancelled', +]; +// Statuses a subcontractor can set themselves (no review gate, no cancel): +export const SUBCONTRACTOR_SELF_STATUSES = [ + 'Assigned', 'Pre-Work Inspection', 'In Progress', 'On Hold', 'Post-Work Review', +]; +// Owner/admin-only transitions (the review gate + cancel): +export const SUBCONTRACTOR_REVIEW_STATUSES = ['Completed', 'Rework Needed', 'Cancelled']; +// Task categories (for the owner table + assignment): +export const SUBCONTRACTOR_TASK_CATEGORIES = [ + 'Roofing', 'Electrical', 'Plumbing', 'Siding', 'Gutters', 'Windows', 'Painting', 'Inspection', 'General', +]; +``` + +- [ ] **Step 2 — add `category` + `assignedDate` to all 10 tasks.** Give each a trade-appropriate `category` (match the task title/trade, e.g. ridge-cap → 'Roofing', panel → 'Electrical') and an `assignedDate` (a real ISO date ≤ its earliest `statusHistory` entry; `createdAt`'s date is fine). +- [ ] **Step 3 — `addSubcontractorTask` factory** (~line 8084): initialise new tasks with `category: input.category || 'General'`, `assignedDate: `, and `activities: []` (so the 3 tasks/pages that read `activities` never hit undefined). +- [ ] **Step 4 — fix `cancelSubcontractorTask`** (~line 8404) so it routes through the same audit path: append a `statusHistory` entry (status 'Cancelled', actor, comment) — reuse `setSubcontractorTaskStatus(taskId, 'Cancelled', comment, actor)` rather than setting the field directly, so every transition is logged. +- [ ] **Step 5 (verify):** `npm run build` → `✓ built`. `git grep -c "category:" -- src/data/mockStore.jsx` reflects +10 (the tasks); `git grep -c "assignedDate:" ...` ≥ 10. Build clean, 0 duplicate-key warnings. +- [ ] **Step 6:** commit `git commit -am "feat(data): expand subcontractor task state machine constants + add category/assignedDate; audit cancel"`. + +## Task 2: Re-stage the 10 demo tasks across the machine + +**Files:** `src/data/mockStore.jsx` only. **After T1.** + +Distribute the 10 tasks across the richer machine so the demo can show every stage, each with a coherent `statusHistory` (note + actor + timestamp) telling the task's story. + +- [ ] **Step 1:** Assign target statuses for variety, e.g.: one `Assigned`, one `Pre-Work Inspection`, two `In Progress`, one `On Hold`, one `Post-Work Review`, one `Rework Needed`, two `Completed`, one `Cancelled`. (Adjust to fit existing data; keep `sct_003`/`sct_007` as `Completed` since they're `Paid`.) +- [ ] **Step 2:** For each task, make `statusHistory` a coherent ordered thread reaching its current status — e.g. a `Post-Work Review` task has entries: Assigned → Pre-Work Inspection (by the sub, with a note "site walk complete, materials staged") → In Progress → Post-Work Review (by the sub, "submitted for review"). A `Rework Needed` task adds: Post-Work Review → Rework Needed (by owner `Justin Johnson`/`Diana Reeves`, note "flashing detail failed; re-seal valley", with a `photos` entry). Dates strictly increasing, ≤ 2026-05-29. `actorId`/`actorName`/`actorRole` real (the assigned sub for sub-steps; the owner for review steps). +- [ ] **Step 3:** Mirror the key transitions into `activities[]` for the timeline (the existing `setSubcontractorTaskStatus` does this at runtime; for seed data, hand-author 1–3 `activities` per task so `ActivityTimelinePanel` shows a populated timeline). Inspection steps use `type: 'status_change'` with a note + photos; progress notes use `type: 'progress_update'`. +- [ ] **Step 4 (verify):** `npm run build` → `✓ built`. `git grep -ohE "status: '[^']+'" -- src/data/mockStore.jsx` over the tasks shows the new stages present (Pre-Work Inspection, Post-Work Review, Rework Needed each ≥ 1). Every `statusHistory`/`activities` actor is a real person; dates increasing. +- [ ] **Step 5:** commit `git commit -am "feat(data): re-stage subcontractor demo tasks across the full state machine with coherent history"`. + +## Task 3: Owner table — Category + Assigned Date columns + +**Files:** `src/pages/owner/SubcontractorTasksPage.jsx` only. **Phase B (parallel).** + +- [ ] **Step 1:** Add two columns to the desktop table header (after `Task` or before `Status`): **Task Category** (renders `task.category` as a small chip) and **Assigned Date** (renders `task.assignedDate` formatted like the existing `Created` column). Add the matching `` cells in the row body. Mirror into the mobile card view if present. +- [ ] **Step 2:** If the page has filters, optionally add a category filter using `SUBCONTRACTOR_TASK_CATEGORIES` (reuse the existing filter pattern; optional — don't overbuild). +- [ ] **Step 3 (verify):** `npm run build` → `✓ built`. Dev-run: the owner table shows Category + Assigned Date for every task. +- [ ] **Step 4:** commit `git commit -am "feat(subtasks): add Task Category + Assigned Date columns to the owner task table"`. + +## Task 4: Owner modal — stage timeline + photos (+ review gate) + +**Files:** `src/components/subcontractor/TaskViewModal.jsx` only. **Phase B (parallel).** + +The owner modal is currently read-only. Make it show the full stage story and (optionally) let owner/admin run the review gate. + +- [ ] **Step 1:** After the meta grid, add a **Stage Timeline** section rendering `task.statusHistory` (reverse-chronological): each entry shows the status (as a badge), `actorName` + `actorRole`, `comment`, and the timestamp — mirror the `StatusHistoryPanel` styling from `SubcontractorTaskDetailPage.jsx` (read it for the pattern). Render any `photos` on an entry as thumbnails. +- [ ] **Step 2:** Add an **issues/review-photos** view: surface the `Rework Needed` / `Post-Work Review` entries' notes + photos prominently (these are the "what's wrong" the owner logged). +- [ ] **Step 3 (review gate — owner/admin only):** when `task.status === 'Post-Work Review'`, show **Approve (→ Completed)** and **Send to Rework (→ Rework Needed)** buttons gated to `['OWNER','ADMIN'].includes(user?.role)`. Each opens a small note (+ optional photo) capture and calls `setSubcontractorTaskStatus(task.id, 'Completed'|'Rework Needed', note, { id: user.id, name: user.name, role: user.role }, photos)`. Pull `setSubcontractorTaskStatus` + `user` from `useMockStore()`/`useAuth()`. (If wiring photo upload is heavy, a note-only capture is acceptable — match the Phase-4 inspection-issue pattern.) +- [ ] **Step 4 (verify):** `npm run build` → `✓ built`. Dev-run: opening a task shows its stage timeline; a `Post-Work Review` task shows Approve/Rework; approving moves it to Completed and appends to history. +- [ ] **Step 5:** commit `git commit -am "feat(subtasks): owner task modal — stage timeline, review photos, and Post-Work review gate"`. + +## Task 5: Subcontractor picker — new stages + review gate + +**Files:** `src/pages/subcontractor/SubcontractorTaskDetailPage.jsx` only. **Phase B (parallel).** + +- [ ] **Step 1:** The `StatusManagementCard` builds its buttons from `SUBCONTRACTOR_SELF_STATUSES` — which now includes `Pre-Work Inspection` and `Post-Work Review`. Confirm the picker renders all five self-stages and that selecting one opens the existing confirm panel (note + photo upload) and calls `setSubcontractorTaskStatus`. Adjust any hardcoded 4-button layout to handle 5 stages. +- [ ] **Step 2:** Label the inspection stages clearly for the sub: `Pre-Work Inspection` = "Start Pre-Work Inspection" (capture a site-walk note + photos), `Post-Work Review` = "Submit for Review" (the sub hands off; they can NOT set Completed — that's the owner's gate). Ensure `Completed`/`Rework Needed`/`Cancelled` do NOT appear as sub-settable buttons (they're in `SUBCONTRACTOR_REVIEW_STATUSES`). +- [ ] **Step 3:** When `task.status === 'Rework Needed'`, show the owner's rework note/photos prominently (from the latest `Rework Needed` `statusHistory`/`activities` entry) and let the sub move back to `In Progress` to redo, then `Post-Work Review` again. +- [ ] **Step 4 (verify):** `npm run build` → `✓ built`. Dev-run as a subcontractor (e.g. login `maya`): advancing Assigned → Pre-Work Inspection → In Progress → Post-Work Review works with notes/photos; the sub cannot mark Completed; a Rework Needed task shows the owner's feedback and can be re-submitted. +- [ ] **Step 5:** commit `git commit -am "feat(subtasks): subcontractor picker supports pre-work/post-work stages; review gate stays owner-side"`. + +## Task 6: Verify (read-only) + +- [ ] `npm run build` clean, 0 duplicate-key warnings. +- [ ] All 10 tasks have `category` + `assignedDate`; the new stages (Pre-Work Inspection, Post-Work Review, Rework Needed) each appear on ≥ 1 task; every `statusHistory`/`activities` actor is a real person; dates increasing and ≤ 2026-05-29. +- [ ] `cancelSubcontractorTask` now writes a `statusHistory` entry (cancel a task in dev → history shows it). +- [ ] **Same task across logins:** open a task as owner (table → modal) and as the assigned subcontractor (`/subcontractor/tasks/:id`) — status, history, and photos agree. +- [ ] The review→rework loop works end-to-end: sub submits Post-Work Review → owner sends to Rework Needed (with note/photo) → sub sees the feedback, redoes, re-submits → owner approves → Completed. Each transition logged with actor + note + timestamp; notifications fire. +- [ ] Owner table shows Category + Assigned Date columns. + +--- + +## Self-review checklist +- [ ] Status set is the full §10 machine; self vs review-gate split enforced (sub can't set Completed/Cancelled). +- [ ] `category` + `assignedDate` on all 10 tasks and in the `addSubcontractorTask` factory; `activities: []` initialised. +- [ ] `cancelSubcontractorTask` audited via statusHistory. +- [ ] 10 tasks re-staged across the machine with coherent histories + real actors. +- [ ] Owner table columns added; owner modal shows stage timeline + photos + review gate; sub picker supports the new stages with the review gate owner-side. +- [ ] Build clean; same task agrees across owner + subcontractor logins. + +## Notes for Plan 6 +Plan 6 (subcontractor portal data + project parity) builds on this: a sub viewing their task should see the **same project** the owner sees (scoped subset). The state machine + the `projectId` link on each task are the join; Plan 6 renders the permitted project view in the subcontractor pages and seeds richer portal data. ABAC visibility (Plan 8) later formalises the "permitted subset".