Skip to content

refactor: PowerSync complete architecture strangler (Phases 0–7) - #41

Closed
Artic0din wants to merge 9 commits into
mainfrom
cursor/architecture-refactor-463f
Closed

refactor: PowerSync complete architecture strangler (Phases 0–7)#41
Artic0din wants to merge 9 commits into
mainfrom
cursor/architecture-refactor-463f

Conversation

@Artic0din

Copy link
Copy Markdown
Collaborator

Summary

Implements the PowerSync complete architecture refactor as a strangler-fig rewrite with controlled migration.

What landed

  • Runtime / control: typed EntryRuntime dual-write, BatteryPort + restore contract, brand capability matrix, optimizer commands routed through BatteryPort
  • HTTP: 63 HomeAssistantView classes moved to api_http/
  • Coordinators: coordinator.py facade over pricing/ + energy/ with PriceProvider / EnergyTelemetry
  • Optimization: slimmed coordinator + extracted mixins (overlays, tariff_windows, price_forecast, reserve_policy, action_executor) and solver split (lp_solver, greedy_solver, schedule_emit); deprecated dead ScheduleExecutor.execute_action path
  • EV: LoadpointArbiter, charger adapter stubs, ownership re-export, planner attach hook
  • Config flow: config_flow.py thin facade over config_flow_pkg/ + portal mixin + migration helper
  • Bootstrap / const: bootstrap/lifecycle.attach_runtime, const.py re-export facade over const_pkg/
  • Docs / tests: Architecture-Refactor.md, tests/architecture/ contract suite

Verification

  • pytest tests/architecture/ tests/test_greedy_priority_export.py tests/test_run_optimization_atomicity.py tests/test_battery_controller_wrapper.py tests/test_optimizer_restore_retry.py tests/test_spread_export_charge_preservation.py45 passed

Follow-ups (intentional strangler remainder)

  • Continue extracting brand force/restore handlers from __init__.py into batteries/
  • Finish provider/battery config-flow step modules under config_flow_pkg/
  • Collapse remaining __init__.py setup closures into bootstrap/
  • Remove service shims after migration window
Open in Web Open in Cursor 

cursoragent and others added 5 commits July 19, 2026 04:54
Move price and energy DataUpdateCoordinator classes out of the monolithic
coordinator.py into pricing/coordinators.py and energy/coordinators.py,
with shared helpers in pricing/_shared.py. Keep coordinator.py as a
compatibility re-export so existing imports continue to work.

Co-authored-by: Artic0din <Artic0din@users.noreply.github.com>
Move all 63 HTTP view classes into custom_components/power_sync/api_http/
(powerwall, energy_brands, tariff, automations, ev, network, optimization)
and re-export them from __init__.py so registration call sites stay unchanged.

Co-authored-by: Artic0din <Artic0din@users.noreply.github.com>
Move PowerSyncConfigFlow/OptionsFlow and helpers into a modular
config_flow_pkg with a thin config_flow.py facade for HA discovery.
Add portal mixin stub, nested-schema migrate helper, and provider/
battery package placeholders for the next extraction phase.

Co-authored-by: Artic0din <Artic0din@users.noreply.github.com>
Wire LoadpointArbiter through ev/ chargers+bridge+ownership re-exports and
attach it when EVChargingModeCoordinator is stored. Extract bootstrap.lifecycle
attach_runtime for EntryRuntime dual-write, and split const into const_pkg
with a stable re-export facade.

Co-authored-by: Artic0din <Artic0din@users.noreply.github.com>
Split OptimizationCoordinator and BatteryOptimizer into focused mixin
modules (overlays, tariff windows, price forecast, reserve policy,
action executor, LP/greedy/schedule emit). Route optimizer hardware
commands through BatteryPort, deprecate ScheduleExecutor.execute_action,
and extend stub const coverage for the new import surface.

Co-authored-by: Artic0din <Artic0din@users.noreply.github.com>
@cursor

cursor Bot commented Jul 19, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_221d3260-293c-4329-a2a9-c5e7ca5bd228)

@Artic0din
Artic0din marked this pull request as ready for review July 20, 2026 05:07
Copilot AI review requested due to automatic review settings July 20, 2026 05:07
@cursor

cursor Bot commented Jul 20, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_84ca946c-b407-44ea-ac77-a19e24a828a5)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 506213a84b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +614 to +615
isinstance(slot_start, datetime)
and isinstance(slot_end, datetime)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Import datetime before using boundary deferral

When a periodic optimizer poll wants to start a forced action mid-slot after a cached non-forced boundary action, this branch evaluates isinstance(..., datetime), but the extracted mixin only imports timedelta. In that scenario _execute_optimizer_action raises NameError instead of deferring or executing the action, so the optimizer cycle fails until the next trigger. Import datetime here as it was available in the original coordinator module.

Useful? React with 👍 / 👎.

bag[RUNTIME_KEY] = runtime
domain_data = hass.data.setdefault(domain, {})
domain_data[entry.entry_id] = bag
domain_data.setdefault("_runtimes", {})[entry.entry_id] = runtime

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove runtime registry entries on unload

Storing _runtimes under hass.data[DOMAIN] makes the domain dict non-empty even after async_unload_entry pops the last config entry; the unload path then skips its if not hass.data[DOMAIN] service cleanup and the _runtimes map keeps EntryRuntime objects holding the unloaded hass/entry references. This affects any last-entry unload/reload path, so the registry should either live outside the entry-domain bag or be pruned when the entry unloads.

Useful? React with 👍 / 👎.


from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-export UpdateFailed from the coordinator facade

For callers that still catch power_sync.coordinator.UpdateFailed or use from ...coordinator import UpdateFailed, this compatibility facade now raises AttributeError/ImportError even though _fetch_with_retry still raises that HA exception from pricing._shared. The facade promises existing imports from this module keep working, so it should import and expose UpdateFailed alongside ConfigEntryAuthFailed.

Useful? React with 👍 / 👎.

Artic0din and others added 3 commits July 22, 2026 10:56
* chore(workflow): adopt GitHub-native task tracking

Replace Linear-first agent guidance with GitHub Issues and the shared Development Project. Keep pull requests and CI authoritative for implementation.

* fix(docs): remove stray workflow marker
…oud setup) (#52)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* ci(github): install Repo Assist

Install the current upstream Copilot workflow, compiled lock file, and
agentic-workflow tooling. Use GitHub-native issues, pull requests, and
persistent repository memory.

* fix(github): satisfy workflow security gates

Disable checkout credential persistence and document the generated
workflow exceptions required by repository linting.

* fix(github): address Repo Assist review findings

Authenticate scheduled checks, remove the undefined merge driver, align
the Copilot setup action, and clarify generated configuration.

* fix(github): use keyless Copilot authentication

Use the per-run GitHub token for Copilot inference, keep capacity skips
green, and align active task guidance with GitHub Issues.

* fix(github): harden Repo Assist pull requests

Use trusted labels for automation capacity and branch updates, produce
conventional pull request titles, and guarantee three distinct tasks.

* fix(github): align Repo Assist maintenance markers

Match generated pull request titles and labels, disclose automated status messages, and avoid an undefined merge driver.

* fix(github): complete Repo Assist output policy

Allow required changelog updates and narrowly permit closing labeled Repo Assist summary issues.

* fix(github): serialize Repo Assist runs

Prevent overlapping repository runs and suppress noisy no-op and failure issues while preserving visible workflow failures.

* fix(github): expose workflow upgrade tools

Allow cloud agents to run the fix and upgrade routes advertised by the installed Agentic Workflows skill.
@Artic0din Artic0din closed this Jul 23, 2026
@Artic0din
Artic0din deleted the cursor/architecture-refactor-463f branch July 23, 2026 10:59

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f2f4d3d137

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +8 to +12
from custom_components.power_sync.control import (
BatteryPort,
ServiceBatteryPort,
apply_restore_success_gate,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep architecture tests off the package initializer

In the normal repo test environment, Home Assistant is not installed and tests are expected to stub it; importing custom_components.power_sync.control first executes custom_components/power_sync/__init__.py, which imports homeassistant.* before any stub is installed. The new architecture tests therefore fail during collection instead of testing the leaf contracts; use the existing power_sync package-stub pattern or load the leaf modules directly.

AGENTS.md reference: AGENTS.md:L18-L20

Useful? React with 👍 / 👎.

Comment on lines +10 to +11
from ..const import (
CONF_CHIP_MODE_ENABLED,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid import-time optional tariff constants

With the existing optimizer fixtures that stub power_sync.const narrowly, importing power_sync.optimization.coordinator now fails before tests run because this new mixin imports chip/demand constants unconditionally at module import time; tests/test_optimization_price_source.py errors on CONF_CHIP_MODE_ENABLED even for unrelated price-source cases. Keep these constants as local imports inside the tariff helpers or update the shared stubs so focused optimizer tests can still import the coordinator.

Useful? React with 👍 / 👎.

@@ -16,10 +16,14 @@
"""
from __future__ import annotations

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the LP solver monkeypatch seam

After the solver split, battery_optimizer.py no longer exposes _solve_lp_highs, but the existing focused optimizer test still monkeypatches power_sync.optimization.battery_optimizer._solve_lp_highs and now raises AttributeError before it can verify the HiGHS time-limit path. Either keep this compatibility seam wired through the facade or update the focused test to patch the new solver module explicitly.

AGENTS.md reference: AGENTS.md:L20-L20

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants