Skip to Content

← All archived runs

Run: milestone-delivery-tracking

run.md

Run: milestone-delivery-tracking

  • issue: #477 # canonical home of the spec + state (labels, comments)
  • branch: claude/gifted-fermat-dj44ih
  • pr: #478

00_intake/stub.md

Stub: Customer dashboard — milestone delivery tracking

  • feature-slug: milestone-delivery-tracking
  • epic: customer-dashboard-data
  • personas: Customer (view); Expert (updates delivery status)
  • initiative: Build the Bridge / objective: Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow
  • depends-on: customer-project-foundation
  • sequence: 2 of 9

Problem

The Progress section (milestones list, progress %, outcome summary) is dummy. The real milestone model (packages/services/src/db/models/milestone.ts) has only name, percentage, dueDate, order and invoice — it has no delivery status, no owner, no customer-facing outcome label, and no blocks-go-live flag. So progress %, the outcomes list, and the milestone cards cannot be real, and value-delivered (finance) and the health engine have no completion signal to read.

Proposed change

  • Extend the milestone model with: delivery status (upcoming | in-progress | done), owner (Expert | Your team | Customer), a customer-facing outcome label, and blocksGoLive.
  • Compute project progress % (percentage-weighted by completed milestones) and the outcomeSummary (outcomes of the in-flight/next milestones) in the read service, extending the foundation envelope.
  • Wire the ProgressSection (milestone cards with status/owner/blocksGoLive, the progress bar, the outcomes list) to real data.
  • Give the Expert a minimal way to advance a milestone's status (the delivery-side trigger from the service-journey delivery flow).

Acceptance criteria (rough)

  • Milestones render with real status, owner, outcome and blocks-go-live, ordered by order.
  • Progress % reflects percentage-weighted completed milestones, not a hard-coded number.
  • Marking a milestone done updates the customer's progress + outcomes without a mock.
  • status / completion is exposed for customer-finance-summary and project-health-engine.

Out of scope (this feature)

  • The finance value-delivered aggregation (customer-finance-summary consumes this completion signal).
  • The computed health/go-live layer (project-health-engine).

Notes for Define

  • The percentages already must sum to 100 (validateMilestonePercentages) — reuse that for progress.
  • Reconcile the existing isPaid/isApproved invoice virtuals with the new delivery status — delivery-done is distinct from invoice-paid.
  • touches: packages/services/src/db/models/milestone.ts (new fields), packages/services/src/server (progress/outcome computation), apps/web/components/dashboard/customer/progress-section.tsx.

01_define/output/spec.md

Spec: Customer dashboard — milestone delivery tracking

  • slug: milestone-delivery-tracking
  • issue: #477
  • personas: Customer (view), Expert (advances delivery status)
  • touches: packages/services/src/db/models/milestone.ts, packages/services/src/db/services/milestone, packages/services/src/db/services (customer project read envelope), apps/web/app/api (expert status-advance route), apps/web/components/dashboard/customer/progress-section.tsx
  • complexity: standard

Problem

The customer dashboard's Progress section ("What is happening" — milestone cards, progress %, and the outcomes summary) is rendered entirely from apps/web/lib/mock/customer.ts. The real milestone model (packages/services/src/db/models/milestone.ts) carries only name, percentage, dueDate, order, and invoice — it has no delivery status, no owner, no customer-facing outcome label, and no blocks-go-live flag. So progress %, the outcomes list, and the milestone cards cannot be real, and the downstream finance (value-delivered) and health-engine features have no completion signal to read. This advances Build the Bridge / Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow: real milestone delivery state is the completion signal the delivery → invoicing → health flow depends on.

Proposed change

  • Extend the milestone model with delivery-tracking fields:
    • status: upcoming | in-progress | done (default upcoming).
    • owner: Expert | Your team | Customer (default Expert).
    • outcome: customer-facing outcome label (optional; the read envelope falls back to name when unset).
    • blocksGoLive: boolean (default false).
    • Delivery status is distinct from invoice paid/approved — the existing invoice isPaid/ isApproved virtuals are not reused or conflated; delivery-done and invoice-paid are independent.
  • Compute, in the read service that feeds the customer project envelope (extending the customer-project-foundation envelope — see dependency below):
    • progress % = sum of percentage across milestones whose status is done, as a whole number. Reuses the existing invariant that a proposal's milestone percentages sum to 100 (validateMilestonePercentages), so a fully-delivered project reads 100%.
    • outcomeSummary = the customer-facing outcomes of the in-flight / next (not-yet-done) milestones.
    • A per-milestone view ordered by order, exposing name, outcome, status, owner, blocksGoLive.
  • Wire the customer ProgressSection (milestone cards, the progress bar, the outcomes list) to the real envelope instead of mock/customer.ts. The component is already typed for this shape; this run replaces its data source.
  • Give the Expert an API-only way to advance a milestone's status — a tenant-scoped service mutation plus an authenticated route handler in apps/web/app/api. This is the delivery-side trigger from the service-journey delivery flow; advancing a milestone to done moves the customer's progress % and outcomes on the next read. No expert-dashboard UI is built this run (see Out of scope).
  • Expose the completion signal (per-milestone status and the computed project progress) on the read envelope so the downstream customer-finance-summary and project-health-engine features can consume it without re-querying milestones.

Dependency / sequencing

This is sequence 2 of the customer-dashboard-data epic and depends on customer-project-foundation (sequence 1), which stands up the customer project read service + envelope and wires the dashboard page off mock. Per the Define decision, this spec extends that foundation envelope rather than duplicating it — so Build is gated on customer-project-foundation being merged first. This is a build-ordering note, not an unresolved question about what to build.

Acceptance criteria

  • The milestone model carries status (upcoming|in-progress|done), owner (Expert|Your team|Customer), outcome (string), and blocksGoLive (boolean), with sensible defaults so existing milestone documents remain valid.
  • The customer project read envelope returns milestones ordered by order, each with real name, outcome, status, owner, and blocksGoLive.
  • Project progress % on the envelope equals the percentage-weighted sum of done milestones (a whole number, 100 when all are done) — not a hard-coded value.
  • The envelope returns an outcomeSummary built from the outcomes of the not-yet-done milestones.
  • The customer ProgressSection renders milestone cards, the progress bar, and the outcomes list from the envelope with no import from lib/mock/customer.ts for this data.
  • An authenticated Expert can advance a milestone's status via the API route; the mutation is tenant-scoped, and the change is reflected in the customer envelope (progress + outcomes) on next read.
  • Per-milestone status and the computed project progress are exposed on the envelope for customer-finance-summary and project-health-engine to consume.

Out of scope

  • Expert-dashboard UI for advancing milestone status — this run ships the mutation + API route only; wiring it into an expert component (e.g. move-to-completion-table) is a later run.
  • The finance value-delivered aggregation (customer-finance-summary consumes this completion signal).
  • The computed health / go-live layer, including daysToGoLive (project-health-engine).
  • Any change to invoice paid/approved semantics — delivery status is added alongside, not merged into them.

Open questions

  • none. (Build is sequenced after customer-project-foundation merges — a build-ordering note, recorded under Dependency above, not an open requirement.)

02_build/output/notes.md

Build notes: milestone-delivery-tracking

  • branch: claude/gifted-fermat-dj44ih (merged origin/main to pick up the shipped customer-project-foundation envelope this feature extends)
  • commits:
    • feat: milestone-delivery-tracking — delivery fields, envelope progress/outcomes, expert status API

What changed

  • packages/services/src/db/models/milestone.ts: added delivery fields to IMilestone and the schema — status (upcoming | in-progress | done, default upcoming), owner (Expert | Your team | Customer, default Expert), outcome (optional, customer-facing label), blocksGoLive (default false). Exported MilestoneStatus / MilestoneOwner types and MILESTONE_STATUSES / MILESTONE_OWNERS enums. Delivery status is added alongside the invoice paid/isApproved virtuals, not merged into them.
  • packages/services/src/db/services/milestone/index.ts:
    • MilestoneDeliveryRow type + findDeliveryByProposal(tenantId, proposalId) — ordered delivery rows, coalescing defaults so milestones written before these fields still render (upcoming / Expert / not blocking).
    • advanceStatus(tenantId, expertId, milestoneId, status) — the expert-side delivery trigger; tenant- and expert-scoped updateOne, returns whether a milestone matched.
  • packages/services/src/db/services/customer-project/index.ts: extended the foundation envelope. CustomerProjectEnvelope gains milestones: CustomerProjectMilestone[] and outcomeSummary: string[]; project.progress is now the percentage-weighted sum of done milestones (replacing the foundation's 0 placeholder). Milestones resolve via the lead's accepted proposal (proposalService.findAcceptedByLeadmilestoneService.findDeliveryByProposal); outcome falls back to name, and outcomeSummary is the outcomes of the not-yet-done milestones.
  • packages/services/src/db/services/index.ts: export CustomerProjectMilestone.
  • apps/web/app/api/milestones/[milestoneId]/status/route.ts: new PATCH route — the API-only expert trigger. Clerk auth → role expert → tenant → app user, validates the status, then milestoneService.advanceStatus. 401/403/400/404 as appropriate.
  • apps/web/app/(app)/customer/dashboard/page.tsx: ProgressSection now reads progress, milestones, and outcomeSummary from the envelope (no mock). daysToGoLive stays on mock — it belongs to project-health-engine (out of scope).
  • apps/web/lib/mock/customer.ts: removed activeProject, milestones, and outcomeSummary — now envelope-sourced. (goLive.daysToGoLive stays for the out-of-scope health layer.)

The ProgressSection component needed no change — the foundation already typed it for this shape; this run only swaps its data source.

Acceptance criteria status

  • Model carries status / owner / outcome / blocksGoLive with defaults; existing documents stay valid (no migration — schema defaults + coalescing read).
  • Envelope returns milestones ordered by order with real name / outcome / status / owner / blocksGoLive.
  • project.progress = percentage-weighted sum of done milestones (whole number, 100 when all done) — not hard-coded.
  • Envelope returns outcomeSummary from the not-yet-done milestones' outcomes.
  • ProgressSection renders cards, progress bar, and outcomes from the envelope — no lib/mock/customer.ts for this data.
  • Authenticated expert can advance a milestone's status via PATCH /api/milestones/[milestoneId]/status; tenant- and expert-scoped; reflected in the customer envelope on next read.
  • Per-milestone status and computed project.progress are on the envelope for customer-finance-summary and project-health-engine to consume.

Verify result

  • Mechanical checks (format · lint · typecheck · build) run in CI + the Vercel preview, not here. No check is expected to fail.

Notes for review

  • Milestone → project linkage goes lead → accepted proposal → milestones; absent an accepted proposal (or any milestones) the envelope stays valid with 0 progress and empty outcomes.
  • The expert trigger is API-only by spec — no expert-dashboard UI this run.

03_ship/output/changelog.md

Changelog: milestone-delivery-tracking

Persona: Customer

Track your delivery progress on your dashboard

Your customer dashboard now shows your real delivery progress instead of sample content:

  • The progress section lists your actual milestones — each with its status (upcoming, underway, or done), who owns it, and the outcome it delivers — with anything blocking go-live clearly flagged.
  • Your overall progress figure is now worked out from the milestones genuinely completed, so it reflects real work delivered.
  • As your expert marks work done, your progress and outcomes update to match.

This continues making your customer dashboard reflect your real project.

No end-user note for the Expert persona — the expert-side delivery trigger ships as a backend API only this run, with no dashboard surface yet.

03_ship/output/investor-update.md

Customers now see real delivery progress on their dashboard

We've connected the customer dashboard's progress view to live delivery data — the second of nine steps turning the customer's home screen from a static demo into a real view of their engagement. This advances Build the Bridge and our Q2 objective to Validate Technical Infrastructure & Payout Flow: it proves real delivery status flows from the expert's work all the way through to what the customer sees.

  • A customer now sees their actual milestones — each with its delivery status, owner, and outcome — and an overall progress figure calculated from the work genuinely completed, not a sample number.
  • Experts can mark delivery progress as it happens, and the customer's view updates to match — the first live link between work delivered and what the customer sees.
  • It builds directly on the project foundation shipped last and feeds the finance and project-health views still to come.

03_ship/output/pr.md

Ship: milestone-delivery-tracking

  • PR: #478 — https://github.com/sustentus/sustentus/pull/478
  • branch: claude/gifted-fermat-dj44ih
  • CI: format pass · lint pass · typecheck pass · preview build pass (build commit; re-running on ship commit)
  • technical docs: no technical docs impact — technical/** documents structure, not individual db model fields or routes (it lists "Milestones" only as a demo-seed entity); the new fields + PATCH /api/milestones/[id]/status route change nothing documented there.
  • business docs: updated apps/docs/app/business/service-journey/delivery/page.mdx — added the delivery status / owner / outcome / blocksGoLive fields to the Milestone Fields table, a "Delivery status" section covering the expert PATCH trigger and percentage-weighted progress, and clarified delivery status is distinct from the invoice isPaid / isApproved virtuals.
  • release notes: both — investor draft (03_ship/output/investor-update.md) + changelog entry published to apps/help/app/changelog/page.mdx in this PR (Customer persona; no end-user Expert note — the expert trigger is API-only this run).

Review summary

  • No blocking findings. /code-review (high effort) over the diff — model, service, envelope, route, page, mock, docs — surfaced no correctness defects.
  • Verified: the expert mutation is correctly authorized — advanceStatus scopes the updateOne to { tenantId, _id, expert: appUser._id }, so an expert can only advance milestones they own (milestone expert shares the user _id); progress is the percentage-weighted sum of done milestones; findDeliveryByProposal coalesces defaults so pre-existing milestones render; the lead → accepted proposal → milestones path degrades to empty progress/outcomes when there's no accepted proposal.
  • Accepted (non-blocking, pre-existing pattern) — a malformed [milestoneId] path param makes Mongoose throw a CastError → 500 rather than 404. This matches the existing api/brd/chat route (which casts leadId the same way); not worth diverging from the house pattern in this PR.

Acceptance check (vs spec)

  • Milestone model carries status / owner / outcome / blocksGoLive with defaults; existing docs stay valid (schema defaults + coalescing read, no migration).
  • Envelope returns milestones ordered by order with real name / outcome / status / owner / blocksGoLive.
  • project.progress = percentage-weighted sum of done milestones (whole number) — not hard-coded.
  • Envelope returns outcomeSummary from the not-yet-done milestones' outcomes.
  • ProgressSection renders from the envelope — no lib/mock/customer.ts for this data.
  • Authenticated expert advances status via PATCH /api/milestones/[milestoneId]/status; tenant- and expert-scoped; reflected in the customer envelope on next read.
  • Per-milestone status and computed project.progress exposed on the envelope for customer-finance-summary and project-health-engine.

Merge & deploy

  • merged: no — awaiting explicit human merge approval (gate:merge-approved).
  • deploy: pending merge.