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
-
.compact_performance_security_section ⇒ Array<String>
Short, copy-pastable baseline for compact serializers (performance, drift, MCP exposure).
-
.database_size_bucket(row_count) ⇒ String?
Human-oriented approximate table size bucket for optional database stats.
-
.database_size_bucket_for_table(context, table_name) ⇒ String?
Optional size bucket for a table from +context[:database_stats]+.
-
.introspected_controller_count(context) ⇒ Integer?
Number of Ruby controller classes under +app/controllers+.
-
.model_complexity_score(data) ⇒ Integer
Complexity score for a single model's introspection data.
-
.model_relevance_score(data, name: nil, context: {}) ⇒ Integer
Task-relevance score for passive context ordering.
-
.models_by_relevance(models, context: {}) ⇒ Array<Array(String, Hash)>
Valid model entries sorted by task relevance, then model name for stable deterministic output when scores tie.
-
.models_grouped_by_semantic_tier(models, context: {}) ⇒ Hash{String => Array<String>}
Valid model names grouped by semantic tier while preserving relevance ordering.
-
.recently_migrated?(table_name, migrations) ⇒ Boolean
Returns true when any migration within the last 30 days references +table_name+.
-
.route_focus_lines(context, limit: 5) ⇒ Array<String>
Bounded route focus lines for passive context.
-
.route_target_controller_count(context) ⇒ Integer?
Distinct controller names referenced in the route set.
-
.routes_stack_line(context) ⇒ String?
One stack bullet for routes + controller inventory, aligned with +rails-controllers+ split files.
-
.safe_config_files(files, limit: nil) ⇒ Array<String>
Filters config file paths down to entries safe for generated assistant context.
-
.semantic_tier_for(data) ⇒ String
Semantic tier with a stable fallback.
-
.sensitive_config_file?(path) ⇒ Boolean
Returns true for config paths that are secret-bearing by name, even when only the path is exposed and file contents are never read.
-
.test_command(context) ⇒ String
Returns the appropriate test command string for this app's test framework.
-
.top_columns(table_data) ⇒ Array<Hash>
Top non-housekeeping columns for a model's table.
Class Method Details
.compact_performance_security_section ⇒ Array<String>
Short, copy-pastable baseline for compact serializers (performance, drift, MCP exposure).
213 214 215 216 217 218 219 220 221 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 213 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 |
.database_size_bucket(row_count) ⇒ String?
Human-oriented approximate table size bucket for optional database stats.
197 198 199 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 197 def database_size_bucket(row_count) DatabaseSize.bucket(row_count) end |
.database_size_bucket_for_table(context, table_name) ⇒ String?
Optional size bucket for a table from +context[:database_stats]+.
206 207 208 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 206 def database_size_bucket_for_table(context, table_name) DatabaseSize.bucket_for_table(context, table_name) end |
.introspected_controller_count(context) ⇒ Integer?
Returns number of Ruby controller classes under +app/controllers+.
32 33 34 35 36 37 38 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 32 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.
93 94 95 96 97 98 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 93 def model_complexity_score(data) Array(data[:associations]).size + Array(data[:validations]).size + Array(data[:callbacks]).size + Array(data[:scopes]).size end |
.model_relevance_score(data, name: nil, context: {}) ⇒ Integer
Task-relevance score for passive context ordering.
106 107 108 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 106 def model_relevance_score(data, name: nil, context: {}) ModelRelevance.new(data: data, name: name, context: context).score end |
.models_by_relevance(models, context: {}) ⇒ Array<Array(String, Hash)>
Valid model entries sorted by task relevance, then model name for stable deterministic output when scores tie.
116 117 118 119 120 121 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 116 def models_by_relevance(models, context: {}) return [] unless models.is_a?(Hash) models.select { |_name, data| data.is_a?(Hash) && !data[:error] } .sort_by { |name, data| [-model_relevance_score(data, name: name, context: context), name.to_s] } end |
.models_grouped_by_semantic_tier(models, context: {}) ⇒ Hash{String => Array<String>}
Valid model names grouped by semantic tier while preserving relevance ordering.
128 129 130 131 132 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 128 def models_grouped_by_semantic_tier(models, context: {}) models_by_relevance(models, context: context).each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |(name, data), groups| groups[semantic_tier_for(data)] << name end 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+.
178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 178 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_focus_lines(context, limit: 5) ⇒ Array<String>
Bounded route focus lines for passive context. Shows busiest endpoint areas without dumping the full route table.
83 84 85 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 83 def route_focus_lines(context, limit: 5) RouteFocus.new(context, limit).lines end |
.route_target_controller_count(context) ⇒ Integer?
Returns distinct controller names referenced in the route set.
42 43 44 45 46 47 48 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 42 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.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 54 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 |
.safe_config_files(files, limit: nil) ⇒ Array<String>
Filters config file paths down to entries safe for generated assistant context.
228 229 230 231 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 228 def safe_config_files(files, limit: nil) safe_files = Array(files).reject { |path| sensitive_config_file?(path) } limit ? safe_files.first(limit) : safe_files end |
.semantic_tier_for(data) ⇒ String
Returns semantic tier with a stable fallback.
136 137 138 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 136 def semantic_tier_for(data) (data.is_a?(Hash) && data[:semantic_tier].presence) || 'supporting' end |
.sensitive_config_file?(path) ⇒ Boolean
Returns true for config paths that are secret-bearing by name, even when only the path is exposed and file contents are never read.
238 239 240 241 242 243 244 245 246 247 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 238 def sensitive_config_file?(path) normalized = normalized_config_path(path) basename = File.basename(normalized) dotenv_file?(basename) || sensitive_config_basename?(basename) || SENSITIVE_CONFIG_EXTENSIONS.include?(File.extname(basename)) || sensitive_config_path_segment?(normalized) || rails_environment_credentials?(normalized) 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.
147 148 149 150 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 147 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.
159 160 161 162 163 164 165 166 167 168 |
# File 'lib/rails_ai_bridge/serializers/context_summary.rb', line 159 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 |