SAFETY-137: the shutdown and Stop 0 W must be the last write every unit hears.
The 0 W of shutdown and of the Stop button now waits, bounded (2 s), for a write already on its way to the same unit, for every driver (GoodWe included), and then goes out. Once shutdown has begun, cycle, heartbeat and safety-stop writes are dropped at the one place a write is created. One shared wait (_await_inflight) serves the 0 W and SMA-10's _release_last. Worst case for a hung write is 2 s + 4 s = 6 s (HAND_BACK_WORST_S).
SAFETY-137: the shutdown and Stop 0 W must be the last write every unit hears.
The 0 W of shutdown and of the Stop button now waits, bounded (2 s), for a write already on its way to the same unit, for every driver (GoodWe included), and then goes out. Once shutdown has begun, cycle, heartbeat and safety-stop writes are dropped at the one place a write is created. One shared wait (`_await_inflight`) serves the 0 W and SMA-10's `_release_last`. Worst case for a hung write is 2 s + 4 s = 6 s (`HAND_BACK_WORST_S`).
- `app/main.py`: `SETTLE_BUDGET_S`, `_await_inflight`, `_write_unit(last=)`, `write_setpoint(last=)`, `command_site_zero(last=)`, `command_site_release`.
- New `test_safety137_last_write.py`; `test_sma10_two_idle_states.py` call-site finder now accepts `await self.name(...)`.
- DECISIONS note and one CHANGELOG line. No user-facing text change. Out of scope as briefed: heartbeat, safety-stop paths, SMA-4, OPT-40.
Not merged. Report: reports/SAFETY-137-dev-report.md.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
The 0 W of shutdown and of the Stop button now waits (bounded, 2 s) for a
write already on its way to the same unit, for every driver, GoodWe included.
Once shutdown has begun, cycle, heartbeat and safety-stop writes are dropped
right before the write is created. One shared wait (_await_inflight) serves
the 0 W and SMA-10's _release_last.
Co-Authored-By: Claude <noreply@anthropic.com>
Review (SAFETY-137, head 8953be0): approved, merging.
Race closed: with a 0.3 s, 0.6 s or hung cycle write on its way, at shutdown and at Stop, N=1 and N=2, GoodWe and a fake that does not hand back, the last write per unit is 0 W on head (base ends on the cycle value). With one unit hung, every unit still gets 0 W, at 2.0 s.
Worst case, with every write hung: 6.005 s on head (4.0 s on base), which is the 6 s bound plus loop overhead.
The gate does not drop the final 0 W. It drops a plain safety-stop 0 W only after shutdown has begun, and the shutdown's own 0 W follows it. Task death, an in-flight write that raises, and the refusal path still end on 0 W.
Mutants b, d (the dev's) and my own e (in-flight tracking only for hand-back drivers) are all caught: 8, 8 and 36 checks red.
GoodWe identity: 7 replay tools byte-identical, announce 0/0/0. Cycle time is the same as base within noise.
Full suite on head: 101 files, 0 failed. CI 1569 and 1570 are green.
Follow-ups (not blocking):
A cycle write that takes 2 to 4 s still lands after the 0 W (measured at 2.5 s and 3.5 s). The CHANGELOG line ("can no longer") says more than that. The ESPHome stall timer still covers this case.
Stop (not shutdown): a cycle that has decided but not yet started its writes can send its share right after the Stop 0 W. The next cycle corrects it. Base does the same.
Review (SAFETY-137, head 8953be0): approved, merging.
- Race closed: with a 0.3 s, 0.6 s or hung cycle write on its way, at shutdown and at Stop, N=1 and N=2, GoodWe and a fake that does not hand back, the last write per unit is 0 W on head (base ends on the cycle value). With one unit hung, every unit still gets 0 W, at 2.0 s.
- Worst case, with every write hung: 6.005 s on head (4.0 s on base), which is the 6 s bound plus loop overhead.
- The gate does not drop the final 0 W. It drops a plain safety-stop 0 W only after shutdown has begun, and the shutdown's own 0 W follows it. Task death, an in-flight write that raises, and the refusal path still end on 0 W.
- Mutants b, d (the dev's) and my own e (in-flight tracking only for hand-back drivers) are all caught: 8, 8 and 36 checks red.
- GoodWe identity: 7 replay tools byte-identical, announce 0/0/0. Cycle time is the same as base within noise.
- Full suite on head: 101 files, 0 failed. CI 1569 and 1570 are green.
Follow-ups (not blocking):
1. A cycle write that takes 2 to 4 s still lands after the 0 W (measured at 2.5 s and 3.5 s). The CHANGELOG line ("can no longer") says more than that. The ESPHome stall timer still covers this case.
2. Stop (not shutdown): a cycle that has decided but not yet started its writes can send its share right after the Stop 0 W. The next cycle corrects it. Base does the same.
admin
merged commit b3cfcf2df4 into release/1.02026-11-08 04:20:05 +01: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.
SAFETY-137: the shutdown and Stop 0 W must be the last write every unit hears.
The 0 W of shutdown and of the Stop button now waits, bounded (2 s), for a write already on its way to the same unit, for every driver (GoodWe included), and then goes out. Once shutdown has begun, cycle, heartbeat and safety-stop writes are dropped at the one place a write is created. One shared wait (
_await_inflight) serves the 0 W and SMA-10's_release_last. Worst case for a hung write is 2 s + 4 s = 6 s (HAND_BACK_WORST_S).app/main.py:SETTLE_BUDGET_S,_await_inflight,_write_unit(last=),write_setpoint(last=),command_site_zero(last=),command_site_release.test_safety137_last_write.py;test_sma10_two_idle_states.pycall-site finder now acceptsawait self.name(...).Not merged. Report: reports/SAFETY-137-dev-report.md.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
Review (SAFETY-137, head
8953be0): approved, merging.Follow-ups (not blocking):