Split Gen1 and Gen2 handling into per-generation strategies - #56
Merged
Conversation
Move the Gen1 wire knowledge (settable-param allowlists, GET-vs-setter renames, section layout) out of RestoreDeviceConfig into the pure domain service gen1_settings_translation, exposing restorable_params and wifi_subresources. The literal wire-param pinning tests move with it and now run against the pure functions with no mocked gateway.
RestoreDeviceConfig keeps orchestration only: backup lookup, identity and generation checks, selection and ordering, unknown-key surfacing, aggregation, and the reboot-if-applied rule. The Gen2 path (SetConfig/PutCode/Schedule.Replace, read-only strip) and the Gen1 path (raw-settings replay, mode pre-phase, WiFi fan-out, legacy reboot) move unchanged into ComponentRestoreStrategy implementations under use_cases/restore_strategies. Strategies are constructed per restore run from the injected gateway: the Gen1 strategy holds the loaded settings between prepare and the component loop, and the use case is a container singleton, so a shared strategy instance would leak state across concurrent restores. The public constructor keeps its signature; the mode timing kwargs are forwarded to the Gen1 strategy.
The generation reached the code in three raw shapes reconciled ad hoc at each site: the integer gen a device status reports, the gen1/gen2 labels stored on backups, and the missing-gen shape of a Gen1 /shelly response. The Generation value object now owns all three constructions; backup capture, restore reconciliation and strategy selection, the bulk-export divert, and the AP detector's Gen1 refusal all go through it. GEN2 covers the whole RPC family since Gen2+ devices share one surface; provisioning keeps the literal int where it reports gen 2 vs 3.
export_bulk_config keeps the export envelope and the per-device loop; how one generation's component configs are captured (Gen2 GetConfig/GetCode/Schedule.List vs the Gen1 mapped-config copy plus raw /settings fetch) moves unchanged into ComponentCaptureStrategy implementations under use_cases/capture_strategies. LEGACY_SETTINGS_KEY moves to the device_backup entity: it names part of the snapshot format, which capture and restore now both consume from the same home.
DeviceStatus.from_raw_response reached into the legacy mapper's private attrs key to merge Gen1 actions into available_actions, so the domain entity knew the mapper's internals. The mapper now declares each component's actions as a first-class available_actions list on its payload, and the entity merges any declared list generically with the actions derived from the RPC method list. attrs keeps only the gateway-private routing keys.
jfmlima
force-pushed
the
refactor/generation-seam
branch
from
July 29, 2026 15:37
c7d2bb2 to
9645b1f
Compare
jfmlima
marked this pull request as ready for review
July 29, 2026 15:41
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
Cut the generation seam: restore and capture get per-generation strategies, the Gen1 wire knowledge moves to a pure domain service, and the three ad hoc generation representations collapse into one domain value.
Context
Follow-on refactor to #51 (design doc: the generation-seam plan reviewed after PR #53). restore_device_config.py owned two complete protocols in ~950 lines, the Gen1 wire tables lived inside a use case whose job is orchestration, and generation was reconciled inline in three shapes (device gen int, backup label string, missing /shelly field). The contract is zero behavior change: every existing test passes with only import moves and fixture relocation, and the literal Gen1 wire-param pins now run against the pure translation service with no mocks. Reviewed commit by commit it reads as code moved and names introduced.
Notes
The legacy mapper now declares component actions as a first-class available_actions list on its payload instead of the private attrs key the domain entity used to reach into; nothing in the repo consumed the old shape. Two independent adversarial reviews (Codex plus a fresh agent) found no behavior drift in any runtime flow.