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.
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
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>
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>
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>
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>
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>
Patch bump: OPT-36 (Leftover solar counts Cheap hours only and Solar first batteries). No option changes.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
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>
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>
Head reviewed: 4f96397 (ls-remote). Base release/1.0371961e, masterd53f70f. Nothing moved.
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).
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.
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.
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.
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.
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.02026-11-05 21:15:15 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Real merge (--no-ff) of master
d53f70f(v0.20.1: OPT-36 #229, TEST-37 #231) into release/1.0371961e(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
Review SYNC-0.20.1: approved
Head reviewed:
4f96397(ls-remote). Baserelease/1.0371961e,masterd53f70f. Nothing moved.371961eandd53f70f. I redidgit merge --no-ff origin/masteron371961e: it merges with no conflicts, and the only difference from4f96397is the CHANGELOG move (4 lines).### Not in a release yetabove## 0.20.1. config.yaml says 0.20.1. test_version_gate passes.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.4f96397locally: 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 1534checks / unit (pull_request)is a success on4f96397, 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_idpinned to4f96397.