Rule Scenarios
This page is a catalogue. Each entry takes a small set of records, a specific set of merge rules and limit rules, and shows what the identity graph does with them and why. The other pages in this section explain the settings one at a time; this one shows them deciding real outcomes together, including the cases where two configurations that look almost identical give opposite answers.
Read a scenario in five steps. At a glance is the outcome in one line. Setup is the rules, in the same columns the product shows them in — remember that priority 1 is the strongest, because a lower number means a more trusted identifier. Records is the data. Result is the profiles that come out, what the run’s row shows on the graph’s Runs tab, and what the Kept apart table shows when you open a profile. Why is the deciding rule in one paragraph, ending with the setting you would change to get a different answer.
Every scenario has its own heading link, so you can send a colleague a link straight to it — copy the link from the heading’s anchor, for example …/identity-resolution/scenarios/#two-people-one-device. None of these are made-up examples: every scenario on this page is exercised by Zeotap’s automated tests.
Where the numbers appear. Two surfaces report what a run did, and they report different things.
- The graph’s Runs tab has one row per run, with Profiles, Merges (records folded into another record’s profile) and Rows read. Beside the profile count it draws up to two badges: N kept apart in amber, and N anonymous rows assigned in blue. Both are drawn only when the number is above zero, so “nothing was kept apart” looks like no badge at all, never a zero. The row also names what actually ran — full or incremental — with a one-line reason underneath when an incremental request was upgraded to a full rebuild. The badges are counts and nothing more: they never name a value or a family.
- The Kept apart table on a customer’s profile (Customer 360) is the only place a value and a family are named, one sentence per value.
Reading the records. A record is one row from one of your models. Records are labelled u1, r1, x1 and so on; emails are written a@x, b@x; cookies C1, C2; phone numbers in full. A profile is written as the set of records it holds, like {u1, u4}. Where a person is named, Alice is a@x and Bob is b@x. One note on quoting: the product writes a per-profile limit as a profile may hold up to 1 email values — the wording is deliberately fixed so it reads the same at every value of N, and the sentences quoted on this page are quoted exactly as they appear on screen.
Start here if…
| What you are seeing | Scenario |
|---|---|
| Two different people ended up on one profile | Every family unlimited on purpose |
| A run’s kept apart badge changed after a rule edit | The same records with the priorities swapped |
| Anonymous sessions each became a profile of their own | Anonymous activity to no one |
| A person’s newest device stopped joining their profile | Five devices stitch and the sixth does not |
| A value is kept apart and you never configured a limit | Nothing configured and still kept apart |
| A profile is missing browsing you expected it to gather | A value shared by too many records |
The scenarios at a glance.
| Scenario | What it shows |
|---|---|
| Merge rule priority | |
| Two people one device | The shared-device case: a cookie seen with two emails links nobody |
| A record with no email of its own | An anonymous record still joins a known person — a missing value cannot breach a limit |
| A chain through an anonymous pair | Two innocent-looking cookies that together would merge two people |
| The same records with the priorities swapped | Identical data and limits, opposite answer, because priority moved |
| The extra devices stop matching, nobody is un-merged | A strong merge is never undone; the weaker family’s surplus values just stop matching |
| A device limit stops a chain of devices | Two profiles with two cookies each stay two, because joining them would be a third cookie |
| Values one profile may hold | |
| One CRM id per account | An identifier that is never used to merge, but that no profile may hold two of |
| Nothing configured and still kept apart | What a brand-new graph does with no limit rules typed at all |
| Every family unlimited on purpose | The same records with every limit explicitly switched off |
| Five devices stitch and the sixth does not | Device families default to five values per profile, not one |
| Ignore values shared by many records | |
| A value shared by too many records | A placeholder value ignored before any link exists, and why it is not counted as kept apart |
| Shared identifier attribution | |
| Anonymous activity to no one | Every anonymous record on a shared cookie becomes its own profile |
| Anonymous activity to the person seen with it first | The default: the anonymous records join whoever had the cookie first |
| Anonymous activity to the person seen with it last | Identical records, identical rules, the other person |
| An anonymous pair pointing at two people | Anonymous activity that could go to either of two people goes to neither |
| Probabilistic matching with limits | |
| A fuzzy name rule takes no default limit | ”Smith” and “Smyth” are two values, so a limit of one would keep every fuzzy match apart |
| Incremental runs | |
| A limited graph still runs incrementally | A per-profile limit no longer forces a full rebuild every run |
| A shared value drops back under the limit | Delete a record, and the value that was being ignored starts matching again |
| Only some of the records sharing a value changed | The shared-value limit counts every record carrying the value, not just the changed ones |
| A deleted row lets a cookie link again | Removing one record withdraws the reason two people were kept apart |
Merge rule priority
Rules are grouped by priority and run strongest first — and priority 1 is the strongest, because a lower number means a more trusted identifier. After each group runs, every values one profile may hold limit belonging to a family at least as strong as the group that just ran is checked, including that group’s own family, and a merge that would carry a profile past one of those limits is not made. These six scenarios are that sentence, executed.
The shared-device records. Four scenarios below use exactly these four records, so they are printed once here.
| Record | Cookie | |
|---|---|---|
| u1 | a@x (Alice) | C1 |
| u2 | b@x (Bob) | C1 |
| u3 | (none) | C1 |
| u4 | a@x (Alice) | C2 |
Two people one device
At a glance: a cookie seen with two different emails links nobody, and the two people stay two profiles.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 | 100 (default) |
cookie_id | 2 | no limit | 100 (default) |
Shared identifier attribution: no one.
Records. The four shared-device records above.
Result. Three profiles: {u1, u4}, {u2} and {u3}. On the Runs tab the run’s row shows 3 under Profiles with a 1 kept apart badge beside it. No anonymous rows assigned badge appears — with attribution set to no one there was nothing to assign, and a zero badge is never drawn. Open any of the three profiles and the Kept apart table names the cookie: C1 is also used by another person. The email limit (1 per profile) kept them separate.
Why. The email rule runs first: a@x links u1 and u4. The cookie rule runs next: C1 proposes joining u1, u2 and u3, and that profile would hold two distinct emails against a limit of one. Because the proposal came from the weaker family, the two people are kept apart outright rather than the cookie being trimmed back — so C1 links nobody, and u1 and u2 stay two people. u3’s only link was that cookie, and attribution is set to no one, so it resolves alone rather than being guessed onto either person. C1 is still an attribute of all three profiles, and still findable by search. Nothing will merge u1 and u2 while the email limit stands: that is the outcome the limit exists to produce. To give u3’s browsing to one of them instead of leaving it on its own, change shared identifier attribution to the person seen with it first.
A record with no email of its own
At a glance: an anonymous record joins a known person, because a missing value cannot breach a limit.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 | 100 (default) |
cookie_id | 2 | no limit | 100 (default) |
Records.
| Record | Cookie | |
|---|---|---|
| u5 | (none) | C9 |
| u6 | c@x | C9 |
Result. One profile, {u5, u6}. The Runs tab shows 1 under Profiles and no kept apart badge. The Kept apart table is empty on that profile.
Why. The limit counts distinct values, not records and not sides of a merge. C9 spans exactly one email value, c@x, because u5 contributes no email at all. A record with nothing to say about a limited family cannot breach that family’s limit, which is what keeps the ordinary anonymous-to-known stitch — joining anonymous activity onto a known person — working on a graph where every family is limited. This is the control for the scenario above, and there is no setting that changes it: what changes the answer is the data. Give u5 an email of its own and this becomes the shared-device case.
A chain through an anonymous pair
At a glance: two cookies that are each harmless alone would together put two people on one profile, so both are kept apart.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 | 100 (default) |
phone | 2 | no limit | 100 (default) |
cookie_id | 3 | no limit | 100 (default) |
Records.
| Record | Phone | Cookie | |
|---|---|---|---|
| r1 | a@x | (none) | C6 |
| ry1 | (none) | 5550001 | C6 |
| ry2 | (none) | 5550001 | C7 |
| r3 | b@x | (none) | C7 |
Result. Three profiles: {r1}, {ry1, ry2} and {r3}. The Runs tab shows 3 under Profiles with a 2 kept apart badge — both C6 and C7. On r1’s profile the Kept apart table reads C6 is also used by another person. Joining would put more than 1 email values on one profile. That second sentence is the one reserved for a conflict reached through another merge rather than directly.
Why. Neither cookie is a bridge on its own. C6 touches one identified person and the anonymous pair the phone merged; C7 touches the other identified person and the same pair. Individually each is innocent, and only together do they put a@x and b@x on one profile. So after each group of rules runs, the run checks the group of records as a whole and not one value at a time — and when the whole group is over the limit it stops every value of that group of rules, including the ones that were individually harmless. That is deliberately conservative: the alternative is choosing which of two symmetric cookies to sacrifice, which is a guess, and would make the result depend on the order records happen to arrive in. The anonymous pair stays merged, because the phone that joined it is stronger than the cookies and was never in question. To let the cookies link, raise email to 2 under values one profile may hold: with two emails allowed on one profile the chain no longer breaches anything, and all four records become one profile.
The same records with the priorities swapped
At a glance: the same four records and the same limit, with the two priority numbers exchanged, give one merged profile instead of three.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
cookie_id | 1 (strongest) | no limit | 100 (default) |
email | 2 | 1 | 100 (default) |
This is Two people one device with the two priority numbers exchanged, and nothing else changed.
Records. The four shared-device records above.
Result. One profile, {u1, u2, u3, u4}, holding two emails. The Runs tab shows 1 under Profiles and no kept apart badge. The Kept apart table is empty.
Why. cookie_id is now the strongest family in the graph, so its merge is the one no weaker family’s limit may veto. When the email rule runs, a@x pulls u4 in as well, and the profile then holds two email values against a limit of one — so the limit does the only other thing it can do: it trims the profile back to one matchable email. a@x stays matchable and b@x is set aside, able to pull nothing further in. Values are ranked by when they were first seen, and where that ties — as it does here, because all four records carry the same timestamp — by the record that appears first in the source, which is why a@x is the one that survives. Nothing is un-merged and nothing is recorded as kept apart. To get the three-profile answer back, put email above cookie_id again: priority, not the limit, is the setting that decides this, which is why priority order is worth reviewing before the first per-profile limit is set.
The extra devices stop matching, nobody is un-merged
At a glance: a strong merge is never undone; the weaker family’s surplus values simply stop matching, and nothing is reported as kept apart.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 | 100 (default) |
cookie_id | 2 | 2 | 100 (default) |
Records.
| Record | Cookie | |
|---|---|---|
| r10 | d@x | C10 |
| r11 | d@x | C11 |
| r12 | d@x | C12 |
| r13 | d@x | C13 |
| r14 | (none) | C10 |
| r15 | (none) | C13 |
Result. Two profiles: {r10, r11, r12, r13, r14} and {r15}. The Runs tab shows 2 under Profiles and no kept apart badge. The Kept apart table is empty on both profiles.
Why. The email rule legitimately merges r10 through r13 on d@x, and that profile now holds four cookie values against a cookie limit of two. A merge proposed by a stronger identifier is never rolled back by a weaker family’s limit, so nothing comes apart. Instead the cookie rule trims: two values stay matchable and the other two are set aside. Which two stay is decided by when each value was first seen; these four records carry the same timestamp, so the tie is settled by the record that appears first in the source, and C10 and C11 are the survivors. r14 joins through C10. r15 does not join through C13 — that value is still on r13’s record and still on the profile’s golden record, it simply cannot pull anything further in. Nothing is reported as kept apart, because nothing was kept apart: a trimmed identifier stopped matching, it did not keep two people separate. To let r15 join too, raise cookie_id to 4 under values one profile may hold.
A device limit stops a chain of devices
At a glance: two profiles holding two cookies each stay two profiles, because joining them would be a third cookie — and the family that objects is the one proposing the merge.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | no limit | 100 (default) |
cookie_id | 2 | 2 | 100 (default) |
Records.
| Record | Cookie | |
|---|---|---|
| a1 | e@x | C20 |
| a2 | e@x | C21 |
| b1 | f@x | C21 |
| b2 | f@x | C22 |
Result. Two profiles: {a1, a2} and {b1, b2}. The Runs tab shows 2 under Profiles with a 1 kept apart badge. Which value and which limit is only visible per customer: open either profile and the Kept apart table names cookie_id itself as the reason — C21 is also used by another person. The cookie_id limit (2 per profile) kept them separate.
Why. Email is unlimited here, so it merges each pair freely, and each resulting profile holds two cookie values, which the limit allows. C21 then proposes joining the two profiles, and the union would hold three cookie values. A group of rules is checked against every limit belonging to a family at least as strong as itself — including its own family’s limit — so the family proposing a merge is not exempt from the constraint it declares. This is what makes a per-profile limit useful on a device family: it bounds how far a chain of device values may stitch, even when no stronger family objects. Raise cookie_id to 3 under values one profile may hold and the two profiles become one.
Values one profile may hold
This limit says a profile may hold at most N distinct values of one family. Every enabled, deterministically matched family has one by default. These four scenarios are about where that default comes from, how to opt out of it, and what the limit does on a family that never merges anything.
One CRM id per account
At a glance: an identifier with no merge rule at all still keeps records apart, and it does so from the very first group of rules.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | no limit | 100 (default) |
cookie_id | 2 | no limit | 100 (default) |
user_id | no merge rule at all | 1 | 100 (default) |
Records.
| Record | User id | Cookie | |
|---|---|---|---|
| x1 | uid1 | g@x | C30 |
| x2 | uid2 | g@x | C31 |
| x3 | (none) | g@x | (none) |
Result. Three profiles: {x1}, {x2} and {x3}. The Runs tab shows 3 under Profiles with a 1 kept apart badge. Which value was kept apart is only visible per customer: open x1’s or x2’s profile and the Kept apart table names the email g@x, with user_id as the family whose limit applied.
Why. This is the “one CRM id per account” rule. user_id never links anything, because it has no merge rule; it only ever keeps things apart. A family that only constrains — a limit with no merge rule, so it can never join two records — is stronger than every merge rule by definition, so it can veto from the very first group of rules, and here it does: the shared email g@x would put uid1 and uid2 on one profile. Note what happens to x3. It carries the same email and no user id of its own, so joining it would breach nothing — and it still resolves alone, because keeping a value apart removes that value’s power to link at all, not just its power to join the two records that caused the objection. That is what makes the result the same however the records are ordered. To let g@x link again, delete the user_id limit rule, or set its values one profile may hold to 0, which means no limit.
Nothing configured and still kept apart
At a glance: a brand-new graph with no limit rules typed at all gives exactly the same three profiles as one where the email limit was typed in by hand.
Setup. No limit rules at all — nothing typed, nothing saved, the state a graph is in the moment its merge rules are created. Both families therefore take their defaults.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 (default) | 100 (default) |
cookie_id | 2 | 5 (default) | 100 (default) |
Records. The four shared-device records above.
Result. Three profiles: {u1, u4}, {u2} and {u3} — identical to Two people one device, where the email limit was typed in by hand. The Runs tab shows 3 under Profiles with a 1 kept apart badge. Open any profile and the Kept apart table names email as the family whose limit applied.
Why. Every enabled, deterministically matched family arrives with a per-profile limit in effect: 1 for an identity-bearing family such as email, and 5 for a device-class family such as cookie_id. So email is limited to one value per profile without anyone configuring it, and C1 keeps the two people apart exactly as before. It is worth reading which family the table names: C1’s records span two email values against a limit of one, and two cookie values against a limit of five. Only the email limit is breached, so only email is reported — a run that applied the identity default of 1 to the device family would report the same value twice, the second time for a reason that is not real. You get this outcome without typing anything. To get the opposite one, where nothing is kept apart, every enabled family has to carry an explicit 0 — which is the next scenario, Every family unlimited on purpose.
Every family unlimited on purpose
At a glance: the same four records become one profile holding two emails, but only when every family is explicitly switched off.
Setup. Both families explicitly set to no limit, by typing 0 into the family’s A profile may hold up to box.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | no limit (explicit 0) | 100 (default) |
cookie_id | 2 | no limit (explicit 0) | 100 (default) |
Records. The four shared-device records above.
Result. One profile, {u1, u2, u3, u4}, holding two emails. The Runs tab shows 1 under Profiles and no kept apart badge. The Kept apart table is empty.
Why. This is the opt-out, and it is deliberately something a graph has to say rather than something it can drift into. The behaviour it restores — links are a set, priority order is unobservable, every chain of shared identifiers merges — is reached only when every enabled family carries an explicit 0. Leaving email at its default of 1 while setting cookie_id to 0 gives the previous scenario’s answer, not this one. Read the three scenarios together and they are the whole of the change: the same records resolve into three profiles when nothing is configured, three profiles when a limit is typed, and one profile only when the limits are deliberately switched off. To step back from the opt-out one family at a time, clear that family’s box: blank means “use the default”, which is what the greyed-out number in the box shows you.
Five devices stitch and the sixth does not
At a glance: one person’s first five devices gather their anonymous browsing and the sixth does not, because a device family’s default limit is five values per profile.
Setup. No limit rules configured, so both families take their defaults.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 (default) | 100 (default) |
cookie_id | 2 | 5 (default) | 100 (default) |
Records. One person, six devices, and three anonymous sessions. “Seen” is when each record was last observed.
| Record | Cookie | Seen | |
|---|---|---|---|
| d1 | a@x | K1 | January |
| d2 | a@x | K2 | February |
| d3 | a@x | K3 | March |
| d4 | a@x | K4 | April |
| d5 | a@x | K5 | May |
| d6 | a@x | K6 | June |
| a1 | (none) | K1 | July |
| a5 | (none) | K5 | July |
| a6 | (none) | K6 | July |
Result. Two profiles: {d1, d2, d3, d4, d5, d6, a1, a5} and {a6}. The Runs tab shows 2 under Profiles and no kept apart badge. The Kept apart table is empty on both profiles.
Why. The email rule merges the six identified records on a@x — one email value, within the limit. That profile now holds six cookie values against the device default of five, so the cookie rule trims by when each value was first seen: K1 through K5 stay matchable and K6 is set aside. The anonymous sessions on the first and fifth devices join; the one on the sixth does not. This is why device-class families do not take the identity default of 1: at a limit of one, only K1 would survive the trim and the person’s second device could never pull its own anonymous browsing in. It is also why they are not left unlimited: five is well above a real person’s device count and far below the point where a shared kiosk fuses a crowd. Nothing is kept apart, because a limit on the family that is proposing the merge trims rather than keeping records separate. To let the sixth device’s session join too, raise cookie_id to 6 under values one profile may hold.
Ignore values shared by many records
The other limit-rule setting counts the opposite thing: how many distinct records may share one identifier value before that value stops being treated as an identity signal. It applies before any link exists, which changes what you see on the Runs tab.
A value shared by too many records
At a glance: a cookie carried by too many records is ignored before it can propose anything, so three profiles result and the run reports nothing kept apart.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 | 100 (default) |
cookie_id | 2 | no limit | 2 |
Two is far below the real default of 100, and is used here so that three records are enough to trip it.
Records.
| Record | Cookie | |
|---|---|---|
| s1 | a@x | C50 |
| s2 | b@x | C50 |
| s3 | c@x | C50 |
Result. Three profiles: {s1}, {s2} and {s3}. The Runs tab shows 3 under Profiles and no kept apart badge. The Kept apart table still explains the separation, but only on the profiles at the two ends of the group — here {s1} and {s3} — where it reads C50 is shared by more than 2 records, so it was not used for matching. The profile in the middle, {s2}, shows nothing: an ignored value is recorded once, against the first and the last record that carried it, because writing a row for every record sharing it is exactly the work this limit exists to avoid.
Why. The two limits compose, and this one goes first. The shared-value limit is applied while links are being built, so C50 produces no link at all and there is never a merge for the per-profile limit to decline. That is exactly why the run’s kept-apart count stays at zero while the ignored value is still recorded and still shown on a profile: that count is of merges the rules had to decline, and an ignored value never proposed one. The three profiles here are the same three a per-profile limit would have produced, and the Kept apart table is the only place the difference is visible — which matters, because the fix is different. An over-shared value is a data problem (a placeholder, a bot, a kiosk); two people kept apart by a per-profile limit is usually a genuinely shared device. To make C50 match again, raise ignore values shared by more than above the number of records carrying it — though on a real graph the better move is usually to fix the value at source.
Shared identifier attribution
Keeping two people apart settles the question of the two people. It opens a second question about every other record carrying the shared identifier: the page views with a cookie and nothing else. The next three scenarios are the same records under each of the three answers, and a fourth adds the case where the answer cannot be given at all.
The shared records are one cookie seen with two identified people — Alice (a@x) and Bob (b@x) — and two anonymous records. These are not the shared-device records used earlier: here both u3 and u4 are anonymous. email is priority 1 with a limit of one value per profile; cookie_id is priority 2 with no per-profile limit; both families use the default shared-value limit of 100. In all three answers C1 links nobody and the run reports 1 kept apart — attribution never merges the people a limit kept apart, it only decides where the traffic belonging to neither of them is credited.
| Record | Cookie | Cookie seen with this record | |
|---|---|---|---|
| u1 | a@x (Alice) | C1 | 1 January |
| u2 | b@x (Bob) | C1 | 1 March |
| u3 | (none) | C1 | 1 April |
| u4 | (none) | C1 | 1 May |
Anonymous activity to no one
At a glance: every anonymous record on the shared cookie becomes a profile of its own, and none of the browsing is credited to a person.
Setup. The shared records above, with shared identifier attribution set to no one.
Result. Four profiles: {u1}, {u2}, {u3} and {u4}. The Runs tab shows 4 under Profiles with a 1 kept apart badge, and no anonymous rows assigned badge — nothing was assigned, and a zero badge is never drawn. On u3’s profile the Kept apart row for C1 carries the line Anonymous activity stayed on its own, by this graph’s setting.
Why. Nothing is guessed. Every record whose only link was C1 becomes its own single-record profile, which is the conservative answer and the right one where anonymous activity must never be credited to a named individual. The cost is visible in the profile count: a household’s browsing is scattered across as many profiles as there are anonymous records. To gather that browsing onto a person instead, change shared identifier attribution to the person seen with it first or the person seen with it last.
Anonymous activity to the person seen with it first
At a glance: the anonymous records join Alice, who had the cookie first, and Alice and Bob are still two profiles.
Setup. The same records, with shared identifier attribution set to the person seen with it first. This is the default on a new graph.
Result. Two profiles: {u1, u3, u4} (Alice) and {u2} (Bob). The Runs tab shows 2 under Profiles, a 1 kept apart badge and a 2 anonymous rows assigned badge. Open Alice’s profile and the Kept apart row for C1 reads C1 is also used by another person. The email limit (1 per profile) kept them separate., with the line Anonymous activity went to this profile. Open Bob’s and the same row reads Anonymous activity went to another profile, linked, so the two views agree about where the traffic went.
Why. u3 and u4 carry no email of their own, so giving them to Alice adds records and no new email value: the profile still holds one email, the limit still holds, and the two people are still two people. Ranking is by when the cookie was seen with each person — January for Alice, March for Bob — not by how old either profile is. That distinction is load-bearing: both profiles here were created by the same run, so ranking by profile age would tie, and this answer and its opposite would come out the same. To send the browsing to Bob instead, change shared identifier attribution to the person seen with it last.
Anonymous activity to the person seen with it last
At a glance: identical records and identical rules, and the anonymous browsing lands on Bob instead of Alice.
Setup. The same records again, with shared identifier attribution set to the person seen with it last.
Result. Two profiles: {u1} (Alice) and {u2, u3, u4} (Bob). The Runs tab shows 2 under Profiles, a 1 kept apart badge and a 2 anonymous rows assigned badge. On Bob’s profile the C1 row carries the line Anonymous activity went to this profile; on Alice’s, Anonymous activity went to another profile.
Why. Identical records, identical rules, the same two people kept apart, and the anonymous browsing lands on Bob instead of Alice — because Bob was seen with C1 in March and Alice in January. Choose this where devices genuinely change hands, such as a shared workstation or a refurbished handset pool, and the first-seen default where the person who set the device up is the one whose device it mostly is. To send the browsing back to Alice, change shared identifier attribution to the person seen with it first, which is the default.
An anonymous pair pointing at two people
At a glance: anonymous activity that could belong to either of two people is given to neither, while a record that reaches only one person is still assigned.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 | 100 (default) |
phone | 2 | no limit | 100 (default) |
cookie_id | 3 | no limit | 100 (default) |
Shared identifier attribution: the person seen with it first.
Records. Two shared cookies, each seen with two identified people, and one anonymous pair joined by a phone that sits on both cookies.
| Record | Phone | Cookie | Seen | |
|---|---|---|---|---|
| u1 | a@x | (none) | C1 | 1 January |
| u2 | b@x | (none) | C1 | 1 March |
| u3 | (none) | (none) | C1 | 1 April |
| u5a | (none) | 5559 | C1 | 1 May |
| u5b | (none) | 5559 | C2 | 1 May |
| u6 | c@x | (none) | C2 | 1 February |
| u7 | d@x | (none) | C2 | 1 June |
Result. Five profiles: {u1, u3}, {u2}, {u5a, u5b}, {u6} and {u7}. The Runs tab shows 5 under Profiles, a 2 kept apart badge and a 1 anonymous rows assigned badge. On the anonymous pair’s profile the Kept apart table names both cookies, each reading C1 is also used by another person. The email limit (1 per profile) kept them separate. and C2 is also used by another person. The email limit (1 per profile) kept them separate. — for any one value the table reports the reason that kept two people apart, because that is the headline. What happened to the pair itself is on the same row, in its anonymous-activity line: it stayed on its own.
Why. C1 kept u1 and u2 apart; C2 kept u6 and u7 apart. Under first-seen, C1 points at u1 and C2 points at u6. The phone 5559 merges u5a and u5b into one anonymous pair that sits on both cookies, so attaching it would join u1’s profile to u6’s — bridging two profiles the limits had just kept apart. Neither cookie’s anonymous activity is given to anyone, and the pair stays on its own. u3, which carries only C1 and reaches only one person, is still given to u1, which is why the count is one row assigned and not zero: the withdrawal is per record, not per setting. No attribution setting changes this pair’s outcome; only removing the ambiguity in the data does — one cookie per person, or a phone that is not shared.
Probabilistic matching with limits
A per-profile limit counts distinct values of a family on one profile, which only means something for a family whose rule merges records on equal values. Probabilistic matching merges records whose values are merely similar, so every merge it makes puts two different values of its own family on one profile.
A fuzzy name rule takes no default limit
At a glance: a similarity-matched family is left unlimited by default, because “Smith” and “Smyth” are two values and a limit of one would keep apart every merge the rule exists to make.
Setup. A probabilistic graph, with four merge rules. Two models contribute records: a people table and a contacts table.
| Family | Priority | Matching | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|---|
email | 1 (strongest) | deterministic | no limit (explicit 0) | 100 (default) |
name | 2 | probabilistic (fuzzy name) | no limit (no rule configured) | 100 (default) |
ip_address | 3 | probabilistic | no limit (explicit 0) | 100 (default) |
address | 4 | probabilistic | no limit (no rule configured) | 100 (default) |
Two families here carry no limit rule at all — name and address — and both are probabilistic, which is the point of the scenario.
Records.
| Record | Model | Surname | Postcode | IP | |
|---|---|---|---|---|---|
| p1 | people | ann@x | Smith | 10001 | 10.0.0.1 |
| p2 | people | bob@x | Smyth | 10001 | 10.0.0.1 |
| p3 | people | cat@x | Jones | 10001 | 10.0.0.2 |
| p4 | people | dan@x | Smithe | 99999 | 10.0.0.1 |
| c1 | contacts | ann@x | Smythe | (none) | (none) |
Result. The fuzzy name rule links Smith, Smyth, Smithe and Smythe to each other across both models — every pair among p1, p2, p4 and c1 — and links none of them to Jones. Those four resolve into one profile carrying four different surname spellings and three different email addresses; p3 stays on its own. The Runs tab shows 2 under Profiles and no kept apart badge: with no per-profile limit anywhere in this graph, there was no merge for a limit to decline. The Kept apart table is empty on both profiles.
Why. A family whose merge rule is probabilistic takes no default per-profile limit, and the reason is visible in the records: a limit of one name value per profile would keep apart every merge the rule exists to make, because joining “Smith” to “Smyth” is precisely joining two different values. The same argument applies to the emails here — the profile ends up holding three of them — which is why a probabilistic graph’s deterministic families are usually set to no limit as well. Two things follow. A family with a disabled merge rule, or none at all, is left unlimited for a different reason: it contributes no matches, so a default there would be a pure constraint nobody asked for. And an explicit limit still applies to any family, probabilistic ones included: to bound how many surname spellings one profile may accumulate, type a number into name’s A profile may hold up to box. It is only the default that is withheld.
Incremental runs
Nothing in this section is a setting. These four scenarios exist so you can trust the numbers an incremental run reports: the profiles and the kept apart count are the same ones a full rebuild would have produced. If a run’s numbers ever look wrong after an incremental update, Rebuild from scratch is the check — and these scenarios say what it should agree with.
A per-profile limit used to force every run to be a full rebuild, because admitting a value is not a local decision when a limit is in play. It no longer does. Instead the run widens the set of changed records until nothing outside that set can affect the answer, then replays the whole rule sequence over that subset alone.
Each scenario carries sixteen extra records that share nothing with anything — one unique email each — so that the widened set stays a minority of the graph. That matters: a run whose widened set grows past half the graph falls back to a full rebuild, and on a four-record graph that set is always everything. Where a scenario below says those sixteen profiles keep the ids they already had, that is the visible sign the run really did recompute only a subset.
A limited graph still runs incrementally
At a glance: a graph with a per-profile limit updates incrementally, reads a handful of records rather than all twenty, and still reports the same 1 kept apart.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 | 100 (default) |
cookie_id | 2 | no limit | 100 (default) |
Run mode automatic.
Records and change. The four shared-device records, plus the sixteen unrelated ones. Run 1 is a full rebuild. Then u4 is updated, and the graph is asked for a run with Run now.
Result. The same three profiles as before, plus sixteen single-record profiles. The run’s row on the Runs tab says incremental, with no full-rebuild reason underneath, and carries a 1 kept apart badge. Rows read is a handful — the changed record and the records reachable from it — not the twenty records in the graph, and every one of the sixteen untouched profiles keeps the profile id it already had.
Why. The set the run has to consider is u1 through u4: u1 because it shares the email a@x with the record that changed, and u2 and u3 because whether C1 keeps anyone apart has to be decided again, and deciding it needs every record carrying that cookie. The whole rule sequence — priority order, trimming, the shared-value limit, the whole-group check, attribution — is replayed over those records only. The kept-apart count survives the shortcut, which is the point: incrementality does not cost you the number you watch. There is no setting for this; it is how incremental runs work.
A shared value drops back under the limit
At a glance: deleting one record takes a cookie back under the shared-value limit, and the next incremental run merges the two records it had been keeping apart.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 | 100 (default) |
cookie_id | 2 | no limit | 2 |
Run mode automatic. No record here has an email, so the email limit never fires; it is what puts the graph on the limited path at all.
Records and change. x1, x2 and x3 all carry C1, plus the sixteen unrelated records. Run 1 is a full rebuild. Then x3 is deleted, and the graph is asked for an incremental run.
Result. After run 1, three separate profiles: C1 is carried by three records against a limit of two, so it links nothing, and the Kept apart table records it as shared by more than 2 records. After the incremental run, {x1, x2} is one profile and the Kept apart table is empty. A full rebuild afterwards over the same data gives the same answer.
Why. Deleting x3 takes C1 down to two carriers, which is under the limit, so a full rebuild would merge x1 and x2 — and an incremental run therefore has to as well, or “incremental gives the same answer as full” is simply false. What makes it true is that the run re-decides the shared-value limit for every value the changed records touch before it widens the set. Without that step the case is unrecoverable: an ignored value has no link to follow, and after the delete no changed record even mentions it. There is no setting for this; it is how incremental runs work.
Only some of the records sharing a value changed
At a glance: the shared-value limit is counted over every record carrying the value, not just the ones a run happened to read, so a partially-changed group stays apart.
Setup. Identical to the scenario above.
Records and change. x1, x2 and x3 all carry C1, plus the sixteen unrelated records, but x3 was last modified well before the others. Run 1 is a full rebuild. Then x1 is touched, so the changed set is x1 and x2, while x3 was last modified before the point the previous run reached and this run does not re-read it.
Result. All three stay apart: {x1}, {x2} and {x3}. C1 is still recorded as shared by more than 2 records. The run’s row on the Runs tab says incremental, with no full-rebuild reason underneath.
Why. This is the converse of the previous scenario and the trap it sets. The restricted rebuild sees exactly two records carrying C1 — and counting the limit over those two would put C1 under it and merge x1 and x2, an answer no full rebuild ever gives. The limit is counted over every record in the graph that carries the value, not over the records the run happened to read, and the outcome is re-recorded by the run rather than copied forward from the last one, because the record the old copy was written against is exactly the one that changed. There is no setting for this; it is how incremental runs work.
A deleted row lets a cookie link again
At a glance: deleting one record removes the reason two people were kept apart, and the cookie that had been keeping them apart links them on the next incremental run.
Setup.
| Family | Priority | Values one profile may hold | Ignore values shared by more than |
|---|---|---|---|
email | 1 (strongest) | 1 | 100 (default) |
cookie_id | 2 | no limit | 100 (default) |
Shared identifier attribution: no one. Run mode automatic. This is one step of a longer chain of incremental runs, so the starting point is whatever the previous run left behind.
Records and change. Before this run: {u1, u2, u3, u4} is one profile carrying a@x; u8 and u9 are two separate profiles, kept apart because C2 is carried by u4 (a@x) and u9 (b@x). Then u4 is deleted.
| Record | Cookie | |
|---|---|---|
| u1 | a@x | C1 |
| u2 | a@x | C1 |
| u3 | (none) | C1 |
| u4 | a@x | C2 |
| u8 | (none) | C2 |
| u9 | b@x | C2 |
Result. Two profiles from these records: {u1, u2, u3} and {u8, u9}. The run’s row on the Runs tab says incremental and carries no kept apart badge; the profile count drops by one against the previous run. The Kept apart table is empty on both profiles. The merged profile keeps the older of the two profile ids — you can see it in the profile header on the Profiles page — and the sixteen unrelated profiles do not move.
Why. u4 was the only record putting a@x on C2. With it gone, C2 is seen with one email only, the reason those two were kept apart disappears, and the cookie links again. This is the case that proves a deleted record is treated as a change: it is inside the set the run has to consider, because the profile it belonged to has to be recomputed without it, and outside everything the run rebuilds. That the older profile id survives the merge matters downstream — a profile id is the key your destinations already hold, so a merge that retired the older one would look to every destination like a delete followed by an insert. There is no setting for this; it is how incremental runs work.
Related pages
- Merge Rules — what a rule is, and what priority does
- Limit Rules — the two limits, their defaults, and how to choose numbers
- Probabilistic Matching — scoring functions and confidence
- Running Resolution — run modes, run metrics, and what each run reports
- Profiles (Customer 360) — reading merge lineage and the Kept apart table for one customer