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.
- checksums.yaml +4 -4
- data/.claude/agents/rails-concern.md +464 -0
- data/.claude/agents/rails-controller.md +424 -0
- data/.claude/agents/rails-hotwire.md +446 -0
- data/.claude/agents/rails-implement.md +374 -0
- data/.claude/agents/rails-job.md +334 -0
- data/.claude/agents/rails-lint.md +294 -0
- data/.claude/agents/rails-mailer.md +371 -0
- data/.claude/agents/rails-migration.md +449 -0
- data/.claude/agents/rails-model.md +420 -0
- data/.claude/agents/rails-policy.md +443 -0
- data/.claude/agents/rails-presenter.md +427 -0
- data/.claude/agents/rails-query.md +412 -0
- data/.claude/agents/rails-review.md +490 -0
- data/.claude/agents/rails-service.md +458 -0
- data/.claude/agents/rails-state-records.md +465 -0
- data/.claude/agents/rails-tdd.md +314 -0
- data/.claude/agents/rails-test.md +441 -0
- data/.claude/agents/rails-view-component.md +418 -0
- data/.claude/hooks/block-secrets.sh +52 -0
- data/.claude/settings.json +85 -0
- data/.claude/skills/action-cable-patterns/SKILL.md +296 -0
- data/.claude/skills/action-mailer-patterns/SKILL.md +295 -0
- data/.claude/skills/active-storage-setup/SKILL.md +311 -0
- data/.claude/skills/api-versioning/SKILL.md +294 -0
- data/.claude/skills/authentication-flow/SKILL.md +335 -0
- data/.claude/skills/authentication-flow/reference/current.md +248 -0
- data/.claude/skills/authentication-flow/reference/passwordless.md +253 -0
- data/.claude/skills/authentication-flow/reference/sessions.md +201 -0
- data/.claude/skills/authorization-pundit/SKILL.md +462 -0
- data/.claude/skills/caching-strategies/SKILL.md +350 -0
- data/.claude/skills/database-migrations/SKILL.md +354 -0
- data/.claude/skills/form-object-patterns/SKILL.md +399 -0
- data/.claude/skills/hotwire-patterns/SKILL.md +247 -0
- data/.claude/skills/hotwire-patterns/reference/stimulus.md +307 -0
- data/.claude/skills/hotwire-patterns/reference/tailwind-integration.md +112 -0
- data/.claude/skills/hotwire-patterns/reference/turbo-frames.md +158 -0
- data/.claude/skills/hotwire-patterns/reference/turbo-streams.md +218 -0
- data/.claude/skills/i18n-patterns/SKILL.md +320 -0
- data/.claude/skills/install/SKILL.md +367 -0
- data/.claude/skills/performance-optimization/SKILL.md +311 -0
- data/.claude/skills/rails-architecture/SKILL.md +259 -0
- data/.claude/skills/rails-architecture/reference/error-handling.md +333 -0
- data/.claude/skills/rails-architecture/reference/event-tracking.md +142 -0
- data/.claude/skills/rails-architecture/reference/layer-interactions.md +417 -0
- data/.claude/skills/rails-architecture/reference/multi-tenancy.md +152 -0
- data/.claude/skills/rails-architecture/reference/query-patterns.md +342 -0
- data/.claude/skills/rails-architecture/reference/service-patterns.md +286 -0
- data/.claude/skills/rails-architecture/reference/state-records.md +250 -0
- data/.claude/skills/rails-architecture/reference/testing-strategy.md +326 -0
- data/.claude/skills/rails-concern/SKILL.md +399 -0
- data/.claude/skills/rails-controller/SKILL.md +336 -0
- data/.claude/skills/rails-model-generator/SKILL.md +321 -0
- data/.claude/skills/rails-model-generator/reference/validations.md +298 -0
- data/.claude/skills/rails-presenter/SKILL.md +274 -0
- data/.claude/skills/rails-query-object/SKILL.md +289 -0
- data/.claude/skills/rails-service-object/SKILL.md +349 -0
- data/.claude/skills/solid-queue-setup/SKILL.md +307 -0
- data/.claude/skills/tdd-cycle/SKILL.md +359 -0
- data/.claude/skills/viewcomponent-patterns/SKILL.md +333 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +2 -0
- data/.ruby-version +1 -1
- data/.vbw-planning/.notification-log.jsonl +192 -0
- data/.vbw-planning/.session-log.jsonl +871 -0
- data/.vbw-planning/PROJECT.md +51 -0
- data/.vbw-planning/REQUIREMENTS.md +50 -0
- data/.vbw-planning/SHIPPED.md +28 -0
- data/.vbw-planning/codebase/ARCHITECTURE.md +147 -0
- data/.vbw-planning/codebase/CONCERNS.md +99 -0
- data/.vbw-planning/codebase/CONVENTIONS.md +97 -0
- data/.vbw-planning/codebase/DEPENDENCIES.md +100 -0
- data/.vbw-planning/codebase/INDEX.md +86 -0
- data/.vbw-planning/codebase/META.md +42 -0
- data/.vbw-planning/codebase/PATTERNS.md +262 -0
- data/.vbw-planning/codebase/STACK.md +101 -0
- data/.vbw-planning/codebase/STRUCTURE.md +324 -0
- data/.vbw-planning/codebase/TESTING.md +154 -0
- data/.vbw-planning/config.json +12 -0
- data/.vbw-planning/discovery.json +24 -0
- data/.vbw-planning/milestones/default/ROADMAP.md +115 -0
- data/.vbw-planning/milestones/default/STATE.md +83 -0
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +56 -0
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +187 -0
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +64 -0
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +137 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +67 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +142 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +64 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +138 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +85 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +147 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +63 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +129 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +74 -0
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +154 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +303 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +510 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +61 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +161 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +66 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +132 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +59 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +171 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +56 -0
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +152 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +33 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +42 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +119 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +52 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +195 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +79 -0
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +130 -0
- data/CHANGELOG.md +28 -0
- data/CLAUDE.md +179 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +114 -101
- data/Rakefile +2 -0
- data/app/assets/builds/source_monitor/application.css +2076 -0
- data/app/assets/builds/source_monitor/application.js +2758 -0
- data/app/assets/builds/source_monitor/application.js.map +7 -0
- data/app/controllers/source_monitor/application_controller.rb +2 -0
- data/app/controllers/source_monitor/health_controller.rb +2 -0
- data/app/controllers/source_monitor/import_sessions/bulk_configuration.rb +106 -0
- data/app/controllers/source_monitor/import_sessions/entry_annotation.rb +187 -0
- data/app/controllers/source_monitor/import_sessions/health_check_management.rb +112 -0
- data/app/controllers/source_monitor/import_sessions/opml_parser.rb +130 -0
- data/app/controllers/source_monitor/import_sessions_controller.rb +6 -507
- data/app/controllers/source_monitor/items_controller.rb +2 -0
- data/app/controllers/source_monitor/sources_controller.rb +0 -14
- data/app/helpers/source_monitor/application_helper.rb +4 -112
- data/app/helpers/source_monitor/health_badge_helper.rb +69 -0
- data/app/helpers/source_monitor/table_sort_helper.rb +53 -0
- data/app/jobs/source_monitor/application_job.rb +2 -0
- data/app/models/source_monitor/application_record.rb +2 -0
- data/app/models/source_monitor/log_entry.rb +0 -2
- data/config/coverage_baseline.json +217 -1862
- data/config/routes.rb +2 -0
- data/db/migrate/20251009103000_add_feed_content_readability_to_sources.rb +2 -0
- data/db/migrate/20251014171659_add_performance_indexes.rb +2 -0
- data/db/migrate/20251014172525_add_fetch_status_check_constraint.rb +2 -0
- data/db/migrate/20251108120116_refresh_fetch_status_constraint.rb +2 -0
- data/db/migrate/20260210204022_add_composite_index_to_log_entries.rb +17 -0
- data/lib/source_monitor/assets/bundler.rb +2 -0
- data/lib/source_monitor/assets.rb +2 -0
- data/lib/source_monitor/configuration/authentication_settings.rb +62 -0
- data/lib/source_monitor/configuration/events.rb +60 -0
- data/lib/source_monitor/configuration/fetching_settings.rb +27 -0
- data/lib/source_monitor/configuration/health_settings.rb +27 -0
- data/lib/source_monitor/configuration/http_settings.rb +43 -0
- data/lib/source_monitor/configuration/model_definition.rb +108 -0
- data/lib/source_monitor/configuration/models.rb +36 -0
- data/lib/source_monitor/configuration/realtime_settings.rb +95 -0
- data/lib/source_monitor/configuration/retention_settings.rb +45 -0
- data/lib/source_monitor/configuration/scraper_registry.rb +67 -0
- data/lib/source_monitor/configuration/scraping_settings.rb +39 -0
- data/lib/source_monitor/configuration/validation_definition.rb +32 -0
- data/lib/source_monitor/configuration.rb +12 -579
- data/lib/source_monitor/dashboard/queries/recent_activity_query.rb +138 -0
- data/lib/source_monitor/dashboard/queries/stats_query.rb +71 -0
- data/lib/source_monitor/dashboard/queries.rb +2 -195
- data/lib/source_monitor/engine.rb +2 -0
- data/lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb +141 -0
- data/lib/source_monitor/fetching/feed_fetcher/entry_processor.rb +89 -0
- data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +200 -0
- data/lib/source_monitor/fetching/feed_fetcher.rb +37 -379
- data/lib/source_monitor/items/item_creator/content_extractor.rb +113 -0
- data/lib/source_monitor/items/item_creator/entry_parser/media_extraction.rb +96 -0
- data/lib/source_monitor/items/item_creator/entry_parser.rb +294 -0
- data/lib/source_monitor/items/item_creator.rb +28 -455
- data/lib/source_monitor/setup/bundle_installer.rb +2 -0
- data/lib/source_monitor/setup/cli.rb +2 -0
- data/lib/source_monitor/setup/dependency_checker.rb +2 -0
- data/lib/source_monitor/setup/detectors.rb +2 -0
- data/lib/source_monitor/setup/gemfile_editor.rb +2 -0
- data/lib/source_monitor/setup/initializer_patcher.rb +2 -0
- data/lib/source_monitor/setup/install_generator.rb +2 -0
- data/lib/source_monitor/setup/migration_installer.rb +2 -0
- data/lib/source_monitor/setup/node_installer.rb +2 -0
- data/lib/source_monitor/setup/prompter.rb +2 -0
- data/lib/source_monitor/setup/requirements.rb +2 -0
- data/lib/source_monitor/setup/shell_runner.rb +2 -0
- data/lib/source_monitor/setup/verification/action_cable_verifier.rb +2 -0
- data/lib/source_monitor/setup/verification/printer.rb +2 -0
- data/lib/source_monitor/setup/verification/result.rb +2 -0
- data/lib/source_monitor/setup/verification/runner.rb +2 -0
- data/lib/source_monitor/setup/verification/solid_queue_verifier.rb +2 -0
- data/lib/source_monitor/setup/verification/telemetry_logger.rb +2 -0
- data/lib/source_monitor/setup/workflow.rb +2 -0
- data/lib/source_monitor/version.rb +3 -1
- data/lib/source_monitor.rb +140 -58
- data/lib/tasks/source_monitor_assets.rake +2 -0
- data/lib/tasks/source_monitor_setup.rake +2 -0
- data/lib/tasks/source_monitor_tasks.rake +2 -0
- data/source_monitor.gemspec +3 -1
- 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
|