service-lead-ownership-and-handoffrun.md02_define/output/spec.mdAt every stage of the customer's service lifecycle there should be exactly one named owner, and today that ownership is unclear either side of BRD approval. No one formally owns a service lead before the customer approves, and the handover to service delivery is a conversation that sometimes does not happen (FR-5..FR-8). This advances Q2-2026 Objective 1: the success measure is 100% of service leads showing exactly one named owner at every stage of their life.
(The stub names Sales as a persona; sales joins the platform's role vocabulary in this run — the
pipeline's PR-label persona set is unchanged, so the header lists the label-vocabulary personas.)
Sales becomes a real user role that owns the lead, and ownership transfers by itself at approval. Paul's terminology governs throughout: this is service lifecycle ownership — the customer's complete service journey — never "pipeline ownership".
licence-class-reporting), lead read scope and party predicates,
the disabled-staff sign-in block, and the web role plumbing (role home, grantable roles, users
table).salesOwner), who it lands on after (csm, existing), and where it came from
(source, from the configured service-lead source list — sales, customer success, support,
professional services, AI, product onboarding; default sales, BR-21/D-10). The stage decides
which field answers today: before BRD approval the Sales owner (BR-24), from the moment of
approval the assigned CSM (FR-8) — so "exactly one owner at every moment" (BR-20) is true by
construction, with no field to keep in step.source (sales) and a salesOwner (its creator, falling back the same chain).approveBrd refuses with a clear message, the approval UI surfaces the refusal,
and the Sales owner is notified of exactly what is missing (scope §13).service-lead.ownership-transferred).03_build/output/notes.mdUSER_ROLES (+ workflow persona
schema), a sales user-model discriminator, own-scoped permission template
defaults (the lead owner's working set), HIERARCHY_ROLES (internal),
LICENCE_CLASS_BY_ROLE.sales = "Sales" (the full remap is stub 6), the
lead read-scope/party/line predicates, the disabled-staff sign-in block,
and the web role plumbing (role home /service-leads, grantable roles,
nav, labels, board/workspace copy). Clerk needs the matching org:sales
role configured — noted below for Verify.salesOwner (who
holds it before approval) and source (where it came from —
shared/lead-sources.ts, default sales, all sources Sales-owned in v1
per D-10). shared/lead-ownership.ts — leadOwnershipView answers the one
named owner for the current stage, the transfer target, the BR-22 blocked
state, and a disabled owner (held by their manager); pure + unit-tested,
client-importable.leadService.create resolves the pre-approval owner
(explicit → Sales-role creator → oldest active Sales user → the
organisation's top). Migration 1787702400000-lead-sales-ownership
backfills source + salesOwner (creator, same fallback chain) and builds
tenantId_salesOwner.approveBrd refuses while lead.csm is
empty — "A CSM must be assigned to this lead before the customer can
approve the BRD." — covering both the live approval and the canned demo
path (both funnel through the same service). The Sales owner is notified
(service-lead.approval-blocked) of exactly what is missing.service-lead.ownership-transferred, email-deduped per person).
Notifiers are best-effort by design and never unwind the approval.salesOwner (and csm)
populate with isActive so the chip derives in place.source recorded, LEAD_SOURCE_OWNER_ROLE resolves the owner (all Sales in v1, Paul's general model with unchanged V1 behaviour).service-lead.approval-blocked notification + visible blocked state on the chip.service-lead.ownership-transferred to both, one email per person.lead.csm; nothing later rewrites it (the expert slot is never consulted, so an expert leaving changes nothing).ownerDisabled + stub 1's effective-holder rule); no data changes, so customer visibility is untouched.org:sales organisation role before a Sales primary
role can be assigned (assignPrimaryRole writes org:${role}); the
in-app grantable-extras path works as soon as the claim carries sales.service-lead.approval-blocked notifier fires on each blocked
attempt; rare in practice, flagged in case Verify wants a dedupe window.inherited-visibility.