vendor-time-to-valuerun.md00_intake/stub.mdThe time-to-value section (time-to-value-section.tsx) shows average time-to-value, a revenue-weighted average, a distribution across bands, and a delivery velocity block (% within SLA, SLA target, BRD→delivery days, a velocity trend) — all from the mock. There is no engine that measures, per lead/project, how long from start to first value, nor how delivery timing performs against SLA. The revenue-risk feature (6) also needs the SLA-breach signal this produces.
<14d / 14–21d / 21–30d / >30d) with count + pct.expert-dashboard-data/sla-stage-targets timing
rather than recomputing time-in-stage.expert-dashboard-data/sla-stage-targets; this consumes
them.sla-stage-targets; if it hasn't landed, stub a minimal read for
the BRD→delivery target rather than forking it.touches: packages/services/src/server (TTV + velocity engines on the envelope),
reads packages/services/src/db/models/{lead,milestone,status-history}.ts and the sla-stage-targets
model, apps/web/components/dashboard/vendor/{time-to-value-section,delivery-velocity-card}.tsx.01_define/output/spec.mdslaConfigService.getSlaDefinitions (packages/services/src/db/services/sla); apps/web/app/(app)/vendor/dashboard/page.tsx (wire the section from the engine, drop the zero-state). Section components (apps/web/components/dashboard/vendor/{time-to-value-section,delivery-velocity-card}.tsx) already render from the view-model — no change expected.The vendor dashboard's service-speed section — "Speed to first value" (avg days to value, a
revenue-weighted average, a < 14d / 14–21d / 21–30d / > 30d distribution) and the "Delivery
performance against SLA" card (% delivered within SLA, an SLA goal line, a velocity trend) — renders
entirely from the zero-state base since the foundation removed the mock
(apps/web/components/dashboard/vendor/time-to-value-section.tsx).
There is no engine that measures, per delivered project, how long from intake to value delivered, nor
how delivery timing performs against the configured SLA. This is feature 3 of the vendor-dashboard-data
epic, off the foundation spine (#528): it advances Build the Bridge → Q2 2026 Objective 1 (Establish
Product-Market Fit with Vendor Partners) by making the paying vendor's delivery-speed cockpit real —
speed-to-value is the metric the breakdown ties directly to retention ("delivery speed directly impacts
retention"), and feature 4 (retention-by-speed) cannot split retention by speed until this engine
produces a per-project time-to-value band.
VendorService, period-scoped like getCustomerHealth):
for each delivered project whose delivered transition falls within the selected window, compute
the time-to-value span and roll it up:t0 = the lead's createdAt (intake — the agreed full-funnel anchor); t1 =
the timestamp the lead first entered the delivered status, read from statushistory
(entityType: "lead", the earliest row whose status is delivered). Fallback when a delivered
project has no delivered history row: the lead's most recent statushistory.changedAt (its
delivery-completed date).total
over the window, read from the foundation envelope's paidInvoices — the breakdown's definition of
service revenue, so this does not depend on feature 2 landing), and the distribution across the
four bands < 14 days / 14–21 days / 21–30 days / > 30 days with count + pct (pct of delivered
projects in band).avgDaysChange), shown with goodWhenNegative (faster is better).pctWithinSLA): the share of delivered projects whose business-days span
(createdAt → delivered) is within the SLA target. The target is the sum of the pre-delivery
stage targets — Request + BRD + Bidding + Delivery targetDays — read from
slaConfigService.getSlaDefinitions(tenantId) (the sla-stage-targets engine, shipped #463); this
consumes the shared targets rather than recomputing or forking them. If those definitions are absent
for the tenant, pctWithinSLA is 0 (no target ⇒ nothing classed within SLA) rather than a
fabricated figure.slaTarget): the adherence goal the card plots as its reference line — the
target percentage of deliveries that should be within SLA (distinct from the days target above).
There is no config entity for an adherence goal, so this is a single documented constant owned by
this feature (see Out of scope).velocityTrend): a monthly series over the trailing 6 months of the % of that
month's delivered projects within SLA, bucketed by the delivered month (reusing the existing
6-month bucket pattern in VendorService).vendor-retention-cohorts splits
retention by speed from this single definition instead of re-deriving it.page.tsx fetches the engine for the resolved window and feeds timeToValue,
deliveryVelocity and velocityTrend into VendorDashboardView (replacing the emptyVendorData
slices), tenant-scoped, with a .catch zero-state on failure matching the existing section reads.createdAt → delivered timing for projects delivered within the selected window,
with avgDaysChange the signed change vs the prior same-length window.createdAt → delivered
span is within the summed pre-delivery SLA target read from slaConfigService.getSlaDefinitions
(not hard-coded); when no SLA definitions exist for the tenant it is 0. The velocity trend is
the monthly % within SLA over the trailing 6 months.vendor-retention-cohorts (feature 4) to consume.getTenantOrNull) and degrades to a zero
state (not mock values) if the engine read fails.expert-dashboard-data/sla-stage-targets /
admin-sla-config; this only reads them.slaTarget reference-% is a fixed documented
constant this round; making it tenant-configurable is a later concern.sla/stage-map.ts) — this feature reads spans from statushistory
and the per-stage targetDays directly; it does not depend on the current-stage deriveLeadSlaTiming
path, so no stage-map change is needed.apps/dashboards (stays the dummy source of truth) and the cosmetic Export button.createdAt, t1 = delivered transition), the SLA target basis
(sum of pre-delivery stage targets), and the calendar-days-for-TTV / business-days-for-SLA split were
decided during Define. The adherence goal-line constant is a documented presentation default (see Out
of scope).02_build/output/notes.mdfeat: vendor-time-to-value — TTV + delivery-velocity engine, wired to the dashboardpackages/services/src/db/services/vendor/index.ts — new getTimeToValue(tenantId, window) on
VendorService, plus a private loadDeliveredSpans helper and the view-model types
(VendorTimeToValue et al.). For each project whose delivered status-history transition lands in
the window it computes the intake→delivered span (t0 = lead createdAt, t1 = earliest delivered
transition): TTV avg (calendar days), revenue-weighted avg (weight = the customer's windowed
paid-invoice revenue), the four-band distribution (count + pct), avgDaysChange vs the prior
same-length window, % within SLA (business-day span ≤ the summed pre-delivery
sla-stage-targets), the 6-month velocity trend, and the per-lead TTV band for feature 4.packages/services/src/db/services/index.ts — re-export the new vendor TTV types from the barrel.apps/web/app/(app)/vendor/dashboard/page.tsx — fetch getTimeToValue for the resolved window
(tenant-scoped, with a zero-state .catch mirroring the other section reads) and feed
timeToValue / deliveryVelocity / velocityTrend into the view, replacing the empty slices.createdAt → delivered
timing for projects delivered in the window, with avgDaysChange vs the prior window.% within SLA = share whose business-days span ≤ the summed pre-delivery target from
slaConfigService.getSlaDefinitions (0 when no definitions exist); velocity trend = monthly %
within SLA over the trailing 6 months.searchParams-resolved window.bandsByLead, four-band vocabulary) for
vendor-retention-cohorts (feature 4).getTenantOrNull) and degrades to a zero state (not mock) on failure.< 14 / 14–21 / 21–30 / > 30 bands); the SLA
within/over classification uses business days against the business-day SLA targets — an
intentional split, documented in the spec.slaTarget is the adherence goal line % the card plots (a documented
constant, SLA_ADHERENCE_TARGET_PCT = 85), distinct from the days target that drives pctWithinSLA.delivered is the real lead-workflow status (work_in_progress → delivered → survey_sent → completed); the engine reads spans from statushistory directly and only consumes the SLA
targetDays, so it does not depend on the SLA stage-map.03_release/output/changelog.mdThe service-speed section of your vendor dashboard now shows real numbers instead of placeholders. You can see your average time to value, a revenue-weighted average, how your deliveries spread across speed bands, the share of deliveries meeting your SLA target, and how that's trending — all for the date range you choose.
03_release/output/investor-update.mdWho it's for: Vendor partners What shipped: The vendor dashboard now computes real time-to-value, a revenue-weighted average, delivery speed bands, and % of deliveries within SLA per project over any date range. Why it matters: Speed-to-value is the lever for retention — vendors can now see and act on it, advancing product-market fit with vendor partners.
Dig deeper: <merged-PR URL> · <changelog entry URL>
03_release/output/release.mdVendorService method; no new app/route/env/architecture)feature-role-matrix/vendors does not enumerate per-datapoint metrics, consistent with the sibling features)Ran /code-review medium (standard complexity). Merge/wiring: clean — Promise.all order matches the
destructured tuple, no duplicate data keys, all referenced retention.*/ttv.* fields exist, barrel
re-exports correct.
currentStart) — resolved on branch: the
prior window now ends at currentStart − 1ms so a delivery on the exact boundary isn't double-counted
(matches the sibling window methods).targetDays from
getSlaDefinitions; null/0 only when none are configured. No silent fabrication.createdAt → delivered timing,
with avgDaysChange vs the prior window — verified in getTimeToValue.slaConfigService.getSlaDefinitions
(0 when none); velocity trend = monthly % within SLA over trailing 6 months — verified.searchParams window — verified in page.tsx.bandsByLead) for feature 4 — verified on the return type..catch →
EMPTY_TIME_TO_VALUE).