Knowledge quality
Working with contradictions, health scores, syntheses and merge candidates.
Four signals tell you whether the memory is in good shape. All four are queryable, so quality can be a dashboard rather than a hunch.
Contradictions
GET/api/v1/memory/contradictions
{
"contradictions": [
{
"contradiction_id": "ctr_9f2c",
"entity_name": "Acme Corp",
"attribute": "deal_stage",
"type": "value_conflict",
"severity": "high",
"existing": { "value": "in_negotiation", "confidence": 0.88, "source_id": "doc_crm" },
"incoming": { "value": "closed", "confidence": 0.95, "source_id": "doc_email" },
"status": "pending"
}
]
}Resolve with one of four outcomes:
POST/api/v1/memory/contradictions/{id}/resolve
| Resolution | Use when |
|---|---|
accept_incoming | The new value is right |
keep_existing | The current value is right |
both_valid | Both were true at different times |
ignore | Not a real conflict |
both_valid is under-used. A contact who changed employer, or a deal that
legitimately moved stage, is a transition — recording it preserves history
that accept_incoming destroys.
Read the queue as a signal
A spike of conflicts from one connector usually means an upstream field mapping is wrong. Fix the source system, not the individual records — otherwise you resolve the same conflict every sync.
Health
GET/api/v1/memory/health/latest
{
"score": 0.74,
"grade": "C",
"dimensions": {
"freshness": 0.82,
"coverage": 0.68,
"connectivity": 0.71,
"coherence": 0.75
},
"summary": "Well connected, but thin on supporting documents...",
"issues": [
{
"type": "KNOWLEDGE_GAP",
"severity": "critical",
"description": "Sales has ingested nothing in 18 days",
"suggestion": "Check connector status and re-trigger sync"
}
]
}Read the dimensions, not the grade. Each points at a different fix:
| Low dimension | Usually means | Fix |
|---|---|---|
| Freshness | A connector stalled | Re-sync the source |
| Coverage | Many entities, few documents | Ingest more, or connect another source |
| Connectivity | Isolated entities | Usually resolves as coverage improves |
| Coherence | Duplicate and contradiction backlog | Work the queues |
Pass vault_id to score one department. A memory can be healthy overall while one
department is unusable — the aggregate hides that, the per-department score does
not.
Scored nightly; POST /health/run forces a recompute.
Syntheses
GET/api/v1/memory/synthesis/{entity_id}
A compiled profile per entity — overview, current relationship, key contacts, recent activity, risks, likely outcome.
{
"entity_name": "Acme Corp",
"content": "## Overview\n\nAcme Corp is a manufacturing company...",
"source_count": 47,
"version": 7,
"updated_at": "2026-07-30T02:14:00Z"
}Always render source_count alongside the content. A synthesis is a summary, not
ground truth, and the source count is what tells a reader how much to trust it.
Syntheses are not yet ranked into /search results automatically — fetch them
explicitly when you know the entity, such as on an account detail page.
Merge candidates
GET/api/v1/memory/coherence/candidates
Entities that look like duplicates but scored below the auto-merge threshold. Confident matches — a shared email, domain or profile URL — are merged automatically and never appear here.
{
"candidates": [
{
"candidate_id": "mc_7a1b",
"left": { "entity_id": "ent_jana_muller", "name": "Jana Müller" },
"right": { "entity_id": "ent_jana_mueller", "name": "Jana Mueller" },
"similarity": 0.87
}
]
}Merging is non-destructive: the losing entity is linked rather than deleted, so a mistake is recoverable.
Wiring it up
Subscribe to the events
memory.entity.contradiction_detected and memory.health.assessed push the
signals to you instead of requiring a poll.
Route by severity
High-severity contradictions to a person; low-severity to a weekly digest.
Alert on freshness, not on sync status
A stalled connector shows up as declining freshness. Sync status alone can look fine while nothing new is arriving.