Files
LynkedUpPro_CRM/docs/superpowers/plans/2026-05-29-plan5-subcontractor-state-machine.md
2026-05-29 19:26:14 +05:30

17 KiB
Raw Permalink Blame History

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 machineAssigned → 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 ReviewCompleted (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:
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: <today ISO>, 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 13 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 <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 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".