Skip to content

feat: ten real-world scenarios recorded against the live backend, plus two live-only fixes they caught - #16

Merged
trh-ds merged 1 commit into
mainfrom
worktree-scenarios
Sep 13, 2026
Merged

trh-ds merged 1 commit into
mainfrom
worktree-scenarios

Conversation

@Neal006

@Neal006 Neal006 commented Sep 13, 2026

Copy link
Copy Markdown
Owner

What

scripts/scenarios.py — ten real-world scenarios against the live backend, told as stories and recorded. Each one: who is involved, what they do (driver REST), what the site does (plan/meter/event frames on /ws, the FM's status), the promise it must keep (checks), and what the operator assistant says when asked. Transcript in docs/scenarios/run.md (+ run.json with every request/response); every line is a real request or a frame from /ws, nothing mocked.

# Scenario Promise checked
1 Priya's normal day plan ends before her time, price ≤ R, more energy in zero-carbon hours than charge-now (58 % vs 0 %), first-hour floor 3.5 kWh
2 Maya's sick kid — Leaving now deferred 0 kW → 7.0 kW within two slots, pays R, ops sees the boost
3 Dev's meeting — Leaving soon at HH:MM ready-by moves, pays R, 8 kWh in the car by the new time
4 Aisha on call — Prioritise (Ben as control) deadline unchanged, pays R, ≥ 90 % pro-rata; both above floors on a quiet site
5 Sam skips the form need estimated from history/site with confidence, plan exists
6 Ravi's PHEV need from SoC (8 kWh), plan and meter never above the car's 3.3 kW
7 Delivery van on the fleet bay full power from slot one, ASAP flag on the ops feed
8 Tom leaves early first-hour floor held (4.9 kWh), honest receipt, plan re-solved, unplug event
9 The grid API goes dark live → cached → tariff in order, no car loses its plan, back to live
10 Two-wave lunchtime rush (17 waiting) EV load never above feed − building, nobody refused, queue formed, 17 move-by times, day peak ≤ feed

36/36 checks pass. Whole run: EV load exceeded headroom by at most 0.0 kW over 335 meter frames.

Two real bugs the recording caught (neither visible to the in-process tests)

  1. Plug-in burst over headroom. POST /sessions / /demo/oversubscribe plug cars in at the 1.833 kW static share, then await (event broadcast, threaded solve) before the plan lands; the control loop ticks in that gap. 20 arrivals on a plan that already filled the feed = +26 kW for a sim-minute (run 2 printed 136.09 vs 110.0 kW). Fix in Sim.arrive: a newcomer's provisional limit is its share out of headroom nobody holds yet, reverting to the full share after DYN_VALID_MIN if no plan ever lands — the no-backend guarantee is unchanged. test_a_burst_of_plug_ins_never_draws_above_headroom_before_the_plan_catches_up fails on the old code (136.66 vs 110).
  2. Peak KPI a minute early. resolve() captures now, then awaits; if a tick runs meanwhile, the session's hist.start is a minute earlier than its first recorded draw, every sample shifts a minute early, and a 0→7 kW ramp at a slot boundary leaks into the previous slot's 5-min average (run 3 reported a 151.8 kW peak on a 150 kW feed that never happened). Fix: meter_history() anchors start to the first minute it records. test_meter_history_anchors_start_to_the_first_minute_it_records fails on the old code.

Also

  • POST /sessions/{id}/unplug — the driver pulls the plug (a real charger's StopTransaction); returns the receipt, re-solves without the car. Shared unplug_session() with the demo. Test added; contract regenerated.
  • Assistant: a bay named in the question is always pinned into the snapshot (run 1 answered "c58 is not among the 15 most relevant bays" when asked about c58); "leaves early" questions get their own template; when/hour no longer route to the signal answer. Tests added.
  • README (Demo paragraph, API block), CHANGELOG, AGENTS.md.

Checks

  • pytest: 96 passed, 1 skipped (cache test needs a key)
  • scripts/smoke.py: SMOKE OK; scripts/scenarios.py: SCENARIOS OK 36/36
  • web/ untouched (assistant drawer unchanged); wattwise/ untouched

Assumptions

  • "Record" = a scripted, replayable transcript (no browser was used, per instruction); the HTML showcase is published separately as an artifact.
  • The recorded run is committed under docs/scenarios/ as the last known-good transcript; it is not diff-checked by CI (numbers vary run to run).

🤖 Generated with Claude Code

https://claude.ai/code/session_017thvbGo7rTVnG2f46cLMzU

…s two live-only fixes they caught

scripts/scenarios.py plays a normal commuter, leaving now / soon, prioritise, a skipped form,
a plug-in hybrid, a fleet van, an early leaver, a grid-API outage and a two-wave lunchtime rush
at SIM_SPEED=120, checks the promise each scenario makes (36 checks) and records every request,
response, event and number to docs/scenarios/run.{md,json}. 36/36 pass; EV load never above
headroom over the whole run.

Fixes found by the recording (in-process tests could not see either):
- A burst of plug-ins over HTTP held the 1.833 kW static share each on top of a plan that already
  filled the feed, because the control loop ticks while the handler awaits the threaded re-solve:
  20 arrivals = +26 kW over headroom for a sim-minute. Sim.arrive now gives a newcomer its share
  only out of headroom nobody holds yet (full share after DYN_VALID_MIN if no plan ever lands).
- meter_history() anchors a session's record to the first minute it records; a re-solve landing a
  tick late used to shift every sample a minute early and report a peak above the feed.

Also: POST /sessions/{id}/unplug (the driver pulls the plug; receipt + re-solve), assistant pins
a bay named in the question into its snapshot and answers "leaves early" questions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017thvbGo7rTVnG2f46cLMzU
Copilot AI lite review requested due to automatic review settings September 13, 2026 02:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@trh-ds
trh-ds merged commit 10573db into main Sep 13, 2026
3 checks passed
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.

3 participants