Class: ClaudeMemory::Observe::Reflector

Inherits:
Object
  • Object
show all
Defined in:
lib/claude_memory/observe/reflector.rb

Overview

Deterministic, free (no LLM) Reflector for the episodic observation log.

Runs inside Sweep, which fires on PreCompact and SessionEnd — Claude Code’s context-pressure lifecycle events. That is the analog of Mastra’s token-threshold-triggered reflection: “reflect when memory gets big” maps onto “reflect when the session is about to compact”, without a wall-clock timer (Claude Code has no cron hook) and without extra API cost.

Two passes, both provenance-preserving (tombstone, never hard-delete):

- dedupe: collapse near-duplicate active observations (same scope) into
  the newest, linking losers via consolidated_into. Similarity is decided
  by an injected matcher (default: lexical token-overlap, #73) so the
  promotion gate can actually accumulate corroboration — exact-string
  matching never folded varied wording, leaving every observation at
  corroboration 1 (the 2026-06-23 audit finding).
- expire_stale_info: retire info-level (🟢 / priority 3) observations
  older than the TTL to bound context size. Important (🔴) and maybe
  (🟡) are never expired — only the lowest-signal tier ages out.

Semantic consolidation (“combine related items, surface patterns”) is deliberately NOT here — it needs the LLM and lands in the Phase-4 Claude-as-reflector pass. This pass is pure Ruby so it can run shell-side in the sweep hook for free.

Defined Under Namespace

Classes: Result

Constant Summary collapse

DEFAULT_INFO_TTL_DAYS =
30

Instance Method Summary collapse

Constructor Details

#initialize(store, info_ttl_days: DEFAULT_INFO_TTL_DAYS, matcher: TokenOverlapMatcher.new) ⇒ Reflector

Returns a new instance of Reflector.



38
39
40
41
42
# File 'lib/claude_memory/observe/reflector.rb', line 38

def initialize(store, info_ttl_days: DEFAULT_INFO_TTL_DAYS, matcher: TokenOverlapMatcher.new)
  @store = store
  @info_ttl_days = info_ttl_days
  @matcher = matcher
end

Instance Method Details

#reflect!Result

Returns number of observations deduped and expired.

Returns:

  • (Result)

    number of observations deduped and expired



45
46
47
48
49
50
51
52
53
# File 'lib/claude_memory/observe/reflector.rb', line 45

def reflect!
  deduped = 0
  expired = 0
  @store.db.transaction do
    deduped = dedupe
    expired = expire_stale_info
  end
  Result.new(deduped: deduped, expired: expired)
end