From 5338975eea1c700de0cfb57f2892ac4e6895a6a3 Mon Sep 17 00:00:00 2001 From: Simon Axelsson Date: Mon, 7 Sep 2026 17:57:23 +0200 Subject: [PATCH] fix: make conflicting company identifiers dominate ER matches --- app/api/0pt/internal/er/candidates/route.ts | 34 ++++++++++++--------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/app/api/0pt/internal/er/candidates/route.ts b/app/api/0pt/internal/er/candidates/route.ts index f03206b..8152aba 100644 --- a/app/api/0pt/internal/er/candidates/route.ts +++ b/app/api/0pt/internal/er/candidates/route.ts @@ -11,23 +11,27 @@ import { SiaxError } from "@/src/platform/errors"; const er = new EntityResolutionService(); +/** + * Company identity hard rule. + * + * Negative identity evidence dominates positive evidence: if two records both + * carry the same identifier type and the values conflict, they MUST NOT be + * merged even if another identifier happens to match. Only when every shared + * stable identifier agrees may one matching identifier force a match. + */ er.registerHardRule({ - rule_id: "0pt.company.lei.v1", + rule_id: "0pt.company.stable-identifiers.v2", decide(a, b) { - const av = stringField(a, "lei"); - const bv = stringField(b, "lei"); - if (!av || !bv) return "abstain"; - return av === bv ? "force_match" : "force_non_match"; - }, -}); - -er.registerHardRule({ - rule_id: "0pt.company.org_nr.v1", - decide(a, b) { - const av = stringField(a, "org_nr"); - const bv = stringField(b, "org_nr"); - if (!av || !bv) return "abstain"; - return av === bv ? "force_match" : "force_non_match"; + const fields = ["lei", "org_nr"] as const; + let sharedMatch = false; + for (const field of fields) { + const av = stringField(a, field); + const bv = stringField(b, field); + if (!av || !bv) continue; + if (av !== bv) return "force_non_match"; + sharedMatch = true; + } + return sharedMatch ? "force_match" : "abstain"; }, });