source_monitor 0.3.3 → 0.5.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +101 -58
  3. data/.claude/skills/sm-configure/SKILL.md +13 -2
  4. data/.claude/skills/sm-configure/reference/configuration-reference.md +33 -0
  5. data/.claude/skills/sm-host-setup/SKILL.md +18 -2
  6. data/.claude/skills/sm-host-setup/reference/setup-checklist.md +33 -0
  7. data/.claude/skills/sm-job/SKILL.md +1 -1
  8. data/.claude/skills/sm-upgrade/SKILL.md +102 -0
  9. data/.claude/skills/sm-upgrade/reference/upgrade-workflow.md +92 -0
  10. data/.claude/skills/sm-upgrade/reference/version-history.md +68 -0
  11. data/.vbw-planning/SHIPPED.md +35 -0
  12. data/.vbw-planning/config.json +24 -1
  13. data/.vbw-planning/discovery.json +3 -1
  14. data/.vbw-planning/{REQUIREMENTS.md → milestones/generator-enhancements/REQUIREMENTS.md} +22 -0
  15. data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +125 -0
  16. data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +40 -0
  17. data/.vbw-planning/milestones/generator-enhancements/STATE.md +43 -0
  18. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +33 -0
  19. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +86 -0
  20. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +61 -0
  21. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +380 -0
  22. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +78 -0
  23. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +46 -0
  24. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +500 -0
  25. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +89 -0
  26. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +48 -0
  27. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +456 -0
  28. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +129 -0
  29. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +70 -0
  30. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +747 -0
  31. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +156 -0
  32. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +69 -0
  33. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +455 -0
  34. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +39 -0
  35. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +488 -0
  36. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +100 -0
  37. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +37 -0
  38. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +345 -0
  39. data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +80 -0
  40. data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +75 -0
  41. data/.vbw-planning/milestones/upgrade-assurance/STATE.md +29 -0
  42. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +144 -0
  43. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +43 -0
  44. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +405 -0
  45. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +27 -0
  46. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +303 -0
  47. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +380 -0
  48. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +36 -0
  49. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +652 -0
  50. data/CHANGELOG.md +48 -0
  51. data/CLAUDE.md +5 -3
  52. data/Gemfile.lock +1 -1
  53. data/VERSION +1 -1
  54. data/app/assets/builds/source_monitor/application.css +9 -0
  55. data/app/helpers/source_monitor/application_helper.rb +38 -0
  56. data/app/jobs/source_monitor/download_content_images_job.rb +72 -0
  57. data/app/models/source_monitor/item_content.rb +2 -0
  58. data/app/views/source_monitor/dashboard/_recent_activity.html.erb +9 -0
  59. data/app/views/source_monitor/items/_details.html.erb +2 -2
  60. data/app/views/source_monitor/logs/index.html.erb +9 -0
  61. data/app/views/source_monitor/sources/_details.html.erb +2 -2
  62. data/app/views/source_monitor/sources/_row.html.erb +1 -1
  63. data/docs/setup.md +10 -1
  64. data/docs/troubleshooting.md +38 -7
  65. data/docs/upgrade.md +140 -0
  66. data/lib/generators/source_monitor/install/install_generator.rb +101 -0
  67. data/lib/source_monitor/configuration/deprecation_registry.rb +237 -0
  68. data/lib/source_monitor/configuration/http_settings.rb +7 -1
  69. data/lib/source_monitor/configuration/images_settings.rb +37 -0
  70. data/lib/source_monitor/configuration.rb +11 -1
  71. data/lib/source_monitor/dashboard/queries/recent_activity_query.rb +16 -7
  72. data/lib/source_monitor/dashboard/recent_activity.rb +1 -0
  73. data/lib/source_monitor/dashboard/recent_activity_presenter.rb +15 -2
  74. data/lib/source_monitor/fetching/feed_fetcher/entry_processor.rb +13 -0
  75. data/lib/source_monitor/http.rb +23 -0
  76. data/lib/source_monitor/images/content_rewriter.rb +81 -0
  77. data/lib/source_monitor/images/downloader.rb +82 -0
  78. data/lib/source_monitor/logs/table_presenter.rb +25 -0
  79. data/lib/source_monitor/setup/cli.rb +7 -0
  80. data/lib/source_monitor/setup/procfile_patcher.rb +31 -0
  81. data/lib/source_monitor/setup/queue_config_patcher.rb +84 -0
  82. data/lib/source_monitor/setup/skills_installer.rb +1 -0
  83. data/lib/source_monitor/setup/upgrade_command.rb +59 -0
  84. data/lib/source_monitor/setup/verification/pending_migrations_verifier.rb +92 -0
  85. data/lib/source_monitor/setup/verification/recurring_schedule_verifier.rb +102 -0
  86. data/lib/source_monitor/setup/verification/runner.rb +1 -1
  87. data/lib/source_monitor/setup/verification/solid_queue_verifier.rb +1 -1
  88. data/lib/source_monitor/setup/workflow.rb +10 -0
  89. data/lib/source_monitor/version.rb +1 -1
  90. data/lib/source_monitor.rb +11 -0
  91. metadata +51 -2
@@ -0,0 +1,500 @@
1
+ ---
2
+ phase: 2
3
+ plan: "01"
4
+ title: recurring-schedule-verifier
5
+ type: execute
6
+ wave: 1
7
+ depends_on: []
8
+ cross_phase_deps:
9
+ - phase: 1
10
+ artifact: "lib/source_monitor/setup/verification/solid_queue_verifier.rb"
11
+ reason: "Phase 2 modifies this file's remediation message (REQ-20)"
12
+ autonomous: true
13
+ effort_override: thorough
14
+ skills_used: []
15
+ files_modified:
16
+ - lib/source_monitor/setup/verification/recurring_schedule_verifier.rb
17
+ - lib/source_monitor/setup/verification/solid_queue_verifier.rb
18
+ - lib/source_monitor/setup/verification/runner.rb
19
+ - lib/source_monitor.rb
20
+ - test/lib/source_monitor/setup/verification/recurring_schedule_verifier_test.rb
21
+ - test/lib/source_monitor/setup/verification/solid_queue_verifier_test.rb
22
+ - test/lib/source_monitor/setup/verification/runner_test.rb
23
+ must_haves:
24
+ truths:
25
+ - "Running `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/recurring_schedule_verifier_test.rb` exits 0 with 0 failures"
26
+ - "Running `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/solid_queue_verifier_test.rb` exits 0 with 0 failures"
27
+ - "Running `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/runner_test.rb` exits 0 with 0 failures"
28
+ - "Running `bin/rubocop lib/source_monitor/setup/verification/recurring_schedule_verifier.rb lib/source_monitor/setup/verification/solid_queue_verifier.rb lib/source_monitor/setup/verification/runner.rb` exits 0 with no offenses"
29
+ - "Running `bin/rails test` exits 0 with 867+ runs and 0 failures"
30
+ artifacts:
31
+ - path: "lib/source_monitor/setup/verification/recurring_schedule_verifier.rb"
32
+ provides: "Verifier that checks SolidQueue recurring tasks registration (REQ-19)"
33
+ contains: "class RecurringScheduleVerifier"
34
+ - path: "lib/source_monitor/setup/verification/solid_queue_verifier.rb"
35
+ provides: "Enhanced remediation mentioning Procfile.dev (REQ-20)"
36
+ contains: "Procfile.dev"
37
+ - path: "lib/source_monitor/setup/verification/runner.rb"
38
+ provides: "Runner wires RecurringScheduleVerifier into default_verifiers"
39
+ contains: "RecurringScheduleVerifier"
40
+ - path: "lib/source_monitor.rb"
41
+ provides: "Autoload declaration for RecurringScheduleVerifier"
42
+ contains: "autoload :RecurringScheduleVerifier"
43
+ - path: "test/lib/source_monitor/setup/verification/recurring_schedule_verifier_test.rb"
44
+ provides: "Tests covering all RecurringScheduleVerifier branches"
45
+ contains: "class RecurringScheduleVerifierTest"
46
+ key_links:
47
+ - from: "recurring_schedule_verifier.rb"
48
+ to: "REQ-19"
49
+ via: "Checks whether recurring tasks are registered with SolidQueue dispatchers"
50
+ - from: "solid_queue_verifier.rb#warning_result remediation"
51
+ to: "REQ-20"
52
+ via: "Remediation now suggests Procfile.dev for bin/dev users"
53
+ - from: "runner.rb#default_verifiers"
54
+ to: "recurring_schedule_verifier.rb"
55
+ via: "Runner includes RecurringScheduleVerifier in the default verifier set"
56
+ ---
57
+ <objective>
58
+ Add a RecurringScheduleVerifier to the verification suite that checks whether recurring tasks are registered with Solid Queue (REQ-19), and enhance the SolidQueueVerifier remediation message to suggest Procfile.dev when workers are not detected (REQ-20). Wire the new verifier into the Runner and autoload system.
59
+ </objective>
60
+ <context>
61
+ @lib/source_monitor/setup/verification/solid_queue_verifier.rb -- The existing verifier to enhance. Constructor accepts `process_relation:`, `connection:`, `clock:` with defaults. The `call` method returns a Result via helper methods `ok_result`, `warning_result`, `error_result`. Key change: line 24's warning_result remediation string must be updated to mention Procfile.dev. Follow the exact same pattern for the new verifier. Key: `:solid_queue`, name: `"Solid Queue"`.
62
+
63
+ @lib/source_monitor/setup/verification/action_cable_verifier.rb -- Pattern reference for verifier design. Shows constructor dependency injection, `call` method with case/when branching, rescue StandardError, and Result helpers. The new RecurringScheduleVerifier should follow the same structure.
64
+
65
+ @lib/source_monitor/setup/verification/result.rb -- Result struct with `key`, `name`, `status`, `details`, `remediation` and status predicates (`ok?`, `warning?`, `error?`). Summary aggregates results. The new verifier should use key: `:recurring_schedule`, name: `"Recurring Schedule"`.
66
+
67
+ @lib/source_monitor/setup/verification/runner.rb -- Orchestrator with `default_verifiers` returning an array. Currently `[SolidQueueVerifier.new, ActionCableVerifier.new]`. Add `RecurringScheduleVerifier.new` to this array.
68
+
69
+ @test/lib/source_monitor/setup/verification/solid_queue_verifier_test.rb -- Test pattern: uses FakeRelation and FakeConnection structs, tests all branches (ok, warning, error for missing gem, missing tables, unexpected failure). The "warns when no recent workers" test should be updated to assert the new remediation mentions Procfile.dev.
70
+
71
+ @test/lib/source_monitor/setup/verification/runner_test.rb -- Tests Runner with stub verifiers. The "uses default verifiers" test stubs SolidQueueVerifier and ActionCableVerifier via `.stub(:new, ...)`. Must add a third stub for RecurringScheduleVerifier and update assertions to expect 3 results.
72
+
73
+ @lib/source_monitor.rb lines 169-177 -- Autoload declarations for Setup::Verification module. Add `autoload :RecurringScheduleVerifier` here.
74
+
75
+ @lib/source_monitor/engine.rb lines 54-60 -- Shows how `SolidQueue::RecurringTask` is used elsewhere in the codebase. The model has columns: key, class_name, command, schedule, queue_name, static. Tasks with `class_name` starting with "SourceMonitor::" or `command` containing "SourceMonitor::" are SourceMonitor-owned entries.
76
+
77
+ @test/dummy/config/recurring.yml -- Shows the 5 recurring tasks configured for the dummy app: source_monitor_schedule_fetches, source_monitor_schedule_scrapes, source_monitor_item_cleanup, source_monitor_log_cleanup, clear_solid_queue_finished_jobs. The first 4 are SourceMonitor-owned (keys start with `source_monitor_` or class_name/command references SourceMonitor::).
78
+
79
+ **Rationale:** The RecurringScheduleVerifier checks that recurring tasks (defined in recurring.yml) are actually loaded into the solid_queue_recurring_tasks table. This catches the common failure where a user has the YAML file but the dispatcher is not configured with `recurring_schedule: config/recurring.yml`, so tasks never get registered. The verifier queries `SolidQueue::RecurringTask` and looks for entries whose key starts with `source_monitor_` OR whose class_name/command references `SourceMonitor::`.
80
+
81
+ **Key design decisions:**
82
+ 1. Check for `SolidQueue::RecurringTask` availability (same pattern as SolidQueueVerifier checking Process)
83
+ 2. Check table existence before querying (same pattern)
84
+ 3. Query for any recurring tasks, then filter for SourceMonitor-specific ones
85
+ 4. Four outcomes: (a) ok if SM tasks found, (b) warning if tasks exist but no SM ones, (c) warning if no tasks at all, (d) error if SolidQueue unavailable
86
+ 5. SourceMonitor task detection: key starts with `source_monitor_` OR class_name starts with `SourceMonitor::` OR command contains `SourceMonitor::`
87
+ 6. Inject `task_relation:` and `connection:` via constructor for testability
88
+ </context>
89
+ <tasks>
90
+ <task type="auto">
91
+ <name>create-recurring-schedule-verifier</name>
92
+ <files>
93
+ lib/source_monitor/setup/verification/recurring_schedule_verifier.rb
94
+ </files>
95
+ <action>
96
+ Create a new file `lib/source_monitor/setup/verification/recurring_schedule_verifier.rb` following the exact pattern of SolidQueueVerifier and ActionCableVerifier.
97
+
98
+ ```ruby
99
+ # frozen_string_literal: true
100
+
101
+ module SourceMonitor
102
+ module Setup
103
+ module Verification
104
+ class RecurringScheduleVerifier
105
+ SOURCE_MONITOR_KEY_PREFIX = "source_monitor_"
106
+ SOURCE_MONITOR_NAMESPACE = "SourceMonitor::"
107
+
108
+ def initialize(task_relation: default_task_relation, connection: default_connection)
109
+ @task_relation = task_relation
110
+ @connection = connection
111
+ end
112
+
113
+ def call
114
+ return missing_gem_result unless task_relation
115
+ return missing_tables_result unless tables_present?
116
+
117
+ tasks = all_tasks
118
+ sm_tasks = source_monitor_tasks(tasks)
119
+
120
+ if sm_tasks.any?
121
+ ok_result("#{sm_tasks.size} SourceMonitor recurring task(s) registered")
122
+ elsif tasks.any?
123
+ warning_result(
124
+ "Recurring tasks exist but none belong to SourceMonitor",
125
+ "Add SourceMonitor entries to config/recurring.yml and ensure the dispatcher has `recurring_schedule: config/recurring.yml`"
126
+ )
127
+ else
128
+ warning_result(
129
+ "No recurring tasks are registered with Solid Queue",
130
+ "Configure a dispatcher with `recurring_schedule: config/recurring.yml` in config/queue.yml and ensure recurring.yml contains SourceMonitor task entries"
131
+ )
132
+ end
133
+ rescue StandardError => e
134
+ error_result(
135
+ "Recurring schedule verification failed: #{e.message}",
136
+ "Verify Solid Queue migrations are up to date and the dispatcher is configured with recurring_schedule"
137
+ )
138
+ end
139
+
140
+ private
141
+
142
+ attr_reader :task_relation, :connection
143
+
144
+ def default_task_relation
145
+ SolidQueue::RecurringTask if defined?(SolidQueue::RecurringTask)
146
+ end
147
+
148
+ def default_connection
149
+ SolidQueue::RecurringTask.connection if defined?(SolidQueue::RecurringTask)
150
+ rescue StandardError
151
+ nil
152
+ end
153
+
154
+ def tables_present?
155
+ return false unless connection
156
+
157
+ connection.table_exists?(task_relation.table_name)
158
+ end
159
+
160
+ def all_tasks
161
+ task_relation.all.to_a
162
+ end
163
+
164
+ def source_monitor_tasks(tasks)
165
+ tasks.select do |task|
166
+ task.key.start_with?(SOURCE_MONITOR_KEY_PREFIX) ||
167
+ task.class_name.to_s.start_with?(SOURCE_MONITOR_NAMESPACE) ||
168
+ task.command.to_s.include?(SOURCE_MONITOR_NAMESPACE)
169
+ end
170
+ end
171
+
172
+ def missing_gem_result
173
+ error_result(
174
+ "Solid Queue gem is not available",
175
+ "Add `solid_queue` to your Gemfile and bundle install"
176
+ )
177
+ end
178
+
179
+ def missing_tables_result
180
+ error_result(
181
+ "Solid Queue recurring tasks table is missing",
182
+ "Run `rails solid_queue:install` or copy the engine's Solid Queue migration"
183
+ )
184
+ end
185
+
186
+ def ok_result(details)
187
+ Result.new(key: :recurring_schedule, name: "Recurring Schedule", status: :ok, details: details)
188
+ end
189
+
190
+ def warning_result(details, remediation)
191
+ Result.new(key: :recurring_schedule, name: "Recurring Schedule", status: :warning, details: details, remediation: remediation)
192
+ end
193
+
194
+ def error_result(details, remediation)
195
+ Result.new(key: :recurring_schedule, name: "Recurring Schedule", status: :error, details: details, remediation: remediation)
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ ```
202
+
203
+ Key design points:
204
+ - Constructor accepts `task_relation:` and `connection:` for dependency injection (testability)
205
+ - Defaults to `SolidQueue::RecurringTask` if available
206
+ - `all_tasks` fetches all recurring tasks, then `source_monitor_tasks` filters by key prefix, class_name namespace, or command namespace
207
+ - Three-tier result: ok (SM tasks found), warning (tasks exist but no SM ones OR no tasks at all), error (gem/table missing or unexpected failure)
208
+ - Same rescue StandardError pattern as other verifiers
209
+ </action>
210
+ <verify>
211
+ Read the created file and confirm: (a) class is in the correct module nesting, (b) constructor follows dependency injection pattern, (c) `call` method handles all 5 outcomes (missing gem, missing tables, SM tasks found, non-SM tasks only, no tasks), (d) helper methods are private, (e) Result key is `:recurring_schedule`.
212
+ </verify>
213
+ <done>
214
+ RecurringScheduleVerifier created with full branch coverage: missing gem, missing tables, SM tasks found (ok), non-SM tasks only (warning), no tasks (warning), unexpected error.
215
+ </done>
216
+ </task>
217
+ <task type="auto">
218
+ <name>enhance-solid-queue-verifier-remediation</name>
219
+ <files>
220
+ lib/source_monitor/setup/verification/solid_queue_verifier.rb
221
+ test/lib/source_monitor/setup/verification/solid_queue_verifier_test.rb
222
+ </files>
223
+ <action>
224
+ **Modify `lib/source_monitor/setup/verification/solid_queue_verifier.rb`:**
225
+
226
+ Change the remediation string on line 24 from:
227
+ ```ruby
228
+ "Start a Solid Queue worker with `bin/rails solid_queue:start` and ensure it stays running"
229
+ ```
230
+ to:
231
+ ```ruby
232
+ "Start a Solid Queue worker with `bin/rails solid_queue:start` or add `jobs: bundle exec rake solid_queue:start` to Procfile.dev and run `bin/dev`"
233
+ ```
234
+
235
+ This is a single-line change in the `call` method's warning_result call (the "no recent workers" branch).
236
+
237
+ **Modify `test/lib/source_monitor/setup/verification/solid_queue_verifier_test.rb`:**
238
+
239
+ Update the "warns when no recent workers" test to also assert the remediation message mentions Procfile.dev:
240
+ ```ruby
241
+ assert_match(/Procfile\.dev/, result.remediation)
242
+ ```
243
+
244
+ Add this assertion after the existing `assert_match(/No Solid Queue workers/, result.details)` line.
245
+ </action>
246
+ <verify>
247
+ Run `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/solid_queue_verifier_test.rb` -- all 5 tests pass. Run `bin/rubocop lib/source_monitor/setup/verification/solid_queue_verifier.rb` -- 0 offenses. Grep for "Procfile.dev" in the verifier file confirms the new remediation text.
248
+ </verify>
249
+ <done>
250
+ SolidQueueVerifier remediation now mentions Procfile.dev with the `bin/dev` workflow. Existing test updated to assert the new message content. REQ-20 satisfied.
251
+ </done>
252
+ </task>
253
+ <task type="auto">
254
+ <name>add-recurring-schedule-verifier-tests</name>
255
+ <files>
256
+ test/lib/source_monitor/setup/verification/recurring_schedule_verifier_test.rb
257
+ </files>
258
+ <action>
259
+ Create a new test file following the exact pattern from `solid_queue_verifier_test.rb`:
260
+
261
+ ```ruby
262
+ # frozen_string_literal: true
263
+
264
+ require "test_helper"
265
+
266
+ module SourceMonitor
267
+ module Setup
268
+ module Verification
269
+ class RecurringScheduleVerifierTest < ActiveSupport::TestCase
270
+ # Fake task struct matching SolidQueue::RecurringTask's interface
271
+ FakeTask = Struct.new(:key, :class_name, :command, keyword_init: true)
272
+
273
+ # Fake relation that returns tasks and supports table_name
274
+ class FakeTaskRelation
275
+ attr_reader :table_name
276
+
277
+ def initialize(tasks, table_name: "solid_queue_recurring_tasks")
278
+ @tasks = tasks
279
+ @table_name = table_name
280
+ end
281
+
282
+ def all
283
+ self
284
+ end
285
+
286
+ def to_a
287
+ @tasks
288
+ end
289
+ end
290
+
291
+ class FakeConnection
292
+ def initialize(tables: [])
293
+ @tables = tables
294
+ end
295
+
296
+ def table_exists?(name)
297
+ @tables.include?(name)
298
+ end
299
+ end
300
+
301
+ test "returns ok when source monitor recurring tasks are registered" do
302
+ tasks = [
303
+ FakeTask.new(key: "source_monitor_schedule_fetches", class_name: "SourceMonitor::ScheduleFetchesJob", command: nil),
304
+ FakeTask.new(key: "source_monitor_item_cleanup", class_name: "SourceMonitor::ItemCleanupJob", command: nil)
305
+ ]
306
+ relation = FakeTaskRelation.new(tasks)
307
+ connection = FakeConnection.new(tables: ["solid_queue_recurring_tasks"])
308
+
309
+ result = RecurringScheduleVerifier.new(task_relation: relation, connection: connection).call
310
+
311
+ assert_equal :ok, result.status
312
+ assert_match(/2 SourceMonitor recurring task/, result.details)
313
+ end
314
+
315
+ test "returns ok when source monitor tasks detected by command" do
316
+ tasks = [
317
+ FakeTask.new(key: "source_monitor_schedule_scrapes", class_name: nil, command: "SourceMonitor::Scraping::Scheduler.run(limit: 100)")
318
+ ]
319
+ relation = FakeTaskRelation.new(tasks)
320
+ connection = FakeConnection.new(tables: ["solid_queue_recurring_tasks"])
321
+
322
+ result = RecurringScheduleVerifier.new(task_relation: relation, connection: connection).call
323
+
324
+ assert_equal :ok, result.status
325
+ end
326
+
327
+ test "warns when tasks exist but none belong to source monitor" do
328
+ tasks = [
329
+ FakeTask.new(key: "other_app_cleanup", class_name: "OtherApp::CleanupJob", command: nil)
330
+ ]
331
+ relation = FakeTaskRelation.new(tasks)
332
+ connection = FakeConnection.new(tables: ["solid_queue_recurring_tasks"])
333
+
334
+ result = RecurringScheduleVerifier.new(task_relation: relation, connection: connection).call
335
+
336
+ assert_equal :warning, result.status
337
+ assert_match(/none belong to SourceMonitor/, result.details)
338
+ assert_match(/recurring\.yml/, result.remediation)
339
+ end
340
+
341
+ test "warns when no recurring tasks are registered" do
342
+ relation = FakeTaskRelation.new([])
343
+ connection = FakeConnection.new(tables: ["solid_queue_recurring_tasks"])
344
+
345
+ result = RecurringScheduleVerifier.new(task_relation: relation, connection: connection).call
346
+
347
+ assert_equal :warning, result.status
348
+ assert_match(/No recurring tasks are registered/, result.details)
349
+ assert_match(/recurring_schedule/, result.remediation)
350
+ end
351
+
352
+ test "errors when solid queue gem is missing" do
353
+ result = RecurringScheduleVerifier.new(task_relation: nil, connection: nil).call
354
+
355
+ assert_equal :error, result.status
356
+ assert_match(/gem is not available/, result.details)
357
+ end
358
+
359
+ test "errors when recurring tasks table is missing" do
360
+ relation = FakeTaskRelation.new([], table_name: "solid_queue_recurring_tasks")
361
+ connection = FakeConnection.new(tables: [])
362
+
363
+ result = RecurringScheduleVerifier.new(task_relation: relation, connection: connection).call
364
+
365
+ assert_equal :error, result.status
366
+ assert_match(/table is missing/, result.details)
367
+ end
368
+
369
+ test "rescues unexpected failures and reports remediation" do
370
+ relation = Class.new do
371
+ def table_name = "solid_queue_recurring_tasks"
372
+ def all = raise "boom"
373
+ end.new
374
+ connection = FakeConnection.new(tables: ["solid_queue_recurring_tasks"])
375
+
376
+ result = RecurringScheduleVerifier.new(task_relation: relation, connection: connection).call
377
+
378
+ assert_equal :error, result.status
379
+ assert_match(/verification failed/i, result.details)
380
+ assert_match(/dispatcher/, result.remediation)
381
+ end
382
+ end
383
+ end
384
+ end
385
+ end
386
+ ```
387
+
388
+ 7 tests covering all branches: ok (by key prefix), ok (by command), warning (non-SM tasks), warning (no tasks), error (missing gem), error (missing table), error (unexpected exception).
389
+ </action>
390
+ <verify>
391
+ Run `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/recurring_schedule_verifier_test.rb` -- all 7 tests pass. Run `bin/rubocop test/lib/source_monitor/setup/verification/recurring_schedule_verifier_test.rb` -- 0 offenses.
392
+ </verify>
393
+ <done>
394
+ 7 tests covering all RecurringScheduleVerifier branches pass. RuboCop clean.
395
+ </done>
396
+ </task>
397
+ <task type="auto">
398
+ <name>wire-into-runner-and-autoload</name>
399
+ <files>
400
+ lib/source_monitor/setup/verification/runner.rb
401
+ lib/source_monitor.rb
402
+ test/lib/source_monitor/setup/verification/runner_test.rb
403
+ </files>
404
+ <action>
405
+ **Modify `lib/source_monitor/setup/verification/runner.rb`:**
406
+
407
+ Add `RecurringScheduleVerifier.new` to the `default_verifiers` array (line 21). The array should become:
408
+ ```ruby
409
+ def default_verifiers
410
+ [ SolidQueueVerifier.new, RecurringScheduleVerifier.new, ActionCableVerifier.new ]
411
+ end
412
+ ```
413
+
414
+ Place RecurringScheduleVerifier between SolidQueue and ActionCable -- it logically groups with SolidQueue (both check SQ state) and should run after the worker heartbeat check but before the ActionCable check.
415
+
416
+ **Modify `lib/source_monitor.rb`:**
417
+
418
+ Add the autoload declaration in the `module Verification` block (around line 174), after the ActionCableVerifier line:
419
+ ```ruby
420
+ autoload :RecurringScheduleVerifier, "source_monitor/setup/verification/recurring_schedule_verifier"
421
+ ```
422
+
423
+ **Modify `test/lib/source_monitor/setup/verification/runner_test.rb`:**
424
+
425
+ Update the "uses default verifiers" test:
426
+ 1. Add a recurring_result: `recurring_result = Result.new(key: :recurring_schedule, name: "Recurring Schedule", status: :ok, details: "ok")`
427
+ 2. Add a recurring_double: `recurring_double = verifier_double.new(recurring_result)`
428
+ 3. Add a third stub inside the existing stub blocks: `RecurringScheduleVerifier.stub(:new, ->(*) { recurring_double }) do`
429
+ 4. Update assertion: `assert_equal 3, summary.results.size`
430
+ 5. Add: `assert_equal 1, recurring_double.calls`
431
+ </action>
432
+ <verify>
433
+ Run `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/runner_test.rb` -- all tests pass. Grep for `RecurringScheduleVerifier` in runner.rb and source_monitor.rb confirms wiring.
434
+ </verify>
435
+ <done>
436
+ RecurringScheduleVerifier wired into Runner.default_verifiers and autoloaded from lib/source_monitor.rb. Runner test updated to expect 3 verifiers.
437
+ </done>
438
+ </task>
439
+ <task type="auto">
440
+ <name>full-suite-verification</name>
441
+ <files>
442
+ lib/source_monitor/setup/verification/recurring_schedule_verifier.rb
443
+ lib/source_monitor/setup/verification/solid_queue_verifier.rb
444
+ lib/source_monitor/setup/verification/runner.rb
445
+ test/lib/source_monitor/setup/verification/recurring_schedule_verifier_test.rb
446
+ test/lib/source_monitor/setup/verification/solid_queue_verifier_test.rb
447
+ test/lib/source_monitor/setup/verification/runner_test.rb
448
+ </files>
449
+ <action>
450
+ Run the full test suite and linting to confirm no regressions:
451
+
452
+ 1. `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/` -- all verification tests pass (existing + new)
453
+ 2. `bin/rails test` -- full suite passes with 867+ runs and 0 failures
454
+ 3. `bin/rubocop lib/source_monitor/setup/verification/ test/lib/source_monitor/setup/verification/` -- zero offenses
455
+ 4. `bin/brakeman --no-pager` -- zero warnings
456
+ 5. Review the final state of all modified files to confirm:
457
+ - RecurringScheduleVerifier follows the exact same pattern as SolidQueueVerifier
458
+ - SolidQueueVerifier remediation mentions Procfile.dev
459
+ - Runner.default_verifiers includes all 3 verifiers
460
+ - Autoload declaration is in the correct module block
461
+ - All tests cover the expected branches
462
+
463
+ If any test failures or RuboCop offenses are found, fix them before completing.
464
+ </action>
465
+ <verify>
466
+ `bin/rails test` exits 0 with 867+ runs, 0 failures. `bin/rubocop` exits 0 with 0 offenses. `bin/brakeman --no-pager` exits 0 with 0 warnings.
467
+ </verify>
468
+ <done>
469
+ Full test suite passes. RuboCop clean. Brakeman clean. All REQ-19, REQ-20 acceptance criteria met.
470
+ </done>
471
+ </task>
472
+ </tasks>
473
+ <verification>
474
+ 1. `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/recurring_schedule_verifier_test.rb` -- 7 tests pass
475
+ 2. `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/solid_queue_verifier_test.rb` -- 5 tests pass with updated assertion
476
+ 3. `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/setup/verification/runner_test.rb` -- 2 tests pass with 3-verifier expectation
477
+ 4. `bin/rails test` -- 867+ runs, 0 failures
478
+ 5. `bin/rubocop` -- 0 offenses
479
+ 6. `bin/brakeman --no-pager` -- 0 warnings
480
+ 7. `grep -n 'class RecurringScheduleVerifier' lib/source_monitor/setup/verification/recurring_schedule_verifier.rb` returns a match
481
+ 8. `grep -n 'Procfile.dev' lib/source_monitor/setup/verification/solid_queue_verifier.rb` returns a match
482
+ 9. `grep -n 'RecurringScheduleVerifier' lib/source_monitor/setup/verification/runner.rb` returns a match
483
+ 10. `grep -n 'RecurringScheduleVerifier' lib/source_monitor.rb` returns a match
484
+ </verification>
485
+ <success_criteria>
486
+ - RecurringScheduleVerifier returns ok when SourceMonitor recurring tasks are registered (REQ-19)
487
+ - RecurringScheduleVerifier warns when recurring tasks exist but none belong to SourceMonitor (REQ-19)
488
+ - RecurringScheduleVerifier warns when no recurring tasks are registered at all (REQ-19)
489
+ - RecurringScheduleVerifier errors when SolidQueue gem is missing (REQ-19)
490
+ - RecurringScheduleVerifier errors when recurring tasks table is missing (REQ-19)
491
+ - SolidQueueVerifier remediation mentions Procfile.dev and bin/dev (REQ-20)
492
+ - RecurringScheduleVerifier is included in Runner.default_verifiers
493
+ - RecurringScheduleVerifier is autoloaded from lib/source_monitor.rb
494
+ - All existing tests continue to pass (no regressions)
495
+ - 7+ new RecurringScheduleVerifier tests cover all branches
496
+ - RuboCop clean, Brakeman clean
497
+ </success_criteria>
498
+ <output>
499
+ .vbw-planning/phases/02-verification/PLAN-01-SUMMARY.md
500
+ </output>
@@ -0,0 +1,89 @@
1
+ ---
2
+ phase: 3
3
+ plan: "01"
4
+ tier: standard
5
+ result: PASS
6
+ passed: 22
7
+ failed: 0
8
+ total: 22
9
+ date: 2026-02-12
10
+ ---
11
+
12
+ ## Must-Have Checks
13
+
14
+ | # | Truth/Condition | Status | Evidence |
15
+ |---|----------------|--------|----------|
16
+ | 1 | grep 'Procfile.dev' .claude/skills/sm-host-setup/SKILL.md returns matches describing automatic patching, not manual steps | PASS | 4 matches found: lines 76 (automation note), 107 (patches action), 193 (patcher file), 236 (auto-handled checklist) |
17
+ | 2 | grep 'recurring_schedule' .claude/skills/sm-host-setup/SKILL.md returns matches describing automatic wiring, not manual configuration | PASS | 4 matches found: lines 77, 110-111 (dispatcher patching action), 237 (auto-handled checklist) |
18
+ | 3 | grep 'automatically' .claude/skills/sm-host-setup/reference/setup-checklist.md returns matches in Phase 6 worker config section | PASS | 3 matches found: lines 79, 105 (Phase 6a), 118 (Phase 6b) |
19
+ | 4 | grep 'recurring_schedule' .claude/skills/sm-configure/SKILL.md returns matches referencing automatic dispatcher wiring | PASS | 1 match found: line 150 checklist item marked [x] with "(handled by install generator)" |
20
+ | 5 | grep 'automatically' docs/setup.md returns matches describing generator automation of Procfile.dev and queue.yml | PASS | 4 matches found: lines 52, 109, 110, 111 describing automatic worker configuration |
21
+ | 6 | grep 'RecurringScheduleVerifier' docs/troubleshooting.md returns matches in recurring jobs and diagnostics sections | PASS | 1 match found: line 30 Issue #4 diagnostics section |
22
+ | 7 | The sm-host-setup SKILL.md "What the Install Generator Does" section lists 5 actions (not 3) | PASS | Line 93: "performs five actions" with all 5 listed (mount, initializer, recurring jobs, Procfile.dev, queue.yml dispatcher) |
23
+ | 8 | The sm-host-setup SKILL.md checklist shows Procfile.dev and dispatcher items as auto-handled (not manual checkboxes) | PASS | Lines 236-237: both marked [x] with "(handled by generator)" |
24
+ | 9 | The setup-checklist.md Phase 6a and 6b sections note that the generator handles these automatically | PASS | Phase 6a title (line 103): "(Automatic)", Phase 6b title (line 116): "(Automatic)" with generator behavior descriptions |
25
+ | 10 | The docs/setup.md manual installation steps 6a and 6b note that the generator handles these automatically | PASS | Lines 110-111: Procfile.dev and recurring schedule bullets describe generator automation with verification guidance |
26
+ | 11 | The docs/troubleshooting.md Issue #4 and #5 mention running the generator as the primary fix | PASS | Issue #4 line 26: "Primary fix" with generator command; Issue #5 line 43: "Primary fix" with generator command |
27
+
28
+ ## Artifact Checks
29
+
30
+ | Artifact | Exists | Contains | Status |
31
+ |----------|--------|----------|--------|
32
+ | .claude/skills/sm-host-setup/SKILL.md | YES | "Patches Procfile.dev" | PASS |
33
+ | .claude/skills/sm-host-setup/reference/setup-checklist.md | YES | "generator handles this automatically" | PASS |
34
+ | .claude/skills/sm-configure/SKILL.md | YES | "recurring_schedule" | PASS |
35
+ | docs/setup.md | YES | "generator automatically" | PASS |
36
+ | docs/troubleshooting.md | YES | "RecurringScheduleVerifier" | PASS |
37
+
38
+ ## Key Link Checks
39
+
40
+ | From | To | Via | Status |
41
+ |------|----|----|--------|
42
+ | .claude/skills/sm-host-setup/SKILL.md | REQ-21 | sm-host-setup skill reflects new generator capabilities (5 actions, auto Procfile.dev and queue.yml) | PASS |
43
+ | .claude/skills/sm-configure/SKILL.md | REQ-21 | sm-configure skill references automatic recurring_schedule wiring | PASS |
44
+ | docs/setup.md | REQ-21 | Setup docs updated to note generator handles both automatically | PASS |
45
+ | docs/troubleshooting.md | REQ-21 | Troubleshooting updated with improved diagnostics (RecurringScheduleVerifier) | PASS |
46
+ | .claude/skills/sm-host-setup/reference/setup-checklist.md | REQ-21 | Setup checklist reflects automation (Phase 6a/6b Automatic) | PASS |
47
+
48
+ ## Anti-Pattern Scan
49
+
50
+ | Pattern | Found | Location | Severity |
51
+ |---------|-------|----------|----------|
52
+ | Manual "add a jobs: entry to Procfile.dev" instructions | NO | Searched .claude/skills and docs | INFO |
53
+ | Manual "add recurring_schedule to" instructions | NO | All replaced with generator automation notes | INFO |
54
+ | Missing re-run guidance after automation note | NO | All automation sections include re-run instructions | INFO |
55
+
56
+ ## Convention Compliance
57
+
58
+ | Convention | File | Status | Detail |
59
+ |------------|------|--------|--------|
60
+ | Maintenance rule: skills aligned with engine code | All 6 modified files | PASS | Skills updated to match generator capabilities from Phase 1 |
61
+ | RuboCop check | N/A | N/A | Markdown files not subject to RuboCop |
62
+
63
+ ## Summary
64
+
65
+ **Tier:** standard (22 checks executed)
66
+
67
+ **Result:** PASS
68
+
69
+ **Passed:** 22/22
70
+
71
+ **Failed:** None
72
+
73
+ **Details:**
74
+
75
+ All must_haves verified successfully:
76
+ - All 6 files modified as specified in the plan
77
+ - sm-host-setup SKILL.md expanded from 3 to 5 generator actions
78
+ - Procfile.dev and dispatcher checklist items marked as auto-handled
79
+ - Phase 6a/6b in setup-checklist.md now titled "(Automatic)" with generator behavior descriptions
80
+ - RecurringScheduleVerifier referenced in setup-checklist.md and troubleshooting.md Issue #4
81
+ - sm-configure SKILL.md added dispatcher recurring_schedule checklist item
82
+ - sm-job SKILL.md mentions automatic queue.yml patching
83
+ - docs/setup.md guided and manual sections describe generator automation
84
+ - docs/troubleshooting.md Issues #4 and #5 use generator as primary fix with diagnostics sections
85
+ - No manual "add this to your file" instructions found (all replaced with automation notes)
86
+ - Commit 7978d61 exists with correct message and 6 file changes
87
+ - All key links to REQ-21 verified
88
+
89
+ Phase 3 execution completed with 100% alignment between generator capabilities (Phase 1), verification tooling (Phase 2), and all consumer-facing documentation.
@@ -0,0 +1,48 @@
1
+ # PLAN-01 Summary: skills-docs-alignment
2
+
3
+ ## Status: COMPLETE
4
+
5
+ ## What Was Done
6
+
7
+ ### Task 1: Update sm-host-setup SKILL.md
8
+ - Replaced manual Procfile.dev/queue.yml comments (lines 76-80) with automation note and re-run guidance
9
+ - Expanded "What the Install Generator Does" from 3 to 5 actions: added Procfile.dev patching and queue.yml dispatcher patching
10
+ - Marked Procfile.dev and dispatcher checklist items as `[x]` with "(handled by generator)"
11
+ - Added `procfile_patcher.rb` and `queue_config_patcher.rb` to Key Source Files table
12
+
13
+ ### Task 2: Update setup-checklist.md
14
+ - Phase 6a renamed to "Procfile.dev for Development (Automatic)" with generator behavior description
15
+ - Phase 6b renamed to "Recurring Schedule Dispatcher Wiring (Automatic)" with generator behavior description
16
+ - Both sections include verification guidance and "re-run" instructions
17
+ - Phase 6b references RecurringScheduleVerifier diagnostics
18
+ - Checklist items marked `[x]` with "(handled by generator)"
19
+
20
+ ### Task 3: Update sm-configure and sm-job Skills
21
+ - sm-configure SKILL.md: updated queue names checklist entry, added `[x]` dispatcher `recurring_schedule` item
22
+ - sm-job SKILL.md: recurring jobs section now mentions automatic `config/queue.yml` patching and idempotency
23
+
24
+ ### Task 4: Update docs/setup.md
25
+ - Guided Setup: replaced manual Procfile.dev and queue.yml instructions with 3-bullet automatic summary and re-run guidance
26
+ - Manual Installation Quick Reference: steps 6a/6b descriptions changed to "Handled by generator"
27
+ - Step-by-step details: Procfile.dev and recurring schedule bullets now describe generator automation with verification guidance
28
+
29
+ ### Task 5: Update docs/troubleshooting.md
30
+ - Issue #4 (Recurring Jobs Not Running): added "Primary fix" with generator command, "Diagnostics" with RecurringScheduleVerifier, kept manual fix as fallback
31
+ - Issue #5 (Jobs Not Processing with bin/dev): added "Primary fix" with generator command, "Diagnostics" with SolidQueueVerifier Procfile.dev suggestion
32
+
33
+ ## Files Modified
34
+ - `.claude/skills/sm-host-setup/SKILL.md` (5-action generator, auto-handled checklist, patcher files in table)
35
+ - `.claude/skills/sm-host-setup/reference/setup-checklist.md` (automated Phase 6a/6b, RecurringScheduleVerifier)
36
+ - `.claude/skills/sm-configure/SKILL.md` (dispatcher recurring_schedule checklist item)
37
+ - `.claude/skills/sm-job/SKILL.md` (automatic queue.yml patching mention)
38
+ - `docs/setup.md` (generator automation in guided and manual sections)
39
+ - `docs/troubleshooting.md` (generator-first remediation, verifier diagnostics)
40
+
41
+ ## Commit
42
+ - `7978d61` docs(03-docs-alignment): update skills and docs for generator automation
43
+
44
+ ## Requirements Satisfied
45
+ - REQ-21: All sm-* skills and documentation updated to reflect generator automation of Procfile.dev patching, queue.yml dispatcher wiring, and RecurringScheduleVerifier diagnostics
46
+
47
+ ## Deviations
48
+ None. All tasks executed as specified in the plan.