rails-ai-context 5.27.0 → 5.29.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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +368 -0
  3. data/CONTEXT.md +37 -0
  4. data/docs/ARCHITECTURE.md +2 -2
  5. data/docs/INTROSPECTORS.md +16 -3
  6. data/docs/TOOLS.md +31 -12
  7. data/docs/TROUBLESHOOTING.md +13 -0
  8. data/exe/rails-ai-context +13 -4
  9. data/lib/rails_ai_context/action_filters.rb +1 -2
  10. data/lib/rails_ai_context/boot_manager.rb +23 -2
  11. data/lib/rails_ai_context/cli/entry_boot.rb +6 -0
  12. data/lib/rails_ai_context/cli/tool_runner.rb +10 -22
  13. data/lib/rails_ai_context/concern_macros.rb +1 -2
  14. data/lib/rails_ai_context/concern_paths.rb +6 -1
  15. data/lib/rails_ai_context/detail_level.rb +7 -4
  16. data/lib/rails_ai_context/doctor.rb +1 -2
  17. data/lib/rails_ai_context/fingerprinter.rb +1 -2
  18. data/lib/rails_ai_context/hydrators/controller_hydrator.rb +2 -4
  19. data/lib/rails_ai_context/hydrators/schema_hint_builder.rb +1 -1
  20. data/lib/rails_ai_context/hydrators/view_hydrator.rb +1 -2
  21. data/lib/rails_ai_context/install/cleanup.rb +23 -7
  22. data/lib/rails_ai_context/install/program.rb +11 -11
  23. data/lib/rails_ai_context/install_mode.rb +1 -2
  24. data/lib/rails_ai_context/introspectors/action_mailbox_introspector.rb +1 -2
  25. data/lib/rails_ai_context/introspectors/action_resolver.rb +45 -13
  26. data/lib/rails_ai_context/introspectors/action_text_introspector.rb +2 -4
  27. data/lib/rails_ai_context/introspectors/active_storage_introspector.rb +4 -8
  28. data/lib/rails_ai_context/introspectors/active_support_introspector.rb +8 -16
  29. data/lib/rails_ai_context/introspectors/api_introspector.rb +7 -15
  30. data/lib/rails_ai_context/introspectors/asset_pipeline_introspector.rb +2 -8
  31. data/lib/rails_ai_context/introspectors/auth_introspector.rb +11 -22
  32. data/lib/rails_ai_context/introspectors/autoload_introspector.rb +6 -12
  33. data/lib/rails_ai_context/introspectors/component_introspector.rb +3 -15
  34. data/lib/rails_ai_context/introspectors/config_introspector.rb +9 -64
  35. data/lib/rails_ai_context/introspectors/connection_pool_introspector.rb +5 -10
  36. data/lib/rails_ai_context/introspectors/controller_filters.rb +1 -2
  37. data/lib/rails_ai_context/introspectors/controller_introspector.rb +23 -114
  38. data/lib/rails_ai_context/introspectors/convention_introspector.rb +6 -50
  39. data/lib/rails_ai_context/introspectors/credentials_introspector.rb +3 -6
  40. data/lib/rails_ai_context/introspectors/database_stats_introspector.rb +3 -6
  41. data/lib/rails_ai_context/introspectors/declared_constant.rb +2 -4
  42. data/lib/rails_ai_context/introspectors/devops_introspector.rb +35 -56
  43. data/lib/rails_ai_context/introspectors/eager_load.rb +1 -2
  44. data/lib/rails_ai_context/introspectors/engine_introspector.rb +8 -15
  45. data/lib/rails_ai_context/introspectors/env_config_introspector.rb +32 -13
  46. data/lib/rails_ai_context/introspectors/env_introspector.rb +2 -4
  47. data/lib/rails_ai_context/introspectors/frontend_framework_introspector.rb +12 -31
  48. data/lib/rails_ai_context/introspectors/gem_introspector.rb +2 -4
  49. data/lib/rails_ai_context/introspectors/i18n_introspector.rb +48 -48
  50. data/lib/rails_ai_context/introspectors/initializer_introspector.rb +4 -8
  51. data/lib/rails_ai_context/introspectors/interaction.rb +127 -0
  52. data/lib/rails_ai_context/introspectors/job_introspector.rb +83 -27
  53. data/lib/rails_ai_context/introspectors/listeners/base_listener.rb +37 -8
  54. data/lib/rails_ai_context/introspectors/listeners/class_definition_listener.rb +2 -2
  55. data/lib/rails_ai_context/introspectors/listeners/generic_macro_listener.rb +15 -0
  56. data/lib/rails_ai_context/introspectors/listeners/mount_listener.rb +110 -20
  57. data/lib/rails_ai_context/introspectors/listeners/routes_dsl_listener.rb +11 -2
  58. data/lib/rails_ai_context/introspectors/middleware_introspector.rb +3 -6
  59. data/lib/rails_ai_context/introspectors/migration_introspector.rb +2 -4
  60. data/lib/rails_ai_context/introspectors/migration_replay.rb +21 -36
  61. data/lib/rails_ai_context/introspectors/model_introspector.rb +44 -25
  62. data/lib/rails_ai_context/introspectors/multi_database_introspector.rb +5 -10
  63. data/lib/rails_ai_context/introspectors/observability_introspector.rb +6 -12
  64. data/lib/rails_ai_context/introspectors/performance_introspector.rb +5 -30
  65. data/lib/rails_ai_context/introspectors/route_introspector.rb +59 -36
  66. data/lib/rails_ai_context/introspectors/schema_introspector.rb +36 -12
  67. data/lib/rails_ai_context/introspectors/schema_reader.rb +1 -2
  68. data/lib/rails_ai_context/introspectors/security_introspector.rb +9 -18
  69. data/lib/rails_ai_context/introspectors/source_introspector.rb +1 -2
  70. data/lib/rails_ai_context/introspectors/stimulus_introspector.rb +6 -51
  71. data/lib/rails_ai_context/introspectors/superclass_chain.rb +98 -0
  72. data/lib/rails_ai_context/introspectors/table_name.rb +1 -2
  73. data/lib/rails_ai_context/introspectors/test_introspector.rb +20 -46
  74. data/lib/rails_ai_context/introspectors/turbo_introspector.rb +19 -51
  75. data/lib/rails_ai_context/introspectors/view_introspector.rb +5 -10
  76. data/lib/rails_ai_context/introspectors/view_template_introspector.rb +29 -3
  77. data/lib/rails_ai_context/legacy_cleanup.rb +3 -1
  78. data/lib/rails_ai_context/mcp_config_generator.rb +42 -67
  79. data/lib/rails_ai_context/migration_status.rb +1 -2
  80. data/lib/rails_ai_context/package_json.rb +98 -0
  81. data/lib/rails_ai_context/payload.rb +54 -0
  82. data/lib/rails_ai_context/portable_path.rb +1 -2
  83. data/lib/rails_ai_context/redaction.rb +26 -10
  84. data/lib/rails_ai_context/resources.rb +1 -1
  85. data/lib/rails_ai_context/schema_adapter.rb +2 -2
  86. data/lib/rails_ai_context/serializers/claude_rules_serializer.rb +6 -34
  87. data/lib/rails_ai_context/serializers/copilot_instructions_serializer.rb +1 -37
  88. data/lib/rails_ai_context/serializers/cursor_rules_serializer.rb +1 -40
  89. data/lib/rails_ai_context/serializers/markdown_serializer.rb +4 -3
  90. data/lib/rails_ai_context/serializers/stack_overview_helper.rb +45 -2
  91. data/lib/rails_ai_context/serializers/tool_guide_helper.rb +7 -1
  92. data/lib/rails_ai_context/tasks/rails_ai_context.rake +5 -10
  93. data/lib/rails_ai_context/tools/analyze_feature.rb +68 -74
  94. data/lib/rails_ai_context/tools/base_tool.rb +60 -35
  95. data/lib/rails_ai_context/tools/dependency_graph.rb +17 -10
  96. data/lib/rails_ai_context/tools/diagnose.rb +33 -4
  97. data/lib/rails_ai_context/tools/generate_test.rb +71 -70
  98. data/lib/rails_ai_context/tools/get_api.rb +1 -6
  99. data/lib/rails_ai_context/tools/get_callbacks.rb +1 -5
  100. data/lib/rails_ai_context/tools/get_component_catalog.rb +1 -5
  101. data/lib/rails_ai_context/tools/get_concern.rb +146 -58
  102. data/lib/rails_ai_context/tools/get_config.rb +8 -2
  103. data/lib/rails_ai_context/tools/get_context.rb +33 -14
  104. data/lib/rails_ai_context/tools/get_controllers.rb +27 -35
  105. data/lib/rails_ai_context/tools/get_conventions.rb +25 -29
  106. data/lib/rails_ai_context/tools/get_engines.rb +9 -6
  107. data/lib/rails_ai_context/tools/get_env.rb +97 -108
  108. data/lib/rails_ai_context/tools/get_frontend_stack.rb +1 -5
  109. data/lib/rails_ai_context/tools/get_helper_methods.rb +58 -51
  110. data/lib/rails_ai_context/tools/get_job_pattern.rb +48 -28
  111. data/lib/rails_ai_context/tools/get_model_details.rb +44 -33
  112. data/lib/rails_ai_context/tools/get_partial_interface.rb +9 -20
  113. data/lib/rails_ai_context/tools/get_routes.rb +40 -10
  114. data/lib/rails_ai_context/tools/get_schema.rb +41 -13
  115. data/lib/rails_ai_context/tools/get_service_pattern.rb +142 -76
  116. data/lib/rails_ai_context/tools/get_stimulus.rb +10 -13
  117. data/lib/rails_ai_context/tools/get_test_info.rb +3 -9
  118. data/lib/rails_ai_context/tools/get_turbo_map.rb +4 -11
  119. data/lib/rails_ai_context/tools/get_view.rb +56 -28
  120. data/lib/rails_ai_context/tools/migration_advisor.rb +2 -4
  121. data/lib/rails_ai_context/tools/onboard.rb +16 -229
  122. data/lib/rails_ai_context/tools/performance_check.rb +28 -35
  123. data/lib/rails_ai_context/tools/runtime_info.rb +4 -11
  124. data/lib/rails_ai_context/tools/search_code.rb +15 -20
  125. data/lib/rails_ai_context/tools/search_docs.rb +1 -2
  126. data/lib/rails_ai_context/tools/section_fetch.rb +1 -2
  127. data/lib/rails_ai_context/tools/security_scan.rb +244 -58
  128. data/lib/rails_ai_context/tools/session_context.rb +1 -2
  129. data/lib/rails_ai_context/tools/validate.rb +1 -2
  130. data/lib/rails_ai_context/tools/validate_semantics.rb +17 -16
  131. data/lib/rails_ai_context/version.rb +1 -1
  132. data/lib/rails_ai_context/vfs.rb +30 -35
  133. data/lib/rails_ai_context.rb +15 -0
  134. data/server.json +1 -1
  135. metadata +4 -2
  136. data/lib/rails_ai_context/serializers/section_guard.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44038590682c51bc7722a4a5b587ee31b5b78f3842575b6386390a584038b81e
4
- data.tar.gz: 4c7de07da1f44144291ba86e9bfc34ab3a3da893c0a159b8cfe99de284d3d3be
3
+ metadata.gz: f036023367c149fd14eb8bc96106167cbd0c4ec7f042673930c74472a7caf44e
4
+ data.tar.gz: 1289481c7e5749263b762ebb9cc41a25504b4355896fd2215d2916e669132b34
5
5
  SHA512:
6
- metadata.gz: b581d96cb4ff655823a103c21527a53ddb71c3cb36d644a59485a796693001e247f6480b381d1f54ea8ec0995e175aa0389109fe1155bfbeea2dfeefd5566d9c
7
- data.tar.gz: 61c4aa682dd49afaf780652300f41de2216a0bfbe0b6ed7e6ec188237c63eade5036fe2051ba6771f89e96803a0971811230e532a7c623d7a25d0bfb1f069df8
6
+ metadata.gz: 5ad0b4e2f8096894ceec5a5c9989472173e8fabd98c38b1e9fc5e4e0ac3b7d90ad49473d6808f592f5d5b0c6ca9c6a897d7d2d6d79a064bbff0b6a44da19a0aa
7
+ data.tar.gz: ea624bf293f2e40a49d0d62579ca10c224200ab9e0f6681cdeb3f62955a599024a9ced4179f701f907d0b6ab9c55d5240ba595243a428b7b3920d47aade0b8d2
data/CHANGELOG.md CHANGED
@@ -5,6 +5,374 @@ 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.29.0] - 2026-09-23
9
+
10
+ Twenty-three QA reports against v5.27.0, each one a wrong answer a reader
11
+ could act on, plus what eight review rounds found in the fixes themselves.
12
+
13
+ ### Added
14
+
15
+ - **Model payloads carry method counts and the model's own methods
16
+ uncapped.** `instance_method_count`, `class_method_count` and
17
+ `source_instance_methods` sit beside the capped `instance_methods` and
18
+ `class_methods` lists, on both tiers, so a reader can tell a cut list from
19
+ a whole one.
20
+ - **The schema payload carries `declared_tables`**, what `db/schema.rb`
21
+ declares, on both tiers: nil for an app whose tables come from
22
+ `structure.sql` or the migrations.
23
+
24
+ ### Changed
25
+
26
+ - **"Mounted Engines" is "Mounted Apps" everywhere it is printed**: the
27
+ `engines`, `routes` and `onboard` tools, the generated context files, and
28
+ the MCP `rails://engines` resource name. The stack overview line reads
29
+ `Mounted:` rather than `Engines:`. Half of what the list holds are plain
30
+ Rack apps. The payload key stays `mounted_engines`.
31
+ - **A mount whose path the source does not spell out carries `path: nil`**
32
+ rather than the string `"unknown"`, and is printed without a path.
33
+ - **`routes` reads a blank `controller` as no filter**, and a name that
34
+ normalizes to nothing (`_controller`) as matching nothing, where the empty
35
+ string used to match every route key.
36
+
37
+ ### Fixed
38
+
39
+ - **A Sidekiq worker answers to its own name, and the bracket carries the
40
+ limit that governs it.** `job:"Billing::Invoices::CreateWorker"` answered
41
+ "No jobs found" for a worker the same tool had just listed, because the
42
+ single-job lookup read the ActiveJob list only. It reads both lists now, and
43
+ a name in neither is a not-found that names what exists. The listing prints
44
+ its "workers the introspector did not see" caveat whenever it prints
45
+ workers, rather than only when `config/sidekiq.yml` happens to exist, and a
46
+ `sidekiq_throttle` prints under the worker it throttles - 438 of 522 workers
47
+ on one app declared one and none of them showed it.
48
+ - **onboard's async section reads the workers out of the hash it already
49
+ had.** The section counted jobs, mailers and channels, so an app whose
50
+ background work is 522 Sidekiq workers read as "4 mailers." and the section
51
+ disappeared entirely when workers were the only async code. The "not
52
+ covered" line no longer prints next to a worker list it contradicts.
53
+ - **What an ActiveInteraction declares is read in one place.** A filter
54
+ declared inside another filter's block (`string :title` inside `hash
55
+ :order_params do`) is a key of that hash, not an input of the class:
56
+ `service_pattern` showed four inputs where `.filters` has two, and
57
+ `generate_test` passed the other two to `.run`, which drops them silently. A
58
+ subclass of the app's own base interaction is an interaction too, with its
59
+ parent's filters first, so `generate_test` stops emitting `.call`, which
60
+ `ActiveInteraction::Base` does not define. `GenericMacroListener` records
61
+ each call's own offset and its enclosing call's, which is what both tools
62
+ pair filters by.
63
+ - **generate_test names the constant the file declares.** A path camelizes
64
+ through Ruby's inflector, which has not read the app's
65
+ `config/initializers/inflections.rb` on the static tier, so
66
+ `ai_reports/build.rb` gave `AiReports::Build` where the app defines
67
+ `AIReports::Build` - a constant nothing defines, in a spec that dies on
68
+ load.
69
+ - **service_pattern looks for callers where the app keeps code.** The scan
70
+ named six `app/` directories, so a caller in `app/tools` or under `lib/` was
71
+ invisible. It reads every `app/` and `lib/` tree, plus whatever else a
72
+ booted app autoloads from, and says when the twenty-entry cap or its own
73
+ file ceiling left the list partial.
74
+ - **schema tells a missing migration from a typo.** Booted, a table declared
75
+ in `db/schema.rb` and absent from the connected database answered "Did you
76
+ mean 'comments'?". The payload carries the declared tables beside the live
77
+ ones, so the answer names the migration that has not run, and the listing
78
+ header says the two counts disagree instead of pairing a live table count
79
+ with the file's version stamp.
80
+ - **A validator under app/models/concerns is not a concern.** The type came
81
+ from the directory alone, so 37 `ActiveModel::Validator` subclasses on one
82
+ app were listed as model concerns used by nothing. They are listed as
83
+ validators - following the app's own validator base class, not one level of
84
+ compare - and looked up by the `validates_with`, or the validation option,
85
+ that wires them, in a model or in a concern's `included` block. The keys
86
+ `validates` reads for itself (`on:`, `if:` and the like) name no
87
+ validator.
88
+ - **dependency_graph counts both header numbers over the same models.** The
89
+ model count was app-wide and the association count covered the fifty nodes
90
+ that survived the cap. Both are app-wide now, and the truncation note says
91
+ how many of the associations the cut graph draws.
92
+ - **get_context reads the views Rails would resolve.** It handed `GetView`
93
+ the last segment of the controller path, so `Api::V1::Admin::OrdersController`
94
+ picked up `app/views/orders`, a directory of templates a background service
95
+ renders. The answer names the directory its views came from, and a
96
+ flat-directory fallback is labelled as one.
97
+ - **analyze_feature finds a test by its path.** A spec whose feature word is a
98
+ directory (`spec/services/billing/invoices/create_spec.rb`) was dropped,
99
+ while the gap checker beside it already matched on the path. The suite's
100
+ own words stay out of the match - the `spec/` root, the type directory it
101
+ files a test under (`models/`, `requests/`), and the `_spec` suffix - so
102
+ `--feature models` is not every model spec.
103
+ - **routes answers an exact controller key with its own routes.** A substring
104
+ filter returned a nested sibling's routes too (`api/v1/admin/orders` swept
105
+ in `api/v1/admin/orders/ai_data`), and `get_context` inherited it. A short
106
+ name still matches every controller that carries it.
107
+ - **A template at the root of app/views is listed.** Its filename became a
108
+ directory group that matched nothing, so the header counted a file the body
109
+ never printed, and the controller-miss hint suggested a directory that does
110
+ not exist.
111
+ - **A word in a quoted string is not an instance variable.** `view` reported
112
+ a chat handle inside a Ruby string literal as a template's ivar; the reader
113
+ strips string literals and keeps interpolation, in ERB tags and in the
114
+ Ruby template handlers (Jbuilder, Builder, `.ruby`), and `get_view`'s
115
+ hydrator reads through the same method. Whichever quote opens first owns
116
+ the literal, so an apostrophe inside `"Don't"` does not swallow the ivar
117
+ beside it.
118
+ - **env_config tells a re-assignment from a tuple.** Two unconditional
119
+ assignments of one key rendered as `:file, :test`, which reads exactly like
120
+ `:mem_cache_store, { pool_size: 5 }`. The winner is named, with what it
121
+ overrode.
122
+ - **env keeps one default per call site.** One label for every site said a
123
+ variable was optional while one of its reads was `ENV.fetch` with no
124
+ default, which raises `KeyError`. A fetch whose fallback is an expression
125
+ says its default is computed at runtime rather than claiming it has none,
126
+ and an `ENV["X"]` read says it is nil when unset, apart from the fetch
127
+ that raises.
128
+ - **A Rack app attached with `match ... to:` is found.** `mount` is that call
129
+ with a name derived, and the exact-path form is what an app writes when an
130
+ unanchored mount would swallow a sibling path. It reaches `engines` and
131
+ `routes`, which names the mounted apps it counts instead of calling them
132
+ engine mounts, and the booted tier lists every Rack endpoint rather than
133
+ `Rails::Engine` subclasses alone. A mount inside a `namespace` or `scope`
134
+ carries that prefix; one whose enclosing scope, or its `path:`, is an expression is listed
135
+ with no path rather than an unprefixed one, and every list prints it
136
+ without one. `engines` follows every file `config/routes.rb` draws,
137
+ through the walk the static `routes` answer uses, so on the static tier
138
+ the two name one set of mounted apps; booted, `routes` reads the live
139
+ route table. `engines`, `onboard`, the MCP resource and the
140
+ generated context files head the same list "Mounted Apps", because half of
141
+ what it holds are not engines. On the static tier a `match ... to: SomeApp`
142
+ is counted once, as the mount it is, rather than also as a construct the
143
+ walk could not expand, and so are `get "/status" => StatusApp` and
144
+ `mount ActionCable.server => "/cable"`, which the walk did not see at all.
145
+ - **config calls a zero-byte initializer empty** rather than "all commented
146
+ out".
147
+ - **Smaller corrections in the same pass.** `validate_semantics` asks the
148
+ loaded model class before calling a callback method missing, and on the
149
+ static tier makes no claim once the payload's method list was cut at its
150
+ cap, so an inherited method past the cap is no longer reported as missing.
151
+ `get_controllers` points
152
+ `rails_get_view` at the controller's full path rather than its last segment,
153
+ which is the directory Rails resolves. The stack overview's `Engines:` line
154
+ is `Mounted:`, because half of what it lists are plain Rack apps.
155
+ - **diagnose stops reading a display cap as a model's whole interface.** A
156
+ method past the thirtieth was reported as not existing, in the same answer
157
+ whose Method Trace printed its definition. Booted, diagnose asks the loaded
158
+ model class, which knows a concern's methods and a gem's as well as the
159
+ model's own. Statically, the model's own methods travel uncapped beside the
160
+ capped display list, and where a concern or a parent could define the
161
+ method the answer declines rather than guesses. `model_details` says how
162
+ many of the model's methods it is showing.
163
+ - **A CamelCase controller name resolves everywhere.** The needle was
164
+ downcased without being underscored, so "GiftCards" never equalled the
165
+ route key's own "gift_cards": `Payload.find_controller` missed it, and every
166
+ tool that resolves a controller through it missed it too. One normalization
167
+ now serves the payload, the routes tool and the MCP resource, and the
168
+ resource answers a name that resolves to nothing with an error naming what
169
+ exists rather than a zero-route success document.
170
+ - **security_scan runs the brakeman the machine has.** The app's bundle
171
+ narrows the load path, so a machine with brakeman installed was told to add
172
+ it to the Gemfile while the other tier scanned the same app. When the
173
+ in-process require fails and the gem is installed, the scan runs it as its
174
+ own process outside the bundle - reading the report from a file of its own,
175
+ since a gem manager's binstub can print to stdout first - and renders the
176
+ result the same way, with a line saying which brakeman answered and from
177
+ where. When the outside run writes no report, the answer carries the last
178
+ line brakeman printed about why. With no brakeman anywhere, the message
179
+ says that instead of guessing, and the availability answer is keyed by
180
+ tier rather than decided once per process.
181
+ - **An exact search with a space keeps its context lines on ripgrep 13.**
182
+ The literal was escaped with Ruby's `\ `, which ripgrep 13 (Ubuntu 22.04,
183
+ Debian 12) rejects, so the search fell back to the Ruby scan and dropped
184
+ context lines and files with no listed extension. The space goes through
185
+ unescaped now, which both engines read the same way.
186
+
187
+ ## [5.28.0] - 2026-09-22
188
+
189
+ Thirty-six changes an architecture survey of v5.27.0 asked for, nine of them
190
+ defects where a user got a wrong answer, plus what three review rounds found in
191
+ the fixes themselves.
192
+
193
+ ### Added
194
+
195
+ - **Services, helpers, jobs and concerns are found wherever the app keeps
196
+ them.** The four tools globbed `app/<kind>` off the app root, so a packwerk
197
+ app whose services live under `packs/billing/app/services` was told it had no
198
+ services directory and may not use the pattern. Directory discovery reads
199
+ `PathResolver.dirs_for`, which already resolved packs, engines and
200
+ `extra_app_paths` for the payload side. `ConcernPaths.resolve` and
201
+ `ActiveSupportIntrospector` read the same resolver, so a concern's listing,
202
+ its lookup and its "Used By" line now agree on one app. Counts move on any
203
+ app with packs or engines. Each walk names the directory a file came from, so
204
+ two files of the same basename under different roots no longer collide, and a
205
+ tool that finds nothing names all three directory patterns it searched
206
+ instead of the conventional one alone.
207
+ - **`RailsAiContext.debug_fail`.** About 258 rescue bodies in `lib/`
208
+ hand-rolled the same three lines: rescue the error, write a `DEBUG`-gated
209
+ warning to stderr, return a fallback. They call one method now, with every
210
+ message string and every fallback value unchanged. `exe/rails-ai-context`
211
+ shims it beside `log_warn`, because the standalone install path loads seven
212
+ files by hand and a converted rescue inside `install_mode.rb` would otherwise
213
+ raise `NoMethodError` after the removed-tool cleanup had already deleted
214
+ files.
215
+ - **`PackageJson`.** GemLock's twin for the node side: `package.json` parsed
216
+ once per root, `dependencies` merged with `devDependencies`, capped by
217
+ `configuration.max_file_size` rather than by a constant no caller can reach,
218
+ and empty for anything it cannot read. A scope named after the package
219
+ counts, so an app reaching tailwind through `@tailwindcss/vite` keeps its
220
+ answer.
221
+
222
+ ### Removed
223
+
224
+ - **`config.job_processor`, gone from `.ai-context.json` and from
225
+ `get_config`.** `ConfigIntrospector` parsed `config/sidekiq.yml` a second
226
+ time to publish it, and nothing read it: no renderer, no spec, no template.
227
+ The two readers disagreed, so a `:queues:` block written with weights,
228
+ `- [critical, 2]`, lost `critical` on the config side and kept it on the jobs
229
+ side, and one sidekiq.yml shipped two queue lists in one payload. The same
230
+ concurrency and queues stay under `jobs.sidekiq_config`, which
231
+ `get_job_pattern` already consumes, and `config.queue_adapter` still names
232
+ the adapter. Both sections sit in the standard and full presets, so no preset
233
+ loses the fact.
234
+ - **`action_bindings` and `outlet_controllers`, gone from every Stimulus
235
+ controller entry.** Filling `action_bindings` read every ERB, HAML and Slim
236
+ file under `app/views` and `app/components` on every generation, and
237
+ `outlet_controllers` restated the outlets list one line above it. Nothing in
238
+ `lib/` read either key: `get_stimulus` renders outlets, and the compact
239
+ serializer touches the section through `controllers` and `total_controllers`.
240
+ Both keys leave the artifact, and one full pass over the view and component
241
+ trees leaves every generation.
242
+ - **`Serializers::SectionGuard`, `DetailLevel::SCHEMA_ENUM` and
243
+ `Install::Program.select_tool_mode`.** SectionGuard was 15 lines forwarding
244
+ to `Tools::SectionFetch.usable?`; both callers sat in `SchemaAdapter`, which
245
+ is not a serializer, and `RouteCoverage` already called SectionFetch
246
+ directly. SCHEMA_ENUM went with its last reference when
247
+ `DetailLevel.schema(description)` replaced the same four-line input-schema
248
+ hash in 21 tools; every published schema is byte-identical, and
249
+ `rails_onboard` keeps its own literal because its levels are quick, standard
250
+ and full. `select_tool_mode` was a shim for `select_setup(surface).tool_mode`
251
+ with no caller left in `lib/`, and it cannot express the MCP-config-only
252
+ mode. `ModelIntrospector#sti_bases` and
253
+ `ControllerIntrospector#extract_permit_details` go with them; only their own
254
+ recursion and their own specs were calling them.
255
+
256
+ ### Changed
257
+
258
+ - **`.github/instructions/rails-context.instructions.md` prints every gem
259
+ category.** `.first(6)` on the grouped Hash returned the first six pairs, so
260
+ the seventh category onward was dropped with nothing saying so, while
261
+ `.cursor/rules/rails-project.mdc` rendered the same grouping uncapped. One
262
+ payload, two gem lists.
263
+ - **`.cursor/rules/rails-project.mdc` bolds `Global before_actions:`** like the
264
+ Claude and Copilot files. The three serializers each hand-wrote the same
265
+ ten-step overview and had drifted; `StackOverviewHelper#overview_lines`
266
+ states the body once and each renderer keeps only its frontmatter, title and
267
+ MCP hint.
268
+ - **Puma settings are read inside blocks.** The hand-rolled recursion stopped
269
+ at the top level, so a `workers` line guarded by an environment conditional,
270
+ which is what the generated `puma.rb` ships, was invisible and the answer
271
+ read as no workers configured. `MethodCallListener` sees a receiverless call
272
+ at any depth. The cost of the widening is that nesting no longer hides a
273
+ setting, so a name set twice reports the last one wherever it sits. Also
274
+ `threads ENV.fetch("RAILS_MIN_THREADS") { 5 }, ENV.fetch("RAILS_MAX_THREADS")
275
+ { 5 }` reports 5 and 5 where it reported nothing, and the digit match is
276
+ anchored away from word characters, so `ENV.fetch("PORT_2", 3000)` no longer
277
+ reads as 2.
278
+ - **`get_conventions`' frontend stack string reads parsed dependencies.**
279
+ `detect_frontend_stack` matched bare substrings against the raw file, with no
280
+ quotes at all, so `vite-plugin-ruby` on its own reported Vite. The markers
281
+ carry alternates where an exact name would have lost an answer the substring
282
+ caught: svelte or `@sveltejs/kit`, `@hotwired/turbo` or
283
+ `@hotwired/turbo-rails`.
284
+ - **`api_introspector`'s codegen list reads parsed dependencies.** It searched
285
+ the file for the quoted tool name, so `openapi-typescript`, `orval` or
286
+ `@graphql-codegen/cli` named anywhere in `package.json`, an `overrides` block
287
+ included, counted as a client generator the app runs.
288
+ - **`rails_generate_test` resolves a controller by the rule
289
+ `rails_get_controllers` uses.** It camelized the string and looked the result
290
+ up, so with `Admin::GiftCardsController` as the only gift-card controller,
291
+ `rails_get_controllers(controller: "gift_cards")` resolved and
292
+ `rails_generate_test(controller: "gift_cards")` answered "not found".
293
+ `Payload.find_controller` answers for both, with the camelize kept as the
294
+ fallback for a payload carrying no controllers at all.
295
+
296
+ ### Fixed
297
+
298
+ Nine defects the survey confirmed against v5.27.0, and the defects three review
299
+ rounds found in the work that fixed them.
300
+
301
+ - **`onboard(detail: "quick")` no longer prints a guessed domain noun.** Eleven
302
+ ordered regexes read model, job and service names and stated the result as
303
+ plain fact with no confidence marker, so one model named `Message` made an
304
+ app "a messaging app". Quick mode now states what it measured: versions,
305
+ table, model and job counts, the frontend and the test framework.
306
+ - **`api_namespaces` invented a namespace and missed one.** The two tiers
307
+ derived the list their own way. The booted regex kept a trailing slash on
308
+ `/api/users`, matched unanchored so `namespace :admin { namespace :api }`
309
+ reported `/api/v1` for an app that serves `/admin/api/v1`, and dropped a
310
+ route sitting at exactly `/api`. The static spelling got the segment boundary
311
+ wrong, so `/apidocs` reported `/api`. Both tiers read one anchored form.
312
+ - **Turbo drive settings were double-counted for layouts.** `app/views/layouts`
313
+ was walked again after `app/views/**/*` had already reached it. The permanent
314
+ element pass threw its duplicate away through `uniq`; the drive settings did
315
+ not, so one attribute in a layout and one in a view printed as 3.
316
+ - **The asset pipeline reported the wrong bundler and CSS framework.** Three
317
+ readers asked whether a package was present by searching the raw file for its
318
+ quoted name, so an app pinning CVE fixes in an `overrides` block was told
319
+ esbuild is its bundler and postcss its CSS framework, while the one reader
320
+ that parsed the file called the same app vite. Both answers landed in one
321
+ payload.
322
+ - **`analyze_feature`'s Jobs, Mailers and Channels sections came from a glob.**
323
+ Each job's queue was read off its own `queue_as`, so a job inheriting the
324
+ queue from `ApplicationJob` was reported on a queue it does not use. The
325
+ three sections read the payload now, which carries what the booted tier
326
+ resolved, mailer actions and channel stream methods included. A section whose
327
+ payload entry is not there, because the `:jobs` introspector is off, is left
328
+ out rather than rendered from a directory walk.
329
+ - **`performance_check` attributed queries to methods that are not actions.** A
330
+ `def` inside a nested class became a call site, and a one-line
331
+ `private def set_post` leaked its body into the action above it.
332
+ `ActionResolver` answers what an action is here, as it does everywhere else.
333
+ A second collision went with it: each body was found by searching the whole
334
+ file for the first `def <name>`, so a nested class defining the same name
335
+ earlier handed back its body and the real action was never scanned. The body
336
+ is cut from the lines the owner-filtered walk recorded, and `get_controllers`
337
+ and `get_context` share the seam. An empty owner filter falls back to the
338
+ line scan rather than answering nothing, and `def self.index` no longer
339
+ stands in for `def index`.
340
+ - **A model whose only rule is `validate :method` printed its bullets under the
341
+ wrong heading.** `## Validations` was emitted only when the reflected list
342
+ had entries, and the custom bullets come from a second list that is disjoint
343
+ from it on both tiers, so they landed under Associations.
344
+ - **Two ERB readers missed `<%== ... %>` and read a commented ivar as used.**
345
+ `check_instance_variable_usage` and `extract_local_variable_references` each
346
+ carried their own copy of the tag regex, byte for byte the same and neither
347
+ matching `ErbSource::TAG`. Both read `<%[=\-]?`, one optional character, so
348
+ `<%== title %>` parsed as a body of "= title" and the local never reached a
349
+ partial's expected locals. The comment skip was a second answer too:
350
+ `get_partial_interface` re-implemented it and `validate_semantics` never had
351
+ one, so `<%# @ghost %>` was reported as an ivar used in the view and not set
352
+ in the controller. Both readers ask `ErbSource` now.
353
+ - **`rails_get_controllers` refused a name the VFS resource accepts.** VFS
354
+ resolved five ways, the tool two, so `controller: "gift_cards"` failed
355
+ against `Admin::GiftCardsController` while the resource answered off the same
356
+ payload. Both read `Payload.find_controller`, so a route key, an unambiguous
357
+ basename and the singularize and classify spellings now resolve in the tool
358
+ too.
359
+ - **Two files behind one name no longer hide each other.** Once services,
360
+ helpers and concerns were read from packs and engines, a name could have more
361
+ than one file behind it. The ambiguous-service list printed
362
+ `app/services/<path>` for files that live under a pack, so two candidates
363
+ printed as two identical lines naming a file that exists in neither, and the
364
+ suggestion that followed handed back the name it had just refused. Paths
365
+ print from the app root, and a narrowing suggestion is offered only when some
366
+ candidate's relative path is unique. `rails_get_helper_methods` rendered the
367
+ first match as the whole module and listed the others under "Also defined
368
+ in" even when they declare different modules, so a file declaring
369
+ `Reports::DashboardHelper` was named under an `Admin::DashboardHelper`
370
+ heading; matches are split by the module each file declares.
371
+ `rails_get_concern` broke on the first directory that resolved, so an app
372
+ with both `app/models/concerns/trackable.rb` and
373
+ `app/controllers/concerns/trackable.rb` saw only the controller one. Every
374
+ "available" list is deduplicated where it is built.
375
+
8
376
  ## [5.27.0] - 2026-09-22
9
377
 
10
378
  ### Added
data/CONTEXT.md CHANGED
@@ -88,6 +88,43 @@ Three senses inside the gem, and the payload one is wider than either everyday R
88
88
 
89
89
  The callable interface of a class as this gem reports it: the class's own public instance methods - a class nested in the same file is a separate owner, not part of the interface - minus framework-shaped `_` names, read source-first, with reflection minus the app-owned base as the honest fallback. `ActionResolver` is the one answer; controller and mailer are configurations of it, and a channel's "stream methods" are a narrower selection of the same reading.
90
90
 
91
+ ## Mounted app
92
+
93
+ A Rack app the routing table attaches at a path, engine or not. `mount App =>
94
+ "/path"` is `match("/path", to: App, via: :all, anchor: false)` with a name
95
+ derived, so both spellings build the same endpoint and both are this. The
96
+ payload key is `mounted_engines` for the sections that predate the widening;
97
+ what it holds is every controller-less, non-dynamic endpoint the route set
98
+ carries, which is what the count beside it has always counted.
99
+
100
+ Distinct from a **Rails engine**, which is a `Rails::Engine` subclass whether
101
+ or not anything mounts it, and which `rails_get_engines` lists separately under
102
+ its loaded classes. The record key is `engine:` for the same reason the section
103
+ key is `mounted_engines:` - it predates the widening, and what it holds is the
104
+ mounted app's constant, engine or not.
105
+
106
+ ## Interaction filter
107
+
108
+ What an ActiveInteraction service declares as its interface, and not the
109
+ **filter chain** above, which is controllers. `Interaction` is the one answer
110
+ for both tools that read it (`rails_get_service_pattern`,
111
+ `rails_generate_test`), because a static answer that differs from the booted
112
+ one is the divergence the module exists to end.
113
+
114
+ **A nested filter is not an input.** `string :title` inside `hash
115
+ :order_params do ... end` is a key of that hash: it never reaches `.filters`,
116
+ and handing it to `.run` as a keyword argument is dropped without a word. So
117
+ nesting is kept on the record rather than flattened, and only the top level is
118
+ an input.
119
+
120
+ **The chain is the class's, not the file's.** An interaction is often a
121
+ subclass of the app's own base interaction rather than of
122
+ `ActiveInteraction::Base`, and its filters are then the parent's first and its
123
+ own after, which is the order `.filters` answers in. Following that chain needs
124
+ the parent's source, which only the caller can find, so it arrives as
125
+ `lookup` - a callable from a constant name to that class's source, and nil to
126
+ stop at the one file.
127
+
91
128
  ## Filter chain
92
129
 
93
130
  Which filters a controller runs, and which of them a given action runs. `ActionFilters` is the one answer, and every controller surface reads its filter line from there, so no two answers can disagree about what a class inherits or skips. `for_controller` answers about the class; `for` answers about one of its actions. Both return `own`, `inherited` and `skipped`.
data/docs/ARCHITECTURE.md CHANGED
@@ -110,11 +110,12 @@ flowchart LR
110
110
 
111
111
  The `Introspector` orchestrator runs configured introspectors and merges results.
112
112
 
113
- Three modules answer questions every introspector used to answer for itself:
113
+ Four modules answer questions every introspector used to answer for itself:
114
114
 
115
115
  - **SourceScan** - one walk over a kind of app source, across every directory `PathResolver` resolves: `paths` stats, `each` reads, `classes` names by the declared constant
116
116
  - **EagerLoad** - loads a directory's constants for a booted-tier walk, one file at a time, so an unloadable file costs only itself
117
117
  - **GemLock** - which gems the app resolved and at what version, read once per lockfile and matched by exact name across GEM, GIT and PATH
118
+ - **PackageJson** - which npm packages the app depends on, read once per `package.json`: present means named in `dependencies` or `devDependencies`, so an `overrides` pin is not a dependency, and `@tailwindcss/vite` counts as tailwindcss
118
119
 
119
120
  Two more answer a question a tool asks:
120
121
 
@@ -198,7 +199,6 @@ Result: controller and view tools automatically include relevant schema informat
198
199
  | `ToolGuideHelper` | MCP/CLI tool reference sections |
199
200
  | `TestCommandDetection` | Test framework detection |
200
201
  | `SectionFacts` | The facts every surface states about an app - auth, assets, associations, the filter chain, an unread entry's row, the static-tier notice - each rendered in one place |
201
- | `SectionGuard` | Whether a section resolved, so a refused one is not rendered |
202
202
  | `SectionMarkerWriter` | Writes a managed section into a file the user also owns |
203
203
  | `ContextModeDispatch` | Picks full or compact rendering for a run |
204
204
 
@@ -126,7 +126,7 @@ end
126
126
 
127
127
  | Introspector | Key | What it extracts |
128
128
  |:-------------|:----|:-----------------|
129
- | JobIntrospector | `:jobs` | Background jobs, Sidekiq workers under `app/workers`, and mailers: queue, retries, `sidekiq_options`, schedules, and the `file:` each one is defined in |
129
+ | JobIntrospector | `:jobs` | Background jobs, Sidekiq workers under `app/workers`, and mailers: queue, retries, `sidekiq_options`, any `sidekiq_throttle`, schedules, and the `file:` each one is defined in |
130
130
  | RakeTaskIntrospector | `:rake_tasks` | Custom rake tasks |
131
131
 
132
132
  ### Security & Auth
@@ -193,7 +193,7 @@ Passed to `SourceIntrospector.walk(path, key => Listener)` when a specific file
193
193
 
194
194
  | Listener | What it detects |
195
195
  |:---------|:---------------|
196
- | GenericMacroListener | Any receiver-less macro you name: `GenericMacroListener.new(:devise, :rate_limit)`. Returns args, values (with a source-slice fallback), options, option values and option nodes |
196
+ | GenericMacroListener | Any receiver-less macro you name: `GenericMacroListener.new(:devise, :rate_limit)`. Returns args, values (with a source-slice fallback), options, option values and option nodes, plus the nesting: `parent_offset` is the offset of the target macro call whose block this one sits in, paired against each call's own `offset` rather than its line |
197
197
  | ChainedCallListener | Calls on a receiver: `ChainedCallListener.new(:includes)`, or `receiver: :inflect` to pin the receiver. Reports the receiver name |
198
198
  | ConfigAssignmentListener | `config.key = value` and `config.a.b = value` in initializers and `config/environments/*.rb`, plus bare `config.jwt do ... end` section references. Takes a root name (`:config` by default, e.g. `:DatabaseCleaner`) |
199
199
  | ClassDefinitionListener | Class definitions with their superclass, namespaces resolved |
@@ -202,7 +202,7 @@ Passed to `SourceIntrospector.walk(path, key => Listener)` when a specific file
202
202
  | SchemaDslListener | `schema.rb`: `create_table`, `t.string`, `t.index`, `add_foreign_key`, `create_enum` |
203
203
  | MigrationDslListener | Migration DSL: `create_table`, `add_column`, `add_index`, `add_reference`, and friends |
204
204
  | RoutesDslListener | `config/routes.rb`, resolving namespace/scope/resources nesting into flat routes; routing concerns (`concern` definitions replayed at each `concerns:` site), `with_options` defaults merged under each inner call, and the `as:`, `param:`, `module:`, `path:` and `only:`/`except:` options |
205
- | MountListener | `mount Sidekiq::Web, at: "/sidekiq"` and the hash form |
205
+ | MountListener | `mount Sidekiq::Web, at: "/sidekiq"`, the hash form, and a Rack app attached with `match "/metrics", to: MetricsApp` - `mount` is that call with a name derived. Paths carry the enclosing `namespace`/`scope` prefix; a scope whose own name is an expression yields no path rather than an unprefixed one |
206
206
  | GemfileDslListener | `gem "name", "version"` and `group :development do ... end` |
207
207
  | RakeTaskDslListener | `namespace`, `desc`, `task` in `.rake` files |
208
208
  | EnvAccessListener | `ENV["KEY"]`, `ENV.fetch("KEY")`, `ENV.fetch("KEY", default)` |
@@ -233,6 +233,19 @@ Regex is the right tool, and stays, for:
233
233
 
234
234
  Every remaining regex over `.rb` content carries a one-line comment saying which of these it is. If you add one without a reason, convert it instead.
235
235
 
236
+ ### Readers built on the listeners
237
+
238
+ Some questions take more than one walk to answer, and the answer has to be the
239
+ same wherever it is asked. Those live as their own modules under
240
+ `Introspectors/`, and a tool calls one rather than repeating the walk:
241
+
242
+ | Module | What it answers |
243
+ |:-------|:---------------|
244
+ | `DeclaredConstant` | The constant a source file calls its own class, against the one its path camelizes to |
245
+ | `TableName` | The table a model reads, from its own declarations |
246
+ | `SuperclassChain` | What a class inherits from, followed through the app's own sources: the chain from a file's class up to a named base, and the constant-to-source lookup over the app's autoload roots that walks it |
247
+ | `Interaction` | Whether a class runs as an ActiveInteraction, following its superclass chain through the app's own sources, and the filters it takes - inherited ones first, one per name, each carrying the filters nested inside its block. See the **Interaction filter** entry in `CONTEXT.md` |
248
+
236
249
  ### Confidence tagging
237
250
 
238
251
  Every AST result carries a confidence tag:
data/docs/TOOLS.md CHANGED
@@ -116,7 +116,7 @@ Full-stack feature analysis: models + controllers + routes + services + jobs + v
116
116
 
117
117
  ### `rails_get_context`
118
118
 
119
- Composite context: schema + model + controller + routes + views for a resource.
119
+ Composite context: schema + model + controller + routes + views for a resource. Views come from the directory Rails resolves for the controller; a flat-directory fallback is labelled.
120
120
 
121
121
  | Parameter | Type | Default | Description |
122
122
  |:----------|:-----|:--------|:------------|
@@ -140,7 +140,10 @@ Narrative app walkthrough for getting up to speed.
140
140
 
141
141
  ### `rails_get_schema`
142
142
 
143
- Database schema with column types, indexes, defaults, encrypted hints.
143
+ Database schema with column types, indexes, defaults, encrypted hints. Booted,
144
+ a table `db/schema.rb` declares and the connected database does not have is
145
+ named as a migration that has not run, and the listing header says when the
146
+ two table counts disagree.
144
147
 
145
148
  | Parameter | Type | Default | Description |
146
149
  |:----------|:-----|:--------|:------------|
@@ -149,7 +152,7 @@ Database schema with column types, indexes, defaults, encrypted hints.
149
152
 
150
153
  ### `rails_get_model_details`
151
154
 
152
- AST-parsed model internals. Every result carries `[VERIFIED]` or `[INFERRED]` confidence tag.
155
+ AST-parsed model internals. Every result carries `[VERIFIED]` or `[INFERRED]` confidence tag. The method list says how many of the model's methods it is showing.
153
156
 
154
157
  | Parameter | Type | Default | Description |
155
158
  |:----------|:-----|:--------|:------------|
@@ -176,7 +179,10 @@ its concerns), not the order Rails registered them in.
176
179
 
177
180
  ### `rails_get_concern`
178
181
 
179
- Concern methods, source code, and which models include it.
182
+ Concern methods, source code, and which models include it. A class under
183
+ `app/models/concerns` that subclasses `ActiveModel::Validator` is listed as a
184
+ validator rather than a concern, and its users are the models that name it in
185
+ `validates_with`.
180
186
 
181
187
  | Parameter | Type | Default | Description |
182
188
  |:----------|:-----|:--------|:------------|
@@ -201,7 +207,11 @@ Controller actions with inherited filters, render map, strong params. Includes s
201
207
 
202
208
  ### `rails_get_routes`
203
209
 
204
- Routes with code-ready helpers (`post_path(@record)`) and required params.
210
+ Routes with code-ready helpers (`post_path(@record)`) and required params. A
211
+ fully qualified controller key answers with its own routes only; a short name
212
+ still matches every controller that carries it. Rack apps attached with `mount`
213
+ or `match ... to:` are named with the path they answer on, when the source
214
+ spells one out.
205
215
 
206
216
  | Parameter | Type | Default | Description |
207
217
  |:----------|:-----|:--------|:------------|
@@ -216,7 +226,7 @@ Routes with code-ready helpers (`post_path(@record)`) and required params.
216
226
 
217
227
  ### `rails_get_view`
218
228
 
219
- View templates with instance variables, Turbo frames, Stimulus controllers, partial locals. Includes schema hints for detected ivars.
229
+ View templates with instance variables, Turbo frames, Stimulus controllers, partial locals. Includes schema hints for detected ivars. A template directly under `app/views` is grouped as `(app/views root)`, which `path:` reaches and `controller:` does not.
220
230
 
221
231
  | Parameter | Type | Default | Description |
222
232
  |:----------|:-----|:--------|:------------|
@@ -299,7 +309,9 @@ Brakeman static analysis: SQL injection, XSS, mass assignment, command injection
299
309
  |:----------|:-----|:--------|:------------|
300
310
  | `detail` | enum | `standard` | `summary`, `standard`, `full` |
301
311
 
302
- > Requires the `brakeman` gem. Gracefully reports "not installed" if missing.
312
+ > Requires the `brakeman` gem. When it cannot be loaded, the answer says which
313
+ > case it is: brakeman is nowhere on the machine, or it is installed and the
314
+ > app's bundle does not carry it, which `--no-boot` scans around.
303
315
 
304
316
  ### `rails_performance_check`
305
317
 
@@ -349,7 +361,7 @@ Notable gems with versions, categories, and config file locations.
349
361
 
350
362
  ### `rails_get_env`
351
363
 
352
- Environment variables + credentials keys (values are never exposed). Scans `.rb`, `.rake`, ERB views and config YAML under `app`, `config` and `lib`; files matching `sensitive_patterns` (`config/database.yml`, credentials, keys) are never read, and the answer says so.
364
+ Environment variables + credentials keys (values are never exposed). Scans `.rb`, `.rake`, ERB views and config YAML under `app`, `config` and `lib`; files matching `sensitive_patterns` (`config/database.yml`, credentials, keys) are never read, and the answer says so. A variable whose call sites pass different defaults is labelled as such rather than with one site's default; `detail:"full"` names each site's.
353
365
 
354
366
  | Parameter | Type | Default | Description |
355
367
  |:----------|:-----|:--------|:------------|
@@ -365,7 +377,12 @@ Application and framework helpers with view cross-references.
365
377
 
366
378
  ### `rails_get_service_pattern`
367
379
 
368
- Service object interface, dependencies, side effects, callers.
380
+ Service object interface, dependencies, side effects, callers. An
381
+ ActiveInteraction's inputs include the ones it inherits, with the filters
382
+ nested inside a `hash` filter shown under it. Callers are read from every
383
+ `app/` and `lib/` tree, and on a booted app from any other directory it
384
+ autoloads, and the page says when the twenty-caller display cap or the scan's
385
+ own file ceiling left the list partial.
369
386
 
370
387
  | Parameter | Type | Default | Description |
371
388
  |:----------|:-----|:--------|:------------|
@@ -376,7 +393,9 @@ Service object interface, dependencies, side effects, callers.
376
393
 
377
394
  Background job queue, retries, guard clauses, broadcasts, schedules. Sidekiq
378
395
  workers under `app/workers` are listed alongside the ActiveJob jobs, with
379
- their `sidekiq_options` and `perform` signature.
396
+ their `sidekiq_options`, any `sidekiq_throttle`, and the `perform` signature. A
397
+ worker that inherits its Sidekiq mixin from a base worker is one of them, and
398
+ `job:` answers a worker name as well as a job name.
380
399
 
381
400
  | Parameter | Type | Default | Description |
382
401
  |:----------|:-----|:--------|:------------|
@@ -414,7 +433,7 @@ ActionMailer mailers: every mailer class with its delivery actions and delivery
414
433
 
415
434
  ### `rails_get_engines`
416
435
 
417
- Rails engines: engines mounted in `config/routes.rb` (with known-engine descriptions) and loaded engine classes with route/model counts.
436
+ What `config/routes.rb` mounts - engines and plain Rack apps alike, with known-engine descriptions, each with the path it answers on when the source spells one out - and loaded engine classes with route/model counts.
418
437
 
419
438
  *No parameters.*
420
439
 
@@ -506,7 +525,7 @@ Reverse file tail with level filtering and sensitive data redaction.
506
525
 
507
526
  ### `rails_diagnose`
508
527
 
509
- One-call error diagnosis with classification, context, git blame, and log correlation.
528
+ One-call error diagnosis with classification, context, git blame, and log correlation. It does not call a method undefined when the model's method list could be missing one - a concern's, a parent's, or anything past the payload's own cap.
510
529
 
511
530
  | Parameter | Type | Default | Description |
512
531
  |:----------|:-----|:--------|:------------|
@@ -256,6 +256,19 @@ bundle add brakeman --group development
256
256
 
257
257
  Without it, the tool reports "not installed" but the gem works fine otherwise.
258
258
 
259
+ ### "Installed on this machine but not in this app's bundle"
260
+
261
+ The scan runs under the app's own bundle, so a brakeman installed globally is
262
+ not on its load path. The tool falls back on its own: it runs the installed
263
+ brakeman as a separate process outside the bundle and says so under the
264
+ results. This message means that fallback produced no report either - the gem
265
+ is there and the run failed. Run `brakeman` in the app directory to see what
266
+ it hit, or add it to the Gemfile so the scan runs in-process:
267
+
268
+ ```bash
269
+ bundle add brakeman --group development
270
+ ```
271
+
259
272
  ---
260
273
 
261
274
  ## Performance issues