@happyvertical/smrt-facts — Specification
Reconcile Algorithm
The reconcile() method is the primary entry point for ingesting new knowledge. It determines whether incoming text represents a new fact, corroborates an existing one, or contradicts it.
Input
interface ReconcileOptions {
rawInput: string; // Text to reconcile
similarityThreshold?: number; // Auto-merge threshold (default: 0.85)
conflictThreshold?: number; // Minimum similarity to consider (default: 0.60)
decisionMergeProbabilityThreshold?: number; // Typed-decision merge threshold (default: 0.75)
type?: FactType; // Classification (default: 'assertion')
domain?: string; // Domain scope
source?: { // Optional provenance
sourceType?: string;
sourceUrl?: string;
sourceTitle?: string;
credibility?: number;
};
}
Decision Flow
semanticSearch(rawInput, limit=5, minSimilarity=conflictThreshold)
│
├─ No match above conflictThreshold (0.60)
│ └─ CREATE new fact (status: active)
│
├─ Top match >= similarityThreshold (0.85)
│ └─ MERGE: increment sourceCount, update textRaw
│
└─ Ambiguous zone (0.60 - 0.85)
└─ Typed choice decision when configured; otherwise legacy AI text disambiguation
├─ Typed "merge" at selected merge probability >= 0.75 and above branch → MERGE
├─ Typed "branch", low selected merge probability, malformed response, or provider error → BRANCH
└─ Legacy text containing "branch" → BRANCH; other successful text → MERGE
The typed-decision merge probability threshold is a conservative operating threshold, not a calibrated assurance score. It is independent from the cosine-similarity thresholds that select the reconciliation zone. Registered reconciliation prompts and tenant or instance overrides supply the typed decision instructions. Values outside [0, 1] are rejected before a configured provider is called.
After the action, if source metadata was provided, a FactSource record is created. On merge with a source, recalculateConfidence() is called to update the fact's confidence score.
Output
interface ReconcileResult {
action: 'created' | 'merged' | 'branched';
fact: Fact; // The resulting fact
source?: FactSource; // Created source record
similarity?: number; // Best match similarity
matchedFact?: Fact; // Existing fact that was matched
}
Confidence Formula
Version 1 uses a simple weighted sum clamped to [0, 1]:
confidence = base + sourceBoost + credibilityBoost + recencyBoost + corroborationBoost
Where:
base = 0.5
sourceBoost = min(sourceCount / 10, 0.3)
credibilityBoost = avgSourceCredibility * 0.2
recencyBoost = max(0, 0.1 - daysSinceLastSource * 0.01)
corroborationBoost = corroborationScore * 0.1
Result: max(0, min(1, confidence))
Defaults (when no sources exist): avgSourceCredibility = 0.5, daysSinceLastSource = 0, corroborationScore = 0 — yielding a baseline confidence of 0.7.
The recalculateConfidence() method loads all FactSource records, computes averages, and saves the updated confidence and sourceCount to the fact.
Evolution Semantics
Facts form directed acyclic graphs via parentId. Each child has an evolutionType:
| Type | Meaning | Parent status change |
|---|---|---|
original | Root fact, no parent | — |
correction | Fixes an error in parent | Parent → superseded |
refinement | Improves wording/precision | No change |
contradiction | Directly contradicts parent | Parent → superseded |
extension | Adds new information to parent | No change |
merge | Combines multiple facts | No change |
Evolution Methods
getEvolutionChain(factId): Walk up viaparentIdto root. Returns[root, ..., factId]. Uses visited-ID Set for cycle safety.getLatestInChain(factId): Walk down children, always picking highest confidence. Returns the leaf. Uses visited-ID Set for cycle safety.getEvolutionTree(factId): Find root viagetEvolutionChain, then BFS all descendants. Returns flat array of entire tree.branch(parentId, data, evolutionType): Create child fact, generate embeddings, and if evolution type iscorrectionorcontradiction, mark parent assuperseded.
Entity Briefing
getEntityBriefing(entityType, entityId) aggregates all facts linked to an entity via FactSubject:
interface EntityBriefing {
entityType: string;
entityId: string;
facts: Fact[];
totalCount: number;
byType: Record<string, number>; // e.g., { assertion: 5, observation: 2 }
byStatus: Record<string, number>; // e.g., { active: 6, superseded: 1 }
}
Type Reference
FactType
assertion | observation | measurement | definition | relationship | event | opinion | prediction
FactStatus
pending | active | disputed | superseded | archived | retracted
EvolutionType
original | correction | refinement | contradiction | extension | merge
SubjectRole
subject | object | source | location | participant | related
FactContentRelationship
extracted_from | referenced_in | supports | contradicts | related
Future Work
- DispatchBus events: Emit
fact.discoveredevents from reconcile() for reactive pipelines - interesting() integration: Use confidence + source count + recency to determine if a fact is noteworthy
- Cross-gnode federation: Share facts between gnodes with provenance chains
- Batch reconcile: Process multiple inputs in a single call with shared semantic search