Class: KairosMcp::StateCommit::SnapshotManager

Inherits:
Object
  • Object
show all
Defined in:
lib/kairos_mcp/state_commit/snapshot_manager.rb

Overview

SnapshotManager: Manages state snapshots (off-chain storage)

Snapshots are stored as JSON files in storage/snapshots/ Each snapshot contains the full manifest and change history.

Constant Summary collapse

DEFAULT_MAX_SNAPSHOTS =
100

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(snapshot_dir: nil, max_snapshots: nil) ⇒ SnapshotManager

Returns a new instance of SnapshotManager.



18
19
20
21
22
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 18

def initialize(snapshot_dir: nil, max_snapshots: nil)
  @snapshot_dir = resolve_snapshot_dir(snapshot_dir)
  @max_snapshots = max_snapshots || DEFAULT_MAX_SNAPSHOTS
  FileUtils.mkdir_p(@snapshot_dir)
end

Instance Attribute Details

#snapshot_dirObject (readonly)

Get the snapshot directory path



177
178
179
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 177

def snapshot_dir
  @snapshot_dir
end

Instance Method Details

#cleanup_old_snapshots(max_count = nil) ⇒ Integer

Cleanup old snapshots to maintain max_snapshots limit

Parameters:

  • max_count (Integer) (defaults to: nil)

    Maximum number to keep (uses default if nil)

Returns:

  • (Integer)

    Number of snapshots deleted



164
165
166
167
168
169
170
171
172
173
174
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 164

def cleanup_old_snapshots(max_count = nil)
  max_count ||= @max_snapshots
  files = snapshot_files
  
  return 0 if files.size <= max_count

  # Delete oldest files (files are sorted newest first)
  to_delete = files[max_count..-1]
  to_delete.each { |f| FileUtils.rm_f(f) }
  to_delete.size
end

#countInteger

Get snapshot count

Returns:

  • (Integer)

    Number of snapshots



132
133
134
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 132

def count
  snapshot_files.size
end

#delete_snapshot(snapshot_hash) ⇒ Boolean

Delete a specific snapshot

Parameters:

  • snapshot_hash (String)

    Hash of snapshot to delete

Returns:

  • (Boolean)

    True if deleted



150
151
152
153
154
155
156
157
158
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 150

def delete_snapshot(snapshot_hash)
  snapshot_files.each do |filepath|
    if File.basename(filepath).include?(snapshot_hash[0..7])
      FileUtils.rm_f(filepath)
      return true
    end
  end
  false
end

#exists?(snapshot_hash) ⇒ Boolean

Check if a snapshot exists

Parameters:

  • snapshot_hash (String)

    Hash to check

Returns:

  • (Boolean)

    True if snapshot exists



140
141
142
143
144
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 140

def exists?(snapshot_hash)
  snapshot_files.any? do |filepath|
    File.basename(filepath).include?(snapshot_hash[0..7])
  end
end

#get_last_snapshotHash?

Get the most recent snapshot

Returns:

  • (Hash, nil)

    Most recent snapshot or nil if none exist



89
90
91
92
93
94
95
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 89

def get_last_snapshot
  files = snapshot_files
  return nil if files.empty?

  # Files are sorted by modification time descending
  load_snapshot_file(files.first)
end

#get_snapshot_metadata(filepath) ⇒ Hash?

Get snapshot metadata without loading full content

Parameters:

  • filepath (String)

    Path to snapshot file

Returns:

  • (Hash, nil)

    Metadata or nil



101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 101

def (filepath)
  snapshot = load_snapshot_file(filepath)
  return nil unless snapshot

  {
    snapshot_hash: snapshot['snapshot_hash'],
    created_at: snapshot['created_at'],
    created_by: snapshot['created_by'],
    commit_type: snapshot['commit_type'],
    reason: snapshot['reason'],
    change_count: snapshot['changes_since_last']&.size || 0
  }
end

#list_snapshots(limit: 50) ⇒ Array<Hash>

List all snapshots (metadata only)

Parameters:

  • limit (Integer) (defaults to: 50)

    Maximum number of snapshots to return

Returns:

  • (Array<Hash>)

    List of snapshot metadata



119
120
121
122
123
124
125
126
127
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 119

def list_snapshots(limit: 50)
  snapshot_files.first(limit).filter_map do |filepath|
     = (filepath)
    next unless 

    [:filename] = File.basename(filepath)
    
  end
end

#load_snapshot(snapshot_hash) ⇒ Hash?

Load a snapshot by hash

Parameters:

  • snapshot_hash (String)

    Hash of the snapshot to load

Returns:

  • (Hash, nil)

    Snapshot data or nil if not found



69
70
71
72
73
74
75
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 69

def load_snapshot(snapshot_hash)
  snapshot_files.each do |filepath|
    snapshot = load_snapshot_file(filepath)
    return snapshot if snapshot && snapshot['snapshot_hash'] == snapshot_hash
  end
  nil
end

#load_snapshot_by_name(filename) ⇒ Hash?

Load a snapshot by filename

Parameters:

  • filename (String)

    Filename of the snapshot

Returns:

  • (Hash, nil)

    Snapshot data or nil if not found



81
82
83
84
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 81

def load_snapshot_by_name(filename)
  filepath = File.join(@snapshot_dir, filename)
  load_snapshot_file(filepath)
end

#save_snapshot(manifest, changes, reason:, actor:, commit_type:) ⇒ Hash

Save a new snapshot

Parameters:

  • manifest (Hash)

    Full manifest from ManifestBuilder

  • changes (Array<Hash>)

    Changes since last commit

  • reason (String)

    Reason for the commit

  • actor (String)

    Who created the commit (human/ai/system)

  • commit_type (String)

    Type of commit (explicit/auto/checkpoint)

Returns:

  • (Hash)

    Snapshot metadata including hash and path



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
# File 'lib/kairos_mcp/state_commit/snapshot_manager.rb', line 32

def save_snapshot(manifest, changes, reason:, actor:, commit_type:)
  timestamp = Time.now
  snapshot_hash = manifest[:combined_hash]

  snapshot = {
    snapshot_hash: snapshot_hash,
    created_at: timestamp.iso8601,
    created_by: actor,
    commit_type: commit_type,
    reason: reason,
    layers: manifest[:layers],
    changes_since_last: changes
  }

  # Generate filename
  filename = "snapshot_#{timestamp.strftime('%Y%m%d_%H%M%S')}_#{snapshot_hash[0..7]}.json"
  filepath = File.join(@snapshot_dir, filename)

  # Save snapshot
  File.write(filepath, JSON.pretty_generate(snapshot))

  # Cleanup old snapshots if needed
  cleanup_old_snapshots

  {
    success: true,
    snapshot_hash: snapshot_hash,
    snapshot_ref: "snapshots/#{filename}",
    filepath: filepath,
    timestamp: timestamp.iso8601
  }
end