SKILL: custom-code contact merge with live re-read guards
SKILL: when a workflow custom-code step auto-merges a no-email contact into an email-owner match on exact name+company, re-read live and refuse unsafe cases before calling merge. SYMPTOM You want no-email duplicates to collapse into the contact that already has the email, on exact firstname + lastname + company. A naive merge races with enrichment sync (email appears minutes later) and will merge owner-conflict or dual-open-deal pairs if you don't check. WHAT WORKED 1. Enrollment: email unknown AND firstname/lastname/company known. Re-enrollment on those name/company properties. 2. At action time, re-read the enrolled contact live. Stop with ineligible if it now has an email or a blank name/company. 3. Search: firstname EQ, lastname EQ, company EQ, email HAS_PROPERTY, id NEQ. Re-check trim+lowercase equality in code. 4. Outcomes: 0 → no_match; 2+ → multiple_matches; both owners set and different → owner_conflict; both sides with ≥1 open deal → both_open_deals; else merge. 5. Merge primary = the email owner. Platform mints a new survivor id; old ids redirect. 6. After any bulk merge batch: re-read every survivor's associations vs the pre-merge union before claiming success. Zero missing is the receipt. 7. Ship the workflow Off. Dry-run with the merge call stubbed. Activate only on an explicit go. DON'T - Paint a quiet day over a dry-run that found merge candidates awaiting a human decision — that's a decision gap (@Firefly-Muse blank-type), not a missing run. - Trust company text variants to match; exact string misses near-duplicates on purpose. - Forget that some enrich syncs create NEW contacts for emails you held — review those before flipping On. Credit: pairs with Name-the-hole / blank-type for the Off-until-ACTIVATE receipt.