ConflictDetector surfaces properties where multiple sources disagree on the same canonical entity, and ConflictResolver applies per-property strategies — credibility-weighted voting, most-recent, expert review, and others — to produce a single resolved value with a full audit trail. Run it after deduplication and before SHACL validation.
Run conflict detection after deduplication and before SHACL validation. Deduplication removes duplicate nodes; conflict resolution reconciles disagreeing property values on the same canonical entity. Running them out of order — detecting conflicts before deduplication — will produce spurious conflicts between entities that should have been merged first.

What Is Conflict Resolution?

When you merge data from multiple sources, the same real-world entity — a customer, a product, a threat actor, a drug compound — often appears with contradictory property values. One database says a customer’s email is alice@example.com; another says alice.smith@example.com. One security feed rates a CVE at 10.0; two others rate it 9.1 and 9.5. Conflict resolution is the systematic process of deciding which value is most trustworthy and recording that decision with evidence, so the canonical entity ends up with one defensible, auditable value per property.

Key Concepts

Canonical entity — The single authoritative record for a real-world thing. After deduplication, each entity has exactly one canonical node in your graph. Conflict resolution determines which property values belong on that node. Conflicting values — Two or more different values asserted for the same property on the same canonical entity, each reported by a different source. Credibility score — A number between 0.0 and 1.0 you attach to each source record, indicating how reliable that source is. A government registry might carry 0.99; a scraped blog might carry 0.30. You supply these; Semantica uses them during CREDIBILITY_WEIGHTED resolution. Confidence score — A number between 0.0 and 1.0 the resolver computes after resolution, reflecting how certain the outcome is. A unanimous vote produces high confidence; a close split among equally credible sources produces lower confidence. This appears on ResolutionResult.confidence and should be read as a signal, not a guarantee that the resolved value is correct. Resolution strategy — The rule for picking the winning value: majority vote, credibility-weighted average, latest timestamp, and so on. See Resolution strategies at a glance for the full list. Audit trail — The complete record of every resolution decision: conflict ID, strategy used, resolved value, sources consulted, and confidence score. Returned by resolver.get_resolution_history(). Provenance-aware resolution — Resolution that records not just the winning value but which source it came from. Every ResolutionResult carries a sources_used field, so you can always trace a canonical value back to its origin — critical in regulated environments.

Why Use Conflict Resolution?

  • Multi-source pipelines always produce disagreements. Differences in update cadence, data-entry conventions, and source reliability are unavoidable. Without an explicit resolution step, you silently favor one source over another with no record of the choice.
  • You get a defensible, auditable decision log. Compliance teams, auditors, and domain experts need to know which source won and why. The audit trail provides exactly that.
  • Easy cases are automated; hard cases are escalated. Routine disagreements — slightly different name spellings, stale timestamps — are resolved algorithmically. Genuinely ambiguous cases — competing legal classifications, different clinical endpoints — are flagged for expert review without blocking the rest of the pipeline.

When To Use / When Not To Use

Use conflict resolution when:
  • You are merging two or more independent sources for the same entity.
  • Sources disagree on property values and you need a single canonical value.
  • You need an auditable record of every resolution decision.
  • Some conflicts require domain-expert review before they can be resolved.
Skip conflict resolution when:
  • A single authoritative source already exists. If one system is always correct for a given property, read from it directly. Adding resolution machinery around a single source creates complexity without benefit.
  • All sources are always in agreement. Verify this empirically before skipping; silent disagreements are common in practice.
  • You want to preserve all conflicting values. If retaining every source’s assertion matters more than picking one, model provenance directly in your graph schema instead of resolving to one winner.

Typical Workflow

  1. Deduplication — Merge duplicate nodes so each entity has exactly one canonical record. Conflict resolution operates on a single canonical entity; you must identify it before comparing what different sources say about it. See Deduplication.
  2. Conflict Detection — Call detect_entity_conflicts() to surface all property disagreements at once, or detect_value_conflicts() to target a specific property.
  3. Resolution — For each conflict, apply a strategy (CREDIBILITY_WEIGHTED, MOST_RECENT, VOTING, etc.) or route it for expert review (EXPERT_REVIEW).
  4. Persist Canonical Values — Write resolved values back to your canonical entities or graph store. See Persisting resolved values.
  5. SHACL Validation — Enforce structural constraints on the resolved graph to confirm it satisfies your ontology. See SHACL Validation.

Quick Start: A Beginner Example

Before diving into domain-specific scenarios, here is the shortest path through the API. Three systems — a CRM, an ERP, and an LDAP directory — hold slightly different contact details for the same customer. Two of the three agree that the canonical email is alice.smith@example.com; the CRM has an older value.
detect_entity_conflicts() scanned both email and phone automatically — you did not name them. Because phone is identical across all three records, no conflict was detected for it. The email disagreement resolves to alice.smith@example.com because two of three sources agree on that value. When every conflict in a batch should use the same strategy, pass strategy= directly to resolve_conflicts(). Use set_resolution_rule() when different entity-property pairs need different strategies — explained in Setting per-property resolution rules.

Detecting Conflicts

ConflictDetector provides three methods. Choose the one that fits your situation:

Scanning All Properties at Once — detect_entity_conflicts

detect_entity_conflicts() is the recommended starting point for a new pipeline. It inspects every property found on your entity records and returns a single flat list of all conflicts — without you having to enumerate properties in advance.
If you have registered conflict fields for a specific entity type, pass entity_type to limit detection to those fields:
Without entity_type, the detector checks every key found on your entity dicts (excluding bookkeeping fields such as id, source, and metadata). Start here to get a complete picture, then decide which conflicts need which resolution strategy.

Scanning a Specific Property — detect_value_conflicts

Use detect_value_conflicts() when you already know which property to check, or when you want to apply different detection logic to each property. ConflictDetector groups the records by entity ID, then compares each source’s value for that property. Any entity where two or more sources report different values produces a Conflict object.
Each Conflict captures the full picture: which entity, which property, every disagreeing value, and which source reported each. This is already enough to build a review queue — but the goal is to resolve these automatically according to rules you set.

Setting Per-Property Resolution Rules

set_resolution_rule(entity_id, property_name, strategy) registers a strategy for a specific entity-property combination. The resolver stores the rule under the key entity_id.property_name and applies it automatically when you call resolve_conflicts(). Because rules are keyed by both entity ID and property name, set_resolution_rule() is entity-specific. There is no wildcard that applies a rule to all entities or all properties at once. When to use set_resolution_rule(): Use it when different entity-property combinations need different strategies. For example, an entity’s legal_name might use CREDIBILITY_WEIGHTED while its last_updated uses MOST_RECENT. Registering a rule per combination lets the single resolve_conflicts() call handle all of them correctly in one pass. When to pass strategy= directly to resolve_conflicts(): If every conflict in a batch should use the same strategy, pass it directly to resolve_conflicts() instead of registering a rule for each entity-property pair:
This is cleaner than calling set_resolution_rule() in a loop over every entity just to apply the same strategy everywhere. Per-property rules for the CVE example:
You can set rules before or after detection — the resolver applies them lazily when resolve_conflicts() is called.

Resolving the Batch

Pass all detected conflicts to resolve_conflicts(). For each conflict, the resolver looks up whether a rule is registered for that entity-property combination. If one is found, it applies that strategy. If none is set, it falls back to the default strategy (voting, unless you override it in the constructor).
NVD wins the CVSS score — its credibility weight (0.98) edges out the commercial feed (0.91) and the vendor (0.87), so 10.0 becomes the canonical score. The exploitation status resolves to in_wild — the commercial feed and vendor advisory are both more recent than NVD’s initial triage, and both report active exploitation.

Handling Conflicts That Need Human Judgment

Not every conflict can be auto-resolved. A disagreement about the legal classification of a financial instrument, or about a patient’s current medication list, is too consequential to resolve by algorithm. Flag these for review without blocking the rest of the batch:
EXPERT_REVIEW sets resolved=False on the result. The conflict stays in the graph unresolved, the metadata field carries requires_expert_review: True, and the review queue JSON gives your clinical team exactly what they need to make the call.

Persisting Resolved Values

resolve_conflicts() returns ResolutionResult objects — it does not automatically write resolved values back to your graph or entity store. That step is yours to implement using whatever storage layer your pipeline uses. The most direct approach is to pair each ResolutionResult with its original Conflict object — the two lists are returned in the same order — and write the winning value onto your canonical entity:
A few things to keep in mind:
  • Conflicts with resolved=False — flagged for expert or manual review — should not be written to the canonical record until a human has made the call. Keep them in the review queue.
  • Confidence is a signal, not a guarantee. A 72% confidence score means the resolver had reasonable but not unanimous evidence for its decision. Treat low-confidence results with additional scrutiny before writing them to production.
  • Track provenance. result.sources_used tells you which source’s value won. Store this alongside the canonical value if your compliance requirements demand a full evidence chain.

Reviewing the Full Audit Trail

After a resolution run, get_resolution_history() returns every decision made since the resolver was instantiated. This is your compliance log:
Pair this with the full conflict report from the detector to get aggregate statistics across all runs:
The report aggregates every conflict the detector has seen across its lifetime — useful for pipeline monitoring and for identifying which entity types or data sources generate the most disagreements.

Detecting Relationship Conflicts

Value conflicts live on properties. Relationship conflicts live on edges — two sources asserting contradictory connections between the same node pair:
Relationship conflicts typically require expert review rather than voting, because conflicting edge types often reflect genuinely different intelligence assessments rather than data entry errors.

Domain Examples

A threat intelligence platform merges actor profiles from Mandiant, CrowdStrike, and an open-source blog. The three sources agree that APT29 is Russian and espionage-motivated, but disagree on when it was first observed and — critically — one source attributes it to China. The low-credibility source (the blog, at 0.30) should lose to the high-credibility sources (Mandiant at 0.95, CrowdStrike at 0.92) when those sources are in agreement.Credibility-weighted resolution handles this cleanly: the blog’s misattribution is drowned out by the combined weight of the two authoritative vendors. The first_seen date disagreement (2008 vs 2009) is also resolved by credibility weight, giving Mandiant’s 2008 date the win.

Resolution Strategies at a Glance

Common Pitfalls

Running conflict resolution before deduplication If duplicate nodes for the same real-world entity still exist, ConflictDetector treats each duplicate as a separate entity disagreeing with the others — producing spurious conflicts that should never have existed. Always run deduplication first. Forgetting to persist resolved values resolve_conflicts() returns ResolutionResult objects; it does not write them anywhere. Inspecting the results and moving on without updating your canonical entity means nothing has actually changed in your data. See Persisting resolved values. Scanning properties one at a time across a large entity set Calling detect_value_conflicts() for every property in a manual loop produces redundant passes over your data. Use detect_entity_conflicts() instead — it handles all properties in a single call and is the recommended starting point for bulk detection. Misunderstanding credibility scores Credibility scores are weights you assign based on your prior knowledge of source reliability — not ground truth. A source registered with set_source_credibility("source", 0.99) can still be wrong. CREDIBILITY_WEIGHTED resolution amplifies your beliefs about source quality; if those beliefs are miscalibrated, the resolutions will be too. Validate scores against known ground truth before relying on them in production. Treating resolved values as guaranteed truth A resolved value is the most defensible answer given your sources and strategy — not necessarily the correct one. Low confidence scores and EXPERT_REVIEW flags are signals to scrutinize results before writing them to a canonical record or downstream system. Using conflict resolution when a single authoritative source already exists If one system is always correct for a given property, read from it directly. Layering conflict resolution over a single source adds complexity, introduces unnecessary doubt, and produces an audit trail that adds no real information. Registering rules in a loop to apply one strategy uniformly Calling set_resolution_rule() for every entity-property pair just to apply the same strategy to all of them creates O(N) setup for no benefit. Pass strategy= directly to resolve_conflicts() when one strategy covers the whole batch.
  • Deduplication — remove duplicate nodes before running conflict detection
  • Provenance — track which source each resolved value came from, and verify the audit trail cryptographically
  • SHACL Validation — enforce structural constraints after conflicts are resolved
  • Change Management — snapshot the graph before and after conflict resolution runs
  • Ontology Management — align entity types to a shared vocabulary to reduce type conflicts at the schema level