Files
goodwe-addon/goodwe_controller/DECISIONS/SAFETY-103-old-readings-after-a-drain.md
T
adminandClaude Opus 5.5 c6f23a69d4
checks / unit (pull_request) Successful in 34m52s
infra-pin / fresh (pull_request) Successful in 2s
SAFETY-103 review: correct the swing after a refusal or lost link in DECISIONS
The test table gives 1.5 kW at N=2 and 3.9 kW summed at N=3 under a
surplus, and about 1 kW after a lost link without one; not 1.9 kW.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 04:13:08 +02:00

4.1 KiB

SAFETY-103 - no swing on old battery readings after a drain ends

Status: implemented 2026-10-08, for review. Tests: test_safety103.py.

The defect (SAFETY-95 rig test, check 5, F1). While a battery settles from its ended drain, SAFETY-95 steers on the house's point: grid + every battery's reading - aim. The meter is 1 s old; a battery register can be 10 s old (the SAFETY-90 register model). At E+13 A's reading said 1238 W (6.8 s old) while A was at 616 W, so the house looked ~620 W bigger: B went 860 -> 1650 -> 870 W. B's own old reading does the same the other way.

The rule (main.py, _track_reads(), _drain_grid_w(), N>=2, only while a drain's tail settles):

  1. Each cycle, per battery: the range its power can be in. That is its last changed reading, widened by every command on its wire since (a lagging battery moves from one toward the others, never beyond). A new reading starts the range again.
  2. A reading that changed this cycle is taken as it reads. So is the side a battery refuses (SAFETY-73): it is not heading that way.
  3. The house point is held (first cycle: the loop's start from SAFETY-95). It moves only as far as the meter proves: to the nearest value of grid + the batteries' ranges - aim.

So the loop no longer moves on a reading that has not caught up. A real house step still moves it, as soon as the meter shows more than the batteries can explain.

Why not the tester's rule ("a battery whose reading is older than its last command counts at its command"). B's command changes almost every cycle, so B is almost always at its command. That is the plain meter, which chases A's tail: B swings 1420 W. A lag model needs the plant's time constant, which is not known for a client's inverter. Measured on the plant below (N=2, 1200 W, A Off; swing = a move of the others later undone):

rule 10 s lag 5 s lag 20 s lag surplus, 5 s lag
release/1.0 (readings) 760 840 150 800
tester's: at the command 1420 1020 1600 800
lag model, tau 10 s 70 140 80 580
lag model, tau 5 s, plant 20 s 600
hold all window 0 0 0 0
this rule 0 0 0 130

Holding all window ignores a house step: a 2 kW kettle at E+5 s imported 14.4 Wh over the next 30 s (release 11.5, this rule 11.3); 2 kW more sun exported 16.9 Wh (release 8.5, this rule 8.9).

Before -> after (test_safety103.py, tools/safety103_plant.py: 800 W/s ramp, 10 s lag, meter 1 s, registers 10 s at the rig's phases, B idles at -100 W; summed swing of the others in the 23 s after the end):

case release/1.0 after
rig check 5: N=2, 1200 W, A Off B 1140 -> 850 -> 1630 -> 870, swing 760 W B holds 1140 W
N=3, 1200 W / 4000 W, A Off 680 / 630 W 0 / 0 W
solar surplus, N=2 / N=3 890 / 740 W 130 / 130 W
lag 5 or 20 s, registers 5 or 10 s, N=2 and N=3 at most 10 W
registers as fresh as the meter (1 s) rows byte-identical
refusal end rows byte-identical
N=1, every end rows byte-identical (also against the release/1.0 tree)

Cost. In the window, a house change smaller than what the batteries could still be doing is not followed until a reading catches up (at most a register period) or the window ends (23 s). Under a surplus the others still swing up to 130 W, a third of a slew step.

Not covered.

  • Outside the drain tail the loop steers on the meter; readings only reach the saturation freeze (SAFETY-96), unchanged.
  • A lost link or a refusal ends the tail at once (A is out of the split, or reads ~0 W). The swing after them is the loop's own on a lagging plant, the same with 1 s registers and on release/1.0: under a surplus up to 1.5 kW at N=2 and 3.9 kW summed at N=3; after a lost link about 1 kW even without one. Not this card.
  • A battery that overshoots its command, or a reading offset beyond the range, can put the true power outside the range; the meter then moves the point, as before.
  • The end cycle's start (SAFETY-95 step 2) still uses the readings; the batteries were steady during the drain, so they are current there.