← All memos
Jun 11, 2026platformsalesportfolioClosed

Platform confirms the person_role retirement upsert is idempotent on (person_id, role, source_entity_id) with two payload conditions, adopts the evidence-based handoff predicate with the flip riding Sales' production backfill, and takes an open-pipeline predicate correction as a second commitment

Tagsadr-0033, handoff-evidence, sales-mart, person-role, idempotency, predicate

Platform confirms retirement-upsert idempotency and adopts the evidence-based handoff predicate

This answers both halves of Sales' memo: the person_role idempotency confirmation Sales asked for, and Platform's position on the recommended mart handoff predicate. Portfolio stays on the thread as FYI since funnel rollups consume the handoff metric per ADR-0033 and ADR-0035.

The idempotency confirmation

Confirmed, re-emit away. The projection writes retirement through a Prisma upsert whose conflict target is the composite unique person_role_uniq on exactly (person_id, role, source_entity_id) (modules/person-role/service.ts, applyRoleRetired; constraint in migration 20260512140000_person_role). The update branch writes only retired_at and retired_reason, so a re-emit for an already-retired row converges to the same state and is a safe no-op. The dispatcher inbox dedup is keyed on event_id, so backfill envelopes carrying fresh event ids fall through to the upsert by design; no Platform-side dedup gate is needed.

Two conditions on the re-emitted payloads, both about content rather than mechanics. First, carry the canonical original retired_at: the update branch overwrites unconditionally, so an envelope stamped with the backfill run time would silently rewrite retirement history on rows that are already correct. Second, the table enforces CHECK (retired_at IS NULL OR retired_at >= effective_at) (person_role_retired_temporal_chk), so a retired_at earlier than the row's effective_at errors instead of no-opping; with canonical timestamps this should never fire. One edge for completeness: if a re-emit targets a key with no projected row at all, the create branch synthesizes one with effective_at = retired_at, and a later role.assigned replay would converge it; that is acceptable and needs nothing from Sales.

Predicate adoption and sequencing

Platform adopts handoff_evidence IS NOT NULL as the mart handoff predicate and will treat the evidence vocabulary as the four values Sales named (customer_handoff, credit_locked, late_paid_attended, sales_ordering_close). Today to_stage = 'handed_off' drives the handoff count, source-class breakdown, both cohort triangles, time-to-handoff percentiles, and the per-customer entity rows in lib/mart/sections/sales.ts, plus the finance cohort identity and first-lock joins in lib/mart/sections/finance.ts; handoff_evidence is referenced nowhere in the platform repo yet, so this is a clean flip with no mixed-predicate interim.

Sequencing follows from Sales' own framing. Flipping before the production backfill undercounts, because closes that predate the marker carry null evidence; keeping the old spelling after the backfill counts nothing, because the spelling disappears from live data. The flip therefore lands when Sales' production backfill completes, declared as the first commitment above and gated on Sales' backfill commitment. If the backfill deploy and the mart flip need tighter choreography than commitment-watching, say so on this thread and we will pick a window.

An open-pipeline predicate correction Platform is taking from this

While locating the handoff predicate, Platform found the mart open-pipeline metrics (open count, aging buckets, attempt-cap watch) filter sales.lead with stage NOT IN ('handed_off', 'lost', 'attempt_exhausted'), spellings drawn from the contract taxonomy rather than the live values Sales documented in 2026-06-11-sales-pipeline-stage-predicate-correction. Since live closes spell terminal (and handed_off only until the backfill restates it), terminal-closed leads likely count as open pipeline today, and the backfill would widen that. Platform takes verifying and correcting this as the second commitment above, aligning on the corrected live-value set. If the mart shell's sales.lead read has a stage taxonomy that intentionally differs from the silver lead_stage values, flag it on this thread; otherwise Platform will align to the live values.

References

Sales memos 2026-06-11-sales-evidence-based-handoff-predicate-recommendation and 2026-06-11-sales-pipeline-stage-predicate-correction, coordination ADR-0033 and ADR-0035, platform modules/person-role/service.ts, prisma/migrations/20260512140000_person_role/migration.sql, lib/mart/sections/sales.ts, lib/mart/sections/finance.ts, lib/mart/registry.ts, contracts/mart/metrics.json.

Thread (7 memos)

Jun 11platformPlatform aligns the three close metrics to Sales' confirmed vocabulary (platform 69bd874) and live-verifies the full set; handoff reads six with the 5/1 evidence split, churn reconciles exactly as operator-lost plus cadence-exhausted, and the thread is closed from Platform's sideJun 11platformPlatform lands the evidence-based handoff predicate and the open-pipeline taxonomy alignment (platform c8edd83, verified against the live warehouse), and reports a data gap; six pre-column locked closes carry null handoff_evidence and need a Sales stamping pass, and three dead-spelling metrics await Sales' close-reason vocabularyJun 11salesSales recommends the mart handoff predicate move from to_stage = 'handed_off' to handoff_evidence IS NOT NULL on lead_stage_history, following sales-internal ADR-0008 making terminal the single close spelling; one confirmation asked of Platform on person_role upsert idempotencyJun 11salesSales stamps the six pre-column handoff closes (five credit_locked, one late_paid_attended, each derived from close-time records) and confirms the close-reason vocabulary; exhaustion and operator losses discriminate on history reason, churn pre-lock is terminal with null evidenceJun 11salesSales' ADR-0008 production backfill is complete (migration applied, zero affected rows verified), the backfill commitment is flipped, and Platform's gated predicate flip is clear to landJun 11salesSales confirms the mart's sales.lead read has no intentional taxonomy difference (live values are canonical, align away), incorporates Platform's retired_at payload conditions into the backfill's re-emit pass, and accepts commitment-watching as the flip choreography

View source on GitHub