Sync 0.20.1 (master) into release/1.0 #233

Merged
admin merged 18 commits from sync-0.20.1 into release/1.0 2026-11-05 21:15:15 +01:00
Owner

Real merge (--no-ff) of master d53f70f (v0.20.1: OPT-36 #229, TEST-37 #231) into release/1.0 371961e (SAFETY-128 #228). No textual conflicts. config.yaml 0.20.1; CHANGELOG: SAFETY-128 entry moved from the 0.20.0 section to "Not in a release yet" above 0.20.1; 0.20.0/0.20.1 sections as on master. No behaviour change beyond the two sides. Do not squash.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC

Real merge (--no-ff) of master d53f70f (v0.20.1: OPT-36 #229, TEST-37 #231) into release/1.0 371961e (SAFETY-128 #228). No textual conflicts. config.yaml 0.20.1; CHANGELOG: SAFETY-128 entry moved from the 0.20.0 section to "Not in a release yet" above 0.20.1; 0.20.0/0.20.1 sections as on master. No behaviour change beyond the two sides. Do not squash. 🤖 Generated with [Claude Code](https://claude.ai/code) https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
admin added 18 commits 2026-11-05 18:34:42 +01:00
OPT-36: Leftover solar counts what Cheap hours and Solar first batteries will give before buying
checks / unit (pull_request) Failing after 59m31s
infra-pin / fresh (pull_request) Successful in 2s
05746c0564
Live 0.20.0: A (Leftover solar, 60 %) charged to 95 % while B (Cheap hours,
94 %) already covered the expensive hours. dayplan.plan() now takes off the
shortfall what Cheap hours batteries will hold when the hours start (the
ceiling or what their cheap run reaches under the peak aim) and what Solar
first batteries will still hold after the night's use. Home reads a give
block after the planned charge, plan_cover() is the plan's own figure, and
the Why line says other batteries cover.

Co-Authored-By: Claude <noreply@anthropic.com>
OPT-36: test a give block before tonight's charge, Solar first refusing and in maintenance
checks / unit (pull_request) Failing after 26m32s
infra-pin / fresh (pull_request) Successful in 4s
490f67924b
Co-Authored-By: Claude <noreply@anthropic.com>
OPT-36: test_opt36 reads possibly-missing values through nth()
checks / unit (pull_request) Failing after 35m3s
infra-pin / fresh (pull_request) Successful in 2s
60674e45ba
Co-Authored-By: Claude <noreply@anthropic.com>
OPT-36: test_opt34 and test_ux54 scenarios keep a shortfall B does not cover
checks / unit (pull_request) Successful in 27m17s
infra-pin / fresh (pull_request) Successful in 2s
6a527de851
With Cheap hours B counted, A buys only what B cannot reach. The OPT-34
night scenarios use a 1.6 x larger house profile, and the ux54 tab scenarios
a 4.8 kW evening (ux54_scenarios.make(evening=)), so A still charges; the
hard-coded levels become relations.

Co-Authored-By: Claude <noreply@anthropic.com>
OPT-36 round 2: test_opt34 keeps its tolerances; fresh sensors read the day they are on
checks / unit (pull_request) Successful in 20m36s
infra-pin / fresh (pull_request) Successful in 2s
3a18475a5a
The silent and fresh nights diverged only because the test's fresh sensors
kept reading the evening's 0.4 kWh 'left today' after midnight, while the
kept figure is the 4th's 6.0 kWh (solarfc.Hold.stand_in, as_today): two
different shortfalls (16.5 and 12.7 kWh) that A's ceiling hid until B was
counted. Fresh sensors now read 6.0 after midnight, so fresh and kept are
the same figure; the 0.3 tolerance is back, the other exact values are
recomputed. test_ux54 literals are recomputed too.

Co-Authored-By: Claude <noreply@anthropic.com>
OPT-36 round 3: B's charge counts the cheap time left; OPT-34 nights on their own shapes
checks / unit (pull_request) Failing after 36m29s
infra-pin / fresh (pull_request) Successful in 2s
e4b9dee699
The run is sized at the batteries' ratings, not the peak limit, so a run too
short to fill a Cheap hours battery is planned again later in the night.
Counting that run alone made B's give grow through the night and A's level
fall after A had bought (round 2: 55 -> 69 -> 41 -> 37 %, A to 56 %).
dayplan._cheap_give_kwh() now also counts the later firm slots before the
expensive hours that a run would still be worth charging in (price + the
spread control asks at most the give run's dearest slot); the Controller
passes the slots and the spread.

test_opt34: back to the original house shapes, fixture and expected values.
B is Off in the nights that test A's level (a Cheap hours B now covers
these hours); B's checks run in new `_b` nights; the 03:00 check runs in a
night from midnight; tail starts A at 60 %; late_fresh reads the kept 6.0.
test_opt36: slot cases, a whole night on one figure (level never moves),
F-1 with B counted (raised level reached and held). test_ux54: the run end
is read from the plan and asserted (02:30); the F1/F2 sweep uses a peak
limit that does not bind and reads a level shown as the target as at it.

Co-Authored-By: Claude <noreply@anthropic.com>
OPT-36 round 3: test_opt36 reads the mid_flat level through nth()
checks / unit (pull_request) Successful in 26m38s
infra-pin / fresh (pull_request) Successful in 2s
1ac7fe98d7
Co-Authored-By: Claude <noreply@anthropic.com>
OPT-36 round 4: count only charge control will make; Solar first drain at the night base
checks / unit (pull_request) Successful in 44m14s
infra-pin / fresh (pull_request) Successful in 2s
eb9ccc2730
Review F1: a slot after B's run counts only if no slot from the expensive
hours on is cheaper (control plans B's charge left in the cheapest window
ahead), and x 0.95 of B's charge is stored. Three-price night: 1ac7fe9 left
the expensive hours 2.61 kWh short; now 0.
Review F2: a Solar first battery's drain until the expensive hours reads each
hour at no less than the night base (the profile is net of the sun). Read net,
a dark morning drained the battery the plan still counted; A's level fell,
the cheap run moved and was lost before it started (+0.40 EUR per 3 days).
The two real-price replays now cost what 0.20.0 does.
F3: the ux54 sweep's peak limit is 15 kW (the option's top). F4: the rest
comes from the grid (EN/NL). F5: CHANGELOG and DECISIONS wording.

Co-Authored-By: Claude <noreply@anthropic.com>
Release 0.20.1
checks / unit (pull_request) Successful in 28m19s
infra-pin / fresh (pull_request) Successful in 1s
ec4ba49f45
Patch bump: OPT-36 (Leftover solar counts Cheap hours only and Solar first batteries). No option changes.

Co-Authored-By: Claude <noreply@anthropic.com>
REL-0.20.1 review: CHANGELOG known issues SAFETY-132/133 wording
checks / unit (pull_request) Failing after 22m19s
infra-pin / fresh (pull_request) Successful in 1s
01eba51dd7
SAFETY-132: the 9.6 kW is the charge the add-on asked for in a test,
not a measured grid figure. SAFETY-133: the quarter also ends over the
3 500 W limit; the guard sees the load late (0.20.1 has no lateness check).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TEST-37: test_opt34 builds the first Controller under the scenario clock
checks / unit (pull_request) Successful in 30m38s
infra-pin / fresh (pull_request) Successful in 2s
95bb214155
The stale_live scenario stores a latch ending 2026-11-04 07:15 CET. The first
Controller was built before the datetime patch, so Controller.__init__ judged
that latch against the real clock and it expired on 2026-11-04. Build it after
the patch (clock = scenario start), as the restart path already did. Test-only.

Co-Authored-By: Claude <noreply@anthropic.com>
Merge hotfix-0.20.1 (TEST-37 #231) into release-0.20.1
checks / unit (pull_request) Successful in 21m40s
infra-pin / fresh (pull_request) Successful in 2s
2fa14e23dc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge pull request 'Release 0.20.1' (#230) from release-0.20.1 into hotfix-0.20.1
checks / unit (pull_request) Successful in 21m15s
infra-pin / fresh (pull_request) Successful in 1s
3219aea9a0
Merge origin/master (0.20.1) into release/1.0
checks / unit (pull_request) Successful in 32m4s
infra-pin / fresh (pull_request) Successful in 2s
4f96397a60
Brings OPT-36 (#229) and TEST-37 (#231) onto release/1.0, which keeps
SAFETY-128 (#228). No textual conflicts. CHANGELOG: the SAFETY-128 entry
moved out of the released 0.20.0 section into "Not in a release yet" above
## 0.20.1; the 0.20.0 and 0.20.1 sections are identical to master.

Co-Authored-By: Claude <noreply@anthropic.com>
Author
Owner

Review SYNC-0.20.1: approved

Head reviewed: 4f96397 (ls-remote). Base release/1.0 371961e, master d53f70f. Nothing moved.

  1. Merge shape: OK. One merge commit, parents 371961e and d53f70f. I redid git merge --no-ff origin/master on 371961e: it merges with no conflicts, and the only difference from 4f96397 is the CHANGELOG move (4 lines).
  2. Both sides kept: OK. Every file only master changed (OPT-36, TEST-37; 11 files incl. dayplan.py, test_opt36.py, test_opt34.py, config.yaml) is byte-identical to master. Every file only release/1.0 changed (SAFETY-128; 10 files incl. lateness.py, control.py, peakguard.py, test_safety128.py) is byte-identical to release/1.0. In the 3 files both changed (main.py, en.json, nl.json), each side's hunks are exactly the same in the result.
  3. CHANGELOG and version: OK. The 0.20.0 and 0.20.1 sections are byte-identical to master (sha256 per section). SAFETY-128 sits under ### Not in a release yet above ## 0.20.1. config.yaml says 0.20.1. test_version_gate passes.
    • Release-time item, not a blocker: 0.20.1's known-issue line says the SAFETY-128 fix "is not in 0.20.1". Update it when this branch is released.
  4. SAFETY-128 and OPT-36 together: no interaction.
    • From the code: lateness.py does not import dayplan, and dayplan does not import lateness. The hold acts only on an unanswered power request. It does not change plan targets.
    • Local probe, not committed: a whole night through the real Controller, A Leftover solar, B Cheap hours, readings 0, 10 and 30 s late, 2 s ticks.
      • live (A 60 %, B 94 %): A's netted need is 0 and A takes no grid charge at every lag.
      • mid (both 25 %): A's level, need, grid energy (4 112 Wh) and end SoC (64.06 / 94.97 %) are identical at every lag. The late verdict is seen (delay fit 10-11 s and 29-31 s). It never holds or goes to 0 W, because at night no request is left unanswered.
      • With no lag there was no late verdict.
  5. Digests: OK. I ran these on base, head and master: ownls_state_epex, ownls_hourly_real (owner's shape), own_state_epex, own_hourly_real, own_dark_epex.
    • Owner's shape: same sha on base and head.
    • own_*: base differs from head, and head has the same sha as master. So OPT-36 alone causes the difference. All figures match the dev report.
    • own_state_epex and own_dark_epex give the same sha on head. Master gives the same result. It fits OPT-36: with B counted, the forecast no longer changes these 3 days. I did not trace it hour by hour.
  6. Tests: OK. Full suite on 4f96397 locally: goodwe-addon-checks:1, --network none, hwsim mounted, GOODWE_REQUIRE_HWSIM=1, so test_freeze part 3 ran on the rig hwsim. Result: 95 files, 0 failed, 1 776 s (suite.log). CI run 1534 checks / unit (pull_request) is a success on 4f96397, with refs/pull/233/head merged into release/1.0: 95 files, 0 failed. Run 1535 infra-pin is a success too.

No findings. Merging with head_commit_id pinned to 4f96397.

## Review SYNC-0.20.1: approved Head reviewed: `4f96397` (ls-remote). Base `release/1.0` 371961e, `master` d53f70f. Nothing moved. 1. **Merge shape: OK.** One merge commit, parents 371961e and d53f70f. I redid `git merge --no-ff origin/master` on 371961e: it merges with no conflicts, and the only difference from 4f96397 is the CHANGELOG move (4 lines). 2. **Both sides kept: OK.** Every file only master changed (OPT-36, TEST-37; 11 files incl. dayplan.py, test_opt36.py, test_opt34.py, config.yaml) is byte-identical to master. Every file only release/1.0 changed (SAFETY-128; 10 files incl. lateness.py, control.py, peakguard.py, test_safety128.py) is byte-identical to release/1.0. In the 3 files both changed (main.py, en.json, nl.json), each side's hunks are exactly the same in the result. 3. **CHANGELOG and version: OK.** The 0.20.0 and 0.20.1 sections are byte-identical to master (sha256 per section). SAFETY-128 sits under `### Not in a release yet` above `## 0.20.1`. config.yaml says 0.20.1. test_version_gate passes. - Release-time item, not a blocker: 0.20.1's known-issue line says the SAFETY-128 fix "is not in 0.20.1". Update it when this branch is released. 4. **SAFETY-128 and OPT-36 together: no interaction.** - From the code: lateness.py does not import dayplan, and dayplan does not import lateness. The hold acts only on an unanswered power request. It does not change plan targets. - Local probe, not committed: a whole night through the real Controller, A Leftover solar, B Cheap hours, readings 0, 10 and 30 s late, 2 s ticks. - `live` (A 60 %, B 94 %): A's netted need is 0 and A takes no grid charge at every lag. - `mid` (both 25 %): A's level, need, grid energy (4 112 Wh) and end SoC (64.06 / 94.97 %) are identical at every lag. The late verdict is seen (delay fit 10-11 s and 29-31 s). It never holds or goes to 0 W, because at night no request is left unanswered. - With no lag there was no late verdict. 5. **Digests: OK.** I ran these on base, head and master: ownls_state_epex, ownls_hourly_real (owner's shape), own_state_epex, own_hourly_real, own_dark_epex. - Owner's shape: same sha on base and head. - own_*: base differs from head, and head has the same sha as master. So OPT-36 alone causes the difference. All figures match the dev report. - own_state_epex and own_dark_epex give the same sha on head. Master gives the same result. It fits OPT-36: with B counted, the forecast no longer changes these 3 days. I did not trace it hour by hour. 6. **Tests: OK.** Full suite on 4f96397 locally: goodwe-addon-checks:1, `--network none`, hwsim mounted, GOODWE_REQUIRE_HWSIM=1, so test_freeze part 3 ran on the rig hwsim. Result: 95 files, 0 failed, 1 776 s (`suite.log`). CI run 1534 `checks / unit (pull_request)` is a success on 4f96397, with refs/pull/233/head merged into release/1.0: 95 files, 0 failed. Run 1535 infra-pin is a success too. No findings. Merging with `head_commit_id` pinned to 4f96397.
admin merged commit af2d0d2f61 into release/1.0 2026-11-05 21:15:15 +01:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/goodwe-addon#233