Inspiration

In May 2024, storms in Sri Lanka brought the Ceylon Electricity Board over 61,000 breakdown complaints in six days, affecting 467,500+ users (Daily FT, 27 May 2024). In May 2025 it was 29,015 breakdowns, handled with limited staff (Ada Derana, 30 May 2025).

Most of those calls describe the same fault. One blown transformer fuse means ~80 complaints; one broken line section means hundreds. The hard part for a control room isn't hearing people. It's turning a flood of messy messages into a short list of real faults, deciding what to fix first, and restoring power without endangering crews.

What it does

GridMedic is decision support for a utility control-room operator:

  1. Understands messy complaints. BD <consumer number> SMS are parsed instantly with no AI call. Free text in English and Sinhala goes through a typed by llm() call that returns language, hazard (wire down, sparks, fire, transformer noise) and landmarks like "near pole 23".
  2. Finds the real faults. Walkers trace each complaint upstream through the grid. Transformers and line sections count their own reports, and the tallies are grouped into faults (line down, transformer fuse, service wire), each with evidence and a confidence score.
  3. Ranks them. Hazards first, then critical loads (hospital, water pump, schools, police), then homes affected and waiting time.
  4. Dispatches crews. The highest-priority fault goes to the nearest idle crew by travel time, and crews move live on the map.
  5. Changes its mind. A crew's free-text field report ("T9 is fine, service line down on Lake Road") is read by the LLM, and the fault and ranking update in front of the operator.
  6. Restores power safely. It proposes tie-switch and microgrid plans, checks feeder capacity, and blocks any switching step that would energise a section where a crew is working.
  7. Verifies fixes. If complaints continue after a repair, it looks for a hidden second fault.

There are two views from one codebase: an operator dashboard (live grid map, ranked faults with a "Why?" panel, crews, event log, crew-report box) and a customer portal (report an outage, check status by consumer number, simulated SMS updates).

A human approves every action. The demo runs on a fictional but realistic radial network: one substation, two feeders, 10 transformers and 600 consumers.

How we built it

GridMedic is built in Jac, and its design follows Jac's model: the grid is a graph, and the agents are walkers.

  • Nodes and typed edges: substation, sections, switches, transformers and homes, joined by Feeds edges in power-flow direction.
  • Walkers: Localiser (walks upstream), Energiser (recomputes who has power), Dispatcher, CrewReport, Restorer, SafetyCheck and Verifier.
  • Node abilities: transformers and sections tally their own complaints as the Localiser arrives, switches refuse to close onto a work zone, and fault nodes re-evaluate themselves from crew reports.
  • by llm() with typed outputs and sem: complaint parsing (batched ~10 per call, cached by text), crew-report parsing and fault explanations, using Gemini 3.5 Flash-Lite.
  • walker:pub endpoints and client/server codespaces: the whole API is walkers. The UI is .cl.jac, calling the server through sv import and receiving typed objects.
  • Persistence under root: the grid, complaints, faults and crews persist with no database setup.

The repository is almost entirely Jac (see GitHub's language bar).

Challenges we ran into

  • Graph traversal speed. Typed-edge filters on our inherited node types cost ~12 ms per node, so one walk over the 600-home grid took 8–10 s. Switching to plain edge traversal with type checks brought it to 0.13 s. We also spawn one Localiser per batch: 109 complaints are localised in 0.4 s.
  • Circular imports between nodes and walkers. We declared every node and walker in one file (grid.jac) and put the bodies in per-feature .impl.jac files, which also let three people work in parallel without merge conflicts.
  • LLM output types had to be declared in the same module as the by llm() function, so we use local "twin" objects copied into shared types.
  • Enums arriving as numbers in the browser, and a hot-reload trap that silently broke walkers. We documented each one as we found it.
  • Free-tier LLM quota. Deterministic SMS parsing, batching and a parse cache made a full storm cost ~15 LLM calls instead of ~150.

Accomplishments that we're proud of

  • An end-to-end agentic loop: complaints → faults → ranking → dispatch → field feedback → safe restoration → verification.
  • The "mind-change" moment: the agent corrects its own hypothesis from a crew's plain-text report.
  • A safety check enforced by the grid itself: switches guard their own work zones.
  • Sinhala complaint understanding, checked live.

What we learned

How naturally a physical network maps onto Jac's nodes, edges and walkers, and that putting logic on the nodes (abilities) makes agent behaviour easy to explain. We also learned a lot about Jac's runtime behaviour, and wrote it all down as we went.

What's next for GridMedic

Real utility network data, integration with existing complaint channels (1987 hotline SMS, CEBCare) and Outage Management Systems, GPS crew tracking, Sinhala voice input, and authentication for multi-operator use.

Built with coding agents

We used coding agents (Claude Code) during the hackathon, guided by our own project brief and work plan. All code was written during the coding window.

Built With

Share this project:

Updates