Class: KairosMcp::Daemon::IdempotentChainRecorder
- Inherits:
-
Object
- Object
- KairosMcp::Daemon::IdempotentChainRecorder
- Defined in:
- lib/kairos_mcp/daemon/idempotent_chain_recorder.rb
Overview
IdempotentChainRecorder — wraps chain_record with proposal_id-based idempotency and retry logic.
Design (P3.2 v0.2 §6.3-6.4, M7):
- Uses proposal_id as externally-supplied idempotency key.
- Tracks recorded proposal_ids in a local ledger file.
- Retries up to MAX_RETRIES on failure, then pauses mandate.
- Recorded (successful) proposal_ids survive restart via ledger.
- Pending retries and exhausted entries are in-memory only;
restart resets the retry budget (intentional: fresh attempt).
Constant Summary collapse
- MAX_RETRIES =
3
Instance Method Summary collapse
-
#has_failures? ⇒ Boolean
True if any record has exhausted retries.
-
#initialize(chain_tool:, ledger_path:, logger: nil) ⇒ IdempotentChainRecorder
constructor
A new instance of IdempotentChainRecorder.
-
#pending_count ⇒ Object
Number of pending (failed) chain records.
-
#record(payload) ⇒ Hash
Record a code_edit to the blockchain.
-
#retry_pending ⇒ Array<Hash>
Retry any pending records (call at cycle start or periodic tick).
Constructor Details
#initialize(chain_tool:, ledger_path:, logger: nil) ⇒ IdempotentChainRecorder
Returns a new instance of IdempotentChainRecorder.
25 26 27 28 29 30 31 32 |
# File 'lib/kairos_mcp/daemon/idempotent_chain_recorder.rb', line 25 def initialize(chain_tool:, ledger_path:, logger: nil) @chain_tool = chain_tool @ledger_path = ledger_path @logger = logger @recorded = load_ledger @pending = [] # Array of { proposal_id:, payload:, retries: } @exhausted = [] # proposal_ids that exceeded MAX_RETRIES end |
Instance Method Details
#has_failures? ⇒ Boolean
True if any record has exhausted retries.
78 79 80 |
# File 'lib/kairos_mcp/daemon/idempotent_chain_recorder.rb', line 78 def has_failures? !@exhausted.empty? end |
#pending_count ⇒ Object
Number of pending (failed) chain records.
73 74 75 |
# File 'lib/kairos_mcp/daemon/idempotent_chain_recorder.rb', line 73 def pending_count @pending.size end |
#record(payload) ⇒ Hash
Record a code_edit to the blockchain. Idempotent by proposal_id.
38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/kairos_mcp/daemon/idempotent_chain_recorder.rb', line 38 def record(payload) proposal_id = payload[:proposal_id] || payload['proposal_id'] raise ArgumentError, 'proposal_id required' if proposal_id.to_s.empty? # Idempotency check if @recorded.include?(proposal_id) return { status: 'duplicate', proposal_id: proposal_id } end attempt_record(proposal_id, payload) end |
#retry_pending ⇒ Array<Hash>
Retry any pending records (call at cycle start or periodic tick).
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/kairos_mcp/daemon/idempotent_chain_recorder.rb', line 52 def retry_pending results = [] remaining = [] @pending.each do |entry| result = attempt_record(entry[:proposal_id], entry[:payload], retry_count: entry[:retries], from_retry: true) results << result case result[:status] when 'pending_retry' remaining << entry.merge(retries: entry[:retries] + 1) when 'failed' @exhausted << entry[:proposal_id] end end @pending = remaining results end |