roast-ai 0.1.0 → 0.1.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.
- checksums.yaml +4 -4
- data/.github/workflows/cla.yml +1 -1
- data/.gitignore +1 -0
- data/CHANGELOG.md +20 -0
- data/CLAUDE.md +3 -1
- data/Gemfile +0 -1
- data/Gemfile.lock +3 -4
- data/README.md +418 -4
- data/Rakefile +1 -6
- data/docs/INSTRUMENTATION.md +202 -0
- data/examples/api_workflow/README.md +85 -0
- data/examples/api_workflow/fetch_api_data/prompt.md +10 -0
- data/examples/api_workflow/generate_report/prompt.md +10 -0
- data/examples/api_workflow/prompt.md +10 -0
- data/examples/api_workflow/transform_data/prompt.md +10 -0
- data/examples/api_workflow/workflow.yml +30 -0
- data/examples/grading/workflow.yml +2 -2
- data/examples/instrumentation.rb +76 -0
- data/examples/rspec_to_minitest/README.md +68 -0
- data/examples/rspec_to_minitest/analyze_spec/prompt.md +30 -0
- data/examples/rspec_to_minitest/create_minitest/prompt.md +33 -0
- data/examples/rspec_to_minitest/run_and_improve/prompt.md +35 -0
- data/examples/rspec_to_minitest/workflow.md +10 -0
- data/examples/rspec_to_minitest/workflow.yml +40 -0
- data/lib/roast/helpers/function_caching_interceptor.rb +72 -8
- data/lib/roast/helpers/prompt_loader.rb +2 -0
- data/lib/roast/resources/api_resource.rb +137 -0
- data/lib/roast/resources/base_resource.rb +47 -0
- data/lib/roast/resources/directory_resource.rb +40 -0
- data/lib/roast/resources/file_resource.rb +33 -0
- data/lib/roast/resources/none_resource.rb +29 -0
- data/lib/roast/resources/url_resource.rb +63 -0
- data/lib/roast/resources.rb +100 -0
- data/lib/roast/tools/coding_agent.rb +69 -0
- data/lib/roast/tools.rb +1 -0
- data/lib/roast/version.rb +1 -1
- data/lib/roast/workflow/base_step.rb +21 -17
- data/lib/roast/workflow/base_workflow.rb +49 -16
- data/lib/roast/workflow/configuration.rb +83 -8
- data/lib/roast/workflow/configuration_parser.rb +171 -3
- data/lib/roast/workflow/file_state_repository.rb +126 -0
- data/lib/roast/workflow/prompt_step.rb +16 -0
- data/lib/roast/workflow/session_manager.rb +82 -0
- data/lib/roast/workflow/state_repository.rb +21 -0
- data/lib/roast/workflow/workflow_executor.rb +99 -9
- data/lib/roast/workflow.rb +4 -0
- data/lib/roast.rb +2 -5
- data/roast.gemspec +1 -1
- data/schema/workflow.json +12 -0
- metadata +31 -6
- data/.rspec +0 -1
@@ -0,0 +1,202 @@
|
|
1
|
+
# Instrumentation Hooks in Roast
|
2
|
+
|
3
|
+
Roast provides built-in instrumentation hooks using ActiveSupport::Notifications, allowing you to track workflow execution, monitor performance, and integrate with your own monitoring systems.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The instrumentation system emits events at key points during workflow execution:
|
8
|
+
|
9
|
+
- Workflow lifecycle (start, complete)
|
10
|
+
- Step execution (start, complete, error)
|
11
|
+
- Chat completion/AI calls (start, complete, error)
|
12
|
+
- Tool function execution
|
13
|
+
|
14
|
+
## Configuration
|
15
|
+
|
16
|
+
To add custom instrumentation, create Ruby files in your project's `.roast/initializers/` directory. These files will be automatically loaded during workflow startup.
|
17
|
+
|
18
|
+
Example structure:
|
19
|
+
```
|
20
|
+
your-project/
|
21
|
+
├── .roast/
|
22
|
+
│ └── initializers/
|
23
|
+
│ ├── logging.rb
|
24
|
+
│ ├── metrics.rb
|
25
|
+
│ └── monitoring.rb
|
26
|
+
└── ...
|
27
|
+
```
|
28
|
+
|
29
|
+
## Available Events
|
30
|
+
|
31
|
+
### Workflow Events
|
32
|
+
|
33
|
+
- `roast.workflow.start` - Emitted when a workflow begins
|
34
|
+
- Payload: `{ workflow_path:, options:, name: }`
|
35
|
+
|
36
|
+
- `roast.workflow.complete` - Emitted when a workflow completes
|
37
|
+
- Payload: `{ workflow_path:, success:, execution_time: }`
|
38
|
+
|
39
|
+
### Step Events
|
40
|
+
|
41
|
+
- `roast.step.start` - Emitted when a step begins execution
|
42
|
+
- Payload: `{ step_name: }`
|
43
|
+
|
44
|
+
- `roast.step.complete` - Emitted when a step completes successfully
|
45
|
+
- Payload: `{ step_name:, success: true, execution_time:, result_size: }`
|
46
|
+
|
47
|
+
- `roast.step.error` - Emitted when a step encounters an error
|
48
|
+
- Payload: `{ step_name:, error:, message:, execution_time: }`
|
49
|
+
|
50
|
+
### AI/Chat Completion Events
|
51
|
+
|
52
|
+
- `roast.chat_completion.start` - Emitted before an AI API call
|
53
|
+
- Payload: `{ model:, parameters: }`
|
54
|
+
|
55
|
+
- `roast.chat_completion.complete` - Emitted after successful AI API call
|
56
|
+
- Payload: `{ success: true, model:, parameters:, execution_time:, response_size: }`
|
57
|
+
|
58
|
+
- `roast.chat_completion.error` - Emitted when AI API call fails
|
59
|
+
- Payload: `{ error:, message:, model:, parameters:, execution_time: }`
|
60
|
+
|
61
|
+
### Tool Execution Events
|
62
|
+
|
63
|
+
- `roast.tool.execute` - Emitted when a tool function is called
|
64
|
+
- Payload: `{ function_name:, params: }`
|
65
|
+
|
66
|
+
- `roast.tool.complete` - Emitted when a tool function completes
|
67
|
+
- Payload: `{ function_name:, execution_time:, cache_enabled: }`
|
68
|
+
|
69
|
+
- `roast.tool.error` - Emitted when a tool execution fails
|
70
|
+
- Payload: `{ function_name:, error:, message:, execution_time: }`
|
71
|
+
|
72
|
+
## Example Usage
|
73
|
+
|
74
|
+
### Basic Logging
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
# .roast/initializers/logging.rb
|
78
|
+
ActiveSupport::Notifications.subscribe(/roast\./) do |name, start, finish, id, payload|
|
79
|
+
duration = finish - start
|
80
|
+
puts "[#{name}] completed in #{duration.round(3)}s"
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
### Performance Monitoring
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
# .roast/initializers/performance.rb
|
88
|
+
ActiveSupport::Notifications.subscribe("roast.step.complete") do |name, start, finish, id, payload|
|
89
|
+
duration = finish - start
|
90
|
+
if duration > 10.0
|
91
|
+
puts "WARNING: Step '#{payload[:step_name]}' took #{duration.round(1)}s"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
### Integration with External Services
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
# .roast/initializers/metrics.rb
|
100
|
+
ActiveSupport::Notifications.subscribe("roast.workflow.complete") do |name, start, finish, id, payload|
|
101
|
+
duration = finish - start
|
102
|
+
|
103
|
+
# Send to your metrics service
|
104
|
+
MyMetricsService.track_event("workflow_execution", {
|
105
|
+
workflow_path: payload[:workflow_path],
|
106
|
+
duration: duration,
|
107
|
+
success: payload[:success]
|
108
|
+
})
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
### Internal Shopify Example
|
113
|
+
|
114
|
+
For the internal Shopify version, you can use these instrumentation hooks to track metrics with Monorail:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
# .roast/initializers/monorail.rb
|
118
|
+
|
119
|
+
# Track workflow execution
|
120
|
+
ActiveSupport::Notifications.subscribe("roast.workflow.start") do |name, start, finish, id, payload|
|
121
|
+
Roast::Support::Monorail.track_command("run", {
|
122
|
+
"workflow_path" => payload[:workflow_path],
|
123
|
+
"options" => payload[:options],
|
124
|
+
"name" => payload[:name]
|
125
|
+
})
|
126
|
+
end
|
127
|
+
|
128
|
+
ActiveSupport::Notifications.subscribe("roast.workflow.complete") do |name, start, finish, id, payload|
|
129
|
+
Roast::Support::Monorail.track_command("run_complete", {
|
130
|
+
"workflow_path" => payload[:workflow_path],
|
131
|
+
"success" => payload[:success],
|
132
|
+
"execution_time" => payload[:execution_time]
|
133
|
+
})
|
134
|
+
end
|
135
|
+
|
136
|
+
# Track AI model usage and performance
|
137
|
+
ActiveSupport::Notifications.subscribe("roast.chat_completion.complete") do |name, start, finish, id, payload|
|
138
|
+
Roast::Support::Monorail.track_command("ai_usage", {
|
139
|
+
"model" => payload[:model],
|
140
|
+
"execution_time" => payload[:execution_time],
|
141
|
+
"response_size" => payload[:response_size],
|
142
|
+
"has_json" => payload[:parameters][:json] || false,
|
143
|
+
"has_loop" => payload[:parameters][:loop] || false
|
144
|
+
})
|
145
|
+
end
|
146
|
+
|
147
|
+
# Track tool execution and caching
|
148
|
+
ActiveSupport::Notifications.subscribe("roast.tool.complete") do |name, start, finish, id, payload|
|
149
|
+
Roast::Support::Monorail.track_command("tool_usage", {
|
150
|
+
"function_name" => payload[:function_name],
|
151
|
+
"execution_time" => payload[:execution_time],
|
152
|
+
"cache_enabled" => payload[:cache_enabled]
|
153
|
+
})
|
154
|
+
end
|
155
|
+
```
|
156
|
+
|
157
|
+
See `examples/monorail_initializer.rb` for a complete example of Monorail integration.
|
158
|
+
|
159
|
+
## Best Practices
|
160
|
+
|
161
|
+
1. **Keep initializers focused**: Each initializer should handle a specific concern (logging, metrics, error reporting, etc.)
|
162
|
+
|
163
|
+
2. **Handle errors gracefully**: Wrap your subscriber code in error handling to prevent crashes:
|
164
|
+
```ruby
|
165
|
+
ActiveSupport::Notifications.subscribe("roast.workflow.start") do |name, start, finish, id, payload|
|
166
|
+
begin
|
167
|
+
# Your instrumentation code here
|
168
|
+
rescue => e
|
169
|
+
$stderr.puts "Instrumentation error: #{e.message}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
3. **Avoid blocking operations**: Instrumentation should be fast and non-blocking. For heavy operations, consider using async processing.
|
175
|
+
|
176
|
+
4. **Use pattern matching**: Subscribe to specific event patterns to reduce overhead:
|
177
|
+
```ruby
|
178
|
+
# Subscribe only to workflow events
|
179
|
+
ActiveSupport::Notifications.subscribe(/roast\.workflow\./) do |name, start, finish, id, payload|
|
180
|
+
# Handle only workflow events
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
5. **Consider performance impact**: While instrumentation is valuable, too many subscribers can impact performance. Be selective about what you instrument.
|
185
|
+
|
186
|
+
## Testing Your Instrumentation
|
187
|
+
|
188
|
+
You can test your instrumentation by creating a simple workflow and observing the events:
|
189
|
+
|
190
|
+
```yaml
|
191
|
+
# test_instrumentation.yml
|
192
|
+
name: instrumentation_test
|
193
|
+
steps:
|
194
|
+
- test_step
|
195
|
+
```
|
196
|
+
|
197
|
+
Then run:
|
198
|
+
```bash
|
199
|
+
roast execute test_instrumentation.yml some_file.rb
|
200
|
+
```
|
201
|
+
|
202
|
+
Your instrumentation should capture the workflow start, step execution, and workflow completion events.
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# API Workflow Example
|
2
|
+
|
3
|
+
This example demonstrates a targetless workflow that interacts with APIs rather than operating on specific files.
|
4
|
+
|
5
|
+
## Structure
|
6
|
+
|
7
|
+
The workflow consists of three steps that work together to create a complete API integration process:
|
8
|
+
|
9
|
+
1. `fetch_api_data` - Simulates fetching data from a weather API and returns a structured JSON response
|
10
|
+
2. `transform_data` - Processes the JSON data into a human-readable markdown format
|
11
|
+
3. `generate_report` - Creates a polished report with recommendations based on the weather data
|
12
|
+
|
13
|
+
## Running the Example
|
14
|
+
|
15
|
+
To run this example, you need to have a valid API token. The example is configured to fetch a token using a shell command:
|
16
|
+
|
17
|
+
```yaml
|
18
|
+
# Dynamic API token using shell command
|
19
|
+
api_token: $(print-token --key)
|
20
|
+
```
|
21
|
+
|
22
|
+
You can modify this to use your own token source, such as:
|
23
|
+
|
24
|
+
```yaml
|
25
|
+
# Using an environment variable
|
26
|
+
api_token: $(echo $OPENAI_API_KEY)
|
27
|
+
|
28
|
+
# Or a direct value (not recommended for production)
|
29
|
+
api_token: $(echo "sk-your-actual-token")
|
30
|
+
```
|
31
|
+
|
32
|
+
Then run the workflow:
|
33
|
+
|
34
|
+
```bash
|
35
|
+
# Run the targetless workflow
|
36
|
+
roast execute examples/api_workflow/workflow.yml
|
37
|
+
|
38
|
+
# Save the output to a file
|
39
|
+
roast execute examples/api_workflow/workflow.yml -o weather_report.md
|
40
|
+
```
|
41
|
+
|
42
|
+
## How Targetless Workflows Work
|
43
|
+
|
44
|
+
Targetless workflows operate without a specific target file. This is useful for:
|
45
|
+
|
46
|
+
- API integrations
|
47
|
+
- Content generation
|
48
|
+
- Data analysis
|
49
|
+
- Report creation
|
50
|
+
- Interactive tools
|
51
|
+
|
52
|
+
Unlike file-based workflows that process each target separately, targetless workflows run once and can retrieve their own data sources (like API calls) or generate content from scratch.
|
53
|
+
|
54
|
+
## Workflow Definition
|
55
|
+
|
56
|
+
```yaml
|
57
|
+
name: API Integration Workflow
|
58
|
+
# Default model for all steps
|
59
|
+
model: gpt-4o-mini
|
60
|
+
|
61
|
+
tools:
|
62
|
+
- Roast::Tools::ReadFile
|
63
|
+
- Roast::Tools::Grep
|
64
|
+
- Roast::Tools::WriteFile
|
65
|
+
|
66
|
+
steps:
|
67
|
+
- fetch_api_data
|
68
|
+
- transform_data
|
69
|
+
- generate_report
|
70
|
+
|
71
|
+
# Tool configurations for API calls (no need to specify model here since it uses global model)
|
72
|
+
fetch_api_data:
|
73
|
+
print_response: true
|
74
|
+
```
|
75
|
+
|
76
|
+
## Creating Your Own Targetless Workflows
|
77
|
+
|
78
|
+
To create your own targetless workflow:
|
79
|
+
|
80
|
+
1. Create a workflow YAML file without a `target` parameter
|
81
|
+
2. Define the steps your workflow will execute
|
82
|
+
3. Create prompt files for each step
|
83
|
+
4. Run the workflow with `roast execute your_workflow.yml`
|
84
|
+
|
85
|
+
Your steps can use the workflow's `output` hash to pass data between them, just like in file-based workflows.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
You are an assistant helping with retrieving data from an API source.
|
2
|
+
|
3
|
+
Your task is to simulate fetching data from a weather API using mock data.
|
4
|
+
|
5
|
+
1. Imagine you are retrieving current weather conditions for various cities
|
6
|
+
2. Create a structured JSON response that represents typical weather API data
|
7
|
+
3. The response should include temperature, conditions, wind, and other relevant weather metrics
|
8
|
+
4. Format the response as valid JSON that could be parsed programmatically
|
9
|
+
|
10
|
+
Return only the JSON data without any additional explanation.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
You are an assistant helping to generate a final weather report.
|
2
|
+
|
3
|
+
Based on the transformed data from the previous step, create a polished report that could be sent to stakeholders.
|
4
|
+
|
5
|
+
1. Take the transformed weather data and enhance it with recommendations and insights
|
6
|
+
2. Add relevant suggestions based on the weather conditions (like what to wear, activities that would be appropriate, etc.)
|
7
|
+
3. Include a "forecast overview" section that summarizes the key points
|
8
|
+
4. Format the output as a professional-looking report with proper headings and structure
|
9
|
+
|
10
|
+
The report should be comprehensive yet concise, easy to scan, and provide actionable insights based on the weather data.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# API Integration Workflow
|
2
|
+
|
3
|
+
This workflow demonstrates how to create an API integration that:
|
4
|
+
1. Fetches data from an external source
|
5
|
+
2. Transforms the data into a usable format
|
6
|
+
3. Generates a report based on the processed data
|
7
|
+
|
8
|
+
The workflow doesn't require a target file because it's designed to work with external APIs and data sources rather than processing specific files.
|
9
|
+
|
10
|
+
You'll be working through a weather data processing example. This is a simulation to demonstrate the workflow pattern - no actual API calls are made.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
You are an assistant helping to transform API data.
|
2
|
+
|
3
|
+
Your task is to process weather data from a JSON format into a more readable summary.
|
4
|
+
|
5
|
+
1. Review the weather data provided in the previous step
|
6
|
+
2. Transform the technical JSON data into a human-readable summary
|
7
|
+
3. Format the output as markdown with appropriate sections and highlights
|
8
|
+
4. Focus on the most relevant information that would be useful to a typical user
|
9
|
+
|
10
|
+
Return a well-formatted markdown summary of the weather data.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# API Integration Workflow
|
2
|
+
|
3
|
+
# This workflow demonstrates how to create an API integration that:
|
4
|
+
# 1. Fetches data from an external source
|
5
|
+
# 2. Transforms the data into a usable format
|
6
|
+
# 3. Generates a report based on the processed data
|
7
|
+
|
8
|
+
# The workflow doesn't require a target file because it's designed to work with external APIs and data sources rather than processing specific files.
|
9
|
+
|
10
|
+
# You'll be working through a weather data processing example. This is a simulation to demonstrate the workflow pattern - no actual API calls are made.
|
11
|
+
|
12
|
+
name: API Integration Workflow
|
13
|
+
model: gpt-4o-mini
|
14
|
+
|
15
|
+
tools:
|
16
|
+
- Roast::Tools::ReadFile
|
17
|
+
- Roast::Tools::Grep
|
18
|
+
- Roast::Tools::WriteFile
|
19
|
+
|
20
|
+
# For demonstration purposes only - in production you would use a real token command
|
21
|
+
# api_token: $(echo "demo_token_123456")
|
22
|
+
|
23
|
+
steps:
|
24
|
+
- fetch_api_data
|
25
|
+
- transform_data
|
26
|
+
- generate_report
|
27
|
+
|
28
|
+
# Tool configurations for API calls
|
29
|
+
fetch_api_data:
|
30
|
+
print_response: true
|
@@ -23,7 +23,7 @@ steps:
|
|
23
23
|
# set non-default attributes for steps below
|
24
24
|
analyze_coverage:
|
25
25
|
model: gpt-4.1-mini
|
26
|
-
|
26
|
+
auto_loop: false
|
27
27
|
json: true
|
28
28
|
|
29
29
|
generate_grades:
|
@@ -32,7 +32,7 @@ generate_grades:
|
|
32
32
|
|
33
33
|
generate_recommendations:
|
34
34
|
model: o3
|
35
|
-
|
35
|
+
auto_loop: false
|
36
36
|
json: true
|
37
37
|
params:
|
38
38
|
max_completion_tokens: 5_000
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Demonstration of how to use the Roast instrumentation hooks
|
4
|
+
# This file would typically be placed in PROJECT_ROOT/.roast/initializers/
|
5
|
+
# for automatic loading during workflow execution
|
6
|
+
|
7
|
+
# Example: Log all workflow and step events
|
8
|
+
ActiveSupport::Notifications.subscribe(/roast\.workflow\./) do |name, start, finish, _id, payload|
|
9
|
+
duration = finish - start
|
10
|
+
|
11
|
+
case name
|
12
|
+
when "roast.workflow.start"
|
13
|
+
puts "\n🚀 Workflow starting: #{payload[:name]}"
|
14
|
+
puts " Path: #{payload[:workflow_path]}"
|
15
|
+
puts " Options: #{payload[:options]}"
|
16
|
+
when "roast.workflow.complete"
|
17
|
+
status = payload[:success] ? "✅ Successfully" : "❌ With errors"
|
18
|
+
puts "\n#{status} completed workflow in #{duration.round(2)} seconds"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Example: Track step execution times
|
23
|
+
ActiveSupport::Notifications.subscribe(/roast\.step\./) do |name, start, finish, _id, payload|
|
24
|
+
duration = finish - start
|
25
|
+
|
26
|
+
case name
|
27
|
+
when "roast.step.start"
|
28
|
+
puts "\n ▶️ Step starting: #{payload[:step_name]}"
|
29
|
+
when "roast.step.complete"
|
30
|
+
puts " ✅ Step completed: #{payload[:step_name]} (#{duration.round(3)}s)"
|
31
|
+
when "roast.step.error"
|
32
|
+
puts " ❌ Step failed: #{payload[:step_name]}"
|
33
|
+
puts " Error: #{payload[:error]} - #{payload[:message]}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Example: Monitor AI interactions
|
38
|
+
ActiveSupport::Notifications.subscribe(/roast\.chat_completion\./) do |name, start, finish, _id, payload|
|
39
|
+
case name
|
40
|
+
when "roast.chat_completion.start"
|
41
|
+
puts "\n 🤖 AI request starting (model: #{payload[:model]})"
|
42
|
+
puts " Parameters: #{payload[:parameters].inspect}" if payload[:parameters].any?
|
43
|
+
when "roast.chat_completion.complete"
|
44
|
+
duration = finish - start
|
45
|
+
puts " 🤖 AI request completed in #{duration.round(2)}s (execution time: #{payload[:execution_time].round(2)}s)"
|
46
|
+
puts " Response size: #{payload[:response_size]} characters"
|
47
|
+
when "roast.chat_completion.error"
|
48
|
+
puts " 🤖 AI request failed: #{payload[:error]} - #{payload[:message]}"
|
49
|
+
puts " Execution time: #{payload[:execution_time].round(2)}s"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Example: Track tool executions
|
54
|
+
ActiveSupport::Notifications.subscribe(/roast\.tool\./) do |name, _start, _finish, _id, payload|
|
55
|
+
case name
|
56
|
+
when "roast.tool.execute"
|
57
|
+
puts "\n 🔧 Executing tool: #{payload[:function_name]}"
|
58
|
+
when "roast.tool.complete"
|
59
|
+
puts " 🔧 Tool completed: #{payload[:function_name]} (#{payload[:execution_time].round(3)}s)"
|
60
|
+
puts " Cache enabled: #{payload[:cache_enabled]}"
|
61
|
+
when "roast.tool.error"
|
62
|
+
puts " 🔧 Tool failed: #{payload[:function_name]}"
|
63
|
+
puts " Error: #{payload[:error]} - #{payload[:message]}"
|
64
|
+
puts " Execution time: #{payload[:execution_time].round(3)}s"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# In a Shopify-specific initializer (e.g., .roast/initializers/monorail.rb),
|
69
|
+
# you could replace these logging examples with actual Monorail tracking:
|
70
|
+
#
|
71
|
+
# ActiveSupport::Notifications.subscribe("roast.workflow.start") do |name, start, finish, id, payload|
|
72
|
+
# Roast::Support::Monorail.track_command("run", {
|
73
|
+
# "workflow_path" => payload[:workflow_path],
|
74
|
+
# "name" => payload[:name]
|
75
|
+
# })
|
76
|
+
# end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# RSpec to Minitest Migration Workflow
|
2
|
+
|
3
|
+
This workflow demonstrates how to automate the migration of RSpec tests to their Minitest equivalents, following a structured approach to ensure proper test coverage and functionality.
|
4
|
+
|
5
|
+
## Workflow Overview
|
6
|
+
|
7
|
+
The workflow consists of three main steps:
|
8
|
+
|
9
|
+
1. **Analyze Spec**: Understand the purpose and structure of the RSpec test, including its dependencies and testing patterns.
|
10
|
+
2. **Create Minitest**: Generate a new Minitest file with equivalent test coverage and assertions.
|
11
|
+
3. **Run and Improve**: Execute the Minitest file and iteratively improve it until all tests pass.
|
12
|
+
|
13
|
+
## Prerequisites
|
14
|
+
|
15
|
+
- Ruby environment with both RSpec and Minitest gems installed
|
16
|
+
- Access to the original codebase being tested
|
17
|
+
- Ability to run tests in the target environment
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
To use this workflow:
|
22
|
+
|
23
|
+
1. Configure the target pattern in `workflow.yml` to match the RSpec files you want to convert (or pass in via CLI --target option):
|
24
|
+
```yaml
|
25
|
+
target: "path/to/specs/**/*_spec.rb"
|
26
|
+
```
|
27
|
+
|
28
|
+
2. Run the workflow with:
|
29
|
+
```
|
30
|
+
roast execute examples/rspec_to_minitest/workflow.yml
|
31
|
+
```
|
32
|
+
|
33
|
+
3. Review the generated Minitest files and ensure they're correctly placed in your test directory.
|
34
|
+
|
35
|
+
## Implementation Details
|
36
|
+
|
37
|
+
The workflow leverages the following tools:
|
38
|
+
|
39
|
+
- Standard file operations (read/write)
|
40
|
+
- Code search capabilities to find related files
|
41
|
+
- Command execution to run tests
|
42
|
+
- CodingAgent for iterative improvements using AI-powered coding assistance
|
43
|
+
|
44
|
+
## Required Tool: CodingAgent
|
45
|
+
|
46
|
+
This workflow introduces a new tool called `CodingAgent` which leverages Claude Code to perform code-related tasks:
|
47
|
+
|
48
|
+
1. Running tests
|
49
|
+
2. Analyzing errors and failures
|
50
|
+
3. Making iterative improvements to code
|
51
|
+
|
52
|
+
The CodingAgent tool is implemented in `lib/roast/tools/coding_agent.rb`.
|
53
|
+
|
54
|
+
## Conversion Mappings
|
55
|
+
|
56
|
+
The workflow handles these common RSpec to Minitest conversions:
|
57
|
+
|
58
|
+
| RSpec Feature | Minitest Equivalent |
|
59
|
+
|---------------|---------------------|
|
60
|
+
| `describe/context` | Test class |
|
61
|
+
| `it` blocks | `test_*` methods |
|
62
|
+
| `before/after` | `setup/teardown` methods |
|
63
|
+
| `let/let!` | Instance variables or helper methods |
|
64
|
+
| `expect(x).to eq(y)` | `assert_equal y, x` |
|
65
|
+
| `expect(x).to be_truthy` | `assert x` |
|
66
|
+
| `expect(x).to be_falsey` | `refute x` |
|
67
|
+
| `expect { ... }.to raise_error` | `assert_raises { ... }` |
|
68
|
+
| Mocks/doubles | Minitest mocking or Mocha |
|
@@ -0,0 +1,30 @@
|
|
1
|
+
In this first step, try to understand the purpose and dependencies of the spec we will be migrating.
|
2
|
+
|
3
|
+
1. Read the provided RSpec file carefully to understand:
|
4
|
+
- The purpose of the test suite
|
5
|
+
- The subject under test (SUT)
|
6
|
+
- Test structure and organization
|
7
|
+
- Dependencies and fixtures used
|
8
|
+
- Mocks, stubs, and doubles
|
9
|
+
|
10
|
+
2. Use your tools to search for the SUT implementation and any other important dependent files so that they will be in the context for future steps in this process.
|
11
|
+
- Dependencies include fixtures
|
12
|
+
- Note that test/fixtures already has quite a bit of fixture files present
|
13
|
+
- If any fixtures are missing, copy them over when you write the new test file later
|
14
|
+
|
15
|
+
3. Identify RSpec-specific features being used, such as:
|
16
|
+
- describe/context blocks
|
17
|
+
- before/after hooks
|
18
|
+
- let and let! declarations
|
19
|
+
- expect(...).to syntax and matchers
|
20
|
+
- shared examples/contexts
|
21
|
+
- metadata and tags
|
22
|
+
|
23
|
+
4. Provide a summary of your analysis, including:
|
24
|
+
- Purpose of the test suite
|
25
|
+
- Main subject under test
|
26
|
+
- Key dependencies
|
27
|
+
- Testing patterns used
|
28
|
+
- Any potentially challenging aspects for Minitest conversion
|
29
|
+
|
30
|
+
This analysis will guide the next steps of creating an equivalent Minitest implementation.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
You are a Ruby testing expert assisting with migrating RSpec tests to Minitest.
|
2
|
+
|
3
|
+
In this step, you'll create a new Minitest file that replicates the functionality of the analyzed RSpec test.
|
4
|
+
|
5
|
+
## Your tasks:
|
6
|
+
|
7
|
+
1. Convert the RSpec test to an equivalent Minitest test, following these guidelines:
|
8
|
+
- Replace RSpec's `describe`/`context` blocks with Minitest test classes
|
9
|
+
- Convert `it` blocks to Minitest test methods (prefixed with `test_`)
|
10
|
+
- Transform `before`/`after` hooks to `setup`/`teardown` methods
|
11
|
+
- Replace `let`/`let!` declarations with instance variables or helper methods
|
12
|
+
- Convert `expect(...).to` assertions to Minitest assertions
|
13
|
+
- Replace RSpec matchers with equivalent Minitest assertions
|
14
|
+
- Handle mocks and stubs using Minitest's mocking capabilities and Mocha
|
15
|
+
|
16
|
+
2. Follow Minitest conventions:
|
17
|
+
- Name the file with `_test.rb` suffix instead of `_spec.rb`
|
18
|
+
- Create a class that inherits from `ActiveSupport::TestCase`
|
19
|
+
- Use that class's `test "description of the test` method to declare tests kind of like RSpec does
|
20
|
+
- Use Minitest's assertion methods (`assert`, `assert_equal`, etc.)
|
21
|
+
- Implement proper setup and teardown methods as needed
|
22
|
+
|
23
|
+
3. Pay attention to:
|
24
|
+
- Maintaining test coverage with equivalent assertions
|
25
|
+
- Preserving the original test's intent and behavior
|
26
|
+
- Handling RSpec-specific features appropriately
|
27
|
+
- Adding necessary require statements for Minitest and dependencies
|
28
|
+
|
29
|
+
4. Write the complete Minitest file and save it to the appropriate location, replacing `_spec.rb` with `_test.rb` in the filename.
|
30
|
+
|
31
|
+
Your converted Minitest file should maintain at least the same test coverage and intent as the original RSpec test while following Minitest's conventions and patterns.
|
32
|
+
|
33
|
+
IMPORTANT: If you see opportunities to improve the test coverage in the newly written tests, you have my permission to do so. However, note that we should focus on testing behavior, not implementation. Do not test private methods, and never use Ruby tricks to make private methods public. Try to avoid mocking or stubbing anything on the SUT class.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
You are a Ruby testing expert assisting with migrating RSpec tests to Minitest.
|
2
|
+
|
3
|
+
In this final step, you'll use the `cmd` and `coding_agent` tool functions to run the newly created Minitest file and iteratively improve it until all tests pass correctly.
|
4
|
+
|
5
|
+
## Your tasks:
|
6
|
+
|
7
|
+
1. Run the converted Minitest file to see if it passes all tests. Use the `cmd` tool function to execute the test using the following syntax:
|
8
|
+
```ruby
|
9
|
+
ruby -Ilib:test path/to/minitest_file.rb
|
10
|
+
```
|
11
|
+
|
12
|
+
2. Analyze any failures or errors that occur during test execution:
|
13
|
+
- Syntax errors
|
14
|
+
- Missing dependencies
|
15
|
+
- Assertion failures
|
16
|
+
- Test setup/teardown issues
|
17
|
+
- Unexpected behavior
|
18
|
+
|
19
|
+
3. For each issue identified, prompt the `coding_agent` tool to make necessary improvements:
|
20
|
+
- Fix syntax errors
|
21
|
+
- Correct assertion formats
|
22
|
+
- Add missing dependencies
|
23
|
+
- Adjust test setup or teardown
|
24
|
+
- Modify assertions to match expected behavior
|
25
|
+
|
26
|
+
4. Run the test again after each set of improvements.
|
27
|
+
|
28
|
+
5. Continue this iterative process until all tests pass successfully.
|
29
|
+
|
30
|
+
6. Once all tests pass, provide a summary of:
|
31
|
+
- Changes made to fix issues
|
32
|
+
- Any remaining considerations or edge cases
|
33
|
+
- Confirmation of test coverage compared to original RSpec tests
|
34
|
+
|
35
|
+
Again, your goal is to ensure the Minitest version provides at least the same test coverage and reliability as the original RSpec tests, while following Minitest best practices and conventions.
|
@@ -0,0 +1,10 @@
|
|
1
|
+
You are a Ruby testing expert assisting with migrating RSpec tests to Minitest. Note that all of your responses should be in nicely formatted markdown.
|
2
|
+
|
3
|
+
Here is the spec we're going to be migrating today:
|
4
|
+
|
5
|
+
```
|
6
|
+
# <%= file %>
|
7
|
+
<%= File.read(file) %>
|
8
|
+
```
|
9
|
+
|
10
|
+
I have given you powerful tool functions to do this work. The coding_agent is especially powerful and you should delegate complex tasks to it whenever possible.
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# RSpec to Minitest Migration Workflow
|
2
|
+
|
3
|
+
# This workflow demonstrates how to convert RSpec test files to Minitest:
|
4
|
+
# 1. Analyzes the RSpec test to understand its purpose and dependencies
|
5
|
+
# 2. Creates a new Minitest file with equivalent assertions and test structure
|
6
|
+
# 3. Runs the tests and makes improvements until they pass
|
7
|
+
|
8
|
+
name: RSpec to Minitest Migration
|
9
|
+
model: gpt-4.1
|
10
|
+
|
11
|
+
# Target expects a glob pattern matching RSpec files to be converted
|
12
|
+
# target: "**/*_spec.rb"
|
13
|
+
|
14
|
+
tools:
|
15
|
+
- Roast::Tools::ReadFile
|
16
|
+
- Roast::Tools::WriteFile
|
17
|
+
- Roast::Tools::Grep
|
18
|
+
- Roast::Tools::Cmd
|
19
|
+
- Roast::Tools::CodingAgent
|
20
|
+
|
21
|
+
steps:
|
22
|
+
- analyze_spec
|
23
|
+
- create_minitest
|
24
|
+
- run_and_improve
|
25
|
+
- rubocop: $(bundle exec rubocop -A)
|
26
|
+
- Summarize the changes made to the codebase.
|
27
|
+
|
28
|
+
# Configure the steps
|
29
|
+
analyze_spec:
|
30
|
+
print_response: false
|
31
|
+
|
32
|
+
create_minitest:
|
33
|
+
print_response: true
|
34
|
+
|
35
|
+
run_and_improve:
|
36
|
+
print_response: true
|
37
|
+
|
38
|
+
functions:
|
39
|
+
grep:
|
40
|
+
cache: true
|