feat(contract): ops to set the car's charge level and PV-only on a loadpoint - #61
Conversation
…adpoint Two command operations in the registry, both on ftw.dispatch.write like the other loadpoint ops: loadpoint.soc.set corrects the car's current charge level and loadpoint.surplus_only.set turns PV-only charging on or off. The OP_ constants and the simulator's scope table follow, because the contract test holds them to the registry in both directions. No UI in this change. Refs #59 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ftw-webapp | bf122ec | Commit Preview URL | Sep 04 2026, 06:14 AM |
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. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bf122ec07c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| `POST /api/loadpoints/{id}/soc` names `loadpoint.soc.set`, and | ||
| `POST /api/loadpoints/{id}/target` names `loadpoint.surplus_only.set` — the |
There was a problem hiding this comment.
Wire the new commands into the simulator route facts
When a client connected to SimBox posts either of these loadpoint routes, the simulator still returns E_USE_CMD without an op: the corresponding ROUTES entries in src/lib/sim/api.ts have no cmdOp, and serve() only includes the operation when that property is present. This contradicts the newly documented protocol and makes the simulated/dev flow report that these controls are unavailable, so add the new constants as cmdOp values for both routes and cover the returned arguments in the passthrough tests.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit bf122ec. Configure here.
| `POST /api/loadpoints/{id}/soc` names `loadpoint.soc.set`, and | ||
| `POST /api/loadpoints/{id}/target` names `loadpoint.surplus_only.set` — the | ||
| one field of that route's body the session can set; the target and its | ||
| deadline still have no command. Every other actuating route names nothing, and |
There was a problem hiding this comment.
Simulator omits named command ops
Medium Severity
The protocol now says POST /api/loadpoints/{id}/soc and POST /api/loadpoints/{id}/target name loadpoint.soc.set and loadpoint.surplus_only.set on E_USE_CMD. The simulator still prices both routes as actuate with no cmdOp, so a refusal still carries no op and the session still reads those controls as unavailable.
Reviewed by Cursor Bugbot for commit bf122ec. Configure here.
miravoss26
left a comment
There was a problem hiding this comment.
Contract-only half of the pair with ftw#1069 (box) and ftw-app#1 (native). Adds loadpoint.soc.set / loadpoint.surplus_only.set to the registry, exports the op constants, updates the sim box's scope table, and documents the two new Via-named routes in docs/protocol.md. No UI/controls yet (later PR per the changeset note) — nothing to review beyond the copy matching the box's registry, which CI's drift check confirms. CI green. Safe to merge from my read.




Contract-pair: srcfl/ftw@59-ops-soc-surplus
Closes #59.
What changed
contract/registry.yaml: two ops underops, both onftw.dispatch.writelike the other loadpoint ops:loadpoint.soc.set—{ id, soc },soca 0–1 fraction. Corrects the car's current charge level.loadpoint.surplus_only.set—{ id, surplus_only }. Turns PV-only charging on or off.src/lib/protocol/messages.ts:OP_LOADPOINT_SOC_SETandOP_LOADPOINT_SURPLUS_ONLY_SET. The contract test holds theOP_constants to the registry in both directions, so the names must land together.src/lib/sim/box.ts: the two ops in the simulator'sOP_SCOPES, which the same test holds to the registry. The simulator answers them with its generic "applied"; a later PR teaches it the real behaviour when the controls arrive.docs/protocol.md: the paragraph that said onlyPOST /api/modenames a command now lists the three routes that do.No UI. The controls come in a later PR.
Why
The box's own page can correct the car's charge level and toggle PV-only. The remote client can do neither: both HTTP routes are priced
Actuate, the passthrough refusesActuateby design (no expiry, so it must not move energy), and until now neither route had acmdop to point at. Over the session the app gotE_USE_CMDwith noop— correctly read as "not available" — and the two settings were dead from a phone.The line on
POST /api/loadpoints/{id}/targetis honest about its limit: the op covers thesurplus_onlyfield only. The target level and its deadline still have no command.Verification
npm run verify: svelte-check clean, 918 tests in 95 files passed (1 skipped, pre-existing), production build.node scripts/check-contract-drift.mjs <box worktree>/contract/registry.yamlagainst the paired srcfl/ftw branch: byte for byte the same.cmpbetween this file and the copies in the two sibling branches: identical.Paired with
Merge order
Both contract jobs read the
Contract-pair:line at the top of the PR body and compare against that branch instead of the default branch, so both CIs are green while all three PRs are open. Merge this one first, then srcfl/ftw, then srcfl/ftw-app. If this branch is deleted when it merges, drop theContract-pair:line from the srcfl/ftw PR body (or repoint it tomain) before its contract job reruns, becausemaincarries the change by then and the branch does not exist.🤖 Generated with Claude Code