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.
- checksums.yaml +4 -4
- data/.claude/commands/release.md +45 -22
- data/.claude/skills/sm-configure/SKILL.md +10 -1
- data/.claude/skills/sm-configure/reference/configuration-reference.md +44 -0
- data/.claude/skills/sm-host-setup/reference/initializer-template.md +17 -0
- data/.claude/skills/sm-host-setup/reference/setup-checklist.md +2 -0
- data/.claude/skills/sm-job/reference/job-conventions.md +26 -0
- data/.claude/skills/sm-upgrade/reference/version-history.md +22 -0
- data/.gitignore +10 -0
- data/AGENTS.md +1 -1
- data/CHANGELOG.md +56 -0
- data/CLAUDE.md +11 -5
- data/Gemfile.lock +1 -1
- data/README.md +6 -4
- data/VERSION +1 -1
- data/app/assets/builds/source_monitor/application.css +43 -0
- data/app/assets/builds/source_monitor/application.js +127 -0
- data/app/assets/builds/source_monitor/application.js.map +3 -3
- data/app/assets/javascripts/source_monitor/application.js +2 -0
- data/app/assets/javascripts/source_monitor/controllers/notification_container_controller.js +138 -0
- data/app/assets/javascripts/source_monitor/controllers/notification_controller.js +11 -0
- data/app/controllers/source_monitor/source_favicon_fetches_controller.rb +38 -0
- data/app/controllers/source_monitor/sources_controller.rb +11 -0
- data/app/helpers/source_monitor/application_helper.rb +51 -0
- data/app/jobs/source_monitor/favicon_fetch_job.rb +71 -0
- data/app/jobs/source_monitor/import_opml_job.rb +9 -0
- data/app/jobs/source_monitor/source_health_check_job.rb +10 -0
- data/app/models/source_monitor/source.rb +2 -0
- data/app/views/layouts/source_monitor/application.html.erb +23 -2
- data/app/views/source_monitor/shared/_toast.html.erb +1 -0
- data/app/views/source_monitor/sources/_details.html.erb +34 -5
- data/app/views/source_monitor/sources/_row.html.erb +11 -6
- data/config/routes.rb +1 -0
- data/docs/configuration.md +1 -1
- data/docs/upgrade.md +22 -0
- data/lib/generators/source_monitor/install/templates/source_monitor.rb.tt +15 -1
- data/lib/source_monitor/configuration/favicons_settings.rb +42 -0
- data/lib/source_monitor/configuration/http_settings.rb +1 -1
- data/lib/source_monitor/configuration/scraping_settings.rb +1 -1
- data/lib/source_monitor/configuration.rb +3 -1
- data/lib/source_monitor/favicons/discoverer.rb +196 -0
- data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +21 -0
- data/lib/source_monitor/fetching/feed_fetcher.rb +1 -0
- data/lib/source_monitor/http.rb +5 -3
- data/lib/source_monitor/version.rb +1 -1
- data/lib/source_monitor.rb +4 -0
- data/lib/tasks/test_fast.rake +11 -0
- data/source_monitor.gemspec +1 -1
- metadata +7 -93
- data/.vbw-planning/PROJECT.md +0 -51
- data/.vbw-planning/ROADMAP.md +0 -32
- data/.vbw-planning/SHIPPED.md +0 -63
- data/.vbw-planning/STATE.md +0 -27
- data/.vbw-planning/codebase/ARCHITECTURE.md +0 -147
- data/.vbw-planning/codebase/CONCERNS.md +0 -99
- data/.vbw-planning/codebase/CONVENTIONS.md +0 -97
- data/.vbw-planning/codebase/DEPENDENCIES.md +0 -100
- data/.vbw-planning/codebase/INDEX.md +0 -86
- data/.vbw-planning/codebase/META.md +0 -42
- data/.vbw-planning/codebase/PATTERNS.md +0 -262
- data/.vbw-planning/codebase/STACK.md +0 -101
- data/.vbw-planning/codebase/STRUCTURE.md +0 -324
- data/.vbw-planning/codebase/TESTING.md +0 -154
- data/.vbw-planning/config.json +0 -53
- data/.vbw-planning/discovery.json +0 -26
- data/.vbw-planning/milestones/default/ROADMAP.md +0 -115
- data/.vbw-planning/milestones/default/STATE.md +0 -82
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +0 -56
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +0 -187
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +0 -64
- data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +0 -137
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +0 -67
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +0 -142
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +0 -64
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +0 -138
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +0 -85
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +0 -147
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +0 -63
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +0 -129
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +0 -74
- data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +0 -154
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +0 -303
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +0 -510
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +0 -61
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +0 -161
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +0 -66
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +0 -132
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +0 -59
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +0 -171
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +0 -56
- data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +0 -152
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +0 -33
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +0 -42
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +0 -119
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +0 -52
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +0 -195
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +0 -79
- data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +0 -130
- data/.vbw-planning/milestones/generator-enhancements/REQUIREMENTS.md +0 -72
- data/.vbw-planning/milestones/generator-enhancements/ROADMAP.md +0 -125
- data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +0 -40
- data/.vbw-planning/milestones/generator-enhancements/STATE.md +0 -43
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-CONTEXT.md +0 -33
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/01-VERIFICATION.md +0 -86
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01-SUMMARY.md +0 -61
- data/.vbw-planning/milestones/generator-enhancements/phases/01-generator-steps/PLAN-01.md +0 -380
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/02-VERIFICATION.md +0 -78
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01-SUMMARY.md +0 -46
- data/.vbw-planning/milestones/generator-enhancements/phases/02-verification/PLAN-01.md +0 -500
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/03-VERIFICATION.md +0 -89
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01-SUMMARY.md +0 -48
- data/.vbw-planning/milestones/generator-enhancements/phases/03-docs-alignment/PLAN-01.md +0 -456
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/04-VERIFICATION.md +0 -129
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -70
- data/.vbw-planning/milestones/generator-enhancements/phases/04-dashboard-ux/PLAN-01.md +0 -747
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/05-VERIFICATION.md +0 -156
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01-SUMMARY.md +0 -69
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-01.md +0 -455
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02-SUMMARY.md +0 -39
- data/.vbw-planning/milestones/generator-enhancements/phases/05-active-storage-images/PLAN-02.md +0 -488
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/06-VERIFICATION.md +0 -100
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -37
- data/.vbw-planning/milestones/generator-enhancements/phases/06-netflix-feed-fix/PLAN-01.md +0 -345
- data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +0 -80
- data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +0 -75
- data/.vbw-planning/milestones/upgrade-assurance/STATE.md +0 -29
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +0 -144
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +0 -43
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +0 -405
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +0 -27
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +0 -303
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +0 -380
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +0 -36
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +0 -652
- data/.vbw-planning/phases/01-aia-certificate-resolution/.context-dev.md +0 -17
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01-SUMMARY.md +0 -26
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-01.md +0 -71
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02-SUMMARY.md +0 -16
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-02.md +0 -56
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03-SUMMARY.md +0 -17
- data/.vbw-planning/phases/01-aia-certificate-resolution/PLAN-03.md +0 -98
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
phase: 6
|
|
3
|
-
plan: "01"
|
|
4
|
-
title: ssl-cert-store-configuration
|
|
5
|
-
type: execute
|
|
6
|
-
wave: 1
|
|
7
|
-
depends_on: []
|
|
8
|
-
cross_phase_deps: []
|
|
9
|
-
autonomous: true
|
|
10
|
-
effort_override: thorough
|
|
11
|
-
skills_used: []
|
|
12
|
-
files_modified:
|
|
13
|
-
- lib/source_monitor/configuration/http_settings.rb
|
|
14
|
-
- lib/source_monitor/http.rb
|
|
15
|
-
- test/lib/source_monitor/http_test.rb
|
|
16
|
-
- test/lib/source_monitor/fetching/feed_fetcher_test.rb
|
|
17
|
-
- test/vcr_cassettes/source_monitor/fetching/netflix_medium_rss.yml
|
|
18
|
-
must_haves:
|
|
19
|
-
truths:
|
|
20
|
-
- "Running `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/http_test.rb` exits 0 with 0 failures"
|
|
21
|
-
- "Running `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` exits 0 with 0 failures"
|
|
22
|
-
- "Running `bin/rubocop lib/source_monitor/http.rb lib/source_monitor/configuration/http_settings.rb test/lib/source_monitor/http_test.rb test/lib/source_monitor/fetching/feed_fetcher_test.rb` exits 0 with no offenses"
|
|
23
|
-
- "Running `bin/rails test` (full suite) exits 0 with 0 failures"
|
|
24
|
-
- "`grep -r 'cert_store' lib/source_monitor/http.rb` returns at least one match"
|
|
25
|
-
- "`grep -r 'ssl_ca_file' lib/source_monitor/configuration/http_settings.rb` returns at least one match"
|
|
26
|
-
- "The VCR cassette at test/vcr_cassettes/source_monitor/fetching/netflix_medium_rss.yml exists and contains 'netflixtechblog'"
|
|
27
|
-
artifacts:
|
|
28
|
-
- path: "lib/source_monitor/http.rb"
|
|
29
|
-
provides: "SSL cert store configuration on Faraday connections"
|
|
30
|
-
contains: "cert_store"
|
|
31
|
-
- path: "lib/source_monitor/configuration/http_settings.rb"
|
|
32
|
-
provides: "Configurable SSL options (ca_file, ca_path, verify)"
|
|
33
|
-
contains: "ssl_ca_file"
|
|
34
|
-
- path: "test/lib/source_monitor/http_test.rb"
|
|
35
|
-
provides: "Tests for SSL configuration on HTTP client"
|
|
36
|
-
contains: "ssl"
|
|
37
|
-
- path: "test/lib/source_monitor/fetching/feed_fetcher_test.rb"
|
|
38
|
-
provides: "Netflix Tech Blog VCR regression test"
|
|
39
|
-
contains: "netflix"
|
|
40
|
-
- path: "test/vcr_cassettes/source_monitor/fetching/netflix_medium_rss.yml"
|
|
41
|
-
provides: "Recorded VCR cassette from real Netflix Tech Blog feed"
|
|
42
|
-
contains: "netflixtechblog"
|
|
43
|
-
key_links:
|
|
44
|
-
- from: "http.rb#configure_ssl"
|
|
45
|
-
to: "REQ-25"
|
|
46
|
-
via: "Configures Faraday SSL with system cert store to fix certificate verification failures"
|
|
47
|
-
- from: "http_settings.rb#ssl_ca_file"
|
|
48
|
-
to: "REQ-25"
|
|
49
|
-
via: "Exposes configurable SSL CA file/path for environments with non-standard cert locations"
|
|
50
|
-
- from: "feed_fetcher_test.rb#netflix_regression"
|
|
51
|
-
to: "REQ-25"
|
|
52
|
-
via: "VCR cassette proves Netflix Tech Blog feed parses successfully"
|
|
53
|
-
---
|
|
54
|
-
<objective>
|
|
55
|
-
Fix SSL certificate verification failures (like Netflix Tech Blog's "unable to get local issuer certificate") by configuring the Faraday HTTP client to use a properly initialized OpenSSL cert store with system default paths. Add configurable SSL options to HTTPSettings so users can override CA file/path in non-standard environments. Record a VCR cassette from the real Netflix Tech Blog feed as a regression test. REQ-25.
|
|
56
|
-
</objective>
|
|
57
|
-
<context>
|
|
58
|
-
@lib/source_monitor/http.rb -- The HTTP client module. Creates Faraday connections via `HTTP.client`. Currently configures timeouts, retry, gzip, follow-redirects, and headers -- but does NOT configure any SSL options. Faraday's `connection.ssl` is left at defaults, which means it relies on the underlying adapter (net_http) to find CA certs. On some systems (macOS with Homebrew Ruby, Docker Alpine, custom OpenSSL builds), the compiled-in `OpenSSL::X509::DEFAULT_CERT_FILE` path may not include all intermediate certificates -- causing "certificate verify failed" for sites like Netflix Tech Blog whose chain depends on intermediates served by the TLS handshake being validated against a complete CA bundle. The fix is to explicitly set `connection.ssl.cert_store` to an `OpenSSL::X509::Store` initialized with `set_default_paths`, which loads both `DEFAULT_CERT_FILE` and `DEFAULT_CERT_DIR`. Optionally, if the user configures `ssl_ca_file` or `ssl_ca_path`, those override the default store.
|
|
59
|
-
|
|
60
|
-
@lib/source_monitor/configuration/http_settings.rb -- The settings class for HTTP configuration. Has 11 `attr_accessor` fields for timeout, retry, proxy, headers, etc. New SSL settings (`ssl_ca_file`, `ssl_ca_path`, `ssl_verify`) should be added here following the same pattern. Default: `ssl_verify = true` (never disable verification), `ssl_ca_file = nil`, `ssl_ca_path = nil` (nil means use system defaults via cert_store).
|
|
61
|
-
|
|
62
|
-
@test/lib/source_monitor/http_test.rb -- 8 existing tests for the HTTP client. Tests inspect `@connection.builder.handlers`, `@connection.options`, and `@connection.headers`. New SSL tests should inspect `@connection.ssl.cert_store`, `@connection.ssl.verify`, and optionally `@connection.ssl.ca_file` when configured.
|
|
63
|
-
|
|
64
|
-
@test/lib/source_monitor/fetching/feed_fetcher_test.rb -- Existing tests use VCR cassettes for RSS, Atom, and JSON feeds (ruby-lang.org, W3C, json_sample). The Netflix regression test should follow the same pattern: `VCR.use_cassette("source_monitor/fetching/netflix_medium_rss")` with a source pointing at `https://netflixtechblog.com/feed`.
|
|
65
|
-
|
|
66
|
-
@test/vcr_cassettes/ -- Contains 3 existing cassettes (rss_success, atom_success, json_success). The Netflix cassette should be recorded with `VCR.use_cassette(..., record: :new_episodes)` during the first test run against the real feed (with WebMock allowing the Netflix host temporarily), then committed as a fixture for CI. This requires temporarily allowing net connect to netflixtechblog.com during recording.
|
|
67
|
-
|
|
68
|
-
@lib/source_monitor/fetching/feed_fetcher.rb -- Lines 84-85 already catch `Faraday::SSLError` and wrap it as `ConnectionError`. This error path will stop triggering once SSL is properly configured, but the error handling remains as a safety net for genuinely invalid certificates.
|
|
69
|
-
|
|
70
|
-
**Root cause analysis:**
|
|
71
|
-
The Netflix Tech Blog (Medium-hosted at netflixtechblog.com, IP 52.1.173.203) serves a TLS certificate chain that requires the client to have Amazon's intermediate CA in its trust store. Ruby's compiled-in `OpenSSL::X509::DEFAULT_CERT_FILE` may point to a cert bundle that is missing this intermediate, or the system's cert directory may not be indexed. By explicitly creating an `OpenSSL::X509::Store` with `set_default_paths` and assigning it to the Faraday connection's `ssl.cert_store`, we ensure Ruby loads all available system certificates -- which on a properly maintained system includes Amazon/AWS intermediates. This is the standard, general fix for SSL verification issues in Ruby HTTP clients.
|
|
72
|
-
|
|
73
|
-
**Key design decisions:**
|
|
74
|
-
1. Use `OpenSSL::X509::Store.new.tap(&:set_default_paths)` as the default cert store -- this is the most cross-platform approach
|
|
75
|
-
2. Add `ssl_ca_file` and `ssl_ca_path` as optional overrides in HTTPSettings -- when set, they configure `connection.ssl.ca_file` / `connection.ssl.ca_path` instead of using the cert store
|
|
76
|
-
3. Keep `ssl_verify = true` as default and do NOT add a way to disable verification globally -- security-first design
|
|
77
|
-
4. The cert store is created fresh per `HTTP.client` call (Faraday connections are short-lived and not shared across threads)
|
|
78
|
-
5. For recording the VCR cassette: use a dedicated recording script or a test with `record: :new_episodes` and temporarily permit net connect
|
|
79
|
-
</context>
|
|
80
|
-
<tasks>
|
|
81
|
-
<task type="auto">
|
|
82
|
-
<name>add-ssl-settings-to-http-settings</name>
|
|
83
|
-
<files>
|
|
84
|
-
lib/source_monitor/configuration/http_settings.rb
|
|
85
|
-
</files>
|
|
86
|
-
<action>
|
|
87
|
-
Add three new `attr_accessor` fields to `HTTPSettings` for SSL configuration:
|
|
88
|
-
|
|
89
|
-
1. `ssl_ca_file` -- Path to a CA certificate file (PEM format). When set, Faraday uses this instead of the default cert store. Default: `nil`.
|
|
90
|
-
2. `ssl_ca_path` -- Path to a directory of CA certificates. When set, Faraday uses this. Default: `nil`.
|
|
91
|
-
3. `ssl_verify` -- Whether to verify SSL certificates. Default: `true`. This exists for completeness but should almost never be set to `false`.
|
|
92
|
-
|
|
93
|
-
Add the three new fields to the `attr_accessor` list (after `retry_statuses`):
|
|
94
|
-
|
|
95
|
-
```ruby
|
|
96
|
-
attr_accessor :timeout,
|
|
97
|
-
:open_timeout,
|
|
98
|
-
:max_redirects,
|
|
99
|
-
:user_agent,
|
|
100
|
-
:proxy,
|
|
101
|
-
:headers,
|
|
102
|
-
:retry_max,
|
|
103
|
-
:retry_interval,
|
|
104
|
-
:retry_interval_randomness,
|
|
105
|
-
:retry_backoff_factor,
|
|
106
|
-
:retry_statuses,
|
|
107
|
-
:ssl_ca_file,
|
|
108
|
-
:ssl_ca_path,
|
|
109
|
-
:ssl_verify
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
In `reset!`, add after `@retry_statuses = nil`:
|
|
113
|
-
|
|
114
|
-
```ruby
|
|
115
|
-
@ssl_ca_file = nil
|
|
116
|
-
@ssl_ca_path = nil
|
|
117
|
-
@ssl_verify = true
|
|
118
|
-
```
|
|
119
|
-
</action>
|
|
120
|
-
<verify>
|
|
121
|
-
Read `lib/source_monitor/configuration/http_settings.rb` and confirm: (a) all three new attr_accessors are present, (b) `reset!` initializes them with correct defaults, (c) `ssl_verify` defaults to `true`.
|
|
122
|
-
</verify>
|
|
123
|
-
<done>
|
|
124
|
-
HTTPSettings now has ssl_ca_file, ssl_ca_path, and ssl_verify configuration options with safe defaults.
|
|
125
|
-
</done>
|
|
126
|
-
</task>
|
|
127
|
-
<task type="auto">
|
|
128
|
-
<name>configure-faraday-ssl-cert-store</name>
|
|
129
|
-
<files>
|
|
130
|
-
lib/source_monitor/http.rb
|
|
131
|
-
</files>
|
|
132
|
-
<action>
|
|
133
|
-
Modify the `HTTP` module to configure SSL on every Faraday connection. Add a `require "openssl"` at the top of the file (after the existing requires).
|
|
134
|
-
|
|
135
|
-
In the `configure_request` method, add SSL configuration BEFORE the adapter line (`connection.adapter Faraday.default_adapter`):
|
|
136
|
-
|
|
137
|
-
```ruby
|
|
138
|
-
configure_ssl(connection, settings)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
Add a new private method `configure_ssl`:
|
|
142
|
-
|
|
143
|
-
```ruby
|
|
144
|
-
def configure_ssl(connection, settings)
|
|
145
|
-
connection.ssl.verify = settings.ssl_verify != false
|
|
146
|
-
|
|
147
|
-
if settings.ssl_ca_file
|
|
148
|
-
connection.ssl.ca_file = settings.ssl_ca_file
|
|
149
|
-
elsif settings.ssl_ca_path
|
|
150
|
-
connection.ssl.ca_path = settings.ssl_ca_path
|
|
151
|
-
else
|
|
152
|
-
connection.ssl.cert_store = default_cert_store
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def default_cert_store
|
|
157
|
-
OpenSSL::X509::Store.new.tap(&:set_default_paths)
|
|
158
|
-
end
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
The logic:
|
|
162
|
-
1. Always set `verify = true` unless explicitly configured to `false` (defense in depth).
|
|
163
|
-
2. If user specifies `ssl_ca_file`, use that (overrides cert store).
|
|
164
|
-
3. Else if user specifies `ssl_ca_path`, use that (overrides cert store).
|
|
165
|
-
4. Otherwise, create a fresh `OpenSSL::X509::Store` with `set_default_paths` -- this is the key fix that resolves the Netflix SSL error by loading all system CA certificates including intermediates.
|
|
166
|
-
|
|
167
|
-
Note: `ca_file` and `ca_path` take precedence over `cert_store` in Faraday/net_http, so we only set one path.
|
|
168
|
-
</action>
|
|
169
|
-
<verify>
|
|
170
|
-
Read `lib/source_monitor/http.rb` and confirm: (a) `require "openssl"` is present, (b) `configure_ssl` is called in `configure_request`, (c) the method creates an `OpenSSL::X509::Store` with `set_default_paths` as the default, (d) `ssl_ca_file` and `ssl_ca_path` override the store when set, (e) `ssl.verify` is always explicitly set. Run `bin/rubocop lib/source_monitor/http.rb` to confirm no offenses.
|
|
171
|
-
</verify>
|
|
172
|
-
<done>
|
|
173
|
-
The HTTP client now explicitly configures SSL with a proper cert store. By default, every Faraday connection gets an OpenSSL::X509::Store initialized with system default paths, which resolves certificate chain verification failures like the Netflix Tech Blog issue.
|
|
174
|
-
</done>
|
|
175
|
-
</task>
|
|
176
|
-
<task type="auto">
|
|
177
|
-
<name>add-ssl-unit-tests</name>
|
|
178
|
-
<files>
|
|
179
|
-
test/lib/source_monitor/http_test.rb
|
|
180
|
-
</files>
|
|
181
|
-
<action>
|
|
182
|
-
Add the following tests to `HTTPTest`:
|
|
183
|
-
|
|
184
|
-
1. **"configures SSL with default cert store"** -- Create a default client, assert `@connection.ssl.verify` is truthy, assert `@connection.ssl.cert_store` is an instance of `OpenSSL::X509::Store`, assert `@connection.ssl.ca_file` is nil (not overridden).
|
|
185
|
-
|
|
186
|
-
2. **"uses configured ssl_ca_file when set"** -- Configure `config.http.ssl_ca_file = "/path/to/custom/ca.pem"`, create a client, assert `connection.ssl.ca_file` equals the configured path, assert `connection.ssl.cert_store` is nil (ca_file takes precedence).
|
|
187
|
-
|
|
188
|
-
3. **"uses configured ssl_ca_path when set"** -- Configure `config.http.ssl_ca_path = "/path/to/certs"`, create a client, assert `connection.ssl.ca_path` equals the configured path.
|
|
189
|
-
|
|
190
|
-
4. **"ssl verify defaults to true"** -- Create a default client, assert `connection.ssl.verify` is `true`.
|
|
191
|
-
|
|
192
|
-
5. **"respects ssl_verify configuration"** -- Configure `config.http.ssl_verify = false`, create a client, assert `connection.ssl.verify` is `false`. (This tests the escape hatch exists, even though it should rarely be used.)
|
|
193
|
-
|
|
194
|
-
Add `require "openssl"` at the top of the test file if not already present.
|
|
195
|
-
|
|
196
|
-
Each test should follow the existing pattern: create a connection via `SourceMonitor::HTTP.client`, then inspect the `connection.ssl` object.
|
|
197
|
-
</action>
|
|
198
|
-
<verify>
|
|
199
|
-
Run `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/http_test.rb` and confirm all tests pass (8 existing + 5 new = 13 tests). Run `bin/rubocop test/lib/source_monitor/http_test.rb` and confirm no offenses.
|
|
200
|
-
</verify>
|
|
201
|
-
<done>
|
|
202
|
-
5 new SSL configuration tests added. All 13 HTTP client tests pass. The cert store, ca_file, ca_path, and verify options are all verified.
|
|
203
|
-
</done>
|
|
204
|
-
</task>
|
|
205
|
-
<task type="auto">
|
|
206
|
-
<name>record-netflix-vcr-cassette-and-regression-test</name>
|
|
207
|
-
<files>
|
|
208
|
-
test/lib/source_monitor/fetching/feed_fetcher_test.rb
|
|
209
|
-
test/vcr_cassettes/source_monitor/fetching/netflix_medium_rss.yml
|
|
210
|
-
</files>
|
|
211
|
-
<action>
|
|
212
|
-
This task records a VCR cassette from the real Netflix Tech Blog feed and adds a regression test.
|
|
213
|
-
|
|
214
|
-
**Step 1: Record the VCR cassette.**
|
|
215
|
-
|
|
216
|
-
Create a temporary recording script or use a one-off test run. The simplest approach: add the test first (below), then run it once with `VCR_RECORD=new_episodes` or equivalent to record the cassette. The cassette will be committed as a test fixture.
|
|
217
|
-
|
|
218
|
-
To record, temporarily allow net connect for the Netflix host. You can do this by running:
|
|
219
|
-
|
|
220
|
-
```bash
|
|
221
|
-
PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb -n test_fetches_netflix_tech_blog_feed_via_medium_rss
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
with VCR configured to `record: :new_episodes` for this specific cassette. If WebMock blocks the request, temporarily use `WebMock.allow_net_connect!` inside the test during recording, then remove it after the cassette is committed.
|
|
225
|
-
|
|
226
|
-
**Alternative recording approach:** Use a standalone Ruby script to fetch the feed and manually create the VCR cassette YAML:
|
|
227
|
-
|
|
228
|
-
```ruby
|
|
229
|
-
require "faraday"
|
|
230
|
-
require "openssl"
|
|
231
|
-
require "yaml"
|
|
232
|
-
|
|
233
|
-
conn = Faraday.new do |f|
|
|
234
|
-
f.ssl.cert_store = OpenSSL::X509::Store.new.tap(&:set_default_paths)
|
|
235
|
-
f.request :gzip
|
|
236
|
-
f.response :follow_redirects, limit: 5
|
|
237
|
-
f.adapter :net_http
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
response = conn.get("https://netflixtechblog.com/feed")
|
|
241
|
-
# Save as VCR cassette format...
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
After recording, verify the cassette file exists at `test/vcr_cassettes/source_monitor/fetching/netflix_medium_rss.yml` and contains a 200 response with RSS/XML body containing Netflix blog entries.
|
|
245
|
-
|
|
246
|
-
**Step 2: Add the regression test.**
|
|
247
|
-
|
|
248
|
-
Add a new test to `FeedFetcherTest`:
|
|
249
|
-
|
|
250
|
-
```ruby
|
|
251
|
-
test "fetches Netflix Tech Blog feed via Medium RSS" do
|
|
252
|
-
source = build_source(
|
|
253
|
-
name: "Netflix Tech Blog",
|
|
254
|
-
feed_url: "https://netflixtechblog.com/feed"
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
result = nil
|
|
258
|
-
VCR.use_cassette("source_monitor/fetching/netflix_medium_rss") do
|
|
259
|
-
result = FeedFetcher.new(source: source, jitter: ->(_) { 0 }).call
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
assert_equal :fetched, result.status
|
|
263
|
-
assert_not_nil result.feed
|
|
264
|
-
assert_kind_of Feedjira::Parser::RSS, result.feed
|
|
265
|
-
assert result.feed.entries.any?, "Expected at least one feed entry"
|
|
266
|
-
assert_match(/netflix/i, result.feed.title.to_s)
|
|
267
|
-
end
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
This test uses the recorded VCR cassette so it works in CI without network access. It validates that the feed parses as RSS and contains Netflix entries.
|
|
271
|
-
|
|
272
|
-
**Important:** The `build_source` helper is already available in this test file. Check existing test patterns to confirm the helper signature.
|
|
273
|
-
</action>
|
|
274
|
-
<verify>
|
|
275
|
-
Confirm: (a) the VCR cassette file exists at `test/vcr_cassettes/source_monitor/fetching/netflix_medium_rss.yml`, (b) it contains `netflixtechblog` in the request URI, (c) the response status is 200, (d) the response body contains RSS/XML content. Run `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb -n test_fetches_Netflix_Tech_Blog_feed_via_Medium_RSS` and confirm it passes.
|
|
276
|
-
</verify>
|
|
277
|
-
<done>
|
|
278
|
-
VCR cassette recorded from real Netflix Tech Blog feed. Regression test passes using the cassette. The feed parses as RSS with Netflix blog entries, proving the SSL fix resolves the original "certificate verify failed" error.
|
|
279
|
-
</done>
|
|
280
|
-
</task>
|
|
281
|
-
<task type="auto">
|
|
282
|
-
<name>full-suite-verification-and-documentation</name>
|
|
283
|
-
<files>
|
|
284
|
-
lib/source_monitor/http.rb
|
|
285
|
-
lib/source_monitor/configuration/http_settings.rb
|
|
286
|
-
test/lib/source_monitor/http_test.rb
|
|
287
|
-
test/lib/source_monitor/fetching/feed_fetcher_test.rb
|
|
288
|
-
</files>
|
|
289
|
-
<action>
|
|
290
|
-
Run the full verification suite:
|
|
291
|
-
|
|
292
|
-
1. `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/http_test.rb test/lib/source_monitor/fetching/feed_fetcher_test.rb` -- all targeted tests pass
|
|
293
|
-
2. `bin/rails test` -- full suite passes with 874+ runs and 0 failures
|
|
294
|
-
3. `bin/rubocop` -- zero offenses
|
|
295
|
-
4. `bin/brakeman --no-pager` -- zero warnings
|
|
296
|
-
|
|
297
|
-
Review all modified files for:
|
|
298
|
-
- `http.rb`: `require "openssl"` present, `configure_ssl` called in `configure_request`, `default_cert_store` creates `OpenSSL::X509::Store` with `set_default_paths`
|
|
299
|
-
- `http_settings.rb`: three new attr_accessors (`ssl_ca_file`, `ssl_ca_path`, `ssl_verify`), initialized in `reset!`
|
|
300
|
-
- `http_test.rb`: 5 new SSL tests covering cert_store default, ca_file override, ca_path override, verify default, verify override
|
|
301
|
-
- `feed_fetcher_test.rb`: Netflix regression test using VCR cassette
|
|
302
|
-
- VCR cassette: valid YAML with Netflix feed content
|
|
303
|
-
|
|
304
|
-
If any failures or offenses are found, fix them before completing.
|
|
305
|
-
|
|
306
|
-
Add a brief inline comment in `http.rb` above `configure_ssl` documenting the root cause:
|
|
307
|
-
|
|
308
|
-
```ruby
|
|
309
|
-
# Configure SSL to use a proper cert store. Without this, some systems
|
|
310
|
-
# fail to verify certificate chains that depend on intermediate CAs
|
|
311
|
-
# (e.g., Medium/Netflix on AWS). OpenSSL::X509::Store#set_default_paths
|
|
312
|
-
# loads all system-trusted CAs including intermediates.
|
|
313
|
-
```
|
|
314
|
-
</action>
|
|
315
|
-
<verify>
|
|
316
|
-
`bin/rails test` exits 0 with 874+ runs, 0 failures. `bin/rubocop` exits 0 with 0 offenses. `bin/brakeman --no-pager` exits 0 with 0 warnings. All modified files are clean and well-documented.
|
|
317
|
-
</verify>
|
|
318
|
-
<done>
|
|
319
|
-
Full suite passes. All quality gates green. SSL cert store fix is general (not Netflix-specific), configurable via HTTPSettings, documented inline, and regression-tested with a VCR cassette.
|
|
320
|
-
</done>
|
|
321
|
-
</task>
|
|
322
|
-
</tasks>
|
|
323
|
-
<verification>
|
|
324
|
-
1. `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/http_test.rb` -- 13+ tests pass (8 existing + 5 new SSL tests)
|
|
325
|
-
2. `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/fetching/feed_fetcher_test.rb` -- all tests pass including Netflix regression
|
|
326
|
-
3. `bin/rails test` -- 874+ runs, 0 failures
|
|
327
|
-
4. `bin/rubocop` -- 0 offenses
|
|
328
|
-
5. `bin/brakeman --no-pager` -- 0 warnings
|
|
329
|
-
6. `grep -n 'cert_store' lib/source_monitor/http.rb` returns matches for configure_ssl and default_cert_store
|
|
330
|
-
7. `grep -n 'ssl_ca_file' lib/source_monitor/configuration/http_settings.rb` returns match in attr_accessor and reset!
|
|
331
|
-
8. `test -f test/vcr_cassettes/source_monitor/fetching/netflix_medium_rss.yml` exits 0
|
|
332
|
-
9. `grep 'netflixtechblog' test/vcr_cassettes/source_monitor/fetching/netflix_medium_rss.yml` returns matches
|
|
333
|
-
</verification>
|
|
334
|
-
<success_criteria>
|
|
335
|
-
- Root cause identified: missing intermediate CA certs when OpenSSL cert store not explicitly initialized (REQ-25)
|
|
336
|
-
- General fix applied: Faraday SSL configured with OpenSSL::X509::Store#set_default_paths on every connection (REQ-25)
|
|
337
|
-
- Configurable: ssl_ca_file, ssl_ca_path, ssl_verify exposed via HTTPSettings for non-standard environments (REQ-25)
|
|
338
|
-
- Netflix Tech Blog feed fetches successfully via VCR cassette regression test (REQ-25)
|
|
339
|
-
- No regressions: existing SSL error wrapping (Faraday::SSLError -> ConnectionError) still works (REQ-25)
|
|
340
|
-
- VCR cassette recorded from real Netflix feed and committed as test fixture (REQ-25)
|
|
341
|
-
- All tests pass, RuboCop clean, Brakeman clean (REQ-25)
|
|
342
|
-
</success_criteria>
|
|
343
|
-
<output>
|
|
344
|
-
.vbw-planning/phases/06-netflix-feed-fix/PLAN-01-SUMMARY.md
|
|
345
|
-
</output>
|
|
@@ -1,80 +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
|
-
### Upgrade Assurance
|
|
58
|
-
|
|
59
|
-
- [ ] **REQ-26**: `bin/source_monitor upgrade` command detects version changes, copies new migrations, re-runs generator, and runs verification
|
|
60
|
-
- [ ] **REQ-27**: `PendingMigrationsVerifier` checks for unmigrated SourceMonitor tables in the verification suite
|
|
61
|
-
- [ ] **REQ-28**: Configuration deprecation framework warns on stale, renamed, or removed initializer options at boot time
|
|
62
|
-
- [ ] **REQ-29**: `sm-upgrade` AI skill teaches agents how to handle gem updates with CHANGELOG parsing and step-by-step guidance
|
|
63
|
-
- [ ] **REQ-30**: Upgrade guide documentation (`docs/upgrade.md`) with version-specific instructions
|
|
64
|
-
|
|
65
|
-
## v2 Requirements
|
|
66
|
-
|
|
67
|
-
- [ ] **REQ-XX**: Improve optional dependency loading with clear error messages
|
|
68
|
-
- [ ] **REQ-XX**: Add database index verification tooling
|
|
69
|
-
- [ ] **REQ-XX**: Document health check endpoint response format
|
|
70
|
-
|
|
71
|
-
## Out of Scope
|
|
72
|
-
|
|
73
|
-
| Item | Reason |
|
|
74
|
-
|------|--------|
|
|
75
|
-
| Multi-database support (MySQL/SQLite) | PostgreSQL-only simplifies development |
|
|
76
|
-
| Built-in authentication | Host app responsibility |
|
|
77
|
-
|
|
78
|
-
## Traceability
|
|
79
|
-
|
|
80
|
-
Requirement-to-phase mapping is tracked in ROADMAP.md.
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
<!-- VBW ROADMAP -- Phase decomposition with requirement mapping -->
|
|
2
|
-
<!-- Created during /vbw scope -->
|
|
3
|
-
|
|
4
|
-
# SourceMonitor Upgrade Assurance Roadmap
|
|
5
|
-
|
|
6
|
-
**Milestone:** upgrade-assurance
|
|
7
|
-
**Goal:** Give host app developers confidence that gem updates go smoothly -- automated migration detection, upgrade command, config validation, and AI-assisted upgrade guidance.
|
|
8
|
-
|
|
9
|
-
## Phases
|
|
10
|
-
|
|
11
|
-
1. [x] Phase 1: Upgrade Command & Migration Verifier
|
|
12
|
-
2. [x] Phase 2: Configuration Deprecation Framework
|
|
13
|
-
3. [x] Phase 3: Upgrade Skill & Documentation
|
|
14
|
-
|
|
15
|
-
## Phase Details
|
|
16
|
-
|
|
17
|
-
### Phase 1: Upgrade Command & Migration Verifier
|
|
18
|
-
|
|
19
|
-
**Goal:** Add `bin/source_monitor upgrade` that detects version changes since last install, copies new migrations, re-runs the idempotent generator, runs verification, and reports what changed. Also add a `PendingMigrationsVerifier` to the existing verification suite.
|
|
20
|
-
|
|
21
|
-
**Requirements:** REQ-26, REQ-27
|
|
22
|
-
|
|
23
|
-
**Success Criteria:**
|
|
24
|
-
- `bin/source_monitor upgrade` compares stored version marker against `SourceMonitor::VERSION`
|
|
25
|
-
- If version changed: copies new migrations, re-runs generator, runs `bin/source_monitor verify`
|
|
26
|
-
- If no version change: reports "Already up to date" with current version
|
|
27
|
-
- `PendingMigrationsVerifier` checks `db:migrate:status` for unmigrated SourceMonitor migrations
|
|
28
|
-
- Verifier integrated into `bin/source_monitor verify` and the upgrade flow
|
|
29
|
-
- Version marker stored in host app (e.g., `.source_monitor_version` or DB-backed)
|
|
30
|
-
- `bin/rails test` passes, RuboCop clean
|
|
31
|
-
|
|
32
|
-
### Phase 2: Configuration Deprecation Framework
|
|
33
|
-
|
|
34
|
-
**Goal:** Add a lightweight framework that warns host app developers when their initializer uses config options that have been renamed, removed, or have changed defaults. Warnings appear at boot time via Rails logger.
|
|
35
|
-
|
|
36
|
-
**Requirements:** REQ-28
|
|
37
|
-
|
|
38
|
-
**Success Criteria:**
|
|
39
|
-
- Engine maintains a deprecation registry (option name, version deprecated, replacement if any)
|
|
40
|
-
- At configuration load time, deprecated option usage triggers a Rails.logger.warn with actionable message
|
|
41
|
-
- Removed options that are still referenced raise a clear error with migration path
|
|
42
|
-
- Framework is opt-in for engine developers (simple DSL to register deprecations)
|
|
43
|
-
- Zero false positives on current valid configuration
|
|
44
|
-
- `bin/rails test` passes, RuboCop clean
|
|
45
|
-
|
|
46
|
-
### Phase 3: Upgrade Skill & Documentation
|
|
47
|
-
|
|
48
|
-
**Goal:** Create an `sm-upgrade` AI skill that guides agents through post-update workflows, and write a versioned upgrade guide for human developers.
|
|
49
|
-
|
|
50
|
-
**Requirements:** REQ-29, REQ-30
|
|
51
|
-
|
|
52
|
-
**Success Criteria:**
|
|
53
|
-
- `sm-upgrade` skill covers: reading CHANGELOG between versions, running upgrade command, interpreting results, handling edge cases
|
|
54
|
-
- Skill references the upgrade command and verification suite
|
|
55
|
-
- `docs/upgrade.md` includes: general upgrade steps, version-specific notes (0.3.x → 0.4.x), troubleshooting
|
|
56
|
-
- Skills installer updated to include `sm-upgrade` in consumer set
|
|
57
|
-
- Existing `sm-host-setup` skill cross-references upgrade flow
|
|
58
|
-
|
|
59
|
-
## Progress
|
|
60
|
-
|
|
61
|
-
| Phase | Status | Plans |
|
|
62
|
-
|-------|--------|-------|
|
|
63
|
-
| 1 | Complete | PLAN-01 (5 tasks, 5 commits) |
|
|
64
|
-
| 2 | Complete | PLAN-01 (4 tasks, 3 commits) |
|
|
65
|
-
| 3 | Complete | PLAN-01 (5 tasks, 4 commits) |
|
|
66
|
-
|
|
67
|
-
## Requirement Mapping
|
|
68
|
-
|
|
69
|
-
| REQ | Phase | Description |
|
|
70
|
-
|-----|-------|-------------|
|
|
71
|
-
| REQ-26 | 1 | Upgrade command with version detection and auto-remediation |
|
|
72
|
-
| REQ-27 | 1 | PendingMigrationsVerifier in verification suite |
|
|
73
|
-
| REQ-28 | 2 | Config deprecation framework with boot-time warnings |
|
|
74
|
-
| REQ-29 | 3 | sm-upgrade AI skill for agent-guided updates |
|
|
75
|
-
| REQ-30 | 3 | Upgrade guide documentation |
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
<!-- VBW STATE -- Current milestone progress -->
|
|
2
|
-
|
|
3
|
-
# State
|
|
4
|
-
|
|
5
|
-
**Milestone:** upgrade-assurance
|
|
6
|
-
**Current Phase:** All phases complete
|
|
7
|
-
**Status:** Ready to archive
|
|
8
|
-
**Date:** 2026-02-13
|
|
9
|
-
|
|
10
|
-
## Progress
|
|
11
|
-
|
|
12
|
-
- Phase 1: Complete (1 plan, 5 tasks, 5 commits) -- QA PASS 34/34
|
|
13
|
-
- Phase 2: Complete (1 plan, 4 tasks, 3 commits)
|
|
14
|
-
- Phase 3: Complete (1 plan, 5 tasks, 4 commits) -- QA PASS 37/38
|
|
15
|
-
|
|
16
|
-
## Decisions
|
|
17
|
-
|
|
18
|
-
| Decision | Date | Rationale |
|
|
19
|
-
|----------|------|-----------|
|
|
20
|
-
| 3 phases: command, config, skill | 2026-02-12 | Each independently valuable; command is foundational |
|
|
21
|
-
|
|
22
|
-
## Metrics
|
|
23
|
-
|
|
24
|
-
| Metric | Value |
|
|
25
|
-
|--------|-------|
|
|
26
|
-
| Phases | 3 |
|
|
27
|
-
| Plans completed | 3 |
|
|
28
|
-
| Tasks completed | 14 |
|
|
29
|
-
| Tests | 1003 |
|