expert-active-projectsrun.md00_intake/stub.mdTwo 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.
lead.expert = me,
isActiveProject = true), joined to customer/contact, CSM, value, stage + SLA timing (from
sla-stage-targets) and milestone progress.nextStep (from the next milestone or next positive status
transition).customer-dashboard-data/milestone-delivery-tracking; this
feature consumes its delivery status/completion signal.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.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.mdOn 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.
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.sla-stage-targets stage taxonomy) route to Move to completion; all other mid-delivery
active-project leads stay in Work in progress.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.sla.breach notification.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.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/dashboard
page scoped to the signed-in expert and the shared read-service envelope this read plugs into.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.lead.expert = me, lead.isActiveProject = true.nextStep sourced from the in-flight milestone's delivery status.sla-stage-targets taxonomy.apps/web/lib/mock/expert.ts for its data — the mock arrays are no
longer consumed by the wired page.customer-dashboard-data/milestone-delivery-tracking; this
feature consumes its delivery status/completion signal.sla-stage-targets; this feature
reads it.expert-workbench-foundation.expert-work-queue, feature 4).expert-earnings-summary, feature 7).02_build/output/notes.mdfeat: expert-active-projects — derive work-in-progress + move-to-completion from active-project leadspackages/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).{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).red/at-risk → at-risk, else on-track.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.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".price via formatCurrency (default EUR, the platform default); "—" when there is no accepted proposal.isActiveProject.0 count and no rows when the expert has no leads in that bucket (component behaviour unchanged from the foundation round).apps/web/lib/mock/expert.ts — the page consumes workbench.workInProgress / workbench.moveToCompletion from the read service.{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.sla column intentionally shows live time (not the target) because that table has no separate time column.03_ship/output/changelog.mdPersona: Expert
Your go live workbench now shows your actual active projects instead of sample rows:
This continues making your go live workbench reflect your real delivery work.
03_ship/output/investor-update.mdSustentus 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.
03_ship/output/pr.mdtechnical/** documents structure, not individual service methods.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.)03_ship/output/investor-update.md) + Expert changelog entry published to apps/help/app/changelog/page.mdx in this PR./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).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..lean<…>() generics and IProposalPopulated.price / _id access type-check.isActiveProject.sla-stage-targets taxonomy; status bands derive from the SLA timing engine.apps/web/lib/mock/expert.ts — the page consumes workbench.workInProgress / workbench.moveToCompletion.gate:merge-approved).