Add entity-aware fact resolution against Nexus (Vikunja #279)

facts gain a Subject/EntityID/ResolutionState triple and an async
enrichment worker that resolves free-text subjects to canonical Nexus
entity_ids, mirroring Praxis's enrichment-worker pattern. Ambiguous or
unreachable Nexus never guesses an entity_id — the fact stays pending
or terminal-ambiguous instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ghELqYhZNLub2TXGMazqA
This commit is contained in:
kami
2026-07-20 12:00:37 +04:00
parent c932cd8677
commit 03fa52dfd4
8 changed files with 485 additions and 0 deletions
+24
View File
@@ -40,8 +40,32 @@ type Fact struct {
Source string // tap:* | infer:* | poll:* | ambient | promote | feedback
Confidence float64
VoidsID sql.NullInt64
// Subject, EntityID, ResolutionState — entity-aware memory (Vikunja #279).
// Subject is the free-text "who/what this fact is about" supplied at write
// time by WriteFactAboutSubject; empty means the fact isn't about a
// resolvable entity (ResolutionState stays "none"). EntityID is the
// canonical Nexus entity_id once the enrichment worker resolves Subject.
Subject string
EntityID sql.NullString
ResolutionState FactResolutionState
}
// FactResolutionState — where a fact's Subject stands in Nexus entity
// resolution. "none" = no subject given (most facts). "pending" = subject
// given, not yet resolved. Terminal states: "resolved", "ambiguous" (Nexus
// returned candidates, not stored — matches the ecosystem's ambiguity-blocks
// invariant), "not_found".
type FactResolutionState string
const (
ResolutionNone FactResolutionState = "none"
ResolutionPending FactResolutionState = "pending"
ResolutionResolved FactResolutionState = "resolved"
ResolutionAmbiguous FactResolutionState = "ambiguous"
ResolutionNotFound FactResolutionState = "not_found"
)
// Bucket — presence hysteresis state.
type Bucket string