Class: ClaudeMemory::Resolve::Resolver

Inherits:
Object
  • Object
show all
Defined in:
lib/claude_memory/resolve/resolver.rb

Overview

Truth maintenance engine that processes distilled extractions into stored facts. Wraps entity resolution, fact insertion, supersession, and conflict detection in a single database transaction.

Examples:

resolver = Resolver.new(store)
result = resolver.apply(extraction, content_item_id: 42, scope: "project")
result[:facts_created]   #=> 3
result[:facts_superseded] #=> 1

Instance Method Summary collapse

Constructor Details

#initialize(store) ⇒ Resolver

Returns a new instance of Resolver.

Parameters:



16
17
18
# File 'lib/claude_memory/resolve/resolver.rb', line 16

def initialize(store)
  @store = store
end

Instance Method Details

#apply(extraction, content_item_id: nil, occurred_at: nil, project_path: nil, scope: "project") ⇒ Hash

Apply a distilled extraction, resolving each fact against existing knowledge. Facts may be inserted, reinforce an existing fact, supersede old facts, or create a conflict when the resolution is ambiguous.

Parameters:

  • extraction (#entities, #facts)

    distilled extraction with entities and facts

  • content_item_id (Integer, nil) (defaults to: nil)

    source content item for provenance

  • occurred_at (String, nil) (defaults to: nil)

    ISO 8601 timestamp (defaults to now)

  • project_path (String, nil) (defaults to: nil)

    project path for scoped facts

  • scope (String) (defaults to: "project")

    default scope for facts (“project” or “global”)

Returns:

  • (Hash)

    counts keyed by :entities_created, :facts_created, :facts_superseded, :conflicts_created, :provenance_created, :observations_created, plus :fact_ids (see below)



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/claude_memory/resolve/resolver.rb', line 32

def apply(extraction, content_item_id: nil, occurred_at: nil, project_path: nil, scope: "project")
  occurred_at ||= Time.now.utc.iso8601

  result = {
    entities_created: 0,
    facts_created: 0,
    facts_superseded: 0,
    conflicts_created: 0,
    provenance_created: 0,
    observations_created: 0,
    # Ids of the facts each input touched (insert/reinforce/conflict),
    # positionally aligned with extraction.facts — so callers like the
    # promotion bridge don't have to re-query for what the resolver
    # already knows. Entries are `nil` where the fact was discarded or
    # lost a conflict, so consumers wanting only real ids must `.compact`.
    fact_ids: []
  }

  # Wrap entire extraction in a single transaction for better concurrency
  # This reduces database lock time compared to per-fact transactions
  @store.db.transaction do
    entity_ids = resolve_entities(extraction.entities)
    result[:entities_created] = entity_ids.size

    extraction.facts.each do |fact_data|
      outcome = resolve_fact(fact_data, entity_ids, content_item_id, occurred_at,
        project_path: project_path, scope: scope)
      result[:facts_created] += outcome[:created]
      result[:facts_superseded] += outcome[:superseded]
      result[:conflicts_created] += outcome[:conflicts]
      result[:provenance_created] += outcome[:provenance]
      result[:fact_ids] << outcome[:fact_id]
    end

    # Episodic layer: persist observations alongside facts. Append-only,
    # no truth maintenance — observations accumulate; the Reflector
    # consolidates them later. Older extractions (and the empty default)
    # carry no observations, so fact behavior is unchanged.
    result[:observations_created] = persist_observations(
      extraction, content_item_id, occurred_at, project_path: project_path, scope: scope
    )
  end

  result
end