Skip to content

fix(grid-status): stop the chat bot reporting a stale VRM reading as live status - #201

Merged
1vav merged 1 commit into
mainfrom
fix/grid-status-stale-battery-and-alarm-visibility
Sep 17, 2026
Merged

1vav merged 1 commit into
mainfrom
fix/grid-status-stale-battery-and-alarm-visibility

Conversation

@1vav

@1vav 1vav commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Summary

A customer asked the Telegram bot for their grid's status while its gateway had been dark for ~17 hours (an active inverter overload alarm, per VRM). The bot replied twice, confidently, that the grid was "currently powered ... battery at 75%," attributing the "unable to monitor" banner purely to a DCU/comms issue.

Three compounding gaps, all in the customer status path:

  1. customer_get_grid_status had no staleness gate on battery/solar/consumption. Inverter power was already gated (an earlier /grids fix), but battery_soc_pct/battery_current_a/solar_power_w/grid_consumption_w were read straight off VRM every time — and VRM keeps serving the last sample indefinitely after a gateway goes dark. The tool's own service_status correctly said "Unknown" / is_stale: true, but that sat right next to a concrete-looking, actually-frozen battery reading.
  2. Nothing told the model what is_stale/"Unknown" meant. Zero mentions of either anywhere in the prompt library, so the model kept the punchy number and downgraded the stale flag to a footnote.
  3. The bot couldn't see the alarm that would have explained it. get_recent_production_errors_and_power (VE.Bus alarms/power history) was staff-only.

Changes

  • live_metrics_view() (client_grid_status.py) gates battery/solar/consumption on the same voltage-reading staleness inverter_power_view already uses — all four readings come from one site's gateway in one request, so a stale/missing/errored voltage reading is the correct shared proxy.
  • customer.system.prompt gets a new "Stale or Unknown Live Data" section: don't state a stale reading as current fact, check the alarm tool first, escalate per the existing Uncertainty policy if that still doesn't explain it.
  • get_recent_production_errors_and_power is now customer-visible. It had no organization scoping of its own — safe only while staff-only. Added the same STAFF_ORG_ID/organization_id filtering get_grid_status already uses, and made the MCP dispatcher require organization_id (erroring if missing, matching every sibling customer tool) rather than falling through to the client method's unscoped default — which stays as the default specifically for the internal get_live_telemetry alert-judgment caller, which has never passed one.
  • Cross-linked both tool descriptions so the model reliably chains them (check the alarm tool when status looks stale/Unknown; translate any alarm into plain language rather than inventing a cause).

Test plan

  • New test_grid_status_stale_battery.py — live_metrics_view blanks battery/solar/consumption on stale/missing/errored/exception voltage readings, keeps fresh readings (including genuine zeros).
  • New test_production_errors_org_scoping.py — customer lookups are scoped to their own org; staff and the internal alert-judgment caller (no organization_id) stay unscoped; the MCP dispatcher rejects a call with no organization_id.
  • PYTHONPATH="$PWD:$PWD/mcp_servers" pytest mcp_servers/tests -q → 785 passed. The only 3 failures (knowledge_server, Google Sheets doc-editing) are a pre-existing local PIL import gap — reproduced identically on main before this change, untouched by this diff.
  • Regenerated mcp_servers/tool_definitions.json via export_tools.py (108 tools, 0 errors) — diff is exactly the two description edits + the visibility flip.
  • Regenerated chat_orchestrator/tests/prompt_checksums.json — diff is exactly the customer.system hash.
  • pre-commit run --all-files clean.
  • Not yet verified against a real fired alert / live customer conversation in prod.

🤖 Generated with Claude Code

…live status

customer_get_grid_status read battery SOC/current, solar power, and grid
consumption straight off VRM with no staleness gate, unlike inverter power
(gated since the earlier /grids fix). VRM keeps serving the last sample after
a gateway goes dark, so a site with no comms for hours could still return a
concrete-looking "75%, discharging" reading sitting right next to a correctly
computed service: "Unknown" / is_stale: true. Nothing in the prompt told the
model to trust the flag over the numbers, and the one tool that could explain
the contradiction (an active inverter alarm) was staff-only, so the bot told
a customer their grid was fine off data many hours old.

- live_metrics_view() gates battery/solar/consumption on the same
  voltage-reading staleness inverter_power_view already uses, since all four
  readings come from one site's gateway in one request.
- customer.system.prompt gets a "Stale or Unknown Live Data" section: don't
  state stale numbers as current fact, check get_recent_production_errors_and_power
  for an active alarm first, escalate if that doesn't explain it either.
- get_recent_production_errors_and_power is now visible_to_customer, so the
  bot can actually see alarm data in a customer conversation. It had no
  organization scoping of its own (safe only while staff-only) -- added the
  same STAFF_ORG_ID/organization_id filtering get_grid_status already has, and
  made the MCP dispatcher require organization_id rather than falling through
  to the client method's unscoped default (kept for the internal
  get_live_telemetry alert-judgment caller).

Pre-existing, unrelated local-env failures (PIL missing) in 3 knowledge_server
tests are untouched by this change -- confirmed by reproducing them on main.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@1vav
1vav merged commit c1e8238 into main Sep 17, 2026
7 checks passed
@1vav
1vav deleted the fix/grid-status-stale-battery-and-alarm-visibility branch September 17, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant