feat(ev): Start becomes "charge now → target", releasing back to the plan - #1007
Merged
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
…plan Field report (#1002): a Start hold pins the wallbox at a fixed amperage until unplug, silently disabling the planner for the whole session — observed as an 11 kW offer held all night against a car that was already full, with the fuse clamp fighting it the whole way. ManualHold gains ReleaseAtSoC (0–1): once the loadpoint's inferred (or BMS-anchored) SoC reaches it, the controller clears the hold and the same tick falls through to automatic surplus/plan dispatch. The target persists with the hold across restarts; a hold without one keeps the legacy pin-until-Stop-or-unplug contract, and the existing releases (unplug, vehicle-declined debounce) still apply. POST /api/loadpoints/{id}/manual_hold accepts release_at_soc_pct (0–100), GET mirrors it, and the loadpoint state carries manual_release_soc so the UI can say where the boost stops. The modal's Start button becomes "Charge now → 80 %" (schedule target when set, 80 % otherwise) and both the manual tab and the plan strip explain the hand-back. Verified in the running dev stack: pressed the button, hold installed with release_at_soc_pct 80; corrected SoC to 85 %; controller logged "charge-now target reached" and released to plan dispatch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C1d4uknzs7QUv7nE9rW4fi
frahlg
force-pushed
the
1002-ev-charge-now
branch
from
August 30, 2026 07:56
d47879c to
84e956b
Compare
frahlg
added a commit
that referenced
this pull request
Sep 4, 2026
"Charge now → target" (#1007) released the hold the moment the SoC estimate reached the schedule target. On a charger that cannot read the car the estimate is a guess, and when it already sat at the target the button released itself on the next tick: five presses, no charge (box log 2026-09-04 06:15–06:18). The manual tab no longer sends a SoC release; Start runs until the car stops asking for current, Stop, or unplug, and the plan takes over after that. The API refuses a release_at_soc_pct the estimate already meets (409) instead of installing a hold that clears on the next tick. Refs #1002. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Part 2 of #1002. Re-opened #1005 against master (the original was auto-closed when its stacked base branch was deleted on #1004's merge). #1004 is merged, so the diff here is only this feature's delta.
Problem
The modal's Start button installs a persistent manual hold that pins the wallbox at a fixed amperage until unplug — the planner is silently dead for the whole session. On 2026-08-29 that meant an 11 kW offer held all night against a car that was already at its own charge limit, with the per-phase fuse clamp fighting the hold the entire time (worst phase measured 23 A on a 16 A main fuse).
What this does
loadpoint.ManualHoldgainsReleaseAtSoC(0–1): once the loadpoint's inferred (or BMS-anchored) SoC reaches it, the controller clears the hold and the same tick falls through to automatic surplus/plan dispatch. Persisted with the hold across restarts. A hold without a target keeps the legacy pin-until-Stop-or-unplug contract; unplug and the vehicle-declined debounce still release as before.POST /api/loadpoints/{id}/manual_holdacceptsrelease_at_soc_pct(0–100, validated); GET mirrors it; loadpoint state carriesmanual_release_soc.Verified in a browser (running dev stack)
Pressed "Charge now → 80 %" in the rendered modal → hold installed with
release_at_soc_pct: 80→ corrected SoC to 85 % via/soc→ controller loggedloadpoint manual hold released — charge-now target reached (soc=0.85, release_at_soc=0.8)and the very next dispatch was the plan's, not the hold's.Tests
TestChargeNowHoldReleasesAtTargetSoC— holds below target, releases at target, same-tick plan dispatchTestLegacyHoldWithoutTargetNeverSoCReleases— legacy contract untouchedweb/ev-charge-now.test.mjs— button contract, POST body, both status texts (445 web tests green)🤖 Generated with Claude Code
https://claude.ai/code/session_01C1d4uknzs7QUv7nE9rW4fi
Note
Medium Risk
Changes EV dispatch control (manual hold lifecycle and when MPC regains authority); behavior is tested but wrong SoC inference could release early or late.
Overview
EV manual Start is now a bounded “charge now” boost instead of pinning the planner off for the whole session. The controller adds
ReleaseAtSoConManualHold: while active it still commands the slider’s watts, but clears the hold when estimated SoC reaches the target and dispatches the MPC plan on the same tick. Holds with no release target keep the old pin-until-Stop-or-unplug behavior.API and UI wire this through:
POST /api/loadpoints/{id}/manual_holdacceptsrelease_at_soc_pct(0–100, validated); loadpoint state exposesmanual_release_soc. The modal renames Start to “Charge now → {target} %” (schedule SoC or 80 % default), postsrelease_at_soc_pct, and updates status copy in the manual tab and plan strip.Tests cover SoC-triggered release with same-tick plan dispatch, legacy holds unchanged, and a small web contract test for the button/POST body.
Reviewed by Cursor Bugbot for commit 84e956b. Bugbot is set up for automated code reviews on this repo. Configure here.