checks / unit (pull_request) Successful in 3m36s
OPT-04 landed after the previous rebase. Conflicts were CHANGELOG.md and one IMPORT LINE in validate.py; both refusal blocks and both option sets survive untouched, and DOCS.md, main.py and config.yaml auto-merged. ⚠️ AND THE MERGE BROKE A CHECK THAT NEITHER CARD COULD HAVE SEEN ALONE. test_peakguard.py graded "the first `.decide(` call in main.py" and asserted there was exactly one. That was true when SAFETY-24 was written; OPT-04 added `arbitrage.decide(...)`, so the merged tree had two and the assertion failed. It failed loudly rather than grading the wrong call - the guard did its job - but it cannot ship red. Fixed by selecting the call by QUALIFIED NAME (`peakguard.decide`) instead of by position, and requiring exactly one of THAT. Other modules may now call their own decide(); this grader can no longer be moved by one of them. Verified still discriminating: a decoy `peakguard.decide` added to main.py turns it red on 7 assertions, and the `maintenance_soc_floor` swap still turns it red on 6. Also folded in the review's wording finding. An assertion read "a CONTROLLER whose SoC entity does not answer hands decide() None" while driving no Controller. The wiring property is stronger than the one claimed, and is now asserted on the SERVICE CALL: a Controller with no SoC never reaches the guard at all, because `inputs_ok` is False and cycle() stops at the safety claim first - so a `float(soc or 100)` defaulting line above the call site cannot disarm the floor. The original decide()-level check is kept, reworded to say what it actually grades. Full suite green on the merged tree: 15 files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015VXjA3LWccN1rJsk3okyPC