Module: RailsAiBridge::Serializers::ContextSummary

Defined in:
lib/rails_ai_bridge/serializers/context_summary.rb

Overview

Shared stack metrics so compact outputs stay consistent with split rule files (e.g. +# Controllers (N)+ uses ControllerIntrospector, not +routes[:by_controller]+ alone).

Constant Summary collapse

HOUSEKEEPING_COLUMNS =

Column names excluded from compact model output — primary key, timestamps, and foreign keys (matched via +_id+ suffix separately).

%w[id created_at updated_at].freeze

Class Method Summary collapse

Class Method Details

.compact_performance_security_sectionArray<String>

Short, copy-pastable baseline for compact serializers (performance, drift, MCP exposure).

Returns:

  • (Array<String>)

    markdown lines (including heading)



130
131
132
133
134
135
136
137
138
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 130

def compact_performance_security_section
  [
    '## Performance & security (baseline)',
    '- Large or hot tables: mind indexes and N+1s; use `includes`, batching, and bounded queries — validate with `rails_get_schema` and real load patterns.',
    '- Treat generated context as **snapshots** that can drift; prefer `rails_*` MCP tools for authoritative structure when in doubt.',
    '- Merge team-specific rules (performance, auth, compliance) into these files or companion rules — generated output is generic.',
    '- MCP is read-only but exposes app structure; avoid exposing the HTTP transport on untrusted networks.'
  ]
end

.introspected_controller_count(context) ⇒ Integer?

Returns number of Ruby controller classes under +app/controllers+.

Parameters:

  • context (Hash)

    full introspection hash

Returns:

  • (Integer, nil)

    number of Ruby controller classes under +app/controllers+



16
17
18
19
20
21
22
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 16

def introspected_controller_count(context)
  ctrl = context[:controllers]
  return nil unless ctrl.is_a?(Hash) && !ctrl[:error]

  count = (ctrl[:controllers] || {}).size
  count.positive? ? count : nil
end

.model_complexity_score(data) ⇒ Integer

Complexity score for a single model's introspection data. Used by compact serializers to surface the most architecturally significant models (high association/validation/callback/scope counts) first.

Parameters:

  • data (Hash)

    single-model entry from +context[:models]+

Returns:

  • (Integer)

    non-negative score; higher = more complex



67
68
69
70
71
72
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 67

def model_complexity_score(data)
  Array(data[:associations]).size +
    Array(data[:validations]).size +
    Array(data[:callbacks]).size +
    Array(data[:scopes]).size
end

.recently_migrated?(table_name, migrations) ⇒ Boolean

Returns true when any migration within the last 30 days references +table_name+. Migration recency is derived from the YYYYMMDDHHMMSS timestamp prefix in +:version+. The table match is based on common Rails migration filename forms such as +create_users+ and +add_email_to_users+.

Parameters:

  • table_name (String, nil)

    snake_case table name (e.g. +"users"+)

  • migrations (Hash, nil)

    +context[:migrations]+ hash; must have a +:recent+ key

Returns:

  • (Boolean)


112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 112

def recently_migrated?(table_name, migrations)
  return false unless table_name && migrations.is_a?(Hash)

  cutoff = Time.zone.today - 30
  Array(migrations[:recent]).any? do |m|
    version = m[:version].to_s
    next false unless version.length >= 8

    migration_date = Date.strptime(version[0..7], '%Y%m%d')
    migration_date >= cutoff && migration_filename_matches_table?(m[:filename], table_name)
  rescue ArgumentError
    false
  end
end

.route_target_controller_count(context) ⇒ Integer?

Returns distinct controller names referenced in the route set.

Parameters:

  • context (Hash)

Returns:

  • (Integer, nil)

    distinct controller names referenced in the route set



26
27
28
29
30
31
32
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 26

def route_target_controller_count(context)
  routes = context[:routes]
  return nil unless routes.is_a?(Hash) && !routes[:error]

  count = (routes[:by_controller] || {}).keys.size
  count.positive? ? count : nil
end

.routes_stack_line(context) ⇒ String?

One stack bullet for routes + controller inventory, aligned with +rails-controllers+ split files.

Parameters:

  • context (Hash)

Returns:

  • (String, nil)

    markdown line starting with "- Routes:" or +nil+ if no routes data



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 38

def routes_stack_line(context)
  routes = context[:routes]
  return nil unless routes.is_a?(Hash) && !routes[:error]

  total = routes[:total_routes]
  ic = introspected_controller_count(context)
  rt = route_target_controller_count(context)

  if ic
    suffix =
      if rt && rt != ic
        " (#{rt} names in routing — can exceed class count when routes reference engines or non-file controllers)"
      else
        ''
      end
    "- Routes: #{total} total — #{ic} controller classes#{suffix}"
  elsif rt
    "- Routes: #{total} total — #{rt} route targets (controller inventory unavailable)"
  else
    "- Routes: #{total} total"
  end
end

.test_command(context) ⇒ String

Returns the appropriate test command string for this app's test framework. Reads +context[:tests][:framework]+ (value returned by the tests introspector: "rspec" or "minitest"). Falls back to +"bundle exec rspec"+ when the key is absent, nil, or contains an unrecognised value.

Parameters:

  • context (Hash)

    full introspection hash

Returns:

  • (String)

    copy-pastable test command



81
82
83
84
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 81

def test_command(context)
  framework = context.dig(:tests, :framework).to_s.strip.downcase
  framework == 'minitest' ? 'bin/rails test' : 'bundle exec rspec'
end

.top_columns(table_data) ⇒ Array<Hash>

Top non-housekeeping columns for a model's table. Excludes primary key (+id+), timestamps (+created_at+, +updated_at+), and foreign key columns (names ending in +_id+). Returns at most 3 columns.

Parameters:

  • table_data (Hash, nil)

    entry from +context[:schema][:tables][table_name]+; must have a +:columns+ key with an array of +{ name:, type: }+ hashes.

Returns:

  • (Array<Hash>)

    up to 3 column hashes with +:name+ and +:type+ keys



93
94
95
96
97
98
99
100
101
102
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 93

def top_columns(table_data)
  return [] unless table_data.is_a?(Hash)

  columns = Array(table_data[:columns])

  columns
    .select { |column| displayable_column?(column) }
    .first(3)
    .map { |column| { name: column[:name], type: column[:type] } }
end