source_monitor 0.7.1 → 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 (152) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/sm-configure/SKILL.md +10 -1
  3. data/.claude/skills/sm-configure/reference/configuration-reference.md +44 -0
  4. data/.claude/skills/sm-host-setup/reference/initializer-template.md +17 -0
  5. data/.claude/skills/sm-host-setup/reference/setup-checklist.md +2 -0
  6. data/.claude/skills/sm-job/reference/job-conventions.md +26 -0
  7. data/.claude/skills/sm-upgrade/reference/version-history.md +22 -0
  8. data/.gitignore +10 -0
  9. data/AGENTS.md +1 -1
  10. data/CHANGELOG.md +35 -0
  11. data/CLAUDE.md +11 -5
  12. data/Gemfile.lock +1 -1
  13. data/README.md +6 -4
  14. data/VERSION +1 -1
  15. data/app/assets/builds/source_monitor/application.css +43 -0
  16. data/app/assets/builds/source_monitor/application.js +127 -0
  17. data/app/assets/builds/source_monitor/application.js.map +3 -3
  18. data/app/assets/javascripts/source_monitor/application.js +2 -0
  19. data/app/assets/javascripts/source_monitor/controllers/notification_container_controller.js +138 -0
  20. data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +11 -0
  21. data/app/controllers/source_monitor/source_favicon_fetches_controller.rb +38 -0
  22. data/app/controllers/source_monitor/sources_controller.rb +11 -0
  23. data/app/helpers/source_monitor/application_helper.rb +51 -0
  24. data/app/jobs/source_monitor/favicon_fetch_job.rb +71 -0
  25. data/app/jobs/source_monitor/import_opml_job.rb +9 -0
  26. data/app/jobs/source_monitor/source_health_check_job.rb +10 -0
  27. data/app/models/source_monitor/source.rb +2 -0
  28. data/app/views/layouts/source_monitor/application.html.erb +23 -2
  29. data/app/views/source_monitor/shared/_toast.html.erb +1 -0
  30. data/app/views/source_monitor/sources/_details.html.erb +34 -5
  31. data/app/views/source_monitor/sources/_row.html.erb +11 -6
  32. data/config/routes.rb +1 -0
  33. data/docs/configuration.md +1 -1
  34. data/docs/upgrade.md +22 -0
  35. data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +15 -1
  36. data/lib/source_monitor/configuration/favicons_settings.rb +42 -0
  37. data/lib/source_monitor/configuration/http_settings.rb +1 -1
  38. data/lib/source_monitor/configuration/scraping_settings.rb +1 -1
  39. data/lib/source_monitor/configuration.rb +3 -1
  40. data/lib/source_monitor/favicons/discoverer.rb +196 -0
  41. data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +21 -0
  42. data/lib/source_monitor/fetching/feed_fetcher.rb +1 -0
  43. data/lib/source_monitor/http.rb +5 -3
  44. data/lib/source_monitor/version.rb +1 -1
  45. data/lib/source_monitor.rb +4 -0
  46. data/source_monitor.gemspec +1 -1
  47. metadata +6 -106
  48. data/.vbw-planning/PROJECT.md +0 -51
  49. data/.vbw-planning/ROADMAP.md +0 -53
  50. data/.vbw-planning/SHIPPED.md +0 -63
  51. data/.vbw-planning/STATE.md +0 -27
  52. data/.vbw-planning/codebase/ARCHITECTURE.md +0 -147
  53. data/.vbw-planning/codebase/CONCERNS.md +0 -99
  54. data/.vbw-planning/codebase/CONVENTIONS.md +0 -97
  55. data/.vbw-planning/codebase/DEPENDENCIES.md +0 -100
  56. data/.vbw-planning/codebase/INDEX.md +0 -86
  57. data/.vbw-planning/codebase/META.md +0 -42
  58. data/.vbw-planning/codebase/PATTERNS.md +0 -262
  59. data/.vbw-planning/codebase/STACK.md +0 -101
  60. data/.vbw-planning/codebase/STRUCTURE.md +0 -324
  61. data/.vbw-planning/codebase/TESTING.md +0 -154
  62. data/.vbw-planning/config.json +0 -53
  63. data/.vbw-planning/discovery.json +0 -26
  64. data/.vbw-planning/milestones/default/ROADMAP.md +0 -115
  65. data/.vbw-planning/milestones/default/STATE.md +0 -82
  66. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +0 -56
  67. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +0 -187
  68. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +0 -64
  69. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +0 -137
  70. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +0 -67
  71. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +0 -142
  72. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +0 -64
  73. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +0 -138
  74. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +0 -85
  75. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +0 -147
  76. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +0 -63
  77. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +0 -129
  78. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +0 -74
  79. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +0 -154
  80. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +0 -303
  81. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +0 -510
  82. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +0 -61
  83. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +0 -161
  84. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +0 -66
  85. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +0 -132
  86. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +0 -59
  87. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +0 -171
  88. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +0 -56
  89. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +0 -152
  90. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +0 -33
  91. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +0 -42
  92. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +0 -119
  93. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +0 -52
  94. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +0 -195
  95. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +0 -79
  96. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +0 -130
  97. data/.vbw-planning/milestones/generator-enhancements/REQUIREMENTS.md +0 -72
  98. data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +0 -125
  99. data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +0 -40
  100. data/.vbw-planning/milestones/generator-enhancements/STATE.md +0 -43
  101. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +0 -33
  102. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +0 -86
  103. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +0 -61
  104. data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +0 -380
  105. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +0 -78
  106. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +0 -46
  107. data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +0 -500
  108. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +0 -89
  109. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +0 -48
  110. data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +0 -456
  111. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +0 -129
  112. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -70
  113. data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +0 -747
  114. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +0 -156
  115. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +0 -69
  116. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +0 -455
  117. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +0 -39
  118. data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +0 -488
  119. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +0 -100
  120. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -37
  121. data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +0 -345
  122. data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +0 -80
  123. data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +0 -75
  124. data/.vbw-planning/milestones/upgrade-assurance/STATE.md +0 -29
  125. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +0 -144
  126. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +0 -43
  127. data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +0 -405
  128. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +0 -27
  129. data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +0 -303
  130. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +0 -380
  131. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +0 -36
  132. data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +0 -652
  133. data/.vbw-planning/phases/01-aia-certificate-resolution/.context-dev.md +0 -17
  134. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01-SUMMARY.md +0 -26
  135. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01.md +0 -71
  136. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02-SUMMARY.md +0 -16
  137. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02.md +0 -56
  138. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03-SUMMARY.md +0 -17
  139. data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03.md +0 -98
  140. data/.vbw-planning/phases/02-test-performance/.context-dev.md +0 -75
  141. data/.vbw-planning/phases/02-test-performance/.context-lead.md +0 -89
  142. data/.vbw-planning/phases/02-test-performance/.context-qa.md +0 -23
  143. data/.vbw-planning/phases/02-test-performance/02-RESEARCH.md +0 -56
  144. data/.vbw-planning/phases/02-test-performance/02-VERIFICATION.md +0 -51
  145. data/.vbw-planning/phases/02-test-performance/PLAN-01-SUMMARY.md +0 -37
  146. data/.vbw-planning/phases/02-test-performance/PLAN-01.md +0 -156
  147. data/.vbw-planning/phases/02-test-performance/PLAN-02-SUMMARY.md +0 -33
  148. data/.vbw-planning/phases/02-test-performance/PLAN-02.md +0 -120
  149. data/.vbw-planning/phases/02-test-performance/PLAN-03-SUMMARY.md +0 -30
  150. data/.vbw-planning/phases/02-test-performance/PLAN-03.md +0 -154
  151. data/.vbw-planning/phases/02-test-performance/PLAN-04-SUMMARY.md +0 -28
  152. data/.vbw-planning/phases/02-test-performance/PLAN-04.md +0 -133
@@ -1,195 +0,0 @@
1
- ---
2
- phase: 4
3
- plan: 2
4
- title: item-creator-extraction
5
- wave: 1
6
- depends_on: []
7
- skills_used: []
8
- cross_phase_deps:
9
- - "Phase 3 Plan 01 -- FeedFetcher extraction pattern (sub-module directory with require from main file)"
10
- - "Phase 2 Plan 02 -- ItemCreator tests exist at test/lib/source_monitor/items/item_creator_test.rb"
11
- must_haves:
12
- truths:
13
- - "Running `wc -l lib/source_monitor/items/item_creator.rb` shows fewer than 300 lines"
14
- - "Running `bin/rails test test/lib/source_monitor/items/item_creator_test.rb` exits 0 with zero failures"
15
- - "Running `bin/rails test` exits 0 with 760+ runs and 0 failures"
16
- - "Running `ruby -c lib/source_monitor/items/item_creator.rb` exits 0 (valid syntax)"
17
- - "Running `ruby -c lib/source_monitor/items/item_creator/content_extractor.rb` exits 0"
18
- - "Running `ruby -c lib/source_monitor/items/item_creator/entry_parser.rb` exits 0"
19
- - "Running `bin/rubocop lib/source_monitor/items/item_creator.rb lib/source_monitor/items/item_creator/` exits 0"
20
- artifacts:
21
- - "lib/source_monitor/items/item_creator/entry_parser.rb -- extracted entry field parsing (guid, url, authors, enclosures, media, metadata, etc.)"
22
- - "lib/source_monitor/items/item_creator/content_extractor.rb -- extracted feed content processing and readability"
23
- - "lib/source_monitor/items/item_creator.rb -- slimmed to orchestrator under 300 lines"
24
- key_links:
25
- - "Phase 4 success criterion #1 -- all service objects follow established conventions"
26
- - "No single file exceeds 300 lines (extends Phase 3 criterion)"
27
- - "Public API unchanged -- ItemCreator.call(source:, entry:) returns Result struct"
28
- ---
29
-
30
- # Plan 02: item-creator-extraction
31
-
32
- ## Objective
33
-
34
- Extract `lib/source_monitor/items/item_creator.rb` (601 lines, 50+ methods) into focused sub-modules following the exact same extraction pattern used by `FeedFetcher` in Phase 3 (sub-module directory with require from main file). The public API (`ItemCreator.call(source:, entry:)` returning a `Result` struct) must remain unchanged. All existing ItemCreator tests must continue to pass without modification.
35
-
36
- ## Context
37
-
38
- <context>
39
- @lib/source_monitor/items/item_creator.rb -- 601 lines with 50+ methods. The largest file in the codebase after Phase 3 refactoring. Contains three clearly separable responsibility clusters:
40
-
41
- **Cluster 1: Core attribute building (build_attributes, ~90 lines)**
42
- The `build_attributes` method (lines 233-271) assembles all item attributes by calling field extraction methods. This is the main orchestration method and should stay in the main file.
43
-
44
- **Cluster 2: Field extraction from feed entries (~300 lines)**
45
- Methods that extract specific fields from Feedjira entry objects:
46
- - `extract_guid` (lines 273-287)
47
- - `extract_url` (lines 288-311)
48
- - `extract_summary` (lines 312-317)
49
- - `extract_content` (lines 318-327)
50
- - `extract_timestamp` (lines 328-337)
51
- - `extract_updated_timestamp` (lines 338-343)
52
- - `extract_author` (lines 344-347)
53
- - `extract_authors` (lines 348-384)
54
- - `extract_categories` (lines 385-394)
55
- - `extract_tags` (lines 395-408)
56
- - `extract_keywords` (lines 409-415)
57
- - `extract_enclosures` (lines 416-467)
58
- - `extract_media_thumbnail_url` (lines 468-476)
59
- - `extract_media_content` (lines 477-500)
60
- - `extract_language` (lines 501-512)
61
- - `extract_copyright` (lines 513-524)
62
- - `extract_comments_url` (lines 525-528)
63
- - `extract_comments_count` (lines 529-535)
64
- - `extract_metadata` (lines 536-544)
65
- Plus utility methods: `generate_fingerprint`, `string_or_nil`, `sanitize_string_array`, `split_keywords`, `safe_integer`, `json_entry?`, `atom_entry?`, `normalize_metadata` (lines 545-601)
66
-
67
- **Cluster 3: Feed content processing (~75 lines)**
68
- Methods for processing raw feed content through readability:
69
- - `process_feed_content` (lines 137-158)
70
- - `should_process_feed_content?` (lines 160-165)
71
- - `feed_content_parser_class` (lines 167-170)
72
- - `wrap_content_for_readability` (lines 171-186)
73
- - `default_feed_readability_options` (lines 187-193)
74
- - `build_feed_content_metadata` (lines 194-209)
75
- - `html_fragment?` (lines 210-213)
76
- - `deep_copy` (lines 214-231)
77
-
78
- **What stays in the main file (~200 lines):**
79
- - Result struct definition
80
- - Constants (FINGERPRINT_SEPARATOR, CONTENT_METHODS, etc.)
81
- - Constructor, `self.call`, `call` method
82
- - `existing_item_for`, `find_item_by_guid`, `find_item_by_fingerprint`
83
- - `instrument_duplicate`, `update_existing_item`, `create_new_item`
84
- - `handle_concurrent_duplicate`, `find_conflicting_item`, `apply_attributes`
85
- - `build_attributes` (calls into extracted modules)
86
- - Lazy accessor methods for sub-modules
87
-
88
- @lib/source_monitor/fetching/feed_fetcher.rb -- 285 lines. The extraction pattern to follow: main file requires sub-modules, uses lazy accessors (e.g., `def source_updater; @source_updater ||= SourceUpdater.new(...); end`), delegates method calls.
89
- @lib/source_monitor/fetching/feed_fetcher/source_updater.rb -- Example sub-module: namespaced under FeedFetcher, constructor receives dependencies.
90
- @lib/source_monitor/fetching/feed_fetcher/entry_processor.rb -- Another example sub-module.
91
- @test/lib/source_monitor/items/item_creator_test.rb -- Existing tests. Must pass without modification.
92
- </context>
93
-
94
- ## Tasks
95
-
96
- ### Task 1: Extract EntryParser module
97
-
98
- - **name:** extract-entry-parser
99
- - **files:**
100
- - `lib/source_monitor/items/item_creator/entry_parser.rb` (new)
101
- - `lib/source_monitor/items/item_creator.rb`
102
- - **action:** Create `lib/source_monitor/items/item_creator/entry_parser.rb` containing a `SourceMonitor::Items::ItemCreator::EntryParser` class. Move these methods from item_creator.rb into the new class:
103
- - `extract_guid` -- entry GUID extraction with JSON/Atom fallbacks
104
- - `extract_url` -- URL extraction with canonical/alternate link resolution
105
- - `extract_summary` -- summary text extraction
106
- - `extract_content` -- content extraction from multiple methods
107
- - `extract_timestamp` -- published_at extraction
108
- - `extract_updated_timestamp` -- updated_at extraction
109
- - `extract_author` -- single author extraction
110
- - `extract_authors` -- multi-author extraction with JSON parsing
111
- - `extract_categories` -- category extraction
112
- - `extract_tags` -- tag extraction
113
- - `extract_keywords` -- keyword extraction with separator splitting
114
- - `extract_enclosures` -- enclosure/attachment extraction
115
- - `extract_media_thumbnail_url` -- media thumbnail extraction
116
- - `extract_media_content` -- media content metadata extraction
117
- - `extract_language` -- language detection
118
- - `extract_copyright` -- copyright extraction
119
- - `extract_comments_url` -- comments link extraction
120
- - `extract_comments_count` -- comments count extraction
121
- - `extract_metadata` -- raw metadata extraction
122
- - `generate_fingerprint` -- content fingerprint generation
123
- - Utility methods: `string_or_nil`, `sanitize_string_array`, `split_keywords`, `safe_integer`, `json_entry?`, `atom_entry?`, `normalize_metadata`
124
-
125
- The EntryParser constructor takes `source:` and `entry:` (same as ItemCreator). It exposes a single public method `parse` that returns a hash of all extracted attributes (what `build_attributes` currently assembles). Add `require_relative "item_creator/entry_parser"` at the top of item_creator.rb. In ItemCreator, create an `entry_parser` lazy accessor and delegate the field extraction to it.
126
- - **verify:** `ruby -c lib/source_monitor/items/item_creator/entry_parser.rb` exits 0 AND `bin/rails test test/lib/source_monitor/items/item_creator_test.rb` exits 0 with zero failures
127
- - **done:** EntryParser extracted with all field extraction methods. Tests pass unchanged.
128
-
129
- ### Task 2: Extract ContentExtractor module
130
-
131
- - **name:** extract-content-extractor
132
- - **files:**
133
- - `lib/source_monitor/items/item_creator/content_extractor.rb` (new)
134
- - `lib/source_monitor/items/item_creator.rb`
135
- - **action:** Create `lib/source_monitor/items/item_creator/content_extractor.rb` containing a `SourceMonitor::Items::ItemCreator::ContentExtractor` class. Move these methods:
136
- - `process_feed_content` -- orchestrates content processing through readability
137
- - `should_process_feed_content?` -- determines if content should be processed
138
- - `feed_content_parser_class` -- resolves the parser class
139
- - `wrap_content_for_readability` -- wraps raw content with HTML structure for parsing
140
- - `default_feed_readability_options` -- default options for readability
141
- - `build_feed_content_metadata` -- builds metadata about processing results
142
- - `html_fragment?` -- checks if content is HTML
143
- - `deep_copy` -- deep copies complex values
144
-
145
- The ContentExtractor constructor takes `source:`. It exposes `process_feed_content(raw_content, title:)` as the primary public method. Add `require_relative "item_creator/content_extractor"` at the top of item_creator.rb. In ItemCreator, create a `content_extractor` lazy accessor. The EntryParser from Task 1 should call `content_extractor.process_feed_content(...)` instead of the local method -- wire this through the constructor or pass as a dependency.
146
- - **verify:** `ruby -c lib/source_monitor/items/item_creator/content_extractor.rb` exits 0 AND `bin/rails test test/lib/source_monitor/items/item_creator_test.rb` exits 0
147
- - **done:** ContentExtractor extracted. Feed content processing isolated. Tests pass unchanged.
148
-
149
- ### Task 3: Slim main ItemCreator and wire modules
150
-
151
- - **name:** slim-item-creator-and-wire
152
- - **files:**
153
- - `lib/source_monitor/items/item_creator.rb`
154
- - **action:** After Tasks 1-2, the main item_creator.rb should contain:
155
- - Require statements for 2 sub-modules
156
- - Existing requires (digest, json, cgi, etc.)
157
- - Result struct definition
158
- - Constants (FINGERPRINT_SEPARATOR, CONTENT_METHODS, TIMESTAMP_METHODS, etc.)
159
- - Constructor and `self.call`
160
- - `call` method (find or create)
161
- - `existing_item_for`, `find_item_by_guid`, `find_item_by_fingerprint`
162
- - `instrument_duplicate`, `update_existing_item`, `create_new_item`
163
- - `handle_concurrent_duplicate`, `find_conflicting_item`, `apply_attributes`
164
- - `build_attributes` (now delegates to entry_parser.parse)
165
- - Lazy accessor methods for entry_parser and content_extractor
166
-
167
- Clean up any dead code, orphaned requires, or duplicated constants. Ensure the main file is under 300 lines. Run RuboCop on all modified/new files.
168
- - **verify:** `wc -l lib/source_monitor/items/item_creator.rb` shows fewer than 300 lines AND `bin/rubocop lib/source_monitor/items/item_creator.rb lib/source_monitor/items/item_creator/` exits 0 AND `bin/rails test test/lib/source_monitor/items/item_creator_test.rb` exits 0
169
- - **done:** ItemCreator main file under 300 lines. All sub-modules wired. RuboCop clean.
170
-
171
- ### Task 4: Full test suite regression check
172
-
173
- - **name:** full-regression-check
174
- - **files:** (no new modifications -- verification only)
175
- - **action:** Run the complete test suite to verify no regressions from the extraction. Check that: (a) all 760+ tests pass, (b) no new RuboCop violations, (c) ItemCreator public API (`ItemCreator.call(source:, entry:)` returning `Result` struct) works identically to before the extraction. Verify by inspecting any tests that use ItemCreator in other test files (e.g., feed_fetcher_test.rb, import_opml_job tests) to confirm they still pass.
176
- - **verify:** `bin/rails test` exits 0 with 760+ runs and 0 failures AND `bin/rubocop -f simple` shows `no offenses detected`
177
- - **done:** Full suite passes. Zero RuboCop violations. No regressions from extraction.
178
-
179
- ## Verification
180
-
181
- 1. `wc -l lib/source_monitor/items/item_creator.rb` shows fewer than 300 lines
182
- 2. `wc -l lib/source_monitor/items/item_creator/entry_parser.rb lib/source_monitor/items/item_creator/content_extractor.rb` shows both exist
183
- 3. `bin/rails test test/lib/source_monitor/items/item_creator_test.rb` exits 0 with zero failures
184
- 4. `bin/rails test` exits 0 with 760+ runs and 0 failures
185
- 5. `bin/rubocop lib/source_monitor/items/` exits 0
186
-
187
- ## Success Criteria
188
-
189
- - [ ] ItemCreator main file under 300 lines
190
- - [ ] Two sub-modules created: entry_parser.rb, content_extractor.rb
191
- - [ ] Public API unchanged -- ItemCreator.call(source:, entry:) returns Result struct
192
- - [ ] All existing tests pass without modification
193
- - [ ] Full test suite passes (760+ runs, 0 failures)
194
- - [ ] RuboCop passes on all modified/new files
195
- - [ ] No file in app/ or lib/ exceeds 300 lines (extends Phase 3 success criterion)
@@ -1,79 +0,0 @@
1
- ---
2
- phase: 4
3
- plan: 3
4
- title: final-verification
5
- status: complete
6
- ---
7
-
8
- # Plan 03 Summary: final-verification
9
-
10
- ## What Was Done
11
-
12
- 1. **Regenerated coverage baseline** -- Coverage baseline reduced from 2117 to 510 uncovered lines (75.9% reduction, far exceeding the 60% target of 847).
13
-
14
- 2. **Fixed test isolation** -- Scoped test queries to specific source/item to prevent cross-test contamination from parallel test state leakage. Affected files: log_cleanup_job_test.rb, paginator_test.rb, item_test.rb, scrape_log_test.rb.
15
-
16
- 3. **Fixed coverage test infrastructure** -- Updated test_helper.rb to use threads with 1 worker for coverage runs (prevents SimpleCov data loss). Removed `refuse_coverage_drop :line` that was blocking coverage regeneration.
17
-
18
- 4. **Fixed remaining RuboCop violations** -- Autocorrected 22 `Layout/SpaceInsideArrayLiteralBrackets` offenses in Phase 2 configuration test files plus 1 `Layout/TrailingEmptyLines` in a generated temp file.
19
-
20
- 5. **Extracted modules to bring all files under 300 lines:**
21
- - EntryParser (308->294): MediaExtraction module extracted
22
- - Queries (356->163): StatsQuery and RecentActivityQuery extracted
23
- - ApplicationHelper (346->236): TableSortHelper and HealthBadgeHelper extracted
24
- - Added test/lib/tmp/ to .rubocop.yml exclusions
25
-
26
- 6. **CI-equivalent verification passed:**
27
- - `bin/rubocop -f simple`: 372 files inspected, no offenses detected
28
- - `bin/brakeman --no-pager -q`: 0 warnings
29
- - `bin/rails test`: 841 runs, 2776 assertions, 0 failures, 0 errors
30
- - No file in app/ or lib/ exceeds 300 lines (max: 294)
31
- - All models and controllers have frozen_string_literal: true
32
-
33
- 7. **Conventions spot-check** -- All core models use ModelExtensions.register (ImportHistory/ImportSession intentionally excluded -- not in MODEL_KEYS). Concerns use ActiveSupport::Concern, jobs inherit from ApplicationJob, no commented-out code. One documented TODO in items_controller.rb. Struct keyword_init not needed (Ruby 4.0 default).
34
-
35
- ## Files Modified
36
-
37
- - `config/coverage_baseline.json` -- Regenerated (510 uncovered lines)
38
- - `test/test_helper.rb` -- Fixed parallel/coverage interaction
39
- - `lib/source_monitor.rb` -- Added missing Scrapers::Fetchers autoload
40
- - `test/jobs/source_monitor/log_cleanup_job_test.rb` -- Test isolation fix
41
- - `test/lib/source_monitor/pagination/paginator_test.rb` -- Test isolation fix
42
- - `test/models/source_monitor/item_test.rb` -- Test isolation fix
43
- - `test/models/source_monitor/scrape_log_test.rb` -- Test isolation fix
44
- - `test/lib/source_monitor/configuration/*.rb` (6 files) -- RuboCop fixes
45
- - `.rubocop.yml` -- Added test/lib/tmp/ exclusion
46
- - `lib/source_monitor/items/item_creator/entry_parser.rb` -- Extracted MediaExtraction
47
- - `lib/source_monitor/items/item_creator/entry_parser/media_extraction.rb` -- New file
48
- - `lib/source_monitor/dashboard/queries.rb` -- Extracted StatsQuery/RecentActivityQuery
49
- - `lib/source_monitor/dashboard/queries/stats_query.rb` -- New file
50
- - `lib/source_monitor/dashboard/queries/recent_activity_query.rb` -- New file
51
- - `app/helpers/source_monitor/application_helper.rb` -- Extracted TableSort/HealthBadge
52
- - `app/helpers/source_monitor/table_sort_helper.rb` -- New file
53
- - `app/helpers/source_monitor/health_badge_helper.rb` -- New file
54
-
55
- ## Test Results
56
-
57
- - 841 runs, 2776 assertions, 0 failures, 0 errors
58
- - 372 files inspected, 0 RuboCop offenses
59
- - 0 Brakeman warnings
60
- - Coverage: 86.97% line, 58.84% branch
61
- - Uncovered lines: 510 (75.9% reduction from 2117)
62
- - Max file size: 294 lines (entry_parser.rb)
63
-
64
- ## Success Criteria
65
-
66
- - [x] Coverage baseline regenerated: 510 lines (75.9% reduction, target was 60%)
67
- - [x] Zero RuboCop violations
68
- - [x] Zero Brakeman warnings
69
- - [x] All 841 tests pass with 0 failures
70
- - [x] No file in app/ or lib/ exceeds 300 lines
71
- - [x] All conventions verified in final spot-check
72
- - [x] Phase 4 complete -- all ROADMAP success criteria met
73
-
74
- ## Notes
75
-
76
- - ImportHistory and ImportSession intentionally excluded from ModelExtensions.register (not in MODEL_KEYS -- they're import workflow models, not core domain models).
77
- - Ruby 4.0.1 Struct accepts keyword args by default; keyword_init: true is redundant.
78
- - One documented TODO in items_controller.rb:39 for future CRUD extraction.
79
- - Transient PG deadlocks in Solid Queue test teardown occur intermittently -- pre-existing, unrelated to Phase 4 changes.
@@ -1,130 +0,0 @@
1
- ---
2
- phase: 4
3
- plan: 3
4
- title: final-verification
5
- wave: 2
6
- depends_on:
7
- - "plan-01 (conventions-audit)"
8
- - "plan-02 (item-creator-extraction)"
9
- skills_used: []
10
- cross_phase_deps:
11
- - "Phase 1 -- coverage baseline established at 2117 uncovered lines across 105 files"
12
- - "Phase 2 -- critical path test coverage added (500+ uncovered lines expected to be covered)"
13
- - "Phase 3 -- large file refactoring (new files created, some lines shifted between files)"
14
- must_haves:
15
- truths:
16
- - "Running `bin/rails test` exits 0 with 760+ runs and 0 failures"
17
- - "Running `bin/rubocop -f simple` shows `no offenses detected`"
18
- - "Running `bin/brakeman --no-pager -q` exits 0 with zero warnings"
19
- - "The regenerated `config/coverage_baseline.json` has at most 847 uncovered lines (60% reduction from 2117)"
20
- - "No file in app/ or lib/ exceeds 300 lines"
21
- artifacts:
22
- - "config/coverage_baseline.json -- regenerated with current coverage data"
23
- key_links:
24
- - "Phase 4 success criterion #1 -- all models, controllers, service objects follow conventions"
25
- - "Phase 4 success criterion #2 -- zero RuboCop violations"
26
- - "Phase 4 success criterion #3 -- coverage baseline at least 60% smaller than original"
27
- - "Phase 4 success criterion #4 -- CI pipeline fully green"
28
- ---
29
-
30
- # Plan 03: final-verification
31
-
32
- ## Objective
33
-
34
- Regenerate the coverage baseline to reflect all test improvements from Phases 2-4, verify the 60% reduction target is met, run full CI-equivalent checks (tests, RuboCop, Brakeman), and confirm no file exceeds 300 lines. This plan is the final gate before Phase 4 (and the entire VBW roadmap) can be marked complete.
35
-
36
- ## Context
37
-
38
- <context>
39
- @config/coverage_baseline.json -- Currently shows 2117 uncovered lines across 105 files. This baseline has NOT been regenerated since Phase 1. Phases 2 and 3 added significant test coverage (Phase 2 targeted ~630 lines directly plus indirect coverage, Phase 3 refactored files which shifted coverage around). The actual current uncovered count should be significantly lower.
40
-
41
- @bin/update-coverage-baseline -- Script that regenerates the baseline from SimpleCov results. Requires running the test suite with coverage first (`COVERAGE=1 bin/rails test` or `bin/test-coverage`).
42
-
43
- @bin/check-diff-coverage -- CI script that checks diff coverage against the baseline.
44
-
45
- @AGENTS.md -- Documents the workflow: "refresh config/coverage_baseline.json by running bin/test-coverage followed by bin/update-coverage-baseline"
46
-
47
- @test/test_helper.rb -- Coverage is enabled when `CI` or `COVERAGE` env var is set. Uses SimpleCov with branch coverage.
48
-
49
- **60% reduction target:** The original baseline has 2117 uncovered lines. A 60% reduction means the new baseline must have at most 847 uncovered lines (2117 * 0.4 = 847). Phase 2 directly targeted ~630 lines in top files, and indirect coverage should bring more. If the target is not met, this task must identify the gap and either add targeted tests or document which files still need coverage.
50
-
51
- **CI-equivalent checks:**
52
- - `bin/rubocop -f github` (lint job)
53
- - `bin/brakeman --no-pager` (security job)
54
- - `bin/rails test` (test job)
55
- - diff coverage check (test job)
56
- </context>
57
-
58
- ## Tasks
59
-
60
- ### Task 1: Regenerate coverage baseline
61
-
62
- - **name:** regenerate-coverage-baseline
63
- - **files:**
64
- - `config/coverage_baseline.json`
65
- - **action:** Run the full test suite with coverage enabled: `COVERAGE=1 bin/rails test`. Then regenerate the baseline: `bin/update-coverage-baseline`. Compare the new uncovered line count to the original 2117. The target is at most 847 uncovered lines (60% reduction). If the target is met, commit the regenerated baseline. If not, document the gap and identify which files still have the most uncovered lines for targeted fix in Task 2.
66
- - **verify:** `ruby -rjson -e 'data = JSON.parse(File.read("config/coverage_baseline.json")); total = data.values.map(&:size).sum; puts "Uncovered: #{total}"; exit(total <= 847 ? 0 : 1)'` exits 0
67
- - **done:** Coverage baseline regenerated. Uncovered line count documented.
68
-
69
- ### Task 2: Address coverage gap if target not met
70
-
71
- - **name:** address-coverage-gap
72
- - **files:**
73
- - Test files as needed (determined by Task 1 gap analysis)
74
- - `config/coverage_baseline.json` (re-regenerate after adding tests)
75
- - **action:** If Task 1 shows the 60% reduction target is NOT met, analyze the regenerated baseline to find the largest remaining gaps. Add targeted tests for the top uncovered files until the 847-line target is met. Focus on files with the most uncovered lines that are NOT in the `:nocov:` exclusion zones. After adding tests, re-run `COVERAGE=1 bin/rails test` and `bin/update-coverage-baseline` to verify. If the target IS already met from Task 1, this task is a no-op -- simply verify and move on.
76
- - **verify:** `ruby -rjson -e 'data = JSON.parse(File.read("config/coverage_baseline.json")); total = data.values.map(&:size).sum; puts "Uncovered: #{total}"; exit(total <= 847 ? 0 : 1)'` exits 0
77
- - **done:** Coverage baseline meets 60% reduction target (at most 847 uncovered lines).
78
-
79
- ### Task 3: Run full CI-equivalent verification
80
-
81
- - **name:** full-ci-verification
82
- - **files:** (no modifications -- verification only)
83
- - **action:** Run all CI-equivalent checks in sequence:
84
- 1. `bin/rubocop -f simple` -- must show `no offenses detected`
85
- 2. `bin/brakeman --no-pager -q` -- must exit 0 with zero warnings
86
- 3. `bin/rails test` -- must exit 0 with 760+ runs and 0 failures
87
- 4. Verify no file in app/ or lib/ exceeds 300 lines: `find app lib -name '*.rb' -exec wc -l {} + | sort -rn | awk '$1 > 300 && $2 != "total" {print; found=1} END {exit found ? 1 : 0}'`
88
- 5. Verify all models have `frozen_string_literal: true`: `grep -rL 'frozen_string_literal: true' app/models/source_monitor/*.rb` returns empty
89
- 6. Verify all controllers have `frozen_string_literal: true`: `grep -rL 'frozen_string_literal: true' app/controllers/source_monitor/*.rb` returns empty
90
-
91
- Document any failures and fix them before marking this task done.
92
- - **verify:** All 6 checks above pass
93
- - **done:** All CI-equivalent checks pass. Codebase fully clean.
94
-
95
- ### Task 4: Final conventions spot-check
96
-
97
- - **name:** final-conventions-spot-check
98
- - **files:** (read-only audit, fix only if issues found)
99
- - **action:** Do a final walkthrough of all models, controllers, and service objects checking:
100
- - All models use `ModelExtensions.register(self, :key)` (except ApplicationRecord)
101
- - All models have appropriate validations for their associations
102
- - All service objects follow the `initialize`/`call` pattern or `self.call` class method
103
- - All jobs inherit from ApplicationJob and use `source_monitor_queue`
104
- - All concerns use `extend ActiveSupport::Concern` and `included do...end`
105
- - No commented-out code blocks remain
106
- - No TODO/FIXME/HACK comments without associated tracking
107
- - All Struct definitions use `keyword_init: true`
108
-
109
- Fix any issues found. This should be a light pass since most conventions were already followed.
110
- - **verify:** `bin/rails test` exits 0 AND `bin/rubocop -f simple` shows `no offenses detected`
111
- - **done:** All conventions verified. Codebase passes final quality gate.
112
-
113
- ## Verification
114
-
115
- 1. `bin/rails test` exits 0 with 760+ runs and 0 failures
116
- 2. `bin/rubocop -f simple` shows `no offenses detected`
117
- 3. `bin/brakeman --no-pager -q` exits 0
118
- 4. Coverage baseline has at most 847 uncovered lines
119
- 5. No Ruby file in app/ or lib/ exceeds 300 lines
120
- 6. All frozen_string_literal pragmas present
121
-
122
- ## Success Criteria
123
-
124
- - [ ] Coverage baseline regenerated and at most 847 uncovered lines (60% reduction from 2117)
125
- - [ ] Zero RuboCop violations
126
- - [ ] Zero Brakeman warnings
127
- - [ ] All 760+ tests pass with 0 failures
128
- - [ ] No file in app/ or lib/ exceeds 300 lines
129
- - [ ] All conventions verified in final spot-check
130
- - [ ] Phase 4 complete -- all ROADMAP success criteria met
@@ -1,72 +0,0 @@
1
- <!-- VBW REQUIREMENTS TEMPLATE (ARTF-06) -- Structured requirements with traceability -->
2
- <!-- Created by Architect agent during /vbw scope -->
3
-
4
- # SourceMonitor Requirements
5
-
6
- Defined: 2026-02-09
7
- Core value: Drop-in Rails engine for feed monitoring, content scraping, and operational dashboards.
8
-
9
- ## v1 Requirements
10
-
11
- ### Test Coverage
12
-
13
- - [ ] **REQ-01**: Close coverage gaps in `FeedFetcher` -- add tests for uncovered branches in the fetch pipeline
14
- - [ ] **REQ-02**: Close coverage gaps in `ItemCreator` -- add tests for item creation edge cases
15
- - [ ] **REQ-03**: Close coverage gaps in `Configuration` -- test nested settings classes and edge cases
16
- - [ ] **REQ-04**: Close coverage gaps in `Dashboard::Queries` -- test dashboard query logic
17
- - [ ] **REQ-05**: Close coverage gaps in `Broadcaster` -- test realtime broadcasting logic
18
- - [ ] **REQ-06**: Close coverage gaps in `BulkSourceScraper` -- test bulk scraping workflows
19
- - [ ] **REQ-07**: Close coverage gaps in `SourcesIndexMetrics` -- test analytics calculations
20
-
21
- ### Refactoring
22
-
23
- - [ ] **REQ-08**: Extract `FeedFetcher` (627 lines) into focused single-responsibility classes
24
- - [ ] **REQ-09**: Extract `Configuration` (655 lines) nested settings classes into separate files
25
- - [ ] **REQ-10**: Extract `ImportSessionsController` (792 lines) wizard steps into step-specific concerns or service objects
26
- - [ ] **REQ-11**: Fix `LogEntry` hard-coded table name to use configurable prefix system
27
- - [ ] **REQ-12**: Replace eager 102+ require statements in `lib/source_monitor.rb` with autoloading
28
-
29
- ### Code Quality
30
-
31
- - [ ] **REQ-13**: Ensure frozen_string_literal is consistent across all Ruby files
32
- - [ ] **REQ-14**: Audit and fix any RuboCop violations against omakase ruleset
33
- - [ ] **REQ-15**: Ensure all models, controllers, and service objects follow Rails conventions
34
-
35
- ### Generator Enhancements
36
-
37
- - [ ] **REQ-16**: Install generator patches `Procfile.dev` with a `jobs:` entry for Solid Queue
38
- - [ ] **REQ-17**: Install generator patches queue config dispatcher with `recurring_schedule: config/recurring.yml`
39
- - [ ] **REQ-18**: Guided workflow (`Setup::Workflow`) integrates both new generator steps
40
- - [ ] **REQ-19**: `RecurringScheduleVerifier` checks that recurring tasks are registered with Solid Queue
41
- - [ ] **REQ-20**: `SolidQueueVerifier` remediation suggests `Procfile.dev` when workers not detected
42
- - [ ] **REQ-21**: Skills and documentation updated to reflect automated Procfile.dev and recurring_schedule setup
43
-
44
- ### Dashboard UX
45
-
46
- - [ ] **REQ-22**: Fetch logs show source URL for both success and failure entries on the dashboard
47
- - [ ] **REQ-23**: Dashboard links to sources and items are clickable and open in a new tab
48
-
49
- ### Active Storage Image Downloads
50
-
51
- - [ ] **REQ-24**: Configurable option to download inline images from items to Active Storage instead of loading from source
52
-
53
- ### Feed Compatibility
54
-
55
- - [ ] **REQ-25**: Investigate and fix failing fetch for Netflix Tech Blog feed (https://netflixtechblog.com/feed)
56
-
57
- ## v2 Requirements
58
-
59
- - [ ] **REQ-XX**: Improve optional dependency loading with clear error messages
60
- - [ ] **REQ-XX**: Add database index verification tooling
61
- - [ ] **REQ-XX**: Document health check endpoint response format
62
-
63
- ## Out of Scope
64
-
65
- | Item | Reason |
66
- |------|--------|
67
- | Multi-database support (MySQL/SQLite) | PostgreSQL-only simplifies development |
68
- | Built-in authentication | Host app responsibility |
69
-
70
- ## Traceability
71
-
72
- Requirement-to-phase mapping is tracked in ROADMAP.md.
@@ -1,125 +0,0 @@
1
- <!-- VBW ROADMAP -- Phase decomposition with requirement mapping -->
2
- <!-- Created during /vbw scope -->
3
-
4
- # SourceMonitor Generator Enhancements Roadmap
5
-
6
- **Milestone:** generator-enhancements
7
- **Goal:** Make the install generator and verification suite catch the two most common host-app setup failures: missing Procfile.dev jobs entry and missing recurring_schedule dispatcher wiring.
8
-
9
- ## Phases
10
-
11
- 1. [x] ~~Phase 0: Documentation Gaps~~ (shipped via quick fix ea788ea)
12
- 2. [x] Phase 1: Install Generator Steps (Procfile.dev + Queue Config)
13
- 3. [x] Phase 2: Recurring Schedule Verifier
14
- 4. [x] Phase 3: Skills & Documentation Alignment
15
- 5. [x] Phase 4: Dashboard UX Improvements
16
- 6. [x] Phase 5: Active Storage Image Downloads
17
- 7. [x] Phase 6: Netflix Feed Investigation
18
-
19
- ## Phase Details
20
-
21
- ### Phase 1: Install Generator Steps
22
-
23
- **Goal:** Add two new idempotent steps to the install generator: (a) patch `Procfile.dev` with a `jobs:` entry for Solid Queue, and (b) patch the queue config dispatcher with `recurring_schedule: config/recurring.yml`.
24
-
25
- **Requirements:** REQ-16, REQ-17, REQ-18
26
-
27
- **Success Criteria:**
28
- - `bin/rails generate source_monitor:install` patches Procfile.dev when present (idempotent, skip if entry exists)
29
- - `bin/rails generate source_monitor:install` patches queue.yml dispatcher with recurring_schedule (idempotent)
30
- - Both steps wired into `Setup::Workflow` for the guided installer
31
- - Generator tests cover: fresh file, existing file with entry, existing file without entry, missing file
32
- - `bin/rails test` passes, RuboCop clean
33
-
34
- ### Phase 2: Recurring Schedule Verifier
35
-
36
- **Goal:** Add a `RecurringScheduleVerifier` to the verification suite that checks whether recurring tasks are actually registered with Solid Queue dispatchers, and enhance the existing `SolidQueueVerifier` to suggest Procfile.dev when workers aren't detected.
37
-
38
- **Requirements:** REQ-19, REQ-20
39
-
40
- **Success Criteria:**
41
- - `bin/source_monitor verify` checks that recurring tasks are registered (not just that workers heartbeat)
42
- - Warning when no recurring tasks found with actionable remediation message
43
- - SolidQueueVerifier remediation mentions Procfile.dev for `bin/dev` users
44
- - Verifier tests with mocked Solid Queue state
45
- - `bin/rails test` passes, RuboCop clean
46
-
47
- ### Phase 3: Skills & Documentation Alignment
48
-
49
- **Goal:** Update all `sm-*` skills and docs to reflect that the generator now automatically handles Procfile.dev and recurring_schedule wiring. Remove manual steps that are now automated.
50
-
51
- **Requirements:** REQ-21
52
-
53
- **Success Criteria:**
54
- - sm-host-setup skill reflects new generator capabilities (auto-patching, not manual steps)
55
- - sm-configure skill references the automatic recurring_schedule wiring
56
- - docs/setup.md updated to note the generator handles both automatically
57
- - docs/troubleshooting.md updated with improved diagnostics
58
- - setup-checklist.md reflects automation (checked by default, not manual)
59
-
60
- ### Phase 4: Dashboard UX Improvements
61
-
62
- **Goal:** Show source URLs in fetch log entries for both successes and failures on the dashboard, and make links to sources and items clickable (opening in a new tab).
63
-
64
- **Requirements:** REQ-22, REQ-23
65
-
66
- **Success Criteria:**
67
- - Fetch log entries on the dashboard display the source URL alongside the existing summary
68
- - Both success and failure fetch logs show the URL
69
- - Source names and item titles are clickable links that open in a new tab
70
- - Existing dashboard layout is preserved
71
- - `bin/rails test` passes, RuboCop clean
72
-
73
- ### Phase 5: Active Storage Image Downloads
74
-
75
- **Goal:** Add a configurable option to download inline images from feed items to Active Storage instead of loading them directly from the source URL. This prevents broken images when sources go offline and improves page load performance.
76
-
77
- **Requirements:** REQ-24
78
-
79
- **Success Criteria:**
80
- - New configuration option (`config.images.download_to_active_storage` or similar) defaults to `false`
81
- - When enabled, inline images in item content are detected and downloaded to Active Storage
82
- - Original image URLs are replaced with Active Storage URLs in the stored content
83
- - Images that fail to download gracefully fall back to original URLs
84
- - Configuration is documented in sm-configure skill
85
- - `bin/rails test` passes, RuboCop clean
86
-
87
- ### Phase 6: Netflix Feed Investigation
88
-
89
- **Goal:** Investigate and fix the failing fetch for `https://netflixtechblog.com/feed`. Determine whether the issue is in the feed parser, HTTP client configuration, or content format, and apply appropriate fixes.
90
-
91
- **Requirements:** REQ-25
92
-
93
- **Success Criteria:**
94
- - Root cause identified and documented
95
- - Fix applied (parser, HTTP client, or configuration change)
96
- - Netflix Tech Blog feed fetches successfully
97
- - No regressions in other feed types
98
- - `bin/rails test` passes, RuboCop clean
99
-
100
- ## Progress
101
-
102
- | Phase | Status | Plans |
103
- |-------|--------|-------|
104
- | 0 | Complete | - |
105
- | 1 | Complete | PLAN-01 (5 tasks, 4 commits) |
106
- | 2 | Complete | PLAN-01 (5 tasks, 1 commit) |
107
- | 3 | Complete | PLAN-01 (5 tasks, 1 commit) |
108
- | 4 | Complete | PLAN-01 (5 tasks, 5 commits) |
109
- | 5 | Complete | PLAN-01 (4 tasks, 5 commits) + PLAN-02 (4 tasks, 4 commits) |
110
- | 6 | Complete | PLAN-01 (5 tasks, 5 commits) |
111
-
112
- ## Requirement Mapping
113
-
114
- | REQ | Phase | Description |
115
- |-----|-------|-------------|
116
- | REQ-16 | 1 | Generator patches Procfile.dev with jobs: entry |
117
- | REQ-17 | 1 | Generator patches queue config with recurring_schedule |
118
- | REQ-18 | 1 | Guided workflow integrates both new steps |
119
- | REQ-19 | 2 | RecurringScheduleVerifier checks recurring task registration |
120
- | REQ-20 | 2 | SolidQueueVerifier remediation mentions Procfile.dev |
121
- | REQ-21 | 3 | Skills and docs reflect automated setup |
122
- | REQ-22 | 4 | Fetch logs show source URL on dashboard |
123
- | REQ-23 | 4 | Dashboard links clickable in new tab |
124
- | REQ-24 | 5 | Download inline images to Active Storage |
125
- | REQ-25 | 6 | Fix Netflix Tech Blog feed fetch |
@@ -1,40 +0,0 @@
1
- # Shipped: generator-enhancements
2
-
3
- **Shipped:** 2026-02-12
4
- **Tag:** milestone/generator-enhancements
5
- **Release:** v0.4.0
6
-
7
- ## Summary
8
-
9
- Made the install generator and verification suite catch the two most common host-app setup failures: missing Procfile.dev jobs entry and missing recurring_schedule dispatcher wiring. Also added dashboard UX improvements, Active Storage image downloads, and SSL certificate store configuration.
10
-
11
- ## Metrics
12
-
13
- | Metric | Value |
14
- |--------|-------|
15
- | Phases | 7 (Phase 0-6) |
16
- | Plans completed | 7 |
17
- | Tasks completed | 34 |
18
- | Commits | ~26 |
19
- | Tests | 973 (up from 841) |
20
- | New requirements satisfied | 10 (REQ-16 through REQ-25) |
21
-
22
- ## Phases
23
-
24
- 1. Phase 0: Documentation Gaps (quick fix)
25
- 2. Phase 1: Install Generator Steps (Procfile.dev + Queue Config)
26
- 3. Phase 2: Recurring Schedule Verifier
27
- 4. Phase 3: Skills & Documentation Alignment
28
- 5. Phase 4: Dashboard UX Improvements
29
- 6. Phase 5: Active Storage Image Downloads
30
- 7. Phase 6: Netflix Feed Investigation (SSL cert store fix)
31
-
32
- ## Key Decisions
33
-
34
- - Docs-first approach: shipped doc fixes before code changes
35
- - Always create/patch Procfile.dev for maximum hand-holding
36
- - Target queue.yml only (Rails 8 default)
37
- - External links open new tab with visual indicator icon
38
- - Fetch log URL display: domain for RSS, item URL for scrapes
39
- - Image downloads via background job to ItemContent (opt-in)
40
- - SSL fix: general cert store config, not Netflix-specific