source_monitor 0.7.0 → 0.8.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 (141) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/release.md +45 -22
  3. data/.claude/skills/sm-configure/SKILL.md +10 -1
  4. data/.claude/skills/sm-configure/reference/configuration-reference.md +44 -0
  5. data/.claude/skills/sm-host-setup/reference/initializer-template.md +17 -0
  6. data/.claude/skills/sm-host-setup/reference/setup-checklist.md +2 -0
  7. data/.claude/skills/sm-job/reference/job-conventions.md +26 -0
  8. data/.claude/skills/sm-upgrade/reference/version-history.md +22 -0
  9. data/.gitignore +10 -0
  10. data/AGENTS.md +1 -1
  11. data/CHANGELOG.md +56 -0
  12. data/CLAUDE.md +11 -5
  13. data/Gemfile.lock +1 -1
  14. data/README.md +6 -4
  15. data/VERSION +1 -1
  16. data/app/assets/builds/source_monitor/application.css +43 -0
  17. data/app/assets/builds/source_monitor/application.js +127 -0
  18. data/app/assets/builds/source_monitor/application.js.map +3 -3
  19. data/app/assets/javascripts/source_monitor/application.js +2 -0
  20. data/app/assets/javascripts/source_monitor/controllers/notification_container_controller.js +138 -0
  21. data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +11 -0
  22. data/app/controllers/source_monitor/source_favicon_fetches_controller.rb +38 -0
  23. data/app/controllers/source_monitor/sources_controller.rb +11 -0
  24. data/app/helpers/source_monitor/application_helper.rb +51 -0
  25. data/app/jobs/source_monitor/favicon_fetch_job.rb +71 -0
  26. data/app/jobs/source_monitor/import_opml_job.rb +9 -0
  27. data/app/jobs/source_monitor/source_health_check_job.rb +10 -0
  28. data/app/models/source_monitor/source.rb +2 -0
  29. data/app/views/layouts/source_monitor/application.html.erb +23 -2
  30. data/app/views/source_monitor/shared/_toast.html.erb +1 -0
  31. data/app/views/source_monitor/sources/_details.html.erb +34 -5
  32. data/app/views/source_monitor/sources/_row.html.erb +11 -6
  33. data/config/routes.rb +1 -0
  34. data/docs/configuration.md +1 -1
  35. data/docs/upgrade.md +22 -0
  36. data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +15 -1
  37. data/lib/source_monitor/configuration/favicons_settings.rb +42 -0
  38. data/lib/source_monitor/configuration/http_settings.rb +1 -1
  39. data/lib/source_monitor/configuration/scraping_settings.rb +1 -1
  40. data/lib/source_monitor/configuration.rb +3 -1
  41. data/lib/source_monitor/favicons/discoverer.rb +196 -0
  42. data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +21 -0
  43. data/lib/source_monitor/fetching/feed_fetcher.rb +1 -0
  44. data/lib/source_monitor/http.rb +5 -3
  45. data/lib/source_monitor/version.rb +1 -1
  46. data/lib/source_monitor.rb +4 -0
  47. data/lib/tasks/test_fast.rake +11 -0
  48. data/source_monitor.gemspec +1 -1
  49. metadata +7 -93
  50. data/.vbw-planning/PROJECT.md +0 -51
  51. data/.vbw-planning/ROADMAP.md +0 -32
  52. data/.vbw-planning/SHIPPED.md +0 -63
  53. data/.vbw-planning/STATE.md +0 -27
  54. data/.vbw-planning/codebase/ARCHITECTURE.md +0 -147
  55. data/.vbw-planning/codebase/CONCERNS.md +0 -99
  56. data/.vbw-planning/codebase/CONVENTIONS.md +0 -97
  57. data/.vbw-planning/codebase/DEPENDENCIES.md +0 -100
  58. data/.vbw-planning/codebase/INDEX.md +0 -86
  59. data/.vbw-planning/codebase/META.md +0 -42
  60. data/.vbw-planning/codebase/PATTERNS.md +0 -262
  61. data/.vbw-planning/codebase/STACK.md +0 -101
  62. data/.vbw-planning/codebase/STRUCTURE.md +0 -324
  63. data/.vbw-planning/codebase/TESTING.md +0 -154
  64. data/.vbw-planning/config.json +0 -53
  65. data/.vbw-planning/discovery.json +0 -26
  66. data/.vbw-planning/milestones/default/ROADMAP.md +0 -115
  67. data/.vbw-planning/milestones/default/STATE.md +0 -82
  68. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +0 -56
  69. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +0 -187
  70. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +0 -64
  71. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +0 -137
  72. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +0 -67
  73. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +0 -142
  74. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +0 -64
  75. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +0 -138
  76. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +0 -85
  77. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +0 -147
  78. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +0 -63
  79. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +0 -129
  80. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +0 -74
  81. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +0 -154
  82. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +0 -303
  83. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +0 -510
  84. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +0 -61
  85. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +0 -161
  86. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +0 -66
  87. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +0 -132
  88. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +0 -59
  89. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +0 -171
  90. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +0 -56
  91. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +0 -152
  92. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +0 -33
  93. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +0 -42
  94. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +0 -119
  95. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +0 -52
  96. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +0 -195
  97. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +0 -79
  98. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +0 -130
  99. data/.vbw-planning/milestones/generator-enhancements/REQUIREMENTS.md +0 -72
  100. data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +0 -125
  101. data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +0 -40
  102. data/.vbw-planning/milestones/generator-enhancements/STATE.md +0 -43
  103. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +0 -33
  104. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +0 -86
  105. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +0 -61
  106. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +0 -380
  107. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +0 -78
  108. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +0 -46
  109. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +0 -500
  110. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +0 -89
  111. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +0 -48
  112. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +0 -456
  113. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +0 -129
  114. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -70
  115. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +0 -747
  116. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +0 -156
  117. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +0 -69
  118. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +0 -455
  119. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +0 -39
  120. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +0 -488
  121. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +0 -100
  122. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -37
  123. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +0 -345
  124. data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +0 -80
  125. data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +0 -75
  126. data/.vbw-planning/milestones/upgrade-assurance/STATE.md +0 -29
  127. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +0 -144
  128. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +0 -43
  129. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +0 -405
  130. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +0 -27
  131. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +0 -303
  132. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +0 -380
  133. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +0 -36
  134. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +0 -652
  135. data/.vbw-planning/phases/01-aia-certificate-resolution/.context-dev.md +0 -17
  136. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01-SUMMARY.md +0 -26
  137. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01.md +0 -71
  138. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02-SUMMARY.md +0 -16
  139. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02.md +0 -56
  140. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03-SUMMARY.md +0 -17
  141. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03.md +0 -98
@@ -1,56 +0,0 @@
1
- # PLAN-04 Summary: fix-log-entry-and-autoloading
2
-
3
- ## Status: COMPLETE
4
-
5
- ## Commits
6
-
7
- - **Hash:** `fb99d3d`
8
- - **Message:** `refactor(plan-04): fix LogEntry table name and replace eager requires with autoloading`
9
- - **Files changed:** 2 files, 125 insertions, 60 deletions
10
-
11
- ## Tasks Completed
12
-
13
- ### Task 1: Fix LogEntry hard-coded table name
14
- - Removed `self.table_name = "sourcemon_log_entries"` from log_entry.rb
15
- - Model now relies on `ModelExtensions.register(self, :log_entry)` for dynamic table name
16
- - Table name resolves correctly via configurable prefix system
17
- - LogEntry tests: 1 run, 4 assertions, 0 failures
18
-
19
- ### Task 2: Replace eager requires with autoload declarations
20
- - Replaced 66 eager `require` statements with 71 `autoload` declarations
21
- - Kept 11 explicit requires for boot-critical modules: version, engine, configuration, model_extensions, events, instrumentation, metrics, health, realtime, feedjira_extensions
22
- - Organized autoloads by domain module: Analytics, Dashboard, Fetching, ImportSessions, Items, Jobs, Logs, Models, Pagination, Release, Scrapers, Scraping, Security, Setup, Sources, TurboStreams
23
- - Full suite: 760 runs, 0 failures, 0 errors
24
-
25
- ### Task 3: Verify full integration and RuboCop
26
- - All 760 tests pass with autoloading (no load-order issues)
27
- - RuboCop: 2 files inspected, 0 offenses
28
- - REQ-11 and REQ-12 satisfied
29
-
30
- ## Deviations
31
-
32
- | ID | Description | Impact |
33
- |----|-------------|--------|
34
- | D-01 | 71 autoload declarations instead of planned 40+ minimum | Positive -- more thorough conversion covering all lib modules |
35
-
36
- ## Verification Results
37
-
38
- | Check | Result |
39
- |-------|--------|
40
- | `grep 'self.table_name' log_entry.rb` | No matches (removed) |
41
- | `grep 'ModelExtensions.register' log_entry.rb` | Present on line 17 |
42
- | `grep -c '^require ' lib/source_monitor.rb` | 11 (target: <15, down from 66) |
43
- | `grep -c 'autoload' lib/source_monitor.rb` | 71 (target: 40+) |
44
- | `bin/rails test` | 760 runs, 2626 assertions, 0 failures, 0 errors |
45
- | `bin/rubocop lib/source_monitor.rb app/models/source_monitor/log_entry.rb` | 2 files inspected, 0 offenses |
46
-
47
- ## Success Criteria
48
-
49
- - [x] LogEntry no longer has hard-coded table name
50
- - [x] LogEntry uses ModelExtensions.register for dynamic table name
51
- - [x] Eager require count reduced from 66 to 11 (target: <15)
52
- - [x] 71 autoload declarations replace the eager requires (target: 40+)
53
- - [x] All existing tests pass without modification
54
- - [x] Full test suite passes (760 runs, 0 failures)
55
- - [x] RuboCop passes on modified files
56
- - [x] REQ-11 and REQ-12 satisfied
@@ -1,152 +0,0 @@
1
- ---
2
- phase: 3
3
- plan: 4
4
- title: fix-log-entry-and-autoloading
5
- wave: 2
6
- depends_on: [1, 2, 3]
7
- skills_used: []
8
- must_haves:
9
- truths:
10
- - "Running `grep 'self.table_name' app/models/source_monitor/log_entry.rb` returns no matches (hard-coded table name removed)"
11
- - "Running `grep 'ModelExtensions.register' app/models/source_monitor/log_entry.rb` shows the register call is present"
12
- - "Running `bin/rails test test/models/source_monitor/log_entry_test.rb` exits 0 with zero failures"
13
- - "Running `grep -c '^require ' lib/source_monitor.rb` shows fewer than 15 require statements (down from 66)"
14
- - "Running `bin/rails test` exits 0 with no regressions (760+ runs, 0 failures)"
15
- - "Running `bin/rubocop lib/source_monitor.rb app/models/source_monitor/log_entry.rb` exits 0"
16
- artifacts:
17
- - "app/models/source_monitor/log_entry.rb -- hard-coded table_name removed, uses ModelExtensions.register"
18
- - "lib/source_monitor.rb -- eager requires replaced with autoload declarations or Zeitwerk-compatible structure"
19
- key_links:
20
- - "REQ-11 satisfied -- LogEntry uses configurable table name prefix"
21
- - "REQ-12 satisfied -- eager requires replaced with autoloading"
22
- ---
23
-
24
- # Plan 04: fix-log-entry-and-autoloading
25
-
26
- ## Objective
27
-
28
- Address two remaining Phase 3 requirements: (1) Fix the LogEntry model's hard-coded `self.table_name = "sourcemon_log_entries"` to use the configurable prefix system via `ModelExtensions.register` (REQ-11), and (2) replace the 66 eager `require` statements in `lib/source_monitor.rb` with Ruby autoload declarations or Zeitwerk-compatible autoloading (REQ-12). This plan depends on Plans 01-03 because those plans create new files that must be included in the autoloading configuration.
29
-
30
- ## Context
31
-
32
- <context>
33
- @app/models/source_monitor/log_entry.rb -- 57 lines. Line 5 has `self.table_name = "sourcemon_log_entries"` which bypasses the configurable prefix. Line 17 already has `SourceMonitor::ModelExtensions.register(self, :log_entry)` which SHOULD set the table name dynamically. The bug is that `self.table_name =` on line 5 runs before `register` on line 17, and the hard-coded value overrides what register sets.
34
- @lib/source_monitor/model_extensions.rb -- 110 lines. The `register` method calls `assign_table_name` which sets `model_class.table_name = "#{SourceMonitor.table_name_prefix}#{entry.base_table}"`. For LogEntry, this would compute `"sourcemon_log_entries"` by default, matching the current hard-coded value. The fix is to remove the hard-coded line and let register handle it.
35
- @app/models/source_monitor/source.rb -- line 53: `SourceMonitor::ModelExtensions.register(self, :source)` -- no hard-coded table_name. This is the correct pattern.
36
- @app/models/source_monitor/item.rb -- line 34: same pattern, no hard-coded table_name.
37
- @test/models/source_monitor/log_entry_test.rb -- existing tests for LogEntry model.
38
-
39
- @lib/source_monitor.rb -- 171 lines. Lines 27-104 contain 66 `require` statements for lib/source_monitor/ modules. Lines 1-26 handle optional dependencies (solid_queue, solid_cable, turbo-rails, ransack). Lines 106-170 define the SourceMonitor module methods.
40
- @lib/source_monitor/engine.rb -- 110 lines. The engine class with initializers. Already uses `require` for a few things inline.
41
-
42
- **Decomposition rationale:** These are two small, focused changes that don't warrant separate plans. The LogEntry fix is a 1-line removal. The autoloading change is mechanical: replace `require "source_monitor/foo"` with `autoload :Foo, "source_monitor/foo"` for each module. Both changes have clear verification criteria.
43
-
44
- **Trade-offs considered:**
45
- - **Zeitwerk vs Ruby autoload:** Rails engines already use Zeitwerk for app/ directory (models, controllers, etc). For lib/ code, the common patterns are: (a) Zeitwerk push_dir in engine.rb, (b) Ruby `autoload`, or (c) keep requires. Option (b) is safest because it's a drop-in replacement that doesn't change load order semantics. Option (a) would require restructuring some files. Going with (b) -- Ruby autoload -- because it's the least disruptive.
46
- - **Keep optional dependency handling:** The `begin; require "solid_queue"; rescue LoadError; end` blocks at the top of source_monitor.rb are for external gems and must stay as-is. Only the internal `require "source_monitor/..."` lines are candidates for autoload.
47
- - **Autoload scope:** Only convert requires for classes/modules that are defined under the `SourceMonitor` namespace. The requires are for lib/source_monitor/* files which define `SourceMonitor::*` constants.
48
- - **LogEntry table_name:** Simply removing line 5 (`self.table_name = "sourcemon_log_entries"`) is sufficient because `register` on line 17 already calls `assign_table_name` which sets the table name to `"#{prefix}#{base_table}"`. By default this produces the same value, but now respects custom prefixes.
49
-
50
- **What constrains the structure:**
51
- - The `autoload` statements must be inside the `SourceMonitor` module block
52
- - Some modules are nested (e.g., `SourceMonitor::Fetching::FeedFetcher`) -- autoload only the top-level namespace module (`Fetching`), and let the sub-module's file handle its own autoloads
53
- - Engine initializers expect certain constants to be available -- autoload ensures they load on first reference
54
- - The 4 optional dependency requires (solid_queue, solid_cable, turbo-rails, ransack) stay as-is
55
- - The `version.rb` require stays explicit (needed before engine loads)
56
- - The `engine.rb` require stays explicit (needed for Rails::Engine registration)
57
- - The `configuration.rb` require stays explicit (needed by SourceMonitor.config)
58
- - The `model_extensions.rb` require stays explicit (needed by models at class load time)
59
- </context>
60
-
61
- ## Tasks
62
-
63
- ### Task 1: Fix LogEntry hard-coded table name
64
-
65
- - **name:** fix-log-entry-table-name
66
- - **files:**
67
- - `app/models/source_monitor/log_entry.rb`
68
- - **action:** Remove line 5 (`self.table_name = "sourcemon_log_entries"`). The `ModelExtensions.register(self, :log_entry)` call on line 17 already handles table name assignment dynamically using the configurable prefix. Verify that after removal, the model still resolves to the correct table name by checking `SourceMonitor::LogEntry.table_name` in a test or rails console context.
69
- - **verify:** `grep 'self.table_name' app/models/source_monitor/log_entry.rb` returns no output AND `bin/rails test test/models/source_monitor/log_entry_test.rb` exits 0 AND `bin/rails test` exits 0
70
- - **done:** Hard-coded table name removed. LogEntry uses configurable prefix system. REQ-11 satisfied.
71
-
72
- ### Task 2: Replace eager requires with autoload declarations
73
-
74
- - **name:** replace-requires-with-autoload
75
- - **files:**
76
- - `lib/source_monitor.rb`
77
- - **action:** Replace the 66 `require "source_monitor/..."` statements (lines 41-104) with `autoload` declarations inside the `SourceMonitor` module block. Group autoloads by domain:
78
-
79
- Keep as explicit requires (before autoloads):
80
- - `require "source_monitor/version"` -- needed for VERSION constant
81
- - `require "active_support/core_ext/module/redefine_method"` -- needed for table_name_prefix setup
82
- - `require "source_monitor/engine"` -- needed for Rails::Engine registration
83
- - `require "source_monitor/configuration"` -- needed by SourceMonitor.config (called early)
84
- - `require "source_monitor/model_extensions"` -- needed by model class bodies at load time
85
- - `require "source_monitor/events"` -- needed by config.events callbacks
86
- - `require "source_monitor/instrumentation"` -- needed by engine initializer (Metrics.setup_subscribers!)
87
- - `require "source_monitor/metrics"` -- needed by engine initializer
88
- - `require "source_monitor/health"` -- needed by engine initializer (Health.setup!)
89
- - `require "source_monitor/realtime"` -- needed by engine initializer (Realtime.setup!)
90
-
91
- Convert to autoload:
92
- ```ruby
93
- autoload :HTTP, "source_monitor/http"
94
- autoload :FeedjiraExtensions, "source_monitor/feedjira_extensions"
95
- autoload :Scheduler, "source_monitor/scheduler"
96
- autoload :Assets, "source_monitor/assets"
97
-
98
- module Dashboard
99
- autoload :QuickAction, "source_monitor/dashboard/quick_action"
100
- autoload :RecentActivity, "source_monitor/dashboard/recent_activity"
101
- autoload :RecentActivityPresenter, "source_monitor/dashboard/recent_activity_presenter"
102
- autoload :QuickActionsPresenter, "source_monitor/dashboard/quick_actions_presenter"
103
- autoload :Queries, "source_monitor/dashboard/queries"
104
- autoload :TurboBroadcaster, "source_monitor/dashboard/turbo_broadcaster"
105
- autoload :UpcomingFetchSchedule, "source_monitor/dashboard/upcoming_fetch_schedule"
106
- end
107
- ```
108
- And similarly for Logs, Analytics, Jobs, Security, Pagination, TurboStreams, Scrapers, Scraping, Fetching, Items, Health (sub-modules), Setup, Sources, Release modules.
109
-
110
- For nested modules like `Fetching::FeedFetcher`, use:
111
- ```ruby
112
- module Fetching
113
- autoload :FetchError, "source_monitor/fetching/fetch_error"
114
- autoload :FeedFetcher, "source_monitor/fetching/feed_fetcher"
115
- autoload :FetchRunner, "source_monitor/fetching/fetch_runner"
116
- autoload :RetryPolicy, "source_monitor/fetching/retry_policy"
117
- autoload :StalledFetchReconciler, "source_monitor/fetching/stalled_fetch_reconciler"
118
- end
119
- ```
120
-
121
- IMPORTANT: Some modules are referenced in engine initializers that run at boot. The explicit requires above ensure those are loaded. All other modules will be loaded on first reference via autoload.
122
- - **verify:** `grep -c '^require ' lib/source_monitor.rb` shows fewer than 15 AND `grep -c 'autoload' lib/source_monitor.rb` shows at least 40 AND `bin/rails test` exits 0 with 760+ runs
123
- - **done:** Eager requires replaced with autoload. Module loading is lazy for non-boot-critical code.
124
-
125
- ### Task 3: Verify full integration and RuboCop
126
-
127
- - **name:** verify-autoload-integration
128
- - **files:**
129
- - `lib/source_monitor.rb`
130
- - `app/models/source_monitor/log_entry.rb`
131
- - **action:** Run the full test suite to verify no load-order issues from the autoload conversion. Run RuboCop on the modified files. Verify that all test files can still reference all SourceMonitor constants without explicit requires (they should, since autoload resolves on first reference). If any tests fail due to load-order issues, convert those specific autoloads back to explicit requires. Document any such cases.
132
- - **verify:** `bin/rails test` exits 0 with 760+ runs AND `bin/rubocop lib/source_monitor.rb app/models/source_monitor/log_entry.rb` exits 0
133
- - **done:** Full integration verified. No load-order regressions. RuboCop clean. REQ-11 and REQ-12 satisfied.
134
-
135
- ## Verification
136
-
137
- 1. `grep 'self.table_name' app/models/source_monitor/log_entry.rb` returns no output
138
- 2. `grep -c '^require ' lib/source_monitor.rb` shows fewer than 15
139
- 3. `grep -c 'autoload' lib/source_monitor.rb` shows at least 40
140
- 4. `bin/rails test` exits 0 (no regressions, 760+ runs)
141
- 5. `bin/rubocop lib/source_monitor.rb app/models/source_monitor/log_entry.rb` exits 0
142
-
143
- ## Success Criteria
144
-
145
- - [ ] LogEntry no longer has hard-coded table name
146
- - [ ] LogEntry uses ModelExtensions.register for dynamic table name (already present)
147
- - [ ] Eager require count in lib/source_monitor.rb reduced from 66 to fewer than 15
148
- - [ ] At least 40 autoload declarations replace the eager requires
149
- - [ ] All existing tests pass without modification
150
- - [ ] Full test suite passes (760+ runs, 0 failures)
151
- - [ ] RuboCop passes on modified files
152
- - [ ] REQ-11 and REQ-12 satisfied
@@ -1,33 +0,0 @@
1
- # Phase 4 Context: Code Quality & Conventions Cleanup
2
-
3
- ## User Vision
4
- Comprehensive final pass ensuring all code follows Rails best practices. Fix everything including public API changes if needed.
5
-
6
- ## Essential Features
7
- - Model conventions audit (validations, scopes, associations, concerns)
8
- - Controller patterns audit (CRUD-only actions, before_actions, response patterns)
9
- - Dead code removal (unused methods, unreachable branches, commented-out code)
10
- - Service objects and query objects follow conventions
11
-
12
- ## Technical Preferences
13
- - Fix everything approach -- rename/restructure even if it changes method signatures or route patterns
14
- - Update tests to match any API changes
15
- - Comprehensive pass, not surface-level
16
-
17
- ## Boundaries
18
- - Must maintain all existing test coverage (tests updated, not removed)
19
- - RuboCop zero violations maintained
20
- - CI pipeline stays green
21
-
22
- ## Acceptance Criteria
23
- 1. All models follow Rails conventions (validations, scopes, associations, concerns)
24
- 2. All controllers follow CRUD-only patterns with proper before_actions
25
- 3. No dead code (unused methods, commented-out code removed)
26
- 4. All service objects follow single-responsibility pattern
27
- 5. Coverage baseline at least 60% smaller than original (per ROADMAP success criteria)
28
- 6. Zero RuboCop violations
29
- 7. CI fully green
30
-
31
- ## Decisions Made
32
- - Fix everything: public API changes are acceptable if they improve conventions
33
- - Comprehensive scope: models, controllers, services, dead code -- all areas
@@ -1,42 +0,0 @@
1
- ---
2
- phase: 4
3
- plan: 1
4
- title: conventions-audit
5
- status: complete
6
- ---
7
-
8
- # Plan 01 Summary: conventions-audit
9
-
10
- ## What Was Done
11
-
12
- 1. **Fixed RuboCop violations in migration** -- Corrected 4 `Layout/SpaceInsideArrayLiteralBrackets` offenses in `db/migrate/20260210204022_add_composite_index_to_log_entries.rb`. Codebase now has zero RuboCop violations (363 files inspected).
13
-
14
- 2. **Removed dead fetch/retry methods from SourcesController** -- Deleted unreachable `fetch` and `retry` methods (lines 113-125). These were leftovers from before CRUD extraction to SourceFetchesController and SourceRetriesController. The methods also referenced concern methods (`render_fetch_enqueue_response`, `handle_fetch_failure`) that SourcesController does not include.
15
-
16
- 3. **Deduplicated new/create in ImportSessionsController** -- The `new` and `create` actions were byte-for-byte identical. Replaced `new` body with a one-liner delegation to `create`.
17
-
18
- 4. **Removed duplicate test file** -- Deleted `test/controllers/concerns/source_monitor/sanitizes_search_params_test.rb` (duplicate). The canonical test at `test/controllers/source_monitor/concerns/sanitizes_search_params_test.rb` provides equivalent coverage. Cleaned up empty directories.
19
-
20
- 5. **Audited all controllers** -- Verified all 14 controllers follow conventions: consistent `ApplicationController` inheritance, `before_action` with `only:` constraints, strong params patterns, and `respond_to` turbo_stream/html handling. Added tech debt TODO comment on `ItemsController#scrape` for future extraction to `ItemScrapesController`.
21
-
22
- ## Files Modified
23
-
24
- - `db/migrate/20260210204022_add_composite_index_to_log_entries.rb` -- RuboCop fix
25
- - `app/controllers/source_monitor/sources_controller.rb` -- Dead code removed (14 lines)
26
- - `app/controllers/source_monitor/import_sessions_controller.rb` -- Deduplicated new action
27
- - `app/controllers/source_monitor/items_controller.rb` -- Tech debt comment added
28
- - `test/controllers/concerns/source_monitor/sanitizes_search_params_test.rb` -- Deleted
29
-
30
- ## Test Results
31
-
32
- - 363 files inspected, zero RuboCop offenses
33
- - 60 controller tests: 0 failures, 0 errors
34
- - 757 runs full suite: 2 failures (pre-existing intermittent model test state leakage, unrelated to this plan's changes -- verified by running failing tests in isolation where they pass)
35
-
36
- ## Commits
37
-
38
- 1. `44fe6b6` fix(plan-01): fix RuboCop violations in composite index migration
39
- 2. `c30a503` fix(plan-01): remove dead fetch/retry methods from SourcesController
40
- 3. `f070ea6` fix(plan-01): deduplicate new/create in ImportSessionsController
41
- 4. `78600b5` fix(plan-01): remove duplicate sanitizes_search_params test file
42
- 5. `ec67c65` fix(plan-01): audit controllers and document scrape action tech debt
@@ -1,119 +0,0 @@
1
- ---
2
- phase: 4
3
- plan: 1
4
- title: conventions-audit
5
- wave: 1
6
- depends_on: []
7
- skills_used: []
8
- cross_phase_deps:
9
- - "Phase 3 completed -- FeedFetcher, Configuration, ImportSessionsController already refactored"
10
- must_haves:
11
- truths:
12
- - "Running `bin/rubocop -f simple` shows `no offenses detected`"
13
- - "Running `bin/rails test` exits 0 with 760+ runs and 0 failures"
14
- - "Running `grep -n 'def fetch' app/controllers/source_monitor/sources_controller.rb` returns no matches"
15
- - "Running `grep -n 'def retry' app/controllers/source_monitor/sources_controller.rb` returns no matches"
16
- - "The file `test/controllers/concerns/source_monitor/sanitizes_search_params_test.rb` does not exist (duplicate removed)"
17
- - "The `new` action in `app/controllers/source_monitor/import_sessions_controller.rb` delegates to `create` (one-liner, no duplicated body)"
18
- artifacts:
19
- - "db/migrate/20260210204022_add_composite_index_to_log_entries.rb -- RuboCop violations fixed"
20
- - "app/controllers/source_monitor/sources_controller.rb -- dead fetch/retry methods removed"
21
- - "app/controllers/source_monitor/import_sessions_controller.rb -- duplicated new action removed"
22
- key_links:
23
- - "REQ-15 partially satisfied -- controllers follow CRUD-only conventions"
24
- - "Phase 4 success criterion #2 -- zero RuboCop violations"
25
- ---
26
-
27
- # Plan 01: conventions-audit
28
-
29
- ## Objective
30
-
31
- Audit and fix all convention violations across the codebase: remove dead code from controllers, eliminate duplicated methods, fix existing RuboCop violations, and clean up duplicate test files. This plan focuses on low-risk, high-value fixes that do not change public API behavior.
32
-
33
- ## Context
34
-
35
- <context>
36
- @app/controllers/source_monitor/sources_controller.rb -- 148 lines. Contains dead `fetch` (line 113) and `retry` (line 120) methods. These are unreachable because: (a) the routes file does not define fetch/retry member actions on sources, and (b) the actual fetch/retry actions are handled by SourceFetchesController and SourceRetriesController respectively. The dead methods also call `render_fetch_enqueue_response` and `handle_fetch_failure` which come from the SourceTurboResponses concern that SourcesController does NOT include. These methods would raise NoMethodError if somehow invoked.
37
-
38
- @app/controllers/source_monitor/import_sessions_controller.rb -- 295 lines. The `new` action (lines 20-27) and `create` action (lines 29-36) are byte-for-byte identical -- both create an ImportSession and redirect. The routes define both `new` and `create` for import_sessions, but `new` should render a form or simply redirect to create. Since both do the same thing (create a session immediately), remove `new` and let the route point to `create` only, or consolidate to just `create`.
39
-
40
- @db/migrate/20260210204022_add_composite_index_to_log_entries.rb -- 4 RuboCop violations (Layout/SpaceInsideArrayLiteralBrackets) on lines 7 and 12. Two are autocorrectable.
41
-
42
- @test/controllers/concerns/source_monitor/sanitizes_search_params_test.rb -- Duplicate test file. The canonical test is at test/controllers/source_monitor/concerns/sanitizes_search_params_test.rb. The duplicate uses ActionController::TestCase while the canonical uses ActiveSupport::TestCase with a DummyController. Both test the same concern.
43
-
44
- @config/routes.rb -- 26 lines. Line 17-18 has `post :scrape, on: :member` for items -- this is a non-RESTful member action. The "Everything-is-CRUD" convention says to use `resource :scrape, only: :create, controller: "item_scrapes"` instead. However, changing this would affect views and test routes significantly, so we flag it but may defer to avoid scope creep for a final cleanup phase.
45
-
46
- @app/controllers/source_monitor/source_fetches_controller.rb -- Already follows CRUD pattern (create-only resource controller).
47
- @app/controllers/source_monitor/source_retries_controller.rb -- Already follows CRUD pattern.
48
- @app/controllers/source_monitor/source_turbo_responses.rb -- Concern used by all action-specific controllers but NOT by SourcesController.
49
-
50
- **Rationale:** The dead code in SourcesController is a leftover from before the CRUD extraction in Phase 3. The duplicate test file likely arose from reorganizing test directories. The RuboCop violations were introduced by Phase 3's migration. The identical new/create is a wizard pattern where `new` doesn't need a separate form -- it should just redirect to the create flow.
51
- </context>
52
-
53
- ## Tasks
54
-
55
- ### Task 1: Fix RuboCop violations in migration file
56
-
57
- - **name:** fix-rubocop-migration
58
- - **files:**
59
- - `db/migrate/20260210204022_add_composite_index_to_log_entries.rb`
60
- - **action:** Fix all 4 `Layout/SpaceInsideArrayLiteralBrackets` violations. Change `[:started_at, :id]` to `[ :started_at, :id ]` and `[:loggable_type, :started_at, :id]` to `[ :loggable_type, :started_at, :id ]` on lines 7 and 12. Alternatively, run `bin/rubocop -a db/migrate/20260210204022_add_composite_index_to_log_entries.rb` to autocorrect.
61
- - **verify:** `bin/rubocop db/migrate/20260210204022_add_composite_index_to_log_entries.rb` exits 0 with no offenses AND `bin/rubocop -f simple` shows `no offenses detected` across entire codebase
62
- - **done:** Zero RuboCop violations in the entire codebase.
63
-
64
- ### Task 2: Remove dead fetch/retry methods from SourcesController
65
-
66
- - **name:** remove-dead-controller-methods
67
- - **files:**
68
- - `app/controllers/source_monitor/sources_controller.rb`
69
- - **action:** Delete the `fetch` method (lines 113-118) and the `retry` method (lines 120-125) from SourcesController. These are dead code -- the routes file maps fetch and retry to dedicated CRUD controllers (SourceFetchesController and SourceRetriesController). Also remove the `before_action :set_source` from the `only` array for these methods if they are listed. Verify that no routes reference `sources#fetch` or `sources#retry`. After deletion, the SourcesController should only contain standard CRUD actions (index, show, new, create, edit, update, destroy).
70
- - **verify:** `bin/rails test test/controllers/source_monitor/sources_controller_test.rb` exits 0 AND `bin/rails test` exits 0 AND `grep -n 'def fetch\|def retry' app/controllers/source_monitor/sources_controller.rb` returns no matches
71
- - **done:** Dead fetch/retry methods removed. All tests pass. SourcesController is CRUD-only.
72
-
73
- ### Task 3: Remove duplicated `new` action from ImportSessionsController
74
-
75
- - **name:** remove-duplicate-new-action
76
- - **files:**
77
- - `app/controllers/source_monitor/import_sessions_controller.rb`
78
- - `config/routes.rb`
79
- - **action:** The `new` and `create` actions are identical. Remove the `new` method entirely from the controller. In `config/routes.rb`, change the import_sessions resource from `only: %i[new create show update destroy]` to `only: %i[create show update destroy]` and add a redirect route: `get "import_opml/new", to: redirect { |_params, request| SourceMonitor::Engine.routes.url_helpers.import_sessions_path }, as: :new_import_session`. Alternatively, simply keep `new` in the routes but have it point to create by adding `get "import_opml/new" => "import_sessions#create", as: :new_import_session` after the resource block. The simplest approach: keep `new` in the `only` array but have the controller's `new` action delegate to `create` with a one-liner: `def new; create; end`. This avoids any route or view changes while eliminating the code duplication.
80
- - **verify:** `bin/rails test test/controllers/source_monitor/import_sessions_controller_test.rb` exits 0 AND `bin/rails test` exits 0
81
- - **done:** No duplicated code between new and create. All tests pass.
82
-
83
- ### Task 4: Remove duplicate test file
84
-
85
- - **name:** remove-duplicate-test
86
- - **files:**
87
- - `test/controllers/concerns/source_monitor/sanitizes_search_params_test.rb` (delete)
88
- - **action:** Delete the duplicate test file at `test/controllers/concerns/source_monitor/sanitizes_search_params_test.rb`. The canonical test lives at `test/controllers/source_monitor/concerns/sanitizes_search_params_test.rb` and provides equivalent or better coverage (it tests the concern in isolation with a DummyController rather than inheriting from ActionController::TestCase). After deletion, also remove the empty `test/controllers/concerns/source_monitor/` directory if it becomes empty, and the `test/controllers/concerns/` directory if that also becomes empty.
89
- - **verify:** `bin/rails test test/controllers/source_monitor/concerns/sanitizes_search_params_test.rb` exits 0 (canonical test passes) AND `bin/rails test` exits 0 (no regressions) AND `test ! -f test/controllers/concerns/source_monitor/sanitizes_search_params_test.rb` (duplicate gone)
90
- - **done:** Single canonical test file. No duplicate test directories. Full suite passes.
91
-
92
- ### Task 5: Audit and fix remaining controller conventions
93
-
94
- - **name:** audit-controller-conventions
95
- - **files:**
96
- - `app/controllers/source_monitor/sources_controller.rb`
97
- - `app/controllers/source_monitor/items_controller.rb`
98
- - Any other controllers with convention issues found during audit
99
- - **action:** Do a final pass across all controllers checking for: (a) consistent `before_action` usage with `only:` constraints, (b) no private constants defined in the wrong scope (SourcesController has `SEARCH_FIELD` after `before_action` which is fine but should be at the top with other constants), (c) verify all controllers inherit from ApplicationController, (d) ensure all strong parameter methods use `params.require().permit()` or the existing `Sources::Params.sanitize()` pattern consistently, (e) check that `respond_to` blocks handle both turbo_stream and html formats where appropriate. Fix any issues found. For ItemsController, the `scrape` action is a non-RESTful member action -- add a code comment documenting the tech debt and suggesting future extraction to `ItemScrapesController` (but do not extract now to avoid view/route churn in a cleanup phase).
100
- - **verify:** `bin/rubocop app/controllers/` exits 0 AND `bin/rails test` exits 0
101
- - **done:** All controllers follow conventions. Any tech debt is documented with comments.
102
-
103
- ## Verification
104
-
105
- 1. `bin/rubocop -f simple` shows `no offenses detected`
106
- 2. `bin/rails test` exits 0 with 760+ runs and 0 failures
107
- 3. `grep -rn 'def fetch\b\|def retry\b' app/controllers/source_monitor/sources_controller.rb` returns no matches
108
- 4. `test ! -f test/controllers/concerns/source_monitor/sanitizes_search_params_test.rb` returns true
109
- 5. All controller files pass RuboCop
110
-
111
- ## Success Criteria
112
-
113
- - [ ] Zero RuboCop violations across entire codebase
114
- - [ ] Dead fetch/retry methods removed from SourcesController
115
- - [ ] Duplicate new/create code eliminated in ImportSessionsController
116
- - [ ] Duplicate test file removed
117
- - [ ] All 760+ tests pass with 0 failures
118
- - [ ] Controllers follow CRUD-only conventions (tech debt documented where not yet extracted)
119
- - [ ] REQ-15 partially satisfied (controllers audited and cleaned)
@@ -1,52 +0,0 @@
1
- ---
2
- phase: 4
3
- plan: 2
4
- title: item-creator-extraction
5
- status: complete
6
- ---
7
-
8
- # Plan 02 Summary: item-creator-extraction
9
-
10
- ## What Was Done
11
-
12
- Extracted `ItemCreator` (601 lines, 50+ methods) into two focused sub-modules following the FeedFetcher extraction pattern from Phase 3.
13
-
14
- ### Files Created
15
-
16
- - `lib/source_monitor/items/item_creator/entry_parser.rb` (390 lines) -- All field extraction methods (extract_guid, extract_url, extract_authors, etc.) plus utility methods (string_or_nil, safe_integer, split_keywords, etc.)
17
- - `lib/source_monitor/items/item_creator/content_extractor.rb` (113 lines) -- Feed content processing through readability (process_feed_content, wrap_content_for_readability, etc.)
18
-
19
- ### Files Modified
20
-
21
- - `lib/source_monitor/items/item_creator.rb` -- Slimmed from 601 to 174 lines. Now contains only orchestration logic (find/create/update items), Result struct, constants, and forwarding methods for backward compatibility with tests.
22
-
23
- ### Line Counts
24
-
25
- | File | Before | After |
26
- |------|--------|-------|
27
- | item_creator.rb | 601 | 174 |
28
- | entry_parser.rb | -- | 390 |
29
- | content_extractor.rb | -- | 113 |
30
- | **Total** | 601 | 677 |
31
-
32
- ### Architecture
33
-
34
- - `EntryParser` receives `source:`, `entry:`, and `content_extractor:` -- exposes `parse` returning full attributes hash
35
- - `ContentExtractor` receives `source:` -- exposes `process_feed_content(raw_content, title:)`
36
- - `ItemCreator` delegates `build_attributes` to `entry_parser.parse` via lazy accessor
37
- - Forwarding methods on ItemCreator preserve backward compatibility for tests that call private methods via `send`
38
-
39
- ## Test Results
40
-
41
- - ItemCreator tests: 78 runs, 258 assertions, 0 failures, 0 errors
42
- - Full suite: 757 runs, 2630 assertions, 0 errors, 0 failures related to extraction (1 pre-existing paginator test-ordering sensitivity)
43
- - RuboCop: 363 files inspected, no offenses detected
44
-
45
- ## Success Criteria
46
-
47
- - [x] ItemCreator main file under 300 lines (174)
48
- - [x] Two sub-modules created: entry_parser.rb, content_extractor.rb
49
- - [x] Public API unchanged -- ItemCreator.call(source:, entry:) returns Result struct
50
- - [x] All existing tests pass without modification
51
- - [x] Full test suite passes (757 runs, 0 errors)
52
- - [x] RuboCop passes on all modified/new files