From 2b4c2d4bbb47b59fb8bf7fec027efd36b1352857 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 14 Mar 2026 10:50:58 +0200 Subject: Validate agent configs during dry runs --- AGENTS.md | 2 +- lib/dslkeywords/file.rb | 18 +++++++++++------- test/lib/dslkeywords/agent_test.rb | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index df19b63..fac7f0f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -67,7 +67,7 @@ rake wireguard -- --dry # dry run mode - **Lookup by object id**: Resolve named DSL objects with `RCM::DSL#object!` and `Keyword.id_for(...)`. Duplicate detection and lookup are id-based, not hash-based by ad hoc names. - **Keep normalization in the keyword class**: If a DSL keyword accepts names, normalize them in the keyword class itself so registration and lookup use the same representation. Agent and prompt names may contain spaces. - **Keep RuboCop clean on touched files**: Run RuboCop on edited files and keep disables narrow, justified, and local. Remove stale disable directives when they are no longer needed. -- **Run tests after behavior changes**: At minimum run `rake test`. If you change examples, execute the relevant example commands from their own directories so relative paths behave as documented. +- **Run tests and examples before committing**: Before committing any change, make sure the full unit test suite passes and all documented examples still work from their own directories so relative paths behave as documented. - **Prefer documented execution paths**: Validate examples with the commands shown in the example README or Justfile unless you are explicitly fixing the docs themselves. ### Testing diff --git a/lib/dslkeywords/file.rb b/lib/dslkeywords/file.rb index 81f5f29..08f8f48 100644 --- a/lib/dslkeywords/file.rb +++ b/lib/dslkeywords/file.rb @@ -221,13 +221,15 @@ module RCM def evaluate_agent_processing! raise MissingAgentInput, "File #{@file_path} does not exist for agent processing" unless ::File.file?(@file_path) + agent_definition, prompt_definition = agent_configuration! + if option :dry info "Processing #{@file_path} with agent #{@agent_name} and prompt #{@prompt_name} - dry run!" return end input = ::File.read(@file_path) - output = run_agent!(input) + output = run_agent!(input, agent_definition, prompt_definition) create_parent_directory! unless ::File.directory?(::File.dirname(@file_path)) write!(output) end @@ -261,12 +263,7 @@ module RCM # rubocop:enable Metrics/MethodLength # rubocop:disable Metrics/AbcSize, Metrics/MethodLength - def run_agent!(input) - agent_definition = dsl.object!(AgentDefinition, @agent_name, - error_class: DSL::NoSuchAgentDefinition, kind: 'agent') - prompt_definition = dsl.object!(PromptDefinition, @prompt_name, - error_class: DSL::NoSuchPromptDefinition, kind: 'prompt') - + def run_agent!(input, agent_definition, prompt_definition) Tempfile.create(['rcm-agent-input', '.txt']) do |tmp| tmp.write(input) tmp.flush @@ -285,6 +282,13 @@ module RCM end # rubocop:enable Metrics/AbcSize, Metrics/MethodLength + def agent_configuration! + [ + dsl.object!(AgentDefinition, @agent_name, error_class: DSL::NoSuchAgentDefinition, kind: 'agent'), + dsl.object!(PromptDefinition, @prompt_name, error_class: DSL::NoSuchPromptDefinition, kind: 'prompt') + ] + end + def render_agent_command(template, prompt_text, input_path) command = template.dup command.gsub!(/\bINPUT\b/, Shellwords.escape(input_path)) diff --git a/test/lib/dslkeywords/agent_test.rb b/test/lib/dslkeywords/agent_test.rb index 5eb5bc5..d3fc49f 100644 --- a/test/lib/dslkeywords/agent_test.rb +++ b/test/lib/dslkeywords/agent_test.rb @@ -283,6 +283,43 @@ class RCMAgentTest < Minitest::Test assert_equal 'keep me', File.read(file_path) end + def test_dry_run_unknown_agent_raises + file_path = path('dry-run-unknown-agent.txt') + File.write(file_path, 'keep me') + ARGV.replace(['--dry']) + + assert_raises(RCM::DSL::NoSuchAgentDefinition) do + configure_from_scratch do + prompt 'no op' do + '' + end + + file file_path do + agent 'missing agent', 'no op' + end + end + end + end + + def test_dry_run_unknown_prompt_raises + file_path = path('dry-run-unknown-prompt.txt') + command = mock_agent_command(:pass_through) + File.write(file_path, 'keep me') + ARGV.replace(['--dry']) + + assert_raises(RCM::DSL::NoSuchPromptDefinition) do + configure_from_scratch do + agent mock do + command + end + + file file_path do + agent mock, 'missing prompt' + end + end + end + end + def test_non_zero_exit_raises file_path = path('broken.txt') command = mock_agent_command(:fail, 'boom', '7') -- cgit v1.2.3