Skip to Content

← All archived runs

Run: sdm-expert-strength

run.md

Run: sdm-expert-strength

  • branch: claude/dreamy-johnson-qw3ab1
  • pr: #522

00_intake/stub.md

Stub: SDM dashboard — expert strength table

  • feature-slug: sdm-expert-strength
  • epic: sdm-dashboard-data
  • personas: SDM
  • initiative: Build the Bridge / objective: Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow
  • depends-on: sdm-supply-coverage
  • sequence: 6 of 8

Problem

The expert strength table (expertStrength in apps/web/lib/mock/sdm.ts, rendered by expert-strength-table.tsx

  • expert-strength-row.tsx) is the per-expert supply view: for each expert it shows type (Freelance/Agency), region, country, taxonomy productSkills, skillLevel, activeWork, slaAdherence, csat, escalationRate, completionRate, repeatUsage, a status (strong/ok/at-risk), and the per-expert demand/supply/gap + availability. Almost all of these metrics are owned by the expert epic (quality metrics, SLA, active work) and the demand/supply/gap is derived from the coverage grid — but nothing assembles them into the SDM's per-expert, area-scoped row today.

Proposed change

  • Assemble a per-expert strength row over the experts in the SDM's area, reusing the expert epic's models: slaAdherence from sla-stage-targets; csat/escalationRate/completionRate/ repeatUsage from expert-quality-metrics (+ the existing csat model); activeWork from expert-active-projects.
  • Add the SDM-specific fields: the expert's taxonomy productSkills, the status classification (reuse the strong/ok/at-risk logic from sdm-supply-coverage so the two agree), the per-expert demand/supply/gap (read from the coverage grid for the cells the expert serves) and availability ("Available now" / "Available soon" from capacity).
  • Wire the expert strength table.

Acceptance criteria (rough)

  • Each row's CSAT / SLA / escalation / completion / repeat / active-work come from the reused expert models, not recomputed.
  • Each expert shows taxonomy product skills, a status, demand/supply/gap from the coverage grid, and availability, scoped to the SDM's area.
  • The expert strength table renders from real data — no lib/mock/sdm.

Out of scope (this feature)

  • Defining/owning the quality + SLA + active-work models — consume the expert epic's models.
  • Any expert write/coaching action — read-only strength view this round.

Notes for Define

  • Keep the status (strong/ok/at-risk) classification identical to the one in sdm-supply-coverage so the grid and the per-expert view never disagree — share one derivation.
  • Confirm expert-quality-metrics, sla-stage-targets and expert-active-projects have landed; define a minimal read interface and coordinate if not — do not fork.
  • touches: packages/services/src/server (per-expert strength assembly), apps/web/components/dashboard/sdm/{expert-strength-table.tsx,expert-strength-row.tsx}.

01_define/output/spec.md

Spec: SDM dashboard — expert strength table

  • slug: sdm-expert-strength
  • personas: SDM
  • touches: packages/services/src/db/services/matching/expert-quality.ts (extend the shared quality model with escalationRate + completionRate), packages/services/src/db/services/sdm-strength/ (new sibling service), packages/services/src/db/services/sdm-coverage/index.ts (extract the shared per-expert classification), packages/services/src/db/services/index.ts, apps/web/app/(app)/sdm/dashboard/page.tsx, apps/web/components/dashboard/sdm/expert-strength-table.tsx, apps/web/components/dashboard/sdm/expert-strength-row.tsx, apps/web/lib/mock/sdm.ts
  • complexity: standard

Problem

The expert strength table on the web SDM "Go live readiness" dashboard (expertStrength in apps/web/lib/mock/sdm.ts, rendered by apps/web/components/dashboard/sdm/expert-strength-table.tsx + expert-strength-row.tsx) is the per-expert supply view: for each expert in the SDM's area it shows type (Freelance/Agency), region, country, taxonomy productSkills, skillLevel, activeWork, slaAdherence, csat, escalationRate, completionRate, repeatUsage, a status (strong/ok/at-risk), and the per-expert demand/supply/gap + availability. Today it is wholly mock: the table reads from lib/mock/sdm and even derives status with a placeholder client-side ranking (top 20% / bottom 20% by SLA), which is unrelated to the real coverage classification. Almost all of these metrics are owned by the expert epic (quality metrics, SLA, active work) and the demand/supply/gap is derived from the coverage grid — but nothing assembles them into the SDM's per-expert, area-scoped row.

This is sequence 6 of 8 in the sdm-dashboard-data epic and advances Build the Bridge / Q2 2026 Objective 3 — Validate Technical Infrastructure & Payout Flow: the SDM cockpit can only validate that the marketplace's supply data is trustworthy once each section, including the per-expert strength view, reflects real per-tenant, per-area data instead of the mock.

Proposed change

  • Extend the shared expert quality model with two real, reused metrics. getExpertQualityMetrics (packages/services/src/db/services/matching/expert-quality.ts) today returns csat, vendorRating, repeatWorkRate, reworkRate. Add two additive fields so escalation and completion are first-class, reusable quality figures (not invented in the SDM feature):

    • escalationRate — the share of the expert's completed leads that had at least one escalated blocker (a blocker record on the lead with escalated: true), as a percentage; null when the expert has no completed leads. Sourced from the existing blocker model, over the same completed-lead set the other rates already use — exactly mirroring how reworkRate reads the per-lead isReworked flag.
    • completionRate — of the leads assigned to the expert (lead.expert == id, excluding unassigned bid-pool matches), the share that are completed (the existing completed-lead definition: isActiveProject: false, bidPool: false, isBrdApproved: true), as a percentage; null when the expert has no assigned leads.

    Both are additive — existing consumers of ExpertQualityMetrics (e.g. the expert-workbench Quality card) keep working unchanged.

  • Assemble a per-expert strength row over the experts in the SDM's area — reading the in-scope experts[] from the sdmAreaService area envelope (sdm-area-foundation) so the row honours the "my area" vs "all" scope and tenant isolation, never re-querying experts itself.

  • Reuse the expert epic's models — do not recompute in the SDM feature:

    • slaAdherence from sla-stage-targets (the per-stage SLA timing engine), derived per expert over their active leads the same way sdm-supply-coverage already derives it (shared, not reforked).
    • csat, escalationRate, completionRate, repeatUsage (the model's repeatWorkRate) from the extended getExpertQualityMetrics above (which itself reuses the csat model).
    • activeWork (load) from the expert-active-projects / matching context (activeProjectCount).
  • Add the SDM-specific fields:

    • the expert's taxonomy productSkills (from the area envelope), region and country (from location), and skillLevel (from the expert's seniority);
    • the expert's typeFreelance vs Agency derived from the expert's vendor link: an expert attached to a vendor (the expert.vendor ref is set) is an Agency expert; a standalone expert (no vendor link) is Freelance. This is an explicit derived field of this feature (not a plain reused read), blessed in-scope here because IExpert carries no type field of its own;
    • the status classification (strong/ok/at-risk) reused from the sdm-supply-coverage derivation — the per-expert view and the coverage grid must share one derivation and never disagree. sdm-supply-coverage's per-expert classification (bid-ready / delivery-ready / strong) is extracted into a shared helper and consumed here; this feature only maps it to the row's label: strongstrong, delivery-ready (but not strong) → ok, otherwise → at-risk. The placeholder SLA-ranking computedStatus in the table is removed;
    • the per-expert demand/supply/gap, aggregated from the sdm-supply-coverage grid over the cells the expert is matched into (the cells whose bid-ready set includes the expert), so the per-expert numbers reconcile with the grid: demand = Σ of those cells' demand, supply = Σ of those cells' availableExperts, gap = Σ of those cells' shortfall;
    • availability ("Available now" / "Available soon") derived from the expert's capacity — "Available now" when isAvailable is true and the expert has no future availability.earliestStartDate, "Available soon" otherwise.
  • Expose one typed per-expert strength assembly from @sustentus/services/server — a new sdmStrengthService sibling in the db/services/ SDM family (alongside sdm-area, sdm-demand, sdm-coverage, sdm-risk), exported through db/services/index.ts after sdm-coverage (it consumes that service) — that takes the area scope and returns the strength rows.

  • Wire the expert strength table (expert-strength-table.tsx / expert-strength-row.tsx) to the real assembly via the SDM dashboard page (apps/web/app/(app)/sdm/dashboard/page.tsx), surfacing the SDM-specific fields (product skills, type, status, demand/supply/gap, availability) alongside the reused metrics. Remove the expertStrength mock from lib/mock/sdm.ts and the placeholder ranking; render a clean empty state when the SDM's area has no experts.

Dependencies (now all merged — Build is unblocked)

This run was defined ahead of its upstreams; they have since merged to main, so Build may proceed. The spec still assumes the epic sequence — it does not re-implement the area envelope, the coverage derivation, or the reused expert models (it only extends the quality model additively, above).

  • sdm-area-foundation (epic feature 2, merged) — provides the server-fetched /sdm page scoped to the signed-in SDM and the sdmAreaService area envelope (in-scope experts[] / leads[]) this assembly plugs into.
  • sdm-supply-coverage (epic feature 4, merged, PR #525) — owns the strong/ok/at-risk status classification and the coverage grid (demand/supply/gap per cell). This feature consumes that status derivation and reads the grid for each expert's cells; it must share the one derivation, not fork it — the per-expert classification is extracted into a shared helper rather than copied. (sdm-supply-coverage's own upstreams sdm-demand-aggregation (#521) and sdm-supply-taxonomy (#524) are also merged.)
  • expert-quality-metrics (#471), sla-stage-targets (#463), expert-active-projects (#472) (expert epic, merged) — supply the quality figures, SLA adherence, and active-work load. This feature reads their per-expert outputs and additively extends the quality model with escalation + completion rates; it never recomputes the existing figures.

Acceptance criteria

  • getExpertQualityMetrics returns two new additive fields: escalationRate (% of the expert's completed leads with at least one escalated blocker, null when none completed) and completionRate (% of leads assigned to the expert that are completed, null when none assigned), both sourced from real data (the blocker model and the lead-assignment/completed definitions); existing consumers keep compiling and behaving unchanged.
  • Each row's csat / slaAdherence / escalationRate / completionRate / repeatUsage / activeWork come from the reused expert models (the extended expert-quality-metrics for the quality figures, sla-stage-targets for SLA adherence, expert-active-projects / matching context for active work), not recomputed in the SDM feature.
  • Each expert shows its taxonomy product skills, type (Freelance/Agency derived from the expert.vendor link), region/country, skill level (from seniority), a status, the demand/supply/gap aggregated from the sdm-supply-coverage grid, and availability — scoped to the SDM's area and honouring the "my area" vs "all" scope.
  • The status classification reuses the same per-expert derivation as sdm-supply-coverage (its bid-ready / delivery-ready / strong classification is extracted into a shared helper, not reimplemented), mapped to strong / ok / at-risk; the placeholder top/bottom-20%-by-SLA ranking is removed from the table.
  • The expert strength table renders from the real per-expert assembly via the SDM dashboard page — it no longer reads expertStrength from apps/web/lib/mock/sdm.ts, and that mock array is removed.
  • The table renders a clean empty state when the SDM's area has no experts.

Out of scope

  • Re-defining the existing quality + SLA + active-work figures — consume the expert epic's models; only the two new escalation/completion fields are added, additively, to the shared quality model.
  • A net-new escalation or blocker capture UI — escalationRate reads the existing blocker model; this run adds no new escalation-raising surface.
  • Defining the status classification or the coverage grid — that is sdm-supply-coverage; this feature reads its derivation and grid (sharing, not forking, the per-expert classification).
  • Standing up the area envelope / "my area" scope or the SDM page server fetch — that is sdm-area-foundation; this feature plugs into it.
  • The platform→module taxonomy and the tagging of experts onto it — that is sdm-supply-taxonomy; this feature consumes the resolved skills/product-skill keys.
  • Any expert write / coaching / outreach action — read-only strength view this round.
  • Realtime / websocket updates — the section is server-fetched on load.
  • Backfilling historical metrics — the assembly reads whatever the upstream models expose.

Open questions

  • none. escalationRate and completionRate are resolved as additive, real-data extensions of the shared getExpertQualityMetrics model (escalation from the blocker model's escalated flag over completed leads; completion as completed ÷ assigned leads). The type field is resolved as a Freelance/Agency derivation off the expert.vendor link. The status-derivation sharing with sdm-supply-coverage, the per-expert demand/supply/gap aggregation over the grid cells, and the (now-merged) dependency sequencing were resolved at Define. </content>

02_build/output/notes.md

Build notes: sdm-expert-strength

  • commits: feat: sdm-expert-strength — real per-expert strength table (+ the Define spec-revision commits already on the branch)

What changed

  • packages/services/src/db/services/matching/expert-quality.ts — extended getExpertQualityMetrics (and its ExpertQualityMetrics type) with two additive, real-data fields:
    • escalationRate — % of the expert's completed leads that have ≥1 escalated blocker (escalated: true), over the same completed-lead set the other rates use.
    • completionRate — completed ÷ all leads assigned to the expert (lead.expert == id). Both default to null when there's no denominator. Existing consumers (expert-workbench Quality card) are untouched — additive only (verified: web typecheck passes).
  • packages/services/src/db/services/sdm-coverage/index.ts — extracted the per-expert classification out of SdmCoverageService into an exported classifyExpertsForCoverage and made ExpertClassification public. The classification now also surfaces slaAdherence, the full quality record, and activeProjectCount (all already computed) so the strength view reuses the one derivation rather than re-deriving anything. getCoverageForSdm calls the shared function; coverage's cell output is unchanged.
  • packages/services/src/db/services/sdm-strength/{index.ts,instance.ts} (new) — sdmStrengthService, a sibling of sdm-area/sdm-demand/sdm-coverage/sdm-risk. getStrengthForSdm reads the area envelope (in-scope experts, scope-aware), runs the shared classification, reads the coverage grid + demand cells, and assembles one row per area expert: reused metrics, shared strong/ok/at-risk status, type (Agency when expert.vendor is set, else Freelance), region/country, skill level (from seniority), availability, and per-expert demand/supply/gap summed from the grid cells the expert is matched into.
  • packages/services/src/db/services/index.ts — export sdmStrengthService (after sdm-coverage) + the strength types and the now-public ExpertClassification.
  • apps/web/components/dashboard/sdm/expert-strength-table.tsx / expert-strength-row.tsx — render the real SdmExpertStrengthRow[]; removed the placeholder top/bottom-20%-by-SLA computedStatus ranking; status renders as a badge; added an empty-state row.
  • apps/web/app/(app)/sdm/dashboard/page.tsx — resolve sdmStrengthService.getStrengthForSdm alongside coverage/risk (degrading to [] on error, same as the other sections) and pass it to the table.
  • apps/web/lib/mock/sdm.ts — removed the expertStrength mock array (the other still-mock sections — market signals, service performance — are out of scope and untouched).

Acceptance criteria status

  • getExpertQualityMetrics returns escalationRate + completionRate from real data; consumers unchanged — verified via web typecheck.
  • Row csat / slaAdherence / escalationRate / completionRate / repeatUsage / activeWork come from the reused models (extended quality model, shared SLA derivation, matching context), not recomputed here.
  • Each expert shows product skills, type (from expert.vendor), region/country, skill level (from seniority), status, grid-derived demand/supply/gap, and availability — area-scoped, scope-aware.
  • Status reuses sdm-supply-coverage's derivation via the shared classifyExpertsForCoverage helper, mapped to strong/ok/at-risk; placeholder ranking removed.
  • The table renders from the real assembly via the page; expertStrength mock removed from lib/mock/sdm.ts.
  • The table renders a clean empty state when the SDM's area has no experts.

Verify result

  • format · lint · typecheck · build run in CI + the Vercel preview, not here. As a confidence check I ran pnpm --filter @sustentus/services typecheck and pnpm --filter web typecheck locally (after rebuilding the services package so the new cross-package exports resolve) — both pass.

Notes for review

  • Two new quality metrics added upstream. escalationRate/completionRate now live on the shared getExpertQualityMetrics, per the Define decision (rather than dropping them or faking them in the SDM feature). They're additive; double-check the chosen definitions (escalation over completed leads via the blocker model; completion as completed ÷ assigned) match intent.
  • Status is shared, not forked. classifyExpertsForCoverage is the single derivation behind both the coverage grid and this view — the strength status is just a label mapping over it.
  • demand/supply/gap are summed straight from the coverage grid cells the expert is matched into, so they reconcile with the Quote-and-bid-readiness / demand tables by construction.

03_release/output/changelog.md

See the real strength of every expert in your area

Your go live readiness dashboard's expert strength table now shows real data for every expert in your area, not placeholder rows:

  • Each expert's CSAT, SLA adherence, escalation rate, completion rate, repeat work and current load — drawn from live delivery data.
  • A strong, ok or at-risk status that matches your coverage view, alongside the demand, supply and gap that expert sits against.
  • Their product skills, type, region and availability, so you can see at a glance who can take on more.

03_release/output/investor-update.md

SDMs can now trust every expert strength number

Who it's for: Service delivery managers What shipped: The go live readiness dashboard's expert strength table now shows real per-expert quality, SLA, load, coverage status and gaps for every expert in an SDM's area. Why it matters: Advances Build the bridge, Q2 objective 3 — validate technical infrastructure & payout flow — by making the SDM's supply data trustworthy.

Escalation and completion rates are now real, reused metrics across the platform.

Dig deeper: <merged-PR URL> · <changelog entry URL>