A mode in which the controller runs its full decision path — strategies,
arbiter, clamps, guards — and commands nothing. The decision, and the
arbiter's own reason for it, are recorded where a human and a later analysis
can both read them.
It exists because every optimiser defect this project has found was invisible
from outside while it was happening, and each was paid for at the meter before
anyone could see it: SAFETY-45's 28 charge onsets an hour with the grid
swinging −455 … +3200 W and a billed quarter 59 W over the cap; TARIFF-14's
price lookup answering for the wrong quarter-hour 45 minutes in every 60;
SAFETY-46's SoC ceiling churning 598 Wh to gain 310 Wh. All three are
visible from a shadow run.
⚠️ The default is auto, not true — and that is the card's real content
The card is titled default-on. Taken literally — shadow_mode: true in config.yaml's options: block — it is the worst change in this repository's
history.
The Supervisor composes an add-on's effective options as {**config.yaml defaults, **the stored user options}, so a new key with a
new default reaches every existing install on the next update, including the
one regulating a real battery at the reference site. It would have stopped it
silently, and the only symptom would have been a battery that stopped doing
anything.
There is no mechanism in the add-on options system by which one key can
default differently for a new install and an existing one — options:is the
default and there is one of it. So "default-on for new" has to be a value the
add-on resolves:
shadow_mode
resolves to
off
live
on
shadow, always — the setting for watching an armed site for a day
auto (shipped)
shadow while control is stopped; live once it is started
a fresh install ships auto_start: false, so it comes up in shadow mode
and observes before it ever commands anything. That is the default-on the
card asked for, delivered where it is safe;
an install that is controlling is started — auto resolves to live and not one watt changes;
an install that is stopped keeps its actuator at 0 W exactly as before,
same writer, same heartbeat. What changes is what the page says, and that
the decision path now runs and is recorded.
⚠️ Under auto, pressing Start leaves shadow mode. That is the point of auto: the button means "control the battery". on is the deliberate
configuration and cannot be reached by accident.
⚠️ "Commands nothing" is a number, not a silence
The inverter holds its last command forever. A shadow mode that simply
stopped writing would leave whatever was standing when it came on standing for
as long as it was left on — under a status page saying nothing is being
commanded. That is the looks healthy but is inert failure this repository
keeps finding.
So the actuator is held at 0.0 W, refreshed on the ordinary heartbeat, by
the ordinary single writer. That also honours firmware/goodwe-master.yaml's contract — "HA must REWRITE the setpoint every
~10 s even when the value has not changed" — because without a refresh "stale" and "steady" are the same thing on the wire. Going quiet would keep
that board's watchdog tripped for the whole run, which is an alarm being used
as a mode.
It is not a second write path. No claim, no transport, no arithmetic of its
own: a substitution inside Controller._write_unit(), the single choke
point every write already reaches (cycle, heartbeat, stop button, shutdown(), maint_abort()), in the same form as the three substitutions above it. It is
the last of them, so SAFETY-28's authorisation gate and SAFETY-03's clamp
still grade the number the decision path produced — a shadow run can report "it would have hit the rating here".
The failsafes, one by one
failsafe
in shadow mode
SAFETY-02/30 sign watchdogs
INERT. They grade Inverter.standing_w() — what stands on the wire — and that is 0 W. ⚠️ Fed the shadow decision they would disagree with a meter that never saw it and latch a trip that survives into live operation and a restart: shadow mode would manufacture the failsafe it exists to help debug
SAFETY-03 clamp
OBSERVES — runs on the decision, logs every engagement
SAFETY-28 write gate
OBSERVES — still asks who chose the number
SAFETY-18 meter freeze
OBSERVES, unchanged; it takes no action live either
SAFETY-07/24/25 peak guard
OBSERVES — level-triggered, unlatched
SAFETY-31 / SAFETY-42
OBSERVE and may legitimately LATCH — their evidence is measured power, not a command
operator stop
The one deliberate change: enforced at the wire, not as a P_SAFETY claim. That claim out-ranks everything, so leaving it in makes every recorded decision read stopped and the run answers the wrong question. Same actuator state, and a wire gate cannot be out-ranked
⚠️A latch that is already up stays up. The shadow banner names it and
goes red for it, so nobody turns shadow mode off into a battery that will not
move and a page that never mentioned why.
⚠️Maintenance is untouched. Its state machines already tick while control
is stopped; shadow mode inherits that, unchanged and unimproved. Recorded
rather than quietly fixed under another card's name.
⚠️ Recorded limitation
_shadow_settle() re-seeds the carried command to what stands on the wire,
which makes the exit safe by construction — the first live cycle slews from
0 W like any other. The cost: the loop's prev_w is 0 W on every shadow cycle,
so the loop leg's recorded decision is one slew step, not a trajectory. A
shadow run answers "what would this controller command now, from where the
plant really is" — true about the present — and not "what would it have done
next", which is a closed-loop property that did not happen. Graded by name in test_watchdog.py part 10.
The strategies are unaffected — they file SET claims at fixed power from
level-triggered inputs — which is why all three defects above are visible.
How "nothing reaches the inverter" is graded
Never on the flag. A check asserting Controller.shadow passes on a build
where the flag is set and the substitution is deleted — mutation M1.
test_clamp.py part 8 drives the shippedController.cycle() and the
shipped heartbeat and asserts on what reached set_number():
the precondition first — the same stimulus at shadow_mode: off puts 30 non-zero writes on the wire, largest 5000 W;
then set(writes) == {0.0}and 26 of them — a check that only said "no
non-zero write" would pass on the silent build too;
entry and exit measured on inv.last_write (updated only on an accepted
write) either side of the boundary, against the same run that never entered
shadow mode.
Closed loop — TEST-23 stage 3's rig, the same hour twice
live
shadow
plant_cmd_w
−2000 W throughout
0 W throughout
batt_w
−2000 … −40.6 W
0.0 W exactly
SoC
50.000 → 62.331 %
50.0 → 50.0 %
grid
390.6 … 2350 W
350 W = the house, exactly
ARBITRAGE_CHARGE samples
3600
3600
first charge instant
2026-01-15T00:00:00+00:00
identical
arb_power_w
−2000 W
identical
arb_reason
the strategy's own sentence
identical, verbatim
arbiter winner
arbitrage
identical
The decision happened — same instant, same signed power, same reason — and the
battery did not move.
⚠️ Three lines of that harness are a transcription and are marked as one: priceloop.Rig has no _write_unit(), no clamp and no failsafe machine
(divergence 2 of its README). ⚠️ And its transcribed cycle predates
SAFETY-45 — arbitrage.decide() gained a required charge_headroom_w after 478ba93 — so the harness supplies it from the shipped peakguard.charge_headroom_w() reading the shipped peak decision, not from
a constant.
Mutations — 22, no survivors
Full table in goodwe_controller/DECISIONS/SAFETY-10-shadow-mode.md §7. The findings worth
reading here:
⚠️M2 first came back a SURVIVOR and was not one. The "stop writing
altogether" build left inv.last_write as None, the check called float()
on it, and the section died with a TypeError — grading none of the checks
after it and reporting zero reds, which reads exactly like a survivor.
MUTATION.md FCST-06's shape. Fixed; M2 is red on three named checks.
⚠️M6 had to become a PAIR, and only the battery said so. standing_w() prefers last_write and falls back to inv.target; either
rung alone keeps the sign detector inert, so dropping the re-seed singly
did not reproduce the hazard it names. TEST-16's rule with a new name.
shadow_would_w = float(self.target) is an equivalent mutation and is
recorded as one rather than reported as a survivor — _shadow_settle() runs
before the re-seed, so the two expressions are the same value at that line.
All 29 test files green.
No version bump — the owner picks the number (CHANGELOG.md says why).
## What this is
A mode in which the controller runs its **full** decision path — strategies,
arbiter, clamps, guards — and **commands nothing**. The decision, and the
arbiter's own reason for it, are recorded where a human and a later analysis
can both read them.
It exists because every optimiser defect this project has found was invisible
from outside while it was happening, and each was paid for at the meter before
anyone could see it: SAFETY-45's **28 charge onsets an hour** with the grid
swinging −455 … +3200 W and a billed quarter 59 W over the cap; TARIFF-14's
price lookup answering for **the wrong quarter-hour 45 minutes in every 60**;
SAFETY-46's SoC ceiling churning **598 Wh to gain 310 Wh**. All three are
visible from a shadow run.
---
## ⚠️ The default is `auto`, not `true` — and that is the card's real content
The card is titled *default-on*. Taken literally — `shadow_mode: true` in
`config.yaml`'s `options:` block — it is the worst change in this repository's
history.
The Supervisor composes an add-on's effective options as
`{**config.yaml defaults, **the stored user options}`, so **a new key with a
new default reaches every existing install on the next update**, including the
one regulating a real battery at the reference site. It would have stopped it
silently, and the only symptom would have been a battery that stopped doing
anything.
There is **no mechanism** in the add-on options system by which one key can
default differently for a new install and an existing one — `options:` *is* the
default and there is one of it. So "default-on for new" has to be a value the
add-on **resolves**:
| `shadow_mode` | resolves to |
|---|---|
| `off` | live |
| `on` | shadow, always — the setting for watching an armed site for a day |
| **`auto`** (shipped) | shadow **while control is stopped**; live once it is **started** |
* a **fresh** install ships `auto_start: false`, so it comes up in shadow mode
and observes before it ever commands anything. That is the default-on the
card asked for, delivered where it is safe;
* an install that is **controlling** is started — `auto` resolves to live and
**not one watt changes**;
* an install that is **stopped** keeps its actuator at 0 W exactly as before,
same writer, same heartbeat. What changes is what the page **says**, and that
the decision path now runs and is recorded.
⚠️ Under `auto`, pressing **Start** leaves shadow mode. That is the point of
`auto`: the button means "control the battery". `on` is the deliberate
configuration and cannot be reached by accident.
---
## ⚠️ "Commands nothing" is a number, not a silence
The inverter **holds its last command forever**. A shadow mode that simply
stopped writing would leave whatever was standing when it came on standing for
as long as it was left on — under a status page saying nothing is being
commanded. That is the *looks healthy but is inert* failure this repository
keeps finding.
So the actuator is held at **0.0 W**, refreshed on the ordinary heartbeat, by
the ordinary single writer. That also honours
`firmware/goodwe-master.yaml`'s contract — *"HA must REWRITE the setpoint every
~10 s even when the value has not changed"* — because without a refresh
**"stale" and "steady" are the same thing on the wire**. Going quiet would keep
that board's watchdog tripped for the whole run, which is an alarm being used
as a mode.
**It is not a second write path.** No claim, no transport, no arithmetic of its
own: a **substitution** inside `Controller._write_unit()`, the single choke
point every write already reaches (cycle, heartbeat, stop button, `shutdown()`,
`maint_abort()`), in the same form as the three substitutions above it. It is
the **last** of them, so SAFETY-28's authorisation gate and SAFETY-03's clamp
still grade the number the decision path produced — a shadow run can report
*"it would have hit the rating here"*.
---
## The failsafes, one by one
| failsafe | in shadow mode |
|---|---|
| **SAFETY-02/30 sign watchdogs** | **INERT.** They grade `Inverter.standing_w()` — what stands on the wire — and that is 0 W. ⚠️ Fed the shadow decision they would disagree with a meter that never saw it and **latch a trip that survives into live operation and a restart**: shadow mode would manufacture the failsafe it exists to help debug |
| **SAFETY-03 clamp** | OBSERVES — runs on the decision, logs every engagement |
| **SAFETY-28 write gate** | OBSERVES — still asks who chose the number |
| **SAFETY-18 meter freeze** | OBSERVES, unchanged; it takes no action live either |
| **SAFETY-07/24/25 peak guard** | OBSERVES — level-triggered, unlatched |
| **SAFETY-31 / SAFETY-42** | OBSERVE and may legitimately LATCH — their evidence is *measured* power, not a command |
| **operator stop** | **The one deliberate change**: enforced at the wire, not as a `P_SAFETY` claim. That claim out-ranks everything, so leaving it in makes every recorded decision read `stopped` and the run answers the wrong question. Same actuator state, and a wire gate cannot be out-ranked |
| inputs missing / non-finite / allocator refusal / arbiter contradiction | unchanged — all resolve to 0 W anyway |
⚠️ **A latch that is already up stays up.** The shadow banner **names** it and
goes red for it, so nobody turns shadow mode off into a battery that will not
move and a page that never mentioned why.
⚠️ **Maintenance is untouched.** Its state machines already tick while control
is stopped; shadow mode inherits that, unchanged and unimproved. Recorded
rather than quietly fixed under another card's name.
---
## ⚠️ Recorded limitation
`_shadow_settle()` re-seeds the carried command to what stands on the wire,
which makes the **exit safe by construction** — the first live cycle slews from
0 W like any other. The cost: the loop's `prev_w` is 0 W on every shadow cycle,
so **the loop leg's recorded decision is one slew step, not a trajectory**. A
shadow run answers *"what would this controller command now, from where the
plant really is"* — true about the present — and not *"what would it have done
next"*, which is a closed-loop property that did not happen. Graded by name in
`test_watchdog.py` part 10.
The strategies are unaffected — they file `SET` claims at fixed power from
level-triggered inputs — which is why all three defects above are visible.
---
## How "nothing reaches the inverter" is graded
**Never on the flag.** A check asserting `Controller.shadow` passes on a build
where the flag is set and the substitution is deleted — mutation M1.
`test_clamp.py` part 8 drives the **shipped** `Controller.cycle()` and the
shipped heartbeat and asserts on what reached `set_number()`:
* **the precondition first** — the same stimulus at `shadow_mode: off` puts
**30 non-zero writes** on the wire, largest 5000 W;
* then `set(writes) == {0.0}` **and 26 of them** — a check that only said "no
non-zero write" would pass on the silent build too;
* entry and exit measured on `inv.last_write` (updated only on an **accepted**
write) either side of the boundary, against the same run that never entered
shadow mode.
### Closed loop — TEST-23 stage 3's rig, the same hour twice
| | live | shadow |
|---|---|---|
| `plant_cmd_w` | −2000 W throughout | **0 W throughout** |
| `batt_w` | −2000 … −40.6 W | **0.0 W exactly** |
| SoC | 50.000 → **62.331 %** | 50.0 → **50.0 %** |
| grid | 390.6 … 2350 W | **350 W = the house, exactly** |
| `ARBITRAGE_CHARGE` samples | 3600 | **3600** |
| first charge instant | `2026-01-15T00:00:00+00:00` | **identical** |
| `arb_power_w` | −2000 W | **identical** |
| `arb_reason` | the strategy's own sentence | **identical, verbatim** |
| arbiter winner | `arbitrage` | **identical** |
The decision happened — same instant, same signed power, same reason — and the
battery did not move.
⚠️ Three lines of that harness are a **transcription** and are marked as one:
`priceloop.Rig` has no `_write_unit()`, no clamp and no failsafe machine
(divergence 2 of its README). ⚠️ And its transcribed cycle **predates
SAFETY-45** — `arbitrage.decide()` gained a required `charge_headroom_w` after
`478ba93` — so the harness supplies it from the **shipped**
`peakguard.charge_headroom_w()` reading the **shipped** peak decision, not from
a constant.
---
## Mutations — 22, no survivors
Full table in
`goodwe_controller/DECISIONS/SAFETY-10-shadow-mode.md` §7. The findings worth
reading here:
* ⚠️ **M2 first came back a SURVIVOR and was not one.** The "stop writing
altogether" build left `inv.last_write` as `None`, the check called `float()`
on it, and the section died with a `TypeError` — grading none of the checks
after it and reporting **zero reds**, which reads exactly like a survivor.
MUTATION.md FCST-06's shape. Fixed; M2 is red on three named checks.
* ⚠️ **M6 had to become a PAIR, and only the battery said so.**
`standing_w()` prefers `last_write` and falls back to `inv.target`; **either
rung alone** keeps the sign detector inert, so dropping the re-seed singly
did **not** reproduce the hazard it names. TEST-16's rule with a new name.
* **`shadow_would_w = float(self.target)` is an equivalent mutation** and is
recorded as one rather than reported as a survivor — `_shadow_settle()` runs
before the re-seed, so the two expressions are the same value at that line.
All 29 test files green.
**No version bump** — the owner picks the number (`CHANGELOG.md` says why).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
Every optimiser defect this project has found was invisible from outside
while it was happening, and each was paid for at the meter first: SAFETY-45's
28 charge onsets an hour against a binding peak_cap_w, TARIFF-14's wrong
quarter-hour for 45 minutes in every 60, SAFETY-46's 598 Wh spent to gain
310 Wh. There was no way to watch what the optimiser WOULD do without letting
it do it.
`shadow_mode` runs strategies, arbiter, clamps and guards, records the
decision with its reason, and commands nothing.
⚠️ IT WRITES. It writes 0 W, on the ordinary heartbeat, through the ordinary
single writer. The inverter holds its last command forever - it has no meter
timeout - so "commands nothing" implemented as "stops writing" would leave
whatever was standing when shadow mode came on standing for as long as it was
left on, under a page saying nothing is being commanded. That is the failure
this repository keeps finding. Refreshing 0 W is also what
firmware/goodwe-master.yaml's 30 s stale-input watchdog requires of us; going
quiet would keep that board tripped for the whole run, which is an alarm being
used as a mode.
⚠️ IT IS NOT A SECOND WRITE PATH. No claim, no transport, no arithmetic of its
own. It is a substitution inside Controller._write_unit() - the single choke
point every write already reaches - in the same form as the three above it,
and it is the LAST of them, so SAFETY-28's gate and SAFETY-03's clamp still
grade the number the decision path produced.
⚠️ THE DEFAULT IS `auto`, NOT `true`, AND THAT IS THE CARD'S REAL CONTENT. The
Supervisor composes options as {**config.yaml defaults, **stored user
options}, so a new key with a new default reaches EVERY EXISTING INSTALL on
the next update - including the one regulating a real battery. A boolean
default-on would have stopped it silently. There is no mechanism by which one
key can default differently for a new install and an existing one, so
"default-on for new" is a value the add-on RESOLVES: `auto` is shadow while
control is STOPPED and live once it is STARTED. A fresh install ships
`auto_start: false` and comes up observing; an install that is controlling is
started and does not change by one watt.
THE FAILSAFES. Sign watchdogs (SAFETY-02/30): INERT - they grade
Inverter.standing_w(), which shadow mode holds at 0 W. Fed the shadow decision
they would disagree with a meter that never saw it and LATCH A TRIP THAT
SURVIVES INTO LIVE OPERATION AND A RESTART. Clamp (SAFETY-03), write gate
(SAFETY-28), meter freeze (SAFETY-18), peak guard: OBSERVE. Circulation
(SAFETY-31) and delivery (SAFETY-42): observe and may legitimately latch,
because their evidence is measured power. The operator stop is the one
deliberate change: enforced at the wire rather than as a P_SAFETY claim, which
would otherwise make every recorded decision read "stopped".
⚠️ RECORDED LIMITATION: _shadow_settle() re-seeds the carried command to what
stands on the wire, which makes the exit safe by construction - and therefore
the loop leg's shadow decision is ONE SLEW STEP, not a trajectory. A shadow
run answers "what would it command now", not "what would it have done next".
The strategies are unaffected; all three defects above are visible from one.
Graded on the writes and on the rendered surface, never on the flag: 22
mutations, no survivors, including one that lets a write escape and one that
goes silent instead of commanding 0 W. Closed loop on TEST-23 stage 3's rig,
same hour twice: live drives the pack to -2000 W and SoC 50.000 -> 62.331 %;
shadow commands 0 W throughout, SoC stays 50.0 exactly, and the arbitrage
decision is identical in action, instant, signed power, reason and arbiter
winner.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
admin
merged commit e6da478f87 into release/1.02026-09-14 17:44:27 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What this is
A mode in which the controller runs its full decision path — strategies,
arbiter, clamps, guards — and commands nothing. The decision, and the
arbiter's own reason for it, are recorded where a human and a later analysis
can both read them.
It exists because every optimiser defect this project has found was invisible
from outside while it was happening, and each was paid for at the meter before
anyone could see it: SAFETY-45's 28 charge onsets an hour with the grid
swinging −455 … +3200 W and a billed quarter 59 W over the cap; TARIFF-14's
price lookup answering for the wrong quarter-hour 45 minutes in every 60;
SAFETY-46's SoC ceiling churning 598 Wh to gain 310 Wh. All three are
visible from a shadow run.
⚠️ The default is
auto, nottrue— and that is the card's real contentThe card is titled default-on. Taken literally —
shadow_mode: trueinconfig.yaml'soptions:block — it is the worst change in this repository'shistory.
The Supervisor composes an add-on's effective options as
{**config.yaml defaults, **the stored user options}, so a new key with anew default reaches every existing install on the next update, including the
one regulating a real battery at the reference site. It would have stopped it
silently, and the only symptom would have been a battery that stopped doing
anything.
There is no mechanism in the add-on options system by which one key can
default differently for a new install and an existing one —
options:is thedefault and there is one of it. So "default-on for new" has to be a value the
add-on resolves:
shadow_modeoffonauto(shipped)auto_start: false, so it comes up in shadow modeand observes before it ever commands anything. That is the default-on the
card asked for, delivered where it is safe;
autoresolves to live andnot one watt changes;
same writer, same heartbeat. What changes is what the page says, and that
the decision path now runs and is recorded.
⚠️ Under
auto, pressing Start leaves shadow mode. That is the point ofauto: the button means "control the battery".onis the deliberateconfiguration and cannot be reached by accident.
⚠️ "Commands nothing" is a number, not a silence
The inverter holds its last command forever. A shadow mode that simply
stopped writing would leave whatever was standing when it came on standing for
as long as it was left on — under a status page saying nothing is being
commanded. That is the looks healthy but is inert failure this repository
keeps finding.
So the actuator is held at 0.0 W, refreshed on the ordinary heartbeat, by
the ordinary single writer. That also honours
firmware/goodwe-master.yaml's contract — "HA must REWRITE the setpoint every~10 s even when the value has not changed" — because without a refresh
"stale" and "steady" are the same thing on the wire. Going quiet would keep
that board's watchdog tripped for the whole run, which is an alarm being used
as a mode.
It is not a second write path. No claim, no transport, no arithmetic of its
own: a substitution inside
Controller._write_unit(), the single chokepoint every write already reaches (cycle, heartbeat, stop button,
shutdown(),maint_abort()), in the same form as the three substitutions above it. It isthe last of them, so SAFETY-28's authorisation gate and SAFETY-03's clamp
still grade the number the decision path produced — a shadow run can report
"it would have hit the rating here".
The failsafes, one by one
Inverter.standing_w()— what stands on the wire — and that is 0 W. ⚠️ Fed the shadow decision they would disagree with a meter that never saw it and latch a trip that survives into live operation and a restart: shadow mode would manufacture the failsafe it exists to help debugP_SAFETYclaim. That claim out-ranks everything, so leaving it in makes every recorded decision readstoppedand the run answers the wrong question. Same actuator state, and a wire gate cannot be out-ranked⚠️ A latch that is already up stays up. The shadow banner names it and
goes red for it, so nobody turns shadow mode off into a battery that will not
move and a page that never mentioned why.
⚠️ Maintenance is untouched. Its state machines already tick while control
is stopped; shadow mode inherits that, unchanged and unimproved. Recorded
rather than quietly fixed under another card's name.
⚠️ Recorded limitation
_shadow_settle()re-seeds the carried command to what stands on the wire,which makes the exit safe by construction — the first live cycle slews from
0 W like any other. The cost: the loop's
prev_wis 0 W on every shadow cycle,so the loop leg's recorded decision is one slew step, not a trajectory. A
shadow run answers "what would this controller command now, from where the
plant really is" — true about the present — and not "what would it have done
next", which is a closed-loop property that did not happen. Graded by name in
test_watchdog.pypart 10.The strategies are unaffected — they file
SETclaims at fixed power fromlevel-triggered inputs — which is why all three defects above are visible.
How "nothing reaches the inverter" is graded
Never on the flag. A check asserting
Controller.shadowpasses on a buildwhere the flag is set and the substitution is deleted — mutation M1.
test_clamp.pypart 8 drives the shippedController.cycle()and theshipped heartbeat and asserts on what reached
set_number():shadow_mode: offputs30 non-zero writes on the wire, largest 5000 W;
set(writes) == {0.0}and 26 of them — a check that only said "nonon-zero write" would pass on the silent build too;
inv.last_write(updated only on an acceptedwrite) either side of the boundary, against the same run that never entered
shadow mode.
Closed loop — TEST-23 stage 3's rig, the same hour twice
plant_cmd_wbatt_wARBITRAGE_CHARGEsamples2026-01-15T00:00:00+00:00arb_power_warb_reasonarbitrageThe decision happened — same instant, same signed power, same reason — and the
battery did not move.
⚠️ Three lines of that harness are a transcription and are marked as one:
priceloop.Righas no_write_unit(), no clamp and no failsafe machine(divergence 2 of its README). ⚠️ And its transcribed cycle predates
SAFETY-45 —
arbitrage.decide()gained a requiredcharge_headroom_wafter478ba93— so the harness supplies it from the shippedpeakguard.charge_headroom_w()reading the shipped peak decision, not froma constant.
Mutations — 22, no survivors
Full table in
goodwe_controller/DECISIONS/SAFETY-10-shadow-mode.md§7. The findings worthreading here:
altogether" build left
inv.last_writeasNone, the check calledfloat()on it, and the section died with a
TypeError— grading none of the checksafter it and reporting zero reds, which reads exactly like a survivor.
MUTATION.md FCST-06's shape. Fixed; M2 is red on three named checks.
standing_w()preferslast_writeand falls back toinv.target; eitherrung alone keeps the sign detector inert, so dropping the re-seed singly
did not reproduce the hazard it names. TEST-16's rule with a new name.
shadow_would_w = float(self.target)is an equivalent mutation and isrecorded as one rather than reported as a survivor —
_shadow_settle()runsbefore the re-seed, so the two expressions are the same value at that line.
All 29 test files green.
No version bump — the owner picks the number (
CHANGELOG.mdsays why).🤖 Generated with Claude Code
https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU