durable_workflow 0.1.0

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 (116) hide show
  1. checksums.yaml +7 -0
  2. data/.claude/todo/01.amend.md +133 -0
  3. data/.claude/todo/02.amend.md +444 -0
  4. data/.claude/todo/phase-1-core/01-GEMSPEC.md +193 -0
  5. data/.claude/todo/phase-1-core/02-TYPES.md +462 -0
  6. data/.claude/todo/phase-1-core/03-EXECUTION.md +551 -0
  7. data/.claude/todo/phase-1-core/04-STEPS.md +603 -0
  8. data/.claude/todo/phase-1-core/05-PARSER.md +719 -0
  9. data/.claude/todo/phase-1-core/todo.md +574 -0
  10. data/.claude/todo/phase-2-runtime/01-STORAGE.md +641 -0
  11. data/.claude/todo/phase-2-runtime/02-RUNNERS.md +511 -0
  12. data/.claude/todo/phase-3-extensions/01-EXTENSION-SYSTEM.md +298 -0
  13. data/.claude/todo/phase-3-extensions/02-AI-PLUGIN.md +936 -0
  14. data/.claude/todo/phase-3-extensions/todo.md +262 -0
  15. data/.claude/todo/phase-4-ai-rework/01-DEPENDENCIES.md +107 -0
  16. data/.claude/todo/phase-4-ai-rework/02-CONFIGURATION.md +123 -0
  17. data/.claude/todo/phase-4-ai-rework/03-TOOL-REGISTRY.md +237 -0
  18. data/.claude/todo/phase-4-ai-rework/04-MCP-SERVER.md +432 -0
  19. data/.claude/todo/phase-4-ai-rework/05-MCP-CLIENT.md +333 -0
  20. data/.claude/todo/phase-4-ai-rework/06-EXECUTORS.md +397 -0
  21. data/.claude/todo/phase-4-ai-rework/todo.md +265 -0
  22. data/.claude/todo/phase-5-validation/.DS_Store +0 -0
  23. data/.claude/todo/phase-5-validation/01-TEST-GAPS.md +615 -0
  24. data/.claude/todo/phase-5-validation/01-TESTS.md +2378 -0
  25. data/.claude/todo/phase-5-validation/02-EXAMPLES-SIMPLE.md +744 -0
  26. data/.claude/todo/phase-5-validation/02-EXAMPLES.md +1857 -0
  27. data/.claude/todo/phase-5-validation/03-EXAMPLE-SUPPORT-AGENT.md +95 -0
  28. data/.claude/todo/phase-5-validation/04-EXAMPLE-ORDER-FULFILLMENT.md +94 -0
  29. data/.claude/todo/phase-5-validation/05-EXAMPLE-DATA-PIPELINE.md +145 -0
  30. data/.env.example +3 -0
  31. data/.rubocop.yml +64 -0
  32. data/0.3.amend.md +89 -0
  33. data/CHANGELOG.md +5 -0
  34. data/CODE_OF_CONDUCT.md +84 -0
  35. data/Gemfile +22 -0
  36. data/Gemfile.lock +192 -0
  37. data/LICENSE.txt +21 -0
  38. data/README.md +39 -0
  39. data/Rakefile +16 -0
  40. data/durable_workflow.gemspec +43 -0
  41. data/examples/approval_request.rb +106 -0
  42. data/examples/calculator.rb +154 -0
  43. data/examples/file_search_demo.rb +77 -0
  44. data/examples/hello_workflow.rb +57 -0
  45. data/examples/item_processor.rb +96 -0
  46. data/examples/order_fulfillment/Gemfile +6 -0
  47. data/examples/order_fulfillment/README.md +84 -0
  48. data/examples/order_fulfillment/run.rb +85 -0
  49. data/examples/order_fulfillment/services.rb +146 -0
  50. data/examples/order_fulfillment/workflow.yml +188 -0
  51. data/examples/parallel_fetch.rb +102 -0
  52. data/examples/service_integration.rb +137 -0
  53. data/examples/support_agent/Gemfile +6 -0
  54. data/examples/support_agent/README.md +91 -0
  55. data/examples/support_agent/config/claude_desktop.json +12 -0
  56. data/examples/support_agent/mcp_server.rb +49 -0
  57. data/examples/support_agent/run.rb +67 -0
  58. data/examples/support_agent/services.rb +113 -0
  59. data/examples/support_agent/workflow.yml +286 -0
  60. data/lib/durable_workflow/core/condition.rb +45 -0
  61. data/lib/durable_workflow/core/engine.rb +145 -0
  62. data/lib/durable_workflow/core/executors/approval.rb +51 -0
  63. data/lib/durable_workflow/core/executors/assign.rb +18 -0
  64. data/lib/durable_workflow/core/executors/base.rb +90 -0
  65. data/lib/durable_workflow/core/executors/call.rb +76 -0
  66. data/lib/durable_workflow/core/executors/end.rb +19 -0
  67. data/lib/durable_workflow/core/executors/halt.rb +24 -0
  68. data/lib/durable_workflow/core/executors/loop.rb +118 -0
  69. data/lib/durable_workflow/core/executors/parallel.rb +77 -0
  70. data/lib/durable_workflow/core/executors/registry.rb +34 -0
  71. data/lib/durable_workflow/core/executors/router.rb +26 -0
  72. data/lib/durable_workflow/core/executors/start.rb +61 -0
  73. data/lib/durable_workflow/core/executors/transform.rb +71 -0
  74. data/lib/durable_workflow/core/executors/workflow.rb +32 -0
  75. data/lib/durable_workflow/core/parser.rb +189 -0
  76. data/lib/durable_workflow/core/resolver.rb +61 -0
  77. data/lib/durable_workflow/core/schema_validator.rb +47 -0
  78. data/lib/durable_workflow/core/types/base.rb +41 -0
  79. data/lib/durable_workflow/core/types/condition.rb +25 -0
  80. data/lib/durable_workflow/core/types/configs.rb +103 -0
  81. data/lib/durable_workflow/core/types/entry.rb +26 -0
  82. data/lib/durable_workflow/core/types/results.rb +41 -0
  83. data/lib/durable_workflow/core/types/state.rb +95 -0
  84. data/lib/durable_workflow/core/types/step_def.rb +15 -0
  85. data/lib/durable_workflow/core/types/workflow_def.rb +43 -0
  86. data/lib/durable_workflow/core/types.rb +29 -0
  87. data/lib/durable_workflow/core/validator.rb +318 -0
  88. data/lib/durable_workflow/extensions/ai/ai.rb +149 -0
  89. data/lib/durable_workflow/extensions/ai/configuration.rb +41 -0
  90. data/lib/durable_workflow/extensions/ai/executors/agent.rb +150 -0
  91. data/lib/durable_workflow/extensions/ai/executors/file_search.rb +52 -0
  92. data/lib/durable_workflow/extensions/ai/executors/guardrail.rb +152 -0
  93. data/lib/durable_workflow/extensions/ai/executors/handoff.rb +33 -0
  94. data/lib/durable_workflow/extensions/ai/executors/mcp.rb +47 -0
  95. data/lib/durable_workflow/extensions/ai/mcp/adapter.rb +73 -0
  96. data/lib/durable_workflow/extensions/ai/mcp/client.rb +77 -0
  97. data/lib/durable_workflow/extensions/ai/mcp/rack_app.rb +66 -0
  98. data/lib/durable_workflow/extensions/ai/mcp/server.rb +122 -0
  99. data/lib/durable_workflow/extensions/ai/tool_registry.rb +63 -0
  100. data/lib/durable_workflow/extensions/ai/types.rb +213 -0
  101. data/lib/durable_workflow/extensions/ai.rb +6 -0
  102. data/lib/durable_workflow/extensions/base.rb +77 -0
  103. data/lib/durable_workflow/runners/adapters/inline.rb +42 -0
  104. data/lib/durable_workflow/runners/adapters/sidekiq.rb +69 -0
  105. data/lib/durable_workflow/runners/async.rb +100 -0
  106. data/lib/durable_workflow/runners/stream.rb +126 -0
  107. data/lib/durable_workflow/runners/sync.rb +40 -0
  108. data/lib/durable_workflow/storage/active_record.rb +148 -0
  109. data/lib/durable_workflow/storage/redis.rb +133 -0
  110. data/lib/durable_workflow/storage/sequel.rb +144 -0
  111. data/lib/durable_workflow/storage/store.rb +43 -0
  112. data/lib/durable_workflow/utils.rb +25 -0
  113. data/lib/durable_workflow/version.rb +5 -0
  114. data/lib/durable_workflow.rb +70 -0
  115. data/sig/durable_workflow.rbs +4 -0
  116. metadata +275 -0
@@ -0,0 +1,95 @@
1
+ # 03-EXAMPLE-SUPPORT-AGENT: AI-Powered Support System
2
+
3
+ ## ⚠️ STATUS: FUTURE/ASPIRATIONAL
4
+
5
+ **This document describes a FUTURE example that requires features not yet implemented:**
6
+
7
+ - `type: agent` step - NOT IMPLEMENTED
8
+ - `type: guardrail` step - NOT IMPLEMENTED
9
+ - `type: handoff` step - NOT IMPLEMENTED
10
+ - `DurableWorkflow::Extensions::AI` - NOT IMPLEMENTED
11
+ - `DurableWorkflow::Extensions::AI::MCP::Server` - NOT IMPLEMENTED
12
+ - `DurableWorkflow.register_service()` - NOT IMPLEMENTED (services use `Object.const_get`)
13
+ - `DurableWorkflow::Runners::Stream` - NOT IMPLEMENTED
14
+ - `runner.subscribe` - NOT IMPLEMENTED
15
+
16
+ **Do not attempt to run this example until these features are built.**
17
+
18
+ ---
19
+
20
+ ## Goal
21
+
22
+ Full-featured example app demonstrating AI extension with MCP integration. Claude Desktop can connect to this as an MCP server.
23
+
24
+ ## Required Features (Not Yet Built)
25
+
26
+ 1. **AI Extension** (`lib/durable_workflow/extensions/ai/`)
27
+ - Agent step executor
28
+ - Tool calling integration
29
+ - Model configuration (OpenAI, Anthropic)
30
+ - Handoff between agents
31
+
32
+ 2. **Guardrails**
33
+ - Content moderation
34
+ - Prompt injection detection
35
+
36
+ 3. **MCP Server**
37
+ - stdio transport for Claude Desktop
38
+ - Tool discovery and invocation
39
+
40
+ 4. **Event Streaming**
41
+ - `runner.subscribe` for real-time events
42
+ - Stream runner for progressive output
43
+
44
+ ---
45
+
46
+ ## Directory Structure (When Implemented)
47
+
48
+ ```
49
+ examples/support_agent/
50
+ README.md
51
+ Gemfile
52
+ workflow.yml
53
+ services.rb
54
+ tools.rb
55
+ run.rb # Interactive CLI
56
+ mcp_server.rb # MCP stdio server for Claude Desktop
57
+ config/
58
+ claude_desktop.json # Example Claude Desktop config
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Workflow Design (Aspirational)
64
+
65
+ The workflow would:
66
+ 1. Receive customer message
67
+ 2. Run content moderation guardrail
68
+ 3. Triage with AI agent to classify request
69
+ 4. Route to specialized agent (billing, technical, general)
70
+ 5. Execute tools as needed (lookup_order, create_ticket, etc.)
71
+ 6. Support handoffs between agents
72
+ 7. Return response
73
+
74
+ ---
75
+
76
+ ## Implementation Prerequisites
77
+
78
+ Before this example can work, implement:
79
+
80
+ 1. `lib/durable_workflow/extensions/ai/` module
81
+ 2. `agent` executor in `lib/durable_workflow/core/executors/agent.rb`
82
+ 3. `guardrail` executor for content checks
83
+ 4. `handoff` executor for agent transfers
84
+ 5. MCP server transport layer
85
+ 6. Event subscription system
86
+
87
+ See the original design below for reference when implementing.
88
+
89
+ ---
90
+
91
+ ## Original Design Reference
92
+
93
+ [The rest of this file contains the aspirational design that was here before.
94
+ It should be used as a reference when implementing these features, NOT as
95
+ working documentation.]
@@ -0,0 +1,94 @@
1
+ # 04-EXAMPLE-ORDER-FULFILLMENT: E-Commerce Order Processing
2
+
3
+ ## ⚠️ STATUS: FUTURE/ASPIRATIONAL
4
+
5
+ **This document describes a FUTURE example that requires features not yet implemented:**
6
+
7
+ - Ruby expression evaluation in YAML (e.g., `$item.quantity * $item.price`) - NOT SUPPORTED
8
+ - `DurableWorkflow.register_service()` - NOT IMPLEMENTED (use `Object.const_get`)
9
+ - `DurableWorkflow::Runners::Stream` - NOT IMPLEMENTED
10
+ - `runner.subscribe` - NOT IMPLEMENTED
11
+ - `runner.run_until_complete` with block - NOT IMPLEMENTED
12
+ - Loop step with inline computation - NOT SUPPORTED (no expression eval)
13
+
14
+ **The resolver only supports `$ref` substitution, NOT expression evaluation.**
15
+
16
+ **Do not attempt to run this example until these features are built or the example is rewritten.**
17
+
18
+ ---
19
+
20
+ ## Goal
21
+
22
+ Complete order fulfillment workflow demonstrating complex business logic, service integration, approvals, and error handling.
23
+
24
+ ## Why This Example Doesn't Work
25
+
26
+ The workflow.yml in the original design relies heavily on Ruby expressions:
27
+
28
+ ```yaml
29
+ # These DO NOT WORK - no expression evaluation
30
+ set:
31
+ line_total: "$item.quantity * $item.price" # ❌ No arithmetic
32
+ subtotal: "$subtotal + $line_total" # ❌ No arithmetic
33
+ tax: "$subtotal * $tax_rate" # ❌ No arithmetic
34
+ shipping_cost: "$subtotal >= 100 ? 0 : 9.99" # ❌ No ternary
35
+ ```
36
+
37
+ ## How To Make This Work
38
+
39
+ To implement this example with current capabilities:
40
+
41
+ 1. **Move ALL computation to services** - Every arithmetic operation must be in Ruby code
42
+ 2. **Use `Object.const_get` for services** - Define as global modules/classes
43
+ 3. **Use `Runners::Sync`** - The only working runner
44
+ 4. **Simplify the workflow** - Use fewer steps, delegate logic to services
45
+
46
+ Example of correct approach:
47
+
48
+ ```ruby
49
+ module OrderCalculator
50
+ def self.calculate_totals(items:, tax_rate: 0.08)
51
+ subtotal = items.sum { |i| i[:quantity] * i[:price] }
52
+ tax = subtotal * tax_rate
53
+ shipping = subtotal >= 100 ? 0 : 9.99
54
+ {
55
+ subtotal: subtotal,
56
+ tax: tax,
57
+ shipping: shipping,
58
+ total: subtotal + tax + shipping
59
+ }
60
+ end
61
+ end
62
+ ```
63
+
64
+ ```yaml
65
+ - id: calculate
66
+ type: call
67
+ service: OrderCalculator
68
+ method: calculate_totals
69
+ input:
70
+ items: "$input.items"
71
+ output: totals
72
+ next: end
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Implementation Prerequisites
78
+
79
+ To run this example AS DESIGNED (with expressions), implement:
80
+
81
+ 1. Expression evaluation in resolver (or a dedicated `eval` step type)
82
+ 2. `DurableWorkflow.register_service()` method
83
+ 3. `Runners::Stream` with event subscription
84
+ 4. `run_until_complete` with halt handling
85
+
86
+ OR rewrite the example to work with current constraints.
87
+
88
+ ---
89
+
90
+ ## Original Design Reference
91
+
92
+ [The original design below should be used as a reference for what the workflow
93
+ SHOULD do, but the YAML syntax needs to be rewritten to use services for all
94
+ computation.]
@@ -0,0 +1,145 @@
1
+ # 05-EXAMPLE-DATA-PIPELINE: ETL Data Processing
2
+
3
+ ## ⚠️ STATUS: FUTURE/ASPIRATIONAL
4
+
5
+ **This document describes a FUTURE example that requires features not yet implemented:**
6
+
7
+ - Ruby expression evaluation in YAML - NOT SUPPORTED
8
+ - `DurableWorkflow.register_service()` - NOT IMPLEMENTED (use `Object.const_get`)
9
+ - `DurableWorkflow.subscribe(pattern:)` - NOT IMPLEMENTED
10
+ - `DurableWorkflow::Runners::Stream` - NOT IMPLEMENTED
11
+ - `runner.subscribe` - NOT IMPLEMENTED
12
+ - Parallel branches as named hash - NOT SUPPORTED (use array of step defs)
13
+
14
+ **The resolver only supports `$ref` substitution, NOT expression evaluation.**
15
+
16
+ **Do not attempt to run this example until these features are built or the example is rewritten.**
17
+
18
+ ---
19
+
20
+ ## Goal
21
+
22
+ Data processing pipeline demonstrating transforms, parallel execution, external MCP consumption, and streaming events.
23
+
24
+ ## Why This Example Doesn't Work
25
+
26
+ The workflow.yml relies on:
27
+
28
+ ```yaml
29
+ # These DO NOT WORK
30
+ set:
31
+ job_id: "'JOB-' + Date.now().toString(36)" # ❌ No JS/Ruby eval
32
+ raw_data: "$csv_data.concat($api_data || [])" # ❌ No method calls
33
+ stats:
34
+ extracted: "$raw_data.length" # ❌ No method calls
35
+
36
+ # Parallel branches format is wrong
37
+ branches:
38
+ csv_extract: # ❌ Named hash not supported
39
+ - id: ...
40
+ api_extract: # ❌ Should be array of steps
41
+ - id: ...
42
+ ```
43
+
44
+ ## How To Make This Work
45
+
46
+ 1. **Move ALL computation to services** - Merging arrays, counting, etc.
47
+ 2. **Use array format for parallel branches**:
48
+
49
+ ```yaml
50
+ branches:
51
+ - id: extract_csv
52
+ type: call
53
+ service: CSVExtractor
54
+ ...
55
+ - id: extract_api
56
+ type: call
57
+ service: APIExtractor
58
+ ...
59
+ ```
60
+
61
+ 3. **Use `Object.const_get` for services** - Global modules
62
+ 4. **Use `Runners::Sync`** - The only working runner
63
+ 5. **Skip event streaming** - Not implemented
64
+
65
+ ---
66
+
67
+ ## Example Files That Were Created (Broken)
68
+
69
+ The `examples/data_pipeline/` directory was created but has been **deleted** because it relied on non-existent features:
70
+
71
+ - `run.rb` - Used `register_service`, `Runners::Stream`, `runner.subscribe`
72
+ - `stream_monitor.rb` - Used `DurableWorkflow.subscribe(pattern:)`
73
+ - `workflow.yml` - Used Ruby expressions throughout
74
+
75
+ ---
76
+
77
+ ## Implementation Prerequisites
78
+
79
+ To run this example AS DESIGNED, implement:
80
+
81
+ 1. Expression evaluation in resolver
82
+ 2. `DurableWorkflow.register_service()` method
83
+ 3. `DurableWorkflow.subscribe(pattern:)` for global event subscription
84
+ 4. `Runners::Stream` with event callbacks
85
+ 5. Named branch support in parallel step (or document array-only)
86
+
87
+ OR rewrite the example to work with current constraints.
88
+
89
+ ---
90
+
91
+ ## Minimal Working Alternative
92
+
93
+ Here's a simplified data pipeline that WOULD work:
94
+
95
+ ```ruby
96
+ module DataPipeline
97
+ def self.run_etl(source_file:, output_path:)
98
+ # Extract
99
+ records = CSV.read(source_file, headers: true).map(&:to_h)
100
+
101
+ # Validate
102
+ valid, invalid = records.partition { |r| r["email"]&.include?("@") }
103
+
104
+ # Transform
105
+ transformed = valid.map do |r|
106
+ r.merge("domain" => r["email"]&.split("@")&.last)
107
+ end
108
+
109
+ # Load
110
+ File.write("#{output_path}/records.json", JSON.pretty_generate(transformed))
111
+ File.write("#{output_path}/errors.json", JSON.pretty_generate(invalid))
112
+
113
+ {
114
+ extracted: records.size,
115
+ valid: valid.size,
116
+ invalid: invalid.size,
117
+ output_path: output_path
118
+ }
119
+ end
120
+ end
121
+ ```
122
+
123
+ ```yaml
124
+ steps:
125
+ - id: start
126
+ type: start
127
+ next: process
128
+
129
+ - id: process
130
+ type: call
131
+ service: DataPipeline
132
+ method: run_etl
133
+ input:
134
+ source_file: "$input.source_file"
135
+ output_path: "$input.output_path"
136
+ output: result
137
+ next: end
138
+
139
+ - id: end
140
+ type: end
141
+ result:
142
+ stats: "$result"
143
+ ```
144
+
145
+ This delegates everything to the service, which is the correct pattern given current resolver limitations.
data/.env.example ADDED
@@ -0,0 +1,3 @@
1
+ # Copy to .env and fill in your API keys
2
+ OPENAI_API_KEY=sk-your-openai-key-here
3
+ ANTHROPIC_API_KEY=sk-ant-your-anthropic-key-here
data/.rubocop.yml ADDED
@@ -0,0 +1,64 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.1
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+ Exclude:
6
+ - 'vendor/**/*'
7
+ - 'examples/**/*'
8
+ - 'spec/**/*'
9
+ - 'test/**/*'
10
+
11
+ # Disable noisy cops for this project
12
+ Style/Documentation:
13
+ Enabled: false
14
+
15
+ Metrics/MethodLength:
16
+ Enabled: false
17
+
18
+ Metrics/AbcSize:
19
+ Enabled: false
20
+
21
+ Metrics/CyclomaticComplexity:
22
+ Enabled: false
23
+
24
+ Metrics/PerceivedComplexity:
25
+ Enabled: false
26
+
27
+ Metrics/ClassLength:
28
+ Enabled: false
29
+
30
+ Metrics/ParameterLists:
31
+ Enabled: false
32
+
33
+ Layout/LineLength:
34
+ Max: 200
35
+
36
+ Gemspec/OrderedDependencies:
37
+ Enabled: false
38
+
39
+ Naming/PredicateMethod:
40
+ Enabled: false
41
+
42
+ Naming/MethodParameterName:
43
+ Enabled: false
44
+
45
+ Lint/MissingSuper:
46
+ Enabled: false
47
+
48
+ Lint/UnusedMethodArgument:
49
+ Enabled: false
50
+
51
+ Lint/UnusedBlockArgument:
52
+ Enabled: false
53
+
54
+ Lint/DuplicateBranch:
55
+ Enabled: false
56
+
57
+ Lint/NonLocalExitFromIterator:
58
+ Enabled: false
59
+
60
+ Style/SafeNavigationChainLength:
61
+ Enabled: false
62
+
63
+ Style/OptionalBooleanParameter:
64
+ Enabled: false
data/0.3.amend.md ADDED
@@ -0,0 +1,89 @@
1
+ # 0.3 Amendment: Kill Fallback Plague
2
+
3
+ ## 1. `run` signature → `input:` kwarg
4
+
5
+ ```ruby
6
+ # Before
7
+ engine.run({ value: 21 })
8
+
9
+ # After
10
+ engine.run(input: { value: 21 })
11
+ ```
12
+
13
+ **Files:**
14
+ - `lib/durable_workflow/core/engine.rb` - `def run(input, ...)` → `def run(input: {}, ...)`
15
+ - `lib/durable_workflow/runners/sync.rb` - `def run(input, ...)` → `def run(input: {}, ...)`
16
+ - `lib/durable_workflow/runners/sync.rb` - `def run_until_complete(input, ...)` → `def run_until_complete(input: {}, ...)`
17
+ - `lib/durable_workflow/runners/stream.rb` - same
18
+ - `lib/durable_workflow/runners/adapters/sidekiq.rb` - `engine.run(args[:input] || {}, ...)` → `engine.run(input: args[:input], ...)`
19
+ - `lib/durable_workflow/runners/adapters/inline.rb` - `engine.run(input || {}, ...)` → `engine.run(input:, ...)`
20
+
21
+ **Examples (update call sites):**
22
+ - `examples/order_fulfillment/run.rb:72` - `runner.run(order)` → `runner.run(input: order)`
23
+ - `examples/file_search_demo.rb:69` - `runner.run({ query: ... })` → `runner.run(input: { query: ... })`
24
+ - `examples/item_processor.rb:75` - `runner.run({...})` → `runner.run(input: {...})`
25
+ - `examples/calculator.rb:148` - `runner.run(input)` → `runner.run(input:)`
26
+ - `examples/approval_request.rb:94,99` - `runner.run({...})` → `runner.run(input: {...})`
27
+ - `examples/support_agent/run.rb:52` - `runner.run({...})` → `runner.run(input: {...})`
28
+ - `examples/service_integration.rb:125,130,135` - `runner.run({...})` → `runner.run(input: {...})`
29
+ - `examples/parallel_fetch.rb:96` - `runner.run({...})` → `runner.run(input: {...})`
30
+ - `examples/hello_workflow.rb:52` - `runner.run({...})` → `runner.run(input: {...})`
31
+
32
+ **Tests (update call sites):**
33
+ - `test/integration/workflow_test.rb:72,80,113,161,201,246,268`
34
+ - `test/unit/runners/async_test.rb:50,58,66,76,86,130`
35
+ - `test/unit/runners/sync_test.rb:78,88,97,107`
36
+ - `test/unit/runners/stream_test.rb:67,79,91,106,118,133,149`
37
+ - `test/unit/core/engine_test.rb:70,87,100,121,139,160,183,231,252,268`
38
+
39
+ ---
40
+
41
+ ## 2. Indifferent access util
42
+
43
+ Add `Utils.fetch(hash, key)` - kills `x[key.to_sym] || x[key.to_s]` pattern.
44
+
45
+ **Add to:**
46
+ - `lib/durable_workflow/utils.rb`
47
+
48
+ **Replace in:**
49
+ - `lib/durable_workflow/core/resolver.rb:49`
50
+ - `lib/durable_workflow/core/validator.rb:249,255`
51
+ - `lib/durable_workflow/core/executors/transform.rb:44`
52
+ - `lib/durable_workflow/extensions/ai/executors/mcp.rb:26,37`
53
+ - `lib/durable_workflow/extensions/ai/executors/agent.rb:110,111`
54
+ - `lib/durable_workflow/extensions/ai/mcp/adapter.rb:54,55`
55
+
56
+ ---
57
+
58
+ ## 3. dry-struct defaults
59
+
60
+ Add `.default()` to type attributes:
61
+
62
+ | Attribute | Default |
63
+ |-----------|---------|
64
+ | `files` | `[]` |
65
+ | `max_results` | `10` |
66
+ | `checks` | `[]` |
67
+ | `arguments` | `{}.freeze` |
68
+ | `data` | `{}` |
69
+ | `reason` | `"Halted"` |
70
+ | `wait` | `"all"` |
71
+ | `tools` | `[]` |
72
+ | `headers` | `{}.freeze` |
73
+ | `transport` | `:http` |
74
+
75
+ ---
76
+
77
+ ## 4. Remove dead fallbacks
78
+
79
+ After defaults are set, remove `|| {}` / `|| []` from:
80
+
81
+ - `lib/durable_workflow/core/executors/halt.rb:10,14,18`
82
+ - `lib/durable_workflow/core/executors/end.rb:11`
83
+ - `lib/durable_workflow/core/executors/loop.rb:91`
84
+ - `lib/durable_workflow/core/executors/parallel.rb:24`
85
+ - `lib/durable_workflow/extensions/ai/executors/file_search.rb:10,11`
86
+ - `lib/durable_workflow/extensions/ai/executors/mcp.rb:11`
87
+ - `lib/durable_workflow/extensions/ai/executors/agent.rb:59`
88
+ - `lib/durable_workflow/extensions/ai/executors/guardrail.rb:30,31`
89
+ - `lib/durable_workflow/extensions/ai/ai.rb:70,109,122,123,131,136,141`
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2025-11-28
4
+
5
+ - Initial release
@@ -0,0 +1,84 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment for our community include:
12
+
13
+ * Demonstrating empathy and kindness toward other people
14
+ * Being respectful of differing opinions, viewpoints, and experiences
15
+ * Giving and gracefully accepting constructive feedback
16
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ * Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * The use of sexualized language or imagery, and sexual attention or
22
+ advances of any kind
23
+ * Trolling, insulting or derogatory comments, and personal or political attacks
24
+ * Public or private harassment
25
+ * Publishing others' private information, such as a physical or email
26
+ address, without their explicit permission
27
+ * Other conduct which could reasonably be considered inappropriate in a
28
+ professional setting
29
+
30
+ ## Enforcement Responsibilities
31
+
32
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
+
34
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
+
36
+ ## Scope
37
+
38
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
+
40
+ ## Enforcement
41
+
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at ben@dee.mx. All complaints will be reviewed and investigated promptly and fairly.
43
+
44
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
+
46
+ ## Enforcement Guidelines
47
+
48
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
+
50
+ ### 1. Correction
51
+
52
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
+
54
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
+
56
+ ### 2. Warning
57
+
58
+ **Community Impact**: A violation through a single incident or series of actions.
59
+
60
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
+
62
+ ### 3. Temporary Ban
63
+
64
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
+
66
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
+
68
+ ### 4. Permanent Ban
69
+
70
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
+
72
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
73
+
74
+ ## Attribution
75
+
76
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
+ available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
+
79
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
+
81
+ [homepage]: https://www.contributor-covenant.org
82
+
83
+ For answers to common questions about this code of conduct, see the FAQ at
84
+ https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ # Development
8
+ gem 'minitest', '~> 5.0'
9
+ gem 'rake', '~> 13.0'
10
+ gem 'rubocop', '~> 1.21'
11
+
12
+ # Storage adapters (for testing)
13
+ gem 'activerecord', '~> 7.0'
14
+ gem 'redis', '~> 5.0'
15
+ gem 'sequel', '~> 5.0'
16
+ gem 'sqlite3', '~> 1.6'
17
+
18
+ # AI extension (now runtime dependencies via gemspec)
19
+ # ruby_llm, mcp, faraday are declared in gemspec
20
+
21
+ # Examples
22
+ gem 'dotenv', '~> 3.0'