Files
goodwe-addon/goodwe_controller/DECISIONS/SAFETY-79-peak-margin.md
T
glenn schrooyenandClaude Opus 5.5 7c4c8f5c66
checks / unit (pull_request) Failing after 11m34s
infra-pin / fresh (pull_request) Successful in 1s
SAFETY-79: peak guard keeps a margin below the limit
New option peak_margin_w (default 50 W, 0-500 W). The peak guard, the
forecast checks and the maintenance charge sizing aim at
peak_cap_w - peak_margin_w; the month peak, the bill, headroom, warnings
and every notice keep the real cap. Owner decision 2026-09-28.

peak_margin_w 0 is byte-identical to ed739d4 (tools/safety79_identity.py,
test_peak_margin.py part 1). validate.py rule 12 refuses a value outside
0-500, not whole, or not below the peak limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UBtqPrG1mDLXzTEBnhEkkV
2026-09-28 23:09:39 +02:00

1.6 KiB

SAFETY-79 — the peak guard keeps a margin below the peak limit

Status: DECIDED 2026-09-28 (owner decision).

Decision

The peak guard aims at peak_cap_w - peak_margin_w, not at peak_cap_w. peak_margin_w is a setting: default 50 W, range 0-500 W, whole watts. An install without the key gets 50.

Why (owner): the guard aimed exactly at the cap, and in the OPT-11 P1 test a quarter banked 3501.9 W against a 3500 W limit (OPT-11-P1-test-report.md, check 4). A peak even 1 W over still sets the billed peak. It is a setting and not a constant because the add-on is sold to several sites.

What uses which number

  • cap - margin (the aim): peakguard.decide() (the claim's floor, and so arbitrage's charge_headroom_w() and OPT-11's give-way, which read it), shaping_suspended() and maintenance_charge_floor() (the forecast checks, via Controller.peak_aim_w()).
  • the real cap (reporting and billing): PeakTracker (month peak, headroom, 80 % warning), the decision's ceiling_w and every notice and status row built from it, Home's limit, and validate.config_notes().

A month peak already banked above cap - margin is aimed at as it stands (quarter_target_w() is quarter_ceiling_w() on the lowered cap), so the margin never holds a quarter below a peak that is already paid for.

Checks

peak_margin_w: 0 is byte-identical to ed739d4 (test_peak_margin.py part 1, digests from tools/safety79_identity.py). validate.py rule 12 refuses a value outside 0-500, not whole, or not below peak_cap_w.

Open

Whether the margin should also apply below a banked month peak above the cap is not decided here; today it does not.