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,314 @@
1
+ ---
2
+ name: rails-tdd
3
+ description: Guides Red-Green-Refactor TDD workflow using Minitest and fixtures. Use when the user wants to implement a feature using TDD, write tests first, follow test-driven practices, or mentions red-green-refactor, test-driven development, or writing tests before code.
4
+ tools: Read, Write, Edit, Bash, Glob, Grep
5
+ ---
6
+
7
+ # TDD Workflow — Red-Green-Refactor
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
+ ## The TDD Cycle
21
+
22
+ ```
23
+ 1. RED → Write a failing test that describes desired behavior
24
+ 2. GREEN → Write the minimum code to make the test pass
25
+ 3. REFACTOR → Improve code while keeping tests green
26
+ 4. REPEAT → Next behavior
27
+ ```
28
+
29
+ ## Workflow Checklist
30
+
31
+ ```
32
+ - [ ] Understand the requirement (input, output, edge cases)
33
+ - [ ] Choose test type (model/controller/system/service/etc.)
34
+ - [ ] Create or update fixtures as needed
35
+ - [ ] Write ONE failing test (RED)
36
+ - [ ] Run test — verify it FAILS with expected error
37
+ - [ ] Write minimum code to pass (GREEN)
38
+ - [ ] Run test — verify it PASSES
39
+ - [ ] Refactor if needed (improve code, keep tests green)
40
+ - [ ] Run ALL tests — verify nothing broke
41
+ - [ ] Repeat for the next behavior
42
+ ```
43
+
44
+ ## Choosing the Right Test Type
45
+
46
+ | What You're Building | Test Type | Base Class |
47
+ |---------------------|-----------|------------|
48
+ | Validation, scope, method | Model test | `ActiveSupport::TestCase` |
49
+ | HTTP endpoint | Controller test | `ActionDispatch::IntegrationTest` |
50
+ | Multi-model business logic | Service test | `ActiveSupport::TestCase` |
51
+ | Complex query | Query test | `ActiveSupport::TestCase` |
52
+ | Authorization rule | Policy test | `ActiveSupport::TestCase` |
53
+ | Reusable UI component | Component test | `ViewComponent::TestCase` |
54
+ | Background processing | Job test | `ActiveJob::TestCase` |
55
+ | Email content | Mailer test | `ActionMailer::TestCase` |
56
+ | Full user flow with JS | System test | `ApplicationSystemTestCase` |
57
+
58
+ ## Writing Good Failing Tests (RED)
59
+
60
+ ### One Concept Per Test
61
+
62
+ ```ruby
63
+ # GOOD: One concept each
64
+ test "requires title" do
65
+ post = Post.new(title: nil)
66
+ assert_not post.valid?
67
+ assert_includes post.errors[:title], "can't be blank"
68
+ end
69
+
70
+ test "requires body" do
71
+ post = Post.new(body: nil)
72
+ assert_not post.valid?
73
+ end
74
+
75
+ # BAD: Multiple concepts
76
+ test "validates presence" do
77
+ post = Post.new(title: nil, body: nil)
78
+ assert_not post.valid?
79
+ assert_includes post.errors[:title], "can't be blank"
80
+ assert_includes post.errors[:body], "can't be blank"
81
+ end
82
+ ```
83
+
84
+ ### Descriptive Test Names
85
+
86
+ ```ruby
87
+ # GOOD: Names describe behavior
88
+ test "published scope returns only published posts"
89
+ test "returns failure when cart is empty"
90
+ test "admin can destroy any event"
91
+
92
+ # BAD: Vague
93
+ test "scope works"
94
+ test "handles error"
95
+ ```
96
+
97
+ ### Verify the Failure
98
+
99
+ Run the test. The failure should tell you what's missing:
100
+
101
+ ```
102
+ # GOOD failures: NameError: uninitialized constant Post
103
+ # NoMethodError: undefined method 'publish!'
104
+ # BAD failures: ActiveRecord::ConnectionNotEstablished
105
+ # SyntaxError: unexpected end
106
+ ```
107
+
108
+ ## Minimal Implementation (GREEN)
109
+
110
+ Write the MINIMUM code to pass — no optimization, no untested edge cases:
111
+
112
+ ```ruby
113
+ # Test says: requires title
114
+ # GREEN: Just add the validation
115
+ validates :title, presence: true
116
+
117
+ # NOT this (over-engineering without tests):
118
+ validates :title, presence: true, length: { minimum: 3, maximum: 255 },
119
+ uniqueness: { scope: :account_id }
120
+ ```
121
+
122
+ ## Refactoring Rules
123
+
124
+ 1. Only refactor when tests are GREEN
125
+ 2. One change at a time
126
+ 3. Run tests after EACH change
127
+ 4. If tests break, undo the last change
128
+ 5. Don't add new features during refactoring
129
+
130
+ ## Example: TDD a Complete Model
131
+
132
+ **Requirement:** Event with name, date, upcoming scope, days_until method
133
+
134
+ ```ruby
135
+ # Cycle 1: RED — test validation
136
+ test "requires name" do
137
+ event = Event.new(name: nil)
138
+ assert_not event.valid?
139
+ assert_includes event.errors[:name], "can't be blank"
140
+ end
141
+ # GREEN: validates :name, presence: true
142
+
143
+ # Cycle 2: RED — test another validation
144
+ test "requires event_date" do
145
+ event = Event.new(event_date: nil)
146
+ assert_not event.valid?
147
+ end
148
+ # GREEN: validates :event_date, presence: true
149
+
150
+ # Cycle 3: RED — test scope
151
+ test ".upcoming returns only future events" do
152
+ assert_includes Event.upcoming, events(:upcoming)
153
+ assert_not_includes Event.upcoming, events(:past)
154
+ end
155
+ # GREEN: scope :upcoming, -> { where("event_date >= ?", Date.current) }
156
+
157
+ # Cycle 4: RED — test method
158
+ test "#days_until returns days until event" do
159
+ event = Event.new(event_date: 5.days.from_now.to_date)
160
+ assert_equal 5, event.days_until
161
+ end
162
+ # GREEN: def days_until = (event_date - Date.current).to_i
163
+ ```
164
+
165
+ ## Example: TDD a Controller Action
166
+
167
+ ```ruby
168
+ # Cycle 1: Authentication required
169
+ test "create requires authentication" do
170
+ post events_url, params: { event: { name: "Test" } }
171
+ assert_redirected_to new_session_url
172
+ end
173
+
174
+ # Cycle 2: Successful create
175
+ test "creates event with valid params" do
176
+ sign_in_as users(:regular)
177
+ assert_difference("Event.count") do
178
+ post events_url, params: {
179
+ event: { name: "New Event", event_date: 1.week.from_now.to_date }
180
+ }
181
+ end
182
+ assert_redirected_to event_url(Event.last)
183
+ end
184
+
185
+ # Cycle 3: Validation errors
186
+ test "renders errors for invalid params" do
187
+ sign_in_as users(:regular)
188
+ assert_no_difference("Event.count") do
189
+ post events_url, params: { event: { name: "" } }
190
+ end
191
+ assert_response :unprocessable_entity
192
+ end
193
+ ```
194
+
195
+ ## Example: TDD a Service Object
196
+
197
+ ```ruby
198
+ # Cycle 1: Success path
199
+ test "returns success with valid params" do
200
+ result = @service.call(user: @user, items: [{ product_id: @product.id, quantity: 1 }])
201
+ assert result.success?
202
+ assert_kind_of Order, result.data
203
+ end
204
+ # GREEN: Minimal service that creates order and returns Result.success
205
+
206
+ # Cycle 2: Line items
207
+ test "creates line items" do
208
+ assert_difference "LineItem.count", 1 do
209
+ @service.call(user: @user, items: [{ product_id: @product.id, quantity: 2 }])
210
+ end
211
+ end
212
+ # GREEN: Add line item creation to service
213
+
214
+ # Cycle 3: Failure path
215
+ test "returns failure with empty items" do
216
+ result = @service.call(user: @user, items: [])
217
+ assert result.failure?
218
+ assert_equal :empty_cart, result.code
219
+ end
220
+ # GREEN: Add guard clause
221
+
222
+ # Cycle 4: Transaction safety
223
+ test "rolls back on error" do
224
+ assert_no_difference "Order.count" do
225
+ @service.call(user: @user, items: [{ product_id: 0, quantity: 1 }])
226
+ end
227
+ end
228
+ # GREEN: Wrap in transaction
229
+ ```
230
+
231
+ ## TDD Order for a New Feature
232
+
233
+ Build from fastest to slowest, most isolated to most integrated:
234
+
235
+ 1. **Model tests** — Validations, scopes, methods
236
+ 2. **Policy tests** — Authorization rules
237
+ 3. **Service tests** — Business logic (if needed)
238
+ 4. **Controller tests** — HTTP integration
239
+ 5. **Component tests** — UI components (if applicable)
240
+ 6. **System tests** — 1-2 critical end-to-end flows
241
+
242
+ ## Anti-Patterns to Avoid
243
+
244
+ ### Testing Implementation, Not Behavior
245
+
246
+ ```ruby
247
+ # BAD: Tests how it works
248
+ test "calls update! on the record" do
249
+ assert_called(order, :update!) { order.fulfill! }
250
+ end
251
+
252
+ # GOOD: Tests what it does
253
+ test "sets fulfilled_at timestamp" do
254
+ order.fulfill!
255
+ assert_not_nil order.reload.fulfilled_at
256
+ end
257
+ ```
258
+
259
+ ### Mystery Guest
260
+
261
+ ```ruby
262
+ # BAD: users(:one) — what state?
263
+ sign_in_as users(:one)
264
+
265
+ # GOOD: Descriptive fixture name
266
+ sign_in_as users(:regular)
267
+ ```
268
+
269
+ ### Over-Specifying
270
+
271
+ ```ruby
272
+ # BAD: Brittle — breaks if wording changes
273
+ assert_equal "Your cart is empty. Please add items.", result.error
274
+
275
+ # GOOD: Tests concept
276
+ assert result.failure?
277
+ assert_equal :empty_cart, result.code
278
+ ```
279
+
280
+ ### Skipping the RED Step
281
+
282
+ Always see the test fail first. If it passes immediately, either the behavior already exists or the test isn't testing what you think.
283
+
284
+ ### Too Many System Tests
285
+
286
+ ```ruby
287
+ # BAD: System test for something a model test covers
288
+ test "title is required" do
289
+ visit new_post_url
290
+ click_button "Create Post"
291
+ assert_text "can't be blank"
292
+ end
293
+
294
+ # GOOD: Model test for validations, system test only for critical flow
295
+ ```
296
+
297
+ ## Running Tests During TDD
298
+
299
+ ```bash
300
+ bin/rails test test/models/event_test.rb # Single file
301
+ bin/rails test test/models/event_test.rb:15 # Single test by line
302
+ bin/rails test test/models/ # All model tests
303
+ bin/rails test # Full suite before commit
304
+ bin/rails test --verbose # See test names
305
+ ```
306
+
307
+ ## TDD Session Checklist
308
+
309
+ - [ ] Understand the full requirement
310
+ - [ ] Identify all behaviors to test
311
+ - [ ] Create/update fixtures for test scenarios
312
+ - [ ] Plan test order (model -> policy -> service -> controller -> system)
313
+ - [ ] Start the RED-GREEN-REFACTOR cycle
314
+ - [ ] Run full test suite before declaring done