diff --git a/docs/adr/0023-installation-in-progress-gated-on-design-document.md b/docs/adr/0023-installation-in-progress-gated-on-design-document.md new file mode 100644 index 00000000..6d76712c --- /dev/null +++ b/docs/adr/0023-installation-in-progress-gated-on-design-document.md @@ -0,0 +1,96 @@ +# 23. Installation in Progress is gated on the Retrofit Design Document, not the HubSpot designStatus flag + +Date: 2026-07-24 + +## Status + +Accepted + +## Context + +On the Live projects screen (`your-projects/live`), a Property's delivery stage +is derived from HubSpot deal fields by the stage classifier +(`resolveDisplayStage` → `classifyDeals` → `computeLiveTrackerData` in +`transforms.ts`). After coordination is complete, the classifier decided +*Design in Progress* vs *Installation in Progress* from the self-reported +`designStatus` text field: `designStatus === "UPLOADED"` meant "design is done, +installation can happen". + +That flag is set in the CRM independently of the actual design work. Observed +values include `"UPLOADED"`, `"uploaded"` and `"IN PROGRESS"`, and it is +routinely set before — or without — the **Retrofit Design Document** +(`retrofit_design_doc`) ever being uploaded. The CRM flag and the document store +are two independent sources of truth that had silently diverged, so large +numbers of Properties with no design in place showed as *Installation in +Progress*. Managers could not trust the pipeline counts, the funnel, or the +stage badges, and Properties genuinely waiting on design were hidden inside a +later stage. + +The domain owner confirmed there is only ever one design document per Property — +the Retrofit Design Document — and its presence is the correct trigger for +*Installation in Progress*. + +## Decision + +**Design-document presence, not the `designStatus` flag, drives the +Design-vs-Installation boundary.** + +- The classifier stops reading `designStatus`. `designStatus` remains on the + deal record and may still be displayed (and still feeds the separate design + throughput trends chart), but it no longer influences the stage. +- Whether a `retrofit_design_doc` exists for a deal is passed **into** the pure + classifier as an explicit input — `resolveDisplayStage(deal, hasDesignDoc)`, + `classifyDeals(deals, designDocDealIds)`, + `computeLiveTrackerData(deals, designDocDealIds)`. The classification + functions perform no I/O; presence is derived by + `deriveDesignDocDealIds(docsByDealId)` from the documents already fetched + (direct deal-id match and UPRN fallback), using `DESIGN_DOC_TYPES` as the + definition of a design document. `page.tsx` and `[dealId]/page.tsx` now fetch + documents ahead of classification so the same fetch feeds both the presence + set and the document-status map. + +- **After coordination, downstream evidence wins; document presence only + decides the Design/Installation boundary.** For a coordination-complete deal + (a `(V1|V2|V3) IOE/MTP COMPLETE` marker, not an RA issue) the stage resolves + in this priority: + + ``` + full lodgement date present → Project Complete + measures lodgement date present → At Post Survey + lodgement status present → At Lodgement + measures installed OR installer handover→ Installation Complete + Retrofit Design Document present → Installation in Progress + otherwise → Design in Progress + ``` + + Gating the whole post-coordination block on document presence would drag a + genuinely installed or lodged Property back to *Design in Progress* when its + design document is merely missing from the store. Document presence is the + weakest signal in the ladder, not a gate on the rest. + +- The document-status map's install bucket is corrected: a `retrofit_design_doc` + is no longer mis-bucketed as an install document, so a Property holding only a + design document is not reported as having install documents. + +Coordination and earlier stages are untouched — the RA-issue → Queries path, +the removed-Property short-circuits, the dealstage-id → raw-stage mapping and +the Coordination in Progress path all behave exactly as before. + +## Consequences + +- A Property shows *Installation in Progress* only once its Retrofit Design + Document exists; one whose coordination is complete but whose document is + missing stays at *Design in Progress*, where the outstanding design work is + visible. Funnel and stage-progress counts (and the "All Projects" roll-up) + follow the corrected per-deal classification, as does the Properties-tab stage + filter. +- A prematurely-set or stale `designStatus` flag can no longer push a Property + into *Installation in Progress*. +- `DESIGN_DOC_TYPES` (previously defined but unused) becomes load-bearing as the + single definition of what counts as a design document. +- Out of scope, deliberately: correcting the underlying `designStatus` data or + alerting on CRM-vs-document mismatch; surfacing a design-completeness badge on + the Documents tab; multi-document or per-measure design requirements (there is + only ever one design document per Property). The Live projects feature itself + remains superseded-in-principle by Ara Projects per ADR-0018; this fix keeps + it correct in the meantime.