Conflict Semantics
What this page is
How Stigmem treats contradictions as first-class facts: both values stored, neither silently overwritten, a system-generated contradiction record linking them.
The problem
Two sources assert different values for the same thing. Agent A says Alice's role is "CEO." Agent B says it's "CTO." Both are confident. Which is right?
In a single-node system, you might impose last-write-wins and move on. But in a federated network where nodes operate independently during partitions, two legitimate assertions can arise concurrently with no causal relationship between them. Silently picking a winner means silently losing information — and in a compliance-sensitive system, that's unacceptable.
Naive approaches and why they fail
Our model
Stigmem treats contradictions as first-class facts. Both values are stored. Neither is silently overwritten. A system-generated contradiction record links them.
Contradiction detection
A contradiction exists when two facts a and b satisfy all of:
Same entity
a.entity == b.entity
Same relation
a.relation == b.relation
Same scope
a.scope == b.scope
Different values
a.value != b.value
Both live
a.confidence > 0.0 && b.confidence > 0.0
When detected (at write time, including federated ingest), the node asserts a contradiction record:
{
"entity": "stigmem:conflict:<uuid>",
"relation": "stigmem:conflict:between",
"value": { "type": "text", "v": "<fact-id-a> <fact-id-b>" },
"source": "system:stigmem",
"confidence": 1.0,
"scope": "<same scope as the conflicting facts>"
}
Resolution order at query time
When a caller queries for a contradicted entity-relation pair, the node resolves as follows.
- Higher confidence wins. A fact asserted with
confidence: 0.95beats one withconfidence: 0.7. - Equal confidence → higher HLC wins. The causally later fact takes precedence.
- True tie → both returned. Each fact is annotated with
contradicted: true. The caller (human or agent) must decide.
Explicit resolution
A human or agent resolves a conflict through
POST /v1/conflicts/:id/resolve:
curl -X POST $STIGMEM_URL/v1/conflicts/$CONFLICT_ID/resolve \
-H "Authorization: Bearer $STIGMEM_API_KEY" \
-d '{
"winning_fact_id": "fact_01J...",
"resolution_note": "Confirmed with Alice: title is CEO as of Q2."
}'
The resolution is itself a new fact with provenance.
You can trace who resolved it, when, and why. Both original facts
remain in the store, immutable. The conflict status updates to
"resolved".
Federation and conflicts
Conflicts across federated nodes are expected, not exceptional. When Node A ingests a fact from Node B that conflicts with a local fact:
- Both facts are stored.
- A contradiction record is generated on the ingesting node.
- Both facts are returned to callers with
contradicted: true. - Conflict entities (
stigmem:conflict:<uuid>) are local — they are never federated (to prevent infinite loops).
Why this is non-obvious
Storing both sides seems redundant
It's not. In a federated system, the "wrong" value on one node may be the "right" value on another — they just haven't reconciled yet. Discarding either side before explicit resolution destroys information that may be needed for compliance, auditing, or debugging.
Contradiction records are facts about facts
The stigmem:conflict:between entity is itself an immutable fact. You can query, filter, and subscribe to conflicts the same way you query any other fact.
Resolution doesn't delete
Resolving asserts a new winning fact and marks the conflict as resolved. The original conflicting facts are untouched. You can reconstruct not just what the final answer was, but what the competing values were and who adjudicated.
Entity normalization prevents ghost conflicts
The strict normalizer (Spec-01-Fact-Model entity normalization) ensures case-variant URIs map to the same canonical form. Before pre-reset, project/EG-18 and project/eg-18 were treated as different entities — so their conflicting facts would never be detected.
What it costs
Storage for conflict records
Every contradiction produces at least two additional fact rows (the stigmem:conflict:between and stigmem:conflict:status facts). Overhead is proportional to the number of disagreements.
Query latency for contradicted triples
Returning two facts instead of one means more data to serialize. The include_contradicted=false default hides this from callers who don't need it.
Operator attention
Unresolved conflicts accumulate. The lint operation (Spec-20) includes a contradiction check; operators should run it periodically and resolve or escalate stale contradictions.
No automatic merge
Stigmem never guesses. If two values conflict and neither has higher confidence or a later HLC, the system surfaces the ambiguity rather than inventing a resolution. Correctness over convenience.