rails-ai-context 5.30.2 → 5.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +868 -0
  3. data/CONTEXT.md +7 -3
  4. data/README.md +1 -1
  5. data/docs/ARCHITECTURE.md +3 -3
  6. data/docs/CLI.md +1 -1
  7. data/docs/COMPATIBILITY.md +50 -14
  8. data/docs/CONFIGURATION.md +4 -1
  9. data/docs/FAQ.md +1 -1
  10. data/docs/GUIDE.md +14 -8
  11. data/docs/INTROSPECTORS.md +50 -23
  12. data/docs/SECURITY.md +56 -1
  13. data/docs/TOOLS.md +58 -12
  14. data/lib/generators/rails_ai_context/install/install_generator.rb +1 -0
  15. data/lib/rails_ai_context/action_filters.rb +121 -48
  16. data/lib/rails_ai_context/app_commands.rb +2 -2
  17. data/lib/rails_ai_context/app_kind.rb +65 -8
  18. data/lib/rails_ai_context/ast_cache.rb +53 -18
  19. data/lib/rails_ai_context/base_mixins.rb +46 -16
  20. data/lib/rails_ai_context/boot_manager.rb +5 -1
  21. data/lib/rails_ai_context/cli/entry_boot.rb +17 -2
  22. data/lib/rails_ai_context/cli/tool_runner.rb +15 -1
  23. data/lib/rails_ai_context/concern_macros.rb +791 -111
  24. data/lib/rails_ai_context/concern_membership.rb +25 -5
  25. data/lib/rails_ai_context/concern_paths.rb +63 -17
  26. data/lib/rails_ai_context/config_yaml.rb +40 -0
  27. data/lib/rails_ai_context/configuration.rb +3 -3
  28. data/lib/rails_ai_context/data/docs/index.json +324 -0
  29. data/lib/rails_ai_context/database_yml.rb +136 -0
  30. data/lib/rails_ai_context/doctor.rb +30 -37
  31. data/lib/rails_ai_context/erb_source.rb +40 -0
  32. data/lib/rails_ai_context/fingerprinter.rb +48 -22
  33. data/lib/rails_ai_context/fixture_keys.rb +42 -1
  34. data/lib/rails_ai_context/gem_lock.rb +275 -47
  35. data/lib/rails_ai_context/git_ignore.rb +13 -11
  36. data/lib/rails_ai_context/hydrators/hydration_formatter.rb +8 -2
  37. data/lib/rails_ai_context/hydrators/schema_hint_builder.rb +10 -5
  38. data/lib/rails_ai_context/introspector.rb +10 -1
  39. data/lib/rails_ai_context/introspectors/action_mailbox_introspector.rb +27 -19
  40. data/lib/rails_ai_context/introspectors/action_presence.rb +11 -8
  41. data/lib/rails_ai_context/introspectors/action_resolver.rb +38 -26
  42. data/lib/rails_ai_context/introspectors/action_text_introspector.rb +2 -2
  43. data/lib/rails_ai_context/introspectors/active_record_settings.rb +56 -0
  44. data/lib/rails_ai_context/introspectors/active_storage_introspector.rb +1 -1
  45. data/lib/rails_ai_context/introspectors/active_support_introspector.rb +98 -22
  46. data/lib/rails_ai_context/introspectors/admin_resources.rb +101 -0
  47. data/lib/rails_ai_context/introspectors/anyway_configs.rb +64 -0
  48. data/lib/rails_ai_context/introspectors/apartment_config.rb +47 -0
  49. data/lib/rails_ai_context/introspectors/api_introspector.rb +125 -26
  50. data/lib/rails_ai_context/introspectors/asset_pipeline_introspector.rb +24 -19
  51. data/lib/rails_ai_context/introspectors/ast_walk.rb +18 -0
  52. data/lib/rails_ai_context/introspectors/auth_introspector.rb +71 -18
  53. data/lib/rails_ai_context/introspectors/autoload_introspector.rb +62 -15
  54. data/lib/rails_ai_context/introspectors/base.rb +38 -0
  55. data/lib/rails_ai_context/introspectors/call_site_expansion.rb +349 -39
  56. data/lib/rails_ai_context/introspectors/component_introspector.rb +26 -14
  57. data/lib/rails_ai_context/introspectors/config_introspector.rb +22 -9
  58. data/lib/rails_ai_context/introspectors/connection_pool_introspector.rb +1 -4
  59. data/lib/rails_ai_context/introspectors/controller_filters.rb +335 -31
  60. data/lib/rails_ai_context/introspectors/controller_introspector.rb +315 -129
  61. data/lib/rails_ai_context/introspectors/controller_settings.rb +182 -0
  62. data/lib/rails_ai_context/introspectors/convention_introspector.rb +18 -15
  63. data/lib/rails_ai_context/introspectors/database_stats_introspector.rb +1 -1
  64. data/lib/rails_ai_context/introspectors/declared_constant.rb +108 -23
  65. data/lib/rails_ai_context/introspectors/eager_load.rb +12 -7
  66. data/lib/rails_ai_context/introspectors/engine_introspector.rb +13 -5
  67. data/lib/rails_ai_context/introspectors/env_config_introspector.rb +62 -17
  68. data/lib/rails_ai_context/introspectors/env_introspector.rb +8 -6
  69. data/lib/rails_ai_context/introspectors/env_references.rb +21 -4
  70. data/lib/rails_ai_context/introspectors/frontend_framework_introspector.rb +37 -22
  71. data/lib/rails_ai_context/introspectors/gem_introspector.rb +3 -1
  72. data/lib/rails_ai_context/introspectors/gemfile_gems.rb +53 -5
  73. data/lib/rails_ai_context/introspectors/grape_endpoints.rb +109 -0
  74. data/lib/rails_ai_context/introspectors/helper_names.rb +12 -1
  75. data/lib/rails_ai_context/introspectors/i18n_introspector.rb +101 -46
  76. data/lib/rails_ai_context/introspectors/includers.rb +6 -1
  77. data/lib/rails_ai_context/introspectors/initializer_introspector.rb +2 -5
  78. data/lib/rails_ai_context/introspectors/job_introspector.rb +548 -103
  79. data/lib/rails_ai_context/introspectors/listener_registration.rb +11 -1
  80. data/lib/rails_ai_context/introspectors/listeners/associations_listener.rb +51 -4
  81. data/lib/rails_ai_context/introspectors/listeners/autoload_ignore_listener.rb +31 -0
  82. data/lib/rails_ai_context/introspectors/listeners/autoload_paths_listener.rb +4 -47
  83. data/lib/rails_ai_context/introspectors/listeners/base_listener.rb +80 -14
  84. data/lib/rails_ai_context/introspectors/listeners/branch_conditions.rb +156 -0
  85. data/lib/rails_ai_context/introspectors/listeners/callbacks_listener.rb +85 -23
  86. data/lib/rails_ai_context/introspectors/listeners/class_definition_listener.rb +35 -6
  87. data/lib/rails_ai_context/introspectors/listeners/component_structure_listener.rb +30 -2
  88. data/lib/rails_ai_context/introspectors/listeners/conditional_macro_listener.rb +19 -0
  89. data/lib/rails_ai_context/introspectors/listeners/config_assignment_listener.rb +255 -10
  90. data/lib/rails_ai_context/introspectors/listeners/constant_reference_listener.rb +34 -0
  91. data/lib/rails_ai_context/introspectors/listeners/constructor_macro_listener.rb +123 -0
  92. data/lib/rails_ai_context/introspectors/listeners/definition_file_paths_listener.rb +66 -0
  93. data/lib/rails_ai_context/introspectors/listeners/enums_listener.rb +2 -2
  94. data/lib/rails_ai_context/introspectors/listeners/env_access_listener.rb +31 -0
  95. data/lib/rails_ai_context/introspectors/listeners/filter_macro_listener.rb +54 -0
  96. data/lib/rails_ai_context/introspectors/listeners/fixture_paths_listener.rb +62 -0
  97. data/lib/rails_ai_context/introspectors/listeners/gemfile_dsl_listener.rb +42 -1
  98. data/lib/rails_ai_context/introspectors/listeners/generic_macro_listener.rb +28 -2
  99. data/lib/rails_ai_context/introspectors/listeners/grape_api_listener.rb +137 -0
  100. data/lib/rails_ai_context/introspectors/listeners/http_client_call_listener.rb +50 -0
  101. data/lib/rails_ai_context/introspectors/listeners/i18n_load_path_listener.rb +54 -0
  102. data/lib/rails_ai_context/introspectors/listeners/literal_paths.rb +123 -0
  103. data/lib/rails_ai_context/introspectors/listeners/macros_listener.rb +248 -4
  104. data/lib/rails_ai_context/introspectors/listeners/mailbox_routing_listener.rb +2 -1
  105. data/lib/rails_ai_context/introspectors/listeners/method_call_listener.rb +7 -1
  106. data/lib/rails_ai_context/introspectors/listeners/methods_listener.rb +352 -69
  107. data/lib/rails_ai_context/introspectors/listeners/migration_dsl_listener.rb +1 -1
  108. data/lib/rails_ai_context/introspectors/listeners/migration_replay_listener.rb +35 -26
  109. data/lib/rails_ai_context/introspectors/listeners/mixins_listener.rb +62 -29
  110. data/lib/rails_ai_context/introspectors/listeners/mount_listener.rb +8 -0
  111. data/lib/rails_ai_context/introspectors/listeners/namespaced_roots_listener.rb +35 -0
  112. data/lib/rails_ai_context/introspectors/listeners/nested_constants_listener.rb +32 -0
  113. data/lib/rails_ai_context/introspectors/listeners/owner_scope.rb +67 -0
  114. data/lib/rails_ai_context/introspectors/listeners/preview_paths_listener.rb +16 -7
  115. data/lib/rails_ai_context/introspectors/listeners/proc_literal_listener.rb +42 -0
  116. data/lib/rails_ai_context/introspectors/listeners/queue_assignment_listener.rb +54 -0
  117. data/lib/rails_ai_context/introspectors/listeners/rake_task_dsl_listener.rb +21 -8
  118. data/lib/rails_ai_context/introspectors/listeners/route_files_listener.rb +4 -11
  119. data/lib/rails_ai_context/introspectors/listeners/routes_dsl_listener.rb +461 -65
  120. data/lib/rails_ai_context/introspectors/listeners/schema_dsl_listener.rb +76 -14
  121. data/lib/rails_ai_context/introspectors/listeners/scopes_listener.rb +40 -1
  122. data/lib/rails_ai_context/introspectors/listeners/validations_listener.rb +10 -1
  123. data/lib/rails_ai_context/introspectors/listeners/view_paths_listener.rb +37 -0
  124. data/lib/rails_ai_context/introspectors/middleware_introspector.rb +47 -3
  125. data/lib/rails_ai_context/introspectors/migration_introspector.rb +3 -3
  126. data/lib/rails_ai_context/introspectors/migration_replay/helpers.rb +4 -3
  127. data/lib/rails_ai_context/introspectors/migration_replay/statements.rb +72 -12
  128. data/lib/rails_ai_context/introspectors/migration_replay.rb +110 -14
  129. data/lib/rails_ai_context/introspectors/model_introspector.rb +708 -214
  130. data/lib/rails_ai_context/introspectors/module_aliases.rb +11 -3
  131. data/lib/rails_ai_context/introspectors/multi_database_introspector.rb +9 -57
  132. data/lib/rails_ai_context/introspectors/observability_introspector.rb +5 -8
  133. data/lib/rails_ai_context/introspectors/performance_introspector.rb +28 -8
  134. data/lib/rails_ai_context/introspectors/rake_task_introspector.rb +118 -51
  135. data/lib/rails_ai_context/introspectors/recurring_schedules.rb +150 -0
  136. data/lib/rails_ai_context/introspectors/rendered_record.rb +87 -0
  137. data/lib/rails_ai_context/introspectors/retry_policy.rb +8 -5
  138. data/lib/rails_ai_context/introspectors/route_introspector.rb +153 -42
  139. data/lib/rails_ai_context/introspectors/schema_conventions.rb +127 -40
  140. data/lib/rails_ai_context/introspectors/schema_dump_path.rb +80 -0
  141. data/lib/rails_ai_context/introspectors/schema_introspector.rb +346 -112
  142. data/lib/rails_ai_context/introspectors/schema_reader.rb +69 -29
  143. data/lib/rails_ai_context/introspectors/service_classes.rb +21 -15
  144. data/lib/rails_ai_context/introspectors/source_calls.rb +1 -1
  145. data/lib/rails_ai_context/introspectors/source_introspector.rb +17 -2
  146. data/lib/rails_ai_context/introspectors/source_scan.rb +174 -26
  147. data/lib/rails_ai_context/introspectors/sqlite_virtual_tables.rb +28 -0
  148. data/lib/rails_ai_context/introspectors/stimulus_introspector.rb +16 -5
  149. data/lib/rails_ai_context/introspectors/structure_sql_reader.rb +372 -98
  150. data/lib/rails_ai_context/introspectors/superclass_chain.rb +14 -15
  151. data/lib/rails_ai_context/introspectors/table_name.rb +46 -45
  152. data/lib/rails_ai_context/introspectors/test_introspector.rb +289 -78
  153. data/lib/rails_ai_context/introspectors/turbo_introspector.rb +10 -5
  154. data/lib/rails_ai_context/introspectors/view_introspector.rb +5 -40
  155. data/lib/rails_ai_context/introspectors/view_template_introspector.rb +12 -10
  156. data/lib/rails_ai_context/method_name.rb +31 -0
  157. data/lib/rails_ai_context/migration_status.rb +5 -5
  158. data/lib/rails_ai_context/output_guard.rb +6 -4
  159. data/lib/rails_ai_context/package_json.rb +96 -5
  160. data/lib/rails_ai_context/path_resolver.rb +170 -15
  161. data/lib/rails_ai_context/payload.rb +48 -0
  162. data/lib/rails_ai_context/pending_migrations.rb +15 -18
  163. data/lib/rails_ai_context/portable_path.rb +76 -4
  164. data/lib/rails_ai_context/redaction.rb +8 -2
  165. data/lib/rails_ai_context/route_coverage.rb +5 -1
  166. data/lib/rails_ai_context/safe_file.rb +4 -1
  167. data/lib/rails_ai_context/safe_path.rb +61 -10
  168. data/lib/rails_ai_context/schema_hint.rb +8 -2
  169. data/lib/rails_ai_context/schema_version.rb +7 -9
  170. data/lib/rails_ai_context/serializers/claude_rules_serializer.rb +1 -1
  171. data/lib/rails_ai_context/serializers/markdown_serializer.rb +13 -4
  172. data/lib/rails_ai_context/serializers/section_facts.rb +5 -1
  173. data/lib/rails_ai_context/serializers/stack_overview_helper.rb +2 -2
  174. data/lib/rails_ai_context/serializers/tool_guide_helper.rb +7 -4
  175. data/lib/rails_ai_context/server.rb +26 -7
  176. data/lib/rails_ai_context/test_framework.rb +32 -5
  177. data/lib/rails_ai_context/tools/analyze_feature.rb +22 -10
  178. data/lib/rails_ai_context/tools/base_tool.rb +69 -30
  179. data/lib/rails_ai_context/tools/diagnose.rb +1 -1
  180. data/lib/rails_ai_context/tools/generate_test.rb +8 -9
  181. data/lib/rails_ai_context/tools/get_active_support.rb +19 -4
  182. data/lib/rails_ai_context/tools/get_api.rb +17 -11
  183. data/lib/rails_ai_context/tools/get_autoload.rb +10 -5
  184. data/lib/rails_ai_context/tools/get_callbacks.rb +65 -14
  185. data/lib/rails_ai_context/tools/get_component_catalog.rb +7 -4
  186. data/lib/rails_ai_context/tools/get_concern.rb +31 -18
  187. data/lib/rails_ai_context/tools/get_config.rb +52 -18
  188. data/lib/rails_ai_context/tools/get_controllers.rb +57 -13
  189. data/lib/rails_ai_context/tools/get_conventions.rb +44 -22
  190. data/lib/rails_ai_context/tools/get_engines.rb +2 -0
  191. data/lib/rails_ai_context/tools/get_env.rb +209 -52
  192. data/lib/rails_ai_context/tools/get_env_config.rb +38 -7
  193. data/lib/rails_ai_context/tools/get_frontend_stack.rb +56 -7
  194. data/lib/rails_ai_context/tools/get_helper_methods.rb +136 -26
  195. data/lib/rails_ai_context/tools/get_job_pattern.rb +143 -87
  196. data/lib/rails_ai_context/tools/get_mailers.rb +59 -2
  197. data/lib/rails_ai_context/tools/get_model_details.rb +178 -28
  198. data/lib/rails_ai_context/tools/get_partial_interface.rb +101 -62
  199. data/lib/rails_ai_context/tools/get_routes.rb +35 -9
  200. data/lib/rails_ai_context/tools/get_schema.rb +160 -61
  201. data/lib/rails_ai_context/tools/get_service_pattern.rb +121 -25
  202. data/lib/rails_ai_context/tools/get_stimulus.rb +1 -1
  203. data/lib/rails_ai_context/tools/get_test_info.rb +152 -82
  204. data/lib/rails_ai_context/tools/get_view.rb +37 -20
  205. data/lib/rails_ai_context/tools/migration_advisor.rb +64 -24
  206. data/lib/rails_ai_context/tools/onboard.rb +174 -88
  207. data/lib/rails_ai_context/tools/performance_check.rb +5 -13
  208. data/lib/rails_ai_context/tools/query.rb +122 -28
  209. data/lib/rails_ai_context/tools/read_logs.rb +45 -17
  210. data/lib/rails_ai_context/tools/runtime_info.rb +1 -1
  211. data/lib/rails_ai_context/tools/safe_call.rb +20 -15
  212. data/lib/rails_ai_context/tools/search_code.rb +114 -40
  213. data/lib/rails_ai_context/tools/search_docs.rb +22 -11
  214. data/lib/rails_ai_context/tools/security_scan.rb +51 -4
  215. data/lib/rails_ai_context/tools/validate.rb +21 -4
  216. data/lib/rails_ai_context/tools/validate_semantics.rb +18 -6
  217. data/lib/rails_ai_context/version.rb +1 -1
  218. data/lib/rails_ai_context/vfs.rb +2 -2
  219. data/lib/rails_ai_context/view_file.rb +53 -3
  220. data/lib/rails_ai_context.rb +10 -2
  221. data/server.json +1 -1
  222. metadata +34 -3
data/CHANGELOG.md CHANGED
@@ -5,6 +5,874 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.32.0] - 2026-10-06
9
+
10
+ ### Added
11
+
12
+ - **`model_details` lists more of what a model declares.** Under Macros:
13
+ `attribute` (with its type and default), `alias_attribute`, `has_rich_text`,
14
+ `strict_loading_by_default`, `implicit_order_column`, `locking_column`,
15
+ `store_full_sti_class`, `attr_readonly` and `query_constraints`, in both
16
+ tiers; a setting assigned inside a method or a nested class is not listed as
17
+ the model's. An STI model names its parent (or a base its subclasses) and its
18
+ type column, honoring `inheritance_column`, and only where the table has that
19
+ column (the static tier reads the schema dump). A model whose base class calls
20
+ `connects_to` gets a Database line with the call as written and the class it
21
+ comes from, in `model_details` and `get_context`; a call under a condition
22
+ says `(only if ...)`, and otherwise the model's columns come from the schema
23
+ of the database it writes to. (#294, #390, #395)
24
+ - **`model_details` lists a model's `default_scope`** at the top of Scopes,
25
+ declared as the macro or as `def self.default_scope` in the model's own class
26
+ (not a class or module nested in its file), with `all_queries: true` when
27
+ declared. A base's default scope comes first, in the order Rails stacks them.
28
+ (#295)
29
+ - **`model_details` reads common model gems.** An `aasm` machine shows its
30
+ states, initial state, events and transitions, and `has_paper_trail`,
31
+ `friendly_id`, `mount_uploader`, `monetize` (with the attribute it adds),
32
+ `pg_search_scope`, `acts_as_list` and other gem macros get a line each, as
33
+ written. The static tier lists the `belongs_to` that `acts_as_tenant`
34
+ declares. A macro written inside a method body or a nested class is not
35
+ credited to the model, and `aasm.current_state` in a method opens no second
36
+ machine. (#389)
37
+ - **`model_details` lists each index a Mongoid document declares**, as written,
38
+ and the fields and indexes its included concerns declare. (#412)
39
+ - **In an Apartment app, `model_details` says where a model's table lives**: in
40
+ the shared schema (`excluded_models`) or in each tenant's schema, whatever the
41
+ `Apartment.configure` block calls its parameter. A list built with `+=` or
42
+ `<<` is named as computed. (#415)
43
+ - **The schema lists views, materialized views and SQLite virtual tables** in
44
+ both tiers (scenic's schema.rb, structure.sql, the connection), labelled in
45
+ the listings. The table view shows a view's SQL or a virtual table's module,
46
+ with its columns by name alone, and the indexes declared on a materialized
47
+ view. Views and virtual tables are not counted as tables with no model file,
48
+ and a table the dumper could not write is listed with its reason. Known limit:
49
+ booted on Rails 7.x with SQLite older than 3.37, a virtual table's shadow
50
+ tables still list as tables. (#354)
51
+ - **`job_pattern` shows more of how a job runs.** A job's page shows its
52
+ `queue_with_priority`, `enqueue_after_transaction_commit`, Solid Queue
53
+ `limits_concurrency` and its enqueue, perform and `after_discard` callbacks;
54
+ `retry_on` keeps its `queue:`, `priority:` and `jitter:`, and a Sidekiq
55
+ worker's `sidekiq_retry_in` and `sidekiq_retries_exhausted` blocks are listed
56
+ under its retries (the worker listing names such a block by its macro only). A
57
+ priority given as a lambda reads as computed. A job that includes
58
+ `ActiveJob::Continuable`, itself or through a base, is marked so, with the
59
+ steps its `perform` runs in order. (#392, #409)
60
+ - **`job_pattern` names the queues Solid Queue's workers poll** in
61
+ `config/queue.yml` and flags every job queue no worker polls, in the listing
62
+ and on the job's own page, when the app runs Solid Queue (its config sets
63
+ `active_job.queue_adapter = :solid_queue`, or sets no adapter and the Gemfile
64
+ has `solid_queue`). (#396)
65
+ - **delayed_job's `handle_asynchronously` methods are listed** with their
66
+ options, in `job_pattern` and `model_details`, where `job_pattern` used to say
67
+ No jobs found. A method a concern wraps is listed under each model that
68
+ includes the concern, directly or through another concern. (#410)
69
+ - **`mailers` lists Action Mailbox and more mailer settings.** The mailboxes,
70
+ the routing rules in the order Rails tries them with the mailbox each sends
71
+ to, and each mailbox's processing callbacks. Each mailer shows its own
72
+ `default`, `layout`, helpers and callbacks, the template formats of each
73
+ action and its preview class; the full listing names the queue `deliver_later`
74
+ uses, the interceptors and observers the config registers, and the mailer
75
+ preview paths. (#386, #393)
76
+ - **`env` lists environment variables from more places**: the env Kamal's
77
+ `config/deploy.yml` sets at the top level, for each role
78
+ (`servers.<role>.env`) and for each `env.tags` tag (secret names, never
79
+ values, and clear values, except one holding a URL or a key-like token, or
80
+ under a secret-named variable, which shows as hidden, and one an ERB tag sets,
81
+ which says so; a `config/deploy.<destination>.yml` is named as not read), the
82
+ setting keys in the config gem's `config/settings.yml` and
83
+ `config/settings/<env>.yml` when the bundle has the config gem, each
84
+ `Anyway::Config` class's attributes with the env names they read (values left
85
+ out), the ENV names `Rails.app.creds` and `Rails.app.envs` read on Rails 8.2
86
+ (`require(:stripe_api_key)` is `STRIPE_API_KEY`, nested keys joined by `__`),
87
+ and the ENV names read in `config.ru`, `db/seeds.rb`, `db/seeds/` and the Ruby
88
+ scripts in `bin/`. (#397, #398, #404, #417)
89
+ - **`config` lists what runs in front of Rails and what CurrentAttributes
90
+ holds**: the middleware `config.ru` adds (with the `if` it runs under, and the
91
+ `map` it sits inside when that map runs the app itself) and the `map` mounts
92
+ that run their own Rack app, in the static tier too, and each
93
+ CurrentAttributes class's attributes, their defaults and its reset hooks.
94
+ (#398, #399)
95
+ - **`env_config` lists the config keys `config/application.rb` sets for every
96
+ environment** (`config.x` included) and, for each `config_for` call (a name,
97
+ or `Rails.root.join` of literal parts), the keys its YAML file gives the
98
+ running environment, or the environment a literal `env:` names. A file on
99
+ `sensitive_patterns`, outside the app, or over `max_file_size` is named with
100
+ that reason and its keys are not read. (#394)
101
+ - **`active_support` lists the ActiveSupport::Notifications events the app
102
+ subscribes to** (`subscribe`, `monotonic_subscribe`, and a Subscriber's
103
+ `attach_to`, inside the class with or without `self.`, or called on it
104
+ afterwards) with the file and line of each, in both tiers. An `attach_to`
105
+ lists one `<method>.<namespace>` event per public method the subscriber
106
+ defines in that file, and says so in words when it defines none. (#387)
107
+ - **`onboard` lists the app's own tooling**: its custom rake tasks (Rakefile,
108
+ `lib/tasks`, `rakelib`) with their arguments, the description `rake -T` prints
109
+ and the file, one entry for a task defined in several places (the first 15 at
110
+ standard detail, every one at full), its own generators (`bin/rails generate
111
+ service`), the `lib/templates` files that replace a built-in generator's
112
+ template, and the Railties under `lib/` with their initializers. (#385, #400)
113
+ - **`onboard` and the gems resource read more Ruby version sources.** A Ruby
114
+ engine other than CRuby is named from `.ruby-version`, `.tool-versions`, the
115
+ Gemfile `ruby` line or the lockfile ("declaring JRuby 9.4.8.0 (Ruby
116
+ 3.1.4p0)"), where a `jruby-` `.ruby-version`, or a bare `jruby` one, gave no
117
+ Ruby at all. A version pinned in mise (`mise.toml`, `mise.local.toml`,
118
+ `.mise.toml`, `mise/config.toml`, `.mise/config.toml`, `.config/mise.toml` or
119
+ `.config/mise/config.toml`) is read when no other file names one. (#420, #421)
120
+ - **`controllers` and `view` say which layout a controller renders in**
121
+ (declared, inherited, set by an app concern it includes, or found by name),
122
+ and `controllers` lists the `allow_browser`, `protect_from_forgery`,
123
+ `add_flash_types`, `default_form_builder` and `wrap_parameters` settings it,
124
+ its ancestors and their app concerns declare. (#337)
125
+ - **`controllers` lists the filters authorization gems add**: cancancan's
126
+ `load_and_authorize_resource`, `load_resource`, `authorize_resource`,
127
+ `check_authorization` and `skip_authorization_check`, and acts_as_tenant's
128
+ `find_tenant_by_subdomain`, in both tiers; one passed `prepend: true` is
129
+ listed first, where cancancan adds it. Each is read only when the lockfile
130
+ resolves that gem and no class on the chain defines a class method of the same
131
+ name. A responders class-level `respond_to :json` shows its formats in both
132
+ tiers, and a subclass shows the formats its ancestors declare, as responders
133
+ copies them. (#406, #407)
134
+ - **`view` marks template and partial variants and locales**:
135
+ `show.html+mobile.erb` is the `mobile` variant of `show`, and
136
+ `show.fr.html.erb` its `fr` locale. (#408)
137
+ - **`routes` lists the endpoints of a mounted Grape API** under its mount: verb,
138
+ full path with prefix and version, and declared params, with a `route_param`'s
139
+ param and a params block before a namespace given to every endpoint inside it.
140
+ Read from `app/api` and `lib/api` in both tiers; an edit there refreshes the
141
+ MCP server's cached answer. (#411)
142
+ - **`service_pattern` reads more service shapes.** It reads `app/interactions`
143
+ and `app/interactors` as well as `app/services` (so does the generated files'
144
+ Services line), lists an interactor organizer's steps in order, and shows an
145
+ Initialize line and an Inputs section for a service whose constructor comes
146
+ from T::Struct `const`/`prop`, Dry::Struct `attribute`, dry-initializer
147
+ `param`/`option` or the attr_extras initializers. Dry::Struct attributes and
148
+ dry-initializer params and options an app superclass declares are included,
149
+ marked with the class they come from. (#402, #403)
150
+ - **`conventions` and `analyze_feature` list the models an admin gem exposes**:
151
+ ActiveAdmin (with the params it permits), Administrate, Avo (2 and 3 resource
152
+ naming), Madmin and Trestle. (#405)
153
+ - **`conventions` lists `.standard.yml`, `.erb_lint.yml` (or the older
154
+ `.erb-lint.yml`), `sorbet/config` and `Steepfile`** under Notable config
155
+ files, beside `.rubocop.yml`. (#422)
156
+ - **The package manager is found at the JS workspace root.** `frontend_stack`
157
+ and `conventions` name it when the lockfile sits in a workspace root above the
158
+ app (never above the git root), and `frontend_stack` says which directory that
159
+ is. (#401)
160
+
161
+ ### Changed
162
+
163
+ - **A configured frontend directory outside the app root is read for its
164
+ manifests.** A `frontend_paths` entry such as `../web-client`, and the JS
165
+ workspace root above the app, are read for `package.json`, lockfiles and the
166
+ presence of a bundler config only, plus a configured entry's `tsconfig.json`
167
+ and the configs it extends inside that directory. `frontend_stack` reports the
168
+ framework, build tool, package manager and TypeScript settings and names the
169
+ directory instead of calling the app API-only; an entry inside the app root
170
+ gets the same build tool and package manager answer. docs/SECURITY.md lists
171
+ this read under "Frontend roots outside the app". (#377, #401)
172
+ - **An app with no lockfile of its own reads the bundle `config/boot.rb` points
173
+ at.** In an engine's `test/dummy`, static `onboard`, `gems`, `env` and the
174
+ Rails version read the Gemfile and lockfile its `BUNDLE_GEMFILE` names when
175
+ they sit inside the app's git repository; outside one, they say that bundle is
176
+ not read and name it. docs/SECURITY.md lists this read, and the engine source
177
+ a booted `test/dummy` reads, under "The bundle config/boot.rb declares" and
178
+ "The engine an app's test/dummy runs in". (#381, #423)
179
+ - **The controllers payload carries `rate_limits`**, one entry per `rate_limit`
180
+ call, in place of `rate_limit` and `rate_limit_parsed`. `controllers` prints
181
+ every limit a controller declares, whole when the call spans lines, and in a
182
+ controller's own detail each limit an ancestor declares, named by the class
183
+ that declares it. (#332)
184
+ - **A booted filter an outside module defines carries that module as
185
+ `from_concern`.** With `include ActiveStorage::SetBlob`, `set_blob` reads
186
+ `(from ActiveStorage::SetBlob)` and is never labelled as not declared in the
187
+ controller chain; an `on_load` filter beside it still is. (#293)
188
+ - **A strong params entry's `nested` list keeps a doubly-wrapped list as an
189
+ inner array.** `params.expect(thing: [items: [[:sku, :qty]]])` carries
190
+ `"items" => [["sku", "qty"]]`, where it was flattened to the single-hash
191
+ `["sku", "qty"]`, and the `controllers` summary prints it as
192
+ `items: [[:sku, :qty]]`. (#333)
193
+ - **The views payload no longer carries `conditional_layouts`.** Each controller
194
+ entry carries `layout` and `settings` instead. (#337)
195
+ - **The mailboxes payload drops each mailbox's `routing` list** (`{pattern,
196
+ action}`). A mailbox carries `routed_from`, the patterns that send to it, the
197
+ section gains a top-level `routes` list (`{pattern, mailbox, file}`) in the
198
+ order Rails tries them, and a mailbox's `file` is relative to the app root
199
+ (`app/mailboxes/replies_mailbox.rb`), not to `app/mailboxes`. (#386)
200
+ - **A job's `callbacks` in the jobs payload holds the declarations as written**
201
+ (`before_perform :log_start`) in place of the bare macro names
202
+ (`before_perform`). (#392)
203
+ - **A Mongoid model's payload always carries `collection:`**, the `store_in`
204
+ name or the one Mongoid derives (`admin_shelves`), where it was set only for
205
+ `store_in`. An embedded document carries `embedded_in:` (its parent class)
206
+ in its place, and a subclass of a document (`class Ebook < Book`) is a
207
+ Mongoid model with its root's collection and fields and `parent_model:`,
208
+ where it was read as an ActiveRecord model with an `ebooks` table. (#382)
209
+ - **`schema --table` JSON for a name that holds different tables in two
210
+ databases is `{table:, databases: {<name> => table}}`** instead of one table's
211
+ keys at the top level. (#356)
212
+ - **Views are counted apart from tables.** Schema entries carry `kind:` (`view`,
213
+ `materialized_view`, `virtual_table`) in JSON, `total_tables` leaves views
214
+ out, and the schema headers read `116 tables and 2 views`. `onboard` counts
215
+ tables only. (#354)
216
+ - **`encryption_details` options are the source text.** Each `encrypts` entry
217
+ carries every option as the file writes it (`"true"`, not `true`), a
218
+ `normalizes` entry carries its `with:` lambda and other options as written,
219
+ and `generates_token_for` its `expires_in` as written. (#391)
220
+ - **`concern` lists only what an includer gains under Class Methods**:
221
+ `class_methods do`, `module ClassMethods`, and a `def self.x` written in
222
+ `included do`. The module's own `def self.x` and `class << self` methods move
223
+ to a new Module Methods section, and its `def self.included(base)` (or
224
+ `extended`, `prepended`) hook is listed under neither. A `def self.x` or
225
+ `class << self` written inside `class_methods do` belongs to ClassMethods
226
+ alone, so neither section lists it. (#312)
227
+ - **The tests payload's `factories` carries `unread: true`** when a test helper
228
+ loads factories from a path built at run time. Its `count` is then the files
229
+ of the paths that were read, and nil when none was; `test_info` and `onboard`
230
+ print no file count in that case. (#373)
231
+ - **`search_docs` no longer accepts `source: "api"`**, which never had entries.
232
+ The valid sources are all, guides, stimulus, turbo and hotwire. (#322)
233
+ - **The auth payload's `has_secure_password` lists only models with the
234
+ default password.** A named one (`has_secure_password :six_digit_code`) goes
235
+ under a new `secure_password_digests` key, model => attributes. A class
236
+ written across several files keeps its password, and a nested class's
237
+ digest is not credited to the outer model. (#369)
238
+ - **`model_details` Structure sections no longer share a line.** Each section
239
+ ends on the line before the next one starts, so a 3-line model reads
240
+ `class definition(1-1) → associations(2-3)` where 5.31.0 printed `(1-2)`.
241
+ - **The schema payload's `declared_tables` includes db/schema.rb's
242
+ `create_virtual_table` names and the tables it records as "Could not dump
243
+ table".** The booted `schema` line that compares
244
+ declared and connected tables counts a virtual table as a table and a view
245
+ as neither, as the header above it does, so the two numbers match.
246
+
247
+ ### Fixed
248
+
249
+ - **Three crashes on older or unusual setups are gone.** After a boot that fails
250
+ inside a Rails 7.0 app's activesupport (Logger not loaded), the static tier
251
+ answers instead of every tool crashing. Booted tools no longer fail with
252
+ "wrong number of arguments" on a Rails 7.0 app whose bundle locks zeitwerk
253
+ 2.5; the gem now accepts zeitwerk 2.5. CLI tools no longer crash with
254
+ Encoding::CompatibilityError on a non-ASCII argument under a C or other
255
+ non-UTF-8 locale. (#272, #273, #275)
256
+ - **A Sinatra app with `config/environment.rb` and `app/` is not read as a Rails
257
+ app**; both tiers answer "No Rails app found". With no lockfile, a Gemfile
258
+ that names neither rails nor railties decides the same before anything boots,
259
+ so the tree's own `bundler/setup` never runs and never writes a lockfile into
260
+ it. A tree whose `config/environment.rb` loads but defines no Rails
261
+ application, with no lockfile to decide, reports a failed boot and answers
262
+ from the static tier. (#274)
263
+ - **An app without Active Record says so.** `schema` says introspection does not
264
+ apply, `onboard` no longer says "on unknown", and `model_details` says there
265
+ is no Active Record model by that name. A sequel-rails app gets "this app uses
266
+ Sequel; ActiveRecord schema introspection does not apply" instead of id-only
267
+ tables replayed from Sequel migrations, and a replay behind an empty schema.rb
268
+ says the dump declares no tables. (#364, #383)
269
+ - **Gems are read from `gems.rb` and `eval_gemfile` files.** An app on `gems.rb`
270
+ and `gems.locked` reads its gems, Rails version and Ruby version in `gems`,
271
+ `onboard` and `doctor` as one on `Gemfile` does, and gems declared in a file
272
+ named by `eval_gemfile` are read, so `env`, `gems`, `helper_methods` and the
273
+ app-kind checks see them. (#418, #419)
274
+ - **A new Rails 8 app reads right.** `onboard` names the adapter database.yml
275
+ declares when there are no tables yet, where it said "on unknown"; `schema`
276
+ lists the solid_queue, solid_cache and solid_cable tables from their own dump
277
+ files and says the primary database has no tables yet. `onboard --detail full`
278
+ says "Dockerfile: present." and "Deployment: kamal.", names a `Procfile.dev`
279
+ on its own line instead of reporting a Procfile, and no longer prints an empty
280
+ "File Storage & Rich Text" heading. The migrations list names a migration
281
+ `active_storage:install` or `action_text:install` copied by its class, without
282
+ the scope suffix. (#276, #280, #281, #284)
283
+ - **`config` reports the Action Cable adapter of the current environment**
284
+ (solid_cable in a default Rails 8 production, not the development block's
285
+ async) and keeps the Database line when the environment lists several
286
+ databases. `observability` reports Rails 8.1's event reporter (`Rails.event`)
287
+ as available, with its subscriber count. (#285, #286)
288
+ - **`test_info` reads Rails 8.1 test setups.** It and `onboard` name
289
+ `config/ci.rb` (`rails_ci`), Buildkite, Jenkins and Bitbucket Pipelines as CI,
290
+ and `test_info` lists the steps `bin/ci` runs. It lists `test/test_helpers`
291
+ (where the authentication generator puts `SessionTestHelper`) as test helpers,
292
+ no longer lists `test/helpers` helper tests as helpers, and shows the
293
+ `parallelize`, `fixtures` and `driven_by` calls. (#277, #287)
294
+ - **Rails 8 generated authentication reads right.** `controllers` no longer
295
+ drops the authentication filter from every controller: a `skip_before_action`
296
+ inside a class method (`allow_unauthenticated_access`) applies only where a
297
+ controller calls that method, with the options the call passes, also through a
298
+ class method of its base, or of a concern either one includes, that calls it.
299
+ `conventions` builds the controller test's sign-in from the call the app's
300
+ tests make (`sign_in_as`), and signs in the app's own users fixture where the
301
+ tests sign in a variable set elsewhere. (#288, #289)
302
+ - **`read_logs` reads the level a formatter writes**, never a word in the
303
+ message, and reads Sidekiq's 6/7, 8 (colored) and JSON formats and
304
+ rails_semantic_logger's one-letter level. It filters by level only when most
305
+ lines carry a severity; otherwise (Rails' default dev and 8.0+ production
306
+ formatter) it shows every line and says it cannot filter by level. It reads a
307
+ rotated log (`file: "development.log.0"`) and lists rotated logs under the
308
+ available files. (#278, #343)
309
+ - **Static `routes` draws more of what Rails draws.** Shallow nesting,
310
+ `path_names`, a `new do` block, every path of a multi-path route (Rails 8.1
311
+ refuses that form, so on 8.1 it counts as not expanded), routes inside
312
+ `controller :x do`, `options` routes, and the routes of a method defined in a
313
+ route file, where it is called. A resource whose options are a variable, and a
314
+ call it cannot expand (`load`, `use_doorkeeper`, `ActiveAdmin.routes(self)`,
315
+ also inside a `constraints` or `authenticate` block), count as not expanded
316
+ instead of listing seven actions or vanishing; `direct` and `resolve` are no
317
+ longer counted, and a lambda mount counts as the booted tier counts it. Routes
318
+ an initializer adds with `Rails.application.routes.prepend` or `.append` are
319
+ listed first and last, as Rails draws them, including one registered inside
320
+ `after_initialize`, which Rails runs before it draws the routes; one
321
+ registered inside another `on_load` hook counts as not expanded, since that
322
+ hook may run after the draw. The framework count says it covers only the app's
323
+ route files, and that routes Rails' own engines and gems draw are read only
324
+ with the app booted; `onboard`, the rake summary and the generated context
325
+ files say so beside their route count too. (#283, #325, #326, #328)
326
+ - **A route, mount or filter under a condition carries it.** Static `routes`
327
+ marks a route under `if`/`unless`, a `case` branch or an `&&`/`||` guard
328
+ (`case Rails.env`, `Rails.env.development?`, `ENV[...]`) with that condition.
329
+ A mounted app in the `routes`, `engines` and `onboard` lists and the generated
330
+ context files, and a controller filter or skip declared under one
331
+ (`before_action :x if Rails.env.test?`), carry it too, on both tiers; a skip
332
+ under one leaves the filter in the chain, and the generated files no longer
333
+ count such a filter as global. (#327)
334
+ - **`routes --detail full` prints each route's constraints and defaults** beside
335
+ its path, as `bin/rails routes` does, on both tiers, and escapes the `|` in a
336
+ merged `PATCH|PUT` or `via:` verb so each route stays one table row. (#279,
337
+ #329)
338
+ - **Engine routes are counted.** Run from a mountable engine's root, `routes`
339
+ and `onboard` list and count the engine's own routes instead of 0, including
340
+ an engine whose namespace does not match its path (`PgHero::Engine` in
341
+ `lib/pghero/engine.rb`). `engines` counts a loaded engine's routes the way
342
+ `routes` does, PATCH and PUT as one, and counts its redirects and lambdas
343
+ beside them (`23 routes, 2 redirect or lambda routes`). (#379, #380)
344
+ - **Booted from a mountable engine's `test/dummy`, the engine's own code is
345
+ read.** `model_details`, `controllers` and the other class listings include
346
+ the engine's own models and controllers, name their files relative to the app
347
+ root (`../../app/models/...`), and list and render the views and layouts in
348
+ the engine's `app/views`; `test_info`, `analyze_feature` and `generate_test`
349
+ read the engine's test suite. With `--no-boot` the test tools read it too when
350
+ the engine's bundle is inside the app's git repository. When it is not, the
351
+ test framework reads "not read" in `test_info`, `onboard` and the generated
352
+ context, where it said "no tests yet", and a `test_info` model or controller
353
+ search says the engine's suite was not searched. `controllers` with
354
+ `--no-boot` from a `test/dummy` says the engine above it is not read, where it
355
+ listed no controllers and said nothing, and `view` and `partial_interface` say
356
+ the engine's views are read only with the app booted. (#381)
357
+ - **`view` lists more templates.** Templates with no handler extension
358
+ (`pwa/service-worker.js`, `pages/about.text`), which Rails renders with its
359
+ raw handler, are listed and shown, each fenced by what renders it. View paths
360
+ `config/application.rb` adds to `config.paths["app/views"]` are read, so a
361
+ template under `app/views/custom` is listed as `posts/show` and an overlay
362
+ such as `enterprise/app/views` is listed at all; `helper_methods` and
363
+ `stimulus` read those paths too. On an API-only app that keeps the two mailer
364
+ layouts `rails new --api` creates, `view` lists them instead of saying
365
+ `app/views` does not exist. (#282, #338, #346)
366
+ - **`partial_interface` reads strict locals and implicit renders right.** A
367
+ strict locals comment that ends in `-%>`, has a default with parentheses, or
368
+ is the empty `locals: ()` (now said to accept no locals) is read. `render
369
+ @posts` is credited to the partial Rails renders: under the view's controller
370
+ namespace unless `prefix_partial_path_with_controller_namespace` is off
371
+ (booted, as ActionView::Base holds it; unbooted, as `config.action_view`,
372
+ ActionView::Base or an `on_load(:action_view)` block sets it in
373
+ config/application.rb, the environment file or any initializer), or at the
374
+ path a model's `to_partial_path` returns when it returns a string literal.
375
+ `render @post.comments` goes to the partial of the records the association
376
+ the model declares holds (its `class_name:` when it has one), a chain on a
377
+ receiver that is no model (`current_user.posts`) goes to the partial of the
378
+ records it names, a trailing call on a collection (`@posts.first`,
379
+ `@posts.recent`) keeps that collection's records, and a chain the app's
380
+ models cannot resolve, or a `through:` association that names its
381
+ `source:`, is credited to no partial. The `view` tool's `renders:` list
382
+ reads the call the same way. (#323, #324)
383
+ - **`schema` shows more of each column and table**, in both tiers: precision,
384
+ scale, limit, the unsigned flag and collation, the table comment, unique
385
+ constraints, a MySQL text or blob column's size (`size: medium`), index
386
+ options (using, include, order), foreign key actions with the deferrable mode
387
+ and `validate: false`, the enabled extensions, the primary key (a composite
388
+ one as its columns, in `model_details` too, which also names a key the model
389
+ sets with `self.primary_key =`, and the table's beside it when they differ),
390
+ and with `--table` the check constraints (a MySQL one without the parentheses
391
+ MySQL adds), the enum types its columns use with their values, and generated
392
+ columns with their expressions. A `t.virtual` column is typed by its `type:`
393
+ in every static source, migration replays included. (#290, #291, #292, #352)
394
+ - **`db/structure.sql` reads like schema.rb.** A structure.sql app gets the
395
+ column types schema.rb gives the same table (hstore, citext, enum with its
396
+ type, jsonb, timestamptz, binary, time, integer with its limit, unsigned),
397
+ plus table and column comments, and its extensions; an index shows its method,
398
+ order, operator class, prefix lengths and INCLUDE list, and a table its unique
399
+ constraints. On SQLite, a table with a multi-line foreign key or a dump with
400
+ no semicolons is read whole, and `sqlite_sequence` is not listed as a table.
401
+ On MySQL, a `FULLTEXT KEY` or `SPATIAL KEY` line is an index with its type,
402
+ not a column. (#348, #349, #350)
403
+ - **MySQL column types read as the dumper writes them.** A schema.rb column
404
+ written as `t.column "kind", "enum('a','b')"` is listed with that type instead
405
+ of dropped, and the booted tier names MySQL enum, set and timestamp columns
406
+ the same way. (#351)
407
+ - **A schema.rb dumped with more than one schema names a public table by its
408
+ bare name** (`users`, not `public.users`), so `--table users` and the `User`
409
+ model find it; foreign keys, indexes and enum types follow. (#353)
410
+ - **The static tier reads the database the app configures.** The adapter comes
411
+ from `DATABASE_URL` or `<NAME>_DATABASE_URL` when set, and from a `url:` in
412
+ database.yml, as Rails merges them, and the replayed schema types its implicit
413
+ primary keys by that adapter. The schema comes from the dump the app
414
+ configures: database.yml's `schema_dump` name and `schema_format = :sql`
415
+ (database.yml on Rails 8.0.3 and later, else
416
+ `config.active_record.schema_format`, which Rails applies after every
417
+ initializer, and only then `ActiveRecord.schema_format`); the doctor, schema
418
+ version, tool guide, onboarding setup command and missing-table messages name
419
+ the same file, a configured dump over `max_schema_file_size` is reported as
420
+ too large, and a change to any `.sql` dump under `db/` refreshes the MCP
421
+ cache. (#355, #357)
422
+ - **The MCP cache, live reload and `watch` cover all of `app/`, `test/` and
423
+ `spec/`.** An edit to a mailer preview, a fixture, a factory, a spec or any
424
+ file under `app/` that a tool reads refreshes the cached answer. A bundler's
425
+ output in `app/assets/builds` does not, and a re-recorded VCR cassette does
426
+ not either.
427
+ - **A table in a secondary database is found** by `schema --table`,
428
+ `model_details`, `analyze_feature`, `diagnose`, `migration_advisor`, the
429
+ controllers schema hint and the model resource. The table view names every
430
+ database that holds it, and where one name holds different tables in two
431
+ databases it shows each database's columns and models. `migration_advisor`
432
+ passes `--database <name>` so the migration lands in that database's
433
+ migrations_paths. For a database only another environment configures (Rails
434
+ 8's queue, cache and cable outside production), the Run line sets that
435
+ `RAILS_ENV` and names its migrations_paths; when no environment configures
436
+ it, no Run line is given. It also leaves out `foreign_key: true` on a reference between
437
+ tables in different databases. A replica or a `database_tasks: false` entry is
438
+ not listed as a database of its own. (#356)
439
+ - **Migration replay follows what Rails runs.** It reads migrations in
440
+ subdirectories of `db/migrate` and in the migrations_paths database.yml sets
441
+ (for the primary and for a secondary with no dump yet), and the migrations
442
+ listing, the pending check and `doctor` read the same directories; a migration
443
+ file symlinked from outside the app is not read, and a secondary database's
444
+ line in `schema` names its pending migrations. A `revert` block is undone (a
445
+ nested one runs forward), and `revert SomeMigration` counts as a statement it
446
+ could not follow. One column is kept when both branches of an `if` declare it,
447
+ temporary and `if_not_exists` tables are skipped, `as:` and `execute "CREATE
448
+ TABLE"` tables are read (a heredoc over several lines too), check constraints
449
+ are replayed, `rename_index` is followed, and `Migration[5.0]` tables get
450
+ integer keys. An `execute` whose CREATE TABLE is built at run time counts as
451
+ not replayed. (#360, #361, #362, #363)
452
+ - **Static primary keys and table names read as Rails reads them.** A primary
453
+ key written as `id: { type: :string, limit: 36 }` is typed `string, limit:
454
+ 36`, and a model whose class, base class or app sets `pluralize_table_names =
455
+ false` reads its singular table with its columns. (#358, #359)
456
+ - **`model_details` prints every association option** (`as:`, `counter_cache:`,
457
+ `touch:`, `inverse_of:`, `extend:`, `before_add:`, the foreign key a
458
+ `has_many` or `has_one` declares and the rest) and an extension block's
459
+ methods; `callbacks` lists association callbacks; and `--no-boot` reads
460
+ `required: false` as optional and `query_constraints:` as the foreign key (a
461
+ `has_many` prints the option). (#296)
462
+ - **`model_details` reads more model declarations.** A `validate do ... end`
463
+ block is a custom validation with its `on:`/`if:`/`unless:` and first line. An
464
+ enum shows its `default:` and, with a prefix or suffix, the methods Rails
465
+ defines (`kind_lead?`). A prefixed delegate names the methods it defines
466
+ (`owner_name`) and `private: true` is marked. Each store column is listed once
467
+ with its accessors, including the names `prefix:` and `suffix:` give. Columns
468
+ dropped with `self.ignored_columns` are left out and listed on an "Ignored
469
+ columns" line. `encrypts`, `normalizes`, `serialize` and `generates_token_for`
470
+ show every option (`expires_in: 2.days`, or `never`), and a named
471
+ `has_secure_password :recovery_password` keeps its name. A Mongoid document's
472
+ schema hint in `controllers` names its collection and `_id` key instead of an
473
+ empty table, and a field shows its default. An array constant wrapped in
474
+ `Ractor.make_shareable` is listed under Constants. A validation, association,
475
+ scope or enum written in a method nothing calls is not listed. (#302, #305,
476
+ #306, #307, #309, #382, #391, #414)
477
+ - **`delegated_type`, `has_secure_token` and `accepts_nested_attributes_for` are
478
+ read.** The static tier lists the polymorphic `belongs_to` that
479
+ `delegated_type` declares, both tiers name its types (booted asks the model's
480
+ `<role>_types` when the source names them through a constant, and the static
481
+ tier names that constant as not read), and `model_details` lists
482
+ `has_secure_token` and `accepts_nested_attributes_for` under Macros. (#365)
483
+ - **`callbacks` and `model_details` read callbacks as Rails registers them.** A
484
+ callback the model removes with `skip_callback`, by name or by callback
485
+ object, is not listed (a conditional skip shows as the negated condition). A
486
+ callback object (`after_commit AuditTrail`, `before_validation
487
+ PostNormalizer.new`) prints as the class or expression the model passes. A
488
+ concern's `prepended do` block applies only to a class that prepends it, and
489
+ its `included do` block only to one that includes it. (#303, #304, #310)
490
+ - **Models outside `app/models` are found**, in both tiers: in another `app/*`
491
+ directory such as `app/domain`, or under a path `config/application.rb` adds
492
+ to `eager_load_paths` or `autoload_paths`, when its superclass is a model. A
493
+ class there that is not a model is left out even when it does not load, and
494
+ the lib subdirectories `autoload_lib(ignore:)` names are not read. The static
495
+ tier also finds models under a symlinked directory or file in `app/models`
496
+ that points inside the app, a model assigned `Class.new(ApplicationRecord)`
497
+ with what its block declares (the `self.table_name` it sets included), and
498
+ reads a job assigned `Class.new(ApplicationJob)`, or a class whose parent is
499
+ such a base, the same way. An assignment counts only when the file's path
500
+ names it, so an error constant such as `PaymentError = Class.new(StandardError)`
501
+ is never listed as a service, serializer or model. The static tier also
502
+ merges a module prepended after the class body (`Note.prepend(EE::Note)`,
503
+ GitLab's `prepend_mod_with("Note")`). (#344, #347, #413, #416)
504
+ - **Booted `model_details` no longer credits every model with a gem's base
505
+ mixins**, such as Kaminari's two modules and `page` and `default_per_page` on
506
+ the app's abstract base, or on a model that subclasses ActiveRecord::Base
507
+ itself, which Kaminari reaches the same way. (#371)
508
+ - **Method listings read more of how Ruby defines methods**, in `model_details`,
509
+ `concern`, `service_pattern` and every tool that reads a file's methods. A
510
+ `private` inside `class << self` or a `concerning` block no longer hides the
511
+ public methods and actions after it. `def Widget.x` is a class method,
512
+ `private_class_method` methods are private, a `def self.x` after `private`
513
+ stays public, and a `module_function` method is a module method
514
+ (`self.total(items)`). Methods defined with `alias`, `alias_method`, `attr_*`,
515
+ `define_method`, `class_attribute` and `cattr_*`/`mattr_*` are listed, an
516
+ inline `private attr_reader :x` or `protected attr_writer :y` keeping its
517
+ visibility, and `service_pattern` writes the class-side reader, writer and
518
+ predicate a `class_attribute` defines with `self.`. A method in a scope or
519
+ association extension block, a `concern :Name do` block, or a `Struct.new`,
520
+ `Data.define`, `Class.new` or `Module.new` block is not the enclosing class's;
521
+ one assigned to a constant is that constant's, and a `Module.new` held in a
522
+ local is the enclosing class's, where it is included. In `model_details` the
523
+ methods written with `def` come first, so a long run of delegations no longer
524
+ pushes them past the 25-row cap. (#297, #298, #308, #388)
525
+ - **A one-line or endless method is cut at its own line.** `callbacks` and
526
+ `concern --detail full` show `def strip_name; end` or `def wrap_save = yield`
527
+ under `(line N)` instead of running to the end of the class, and `search_code`
528
+ trace cuts such a body the same way. (#301)
529
+ - **`search_code` finds more definitions.** Definition and trace modes find
530
+ `private def x`, `def Widget.x`, `ruby2_keywords def x` and other modifier
531
+ forms; trace counts a call inside an endless def body as a call site; class
532
+ mode finds `class Billing::Ledger`, `class ::Top` and a constant assigned
533
+ `Data.define`, `Struct.new`, `Class.new` or `Module.new`. (#300)
534
+ - **A compact class name resolves its superclass as Ruby does.** `class
535
+ Api::UsersController < BaseController` gets the top-level `BaseController` as
536
+ its parent and its filters, on both tiers. The static model list, job chains,
537
+ service and component base detection, mailer chains and `generate_test` read a
538
+ compact class the same way, so `class Admin::Thing < Base` is not a model when
539
+ `Base` is a plain class. (#299)
540
+ - **`concern` lists a class that prepends the concern** under Included By.
541
+ (#311)
542
+ - **`controllers` lists block, lambda and object filters, in the order Rails
543
+ adds them to the chain** (after filters run last-declared first). A block or
544
+ proc filter (`proc {}`, `Proc.new {}`) reads `block (line N)` and a lambda
545
+ (`-> {}`, `lambda {}`) `lambda (line N)`, or `(line N of <file>)` when
546
+ written outside the controller's own file (a concern's class method, an
547
+ ancestor), each with its own `only:`/`except:`, in both tiers; every name a
548
+ `before_action :a, :b` call gives is listed in the order Rails runs them (a
549
+ lambda where it stands, the block last); the static tier reads
550
+ `prepend_after_action`, `prepend_around_action` and `append_around_action`,
551
+ each prepended filter at the front of the chain; `skip_forgery_protection`
552
+ shows as the `verify_authenticity_token` skip it is. An object filter
553
+ (`around_action TimingFilter.new`) reads `TimingFilter (object)` in both
554
+ tiers, where the booted tier printed a memory address. The booted tier leaves
555
+ out a block filter Rails or a gem adds (`allow_browser`, `rate_limit`), even
556
+ when the bundle is installed under the app root. The static tier names a gem
557
+ module the controller or one of its app bases includes
558
+ (`include ActiveStorage::SetBlob`) as an included module not read, where a
559
+ filter it adds would be missing, and leaves out a Rails module whose include
560
+ adds no filter (`ActionController::Live`). `http_basic_authenticate_with` is a
561
+ before filter with its `only:`/`except:`, never its credentials. (#293, #332,
562
+ #335)
563
+ - **`controllers` reads strong params and delegated actions.**
564
+ `params.expect(user: [tags: []])` reads as an array of scalars, and the
565
+ summary lists nested keys, arrays and `key: {}` hashes beside the flat
566
+ permits, a hash that names its keys with only those keys. An action defined
567
+ with `def_delegators`, `def_delegator` or `delegate` is counted, so
568
+ `controllers` and `validate` no longer report a route to one as missing.
569
+ (#333, #334)
570
+ - **`helper_methods` lists the view helpers declared with `helper_method`** in a
571
+ controller, a controller concern or a lib module the controller includes, each
572
+ with the class or module that declares it. (#336)
573
+ - **`test_info` reads fixtures and factories where Rails and factory_bot load
574
+ them.** Fixture files that open with an ERB line or use the `DEFAULTS` alias
575
+ are read, subfolder sets such as `admin/posts` stay apart, `_fixture` and the
576
+ labels its `ignore:` names are dropped, and directories a helper adds to
577
+ `fixture_paths` are read (inside `RSpec.configure do |config|` too, on
578
+ `ActiveSupport::TestCase` by name, and as a path built from `__dir__` or
579
+ `__FILE__`); an RSpec `spec/fixtures` no helper loads gives no sets, and a
580
+ file it cannot parse lists its labels. A fixture file that links out of the
581
+ app or to a sensitive file is not read, here or by `generate_test` and
582
+ `conventions`, and a file over `max_test_file_size` is named as too large. A
583
+ label or value ERB computes (`row_<%= i %>`) is shown as ERB, and such a label
584
+ is never written as a fixture key. Factories are found in `factories.rb`,
585
+ `factories/`, `test/factories.rb`, `spec/factories.rb` and each pack's
586
+ `spec/factories` and `test/factories` (both suites' when both exist). A helper
587
+ that sets or appends to `FactoryBot.definition_file_paths` is followed when it
588
+ then calls `FactoryBot.find_definitions` (added to the defaults
589
+ factory_bot_rails already loaded) or `FactoryBot.reload` (in place of them);
590
+ `config.factory_bot.definition_file_paths` is not read. Fabrication's
591
+ fabricators are listed, `onboard` names Fabrication as the data setup of a
592
+ fabricators-only suite, the generated context lists the fabricators directory,
593
+ and a Cucumber `features/` tree is counted. (#313, #373)
594
+ - **`i18n` reads the locale files Rails loads.** `.rb` locales are read for
595
+ their keys (the plural rule and transliteration settings under `i18n.plural`
596
+ and `i18n.transliterate` are not counted as keys), `.yaml` files Rails ignores
597
+ are not counted, files `config.i18n.load_path` adds or replaces (`+=`, `<<`,
598
+ `=`) are listed and counted in both tiers, a sensitive file a `load_path` glob
599
+ reaches is never read, and the Fallbacks section shows on a booted app whose
600
+ `config.i18n.fallbacks` sets only default locales. (#314, #315)
601
+ - **Initializers in subdirectories of `config/initializers` are read** for the
602
+ default locale, middleware, tagged logging, OmniAuth providers, component
603
+ preview paths, CORS, Rack::Attack, inflections and the shard selector, and
604
+ `config` lists them by their path, as Rails loads them at any depth. (#316)
605
+ - **`env` and `env_config` list what the app reads.** The environment variable
606
+ catalog drops `RAILS_EAGER_LOAD` and `RAILS_EVENT_REPORTER`, which Rails never
607
+ reads, and adds `SECRET_KEY_BASE_DUMMY`, `RAILS_DEVELOPMENT_HOSTS`,
608
+ `RAILS_GROUPS`, `RAILS_CACHE_ID`, `RAILS_APP_VERSION` and
609
+ `SOLID_QUEUE_IN_PUMA`. Credentials that decrypt to nothing are said to hold no
610
+ keys. External services called as `Net::HTTP.get(URI("..."))`,
611
+ `Net::HTTP.start("host")`, `Faraday.get("...")`, `RestClient`, `HTTP`
612
+ (http.rb), `Excon`, `Typhoeus` or `URI.open`, and HTTP clients in `lib/` or
613
+ `config/`, are listed; a loopback, private or unspecified (`0.0.0.0`) IP, a
614
+ file name handed to a client, and a call in a comment or a string are not.
615
+ `env_config` lists keys set with `<<`, `+=`/`||=`/`&&=`, an index write
616
+ (`config.action_dispatch.default_headers["X-Frame-Options"] = "DENY"`), a
617
+ write through an index read (`config.paths["app/views"] << "x"`), a method
618
+ call or a block, and not a call whose value is used (`x =
619
+ config.root.join("tmp")`), a method argument named `config`, or another
620
+ library's config (`OmniAuth.config.test_mode`). (#317, #318, #319, #320)
621
+ - **`active_support` lists files that sign with
622
+ `Rails.application.message_verifier`**, `message_verifiers` or
623
+ `ActiveStorage.verifier`, and not a file that only rescues
624
+ `MessageVerifier::InvalidSignature` or names one in a comment or a string.
625
+ (#321)
626
+ - **`search_docs` finds the 21 Rails guides the index lacked** (upgrading,
627
+ engines, autoloading, error reporting, generators, core extensions and more).
628
+ A guide newer than the app's Rails links to main and says so, since the app's
629
+ stable branch has no file for it. (#322)
630
+ - **`frontend_stack` names the asset pipeline and CSS and build tools.** A
631
+ Propshaft or Sprockets pipeline is named (with Sprockets' `manifest.js` link
632
+ lines), an app with no JavaScript build is said so, and an app is called
633
+ API-only only when `config.api_only` is set. webpack, rollup or bun is named
634
+ as a jsbundling-rails app's build tool, and `conventions` names Rollup and
635
+ reads bun from the text `bun.lock`. `conventions` and `frontend_stack` name
636
+ Tailwind CSS or Bootstrap on a `rails new --css` app. (#339, #340, #341)
637
+ - **`validate` checks Ruby syntax with the app's own Ruby grammar** (the running
638
+ Ruby when booted, the declared one with `--no-boot`, or the running Ruby when
639
+ the app declares none), so a file valid on Ruby 3.3 is not failed for a rule
640
+ 3.4 added. Ruby 3.1 and 3.2 are checked as 3.3, the oldest grammar prism has.
641
+ An app that declares JRuby or TruffleRuby and no Ruby version is checked with
642
+ the running Ruby's grammar, and a syntax error says so. (#342)
643
+ - **`autoload` lists every root Rails autoloads from**: app/models, app/lib,
644
+ app/services and the rest, other engines' app/* directories, a directory an
645
+ initializer adds with `push_dir` (with the namespace it loads under),
646
+ autoload-once paths added through `config.paths`, and directories a loader
647
+ keeps out of eager loading. (#345)
648
+ - **`job_pattern` reads more queues and schedules.** Channels show their
649
+ streams, timers and actions without booting, a `periodically` timer with a
650
+ block is listed in both tiers, and the connection says what it is
651
+ `identified_by`. A job's schedule comes from the task that runs it (Solid
652
+ Queue `config/recurring.yml` with its environment, sidekiq-cron,
653
+ sidekiq-scheduler, GoodJob `config.good_job.cron` including `merge!`,
654
+ whenever's `config/schedule.rb`); a job named only in a comment is not
655
+ scheduled, and `--detail full` lists every recurring task, on an app with no
656
+ job classes too. A schedule an ERB tag builds reads as computed. Without
657
+ booting, queues are named as ActiveJob names them, with `queue_name_prefix`,
658
+ `queue_name_delimiter` and `default_queue_name` applied, and a job with no
659
+ `queue_as` is on the default queue in both tiers. A Resque job's `@queue` (or
660
+ `def self.queue` when it sets none) and a Que job's `self.queue =` are read,
661
+ Que::Job subclasses are listed with their `run` signature, and `run` is the
662
+ entry point of a Que job only. (#330, #331, #366, #374)
663
+ - **`api` names the serialization layer and OpenAPI specs right.** Blueprinter
664
+ blueprints, Alba resources and RABL templates are named; an Active Job
665
+ argument serializer and a mixin module the serializers include are not counted
666
+ as response serializers. A file is an OpenAPI spec only when its top-level
667
+ `openapi` or `swagger` key holds a version number, found in `public/`, `doc/`,
668
+ `app/` and `config/` as well; a docs site's `_config.yml` and locale files are
669
+ not listed. A spec over the per-file read limit is still listed: the check
670
+ reads only the first 64KB of each candidate. `gems` says rswag-api serves
671
+ specs from its configured `openapi_root`, and names
672
+ `config/initializers/rswag_api.rb` when the app has it. (#367, #368, #375)
673
+ - **`onboard` names auth and serialization like the other tools.** An
674
+ "Authentication & Authorization" section covers Rodauth, Action Policy and
675
+ Rails 8 generated authentication; the Devise model is named with its modules,
676
+ those a concern's `included` block adds too; the authentication gem is still
677
+ named when the only auth finding is policy classes. A named
678
+ `has_secure_password :six_digit_code` is listed as a digest, not as login,
679
+ there and in the generated context. The API section names the
680
+ serialization as `api` does and is left out when there is none. `gems` lists
681
+ `action_policy` and `rolify` under auth. (#369)
682
+ - **`conventions` names more model patterns**: full-text search, tree structures
683
+ and soft deletes from `pg_search_scope`, `multisearchable`, `has_ancestry`,
684
+ `has_closure_tree` and `include Discard::Model`, multi-tenancy from
685
+ activerecord-multi-tenant, and a `discarded_at` column as soft delete. (#370)
686
+ - **`component_catalog` reads more components.** The usage example for a
687
+ `renders_many` slot calls the singular setter (`c.with_item do`), and the
688
+ one for a polymorphic slot calls each setter its `types:` define. A component
689
+ in the layout
690
+ `phlex:install` generates types as Phlex, read from its root even outside
691
+ `app/components` (`app/views/components`), and is named by the constant that
692
+ renders it (`Components::Badge`). (#372, #378)
693
+ - **`performance_check` no longer suggests `counter_cache: true`** on a
694
+ `belongs_to` whose count column `counter_culture` already keeps. (#376)
695
+ - **Rake tasks are read whole.** Tasks after a brace-form `namespace(:x) { }`
696
+ are not put inside it, `task "a:b"` names and `multitask` are listed, and the
697
+ Rakefile and `rakelib/*.rake` are read; a rake file symlinked out of the app
698
+ is not. (#384)
699
+ - **A tool failure's `At:` line names a frame outside the app and every gem by
700
+ its file name and line**, never an absolute path from the answering machine.
701
+ A frame inside an `eval` names the evaluated file the same way.
702
+
703
+ ## [5.31.0] - 2026-10-05
704
+
705
+ ### Changed
706
+
707
+ - **A committed placeholder is not a secret.** A file whose name ends in
708
+ `.example`, `.sample`, `.template` or `.dist` (`.env.example`) is readable by
709
+ `rails_get_edit_context` and the other file tools, although the default
710
+ `.env.*` pattern matches it, and `doctor` no longer lists it as unread or
711
+ says to gitignore it. A `sensitive_patterns` entry that names the file
712
+ exactly, with no glob characters, still blocks it, and so does a path
713
+ pattern with a `/` (`.ssh/*` keeps `.ssh/id_rsa.template` blocked).
714
+ Redaction treats the four suffixes alike. `rails_search_code` now drops
715
+ sensitive files from ripgrep's output where it used to exclude them by glob,
716
+ so a sensitive file ripgrep cannot open is not reported as an error and does
717
+ not send a no-match search through the Ruby fallback. (#267)
718
+ - **`rails_query` on a SQLite file stops at `query_timeout`.** The query runs
719
+ in a child process with its own read-only connection, which is stopped at the
720
+ deadline. sqlite3 2.x's `statement_timeout=` interrupts any statement of more
721
+ than a few thousand steps whatever the value, and the extension holds the GVL
722
+ while a statement runs, so no in-process limit exists. The child takes the
723
+ app's `extensions:` and waits on a locked database as the app would. An
724
+ in-memory database, a platform without `fork`, or a query that needs a
725
+ function, virtual-table module, collation or encryption key only the app's
726
+ own connection has runs in-process; the table and EXPLAIN answers then say it
727
+ ran without a time limit, and CSV output stays plain data. (#256)
728
+ - **`rails_get_callbacks` lists callbacks in the order Rails runs them**: base
729
+ classes first, a concern's callbacks where its `include` line stands, then
730
+ the model's own, and a `before_` or `around_` callback with `prepend: true`
731
+ first. `after_commit` and `after_rollback` list last declared first, as
732
+ Rails runs them, unless `run_after_transaction_callbacks_in_order_defined`
733
+ is on (`load_defaults 7.1` or later). The static tier reads that from
734
+ `config/application.rb` and the initializers, the last line that sets it
735
+ winning, and when the config does not tell, `rails_get_callbacks` and
736
+ `rails_get_model_details` say so. A method declared twice under one callback
737
+ type shows the declaration Rails keeps, the later one, which corrects the
738
+ 5.30.0 entry that said each declaration is shown. A declaration inside a
739
+ method body counts where the class calls the method, and not at all when
740
+ nothing does, so an uncalled method that redeclares a callback no longer
741
+ hides it. A call counts written bare or as `self.name`, made inside a class
742
+ method it runs, or made from a concern's `included` block. A module joins a
743
+ class chain once, at the first place the chain adds it: the outermost class
744
+ that adds it, at its first include there, which can be a call to a class
745
+ method that includes it. A Concern's `included` block runs there only; a
746
+ plain `self.included` hook runs on every include. A call runs the definition
747
+ Ruby's lookup finds as of the call: a module the class prepends to its
748
+ singleton class, the class's own class methods, then the modules it extends
749
+ and its concerns' class methods, the last added first, then the same for
750
+ each base, then what every model has: class methods an initializer writes in
751
+ `ActiveRecord::Base.class_eval` or a reopened base, the file Rails loads
752
+ later winning (Canvas's `validates_locale` adds its `before_validation` to
753
+ Account, User and Course), then the modules mixed into the base. One an
754
+ initializer writes on `ApplicationRecord` replaces ApplicationRecord's own.
755
+ A class's own class methods are its `def self.`, an `alias_method` in
756
+ `class << self` (it runs what its original name ran at the alias), and those
757
+ a Concern's `included` block or a hook writes on the class (`def self.x` or
758
+ `class << self`, in the block, in `base.class_eval` or in `class << base`),
759
+ each from its line where that code runs. A module counts from the line
760
+ adding it, where that line runs: an `extend`, `singleton_class.include` or
761
+ `singleton_class.prepend` (which counts as prepended) in the class, in a
762
+ hook or in a Concern's `included` block, and an `extend` in a class method a
763
+ call reaches (an `acts_as_x` that extends its methods, whose `self.extended`
764
+ hook then runs); a plain module's nested `ClassMethods` counts only when
765
+ something extends it. A module a hook includes runs its own hooks. Two
766
+ concerns' modules of one name (each concern's `ClassMethods`) stay two: each
767
+ name is the constant it resolves to where it is written, and one that
768
+ resolves to nothing no longer hides the other. A `super` runs the next
769
+ definition, at the `super`. An instance method or a nested class's method of
770
+ the same name never runs. A module a class method includes joins only when a
771
+ call reaches that method, and only calls after it see its methods. A nested
772
+ class's callbacks are its own, and a module nested in the model's file reads
773
+ like any concern: `include` runs its `included` block, `extend` its methods
774
+ and `self.extended` only. A module a plugin's lib loads by a glob `require`
775
+ is read from beside the file naming it, and from its definition rather than
776
+ an empty namespace stub above it, so OpenProject's journalized models list
777
+ `save_journals`. A Mongoid document's own callbacks list in the same run
778
+ order, one in a class method where the document calls it. Neither tier
779
+ evaluates `send`, `define_singleton_method` or a module included in
780
+ `class << self` for this, so the list can miss what they register or show
781
+ the definition they replace. (#253)
782
+ - **A lambda or Proc given to `queue_as` reads as what ActiveJob does with
783
+ it.** ActiveJob never calls it; it names the queue after the Proc's text.
784
+ Both tiers now say so, with the source, where the booted tier printed the
785
+ Proc's address and the static tier called it computed. A `queue_as` block is
786
+ the computed case and now shows its source too; a block argument
787
+ (`queue_as(&pick)`) reads as computed in both tiers. The 5.30.0 entry used
788
+ `queue_as -> { ... }` as its example of a computed queue; that was wrong.
789
+ (#271)
790
+ - **An MCP stdio session carries nothing but JSON-RPC on stdout for its whole
791
+ life.** Output written during a tool call (a logger built on first use, as
792
+ Sidekiq's is, `puts`, a child process) goes to stderr; only boot was guarded
793
+ before. (#255)
794
+ - **The booted `rails_security_scan` parses in its own process.** Brakeman's
795
+ forked parse workers could not return a parse error from an app that loads
796
+ web-console, and the scan failed where the static tier scanned. (#257)
797
+
798
+ ### Fixed
799
+
800
+ - **A concern's declarations name the module the include resolves to.**
801
+ `rails_get_callbacks` on OpenProject's WorkPackage lists
802
+ `after_save :save_journals` from `Acts::Journalized::SaveHooks`, where it
803
+ said `SaveHooks`.
804
+ - **A module declared in its outer module's file is read from there.**
805
+ Canvas's `Role::AssociationHelper` lives in role.rb, and its `included`
806
+ hook adds `before_save :resolve_cross_account_role` to Enrollment and its
807
+ subclasses, AccountUser and RoleOverride; the static tier called the
808
+ module unread and missed the callback. `rails_get_callbacks`,
809
+ `rails_get_model_details` and the controller filters and actions find it
810
+ there and read that module, not the outer class.
811
+ - **`rails_get_callbacks` lists models of one callback count by name**, so a
812
+ model gaining a callback no longer moves the sections around it.
813
+ - **A model macro written over `*args` reads each call's own arguments.**
814
+ The list binds to the call's positionals past the method's other
815
+ parameters, less the options hash the body takes off the end
816
+ (`args.extract_options!`), plus literals its leading statements push. A
817
+ block over the list is read once per item, so Canvas's
818
+ `validates_locale :locale, :browser_locale, allow_nil: true` lists an
819
+ inclusion validation on each field with its own `if: :<field>_changed?`, in
820
+ place of the macro's own row and one on a computed `field`. An options
821
+ hash, list or parameter the method changes in place (`options[:x] = 1`,
822
+ `options[:x] ||= 1`, `reverse_merge!`, `delete`) is not read as the call's
823
+ value. A declaration it decides, the method name included (`before_save
824
+ name` after `name.strip!`), is held back and listed under "Not decided from
825
+ the source (the method name or a condition)", the heading that was "Only
826
+ under a condition the source does not decide".
827
+ - **A `load_defaults` written after `belongs_to_required_by_default = false`
828
+ turns the default back on** in the static tier, as Rails does, so the
829
+ implicit presence of a required `belongs_to` is listed. A literal assignment
830
+ used to win wherever it stood. A value the source cannot evaluate
831
+ (`ENV.fetch(...) == "true"`), app-wide or in the class, makes the presence
832
+ conditional on it, named in the answer. A literal `nil` reads as off, as
833
+ Rails reads it, here and for the commit-order setting. A `belongs_to` with
834
+ `required: nil` is optional, and `required:` decides over `optional:` when
835
+ both are written, as in Rails.
836
+ - **`rails_get_callbacks` prints `on:`** for validation callbacks and any
837
+ callback whose type does not already name the event, before `if:` and
838
+ `unless:`. (#252)
839
+ - **`rails_performance_check` with `model` and `category` counts only what it
840
+ lists.** (#265)
841
+ - **`rails_search_docs` caches a fetched guide whole.** A guide with non-ASCII
842
+ text failed to write in the booted tier and left an empty file that read as a
843
+ cached guide for 24 hours; the write is now binary and atomic, and an empty
844
+ cache file is a miss. (#266)
845
+ - **`rails_search_code` trace finds one definition and no sibling of itself**:
846
+ `qa_ping` no longer matches `def qa_ping?`, `qa_ping!` or `qa_ping=`, and
847
+ `def self.qa_ping` is left out of its own sibling list, where a setter now
848
+ appears. Every method-body lookup (concerns, callbacks, actions, conventions)
849
+ ends a name the same way, and ripgrep and the Ruby fallback agree on
850
+ non-ASCII text, where any character outside ASCII is part of the name, as
851
+ Ruby reads it. (#258)
852
+ - **`rails_search_code` call sites skip comment lines** with the rule trace
853
+ uses, by file type: `#` in Ruby, `//` and `/*` in JS and TS, `/*` in CSS and
854
+ `//` too in SCSS, Sass and Less, `-#` in Haml, `/` in Slim. A stylesheet
855
+ line that starts with a `#id` selector is not a call site either. A line
856
+ holding a live ERB tag is a call site, and a spaced setter call
857
+ (`obj.qa_ping = 1`) is not a call of `qa_ping`. (#269)
858
+ - **Both search backends read the same excludes file.** The Ruby fallback read
859
+ `core.excludesFile` from the repository's own config, which ripgrep does not.
860
+ It now reads what ripgrep 15 reads: `~/.gitconfig`, then the XDG git config,
861
+ then the default ignore file. (#268)
862
+ - **A `db/structure.sql` table that `INHERITS` reads its parent's columns
863
+ first, then its own**, with multiple parents, chains, a parent defined later
864
+ in the file, and `ALTER COLUMN ... SET NOT NULL`/`SET DEFAULT`. A parent in
865
+ another schema resolves; one the file does not define is named. An index, key
866
+ or foreign key lands on the table its statement names, schema included. The
867
+ one-line form read its column type as `integer) INHERITS (...`. (#259)
868
+ - **A Stimulus controller under `app/javascript/controllers` keeps its
869
+ identifier** when another `controllers` directory holds the same path, and a
870
+ nested `controllers` directory stays in the name (`admin--controllers--x`).
871
+ (#264)
872
+ - **`query_allowed_columns` exempts a column from result redaction too**, as
873
+ it does from the pre-query check. Columns declared with `encrypts` stay
874
+ redacted. (#270)
875
+
8
876
  ## [5.30.2] - 2026-10-01
9
877
 
10
878
  ### Changed