smith-agents 0.5.0 → 0.6.1

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +72 -0
  3. data/README.md +83 -0
  4. data/lib/smith/agent.rb +13 -0
  5. data/lib/smith/errors.rb +4 -0
  6. data/lib/smith/version.rb +2 -2
  7. data/lib/smith/workflow/composite/branch.rb +93 -0
  8. data/lib/smith/workflow/composite/branch_budget_contract.rb +41 -0
  9. data/lib/smith/workflow/composite/branch_contract.rb +102 -0
  10. data/lib/smith/workflow/composite/branch_execution.rb +124 -0
  11. data/lib/smith/workflow/composite/branch_failure.rb +91 -0
  12. data/lib/smith/workflow/composite/branch_outcome.rb +108 -0
  13. data/lib/smith/workflow/composite/budget_allocator.rb +78 -0
  14. data/lib/smith/workflow/composite/budget_math.rb +51 -0
  15. data/lib/smith/workflow/composite/contract.rb +100 -0
  16. data/lib/smith/workflow/composite/effects.rb +140 -0
  17. data/lib/smith/workflow/composite/effects_application.rb +36 -0
  18. data/lib/smith/workflow/composite/effects_baseline.rb +36 -0
  19. data/lib/smith/workflow/composite/effects_preflight.rb +102 -0
  20. data/lib/smith/workflow/composite/encoded_value_budget.rb +36 -0
  21. data/lib/smith/workflow/composite/enums.rb +27 -0
  22. data/lib/smith/workflow/composite/error.rb +54 -0
  23. data/lib/smith/workflow/composite/error_evidence.rb +61 -0
  24. data/lib/smith/workflow/composite/execution_contract.rb +73 -0
  25. data/lib/smith/workflow/composite/fanout_branch_contract.rb +61 -0
  26. data/lib/smith/workflow/composite/input.rb +53 -0
  27. data/lib/smith/workflow/composite/outcome_accumulator.rb +131 -0
  28. data/lib/smith/workflow/composite/outcome_set.rb +23 -0
  29. data/lib/smith/workflow/composite/payload.rb +128 -0
  30. data/lib/smith/workflow/composite/payload_digest.rb +28 -0
  31. data/lib/smith/workflow/composite/plan.rb +130 -0
  32. data/lib/smith/workflow/composite/plan_integrity.rb +52 -0
  33. data/lib/smith/workflow/composite/planner.rb +53 -0
  34. data/lib/smith/workflow/composite/preparation.rb +27 -0
  35. data/lib/smith/workflow/composite/reducer.rb +133 -0
  36. data/lib/smith/workflow/composite/reduction.rb +31 -0
  37. data/lib/smith/workflow/composite/value_budget.rb +90 -0
  38. data/lib/smith/workflow/composite_branch_execution_authorization.rb +52 -0
  39. data/lib/smith/workflow/deadline_enforcement.rb +22 -5
  40. data/lib/smith/workflow/execution.rb +16 -16
  41. data/lib/smith/workflow/fanout_execution.rb +19 -18
  42. data/lib/smith/workflow/message_value_normalizer.rb +11 -10
  43. data/lib/smith/workflow/parallel_execution.rb +41 -13
  44. data/lib/smith/workflow/prepared_branch_execution.rb +31 -0
  45. data/lib/smith/workflow/prepared_step_execution_authorization.rb +14 -29
  46. data/lib/smith/workflow/prepared_step_execution_scope.rb +81 -15
  47. data/lib/smith/workflow/process_local.rb +33 -0
  48. data/lib/smith/workflow/split_step_persistence/composite_branch_authorization.rb +99 -0
  49. data/lib/smith/workflow/split_step_persistence/composite_branch_effects.rb +41 -0
  50. data/lib/smith/workflow/split_step_persistence/composite_branch_execution.rb +95 -0
  51. data/lib/smith/workflow/split_step_persistence/composite_branch_outcome.rb +36 -0
  52. data/lib/smith/workflow/split_step_persistence/composite_execution.rb +39 -0
  53. data/lib/smith/workflow/split_step_persistence/composite_preparation.rb +57 -0
  54. data/lib/smith/workflow/split_step_persistence/composite_reduction_execution.rb +122 -0
  55. data/lib/smith/workflow/split_step_persistence/execution.rb +7 -53
  56. data/lib/smith/workflow/split_step_persistence/execution_authorization.rb +41 -21
  57. data/lib/smith/workflow/split_step_persistence/execution_authorization_issuance.rb +57 -0
  58. data/lib/smith/workflow/split_step_persistence/execution_binding_collector.rb +5 -0
  59. data/lib/smith/workflow/split_step_persistence/execution_binding_snapshot.rb +17 -3
  60. data/lib/smith/workflow/split_step_persistence/execution_lifecycle.rb +96 -0
  61. data/lib/smith/workflow/split_step_persistence/execution_verification.rb +17 -5
  62. data/lib/smith/workflow/split_step_persistence/preparation_claim.rb +8 -0
  63. data/lib/smith/workflow/split_step_persistence/subclass_boundary.rb +21 -5
  64. data/lib/smith/workflow/split_step_persistence.rb +14 -0
  65. data/lib/smith/workflow/step_context.rb +46 -0
  66. data/lib/smith/workflow/thread_context_snapshot.rb +103 -0
  67. data/lib/smith/workflow/transition.rb +16 -1
  68. data/lib/smith/workflow.rb +21 -1
  69. data/lib/smith.rb +3 -0
  70. metadata +46 -1
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smith
4
+ class Workflow
5
+ module SplitStepPersistence
6
+ module ExecutionAuthorizationIssuance
7
+ def authorize_prepared_step_execution!
8
+ authorization = nil
9
+ handed_off = false
10
+ authorization = ExecutionAuthorizationIssuance
11
+ .instance_method(:interrupt_safe_prepared_step_authorization)
12
+ .bind_call(self)
13
+ handed_off = true
14
+ authorization
15
+ ensure
16
+ unless handed_off
17
+ ExecutionLifecycle
18
+ .instance_method(:release_failed_execution_authorization!)
19
+ .bind_call(self, authorization)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def with_prepared_step_execution_authorization
26
+ Thread.handle_interrupt(Object => :never) do
27
+ authorization = interrupt_safe_prepared_step_authorization
28
+ begin
29
+ Thread.handle_interrupt(Object => :immediate) { yield(authorization) }
30
+ ensure
31
+ ExecutionLifecycle
32
+ .instance_method(:release_failed_execution_authorization!)
33
+ .bind_call(self, authorization)
34
+ end
35
+ end
36
+ end
37
+
38
+ def interrupt_safe_prepared_step_authorization
39
+ Thread.handle_interrupt(Object => :never) do
40
+ verification_token = ExecutionAuthorization
41
+ .instance_method(:claim_framework_execution_verification!)
42
+ .bind_call(self)
43
+ begin
44
+ ExecutionAuthorization
45
+ .instance_method(:authorize_claimed_prepared_step_execution!)
46
+ .bind_call(self, verification_token)
47
+ ensure
48
+ ExecutionAuthorization
49
+ .instance_method(:restore_framework_execution_verification!)
50
+ .bind_call(self, verification_token)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -29,6 +29,11 @@ module Smith
29
29
  end
30
30
  end
31
31
 
32
+ def capture_agent(name, workflow_class:, transition:, role:)
33
+ capture_binding(name, workflow_class:, transition:, role:)
34
+ self
35
+ end
36
+
32
37
  def resolve!
33
38
  @bindings = Agent::Registry.capture_bindings!(@requests.values)
34
39
 
@@ -14,9 +14,14 @@ module Smith
14
14
  new(transition, workflow_class:).capture
15
15
  end
16
16
 
17
- def initialize(transition, workflow_class:)
17
+ def self.capture_agent(transition, workflow_class:, name:, role:)
18
+ new(transition, workflow_class:, selected_agent: { name:, role: }).capture
19
+ end
20
+
21
+ def initialize(transition, workflow_class:, selected_agent: nil)
18
22
  @transition = transition
19
23
  @workflow_class = workflow_class
24
+ @selected_agent = selected_agent
20
25
  @bindings = ExecutionBindingCollector.new
21
26
  @visited_workflows = {}.compare_by_identity
22
27
  @visited_workflows[workflow_class] = true
@@ -27,8 +32,17 @@ module Smith
27
32
  end
28
33
 
29
34
  def capture
30
- capture_transition(@transition, workflow_class: @workflow_class)
31
- drain_workflow_queue
35
+ if @selected_agent
36
+ @bindings.capture_agent(
37
+ @selected_agent.fetch(:name),
38
+ workflow_class: @workflow_class,
39
+ transition: @transition,
40
+ role: @selected_agent.fetch(:role)
41
+ )
42
+ else
43
+ capture_transition(@transition, workflow_class: @workflow_class)
44
+ drain_workflow_queue
45
+ end
32
46
  @bindings.resolve!
33
47
  @bindings.freeze
34
48
  @workflow_snapshots.freeze
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Smith
4
+ class Workflow
5
+ module SplitStepPersistence
6
+ module ExecutionLifecycle
7
+ private
8
+
9
+ def perform_authorized_prepared_step_execution!(authorization)
10
+ authorization = validate_split_step_execution_authorization!(authorization)
11
+ execution_thread = Thread.current
12
+ execute_prepared_step_lifecycle!(authorization, execution_thread) { yield(execution_thread) }
13
+ end
14
+
15
+ def execute_prepared_step_lifecycle!(authorization, execution_thread)
16
+ execution_active = false
17
+ step = nil
18
+ result = nil
19
+ Thread.handle_interrupt(Object => :never) do
20
+ activate_split_step_execution!(authorization, execution_thread)
21
+ execution_active = true
22
+ Thread.handle_interrupt(Object => :immediate) do
23
+ step, result = yield(execution_thread)
24
+ end
25
+ ensure
26
+ finish_split_step_execution!(step, authorization, execution_thread) if execution_active
27
+ end
28
+ result
29
+ end
30
+
31
+ def activate_split_step_execution!(authorization, execution_thread)
32
+ Thread.handle_interrupt(Object => :never) do
33
+ @split_step_mutex.synchronize do
34
+ ensure_active_split_step_execution_authorization!(authorization)
35
+ ensure_split_step_definition_current!
36
+ ensure_prepared_split_step_transition_matches!
37
+ activate_prepared_step_execution_scope!(authorization)
38
+ publish_active_split_step_execution!(authorization, execution_thread)
39
+ end
40
+ end
41
+ end
42
+
43
+ def finish_split_step_execution!(step, authorization, execution_thread)
44
+ Thread.handle_interrupt(Object => :never) do
45
+ @split_step_mutex.synchronize do
46
+ reset_active_split_step_execution!(step) if active_split_step_execution?(execution_thread)
47
+ end
48
+ ensure
49
+ PreparedStepExecutionAuthorization.instance_method(:close_execution!).bind_call(authorization)
50
+ end
51
+ end
52
+
53
+ def ensure_active_split_step_execution_authorization!(authorization)
54
+ return if active_split_step_execution_authorization?(authorization)
55
+
56
+ raise WorkflowError, "the prepared-step execution authorization is no longer active"
57
+ end
58
+
59
+ def activate_prepared_step_execution_scope!(authorization)
60
+ PreparedStepExecutionAuthorization.instance_method(:activate_execution!).bind_call(authorization)
61
+ end
62
+
63
+ def publish_active_split_step_execution!(authorization, execution_thread)
64
+ @split_step_active_execution_authorization = authorization
65
+ @split_step_execution_result = nil
66
+ @split_step_phase = :executing
67
+ clear_split_step_execution_authorization!
68
+ @split_step_execution_thread = execution_thread
69
+ @split_step_advance_permit = true
70
+ end
71
+
72
+ def active_split_step_execution?(execution_thread)
73
+ @split_step_phase == :executing && @split_step_execution_thread.equal?(execution_thread)
74
+ end
75
+
76
+ def reset_active_split_step_execution!(step)
77
+ @split_step_execution_thread = nil
78
+ @split_step_advance_permit = false
79
+ @split_step_execution_previous_phase = nil
80
+ @split_step_active_execution_authorization = nil
81
+ @split_step_execution_result = nil
82
+ @split_step_phase = step ? :executed : :attempted
83
+ end
84
+
85
+ def release_failed_execution_authorization!(authorization)
86
+ return unless authorization
87
+ return unless active_split_step_execution_authorization?(authorization)
88
+
89
+ ExecutionAuthorization
90
+ .instance_method(:release_prepared_step_execution!)
91
+ .bind_call(self, authorization)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -8,11 +8,18 @@ module Smith
8
8
 
9
9
  def claim_split_step_execution_verification!
10
10
  @split_step_mutex.synchronize do
11
- ensure_split_step_definition_current!
12
- expected = restart_safe_split_step? ? :dispatch_claimed : :prepared
11
+ DefinitionBoundary
12
+ .instance_method(:ensure_split_step_definition_current!)
13
+ .bind_call(self)
14
+ restart_safe = DefinitionBoundary
15
+ .instance_method(:restart_safe_split_step?)
16
+ .bind_call(self)
17
+ expected = restart_safe ? :dispatch_claimed : :prepared
13
18
  raise WorkflowError, "no persisted step is prepared for execution" unless @split_step_phase == expected
14
19
 
15
- ensure_prepared_split_step_transition_matches!
20
+ ExecutionVerification
21
+ .instance_method(:ensure_prepared_split_step_transition_matches!)
22
+ .bind_call(self)
16
23
 
17
24
  token = Object.new.freeze
18
25
  @split_step_execution_previous_phase = expected
@@ -23,8 +30,13 @@ module Smith
23
30
  end
24
31
 
25
32
  def verify_claimed_split_step_execution!(verification_token)
26
- if restart_safe_split_step?
27
- verify_split_step_dispatch_available!(verification_token)
33
+ restart_safe = DefinitionBoundary
34
+ .instance_method(:restart_safe_split_step?)
35
+ .bind_call(self)
36
+ if restart_safe
37
+ DispatchVerification
38
+ .instance_method(:verify_split_step_dispatch_available!)
39
+ .bind_call(self, verification_token)
28
40
  else
29
41
  verify_split_step_preparation_available!
30
42
  end
@@ -33,6 +33,8 @@ module Smith
33
33
  def finalize_split_step_preparation!(transition, transition_name, key, adapter, persistence_ttl)
34
34
  transaction_identity = TransactionIdentity.capture(adapter)
35
35
  transition_signature = TransitionContract.capture(transition)
36
+ namespace_preparer = PreparationClaim.instance_method(:prepare_composite_execution_namespace!)
37
+ namespace_preparer.bind_call(self, transition)
36
38
  @split_step_mutex.synchronize do
37
39
  unless active_split_step_preparation_claim?
38
40
  raise WorkflowError, "the split-step preparation claim is no longer active"
@@ -65,6 +67,12 @@ module Smith
65
67
  @split_step_preparation_thread.equal?(Thread.current)
66
68
  end
67
69
 
70
+ def prepare_composite_execution_namespace!(transition)
71
+ return unless transition&.parallel? || transition&.fanout?
72
+
73
+ ArtifactIntegration.instance_method(:execution_namespace).bind_call(self)
74
+ end
75
+
68
76
  def mark_split_step_prepared!(_adapter)
69
77
  @split_step_mutex.synchronize do
70
78
  phase = @split_step_transaction_identity ? :prepared_uncommitted : :prepared
@@ -68,15 +68,31 @@ module Smith
68
68
  super
69
69
  end
70
70
 
71
+ def authorize_prepared_step_execution!(...)
72
+ ExecutionAuthorizationIssuance.instance_method(:authorize_prepared_step_execution!).bind_call(self, ...)
73
+ end
74
+
75
+ def prepare_composite_step!(...)
76
+ CompositeExecution.instance_method(:prepare_composite_step!).bind_call(self, ...)
77
+ end
78
+
79
+ def execute_prepared_composite_branch!(...)
80
+ CompositeExecution.instance_method(:execute_prepared_composite_branch!).bind_call(self, ...)
81
+ end
82
+
83
+ def reduce_prepared_composite_step!(...)
84
+ CompositeExecution.instance_method(:reduce_prepared_composite_step!).bind_call(self, ...)
85
+ end
86
+
71
87
  private
72
88
 
73
89
  def smith_prepared_execution_active?
74
- root_execution = @split_step_phase == :executing &&
75
- @split_step_execution_thread.equal?(Thread.current)
76
- return true if root_execution
77
-
78
90
  authorization = @split_step_active_execution_authorization
79
- authorization&.active_in_current_execution? == true
91
+ return true if authorization&.active_in_current_execution? == true
92
+
93
+ authorization = @split_step_execution_authorization
94
+ @split_step_phase == :execution_authorized &&
95
+ authorization&.issued_in_current_process? == true
80
96
  end
81
97
 
82
98
  def effective_persistence_ttl
@@ -36,7 +36,14 @@ require_relative "split_step_persistence/dispatch_confirmation"
36
36
  require_relative "split_step_persistence/execution_verification"
37
37
  require_relative "split_step_persistence/execution_binding_snapshot"
38
38
  require_relative "split_step_persistence/execution_authorization"
39
+ require_relative "split_step_persistence/execution_authorization_issuance"
39
40
  require_relative "split_step_persistence/execution_result_capture"
41
+ require_relative "split_step_persistence/execution_lifecycle"
42
+ require_relative "split_step_persistence/composite_preparation"
43
+ require_relative "split_step_persistence/composite_branch_authorization"
44
+ require_relative "split_step_persistence/composite_branch_execution"
45
+ require_relative "split_step_persistence/composite_reduction_execution"
46
+ require_relative "split_step_persistence/composite_execution"
40
47
  require_relative "split_step_persistence/execution"
41
48
  require_relative "split_step_persistence/checkpoint_state"
42
49
  require_relative "split_step_persistence/checkpoint"
@@ -69,7 +76,14 @@ module Smith
69
76
  include DispatchConfirmation
70
77
  include ExecutionVerification
71
78
  include ExecutionAuthorization
79
+ include ExecutionAuthorizationIssuance
72
80
  include ExecutionResultCapture
81
+ include ExecutionLifecycle
82
+ include CompositePreparation
83
+ include CompositeBranchAuthorization
84
+ include CompositeBranchExecution
85
+ include CompositeReductionExecution
86
+ include CompositeExecution
73
87
  include Execution
74
88
  include CheckpointState
75
89
  include Checkpoint
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "thread_context_snapshot"
4
+
5
+ module Smith
6
+ class Workflow
7
+ module StepContext
8
+ private
9
+
10
+ def with_step_context(transition, &block)
11
+ ThreadContextSnapshot.new.around do
12
+ setup_step_context
13
+ Thread.handle_interrupt(Object => :immediate, &block)
14
+ rescue StandardError => e
15
+ @outcome = nil
16
+ GuardrailIntegration.instance_method(:handle_step_failure).bind_call(self, transition, e)
17
+ ensure
18
+ teardown_step_context
19
+ end
20
+ end
21
+
22
+ def within_raw_step_context(&block)
23
+ ThreadContextSnapshot.new.around do
24
+ setup_step_context
25
+ Thread.handle_interrupt(Object => :immediate, &block)
26
+ ensure
27
+ teardown_step_context
28
+ end
29
+ end
30
+
31
+ def setup_step_context
32
+ Tool.current_deadline = wall_clock_deadline
33
+ Tool.current_ledger = @ledger
34
+ Tool.current_tool_result_collector = tool_result_collector
35
+ end
36
+
37
+ def teardown_step_context
38
+ Tool.current_guardrails = nil
39
+ Tool.current_deadline = nil
40
+ Tool.current_ledger = nil
41
+ Tool.current_tool_result_collector = nil
42
+ Smith.scoped_artifacts = nil
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry-initializer"
4
+
5
+ require_relative "process_local"
6
+
7
+ module Smith
8
+ class Workflow
9
+ class ThreadContextSnapshot
10
+ extend Dry::Initializer
11
+ include ProcessLocal
12
+
13
+ TOOL_ATTRIBUTES = %i[
14
+ current_guardrails
15
+ current_deadline
16
+ current_ledger
17
+ current_tool_call_allowance
18
+ current_tool_result_collector
19
+ ].freeze
20
+ THREAD_KEYS = %i[
21
+ smith_call_deadline
22
+ smith_call_ledger
23
+ smith_failed_agent_results
24
+ smith_last_agent_result
25
+ smith_parallel_agent_binding
26
+ ].freeze
27
+
28
+ option :tool_attributes, default: proc { TOOL_ATTRIBUTES }
29
+ option :thread_keys, default: proc { THREAD_KEYS }
30
+ option :scoped_artifacts, default: proc { true }
31
+
32
+ def initialize(...)
33
+ super
34
+ @tool_attributes = tool_attributes.dup.freeze
35
+ @thread_keys = thread_keys.dup.freeze
36
+ validate_selection!
37
+ @tool_values = capture_tool_values
38
+ @thread_values = capture_thread_values
39
+ @artifact_store = Smith.scoped_artifacts if scoped_artifacts
40
+ @process_id = Process.pid
41
+ @thread = Thread.current
42
+ @fiber = Fiber.current
43
+ @restored = false
44
+ end
45
+
46
+ def restore!
47
+ ensure_owner!
48
+ Thread.handle_interrupt(Object => :never) { restore_context! }
49
+ self
50
+ end
51
+
52
+ def around
53
+ ensure_owner!
54
+ Thread.handle_interrupt(Object => :never) do
55
+ raise WorkflowError, "thread context snapshot has already been restored" if @restored
56
+
57
+ begin
58
+ yield
59
+ ensure
60
+ restore_context!
61
+ end
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def restore_context!
68
+ raise WorkflowError, "thread context snapshot has already been restored" if @restored
69
+
70
+ @tool_values.each { |attribute, value| Tool.public_send("#{attribute}=", value) }
71
+ @thread_values.each { |key, value| Thread.current[key] = value }
72
+ Smith.scoped_artifacts = @artifact_store if scoped_artifacts
73
+ @restored = true
74
+ end
75
+
76
+ def capture_tool_values
77
+ tool_attributes.to_h { |attribute| [attribute, Tool.public_send(attribute)] }.freeze
78
+ end
79
+
80
+ def capture_thread_values
81
+ thread_keys.to_h { |key| [key, Thread.current[key]] }.freeze
82
+ end
83
+
84
+ def ensure_owner!
85
+ owner = @process_id == Process.pid && @thread.equal?(Thread.current) && @fiber.equal?(Fiber.current)
86
+ return if owner
87
+
88
+ raise WorkflowError, "thread context snapshot belongs to another process, thread, or fiber"
89
+ end
90
+
91
+ def validate_selection!
92
+ unless (tool_attributes - TOOL_ATTRIBUTES).empty?
93
+ raise ArgumentError, "thread context snapshot contains unsupported tool attributes"
94
+ end
95
+ return if (thread_keys - THREAD_KEYS).empty?
96
+
97
+ raise ArgumentError, "thread context snapshot contains unsupported thread keys"
98
+ end
99
+ end
100
+
101
+ private_constant :ThreadContextSnapshot
102
+ end
103
+ end
@@ -111,7 +111,11 @@ module Smith
111
111
  def fan_out(branches:)
112
112
  validate_execution_primitive_conflict!("fan_out")
113
113
 
114
- config = { branches: normalize_fanout_branches!(branches) }.freeze
114
+ normalized = normalize_fanout_branches!(branches)
115
+ @fanout_branch_lookup = normalized.to_h do |key, agent|
116
+ [key.to_s.freeze, [key, agent].freeze]
117
+ end.freeze
118
+ config = { branches: normalized }.freeze
115
119
 
116
120
  @fanout_config = config
117
121
  commit_execution_primitive!("fan_out")
@@ -156,6 +160,17 @@ module Smith
156
160
  !@fanout_config.nil?
157
161
  end
158
162
 
163
+ def fetch_fanout_agent!(branch_key)
164
+ fetch_fanout_branch!(branch_key).last
165
+ end
166
+
167
+ def fetch_fanout_branch!(branch_key)
168
+ @fanout_branch_lookup.fetch(branch_key.to_s) do
169
+ raise WorkflowError, "fan_out branch #{branch_key.inspect} is not declared"
170
+ end
171
+ end
172
+ private :fetch_fanout_agent!, :fetch_fanout_branch!
173
+
159
174
  def optimized?
160
175
  !@optimization_config.nil?
161
176
  end
@@ -52,6 +52,9 @@ module Smith
52
52
  "Smith::AgentError" => ->(s) { Smith::AgentError.new(s[:error_message]) },
53
53
  "Smith::DeadlineExceeded" => ->(s) { Smith::DeadlineExceeded.new(s[:error_message]) },
54
54
  "Smith::WorkflowError" => ->(s) { Smith::WorkflowError.new(s[:error_message]) },
55
+ "Smith::Workflow::Composite::BranchFailure" => lambda { |s|
56
+ Smith::Workflow::Composite::BranchFailure.from_details(s[:error_details])
57
+ },
55
58
  # Smith errors with non-message constructors map to compatible
56
59
  # superclass — message preserved, original metadata (agent_name,
57
60
  # model_used, requested_name, workflow_class, origin_state) lossy
@@ -493,5 +496,22 @@ Smith::Workflow::SplitStepPersistence::SubclassBoundary.protect_execution_path!(
493
496
  Smith::Workflow::EventIntegration,
494
497
  Smith::Workflow::ArtifactIntegration,
495
498
  Smith::Workflow::DataVolumePolicy,
496
- Smith::Workflow::DeadlineEnforcement
499
+ Smith::Workflow::DeadlineEnforcement,
500
+ Smith::Workflow::SplitStepPersistence::DefinitionBoundary,
501
+ Smith::Workflow::SplitStepPersistence::ExecutionVerification,
502
+ Smith::Workflow::SplitStepPersistence::ExecutionAuthorization,
503
+ Smith::Workflow::SplitStepPersistence::ExecutionAuthorizationIssuance,
504
+ Smith::Workflow::SplitStepPersistence::ExecutionLifecycle,
505
+ Smith::Workflow::Composite::Contract,
506
+ Smith::Workflow::Composite::BranchContract,
507
+ Smith::Workflow::Composite::FanoutBranchContract,
508
+ Smith::Workflow::Composite::BranchBudgetContract,
509
+ Smith::Workflow::Composite::ExecutionContract,
510
+ Smith::Workflow::SplitStepPersistence::CompositeBranchEffects,
511
+ Smith::Workflow::SplitStepPersistence::CompositeBranchOutcome,
512
+ Smith::Workflow::SplitStepPersistence::CompositePreparation,
513
+ Smith::Workflow::SplitStepPersistence::CompositeBranchAuthorization,
514
+ Smith::Workflow::SplitStepPersistence::CompositeBranchExecution,
515
+ Smith::Workflow::SplitStepPersistence::CompositeReductionExecution,
516
+ Smith::Workflow::SplitStepPersistence::CompositeExecution
497
517
  )
data/lib/smith.rb CHANGED
@@ -267,6 +267,8 @@ require_relative "smith/workflow/graph_dsl"
267
267
  require_relative "smith/workflow/run_result"
268
268
  require_relative "smith/workflow/agent_result"
269
269
  require_relative "smith/workflow/usage_entry"
270
+ require_relative "smith/workflow/process_local"
271
+ require_relative "smith/workflow/thread_context_snapshot"
270
272
  require_relative "smith/workflow/branch_env"
271
273
  require_relative "smith/workflow/optimization_state"
272
274
  require_relative "smith/workflow/orchestration_state"
@@ -274,6 +276,7 @@ require_relative "smith/workflow/worker_execution"
274
276
  require_relative "smith/workflow/dsl"
275
277
  require_relative "smith/workflow/definition_identity"
276
278
  require_relative "smith/workflow/prepared_step_dispatch"
279
+ require_relative "smith/workflow/prepared_branch_execution"
277
280
  require_relative "smith/workflow/message_value_normalizer"
278
281
  require_relative "smith/workflow/message_admission"
279
282
  require_relative "smith/workflow/message_batch"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smith-agents
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Ralak
@@ -257,6 +257,38 @@ files:
257
257
  - lib/smith/workflow/branch_env.rb
258
258
  - lib/smith/workflow/budget_integration.rb
259
259
  - lib/smith/workflow/claim.rb
260
+ - lib/smith/workflow/composite/branch.rb
261
+ - lib/smith/workflow/composite/branch_budget_contract.rb
262
+ - lib/smith/workflow/composite/branch_contract.rb
263
+ - lib/smith/workflow/composite/branch_execution.rb
264
+ - lib/smith/workflow/composite/branch_failure.rb
265
+ - lib/smith/workflow/composite/branch_outcome.rb
266
+ - lib/smith/workflow/composite/budget_allocator.rb
267
+ - lib/smith/workflow/composite/budget_math.rb
268
+ - lib/smith/workflow/composite/contract.rb
269
+ - lib/smith/workflow/composite/effects.rb
270
+ - lib/smith/workflow/composite/effects_application.rb
271
+ - lib/smith/workflow/composite/effects_baseline.rb
272
+ - lib/smith/workflow/composite/effects_preflight.rb
273
+ - lib/smith/workflow/composite/encoded_value_budget.rb
274
+ - lib/smith/workflow/composite/enums.rb
275
+ - lib/smith/workflow/composite/error.rb
276
+ - lib/smith/workflow/composite/error_evidence.rb
277
+ - lib/smith/workflow/composite/execution_contract.rb
278
+ - lib/smith/workflow/composite/fanout_branch_contract.rb
279
+ - lib/smith/workflow/composite/input.rb
280
+ - lib/smith/workflow/composite/outcome_accumulator.rb
281
+ - lib/smith/workflow/composite/outcome_set.rb
282
+ - lib/smith/workflow/composite/payload.rb
283
+ - lib/smith/workflow/composite/payload_digest.rb
284
+ - lib/smith/workflow/composite/plan.rb
285
+ - lib/smith/workflow/composite/plan_integrity.rb
286
+ - lib/smith/workflow/composite/planner.rb
287
+ - lib/smith/workflow/composite/preparation.rb
288
+ - lib/smith/workflow/composite/reducer.rb
289
+ - lib/smith/workflow/composite/reduction.rb
290
+ - lib/smith/workflow/composite/value_budget.rb
291
+ - lib/smith/workflow/composite_branch_execution_authorization.rb
260
292
  - lib/smith/workflow/data_volume_policy.rb
261
293
  - lib/smith/workflow/deadline_enforcement.rb
262
294
  - lib/smith/workflow/definition_identity.rb
@@ -325,12 +357,14 @@ files:
325
357
  - lib/smith/workflow/parallel_execution.rb
326
358
  - lib/smith/workflow/persistence.rb
327
359
  - lib/smith/workflow/pipeline.rb
360
+ - lib/smith/workflow/prepared_branch_execution.rb
328
361
  - lib/smith/workflow/prepared_step.rb
329
362
  - lib/smith/workflow/prepared_step_dispatch.rb
330
363
  - lib/smith/workflow/prepared_step_execution_authorization.rb
331
364
  - lib/smith/workflow/prepared_step_execution_result.rb
332
365
  - lib/smith/workflow/prepared_step_execution_scope.rb
333
366
  - lib/smith/workflow/prepared_step_recovery.rb
367
+ - lib/smith/workflow/process_local.rb
334
368
  - lib/smith/workflow/retry_execution.rb
335
369
  - lib/smith/workflow/router.rb
336
370
  - lib/smith/workflow/run_result.rb
@@ -340,6 +374,13 @@ files:
340
374
  - lib/smith/workflow/split_step_persistence/canonical_payload_digest.rb
341
375
  - lib/smith/workflow/split_step_persistence/checkpoint.rb
342
376
  - lib/smith/workflow/split_step_persistence/checkpoint_state.rb
377
+ - lib/smith/workflow/split_step_persistence/composite_branch_authorization.rb
378
+ - lib/smith/workflow/split_step_persistence/composite_branch_effects.rb
379
+ - lib/smith/workflow/split_step_persistence/composite_branch_execution.rb
380
+ - lib/smith/workflow/split_step_persistence/composite_branch_outcome.rb
381
+ - lib/smith/workflow/split_step_persistence/composite_execution.rb
382
+ - lib/smith/workflow/split_step_persistence/composite_preparation.rb
383
+ - lib/smith/workflow/split_step_persistence/composite_reduction_execution.rb
343
384
  - lib/smith/workflow/split_step_persistence/definition_boundary.rb
344
385
  - lib/smith/workflow/split_step_persistence/dispatch_boundary.rb
345
386
  - lib/smith/workflow/split_step_persistence/dispatch_claim.rb
@@ -347,8 +388,10 @@ files:
347
388
  - lib/smith/workflow/split_step_persistence/dispatch_verification.rb
348
389
  - lib/smith/workflow/split_step_persistence/execution.rb
349
390
  - lib/smith/workflow/split_step_persistence/execution_authorization.rb
391
+ - lib/smith/workflow/split_step_persistence/execution_authorization_issuance.rb
350
392
  - lib/smith/workflow/split_step_persistence/execution_binding_collector.rb
351
393
  - lib/smith/workflow/split_step_persistence/execution_binding_snapshot.rb
394
+ - lib/smith/workflow/split_step_persistence/execution_lifecycle.rb
352
395
  - lib/smith/workflow/split_step_persistence/execution_result_capture.rb
353
396
  - lib/smith/workflow/split_step_persistence/execution_verification.rb
354
397
  - lib/smith/workflow/split_step_persistence/execution_workflow_snapshot.rb
@@ -371,7 +414,9 @@ files:
371
414
  - lib/smith/workflow/split_step_persistence/transition_contract_signature.rb
372
415
  - lib/smith/workflow/split_step_persistence/transition_contract_structured_values.rb
373
416
  - lib/smith/workflow/step_completion.rb
417
+ - lib/smith/workflow/step_context.rb
374
418
  - lib/smith/workflow/string_snapshot.rb
419
+ - lib/smith/workflow/thread_context_snapshot.rb
375
420
  - lib/smith/workflow/transition.rb
376
421
  - lib/smith/workflow/transition_actionability.rb
377
422
  - lib/smith/workflow/usage_entry.rb