source_monitor 0.3.0 → 0.3.2

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/sm-architecture/SKILL.md +233 -0
  3. data/.claude/skills/sm-architecture/reference/extraction-patterns.md +192 -0
  4. data/.claude/skills/sm-architecture/reference/module-map.md +194 -0
  5. data/.claude/skills/sm-configuration-setting/SKILL.md +264 -0
  6. data/.claude/skills/sm-configuration-setting/reference/settings-catalog.md +248 -0
  7. data/.claude/skills/sm-configuration-setting/reference/settings-pattern.md +297 -0
  8. data/.claude/skills/sm-configure/SKILL.md +153 -0
  9. data/.claude/skills/sm-configure/reference/configuration-reference.md +321 -0
  10. data/.claude/skills/sm-dashboard-widget/SKILL.md +344 -0
  11. data/.claude/skills/sm-dashboard-widget/reference/dashboard-patterns.md +304 -0
  12. data/.claude/skills/sm-domain-model/SKILL.md +188 -0
  13. data/.claude/skills/sm-domain-model/reference/model-graph.md +114 -0
  14. data/.claude/skills/sm-domain-model/reference/table-structure.md +348 -0
  15. data/.claude/skills/sm-engine-migration/SKILL.md +395 -0
  16. data/.claude/skills/sm-engine-migration/reference/migration-conventions.md +255 -0
  17. data/.claude/skills/sm-engine-test/SKILL.md +302 -0
  18. data/.claude/skills/sm-engine-test/reference/test-helpers.md +259 -0
  19. data/.claude/skills/sm-engine-test/reference/test-patterns.md +411 -0
  20. data/.claude/skills/sm-event-handler/SKILL.md +265 -0
  21. data/.claude/skills/sm-event-handler/reference/events-api.md +229 -0
  22. data/.claude/skills/sm-health-rule/SKILL.md +327 -0
  23. data/.claude/skills/sm-health-rule/reference/health-system.md +269 -0
  24. data/.claude/skills/sm-host-setup/SKILL.md +223 -0
  25. data/.claude/skills/sm-host-setup/reference/initializer-template.md +195 -0
  26. data/.claude/skills/sm-host-setup/reference/setup-checklist.md +134 -0
  27. data/.claude/skills/sm-job/SKILL.md +263 -0
  28. data/.claude/skills/sm-job/reference/job-conventions.md +245 -0
  29. data/.claude/skills/sm-model-extension/SKILL.md +287 -0
  30. data/.claude/skills/sm-model-extension/reference/extension-api.md +317 -0
  31. data/.claude/skills/sm-pipeline-stage/SKILL.md +254 -0
  32. data/.claude/skills/sm-pipeline-stage/reference/completion-handlers.md +152 -0
  33. data/.claude/skills/sm-pipeline-stage/reference/entry-processing.md +191 -0
  34. data/.claude/skills/sm-pipeline-stage/reference/feed-fetcher-architecture.md +198 -0
  35. data/.claude/skills/sm-scraper-adapter/SKILL.md +284 -0
  36. data/.claude/skills/sm-scraper-adapter/reference/adapter-contract.md +167 -0
  37. data/.claude/skills/sm-scraper-adapter/reference/example-adapter.md +274 -0
  38. data/.vbw-planning/.notification-log.jsonl +102 -0
  39. data/.vbw-planning/.session-log.jsonl +505 -0
  40. data/AGENTS.md +20 -57
  41. data/CHANGELOG.md +19 -0
  42. data/CLAUDE.md +44 -1
  43. data/CONTRIBUTING.md +5 -5
  44. data/Gemfile.lock +20 -21
  45. data/README.md +18 -5
  46. data/VERSION +1 -0
  47. data/docs/deployment.md +1 -1
  48. data/docs/setup.md +4 -4
  49. data/lib/source_monitor/setup/skills_installer.rb +94 -0
  50. data/lib/source_monitor/setup/workflow.rb +17 -2
  51. data/lib/source_monitor/version.rb +1 -1
  52. data/lib/tasks/source_monitor_setup.rake +58 -0
  53. data/source_monitor.gemspec +1 -0
  54. metadata +39 -1
@@ -0,0 +1,297 @@
1
+ # Step-by-Step: Adding a Configuration Setting
2
+
3
+ ## Scenario A: Add to Existing Section
4
+
5
+ ### Example: Add `stale_threshold_hours` to FetchingSettings
6
+
7
+ **Step 1: Edit the settings file**
8
+
9
+ ```ruby
10
+ # lib/source_monitor/configuration/fetching_settings.rb
11
+ class FetchingSettings
12
+ attr_accessor :min_interval_minutes,
13
+ :max_interval_minutes,
14
+ :increase_factor,
15
+ :decrease_factor,
16
+ :failure_increase_factor,
17
+ :jitter_percent,
18
+ :stale_threshold_hours # ADD: new accessor
19
+
20
+ def reset!
21
+ @min_interval_minutes = 5
22
+ @max_interval_minutes = 24 * 60
23
+ @increase_factor = 1.25
24
+ @decrease_factor = 0.75
25
+ @failure_increase_factor = 1.5
26
+ @jitter_percent = 0.1
27
+ @stale_threshold_hours = 48 # ADD: default value
28
+ end
29
+ end
30
+ ```
31
+
32
+ **Step 2: Write tests**
33
+
34
+ ```ruby
35
+ # test/lib/source_monitor/configuration_test.rb
36
+
37
+ test "stale_threshold_hours has correct default" do
38
+ assert_equal 48, SourceMonitor.config.fetching.stale_threshold_hours
39
+ end
40
+
41
+ test "stale_threshold_hours can be overridden" do
42
+ SourceMonitor.configure do |config|
43
+ config.fetching.stale_threshold_hours = 72
44
+ end
45
+ assert_equal 72, SourceMonitor.config.fetching.stale_threshold_hours
46
+ end
47
+
48
+ test "stale_threshold_hours resets with configuration" do
49
+ SourceMonitor.configure do |config|
50
+ config.fetching.stale_threshold_hours = 72
51
+ end
52
+ SourceMonitor.reset_configuration!
53
+ assert_equal 48, SourceMonitor.config.fetching.stale_threshold_hours
54
+ end
55
+ ```
56
+
57
+ **Step 3: Run tests**
58
+
59
+ ```bash
60
+ PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/configuration_test.rb
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Scenario B: Add Setting with Validation
66
+
67
+ ### Example: Add validated `strategy` to a section
68
+
69
+ **Step 1: Edit the settings file with custom setter**
70
+
71
+ ```ruby
72
+ class MySettings
73
+ VALID_MODES = %i[fast balanced thorough].freeze
74
+
75
+ attr_reader :mode
76
+
77
+ def initialize
78
+ reset!
79
+ end
80
+
81
+ def mode=(value)
82
+ normalized = value&.to_sym
83
+ unless VALID_MODES.include?(normalized)
84
+ raise ArgumentError, "Invalid mode #{value.inspect}. Must be one of: #{VALID_MODES.join(', ')}"
85
+ end
86
+ @mode = normalized
87
+ end
88
+
89
+ def reset!
90
+ @mode = :balanced
91
+ end
92
+ end
93
+ ```
94
+
95
+ **Step 2: Write tests for all paths**
96
+
97
+ ```ruby
98
+ test "mode defaults to balanced" do
99
+ assert_equal :balanced, SourceMonitor.config.my_section.mode
100
+ end
101
+
102
+ test "mode accepts valid values" do
103
+ %i[fast balanced thorough].each do |mode|
104
+ SourceMonitor.configure do |config|
105
+ config.my_section.mode = mode
106
+ end
107
+ assert_equal mode, SourceMonitor.config.my_section.mode
108
+ end
109
+ end
110
+
111
+ test "mode accepts string values" do
112
+ SourceMonitor.configure do |config|
113
+ config.my_section.mode = "fast"
114
+ end
115
+ assert_equal :fast, SourceMonitor.config.my_section.mode
116
+ end
117
+
118
+ test "mode rejects invalid values" do
119
+ assert_raises(ArgumentError, /Invalid mode/) do
120
+ SourceMonitor.configure do |config|
121
+ config.my_section.mode = :invalid
122
+ end
123
+ end
124
+ end
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Scenario C: Add Setting with Normalization
130
+
131
+ ### Example: Numeric setting that normalizes edge cases
132
+
133
+ Follow the `ScrapingSettings` pattern:
134
+
135
+ ```ruby
136
+ class MySettings
137
+ DEFAULT_LIMIT = 50
138
+
139
+ attr_reader :limit
140
+
141
+ def initialize
142
+ reset!
143
+ end
144
+
145
+ def limit=(value)
146
+ @limit = normalize_numeric(value)
147
+ end
148
+
149
+ def reset!
150
+ @limit = DEFAULT_LIMIT
151
+ end
152
+
153
+ private
154
+
155
+ def normalize_numeric(value)
156
+ return nil if value.nil?
157
+ return nil if value == ""
158
+ integer = value.respond_to?(:to_i) ? value.to_i : value
159
+ integer.positive? ? integer : nil
160
+ end
161
+ end
162
+ ```
163
+
164
+ **Test normalization edge cases:**
165
+
166
+ ```ruby
167
+ test "limit normalizes string to integer" do
168
+ SourceMonitor.configure { |c| c.my_section.limit = "10" }
169
+ assert_equal 10, SourceMonitor.config.my_section.limit
170
+ end
171
+
172
+ test "limit normalizes nil to nil" do
173
+ SourceMonitor.configure { |c| c.my_section.limit = nil }
174
+ assert_nil SourceMonitor.config.my_section.limit
175
+ end
176
+
177
+ test "limit normalizes empty string to nil" do
178
+ SourceMonitor.configure { |c| c.my_section.limit = "" }
179
+ assert_nil SourceMonitor.config.my_section.limit
180
+ end
181
+
182
+ test "limit normalizes zero to nil" do
183
+ SourceMonitor.configure { |c| c.my_section.limit = 0 }
184
+ assert_nil SourceMonitor.config.my_section.limit
185
+ end
186
+
187
+ test "limit normalizes negative to nil" do
188
+ SourceMonitor.configure { |c| c.my_section.limit = -5 }
189
+ assert_nil SourceMonitor.config.my_section.limit
190
+ end
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Scenario D: Create a New Settings Section
196
+
197
+ ### Step 1: Create the settings file
198
+
199
+ ```ruby
200
+ # lib/source_monitor/configuration/notifications_settings.rb
201
+ # frozen_string_literal: true
202
+
203
+ module SourceMonitor
204
+ class Configuration
205
+ class NotificationsSettings
206
+ attr_accessor :enabled, :channels, :throttle_seconds
207
+
208
+ def initialize
209
+ reset!
210
+ end
211
+
212
+ def reset!
213
+ @enabled = true
214
+ @channels = []
215
+ @throttle_seconds = 60
216
+ end
217
+ end
218
+ end
219
+ end
220
+ ```
221
+
222
+ ### Step 2: Add require and reader to Configuration
223
+
224
+ ```ruby
225
+ # lib/source_monitor/configuration.rb
226
+
227
+ # At the top, add require:
228
+ require "source_monitor/configuration/notifications_settings"
229
+
230
+ # In the class:
231
+ attr_reader :http, :scrapers, :retention, :events, :models,
232
+ :realtime, :fetching, :health, :authentication, :scraping,
233
+ :notifications
234
+
235
+ # In initialize:
236
+ def initialize
237
+ # ... existing ...
238
+ @notifications = NotificationsSettings.new
239
+ end
240
+ ```
241
+
242
+ ### Step 3: Write tests
243
+
244
+ ```ruby
245
+ # test/lib/source_monitor/configuration_test.rb
246
+
247
+ test "notifications settings have correct defaults" do
248
+ settings = SourceMonitor.config.notifications
249
+ assert_equal true, settings.enabled
250
+ assert_equal [], settings.channels
251
+ assert_equal 60, settings.throttle_seconds
252
+ end
253
+
254
+ test "notifications settings can be configured" do
255
+ SourceMonitor.configure do |config|
256
+ config.notifications.enabled = false
257
+ config.notifications.channels = [:email]
258
+ config.notifications.throttle_seconds = 30
259
+ end
260
+
261
+ settings = SourceMonitor.config.notifications
262
+ assert_equal false, settings.enabled
263
+ assert_equal [:email], settings.channels
264
+ assert_equal 30, settings.throttle_seconds
265
+ end
266
+
267
+ test "notifications reset restores defaults" do
268
+ SourceMonitor.configure do |config|
269
+ config.notifications.enabled = false
270
+ end
271
+ SourceMonitor.reset_configuration!
272
+ assert_equal true, SourceMonitor.config.notifications.enabled
273
+ end
274
+ ```
275
+
276
+ ### Step 4: Run tests
277
+
278
+ ```bash
279
+ PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/configuration_test.rb
280
+ ```
281
+
282
+ ---
283
+
284
+ ## Checklist for Any New Setting
285
+
286
+ - [ ] Attribute added to settings class (attr_accessor or custom setter)
287
+ - [ ] Default value set in `reset!` (or `initialize` for classes without `reset!`)
288
+ - [ ] require statement added (if new file)
289
+ - [ ] Reader method exposed on Configuration (if new section)
290
+ - [ ] Initialization in Configuration#initialize (if new section)
291
+ - [ ] Tests: default value is correct
292
+ - [ ] Tests: value can be overridden
293
+ - [ ] Tests: reset restores default
294
+ - [ ] Tests: validation raises ArgumentError (if applicable)
295
+ - [ ] Tests: edge cases for normalization (if applicable)
296
+ - [ ] All tests pass: `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/configuration_test.rb`
297
+ - [ ] RuboCop clean: `bin/rubocop lib/source_monitor/configuration/`
@@ -0,0 +1,153 @@
1
+ ---
2
+ name: sm-configure
3
+ description: Use when configuring SourceMonitor engine settings via the DSL, including queue settings, HTTP client, fetching, health, scrapers, retention, scraping controls, events, model extensions, realtime, and authentication.
4
+ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
5
+ ---
6
+
7
+ # sm-configure: Engine Configuration DSL
8
+
9
+ Comprehensive reference for configuring SourceMonitor via `SourceMonitor.configure`.
10
+
11
+ ## When to Use
12
+
13
+ - Adding or modifying settings in `config/initializers/source_monitor.rb`
14
+ - Understanding what configuration options are available
15
+ - Debugging configuration-related issues
16
+ - Setting up environment-specific overrides
17
+
18
+ ## Configuration Entry Point
19
+
20
+ All configuration lives inside the `configure` block in the host app's initializer:
21
+
22
+ ```ruby
23
+ SourceMonitor.configure do |config|
24
+ # settings here
25
+ end
26
+ ```
27
+
28
+ After the block executes, `ModelExtensions.reload!` runs automatically to apply any model changes. Restart web and worker processes after changes.
29
+
30
+ ## Configuration Sections
31
+
32
+ The `config` object (`SourceMonitor::Configuration`) has 10 sub-sections plus top-level queue/job settings:
33
+
34
+ | Section | Accessor | Class |
35
+ |---|---|---|
36
+ | Top-level | `config.*` | `Configuration` |
37
+ | HTTP | `config.http` | `HTTPSettings` |
38
+ | Fetching | `config.fetching` | `FetchingSettings` |
39
+ | Health | `config.health` | `HealthSettings` |
40
+ | Scrapers | `config.scrapers` | `ScraperRegistry` |
41
+ | Retention | `config.retention` | `RetentionSettings` |
42
+ | Scraping | `config.scraping` | `ScrapingSettings` |
43
+ | Events | `config.events` | `Events` |
44
+ | Models | `config.models` | `Models` |
45
+ | Realtime | `config.realtime` | `RealtimeSettings` |
46
+ | Authentication | `config.authentication` | `AuthenticationSettings` |
47
+
48
+ See `reference/configuration-reference.md` for every setting with types, defaults, and examples.
49
+
50
+ ## Quick Examples
51
+
52
+ ### Queue Configuration
53
+ ```ruby
54
+ config.queue_namespace = "source_monitor"
55
+ config.fetch_queue_name = "source_monitor_fetch"
56
+ config.fetch_queue_concurrency = 4
57
+ ```
58
+
59
+ ### HTTP Client
60
+ ```ruby
61
+ config.http.timeout = 30
62
+ config.http.proxy = ENV["HTTP_PROXY"]
63
+ config.http.retry_max = 3
64
+ ```
65
+
66
+ ### Authentication (Devise)
67
+ ```ruby
68
+ config.authentication.authenticate_with :authenticate_user!
69
+ config.authentication.authorize_with ->(c) { c.current_user&.admin? }
70
+ ```
71
+
72
+ ### Events
73
+ ```ruby
74
+ config.events.after_item_created { |e| Notifier.new_item(e.item) }
75
+ config.events.register_item_processor ->(ctx) { Indexer.index(ctx.item) }
76
+ ```
77
+
78
+ ### Model Extensions
79
+ ```ruby
80
+ config.models.table_name_prefix = "sm_"
81
+ config.models.source.include_concern "MyApp::SourceExtension"
82
+ config.models.item.validate :custom_check
83
+ ```
84
+
85
+ ### Realtime
86
+ ```ruby
87
+ config.realtime.adapter = :redis
88
+ config.realtime.redis_url = ENV["REDIS_URL"]
89
+ ```
90
+
91
+ ## Helper APIs
92
+
93
+ ```ruby
94
+ SourceMonitor.config # Current configuration
95
+ SourceMonitor.configure { |c| ... } # Set configuration
96
+ SourceMonitor.reset_configuration! # Revert to defaults (for tests)
97
+ SourceMonitor.events # Shortcut to config.events
98
+ SourceMonitor.queue_name(:fetch) # Resolved queue name
99
+ SourceMonitor.queue_concurrency(:scrape) # Resolved concurrency
100
+ SourceMonitor.mission_control_dashboard_path # Resolved MC path or nil
101
+ ```
102
+
103
+ ## Key Source Files
104
+
105
+ | File | Purpose |
106
+ |---|---|
107
+ | `lib/source_monitor/configuration.rb` | Main Configuration class |
108
+ | `lib/source_monitor/configuration/http_settings.rb` | HTTP client settings |
109
+ | `lib/source_monitor/configuration/fetching_settings.rb` | Adaptive scheduling |
110
+ | `lib/source_monitor/configuration/health_settings.rb` | Health monitoring |
111
+ | `lib/source_monitor/configuration/scraper_registry.rb` | Scraper adapter registry |
112
+ | `lib/source_monitor/configuration/retention_settings.rb` | Item retention |
113
+ | `lib/source_monitor/configuration/scraping_settings.rb` | Scraping controls |
114
+ | `lib/source_monitor/configuration/events.rb` | Event callbacks |
115
+ | `lib/source_monitor/configuration/models.rb` | Model extensions config |
116
+ | `lib/source_monitor/configuration/model_definition.rb` | Per-model definition |
117
+ | `lib/source_monitor/configuration/realtime_settings.rb` | Action Cable settings |
118
+ | `lib/source_monitor/configuration/authentication_settings.rb` | Auth settings |
119
+ | `lib/source_monitor/configuration/validation_definition.rb` | Validation wrapper |
120
+
121
+ ## References
122
+
123
+ - `reference/configuration-reference.md` -- Complete settings reference
124
+ - `docs/configuration.md` -- Official configuration documentation
125
+ - `lib/generators/source_monitor/install/templates/source_monitor.rb.tt` -- Initializer template
126
+
127
+ ## Testing
128
+
129
+ Reset configuration between tests:
130
+ ```ruby
131
+ setup do
132
+ SourceMonitor.reset_configuration!
133
+ end
134
+ ```
135
+
136
+ Test custom configuration:
137
+ ```ruby
138
+ test "custom queue name" do
139
+ SourceMonitor.configure do |config|
140
+ config.fetch_queue_name = "custom_fetch"
141
+ end
142
+ assert_equal "custom_fetch", SourceMonitor.queue_name(:fetch)
143
+ end
144
+ ```
145
+
146
+ ## Checklist
147
+
148
+ - [ ] Initializer exists at `config/initializers/source_monitor.rb`
149
+ - [ ] Queue names match `config/solid_queue.yml` entries
150
+ - [ ] Authentication hooks configured for host auth system
151
+ - [ ] HTTP timeouts appropriate for target feeds
152
+ - [ ] Retention policy set for production
153
+ - [ ] Workers restarted after configuration changes