roast-ai 0.1.0 → 0.1.2

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/cla.yml +1 -1
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +28 -0
  5. data/CLAUDE.md +3 -1
  6. data/Gemfile +0 -1
  7. data/Gemfile.lock +3 -4
  8. data/README.md +419 -5
  9. data/Rakefile +1 -6
  10. data/docs/INSTRUMENTATION.md +202 -0
  11. data/examples/api_workflow/README.md +85 -0
  12. data/examples/api_workflow/fetch_api_data/prompt.md +10 -0
  13. data/examples/api_workflow/generate_report/prompt.md +10 -0
  14. data/examples/api_workflow/prompt.md +10 -0
  15. data/examples/api_workflow/transform_data/prompt.md +10 -0
  16. data/examples/api_workflow/workflow.yml +30 -0
  17. data/examples/grading/format_result.rb +25 -9
  18. data/examples/grading/js_test_runner +31 -0
  19. data/examples/grading/rb_test_runner +19 -0
  20. data/examples/grading/read_dependencies/prompt.md +14 -0
  21. data/examples/grading/run_coverage.rb +2 -2
  22. data/examples/grading/workflow.yml +3 -12
  23. data/examples/instrumentation.rb +76 -0
  24. data/examples/rspec_to_minitest/README.md +68 -0
  25. data/examples/rspec_to_minitest/analyze_spec/prompt.md +30 -0
  26. data/examples/rspec_to_minitest/create_minitest/prompt.md +33 -0
  27. data/examples/rspec_to_minitest/run_and_improve/prompt.md +35 -0
  28. data/examples/rspec_to_minitest/workflow.md +10 -0
  29. data/examples/rspec_to_minitest/workflow.yml +40 -0
  30. data/lib/roast/helpers/function_caching_interceptor.rb +72 -8
  31. data/lib/roast/helpers/prompt_loader.rb +2 -0
  32. data/lib/roast/resources/api_resource.rb +137 -0
  33. data/lib/roast/resources/base_resource.rb +47 -0
  34. data/lib/roast/resources/directory_resource.rb +40 -0
  35. data/lib/roast/resources/file_resource.rb +33 -0
  36. data/lib/roast/resources/none_resource.rb +29 -0
  37. data/lib/roast/resources/url_resource.rb +63 -0
  38. data/lib/roast/resources.rb +100 -0
  39. data/lib/roast/tools/coding_agent.rb +69 -0
  40. data/lib/roast/tools.rb +1 -0
  41. data/lib/roast/version.rb +1 -1
  42. data/lib/roast/workflow/base_step.rb +21 -17
  43. data/lib/roast/workflow/base_workflow.rb +69 -17
  44. data/lib/roast/workflow/configuration.rb +83 -8
  45. data/lib/roast/workflow/configuration_parser.rb +218 -3
  46. data/lib/roast/workflow/file_state_repository.rb +156 -0
  47. data/lib/roast/workflow/prompt_step.rb +16 -0
  48. data/lib/roast/workflow/session_manager.rb +82 -0
  49. data/lib/roast/workflow/state_repository.rb +21 -0
  50. data/lib/roast/workflow/workflow_executor.rb +99 -9
  51. data/lib/roast/workflow.rb +4 -0
  52. data/lib/roast.rb +2 -5
  53. data/roast.gemspec +1 -1
  54. data/schema/workflow.json +12 -0
  55. metadata +34 -6
  56. 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
@@ -16,7 +16,6 @@ class FormatResult < Roast::Workflow::BaseStep
16
16
  append_to_final_output(<<~OUTPUT)
17
17
  ========== TEST GRADE REPORT ==========
18
18
  Test file: #{workflow.file}
19
- Source file: #{workflow.subject_file}
20
19
  OUTPUT
21
20
 
22
21
  format_results
@@ -26,22 +25,39 @@ class FormatResult < Roast::Workflow::BaseStep
26
25
  private
27
26
 
28
27
  def format_results
29
- format_grade
28
+ # With HashWithIndifferentAccess, we can simply access with either syntax
29
+ grade_data = workflow.output["calculate_final_grade"]
30
+
31
+ unless grade_data
32
+ return append_to_final_output("Error: Grading data not available. This may be because you're replaying the workflow from this step, but the previous step data is missing or not found in the selected session.")
33
+ end
34
+
35
+ format_grade(grade_data)
36
+
37
+ # Make sure rubric_scores exists before trying to iterate over it
38
+ unless grade_data[:rubric_scores]
39
+ return append_to_final_output("Error: Rubric scores data not available in the workflow output.")
40
+ end
30
41
 
31
42
  append_to_final_output("RUBRIC SCORES:")
32
- workflow.output["calculate_final_grade"][:rubric_scores].each do |category, data|
33
- append_to_final_output(" #{RUBRIC[category][:description]} (#{(RUBRIC[category][:weight] * 100).round}% of grade):")
34
- append_to_final_output(" Value: #{data[:raw_value]}")
35
- append_to_final_output(" Score: #{(data[:score] * 10).round}/10 - \"#{data[:description]}\"")
43
+ grade_data[:rubric_scores].each do |category, data|
44
+ # Safely access RUBRIC with a fallback for potentially missing categories
45
+ rubric_item = RUBRIC[category.to_sym] || { description: "Unknown Category", weight: 0 }
46
+
47
+ append_to_final_output(" #{rubric_item[:description]} (#{(rubric_item[:weight] * 100).round}% of grade):")
48
+ append_to_final_output(" Value: #{data[:raw_value] || "N/A"}")
49
+ append_to_final_output(" Score: #{data[:score] ? (data[:score] * 10).round : "N/A"}/10 - \"#{data[:description] || "No description available"}\"")
36
50
  end
37
51
  end
38
52
 
39
- def format_grade
40
- letter_grade = workflow.output["calculate_final_grade"][:final_score][:letter_grade]
53
+ def format_grade(grade_data)
54
+ return append_to_final_output("\nError: Final grade data not available.") unless grade_data && grade_data[:final_score]
55
+
56
+ letter_grade = grade_data[:final_score][:letter_grade]
41
57
  celebration_emoji = letter_grade == "A" ? "🎉" : ""
42
58
  append_to_final_output(<<~OUTPUT)
43
59
  \nFINAL GRADE:
44
- Score: #{(workflow.output["calculate_final_grade"][:final_score][:weighted_score] * 100).round}/100
60
+ Score: #{(grade_data[:final_score][:weighted_score] * 100).round}/100
45
61
  Letter Grade: #{letter_grade} #{celebration_emoji}
46
62
  OUTPUT
47
63
  end
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ if ARGV.length != 2
5
+ puts "Usage: #{File.basename($PROGRAM_NAME)} SUBJECT_FILE TEST_FILE"
6
+ exit 1
7
+ end
8
+
9
+ subject_file, test_file = ARGV
10
+
11
+ def detect_package_manager
12
+ return "pnpm" if File.exist?(File.join(Dir.pwd, "pnpm-lock.yaml"))
13
+ return "yarn" if File.exist?(File.join(Dir.pwd, "yarn.lock"))
14
+
15
+ "npm"
16
+ end
17
+
18
+ jest_options = [
19
+ "--verbose",
20
+ "--no-colors",
21
+ "--ci",
22
+ "--coverageReporters=text-summary",
23
+ "--collectCoverageFrom=#{subject_file}",
24
+ ]
25
+
26
+ # Assumes the test command is `test:coverage`
27
+ # Both admin-web and checkout-web use this command
28
+ command = "#{detect_package_manager} run test:coverage -- #{test_file} #{jest_options.join(" ")}"
29
+
30
+ $stderr.puts "Running: #{command}"
31
+ puts system(command)
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "rubygems"
5
+ require "bundler/setup"
6
+
7
+ require_relative "../../lib/roast/helpers/minitest_coverage_runner"
8
+
9
+ # Suppress fancy minitest reporting
10
+ ENV["RM_INFO"] = "true"
11
+
12
+ if ARGV.length != 2
13
+ puts "Usage: #{File.basename($PROGRAM_NAME)} SUBJECT_FILE TEST_FILE"
14
+ exit 1
15
+ end
16
+
17
+ test_file, subject_file = ARGV
18
+
19
+ Roast::Helpers::MinitestCoverageRunner.new(test_file, subject_file).run
@@ -0,0 +1,14 @@
1
+ Use the provided functions to find and read important dependencies of the provided test file named <%= workflow.file %>.
2
+
3
+ The first dependency you should always look for is the source file for the prime subject of the test (whatever class this test file is claiming to test). Use `read_file` to read the subject's source code into your conversation transcript, but only if it's not already there from a previous chat.
4
+
5
+ If you can identify other important application-level dependencies then read them too.
6
+ How many extra dependencies to research is left to your discretion, but ALWAYS make sure you have the subject under test (SUT) in your context before responding.
7
+
8
+ Once you are finished using tool functions, respond with the relative path to the source file of the SUT inside <sut> tags.
9
+
10
+ Example:
11
+
12
+ If you are told to find the dependencies of `test/services/country_db_interface_test.rb`,
13
+ then you would use the functions as explained above and ultimately respond with `<sut>./app/services/country_db_interface.rb</sut>`
14
+
@@ -20,7 +20,7 @@ class RunCoverage < Roast::Workflow::BaseStep
20
20
  extension = "js" if ["js", "jsx", "ts", "tsx"].include?(extension)
21
21
 
22
22
  # Get the absolute path to the test_runner executable
23
- test_runner_path = File.expand_path("../../bin/#{extension}_test_runner", __dir__)
23
+ test_runner_path = File.expand_path("#{extension}_test_runner", __dir__)
24
24
 
25
25
  # Make sure the test_runner executable exists
26
26
  unless File.exist?(test_runner_path)
@@ -33,7 +33,7 @@ class RunCoverage < Roast::Workflow::BaseStep
33
33
  resolved_test_file = Roast::Helpers::PathResolver.resolve(test_file)
34
34
 
35
35
  # Run the test_runner using shadowenv for environment consistency
36
- command = "shadowenv exec --dir . -- #{test_runner_path} #{resolved_subject_file} #{resolved_test_file}"
36
+ command = "shadowenv exec --dir . -- #{test_runner_path} #{resolved_test_file} #{resolved_subject_file}"
37
37
  output, status = Open3.capture2(command)
38
38
 
39
39
  unless status.success?
@@ -5,7 +5,7 @@ tools:
5
5
  - Roast::Tools::ReadFile
6
6
  - Roast::Tools::SearchFile
7
7
 
8
- each: '% cd $(git rev-parse --show-toplevel) && git status --porcelain | grep "_test\.rb" | cut -c4- | xargs realpath'
8
+ # each: '% cd $(git rev-parse --show-toplevel) && git status --porcelain | grep "_test\.rb" | cut -c4- | xargs realpath'
9
9
 
10
10
  steps:
11
11
  - read_dependencies
@@ -18,12 +18,11 @@ steps:
18
18
  - calculate_final_grade
19
19
  - format_result
20
20
  - generate_recommendations
21
- - annotate_pr_with_comments
22
21
 
23
22
  # set non-default attributes for steps below
24
23
  analyze_coverage:
25
24
  model: gpt-4.1-mini
26
- loop: false
25
+ auto_loop: false
27
26
  json: true
28
27
 
29
28
  generate_grades:
@@ -32,15 +31,7 @@ generate_grades:
32
31
 
33
32
  generate_recommendations:
34
33
  model: o3
35
- loop: false
34
+ auto_loop: false
36
35
  json: true
37
36
  params:
38
37
  max_completion_tokens: 5_000
39
-
40
- annotate_pr_with_comments:
41
- tools:
42
- - Roast::Tools::Github::Annotator
43
- model: o3
44
- params:
45
- max_completion_tokens: 5_000
46
- if: "workflow.pr? && output.recommendations.any?"
@@ -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