# 04 — Cutover Attribution Freeze Strategy

**To:** Candace Jenum, Laurelle Roseman · **From:** Dico Angelo · **Date:** 2026-04-15

## The problem in one sentence

Clari reads pipeline from both HubSpot and Salesforce during parallel-run, and there is no field today that tells it which record is the system of record for a given opportunity. This is the double-count.

## The move: name the gate, not the rule

I'm not proposing "don't double-count." I'm proposing a **governance gate** — one field (`Origin_Flag__c`), one state (`Locked__c`), one window (T-14). Rules without a gate drift. A gate with rules downstream of it doesn't.

## The three-part gate

**1. Freeze window — T-14 days before cutover cohort**

For each waved cohort of accounts, 14 days before SF becomes authoritative:
- HubSpot records for the cohort go read-only (workflow-disabled, not archived).
- No new partner-sourced opps may be created in HubSpot.
- A banner lands on the rep's HubSpot view: *"Cohort frozen. Work the SF record."*

This is the Contentsquare One-CRM pattern applied: cohort-waved, not big-bang.

**2. Attribution lock — at Stage=Propose**

When an opportunity reaches Stage=Propose in SF, `Deal_Registration__c.Locked__c = true` fires. After lock, the credited partner and credit split cannot change without a Partner Ops override (tracked via audit trail). This closes the retroactive re-attribution hole that breaks every quarterly close.

**3. Dual-write validator — runs hourly during parallel-run**

A scheduled Apex job (or Scheduled Flow) that:
- Joins HubSpot records (via `Hubspot_Legacy_Id__c` external id) to SF records.
- Flags any opportunity present in both systems with differing Amount, Stage, or Partner_Role.
- Writes divergences to `Cutover_Exception__c` with severity tiers.
- Pushes a Slack alert to #partner-ops-cutover for severity=high.

The validator runs for the entire parallel-run window and hits zero rows two weeks before we retire HubSpot. That's the green light.

## What Clari reads

During parallel-run, Clari pulls from Salesforce only, filtered to records where `Source_System__c IN ('salesforce','migrated_verified')`. HubSpot records flagged as `migration_pending` are excluded from the forecast feed. This is a single data-source rule Clari's admin can set in 15 minutes — but it only works because the gate above ensures every migrated record is tagged.

## Success criteria

- Zero opportunities appear in both systems post-cohort freeze.
- 100% of sourced pipeline has an Approved Deal_Registration__c before Stage=Propose.
- Dual-write validator exception count trends to zero over the parallel-run window.
- Clari quarterly actuals match SF quarterly actuals to within $0.

## Why this works

Every failed CRM cutover I've seen (Contentsquare One-CRM, which I built against at CS) died the same way: two sources of truth, no gate between them, reps working wherever the data felt cleaner. The fix is never a migration script. It's a gate that makes the *old* system inhospitable to new work while the *new* system is still catching up.
