sdm-supply-coveragerun.md00_intake/stub.mdThe quote-and-bid readiness table (quoteAndBidReadiness in
apps/web/lib/mock/sdm.ts, rendered by
bid-readiness-table.tsx) is the
supply side of each demand cell: per (platform, module, skillLevel, region, language) it shows
brdDemand, and then classifies the matched experts into bidReadyExperts, deliveryReadyExperts and
strongExperts, with availableExperts, shortfall, a readinessStatus RAG, a coverageStatus
label and an overloaded flag. This classification — what makes an expert "bid-ready" vs
"delivery-ready" vs "strong" for a cell, and whether the cell is covered — does not exist as a
derivation. It is the core net-new SDM logic, and it also fills the coverage columns left pending by
sdm-demand-aggregation.
sla-stage-targets).expert-quality-metrics).availableExperts, shortfall (demand − available), coverageStatus
(Fully covered / Partial / Not covered), the readinessStatus RAG, and overloaded (the matched
experts are over capacity, from active work).status, readiness, coverage, coverageGap) from the same derivation.availableExperts, shortfall, coverageStatus, readinessStatus and overloaded are derived
per cell over the SDM's area.lib/mock/sdm for supply/coverage.expert-dashboard-data/{expert-quality-metrics,sla-stage-targets,expert-active-projects}.sla-stage-targets, expert-quality-metrics and expert-active-projects have landed; if
not, define a minimal read interface and coordinate — do not fork those models.touches: packages/services/src/server (coverage derivation over experts + the demand cells),
apps/web/components/dashboard/sdm/{bid-readiness-table.tsx,demand-table.tsx}.01_define/output/spec.mdThe SDM "Go live readiness" dashboard is a supply↔demand coverage cockpit: can the expert pool cover
the demand flowing through BRD → quote → bid? sdm-demand-aggregation (#3, merged) now builds the
demand half — real SdmDemandCell[] keyed (platform, module, region, language) with demand,
quotesWaiting, bidReadyExperts, timeBand and daysToStart. But the supply half does not
exist as a derivation: the quote-and-bid readiness table still reads quoteAndBidReadiness from
apps/web/lib/mock/sdm.ts, and the demand table renders its two coverage columns (Coverage status,
Coverage gap) as neutral Pending placeholders, explicitly waiting on this feature. Classifying
matched experts into bid-ready / delivery-ready / strong per cell — and deriving whether a cell is
covered — is the core net-new SDM logic of this epic. Without it the SDM cannot see where supply fails
demand, which is the readiness signal behind Build-the-Bridge / Q2 2026 Objective 3 (validate the
infrastructure that lets leads complete end-to-end): an uncovered or weak-bid cell is a lead that will
stall before go-live.
Build a supply-coverage derivation that enriches each demand cell with a supply classification, then wire both tables from it and surface the thresholds as admin-configurable tenant policy.
sdmCoverageService)A new tenant-isolated, area-scoped read-service — getCoverageForSdm(tenantId, sdmId, scope) — that
resolves the demand cells (via the existing sdmDemandService.getDemandForSdm, not a re-aggregation)
and, for each cell, classifies the experts matched to that cell's leads. It consumes the already-merged
upstream models and does not recompute any of them:
lead_expert_match records (the
same source sdm-demand uses for bidReadyExperts), resolved to ids + names.expert.isAvailable / availability), i.e. the existing bid-ready set.deriveLeadSlaTiming / the SLA service — over the expert's
recent/active leads, since no per-expert aggregate exists upstream) is at or above the configurable
deliveryReadyMinSlaAdherence bar.getExpertQualityMetrics CSAT ≥
strongMinCsat and reworkRate ≤ strongMaxReworkRate (rework rate is the upstream analog of
the mock's escalation rate; CSAT/rework/repeat all come from getExpertQualityMetrics — not
recomputed here).getExpertMatchingContext activeProjectCount, or availability.weeklyCapacityHours) at or above
the configurable overloadedMinActiveProjects bar for all/most matched experts.availableExperts (count of bid-ready), shortfall (max(0, demand − availableExperts)),
coverageStatus (Fully covered when availableExperts ≥ demand · Partial when 0 < availableExperts < demand ·
Not covered when 0), and a readinessStatus RAG (green / amber / red) from coverageStatus plus
whether the cell has any delivery-ready / strong experts.The service returns demand cells enriched with { bidReadyExperts, deliveryReadyExperts, strongExperts, availableExperts, shortfall, coverageStatus, readinessStatus, overloaded } so one read
drives both tables. Empty/again-degraded reads return an empty list (same resilience pattern the page
already uses for demand).
The classification bars are SDM policy, configured per tenant via the existing tenant-settings registry pattern (not scattered constants):
coverage) to SETTINGS_REGISTRY: coverage.deliveryReadyMinSlaAdherence
(number, %), coverage.strongMinCsat (number, /5), coverage.strongMaxReworkRate (number, %),
coverage.overloadedMinActiveProjects (number) — each with a sensible default and description.getCoverageThresholds(tenantId) on TenantSettingService (mirroring
getCommercialConfig / getEscalationConfig) that resolves stored values or registry defaults; the
derivation reads thresholds through it.coverage tab, consistent
with the existing category tabs (general, commercial, …) — so an admin tunes them in-app this run.bid-readiness-table.tsx) renders from the enriched cells
(platform/vendor, module/product, demand, available experts, shortfall, readiness RAG) instead of
mock/sdm.demand-table.tsx) fills its Coverage status and Coverage gap columns from the
enriched cells, replacing the Pending placeholders (status from coverageStatus / readinessStatus,
gap from coverageStatus).sdmCoverageService.getCoverageForSdm(tenantId, sdmId, scope)
returns the demand cells enriched with the supply classification, reusing sdmDemandService for
the demand half (no second demand aggregation) and honouring scope (mine / tenant).lead_expert_match + reused SLA timing + getExpertQualityMetrics (none of CSAT / SLA / rework is
recomputed in this feature).availableExperts, shortfall, coverageStatus (Fully covered / Partial / Not covered),
readinessStatus RAG and overloaded are derived per cell over the SDM's area.getCoverageThresholds, defined in
SETTINGS_REGISTRY with defaults, and editable by an admin in the admin Settings UI.quoteAndBidReadiness from lib/mock/sdm.Coverage status and Coverage gap columns fill in from the same derivation,
replacing the Pending placeholders.lib/mock/sdm for supply/coverage; an SDM with no area (under
mine) or a failed read degrades to an empty/placeholder table, not a blanked dashboard.getExpertQualityMetrics, the SLA
timing layer, and getExpertMatchingContext (the merged expert epic).sdm-coverage-risk-outreach, #5)
— they derive from this coverage output.sdm-expert-strength, #6) and the two analytics sections
(market signals #7, service performance #8).skillLevel as a cell key (the demand cells from #3 key on
platform × module × region × language, and this feature matches that granularity).apps/dashboards (it stays the dummy source-of-truth) and to the demand aggregation
itself (owned by #3).02_build/output/notes.mdfeat(sdm-supply-coverage): supply-coverage derivation, admin thresholds, dashboard wiringpackages/services/src/db/services/sdm-coverage/ (new): SdmCoverageService.getCoverageForSdm(tenantId, sdmId, scope). Reuses sdmDemandService.getDemandForSdm for the demand half (no second aggregation), then classifies each cell's matched experts:isAvailable; delivery-ready = bid-ready + per-expert SLA adherence ≥ bar (adherence = share of the expert's active leads whose reused SLA band is not red); strong = delivery-ready + CSAT ≥ bar + rework ≤ bar (from getExpertQualityMetrics). overloaded cell = has bid-ready experts and all of them are at/over the active-project bar (getExpertMatchingContext).availableExperts, shortfall, coverageStatus (Fully covered / Partial / Not covered), readinessStatus RAG and overloaded per cell.packages/services/src/db/services/sdm-demand/index.ts: extended SdmDemandCell with bidReadyExpertIds (the matched expert ids) so coverage can classify supply without re-running the aggregation. Demand counting is unchanged.packages/services/src/db/services/tenant-setting/: added the coverage category to SETTINGS_REGISTRY (4 numeric thresholds with defaults) and a typed getCoverageThresholds(tenantId) getter (registry defaults when unset) — mirrors getCommercialConfig.packages/services/src/db/services/index.ts: barrel exports for sdmCoverageService (placed after its dependency services) + SdmCoverageCell/CoverageStatus/CoverageReadiness/CoverageThresholds types.apps/web/.../sdm/dashboard/page.tsx: resolves coverage once (alongside area) and feeds both tables; coverage failure degrades to empty tables (area stays the gate).apps/web/components/dashboard/sdm/bid-readiness-table.tsx: renders from the derivation (platform, product, demand, available experts, shortfall, readiness RAG) — no longer lib/mock/sdm.apps/web/components/dashboard/sdm/demand-table.tsx: Coverage status + Coverage gap columns fill from the derivation, replacing the Pending placeholders; row RAG from readinessStatus.apps/web/.../admin/settings/coverage/page.tsx (new) + tabs-config.ts: a Coverage admin Settings tab that renders the coverage thresholds via the existing SettingsRegistryManager (admin-only, covered by the existing /admin/settings route policy).sdmCoverageService.getCoverageForSdm(tenantId, sdmId, scope) returns demand cells enriched with the supply classification, reusing sdmDemandService (no second aggregation), honouring mine/tenant.lead_expert_match + reused SLA timing + getExpertQualityMetrics (nothing recomputed here).availableExperts, shortfall, coverageStatus, readinessStatus and overloaded derived per cell over the SDM's area.getCoverageThresholds, defined in SETTINGS_REGISTRY with defaults, editable in the admin Settings UI (new Coverage tab + the general Settings tab).quoteAndBidReadiness from lib/mock/sdm.Coverage status + Coverage gap columns fill from the same derivation, replacing Pending.lib/mock/sdm for supply/coverage; a no-area or failed read degrades to empty/placeholder tables, not a blanked dashboard.pnpm typecheck was run once after the changes and passed (9/9 tasks) — the service-type + barrel + component changes compile clean across the monorepo.red; an expert with no active leads is treated as 100% (no breaches). Worth a sanity check that this matches the intended "delivery bar".getExpertQualityMetrics exposes reworkRate).(platform, module, region, language) — skillLevel is intentionally not a cell key (the merged demand cells from #3 don't carry it).03_release/output/changelog.mdYour go live readiness dashboard now shows whether you have the supply to meet demand:
03_release/output/investor-update.mdWho it's for: Service delivery managers (SDM); admins set the thresholds What shipped: The go live readiness dashboard's quote-and-bid readiness table and demand coverage columns now classify matched experts as bid-ready, delivery-ready or strong from real quality and SLA data, with admin-tunable thresholds. Why it matters: SDMs see real coverage gaps before they bite — Build the Bridge, Q2 2026 Objective 3 (validate technical infrastructure & payout flow).
Dig deeper: https://github.com/sustentus/sustentus/pull/525 · https://help.sustentus.com/changelog/2026-06-23-sdm-supply-coverage