Class: RailsAiBridge::BootManager

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_ai_bridge/boot_manager.rb

Overview

Manages bounded Rails app boot with stdout quarantine and timeout.

In standalone mode, the executable needs to boot the host app's Rails without contaminating stdout (which is used for MCP protocol) and without hanging indefinitely. BootManager handles:

  1. Locating the app root (Gemfile or config/application.rb)
  2. Quarantining boot stdout to stderr
  3. Honoring a configurable timeout
  4. Returning a structured result for StandardError and ScriptError failures
  5. Offering a StaticApp fallback for commands that permit it

Examples:

Bounded boot

result = RailsAiBridge::BootManager.boot('/path/to/app', timeout: 30)
if result[:success]
  app = result[:app]
else
  app = RailsAiBridge::BootManager.static_fallback('/path/to/app')
end

Constant Summary collapse

STATIC_ALLOWED_COMMANDS =

Commands that can operate in static mode (no Rails boot required).

%i[context inspect doctor].freeze
DEFAULT_TIMEOUT =

Default boot timeout in seconds.

30

Class Method Summary collapse

Class Method Details

.boot(root, timeout: DEFAULT_TIMEOUT) ⇒ Hash

Attempts to boot the Rails app at the given root with a bounded timeout.

Boot stdout is redirected to stderr so that MCP stdio protocol is not contaminated. The result is always a structured hash — never raises.

:reek:TooManyStatements -- boot orchestration requires setup, rescue, and ensure

Parameters:

  • root (String, Pathname)

    the app root to boot

  • timeout (Integer) (defaults to: DEFAULT_TIMEOUT)

    maximum seconds to wait for boot

Returns:

  • (Hash)

    structured result:

    • :success [Boolean] whether boot succeeded
    • :app [Rails::Application, nil] the booted app (on success)
    • :error [String, nil] error message (on failure)
    • :error_class [String, nil] error class name (on failure)
    • :stdout_quarantined [Boolean] whether stdout was redirected


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/rails_ai_bridge/boot_manager.rb', line 63

def boot(root, timeout: DEFAULT_TIMEOUT)
  root = Pathname.new(root)
  original_stdout = $stdout
  original_dir = Dir.pwd
  $stdout = $stderr
  result = { stdout_quarantined: true, success: false, app: nil, error: nil, error_class: nil }

  begin
    Dir.chdir(root)
    Timeout.timeout(timeout) do
      require File.join(root, 'config', 'application')
      app = Rails.application
      app.initialize!
      result[:success] = true
      result[:app] = app
    end
  rescue StandardError, ScriptError => error
    prefix = error.is_a?(Timeout::Error) ? "Boot timed out after #{timeout}s: " : ''
    result[:error] = "#{prefix}#{error.message}"
    result[:error_class] = error.class.name
  ensure
    $stdout = original_stdout
    Dir.chdir(original_dir)
  end

  result
end

.locate_root(start_path) ⇒ Pathname?

Locates the Rails app root by searching for a Gemfile or config/application.rb, walking up from the given path.

:reek:DuplicateMethodCall -- root_markers? is called in loop condition and result, both necessary

Parameters:

  • start_path (String, Pathname)

    directory to search from

Returns:

  • (Pathname, nil)

    the app root, or nil if not found



42
43
44
45
46
47
# File 'lib/rails_ai_bridge/boot_manager.rb', line 42

def locate_root(start_path)
  current = Pathname.new(start_path).expand_path
  current = current.parent until current.root? || root_markers?(current)

  root_markers?(current) ? current : nil
end

.offer_static_fallback?(command) ⇒ Boolean

Checks whether a command permits static fallback when boot fails.

Parameters:

  • command (Symbol)

    the CLI command (e.g. :context, :serve)

Returns:

  • (Boolean)


104
105
106
# File 'lib/rails_ai_bridge/boot_manager.rb', line 104

def offer_static_fallback?(command)
  STATIC_ALLOWED_COMMANDS.include?(command)
end

.static_fallback(root) ⇒ StaticApp

Returns a StaticApp for the given root, for use when boot fails or is explicitly skipped via --no-boot.

Parameters:

  • root (String, Pathname)

    the app root

Returns:



96
97
98
# File 'lib/rails_ai_bridge/boot_manager.rb', line 96

def static_fallback(root)
  StaticApp.new(root)
end