17 KiB
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 taskssct_001..sct_010. Per-task fields includestatus,priority,dueDate,projectId(→PRJ-2026-###;sct_009isnull),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.activitiesabsent onsct_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)(writesstatusHistory+activities+status_changednotification — 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. OpensTaskViewModal(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 fromSUBCONTRACTOR_SELF_STATUSES+ confirm panel with note + photo upload),StatusHistoryPanel,ActivityTimelinePanel,TaskThreadCard, expenses/fees. Inbox:SubContractorDashboardfilterssubcontractorTasksbysubcontractorId→ routes to the detail page. - Notifications
MOCK_NOTIFICATIONS: typestask_assigned/message_received/task_updated/status_changed;addNotificationhelper fired inside mutators; subcontractor consumes viaNotificationsPanel.
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) orRework Needed(fail, with note + issue photos); andCancelledfrom any stage. - Attribution:
Pre-Work InspectionandPost-Work Reviewtransitions record who performed them via the existingactoronsetSubcontractorTaskStatus(actorName/actorRole land instatusHistory+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:
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+assignedDateto all 10 tasks. Give each a trade-appropriatecategory(match the task title/trade, e.g. ridge-cap → 'Roofing', panel → 'Electrical') and anassignedDate(a real ISO date ≤ its earlieststatusHistoryentry;createdAt's date is fine). - Step 3 —
addSubcontractorTaskfactory (~line 8084): initialise new tasks withcategory: input.category || 'General',assignedDate: <today ISO>, andactivities: [](so the 3 tasks/pages that readactivitiesnever hit undefined). - Step 4 — fix
cancelSubcontractorTask(~line 8404) so it routes through the same audit path: append astatusHistoryentry (status 'Cancelled', actor, comment) — reusesetSubcontractorTaskStatus(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.jsxreflects +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, onePre-Work Inspection, twoIn Progress, oneOn Hold, onePost-Work Review, oneRework Needed, twoCompleted, oneCancelled. (Adjust to fit existing data; keepsct_003/sct_007asCompletedsince they'rePaid.) - Step 2: For each task, make
statusHistorya coherent ordered thread reaching its current status — e.g. aPost-Work Reviewtask 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"). ARework Neededtask adds: Post-Work Review → Rework Needed (by ownerJustin Johnson/Diana Reeves, note "flashing detail failed; re-seal valley", with aphotosentry). Dates strictly increasing, ≤ 2026-05-29.actorId/actorName/actorRolereal (the assigned sub for sub-steps; the owner for review steps). - Step 3: Mirror the key transitions into
activities[]for the timeline (the existingsetSubcontractorTaskStatusdoes this at runtime; for seed data, hand-author 1–3activitiesper task soActivityTimelinePanelshows a populated timeline). Inspection steps usetype: 'status_change'with a note + photos; progress notes usetype: 'progress_update'. - Step 4 (verify):
npm run build→✓ built.git grep -ohE "status: '[^']+'" -- src/data/mockStore.jsxover the tasks shows the new stages present (Pre-Work Inspection, Post-Work Review, Rework Needed each ≥ 1). EverystatusHistory/activitiesactor 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
Taskor beforeStatus): Task Category (renderstask.categoryas a small chip) and Assigned Date (renderstask.assignedDateformatted like the existingCreatedcolumn). Add the matching<td>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 theStatusHistoryPanelstyling fromSubcontractorTaskDetailPage.jsx(read it for the pattern). Render anyphotoson an entry as thumbnails. - Step 2: Add an issues/review-photos view: surface the
Rework Needed/Post-Work Reviewentries' 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 callssetSubcontractorTaskStatus(task.id, 'Completed'|'Rework Needed', note, { id: user.id, name: user.name, role: user.role }, photos). PullsetSubcontractorTaskStatus+userfromuseMockStore()/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; aPost-Work Reviewtask 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
StatusManagementCardbuilds its buttons fromSUBCONTRACTOR_SELF_STATUSES— which now includesPre-Work InspectionandPost-Work Review. Confirm the picker renders all five self-stages and that selecting one opens the existing confirm panel (note + photo upload) and callssetSubcontractorTaskStatus. 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). EnsureCompleted/Rework Needed/Cancelleddo NOT appear as sub-settable buttons (they're inSUBCONTRACTOR_REVIEW_STATUSES). - Step 3: When
task.status === 'Rework Needed', show the owner's rework note/photos prominently (from the latestRework NeededstatusHistory/activitiesentry) and let the sub move back toIn Progressto redo, thenPost-Work Reviewagain. - Step 4 (verify):
npm run build→✓ built. Dev-run as a subcontractor (e.g. loginmaya): 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 buildclean, 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; everystatusHistory/activitiesactor is a real person; dates increasing and ≤ 2026-05-29. cancelSubcontractorTasknow writes astatusHistoryentry (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+assignedDateon all 10 tasks and in theaddSubcontractorTaskfactory;activities: []initialised.cancelSubcontractorTaskaudited 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".