lumnik. / scénarios Tous les scénarios · English

Un client établi ne change pas de fiche en silence

Objectif Voir quand un contact du CRM se met à désigner un autre client, au lieu de le déplacer en silence.

Hier, le contact CC-42 et le compte AC-700 désignaient Nova Pumps. Aujourd'hui, son email pointe vers Helios Valves.

erp-customers.csv
account_code,company_name,email
AC-700,Nova Pumps,service@nova-pumps.example
AC-800,Helios Valves,maintenance@helios-valves.example
crm-contact-day-1.csv
contact_code,display_name,email,phone
CC-42,Nova Pumps,service@nova-pumps.example,+33 4 72 42 42 42
manifests/30-customer.yaml
kind: Entityname: ScenarioResolutionDriftDdescription: "A resolved CRM contact whose matching data later contradicts its established identity."scopes: [entity-resolution]sources:  - table: t_scenario_resolution_drift_erp_d    map:      account_code: account_code      company_name: name      email: email  - table: t_scenario_resolution_drift_crm_d    map:      contact_code: crm_id      display_name: name      email: email      phone: phoneresolution:  identity:    t_scenario_resolution_drift_erp_d: account_code    t_scenario_resolution_drift_crm_d: crm_id  cascade:    - id: email      exact:        fields: [email]        normalize: [lower]
crm-contact-day-2.csv
contact_code,display_name,email,phone
CC-42,Nova Pumps,maintenance@helios-valves.example,+33 4 72 42 42 42
$ lm entity assert-same ScenarioResolutionDriftD --left t_scenario_resolution_drift_crm_d:CC-42 --right t_scenario_resolution_drift_erp_d:AC-700 --reason "The CRM email was changed in error; the contact remains Nova Pumps"
recorded: t_scenario_resolution_drift_crm_d:CC-42 and t_scenario_resolution_drift_erp_d:AC-700 are same

$ lm entity resolve ScenarioResolutionDriftD
rows seen: 3
resolved: 3
ambiguous: 0
unresolved (no rule): 0
unresolved (provider): 0
newly minted: 0
provider calls: 0
provider failures: 0
rule hits:
  scenarioresolutiondriftd:email: 2

$ lm entity ambiguous ScenarioResolutionDriftD -o json
[]

$ lm entity get ScenarioResolutionDriftD --filter crm_id=CC-42 -o json
[
  {
    "account_code": "AC-700",
    "name": "Nova Pumps",
    "email": "service@nova-pumps.example",
    "crm_id": "CC-42",
    "phone": "+33 4 72 42 42 42",
    "_conflicts": "{\"email\": [{\"src\": \"t_scenario_resolution_drift_erp_d\", \"value\": \"service@nova-pumps.example\"}, {\"src\": \"t_scenario_resolution_drift_crm_d\", \"value\": \"maintenance@helios-valves.example\"}]}"
  }
]
$ lm entity resolve ScenarioResolutionDriftD
rows seen: 3
resolved: 3
ambiguous: 0
unresolved (no rule): 0
unresolved (provider): 0
newly minted: 0
provider calls: 0
provider failures: 0
rule hits:
  scenarioresolutiondriftd:email: 2

$ lm entity get ScenarioResolutionDriftD --filter crm_id=CC-42 -o json
[
  {
    "account_code": "AC-700",
    "name": "Nova Pumps",
    "email": "service@nova-pumps.example",
    "crm_id": "CC-42",
    "phone": "+33 4 72 42 42 42",
    "_conflicts": "{\"email\": [{\"src\": \"t_scenario_resolution_drift_erp_d\", \"value\": \"service@nova-pumps.example\"}, {\"src\": \"t_scenario_resolution_drift_crm_d\", \"value\": \"maintenance@helios-valves.example\"}]}"
  }
]

$ lm entity history ScenarioResolutionDriftD t_scenario_resolution_drift_crm_d CC-42 -o json
[
  {
    "ruleDeclaredId": "email",
    "ruleVersion": "ce968f5902d73417@engine-v1@hmac-k1@lower:1.0.0",
    "provider": "",
    "candidates": [
      "27c8919d-7484-4832-8508-0ea80c566d88"
    ],
    "discardedCandidates": [],
    "setAside": [],
    "outcome": "RESOLVED",
    "decidedBy": "automatic",
    "runId": "",
    "occurredAt": "2026-11-03T15:24:39.894428Z",
    "assertedBy": null,
    "assertedByLogin": null,
    "reason": null
  },
  {
    "ruleDeclaredId": "email",
    "ruleVersion": "ce968f5902d73417@engine-v1@hmac-k1@lower:1.0.0",
    "provider": "",
    "candidates": [
      "27c8919d-7484-4832-8508-0ea80c566d88",
      "afcec735-875d-422f-92fc-45d24763a5f0"
    ],
    "discardedCandidates": [],
    "setAside": [],
    "outcome": "AMBIGUOUS",
    "decidedBy": "automatic",
    "runId": "0140870a-0e0f-4b91-b81f-e68ba077c789",
    "occurredAt": "2026-11-03T15:24:40.030132Z",
    "assertedBy": null,
    "assertedByLogin": null,
    "reason": null
  },
  {
    "ruleDeclaredId": "",
    "ruleVersion": "",
    "provider": "",
    "candidates": [
      "27c8919d-7484-4832-8508-0ea80c566d88"
    ],
    "discardedCandidates": [],
    "setAside": [],
    "outcome": "RESOLVED",
    "decidedBy": "asserted",
    "runId": "",
    "occurredAt": "2026-11-03T15:24:40.214667Z",
    "assertedBy": "1",
    "assertedByLogin": "integrator",
    "reason": "The CRM email was changed in error; the contact remains Nova Pumps"
  },
  {
    "ruleDeclaredId": "",
    "ruleVersion": "",
    "provider": "",
    "candidates": [
      "27c8919d-7484-4832-8508-0ea80c566d88"
    ],
    "discardedCandidates": [],
    "setAside": [],
    "outcome": "RESOLVED",
    "decidedBy": "asserted",
    "runId": "",
    "occurredAt": "2026-11-03T15:24:40.322616Z",
    "assertedBy": "1",
    "assertedByLogin": "integrator",
    "reason": "The CRM email was changed in error; the contact remains Nova Pumps"
  }
]

Limites

  • Le scénario protège contre un déplacement ; il ne dit pas pourquoi le CRM a changé.
  • Les assertions et les curseurs de source sont durables: un rejeu complet demande une nouvelle instance.
  • Données synthétiques.

Clients · ERP et CRM

Un contact déjà rattaché à son client.

Le CRM change son email.

Deux comptes, un contact, deux jours

Le problème

L'email désigne un autre client.

  • Hier: Nova Pumps.
  • Aujourd'hui: Helios Valves.

Ce qui n'est pas modifié

Les sources restent telles quelles.

  • Un fichier CRM par jour.
  • Aucune correction à la main.

Le piège

Déplacer en silence.

  • Le contact change de client.
  • Rien ne le signale.

Ce qu'il a fallu écrire

normalize: [lower]

Une ligne.

  • Une règle: l'email.
  • Un lien établi ne bouge pas seul.
  • Trois manifestes, 51 lignes.

La décision

Un humain tranche.

  • CC-42 reste sur Nova Pumps.
  • L'historique nomme l'auteur et la raison.

La limite

Le hub ne sait pas pourquoi le CRM a changé.

  • Il le montre, il ne l'explique pas.

Essayer

Le scénario se rejoue en local.

Chaque étape est publiée avec sa sortie.

Ce scénario tourne sur lumnik Open, gratuit et open source.

Essayer lumnik Open →