[
  {
    "id": "01-system-context",
    "title": "Customer 360 Platform — System Context",
    "layout": "context",
    "canvas": {
      "width": 1900
    },
    "colWidth": 300,
    "system": {
      "label": "Customer 360 Platform",
      "sub": "Denodo logical data layer",
      "icon": "fa5_layer_group"
    },
    "groups": [
      {
        "side": "left",
        "title": "People it serves",
        "nodes": [
          {
            "id": "agent",
            "label": "Service Agent",
            "sub": "1,800 seats",
            "kind": "actor",
            "rel": "360 view",
            "dir": "in",
            "icon": "fa5_headset"
          },
          {
            "id": "rm",
            "label": "Relationship Manager",
            "sub": "420 seats",
            "kind": "actor",
            "rel": "account book",
            "dir": "in",
            "icon": "person_exec"
          },
          {
            "id": "bidev",
            "label": "BI Developer",
            "sub": "90 authors",
            "kind": "actor",
            "rel": "certified data",
            "dir": "in",
            "icon": "role_dataengineer"
          },
          {
            "id": "steward",
            "label": "Data Steward",
            "sub": "Customer Data Team",
            "kind": "actor",
            "rel": "governs",
            "dir": "in",
            "icon": "person_approver"
          }
        ]
      },
      {
        "side": "top",
        "title": "Systems of record",
        "nodes": [
          {
            "id": "sfdc",
            "label": "Salesforce CRM",
            "sub": "profile, segment",
            "kind": "external",
            "rel": "REST",
            "icon": "di2_salesforce"
          },
          {
            "id": "erp",
            "label": "SAP and Oracle ERP",
            "sub": "orders, billing",
            "kind": "external",
            "rel": "JDBC",
            "icon": "fa5_industry"
          },
          {
            "id": "snow",
            "label": "ServiceNow CSM",
            "sub": "cases, SLA",
            "kind": "external",
            "rel": "REST",
            "icon": "fa5_life_ring"
          }
        ]
      },
      {
        "side": "right",
        "title": "Analytical and SaaS sources",
        "nodes": [
          {
            "id": "lake",
            "label": "Databricks Lakehouse",
            "sub": "CLV, ML scores",
            "kind": "external",
            "rel": "daily",
            "kind2": "batch"
          },
          {
            "id": "mktg",
            "label": "Marketing Cloud",
            "sub": "campaigns",
            "kind": "external",
            "rel": "hourly",
            "icon": "fa5_bullhorn"
          },
          {
            "id": "events",
            "label": "Digital Event Stream",
            "sub": "web and mobile",
            "kind": "external",
            "rel": "events",
            "kind2": "async",
            "icon": "fa5_stream"
          }
        ]
      },
      {
        "side": "bottom",
        "title": "Consuming applications and metadata",
        "nodes": [
          {
            "id": "pbi",
            "label": "Power BI",
            "sub": "1,100 report users",
            "kind": "external",
            "rel": "SQL"
          },
          {
            "id": "desktop",
            "label": "Agent Desktop",
            "sub": "contact centre",
            "kind": "external",
            "rel": "REST API",
            "icon": "fa5_headset"
          },
          {
            "id": "ai",
            "label": "Customer AI Assistant",
            "sub": "Azure OpenAI",
            "kind": "external",
            "rel": "tools",
            "icon": "azure_openai"
          },
          {
            "id": "nb",
            "label": "Data Science Notebooks",
            "sub": "masked cohorts",
            "kind": "external",
            "rel": "JDBC",
            "icon": "jupyter"
          }
        ]
      },
      {
        "side": "top",
        "title": "Enterprise services",
        "nodes": [
          {
            "id": "entra",
            "label": "Microsoft Entra ID",
            "kind": "security",
            "rel": "auth",
            "dir": "in",
            "icon": "azure_AzureActiveDirectory__Identity"
          },
          {
            "id": "purview",
            "label": "Microsoft Purview",
            "kind": "security",
            "rel": "lineage",
            "kind2": "batch"
          }
        ]
      }
    ],
    "note": "Out of scope: campaign execution, CRM customisation and remediation inside the source systems. The platform reads; the owning system still writes.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "02-high-level-architecture",
    "title": "High-Level Architecture — Federate by Default, Materialise by Exception",
    "layout": "flow",
    "canvas": {
      "width": 1780
    },
    "chain": true,
    "align": "middle",
    "stages": [
      {
        "title": "Sources",
        "nodes": [
          {
            "id": "s-sfdc",
            "label": "Salesforce CRM",
            "kind": "external",
            "icon": "di2_salesforce"
          },
          {
            "id": "s-sap",
            "label": "SAP S/4HANA",
            "kind": "external",
            "icon": "fa5_industry"
          },
          {
            "id": "s-snow",
            "label": "ServiceNow CSM",
            "kind": "external",
            "icon": "fa5_life_ring"
          },
          {
            "id": "s-evt",
            "label": "Digital Events",
            "kind": "external",
            "icon": "fa5_stream"
          }
        ]
      },
      {
        "title": "Move only what must move",
        "nodes": [
          {
            "id": "eh",
            "label": "Azure Event Hubs",
            "sub": "Kafka protocol",
            "kind": "queue",
            "icon": "azure_AzureEventHub__Analytics"
          },
          {
            "id": "cdc",
            "label": "CDC and Batch Load",
            "sub": "Debezium, ADF",
            "kind": "integration"
          },
          {
            "id": "delta",
            "label": "Delta Lakehouse",
            "sub": "ADLS Gen2",
            "kind": "store"
          }
        ]
      },
      {
        "title": "Resolve identity",
        "nodes": [
          {
            "id": "zingg",
            "label": "Entity Resolution",
            "sub": "Zingg on Spark",
            "kind": "app",
            "icon": "spark"
          },
          {
            "id": "xref",
            "label": "ECID Crosswalk",
            "sub": "PostgreSQL",
            "kind": "store",
            "icon": "postgresql"
          }
        ]
      },
      {
        "title": "Denodo logical layer",
        "nodes": [
          {
            "id": "vdp",
            "label": "Virtual DataPort",
            "sub": "federated engine",
            "kind": "app",
            "icon": "fa5_layer_group"
          },
          {
            "id": "sem",
            "label": "Semantic Model",
            "sub": "Customer360",
            "kind": "app",
            "icon": "fa5_project_diagram"
          },
          {
            "id": "cache",
            "label": "Cache and Summaries",
            "sub": "PostgreSQL, MPP",
            "kind": "store",
            "icon": "postgresql"
          }
        ]
      },
      {
        "title": "Delivery",
        "nodes": [
          {
            "id": "jdbc",
            "label": "JDBC and ODBC",
            "kind": "integration"
          },
          {
            "id": "rest",
            "label": "REST and GraphQL",
            "kind": "integration"
          },
          {
            "id": "apim",
            "label": "API Management",
            "sub": "Azure APIM",
            "kind": "integration"
          }
        ]
      },
      {
        "title": "Consumers",
        "nodes": [
          {
            "id": "c-pbi",
            "label": "Power BI",
            "kind": "external"
          },
          {
            "id": "c-desk",
            "label": "Agent Desktop",
            "kind": "external",
            "icon": "fa5_headset"
          },
          {
            "id": "c-ai",
            "label": "AI Assistant",
            "kind": "external",
            "icon": "azure_openai"
          },
          {
            "id": "c-nb",
            "label": "Notebooks",
            "kind": "external",
            "icon": "jupyter"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "s-evt",
        "to": "eh",
        "label": "clickstream",
        "kind": "async"
      },
      {
        "from": "delta",
        "to": "zingg",
        "label": "nightly",
        "kind": "batch"
      },
      {
        "from": "xref",
        "to": "vdp",
        "label": "join key"
      }
    ],
    "note": "Only digital events, deep history and model output are physically moved. Profile, order and case attributes are read where they live.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "03-access-not-truth",
    "title": "The Governing Principle — Denodo Owns Access, Not Truth",
    "layout": "nested",
    "canvas": {
      "width": 1720
    },
    "boxes": [
      {
        "title": "Denodo Platform — owns the access contract",
        "kind": "boundary",
        "nodes": [
          {
            "id": "p-sem",
            "label": "Semantic Model",
            "sub": "one Customer360",
            "icon": "fa5_project_diagram"
          },
          {
            "id": "p-fed",
            "label": "Federated Execution",
            "sub": "pushdown, MPP",
            "icon": "fa5_random"
          },
          {
            "id": "p-pol",
            "label": "Policy Enforcement",
            "sub": "row, column, mask",
            "icon": "fa5_shield_alt"
          },
          {
            "id": "p-svc",
            "label": "Data Services",
            "sub": "SQL, REST, GraphQL",
            "icon": "fa5_plug"
          },
          {
            "id": "p-cat",
            "label": "Catalog and Lineage",
            "sub": "certified assets",
            "icon": "fa5_sitemap"
          }
        ]
      },
      {
        "title": "Specialist platforms — own the truth",
        "kind": "cloud",
        "dir": "row",
        "children": [
          {
            "title": "Identity",
            "kind": "boundary",
            "nodes": [
              {
                "id": "t-er",
                "label": "Entity Resolution",
                "sub": "Zingg on Spark",
                "icon": "spark"
              },
              {
                "id": "t-xref",
                "label": "ECID Crosswalk",
                "kind": "store",
                "icon": "postgresql"
              },
              {
                "id": "t-surv",
                "label": "Survivorship Rules",
                "sub": "versioned in Git",
                "icon": "fa5_balance_scale"
              }
            ]
          },
          {
            "title": "History and models",
            "kind": "boundary",
            "nodes": [
              {
                "id": "t-lake",
                "label": "Delta Lakehouse",
                "kind": "store"
              },
              {
                "id": "t-clv",
                "label": "CLV and Segments",
                "sub": "daily model run"
              },
              {
                "id": "t-agg",
                "label": "Pre-aggregations",
                "kind": "store"
              }
            ]
          },
          {
            "title": "Movement",
            "kind": "boundary",
            "nodes": [
              {
                "id": "t-eh",
                "label": "Event Hubs",
                "kind": "queue",
                "icon": "azure_AzureEventHub__Analytics"
              },
              {
                "id": "t-str",
                "label": "Stream Processing",
                "sub": "Spark Streaming"
              }
            ]
          },
          {
            "title": "Systems of record",
            "kind": "onprem",
            "nodes": [
              {
                "id": "t-sfdc",
                "label": "Salesforce",
                "kind": "external",
                "icon": "di2_salesforce"
              },
              {
                "id": "t-sap",
                "label": "SAP S/4HANA",
                "kind": "external",
                "icon": "fa5_industry"
              },
              {
                "id": "t-snow",
                "label": "ServiceNow",
                "kind": "external",
                "icon": "fa5_life_ring"
              }
            ]
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "p-fed",
        "to": "t-xref",
        "label": "joins ECID"
      },
      {
        "from": "p-fed",
        "to": "t-lake",
        "label": "reads gold"
      },
      {
        "from": "p-fed",
        "to": "t-sfdc",
        "label": "reads live"
      },
      {
        "from": "p-sem",
        "to": "t-surv",
        "label": "applies",
        "kind": "batch"
      }
    ],
    "note": "No box in the upper container stores an authoritative customer fact. Removing Denodo would slow every consumer down; it would not lose a record.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "04-actors-and-journeys",
    "title": "Who the Customer 360 Is For, and What Each of Them Gets to Do",
    "layout": "actors",
    "canvas": {
      "width": 1820
    },
    "cardWidth": 300,
    "groups": [
      {
        "title": "People who take value from it",
        "kind": "boundary",
        "actors": [
          {
            "id": "a-agent",
            "label": "Customer Service Agent",
            "sub": "1,800 seats",
            "goal": "Know who I am talking to before they finish saying their name, and tell whether their open case is really still open.",
            "journeys": [
              {
                "id": "j-call",
                "label": "Answer an inbound call",
                "sub": "9,400 calls a day"
              },
              {
                "label": "Check an order or invoice"
              },
              {
                "label": "Raise a complaint"
              }
            ],
            "icon": "fa5_headset"
          },
          {
            "id": "a-rm",
            "label": "Relationship Manager",
            "sub": "420 seats",
            "goal": "Walk into a meeting knowing what this account bought, what broke, and what we last promised them.",
            "journeys": [
              {
                "label": "Prepare for an account meeting"
              },
              {
                "label": "Review my book",
                "sub": "weekly"
              }
            ],
            "icon": "person_exec"
          },
          {
            "id": "a-bi",
            "label": "BI Developer",
            "sub": "90 authors",
            "goal": "Build a customer report on one certified dataset instead of joining five systems whose table names I do not know.",
            "journeys": [
              {
                "id": "j-discover",
                "label": "Find a certified dataset"
              },
              {
                "label": "Build a Power BI report"
              }
            ],
            "icon": "role_dataengineer"
          },
          {
            "id": "a-ds",
            "label": "Data Scientist",
            "sub": "35 users",
            "goal": "Pull a governed cohort into a notebook with the personal fields already masked, without raising a ticket first.",
            "journeys": [
              {
                "label": "Pull a masked cohort"
              },
              {
                "label": "Publish a score back"
              }
            ]
          }
        ]
      },
      {
        "title": "People who run and govern it",
        "kind": "trust",
        "actors": [
          {
            "id": "a-steward",
            "label": "Data Steward",
            "sub": "Customer Data Team",
            "goal": "Prove where every attribute on the 360 came from, and merge two records without anyone losing history.",
            "journeys": [
              {
                "id": "j-merge",
                "label": "Resolve a duplicate customer"
              },
              {
                "label": "Certify a dataset"
              },
              {
                "label": "Answer a lineage question"
              }
            ],
            "icon": "person_approver"
          },
          {
            "id": "a-sre",
            "label": "Platform SRE",
            "sub": "follow the sun",
            "goal": "Find out that a source has gone slow before the contact centre tells me.",
            "journeys": [
              {
                "label": "Triage a slow query"
              },
              {
                "label": "Handle a source outage"
              }
            ],
            "icon": "role_devops"
          },
          {
            "id": "a-dpo",
            "label": "Privacy Officer",
            "sub": "DPO office",
            "goal": "Answer a subject access request inside the statutory window without a manual trawl through six systems.",
            "journeys": [
              {
                "label": "Fulfil a DSAR"
              },
              {
                "label": "Approve a masking exception"
              }
            ],
            "icon": "fa5_user_shield"
          }
        ]
      },
      {
        "title": "Systems that consume it",
        "kind": "cloud",
        "actors": [
          {
            "id": "a-desk",
            "label": "Agent Desktop App",
            "kind": "external",
            "sub": "60 rps at peak",
            "goal": "Get the whole customer panel in one call, in under two seconds, or a clear reason why not.",
            "journeys": [
              {
                "label": "Load the customer panel"
              }
            ],
            "icon": "fa5_headset"
          },
          {
            "id": "a-ai",
            "label": "Customer AI Assistant",
            "kind": "external",
            "sub": "Azure OpenAI",
            "goal": "Answer in the caller's words using only what the agent asking is allowed to see.",
            "journeys": [
              {
                "label": "Answer a grounded question"
              }
            ],
            "icon": "azure_openai"
          },
          {
            "id": "a-partner",
            "label": "Partner Portal",
            "kind": "external",
            "sub": "4 partners",
            "goal": "Read the subset of the 360 our contract allows, and nothing beyond it.",
            "journeys": [
              {
                "label": "Fetch an entitled subset"
              }
            ],
            "icon": "fa5_handshake"
          }
        ]
      },
      {
        "title": "Machines that act unasked",
        "kind": "lane",
        "actors": [
          {
            "id": "a-ecid",
            "label": "ECID Resolution Job",
            "kind": "platform",
            "sub": "nightly, 02:00 UTC",
            "goal": "Reconcile every new source identifier to one enterprise customer before the business day starts.",
            "journeys": [
              {
                "label": "Resolve new identifiers"
              },
              {
                "label": "Publish crosswalk deltas"
              }
            ],
            "icon": "spark"
          },
          {
            "id": "a-sched",
            "label": "Cache Refresh Scheduler",
            "kind": "platform",
            "sub": "Denodo Scheduler",
            "goal": "Keep each cached view inside its declared freshness window without stampeding the source.",
            "journeys": [
              {
                "label": "Refresh on schedule"
              },
              {
                "label": "Invalidate on ECID change"
              }
            ],
            "icon": "fa5_clock"
          },
          {
            "id": "a-score",
            "label": "Model Scoring Job",
            "kind": "platform",
            "sub": "daily, Databricks",
            "goal": "Land lifetime value, churn and segment scores where the semantic layer can join them by morning.",
            "journeys": [
              {
                "label": "Score and publish"
              }
            ]
          }
        ]
      }
    ],
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "05-journey-service-agent",
    "title": "Journey — A Service Agent Answers an Inbound Call",
    "layout": "journey",
    "actor": {
      "label": "Customer Service Agent",
      "sub": "contact centre, 1,800 seats",
      "goal": "Answer the caller without asking them to repeat what we already know",
      "trigger": "An inbound call arrives, with a caller-line match or none at all",
      "success": "Resolved in one call, against an accurate order and case picture",
      "icon": "fa5_headset"
    },
    "phases": [
      {
        "title": "Take the call",
        "sub": "0-5 s"
      },
      {
        "title": "See who it is",
        "sub": "5-8 s",
        "moment": true
      },
      {
        "title": "Look it up",
        "sub": "8-40 s",
        "moment": true
      },
      {
        "title": "Act"
      },
      {
        "title": "Wrap up"
      }
    ],
    "lanes": [
      {
        "title": "What they do",
        "kind": "step",
        "cells": [
          [
            {
              "label": "Answers and greets"
            }
          ],
          [
            {
              "label": "Confirms identity"
            }
          ],
          [
            {
              "label": "Reads orders and cases"
            }
          ],
          [
            {
              "label": "Raises or updates case"
            }
          ],
          [
            {
              "label": "Logs the outcome"
            }
          ]
        ]
      },
      {
        "title": "Touchpoint",
        "kind": "touch",
        "cells": [
          [
            {
              "label": "Softphone"
            }
          ],
          [
            {
              "label": "Agent Desktop",
              "icon": "fa5_headset"
            }
          ],
          [
            {
              "label": "Customer 360 panel"
            }
          ],
          [
            {
              "label": "ServiceNow form"
            }
          ],
          [
            {
              "label": "Wrap-up notes"
            }
          ]
        ]
      },
      {
        "title": "How it feels",
        "kind": "emotion",
        "levels": [
          "In control",
          "Coping",
          "Exposed"
        ],
        "points": [
          2,
          1,
          0,
          1,
          2
        ]
      },
      {
        "title": "Behind the glass",
        "kind": "system",
        "cells": [
          [
            {
              "label": "CTI screen pop"
            }
          ],
          [
            {
              "label": "ECID lookup"
            }
          ],
          [
            {
              "label": "Customer360 REST"
            }
          ],
          [
            {
              "label": "ServiceNow write"
            }
          ],
          [
            {
              "label": "Interaction log"
            }
          ]
        ]
      },
      {
        "title": "Where it hurts",
        "kind": "pain",
        "cells": [
          [],
          [
            {
              "label": "No caller-line match"
            }
          ],
          [
            {
              "label": "Support panel blank"
            },
            {
              "label": "Down, or truly empty?"
            }
          ],
          [],
          []
        ]
      },
      {
        "title": "What would fix it",
        "kind": "gain",
        "cells": [
          [],
          [
            {
              "label": "Search on three fields"
            }
          ],
          [
            {
              "label": "Unavailable marker"
            },
            {
              "label": "Last known, timestamped"
            }
          ],
          [],
          []
        ]
      },
      {
        "title": "How we know",
        "kind": "metric",
        "cells": [
          [],
          [
            {
              "label": "Match rate 94%"
            }
          ],
          [
            {
              "label": "P95 1.6 s"
            }
          ],
          [],
          [
            {
              "label": "Handle time 4m 10s"
            }
          ]
        ]
      }
    ],
    "chain": true,
    "note": "The trough is not slowness. It is an empty support panel that cannot be told apart from a source outage — which is why partial results carry an explicit per-domain status.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "06-journey-bi-developer",
    "title": "Journey — A BI Developer Builds a Customer Report",
    "layout": "journey",
    "actor": {
      "label": "BI Developer",
      "sub": "reporting team, 90 authors",
      "goal": "Ship a churn-by-region report this sprint without learning SAP table names",
      "trigger": "A business request that needs profile, orders and lifetime value together",
      "success": "A published report reading one certified dataset, refreshing inside its SLA",
      "icon": "role_dataengineer"
    },
    "phases": [
      {
        "title": "Find",
        "sub": "Data Catalog"
      },
      {
        "title": "Understand"
      },
      {
        "title": "Try"
      },
      {
        "title": "Ask",
        "moment": true
      },
      {
        "title": "Publish"
      }
    ],
    "lanes": [
      {
        "title": "What they do",
        "kind": "step",
        "cells": [
          [
            {
              "label": "Searches for customer"
            }
          ],
          [
            {
              "label": "Reads the glossary"
            }
          ],
          [
            {
              "label": "Runs a sample query"
            }
          ],
          [
            {
              "label": "Requests access"
            }
          ],
          [
            {
              "label": "Builds the report"
            }
          ]
        ]
      },
      {
        "title": "Touchpoint",
        "kind": "touch",
        "cells": [
          [
            {
              "label": "Denodo Data Catalog",
              "icon": "fa5_book_open"
            }
          ],
          [
            {
              "label": "Asset page"
            }
          ],
          [
            {
              "label": "Design Studio",
              "icon": "fa5_drafting_compass"
            }
          ],
          [
            {
              "label": "Access request form"
            }
          ],
          [
            {
              "label": "Power BI Desktop"
            }
          ]
        ]
      },
      {
        "title": "How it feels",
        "kind": "emotion",
        "levels": [
          "Productive",
          "Coping",
          "Blocked"
        ],
        "points": [
          2,
          2,
          1,
          0,
          2
        ]
      },
      {
        "title": "Behind the glass",
        "kind": "system",
        "cells": [
          [
            {
              "label": "Catalog search index"
            }
          ],
          [
            {
              "label": "Glossary and lineage"
            }
          ],
          [
            {
              "label": "Sample execution"
            }
          ],
          [
            {
              "label": "Approval workflow"
            }
          ],
          [
            {
              "label": "DirectQuery to VDP"
            }
          ]
        ]
      },
      {
        "title": "Where it hurts",
        "kind": "pain",
        "cells": [
          [],
          [],
          [
            {
              "label": "Lifetime value masked"
            }
          ],
          [
            {
              "label": "Approval takes six days"
            }
          ],
          []
        ]
      },
      {
        "title": "What would fix it",
        "kind": "gain",
        "cells": [
          [],
          [],
          [
            {
              "label": "Mask reason on the field"
            }
          ],
          [
            {
              "label": "Named owner on asset"
            },
            {
              "label": "One working day SLA"
            }
          ],
          []
        ]
      }
    ],
    "chain": true,
    "note": "The dataset was certified and still blocked the work. Discovery without a named owner and a bounded approval turns a catalogue into a directory of things you cannot have.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "07-journey-data-steward",
    "title": "Journey — A Data Steward Resolves a Duplicate Customer",
    "layout": "journey",
    "actor": {
      "label": "Data Steward",
      "sub": "Customer Data Team, 6 people",
      "goal": "Merge two records for the same person without anyone losing history",
      "trigger": "A duplicate-suspect pair scores above the review threshold overnight",
      "success": "One enterprise id, both source ids retained, every consumer consistent within the hour",
      "icon": "person_approver"
    },
    "phases": [
      {
        "title": "Notice",
        "sub": "queue"
      },
      {
        "title": "Investigate"
      },
      {
        "title": "Decide",
        "moment": true
      },
      {
        "title": "Merge",
        "moment": true
      },
      {
        "title": "Verify"
      }
    ],
    "lanes": [
      {
        "title": "What they do",
        "kind": "step",
        "cells": [
          [
            {
              "label": "Opens the queue"
            }
          ],
          [
            {
              "label": "Compares both records"
            }
          ],
          [
            {
              "label": "Confirms same person"
            }
          ],
          [
            {
              "label": "Approves the merge"
            }
          ],
          [
            {
              "label": "Rechecks the 360"
            }
          ]
        ]
      },
      {
        "title": "Touchpoint",
        "kind": "touch",
        "cells": [
          [
            {
              "label": "Quality dashboard"
            }
          ],
          [
            {
              "label": "Side-by-side compare"
            }
          ],
          [
            {
              "label": "Steward console"
            }
          ],
          [
            {
              "label": "Crosswalk API"
            }
          ],
          [
            {
              "label": "Customer 360 panel"
            }
          ]
        ]
      },
      {
        "title": "How it feels",
        "kind": "emotion",
        "levels": [
          "Confident",
          "Cautious",
          "Alarmed"
        ],
        "points": [
          2,
          1,
          1,
          0,
          2
        ]
      },
      {
        "title": "Behind the glass",
        "kind": "system",
        "cells": [
          [
            {
              "label": "Zingg candidate pairs"
            }
          ],
          [
            {
              "label": "Source attribute trace"
            }
          ],
          [
            {
              "label": "Survivorship rules",
              "icon": "fa5_balance_scale"
            }
          ],
          [
            {
              "label": "Crosswalk write"
            }
          ],
          [
            {
              "label": "Cache invalidation"
            }
          ]
        ]
      },
      {
        "title": "Where it hurts",
        "kind": "pain",
        "cells": [
          [],
          [],
          [],
          [
            {
              "label": "Old id still cached"
            },
            {
              "label": "Split history for a day"
            }
          ],
          []
        ]
      },
      {
        "title": "What would fix it",
        "kind": "gain",
        "cells": [
          [],
          [],
          [],
          [
            {
              "label": "Crosswalk never cached"
            },
            {
              "label": "Merge event invalidates"
            }
          ],
          []
        ]
      }
    ],
    "chain": true,
    "note": "A merge is correct the instant it is approved and wrong for every consumer until the cache agrees. That gap is what makes the crosswalk the one join Denodo is forbidden to cache.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "08-layered-architecture",
    "title": "Layered Architecture — Dependencies Point Downward Only",
    "layout": "bands",
    "canvas": {
      "width": 1760
    },
    "layerHeaderWidth": 170,
    "bands": [
      {
        "name": "Consumption",
        "nodes": [
          {
            "id": "l-pbi",
            "label": "Power BI",
            "kind": "external"
          },
          {
            "id": "l-desk",
            "label": "Agent Desktop",
            "kind": "external",
            "icon": "fa5_headset"
          },
          {
            "id": "l-ai",
            "label": "AI Assistant",
            "kind": "external",
            "icon": "azure_openai"
          },
          {
            "id": "l-nb",
            "label": "Notebooks",
            "kind": "external",
            "icon": "jupyter"
          },
          {
            "id": "l-part",
            "label": "Partner Portal",
            "kind": "external",
            "icon": "fa5_handshake"
          }
        ]
      },
      {
        "name": "Delivery",
        "nodes": [
          {
            "id": "l-jdbc",
            "label": "JDBC and ODBC",
            "kind": "integration"
          },
          {
            "id": "l-rest",
            "label": "REST and OData",
            "kind": "integration"
          },
          {
            "id": "l-gql",
            "label": "GraphQL",
            "kind": "integration"
          },
          {
            "id": "l-apim",
            "label": "API Management",
            "sub": "quota, keys",
            "kind": "integration"
          }
        ]
      },
      {
        "name": "Business views",
        "nodes": [
          {
            "id": "l-c360",
            "label": "Customer360"
          },
          {
            "id": "l-prof",
            "label": "Customer Profile"
          },
          {
            "id": "l-ord",
            "label": "Customer Orders"
          },
          {
            "id": "l-sup",
            "label": "Customer Support"
          },
          {
            "id": "l-val",
            "label": "Customer Value"
          }
        ]
      },
      {
        "name": "Integration views",
        "nodes": [
          {
            "id": "l-ivc",
            "label": "iv_customer",
            "sub": "ECID join"
          },
          {
            "id": "l-ivo",
            "label": "iv_customer_order"
          },
          {
            "id": "l-ivs",
            "label": "iv_customer_case"
          },
          {
            "id": "l-iva",
            "label": "iv_customer_activity"
          },
          {
            "id": "l-ivq",
            "label": "iv_dq_exception",
            "sub": "quality flags"
          }
        ]
      },
      {
        "name": "Base views",
        "nodes": [
          {
            "id": "l-bsf",
            "label": "bv_sf_account"
          },
          {
            "id": "l-bsap",
            "label": "bv_sap_kna1"
          },
          {
            "id": "l-bsn",
            "label": "bv_snow_case"
          },
          {
            "id": "l-blk",
            "label": "bv_lake_clv"
          },
          {
            "id": "l-bxr",
            "label": "bv_ecid_xref"
          }
        ]
      },
      {
        "name": "Physical sources",
        "nodes": [
          {
            "id": "l-sf",
            "label": "Salesforce",
            "kind": "external",
            "icon": "di2_salesforce"
          },
          {
            "id": "l-sap",
            "label": "SAP S/4HANA",
            "kind": "external",
            "icon": "fa5_industry"
          },
          {
            "id": "l-sn",
            "label": "ServiceNow",
            "kind": "external",
            "icon": "fa5_life_ring"
          },
          {
            "id": "l-dbx",
            "label": "Delta Lakehouse",
            "kind": "store"
          },
          {
            "id": "l-pg",
            "label": "ECID Crosswalk",
            "kind": "store",
            "icon": "postgresql"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "l-desk",
        "to": "l-rest",
        "label": "HTTPS"
      },
      {
        "from": "l-rest",
        "to": "l-c360",
        "label": "publishes"
      },
      {
        "from": "l-c360",
        "to": "l-ivc",
        "label": "composes"
      },
      {
        "from": "l-ivc",
        "to": "l-bsf",
        "label": "normalises"
      },
      {
        "from": "l-bsf",
        "to": "l-sf",
        "label": "delegates"
      }
    ],
    "note": "Security, catalogue and lineage are not a layer — they are enforced at the integration layer and inherited upward by every view and every delivery channel above it.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "09-container-architecture",
    "title": "Container Architecture — The Deployable Units and What They Talk To",
    "layout": "nested",
    "canvas": {
      "width": 1760
    },
    "boxes": [
      {
        "title": "Azure — Customer 360 landing zone",
        "kind": "cloud",
        "dir": "col",
        "children": [
          {
            "title": "Edge and delivery",
            "kind": "boundary",
            "nodes": [
              {
                "id": "k-afd",
                "label": "Front Door and WAF",
                "sub": "TLS 1.3",
                "kind": "integration"
              },
              {
                "id": "k-lb",
                "label": "Denodo Load Balancer",
                "sub": "JDBC and ODBC",
                "kind": "integration",
                "icon": "pattern_loadbalancer"
              },
              {
                "id": "k-apim",
                "label": "API Management",
                "sub": "quota, keys, JWT",
                "kind": "integration"
              }
            ]
          },
          {
            "title": "Denodo query tier — AKS node pools",
            "kind": "boundary",
            "nodes": [
              {
                "id": "k-vdpi",
                "label": "VDP Interactive",
                "sub": "3 pods, BI and desktop",
                "icon": "fa5_layer_group"
              },
              {
                "id": "k-vdpa",
                "label": "VDP Data Services",
                "sub": "2 pods, REST",
                "icon": "fa5_layer_group"
              },
              {
                "id": "k-vdpb",
                "label": "VDP Analytical",
                "sub": "2 pods, science",
                "icon": "fa5_layer_group"
              },
              {
                "id": "k-mpp",
                "label": "Embedded MPP",
                "sub": "Presto, 6 workers",
                "icon": "trino"
              }
            ]
          },
          {
            "title": "Denodo control tier",
            "kind": "boundary",
            "nodes": [
              {
                "id": "k-sm",
                "label": "Solution Manager",
                "sub": "licence, promotion",
                "icon": "fa5_tasks"
              },
              {
                "id": "k-ds",
                "label": "Design Studio",
                "sub": "modelling",
                "icon": "fa5_drafting_compass"
              },
              {
                "id": "k-cat",
                "label": "Data Catalog",
                "sub": "search, certification",
                "icon": "fa5_book_open"
              },
              {
                "id": "k-sch",
                "label": "Scheduler",
                "sub": "cache refresh"
              }
            ]
          },
          {
            "title": "State",
            "kind": "boundary",
            "nodes": [
              {
                "id": "k-cache",
                "label": "Cache Database",
                "sub": "PostgreSQL Flexible",
                "kind": "store",
                "icon": "postgresql"
              },
              {
                "id": "k-meta",
                "label": "Metadata Database",
                "sub": "VDP catalogue",
                "kind": "store",
                "icon": "postgresql"
              },
              {
                "id": "k-xref",
                "label": "ECID Crosswalk",
                "sub": "PostgreSQL",
                "kind": "store",
                "icon": "postgresql"
              },
              {
                "id": "k-qlog",
                "label": "Query Log",
                "sub": "Log Analytics",
                "kind": "store"
              }
            ]
          },
          {
            "title": "Azure data platform",
            "kind": "boundary",
            "nodes": [
              {
                "id": "k-dbx",
                "label": "Databricks SQL",
                "sub": "serverless warehouse"
              },
              {
                "id": "k-lake",
                "label": "Delta Lakehouse",
                "sub": "ADLS Gen2",
                "kind": "store"
              },
              {
                "id": "k-eh",
                "label": "Event Hubs",
                "sub": "Kafka protocol",
                "kind": "queue",
                "icon": "azure_AzureEventHub__Analytics"
              },
              {
                "id": "k-uc",
                "label": "Unity Catalog",
                "sub": "lake grants",
                "kind": "security"
              }
            ]
          },
          {
            "title": "Platform services",
            "kind": "trust",
            "nodes": [
              {
                "id": "k-entra",
                "label": "Microsoft Entra ID",
                "kind": "security",
                "icon": "azure_AzureActiveDirectory__Identity"
              },
              {
                "id": "k-kv",
                "label": "Key Vault",
                "sub": "source credentials",
                "kind": "security"
              },
              {
                "id": "k-mon",
                "label": "Azure Monitor",
                "sub": "metrics and logs",
                "kind": "security"
              },
              {
                "id": "k-pv",
                "label": "Microsoft Purview",
                "sub": "enterprise catalogue",
                "kind": "security"
              }
            ]
          }
        ]
      }
    ],
    "outside": [
      {
        "id": "k-sf",
        "label": "Salesforce",
        "kind": "external",
        "icon": "di2_salesforce"
      },
      {
        "id": "k-sap",
        "label": "SAP S/4HANA",
        "kind": "external",
        "icon": "fa5_industry"
      },
      {
        "id": "k-ora",
        "label": "Oracle Billing",
        "kind": "external",
        "icon": "fa5_file_invoice_dollar"
      },
      {
        "id": "k-snow",
        "label": "ServiceNow",
        "kind": "external",
        "icon": "fa5_life_ring"
      },
      {
        "id": "k-mktg",
        "label": "Marketing Cloud",
        "kind": "external",
        "icon": "fa5_bullhorn"
      }
    ],
    "edges": [
      {
        "from": "k-apim",
        "to": "k-vdpa",
        "label": "REST"
      },
      {
        "from": "k-lb",
        "to": "k-vdpi",
        "label": "JDBC"
      },
      {
        "from": "k-vdpi",
        "to": "k-cache",
        "label": "cache read"
      },
      {
        "from": "k-vdpb",
        "to": "k-mpp",
        "label": "offload"
      },
      {
        "from": "k-mpp",
        "to": "k-lake",
        "label": "Parquet scan"
      },
      {
        "from": "k-vdpa",
        "to": "k-sf",
        "label": "OAuth REST"
      },
      {
        "from": "k-sch",
        "to": "k-cache",
        "label": "refresh",
        "kind": "batch"
      }
    ],
    "note": "Three VDP pools, not one cluster: a data scientist scanning three years of orders cannot take latency from the contact centre. Source credentials are drawn on view 35, not here.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "10-denodo-view-hierarchy",
    "title": "The Denodo View Hierarchy — Four Layers, One Naming Standard",
    "layout": "flow",
    "canvas": {
      "width": 1800
    },
    "chain": true,
    "align": "top",
    "stages": [
      {
        "title": "Physical sources",
        "nodes": [
          {
            "id": "h-sf",
            "label": "Salesforce",
            "kind": "external",
            "icon": "di2_salesforce"
          },
          {
            "id": "h-sap",
            "label": "SAP S/4HANA",
            "kind": "external",
            "icon": "fa5_industry"
          },
          {
            "id": "h-sn",
            "label": "ServiceNow",
            "kind": "external",
            "icon": "fa5_life_ring"
          },
          {
            "id": "h-lk",
            "label": "Delta Lakehouse",
            "kind": "store"
          }
        ]
      },
      {
        "title": "Layer 1 — base views",
        "nodes": [
          {
            "id": "h-b1",
            "label": "bv_sf_account",
            "sub": "no logic"
          },
          {
            "id": "h-b2",
            "label": "bv_sap_kna1",
            "sub": "no logic"
          },
          {
            "id": "h-b3",
            "label": "bv_snow_case",
            "sub": "no logic"
          },
          {
            "id": "h-b4",
            "label": "bv_lake_clv",
            "sub": "no logic"
          }
        ]
      },
      {
        "title": "Layer 2 — integration views",
        "nodes": [
          {
            "id": "h-i1",
            "label": "iv_customer",
            "sub": "ECID, survivorship"
          },
          {
            "id": "h-i2",
            "label": "iv_customer_order"
          },
          {
            "id": "h-i3",
            "label": "iv_customer_case"
          },
          {
            "id": "h-i4",
            "label": "iv_customer_value"
          }
        ]
      },
      {
        "title": "Layer 3 — business views",
        "nodes": [
          {
            "id": "h-v1",
            "label": "Customer Profile"
          },
          {
            "id": "h-v2",
            "label": "Customer Orders"
          },
          {
            "id": "h-v3",
            "label": "Customer Support"
          },
          {
            "id": "h-v4",
            "label": "Customer Value"
          }
        ]
      },
      {
        "title": "Layer 4 — the 360",
        "nodes": [
          {
            "id": "h-c360",
            "label": "Customer360",
            "sub": "certified"
          }
        ]
      },
      {
        "title": "Published",
        "nodes": [
          {
            "id": "h-p1",
            "label": "SQL endpoint",
            "kind": "integration"
          },
          {
            "id": "h-p2",
            "label": "REST resource",
            "kind": "integration"
          },
          {
            "id": "h-p3",
            "label": "GraphQL schema",
            "kind": "integration"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "h-i1",
        "to": "h-v1",
        "label": "renames"
      },
      {
        "from": "h-c360",
        "to": "h-p2",
        "label": "publishes"
      }
    ],
    "note": "Base views carry no transformation and no business name. Security is declared once at layer 2, so no consumer can reach a source structure that has not passed through it.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "11-integration-surface",
    "title": "Integration Surface — Every Interface In and Out",
    "layout": "hub",
    "canvas": {
      "width": 1860
    },
    "left": {
      "title": "Consumers that call us",
      "nodes": [
        {
          "id": "n-pbi",
          "label": "Power BI",
          "sub": "1,100 users",
          "kind": "external",
          "rel": "ODBC"
        },
        {
          "id": "n-desk",
          "label": "Agent Desktop",
          "sub": "60 rps peak",
          "kind": "external",
          "rel": "REST",
          "icon": "fa5_headset"
        },
        {
          "id": "n-ai",
          "label": "AI Assistant",
          "sub": "tool calls",
          "kind": "external",
          "rel": "MCP",
          "icon": "azure_openai"
        },
        {
          "id": "n-nb",
          "label": "Notebooks",
          "sub": "35 users",
          "kind": "external",
          "rel": "JDBC",
          "icon": "jupyter"
        },
        {
          "id": "n-part",
          "label": "Partner Portal",
          "sub": "4 partners",
          "kind": "external",
          "rel": "APIM",
          "icon": "fa5_handshake"
        },
        {
          "id": "n-pv",
          "label": "Microsoft Purview",
          "sub": "nightly scan",
          "kind": "security",
          "rel": "meta",
          "kind2": "batch"
        }
      ]
    },
    "centre": {
      "title": "Denodo Virtual DataPort",
      "nodes": [
        {
          "id": "n-vdp",
          "label": "Customer 360 Logical Layer",
          "sub": "one access contract",
          "icon": "fa5_layer_group"
        }
      ]
    },
    "right": {
      "title": "Sources we call",
      "nodes": [
        {
          "id": "n-sf",
          "label": "Salesforce CRM",
          "sub": "on demand",
          "kind": "external",
          "rel": "REST",
          "dir": "out",
          "icon": "di2_salesforce"
        },
        {
          "id": "n-sap",
          "label": "SAP S/4HANA",
          "sub": "on demand",
          "kind": "external",
          "rel": "JDBC",
          "dir": "out",
          "icon": "fa5_industry"
        },
        {
          "id": "n-ora",
          "label": "Oracle Billing",
          "sub": "cached 30 min",
          "kind": "external",
          "rel": "JDBC",
          "dir": "out",
          "icon": "fa5_file_invoice_dollar"
        },
        {
          "id": "n-sn",
          "label": "ServiceNow CSM",
          "sub": "cached 15 min",
          "kind": "external",
          "rel": "REST",
          "dir": "out",
          "icon": "fa5_life_ring"
        },
        {
          "id": "n-mk",
          "label": "Marketing Cloud",
          "sub": "cached 1 hour",
          "kind": "external",
          "rel": "REST",
          "dir": "out",
          "icon": "fa5_bullhorn"
        },
        {
          "id": "n-dbx",
          "label": "Databricks SQL",
          "sub": "gold tables only",
          "kind": "external",
          "rel": "JDBC",
          "dir": "out"
        },
        {
          "id": "n-lk",
          "label": "Delta and ADLS",
          "sub": "MPP scan",
          "kind": "store",
          "rel": "Parquet",
          "dir": "out",
          "icon": "lakehouse"
        },
        {
          "id": "n-xr",
          "label": "ECID Crosswalk",
          "sub": "never cached",
          "kind": "store",
          "rel": "JDBC",
          "dir": "out",
          "icon": "postgresql"
        }
      ]
    },
    "note": "Fourteen interfaces, one contract. No consumer holds a credential for a source, and no source is reachable except through a layer 2 view.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "12-source-connectivity-matrix",
    "title": "Source Connectivity — Connector, Identity, Delegation and Strategy",
    "layout": "grid",
    "canvas": {
      "width": 1800
    },
    "laneHeaderWidth": 190,
    "columns": [
      "Connector",
      "Authentication",
      "What is delegated",
      "Access strategy"
    ],
    "rows": [
      {
        "title": "SaaS CRM",
        "cells": [
          [
            {
              "label": "Salesforce adapter",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "OAuth 2.0 JWT bearer",
              "kind": "security"
            }
          ],
          [
            {
              "label": "SOQL filters, limits"
            }
          ],
          [
            {
              "label": "Federated, no cache",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "ERP",
        "cells": [
          [
            {
              "label": "SAP HANA JDBC",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Vault-held tech user",
              "kind": "security",
              "icon": "azure_AzureKeyVault__Security"
            }
          ],
          [
            {
              "label": "Joins, aggregates"
            }
          ],
          [
            {
              "label": "Federated plus partial",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Billing",
        "cells": [
          [
            {
              "label": "Oracle JDBC",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Vault-held tech user",
              "kind": "security",
              "icon": "azure_AzureKeyVault__Security"
            }
          ],
          [
            {
              "label": "Joins, aggregates"
            }
          ],
          [
            {
              "label": "Partial cache, 30 min",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "ITSM",
        "cells": [
          [
            {
              "label": "ServiceNow REST",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "OAuth 2.0",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Filters only"
            }
          ],
          [
            {
              "label": "Cached 15 min",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Marketing",
        "cells": [
          [
            {
              "label": "Marketing Cloud REST",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "OAuth 2.0",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Filters only"
            }
          ],
          [
            {
              "label": "Cached 1 hour",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Lakehouse",
        "cells": [
          [
            {
              "label": "Databricks JDBC",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Managed identity",
              "kind": "security",
              "icon": "azure_AzureManagedIdentity__Identity"
            }
          ],
          [
            {
              "label": "Full SQL pushdown"
            }
          ],
          [
            {
              "label": "Federated on gold",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Lake files",
        "cells": [
          [
            {
              "label": "Parquet base view",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Managed identity",
              "kind": "security",
              "icon": "azure_AzureManagedIdentity__Identity"
            }
          ],
          [
            {
              "label": "Partition pruning"
            }
          ],
          [
            {
              "label": "MPP acceleration",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Streaming",
        "cells": [
          [
            {
              "label": "Delta streaming table",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Managed identity",
              "kind": "security",
              "icon": "azure_AzureManagedIdentity__Identity"
            }
          ],
          [
            {
              "label": "Partition pruning"
            }
          ],
          [
            {
              "label": "Federated, 60 s lag",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Identity crosswalk",
        "cells": [
          [
            {
              "label": "PostgreSQL JDBC",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Managed identity",
              "kind": "security",
              "icon": "azure_AzureManagedIdentity__Identity"
            }
          ],
          [
            {
              "label": "Full SQL pushdown"
            }
          ],
          [
            {
              "label": "Never cached",
              "kind": "risk"
            }
          ]
        ]
      }
    ],
    "note": "The two SaaS rows delegate least, which is why they are the two that get cached. Delegation capability, not preference, decides the strategy column.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "13-identity-resolution",
    "title": "Identity Resolution — How Five Source Ids Become One Enterprise Customer",
    "layout": "flow",
    "canvas": {
      "width": 1820
    },
    "chain": true,
    "align": "middle",
    "stages": [
      {
        "title": "Collect identifiers",
        "nodes": [
          {
            "id": "r-crm",
            "label": "CRM ids",
            "sub": "CUST-1001",
            "kind": "external"
          },
          {
            "id": "r-erp",
            "label": "ERP ids",
            "sub": "000045678",
            "kind": "external"
          },
          {
            "id": "r-sup",
            "label": "Support and marketing",
            "sub": "USER-9876",
            "kind": "external"
          },
          {
            "id": "r-dev",
            "label": "Device and web ids",
            "sub": "DEVICE-2345",
            "kind": "external"
          }
        ]
      },
      {
        "title": "Standardise",
        "nodes": [
          {
            "id": "r-name",
            "label": "Name and address parse",
            "sub": "libpostal"
          },
          {
            "id": "r-cont",
            "label": "Email and phone",
            "sub": "E.164, RFC 5322"
          },
          {
            "id": "r-ref",
            "label": "Country reference",
            "kind": "store"
          }
        ]
      },
      {
        "title": "Block and score",
        "nodes": [
          {
            "id": "r-blk",
            "label": "Blocking keys",
            "sub": "cuts the pair space",
            "icon": "fa5_filter"
          },
          {
            "id": "r-det",
            "label": "Deterministic rules",
            "sub": "tax id, email",
            "icon": "fa5_balance_scale"
          },
          {
            "id": "r-ml",
            "label": "Zingg similarity",
            "sub": "Spark ML",
            "icon": "spark"
          }
        ]
      },
      {
        "title": "Decide",
        "nodes": [
          {
            "id": "r-auto",
            "label": "Auto-merge",
            "sub": "score above 0.95",
            "kind": "decision"
          },
          {
            "id": "r-rev",
            "label": "Steward review",
            "sub": "0.80 to 0.95",
            "kind": "decision"
          },
          {
            "id": "r-rej",
            "label": "Keep separate",
            "sub": "below 0.80",
            "kind": "decision"
          }
        ]
      },
      {
        "title": "Master and publish",
        "nodes": [
          {
            "id": "r-surv",
            "label": "Survivorship rules",
            "sub": "source precedence",
            "icon": "fa5_balance_scale"
          },
          {
            "id": "r-ecid",
            "label": "ECID assignment",
            "sub": "stable, never reused",
            "icon": "fa5_id_card"
          },
          {
            "id": "r-xw",
            "label": "ECID Crosswalk",
            "sub": "source id retained",
            "kind": "store",
            "icon": "postgresql"
          }
        ]
      },
      {
        "title": "Serve and notify",
        "nodes": [
          {
            "id": "r-den",
            "label": "Denodo join key",
            "sub": "iv_customer"
          },
          {
            "id": "r-evt",
            "label": "Merge event",
            "sub": "invalidates cache",
            "kind": "queue"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "r-rev",
        "to": "r-surv",
        "label": "on approval"
      },
      {
        "from": "r-xw",
        "to": "r-evt",
        "label": "on change",
        "kind": "async"
      }
    ],
    "note": "Denodo consumes the crosswalk; it does not produce it. Matching rules, thresholds and survivorship live in Git and run on Spark, where they can be tested against a labelled set.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "14-data-flow",
    "title": "Data Flow — What Is Copied, What Is Only Read, and How Often",
    "layout": "flow",
    "canvas": {
      "width": 1840
    },
    "chain": true,
    "align": "middle",
    "stages": [
      {
        "title": "Sources",
        "nodes": [
          {
            "id": "d-saas",
            "label": "Salesforce CRM",
            "kind": "external",
            "icon": "di2_salesforce"
          },
          {
            "id": "d-erp",
            "label": "SAP and Oracle",
            "kind": "external",
            "icon": "fa5_industry"
          },
          {
            "id": "d-itsm",
            "label": "ServiceNow CSM",
            "kind": "external",
            "icon": "fa5_life_ring"
          },
          {
            "id": "d-web",
            "label": "Web and mobile",
            "kind": "external",
            "icon": "fa5_mobile_alt"
          }
        ]
      },
      {
        "title": "Capture",
        "nodes": [
          {
            "id": "d-eh",
            "label": "Event Hubs",
            "sub": "Kafka protocol",
            "kind": "queue",
            "icon": "azure_AzureEventHub__Analytics"
          },
          {
            "id": "d-cdc",
            "label": "Debezium CDC",
            "sub": "5 min",
            "kind": "integration"
          },
          {
            "id": "d-adf",
            "label": "Data Factory",
            "sub": "nightly extract",
            "kind": "integration"
          }
        ]
      },
      {
        "title": "Land and curate",
        "nodes": [
          {
            "id": "d-bz",
            "label": "Bronze",
            "sub": "raw, 30 days",
            "kind": "store"
          },
          {
            "id": "d-sv",
            "label": "Silver",
            "sub": "conformed, 13 months",
            "kind": "store"
          },
          {
            "id": "d-gd",
            "label": "Gold",
            "sub": "analytical, 7 years",
            "kind": "store"
          }
        ]
      },
      {
        "title": "Enrich",
        "nodes": [
          {
            "id": "d-er",
            "label": "Entity Resolution",
            "sub": "Zingg, nightly",
            "icon": "spark"
          },
          {
            "id": "d-dq",
            "label": "Quality Rules",
            "sub": "Soda Core"
          },
          {
            "id": "d-ml",
            "label": "Model Scoring",
            "sub": "CLV, churn"
          }
        ]
      },
      {
        "title": "Serve logically",
        "nodes": [
          {
            "id": "d-bv",
            "label": "Denodo base views",
            "icon": "fa5_layer_group"
          },
          {
            "id": "d-cache",
            "label": "Denodo cache",
            "sub": "TTL per view",
            "kind": "store",
            "icon": "postgresql"
          },
          {
            "id": "d-mpp",
            "label": "Embedded MPP",
            "sub": "Presto over Delta",
            "icon": "trino"
          }
        ]
      },
      {
        "title": "Consume",
        "nodes": [
          {
            "id": "d-pbi",
            "label": "Power BI",
            "kind": "external"
          },
          {
            "id": "d-desk",
            "label": "Agent Desktop",
            "kind": "external",
            "icon": "fa5_headset"
          },
          {
            "id": "d-ai",
            "label": "AI Assistant",
            "kind": "external",
            "icon": "azure_openai"
          },
          {
            "id": "d-nb",
            "label": "Notebooks",
            "kind": "external",
            "icon": "jupyter"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "d-erp",
        "to": "d-cdc",
        "label": "5 min",
        "kind": "async"
      },
      {
        "from": "d-gd",
        "to": "d-ml",
        "label": "03:00 daily",
        "kind": "batch"
      },
      {
        "from": "d-saas",
        "to": "d-bv",
        "label": "no copy",
        "route": "gutter"
      },
      {
        "from": "d-itsm",
        "to": "d-bv",
        "label": "cached",
        "route": "gutter"
      }
    ],
    "note": "The two gutter arrows are the point of the platform: profile and case data reach the consumer without ever landing in the lake.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "15-data-architecture-zones",
    "title": "Storage Zones — Who Owns Each Byte, and What Could Be Rebuilt",
    "layout": "nested",
    "canvas": {
      "width": 1780
    },
    "boxes": [
      {
        "title": "Systems of record — the platform never writes here",
        "kind": "onprem",
        "nodes": [
          {
            "id": "z-sf",
            "label": "Salesforce CRM",
            "sub": "profile, segment",
            "kind": "external",
            "icon": "di2_salesforce"
          },
          {
            "id": "z-sap",
            "label": "SAP S/4HANA",
            "sub": "orders, invoices",
            "kind": "external",
            "icon": "fa5_industry"
          },
          {
            "id": "z-ora",
            "label": "Oracle Billing",
            "sub": "payments",
            "kind": "external",
            "icon": "fa5_file_invoice_dollar"
          },
          {
            "id": "z-sn",
            "label": "ServiceNow CSM",
            "sub": "cases",
            "kind": "external",
            "icon": "fa5_life_ring"
          },
          {
            "id": "z-mk",
            "label": "Marketing Cloud",
            "sub": "campaigns",
            "kind": "external",
            "icon": "fa5_bullhorn"
          }
        ]
      },
      {
        "title": "Azure lakehouse — rebuildable from source in 14 hours",
        "kind": "cloud",
        "dir": "row",
        "children": [
          {
            "title": "Bronze",
            "kind": "boundary",
            "nodes": [
              {
                "id": "z-bz1",
                "label": "Event landing",
                "sub": "30 days",
                "kind": "store"
              },
              {
                "id": "z-bz2",
                "label": "CDC change log",
                "sub": "30 days",
                "kind": "store"
              }
            ]
          },
          {
            "title": "Silver",
            "kind": "boundary",
            "nodes": [
              {
                "id": "z-sv1",
                "label": "customer_activity",
                "sub": "13 months",
                "kind": "store"
              },
              {
                "id": "z-sv2",
                "label": "order_history",
                "sub": "7 years",
                "kind": "store"
              }
            ]
          },
          {
            "title": "Gold",
            "kind": "boundary",
            "nodes": [
              {
                "id": "z-gd1",
                "label": "customer_value",
                "sub": "daily snapshot",
                "kind": "store"
              },
              {
                "id": "z-gd2",
                "label": "segment_and_churn",
                "sub": "daily snapshot",
                "kind": "store"
              }
            ]
          }
        ]
      },
      {
        "title": "Platform-owned state — the only data this platform masters",
        "kind": "boundary",
        "nodes": [
          {
            "id": "z-xr",
            "label": "ECID Crosswalk",
            "sub": "backed up, PITR 7 d",
            "kind": "store",
            "icon": "postgresql"
          },
          {
            "id": "z-rules",
            "label": "Survivorship Rules",
            "sub": "Git, versioned",
            "kind": "store",
            "icon": "fa5_balance_scale"
          },
          {
            "id": "z-dq",
            "label": "DQ Exceptions",
            "sub": "90 days",
            "kind": "store"
          },
          {
            "id": "z-meta",
            "label": "Denodo Metadata",
            "sub": "views, policies",
            "kind": "store"
          }
        ]
      },
      {
        "title": "Disposable acceleration — losing all of it costs latency, not data",
        "kind": "plain",
        "nodes": [
          {
            "id": "z-c1",
            "label": "Cache tables",
            "sub": "TTL 15 min to 24 h",
            "kind": "store",
            "icon": "postgresql"
          },
          {
            "id": "z-c2",
            "label": "Summaries",
            "sub": "rebuilt nightly",
            "kind": "store",
            "icon": "fa5_table"
          },
          {
            "id": "z-c3",
            "label": "MPP result cache",
            "sub": "in memory",
            "kind": "store"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "z-sap",
        "to": "z-bz2",
        "label": "CDC",
        "kind": "async"
      },
      {
        "from": "z-sv2",
        "to": "z-gd1",
        "label": "nightly",
        "kind": "batch"
      },
      {
        "from": "z-xr",
        "to": "z-c1",
        "label": "invalidates",
        "kind": "async"
      }
    ],
    "note": "Only the third box needs a backup strategy. Everything above it is authoritative elsewhere, and everything below it is rebuildable on demand.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "16-customer360-data-model",
    "title": "Customer 360 — The Logical Data Model Consumers See",
    "layout": "er",
    "canvas": {
      "width": 1800,
      "cols": 4
    },
    "rowGap": 270,
    "entities": [
      {
        "id": "xref",
        "name": "source_customer_xref",
        "kind": "store",
        "row": 0,
        "col": 0,
        "attrs": [
          "xref_id  PK",
          "ecid  FK -> enterprise_customer",
          "source_system",
          "source_customer_id",
          "match_score",
          "valid_from"
        ]
      },
      {
        "id": "contact",
        "name": "customer_contact",
        "row": 0,
        "col": 1,
        "attrs": [
          "contact_id  PK",
          "ecid  FK",
          "email  PII",
          "phone  PII",
          "postal_address  PII",
          "is_primary"
        ]
      },
      {
        "id": "account",
        "name": "customer_account",
        "row": 0,
        "col": 2,
        "attrs": [
          "account_id  PK",
          "ecid  FK",
          "product_code",
          "opened_on",
          "account_status"
        ]
      },
      {
        "id": "ordr",
        "name": "customer_order",
        "row": 0,
        "col": 3,
        "attrs": [
          "order_id  PK",
          "account_id  FK",
          "order_date",
          "net_amount",
          "currency"
        ]
      },
      {
        "id": "activity",
        "name": "customer_activity",
        "row": 1,
        "col": 0,
        "attrs": [
          "activity_id  PK",
          "ecid  FK",
          "event_time",
          "event_type",
          "channel"
        ]
      },
      {
        "id": "cust",
        "name": "enterprise_customer",
        "kind": "app",
        "row": 1,
        "col": 1,
        "attrs": [
          "ecid  PK",
          "primary_name",
          "customer_status",
          "segment_code",
          "authoritative_source",
          "resolved_at"
        ]
      },
      {
        "id": "kase",
        "name": "customer_case",
        "row": 1,
        "col": 2,
        "attrs": [
          "case_id  PK",
          "ecid  FK",
          "opened_at",
          "sla_due_at",
          "case_state",
          "channel"
        ]
      },
      {
        "id": "invoice",
        "name": "customer_invoice",
        "row": 1,
        "col": 3,
        "attrs": [
          "invoice_id  PK",
          "order_id  FK",
          "due_date",
          "paid_flag",
          "outstanding"
        ]
      },
      {
        "id": "dq",
        "name": "dq_exception",
        "kind": "risk",
        "row": 2,
        "col": 0,
        "attrs": [
          "exception_id  PK",
          "ecid  FK",
          "rule_code",
          "severity",
          "detected_at"
        ]
      },
      {
        "id": "value",
        "name": "customer_value",
        "row": 2,
        "col": 1,
        "attrs": [
          "ecid  PK, FK",
          "lifetime_value",
          "churn_score",
          "scored_on"
        ]
      },
      {
        "id": "campaign",
        "name": "customer_campaign",
        "row": 2,
        "col": 2,
        "attrs": [
          "campaign_ref  PK",
          "ecid  FK",
          "sent_at",
          "responded_flag",
          "channel"
        ]
      }
    ],
    "relations": [
      {
        "from": "cust",
        "to": "xref",
        "label": "1 : N",
        "from_side": "n1",
        "to_side": "s"
      },
      {
        "from": "cust",
        "to": "contact",
        "label": "1 : N",
        "from_side": "n",
        "to_side": "s"
      },
      {
        "from": "cust",
        "to": "account",
        "label": "1 : N",
        "from_side": "n3",
        "to_side": "s"
      },
      {
        "from": "account",
        "to": "ordr",
        "label": "1 : N",
        "from_side": "e",
        "to_side": "w"
      },
      {
        "from": "ordr",
        "to": "invoice",
        "label": "1 : N",
        "from_side": "s",
        "to_side": "n"
      },
      {
        "from": "cust",
        "to": "activity",
        "label": "1 : N",
        "from_side": "w",
        "to_side": "e"
      },
      {
        "from": "cust",
        "to": "kase",
        "label": "1 : N",
        "from_side": "e",
        "to_side": "w"
      },
      {
        "from": "cust",
        "to": "value",
        "label": "1 : 1",
        "from_side": "s",
        "to_side": "n"
      },
      {
        "from": "cust",
        "to": "dq",
        "label": "1 : N",
        "from_side": "s1",
        "to_side": "n"
      },
      {
        "from": "cust",
        "to": "campaign",
        "label": "1 : N",
        "from_side": "s3",
        "to_side": "n"
      }
    ],
    "note": "The enterprise id is the only join key above layer 2. Source keys survive in the crosswalk so any figure can be traced back to the system that produced it.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "17-freshness-and-materialisation",
    "title": "Freshness Contract — What Is Federated, Cached or Materialised, and Why",
    "layout": "grid",
    "canvas": {
      "width": 1820
    },
    "laneHeaderWidth": 210,
    "columns": [
      "Freshness required",
      "Access strategy",
      "Where it materialises",
      "Refresh trigger"
    ],
    "rows": [
      {
        "title": "Customer profile",
        "cells": [
          [
            {
              "label": "Near real time"
            }
          ],
          [
            {
              "label": "Federated",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Nowhere"
            }
          ],
          [
            {
              "label": "Not applicable"
            }
          ]
        ]
      },
      {
        "title": "Contact details",
        "cells": [
          [
            {
              "label": "Near real time"
            }
          ],
          [
            {
              "label": "Federated, masked",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Nowhere"
            }
          ],
          [
            {
              "label": "Not applicable"
            }
          ]
        ]
      },
      {
        "title": "Account status",
        "cells": [
          [
            {
              "label": "Near real time"
            }
          ],
          [
            {
              "label": "Federated",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Nowhere"
            }
          ],
          [
            {
              "label": "Not applicable"
            }
          ]
        ]
      },
      {
        "title": "Orders, last 90 days",
        "cells": [
          [
            {
              "label": "Near real time"
            }
          ],
          [
            {
              "label": "Federated",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Nowhere"
            }
          ],
          [
            {
              "label": "Not applicable"
            }
          ]
        ]
      },
      {
        "title": "Order history, 3 years",
        "cells": [
          [
            {
              "label": "Daily"
            }
          ],
          [
            {
              "label": "MPP over Delta",
              "kind": "integration",
              "icon": "trino"
            }
          ],
          [
            {
              "label": "Silver tables",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Nightly ELT",
              "kind": "queue"
            }
          ]
        ]
      },
      {
        "title": "Open support cases",
        "cells": [
          [
            {
              "label": "Under 15 minutes"
            }
          ],
          [
            {
              "label": "Partial cache",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Cache database",
              "kind": "store",
              "icon": "postgresql"
            }
          ],
          [
            {
              "label": "Scheduler, 15 min",
              "kind": "queue"
            }
          ]
        ]
      },
      {
        "title": "Marketing engagement",
        "cells": [
          [
            {
              "label": "Under 1 hour"
            }
          ],
          [
            {
              "label": "Full cache",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Cache database",
              "kind": "store",
              "icon": "postgresql"
            }
          ],
          [
            {
              "label": "Scheduler, hourly",
              "kind": "queue"
            }
          ]
        ]
      },
      {
        "title": "Digital activity",
        "cells": [
          [
            {
              "label": "Under 60 seconds"
            }
          ],
          [
            {
              "label": "Federated on Delta",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Silver streaming",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Continuous",
              "kind": "queue"
            }
          ]
        ]
      },
      {
        "title": "Lifetime value, scores",
        "cells": [
          [
            {
              "label": "Daily"
            }
          ],
          [
            {
              "label": "Federated on gold",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Gold tables",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Model run, 03:00",
              "kind": "queue"
            }
          ]
        ]
      },
      {
        "title": "Aggregated KPIs",
        "cells": [
          [
            {
              "label": "Daily"
            }
          ],
          [
            {
              "label": "Denodo summary",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Summary table",
              "kind": "store"
            }
          ],
          [
            {
              "label": "After model run",
              "kind": "queue"
            }
          ]
        ]
      },
      {
        "title": "Identity crosswalk",
        "cells": [
          [
            {
              "label": "Immediate"
            }
          ],
          [
            {
              "label": "Federated only",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Never",
              "kind": "risk"
            }
          ],
          [
            {
              "label": "Merge event",
              "kind": "queue"
            }
          ]
        ]
      }
    ],
    "note": "This table is the architecture, not a tuning guide. A view author cannot choose a strategy; they declare a freshness class and the platform assigns one.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "18-caching-and-acceleration",
    "title": "Caching and Acceleration — What Answers a Query, and What Keeps It Honest",
    "layout": "flow",
    "canvas": {
      "width": 1800
    },
    "chain": true,
    "align": "middle",
    "stages": [
      {
        "title": "Request",
        "nodes": [
          {
            "id": "q-req",
            "label": "Customer360 query",
            "sub": "SQL or REST"
          }
        ]
      },
      {
        "title": "Planner decides",
        "nodes": [
          {
            "id": "q-d1",
            "label": "Cache still valid?",
            "kind": "decision"
          },
          {
            "id": "q-d2",
            "label": "Summary covers it?",
            "kind": "decision"
          },
          {
            "id": "q-d3",
            "label": "Source can delegate?",
            "kind": "decision"
          }
        ]
      },
      {
        "title": "Served from",
        "nodes": [
          {
            "id": "q-c",
            "label": "Cache tables",
            "sub": "PostgreSQL",
            "kind": "store",
            "icon": "postgresql"
          },
          {
            "id": "q-s",
            "label": "Summary tables",
            "sub": "pre-aggregated",
            "kind": "store",
            "icon": "fa5_table"
          },
          {
            "id": "q-m",
            "label": "MPP over Delta",
            "sub": "Presto workers",
            "kind": "store",
            "icon": "trino"
          },
          {
            "id": "q-src",
            "label": "Source system",
            "sub": "delegated SQL",
            "kind": "external",
            "icon": "fa5_industry"
          }
        ]
      },
      {
        "title": "Kept fresh by",
        "nodes": [
          {
            "id": "q-sch",
            "label": "Denodo Scheduler",
            "sub": "per-view TTL",
            "icon": "fa5_clock"
          },
          {
            "id": "q-inc",
            "label": "Incremental refresh",
            "sub": "on change column"
          },
          {
            "id": "q-evt",
            "label": "Merge event listener",
            "sub": "ECID changes",
            "kind": "queue"
          }
        ]
      },
      {
        "title": "Bounded by",
        "nodes": [
          {
            "id": "q-b1",
            "label": "Never-cache list",
            "sub": "crosswalk, consent",
            "kind": "risk"
          },
          {
            "id": "q-b2",
            "label": "Stampede control",
            "sub": "single refresher"
          },
          {
            "id": "q-b3",
            "label": "Stale-read budget",
            "sub": "declared per view"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "q-d1",
        "to": "q-c",
        "label": "hit"
      },
      {
        "from": "q-d3",
        "to": "q-src",
        "label": "delegate"
      },
      {
        "from": "q-evt",
        "to": "q-c",
        "label": "invalidate",
        "kind": "error"
      }
    ],
    "note": "A cache miss is a latency event; a stale cache after a merge is a correctness event. Only the second one is drawn in red.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "19-data-quality",
    "title": "Data Quality — Detected at the Source, Exposed on the 360, Fixed by the Owner",
    "layout": "swimlane",
    "canvas": {
      "width": 1820
    },
    "laneHeaderWidth": 160,
    "stages": [
      "Detect",
      "Classify",
      "Expose",
      "Remediate",
      "Prove"
    ],
    "lanes": [
      {
        "title": "Completeness",
        "cells": [
          [
            {
              "label": "Missing email rule"
            }
          ],
          [
            {
              "label": "Warn",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "dq_flag on the 360"
            }
          ],
          [
            {
              "label": "CRM owner task",
              "kind": "external"
            }
          ],
          [
            {
              "label": "Coverage 98.2%",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Validity",
        "cells": [
          [
            {
              "label": "Phone format rule"
            }
          ],
          [
            {
              "label": "Warn",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "dq_flag on the 360"
            }
          ],
          [
            {
              "label": "Source correction",
              "kind": "external"
            }
          ],
          [
            {
              "label": "Valid 96.4%",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Uniqueness",
        "cells": [
          [
            {
              "label": "Duplicate candidates"
            }
          ],
          [
            {
              "label": "Block",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "Held out of the 360",
              "kind": "risk"
            }
          ],
          [
            {
              "label": "Steward merge"
            }
          ],
          [
            {
              "label": "Duplicates 0.4%",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Consistency",
        "cells": [
          [
            {
              "label": "Address conflict"
            }
          ],
          [
            {
              "label": "Warn",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "Survivorship pick"
            }
          ],
          [
            {
              "label": "Precedence rule change"
            }
          ],
          [
            {
              "label": "Conflicts 1.1%",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Timeliness",
        "cells": [
          [
            {
              "label": "Stale cache check"
            }
          ],
          [
            {
              "label": "Block",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "Row marked stale",
              "kind": "risk"
            }
          ],
          [
            {
              "label": "Forced refresh"
            }
          ],
          [
            {
              "label": "Freshness 99.5%",
              "kind": "store"
            }
          ]
        ]
      }
    ],
    "note": "The platform never silently repairs a value. It publishes the defect beside the record and routes the fix to the system that owns it.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "20-customer360-query-sequence",
    "title": "Runtime — One Customer360 Query, End to End",
    "layout": "sequence",
    "canvas": {
      "width": 1780
    },
    "lifelines": [
      {
        "id": "bi",
        "label": "Power BI",
        "kind": "external"
      },
      {
        "id": "lb",
        "label": "Denodo Load Balancer",
        "kind": "integration",
        "icon": "pattern_loadbalancer"
      },
      {
        "id": "vdp",
        "label": "Virtual DataPort",
        "icon": "fa5_layer_group"
      },
      {
        "id": "xw",
        "label": "ECID Crosswalk",
        "kind": "store",
        "icon": "postgresql"
      },
      {
        "id": "ca",
        "label": "Cache Database",
        "kind": "store",
        "icon": "postgresql"
      },
      {
        "id": "sf",
        "label": "Salesforce",
        "kind": "external",
        "icon": "di2_salesforce"
      },
      {
        "id": "sap",
        "label": "SAP S/4HANA",
        "kind": "external",
        "icon": "fa5_industry"
      },
      {
        "id": "mpp",
        "label": "Embedded MPP",
        "icon": "trino"
      }
    ],
    "messages": [
      {
        "from": "bi",
        "to": "lb",
        "label": "SQL on Customer360",
        "kind": "call"
      },
      {
        "from": "lb",
        "to": "vdp",
        "label": "route to interactive pool",
        "kind": "call"
      },
      {
        "from": "vdp",
        "to": "vdp",
        "label": "validate token, resolve roles",
        "kind": "self"
      },
      {
        "from": "vdp",
        "to": "vdp",
        "label": "expand views, prune branches",
        "kind": "self"
      },
      {
        "from": "vdp",
        "to": "xw",
        "label": "enterprise ids for this region",
        "kind": "call"
      },
      {
        "from": "xw",
        "to": "vdp",
        "label": "4,812 ids",
        "kind": "return"
      },
      {
        "from": "vdp",
        "to": "ca",
        "label": "open cases, refreshed 4 min ago",
        "kind": "call"
      },
      {
        "from": "ca",
        "to": "vdp",
        "label": "case rows",
        "kind": "return"
      },
      {
        "from": "vdp",
        "to": "sf",
        "label": "SOQL, filtered and projected",
        "kind": "call"
      },
      {
        "from": "vdp",
        "to": "sap",
        "label": "delegated join and aggregate",
        "kind": "call"
      },
      {
        "from": "sf",
        "to": "vdp",
        "label": "profile rows",
        "kind": "return"
      },
      {
        "from": "sap",
        "to": "vdp",
        "label": "pre-aggregated totals",
        "kind": "return"
      },
      {
        "from": "vdp",
        "to": "mpp",
        "label": "three-year history scan",
        "kind": "call"
      },
      {
        "from": "mpp",
        "to": "vdp",
        "label": "aggregate over Delta",
        "kind": "return"
      },
      {
        "from": "vdp",
        "to": "vdp",
        "label": "apply row filter and column masks",
        "kind": "self"
      },
      {
        "from": "vdp",
        "to": "bi",
        "label": "result set, 1.9 s",
        "kind": "return"
      }
    ],
    "note": "Five sources, two of them never touched: the branch pruner removed marketing and billing because no projected column came from them.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "21-query-optimisation",
    "title": "Query Optimisation — How a Five-Source View Becomes Two Source Calls",
    "layout": "flow",
    "canvas": {
      "width": 1820
    },
    "chain": true,
    "align": "middle",
    "stages": [
      {
        "title": "Parse",
        "nodes": [
          {
            "id": "o-sql",
            "label": "Incoming statement",
            "sub": "SQL, REST or GraphQL"
          },
          {
            "id": "o-meta",
            "label": "Metadata lookup",
            "sub": "views, stats, policies",
            "kind": "store"
          }
        ]
      },
      {
        "title": "Rewrite",
        "nodes": [
          {
            "id": "o-exp",
            "label": "View expansion"
          },
          {
            "id": "o-pred",
            "label": "Predicate normalisation"
          },
          {
            "id": "o-const",
            "label": "Constant folding"
          }
        ]
      },
      {
        "title": "Prune",
        "nodes": [
          {
            "id": "o-br",
            "label": "Branch pruning",
            "sub": "unreferenced sources",
            "kind": "decision"
          },
          {
            "id": "o-col",
            "label": "Column trimming"
          },
          {
            "id": "o-part",
            "label": "Partition pruning",
            "sub": "Delta files skipped"
          }
        ]
      },
      {
        "title": "Delegate",
        "nodes": [
          {
            "id": "o-cap",
            "label": "Capability check",
            "sub": "what the source can do",
            "kind": "decision"
          },
          {
            "id": "o-agg",
            "label": "Aggregation pushdown"
          },
          {
            "id": "o-join",
            "label": "Join delegation",
            "sub": "same-source joins"
          }
        ]
      },
      {
        "title": "Execute",
        "nodes": [
          {
            "id": "o-cbo",
            "label": "Cost-based join order",
            "sub": "statistics driven"
          },
          {
            "id": "o-mpp",
            "label": "MPP offload",
            "sub": "when scan exceeds 10 M",
            "kind": "decision"
          },
          {
            "id": "o-par",
            "label": "Parallel fetch",
            "sub": "per data source pool"
          }
        ]
      },
      {
        "title": "Return",
        "nodes": [
          {
            "id": "o-sec",
            "label": "Row filter and masking",
            "kind": "security"
          },
          {
            "id": "o-res",
            "label": "Result to client"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "o-br",
        "to": "o-cap",
        "label": "two sources left"
      },
      {
        "from": "o-cap",
        "to": "o-mpp",
        "label": "cannot delegate",
        "kind": "error"
      }
    ],
    "note": "Masking is applied after execution, never pushed down. A masked column is never sent to a source system as a predicate, so masking cannot leak through a WHERE clause.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "22-api-request-sequence",
    "title": "Runtime — A Data Service Call from the Agent Desktop",
    "layout": "sequence",
    "canvas": {
      "width": 1780
    },
    "lifelines": [
      {
        "id": "app",
        "label": "Agent Desktop",
        "kind": "external",
        "icon": "fa5_headset"
      },
      {
        "id": "fd",
        "label": "Front Door and WAF",
        "kind": "integration"
      },
      {
        "id": "apim",
        "label": "API Management",
        "kind": "integration"
      },
      {
        "id": "id",
        "label": "Microsoft Entra ID",
        "kind": "security",
        "icon": "azure_AzureActiveDirectory__Identity"
      },
      {
        "id": "ds",
        "label": "VDP Data Services",
        "icon": "fa5_layer_group"
      },
      {
        "id": "xw",
        "label": "ECID Crosswalk",
        "kind": "store",
        "icon": "postgresql"
      },
      {
        "id": "src",
        "label": "Source Systems",
        "kind": "external",
        "icon": "fa5_industry"
      },
      {
        "id": "log",
        "label": "Log Analytics",
        "kind": "store"
      }
    ],
    "messages": [
      {
        "from": "app",
        "to": "fd",
        "label": "GET /v1/customers/{id}/summary",
        "kind": "call"
      },
      {
        "from": "fd",
        "to": "apim",
        "label": "forwarded, WAF passed",
        "kind": "call"
      },
      {
        "from": "apim",
        "to": "id",
        "label": "validate bearer token",
        "kind": "call"
      },
      {
        "from": "id",
        "to": "apim",
        "label": "claims and group memberships",
        "kind": "return"
      },
      {
        "from": "apim",
        "to": "apim",
        "label": "quota check, 600 per minute",
        "kind": "self"
      },
      {
        "from": "apim",
        "to": "ds",
        "label": "REST call with delegated token",
        "kind": "call"
      },
      {
        "from": "ds",
        "to": "xw",
        "label": "resolve caller id to enterprise id",
        "kind": "call"
      },
      {
        "from": "xw",
        "to": "ds",
        "label": "one enterprise id",
        "kind": "return"
      },
      {
        "from": "ds",
        "to": "src",
        "label": "delegated reads, four domains",
        "kind": "call"
      },
      {
        "from": "src",
        "to": "ds",
        "label": "three succeed, one times out",
        "kind": "return"
      },
      {
        "from": "ds",
        "to": "ds",
        "label": "mask by role, build status block",
        "kind": "self"
      },
      {
        "from": "ds",
        "to": "apim",
        "label": "200 with per-domain status",
        "kind": "return"
      },
      {
        "from": "apim",
        "to": "app",
        "label": "response in 1.4 s",
        "kind": "return"
      },
      {
        "from": "ds",
        "to": "log",
        "label": "who asked, what was returned",
        "kind": "async"
      }
    ],
    "note": "A partial answer is a 200 with an explicit per-domain status, not a 500 and not a silent omission. The client renders the gap; it never has to guess at it.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "23-digital-activity-pipeline",
    "title": "Real-Time Digital Activity — Sixty Seconds from Tap to the 360",
    "layout": "flow",
    "canvas": {
      "width": 1800
    },
    "chain": true,
    "align": "middle",
    "stages": [
      {
        "title": "Emit",
        "nodes": [
          {
            "id": "e-web",
            "label": "Web SDK",
            "sub": "page and product views",
            "kind": "external",
            "icon": "fa5_code"
          },
          {
            "id": "e-mob",
            "label": "Mobile SDK",
            "sub": "logins, taps",
            "kind": "external",
            "icon": "fa5_mobile_alt"
          }
        ]
      },
      {
        "title": "Transport",
        "nodes": [
          {
            "id": "e-eh",
            "label": "Event Hubs",
            "sub": "32 partitions",
            "kind": "queue",
            "icon": "azure_AzureEventHub__Analytics"
          },
          {
            "id": "e-sr",
            "label": "Schema Registry",
            "sub": "Avro, versioned",
            "kind": "store",
            "icon": "fa5_file_code"
          }
        ]
      },
      {
        "title": "Process",
        "nodes": [
          {
            "id": "e-ss",
            "label": "Structured Streaming",
            "sub": "Databricks job"
          },
          {
            "id": "e-enr",
            "label": "ECID enrichment",
            "sub": "device to customer",
            "icon": "fa5_id_card"
          },
          {
            "id": "e-ses",
            "label": "Sessionisation",
            "sub": "30 min window"
          }
        ]
      },
      {
        "title": "Land",
        "nodes": [
          {
            "id": "e-sv",
            "label": "Silver activity",
            "sub": "Delta, 13 months",
            "kind": "store"
          },
          {
            "id": "e-ck",
            "label": "Checkpoints",
            "sub": "exactly once",
            "kind": "store"
          },
          {
            "id": "e-hold",
            "label": "Unmatched holding",
            "sub": "device not yet resolved",
            "kind": "store"
          }
        ]
      },
      {
        "title": "Expose",
        "nodes": [
          {
            "id": "e-bv",
            "label": "bv_activity_delta",
            "sub": "federated, no cache"
          },
          {
            "id": "e-view",
            "label": "Customer Digital Activity",
            "sub": "business view"
          }
        ]
      },
      {
        "title": "Consume",
        "nodes": [
          {
            "id": "e-desk",
            "label": "Agent Desktop",
            "sub": "last activity",
            "kind": "external",
            "icon": "fa5_headset"
          },
          {
            "id": "e-ai",
            "label": "AI Assistant",
            "kind": "external",
            "icon": "azure_openai"
          },
          {
            "id": "e-pbi",
            "label": "Power BI",
            "sub": "hourly refresh",
            "kind": "external"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "e-eh",
        "to": "e-ss",
        "label": "60 s target",
        "kind": "async"
      },
      {
        "from": "e-enr",
        "to": "e-hold",
        "label": "no ECID yet",
        "kind": "error"
      }
    ],
    "note": "Denodo reads the Delta table directly rather than the stream. Nothing in the logical layer is stateful, so an outage here shows as staleness, not as a broken view.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "24-ai-assistant-flow",
    "title": "Runtime — The AI Assistant Inherits the Agent's Entitlements",
    "layout": "sequence",
    "canvas": {
      "width": 1800
    },
    "lifelines": [
      {
        "id": "ag",
        "label": "Service Agent",
        "kind": "actor",
        "icon": "fa5_headset"
      },
      {
        "id": "ui",
        "label": "Assistant UI",
        "kind": "external",
        "icon": "fa5_comments"
      },
      {
        "id": "en",
        "label": "Microsoft Entra ID",
        "kind": "security",
        "icon": "azure_AzureActiveDirectory__Identity"
      },
      {
        "id": "oa",
        "label": "Azure OpenAI",
        "kind": "external"
      },
      {
        "id": "mcp",
        "label": "Denodo MCP Server",
        "kind": "integration",
        "icon": "fa5_plug"
      },
      {
        "id": "ds",
        "label": "VDP Data Services",
        "icon": "fa5_layer_group"
      },
      {
        "id": "src",
        "label": "Sources and Lakehouse",
        "kind": "external",
        "icon": "fa5_industry"
      },
      {
        "id": "au",
        "label": "Audit Log",
        "kind": "store",
        "icon": "fa5_archive"
      }
    ],
    "messages": [
      {
        "from": "ag",
        "to": "ui",
        "label": "Why is this invoice unpaid?",
        "kind": "call"
      },
      {
        "from": "ui",
        "to": "en",
        "label": "on-behalf-of token for this agent",
        "kind": "call"
      },
      {
        "from": "en",
        "to": "ui",
        "label": "delegated token, agent scopes",
        "kind": "return"
      },
      {
        "from": "ui",
        "to": "oa",
        "label": "prompt and tool schema",
        "kind": "call"
      },
      {
        "from": "oa",
        "to": "mcp",
        "label": "call get_customer_summary",
        "kind": "call"
      },
      {
        "from": "mcp",
        "to": "ds",
        "label": "query as the agent, not as a service",
        "kind": "call"
      },
      {
        "from": "ds",
        "to": "ds",
        "label": "row filter and masks applied",
        "kind": "self"
      },
      {
        "from": "ds",
        "to": "src",
        "label": "delegated reads",
        "kind": "call"
      },
      {
        "from": "src",
        "to": "ds",
        "label": "rows",
        "kind": "return"
      },
      {
        "from": "ds",
        "to": "mcp",
        "label": "masked rows with lineage refs",
        "kind": "return"
      },
      {
        "from": "mcp",
        "to": "oa",
        "label": "tool result and citations",
        "kind": "return"
      },
      {
        "from": "oa",
        "to": "mcp",
        "label": "call get_full_card_number",
        "kind": "call"
      },
      {
        "from": "mcp",
        "to": "oa",
        "label": "denied, not in the agent's grants",
        "kind": "error"
      },
      {
        "from": "oa",
        "to": "ui",
        "label": "answer grounded on what was returned",
        "kind": "return"
      },
      {
        "from": "ui",
        "to": "ag",
        "label": "answer with named sources",
        "kind": "return"
      },
      {
        "from": "ds",
        "to": "au",
        "label": "prompt, tools called, rows returned",
        "kind": "async"
      }
    ],
    "note": "The model has no credential of its own. Step 13 is refused by the same policy that would refuse the agent in Power BI, which is why the assistant needed no security design of its own.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "25-source-outage-degradation",
    "title": "When a Source Goes Down — Partial, Labelled, and Never Silent",
    "layout": "swimlane",
    "canvas": {
      "width": 1820
    },
    "laneHeaderWidth": 150,
    "stages": [
      "Healthy",
      "Detect",
      "Degrade",
      "Inform",
      "Recover"
    ],
    "lanes": [
      {
        "title": "Source",
        "cells": [
          [
            {
              "label": "ServiceNow responding",
              "kind": "external",
              "icon": "fa5_life_ring"
            }
          ],
          [
            {
              "label": "Latency past 3 s",
              "kind": "risk"
            }
          ],
          [
            {
              "label": "Errors above 25%",
              "kind": "risk"
            }
          ],
          [
            {
              "label": "Vendor incident open",
              "kind": "external"
            }
          ],
          [
            {
              "label": "Probe succeeds",
              "kind": "external"
            }
          ]
        ]
      },
      {
        "title": "Denodo",
        "cells": [
          [
            {
              "label": "Full 360 assembled"
            }
          ],
          [
            {
              "label": "Timeout at 3 s",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "Circuit opens"
            },
            {
              "label": "Partial result built"
            }
          ],
          [
            {
              "label": "Per-domain status set"
            }
          ],
          [
            {
              "label": "Half-open retry"
            }
          ]
        ]
      },
      {
        "title": "Consumer",
        "cells": [
          [
            {
              "label": "Every panel populated",
              "kind": "external"
            }
          ],
          [
            {
              "label": "No visible change",
              "kind": "external"
            }
          ],
          [
            {
              "label": "Support panel greyed",
              "kind": "external"
            }
          ],
          [
            {
              "label": "Cases unavailable, 09:14",
              "kind": "external"
            }
          ],
          [
            {
              "label": "Panel repopulates",
              "kind": "external"
            }
          ]
        ]
      },
      {
        "title": "Operator",
        "cells": [
          [
            {
              "label": "Source SLO green",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Alert on error budget",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Runbook opened",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Status page updated",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Post-incident review",
              "kind": "security"
            }
          ]
        ]
      }
    ],
    "note": "This is the answer to the agent journey's worst moment. An empty panel and an unreachable source must never look the same, so the contract carries a status per domain rather than one status per response.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "26-dsar-and-erasure",
    "title": "Runtime — Subject Access and Erasure Across Every Copy",
    "layout": "sequence",
    "canvas": {
      "width": 1800
    },
    "lifelines": [
      {
        "id": "subj",
        "label": "Data Subject",
        "kind": "actor",
        "icon": "role_enduser"
      },
      {
        "id": "dpo",
        "label": "Privacy Officer",
        "kind": "actor",
        "icon": "fa5_user_shield"
      },
      {
        "id": "con",
        "label": "DSAR Console",
        "kind": "external"
      },
      {
        "id": "xw",
        "label": "ECID Crosswalk",
        "kind": "store",
        "icon": "postgresql"
      },
      {
        "id": "vdp",
        "label": "Virtual DataPort",
        "icon": "fa5_layer_group"
      },
      {
        "id": "src",
        "label": "Source Systems",
        "kind": "external",
        "icon": "fa5_industry"
      },
      {
        "id": "lake",
        "label": "Lakehouse",
        "kind": "store"
      },
      {
        "id": "au",
        "label": "Audit Log",
        "kind": "store",
        "icon": "fa5_archive"
      }
    ],
    "messages": [
      {
        "from": "subj",
        "to": "dpo",
        "label": "subject access request",
        "kind": "call"
      },
      {
        "from": "dpo",
        "to": "con",
        "label": "open case, verify identity",
        "kind": "call"
      },
      {
        "from": "con",
        "to": "xw",
        "label": "every source id for this person",
        "kind": "call"
      },
      {
        "from": "xw",
        "to": "con",
        "label": "five identifiers, five systems",
        "kind": "return"
      },
      {
        "from": "con",
        "to": "vdp",
        "label": "assemble the full record, unmasked",
        "kind": "call"
      },
      {
        "from": "vdp",
        "to": "src",
        "label": "delegated reads",
        "kind": "call"
      },
      {
        "from": "vdp",
        "to": "lake",
        "label": "activity, scores, history",
        "kind": "call"
      },
      {
        "from": "vdp",
        "to": "con",
        "label": "complete record with provenance",
        "kind": "return"
      },
      {
        "from": "con",
        "to": "dpo",
        "label": "export pack, signed",
        "kind": "return"
      },
      {
        "from": "dpo",
        "to": "src",
        "label": "erasure instruction per system",
        "kind": "async"
      },
      {
        "from": "dpo",
        "to": "lake",
        "label": "erase and rewrite partitions",
        "kind": "async"
      },
      {
        "from": "con",
        "to": "xw",
        "label": "tombstone the enterprise id",
        "kind": "call"
      },
      {
        "from": "con",
        "to": "vdp",
        "label": "invalidate every cached row",
        "kind": "async"
      },
      {
        "from": "con",
        "to": "au",
        "label": "who exported, who erased, when",
        "kind": "async"
      }
    ],
    "note": "The crosswalk is what makes erasure provable: it is the only place that knows all five identifiers. Denodo finds the copies; the owning system performs the deletion.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "27-merge-propagation",
    "title": "A Merge Propagates — From Steward Approval to Every Consumer",
    "layout": "flow",
    "canvas": {
      "width": 1800
    },
    "chain": true,
    "align": "middle",
    "stages": [
      {
        "title": "Approved",
        "nodes": [
          {
            "id": "m-app",
            "label": "Steward approves",
            "sub": "two records, one person"
          }
        ]
      },
      {
        "title": "Crosswalk changes",
        "nodes": [
          {
            "id": "m-tomb",
            "label": "Losing id tombstoned",
            "sub": "never reused",
            "kind": "store"
          },
          {
            "id": "m-keep",
            "label": "Both source ids kept",
            "sub": "traceability",
            "kind": "store"
          },
          {
            "id": "m-surv",
            "label": "Survivorship applied",
            "sub": "attribute precedence",
            "icon": "fa5_balance_scale"
          }
        ]
      },
      {
        "title": "Notify",
        "nodes": [
          {
            "id": "m-evt",
            "label": "Merge event published",
            "sub": "Event Hubs topic",
            "kind": "queue"
          }
        ]
      },
      {
        "title": "Invalidate",
        "nodes": [
          {
            "id": "m-lis",
            "label": "Cache listener",
            "sub": "VDP stored procedure"
          },
          {
            "id": "m-drop",
            "label": "Affected rows dropped",
            "sub": "both enterprise ids",
            "kind": "store"
          },
          {
            "id": "m-sum",
            "label": "Summaries flagged",
            "sub": "rebuild on next run",
            "icon": "fa5_table"
          }
        ]
      },
      {
        "title": "Rebuild",
        "nodes": [
          {
            "id": "m-next",
            "label": "Next query repopulates",
            "sub": "cold read, 2.6 s"
          },
          {
            "id": "m-night",
            "label": "Nightly summary rebuild",
            "sub": "03:30"
          }
        ]
      },
      {
        "title": "Verified",
        "nodes": [
          {
            "id": "m-rec",
            "label": "Reconciliation check",
            "sub": "hourly"
          },
          {
            "id": "m-ok",
            "label": "Consistent within 1 hour",
            "kind": "opportunity"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "m-evt",
        "to": "m-lis",
        "label": "at-least-once",
        "kind": "async"
      },
      {
        "from": "m-lis",
        "to": "m-rec",
        "label": "listener down, stale to TTL",
        "kind": "error",
        "route": "gutter"
      }
    ],
    "note": "The red path is the residual risk: if the listener misses an event, the reconciliation job is what closes the gap, and the cache TTL bounds it at 24 hours.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "28-deployment-architecture",
    "title": "Deployment — Three Zones Live, One Region Warm",
    "layout": "nested",
    "canvas": {
      "width": 1800
    },
    "boxes": [
      {
        "title": "Azure North Europe — primary, active",
        "kind": "cloud",
        "dir": "row",
        "children": [
          {
            "title": "Availability zone 1",
            "kind": "boundary",
            "nodes": [
              {
                "id": "y-i1",
                "label": "VDP Interactive",
                "sub": "1 pod, 8 vCPU",
                "icon": "fa5_layer_group"
              },
              {
                "id": "y-a1",
                "label": "VDP Data Services",
                "sub": "1 pod, 8 vCPU",
                "icon": "fa5_layer_group"
              },
              {
                "id": "y-m1",
                "label": "MPP workers",
                "sub": "2 nodes",
                "icon": "trino"
              }
            ]
          },
          {
            "title": "Availability zone 2",
            "kind": "boundary",
            "nodes": [
              {
                "id": "y-i2",
                "label": "VDP Interactive",
                "sub": "1 pod, 8 vCPU",
                "icon": "fa5_layer_group"
              },
              {
                "id": "y-a2",
                "label": "VDP Data Services",
                "sub": "1 pod, 8 vCPU",
                "icon": "fa5_layer_group"
              },
              {
                "id": "y-m2",
                "label": "MPP workers",
                "sub": "2 nodes",
                "icon": "trino"
              }
            ]
          },
          {
            "title": "Availability zone 3",
            "kind": "boundary",
            "nodes": [
              {
                "id": "y-i3",
                "label": "VDP Interactive",
                "sub": "1 pod, 8 vCPU",
                "icon": "fa5_layer_group"
              },
              {
                "id": "y-b3",
                "label": "VDP Analytical",
                "sub": "2 pods, 16 vCPU",
                "icon": "fa5_layer_group"
              },
              {
                "id": "y-m3",
                "label": "MPP workers",
                "sub": "2 nodes",
                "icon": "trino"
              }
            ]
          },
          {
            "title": "Zone-redundant services",
            "kind": "boundary",
            "nodes": [
              {
                "id": "y-pg",
                "label": "PostgreSQL Flexible",
                "sub": "ZRS, HA pair",
                "kind": "store",
                "icon": "postgresql"
              },
              {
                "id": "y-adls",
                "label": "ADLS Gen2",
                "sub": "ZRS",
                "kind": "store"
              },
              {
                "id": "y-kv",
                "label": "Key Vault",
                "sub": "zone redundant",
                "kind": "security"
              },
              {
                "id": "y-eh",
                "label": "Event Hubs",
                "sub": "ZRS namespace",
                "kind": "queue",
                "icon": "azure_AzureEventHub__Analytics"
              }
            ]
          }
        ]
      },
      {
        "title": "Azure West Europe — warm standby",
        "kind": "cloud",
        "dir": "row",
        "children": [
          {
            "title": "Compute, scaled down",
            "kind": "boundary",
            "nodes": [
              {
                "id": "y-dr1",
                "label": "VDP pods",
                "sub": "1 per pool, idle",
                "icon": "fa5_layer_group"
              },
              {
                "id": "y-dr2",
                "label": "Solution Manager",
                "sub": "standby node",
                "icon": "fa5_tasks"
              }
            ]
          },
          {
            "title": "Replicated state",
            "kind": "boundary",
            "nodes": [
              {
                "id": "y-dr3",
                "label": "PostgreSQL replica",
                "sub": "geo, RPO 5 min",
                "kind": "store",
                "icon": "postgresql"
              },
              {
                "id": "y-dr4",
                "label": "ADLS GRS",
                "sub": "asynchronous",
                "kind": "store"
              },
              {
                "id": "y-dr5",
                "label": "Metadata replica",
                "sub": "revision synced",
                "kind": "store"
              }
            ]
          }
        ]
      }
    ],
    "outside": [
      {
        "id": "y-sf",
        "label": "Salesforce",
        "kind": "external",
        "icon": "di2_salesforce"
      },
      {
        "id": "y-sap",
        "label": "SAP S/4HANA",
        "kind": "external",
        "icon": "fa5_industry"
      },
      {
        "id": "y-sn",
        "label": "ServiceNow",
        "kind": "external",
        "icon": "fa5_life_ring"
      },
      {
        "id": "y-dns",
        "label": "Traffic Manager",
        "sub": "health-probed failover",
        "kind": "integration",
        "icon": "azure_traffic-manager-profiles"
      }
    ],
    "edges": [
      {
        "from": "y-pg",
        "to": "y-dr3",
        "label": "geo-replication",
        "kind": "async"
      },
      {
        "from": "y-adls",
        "to": "y-dr4",
        "label": "GRS",
        "kind": "async"
      },
      {
        "from": "y-dns",
        "to": "y-dr1",
        "label": "failover",
        "kind": "error"
      }
    ],
    "note": "Losing a zone loses a third of capacity and no session state. Losing the region costs 30 minutes to RTO, and the cache is deliberately not replicated because it is rebuildable.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "29-workload-isolation",
    "title": "Workload Isolation — Why a Notebook Cannot Slow the Contact Centre",
    "layout": "nested",
    "canvas": {
      "width": 1780
    },
    "boxes": [
      {
        "title": "One Denodo estate, three isolated workload classes",
        "kind": "boundary",
        "dir": "row",
        "children": [
          {
            "title": "Interactive — target under 2 s",
            "kind": "lane",
            "nodes": [
              {
                "id": "w-i1",
                "label": "VDP Interactive",
                "sub": "3 pods",
                "icon": "fa5_layer_group"
              },
              {
                "id": "w-i2",
                "label": "Connection pool",
                "sub": "200 per source"
              },
              {
                "id": "w-i3",
                "label": "Query timeout",
                "sub": "8 seconds",
                "kind": "decision"
              },
              {
                "id": "w-i4",
                "label": "Cache-first plans"
              }
            ]
          },
          {
            "title": "Data services — target under 2 s",
            "kind": "lane",
            "nodes": [
              {
                "id": "w-a1",
                "label": "VDP Data Services",
                "sub": "2 pods",
                "icon": "fa5_layer_group"
              },
              {
                "id": "w-a2",
                "label": "APIM quota",
                "sub": "600 per minute"
              },
              {
                "id": "w-a3",
                "label": "Query timeout",
                "sub": "5 seconds",
                "kind": "decision"
              },
              {
                "id": "w-a4",
                "label": "No ad hoc SQL",
                "sub": "published views only"
              }
            ]
          },
          {
            "title": "Analytical — minutes are acceptable",
            "kind": "lane",
            "nodes": [
              {
                "id": "w-b1",
                "label": "VDP Analytical",
                "sub": "2 pods",
                "icon": "fa5_layer_group"
              },
              {
                "id": "w-b2",
                "label": "MPP workers",
                "sub": "6, autoscaled",
                "icon": "trino"
              },
              {
                "id": "w-b3",
                "label": "Query timeout",
                "sub": "30 minutes",
                "kind": "decision"
              },
              {
                "id": "w-b4",
                "label": "Row limit",
                "sub": "50 million"
              }
            ]
          }
        ]
      },
      {
        "title": "Shared below the line, and therefore governed by quota",
        "kind": "plain",
        "nodes": [
          {
            "id": "w-s1",
            "label": "Source connection pools",
            "sub": "per-class ceilings",
            "kind": "risk"
          },
          {
            "id": "w-s2",
            "label": "Cache database",
            "sub": "shared, sized for peak",
            "kind": "store",
            "icon": "postgresql"
          },
          {
            "id": "w-s3",
            "label": "Metadata database",
            "sub": "read-mostly",
            "kind": "store",
            "icon": "postgresql"
          },
          {
            "id": "w-s4",
            "label": "Licence cores",
            "sub": "the real ceiling",
            "kind": "risk"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "w-b2",
        "to": "w-s1",
        "label": "capped at 40%",
        "kind": "error"
      },
      {
        "from": "w-i2",
        "to": "w-s1",
        "label": "priority share"
      }
    ],
    "note": "Isolation stops at the source. A single ERP connection pool is the one thing all three classes still share, which is why it is drawn as a risk and capped per class.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "30-cicd-and-environments",
    "title": "How a View Change Reaches Production, and What Stops a Bad One",
    "layout": "flow",
    "canvas": {
      "width": 1840
    },
    "chain": true,
    "align": "middle",
    "stages": [
      {
        "title": "Author",
        "nodes": [
          {
            "id": "p-ds",
            "label": "Design Studio",
            "sub": "development server",
            "icon": "fa5_drafting_compass"
          },
          {
            "id": "p-vql",
            "label": "VQL export",
            "sub": "text, diffable"
          }
        ]
      },
      {
        "title": "Version",
        "nodes": [
          {
            "id": "p-git",
            "label": "Git repository",
            "sub": "Azure Repos",
            "kind": "store"
          },
          {
            "id": "p-pr",
            "label": "Pull request",
            "sub": "one view set"
          }
        ]
      },
      {
        "title": "Validate",
        "nodes": [
          {
            "id": "p-lint",
            "label": "VQL lint",
            "sub": "naming, layering",
            "icon": "fa5_spell_check"
          },
          {
            "id": "p-reg",
            "label": "Regression suite",
            "sub": "180 queries",
            "icon": "fa5_clipboard_check"
          },
          {
            "id": "p-pol",
            "label": "Policy scan",
            "sub": "no unmasked PII",
            "icon": "fa5_shield_alt"
          }
        ]
      },
      {
        "title": "Approve",
        "nodes": [
          {
            "id": "p-peer",
            "label": "Peer review",
            "kind": "decision"
          },
          {
            "id": "p-stew",
            "label": "Steward sign-off",
            "sub": "certified views only",
            "kind": "decision"
          }
        ]
      },
      {
        "title": "Promote",
        "nodes": [
          {
            "id": "p-sm",
            "label": "Solution Manager",
            "sub": "revision, one artefact",
            "icon": "fa5_tasks"
          },
          {
            "id": "p-prop",
            "label": "Environment properties",
            "sub": "no hard-coded hosts"
          }
        ]
      },
      {
        "title": "Environments",
        "nodes": [
          {
            "id": "p-test",
            "label": "Test",
            "sub": "masked subset",
            "icon": "fa5_vial"
          },
          {
            "id": "p-uat",
            "label": "UAT",
            "sub": "production shape",
            "icon": "fa5_clipboard_check"
          },
          {
            "id": "p-prod",
            "label": "Production",
            "sub": "change window",
            "icon": "fa5_server"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "p-reg",
        "to": "p-pr",
        "label": "row counts differ",
        "kind": "error",
        "route": "gutter"
      },
      {
        "from": "p-prod",
        "to": "p-sm",
        "label": "rollback to prior revision",
        "kind": "error",
        "route": "gutter"
      }
    ],
    "note": "The regression suite asserts row counts and column sets against a frozen dataset, so a widened join is caught before UAT rather than by a report that quietly doubles.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "31-observability",
    "title": "Observability — Every Signal, From Emission to the Person Who Acts",
    "layout": "grid",
    "canvas": {
      "width": 1860
    },
    "laneHeaderWidth": 190,
    "columns": [
      "Emitted by",
      "Collected by",
      "Stored in",
      "Consumed by",
      "Acted on by"
    ],
    "rows": [
      {
        "title": "Query performance",
        "cells": [
          [
            {
              "label": "VDP JMX metrics"
            }
          ],
          [
            {
              "label": "Prometheus exporter",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Azure Monitor",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Grafana dashboard"
            }
          ],
          [
            {
              "label": "SRE on call",
              "kind": "actor",
              "icon": "role_devops"
            }
          ]
        ]
      },
      {
        "title": "Source health",
        "cells": [
          [
            {
              "label": "Data source probes"
            }
          ],
          [
            {
              "label": "Denodo Monitor",
              "kind": "integration",
              "icon": "fa5_eye"
            }
          ],
          [
            {
              "label": "Log Analytics",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Source SLO board"
            }
          ],
          [
            {
              "label": "Circuit breaker",
              "kind": "decision"
            }
          ]
        ]
      },
      {
        "title": "Cache effectiveness",
        "cells": [
          [
            {
              "label": "Hit and miss counters"
            }
          ],
          [
            {
              "label": "Prometheus exporter",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Azure Monitor",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Materialisation review"
            }
          ],
          [
            {
              "label": "Scheduler tuning"
            }
          ]
        ]
      },
      {
        "title": "Slow and failed queries",
        "cells": [
          [
            {
              "label": "VDP query log"
            }
          ],
          [
            {
              "label": "Diagnostic tool",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Log Analytics",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Weekly slow report"
            }
          ],
          [
            {
              "label": "View owner",
              "kind": "actor",
              "icon": "role_developer"
            }
          ]
        ]
      },
      {
        "title": "API usage",
        "cells": [
          [
            {
              "label": "APIM telemetry"
            }
          ],
          [
            {
              "label": "Application Insights",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Log Analytics",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Consumer report"
            }
          ],
          [
            {
              "label": "Quota change"
            }
          ]
        ]
      },
      {
        "title": "Dataset usage",
        "cells": [
          [
            {
              "label": "Catalog access log"
            }
          ],
          [
            {
              "label": "Catalog exporter",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Log Analytics",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Certification review"
            }
          ],
          [
            {
              "label": "Data steward",
              "kind": "actor"
            }
          ]
        ]
      },
      {
        "title": "Access and audit",
        "cells": [
          [
            {
              "label": "VDP audit events"
            }
          ],
          [
            {
              "label": "Diagnostic tool",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Microsoft Sentinel",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Detection rules"
            }
          ],
          [
            {
              "label": "Security operations",
              "kind": "actor",
              "icon": "role_secops"
            }
          ]
        ]
      },
      {
        "title": "Cost",
        "cells": [
          [
            {
              "label": "Licence core usage"
            }
          ],
          [
            {
              "label": "Azure cost export",
              "kind": "integration"
            }
          ],
          [
            {
              "label": "Cost workspace",
              "kind": "store"
            }
          ],
          [
            {
              "label": "Chargeback report"
            }
          ],
          [
            {
              "label": "Platform owner",
              "kind": "actor",
              "icon": "person_exec"
            }
          ]
        ]
      }
    ],
    "note": "Every row ends in a named person or an automated control. A signal with no one in the last column is a dashboard, not an operational capability.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "32-materialisation-loop",
    "title": "The Operating Loop — How the Platform Decides What to Materialise Next",
    "layout": "cycle",
    "canvas": {
      "width": 1500
    },
    "centre": {
      "label": "Materialisation loop",
      "icon": "fa5_sync_alt"
    },
    "nodes": [
      {
        "id": "cy1",
        "label": "Usage telemetry",
        "sub": "query log, catalog hits"
      },
      {
        "id": "cy2",
        "label": "Slow query analysis",
        "sub": "top 20 by cost"
      },
      {
        "id": "cy3",
        "label": "Freshness class review",
        "sub": "with the data owner"
      },
      {
        "id": "cy4",
        "label": "Cache or summary change",
        "sub": "one artefact"
      },
      {
        "id": "cy5",
        "label": "Promotion through gates",
        "sub": "same pipeline as a view"
      },
      {
        "id": "cy6",
        "label": "Measure the effect",
        "sub": "P95 and source load"
      }
    ],
    "ringLabels": [
      "what is actually used",
      "candidates",
      "agreed staleness",
      "revision",
      "new baseline",
      "next month's signals"
    ],
    "rx": 430,
    "ry": 220,
    "note": "Materialisation is a governed change, not a tuning knob. A summary added without an owner agreeing its staleness is a correctness decision taken by an engineer.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "33-cost-model",
    "title": "Cost Model — What Drives Spend, and Which Levers Move It",
    "layout": "flow",
    "canvas": {
      "width": 1820
    },
    "chain": false,
    "align": "top",
    "stages": [
      {
        "title": "Fixed, whatever the load",
        "nodes": [
          {
            "id": "$1",
            "label": "Denodo licence cores",
            "sub": "largest single line"
          },
          {
            "id": "$2",
            "label": "Control tier",
            "sub": "Solution Manager, Studio"
          },
          {
            "id": "$3",
            "label": "Baseline AKS nodes",
            "sub": "3 zones, minimum"
          }
        ]
      },
      {
        "title": "Scales with concurrency",
        "nodes": [
          {
            "id": "$4",
            "label": "VDP pods",
            "sub": "per workload class",
            "icon": "fa5_layer_group"
          },
          {
            "id": "$5",
            "label": "API Management tier",
            "sub": "premium, zonal"
          },
          {
            "id": "$6",
            "label": "Source connections",
            "sub": "vendor call limits"
          }
        ]
      },
      {
        "title": "Scales with data",
        "nodes": [
          {
            "id": "$7",
            "label": "MPP workers",
            "sub": "per TB scanned",
            "icon": "trino"
          },
          {
            "id": "$8",
            "label": "Cache database",
            "sub": "storage and IOPS",
            "icon": "postgresql"
          },
          {
            "id": "$9",
            "label": "ADLS and Delta",
            "sub": "7-year retention"
          },
          {
            "id": "$10",
            "label": "Databricks SQL",
            "sub": "gold serving"
          }
        ]
      },
      {
        "title": "Scales with movement",
        "nodes": [
          {
            "id": "$11",
            "label": "Event Hubs units",
            "sub": "throughput"
          },
          {
            "id": "$12",
            "label": "CDC and egress",
            "sub": "on-premises links"
          },
          {
            "id": "$13",
            "label": "Geo-replication",
            "sub": "DR only"
          }
        ]
      },
      {
        "title": "Levers that actually move it",
        "nodes": [
          {
            "id": "$14",
            "label": "Summary over MPP",
            "sub": "cheapest per query",
            "kind": "opportunity"
          },
          {
            "id": "$15",
            "label": "Shorter TTL only where needed",
            "kind": "opportunity"
          },
          {
            "id": "$16",
            "label": "Federate rather than copy",
            "sub": "no storage, no pipeline",
            "kind": "opportunity"
          },
          {
            "id": "$17",
            "label": "Right-size the analytical pool",
            "kind": "opportunity"
          }
        ]
      }
    ],
    "note": "Virtualisation removes pipeline and storage cost and adds query cost. The platform is cheaper than a physical hub only while the freshness contract keeps expensive queries off the critical path.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "34-security-zones",
    "title": "Trust Zones — What Crosses Each Boundary, and What Proves It May",
    "layout": "zones",
    "canvas": {
      "width": 1820
    },
    "zones": [
      {
        "title": "Internet — untrusted",
        "kind": "trust",
        "nodes": [
          {
            "id": "sz-cli",
            "label": "Browser and mobile",
            "kind": "external"
          },
          {
            "id": "sz-r1",
            "label": "Credential stuffing",
            "kind": "risk"
          },
          {
            "id": "sz-r2",
            "label": "Scripted extraction",
            "kind": "risk"
          }
        ]
      },
      {
        "title": "Perimeter — Azure edge",
        "kind": "trust",
        "nodes": [
          {
            "id": "sz-fd",
            "label": "Front Door and WAF",
            "sub": "TLS 1.3, OWASP set",
            "kind": "integration"
          },
          {
            "id": "sz-apim",
            "label": "API Management",
            "sub": "JWT, quota, schema",
            "kind": "integration"
          },
          {
            "id": "sz-ddos",
            "label": "DDoS Protection",
            "kind": "security"
          }
        ]
      },
      {
        "title": "Application — private virtual network",
        "kind": "trust",
        "nodes": [
          {
            "id": "sz-vdp",
            "label": "VDP pods",
            "sub": "no public address",
            "icon": "fa5_layer_group"
          },
          {
            "id": "sz-cat",
            "label": "Data Catalog",
            "icon": "fa5_book_open"
          },
          {
            "id": "sz-ds",
            "label": "Design Studio",
            "sub": "named users only",
            "icon": "fa5_drafting_compass"
          },
          {
            "id": "sz-sm",
            "label": "Solution Manager",
            "icon": "fa5_tasks"
          }
        ]
      },
      {
        "title": "Data — reachable by private endpoint only",
        "kind": "trust",
        "nodes": [
          {
            "id": "sz-pg",
            "label": "Cache database",
            "kind": "store",
            "icon": "postgresql"
          },
          {
            "id": "sz-xw",
            "label": "ECID Crosswalk",
            "kind": "store",
            "icon": "postgresql"
          },
          {
            "id": "sz-adls",
            "label": "ADLS Gen2",
            "sub": "CMK encrypted",
            "kind": "store"
          },
          {
            "id": "sz-dbx",
            "label": "Databricks",
            "sub": "no public workspace"
          }
        ]
      },
      {
        "title": "Control — separate subscription, separate administrators",
        "kind": "trust",
        "nodes": [
          {
            "id": "sz-kv",
            "label": "Key Vault",
            "sub": "HSM-backed keys",
            "kind": "security"
          },
          {
            "id": "sz-entra",
            "label": "Microsoft Entra ID",
            "sub": "conditional access",
            "kind": "security",
            "icon": "azure_AzureActiveDirectory__Identity"
          },
          {
            "id": "sz-sen",
            "label": "Microsoft Sentinel",
            "sub": "write-once sink",
            "kind": "security"
          },
          {
            "id": "sz-arch",
            "label": "Audit archive",
            "sub": "13 months, immutable",
            "kind": "store",
            "icon": "fa5_archive"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "sz-cli",
        "to": "sz-fd",
        "label": "TLS 1.3, WAF"
      },
      {
        "from": "sz-r1",
        "to": "sz-apim",
        "label": "blocked",
        "kind": "error"
      },
      {
        "from": "sz-apim",
        "to": "sz-vdp",
        "label": "bearer, mTLS"
      },
      {
        "from": "sz-vdp",
        "to": "sz-pg",
        "label": "Private Link"
      },
      {
        "from": "sz-vdp",
        "to": "sz-kv",
        "label": "managed identity"
      },
      {
        "from": "sz-vdp",
        "to": "sz-sen",
        "label": "audit",
        "kind": "async"
      }
    ],
    "note": "No secret is held by a consumer and none by a pod. Every credential is fetched at runtime under a managed identity, and the audit path is one-directional by design.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "35-identity-and-access",
    "title": "Identity — From a Sign-In to a Filtered, Masked Result Set",
    "layout": "sequence",
    "canvas": {
      "width": 1800
    },
    "lifelines": [
      {
        "id": "u",
        "label": "Relationship Manager",
        "kind": "actor",
        "icon": "person_exec"
      },
      {
        "id": "cl",
        "label": "Power BI",
        "kind": "external"
      },
      {
        "id": "en",
        "label": "Microsoft Entra ID",
        "kind": "security",
        "icon": "azure_AzureActiveDirectory__Identity"
      },
      {
        "id": "lb",
        "label": "Denodo Load Balancer",
        "kind": "integration",
        "icon": "pattern_loadbalancer"
      },
      {
        "id": "vdp",
        "label": "Virtual DataPort",
        "icon": "fa5_layer_group"
      },
      {
        "id": "kv",
        "label": "Key Vault",
        "kind": "security"
      },
      {
        "id": "src",
        "label": "SAP S/4HANA",
        "kind": "external",
        "icon": "fa5_industry"
      },
      {
        "id": "sen",
        "label": "Microsoft Sentinel",
        "kind": "store"
      }
    ],
    "messages": [
      {
        "from": "u",
        "to": "cl",
        "label": "opens the account report",
        "kind": "call"
      },
      {
        "from": "cl",
        "to": "en",
        "label": "OIDC authorisation code flow",
        "kind": "call"
      },
      {
        "from": "en",
        "to": "cl",
        "label": "access token with group claims",
        "kind": "return"
      },
      {
        "from": "cl",
        "to": "lb",
        "label": "ODBC connection, bearer token",
        "kind": "call"
      },
      {
        "from": "lb",
        "to": "vdp",
        "label": "least-busy node",
        "kind": "call"
      },
      {
        "from": "vdp",
        "to": "en",
        "label": "validate signature, read claims",
        "kind": "call"
      },
      {
        "from": "en",
        "to": "vdp",
        "label": "roles: rm_emea, no_payment_data",
        "kind": "return"
      },
      {
        "from": "vdp",
        "to": "vdp",
        "label": "map roles to view grants",
        "kind": "self"
      },
      {
        "from": "vdp",
        "to": "kv",
        "label": "fetch the SAP service credential",
        "kind": "call"
      },
      {
        "from": "kv",
        "to": "vdp",
        "label": "secret, 60-minute lease",
        "kind": "return"
      },
      {
        "from": "src",
        "to": "vdp",
        "label": "rows for the delegated predicate",
        "kind": "return"
      },
      {
        "from": "vdp",
        "to": "vdp",
        "label": "row filter by book, mask payment columns",
        "kind": "self"
      },
      {
        "from": "vdp",
        "to": "cl",
        "label": "authorised rows only",
        "kind": "return"
      },
      {
        "from": "vdp",
        "to": "sen",
        "label": "principal, view, row count, masks",
        "kind": "async"
      }
    ],
    "note": "The source sees the platform identity, not the user. That is a deliberate trade: pass-through authentication was rejected because SAP and Salesforce cannot express the enterprise role model, so the filter must be applied where the model exists.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "36-authorisation-model",
    "title": "Authorisation — One Policy Set, Inherited by Every Channel",
    "layout": "grid",
    "canvas": {
      "width": 1840
    },
    "laneHeaderWidth": 200,
    "columns": [
      "Datasets granted",
      "Column policy",
      "Row policy",
      "Channels"
    ],
    "rows": [
      {
        "title": "Service Agent",
        "cells": [
          [
            {
              "label": "Customer360, Support"
            }
          ],
          [
            {
              "label": "Contact masked to last 4",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Own contact-centre queue"
            }
          ],
          [
            {
              "label": "Desktop REST, assistant",
              "kind": "integration"
            }
          ]
        ]
      },
      {
        "title": "Relationship Manager",
        "cells": [
          [
            {
              "label": "Customer360, Orders, Value"
            }
          ],
          [
            {
              "label": "Full contact, no payment",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Own account book"
            }
          ],
          [
            {
              "label": "Power BI, Excel",
              "kind": "integration"
            }
          ]
        ]
      },
      {
        "title": "BI Developer",
        "cells": [
          [
            {
              "label": "Certified business views"
            }
          ],
          [
            {
              "label": "Value masked until granted",
              "kind": "security"
            }
          ],
          [
            {
              "label": "All rows, aggregate only"
            }
          ],
          [
            {
              "label": "Design Studio, Power BI",
              "kind": "integration"
            }
          ]
        ]
      },
      {
        "title": "Data Scientist",
        "cells": [
          [
            {
              "label": "Integration and business views"
            }
          ],
          [
            {
              "label": "Pseudonymised keys",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Sampled cohorts"
            }
          ],
          [
            {
              "label": "JDBC from notebooks",
              "kind": "integration"
            }
          ]
        ]
      },
      {
        "title": "Data Steward",
        "cells": [
          [
            {
              "label": "Every view and the crosswalk"
            }
          ],
          [
            {
              "label": "Unmasked, reason recorded",
              "kind": "security"
            }
          ],
          [
            {
              "label": "All rows"
            }
          ],
          [
            {
              "label": "Studio, catalog, console",
              "kind": "integration"
            }
          ]
        ]
      },
      {
        "title": "Privacy Officer",
        "cells": [
          [
            {
              "label": "DSAR assembly view only"
            }
          ],
          [
            {
              "label": "Unmasked, case-scoped",
              "kind": "security"
            }
          ],
          [
            {
              "label": "One subject at a time"
            }
          ],
          [
            {
              "label": "DSAR console",
              "kind": "integration"
            }
          ]
        ]
      },
      {
        "title": "Platform SRE",
        "cells": [
          [
            {
              "label": "Metadata and logs only"
            }
          ],
          [
            {
              "label": "No customer columns",
              "kind": "security"
            }
          ],
          [
            {
              "label": "No customer rows"
            }
          ],
          [
            {
              "label": "Monitor, Solution Manager",
              "kind": "integration"
            }
          ]
        ]
      },
      {
        "title": "AI Assistant",
        "cells": [
          [
            {
              "label": "Whatever the caller has"
            }
          ],
          [
            {
              "label": "Whatever the caller has",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Whatever the caller has"
            }
          ],
          [
            {
              "label": "MCP tools only",
              "kind": "integration"
            }
          ]
        ]
      },
      {
        "title": "Partner Portal",
        "cells": [
          [
            {
              "label": "Contracted subset"
            }
          ],
          [
            {
              "label": "No personal data at all",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Own contracted accounts"
            }
          ],
          [
            {
              "label": "REST through APIM",
              "kind": "integration"
            }
          ]
        ]
      }
    ],
    "note": "The assistant row is the whole security argument for AI: it has no grants of its own, so nothing had to be reasoned about twice.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "37-pii-and-masking",
    "title": "Sensitive Data — Classified Once, Enforced Everywhere",
    "layout": "swimlane",
    "canvas": {
      "width": 1820
    },
    "laneHeaderWidth": 175,
    "stages": [
      "Classify",
      "Tag",
      "Policy",
      "Enforce",
      "Prove"
    ],
    "lanes": [
      {
        "title": "Direct identifiers",
        "cells": [
          [
            {
              "label": "Name, tax id"
            }
          ],
          [
            {
              "label": "pii.direct",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Deny unless granted",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "Full redaction"
            }
          ],
          [
            {
              "label": "Access log per query",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Contact data",
        "cells": [
          [
            {
              "label": "Email, phone, address"
            }
          ],
          [
            {
              "label": "pii.contact",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Partial by role",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "Last four shown"
            }
          ],
          [
            {
              "label": "Mask applied count",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Financial",
        "cells": [
          [
            {
              "label": "Card, IBAN, balance"
            }
          ],
          [
            {
              "label": "pii.financial",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Deny by default",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "Tokenised at source"
            }
          ],
          [
            {
              "label": "Never cached",
              "kind": "risk"
            }
          ]
        ]
      },
      {
        "title": "Behavioural",
        "cells": [
          [
            {
              "label": "Clickstream, sessions"
            }
          ],
          [
            {
              "label": "pii.behaviour",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Pseudonymised",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "Hashed device id"
            }
          ],
          [
            {
              "label": "Re-identification review",
              "kind": "store"
            }
          ]
        ]
      },
      {
        "title": "Derived scores",
        "cells": [
          [
            {
              "label": "Lifetime value, churn"
            }
          ],
          [
            {
              "label": "restricted.model",
              "kind": "security"
            }
          ],
          [
            {
              "label": "Approved purpose only",
              "kind": "decision"
            }
          ],
          [
            {
              "label": "Row filter by book"
            }
          ],
          [
            {
              "label": "Purpose recorded",
              "kind": "store"
            }
          ]
        ]
      }
    ],
    "note": "Classification is declared on the integration view, not on each business view. A new business view inherits every mask the moment it is created, which is why the layering rule is a security control.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "38-governance-and-lineage",
    "title": "Governance — From a Business Definition to a Provable Column Lineage",
    "layout": "flow",
    "canvas": {
      "width": 1840
    },
    "chain": true,
    "align": "middle",
    "stages": [
      {
        "title": "Describe",
        "nodes": [
          {
            "id": "g-glo",
            "label": "Business glossary",
            "sub": "one definition each"
          },
          {
            "id": "g-tech",
            "label": "Technical metadata",
            "sub": "harvested from VQL"
          },
          {
            "id": "g-own",
            "label": "Named owner",
            "sub": "person, not a team"
          }
        ]
      },
      {
        "title": "Classify",
        "nodes": [
          {
            "id": "g-sens",
            "label": "Sensitivity tags",
            "sub": "drives masking",
            "kind": "security"
          },
          {
            "id": "g-ret",
            "label": "Retention class"
          },
          {
            "id": "g-pur",
            "label": "Permitted purpose"
          }
        ]
      },
      {
        "title": "Certify",
        "nodes": [
          {
            "id": "g-rev",
            "label": "Steward review",
            "kind": "decision"
          },
          {
            "id": "g-cert",
            "label": "Certified badge",
            "sub": "with an expiry"
          },
          {
            "id": "g-dep",
            "label": "Deprecation notice",
            "sub": "90 days"
          }
        ]
      },
      {
        "title": "Discover",
        "nodes": [
          {
            "id": "g-cat",
            "label": "Denodo Data Catalog",
            "sub": "search and sample",
            "icon": "fa5_book_open"
          },
          {
            "id": "g-pv",
            "label": "Purview federation",
            "sub": "estate-wide search"
          }
        ]
      },
      {
        "title": "Request",
        "nodes": [
          {
            "id": "g-req",
            "label": "Access request",
            "sub": "on the asset page"
          },
          {
            "id": "g-app",
            "label": "Owner approval",
            "sub": "one working day",
            "kind": "decision"
          },
          {
            "id": "g-gr",
            "label": "Grant provisioned",
            "sub": "Entra group"
          }
        ]
      },
      {
        "title": "Trace",
        "nodes": [
          {
            "id": "g-lin",
            "label": "Column lineage",
            "sub": "to the source field"
          },
          {
            "id": "g-imp",
            "label": "Impact analysis",
            "sub": "before any change"
          },
          {
            "id": "g-use",
            "label": "Usage statistics",
            "sub": "who, what, how often"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "g-imp",
        "to": "g-rev",
        "label": "recertify on change",
        "kind": "batch",
        "route": "gutter"
      }
    ],
    "note": "Lineage is derived from the view definitions themselves, so it cannot drift from the code. The part that can drift is ownership, which is why certification expires.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  },
  {
    "id": "39-failure-modes",
    "title": "Failure Modes — What Breaks, What Absorbs It, and What Is Left Over",
    "layout": "nested",
    "canvas": {
      "width": 1800
    },
    "boxes": [
      {
        "title": "Source-side — expected, absorbed",
        "kind": "boundary",
        "nodes": [
          {
            "id": "f1",
            "label": "Source unavailable",
            "sub": "partial result, view 25",
            "kind": "risk"
          },
          {
            "id": "f2",
            "label": "Source slow",
            "sub": "timeout, circuit opens",
            "kind": "risk"
          },
          {
            "id": "f3",
            "label": "API quota exhausted",
            "sub": "backoff, cache serves",
            "kind": "risk"
          },
          {
            "id": "f4",
            "label": "Schema drift",
            "sub": "base view breaks in CI",
            "kind": "risk"
          }
        ]
      },
      {
        "title": "Platform — degraded, not lost",
        "kind": "boundary",
        "nodes": [
          {
            "id": "f5",
            "label": "VDP pod loss",
            "sub": "retried, no session state",
            "kind": "risk"
          },
          {
            "id": "f6",
            "label": "Cache database loss",
            "sub": "cold reads, 3x latency",
            "kind": "risk"
          },
          {
            "id": "f7",
            "label": "MPP cluster loss",
            "sub": "falls back to the source",
            "kind": "risk"
          },
          {
            "id": "f8",
            "label": "Region loss",
            "sub": "RTO 30 min, RPO 5 min",
            "kind": "risk"
          }
        ]
      },
      {
        "title": "Correctness — the class that actually matters",
        "kind": "boundary",
        "nodes": [
          {
            "id": "f9",
            "label": "Stale cache after a merge",
            "sub": "reconciler bounds at 1 h",
            "kind": "risk"
          },
          {
            "id": "f10",
            "label": "Crosswalk corruption",
            "sub": "point in time, 7 days",
            "kind": "risk"
          },
          {
            "id": "f11",
            "label": "Wrong survivorship pick",
            "sub": "replayable from source ids",
            "kind": "risk"
          },
          {
            "id": "f12",
            "label": "Masking regression",
            "sub": "policy scan blocks release",
            "kind": "risk"
          }
        ]
      },
      {
        "title": "Programme — slow, and therefore easy to miss",
        "kind": "plain",
        "nodes": [
          {
            "id": "f13",
            "label": "Uncertified view in use",
            "sub": "promotion gate blocks it",
            "kind": "risk"
          },
          {
            "id": "f14",
            "label": "Owner leaves, asset orphaned",
            "sub": "certification expires",
            "kind": "risk"
          },
          {
            "id": "f15",
            "label": "Extract copied to a spreadsheet",
            "sub": "usage telemetry only",
            "kind": "risk"
          },
          {
            "id": "f16",
            "label": "Denodo becomes the bottleneck",
            "sub": "the real programme risk",
            "kind": "risk"
          }
        ]
      }
    ],
    "edges": [
      {
        "from": "f9",
        "to": "f11",
        "label": "same root cause"
      },
      {
        "from": "f16",
        "to": "f8",
        "label": "concentration risk",
        "kind": "error"
      }
    ],
    "note": "The last box has no technical mitigation. Every consumer depending on one logical layer is the price of the design, and it is managed by workload isolation and honest capacity planning rather than by architecture.",
    "meta": {
      "v": "1.0",
      "owner": "Data & AI Global Practice",
      "date": "2026-09"
    }
  }
]
