Resolve identity → map referrals → activate cohorts. Built bottom-up from a synthetic 20-person book to answer one question: who actually drives your business?
Synthetic data throughout: a thinking artifact, not a Cloze export. The engine is ~1,160 lines of stdlib Python (resolver → referral graph → cohort analytics), plus ~470 more for the firm layer at the end; this page renders its JSON.
It starts with capture and ends with proof. Layer 1 is where the data comes in; everything downstream depends on Layer 2 getting identity right first.
Before any graph or analytics, you have to know who is who. A 59-row import (nicknames, diacritics, Jr/Sr, spouses sharing an inbox, office lines, junk) resolved into clean entities. (This import fixture is the resolver's own stress set, separate from the 20-person referral book the later layers run on.) The design that matters: assisted, banded, evidence-rich, non-destructive. It merges only what's certain and hands the gray zone to a person with the reasons attached.
Every call carries its evidence. Names are synthetic; the reasoning is the product.
The import above is realistic mess; this second fixture is engineered traps. Every decision below is the engine's current output on it, and the pattern is one rule applied everywhere: when the evidence cuts both ways, ask a person instead of guessing.
Still missed, and named: a flipped name order (Zhang Wei / Wei Zhang) never even becomes a candidate pair (needs its own blocking pass; parked), and a nickname pair with two fully-populated records and zero shared identifiers scores below the review bar; the real fix there is multi-value identifiers, on the roadmap. Score on this set: auto-merge precision 1.00, recall including review 0.71. Worse numbers than the clean import, on purpose; publishing them anyway is the point.
On the honest edges: measured against Cloze's real importer (a 59-row live test, July 2026), the overlap is real: both merge on a shared email or typed phone when the names agree. The differences are at the edges. An importer can't reach a duplicate with no shared identifier (an email-only record and a phone-only record for the same person), and when a shared identifier meets a changed surname it drops the second record as invalid; this engine links the first case and routes the second to review, and it never discards data. Candor cuts both ways: the adversarial set above initially cracked this auto band too, fixed with guards that demote to review rather than guess. A household split is a flag, not an auto-action: we surface the two people and bundle the shared contact info, but assigning which phone or email belongs to whom is a human call (and cleanly needs multi-valued contact fields, on the roadmap). And middle names compare on the first token, so “Chad Michael” still matches “Chad” rather than mis-flagging as a household.
A flat “referred by” field stops at one hop. This traces credit multi-hop, decayed per hop, so the person who seeded a chain gets seen, even when they closed nothing themselves. Nodes are colored by referral archetype (a behavioral attribute Cloze doesn't segment on) and sized by the volume they touch. Hollow rings closed $0 of their own; a dashed ring marks an outlier whale deal. Flip the toggle and watch the hidden originators grow.
| Person | Direct referrals | Influence volume | Influence GCI | Mix L/B/D | Infl ÷ direct |
|---|---|---|---|---|---|
| Wendy Hart | 3 | $3.35M | $92K | 2L/1B/1D | 1.0× |
| Greg Donovan | 5 | $1.94M | $54K | 2L/4B/0D | 1.1× |
| Karen Whitfield hidden originator | 2 | $1.83M | $47K | 3L/5B/1D | 2.8× |
| Aisha Patel hidden originator | 1 | $1.37M | $37K | 3L/4B/0D | 3.4× |
| Luis Romero hidden originator | 1 | $1.10M | $24K | 3L/4B/1D | 2.6× |
| Raj Patel | 2 | $985K | $29K | 0L/5B/0D | 1.0× |
| Marcus Lin | 1 | $205K | $6K | 0L/1B/0D | 1.0× |
$2.83M of originated volume sits with Karen Whitfield, Aisha Patel, Luis Romero, invisible to a one-hop field. Influence is an influence score, not accounting (credit duplicates up the chain). A listing self-markets and spins off buyer leads, so the mix matters as much as the dollar figure.
Transactions model as deals + sides: a deal is one property; each side ties a contact to it as a buy or a sell, carrying that side's GCI. A deal with both sides = dual agency (you repped buyer and seller, one transaction). So the book classifies every client and flags dual deals, not just a single buy/sell tag, and volume counts the property once even when you earn both commissions.
Cloze segments by stage and lifecycle. None of them segment by referral
behavior. Five bins, tendencies not verdicts: a lens. And they're living labels, recomputed
from current activity every run: tag a One-and-done as a referrer on your next deal and she moves toward
Connector; as the book's average sale price rises, a Harpoon slides toward Connector. Not a permanent stamp.
Property-sourced leads aren't referral-archetyped; they have no referral behavior to
classify, so they sit out of these bins (see Lead sources below).
The graph above is mostly your personal-derived engine: people sending people. But a property is a lead-gen node too: a listing's open house spawns buyers who often close elsewhere, and a sale farms its neighbors. Those are property-derived leads. In the graph they hang off the source listing's owner on a dashed line: a real link, but not a referral, so it carries no referral credit. One book, two engines; in production the property side hydrates from the MLS, another source plugging into the same resolved-entity core (Layer 1).
Properties and people aren't separate graphs; they're one. Here a single listing is the hub: a referral brought you the seller, and the listing brought you more buyers. Both engines, one resolved property.
Cloze's promise is “systematic, not aspirational.” This is the layer that proves it, rolling deals up to the cohorts that touched them, then measuring whether a deliberate touch actually moved the needle.
| Cohort | Members | Deals closed | Touched volume | % of book | Mix L/B/D |
|---|---|---|---|---|---|
| Sphere/Partner | 3 | 0 | $7.03M | 86% | 5L/11B/1D |
| Past Client | 13 | 16 | $6.54M | 80% | 5L/10B/1D |
| Cold List | 4 | 2 | $4.33M | 53% | 2L/6B/1D |
| Christmas Card List | 7 | 6 | $3.10M | 38% | 3L/5B/1D |
A Sphere/Partner cohort that closed $0 itself is upstream of 100% of GCI: what the referral graph shows, now in cohort form. % of book runs past 100% because people belong to several lists; honest double-count, not a bug.
A brokerage promises the client a relationship with the firm, but every book is isolated, rightly. So when the roster changes, the firm forgets. The question that answers is the one this whole project started from: how does a brokerage pool its agents' data without breaking the per-user isolation and the real-mailbox moat?
Each agent's book stays isolated. The pool is a read-mostly governed projection on top: a resolved customer graph plus firm analytics. Inside one book, resolution merges duplicates. Across books it links and never merges, because two agents who both know a client each keep their own relationship and history, and the firm only learns “same human.” That is why Dan's word is customer data pool and not “merge everyone's CRM.”
Flip the toggle. 1 of 3 agents departs and the graph routes each orphaned client to the agent who already has a link, not round-robin. 3 of 4 land on a real relationship path, and the one that cannot is flagged as a load-balanced fallback rather than dressed up as a match.
Co-owned GCI is counted once, in the owning book, and the co-ownership is flagged rather than double-billed. Tom Gallagher earned $10K of influence inside Maria Santos's book by handing over one buyer, which a per-agent production ranking cannot see. Cross-agent links are assisted and banded: a human confirms, and a name-only match is review, never auto.
Brokers buy the outcome. Governance is why the agents say yes, and it is the whole difference between this and the surveillance version anyone could build. So the visibility rules ship opinionated: one matrix, not a matrix builder.
| What pools | The agent sees | The broker sees | Another agent sees |
|---|---|---|---|
| Records, notes, message content, the mailboxThe moat. This is the row that never moves. | Everything, unchanged | Nothing | Nothing |
| Resolved identity links (“same human”)Link, never merge. A false link leaks a client, so name-only is review. | Own book, plus an unnamed co-ownership flag | Aggregate collision counts | Nothing until both agents confirm |
| Transaction facts: deals, sides, GCIThe firm already owns these. Pooling them breaks nothing. | Own production, plus influence they earned elsewhere | Firm rollups by agent and by cohort | Nothing |
| Individual activity: calls, opens, cadenceAggregates to the broker are fine. Individual activity is surveillance. | Their own | Never, at any granularity | Never |
| At the orphan momentThe one moment naming is on: the client needs an agent today. | Sees their book leave with them, in writing | A ranked reassignment with the reason shown | The named client, on assignment |
Agreed while relations are good, so offboarding stops being a fight, and it cuts both directions: the same architecture is the arrival-side answer to a recruit asking what the firm will be able to see. Every surface answers to five checks:
Synthetic 3-agent fixture, ~470 lines of stdlib Python on top of the same resolver above; both this panel and the companion render one JSON the engine emits. Production wires the identical engine to the real per-agent books.