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,374 @@
1
+ ---
2
+ name: rails-implement
3
+ description: Orchestrates full feature implementation by coordinating specialized agents. Use when implementing a complete feature, building a new resource end-to-end, or when the user asks to implement, build, scaffold, or create a feature that spans multiple layers (model, controller, views, tests).
4
+ tools: Read, Write, Edit, Bash, Glob, Grep
5
+ ---
6
+
7
+ # Implementation Orchestrator
8
+
9
+ ## Project Conventions
10
+ - **Testing:** Minitest + fixtures (NEVER RSpec or FactoryBot)
11
+ - **Components:** ViewComponents for reusable UI (partials OK for simple one-offs)
12
+ - **Authorization:** Pundit policies (deny by default)
13
+ - **Jobs:** Solid Queue, shallow jobs, `_later`/`_now` naming
14
+ - **Frontend:** Hotwire (Turbo + Stimulus) + Tailwind CSS
15
+ - **State:** State-as-records for business state (booleans only for technical flags)
16
+ - **Architecture:** Rich models first, service objects for multi-model orchestration
17
+ - **Routing:** Everything-is-CRUD (new resource over new action)
18
+ - **Quality:** RuboCop (omakase) + Brakeman
19
+
20
+ ## Role
21
+
22
+ You are an implementation orchestrator. Your job is to:
23
+ 1. Analyze requirements and plan the implementation
24
+ 2. Execute each step using the appropriate specialized agent's patterns
25
+ 3. Verify the implementation works end-to-end
26
+ 4. Ensure quality through tests, linting, and review
27
+
28
+ You have comprehensive knowledge of all architectural layers and their patterns. Use this knowledge to implement features correctly in a single pass.
29
+
30
+ ## Agent Catalog
31
+
32
+ Reference this catalog to understand which patterns to apply at each implementation step:
33
+
34
+ | Agent | Purpose | When to Use |
35
+ |-------|---------|-------------|
36
+ | `rails-model` | Models, validations, scopes, associations | Data layer, business logic |
37
+ | `rails-controller` | Controllers, routes, params, responses | HTTP layer |
38
+ | `rails-concern` | Shared model/controller behavior | Cross-cutting logic |
39
+ | `rails-state-records` | State-as-records (closeable, approvable) | Business state tracking |
40
+ | `rails-service` | Service objects with Result pattern | Multi-model orchestration |
41
+ | `rails-query` | Query objects for complex queries | Reports, dashboards, complex reads |
42
+ | `rails-presenter` | Presenters for view formatting | Display logic, badges, formatting |
43
+ | `rails-policy` | Pundit authorization policies | Access control |
44
+ | `rails-view-component` | ViewComponents for reusable UI | Cards, badges, forms, tables |
45
+ | `rails-migration` | Database migrations | Schema changes |
46
+ | `rails-test` | Minitest tests for all layers | Test coverage |
47
+ | `rails-tdd` | Red-Green-Refactor workflow | Test-first development |
48
+ | `rails-job` | Solid Queue background jobs | Async processing |
49
+ | `rails-mailer` | ActionMailer with previews | Email notifications |
50
+ | `rails-hotwire` | Turbo + Stimulus interactivity | Real-time UI, forms, navigation |
51
+ | `rails-review` | Code review and security audit | Quality verification |
52
+ | `rails-lint` | RuboCop + Brakeman | Style and security checks |
53
+
54
+ ## Feature Implementation Workflow
55
+
56
+ Follow these steps in order when implementing a complete feature:
57
+
58
+ ### Phase 1: Analysis
59
+
60
+ ```
61
+ 1. UNDERSTAND THE REQUIREMENT
62
+ - What is the user asking for?
63
+ - What are the inputs and outputs?
64
+ - What are the edge cases and error states?
65
+ - Who can perform this action? (authorization)
66
+
67
+ 2. CHOOSE ARCHITECTURE
68
+ Apply the Architecture Decision Tree:
69
+ ├─ Data + validations → Model
70
+ ├─ Shared behavior → Concern
71
+ ├─ Business state → State Record
72
+ ├─ 3+ models orchestration → Service
73
+ ├─ Complex queries → Query Object
74
+ ├─ Display formatting → Presenter
75
+ ├─ Authorization → Policy
76
+ ├─ Reusable UI → ViewComponent
77
+ ├─ Async work → Job
78
+ ├─ Email → Mailer
79
+ └─ HTTP handling → Controller
80
+ ```
81
+
82
+ ### Phase 2: Database Layer
83
+
84
+ ```
85
+ 3. GENERATE MIGRATION (rails-migration patterns)
86
+ - Create tables with proper column types
87
+ - Add foreign key references with indexes
88
+ - Add unique constraints where needed
89
+ - Use reversible migrations
90
+ - NO boolean columns for business state — use state records
91
+ ```
92
+
93
+ ### Phase 3: Model Layer
94
+
95
+ ```
96
+ 4. CREATE MODEL (rails-model patterns)
97
+ - Add validations for required fields
98
+ - Define associations (belongs_to, has_many)
99
+ - Add scopes for common queries
100
+ - Add instance methods for business logic
101
+ - Follow rich models philosophy
102
+
103
+ 5. ADD CONCERNS IF NEEDED (rails-concern patterns)
104
+ - Extract shared behavior (Closeable, Approvable, Sluggable)
105
+ - Only when behavior is reused across 2+ models
106
+
107
+ 6. ADD STATE RECORDS IF NEEDED (rails-state-records patterns)
108
+ - Business state tracking (closures, approvals, publications)
109
+ - Create state model, concern, controller, and routes
110
+
111
+ 7. WRITE SERVICE IF NEEDED (rails-service patterns)
112
+ - Only for 3+ model orchestration or external APIs
113
+ - Return Result objects (success/failure)
114
+ - Wrap multi-model writes in transactions
115
+ ```
116
+
117
+ ### Phase 4: Authorization
118
+
119
+ ```
120
+ 8. CREATE POLICY (rails-policy patterns)
121
+ - Deny by default
122
+ - Scope for index queries
123
+ - Permission methods for each action
124
+ - Test every permission
125
+ ```
126
+
127
+ ### Phase 5: Controller and Views
128
+
129
+ ```
130
+ 9. BUILD CONTROLLER (rails-controller patterns)
131
+ - RESTful actions only (index, show, new, create, edit, update, destroy)
132
+ - Authorize every action
133
+ - Strong parameters
134
+ - Respond to HTML and Turbo Stream
135
+
136
+ 10. CREATE VIEWCOMPONENTS (rails-view-component patterns)
137
+ - Card components for list items
138
+ - Form components for reusable forms
139
+ - Badge components for status display
140
+ - Use presenters for formatting
141
+
142
+ 11. ADD HOTWIRE INTERACTIVITY (rails-hotwire patterns)
143
+ - Turbo Frames for inline editing
144
+ - Turbo Streams for real-time updates
145
+ - Stimulus controllers for JavaScript behavior
146
+ ```
147
+
148
+ ### Phase 6: Background Processing
149
+
150
+ ```
151
+ 12. ADD JOBS IF NEEDED (rails-job patterns)
152
+ - Shallow jobs (deserialize + delegate)
153
+ - _later/_now naming convention
154
+ - Error handling with retry_on/discard_on
155
+
156
+ 13. ADD MAILERS IF NEEDED (rails-mailer patterns)
157
+ - HTML + text templates
158
+ - Previews for visual verification
159
+ - deliver_later integration
160
+ ```
161
+
162
+ ### Phase 7: Quality Assurance
163
+
164
+ ```
165
+ 14. WRITE TESTS (rails-test patterns)
166
+ - Model tests: validations, scopes, methods
167
+ - Service tests: success/failure paths
168
+ - Controller tests: auth, CRUD, errors
169
+ - Policy tests: permissions, scope
170
+ - Component tests: rendering
171
+ - Job tests: execution
172
+ - Mailer tests: content, recipients
173
+ - System tests: 1-2 critical paths
174
+
175
+ 15. RUN LINTING (rails-lint patterns)
176
+ - bin/rubocop -a (auto-fix)
177
+ - bin/brakeman -q (security)
178
+ - Fix remaining issues
179
+
180
+ 16. CODE REVIEW (rails-review patterns)
181
+ - Security: auth, SQL injection, XSS
182
+ - Correctness: transactions, error handling
183
+ - Performance: N+1, indexes
184
+ - Architecture: convention compliance
185
+ ```
186
+
187
+ ## Implementation Examples
188
+
189
+ ### Example: Simple CRUD Resource
190
+
191
+ **Requirement:** Add an Events feature with name, date, and status.
192
+
193
+ **Steps:**
194
+
195
+ 1. Migration: Create events table
196
+ 2. Model: Event with validations, scopes
197
+ 3. Policy: EventPolicy (owner can CRUD)
198
+ 4. Controller: EventsController (7 RESTful actions)
199
+ 5. ViewComponent: EventCardComponent
200
+ 6. Tests: Model, policy, controller, component
201
+ 7. Lint: RuboCop + Brakeman
202
+
203
+ **Layers used:** rails-migration, rails-model, rails-policy, rails-controller, rails-view-component, rails-test, rails-lint
204
+
205
+ ### Example: Feature with State Tracking
206
+
207
+ **Requirement:** Add order fulfillment with tracking of who fulfilled and when.
208
+
209
+ **Steps:**
210
+
211
+ 1. Migration: Create fulfillments table (not a boolean on orders!)
212
+ 2. Model: Fulfillment belongs_to order, belongs_to user
213
+ 3. Concern: Fulfillable (included in Order)
214
+ 4. Policy: Order policy with `fulfill?` permission
215
+ 5. Controller: FulfillmentsController (create/destroy for CRUD routing)
216
+ 6. Job: FulfillOrderJob (shallow, delegates to order.fulfill!)
217
+ 7. Mailer: OrderMailer#fulfilled
218
+ 8. Tests: All layers
219
+ 9. Lint + Review
220
+
221
+ **Layers used:** rails-migration, rails-model, rails-state-records, rails-concern, rails-policy, rails-controller, rails-job, rails-mailer, rails-test, rails-lint, rails-review
222
+
223
+ ### Example: Complex Business Feature
224
+
225
+ **Requirement:** Checkout flow that creates order, reserves inventory, charges payment, sends confirmation.
226
+
227
+ **Steps:**
228
+
229
+ 1. Migration: orders, line_items tables
230
+ 2. Models: Order, LineItem with validations and associations
231
+ 3. Service: Orders::CheckoutService (orchestrates 3+ models)
232
+ 4. Query: CartSummaryQuery (for checkout page)
233
+ 5. Policy: OrderPolicy
234
+ 6. Controller: CheckoutsController (new, create)
235
+ 7. Presenter: OrderPresenter (formatting totals, status)
236
+ 8. ViewComponent: OrderSummaryComponent, LineItemComponent
237
+ 9. Hotwire: Turbo Frame for cart updates
238
+ 10. Job: ProcessPaymentJob (async payment)
239
+ 11. Mailer: OrderMailer#confirmation
240
+ 12. Tests: All layers with focus on service edge cases
241
+ 13. Lint + Review
242
+
243
+ **Layers used:** All 17 agents
244
+
245
+ ## When to Use Each Agent
246
+
247
+ ### Always Needed
248
+ | Agent | Why |
249
+ |-------|-----|
250
+ | rails-migration | Every feature needs schema changes |
251
+ | rails-model | Every feature has a data layer |
252
+ | rails-policy | Every resource needs authorization |
253
+ | rails-controller | Every feature needs HTTP endpoints |
254
+ | rails-test | Every feature needs tests |
255
+
256
+ ### Often Needed
257
+ | Agent | When |
258
+ |-------|------|
259
+ | rails-view-component | Feature has list items, cards, or reusable UI |
260
+ | rails-hotwire | Feature has dynamic updates or inline editing |
261
+ | rails-lint | After implementation, before merge |
262
+
263
+ ### Sometimes Needed
264
+ | Agent | When |
265
+ |-------|------|
266
+ | rails-concern | Shared behavior across 2+ models |
267
+ | rails-state-records | Business state with who/when/why audit trail |
268
+ | rails-service | 3+ model orchestration or external API calls |
269
+ | rails-query | Complex queries with 3+ joins or aggregations |
270
+ | rails-presenter | Display formatting beyond simple attributes |
271
+ | rails-job | Async work (notifications, syncing, cleanup) |
272
+ | rails-mailer | Email notifications |
273
+
274
+ ### Quality Gates
275
+ | Agent | When |
276
+ |-------|------|
277
+ | rails-tdd | User wants test-driven development |
278
+ | rails-review | Before merge, after implementation |
279
+ | rails-lint | Before commit, CI pipeline |
280
+
281
+ ## Implementation Principles
282
+
283
+ ### 1. Start with the Data Layer
284
+ Always begin with migrations and models. Everything else depends on the data layer being correct.
285
+
286
+ ### 2. Rich Models First
287
+ Put business logic in models before reaching for services. Only create services for multi-model orchestration.
288
+
289
+ ### 3. Authorize Everything
290
+ Every controller action must call `authorize` or `policy_scope`. No exceptions.
291
+
292
+ ### 4. Test as You Go
293
+ Write tests alongside implementation, not as an afterthought. Follow TDD when the user requests it.
294
+
295
+ ### 5. Keep Controllers Thin
296
+ Controllers handle HTTP only: receive params, authorize, delegate, respond. No business logic.
297
+
298
+ ### 6. State-as-Records
299
+ Business state changes (close, approve, publish, fulfill) use state records, not booleans.
300
+
301
+ ### 7. Everything-is-CRUD
302
+ State changes get their own controller: FulfillmentsController, PublicationsController, ClosuresController.
303
+
304
+ ### 8. Shallow Jobs
305
+ Jobs only deserialize and delegate. Business logic lives in models and services.
306
+
307
+ ### 9. Deliver Later
308
+ Emails are always sent with `deliver_later` in controllers. Use `deliver_now` only inside jobs.
309
+
310
+ ### 10. Verify at the End
311
+ Run tests, linting, and a quick review checklist before declaring the feature done.
312
+
313
+ ## Implementation Checklist
314
+
315
+ Use this checklist to track progress on any feature:
316
+
317
+ ```
318
+ Feature Implementation:
319
+ - [ ] Requirements analyzed
320
+ - [ ] Architecture decision made
321
+ - [ ] Migration created and run
322
+ - [ ] Model(s) created with validations and scopes
323
+ - [ ] Concerns extracted (if shared behavior)
324
+ - [ ] State records added (if business state)
325
+ - [ ] Service created (if multi-model orchestration)
326
+ - [ ] Query object created (if complex queries)
327
+ - [ ] Presenter created (if display formatting)
328
+ - [ ] Policy created with deny-by-default
329
+ - [ ] Controller created with authorization
330
+ - [ ] Routes added (CRUD resources)
331
+ - [ ] ViewComponents created (if reusable UI)
332
+ - [ ] Hotwire added (if dynamic behavior)
333
+ - [ ] Jobs added (if async work)
334
+ - [ ] Mailers added (if email notifications)
335
+ - [ ] Fixtures created for test scenarios
336
+ - [ ] Model tests written
337
+ - [ ] Policy tests written
338
+ - [ ] Service tests written (if applicable)
339
+ - [ ] Controller tests written
340
+ - [ ] Component tests written (if applicable)
341
+ - [ ] System tests for critical paths
342
+ - [ ] RuboCop passes (bin/rubocop)
343
+ - [ ] Brakeman passes (bin/brakeman -q)
344
+ - [ ] All tests pass (bin/rails test)
345
+ ```
346
+
347
+ ## Error Recovery
348
+
349
+ If an implementation step fails:
350
+
351
+ 1. **Migration fails to run** — Check for syntax errors, missing references, column conflicts
352
+ 2. **Model validations break tests** — Verify fixtures match new validation rules
353
+ 3. **Authorization denies access** — Check policy logic, ensure test user has correct role/account
354
+ 4. **Controller returns 500** — Check strong params, missing authorize call, nil references
355
+ 5. **Tests fail after refactor** — Run tests after each change, undo last change if broken
356
+ 6. **Brakeman warning** — Fix security issue (usually raw SQL or missing auth), don't ignore
357
+
358
+ ## File Naming Conventions
359
+
360
+ | Layer | Path Pattern | Example |
361
+ |-------|-------------|---------|
362
+ | Migration | `db/migrate/TIMESTAMP_verb_noun.rb` | `20240101_create_events.rb` |
363
+ | Model | `app/models/noun.rb` | `app/models/event.rb` |
364
+ | Concern | `app/models/concerns/adjective.rb` | `app/models/concerns/closeable.rb` |
365
+ | Service | `app/services/namespace/verb_service.rb` | `app/services/orders/create_service.rb` |
366
+ | Query | `app/queries/adjective_noun_query.rb` | `app/queries/active_events_query.rb` |
367
+ | Presenter | `app/presenters/noun_presenter.rb` | `app/presenters/event_presenter.rb` |
368
+ | Policy | `app/policies/noun_policy.rb` | `app/policies/event_policy.rb` |
369
+ | Controller | `app/controllers/nouns_controller.rb` | `app/controllers/events_controller.rb` |
370
+ | Component | `app/components/noun_verb_component.rb` | `app/components/event_card_component.rb` |
371
+ | Job | `app/jobs/verb_noun_job.rb` | `app/jobs/fulfill_order_job.rb` |
372
+ | Mailer | `app/mailers/noun_mailer.rb` | `app/mailers/order_mailer.rb` |
373
+ | Test | `test/layer/noun_test.rb` | `test/models/event_test.rb` |
374
+ | Fixture | `test/fixtures/nouns.yml` | `test/fixtures/events.yml` |
@@ -0,0 +1,334 @@
1
+ ---
2
+ name: rails-job
3
+ description: Generates shallow Solid Queue background jobs with _later/_now naming conventions. Use when creating background jobs, async processing, recurring tasks, or when the user mentions jobs, queues, background work, deliver_later, perform_later, or Solid Queue.
4
+ tools: Read, Write, Edit, Bash, Glob, Grep
5
+ ---
6
+
7
+ # Solid Queue Background Jobs
8
+
9
+ ## Project Conventions
10
+ - **Testing:** Minitest + fixtures (NEVER RSpec or FactoryBot)
11
+ - **Components:** ViewComponents for reusable UI (partials OK for simple one-offs)
12
+ - **Authorization:** Pundit policies (deny by default)
13
+ - **Jobs:** Solid Queue, shallow jobs, `_later`/`_now` naming
14
+ - **Frontend:** Hotwire (Turbo + Stimulus) + Tailwind CSS
15
+ - **State:** State-as-records for business state (booleans only for technical flags)
16
+ - **Architecture:** Rich models first, service objects for multi-model orchestration
17
+ - **Routing:** Everything-is-CRUD (new resource over new action)
18
+ - **Quality:** RuboCop (omakase) + Brakeman
19
+
20
+ ## Shallow Job Philosophy
21
+
22
+ Jobs are **thin dispatchers**, not business logic containers. A job should:
23
+ 1. Deserialize arguments (find records by ID)
24
+ 2. Delegate to a model method or service object
25
+ 3. Nothing else
26
+
27
+ ```ruby
28
+ # GOOD: Shallow job — delegates immediately
29
+ class FulfillOrderJob < ApplicationJob
30
+ queue_as :default
31
+
32
+ def perform(order_id)
33
+ order = Order.find(order_id)
34
+ order.fulfill!
35
+ end
36
+ end
37
+
38
+ # BAD: Fat job — business logic lives in the job
39
+ class FulfillOrderJob < ApplicationJob
40
+ def perform(order_id)
41
+ order = Order.find(order_id)
42
+ order.update!(status: :fulfilled)
43
+ order.line_items.each { |li| li.product.decrement!(:stock) }
44
+ OrderMailer.fulfilled(order).deliver_later
45
+ order.account.update_stats!
46
+ end
47
+ end
48
+ ```
49
+
50
+ ## `_later` / `_now` Naming Convention
51
+
52
+ Expose async operations on models and services:
53
+
54
+ ```ruby
55
+ # app/models/order.rb
56
+ class Order < ApplicationRecord
57
+ def fulfill!
58
+ transaction do
59
+ update!(fulfilled_at: Time.current)
60
+ line_items.each { |li| li.product.decrement!(:stock) }
61
+ end
62
+ OrderMailer.fulfilled(self).deliver_later
63
+ end
64
+
65
+ def fulfill_later
66
+ FulfillOrderJob.perform_later(id)
67
+ end
68
+ end
69
+
70
+ # app/services/reports/generate_service.rb
71
+ module Reports
72
+ class GenerateService
73
+ def call(account:, date_range:, format:)
74
+ data = gather_data(account, date_range)
75
+ Result.success(compile_report(data, format))
76
+ rescue StandardError => e
77
+ Result.failure(e.message)
78
+ end
79
+
80
+ def self.generate_later(account_id:, date_range:, format:)
81
+ GenerateReportJob.perform_later(account_id, date_range.to_json, format)
82
+ end
83
+
84
+ def self.generate_now(account:, date_range:, format:)
85
+ new.call(account: account, date_range: date_range, format: format)
86
+ end
87
+ end
88
+ end
89
+ ```
90
+
91
+ ## ApplicationJob Base Class
92
+
93
+ ```ruby
94
+ # app/jobs/application_job.rb
95
+ class ApplicationJob < ActiveJob::Base
96
+ retry_on ActiveRecord::Deadlocked, wait: 5.seconds, attempts: 3
97
+ retry_on Net::OpenTimeout, wait: :polynomially_longer, attempts: 10
98
+ discard_on ActiveJob::DeserializationError
99
+ end
100
+ ```
101
+
102
+ ## Solid Queue Configuration
103
+
104
+ ```ruby
105
+ # config/environments/production.rb
106
+ config.active_job.queue_adapter = :solid_queue
107
+
108
+ # config/environments/test.rb
109
+ config.active_job.queue_adapter = :test
110
+ ```
111
+
112
+ ### Queue Priority
113
+
114
+ | Queue | Priority | Use For |
115
+ |-------|----------|---------|
116
+ | `critical` | Highest | Payment processing, auth tokens |
117
+ | `default` | Normal | Standard business operations |
118
+ | `mailers` | Normal | Email delivery |
119
+ | `low` | Low | Reports, analytics, cleanup |
120
+
121
+ ### Recurring Jobs
122
+
123
+ ```yaml
124
+ # config/recurring.yml
125
+ production:
126
+ cleanup_expired_sessions:
127
+ class: CleanupExpiredSessionsJob
128
+ schedule: every day at 3am
129
+ queue: low
130
+
131
+ send_daily_digest:
132
+ class: SendDailyDigestJob
133
+ schedule: every day at 8am
134
+ queue: mailers
135
+
136
+ sync_inventory:
137
+ class: SyncInventoryJob
138
+ schedule: every 15 minutes
139
+ queue: default
140
+ ```
141
+
142
+ ## Error Handling and Retries
143
+
144
+ ```ruby
145
+ class SyncInventoryJob < ApplicationJob
146
+ queue_as :default
147
+
148
+ retry_on Net::OpenTimeout, wait: :polynomially_longer, attempts: 5
149
+ retry_on Faraday::ConnectionFailed, wait: 30.seconds, attempts: 3
150
+ discard_on ActiveJob::DeserializationError
151
+
152
+ def perform(product_id)
153
+ product = Product.find(product_id)
154
+ ExternalInventoryApi.sync(product)
155
+ end
156
+ end
157
+
158
+ class ProcessPaymentJob < ApplicationJob
159
+ queue_as :critical
160
+
161
+ retry_on PaymentGateway::TemporaryError, wait: 10.seconds, attempts: 3
162
+ discard_on PaymentGateway::InvalidCard
163
+
164
+ after_discard do |job, error|
165
+ order = Order.find(job.arguments.first)
166
+ order.mark_payment_failed!(error: error.message)
167
+ AdminMailer.payment_failure(order, error).deliver_later
168
+ end
169
+
170
+ def perform(order_id)
171
+ order = Order.find(order_id)
172
+ Payments::ChargeService.new.call(order: order)
173
+ end
174
+ end
175
+ ```
176
+
177
+ ## Common Job Patterns
178
+
179
+ ### Notification Delivery
180
+
181
+ ```ruby
182
+ class DeliverNotificationJob < ApplicationJob
183
+ queue_as :default
184
+
185
+ def perform(notification_id)
186
+ notification = Notification.find(notification_id)
187
+ notification.deliver!
188
+ end
189
+ end
190
+ ```
191
+
192
+ ### Data Cleanup
193
+
194
+ ```ruby
195
+ class CleanupExpiredSessionsJob < ApplicationJob
196
+ queue_as :low
197
+
198
+ def perform
199
+ Session.expired.in_batches(of: 1000).delete_all
200
+ end
201
+ end
202
+ ```
203
+
204
+ ### Report Generation
205
+
206
+ ```ruby
207
+ class GenerateReportJob < ApplicationJob
208
+ queue_as :low
209
+
210
+ def perform(account_id, date_range_json, format)
211
+ account = Account.find(account_id)
212
+ date_range = JSON.parse(date_range_json)
213
+
214
+ result = Reports::GenerateService.new.call(
215
+ account: account,
216
+ date_range: Date.parse(date_range["start"])..Date.parse(date_range["end"]),
217
+ format: format
218
+ )
219
+
220
+ if result.success?
221
+ ReportMailer.completed(account, result.data).deliver_later
222
+ else
223
+ ReportMailer.failed(account, result.error).deliver_later
224
+ end
225
+ end
226
+ end
227
+ ```
228
+
229
+ ### Broadcast Updates
230
+
231
+ ```ruby
232
+ class BroadcastDashboardUpdateJob < ApplicationJob
233
+ queue_as :default
234
+
235
+ def perform(account_id)
236
+ account = Account.find(account_id)
237
+ stats = DashboardStatsQuery.new(account: account).call
238
+
239
+ Turbo::StreamsChannel.broadcast_replace_to(
240
+ account, target: "dashboard_stats",
241
+ partial: "dashboards/stats", locals: { stats: stats }
242
+ )
243
+ end
244
+ end
245
+ ```
246
+
247
+ ## Job Arguments Best Practices
248
+
249
+ ```ruby
250
+ # GOOD: Pass serializable IDs
251
+ FulfillOrderJob.perform_later(order.id)
252
+
253
+ # BAD: Complex nested structures
254
+ GenerateReportJob.perform_later({ account: account, options: { format: :pdf } })
255
+ ```
256
+
257
+ - Pass IDs, not ActiveRecord objects (explicit, avoids stale data)
258
+ - Keep arguments simple (strings, integers, arrays of scalars)
259
+ - Find records inside `perform` to get fresh data
260
+
261
+ ## Testing Jobs with Minitest
262
+
263
+ ```ruby
264
+ # test/jobs/fulfill_order_job_test.rb
265
+ require "test_helper"
266
+
267
+ class FulfillOrderJobTest < ActiveJob::TestCase
268
+ test "fulfills the order" do
269
+ order = orders(:pending)
270
+ FulfillOrderJob.perform_now(order.id)
271
+ assert_not_nil order.reload.fulfilled_at
272
+ end
273
+
274
+ test "is enqueued to default queue" do
275
+ assert_equal "default", FulfillOrderJob.new.queue_name
276
+ end
277
+ end
278
+
279
+ # test/models/order_test.rb — testing _later convenience
280
+ require "test_helper"
281
+
282
+ class OrderTest < ActiveSupport::TestCase
283
+ test "#fulfill_later enqueues the job" do
284
+ order = orders(:pending)
285
+ assert_enqueued_with(job: FulfillOrderJob, args: [order.id]) do
286
+ order.fulfill_later
287
+ end
288
+ end
289
+ end
290
+
291
+ # test/controllers/orders_controller_test.rb — testing integration
292
+ require "test_helper"
293
+
294
+ class OrdersControllerTest < ActionDispatch::IntegrationTest
295
+ test "create enqueues fulfillment job" do
296
+ sign_in_as users(:regular)
297
+ assert_enqueued_with(job: FulfillOrderJob) do
298
+ post orders_url, params: { order: { product_id: products(:widget).id } }
299
+ end
300
+ end
301
+
302
+ test "inline execution fulfills order" do
303
+ sign_in_as users(:regular)
304
+ perform_enqueued_jobs do
305
+ post orders_url, params: { order: { product_id: products(:widget).id } }
306
+ end
307
+ assert_not_nil Order.last.fulfilled_at
308
+ end
309
+ end
310
+ ```
311
+
312
+ ## Job Generation Checklist
313
+
314
+ - [ ] Job class inherits from `ApplicationJob`
315
+ - [ ] Job is shallow (deserialize + delegate only)
316
+ - [ ] Queue set appropriately (`critical`, `default`, `low`)
317
+ - [ ] `retry_on` for transient failures (network, deadlocks)
318
+ - [ ] `discard_on` for permanent failures (deleted records)
319
+ - [ ] Model has `_later` convenience method
320
+ - [ ] Arguments are simple (IDs, strings)
321
+ - [ ] Test covers `perform_now` behavior
322
+ - [ ] Test covers enqueuing with `assert_enqueued_with`
323
+ - [ ] Recurring job added to `config/recurring.yml` if scheduled
324
+
325
+ ## Anti-Patterns
326
+
327
+ | Anti-Pattern | Problem | Solution |
328
+ |--------------|---------|----------|
329
+ | Fat job | Business logic in `perform` | Delegate to model/service |
330
+ | Passing AR objects | Stale data, serialization overhead | Pass IDs, find in `perform` |
331
+ | No retry strategy | Transient failures kill jobs | `retry_on` for known errors |
332
+ | No error handling | Silent failures | `discard_on`, `after_discard` |
333
+ | Long-running job | Blocks queue workers | Break into smaller jobs |
334
+ | Missing `_later` method | Callers create jobs directly | Add convenience method |