igniter 0.4.0 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/README.md +238 -218
  4. data/docs/LLM_V1.md +335 -0
  5. data/docs/PATTERNS.md +189 -0
  6. data/docs/SERVER_V1.md +313 -0
  7. data/examples/README.md +129 -0
  8. data/examples/agents.rb +150 -0
  9. data/examples/differential.rb +161 -0
  10. data/examples/distributed_server.rb +94 -0
  11. data/examples/effects.rb +184 -0
  12. data/examples/incremental.rb +142 -0
  13. data/examples/invariants.rb +179 -0
  14. data/examples/order_pipeline.rb +163 -0
  15. data/examples/provenance.rb +122 -0
  16. data/examples/saga.rb +110 -0
  17. data/lib/igniter/agent/mailbox.rb +96 -0
  18. data/lib/igniter/agent/message.rb +21 -0
  19. data/lib/igniter/agent/ref.rb +86 -0
  20. data/lib/igniter/agent/runner.rb +129 -0
  21. data/lib/igniter/agent/state_holder.rb +23 -0
  22. data/lib/igniter/agent.rb +155 -0
  23. data/lib/igniter/compiler/validators/callable_validator.rb +21 -3
  24. data/lib/igniter/differential/divergence.rb +29 -0
  25. data/lib/igniter/differential/formatter.rb +96 -0
  26. data/lib/igniter/differential/report.rb +86 -0
  27. data/lib/igniter/differential/runner.rb +130 -0
  28. data/lib/igniter/differential.rb +51 -0
  29. data/lib/igniter/dsl/contract_builder.rb +32 -0
  30. data/lib/igniter/effect.rb +91 -0
  31. data/lib/igniter/effect_registry.rb +78 -0
  32. data/lib/igniter/errors.rb +11 -1
  33. data/lib/igniter/execution_report/builder.rb +54 -0
  34. data/lib/igniter/execution_report/formatter.rb +50 -0
  35. data/lib/igniter/execution_report/node_entry.rb +24 -0
  36. data/lib/igniter/execution_report/report.rb +65 -0
  37. data/lib/igniter/execution_report.rb +32 -0
  38. data/lib/igniter/extensions/differential.rb +114 -0
  39. data/lib/igniter/extensions/execution_report.rb +27 -0
  40. data/lib/igniter/extensions/incremental.rb +50 -0
  41. data/lib/igniter/extensions/invariants.rb +116 -0
  42. data/lib/igniter/extensions/provenance.rb +45 -0
  43. data/lib/igniter/extensions/saga.rb +74 -0
  44. data/lib/igniter/incremental/formatter.rb +81 -0
  45. data/lib/igniter/incremental/result.rb +69 -0
  46. data/lib/igniter/incremental/tracker.rb +108 -0
  47. data/lib/igniter/incremental.rb +50 -0
  48. data/lib/igniter/integrations/agents.rb +18 -0
  49. data/lib/igniter/invariant.rb +50 -0
  50. data/lib/igniter/model/effect_node.rb +37 -0
  51. data/lib/igniter/model.rb +1 -0
  52. data/lib/igniter/property_testing/formatter.rb +66 -0
  53. data/lib/igniter/property_testing/generators.rb +115 -0
  54. data/lib/igniter/property_testing/result.rb +45 -0
  55. data/lib/igniter/property_testing/run.rb +43 -0
  56. data/lib/igniter/property_testing/runner.rb +47 -0
  57. data/lib/igniter/property_testing.rb +64 -0
  58. data/lib/igniter/provenance/builder.rb +97 -0
  59. data/lib/igniter/provenance/lineage.rb +82 -0
  60. data/lib/igniter/provenance/node_trace.rb +65 -0
  61. data/lib/igniter/provenance/text_formatter.rb +70 -0
  62. data/lib/igniter/provenance.rb +29 -0
  63. data/lib/igniter/registry.rb +67 -0
  64. data/lib/igniter/runtime/cache.rb +35 -6
  65. data/lib/igniter/runtime/execution.rb +8 -2
  66. data/lib/igniter/runtime/node_state.rb +7 -2
  67. data/lib/igniter/runtime/resolver.rb +84 -15
  68. data/lib/igniter/saga/compensation.rb +31 -0
  69. data/lib/igniter/saga/compensation_record.rb +20 -0
  70. data/lib/igniter/saga/executor.rb +85 -0
  71. data/lib/igniter/saga/formatter.rb +49 -0
  72. data/lib/igniter/saga/result.rb +47 -0
  73. data/lib/igniter/saga.rb +56 -0
  74. data/lib/igniter/stream_loop.rb +80 -0
  75. data/lib/igniter/supervisor.rb +167 -0
  76. data/lib/igniter/version.rb +1 -1
  77. data/lib/igniter.rb +10 -0
  78. metadata +63 -1
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Saga
5
+ # Immutable record of a single compensation that was attempted.
6
+ class CompensationRecord
7
+ attr_reader :node_name, :error
8
+
9
+ def initialize(node_name:, success:, error: nil)
10
+ @node_name = node_name
11
+ @success = success
12
+ @error = error
13
+ freeze
14
+ end
15
+
16
+ def success? = @success
17
+ def failed? = !@success
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Saga
5
+ # Runs declared compensations for all successfully completed nodes,
6
+ # in reverse topological order.
7
+ #
8
+ # A node is eligible for compensation if:
9
+ # 1. Its state in the cache is `succeeded?`
10
+ # 2. A compensation is declared for it, via one of:
11
+ # a. Contract-level `compensate :node_name do ... end` (takes precedence)
12
+ # b. Built-in compensation defined on the Igniter::Effect adapter class
13
+ #
14
+ # Compensation failures are captured as failed CompensationRecords and
15
+ # do NOT halt the rollback of other nodes.
16
+ class Executor
17
+ def initialize(contract)
18
+ @contract = contract
19
+ @graph = contract.execution.compiled_graph
20
+ @cache = contract.execution.cache
21
+ end
22
+
23
+ # @return [Array<CompensationRecord>]
24
+ def run_compensations
25
+ eligible_nodes_reversed.filter_map do |node|
26
+ compensation = find_compensation(node)
27
+ next unless compensation
28
+
29
+ attempt(compensation, node)
30
+ end
31
+ end
32
+
33
+ # Find the first node whose state is :failed in the cache.
34
+ # @return [Symbol, nil]
35
+ def failed_node_name
36
+ @cache.to_h.find { |_name, state| state.failed? }&.first
37
+ end
38
+
39
+ private
40
+
41
+ # Resolve the compensation to use for a node, if any.
42
+ #
43
+ # Contract-level `compensate :node_name` takes precedence over built-in
44
+ # compensation declared on an Igniter::Effect subclass.
45
+ #
46
+ # @return [Igniter::Saga::Compensation, nil]
47
+ def find_compensation(node)
48
+ declared = @contract.class.compensations
49
+ return declared[node.name] if declared.key?(node.name)
50
+
51
+ return nil unless node.kind == :effect
52
+
53
+ built_in = node.adapter_class.built_in_compensation
54
+ return nil unless built_in
55
+
56
+ Compensation.new(node.name, &built_in)
57
+ end
58
+
59
+ # Nodes that succeeded, in reverse resolution order.
60
+ def eligible_nodes_reversed
61
+ @graph.resolution_order
62
+ .select { |node| @cache.fetch(node.name)&.succeeded? }
63
+ .reverse
64
+ end
65
+
66
+ def attempt(compensation, node)
67
+ inputs = extract_inputs(node)
68
+ value = @cache.fetch(node.name)&.value
69
+
70
+ compensation.run(inputs: inputs, value: value)
71
+ CompensationRecord.new(node_name: compensation.node_name, success: true)
72
+ rescue StandardError => e
73
+ CompensationRecord.new(node_name: compensation.node_name, success: false, error: e)
74
+ end
75
+
76
+ # Gather the resolved values of the node's direct dependencies.
77
+ def extract_inputs(node)
78
+ node.dependencies.each_with_object({}) do |dep_name, acc|
79
+ state = @cache.fetch(dep_name)
80
+ acc[dep_name.to_sym] = state&.value
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Saga
5
+ # Formats a SagaResult as a human-readable text block.
6
+ #
7
+ # Example:
8
+ #
9
+ # Contract: OrderWorkflow
10
+ # Status: FAILED
11
+ # Error: Insufficient funds
12
+ # At node: :charge_card
13
+ #
14
+ # COMPENSATIONS (1):
15
+ # [ok] :reserve_stock
16
+ #
17
+ module Formatter
18
+ class << self
19
+ def format(result)
20
+ lines = []
21
+ lines << "Contract: #{result.contract.class.name}"
22
+ lines << "Status: #{result.success? ? "SUCCESS" : "FAILED"}"
23
+
24
+ if result.failed?
25
+ lines << "Error: #{result.error&.message}"
26
+ lines << "At node: :#{result.failed_node}" if result.failed_node
27
+ end
28
+
29
+ append_compensations(result, lines)
30
+ lines.join("\n")
31
+ end
32
+
33
+ private
34
+
35
+ def append_compensations(result, lines)
36
+ return if result.compensations.empty?
37
+
38
+ lines << ""
39
+ lines << "COMPENSATIONS (#{result.compensations.size}):"
40
+ result.compensations.each do |rec|
41
+ tag = rec.success? ? "[ok] " : "[fail] "
42
+ lines << " #{tag} :#{rec.node_name}"
43
+ lines << " error: #{rec.error.message}" if rec.failed?
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ module Saga
5
+ # Immutable result of a saga execution (resolve_saga call).
6
+ #
7
+ # Attributes:
8
+ # contract — the contract instance that was executed
9
+ # error — Igniter::Error that caused failure (nil on success)
10
+ # failed_node — Symbol name of the first node that failed (nil on success)
11
+ # compensations — Array<CompensationRecord> for all attempted compensations
12
+ class Result
13
+ attr_reader :contract, :error, :failed_node, :compensations
14
+
15
+ def initialize(success:, contract:, error: nil, failed_node: nil, compensations: [])
16
+ @success = success
17
+ @contract = contract
18
+ @error = error
19
+ @failed_node = failed_node
20
+ @compensations = compensations.freeze
21
+ freeze
22
+ end
23
+
24
+ def success? = @success
25
+ def failed? = !@success
26
+
27
+ # Human-readable saga report.
28
+ def explain
29
+ Formatter.format(self)
30
+ end
31
+
32
+ alias to_s explain
33
+
34
+ # Structured (serialisable) representation.
35
+ def to_h
36
+ {
37
+ success: success?,
38
+ failed_node: failed_node,
39
+ error: error&.message,
40
+ compensations: compensations.map do |rec|
41
+ { node: rec.node_name, success: rec.success?, error: rec.error&.message }
42
+ end
43
+ }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "errors"
4
+ require_relative "saga/compensation"
5
+ require_relative "saga/compensation_record"
6
+ require_relative "saga/formatter"
7
+ require_relative "saga/result"
8
+ require_relative "saga/executor"
9
+
10
+ module Igniter
11
+ # Saga pattern — compensating transactions for Igniter contracts.
12
+ #
13
+ # When a contract execution fails partway through, the saga system
14
+ # automatically runs the compensating actions for all previously
15
+ # SUCCEEDED nodes, in reverse topological order.
16
+ #
17
+ # Usage:
18
+ #
19
+ # require "igniter/extensions/saga"
20
+ #
21
+ # class OrderWorkflow < Igniter::Contract
22
+ # define do
23
+ # input :order_id
24
+ # input :amount
25
+ #
26
+ # compute :reserve_stock, depends_on: :order_id do |order_id:|
27
+ # InventoryService.reserve(order_id)
28
+ # end
29
+ #
30
+ # compute :charge_card, depends_on: %i[order_id amount reserve_stock] do |amount:, **|
31
+ # raise "Declined" if amount > 1000
32
+ # PaymentService.charge(amount)
33
+ # end
34
+ #
35
+ # output :charge_card
36
+ # end
37
+ #
38
+ # compensate :charge_card do |inputs:, value:|
39
+ # PaymentService.refund(value[:charge_id])
40
+ # end
41
+ #
42
+ # compensate :reserve_stock do |inputs:, value:|
43
+ # InventoryService.release(value[:reservation_id])
44
+ # end
45
+ # end
46
+ #
47
+ # result = OrderWorkflow.new(order_id: "x1", amount: 1500).resolve_saga
48
+ # result.success? # => false
49
+ # result.failed_node # => :charge_card
50
+ # result.compensations.map(&:node_name) # => [:reserve_stock]
51
+ # puts result.explain
52
+ #
53
+ module Saga
54
+ class SagaError < Igniter::Error; end
55
+ end
56
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ # Runs an Igniter contract in a continuous tick-loop.
5
+ #
6
+ # Each tick resolves the contract with the current inputs and delivers the
7
+ # result to the on_result callback. Useful for sensor polling, feed
8
+ # processing, or any recurring computation.
9
+ #
10
+ # stream = Igniter::StreamLoop.new(
11
+ # contract: SensorContract,
12
+ # tick_interval: 0.1,
13
+ # inputs: { sensor_id: "temp-1", threshold: 25.0 },
14
+ # on_result: ->(result) { puts result.status },
15
+ # on_error: ->(err) { warn err.message }
16
+ # )
17
+ #
18
+ # stream.start
19
+ # stream.update_inputs(threshold: 30.0) # hot-swap inputs between ticks
20
+ # stream.stop
21
+ #
22
+ class StreamLoop
23
+ def initialize(contract:, tick_interval: 1.0, inputs: {}, on_result: nil, on_error: nil)
24
+ @contract_class = contract
25
+ @tick_interval = tick_interval.to_f
26
+ @on_result = on_result
27
+ @on_error = on_error
28
+ @mutex = Mutex.new
29
+ @current_inputs = inputs.dup
30
+ @running = false
31
+ @thread = nil
32
+ end
33
+
34
+ # Start the loop in a background thread. Returns self.
35
+ def start
36
+ @running = true
37
+ @thread = Thread.new { loop_body }
38
+ @thread.abort_on_exception = false
39
+ self
40
+ end
41
+
42
+ # Stop the loop and wait for the current tick to finish.
43
+ def stop(timeout: 5)
44
+ @running = false
45
+ @thread&.join(timeout)
46
+ self
47
+ end
48
+
49
+ # Merge +new_inputs+ into the current input set. Takes effect on the next tick.
50
+ def update_inputs(new_inputs)
51
+ @mutex.synchronize { @current_inputs.merge!(new_inputs) }
52
+ self
53
+ end
54
+
55
+ def alive?
56
+ @thread&.alive? || false
57
+ end
58
+
59
+ private
60
+
61
+ def loop_body
62
+ while @running
63
+ tick_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
64
+ run_tick
65
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - tick_start
66
+ sleep_for = [@tick_interval - elapsed, 0].max
67
+ sleep(sleep_for) if sleep_for.positive? && @running
68
+ end
69
+ end
70
+
71
+ def run_tick
72
+ inputs = @mutex.synchronize { @current_inputs.dup }
73
+ contract = @contract_class.new(**inputs)
74
+ contract.resolve_all
75
+ @on_result&.call(contract.result)
76
+ rescue StandardError => e
77
+ @on_error&.call(e)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Igniter
4
+ # Supervises a group of agents and restarts them when they crash.
5
+ #
6
+ # Subclass Supervisor and declare children with the class-level DSL:
7
+ #
8
+ # class AppSupervisor < Igniter::Supervisor
9
+ # strategy :one_for_one # default
10
+ # max_restarts 5, within: 60 # default
11
+ #
12
+ # children do |c|
13
+ # c.worker :counter, CounterAgent
14
+ # c.worker :logger, LoggerAgent, initial_state: { level: :info }
15
+ # end
16
+ # end
17
+ #
18
+ # sup = AppSupervisor.start
19
+ # sup.child(:counter).send(:increment, by: 1)
20
+ # sup.stop
21
+ #
22
+ # Restart strategies:
23
+ # :one_for_one — restart only the crashed agent (default)
24
+ # :one_for_all — stop all agents and restart them all when any one crashes
25
+ #
26
+ # Restart budget: if more than +max_restarts+ crashes happen within +within+
27
+ # seconds, the supervisor logs the failure and stops trying to restart.
28
+ #
29
+ class Supervisor
30
+ class RestartBudgetExceeded < Igniter::Error; end
31
+
32
+ # ── ChildSpec ────────────────────────────────────────────────────────────
33
+
34
+ ChildSpec = Struct.new(:name, :agent_class, :init_opts, keyword_init: true)
35
+
36
+ class ChildSpecBuilder
37
+ attr_reader :specs
38
+
39
+ def initialize
40
+ @specs = []
41
+ end
42
+
43
+ def worker(name, agent_class, **opts)
44
+ @specs << ChildSpec.new(name: name.to_sym, agent_class: agent_class, init_opts: opts)
45
+ end
46
+ end
47
+
48
+ # ── Class-level defaults ─────────────────────────────────────────────────
49
+
50
+ @strategy = :one_for_one
51
+ @max_restarts = 5
52
+ @restart_window = 60
53
+ @spec_builder = ChildSpecBuilder.new
54
+
55
+ class << self
56
+ def inherited(subclass)
57
+ super
58
+ subclass.instance_variable_set(:@strategy, :one_for_one)
59
+ subclass.instance_variable_set(:@max_restarts, 5)
60
+ subclass.instance_variable_set(:@restart_window, 60)
61
+ subclass.instance_variable_set(:@spec_builder, ChildSpecBuilder.new)
62
+ end
63
+
64
+ def strategy(sym)
65
+ @strategy = sym
66
+ end
67
+
68
+ def max_restarts(count, within:)
69
+ @max_restarts = count
70
+ @restart_window = within
71
+ end
72
+
73
+ def children(&block)
74
+ block.call(@spec_builder)
75
+ end
76
+
77
+ def child_specs
78
+ @spec_builder.specs
79
+ end
80
+
81
+ def start
82
+ new.tap(&:start_all)
83
+ end
84
+ end
85
+
86
+ # ── Instance ─────────────────────────────────────────────────────────────
87
+
88
+ def initialize
89
+ @refs = {}
90
+ @specs_by_name = {}
91
+ @restart_log = []
92
+ @mutex = Mutex.new
93
+ end
94
+
95
+ def start_all
96
+ self.class.child_specs.each do |spec|
97
+ @specs_by_name[spec.name] = spec
98
+ start_child(spec)
99
+ end
100
+ end
101
+
102
+ # Return the Ref for a named child. Returns nil if not found.
103
+ def child(name)
104
+ @mutex.synchronize { @refs[name.to_sym] }
105
+ end
106
+
107
+ # Stop all children gracefully.
108
+ def stop
109
+ refs = @mutex.synchronize { @refs.values.dup }
110
+ refs.each do |ref|
111
+ ref.stop
112
+ rescue StandardError
113
+ nil
114
+ end
115
+ self
116
+ end
117
+
118
+ private
119
+
120
+ def start_child(spec)
121
+ opts = spec.init_opts.dup
122
+ opts[:on_crash] = ->(error) { handle_crash(spec, error) }
123
+ ref = spec.agent_class.start(**opts)
124
+ @mutex.synchronize { @refs[spec.name] = ref }
125
+ ref
126
+ end
127
+
128
+ def handle_crash(spec, _error)
129
+ check_restart_budget!
130
+
131
+ case self.class.instance_variable_get(:@strategy)
132
+ when :one_for_one
133
+ start_child(spec)
134
+ when :one_for_all
135
+ stop_all_children
136
+ self.class.child_specs.each { |s| start_child(s) }
137
+ end
138
+ rescue RestartBudgetExceeded => e
139
+ warn "Igniter::Supervisor #{self.class.name}: #{e.message}"
140
+ end
141
+
142
+ def check_restart_budget! # rubocop:disable Metrics/MethodLength
143
+ now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
144
+ window = self.class.instance_variable_get(:@restart_window).to_f
145
+ max = self.class.instance_variable_get(:@max_restarts)
146
+
147
+ @mutex.synchronize do
148
+ @restart_log.reject! { |t| now - t > window }
149
+ @restart_log << now
150
+
151
+ if @restart_log.size > max
152
+ raise RestartBudgetExceeded,
153
+ "#{@restart_log.size} crashes in #{window}s (max=#{max})"
154
+ end
155
+ end
156
+ end
157
+
158
+ def stop_all_children
159
+ refs = @mutex.synchronize { @refs.values.dup }
160
+ refs.each do |ref|
161
+ ref.stop(timeout: 2)
162
+ rescue StandardError
163
+ nil
164
+ end
165
+ end
166
+ end
167
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Igniter
4
- VERSION = "0.4.0"
4
+ VERSION = "0.4.5"
5
5
  end
data/lib/igniter.rb CHANGED
@@ -5,6 +5,8 @@ require_relative "igniter/errors"
5
5
  require_relative "igniter/type_system"
6
6
  require_relative "igniter/executor"
7
7
  require_relative "igniter/executor_registry"
8
+ require_relative "igniter/effect"
9
+ require_relative "igniter/effect_registry"
8
10
  require_relative "igniter/model"
9
11
  require_relative "igniter/compiler"
10
12
  require_relative "igniter/events"
@@ -32,6 +34,14 @@ module Igniter
32
34
  executor_registry.register(key, executor_class, **metadata)
33
35
  end
34
36
 
37
+ def effect_registry
38
+ @effect_registry ||= EffectRegistry.new
39
+ end
40
+
41
+ def register_effect(key, adapter_class, **metadata)
42
+ effect_registry.register(key, adapter_class, **metadata)
43
+ end
44
+
35
45
  def compile(&block)
36
46
  DSL::ContractBuilder.compile(&block)
37
47
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: igniter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander
@@ -57,19 +57,36 @@ files:
57
57
  - docs/DISTRIBUTED_CONTRACTS_V1.md
58
58
  - docs/EXECUTION_MODEL_V2.md
59
59
  - docs/IGNITER_CONCEPTS.md
60
+ - docs/LLM_V1.md
60
61
  - docs/PATTERNS.md
62
+ - docs/SERVER_V1.md
61
63
  - docs/STORE_ADAPTERS.md
62
64
  - examples/README.md
65
+ - examples/agents.rb
63
66
  - examples/async_store.rb
64
67
  - examples/basic_pricing.rb
65
68
  - examples/collection.rb
66
69
  - examples/collection_partial_failure.rb
67
70
  - examples/composition.rb
68
71
  - examples/diagnostics.rb
72
+ - examples/differential.rb
73
+ - examples/distributed_server.rb
69
74
  - examples/distributed_workflow.rb
75
+ - examples/effects.rb
76
+ - examples/incremental.rb
77
+ - examples/invariants.rb
70
78
  - examples/marketing_ergonomics.rb
79
+ - examples/order_pipeline.rb
80
+ - examples/provenance.rb
71
81
  - examples/ringcentral_routing.rb
82
+ - examples/saga.rb
72
83
  - lib/igniter.rb
84
+ - lib/igniter/agent.rb
85
+ - lib/igniter/agent/mailbox.rb
86
+ - lib/igniter/agent/message.rb
87
+ - lib/igniter/agent/ref.rb
88
+ - lib/igniter/agent/runner.rb
89
+ - lib/igniter/agent/state_holder.rb
73
90
  - lib/igniter/compiler.rb
74
91
  - lib/igniter/compiler/compiled_graph.rb
75
92
  - lib/igniter/compiler/graph_compiler.rb
@@ -91,26 +108,49 @@ files:
91
108
  - lib/igniter/diagnostics/introspection/formatters/mermaid_formatter.rb
92
109
  - lib/igniter/diagnostics/introspection/formatters/text_tree_formatter.rb
93
110
  - lib/igniter/diagnostics/report.rb
111
+ - lib/igniter/differential.rb
112
+ - lib/igniter/differential/divergence.rb
113
+ - lib/igniter/differential/formatter.rb
114
+ - lib/igniter/differential/report.rb
115
+ - lib/igniter/differential/runner.rb
94
116
  - lib/igniter/dsl.rb
95
117
  - lib/igniter/dsl/contract_builder.rb
96
118
  - lib/igniter/dsl/schema_builder.rb
119
+ - lib/igniter/effect.rb
120
+ - lib/igniter/effect_registry.rb
97
121
  - lib/igniter/errors.rb
98
122
  - lib/igniter/events.rb
99
123
  - lib/igniter/events/bus.rb
100
124
  - lib/igniter/events/event.rb
125
+ - lib/igniter/execution_report.rb
126
+ - lib/igniter/execution_report/builder.rb
127
+ - lib/igniter/execution_report/formatter.rb
128
+ - lib/igniter/execution_report/node_entry.rb
129
+ - lib/igniter/execution_report/report.rb
101
130
  - lib/igniter/executor.rb
102
131
  - lib/igniter/executor_registry.rb
103
132
  - lib/igniter/extensions.rb
104
133
  - lib/igniter/extensions/auditing.rb
105
134
  - lib/igniter/extensions/auditing/timeline.rb
135
+ - lib/igniter/extensions/differential.rb
136
+ - lib/igniter/extensions/execution_report.rb
137
+ - lib/igniter/extensions/incremental.rb
106
138
  - lib/igniter/extensions/introspection.rb
107
139
  - lib/igniter/extensions/introspection/graph_formatter.rb
108
140
  - lib/igniter/extensions/introspection/plan_formatter.rb
109
141
  - lib/igniter/extensions/introspection/runtime_formatter.rb
142
+ - lib/igniter/extensions/invariants.rb
143
+ - lib/igniter/extensions/provenance.rb
110
144
  - lib/igniter/extensions/reactive.rb
111
145
  - lib/igniter/extensions/reactive/engine.rb
112
146
  - lib/igniter/extensions/reactive/matcher.rb
113
147
  - lib/igniter/extensions/reactive/reaction.rb
148
+ - lib/igniter/extensions/saga.rb
149
+ - lib/igniter/incremental.rb
150
+ - lib/igniter/incremental/formatter.rb
151
+ - lib/igniter/incremental/result.rb
152
+ - lib/igniter/incremental/tracker.rb
153
+ - lib/igniter/integrations/agents.rb
114
154
  - lib/igniter/integrations/llm.rb
115
155
  - lib/igniter/integrations/llm/config.rb
116
156
  - lib/igniter/integrations/llm/context.rb
@@ -126,17 +166,31 @@ files:
126
166
  - lib/igniter/integrations/rails/generators/install/install_generator.rb
127
167
  - lib/igniter/integrations/rails/railtie.rb
128
168
  - lib/igniter/integrations/rails/webhook_concern.rb
169
+ - lib/igniter/invariant.rb
129
170
  - lib/igniter/model.rb
130
171
  - lib/igniter/model/await_node.rb
131
172
  - lib/igniter/model/branch_node.rb
132
173
  - lib/igniter/model/collection_node.rb
133
174
  - lib/igniter/model/composition_node.rb
134
175
  - lib/igniter/model/compute_node.rb
176
+ - lib/igniter/model/effect_node.rb
135
177
  - lib/igniter/model/graph.rb
136
178
  - lib/igniter/model/input_node.rb
137
179
  - lib/igniter/model/node.rb
138
180
  - lib/igniter/model/output_node.rb
139
181
  - lib/igniter/model/remote_node.rb
182
+ - lib/igniter/property_testing.rb
183
+ - lib/igniter/property_testing/formatter.rb
184
+ - lib/igniter/property_testing/generators.rb
185
+ - lib/igniter/property_testing/result.rb
186
+ - lib/igniter/property_testing/run.rb
187
+ - lib/igniter/property_testing/runner.rb
188
+ - lib/igniter/provenance.rb
189
+ - lib/igniter/provenance/builder.rb
190
+ - lib/igniter/provenance/lineage.rb
191
+ - lib/igniter/provenance/node_trace.rb
192
+ - lib/igniter/provenance/text_formatter.rb
193
+ - lib/igniter/registry.rb
140
194
  - lib/igniter/runtime.rb
141
195
  - lib/igniter/runtime/cache.rb
142
196
  - lib/igniter/runtime/collection_result.rb
@@ -157,6 +211,12 @@ files:
157
211
  - lib/igniter/runtime/stores/file_store.rb
158
212
  - lib/igniter/runtime/stores/memory_store.rb
159
213
  - lib/igniter/runtime/stores/redis_store.rb
214
+ - lib/igniter/saga.rb
215
+ - lib/igniter/saga/compensation.rb
216
+ - lib/igniter/saga/compensation_record.rb
217
+ - lib/igniter/saga/executor.rb
218
+ - lib/igniter/saga/formatter.rb
219
+ - lib/igniter/saga/result.rb
160
220
  - lib/igniter/server.rb
161
221
  - lib/igniter/server/client.rb
162
222
  - lib/igniter/server/config.rb
@@ -170,6 +230,8 @@ files:
170
230
  - lib/igniter/server/rack_app.rb
171
231
  - lib/igniter/server/registry.rb
172
232
  - lib/igniter/server/router.rb
233
+ - lib/igniter/stream_loop.rb
234
+ - lib/igniter/supervisor.rb
173
235
  - lib/igniter/type_system.rb
174
236
  - lib/igniter/version.rb
175
237
  - sig/igniter.rbs