- quarter_target_w() is now the margin under quarter_ceiling_w(): a peak
banked between cap - margin and the cap was aimed at exactly, so one
overshoot crossed the cap; above the cap the same overshoot raised the bill.
- safety79_identity.py hashes charge_headroom_w() and the shaping suspension,
and adds n1_maint/n2_maint (cheap window, forecast across cap - 50 .. cap).
Digests re-printed on ed739d4 and pinned; margin 0 still identical.
- test_margin_under_banked_peak; DECISIONS and DOCS updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 lines
2.0 KiB
Markdown
42 lines
2.0 KiB
Markdown
# SAFETY-79 — the peak guard keeps a margin below the peak limit
|
|
|
|
**Status: DECIDED 2026-09-28 (owner decision).**
|
|
|
|
## Decision
|
|
|
|
The peak guard aims `peak_margin_w` under the limit it holds: under
|
|
`peak_cap_w`, or under a month peak already banked above it
|
|
(`quarter_target_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.
|
|
|
|
**Banked peak (review, 2026-09-29):** the PR first aimed at a banked month
|
|
peak as it stood. That can bank a few W over it, which raises the bill the
|
|
same way; and a peak banked between cap - margin and the cap (common in the
|
|
month an install upgrades, since the old guard held quarters at the cap) was
|
|
aimed at exactly, so one overshoot went over the cap. The margin now applies
|
|
under it too. Cost: up to `peak_margin_w` of a peak already paid for goes
|
|
unused (more discharge, less charge headroom); no bill cost. Undo is one line
|
|
in `quarter_target_w()`.
|
|
|
|
## What uses which number
|
|
|
|
- **the aim:** `peakguard.decide()` (the claim's floor, and so arbitrage's
|
|
`charge_headroom_w()` and OPT-11's give-way, which read it), and, as cap -
|
|
margin, `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()`.
|
|
|
|
## Checks
|
|
|
|
`peak_margin_w: 0` is byte-identical to ed739d4 (test_peak_margin.py part 1,
|
|
digests from tools/safety79_identity.py, which also runs a maintenance charge
|
|
with a forecast). validate.py rule 12 refuses a value outside 0-500, not
|
|
whole, or not below `peak_cap_w`.
|