From 75270f9e9ccfef4d2c23526ea1e51154f6a937d8 Mon Sep 17 00:00:00 2001 From: Minerva Sky <258328972+minerva-sky@users.noreply.github.com> Date: Mon, 19 Oct 2026 01:40:05 -0400 Subject: [PATCH] refactor: load lib/agentic through Zeitwerk alone lib/agentic.rb makes Zeitwerk the single code loader and says files must not require_relative their siblings. 57 lines in 26 files did it anyway, all reintroduced by the #7 merge two months after 11c9407 took the count to zero, because nothing tested the promise behind the rule. Delete all 57 (every target is a loader-managed file under lib/agentic/) plus the inline require_relative in VerificationHub#add_strategy_from_factory, which did a path lookup on every call. Add spec/zeitwerk_spec.rb: a fresh process eager-loads the gem with force: true (so lib/agentic/cli is walked too) and resolves Agentic::CLI, and a second example fails on any require_relative that comes back under lib/agentic/. Closes #57 Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 1 + lib/agentic/artifact_generator.rb | 5 --- .../file_generation_capability.rb | 2 - .../capabilities/register_file_generation.rb | 5 --- lib/agentic/cli/execution_observer.rb | 2 - .../cli/human_intervention_commands.rb | 1 - .../streaming/enhanced_progress_tracker.rb | 2 - .../cli/streaming/multi_zone_display.rb | 2 - lib/agentic/configuration.rb | 5 --- lib/agentic/configuration/builder.rb | 2 - lib/agentic/configuration/schema_registry.rb | 2 - lib/agentic/configuration/schemas.rb | 3 -- lib/agentic/human_intervention/portal.rb | 3 -- lib/agentic/observability/adapter_factory.rb | 4 -- lib/agentic/observability/console_adapter.rb | 2 - lib/agentic/observability/event_dispatcher.rb | 2 - lib/agentic/observability/file_adapter.rb | 3 -- lib/agentic/observability_engine.rb | 4 -- lib/agentic/performance.rb | 4 -- lib/agentic/performance/cache_manager.rb | 2 - lib/agentic/performance/optimizer.rb | 2 - lib/agentic/security.rb | 4 -- lib/agentic/security/secure_error_mixin.rb | 2 - lib/agentic/verification/strategy_factory.rb | 4 -- .../verification/verification_helpers.rb | 3 -- lib/agentic/verification/verification_hub.rb | 1 - lib/agentic/workspace.rb | 4 -- spec/zeitwerk_spec.rb | 43 +++++++++++++++++++ 28 files changed, 44 insertions(+), 75 deletions(-) create mode 100644 spec/zeitwerk_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index a314b10..f452989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## [Unreleased] ### Fixed +- Every file under `lib/agentic/` now loads through Zeitwerk alone: the 57 `require_relative` lines between loader-managed files are gone, and `spec/zeitwerk_spec.rb` eager-loads the gem in a fresh process and fails on any `require_relative` that comes back - Test suite loads on Ruby 4.0: `benchmark` is a bundled (not default) gem there, so it is now declared in the Gemfile - `EventDispatcher` slow-observer spec uses a monotonic clock and a looser bound so it no longer flakes on loaded hosts diff --git a/lib/agentic/artifact_generator.rb b/lib/agentic/artifact_generator.rb index 262f4f4..9c2cc8e 100644 --- a/lib/agentic/artifact_generator.rb +++ b/lib/agentic/artifact_generator.rb @@ -1,10 +1,5 @@ # frozen_string_literal: true -require_relative "artifact" -require_relative "artifact_generation_result" -require_relative "workspace" -require_relative "capabilities/file_generation_capability" - module Agentic # Coordinates artifact generation using LLM agents # diff --git a/lib/agentic/capabilities/file_generation_capability.rb b/lib/agentic/capabilities/file_generation_capability.rb index 09c06d0..ab4e92b 100644 --- a/lib/agentic/capabilities/file_generation_capability.rb +++ b/lib/agentic/capabilities/file_generation_capability.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "../artifact" -require_relative "../workspace" require "json" module Agentic diff --git a/lib/agentic/capabilities/register_file_generation.rb b/lib/agentic/capabilities/register_file_generation.rb index 41478dd..badc98d 100644 --- a/lib/agentic/capabilities/register_file_generation.rb +++ b/lib/agentic/capabilities/register_file_generation.rb @@ -1,10 +1,5 @@ # frozen_string_literal: true -require_relative "file_generation_capability" -require_relative "../capability_specification" -require_relative "../capability_provider" -require_relative "../agent_capability_registry" - module Agentic module Capabilities # Register the file_generation capability with the registry diff --git a/lib/agentic/cli/execution_observer.rb b/lib/agentic/cli/execution_observer.rb index 229b468..78850ff 100644 --- a/lib/agentic/cli/execution_observer.rb +++ b/lib/agentic/cli/execution_observer.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "progress_tracker" - module Agentic class CLI < Thor # Observer that provides real-time feedback during plan execution diff --git a/lib/agentic/cli/human_intervention_commands.rb b/lib/agentic/cli/human_intervention_commands.rb index 9c58dcf..0cc4aaa 100644 --- a/lib/agentic/cli/human_intervention_commands.rb +++ b/lib/agentic/cli/human_intervention_commands.rb @@ -3,7 +3,6 @@ require "thor" require "json" require "yaml" -require_relative "../human_intervention/portal" module Agentic class CLI diff --git a/lib/agentic/cli/streaming/enhanced_progress_tracker.rb b/lib/agentic/cli/streaming/enhanced_progress_tracker.rb index 6bc64b0..3d88a10 100644 --- a/lib/agentic/cli/streaming/enhanced_progress_tracker.rb +++ b/lib/agentic/cli/streaming/enhanced_progress_tracker.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "multi_zone_display" - module Agentic class CLI < Thor module Streaming diff --git a/lib/agentic/cli/streaming/multi_zone_display.rb b/lib/agentic/cli/streaming/multi_zone_display.rb index a1bbff2..0664837 100644 --- a/lib/agentic/cli/streaming/multi_zone_display.rb +++ b/lib/agentic/cli/streaming/multi_zone_display.rb @@ -3,8 +3,6 @@ require "tty-box" require "tty-cursor" require "tty-screen" -require_relative "streaming_zone" -require_relative "progress_zone" module Agentic class CLI < Thor diff --git a/lib/agentic/configuration.rb b/lib/agentic/configuration.rb index 3578cbb..2542cfd 100644 --- a/lib/agentic/configuration.rb +++ b/lib/agentic/configuration.rb @@ -1,10 +1,5 @@ # frozen_string_literal: true -require_relative "configuration/schema" -require_relative "configuration/schema_registry" -require_relative "configuration/schemas" -require_relative "configuration/builder" - module Agentic # Unified configuration system with schema validation and type checking # diff --git a/lib/agentic/configuration/builder.rb b/lib/agentic/configuration/builder.rb index fcab825..359aed0 100644 --- a/lib/agentic/configuration/builder.rb +++ b/lib/agentic/configuration/builder.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "schemas" - module Agentic module Configuration # Configuration builder with fluent interface and validation diff --git a/lib/agentic/configuration/schema_registry.rb b/lib/agentic/configuration/schema_registry.rb index 438424d..f474826 100644 --- a/lib/agentic/configuration/schema_registry.rb +++ b/lib/agentic/configuration/schema_registry.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "schema" - module Agentic module Configuration # Central registry for managing configuration schemas diff --git a/lib/agentic/configuration/schemas.rb b/lib/agentic/configuration/schemas.rb index bdf5f29..eab19ce 100644 --- a/lib/agentic/configuration/schemas.rb +++ b/lib/agentic/configuration/schemas.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -require_relative "schema" -require_relative "schema_registry" - module Agentic module Configuration # Pre-defined schemas for core Agentic configuration objects diff --git a/lib/agentic/human_intervention/portal.rb b/lib/agentic/human_intervention/portal.rb index ff320d9..b891494 100644 --- a/lib/agentic/human_intervention/portal.rb +++ b/lib/agentic/human_intervention/portal.rb @@ -3,9 +3,6 @@ require "json" require "securerandom" require "monitor" -require_relative "workflow_manager" -require_relative "monitoring_system" -require_relative "authentication_system" module Agentic module HumanIntervention diff --git a/lib/agentic/observability/adapter_factory.rb b/lib/agentic/observability/adapter_factory.rb index 36eef15..142c01b 100644 --- a/lib/agentic/observability/adapter_factory.rb +++ b/lib/agentic/observability/adapter_factory.rb @@ -1,9 +1,5 @@ # frozen_string_literal: true -require_relative "base_adapter" -require_relative "console_adapter" -require_relative "file_adapter" - module Agentic module Observability # Factory for creating observability adapters diff --git a/lib/agentic/observability/console_adapter.rb b/lib/agentic/observability/console_adapter.rb index fc8b7d5..30e2d46 100644 --- a/lib/agentic/observability/console_adapter.rb +++ b/lib/agentic/observability/console_adapter.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "base_adapter" - module Agentic module Observability # Console adapter for outputting events to STDOUT/STDERR diff --git a/lib/agentic/observability/event_dispatcher.rb b/lib/agentic/observability/event_dispatcher.rb index 4f921ba..1f745bb 100644 --- a/lib/agentic/observability/event_dispatcher.rb +++ b/lib/agentic/observability/event_dispatcher.rb @@ -2,8 +2,6 @@ require "async" require "async/queue" -require_relative "event_pipeline" -require_relative "event_context" module Agentic module Observability diff --git a/lib/agentic/observability/file_adapter.rb b/lib/agentic/observability/file_adapter.rb index 59cd942..060bae1 100644 --- a/lib/agentic/observability/file_adapter.rb +++ b/lib/agentic/observability/file_adapter.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -require_relative "base_adapter" -require_relative "file_observer" - module Agentic module Observability # File adapter for logging events to local files diff --git a/lib/agentic/observability_engine.rb b/lib/agentic/observability_engine.rb index 958f368..aa804b7 100644 --- a/lib/agentic/observability_engine.rb +++ b/lib/agentic/observability_engine.rb @@ -3,10 +3,6 @@ require "set" require "json" require "async" -require_relative "observability/event_data" -require_relative "observability/file_observer" -require_relative "observability/adapter_factory" -require_relative "observability/event_dispatcher" module Agentic # Unified observability engine for all event coordination diff --git a/lib/agentic/performance.rb b/lib/agentic/performance.rb index 526fc8a..962fc89 100644 --- a/lib/agentic/performance.rb +++ b/lib/agentic/performance.rb @@ -1,9 +1,5 @@ # frozen_string_literal: true -require_relative "performance/cache" -require_relative "performance/cache_manager" -require_relative "performance/optimizer" - module Agentic # Performance optimization framework for the Agentic system # diff --git a/lib/agentic/performance/cache_manager.rb b/lib/agentic/performance/cache_manager.rb index cbbff27..dab1d68 100644 --- a/lib/agentic/performance/cache_manager.rb +++ b/lib/agentic/performance/cache_manager.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "cache" - module Agentic module Performance # Cache manager for coordinating multiple cache instances and strategies diff --git a/lib/agentic/performance/optimizer.rb b/lib/agentic/performance/optimizer.rb index f02d89e..eddf98c 100644 --- a/lib/agentic/performance/optimizer.rb +++ b/lib/agentic/performance/optimizer.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "cache_manager" - module Agentic module Performance # Performance optimization framework with caching, pooling, and monitoring diff --git a/lib/agentic/security.rb b/lib/agentic/security.rb index 53cc799..e53e309 100644 --- a/lib/agentic/security.rb +++ b/lib/agentic/security.rb @@ -1,9 +1,5 @@ # frozen_string_literal: true -require_relative "security/sanitizer" -require_relative "security/config" -require_relative "security/secure_error_mixin" - module Agentic # Security module providing comprehensive PII sanitization and secure error handling # diff --git a/lib/agentic/security/secure_error_mixin.rb b/lib/agentic/security/secure_error_mixin.rb index e976894..5f267bd 100644 --- a/lib/agentic/security/secure_error_mixin.rb +++ b/lib/agentic/security/secure_error_mixin.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative "config" - module Agentic module Security # Mixin to add security-aware error handling to existing error classes diff --git a/lib/agentic/verification/strategy_factory.rb b/lib/agentic/verification/strategy_factory.rb index eb30b8b..c206670 100644 --- a/lib/agentic/verification/strategy_factory.rb +++ b/lib/agentic/verification/strategy_factory.rb @@ -1,9 +1,5 @@ # frozen_string_literal: true -require_relative "verification_strategy" -require_relative "llm_verification_strategy" -require_relative "schema_verification_strategy" - module Agentic module Verification # Factory for creating verification strategies with consistent configuration diff --git a/lib/agentic/verification/verification_helpers.rb b/lib/agentic/verification/verification_helpers.rb index 77ea8af..4ca0ba1 100644 --- a/lib/agentic/verification/verification_helpers.rb +++ b/lib/agentic/verification/verification_helpers.rb @@ -1,8 +1,5 @@ # frozen_string_literal: true -require_relative "strategy_factory" -require_relative "verification_hub" - module Agentic module Verification # Helper methods for setting up verification using standardized patterns diff --git a/lib/agentic/verification/verification_hub.rb b/lib/agentic/verification/verification_hub.rb index a01b1c7..f7b4e25 100644 --- a/lib/agentic/verification/verification_hub.rb +++ b/lib/agentic/verification/verification_hub.rb @@ -42,7 +42,6 @@ def add_strategy(strategy) # @param dependencies [Hash] Strategy dependencies # @return [void] def add_strategy_from_factory(type, config: {}, **dependencies) - require_relative "strategy_factory" strategy = StrategyFactory.create(type, config: config, **dependencies) add_strategy(strategy) end diff --git a/lib/agentic/workspace.rb b/lib/agentic/workspace.rb index 059c545..632635a 100644 --- a/lib/agentic/workspace.rb +++ b/lib/agentic/workspace.rb @@ -2,10 +2,6 @@ require "securerandom" require "fileutils" -require_relative "artifact" -require_relative "artifact_graph" -require_relative "observable" -require_relative "verification/artifact_verification_strategy" module Agentic # Manages an isolated workspace for artifact generation diff --git a/spec/zeitwerk_spec.rb b/spec/zeitwerk_spec.rb new file mode 100644 index 0000000..5b7f22d --- /dev/null +++ b/spec/zeitwerk_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require "open3" + +# Loader contract check. lib/agentic.rb makes Zeitwerk the single code loader +# and tells files not to require_relative their siblings. Zeitwerk tolerates +# a managed file being required directly, so a stray require never crashes; +# it only hand-maintains a load-order edge the loader already owns, which is +# how the rule came back 57 lines strong with no red build. The first example +# tests the promise behind the rule, the second tests the rule. +RSpec.describe "Zeitwerk loader" do + let(:lib_dir) { File.expand_path("../lib", __dir__) } + + # A fresh interpreter, so no constant is already loaded by spec_helper and + # nothing in this process's state can hide a file that only loads because + # another file was required first. force: true also walks lib/agentic/cli, + # which the loader excludes from normal eager loading. + it "eager-loads every constant under lib/agentic/ in a fresh process" do + script = <<~RUBY + require "agentic" + loader = Zeitwerk::Registry.loaders.find { |l| l.tag == "agentic" } + abort "no Zeitwerk loader tagged agentic (did the gem name change?)" unless loader + loader.eager_load(force: true) + print Agentic::CLI.name + RUBY + + out, err, status = Open3.capture3(RbConfig.ruby, "-W0", "-I", lib_dir, "-e", script) + + expect(status).to be_success, "eager load failed:\n#{err}" + expect(out).to eq("Agentic::CLI") + end + + it "manages every file under lib/agentic/ without require_relative" do + offenders = Dir[File.join(lib_dir, "agentic", "**", "*.rb")].sort.flat_map do |file| + File.foreach(file).with_index(1).filter_map do |line, number| + "#{file.delete_prefix("#{lib_dir}/")}:#{number}: #{line.strip}" if line.match?(/\A\s*require_relative\b/) + end + end + + expect(offenders).to be_empty, + "require_relative between loader-managed files (reference the constant instead):\n #{offenders.join("\n ")}" + end +end