Commit Graph
721 Commits
Author SHA1 Message Date
glenn schrooyenandClaude 1ac7fe98d7 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
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-02 10:57:22 +01:00
glenn schrooyenandClaude e4b9dee699 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
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>
2026-11-02 10:15:20 +01:00
glenn schrooyenandClaude 3a18475a5a 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
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>
2026-11-02 05:34:32 +01:00
glenn schrooyenandClaude 6a527de851 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
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>
2026-11-02 03:30:39 +01:00
glenn schrooyenandClaude 60674e45ba 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
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-02 02:50:33 +01:00
glenn schrooyenandClaude 490f67924b 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
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-02 02:43:02 +01:00
glenn schrooyenandClaude 05746c0564 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
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>
2026-11-02 02:03:04 +01:00
admin 01e88a24f2 Release 0.20.0: release/1.0 -> master (#227) v0.20.0 2026-11-01 18:58:04 +01:00
admin 77fe31b161 Merge pull request 'Release 0.20.0' (#226) from release-0.20.0 into release/1.0
infra-pin / fresh (push) Successful in 1s
checks / unit (pull_request) Successful in 18m57s
infra-pin / fresh (pull_request) Successful in 1s
2026-11-01 17:22:03 +01:00
glenn schrooyenandClaude Opus 5.5 e9fa11e60d Release 0.20.0 review: CHANGELOG wording (OPT-23 headline, CI-15 time, UX-75 quote)
checks / unit (pull_request) Successful in 19m14s
infra-pin / fresh (pull_request) Successful in 1s
- The OPT-23 headline said a daily-total forecast is now counted right by
  day; OPT-30 (known issue) still over-counts by day. Say what changed.
- CI-15: 16 min was one run; runs took 16 to 24 min.
- UX-75: quote the page text as it reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 17:02:17 +01:00
glenn schrooyenandClaude d2f63fe86a Release 0.20.0: version bump and CHANGELOG
checks / unit (pull_request) Successful in 24m4s
infra-pin / fresh (pull_request) Successful in 2s
config.yaml version 0.20.0 (minor; no option change) and a ## 0.20.0
CHANGELOG section that folds in the unreleased block and lists every
first-parent merge since 0.19.2 (f1c9733).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
2026-11-01 16:12:45 +01:00
admin cdafa2c189 Merge pull request 'OPT-34 fix (rig F-1): a replaced evening forecast does not come back' (#225) from OPT-34-fix into release/1.0
infra-pin / fresh (push) Successful in 1s
2026-11-01 14:21:44 +01:00
glenn schrooyenandClaude 6263a57b6b OPT-34 fix (rig F-1): a replaced evening forecast does not come back
checks / unit (pull_request) Successful in 21m5s
infra-pin / fresh (pull_request) Successful in 2s
After a fresh today reading in the night (02:00 on the rig) went stale
(05:00), Hold.stand_in(as_today=True) served the 17:05 "tomorrow" figure
again until 06:00: the plan went back to 95 %, the latch took it, and
Leftover solar charged back up. read_sum() now skips that stand-in once
every today sensor has given a fresh reading for the day
(Hold.replaced()); the stale newer reading follows the OPT-27 rules.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 12:26:05 +01:00
admin d4c01daca5 Merge pull request 'MAINT-20 part 1: comments and docstrings in peakguard and the price files' (#223) from MAINT-20-1 into release/1.0
infra-pin / fresh (push) Successful in 1s
MAINT-20 part 1: comments and docstrings in peakguard.py and the price files cut to one line + a DECISIONS pointer (#223)
2026-11-01 03:24:54 +01:00
glenn schrooyenandClaude Opus 5.5 63dda4b0d3 MAINT-20 part 1 review: restore three lost lines, rewrap ragged comments
checks / unit (pull_request) Successful in 19m51s
infra-pin / fresh (pull_request) Successful in 2s
Comment, docstring and DECISIONS only (same-code script OK; python -OO
bytecode now identical for all six files).
- peakguard.charge_room_w: one-line docstring + SAFETY-125 pointer (cut whole).
- peakguard.shaping_suspended: keep 'the one place this conjunction is
  written; do not inline it'.
- price_forecast module note: an hour with no price and no yesterday is left
  out, never filled (test_price.py 14d cites the module note for it).
- Refill comment blocks the cut left ragged; DECISIONS pointers on one line.
- FCST-04 note: _yesterday_of(), not _previous_day(); FCST-03 note: the 49
  stronger cases are why the rule is a real change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
2026-11-01 03:02:51 +01:00
glenn schrooyenandClaude 6e2fde63ff MAINT-20 part 1: peakguard.py keeps the reference-pack sentence a test pins
checks / unit (pull_request) Successful in 25m32s
infra-pin / fresh (pull_request) Successful in 2s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:55:59 +01:00
glenn schrooyenandClaude 72ccfd788c MAINT-20 part 1: price_chain.py comments trimmed
checks / unit (pull_request) Failing after 38m0s
infra-pin / fresh (pull_request) Successful in 2s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:19:29 +01:00
glenn schrooyenandClaude c432d4e419 MAINT-20 part 1: price_forecast.py comments and docstrings cut
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:19:29 +01:00
glenn schrooyenandClaude 50332d4190 MAINT-20 part 1: DECISIONS note for the forecast fill (FCST-03)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:19:29 +01:00
glenn schrooyenandClaude eda6d0aed1 MAINT-20 part 1: custom_formula.py comments cut; new TARIFF-16 note
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:17:46 +01:00
glenn schrooyenandClaude a3f725e8a4 MAINT-20 part 1: price_history.py comments cut; FCST-04 note gets the store details
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:16:53 +01:00
glenn schrooyenandClaude c08eb124c8 MAINT-20 part 1: price.py comments and docstrings cut to a line and a pointer
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:15:46 +01:00
glenn schrooyenandClaude 194efac98d MAINT-20 part 1: DECISIONS note for the price provider plugins (TARIFF-02)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:15:46 +01:00
glenn schrooyenandClaude 96ca66fc38 MAINT-20 part 1: peakguard.py comments and docstrings cut to a line and a pointer
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:13:48 +01:00
glenn schrooyenandClaude e0db3b0e8a MAINT-20 part 1: DECISIONS note for the peak guard (SAFETY-07, -24, CFG-03)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:13:48 +01:00
glenn schrooyenandClaude f31307105a MAINT-20 part 1: same-code and ratio script
Co-Authored-By: Claude <noreply@anthropic.com>
2026-11-01 01:13:48 +01:00
admin 8afadbdd9a OPT-34: the night charge keeps its level when the kept forecast ends (#222)
infra-pin / fresh (push) Successful in 2s
2026-11-01 00:19:09 +01:00
glenn schrooyenandClaude bb57a85b35 OPT-34 round 3: the latch ends on time, with the run, with the mode, and with the night
checks / unit (pull_request) Successful in 20m15s
infra-pin / fresh (pull_request) Successful in 1s
- F1: an expired latch is dropped whatever the run state; plan_targets() and the
  store loader ignore one that has ended (no stale latch after a restart).
- F2: the "charge goes on" line only while a cheap run is on.
- F3: a battery that is off or no longer Leftover solar leaves the latch.
- F4: a run carries the latch on only if it starts by the bell start (06:00
  local) after the latch began (manager's call, 2026-10-31).
Tests: test_opt34 scenarios stale/stale_live and part 5; test_modes_p4 asks at a
moment inside the latch.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
2026-10-31 23:04:36 +01:00
Claude (OPT-34 review) 57b0eb2f20 OPT-34 review: read the inverter in part 4 with a default
checks / unit (pull_request) Successful in 23m57s
infra-pin / fresh (pull_request) Successful in 2s
test_named_absence.py flagged next() without a default (CI run 1473).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 21:18:59 +01:00
Claude (OPT-34 review) 8f0af9f318 OPT-34 review: test that a battery leaving the plan leaves the latch
checks / unit (pull_request) Failing after 19m32s
infra-pin / fresh (pull_request) Successful in 1s
test_opt34.py part 4 drives Controller._latch_charge() directly: a battery
that leaves the plan is dropped at once, does not rejoin while nothing
charges, an empty latch is cleared from the store, and the screen says the
charge goes on only while the latched battery is below its level. Catches
two reviewer mutants that survived the round-2 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 20:50:02 +01:00
glenn schrooyenandClaude eee7616022 OPT-34 round 2: the latch follows the plan, up or down, and drops what left it
checks / unit (pull_request) Successful in 18m37s
infra-pin / fresh (pull_request) Successful in 1s
While a forecast is present and the cheap run holds, the night-charge latch is
rebuilt from the plan: a latched battery takes its current level whatever its
role this tick (a fresh figure may lower it below the SoC), one that left the
plan is dropped, a new one comes in only while it charges from the grid.
Test: a fresh brighter figure lowers A below its SoC, the sensors go quiet, A
does not charge back up. CHANGELOG summary says two fixes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
2026-10-31 19:17:20 +01:00
glenn schrooyenandClaude 412e47f111 OPT-34: the night charge keeps its level when the kept forecast ends
checks / unit (pull_request) Successful in 18m59s
infra-pin / fresh (pull_request) Successful in 1s
With the forecast sensors silent from 17:05 the kept evening figure ended at
midnight + 3 h, in the middle of the night grid charge, and Leftover solar's
planned level went with it (rig: A 49.8 % against 79.9 %).

- A night grid charge that has started keeps its planned level when the forecast
  goes (Controller._latch_charge, store key night_charge_latch), and the narrow
  price gap with it (weather_held). A fresh figure still moves the level.
- The evening's tomorrow figure is today's until the bell starts (06:00 local),
  up to 15 h old but only if it was usable at midnight (solarfc.Hold.stand_in).
- Screen: "No solar forecast now. The charge under way goes on to its planned
  level." (EN/NL) while a latched battery is below its level.
- test_opt34.py (whole nights through the real control cycle, mutants), test_opt27
  and test_modes_p4 adjusted to the new window and the kept level.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
2026-10-31 17:00:31 +01:00
admin deee98d352 Merge pull request 'SAFETY-126: no false peak reserve alarm during the night grid charge' (#221) from SAFETY-126 into release/1.0
infra-pin / fresh (push) Successful in 2s
2026-10-31 12:26:03 +01:00
glenn schrooyenandClaude a66a7f1a0f SAFETY-126 round 2: state what the 10 W tolerance costs; test the latch reset
checks / unit (pull_request) Successful in 18m11s
infra-pin / fresh (pull_request) Successful in 2s
Text only (DECISIONS, DOCS, CHANGELOG): a step load alarms on the same cycle
as before; a steadily rising load alarms a little later; with peak_margin_w 0
a period can end up to 10 W over the limit without this alarm, as for 'Load
too high' (SAFETY-81). Owner accepted 2026-10-31.

test_safety126.py part 2: after a real overrun ends, _peak_landing()'s latch
is reset and the night's later landings are quiet (review F3).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-31 11:52:27 +01:00
glenn schrooyenandClaude 8e2463bc32 SAFETY-126: test reads through named_absence; points at MUTATION.md
checks / unit (pull_request) Successful in 19m25s
infra-pin / fresh (pull_request) Successful in 1s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-31 01:54:16 +01:00
glenn schrooyenandClaude 40595e42ae SAFETY-126: a landing error on the aim is not an overrun
During the night grid charge under the peak reserve the guard alarmed
(ERROR PEAK_GUARD_FLOOR_LIMITED, "Battery stopped limiting the peak") and the
Batteries "Why" said "The peak limit needs power" at quarter ends, while every
quarter landed on the aim. The charge lands the period 0-7 W over the aim and
excess_w()'s 900 / remaining turns that into tens of kW in the last second;
with nothing able to give, check_peak_floor_limited() alarmed on any
wanted > 0.

Each cycle the guard wants power, _peak_landing() asks misses_aim(): with the
batteries at 0 W, does the period end more than ALARM_MISS_W over the aim? If
not, the decision is within_aim: no reserve alarm, no red peak row, no "Why"
peak. The claim and the commands are unchanged. A real overrun under the
reserve alarms on the same cycle as before.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-31 01:26:02 +01:00
admin cc9bbeb73e Merge pull request 'OPT-27: the night cheap run is judged with the last fresh forecast' (#219) from OPT-27 into release/1.0
infra-pin / fresh (push) Successful in 2s
2026-10-30 22:16:32 +01:00
glenn schrooyenandClaude b9dea6d90d OPT-27 round 3: the kept-forecast wording states the 12 h limit (text only)
checks / unit (pull_request) Successful in 15m27s
infra-pin / fresh (pull_request) Successful in 1s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 21:29:29 +01:00
glenn schrooyenandClaude 13b61d586a OPT-27 round 3: a kept figure is at most 12 h old; DST-safe midnight bound; pruning test
checks / unit (pull_request) Successful in 15m37s
infra-pin / fresh (pull_request) Successful in 1s
F1: the evening rule has an age limit (KEPT_MAX_AGE_S).
F2: the evening bound is the next local midnight (_bounds), right on 25 h and 23 h days.
F5: test that old days are dropped from the stored hold.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 20:41:34 +01:00
glenn schrooyenandClaude Opus 5.5 307cb47caf OPT-27 review: the kept forecast line says "kept for tonight"
checks / unit (pull_request) Successful in 17m20s
infra-pin / fresh (pull_request) Successful in 1s
With battery modes off the forecast is still read and shown (Settings,
Simple, and the Forecast tab), but no plan uses it, so "used for tonight's
plan" was not true there (UX-53). "Kept for tonight" is true in every
shape. NL "vanavond" read wrong after midnight; "vannacht" covers the
whole night. DECISIONS' "modes off: nothing reads the sensors" corrected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
2026-10-30 19:59:29 +01:00
glenn schrooyenandClaude 60aa7f310c OPT-27: a kept figure sizes the night under OPT-28's shortfall, as a fresh one does
checks / unit (pull_request) Successful in 17m22s
infra-pin / fresh (pull_request) Successful in 1s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 18:57:37 +01:00
glenn schrooyenandClaude bf842997c1 OPT-27 round 2: a kept tomorrow figure is bounded on both sides
Hold.stand_in(): day0 <= now < day0 + 3 h. A tomorrow sensor is asked with
day0 = the next midnight, so the upper bound alone was true all day and a
figure stood in at any hour. test_opt27.py covers daytime tomorrow outages.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 18:54:41 +01:00
glenn schrooyenandClaude 4fde0b177c OPT-27: test_modes_p4 says what a covered sensor does; test_opt27 guards
test_modes_p4.py: 'the forecast is lost mid-run' now loses it for good (a hold
that never covers); a second scenario is the same loss with the hold, which
keeps Leftover solar charging. test_opt27.py: a failing check no longer hides
the ones after it.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 18:54:41 +01:00
glenn schrooyenandClaude 50b567d6d1 OPT-27: the night plan keeps the last fresh solar forecast
Forecast.Solar's sensors stop updating in the evening, so at night every such
site had no forecast: the night cheap run needed 1.5x the cycle cost and
Leftover solar planned nothing. One missed poll also dropped it at once.

solarfc.Hold keeps the last fresh reading of each sensor, for the day it is
for. A missing or stale sensor is covered by it for up to FRESH_S (a short
outage), and from the end of the bell (20:00) until local midnight + 3 h
however old. A tomorrow figure is tomorrow's until midnight and then today's;
a today figure is only today's. Stored in the controller's store
(solar_fc_hold), so an evening restart keeps it. The forecast line says
'Solar forecast read 17:05, used for tonight's plan.' (EN, NL).

test_tariff24_weather.py runs with a hold that never covers: it tests what the
plan does when the forecast IS lost.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 18:54:41 +01:00
admin f1b72443ff Merge pull request 'Sync 0.19.x into release/1.0: TARIFF-24 and SAFETY-125, with the forward-port fixes' (#220) from sync-0.19 into release/1.0
infra-pin / fresh (push) Successful in 2s
2026-10-30 18:51:53 +01:00
glenn schrooyenandClaude 60a3fa615e TARIFF-24 forward-port: part 6 reads its last row through nth() (test_named_absence)
checks / unit (pull_request) Successful in 28m5s
infra-pin / fresh (pull_request) Successful in 2s
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 08:30:25 +01:00
glenn schrooyenandClaude 1c9fefd9dd Sync 0.19: the Forecast tab extends a cheap run with control's gap (TARIFF-24)
UX-54's Forecast tab (release/1.0) re-plans tomorrow's cheap run as control
will, with the gap `required`. It still added the "no weather" x1.5 always,
while control (TARIFF-24, from master) drops it when a forecast is read. In
the UX-54 scenarios the tab asked 0.225 where control needs 0.15. It now asks
`arbitrage.weather_uncertainty(True, c.solar_fc)`, so the two agree again, as
they did on release/1.0 alone. Display only; no control change. The tab's
blocks in every UX-54 scenario, every 2 h, are identical to release/1.0.

test_ux54.py pins it (the old line fails it). The TARIFF-24 DECISIONS note
names the tab as the fourth place that reads the rule.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 08:11:42 +01:00
glenn schrooyenandClaude 8cca671a05 TARIFF-24 / SAFETY-125 forward-port: record text (review F2, F3, CHANGELOG nit)
- TARIFF-24 DECISIONS: the hold keeps a Cheap hours charge; a Leftover solar
  battery still stops without a forecast (it has no planned level, still true
  after OPT-28: `dayplan.plan()` returns None and `charge_to()` gives None).
  Adds "a run that has started runs to its end" for a give run begun in a hold.
- CHANGELOG 0.19.2: "when the period is over" -> "when the period would go
  over the limit".

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 08:09:44 +01:00
glenn schrooyenandClaude 9dc22fb987 TARIFF-24 forward-port: pin the hold's edges (review F1, F2)
Part 6 of test_tariff24_weather.py, the review's probes R4 and R5:
- R4: a forecast lost while only the day's give run is under way holds to
  that run's end (22:00) and no further; the night's cheap run then needs the
  wider gap. Pins the `solar_fc is None` return in `_wx_track()`: without it
  the hold chains into the cheap run and B charges (mutant M2, now red).
- R5: a give run that starts during a hold runs to its end (`keep_started()`),
  with no grid charge after the hold. Behaviour unchanged; a give run not kept
  goes red.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 08:09:44 +01:00
glenn schrooyenandClaude 5ac1017eed UX-54 scenarios keep the gate they were written for (cycle cost 0.15 with a forecast)
With master merged in (TARIFF-24), a forecast replaces the 1.5x widening, so
the shipped cost 0.10 is now the whole gap with a forecast. test_ux54.py's
modes-on scenarios were written for 0.15 and 21 of its checks failed on
release/1.0 + master (they pass on release/1.0 alone). They now set the cost
to 0.15 where they have a forecast: the same gate as before. Scenarios with no
forecast (nofc, stale) and modes off keep 0.10, widened as before.
(cherry picked from commit 665653517c, branch OPT-27)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-30 08:09:44 +01:00