CRISPcore

Principle

Extensions

Multiple vendors / systems / sources of the same product type (e.g. several billing engines) often each have their own native party / customer form. Shared properties become locked canon (typed Conform columns). Source-specific party vocabulary stays as variance under each lane’s extensions — still about that entity’s implementation, not fields that belong on other entities. That keeps native meaning for debug and lineage, without teaching consumers to bypass Publish and join the raw feeds.

Same product type · multiple sources

Several billing products (vendors / systems / sources) can each model a customer / party (AccountHolder, BillParty, …). Native names differ slightly; each also carries unique party columns the others lack. Adaptors land all of them on PARTY. The ∩ is typed canon — including the Identity winner name — while lane-only fields stay under extensions until promoted. (Venn shows A and B; C, D, … follow the same rule.)

Multiple same-type vendors → PARTY; A and B shown with variance outside canon ∩Simplified two-lobe Venn for multiple billing vendors. Left lobe System A: native name and A-only sic_code 65120. Right lobe System B: native name and B-only lei. Centre is Identity winner display_name plus typed canon columns. Further vendors add more extensions namespaces the same way.System AAccountHolderextensions:sys_aAH-77421NORTHBRIDGEMARINE LTDsic_code65120A-only · not canonSystem BBillPartyextensions:sys_bBP-99102NorthbridgeMarine Limitedlei5493001KJT…B-only · not canonCANON · PARTYidentity winnerNorthbridgeMarine LtdORGANISATIONcountry GB

Left lobe — System A party form

Centre ∩ — canon / winner

Right lobe — System B party form

Same customer across multiple same-type billing sources → one Conform PARTY. Diagram shows two lanes (A / B) for clarity; each additional vendor / system / source adds another extensions namespace. Lobes keep unique party columns (A: sic_code, B: lei) plus native name variants — not yet canon. The ∩ is programme canon / Identity winner.

Canon ∩ vs variance

Why not force one big typed row?

Forcing every native field into typed CORE day one invents false sameness or drops SME evidence. Forcing consumers onto raw lanes invents a Publish bypass. Extensions keep variance on the Conform assertion so Identity, Span, and Publish can do their jobs — and so “why this number?” still drills back to source-shaped evidence.

Later stages — participants, winner, and lineage

Identity clusters the Conform assertions (any number of vendor lanes) and elects a winner. Span projects one consume_party_key row with that winner’s typed facade and a participants array so System B’s lei (never promoted to canon) is still reachable when needed — without teaching BI to join raw vendor feeds.

A + B → Span participants · winner

After Conform/Rules, Identity decides sameness and which assertion is WINNER. Span keeps every participant (and their extensions) on the consume projection so lineage and rare lookups still work.

Conform · System A

AH-77421

NORTHBRIDGE MARINE LTD

sic_code: 65120

extensions:sys_a

Conform · System B

BP-99102

Northbridge Marine Limited

lei: 5493001KJT…

extensions:sys_b

Identitycluster

Span · PARTY

consume_party_key

CPT-NORTHBRIDGE-01

display_name (winner)

Northbridge Marine Ltd

Why A won: winner_rank · valid_from ASC (A asserted earlier; B is MEMBER)

SPAN.PARTY.extensions · participants + winner

{
  "winner": {
    "lane": "sys_a",
    "source_natural_id": "SYS_A:AH-77421",
    "cluster_role": "WINNER",
    "reason": "valid_from ASC · earliest open assertion"
  },
  "participants": [
    {
      "lane": "sys_a",
      "cluster_role": "WINNER",
      "native_name": "NORTHBRIDGE MARINE LTD",
      "sic_code": "65120"
    },
    {
      "lane": "sys_b",
      "cluster_role": "MEMBER",
      "native_name": "Northbridge Marine Limited",
      "lei": "5493001KJTIIGC8Y1R12"
    }
  ]
}

Need the LEI later? Read participants[lane=sys_b].lei from Span — still on the consume row, without joining System B raw or bypassing Publish for the default customer facade. How Span is formed from Identity rules: Span stage.

Identity clusters vendor lanes (A, B, …); Span projects one consume row with a winner and a participants array. Member-only fields (lei) stay reachable without joining raw sources.
Use caseWhere it resolves
Two (or more) natives are the same party — which row is the winner?Identity elects via winner_rank (here valid_from ASC); Span projects winner + participants
Need B’s lei after collapse?Reachable via Span participants — see Span for how Identity rules form the projection; fuller lane-field bag on this page.
KPI or entity facade for BI / APIPublish only — default customer shape is the winner; do not join raw vendor party feeds
Debug: which native fields drove this Publish row?Follow consume → Span participants / Conform header → extensions:<lane>
Promote a lane field (e.g. lei) to typed CORE laterEvolution — rebuild + replay from extensions

Hints are not Identity

Adaptors may emit optional cluster hints inside extensions. They must not merge or invent golden keys. Identity owns hubs, members, exceptions, and rebuild. A hint no rule reads is inert.

Same customer · multiple billing vendors → PARTY (A + B shown)
# Intersection (canon) — Identity winner name
display_name = "Northbridge Marine Ltd"   # winner
party_kind   = ORGANISATION
country_iso  = GB
# + header_key, valid_*, system_*, warehouse_*

# System A — native AccountHolder (party variance)
extensions.sys_a = {
  holder_id: "AH-77421",
  native_name: "NORTHBRIDGE MARINE LTD",
  sic_code: "65120"              # A-only · not in canon
}
extensions.source_natural_id = "SYS_A:AH-77421"

# System B — native BillParty (party variance)
extensions.sys_b = {
  bp_key: "BP-99102",
  native_name: "Northbridge Marine Limited",
  lei: "5493001KJTIIGC8Y1R12"    # B-only · not in canon
}
extensions.source_natural_id = "SYS_B:BP-99102"

# … System C, D, … same pattern — each lane’s extensions:<lane>

# Later — not in the adaptor:
rebuild_identity("party")       # I · clusters; picks winner display_name
SPAN.PARTY → consume_party_key  # S · winner projection
PUBLISH.PARTY / measures        # P · consumers stop here

Checklist

  1. Lock the ∩ (canon) deliberately — everything else is lane variance.
  2. Namespace extensions per source — do not flatten unrelated keys.
  3. Do not invent consume keys or cluster winners in Conform adaptors.
  4. Keep Publish as the consumer contract — use extensions for lineage/debug. Do not join raw vendor party feeds in the dashboard for “the customer”.
  5. Treat promotion out of extensions as Evolution, not a silent ALTER.