TEST-21: delivery_hold_s's floor re-derived — 30.0 s did not reproduce, it is 35.0 s, and 185 still stands #67

Merged
admin merged 2 commits from TEST-21 into release/1.0 2026-09-07 22:41:36 +02:00
Owner

No code changes and no option changes. What changes is a number this repository claimed about a shipped safety constant.

Companion to admin/goodwe#46, which commits the harness.

The finding

delivery_hold_s: 185 shipped resting on "30.0 s, the worst continuous legitimate episode at the shipped threshold across all 2640 runs", plus a lag sweep giving 45.0 s and 90.0 s — from a harness DECISIONS/SAFETY-42 §1 deliberately did not commit, because it imports plantsim and frame_check out of admin/goodwe. So the floor under a safety constant could not be re-run. PROC-01's shape, eleven instances in three days.

⚠️ 30.0 s did not reproduce. It is 35.0 s.

Recorded as measured. The harness was not adjusted until it agreed — the one correction made during the rebuild was made against the doc's number (admin/goodwe#46 and §1 record it) and made every figure larger.

threshold reversals (1050) flapping (840) weighted (750) N=1 control (2640)
100 W 120.0 s 0.0 s 165.0 s 0.0 s
250 W 65.0 s 0.0 s 100.0 s 0.0 s
500 W 30.0 s 0.0 s 35.0 s 0.0 s
700 W 25.0 s 0.0 s 30.0 s 0.0 s
1000 W 15.0 s 0.0 s 25.0 s 0.0 s
1500 W 0.0 s 0.0 s 20.0 s 0.0 s
2000 W 0.0 s 0.0 s 0.0 s 0.0 s

Reproduced — and it is the part the compatibility promise rests on:

  • the N=1 control arm is 0.0 s at every threshold in every run. §3.2's algebra measured, not argued.
  • the flapping family is 0.0 s everywhere in all 840 runs — the family that defeated SAFETY-30's site window still cannot touch this check.
  • 45.0 s at twice A's lag, to the cycle.

Not reproduced:

  • the headline, by one control cycle (30.0 → 35.0 s); the reversal family by two (20.0 → 30.0 s).
  • the non-monotone curve. §3.3 finding 3 read 55.0 s at 250 W against 40.0 s at 100 W as a property of the reduction. This rebuild's curve is monotone. The mechanism is real and is still the reason not to read either table as a sensitivity curve — but it is an argument now, not a measurement, and it is written down as weaker.
  • the low-threshold rows, badly (40.0 → 120.0 s at 100 W). Nothing ships there, but that gap is the honest measure of how much of the original table was a property of axes nobody wrote down. §3.3 fixes every axis count and range and no individual value; the rebuild prints its choices above its own table so the caveat travels with the number.
  • the four-times-slower row moved the other way: 75.0 s, not 90.0 s.

185 s is NOT changed, and here is the arithmetic

bound claimed now
the rig's worst legitimate episode 6.2× (30.0 s) 5.29× (35.0 s)
a hypothetical 4×-slower second unit 2.06× (90.0 s) 2.47× (75.0 s)
firmware veto LINK_STALE_S 6.2× (30 s) 6.2× (30 s)
inside the billing quarter 4.9× (900 s) 4.9× (900 s)

All four still clear, none narrowly, and the ordering of the argument is unchanged. A shipped safety constant is not moved because a margin went from 6.2× to 5.3× while every bound holds — moving it would re-open SAFETY-41's four-attempt false-trip regime, after "i'm getting really annoyed by the notifications in my HA", for nothing. What changed is the claim, not the value.

⚠️ One consequence an operator can hit

At 30.0 s the rig's floor coincided with the firmware's 30 s veto and a reader could not tell which was binding. At 35.0 s the rig's is strictly the binding one — and it is ABOVE the schema's own int(30,900) minimum. So delivery_hold_s: 30 is now below a measured legitimate episode and would report on ordinary saturation. Both translations now say so in the operator's own words.

Raising the schema floor to 35 is a change to a shipped option's contract and is left to its own card rather than smuggled in here.

⚠️ The B-lag rows are relabelled throughout

Inverter B does not physically exist (~2026-09-12). plantsim.INVERTER_B is inverter A copied, measured=False. Every two-inverter figure in this record is two copies of A, and the lag sweep is a sensitivity study on a hypothesis about B, not a measurement of it. The old wording — "MEASURED, not felt" — overstated exactly that. And "the RAMP does not move it at all" was too strong: halving it is worth 0 s at A's lag and +5 s at twice it.

Reproducing it — the ⭐ acceptance criterion

The harness is committed in admin/goodwe as sim/delivery_window.py, not here: plantsim and frame_check are that repo's, and restating them in this tree is CI-08's coupling, which has broken three times. There the dependency runs one way — this repo's control, arbiter and the shipped delivery.delinquent() are imported live.

cd goodwe/sim && GOODWE_ADDON_REPO=/path/to/goodwe-addon \
    python3 -B delivery_window.py --selftest --campaign --blag

Its --selftest is now a step in that repo's checks.yml, so a change here that moves saturation_w, max_w, delivery_hold_s or the shape of delinquent() turns that step red instead of leaving a stale number here.

Touched

DECISIONS/SAFETY-42-unit-delivery.md §1 and §3.3 (both tables kept — a record that quietly replaced its own numbers is worth less than one that shows both), app/delivery.py's provenance comment, config.yaml's delivery_hold_s block, translations/en.yaml + nl.yaml, CHANGELOG.md Unreleased.

Checks

26 files, all checks passed, exit 0 — goodwe-addon-checks:1, the same suite green on release/1.0 before the change and after it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU

**No code changes and no option changes.** What changes is a number this repository claimed about a shipped safety constant. Companion to **admin/goodwe#46**, which commits the harness. ## The finding `delivery_hold_s: 185` shipped resting on *"30.0 s, the worst continuous legitimate episode at the shipped threshold across all 2640 runs"*, plus a lag sweep giving 45.0 s and 90.0 s — from a harness `DECISIONS/SAFETY-42` §1 deliberately did **not** commit, because it imports `plantsim` and `frame_check` out of `admin/goodwe`. So the floor under a safety constant could not be re-run. PROC-01's shape, eleven instances in three days. ## ⚠️ 30.0 s did not reproduce. It is 35.0 s. Recorded as measured. **The harness was not adjusted until it agreed** — the one correction made during the rebuild was made *against* the doc's number (admin/goodwe#46 and §1 record it) and made every figure **larger**. | threshold | reversals (1050) | flapping (840) | weighted (750) | N=1 control (2640) | |---:|---:|---:|---:|---:| | 100 W | 120.0 s | 0.0 s | 165.0 s | **0.0 s** | | 250 W | 65.0 s | 0.0 s | 100.0 s | **0.0 s** | | **500 W** | **30.0 s** | **0.0 s** | **35.0 s** | **0.0 s** | | 700 W | 25.0 s | 0.0 s | 30.0 s | **0.0 s** | | 1000 W | 15.0 s | 0.0 s | 25.0 s | **0.0 s** | | 1500 W | 0.0 s | 0.0 s | 20.0 s | **0.0 s** | | 2000 W | 0.0 s | 0.0 s | 0.0 s | **0.0 s** | **Reproduced** — and it is the part the compatibility promise rests on: * the **N=1 control arm is 0.0 s at every threshold in every run**. §3.2's algebra measured, not argued. * the **flapping family is 0.0 s everywhere** in all 840 runs — the family that defeated SAFETY-30's site window still cannot touch this check. * **45.0 s at twice A's lag**, to the cycle. **Not reproduced:** * the headline, by one control cycle (30.0 → 35.0 s); the reversal family by two (20.0 → 30.0 s). * the **non-monotone curve**. §3.3 finding 3 read 55.0 s at 250 W against 40.0 s at 100 W as a property of the reduction. This rebuild's curve is **monotone**. The mechanism is real and is still the reason not to read either table as a sensitivity curve — but it is an **argument** now, not a measurement, and it is written down as weaker. * the low-threshold rows, badly (40.0 → 120.0 s at 100 W). Nothing ships there, but that gap is the honest measure of how much of the original table was a property of axes nobody wrote down. §3.3 fixes every axis *count* and *range* and no individual *value*; the rebuild prints its choices above its own table so the caveat travels with the number. * the four-times-slower row moved the **other** way: 75.0 s, not 90.0 s. ## 185 s is NOT changed, and here is the arithmetic | bound | claimed | now | |---|---:|---:| | the rig's worst legitimate episode | 6.2× (30.0 s) | **5.29× (35.0 s)** | | a hypothetical 4×-slower second unit | 2.06× (90.0 s) | **2.47× (75.0 s)** | | firmware veto `LINK_STALE_S` | 6.2× (30 s) | 6.2× (30 s) | | inside the billing quarter | 4.9× (900 s) | 4.9× (900 s) | All four still clear, none narrowly, and the ordering of the argument is unchanged. A shipped safety constant is not moved because a margin went from 6.2× to 5.3× while every bound holds — moving it would re-open SAFETY-41's four-attempt false-trip regime, after *"i'm getting really annoyed by the notifications in my HA"*, for nothing. **What changed is the claim, not the value.** ## ⚠️ One consequence an operator can hit At 30.0 s the rig's floor coincided with the firmware's 30 s veto and a reader could not tell which was binding. At **35.0 s the rig's is strictly the binding one — and it is ABOVE the schema's own `int(30,900)` minimum**. So `delivery_hold_s: 30` is now below a *measured* legitimate episode and would report on ordinary saturation. Both translations now say so in the operator's own words. Raising the schema floor to 35 is a change to a shipped option's contract and is **left to its own card** rather than smuggled in here. ## ⚠️ The B-lag rows are relabelled throughout Inverter B **does not physically exist** (~2026-09-12). `plantsim.INVERTER_B` is inverter A **copied**, `measured=False`. Every two-inverter figure in this record is two copies of A, and the lag sweep is a **sensitivity study on a hypothesis about B**, not a measurement of it. The old wording — *"MEASURED, not felt"* — overstated exactly that. And *"the RAMP does not move it at all"* was too strong: halving it is worth 0 s at A's lag and **+5 s at twice it**. ## Reproducing it — the ⭐ acceptance criterion The harness is committed in `admin/goodwe` as `sim/delivery_window.py`, **not here**: `plantsim` and `frame_check` are that repo's, and restating them in this tree is CI-08's coupling, which has broken three times. There the dependency runs one way — this repo's `control`, `arbiter` and the shipped `delivery.delinquent()` are imported **live**. ``` cd goodwe/sim && GOODWE_ADDON_REPO=/path/to/goodwe-addon \ python3 -B delivery_window.py --selftest --campaign --blag ``` Its `--selftest` is now a step in that repo's `checks.yml`, so **a change here** that moves `saturation_w`, `max_w`, `delivery_hold_s` or the shape of `delinquent()` turns that step red instead of leaving a stale number here. ## Touched `DECISIONS/SAFETY-42-unit-delivery.md` §1 and §3.3 (both tables kept — a record that quietly replaced its own numbers is worth less than one that shows both), `app/delivery.py`'s provenance comment, `config.yaml`'s `delivery_hold_s` block, `translations/en.yaml` + `nl.yaml`, `CHANGELOG.md` Unreleased. ## Checks **26 files, all checks passed, exit 0** — `goodwe-addon-checks:1`, the same suite green on `release/1.0` before the change and after it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
admin added 2 commits 2026-09-07 21:53:44 +02:00
No code and no option changes. What changes is a number this repository claimed
about a shipped safety constant.

`delivery_hold_s: 185` shipped resting on "30.0 s, the worst continuous
legitimate episode at the shipped threshold across all 2640 runs", plus a lag
sweep giving 45.0 s and 90.0 s - from a harness `DECISIONS/SAFETY-42` §1
deliberately did NOT commit, because it imports `plantsim` and `frame_check` out
of admin/goodwe. So the floor under a safety constant could not be re-run. That
is PROC-01's shape, with eleven instances in three days.

The harness is committed now, in admin/goodwe as `sim/delivery_window.py`, and
NOT here. `plantsim` and `frame_check` are that repo's; restating them in this
tree is CI-08's coupling, which has broken three times, and the alternative
fate - skipped for want of its imports - is a green that establishes nothing.
There the dependency runs one way: THIS repo's `control`, `arbiter` and the
shipped `delivery.delinquent()` are imported live out of whichever checkout is
under test. Its `--selftest` is now a step in that repo's `checks.yml`, so a
change here that moves `saturation_w`, `max_w`, `delivery_hold_s` or the shape
of `delinquent()` goes red there instead of leaving a stale number here.

    cd goodwe/sim && GOODWE_ADDON_REPO=/path/to/goodwe-addon \
        python3 -B delivery_window.py --selftest --campaign --blag

⚠️ THE HEADLINE DID NOT REPRODUCE: 35.0 s, not 30.0 s. Recorded as measured. The
harness was not adjusted until it agreed - the one correction made during the
rebuild was made AGAINST the doc's number (§1 records it) and made every figure
LARGER.

WHAT REPRODUCED - and it is the part the compatibility promise rests on:
  * the N=1 control arm is 0.0 s at EVERY threshold in EVERY run. §3.2's algebra
    measured, not argued.
  * the flapping family is 0.0 s everywhere in all 840 runs - the family that
    defeated SAFETY-30's site window still cannot touch this check.
  * 45.0 s at twice A's lag, to the cycle.

WHAT DID NOT:
  * the headline, by one control cycle (30.0 -> 35.0 s); the reversal family by
    two (20.0 -> 30.0 s).
  * the NON-MONOTONE curve. §3.3 finding 3 read 55.0 s at 250 W against 40.0 s
    at 100 W as a property of the reduction. This rebuild's curve is monotone.
    The mechanism is real and is still the reason not to read either table as a
    sensitivity curve, but it is an ARGUMENT now, not a measurement, and it is
    written down as weaker.
  * the low-threshold rows, badly (40.0 -> 120.0 s at 100 W). Nothing ships
    there, but that gap is the honest measure of how much of the original table
    was a property of axes nobody wrote down. §3.3 fixes every axis COUNT and
    RANGE and no individual value; the rebuild prints its choices above its own
    table so the caveat travels with the number.
  * the four-times-slower row moved the OTHER way: 75.0 s, not 90.0 s.

185 s IS NOT CHANGED. It is 5.29x the re-measured worst (the record claimed
6.2x), 2.47x the four-times-slower case, 6.2x the firmware's LINK_STALE_S veto
and 4.9x inside the billing quarter. All four bounds clear and none narrowly,
and the ordering of the argument is unchanged. Moving a shipped safety constant
because a margin went from 6.2x to 5.3x while every bound holds would re-open
SAFETY-41's four-attempt false-trip regime for nothing.

⚠️ ONE CONSEQUENCE AN OPERATOR CAN HIT. At 30.0 s the rig's floor coincided with
the firmware's veto and a reader could not tell which was binding. At 35.0 s the
rig's is strictly the binding one - and it is ABOVE the schema's own
`int(30,900)` minimum. `delivery_hold_s: 30` is therefore below a MEASURED
legitimate episode and would report on ordinary saturation. Both translations
now say so. Raising the schema floor is a change to a shipped option's contract
and is left to its own card rather than smuggled in here.

⚠️ THE B-LAG ROWS ARE RELABELLED THROUGHOUT. Inverter B does not physically
exist (~2026-09-12); `plantsim.INVERTER_B` is A COPIED, `measured=False`. Every
two-inverter figure in this record is two copies of A, and the lag sweep is a
SENSITIVITY STUDY ON A HYPOTHESIS ABOUT B, not a measurement of it. The old
wording - "MEASURED, not felt" - overstated exactly that, and "the RAMP does not
move it at all" was too strong: halving it is worth 0 s at A's lag and +5 s at
twice it.

Touched: DECISIONS/SAFETY-42 §1 and §3.3, app/delivery.py's provenance comment,
config.yaml's `delivery_hold_s` block, both translations, CHANGELOG Unreleased.
26/26 suites pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
§3.3 already set the standard - "a worst case sitting on an edge of the grid
would be a number about the grid" - and then did not check its own argmax
against it. Review did. Reproduced here independently before writing it down:

    stagger  0 -> 1 -> 2 -> 3 -> 4      0.0  15.0  25.0  30.0  35.0 s
    weights (A) at stagger 4            0.5 -> 20.0   0.7/0.9/0.99 -> 0.0
                                        0.10 -> 35.0 s

Strictly increasing in stagger, maximal at the largest modelled stagger, and
maximal at the most asymmetric split. Interior only in `gain` and `slew_w`.

⚠️ SO THE HEADLINE IS 0.0 s AT stagger=0. The 35.0 s is ENTIRELY a product of
the modelled serialised-write stagger, which is declared MODELLED in the harness
and is a TERM of the figure rather than a detail of it. A site whose two writes
land in the same cycle does not produce this episode at all.

It does not move `delivery_hold_s`: 185 s must cover the worst the modelled site
can produce, and four cycles of stagger is inside what a real two-inverter write
path can do. What changes is that a reader asking "is 35.0 s about the plant or
about the grid?" now gets the answer instead of the invitation.

⚠️ THE PR BODY HAD CLAIMED THE ARGMAX WAS "interior to the grid, not on an edge
of an axis I had to choose". That was wrong, and it is the claim the doc's own
standard exists to test. The grid is the record's own, fixed by §3.3 before this
rebuild ran, which is the mitigating half and is stated too.

Also: `nl.yaml` wrote "35.0 s" beside "5,29 keer" and "75,0 s" in one sentence.
Dutch takes the comma.

Suite: 26 files, all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
admin force-pushed TEST-21 from bacb700235 to f76639c7b2 2026-09-07 21:53:44 +02:00 Compare
admin merged commit 6719bd882f into release/1.0 2026-09-07 22:41:36 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/goodwe-addon#67