Class: Insika::PendingActionStore
- Inherits:
-
Object
- Object
- Insika::PendingActionStore
- Includes:
- Coercion
- Defined in:
- lib/insika/pending_action_store.rb
Overview
Domain store for PENDING approval ACTIONS ("state as a
record, not a flag"). A tool marked approval (Policy) creates a
PendingAction and the turn goes to :waiting; the operator resolves it via ApproveAction.
Durable (over an injected Insika::Store): survives a kill -9 — the operator
approves after the reboot, and Recovery rehydrates the task in :waiting.
Normalizes symbol→string on WRITE (the backend only guarantees round-trip of JSON types), like the other domain stores.
Defined Under Namespace
Classes: PendingAction
Constant Summary collapse
- SCOPE =
"pending_actions"- KEY_PREFIX =
"pending:"- STATUSES =
%i[pending approved rejected].freeze
Instance Method Summary collapse
-
#all_open ⇒ Object
-> [PendingAction] every :pending across all tasks — the approvals inbox (§12 G5, Studio).
-
#create(task_id:, turn:, tool:, args: {}, id: SecureRandom.uuid) ⇒ Object
-> PendingAction (:pending).
-
#find(id) ⇒ Object
-> PendingAction | nil.
-
#initialize(store:) ⇒ PendingActionStore
constructor
A new instance of PendingActionStore.
-
#open_for(task_id) ⇒ Object
-> [PendingAction] :pending for the task (recovery/UI).
-
#resolve(id, decision:, operator: nil) ⇒ Object
-> resolved PendingAction.
Methods included from Coercion
blank?, deep_stringify, presence, present?, utf8
Constructor Details
#initialize(store:) ⇒ PendingActionStore
Returns a new instance of PendingActionStore.
26 27 28 |
# File 'lib/insika/pending_action_store.rb', line 26 def initialize(store:) @store = store end |
Instance Method Details
#all_open ⇒ Object
-> [PendingAction] every :pending across all tasks — the approvals inbox (§12 G5, Studio). Single O(n) scan (vs. open_for per task = O(n·m)); the UI resolves task context afterwards via TaskStore#find.
69 70 71 72 73 74 75 76 77 |
# File 'lib/insika/pending_action_store.rb', line 69 def all_open @store.list(SCOPE, KEY_PREFIX).filter_map do |key| record = @store.get(SCOPE, key) next if record.nil? pa = to_pending(record) pa if pa.status == :pending end end |
#create(task_id:, turn:, tool:, args: {}, id: SecureRandom.uuid) ⇒ Object
-> PendingAction (:pending). args is the Hash of the tool call's arguments.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/insika/pending_action_store.rb', line 31 def create(task_id:, turn:, tool:, args: {}, id: SecureRandom.uuid) record = { "id" => id.to_s, "task_id" => task_id.to_s, "turn" => turn, "tool" => tool.to_s, "args" => deep_stringify(args), "status" => "pending", "requested_at" => , "resolved_by" => nil, "resolved_at" => nil } @store.set(SCOPE, key_for(id), record) to_pending(record) end |
#find(id) ⇒ Object
-> PendingAction | nil
48 49 50 51 |
# File 'lib/insika/pending_action_store.rb', line 48 def find(id) record = @store.get(SCOPE, key_for(id)) record && to_pending(record) end |
#open_for(task_id) ⇒ Object
-> [PendingAction] :pending for the task (recovery/UI). O(n) scan — single-node, like TaskStore#running_or_interrupted.
55 56 57 58 59 60 61 62 63 64 |
# File 'lib/insika/pending_action_store.rb', line 55 def open_for(task_id) id = task_id.to_s @store.list(SCOPE, KEY_PREFIX).filter_map do |key| record = @store.get(SCOPE, key) next if record.nil? pa = to_pending(record) pa if pa.status == :pending && pa.task_id == id end end |
#resolve(id, decision:, operator: nil) ⇒ Object
-> resolved PendingAction. Only resolves :pending: a double resolution or an invalid decision -> ValidationError; absent -> NotFoundError.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/insika/pending_action_store.rb', line 81 def resolve(id, decision:, operator: nil) target = decision.to_sym unless %i[approved rejected].include?(target) raise Insika::ValidationError, "invalid decision: #{decision} (approved|rejected)" end record = @store.get(SCOPE, key_for(id)) || (raise Insika::NotFoundError, "pending action not found: #{id}") unless record["status"] == "pending" raise Insika::ValidationError, "pending action '#{id}' already resolved (#{record["status"]})" end record["status"] = target.to_s record["resolved_by"] = operator&.to_s record["resolved_at"] = @store.set(SCOPE, key_for(id), record) to_pending(record) end |