source_monitor 0.4.0 → 0.5.1
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/skills/sm-host-setup/SKILL.md +3 -1
- data/.claude/skills/sm-upgrade/SKILL.md +102 -0
- data/.claude/skills/sm-upgrade/reference/upgrade-workflow.md +92 -0
- data/.claude/skills/sm-upgrade/reference/version-history.md +68 -0
- data/.gitignore +1 -0
- data/.vbw-planning/SHIPPED.md +35 -0
- data/.vbw-planning/milestones/generator-enhancements/SHIPPED.md +40 -0
- data/.vbw-planning/milestones/upgrade-assurance/REQUIREMENTS.md +80 -0
- data/.vbw-planning/milestones/upgrade-assurance/ROADMAP.md +75 -0
- data/.vbw-planning/milestones/upgrade-assurance/STATE.md +29 -0
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/01-VERIFICATION.md +144 -0
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01-SUMMARY.md +43 -0
- data/.vbw-planning/milestones/upgrade-assurance/phases/01-upgrade-command/PLAN-01.md +405 -0
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01-SUMMARY.md +27 -0
- data/.vbw-planning/milestones/upgrade-assurance/phases/02-config-deprecation/PLAN-01.md +303 -0
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/03-VERIFICATION.md +380 -0
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01-SUMMARY.md +36 -0
- data/.vbw-planning/milestones/upgrade-assurance/phases/03-upgrade-skill-docs/PLAN-01.md +652 -0
- data/CHANGELOG.md +25 -0
- data/CLAUDE.md +5 -3
- data/Gemfile.lock +4 -4
- data/VERSION +1 -1
- data/docs/upgrade.md +140 -0
- data/lib/source_monitor/configuration/deprecation_registry.rb +237 -0
- data/lib/source_monitor/configuration.rb +8 -0
- data/lib/source_monitor/setup/cli.rb +7 -0
- data/lib/source_monitor/setup/skills_installer.rb +1 -0
- data/lib/source_monitor/setup/upgrade_command.rb +59 -0
- data/lib/source_monitor/setup/verification/pending_migrations_verifier.rb +92 -0
- data/lib/source_monitor/setup/verification/runner.rb +1 -1
- data/lib/source_monitor/version.rb +1 -1
- data/lib/source_monitor.rb +3 -0
- metadata +44 -25
- /data/.vbw-planning/{REQUIREMENTS.md → milestones/generator-enhancements/REQUIREMENTS.md} +0 -0
- /data/.vbw-planning/{ROADMAP.md → milestones/generator-enhancements/ROADMAP.md} +0 -0
- /data/.vbw-planning/{STATE.md → milestones/generator-enhancements/STATE.md} +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/01-generator-steps/01-CONTEXT.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/01-generator-steps/01-VERIFICATION.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/01-generator-steps/PLAN-01-SUMMARY.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/01-generator-steps/PLAN-01.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/02-verification/02-VERIFICATION.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/02-verification/PLAN-01-SUMMARY.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/02-verification/PLAN-01.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/03-docs-alignment/03-VERIFICATION.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/03-docs-alignment/PLAN-01-SUMMARY.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/03-docs-alignment/PLAN-01.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/04-dashboard-ux/04-VERIFICATION.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/04-dashboard-ux/PLAN-01-SUMMARY.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/04-dashboard-ux/PLAN-01.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/05-active-storage-images/05-VERIFICATION.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/05-active-storage-images/PLAN-01-SUMMARY.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/05-active-storage-images/PLAN-01.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/05-active-storage-images/PLAN-02-SUMMARY.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/05-active-storage-images/PLAN-02.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/06-netflix-feed-fix/06-VERIFICATION.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/06-netflix-feed-fix/PLAN-01-SUMMARY.md +0 -0
- /data/.vbw-planning/{phases → milestones/generator-enhancements/phases}/06-netflix-feed-fix/PLAN-01.md +0 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
---
|
|
2
|
+
phase: 2
|
|
3
|
+
plan: "01"
|
|
4
|
+
title: config-deprecation-framework
|
|
5
|
+
type: execute
|
|
6
|
+
wave: 1
|
|
7
|
+
depends_on: []
|
|
8
|
+
cross_phase_deps: [{phase: 1, plan: "01", artifact: "lib/source_monitor.rb", reason: "Autoload declarations pattern established in Phase 1"}]
|
|
9
|
+
autonomous: true
|
|
10
|
+
effort_override: thorough
|
|
11
|
+
skills_used: [sm-configuration-setting, sm-engine-test]
|
|
12
|
+
files_modified:
|
|
13
|
+
- lib/source_monitor/configuration/deprecation_registry.rb
|
|
14
|
+
- lib/source_monitor.rb
|
|
15
|
+
- lib/source_monitor/configuration.rb
|
|
16
|
+
- test/lib/source_monitor/configuration/deprecation_registry_test.rb
|
|
17
|
+
must_haves:
|
|
18
|
+
truths:
|
|
19
|
+
- "Running `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/configuration/deprecation_registry_test.rb` exits 0 with 0 failures"
|
|
20
|
+
- "Running `bin/rubocop lib/source_monitor/configuration/deprecation_registry.rb` exits 0 with no offenses"
|
|
21
|
+
- "Running `bin/rails test` exits 0 with 992+ runs and 0 failures"
|
|
22
|
+
- "Running `bin/rubocop` exits 0 with no offenses"
|
|
23
|
+
- "SourceMonitor.configure with only valid current options produces zero deprecation warnings"
|
|
24
|
+
artifacts:
|
|
25
|
+
- path: "lib/source_monitor/configuration/deprecation_registry.rb"
|
|
26
|
+
provides: "Deprecation registry that stores entries and checks config for deprecated option usage (REQ-28)"
|
|
27
|
+
contains: "class DeprecationRegistry"
|
|
28
|
+
- path: "lib/source_monitor/configuration.rb"
|
|
29
|
+
provides: "Configuration#check_deprecations! called from SourceMonitor.configure"
|
|
30
|
+
contains: "check_deprecations!"
|
|
31
|
+
- path: "lib/source_monitor.rb"
|
|
32
|
+
provides: "SourceMonitor.configure calls check_deprecations! after yielding config block"
|
|
33
|
+
contains: "check_deprecations!"
|
|
34
|
+
- path: "test/lib/source_monitor/configuration/deprecation_registry_test.rb"
|
|
35
|
+
provides: "Tests covering all DeprecationRegistry branches including warning, error, no-op, and DSL"
|
|
36
|
+
contains: "class DeprecationRegistryTest"
|
|
37
|
+
key_links:
|
|
38
|
+
- from: "deprecation_registry.rb"
|
|
39
|
+
to: "REQ-28"
|
|
40
|
+
via: "Stores deprecation entries and checks config object for stale/renamed/removed options"
|
|
41
|
+
- from: "configuration.rb#check_deprecations!"
|
|
42
|
+
to: "deprecation_registry.rb"
|
|
43
|
+
via: "Configuration delegates deprecation checking to the registry"
|
|
44
|
+
- from: "source_monitor.rb#configure"
|
|
45
|
+
to: "configuration.rb#check_deprecations!"
|
|
46
|
+
via: "configure method triggers deprecation check after block completes"
|
|
47
|
+
---
|
|
48
|
+
<objective>
|
|
49
|
+
Build a lightweight configuration deprecation framework (REQ-28) that warns host app developers when their initializer uses config options that have been renamed or removed. The framework provides a DeprecationRegistry DSL for engine developers to register deprecations, hooks into SourceMonitor.configure to scan after the config block completes, and produces actionable Rails.logger.warn messages for :warning severity or raises SourceMonitor::DeprecatedOptionError for :error severity. No real deprecations exist today -- the framework is infrastructure for future use, validated with synthetic test deprecations.
|
|
50
|
+
</objective>
|
|
51
|
+
<context>
|
|
52
|
+
@lib/source_monitor/configuration.rb -- Main configuration class. Has attr_accessor for top-level options (queue_namespace, fetch_queue_name, etc.) and attr_reader for nested settings objects (http, scrapers, retention, events, models, realtime, fetching, health, authentication, scraping, images). The check_deprecations! method will be added here, delegating to DeprecationRegistry.check!(self). Key: deprecation paths use dot notation matching this structure -- e.g. "queue_namespace" for top-level, "http.timeout" for nested.
|
|
53
|
+
|
|
54
|
+
@lib/source_monitor/configuration/http_settings.rb -- Example settings class pattern. Plain Ruby class with attr_accessor and reset!. Shows the structure that deprecation checking must traverse: each settings class is a separate object accessible via config.http, config.fetching, etc. The registry needs to resolve dot-notation paths by splitting on "." and walking the config object graph.
|
|
55
|
+
|
|
56
|
+
@lib/source_monitor/configuration/fetching_settings.rb -- Another settings class showing the pattern: initialize calls reset!, attr_accessor for all fields. The deprecation framework does NOT modify these classes. It inspects them from outside by calling respond_to? and public_send.
|
|
57
|
+
|
|
58
|
+
@lib/source_monitor/configuration/retention_settings.rb -- Settings class with custom setters (strategy=). Shows that some settings use custom writer methods rather than raw attr_accessor. The deprecation framework checks if an option was SET, not just if it exists. Approach: the registry stores the old option path and checks if the config object responds to that method. For renamed options, it checks if the old method exists (it should not on current config -- if it does, someone is calling it). For removed options, if someone references a method that no longer exists, Ruby will raise NoMethodError before our check runs. So the practical approach is: register deprecations with the OLD path, and during check!, attempt to resolve the path. If the path resolves (meaning the old option still exists as an accessor), log the warning. If it does not resolve, skip silently (the option was already removed, and any access would have raised NoMethodError naturally). For `:error` severity removed options, we use a different approach: install a method_missing trap or explicitly define a method that raises.
|
|
59
|
+
|
|
60
|
+
**Simpler design decision:** The registry operates in two modes:
|
|
61
|
+
1. **Renamed options** (severity: :warning): The old option path still exists as an alias or the registry defines a getter that warns and delegates. But this adds complexity. Simpler: just scan a "deprecation log" that tracks which deprecated setters were called.
|
|
62
|
+
2. **Post-configure scan** (chosen approach): After the configure block runs, iterate all registered deprecations and check if the deprecated option was accessed. Since we cannot easily detect "was this setter called" without wrapping it, the simplest approach is:
|
|
63
|
+
- For :warning (renamed): Register a `method_missing` or `define_method` on the relevant settings class that logs a warning and forwards to the new option.
|
|
64
|
+
- For :error (removed): Register a `define_method` on the relevant settings class that raises.
|
|
65
|
+
- Alternative: Use a tracking hash. When register is called, we dynamically define a setter on the settings class that records the access in the registry, then the post-configure check reports all recorded accesses.
|
|
66
|
+
|
|
67
|
+
**Final design (simplest):** The DeprecationRegistry stores entries. When an entry is registered, it dynamically defines a method on the target settings class (or Configuration itself for top-level options) that:
|
|
68
|
+
- For :warning -- logs via Rails.logger.warn, then forwards to the replacement option
|
|
69
|
+
- For :error -- raises SourceMonitor::DeprecatedOptionError with the migration message
|
|
70
|
+
This is clean because: (a) the method is defined once at registration time, (b) it fires when the host app actually calls the deprecated setter in their configure block, (c) no post-scan needed, (d) zero overhead for non-deprecated options.
|
|
71
|
+
|
|
72
|
+
The `register` DSL: `DeprecationRegistry.register("http.proxy_url", removed_in: "0.5.0", replacement: "http.proxy", severity: :warning, message: "Use config.http.proxy instead")`. The registry parses the path, finds the settings class, and defines the trapping method.
|
|
73
|
+
|
|
74
|
+
For testing, we register synthetic deprecations (e.g. a fake option "http.old_timeout") and verify that accessing it in a configure block triggers the warning/error.
|
|
75
|
+
|
|
76
|
+
@lib/source_monitor.rb lines 197-217 -- The `configure` method yields config, then calls ModelExtensions.reload!. The deprecation framework hooks in here. Since we chose the define_method approach (methods fire on access, not post-scan), the configure method does NOT need a post-scan call. However, adding `config.check_deprecations!` as a post-configure hook is still useful as a safety net and for "removed option" detection. Actually, with the define_method approach, removed options raise immediately when accessed. So the only role for check_deprecations! is belt-and-suspenders. We can keep it simple: register defines methods, no post-scan needed. But for completeness and the "zero false positives" criterion, add a `check_deprecations!` that the configure method calls. This method iterates all :error entries and verifies the config is clean (no-op if no errors were raised, which is guaranteed by the define_method traps). Actually, let's just keep the define_method approach and skip the post-scan entirely. The `configure` method stays unchanged. The registry is self-contained.
|
|
77
|
+
|
|
78
|
+
**Revised final design:** DeprecationRegistry is a class with class-level state (entries hash). `register` stores the entry and defines a method on the target class. `clear!` removes all registered deprecations and undefines the methods (for test isolation). `entries` returns the hash for inspection. No changes to `SourceMonitor.configure` needed -- the trapping methods fire during the configure block naturally. Add `check_deprecations!` to Configuration anyway for explicit post-configure validation (iterates entries, no-op for now, but extensible for future "default changed" checks).
|
|
79
|
+
|
|
80
|
+
@test/lib/source_monitor/configuration_test.rb -- Existing configuration tests with setup/teardown that calls reset_configuration!. The deprecation registry test file follows the same pattern but also clears the registry in teardown. Tests use synthetic deprecations registered in setup, then verify warning/error behavior.
|
|
81
|
+
|
|
82
|
+
@lib/source_monitor/configuration/scraping_settings.rb -- Shows custom setter pattern (normalize_numeric). If a deprecated option path targets a class that already has a custom setter, the registry-defined method must coexist. Since we define a NEW method (the old/deprecated name), there is no collision with existing methods.
|
|
83
|
+
</context>
|
|
84
|
+
<tasks>
|
|
85
|
+
<task type="auto">
|
|
86
|
+
<name>create-deprecation-registry</name>
|
|
87
|
+
<files>
|
|
88
|
+
lib/source_monitor/configuration/deprecation_registry.rb
|
|
89
|
+
</files>
|
|
90
|
+
<action>
|
|
91
|
+
Create `lib/source_monitor/configuration/deprecation_registry.rb` with the DeprecationRegistry class.
|
|
92
|
+
|
|
93
|
+
Module nesting: `SourceMonitor::Configuration::DeprecationRegistry`.
|
|
94
|
+
|
|
95
|
+
Class-level state (use class instance variables, not class variables):
|
|
96
|
+
- `@entries` -- Hash mapping `"path"` to entry hash `{ path:, removed_in:, replacement:, severity:, message: }`
|
|
97
|
+
- `@defined_methods` -- Array of `[klass, method_name]` tuples for cleanup
|
|
98
|
+
|
|
99
|
+
Class methods:
|
|
100
|
+
|
|
101
|
+
**`register(path, removed_in:, replacement: nil, severity: :warning, message: nil)`**
|
|
102
|
+
1. Parse `path` -- split on ".". If one segment, target class is `Configuration`. If two segments, first is the settings accessor name, second is the deprecated option name.
|
|
103
|
+
2. Resolve target class: for "http.old_option", the target is `Configuration::HTTPSettings`. Use a mapping hash: `{ "http" => HTTPSettings, "fetching" => FetchingSettings, "health" => HealthSettings, "scraping" => ScrapingSettings, "retention" => RetentionSettings, "realtime" => RealtimeSettings, "authentication" => AuthenticationSettings, "images" => ImagesSettings, "scraper" => ScraperRegistry, "events" => Events, "models" => Models }`. For top-level, target is `Configuration`.
|
|
104
|
+
3. Build the deprecation message: `"[SourceMonitor] DEPRECATION: '#{path}' was deprecated in v#{removed_in}#{replacement_text}. #{message}"`. Where replacement_text is ` and replaced by '#{replacement}'` if replacement is present.
|
|
105
|
+
4. Define a writer method (`"#{option_name}="`) on the target class:
|
|
106
|
+
- For `:warning` severity: the method logs via `Rails.logger.warn(deprecation_message)` and, if replacement is present, forwards the value to the replacement setter. If no replacement, the value is silently dropped.
|
|
107
|
+
- For `:error` severity: the method raises `SourceMonitor::DeprecatedOptionError.new(deprecation_message)`.
|
|
108
|
+
5. Also define a reader method (`option_name`) for :warning that forwards to replacement getter, or for :error that raises.
|
|
109
|
+
6. Store the entry in `@entries` and record `[target_class, method_name]` in `@defined_methods`.
|
|
110
|
+
|
|
111
|
+
**`clear!`**
|
|
112
|
+
Remove all defined methods from their target classes (use `remove_method`), clear `@entries` and `@defined_methods`. This is essential for test isolation.
|
|
113
|
+
|
|
114
|
+
**`entries`** -- returns `@entries.dup`
|
|
115
|
+
|
|
116
|
+
**`registered?(path)`** -- returns boolean
|
|
117
|
+
|
|
118
|
+
Also define `SourceMonitor::DeprecatedOptionError < StandardError` in this file.
|
|
119
|
+
|
|
120
|
+
Key design points:
|
|
121
|
+
- The SETTINGS_CLASSES mapping resolves settings accessor names to their Ruby classes.
|
|
122
|
+
- `define_method` is used on the target class so the trap fires during normal configure block usage.
|
|
123
|
+
- `remove_method` (not `undef_method`) is used in clear! so the class reverts to its original behavior.
|
|
124
|
+
- Thread safety: registration happens at boot time (in an initializer or engine config), not at runtime. No mutex needed.
|
|
125
|
+
- If the target class already responds to the deprecated method name, skip defining (the option is not actually removed/renamed yet -- this is a configuration error by the engine developer). Log a warning to stderr instead.
|
|
126
|
+
</action>
|
|
127
|
+
<verify>
|
|
128
|
+
Read the created file. Confirm: (a) class is SourceMonitor::Configuration::DeprecationRegistry, (b) register method accepts path/removed_in/replacement/severity/message, (c) define_method on target class for both reader and writer, (d) :warning severity logs and forwards, (e) :error severity raises DeprecatedOptionError, (f) clear! removes defined methods and resets state, (g) SETTINGS_CLASSES mapping covers all 11 settings classes, (h) DeprecatedOptionError is defined. Run `bin/rubocop lib/source_monitor/configuration/deprecation_registry.rb` -- 0 offenses.
|
|
129
|
+
</verify>
|
|
130
|
+
<done>
|
|
131
|
+
DeprecationRegistry class created with register/clear!/entries/registered? class methods. Defines trapping methods on target settings classes for both :warning and :error severities. DeprecatedOptionError defined. SETTINGS_CLASSES maps all 11 settings accessor names.
|
|
132
|
+
</done>
|
|
133
|
+
</task>
|
|
134
|
+
<task type="auto">
|
|
135
|
+
<name>wire-registry-into-configuration-and-autoload</name>
|
|
136
|
+
<files>
|
|
137
|
+
lib/source_monitor/configuration.rb
|
|
138
|
+
lib/source_monitor.rb
|
|
139
|
+
</files>
|
|
140
|
+
<action>
|
|
141
|
+
**Modify `lib/source_monitor/configuration.rb`:**
|
|
142
|
+
|
|
143
|
+
Add a require at the top (after the existing requires, before the module definition):
|
|
144
|
+
```ruby
|
|
145
|
+
require "source_monitor/configuration/deprecation_registry"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Add a public method to Configuration:
|
|
149
|
+
```ruby
|
|
150
|
+
def check_deprecations!
|
|
151
|
+
DeprecationRegistry.check_defaults!(self)
|
|
152
|
+
end
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
This method is a hook point for future "default changed" checks. For now, DeprecationRegistry.check_defaults! is a no-op class method (define it in the registry). It exists so that future phases can add checks like "option X changed its default from A to B in version Y".
|
|
156
|
+
|
|
157
|
+
**Modify `lib/source_monitor.rb`:**
|
|
158
|
+
|
|
159
|
+
In the `configure` method (around line 198), add `config.check_deprecations!` after `yield config` and before `ModelExtensions.reload!`:
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
def configure
|
|
163
|
+
yield config
|
|
164
|
+
config.check_deprecations!
|
|
165
|
+
SourceMonitor::ModelExtensions.reload!
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Also in the `reset_configuration!` method, add `DeprecationRegistry.clear!` call to ensure test isolation works when resetting config:
|
|
170
|
+
|
|
171
|
+
Actually, NO -- `clear!` should NOT be called on reset_configuration. The registry is global engine state (deprecations are registered once at boot), not per-configuration-instance state. Clearing on reset would break the deprecation framework. Instead, test isolation for registry tests should call `DeprecationRegistry.clear!` in their own teardown.
|
|
172
|
+
|
|
173
|
+
So the only change to `reset_configuration!` is: nothing. Leave it as-is.
|
|
174
|
+
|
|
175
|
+
Summary of changes:
|
|
176
|
+
1. `configuration.rb`: Add `require` for deprecation_registry, add `check_deprecations!` method
|
|
177
|
+
2. `source_monitor.rb`: Add `config.check_deprecations!` in `configure` method after yield
|
|
178
|
+
</action>
|
|
179
|
+
<verify>
|
|
180
|
+
Read `lib/source_monitor/configuration.rb` -- confirm `require "source_monitor/configuration/deprecation_registry"` is present and `check_deprecations!` method exists. Read `lib/source_monitor.rb` -- confirm `config.check_deprecations!` is called in `configure` after yield. Run `bin/rubocop lib/source_monitor/configuration.rb lib/source_monitor.rb` -- 0 offenses.
|
|
181
|
+
</verify>
|
|
182
|
+
<done>
|
|
183
|
+
DeprecationRegistry required from configuration.rb. check_deprecations! method added to Configuration class. SourceMonitor.configure calls check_deprecations! after yield. No changes to reset_configuration! (registry state is global, not per-instance).
|
|
184
|
+
</done>
|
|
185
|
+
</task>
|
|
186
|
+
<task type="auto">
|
|
187
|
+
<name>create-deprecation-registry-tests</name>
|
|
188
|
+
<files>
|
|
189
|
+
test/lib/source_monitor/configuration/deprecation_registry_test.rb
|
|
190
|
+
</files>
|
|
191
|
+
<action>
|
|
192
|
+
Create `test/lib/source_monitor/configuration/deprecation_registry_test.rb` with comprehensive tests for the deprecation framework.
|
|
193
|
+
|
|
194
|
+
Module nesting: `SourceMonitor::Configuration::DeprecationRegistryTest < ActiveSupport::TestCase`.
|
|
195
|
+
|
|
196
|
+
Setup/teardown:
|
|
197
|
+
```ruby
|
|
198
|
+
setup do
|
|
199
|
+
SourceMonitor.reset_configuration!
|
|
200
|
+
DeprecationRegistry.clear!
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
teardown do
|
|
204
|
+
DeprecationRegistry.clear!
|
|
205
|
+
SourceMonitor.reset_configuration!
|
|
206
|
+
end
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Tests to write (10 tests covering all branches):
|
|
210
|
+
|
|
211
|
+
1. **"register stores entry in registry"** -- Register a synthetic deprecation `"http.old_proxy_url"`. Assert `DeprecationRegistry.registered?("http.old_proxy_url")` is true. Assert `DeprecationRegistry.entries` has one entry with correct attributes.
|
|
212
|
+
|
|
213
|
+
2. **"warning severity logs deprecation and forwards to replacement"** -- Register `"http.old_proxy_url"` with severity: :warning, replacement: "http.proxy", removed_in: "0.5.0". Use a mock or string IO to capture Rails.logger.warn output. Call `SourceMonitor.configure { |c| c.http.old_proxy_url = "socks5://localhost" }`. Assert the warning message was logged (contains "DEPRECATION", "old_proxy_url", "0.5.0", "http.proxy"). Assert `SourceMonitor.config.http.proxy` equals "socks5://localhost" (forwarded).
|
|
214
|
+
|
|
215
|
+
3. **"warning severity reader forwards to replacement getter"** -- After registering and setting via the deprecated writer (as in test 2), read `config.http.old_proxy_url` and assert it returns the same value as `config.http.proxy`.
|
|
216
|
+
|
|
217
|
+
4. **"error severity raises DeprecatedOptionError on write"** -- Register `"http.removed_option"` with severity: :error, removed_in: "0.5.0", message: "This option was removed. Use X instead." Assert that `SourceMonitor.configure { |c| c.http.removed_option = "value" }` raises `SourceMonitor::DeprecatedOptionError` with message containing "removed_option" and "0.5.0".
|
|
218
|
+
|
|
219
|
+
5. **"error severity raises DeprecatedOptionError on read"** -- Register same as test 4. Assert that calling `SourceMonitor.config.http.removed_option` raises `SourceMonitor::DeprecatedOptionError`.
|
|
220
|
+
|
|
221
|
+
6. **"clear removes defined methods and entries"** -- Register a deprecation. Assert registered. Call `clear!`. Assert NOT registered. Assert `entries` is empty. Assert `SourceMonitor.config.http` does NOT respond_to the deprecated method name.
|
|
222
|
+
|
|
223
|
+
7. **"top-level option deprecation works"** -- Register `"old_queue_prefix"` (no dot -- targets Configuration directly) with severity: :warning, replacement: "queue_namespace". Configure with `config.old_queue_prefix = "my_app"`. Assert warning logged. Assert `config.queue_namespace` equals "my_app".
|
|
224
|
+
|
|
225
|
+
8. **"no warnings for valid current configuration"** (zero false positives criterion) -- Register a deprecation for a synthetic option. Then configure using ONLY valid current options (e.g. `config.http.timeout = 30`). Assert NO deprecation warnings were logged.
|
|
226
|
+
|
|
227
|
+
9. **"multiple deprecations can coexist"** -- Register two deprecations on different settings classes. Trigger both in one configure block. Assert both warnings logged.
|
|
228
|
+
|
|
229
|
+
10. **"check_deprecations! is called during configure"** -- Use a mock to verify that `check_deprecations!` is called. This validates the wiring from task 2.
|
|
230
|
+
|
|
231
|
+
For capturing Rails.logger.warn output, use:
|
|
232
|
+
```ruby
|
|
233
|
+
log_output = StringIO.new
|
|
234
|
+
original_logger = Rails.logger
|
|
235
|
+
Rails.logger = ActiveSupport::Logger.new(log_output)
|
|
236
|
+
# ... run configure ...
|
|
237
|
+
Rails.logger = original_logger
|
|
238
|
+
assert_match(/DEPRECATION/, log_output.string)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
All 10 tests should pass. RuboCop clean.
|
|
242
|
+
</action>
|
|
243
|
+
<verify>
|
|
244
|
+
Run `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/configuration/deprecation_registry_test.rb` -- all 10 tests pass with 0 failures. Run `bin/rubocop test/lib/source_monitor/configuration/deprecation_registry_test.rb` -- 0 offenses.
|
|
245
|
+
</verify>
|
|
246
|
+
<done>
|
|
247
|
+
10 tests pass covering: registration, warning with forwarding, warning reader, error on write, error on read, clear cleanup, top-level option, zero false positives, multiple coexistence, and configure wiring. RuboCop clean.
|
|
248
|
+
</done>
|
|
249
|
+
</task>
|
|
250
|
+
<task type="auto">
|
|
251
|
+
<name>full-suite-verification-and-brakeman</name>
|
|
252
|
+
<files>
|
|
253
|
+
</files>
|
|
254
|
+
<action>
|
|
255
|
+
Run the full verification suite to confirm no regressions and all quality gates pass.
|
|
256
|
+
|
|
257
|
+
1. `bin/rails test` -- full test suite passes with 992+ runs, 0 failures (the 10 new deprecation tests + existing 992)
|
|
258
|
+
2. `bin/rubocop` -- 0 offenses across all files
|
|
259
|
+
3. `bin/brakeman --no-pager` -- 0 warnings
|
|
260
|
+
|
|
261
|
+
If any failures:
|
|
262
|
+
- Test failures: read the failure output, identify the root cause, fix in the appropriate file
|
|
263
|
+
- RuboCop offenses: fix style issues in the offending files
|
|
264
|
+
- Brakeman warnings: evaluate and fix security concerns
|
|
265
|
+
|
|
266
|
+
After all gates pass, confirm:
|
|
267
|
+
- `grep -rn 'class DeprecationRegistry' lib/` returns the registry file
|
|
268
|
+
- `grep -rn 'check_deprecations!' lib/source_monitor/configuration.rb` returns the method
|
|
269
|
+
- `grep -rn 'check_deprecations!' lib/source_monitor.rb` returns the configure hook
|
|
270
|
+
- `grep -rn 'DeprecatedOptionError' lib/` returns the error class definition
|
|
271
|
+
- The existing configuration_test.rb still passes (reset_configuration! does not interfere with registry)
|
|
272
|
+
</action>
|
|
273
|
+
<verify>
|
|
274
|
+
`bin/rails test` exits 0 with 1002+ runs, 0 failures. `bin/rubocop` exits 0. `bin/brakeman --no-pager` exits 0. All grep checks return matches.
|
|
275
|
+
</verify>
|
|
276
|
+
<done>
|
|
277
|
+
Full suite green with 1002+ runs. RuboCop clean. Brakeman clean. All Phase 2 success criteria met. REQ-28 implemented: deprecation registry with DSL, boot-time warnings for :warning severity, errors for :error severity, zero false positives on current valid config.
|
|
278
|
+
</done>
|
|
279
|
+
</task>
|
|
280
|
+
</tasks>
|
|
281
|
+
<verification>
|
|
282
|
+
1. `PARALLEL_WORKERS=1 bin/rails test test/lib/source_monitor/configuration/deprecation_registry_test.rb` -- 10 tests pass
|
|
283
|
+
2. `bin/rails test` -- 1002+ runs, 0 failures
|
|
284
|
+
3. `bin/rubocop` -- 0 offenses
|
|
285
|
+
4. `bin/brakeman --no-pager` -- 0 warnings
|
|
286
|
+
5. `grep -n 'class DeprecationRegistry' lib/source_monitor/configuration/deprecation_registry.rb` returns a match
|
|
287
|
+
6. `grep -n 'class DeprecatedOptionError' lib/source_monitor/configuration/deprecation_registry.rb` returns a match
|
|
288
|
+
7. `grep -n 'check_deprecations!' lib/source_monitor/configuration.rb` returns a match
|
|
289
|
+
8. `grep -n 'check_deprecations!' lib/source_monitor.rb` returns a match
|
|
290
|
+
9. `grep -n 'DeprecationRegistry' lib/source_monitor/configuration.rb` returns a match (require)
|
|
291
|
+
10. Existing `test/lib/source_monitor/configuration_test.rb` passes without modification
|
|
292
|
+
</verification>
|
|
293
|
+
<success_criteria>
|
|
294
|
+
- Engine maintains a deprecation registry with option path, version deprecated, replacement, and severity (REQ-28)
|
|
295
|
+
- At configuration load time, deprecated option usage triggers Rails.logger.warn with actionable message (REQ-28)
|
|
296
|
+
- Removed options that are still referenced raise DeprecatedOptionError with migration path (REQ-28)
|
|
297
|
+
- Framework is opt-in via simple DSL: DeprecationRegistry.register(path, removed_in:, ...) (REQ-28)
|
|
298
|
+
- Zero false positives on current valid configuration -- only synthetic test deprecations trigger warnings (REQ-28)
|
|
299
|
+
- bin/rails test passes with 1002+ runs, RuboCop clean, Brakeman clean
|
|
300
|
+
</success_criteria>
|
|
301
|
+
<output>
|
|
302
|
+
.vbw-planning/phases/02-config-deprecation/PLAN-01-SUMMARY.md
|
|
303
|
+
</output>
|