DepotOps
Chapter 1 — where the thesis started
A live operations agent that re-simulates hundreds of possible remainders of the night after every depot event, then tests what to do about it.
- Futures simulated after each event
- 200
- Paired futures per candidate action
- 400
- Risk after the 18:40 charger failure
- ~68%
- of 200 futures end with a van not ready
- Risk after the recommended action
- ~68%→~7%
Figures with a dotted underline carry their source. Hover one to see whether it was verified in code or reported in project documentation.
Context
The situation
Electric delivery depots run on a nightly plan built from averages. The plan is made once, in the evening, against a night that has not happened yet.
The problem
A point forecast is not the problem. The problem is that several things vary at once — arrival times, plug-in state of charge, charger availability, energy price — and a plan that is optimal for the expected night can fail badly on a plausible one. Deterministic optimisation does not cover that space.
What I built
Model the depot physically, then sample the night many times. A deterministic charging-physics engine allocates power under a hard site cap; a seeded scenario generator introduces delays, failures and price spikes. After each real event the agent re-runs hundreds of rollouts from the live state, and when risk jumps above the session baseline it evaluates five discrete counteractions against the same sampled futures and ranks them.
Every night a depot operator makes one bet: that tonight’s charging plan gets every van to its departure slot on time. Vans arrive late, a charger dies, the grid price spikes, and a plan that looked fine at 17:00 quietly becomes one where vans miss their morning routes. DepotOps detects that before it happens. After every event it simulates hundreds of possible remainders of the night from the live depot state, and when failure risk jumps above the session’s own measured baseline it plays five candidate interventions through the same sampled futures and recommends one — with the numbers attached.
Architecture
How it fits together
- Depot eventsinput
arrivals, failures, prices
- LiveSessiondeterministic
live depot state, advanced event by event
- Charging physicsdeterministic
power allocation under a hard site cap
- Monte Carlodeterministic
200 futures after every event
- Risk detectorguard
fires on a jump above session baseline
- Counterfactualsdeterministic
5 actions × 400 paired futures
- Operator brieflanguage model
LLM, with deterministic fallback
- Operator decisionoutput
accept, override, ask why
Key decisions
The choices that shaped it
- 01
The site cap is unbreakable by construction
The allocator distributes a power budget rather than checking a limit afterwards. No charging policy, however wrong, can exceed the site cap — it is a structural guarantee, not a validation step that a future refactor could skip.
engine/physics.py
- 02
Counts, never confidence scores
Every risk figure is phrased as a count over concrete simulated outcomes: X% of 200 simulated futures end with a van not ready. An operator can reason about a count of nights. An abstract confidence number invites either blind trust or dismissal.
- 03
Paired futures, and a noise floor that is honoured
Each candidate action is played through the same sampled nights, so the comparison reflects the actions rather than sampling luck. Differences that fall inside the Monte Carlo noise floor are treated as ties and decided on cost, instead of pretending to a precision the sample size does not support.
agent/recommender.py
- 04
The language model is not on the critical path
It writes the operator brief and answers grounded "why" questions. It never chooses an action. If it is unreachable a deterministic template produces the same brief from the same numbers, and the operator cannot tell the difference.
agent/briefing.py
- 05
A real tariff, not an invented one
The demo depot’s tariff is fitted from the EPEX France day-ahead curve for 12 December 2024, fetched from the free energy-charts.info API (Fraunhofer ISE, CC BY 4.0) — the same feed a production deployment would consume. The raw data is committed for provenance.
scripts/fit_tariff.py
- 06
The demo is a regression test
The scripted story beats are pinned by a test that runs in CI on every push. The rehearsal runs on the build server, not just on stage, so the numbers in the write-up cannot drift away from the engine.
tests/test_demo_story.py
Evidence
What it actually looked like

The operator interface: event feed, agent recommendation, live depot state.
InterfaceMeasured
Every figure, with its source
- Futures simulated after each event
- 200
- Paired futures per candidate action
- 400
- Risk after the 18:40 charger failure
- ~68%
- of 200 futures end with a van not ready
- Risk after the recommended action
- ~68%→~7%
- Monte Carlo throughput
- 1,000 rollouts < 3s
- Tests
- 30
- physics invariants, seeded reproducibility, calibration gap, agent flow, API smoke, demo story
Figures with a dotted underline carry their source. Hover one to see whether it was verified in code or reported in project documentation.
Recognition
Result
500+ participants
Awarded for placing as winner of the RAISE Hackathon Remote.

Certificate of First Place — Crusoe track, 4–5 July 2026.
CertificateThe result is context. What the system does, and how it was measured, is everything above this.
My contribution
Sole author — engine, agent, API, interface and deployment.
What I owned
- Deterministic charging-physics engine with a site cap that cannot be exceeded by construction
- Seeded scenario generator behind an UncertaintyModel protocol
- Monte Carlo layer, including mid-night rollouts from live state
- Risk detector, five candidate actions, paired counterfactual evaluation and noise-aware ranking
- FastAPI boundary, React operator interface, Docker Compose, Azure deployment and CI
Stack and limits
Built with
- Python
- NumPy
- FastAPI
- React
- Vite
- Docker
- Azure Container Instances
- GitHub Actions
Techniques
- Seeded Monte Carlo simulation
- Counterfactual evaluation on paired futures
- Deterministic physics modelling
- LLM briefing with deterministic fallback
- Operator preference learning from overrides
What it is not
- The charging model is decision-support, not physics: it does not model battery charge curves, thermal behaviour, cable losses or degradation.
- All operational data is synthetic. No public depot-fleet telemetry exists, and that gap is part of what makes the problem worth working on.
- It compares a fixed set of named policies. It does not search for an optimal one.
- A production deployment would need integration with telematics, charge point management and an energy supplier, plus validation against measured outcomes.
What carried forward
Two things carried forward. First, that operators trust a count of simulated nights far more than a probability. Second, that the interesting boundary is not "how good is the model" but "which parts of the answer is the model allowed to produce at all". Forkcast was built to make that boundary the product.
Next
Chapter 02
Forkcast
A language model can explain a decision. It cannot be allowed to invent the numbers behind it. Forkcast enforces that in code, and exposes the verifier so anyone can attack it.
Explore case study→Chapter 03
FleetMind
Two prototypes converged into one product direction. Simulate the operational night, verify every figure, and give the operator a decision they can question and override.
Explore case study→