During a HOLD the holding battery gives what keeps the quarter at the peak aim. N=1: the peak floor is refiled at P_MAINTENANCE, as for a drain (SAFETY-83). N>=2: the hold's pin is raised to the floor minus what the other live batteries may place, so they give first. A site stop still decides alone. The hold ends on its clock even if the level dipped. Below the aim nothing changes (rows identical). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
35 lines
1.9 KiB
Markdown
35 lines
1.9 KiB
Markdown
# SAFETY-83 - the peak guard outranks the maintenance drain at N=1
|
|
|
|
**Status: DECIDED 2026-09-29 (owner go, P1).** Follows SAFETY-78's "Not
|
|
changed" list. Tests: `test_safety83.py`.
|
|
|
|
**The defect.** At N=1 the drain is the site SET at `P_MAINTENANCE` (60). The
|
|
peak floor is a limit at `P_PEAK` (50), and clause 2 applies only limits at or
|
|
above the winner, so the floor was ignored and the house imported what the
|
|
drain left. A 5000 W house under a 2500 W limit and a 2000 W drain banked
|
|
3000 W quarters; on the owner's settings (limit 3500, drain 2500, margin 50) a
|
|
breach starts at about 5950 W.
|
|
|
|
**The fix.** While a drain owns the setpoint at N=1 and the peak floor asks
|
|
for more than the drain rate, `main.py` (`_peak_over_drain()`) refiles that
|
|
peak claim at `P_MAINTENANCE`. The arbiter then applies it: the target is the
|
|
floor, still inside the safety limits, and a safety SET (100) still parks
|
|
everything. Where the floor is at or below the drain rate nothing changes.
|
|
|
|
- The order in effect is now the same at N=1 and N=2: safety limits and the
|
|
SAFETY-73 refusal > peak guard (with the SAFETY-79 margin) > maintenance >
|
|
modes > trade > loop (`arbiter.py`, beside the priorities).
|
|
- The drain keeps counting: the battery discharges at least the drain rate on
|
|
every cycle, so the cycle is not reset or stalled; it only ends sooner.
|
|
- Below the drain rate the rest is still exported (owner, 2026-09-29).
|
|
- Identity: `tools/safety78_identity.py --diff` against 35cdffe changes only
|
|
cycles where the guard asked for more than the drain; after such an episode
|
|
the commands are identical again and only the log and the idle loop's
|
|
integrator differ. N=2 is unchanged.
|
|
|
|
**Not changed.**
|
|
- Between the drain rate and the peak aim, the house still imports what the
|
|
drain leaves (N=1 behaviour kept by the identity). At N=2 the loop covers it.
|
|
- A maintenance HOLD (0 W, 120 min, battery full) still outranked the peak
|
|
guard at N=1. Fixed by [[SAFETY-86]].
|