fix(web): say why planning cannot run - #954
Conversation
A selected planner mode used to show Manual and "choose a strategy" when buildMPC had skipped the planner. Diagnose now names the skip reason, and the Plan brief repeats it in household words. Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 087f5331fb
ℹ️ 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".
| if s.deps.Cfg != nil { | ||
| plannerOn = s.deps.Cfg.Planner != nil && s.deps.Cfg.Planner.Enabled | ||
| if s.deps.Cfg.Price != nil { | ||
| priceProvider = s.deps.Cfg.Price.Provider | ||
| } |
There was a problem hiding this comment.
Preserve the build-time blocker across config reloads
When an operator follows the new guidance and enables the planner, config reload immediately replaces deps.Cfg even though planner and price changes require a restart; adding a battery similarly updates deps.Capacities without constructing MPC. Once all three current values look valid, this function returns an empty reason while MPC remains nil, so the endpoints and Plan view lose the actual blocker and fall back to the generic message instead of explaining that a restart is now required. Preserve the reason captured when buildMPC skipped construction, or expose a distinct restart-required state when the live gates become ready.
Useful? React with 👍 / 👎.
| window.dispatchEvent(new CustomEvent("ftw-plan-data", { | ||
| detail: { plan: state.plan }, | ||
| })); | ||
| applyPlannerModeAvailability(); |
There was a problem hiding this comment.
Keep planner controls usable when the plan fetch fails
If only /api/mpc/plan has a transient network, JSON-decoding, authentication, or server error, fetchAll converts that request to {}, sets state.enabled.mpc to a falsy value, and this call disables every planner strategy button and Replan until the next 30-second plan poll. The status and mode endpoints may still be healthy, so a failed diagnostic read should not remove otherwise working controls or claim the planner is unavailable; retain the last confirmed availability and disable controls only after an explicit {enabled:false} response.
Useful? React with 👍 / 👎.
Fixes #766. Fixes #765.
Accepted text proposal
Issue or Discussion: #766 and #765
Maintainer comment that accepted this scope: both issues already specify the vocabulary and the Plan-view copy.
What changed
buildMPCalready has three skip gates (planner off, no price provider, no controllable battery) and logs two of them at boot./api/mpc/diagnoseand/api/mpc/plannow return that same reason when the planner was never constructed:{"enabled": false, "reason": "planner-disabled" | "no-price-provider" | "no-battery-capacity"}The Plan brief uses the reason instead of "Choose a planning strategy" when a
planner_*mode is selected but the planner cannot run. Planner strategy buttons and Replan are disabled, with the blocker in the hint.Why
On a site with a read-only driver and no controllable battery, the operator could pick Passive arbitrage and still see Manual plus "Select a planning strategy to enable it". The skip reason existed only in the boot log. Support questions like "why won't it plan?" required source-level tracing.
Boundaries and safety
Verification
go test ./internal/mpc/ ./internal/api/ ./cmd/ftw/— includingTestUnavailableReasonOrderandTestMPCDisabledEndpointsNameTheSkipReasonnode --test web/plan-brief.test.mjs web/plan-unavailable.test.mjs web/dashboard-simplification.test.mjsChecklist
web/plan.js(HTML escaping); this change is in the brief/strategy path, not the XSS sites. fix(api): protect diagnose, series and EV detail on public hosts #949 protects diagnose on public hosts and does not change the payload. fix(api): turn the house password on only from the box #950/fix(api): stop treating no-dot hosts as local #952 touch other regions ofapi.go.