Interactive Demo
AtomicAttributeGraph Explorer
Walk through the Bronze → Silver → Gold pipeline for three scenarios. See exactly why field-level edge timestamps produce a different — and correct — resolved profile.
1 — Bronze: Raw events2 — Silver: graph.nodes + graph.relationships3 — Gold: Resolved profile
Select a demo scenario
C001 — Santosh P. — Salesforce performs a full record sync at 09:05 — the record timestamp is newer than Shopify's, so a traditional CDP would overwrite the correct phone number. AAG resolves phone to Shopify because the field-level edge timestamp is what matters.
Nodes:customercompanyassetemailphonecitySources:salesforcesapsegment
1Bronze — Raw EventsRecord-level timestamps are the source of truth here. This is the problem.
| Source | Field | Value | Timestamp | Type | Note |
|---|---|---|---|---|---|
| salesforce | alex@example.demo | 09:05 | record | ||
| salesforce | phone | +49 111 000 0000 | 09:05 | record | Record timestamp. Field unchanged since 2024. |
| salesforce | city | Berlin | 09:05 | record | Record timestamp. Field unchanged since 2023. |
| shopify | phone | +49 987 654 3210 | 09:00 | field | Genuine field update today. |
| segment | alex@example.demo | 08:45 | field |
2aSilver — graph.nodesOne row per distinct value. Entity nodes carry no value.
| node_id | node_type | value | checksum | properties |
|---|---|---|---|---|
| cust-c001 | customer | — | — | — |
| val-em-001 | alex@example.demo | a3f9… | — | |
| val-ph-001 | phone | +49 111 000 0000 | b12c… | — |
| val-ph-002 | phone | +49 987 654 3210 | c45d… | — |
| val-ct-001 | city | Berlin | d78e… | — |
2bSilver — graph.relationshipsEdges carry field-level timestamps, not record-level ones.
| source_node_id | rel_type | target_node_id | updated_at | source | is_current | resolution | note |
|---|---|---|---|---|---|---|---|
| cust-c001 | HAS_EMAIL | val-em-001 | 09:05 | salesforce | TRUE | ✓ wins | |
| cust-c001 | HAS_PHONE | val-ph-001 | 09:05 | salesforce | TRUE | ✗ loses | Loses — field-level ts older than Shopify. |
| cust-c001 | HAS_PHONE | val-ph-002 | 09:00 | shopify | TRUE | ✓ wins | Wins — field was genuinely updated today. |
| cust-c001 | HAS_CITY | val-ct-001 | 09:05 | salesforce | TRUE | ✓ wins |
3Gold — Resolved ProfileOne row per customer. Each field attributed to its winning source.
| field | resolved value | winning source | field_updated_at | conflict? | losing value (overridden) |
|---|---|---|---|---|---|
| alex@example.demo | salesforce | 09:05 | — | — | |
| phone | +49 987 654 3210 | shopify | 09:00 | conflict | +49 111 000 0000 (salesforce, 09:05) |
| city | Berlin | salesforce | 09:05 | — | — |
Schema reference — the two Silver tables
graph.nodes
node_id STRING -- UUID (entity) or SHA-256 (value) node_type STRING -- customer | company | asset | email | ... value STRING -- NULL for entity nodes checksum STRING -- dedup key for value nodes created_at TIMESTAMP properties MAP<STRING,STRING>
graph.relationships
source_node_id STRING -- any node type target_node_id STRING -- any node type rel_type STRING -- HAS_EMAIL | WORKS_AT | OWNS | ... updated_at TIMESTAMP -- field-level, not record-level confidence FLOAT is_current BOOLEAN source_system STRING properties MAP<STRING,STRING>
All data is synthetic demo data. Built by Santosh Pradhan · AAG framework post · Open source on GitHub