From b380c800699b1237cea27e4b7380203dfebd0b94 Mon Sep 17 00:00:00 2001 From: george kosmopoulos Date: Thu, 13 Aug 2026 11:44:16 +0200 Subject: [PATCH] fix: migration class names must match the RcrewAI acronym inflection The engine registers an RcrewAI acronym so Zeitwerk resolves rcrewai/rails/span.rb to RcrewAI::Rails::Span. That same inflection makes Rails camelize create_rcrewai_tables to CreateRcrewAITables, but the migrations declared CreateRcrewaiTables. Rails resolves a migration class from its filename, so rails db:migrate raised NameError on a verbatim install of 0.7.0. Nine db/migrate files and the install template were affected. The prior generator spec hardcoded the wrong name and so asserted the bug was correct; it now derives the expected name the way Rails does, and a new spec runs the generated migration through ActiveRecord::MigrationContext - the resolution step a hand-rolled load-and-call skips. --- CHANGELOG.md | 19 ++++++++- .../002_add_config_to_rcrewai_agents.rb | 2 +- ..._add_output_processing_to_rcrewai_tasks.rb | 2 +- .../004_add_lifecycle_to_rcrewai_crews.rb | 2 +- .../005_add_batch_id_to_rcrewai_executions.rb | 2 +- .../006_create_rcrewai_knowledge_sources.rb | 2 +- db/migrate/007_create_rcrewai_flows.rb | 2 +- ...8_add_consensus_agents_to_rcrewai_crews.rb | 2 +- db/migrate/010_create_rcrewai_spans.rb | 2 +- ...servation_rollups_to_rcrewai_executions.rb | 2 +- .../templates/create_rcrewai_tables.rb | 2 +- lib/rcrewai/rails/version.rb | 2 +- spec/generators/install_generator_spec.rb | 22 ++++++++-- spec/migrations_spec.rb | 41 +++++++++++++++++++ spec/real_migrate_spec.rb | 40 ++++++++++++++++++ 15 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 spec/migrations_spec.rb create mode 100644 spec/real_migrate_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e927be..8f8fdff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.7.1] - 2026-08-13 + +### Fixed +- **`rails db:migrate` raised `NameError` on 0.7.0.** The engine registers an + `RcrewAI` acronym inflection (Zeitwerk needs it to resolve + `rcrewai/rails/span.rb` to `RcrewAI::Rails::Span`), so Rails camelizes + `create_rcrewai_tables` to `CreateRcrewAITables` — but the shipped migrations + declared `CreateRcrewaiTables`. Rails resolves a migration's class from its + filename, so following the documented install verbatim failed. Nine migrations + in `db/migrate` and the install template were affected; all now declare the + name Rails derives. Thanks to the user who reported this from a real install. + + **If you already installed 0.7.0** and renamed the class by hand, no action is + needed — your migration works. Otherwise, delete the copied migration, upgrade, + and re-run the install (or `rcrew_ai_rails:install:migrations`). + ## [0.7.0] - 2026-08-13 ### Added @@ -162,7 +178,8 @@ existing agents, tasks, and crews build unchanged. ### Changed - Rename generators from `rcrew_a_i` to `rcrewai` namespacing. -[Unreleased]: https://github.com/gkosmo/rcrewai-rails/compare/v0.7.0...HEAD +[Unreleased]: https://github.com/gkosmo/rcrewai-rails/compare/v0.7.1...HEAD +[0.7.1]: https://github.com/gkosmo/rcrewai-rails/compare/v0.7.0...v0.7.1 [0.7.0]: https://github.com/gkosmo/rcrewai-rails/compare/v0.6.1...v0.7.0 [0.6.1]: https://github.com/gkosmo/rcrewai-rails/compare/v0.6.0...v0.6.1 [0.6.0]: https://github.com/gkosmo/rcrewai-rails/compare/v0.5.1...v0.6.0 diff --git a/db/migrate/002_add_config_to_rcrewai_agents.rb b/db/migrate/002_add_config_to_rcrewai_agents.rb index a0a4c7f..c1b4346 100644 --- a/db/migrate/002_add_config_to_rcrewai_agents.rb +++ b/db/migrate/002_add_config_to_rcrewai_agents.rb @@ -1,4 +1,4 @@ -class AddConfigToRcrewaiAgents < ActiveRecord::Migration[7.0] +class AddConfigToRcrewAIAgents < ActiveRecord::Migration[7.0] def change # max_rpm and llm_config already exist on rcrewai_agents from the # original create table; only the 0.5.0 additions are new. diff --git a/db/migrate/003_add_output_processing_to_rcrewai_tasks.rb b/db/migrate/003_add_output_processing_to_rcrewai_tasks.rb index ae83def..f52e0c5 100644 --- a/db/migrate/003_add_output_processing_to_rcrewai_tasks.rb +++ b/db/migrate/003_add_output_processing_to_rcrewai_tasks.rb @@ -1,4 +1,4 @@ -class AddOutputProcessingToRcrewaiTasks < ActiveRecord::Migration[7.0] +class AddOutputProcessingToRcrewAITasks < ActiveRecord::Migration[7.0] def change # output_file already exists on rcrewai_tasks from the original create # table; only the new 0.4/0.5 output-processing options are added here. diff --git a/db/migrate/004_add_lifecycle_to_rcrewai_crews.rb b/db/migrate/004_add_lifecycle_to_rcrewai_crews.rb index 0c81c3d..333bb15 100644 --- a/db/migrate/004_add_lifecycle_to_rcrewai_crews.rb +++ b/db/migrate/004_add_lifecycle_to_rcrewai_crews.rb @@ -1,4 +1,4 @@ -class AddLifecycleToRcrewaiCrews < ActiveRecord::Migration[7.0] +class AddLifecycleToRcrewAICrews < ActiveRecord::Migration[7.0] def change add_column :rcrewai_crews, :planning, :boolean, default: false, null: false add_column :rcrewai_crews, :planning_llm, :string diff --git a/db/migrate/005_add_batch_id_to_rcrewai_executions.rb b/db/migrate/005_add_batch_id_to_rcrewai_executions.rb index 3292cd6..df9a268 100644 --- a/db/migrate/005_add_batch_id_to_rcrewai_executions.rb +++ b/db/migrate/005_add_batch_id_to_rcrewai_executions.rb @@ -1,4 +1,4 @@ -class AddBatchIdToRcrewaiExecutions < ActiveRecord::Migration[7.0] +class AddBatchIdToRcrewAIExecutions < ActiveRecord::Migration[7.0] def change add_column :rcrewai_executions, :batch_id, :string add_index :rcrewai_executions, :batch_id diff --git a/db/migrate/006_create_rcrewai_knowledge_sources.rb b/db/migrate/006_create_rcrewai_knowledge_sources.rb index 832ae3a..69f2be7 100644 --- a/db/migrate/006_create_rcrewai_knowledge_sources.rb +++ b/db/migrate/006_create_rcrewai_knowledge_sources.rb @@ -1,4 +1,4 @@ -class CreateRcrewaiKnowledgeSources < ActiveRecord::Migration[7.0] +class CreateRcrewAIKnowledgeSources < ActiveRecord::Migration[7.0] def change create_table :rcrewai_knowledge_sources do |t| t.references :owner, polymorphic: true, null: false diff --git a/db/migrate/007_create_rcrewai_flows.rb b/db/migrate/007_create_rcrewai_flows.rb index 6288f3c..dda2cce 100644 --- a/db/migrate/007_create_rcrewai_flows.rb +++ b/db/migrate/007_create_rcrewai_flows.rb @@ -1,4 +1,4 @@ -class CreateRcrewaiFlows < ActiveRecord::Migration[7.0] +class CreateRcrewAIFlows < ActiveRecord::Migration[7.0] def change create_table :rcrewai_flow_states do |t| t.string :state_id, null: false diff --git a/db/migrate/008_add_consensus_agents_to_rcrewai_crews.rb b/db/migrate/008_add_consensus_agents_to_rcrewai_crews.rb index 7560206..4a86ca8 100644 --- a/db/migrate/008_add_consensus_agents_to_rcrewai_crews.rb +++ b/db/migrate/008_add_consensus_agents_to_rcrewai_crews.rb @@ -1,4 +1,4 @@ -class AddConsensusAgentsToRcrewaiCrews < ActiveRecord::Migration[7.0] +class AddConsensusAgentsToRcrewAICrews < ActiveRecord::Migration[7.0] def change add_column :rcrewai_crews, :consensus_agents, :integer end diff --git a/db/migrate/010_create_rcrewai_spans.rb b/db/migrate/010_create_rcrewai_spans.rb index 9fc6112..365058a 100644 --- a/db/migrate/010_create_rcrewai_spans.rb +++ b/db/migrate/010_create_rcrewai_spans.rb @@ -1,4 +1,4 @@ -class CreateRcrewaiSpans < ActiveRecord::Migration[7.0] +class CreateRcrewAISpans < ActiveRecord::Migration[7.0] def change create_table :rcrewai_spans do |t| t.references :execution, null: false, foreign_key: { to_table: :rcrewai_executions } diff --git a/db/migrate/011_add_observation_rollups_to_rcrewai_executions.rb b/db/migrate/011_add_observation_rollups_to_rcrewai_executions.rb index f4b08b2..12c4b84 100644 --- a/db/migrate/011_add_observation_rollups_to_rcrewai_executions.rb +++ b/db/migrate/011_add_observation_rollups_to_rcrewai_executions.rb @@ -1,4 +1,4 @@ -class AddObservationRollupsToRcrewaiExecutions < ActiveRecord::Migration[7.0] +class AddObservationRollupsToRcrewAIExecutions < ActiveRecord::Migration[7.0] def change add_column :rcrewai_executions, :total_cost_usd, :decimal, precision: 12, scale: 6 add_column :rcrewai_executions, :total_tokens, :integer diff --git a/lib/generators/rcrewai/rails/install/templates/create_rcrewai_tables.rb b/lib/generators/rcrewai/rails/install/templates/create_rcrewai_tables.rb index 49d9de6..204bf02 100644 --- a/lib/generators/rcrewai/rails/install/templates/create_rcrewai_tables.rb +++ b/lib/generators/rcrewai/rails/install/templates/create_rcrewai_tables.rb @@ -1,4 +1,4 @@ -class CreateRcrewaiTables < ActiveRecord::Migration[7.0] +class CreateRcrewAITables < ActiveRecord::Migration[7.0] def change create_table :rcrewai_crews do |t| t.string :name, null: false diff --git a/lib/rcrewai/rails/version.rb b/lib/rcrewai/rails/version.rb index 5c03f22..fcac49f 100644 --- a/lib/rcrewai/rails/version.rb +++ b/lib/rcrewai/rails/version.rb @@ -1,6 +1,6 @@ module RcrewAI module Rails - VERSION = "0.7.0" + VERSION = "0.7.1" Version = VERSION end end \ No newline at end of file diff --git a/spec/generators/install_generator_spec.rb b/spec/generators/install_generator_spec.rb index ff2f10c..ec2de05 100644 --- a/spec/generators/install_generator_spec.rb +++ b/spec/generators/install_generator_spec.rb @@ -54,6 +54,23 @@ def migration_paths end describe "the migration" do + # The engine registers an `RcrewAI` acronym inflection, so Rails camelizes + # `create_rcrewai_tables` to `CreateRcrewAITables`, not `CreateRcrewaiTables`. + # Rails resolves a migration's class from its filename, so a template whose + # class name disagrees raises NameError on `rails db:migrate` — which is + # exactly what shipped in 0.7.0. Assert the name Rails will actually look + # for, derived the same way Rails derives it, rather than hardcoding it. + it "declares the class name Rails derives from the filename" do + run_generator + filename = File.basename(migration_paths.first, ".rb").sub(/\A\d+_/, "") + expected = ActiveSupport::Inflector.camelize(filename) + declared = File.read(migration_paths.first)[/class (\w+)/, 1] + + expect(declared).to eq(expected), + "Rails will look for #{expected} but the migration declares #{declared}; " \ + "`rails db:migrate` would raise NameError" + end + it "creates a timestamped migration" do run_generator expect(migration_paths.size).to eq(1) @@ -81,10 +98,9 @@ def migration_paths end end - it "defines a class name matching the Rails migration filename convention" do + it "subclasses ActiveRecord::Migration" do run_generator - content = File.read(migration_paths.first) - expect(content).to include("class CreateRcrewaiTables < ActiveRecord::Migration") + expect(File.read(migration_paths.first)).to match(/class \w+ < ActiveRecord::Migration/) end it "is valid Ruby" do diff --git a/spec/migrations_spec.rb b/spec/migrations_spec.rb new file mode 100644 index 0000000..4f472f1 --- /dev/null +++ b/spec/migrations_spec.rb @@ -0,0 +1,41 @@ +require "rails_helper" + +# Rails resolves a migration's class from its filename via the active +# inflections. This engine registers an `RcrewAI` acronym (which Zeitwerk needs +# to map rcrewai/rails/span.rb -> RcrewAI::Rails::Span), so any migration named +# *_rcrewai_* camelizes to `RcrewAI`, not `Rcrewai`. +# +# 0.7.0 shipped 9 migrations plus the install template whose class names +# disagreed with that, so `rails db:migrate` raised NameError on a verbatim +# install. These are the migrations host apps run, so a mismatch is only +# reachable in someone else's app — hence this spec. +RSpec.describe "migration class names" do + def declared_class_name(path) + File.read(path)[/class (\w+)/, 1] + end + + def expected_class_name(path) + ActiveSupport::Inflector.camelize( + File.basename(path, ".rb").sub(/\A\d+_/, "") + ) + end + + engine_root = File.expand_path("..", __dir__) + migrations = Dir[File.join(engine_root, "db/migrate/*.rb")].sort + template = File.join( + engine_root, + "lib/generators/rcrewai/rails/install/templates/create_rcrewai_tables.rb" + ) + + it "finds migrations to check" do + expect(migrations).not_to be_empty + end + + (migrations + [template]).each do |path| + it "#{File.basename(path)} declares the class Rails derives from its filename" do + expect(declared_class_name(path)).to eq(expected_class_name(path)), + "Rails will look for #{expected_class_name(path)} but the file " \ + "declares #{declared_class_name(path)}; `rails db:migrate` raises NameError" + end + end +end diff --git a/spec/real_migrate_spec.rb b/spec/real_migrate_spec.rb new file mode 100644 index 0000000..4d81f5b --- /dev/null +++ b/spec/real_migrate_spec.rb @@ -0,0 +1,40 @@ +require "rails_helper" +require "rails/generators" +require "generators/rcrewai/rails/install/install_generator" +require "fileutils" + +RSpec.describe "running the generated migration the way Rails does" do + let(:destination) { File.expand_path("../tmp/real_migrate", __dir__) } + + before do + FileUtils.rm_rf(destination) + FileUtils.mkdir_p(File.join(destination, "config")) + File.write(File.join(destination, "config", "routes.rb"), "Rails.application.routes.draw do\nend\n") + original = $stdout + $stdout = StringIO.new + begin + RcrewAI::Rails::Generators::InstallGenerator.start([], destination_root: destination) + ensure + $stdout = original + end + end + + after { FileUtils.rm_rf(destination) } + + it "migrates without raising NameError" do + migration_dir = File.join(destination, "db/migrate") + + conn = ActiveRecord::Base.establish_connection( + adapter: "sqlite3", database: ":memory:" + ).lease_connection + + # MigrationContext resolves each migration's class from its filename via the + # active inflections — the exact step a hand-rolled `load` + constant call + # skips, and the step that raised NameError in 0.7.0. + context = ActiveRecord::MigrationContext.new(migration_dir) + expect { context.migrate }.not_to raise_error + + tables = conn.tables.grep(/rcrewai/) + expect(tables).to include("rcrewai_crews", "rcrewai_spans", "rcrewai_span_events", "rcrewai_tools") + end +end