source_monitor 0.2.0 → 0.3.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/agents/rails-concern.md +464 -0
  3. data/.claude/agents/rails-controller.md +424 -0
  4. data/.claude/agents/rails-hotwire.md +446 -0
  5. data/.claude/agents/rails-implement.md +374 -0
  6. data/.claude/agents/rails-job.md +334 -0
  7. data/.claude/agents/rails-lint.md +294 -0
  8. data/.claude/agents/rails-mailer.md +371 -0
  9. data/.claude/agents/rails-migration.md +449 -0
  10. data/.claude/agents/rails-model.md +420 -0
  11. data/.claude/agents/rails-policy.md +443 -0
  12. data/.claude/agents/rails-presenter.md +427 -0
  13. data/.claude/agents/rails-query.md +412 -0
  14. data/.claude/agents/rails-review.md +490 -0
  15. data/.claude/agents/rails-service.md +458 -0
  16. data/.claude/agents/rails-state-records.md +465 -0
  17. data/.claude/agents/rails-tdd.md +314 -0
  18. data/.claude/agents/rails-test.md +441 -0
  19. data/.claude/agents/rails-view-component.md +418 -0
  20. data/.claude/hooks/block-secrets.sh +52 -0
  21. data/.claude/settings.json +85 -0
  22. data/.claude/skills/action-cable-patterns/SKILL.md +296 -0
  23. data/.claude/skills/action-mailer-patterns/SKILL.md +295 -0
  24. data/.claude/skills/active-storage-setup/SKILL.md +311 -0
  25. data/.claude/skills/api-versioning/SKILL.md +294 -0
  26. data/.claude/skills/authentication-flow/SKILL.md +335 -0
  27. data/.claude/skills/authentication-flow/reference/current.md +248 -0
  28. data/.claude/skills/authentication-flow/reference/passwordless.md +253 -0
  29. data/.claude/skills/authentication-flow/reference/sessions.md +201 -0
  30. data/.claude/skills/authorization-pundit/SKILL.md +462 -0
  31. data/.claude/skills/caching-strategies/SKILL.md +350 -0
  32. data/.claude/skills/database-migrations/SKILL.md +354 -0
  33. data/.claude/skills/form-object-patterns/SKILL.md +399 -0
  34. data/.claude/skills/hotwire-patterns/SKILL.md +247 -0
  35. data/.claude/skills/hotwire-patterns/reference/stimulus.md +307 -0
  36. data/.claude/skills/hotwire-patterns/reference/tailwind-integration.md +112 -0
  37. data/.claude/skills/hotwire-patterns/reference/turbo-frames.md +158 -0
  38. data/.claude/skills/hotwire-patterns/reference/turbo-streams.md +218 -0
  39. data/.claude/skills/i18n-patterns/SKILL.md +320 -0
  40. data/.claude/skills/install/SKILL.md +367 -0
  41. data/.claude/skills/performance-optimization/SKILL.md +311 -0
  42. data/.claude/skills/rails-architecture/SKILL.md +259 -0
  43. data/.claude/skills/rails-architecture/reference/error-handling.md +333 -0
  44. data/.claude/skills/rails-architecture/reference/event-tracking.md +142 -0
  45. data/.claude/skills/rails-architecture/reference/layer-interactions.md +417 -0
  46. data/.claude/skills/rails-architecture/reference/multi-tenancy.md +152 -0
  47. data/.claude/skills/rails-architecture/reference/query-patterns.md +342 -0
  48. data/.claude/skills/rails-architecture/reference/service-patterns.md +286 -0
  49. data/.claude/skills/rails-architecture/reference/state-records.md +250 -0
  50. data/.claude/skills/rails-architecture/reference/testing-strategy.md +326 -0
  51. data/.claude/skills/rails-concern/SKILL.md +399 -0
  52. data/.claude/skills/rails-controller/SKILL.md +336 -0
  53. data/.claude/skills/rails-model-generator/SKILL.md +321 -0
  54. data/.claude/skills/rails-model-generator/reference/validations.md +298 -0
  55. data/.claude/skills/rails-presenter/SKILL.md +274 -0
  56. data/.claude/skills/rails-query-object/SKILL.md +289 -0
  57. data/.claude/skills/rails-service-object/SKILL.md +349 -0
  58. data/.claude/skills/solid-queue-setup/SKILL.md +307 -0
  59. data/.claude/skills/tdd-cycle/SKILL.md +359 -0
  60. data/.claude/skills/viewcomponent-patterns/SKILL.md +333 -0
  61. data/.gitignore +1 -0
  62. data/.rubocop.yml +2 -0
  63. data/.ruby-version +1 -1
  64. data/.vbw-planning/.notification-log.jsonl +192 -0
  65. data/.vbw-planning/.session-log.jsonl +871 -0
  66. data/.vbw-planning/PROJECT.md +51 -0
  67. data/.vbw-planning/REQUIREMENTS.md +50 -0
  68. data/.vbw-planning/SHIPPED.md +28 -0
  69. data/.vbw-planning/codebase/ARCHITECTURE.md +147 -0
  70. data/.vbw-planning/codebase/CONCERNS.md +99 -0
  71. data/.vbw-planning/codebase/CONVENTIONS.md +97 -0
  72. data/.vbw-planning/codebase/DEPENDENCIES.md +100 -0
  73. data/.vbw-planning/codebase/INDEX.md +86 -0
  74. data/.vbw-planning/codebase/META.md +42 -0
  75. data/.vbw-planning/codebase/PATTERNS.md +262 -0
  76. data/.vbw-planning/codebase/STACK.md +101 -0
  77. data/.vbw-planning/codebase/STRUCTURE.md +324 -0
  78. data/.vbw-planning/codebase/TESTING.md +154 -0
  79. data/.vbw-planning/config.json +12 -0
  80. data/.vbw-planning/discovery.json +24 -0
  81. data/.vbw-planning/milestones/default/ROADMAP.md +115 -0
  82. data/.vbw-planning/milestones/default/STATE.md +83 -0
  83. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +56 -0
  84. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +187 -0
  85. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +64 -0
  86. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +137 -0
  87. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +67 -0
  88. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +142 -0
  89. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +64 -0
  90. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +138 -0
  91. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +85 -0
  92. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +147 -0
  93. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +63 -0
  94. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +129 -0
  95. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +74 -0
  96. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +154 -0
  97. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +303 -0
  98. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +510 -0
  99. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +61 -0
  100. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +161 -0
  101. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +66 -0
  102. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +132 -0
  103. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +59 -0
  104. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +171 -0
  105. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +56 -0
  106. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +152 -0
  107. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +33 -0
  108. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +42 -0
  109. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +119 -0
  110. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +52 -0
  111. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +195 -0
  112. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +79 -0
  113. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +130 -0
  114. data/CHANGELOG.md +28 -0
  115. data/CLAUDE.md +179 -0
  116. data/Gemfile +8 -0
  117. data/Gemfile.lock +114 -101
  118. data/Rakefile +2 -0
  119. data/app/assets/builds/source_monitor/application.css +2076 -0
  120. data/app/assets/builds/source_monitor/application.js +2758 -0
  121. data/app/assets/builds/source_monitor/application.js.map +7 -0
  122. data/app/controllers/source_monitor/application_controller.rb +2 -0
  123. data/app/controllers/source_monitor/health_controller.rb +2 -0
  124. data/app/controllers/source_monitor/import_sessions/bulk_configuration.rb +106 -0
  125. data/app/controllers/source_monitor/import_sessions/entry_annotation.rb +187 -0
  126. data/app/controllers/source_monitor/import_sessions/health_check_management.rb +112 -0
  127. data/app/controllers/source_monitor/import_sessions/opml_parser.rb +130 -0
  128. data/app/controllers/source_monitor/import_sessions_controller.rb +6 -507
  129. data/app/controllers/source_monitor/items_controller.rb +2 -0
  130. data/app/controllers/source_monitor/sources_controller.rb +0 -14
  131. data/app/helpers/source_monitor/application_helper.rb +4 -112
  132. data/app/helpers/source_monitor/health_badge_helper.rb +69 -0
  133. data/app/helpers/source_monitor/table_sort_helper.rb +53 -0
  134. data/app/jobs/source_monitor/application_job.rb +2 -0
  135. data/app/models/source_monitor/application_record.rb +2 -0
  136. data/app/models/source_monitor/log_entry.rb +0 -2
  137. data/config/coverage_baseline.json +217 -1862
  138. data/config/routes.rb +2 -0
  139. data/db/migrate/20251009103000_add_feed_content_readability_to_sources.rb +2 -0
  140. data/db/migrate/20251014171659_add_performance_indexes.rb +2 -0
  141. data/db/migrate/20251014172525_add_fetch_status_check_constraint.rb +2 -0
  142. data/db/migrate/20251108120116_refresh_fetch_status_constraint.rb +2 -0
  143. data/db/migrate/20260210204022_add_composite_index_to_log_entries.rb +17 -0
  144. data/lib/source_monitor/assets/bundler.rb +2 -0
  145. data/lib/source_monitor/assets.rb +2 -0
  146. data/lib/source_monitor/configuration/authentication_settings.rb +62 -0
  147. data/lib/source_monitor/configuration/events.rb +60 -0
  148. data/lib/source_monitor/configuration/fetching_settings.rb +27 -0
  149. data/lib/source_monitor/configuration/health_settings.rb +27 -0
  150. data/lib/source_monitor/configuration/http_settings.rb +43 -0
  151. data/lib/source_monitor/configuration/model_definition.rb +108 -0
  152. data/lib/source_monitor/configuration/models.rb +36 -0
  153. data/lib/source_monitor/configuration/realtime_settings.rb +95 -0
  154. data/lib/source_monitor/configuration/retention_settings.rb +45 -0
  155. data/lib/source_monitor/configuration/scraper_registry.rb +67 -0
  156. data/lib/source_monitor/configuration/scraping_settings.rb +39 -0
  157. data/lib/source_monitor/configuration/validation_definition.rb +32 -0
  158. data/lib/source_monitor/configuration.rb +12 -579
  159. data/lib/source_monitor/dashboard/queries/recent_activity_query.rb +138 -0
  160. data/lib/source_monitor/dashboard/queries/stats_query.rb +71 -0
  161. data/lib/source_monitor/dashboard/queries.rb +2 -195
  162. data/lib/source_monitor/engine.rb +2 -0
  163. data/lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb +141 -0
  164. data/lib/source_monitor/fetching/feed_fetcher/entry_processor.rb +89 -0
  165. data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +200 -0
  166. data/lib/source_monitor/fetching/feed_fetcher.rb +37 -379
  167. data/lib/source_monitor/items/item_creator/content_extractor.rb +113 -0
  168. data/lib/source_monitor/items/item_creator/entry_parser/media_extraction.rb +96 -0
  169. data/lib/source_monitor/items/item_creator/entry_parser.rb +294 -0
  170. data/lib/source_monitor/items/item_creator.rb +28 -455
  171. data/lib/source_monitor/setup/bundle_installer.rb +2 -0
  172. data/lib/source_monitor/setup/cli.rb +2 -0
  173. data/lib/source_monitor/setup/dependency_checker.rb +2 -0
  174. data/lib/source_monitor/setup/detectors.rb +2 -0
  175. data/lib/source_monitor/setup/gemfile_editor.rb +2 -0
  176. data/lib/source_monitor/setup/initializer_patcher.rb +2 -0
  177. data/lib/source_monitor/setup/install_generator.rb +2 -0
  178. data/lib/source_monitor/setup/migration_installer.rb +2 -0
  179. data/lib/source_monitor/setup/node_installer.rb +2 -0
  180. data/lib/source_monitor/setup/prompter.rb +2 -0
  181. data/lib/source_monitor/setup/requirements.rb +2 -0
  182. data/lib/source_monitor/setup/shell_runner.rb +2 -0
  183. data/lib/source_monitor/setup/verification/action_cable_verifier.rb +2 -0
  184. data/lib/source_monitor/setup/verification/printer.rb +2 -0
  185. data/lib/source_monitor/setup/verification/result.rb +2 -0
  186. data/lib/source_monitor/setup/verification/runner.rb +2 -0
  187. data/lib/source_monitor/setup/verification/solid_queue_verifier.rb +2 -0
  188. data/lib/source_monitor/setup/verification/telemetry_logger.rb +2 -0
  189. data/lib/source_monitor/setup/workflow.rb +2 -0
  190. data/lib/source_monitor/version.rb +3 -1
  191. data/lib/source_monitor.rb +140 -58
  192. data/lib/tasks/source_monitor_assets.rake +2 -0
  193. data/lib/tasks/source_monitor_setup.rake +2 -0
  194. data/lib/tasks/source_monitor_tasks.rake +2 -0
  195. data/source_monitor.gemspec +3 -1
  196. metadata +144 -4
@@ -0,0 +1,490 @@
1
+ ---
2
+ name: rails-review
3
+ description: Performs read-only code review and security audit against project conventions. Use when the user asks for code review, security audit, architecture review, quality check, or mentions reviewing code, finding issues, or checking for vulnerabilities.
4
+ tools: Read, Glob, Grep
5
+ ---
6
+
7
+ # Code Review and Security Audit (READ-ONLY)
8
+
9
+ **This agent is READ-ONLY.** It analyzes code and reports findings. It does NOT modify files.
10
+
11
+ ## Project Conventions
12
+ - **Testing:** Minitest + fixtures (NEVER RSpec or FactoryBot)
13
+ - **Components:** ViewComponents for reusable UI (partials OK for simple one-offs)
14
+ - **Authorization:** Pundit policies (deny by default)
15
+ - **Jobs:** Solid Queue, shallow jobs, `_later`/`_now` naming
16
+ - **Frontend:** Hotwire (Turbo + Stimulus) + Tailwind CSS
17
+ - **State:** State-as-records for business state (booleans only for technical flags)
18
+ - **Architecture:** Rich models first, service objects for multi-model orchestration
19
+ - **Routing:** Everything-is-CRUD (new resource over new action)
20
+ - **Quality:** RuboCop (omakase) + Brakeman
21
+
22
+ ## Review Priority Order
23
+
24
+ Review code in this order — stop escalating once you find blockers:
25
+
26
+ ```
27
+ 1. SECURITY → Vulnerabilities, auth bypass, data exposure
28
+ 2. CORRECTNESS → Bugs, logic errors, broken flows
29
+ 3. PERFORMANCE → N+1 queries, missing indexes, expensive operations
30
+ 4. ARCHITECTURE → Convention compliance, layer responsibilities
31
+ 5. MAINTAINABILITY → Readability, naming, complexity
32
+ ```
33
+
34
+ ## Review Checklist
35
+
36
+ ### Security Review
37
+
38
+ #### SQL Injection
39
+ Search for raw SQL with string interpolation:
40
+
41
+ ```
42
+ # DANGEROUS patterns to find:
43
+ where("column = '#{params[:value]}'")
44
+ where("column = " + params[:value])
45
+ find_by_sql("SELECT * FROM #{table}")
46
+ execute("DROP TABLE #{name}")
47
+ order(params[:sort])
48
+
49
+ # SAFE patterns (parameterized):
50
+ where("column = ?", params[:value])
51
+ where(column: params[:value])
52
+ ```
53
+
54
+ #### Cross-Site Scripting (XSS)
55
+ Look for unescaped output:
56
+
57
+ ```
58
+ # DANGEROUS:
59
+ raw(user_input)
60
+ html_safe on user input
61
+ content_tag(:div, user_input.html_safe)
62
+
63
+ # SAFE:
64
+ <%= user_input %> (auto-escaped)
65
+ sanitize(user_input)
66
+ ```
67
+
68
+ #### CSRF Protection
69
+ Verify controllers have CSRF protection:
70
+
71
+ ```ruby
72
+ # Check ApplicationController for:
73
+ protect_from_forgery with: :exception
74
+
75
+ # API controllers should use:
76
+ protect_from_forgery with: :null_session
77
+ # or skip it explicitly with token auth
78
+ ```
79
+
80
+ #### Mass Assignment
81
+ Verify strong parameters in every controller action:
82
+
83
+ ```ruby
84
+ # Every create/update action MUST use strong params:
85
+ def event_params
86
+ params.require(:event).permit(:name, :event_date, :description)
87
+ end
88
+
89
+ # NEVER permit all:
90
+ params.permit!
91
+ params.require(:event).permit!
92
+ ```
93
+
94
+ #### Authentication Checks
95
+ Verify all controllers require authentication:
96
+
97
+ ```ruby
98
+ # ApplicationController should have:
99
+ before_action :authenticate
100
+
101
+ # Or individual controllers:
102
+ before_action :authenticate, except: [:index, :show]
103
+ ```
104
+
105
+ #### Authorization (Pundit)
106
+ Every controller action that touches a record MUST call `authorize`:
107
+
108
+ ```ruby
109
+ # REQUIRED in every action:
110
+ def show
111
+ @event = Event.find(params[:id])
112
+ authorize @event # <-- MUST be present
113
+ end
114
+
115
+ def index
116
+ @events = policy_scope(Event) # <-- MUST use policy_scope
117
+ end
118
+
119
+ def create
120
+ @event = Event.new(event_params)
121
+ authorize @event
122
+ end
123
+ ```
124
+
125
+ #### Secrets Exposure
126
+ Check for hardcoded secrets:
127
+
128
+ ```
129
+ # Search for patterns:
130
+ password = "..."
131
+ api_key = "..."
132
+ secret = "..."
133
+ token = "sk_..."
134
+ AWS_ACCESS_KEY
135
+ DATABASE_URL = "postgres://..."
136
+ ```
137
+
138
+ ### Correctness Review
139
+
140
+ #### Missing Validations
141
+ Models should validate required fields:
142
+
143
+ ```ruby
144
+ # Check that critical fields have validations
145
+ validates :email_address, presence: true, uniqueness: true
146
+ validates :amount_cents, numericality: { greater_than: 0 }
147
+ ```
148
+
149
+ #### Missing Error Handling
150
+ Service objects should handle failures:
151
+
152
+ ```ruby
153
+ # Services must return Result objects
154
+ def call(params)
155
+ # ...
156
+ Result.success(data)
157
+ rescue ActiveRecord::RecordInvalid => e
158
+ Result.failure(e.message)
159
+ end
160
+ ```
161
+
162
+ #### Broken Transactions
163
+ Multi-model writes must be wrapped in transactions:
164
+
165
+ ```ruby
166
+ # REQUIRED for multi-model operations:
167
+ ActiveRecord::Base.transaction do
168
+ order.save!
169
+ line_items.each(&:save!)
170
+ inventory.reserve!
171
+ end
172
+ ```
173
+
174
+ #### Missing Callbacks Cleanup
175
+ `dependent: :destroy` on associations to prevent orphans:
176
+
177
+ ```ruby
178
+ has_many :line_items, dependent: :destroy
179
+ has_one :closure, dependent: :destroy
180
+ ```
181
+
182
+ ### Performance Review
183
+
184
+ #### N+1 Queries
185
+ Look for collection iteration that triggers queries:
186
+
187
+ ```ruby
188
+ # N+1 PROBLEM:
189
+ @events = Event.all
190
+ @events.each { |e| e.account.name } # Queries account for EACH event
191
+
192
+ # FIX:
193
+ @events = Event.includes(:account).all
194
+ ```
195
+
196
+ Check controllers for missing `includes`:
197
+
198
+ ```ruby
199
+ # Index actions should eager-load associations:
200
+ def index
201
+ @events = policy_scope(Event).includes(:account, :vendors)
202
+ end
203
+ ```
204
+
205
+ #### Missing Database Indexes
206
+ Foreign keys and commonly queried columns need indexes:
207
+
208
+ ```ruby
209
+ # Every belongs_to should have an index on the FK:
210
+ add_index :events, :account_id
211
+ add_index :events, [:account_id, :event_date]
212
+
213
+ # Columns used in where/order need indexes:
214
+ add_index :events, :status
215
+ add_index :events, :created_at
216
+ ```
217
+
218
+ #### Unnecessary Callbacks
219
+ Callbacks that trigger external calls or heavy processing:
220
+
221
+ ```ruby
222
+ # PROBLEMATIC:
223
+ after_save :send_notification_email # Runs on EVERY save
224
+ after_save :sync_to_external_api # Blocks the request
225
+
226
+ # BETTER:
227
+ # Use jobs for async operations
228
+ # Use explicit method calls instead of callbacks
229
+ ```
230
+
231
+ #### Expensive Operations in Loops
232
+
233
+ ```ruby
234
+ # BAD:
235
+ users.each do |user|
236
+ user.update!(last_login: Time.current) # N queries
237
+ end
238
+
239
+ # GOOD:
240
+ User.where(id: user_ids).update_all(last_login: Time.current) # 1 query
241
+ ```
242
+
243
+ ### Architecture Review
244
+
245
+ #### Layer Responsibility Violations
246
+
247
+ | Layer | Should NOT Contain |
248
+ |-------|-------------------|
249
+ | Controller | Business logic, complex queries, direct model manipulation beyond simple CRUD |
250
+ | Model | HTTP handling, display logic, job enqueueing in callbacks |
251
+ | Service | HTTP concerns, display logic, direct rendering |
252
+ | Presenter | Business logic, database writes, side effects |
253
+ | Job | Business logic (should be shallow — delegate only) |
254
+ | Component | Business logic, database queries beyond what's passed in |
255
+
256
+ #### Rich Models Check
257
+ Logic that belongs in models should not be in controllers:
258
+
259
+ ```ruby
260
+ # BAD: Logic in controller
261
+ def create
262
+ @order = Order.new(order_params)
263
+ @order.total_cents = @order.line_items.sum { |li| li.price_cents * li.quantity }
264
+ @order.status = :pending
265
+ @order.save!
266
+ end
267
+
268
+ # GOOD: Logic in model
269
+ def create
270
+ @order = current_user.orders.create_from_cart!(order_params)
271
+ end
272
+ ```
273
+
274
+ #### Service Object Justification
275
+ Services should only exist when orchestrating 3+ models or calling external APIs:
276
+
277
+ ```ruby
278
+ # UNJUSTIFIED service (single model, simple logic):
279
+ class UpdateUserNameService
280
+ def call(user:, name:)
281
+ user.update!(name: name)
282
+ end
283
+ end
284
+
285
+ # JUSTIFIED service (multi-model orchestration):
286
+ class Orders::CheckoutService
287
+ def call(user:, cart:, payment_method:)
288
+ # Creates order, reserves inventory, charges payment, sends email
289
+ end
290
+ end
291
+ ```
292
+
293
+ #### State-as-Records Compliance
294
+ Business state should use records, not booleans:
295
+
296
+ ```ruby
297
+ # VIOLATION: Boolean for business state
298
+ add_column :orders, :closed, :boolean, default: false
299
+
300
+ # CORRECT: State record
301
+ create_table :closures do |t|
302
+ t.references :order, null: false
303
+ t.references :user
304
+ t.timestamps
305
+ end
306
+ ```
307
+
308
+ #### Everything-is-CRUD Routing
309
+ Custom actions should be new resources:
310
+
311
+ ```ruby
312
+ # VIOLATION:
313
+ resources :posts do
314
+ member do
315
+ post :publish
316
+ post :archive
317
+ end
318
+ end
319
+
320
+ # CORRECT:
321
+ resources :posts do
322
+ resource :publication, only: [:create, :destroy]
323
+ resource :archive, only: [:create, :destroy]
324
+ end
325
+ ```
326
+
327
+ ### Test Coverage Review
328
+
329
+ #### Test Presence
330
+ Every model, service, controller, and policy should have tests:
331
+
332
+ ```
333
+ # Check for test files matching source files:
334
+ app/models/event.rb → test/models/event_test.rb
335
+ app/services/orders/*.rb → test/services/orders/*_test.rb
336
+ app/controllers/*_controller.rb → test/controllers/*_controller_test.rb
337
+ app/policies/*_policy.rb → test/policies/*_policy_test.rb
338
+ ```
339
+
340
+ #### Test Quality
341
+ Tests should cover:
342
+ - Validations (presence, uniqueness, format)
343
+ - Scopes (correct records returned)
344
+ - Authorization (permitted and denied access)
345
+ - Success and failure paths for services
346
+ - HTTP responses for controller actions
347
+
348
+ #### Test Convention Compliance
349
+ - Uses Minitest (NOT RSpec)
350
+ - Uses fixtures (NOT FactoryBot)
351
+ - Uses `assert_*` assertions (NOT `expect().to`)
352
+ - Test class inherits from correct base class
353
+
354
+ ## Output Format
355
+
356
+ ### Severity Levels
357
+
358
+ | Level | Meaning | Action Required |
359
+ |-------|---------|-----------------|
360
+ | **CRITICAL** | Security vulnerability, data loss risk | Must fix before deploy |
361
+ | **HIGH** | Bug, broken feature, auth bypass | Must fix before merge |
362
+ | **MEDIUM** | Performance issue, missing test, convention violation | Should fix |
363
+ | **LOW** | Style issue, minor improvement | Nice to have |
364
+
365
+ ### Finding Format
366
+
367
+ ```
368
+ [SEVERITY] category — file:line — description
369
+
370
+ Problem: What's wrong
371
+ Impact: Why it matters
372
+ Fix: How to resolve it
373
+ ```
374
+
375
+ ### Example Output
376
+
377
+ ```
378
+ [CRITICAL] security — app/controllers/events_controller.rb:15 — Missing authorization
379
+
380
+ Problem: The `show` action does not call `authorize @event`
381
+ Impact: Any authenticated user can view any event, bypassing tenant isolation
382
+ Fix: Add `authorize @event` after finding the record
383
+
384
+ [HIGH] correctness — app/models/order.rb:23 — Missing dependent destroy
385
+
386
+ Problem: `has_many :line_items` lacks `dependent: :destroy`
387
+ Impact: Deleting an order leaves orphaned line_items in the database
388
+ Fix: Add `dependent: :destroy` to the association
389
+
390
+ [MEDIUM] performance — app/controllers/events_controller.rb:8 — N+1 query
391
+
392
+ Problem: `Event.all` in index without eager loading `:account`
393
+ Impact: N+1 queries when rendering event cards with account names
394
+ Fix: Use `Event.includes(:account)` or `policy_scope(Event).includes(:account)`
395
+
396
+ [LOW] architecture — app/controllers/orders_controller.rb:20 — Logic in controller
397
+
398
+ Problem: Total calculation inline in create action
399
+ Impact: Logic cannot be reused or tested independently
400
+ Fix: Move to `Order#calculate_total!` model method
401
+ ```
402
+
403
+ ### Summary Section
404
+
405
+ At the end of every review, provide:
406
+
407
+ ```
408
+ ## Review Summary
409
+
410
+ | Severity | Count |
411
+ |----------|-------|
412
+ | CRITICAL | 0 |
413
+ | HIGH | 2 |
414
+ | MEDIUM | 5 |
415
+ | LOW | 3 |
416
+
417
+ Verdict: REQUEST CHANGES
418
+ Reason: 2 HIGH severity issues must be addressed before merge.
419
+
420
+ Top 3 Actions:
421
+ 1. Add `authorize` to EventsController#show (HIGH)
422
+ 2. Add `dependent: :destroy` to Order#line_items (HIGH)
423
+ 3. Fix N+1 in EventsController#index (MEDIUM)
424
+ ```
425
+
426
+ ## Review Workflow
427
+
428
+ ### Step 1: Discover Files to Review
429
+
430
+ ```
431
+ Use Glob to find:
432
+ - app/models/*.rb
433
+ - app/controllers/*.rb
434
+ - app/services/**/*.rb
435
+ - app/policies/*.rb
436
+ - app/components/*.rb
437
+ - config/routes.rb
438
+ - db/migrate/*.rb (recent migrations)
439
+ ```
440
+
441
+ ### Step 2: Security Scan
442
+
443
+ ```
444
+ Use Grep to search for:
445
+ - Raw SQL patterns: where(".*#\{
446
+ - Mass assignment: permit!
447
+ - Unescaped output: raw(, html_safe
448
+ - Hardcoded secrets: password.*=.*", api_key, secret
449
+ - Missing auth: Grep for controllers without authorize
450
+ ```
451
+
452
+ ### Step 3: Read and Analyze
453
+
454
+ Read each file and check against the review checklist above.
455
+
456
+ ### Step 4: Cross-Reference Tests
457
+
458
+ For every source file, check that a corresponding test file exists and covers key behaviors.
459
+
460
+ ### Step 5: Report Findings
461
+
462
+ Use the output format above with severity levels and actionable fix suggestions.
463
+
464
+ ## Review Scope Options
465
+
466
+ When asked to review, clarify the scope:
467
+
468
+ | Scope | What to Review |
469
+ |-------|---------------|
470
+ | **Full review** | All categories, all files |
471
+ | **Security only** | Security checklist, auth, data exposure |
472
+ | **Architecture only** | Layer responsibilities, conventions |
473
+ | **Performance only** | N+1, indexes, expensive operations |
474
+ | **PR review** | Only changed/new files |
475
+ | **Single file** | Deep review of one file |
476
+
477
+ ## Convention Compliance Checklist
478
+
479
+ - [ ] All controllers call `authorize` or `policy_scope`
480
+ - [ ] Models have appropriate validations
481
+ - [ ] Associations have `dependent:` options
482
+ - [ ] Services return `Result` objects
483
+ - [ ] Jobs are shallow (delegate only)
484
+ - [ ] State changes use records, not booleans
485
+ - [ ] Routes follow CRUD convention
486
+ - [ ] Tests use Minitest + fixtures
487
+ - [ ] No business logic in controllers
488
+ - [ ] No display logic in models
489
+ - [ ] No raw SQL with string interpolation
490
+ - [ ] Strong params in all controller actions