Skip to Content

← All archived runs

Run: leads-board-parity

run.md

Run: leads-board-parity

  • branch: claude/pipeline-leads-board-parity-sjk5ql
  • pr: #713

01_define/output/spec.md

Spec: Leads board (Kanban) parity

  • slug: leads-board-parity
  • personas: Vendor, CSM, SDM, Admin, Customer, Expert
  • touches: apps/web/app/(app)/service-leads, apps/web/components/service-leads/kanban-board, packages/services/server (read-only status + leads services)
  • complexity: standard

Problem

Web's service-leads board and demo's leads board have diverged into two different products. Demo's board is a persona-lensed pipeline view — per-persona title/intro and column labels, a four-tile summary row with pipeline value, per-column monetary totals, and cards showing the assigned expert and deal value — built faithfully on the canonical nine-status lead workflow. Web's board is a single generic, read-only board with eight grouped columns keyed to stale/invented status strings (analysis, csat, requirements_analysis, a Closed lost column with no engine backing), no summary row, no pipeline value, no expert, and no persona lensing. This blunts the vendor's view of their book of work, which is exactly what Refine the Bridge → Q2 2026 Objective 1 (Establish Product-Market Fit with Vendor Partners) depends on: the KR "maintain active engagement rate from onboarded vendors (90%+)" needs vendors to see a clear, lensed pipeline of their leads and its value.

Proposed change

Bring web's board to demo visual parity while keeping it read-only (no status mutation this run). Reconcile web's grouped/stale columns to the canonical nine-status lead workflow (one column per real status, rendered only when the current persona has leads in that status — demo behaviour). Add per-persona lensing (title/intro and per-column status labels, keyed to the authenticated user's role), the four-tile StatBox summary row (Open / Pipeline value / Wrapping up / Completed), per-column monetary totals, and cards showing the assigned expert (avatar + name) and deal value. Monetary figures use each lead's budgetMin. The board stays read-only — cards remain links to the lead/workspace view; drag-to-change-status is explicitly deferred to its own run.

Acceptance criteria

  • Board columns derive from the canonical lead workflow (pending, backlog, quotation_process, awaiting_confirmation, work_in_progress, delivered, survey_sent, completed, qualified_out) in journey order — web's grouped/dead columns and the Closed lost column are removed.
  • A status column renders only when the current persona has at least one lead in that status; qualified_out sorts last (matching demo).
  • The board shows a per-persona title and intro, keyed to the authenticated user's persona (the LEADS_COPY equivalent — e.g. Customer sees "Projects", Expert sees "Work queue").
  • Each column header shows the per-persona status label (the statusLabel(status, persona) equivalent) plus the lead count for that column.
  • A StatBox summary row renders four tiles: Open (count of leads pending→work_in_progress), Pipeline value (sum of budgetMin over open leads), Wrapping up (count delivered or survey_sent), and Completed (count completed, with "N in active delivery" subtext where N = work_in_progress).
  • Each column header shows its monetary total (sum of budgetMin for that column) when the total is greater than zero.
  • Each card shows the assigned expert's avatar (initials) + name, shown only from awaiting_confirmation onward (earlier statuses show "Unassigned"), and the deal value as formatMoney(budgetMin).
  • The board remains read-only: no drag-and-drop and no status mutation; cards still navigate to the existing lead/workspace destination.
  • All monetary figures (card value, per-column totals, pipeline-value tile) use budgetMin; a lead with no budgetMin counts as 0 in sums and renders "—" on the card.

Out of scope

  • Drag-and-drop and any lead-status mutation — deferred to its own follow-up run (persistence would route through the workflow-engine guardrail; not built here).
  • Any column or status not in the canonical lead workflow, including the Closed lost column and the dead grouped statuses.
  • The lead detail page (service-leads/[id]) and the create/import flows (web-only; unchanged).
  • Writing or otherwise mutating lead data — this run only reads.
  • The demo board itself — it is the parity reference, not a target for change.

Open questions

  • none. (Build notes, non-blocking: current persona is resolved from the authenticated user's role, already available in web; per-persona status labels reuse the engine's label overrides — the same source demo's statusLabel mirrors.)

02_build/output/notes.md

Build notes: leads-board-parity

  • commits: feat: leads-board-parity — persona-lensed, canonical-taxonomy service-leads board

What changed

  • packages/services/src/db/services/leads/index.ts: added read-only getLeadValueSummary(tenantId) — one $group aggregation returning per-status count + summed budgetMin (missing budgets count as 0), scoped to the tenant and non-deleted leads. Powers the summary tiles and per-column totals accurately across the whole book rather than only the page-limited cards.
  • apps/web/components/service-leads/leads-copy.ts (new): LEADS_COPY keyed by UserRole — per-persona title/intro, ported from the demo (Customer → "Projects", Expert → "Work queue", others → "Service leads"). Nothing imported from apps/demo.
  • apps/web/app/(app)/service-leads/page.tsx: replaced the hardcoded, grouped/stale KANBAN_COLUMNS with columns derived from statusService.getByWorkflow("lead") (canonical 9 statuses, journey order, qualified_out last). Resolves the effective persona with getRole() (emulation/active-role aware), lenses column labels via statusService.resolveLabel(status, persona), maps expert (from awaiting_confirmation onward) + budgetMin onto each card, filters to columns with ≥1 lead, and computes the four summary tiles from the aggregation.
  • apps/web/components/service-leads/kanban-board/kanban-board.tsx: extended card/column types with expert + value; added the four-tile summary row (Open / Pipeline value / Wrapping up / Completed) using shared Card/Typography; threads column value through.
  • apps/web/components/service-leads/kanban-board/kanban-column.tsx: column header now shows its budgetMin total when > 0 alongside the count; each card shows the assigned expert avatar (initials) + name or "Unassigned", and the deal value as formatMoney(budgetMin) (or "—" when absent). Board stays read-only — cards remain <a href="/workspace/:id"> links, no drag-and-drop.

Acceptance criteria status

  • Columns derive from the canonical lead workflow in journey order — grouped/dead columns and Closed lost removed (getByWorkflow("lead")).
  • A status column renders only when it has ≥1 lead; qualified_out sorts last (filter total > 0; order inherited from the workflow).
  • Per-persona title + intro, keyed to the authenticated user's persona (getRole() + LEADS_COPY).
  • Each column header shows the per-persona status label + lead count (resolveLabel(status, persona) + total).
  • Summary row: Open (order ≤ work_in_progress) / Pipeline value (sum budgetMin over open) / Wrapping up (delivered or survey_sent) / Completed (with "N in active delivery" = work_in_progress).
  • Column header shows its budgetMin total when > 0.
  • Card shows expert avatar (initials) + name from awaiting_confirmation onward, else "Unassigned"; deal value = formatMoney(budgetMin).
  • Board stays read-only — no drag-and-drop, cards navigate to /workspace/:id.
  • All monetary figures use budgetMin; missing budget = 0 in sums, "—" on the card.

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

  • Counts and value sums come from getLeadValueSummary (one aggregation), so they are accurate even when a column has more than the 20 rendered cards ("+N more"). The cards themselves remain page-limited for display, matching the prior board.
  • Data scope is unchanged (tenant-wide, read-only). Per-persona data scoping (customer → own, expert → assigned/bid-pool) was deliberately not introduced — the spec lenses display only and keeps the board read-only; re-scoping is an authorization change out of scope for this run.
  • Expert gating (order >= 3, awaiting_confirmation onward) mirrors the demo's expertOf.

03_release/output/changelog.md

Changelog: leads-board-parity

Live entry: apps/help/app/changelog/2026-07-27-leads-board-parity/page.mdx


Title: Your service leads board now reads as a pipeline built around you Personas: vendor, csm, sdm, admin, customer, expert

The service leads board is now a pipeline view tailored to your role — per-persona heading and wording, columns that follow the real lead journey (only the stages you have work in), a summary row (open / pipeline value / wrapping up / completed), per-column value totals, and the assigned expert plus deal value on every card. Read-only, so nothing changes by looking.

03_release/output/investor-update.md

The service leads board is now a persona-lensed pipeline

Who it's for: Vendors, CSMs, SDMs, admins, customers and experts What shipped: The web service leads board now reads per persona, with canonical pipeline columns, a value summary row, per-column totals, and the assigned expert and deal value on each card. Why it matters: A clearer pipeline view keeps onboarded vendors engaged — the core of Refine the Bridge's Q2 objective, establishing product-market fit with vendor partners.

All nine acceptance criteria met; read-only, no new data or infrastructure.

Dig deeper: https://github.com/sustentus/sustentus/pull/713 · https://help.sustentus.com/changelog/2026-07-27-leads-board-parity

03_release/output/release.md

Release: leads-board-parity

  • pr: #713 (https://github.com/sustentus/sustentus/pull/713) · merged: yes — 2026-07-27, squash 472cd0c
  • CI: green — Quality Project, Migrate preview database, Spec structure (advisory) all passed; Claude review + prod migration skipped
  • technical docs: no technical docs impact — a read-only service method (getLeadValueSummary) and UI behaviour on an existing route; no new app/package/route/env/architecture
  • business docs: no business docs impact — the documented per-persona capability (viewing the lead pipeline) is unchanged; this is a display-parity enhancement to an existing board
  • release notes: both — changelog entry (apps/help/app/changelog/2026-07-27-leads-board-parity/page.mdx) + investor draft in this PR
  • sent: investor update sent to 2 recipients on 2026-07-27

Review summary

  • Diff reviewed against CONVENTIONS.md (standard → medium). Arrow functions, type over interface, named imports, correct @sustentus/services/server + @/lib/auth boundaries, sentence-case UI copy — all conform. No dead code or stray debug. The pre-existing .catch() on the per-column fetch is retained (ESLint clean). No cleanup surfaced.
  • No Claude Code Review comments (action skipped) and no human review threads.

Acceptance check (vs spec)

  • Columns derive from the canonical lead workflow in journey order — grouped/dead columns and Closed lost removed (getByWorkflow("lead")).
  • A status column renders only when it has ≥1 lead; qualified_out last.
  • Per-persona title + intro (getRole() + LEADS_COPY).
  • Per-column per-persona status label + count (resolveLabel).
  • Summary row: Open / Pipeline value / Wrapping up / Completed (+ "N in active delivery").
  • Per-column budgetMin total when > 0.
  • Card expert avatar + name from awaiting_confirmation onward, else "Unassigned"; value formatMoney(budgetMin).
  • Board read-only — no DnD, cards navigate to /workspace/:id.
  • All money uses budgetMin; missing → 0 in sums, "—" on card.