Class: ClaudeMemory::Sweep::Sweeper
- Inherits:
-
Object
- Object
- ClaudeMemory::Sweep::Sweeper
- Defined in:
- lib/claude_memory/sweep/sweeper.rb
Constant Summary collapse
- DEFAULT_CONFIG =
{ proposed_fact_ttl_days: 14, disputed_fact_ttl_days: 30, content_retention_days: 30, mcp_tool_call_retention_days: 90, default_budget_seconds: 5 }.freeze
- ESCALATION_LEVELS =
Three-level escalation ensures sweep always makes progress. Source: lossless-claw three-level escalation pattern
%i[normal aggressive fallback].freeze
Instance Method Summary collapse
-
#initialize(store, config: {}) ⇒ Sweeper
constructor
A new instance of Sweeper.
- #run!(budget_seconds: nil) ⇒ Object
-
#run_with_escalation!(budget_seconds: nil) ⇒ Object
Run sweep with escalation: if normal sweep makes no progress, escalate to aggressive (halved TTLs), then fallback (force-expire oldest).
Constructor Details
#initialize(store, config: {}) ⇒ Sweeper
Returns a new instance of Sweeper.
18 19 20 21 22 23 |
# File 'lib/claude_memory/sweep/sweeper.rb', line 18 def initialize(store, config: {}) @store = store @config = DEFAULT_CONFIG.merge(config) @start_time = nil @stats = nil end |
Instance Method Details
#run!(budget_seconds: nil) ⇒ Object
25 26 27 28 29 30 31 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 |
# File 'lib/claude_memory/sweep/sweeper.rb', line 25 def run!(budget_seconds: nil) budget = budget_seconds || @config[:default_budget_seconds] @start_time = Time.now @stats = { proposed_facts_expired: 0, disputed_facts_expired: 0, orphaned_provenance_deleted: 0, old_content_pruned: 0, mcp_tool_calls_pruned: 0, escalation_level: :normal } maintenance = build_maintenance(:normal) run_if_within_budget { @stats[:proposed_facts_expired] = maintenance.expire_proposed_facts } run_if_within_budget { @stats[:disputed_facts_expired] = maintenance.expire_disputed_facts } run_if_within_budget { @stats[:orphaned_provenance_deleted] = maintenance.prune_orphaned_provenance } run_if_within_budget { @stats[:old_content_pruned] = maintenance.prune_old_content } run_if_within_budget { @stats[:mcp_tool_calls_pruned] = maintenance.prune_old_mcp_tool_calls } run_if_within_budget { @stats[:vec_backfilled] = maintenance.backfill_vec_index } run_if_within_budget { @stats[:vec_cleaned] = maintenance.cleanup_vec_expired } run_if_within_budget { @stats[:wal_checkpointed] = maintenance.checkpoint_wal } @stats[:elapsed_seconds] = Time.now - @start_time @stats[:budget_honored] = @stats[:elapsed_seconds] <= budget ClaudeMemory.logger.info("sweep", message: "Sweep complete", elapsed_seconds: @stats[:elapsed_seconds].round(3), budget_honored: @stats[:budget_honored], escalation_level: @stats[:escalation_level], proposed_expired: @stats[:proposed_facts_expired], disputed_expired: @stats[:disputed_facts_expired]) @stats end |
#run_with_escalation!(budget_seconds: nil) ⇒ Object
Run sweep with escalation: if normal sweep makes no progress, escalate to aggressive (halved TTLs), then fallback (force-expire oldest). Returns stats hash with :escalation_level indicating which level succeeded.
Source: lossless-claw three-level escalation pattern
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/claude_memory/sweep/sweeper.rb', line 65 def run_with_escalation!(budget_seconds: nil) stats = run!(budget_seconds: budget_seconds) total = stats[:proposed_facts_expired] + stats[:disputed_facts_expired] + stats[:orphaned_provenance_deleted] + stats[:old_content_pruned] if total == 0 # Escalate to aggressive — halved TTLs aggressive_maintenance = build_maintenance(:aggressive) added = 0 added += aggressive_maintenance.expire_proposed_facts added += aggressive_maintenance.expire_disputed_facts added += aggressive_maintenance.prune_old_content if added > 0 stats[:proposed_facts_expired] += added stats[:escalation_level] = :aggressive else # Fallback — force-expire oldest proposed/disputed facts fallback_count = force_expire_oldest stats[:proposed_facts_expired] += fallback_count stats[:escalation_level] = :fallback if fallback_count > 0 end stats[:elapsed_seconds] = Time.now - @start_time end stats end |