Skip to Content

← All archived runs

Run: expert-active-projects

run.md

Run: expert-active-projects

  • issue: #470 # canonical home of the spec + state (labels, comments)
  • branch: claude/funny-goodall-g6wo5p
  • pr: #472

00_intake/stub.md

Stub: Work in progress + move to completion

  • feature-slug: expert-active-projects
  • epic: expert-dashboard-data
  • personas: Expert
  • initiative: Build the Bridge / objective: Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow
  • depends-on: expert-workbench-foundation, sla-stage-targets
  • sequence: 5 of 8

Problem

Two workbench tables are dummy and share one source: Work in progress (work-in-progress-table.tsx) and Move to completion (move-to-completion-table.tsx). Both list the expert's active-project leads with customer, contact, work, stage, sla/timeRemaining, CSM, on-track/at-risk status and value. They differ only by which stage bucket a lead is in (mid-delivery vs near-completion) and Move-to-completion's extra nextStep. The data — active-project leads + milestone staging — exists, but nothing buckets or stages it for the workbench.

Proposed change

  • Build one read query for the expert's active-project leads (lead.expert = me, isActiveProject = true), joined to customer/contact, CSM, value, stage + SLA timing (from sla-stage-targets) and milestone progress.
  • Bucket leads into Work-in-progress vs Move-to-completion by stage (e.g. near-final stages — CSAT / Sign-off / Review — go to Move-to-completion).
  • For Move-to-completion, derive nextStep (from the next milestone or next positive status transition).
  • Wire both tables, honouring on-track / at-risk status from the SLA timing engine.

Acceptance criteria (rough)

  • Work-in-progress lists the expert's mid-delivery active projects with real stage, SLA/time remaining, CSM, status and value.
  • Move-to-completion lists near-final active projects with a derived next step.
  • A lead appears in exactly one bucket; status bands derive from the SLA timing engine.
  • Empty states render cleanly for either table.

Out of scope (this feature)

  • Extending the milestone model — that is customer-dashboard-data/milestone-delivery-tracking; this feature consumes its delivery status/completion signal.
  • Earnings aggregation (feature 7) and the work queue (feature 4).

Notes for Define

  • Depends on milestone delivery status from customer-dashboard-data/milestone-delivery-tracking to know which milestone is in-flight. If that hasn't landed, either sequence after it or fall back to the lead status workflow for staging; note the choice.
  • Define the bucket boundary (which stages count as "move to completion") alongside the SLA stage taxonomy from feature 2 so they agree.
  • touches: packages/services/src/server (active-projects read + bucketing), apps/web/components/dashboard/expert/{work-in-progress-table,move-to-completion-table}.tsx. </content> </invoke>

01_define/output/spec.md

Spec: Expert active projects — work-in-progress + move-to-completion tables

  • slug: expert-active-projects
  • issue: #470
  • personas: Expert
  • touches: packages/services/src/db/services (active-projects read + bucketing), packages/services/src/server, apps/web/app/(app)/expert/dashboard/page.tsx, apps/web/components/dashboard/expert/work-in-progress-table.tsx, apps/web/components/dashboard/expert/move-to-completion-table.tsx, apps/web/lib/mock/expert.ts
  • complexity: standard

Problem

On the expert Go live workbench (/expert/dashboard), two of the six sections — Work in progress and Move to completion — are still driven by dummy data in apps/web/lib/mock/expert.ts. Both list the expert's active-project leads (customer, contact, work, stage, SLA/time remaining, CSM, on-track/at-risk status, value); they differ only by which stage bucket a lead sits in (mid-delivery vs near-completion) and Move-to-completion's extra derived nextStep. The underlying data — active-project leads (lead.isActiveProject, lead.expert), their workflow stage and milestone progress — exists, but nothing reads it for this expert, stages it, or buckets it for the workbench. This is feature 5 of 8 in the expert-dashboard-data epic, advancing Build the Bridge / Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow: the workbench can only validate the expert delivery + payout flow once each section reflects real per-tenant, per-expert data instead of the mock.

Proposed change

  • Build one read query for the signed-in expert's active-project leads (lead.expert = me, lead.isActiveProject = true, current tenant), joined to customer + contact, CSM (lead.manager), value, the lead's delivery stage (status.displayName), per-stage SLA timing (time remaining / overdue / at-risk) from sla-stage-targets, and milestone delivery status from milestone-delivery-tracking.
  • Bucket each active-project lead into exactly one of the two tables by its delivery stage: near-final stages (the close-out stages — e.g. Review / Sign-off / CSAT / Closure, named to match the sla-stage-targets stage taxonomy) route to Move to completion; all other mid-delivery active-project leads stay in Work in progress.
  • For Move to completion, derive nextStep from the in-flight milestone's delivery status (the next incomplete milestone) — falling back to the next positive status transition when no milestone is in flight.
  • Derive each lead's on-track / at-risk status band from the SLA timing engine (overdue / time-stuck ⇒ at-risk), not from the ad-hoc sla.breach notification.
  • Wire WorkInProgressTable and MoveToCompletionTable to the real read service via the expert-workbench-foundation read envelope, replacing the workInProgress / moveToCompletion mock arrays. Both tables render a clean empty state when the expert has no leads in that bucket.

Dependencies (Build is gated behind these — declare, do not duplicate)

This run is defined ahead of its upstreams; Build must not start until all three have merged. The spec assumes the epic sequence — it does not re-implement the foundation, SLA, or milestone models.

  • expert-workbench-foundation (epic feature 1) — provides the server-fetched /expert/dashboard page scoped to the signed-in expert and the shared read-service envelope this read plugs into.
  • sla-stage-targets (epic feature 2) — provides the per-stage SLA-target model + the timing derivation (time-in-stage, overdue, at-risk) and the canonical stage taxonomy that names the near-final close-out stages used for bucketing.
  • customer-dashboard-data/milestone-delivery-tracking (cross-epic) — adds delivery status/outcome to the milestone model. This feature hard-depends on that completion signal for staging and for the Move-to-completion nextStep; it consumes the model, never extends it.

Acceptance criteria

  • Work in progress lists the signed-in expert's mid-delivery active-project leads (real customer, contact, work, stage, SLA/time remaining, CSM, status, value) — scoped to the current tenant and lead.expert = me, lead.isActiveProject = true.
  • Move to completion lists the expert's near-final active-project leads, each with a derived nextStep sourced from the in-flight milestone's delivery status.
  • A lead appears in exactly one of the two tables; the bucket boundary is the near-final close-out stages named by the sla-stage-targets taxonomy.
  • Each row's on-track / at-risk band derives from the SLA timing engine (overdue / time-stuck ⇒ at-risk).
  • Both tables render a clean empty state when the expert has no leads in that bucket.
  • Neither table reads from apps/web/lib/mock/expert.ts for its data — the mock arrays are no longer consumed by the wired page.

Out of scope

  • Extending the milestone model — that is customer-dashboard-data/milestone-delivery-tracking; this feature consumes its delivery status/completion signal.
  • Defining the SLA-target model or its timing derivation — that is sla-stage-targets; this feature reads it.
  • Standing up the server-fetched page / read envelope — that is expert-workbench-foundation.
  • The Work queue table + Action required summary (expert-work-queue, feature 4).
  • Earnings aggregation, including the "at risk" figure (expert-earnings-summary, feature 7).
  • The Respond / Open / Complete row actions are left as-is (UI affordances, not wired here).
  • Realtime updates — the section is server-fetched on load like the rest of the workbench.

Open questions

  • none — the milestone hard-dependency, the near-final bucket boundary, and the Build-gated-on-deps sequencing were resolved at Define.

02_build/output/notes.md

Build notes: expert-active-projects

  • branch: claude/funny-goodall-g6wo5p
  • commits:
    • feat: expert-active-projects — derive work-in-progress + move-to-completion from active-project leads

What changed

  • packages/services/src/db/services/expert-workbench/active-projects.ts (new): deriveActiveProjects(tenantId, expertUserId) — reads the expert's active-project leads (expert = me, isActiveProject = true, tenant-scoped), joins customer/contact, CSM (lead.manager), value (accepted-proposal price), delivery stage + SLA timing, and milestone delivery status, then buckets each lead into exactly one of the two tables.
  • packages/services/src/db/services/expert-workbench/index.ts: getWorkbench now fills the previously-empty workInProgress / moveToCompletion slots from deriveActiveProjects (run alongside specialisation + quality in the same Promise.all).

Design decisions (within the approved spec)

  • Bucket boundary = {Billing, CSAT}. The sla-stage-targets stage-map collapses review/sign-off into Delivery, so the finest close-out boundary the taxonomy exposes is its final two stages: Billing (invoicing) and CSAT (post-delivery sign-off/feedback) → Move to completion; Delivery and earlier → Work in progress. A lead with an unmapped/null SLA stage stays in Work in progress (the safe "still active" bucket).
  • Status band comes straight from the timing engine: red/at-riskat-risk, else on-track.
  • Timing strings. Work-in-progress uses both columns — timeRemaining = live time ("3d overdue" / "2d left" / "Due today"), sla = target ("5d target"). Move-to-completion has only a deadline column, so its sla carries the live time so the overdue/at-risk signal still surfaces.
  • Next step (Move to completion) = the in-flight milestone's outcome/name (first in-progress, else first non-done by order). Fallback when no milestone is in flight: the lead's next positive status transition ("Advance to <status>"), then "Confirm completion and close".
  • Value = accepted proposal price via formatCurrency (default EUR, the platform default); "—" when there is no accepted proposal.
  • Queries are bounded: one lead query, one batched SLA-timing call (2 queries for N leads), one accepted-proposal lookup per lead, and milestones only for the close-out leads.

Acceptance criteria status

  • Work in progress lists the expert's mid-delivery active projects (stage, SLA/time remaining, CSM, status, value), tenant- + expert-scoped on isActiveProject.
  • Move to completion lists near-final active projects with a derived next step sourced from the in-flight milestone's delivery status.
  • A lead appears in exactly one bucket; boundary is the near-final close-out stages from the SLA taxonomy; status bands derive from the SLA timing engine.
  • Each row's on-track / at-risk band derives from the SLA timing engine.
  • Empty states render cleanly — both tables render their chrome with a 0 count and no rows when the expert has no leads in that bucket (component behaviour unchanged from the foundation round).
  • Neither table reads from apps/web/lib/mock/expert.ts — the page consumes workbench.workInProgress / workbench.moveToCompletion from the read service.

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

  • The close-out boundary {Billing, CSAT} is the one judgement call — it follows from the SLA stage-map collapsing review/sign-off into Delivery. If a finer "in sign-off vs invoicing" split is wanted later, it belongs in the SLA taxonomy, not here.
  • Move-to-completion's sla column intentionally shows live time (not the target) because that table has no separate time column.

03_ship/output/changelog.md

Changelog: expert-active-projects

Persona: Expert

See your real active projects on the workbench

Your go live workbench now shows your actual active projects instead of sample rows:

  • Work in progress lists the projects you're mid-delivery on, with the real stage, the time left against its target, the customer success manager, and the value at stake.
  • Move to completion gathers the projects nearing sign-off, each with the next step to close it out — drawn from your live milestones.
  • Each project sits in just one of the two tables, and its on-track or at-risk flag comes from real delivery timing, so an empty table means there is genuinely nothing waiting there.

This continues making your go live workbench reflect your real delivery work.

03_ship/output/investor-update.md

Experts now see their live delivery pipeline on the workbench

Sustentus experts now open the go-live workbench to their real active projects — what's mid-delivery and what's ready to close out — instead of placeholder rows. Each project shows its true delivery stage, the time remaining against its target, the value at stake, and, for work nearing sign-off, the next step drawn from live milestones. This advances Build the Bridge and our Q2 2026 Objective 3: Validate Technical Infrastructure & Payout Flow: experts can only be trusted to drive the lead-to-payout cycle when the workbench reflects their real delivery state.

  • Active projects split automatically into work in progress and close-out — each lead in exactly one place, by its real delivery stage.
  • On-track and at-risk flags and time remaining come from the shared SLA timing engine, not estimates.
  • The close-out next step is sourced from each project's live milestone delivery status, tying the expert's view to the same delivery data the customer sees.

03_ship/output/pr.md

Ship: expert-active-projects

  • PR: #472 — https://github.com/sustentus/sustentus/pull/472
  • branch: claude/funny-goodall-g6wo5p
  • CI: format pass · lint pass · typecheck pass · issue-link pass · preview build pass (all green on the ship commit)
  • technical docs: no technical docs impact — the change fills two existing workbench read-service slots with a derivation; no new app, package, route, env var, or architecture. technical/** documents structure, not individual service methods.
  • business docs: no business docs impact — no new product capability, journey step, or model field. The documented "Work in Progress" delivery stage and isActiveProject are unchanged; this surfaces real data in two existing dashboard tables. (Contrast the milestone-delivery sibling, which updated service-journey/delivery because it added new model fields + a route.)
  • release notes: both — investor draft (03_ship/output/investor-update.md) + Expert changelog entry published to apps/help/app/changelog/page.mdx in this PR.

Review summary

  • No blocking findings. /code-review (high effort) over the diff (the new active-projects.ts + the 8-line wire) found no correctness bugs: every active-project lead lands in exactly one bucket; a lead with null timing (no SLA definition / no status history) falls safely to Work in progress with no false "overdue"; all awaits are present; the expert: expertUserId string is coerced to ObjectId by Mongoose (same pattern as the sibling customer-project query); no import cycle (the ./index.js import is type-only).
  • Accepted (non-blocking) — fullName / milestone outcome ?? name duplicate small helpers in customer-project and the same module's index.ts; not worth a shared-util refactor for ~3 lines. The per-lead findAcceptedByLead / findDeliveryByProposal lookups are N-per-expert but run in parallel and are fine for an expert's active-project cardinality.
  • Verified green: typecheck CI passing confirms the .lean<…>() generics and IProposalPopulated.price / _id access type-check.

Acceptance check (vs spec)

  • Work in progress lists the expert's mid-delivery active-project leads (stage, SLA/time remaining, CSM, status, value) — tenant- + expert-scoped on isActiveProject.
  • Move to completion lists near-final active projects with a derived next step sourced from the in-flight milestone's delivery status.
  • A lead appears in exactly one bucket; boundary is the near-final close-out stages ({Billing, CSAT}) from the sla-stage-targets taxonomy; status bands derive from the SLA timing engine.
  • Each row's on-track / at-risk band derives from the SLA timing engine.
  • Both tables render a clean empty state when the expert has no leads in that bucket.
  • Neither table reads from apps/web/lib/mock/expert.ts — the page consumes workbench.workInProgress / workbench.moveToCompletion.

Merge & deploy

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