Hermes at Riverside — an agentic substation copilot on Sisyphean Power & Light¶

This notebook walks through five scenarios where Hermes reasons over SP&L's Riverside substation (SUB-001) using the same data that powers the Dynamic Network Model. Three are VVO (volt-VAR optimization) scenarios; two are restoration scenarios.

How the traces in this notebook were produced¶

Every agent response in this notebook was actually run, not simulated. Hermes ran once against each scenario on real consumer hardware and the full interaction (query → tool calls → tool results → final response) was captured as a JSON fixture. The notebook replays those fixtures offline. Nothing on this page is invented for the demo.

The inference path that produced the traces:

  • Hardware. Apple Silicon Mac Mini (M-series). Consumer-class. No cloud GPU. No managed inference API.
  • Model. gemma4:e4b — Google's Gemma 4 E4B open-weights MoE, the model named in The Agentic Epoch. Quantized to Q4_K_M, ~9.6 GB on disk, runs at interactive latency on the hardware above.
  • Runtime. Ollama serving the model over a standard HTTP, OpenAI-compatible API. Zero egress to any third party during a scenario.
  • Agent loop. Hermes' tool-calling loop in this repo — 200 lines, hand-rolled, auditable. No LangChain.

The per-scenario elapsed_seconds field in each trace file is the real wall-clock time Hermes took to reason and respond. The tool calls are the calls Hermes actually made, in the order it made them. The final response is byte-for-byte what the model produced. Re-run python -m hermes.cli record --scenario all on your own hardware to produce your own copy of these traces.

The architecture this notebook showcases is described in five articles on sgridworks.com/resources:

  1. Shadow AI — why governance beats prohibition
  2. Regulatory landscape — CEII, NERC CIP, data-tier thinking
  3. Three-zone architecture — where AI is allowed and where it isn't
  4. Implementation — the 90-day path from zero to governed
  5. The Agentic Epoch — the 5-rung autonomy ladder and the open stack

Hermes here operates at Rung 2 (Shadow): it reasons and recommends, nothing actuates. The code that would run this live — LiteLLM provider adapter, Bedrock-VPC compliance gate, Streamlit UI — ships alongside this notebook. The notebook itself runs offline.

Riverside substation¶

All numbers below are read live from the Dynamic Network Model via the adapter in hermes/data/spl.py. Nothing on this page is invented for the demo.

In [1]:
from hermes.data import spl
import json

summary = spl.riverside_summary()
feeders = spl.riverside_feeders()
mg = spl.riverside_microgrid()
hc = spl.riverside_hosting_capacity()
solar = spl.riverside_solar_summary()
topo = spl.riverside_topology()
outages = spl.riverside_outages()

print(f"Substation:           {summary['name']} ({summary['substation_id']})")
print(f"Voltage:              {summary['voltage_high_kv']} / {summary['voltage_low_kv']} kV")
print(f"Rated / peak MVA:     {summary['rated_capacity_mva']} / {summary['peak_load_mva']}")
print(f"Age:                  {summary['age_years']} years")
print(f"Feeders:              {len(feeders)} — {', '.join(f['feeder_id'] for f in feeders)}")
print(f"Microgrid:            {mg['facility_name']} on {mg['feeder_id']} (island {mg['island_duration_hours']}h)")
print(f"Hosting capacity:     {hc['total_binding_kw']/1000:.1f} MW, limiting: {hc['limiting_factors']}")
print(f"Solar installs:       {solar['total_sites']} sites, {solar['total_kw']/1000:.1f} MW")
print(f"Topology:             {topo['nodes']['total']} nodes, {topo['edges']['total']} edges")
print(f"Outage history:       {len(outages)} events")
Substation:           Riverside (SUB-001)
Voltage:              230 / 12.47 kV
Rated / peak MVA:     20 / 16.5
Age:                  19 years
Feeders:              3 — FDR-0001, FDR-0002, FDR-0003
Microgrid:            Luke AFB Annex on FDR-0002 (island 2.3h)
Hosting capacity:     48.9 MW, limiting: {'voltage': 766, 'thermal': 41}
Solar installs:       645 sites, 24.2 MW
Topology:             1647 nodes, 1647 edges
Outage history:       117 events

Afternoon DER absorption on FDR-0001¶

Riverside's west feeder (FDR-0001) holds the highest concentration of rooftop solar on the substation — 12.3 MW of existing DER against a voltage-limited hosting capacity. On a clear July afternoon the feeder is running near voltage ceiling as solar generation peaks. The agent is asked what a VVO advisor would recommend for the next 15-minute interval.

In [2]:
import json
from scripts.scenarios import by_id
scenario = by_id('01-afternoon-der')
for label, fn in scenario.preamble:
    print(f'--- {label} ---')
    print(json.dumps(fn(), indent=2, default=str)[:1500])
    print()
--- hosting_capacity ---
{
  "total_binding_kw": 48865.799999999996,
  "total_existing_der_kw": 27787.899999999998,
  "by_feeder": [
    {
      "feeder_id": "FDR-0001",
      "transformers": 364,
      "thermal_kw": 32749.9,
      "voltage_kw": 22975.5,
      "binding_kw": 22780.8,
      "existing_der_kw": 12331.7
    },
    {
      "feeder_id": "FDR-0002",
      "transformers": 157,
      "thermal_kw": 16129.9,
      "voltage_kw": 10980.6,
      "binding_kw": 10930.8,
      "existing_der_kw": 5963.3
    },
    {
      "feeder_id": "FDR-0003",
      "transformers": 286,
      "thermal_kw": 22792.2,
      "voltage_kw": 15368.9,
      "binding_kw": 15154.2,
      "existing_der_kw": 9492.9
    }
  ],
  "limiting_factors": {
    "voltage": 766,
    "thermal": 41
  }
}

--- feeders ---
{
  "feeders": [
    {
      "feeder_id": "FDR-0001",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 1",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.528095,
      "longitude_tail": -112.190379,
      "direction": "W",
      "length_miles": 2.7,
      "conductor_type": "795 ACSR",
      "rated_capacity_mw": 18.5,
      "peak_load_mw": 14.8,
      "num_customers": 2372,
      "status": "active"
    },
    {
      "feeder_id": "FDR-0002",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 2",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.577361,
      "longitude_tail": -112.14348,
      "direction": "N",
      "length_miles": 3.4,
      "conductor_type": "477 ACSR",
      "rated_capacity_mw": 18.5,
      "peak_load_mw": 15.8,
      "num_customers": 1028,
      "status": "active"
    },
    {
      "feeder_id": "FDR-0003",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 3",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.435359,
      "longitude_tail": -112.14348,
      "direction": "S",
      "length_miles": 6.4,
      "conductor_type": "336 ACSR",
      "rated_capacity_mw": 19.7,
      "peak_load_mw": 12.9,
      "num_customers": 1897,
      "status": "active"
    }
  ]
}

--- der_inventory ---
{
  "solar": {
    "total_sites": 645,
    "total_kw": 24244.100000000002,
    "by_feeder": [
      {
        "feeder_id": "FDR-0001",
        "sites": 288,
        "total_kw": 10749.4
      },
      {
        "feeder_id": "FDR-0002",
        "sites": 136,
        "total_kw": 5246.2
      },
      {
        "feeder_id": "FDR-0003",
        "sites": 221,
        "total_kw": 8248.5
      }
    ]
  },
  "battery": {
    "total_sites": 147,
    "total_kwh": 8826.8
  }
}

--- load_snapshot_2023-07-19_14h ---
{
  "requested": "2023-07-19 14:00",
  "by_feeder": {
    "FDR-0001": {
      "timestamp": "2023-07-19T14:00:00",
      "load_mw": 12.149,
      "load_mvar": 2.826,
      "voltage_pu": 1.0004,
      "power_factor": 0.974
    },
    "FDR-0002": {
      "timestamp": "2023-07-19T14:00:00",
      "load_mw": 12.515,
      "load_mvar": 3.452,
      "voltage_pu": 0.9526,
      "power_factor": 0.964
    },
    "FDR-0003": {
      "timestamp": "2023-07-19T14:00:00",
      "load_mw": 10.734,
      "load_mvar": 2.917,
      "voltage_pu": 1.0471,
      "power_factor": 0.965
    }
  }
}

--- solar_snapshot_2023-07-19_14h ---
{
  "requested": "2023-07-19 14:00",
  "profile_applied_month": 7,
  "profile_applied_hour": 14,
  "clear_sky_factor": 0.768,
  "generation_pct_of_capacity": 76.8,
  "ghi_w_per_m2": 767.5,
  "estimated_solar_kw_by_feeder": {
    "FDR-0001": 8255.5,
    "FDR-0002": 4029.1,
    "FDR-0003": 6334.8
  }
}

Agent transcript¶

Query. It's July 19, 2023, 2pm local. FDR-0001 is carrying heavy solar output and voltage is pushing the upper band. What should I do for the next 15 minutes? Keep it specific — name the devices and the direction of change.

Recorded against ollama / gemma4:e4b in 77.3 s, 3 tool calls.

Hermes.

Based on the data for SUB-001 at 14:00 on 2023-07-19, voltage is the binding constraint across all monitored feeders, particularly on FDR-0001 and FDR-0003, due to high solar input.

As a VVO, I recommend initiating reactive power support to manage the voltage rise.

Recommendation (Rung 2 Draft):

  1. VAR Support (Targeting Voltage Rise): Direct operational adjustment to VAR sources to absorb reactive power ($\text{Q}$).

    • Device Action: Increase leading $\text{Q}$ injection (or set capacitor banks to absorb $\text{VARs}$) on FDR-0001 and FDR-0003.
    • Direction: Increase $\text{Q}$ absorption (or decrease $\text{Q}$ injection) at the source point for both feeders to pull the voltage back toward nominal range.
  2. Monitoring: Monitor the resulting voltage profiles immediately following any adjustment. If the voltage remains elevated, evaluate curtailing solar generation on FDR-0001 as a secondary measure.

This proposal requires operator review for executing specific setpoint changes.

Tool trace¶

1. get_load_snapshot({"timestamp": "2023-07-19 14:00"})

{"requested": "2023-07-19 14:00", "by_feeder": {"FDR-0001": {"timestamp": "2023-07-19T14:00:00", "load_mw": 12.149, "load_mvar": 2.826, "voltage_pu": 1.0004, "power_factor": 0.974}, "FDR-0002": {"timestamp": "2023-07-19T14:00:00", "load_mw": 12.515, "load_mvar": 3.452, "voltage_pu": 0.9526, "power_factor": 0.964}, "FDR-0003": {"timestamp": "2023-07-19T14:00:00", "load_mw": 10.734, "load_mvar": 2.917, "voltage_pu": 1.0471, "power_factor": 0.965}}}

2. get_solar_snapshot({"timestamp": "2023-07-19 14:00"})

{"requested": "2023-07-19 14:00", "profile_applied_month": 7, "profile_applied_hour": 14, "clear_sky_factor": 0.768, "generation_pct_of_capacity": 76.8, "ghi_w_per_m2": 767.5, "estimated_solar_kw_by_feeder": {"FDR-0001": 8255.5, "FDR-0002": 4029.1, "FDR-0003": 6334.8}}

3. get_hosting_capacity({})

{"total_binding_kw": 48865.799999999996, "total_existing_der_kw": 27787.899999999998, "by_feeder": [{"feeder_id": "FDR-0001", "transformers": 364, "thermal_kw": 32749.9, "voltage_kw": 22975.5, "binding_kw": 22780.8, "existing_der_kw": 12331.7}, {"feeder_id": "FDR-0002", "transformers": 157, "thermal_kw": 16129.9, "voltage_kw": 10980.6, "binding_kw": 10930.8, "existing_der_kw": 5963.3}, {"feeder_id": "FDR-0003", "transformers": 286, "thermal_kw": 22792.2, "voltage_kw": 15368.9, "binding_kw": 1515…

Takeaway. The hosting-capacity split tells the operator where the room is. When voltage is the binding constraint on 95% of transformers, Q-mode reactive support on existing DER and a cap-bank switch order buys real MW of headroom without capital spend. A utility extending this would feed the agent's recommendations into its DERMS as shadow-mode proposals for 2–3 quarters before a human-confirmed Rung-3 pilot.


Evening voltage sag on FDR-0003¶

FDR-0003 runs south for 6.4 miles — the longest Riverside feeder, and the one with the most residential load hitting its evening peak. Solar is dropping, AC load is still up, and the far-end voltage is sagging. The agent is asked to coordinate BESS dispatch and voltage support.

In [3]:
import json
from scripts.scenarios import by_id
scenario = by_id('02-evening-sag')
for label, fn in scenario.preamble:
    print(f'--- {label} ---')
    print(json.dumps(fn(), indent=2, default=str)[:1500])
    print()
--- feeders ---
{
  "feeders": [
    {
      "feeder_id": "FDR-0001",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 1",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.528095,
      "longitude_tail": -112.190379,
      "direction": "W",
      "length_miles": 2.7,
      "conductor_type": "795 ACSR",
      "rated_capacity_mw": 18.5,
      "peak_load_mw": 14.8,
      "num_customers": 2372,
      "status": "active"
    },
    {
      "feeder_id": "FDR-0002",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 2",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.577361,
      "longitude_tail": -112.14348,
      "direction": "N",
      "length_miles": 3.4,
      "conductor_type": "477 ACSR",
      "rated_capacity_mw": 18.5,
      "peak_load_mw": 15.8,
      "num_customers": 1028,
      "status": "active"
    },
    {
      "feeder_id": "FDR-0003",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 3",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.435359,
      "longitude_tail": -112.14348,
      "direction": "S",
      "length_miles": 6.4,
      "conductor_type": "336 ACSR",
      "rated_capacity_mw": 19.7,
      "peak_load_mw": 12.9,
      "num_customers": 1897,
      "status": "active"
    }
  ]
}

--- hosting_capacity ---
{
  "total_binding_kw": 48865.799999999996,
  "total_existing_der_kw": 27787.899999999998,
  "by_feeder": [
    {
      "feeder_id": "FDR-0001",
      "transformers": 364,
      "thermal_kw": 32749.9,
      "voltage_kw": 22975.5,
      "binding_kw": 22780.8,
      "existing_der_kw": 12331.7
    },
    {
      "feeder_id": "FDR-0002",
      "transformers": 157,
      "thermal_kw": 16129.9,
      "voltage_kw": 10980.6,
      "binding_kw": 10930.8,
      "existing_der_kw": 5963.3
    },
    {
      "feeder_id": "FDR-0003",
      "transformers": 286,
      "thermal_kw": 22792.2,
      "voltage_kw": 15368.9,
      "binding_kw": 15154.2,
      "existing_der_kw": 9492.9
    }
  ],
  "limiting_factors": {
    "voltage": 766,
    "thermal": 41
  }
}

--- der_inventory ---
{
  "solar": {
    "total_sites": 645,
    "total_kw": 24244.100000000002,
    "by_feeder": [
      {
        "feeder_id": "FDR-0001",
        "sites": 288,
        "total_kw": 10749.4
      },
      {
        "feeder_id": "FDR-0002",
        "sites": 136,
        "total_kw": 5246.2
      },
      {
        "feeder_id": "FDR-0003",
        "sites": 221,
        "total_kw": 8248.5
      }
    ]
  },
  "battery": {
    "total_sites": 147,
    "total_kwh": 8826.8
  }
}

--- load_snapshot_2023-07-19_19h ---
{
  "requested": "2023-07-19 19:00",
  "by_feeder": {
    "FDR-0001": {
      "timestamp": "2023-07-19T19:00:00",
      "load_mw": 13.378,
      "load_mvar": 2.786,
      "voltage_pu": 0.998,
      "power_factor": 0.979
    },
    "FDR-0002": {
      "timestamp": "2023-07-19T19:00:00",
      "load_mw": 15.358,
      "load_mvar": 5.875,
      "voltage_pu": 0.9551,
      "power_factor": 0.934
    },
    "FDR-0003": {
      "timestamp": "2023-07-19T19:00:00",
      "load_mw": 12.392,
      "load_mvar": 2.824,
      "voltage_pu": 1.0108,
      "power_factor": 0.975
    }
  }
}

--- solar_snapshot_2023-07-19_19h ---
{
  "requested": "2023-07-19 19:00",
  "profile_applied_month": 7,
  "profile_applied_hour": 19,
  "clear_sky_factor": 0.0,
  "generation_pct_of_capacity": 0.0,
  "ghi_w_per_m2": 0.0,
  "estimated_solar_kw_by_feeder": {
    "FDR-0001": 0.0,
    "FDR-0002": 0.0,
    "FDR-0003": 0.0
  }
}

Agent transcript¶

Query. Evening peak on FDR-0003, July 19 2023, 7pm local. Far-end voltage is sagging. I have BESS sites on feeder. What coordinated action should I take? Include the load/solar context in your reasoning.

Recorded against ollama / gemma4:e4b in 58.7 s, 4 tool calls.

Hermes.

Based on your report of voltage sagging on FDR-0003 during the evening peak (7 PM, July 19, 2023), and given that solar generation is zero at this time (night), VAR support is the most appropriate action.

The current context shows:

  • Load Snapshot (FDR-0003): Load is 12.392 MW.
  • Weather: High ambient temperature (107.6°F) suggests high cooling/base load, stressing the system.
  • Hosting Capacity: Your feeder has available headroom, but the symptom points to a voltage deficiency requiring reactive power injection.

Recommended Coordinated Action (Draft for Operator Review):

  1. Verify: Confirm the current voltage reading across the affected zone on FDR-0003.
  2. Dispatch: If voltage remains low, initiate a controlled, phased discharge of reactive power ($\text{VAR}$) from the BESS located on FDR-0003.
    • Rationale: Injecting reactive power stabilizes the voltage profile by supporting the local $\text{V}$ magnitude. Since solar input is negligible, direct VAR support from storage is necessary.
  3. Monitor: Monitor the voltage stability and subsequent feeder loading post-dispatch.

Note: This is a VVO recommendation only; do not actuate. The operator must confirm the required MVAR injection rate.

Tool trace¶

1. get_load_snapshot({"timestamp": "2023-07-19 19:00"})

{"requested": "2023-07-19 19:00", "by_feeder": {"FDR-0001": {"timestamp": "2023-07-19T19:00:00", "load_mw": 13.378, "load_mvar": 2.786, "voltage_pu": 0.998, "power_factor": 0.979}, "FDR-0002": {"timestamp": "2023-07-19T19:00:00", "load_mw": 15.358, "load_mvar": 5.875, "voltage_pu": 0.9551, "power_factor": 0.934}, "FDR-0003": {"timestamp": "2023-07-19T19:00:00", "load_mw": 12.392, "load_mvar": 2.824, "voltage_pu": 1.0108, "power_factor": 0.975}}}

2. get_solar_snapshot({"timestamp": "2023-07-19 19:00"})

{"requested": "2023-07-19 19:00", "profile_applied_month": 7, "profile_applied_hour": 19, "clear_sky_factor": 0.0, "generation_pct_of_capacity": 0.0, "ghi_w_per_m2": 0.0, "estimated_solar_kw_by_feeder": {"FDR-0001": 0.0, "FDR-0002": 0.0, "FDR-0003": 0.0}}

3. get_weather({"timestamp": "2023-07-19 19:00"})

{"requested": "2023-07-19 19:00", "at_timestamp": "2023-07-19T19:00:00", "temperature_f": 107.6, "humidity_pct": 20.1, "wind_speed_mph": 9.5, "ghi_w_per_m2": 0.0, "cloud_cover_pct": 15.0, "is_heatwave": false, "is_storm": false}

4. get_hosting_capacity({})

{"total_binding_kw": 48865.799999999996, "total_existing_der_kw": 27787.899999999998, "by_feeder": [{"feeder_id": "FDR-0001", "transformers": 364, "thermal_kw": 32749.9, "voltage_kw": 22975.5, "binding_kw": 22780.8, "existing_der_kw": 12331.7}, {"feeder_id": "FDR-0002", "transformers": 157, "thermal_kw": 16129.9, "voltage_kw": 10980.6, "binding_kw": 10930.8, "existing_der_kw": 5963.3}, {"feeder_id": "FDR-0003", "transformers": 286, "thermal_kw": 22792.2, "voltage_kw": 15368.9, "binding_kw": 1515…

Takeaway. Evening peak is where distributed BESS earns its keep — if the agent can reason about the coincident load-minus-solar shape, BESS dispatch becomes a voltage-support tool, not just a peak-shave tool. That reframing is what utilities are buying when they fund a DER orchestration layer; the agent makes the reframing cheap.


Monsoon event — DER absorption vs voltage hold¶

A summer monsoon storm cell is over west Phoenix. Cloud transients are driving sharp solar output swings on FDR-0001. The agent is asked how to think about the tradeoff between absorbing remaining DER and holding voltage steady as the storm passes.

In [4]:
import json
from scripts.scenarios import by_id
scenario = by_id('03-monsoon')
for label, fn in scenario.preamble:
    print(f'--- {label} ---')
    print(json.dumps(fn(), indent=2, default=str)[:1500])
    print()
--- weather_2020-07-08_16h ---
{
  "requested": "2020-07-08 16:00",
  "at_timestamp": "2020-07-08T16:00:00",
  "temperature_f": 115.9,
  "humidity_pct": 37.2,
  "wind_speed_mph": 25.9,
  "ghi_w_per_m2": 191.1,
  "cloud_cover_pct": 58.9,
  "is_heatwave": false,
  "is_storm": true
}

--- solar_snapshot_2020-07-08_16h ---
{
  "requested": "2020-07-08 16:00",
  "profile_applied_month": 7,
  "profile_applied_hour": 16,
  "clear_sky_factor": 0.32,
  "generation_pct_of_capacity": 32.0,
  "ghi_w_per_m2": 319.7,
  "estimated_solar_kw_by_feeder": {
    "FDR-0001": 3439.8,
    "FDR-0002": 1678.8,
    "FDR-0003": 2639.5
  }
}

--- load_snapshot_2020-07-08_16h ---
{
  "requested": "2020-07-08 16:00",
  "by_feeder": {
    "FDR-0001": {
      "timestamp": "2020-07-15T00:00:00",
      "load_mw": 6.268,
      "load_mvar": 2.708,
      "voltage_pu": 1.0252,
      "power_factor": 0.918
    },
    "FDR-0002": {
      "timestamp": "2020-07-15T00:00:00",
      "load_mw": 6.993,
      "load_mvar": 3.083,
      "voltage_pu": 1.043,
      "power_factor": 0.915
    },
    "FDR-0003": {
      "timestamp": "2020-07-15T00:00:00",
      "load_mw": 5.877,
      "load_mvar": 1.932,
      "voltage_pu": 0.967,
      "power_factor": 0.95
    }
  }
}

--- hosting_capacity ---
{
  "total_binding_kw": 48865.799999999996,
  "total_existing_der_kw": 27787.899999999998,
  "by_feeder": [
    {
      "feeder_id": "FDR-0001",
      "transformers": 364,
      "thermal_kw": 32749.9,
      "voltage_kw": 22975.5,
      "binding_kw": 22780.8,
      "existing_der_kw": 12331.7
    },
    {
      "feeder_id": "FDR-0002",
      "transformers": 157,
      "thermal_kw": 16129.9,
      "voltage_kw": 10980.6,
      "binding_kw": 10930.8,
      "existing_der_kw": 5963.3
    },
    {
      "feeder_id": "FDR-0003",
      "transformers": 286,
      "thermal_kw": 22792.2,
      "voltage_kw": 15368.9,
      "binding_kw": 15154.2,
      "existing_der_kw": 9492.9
    }
  ],
  "limiting_factors": {
    "voltage": 766,
    "thermal": 41
  }
}

--- der_inventory ---
{
  "solar": {
    "total_sites": 645,
    "total_kw": 24244.100000000002,
    "by_feeder": [
      {
        "feeder_id": "FDR-0001",
        "sites": 288,
        "total_kw": 10749.4
      },
      {
        "feeder_id": "FDR-0002",
        "sites": 136,
        "total_kw": 5246.2
      },
      {
        "feeder_id": "FDR-0003",
        "sites": 221,
        "total_kw": 8248.5
      }
    ]
  },
  "battery": {
    "total_sites": 147,
    "total_kwh": 8826.8
  }
}

Agent transcript¶

Query. Monsoon cell over the west Valley — July 8, 2020, 4pm local, storm flag is set on the weather feed. Solar output is swinging sharply every few minutes. For FDR-0001 in the next hour, what's the right tradeoff between chasing DER absorption and holding voltage steady? Be explicit about what you would NOT do.

Recorded against ollama / gemma4:e4b in 76.1 s, 4 tool calls.

Hermes.

Based on the current network constraints for FDR-0001, the primary binding factor is thermal capacity, not voltage.

In a rapidly fluctuating environment like a monsoon storm with swinging solar output, the priority must shift to system stability and thermal adherence over maximizing DER absorption.

Tradeoff Recommendation: Prioritize stable voltage profiles and respecting the thermal limit. Do not attempt to absorb the entire fluctuating DER output to "chase" apparent capacity headroom. The high variability inherent to the storm conditions risks causing rapid, protective over-current or voltage excursions that the grid model cannot predict in real-time.

Proposed Strategy (Draft for Operator Review):

  1. Voltage Support (VAR Control): If voltage sags occur, utilize VAR support (e.g., dispatching capacitor banks or smart inverter Q) to keep the voltage within steady band limits. This is the highest priority control action.
  2. DER Management: Implement smoothing controls or curtailment schedules on the variable solar sources. The goal is to limit the rate of change of power flow ($\text{dP/dt}$), rather than the instantaneous peak power absorption.
  3. System Monitoring: Continuously monitor the load vs. available thermal capacity. If the system approaches the thermal limit, immediate, controlled reduction of DER injection is required, even if solar generation is technically available.

What You Must NOT Do:

  • Do Not allow the DER management system to operate in "maximum absorption" or "maximum tracking" mode. The inherent variability will violate the stability assumptions needed for reliable operation.
  • Do Not ignore the thermal limit. Treating the thermal limit as secondary to voltage stability in this scenario is unsafe; both must be respected, but thermal constraints dictate the upper physical boundary for power flow.
  • Do Not assume that because voltage support (VARs) could theoretically unlock more power, this headroom is available for transient, fast-changing DER input under storm conditions.

This recommendation assumes the operator can access granular DER curtailment controls to manage ramp rates.

Tool trace¶

1. get_weather({"timestamp": "2020-07-08 16:00"})

{"requested": "2020-07-08 16:00", "at_timestamp": "2020-07-08T16:00:00", "temperature_f": 115.9, "humidity_pct": 37.2, "wind_speed_mph": 25.9, "ghi_w_per_m2": 191.1, "cloud_cover_pct": 58.9, "is_heatwave": false, "is_storm": true}

2. get_solar_snapshot({"timestamp": "2020-07-08 16:00"})

{"requested": "2020-07-08 16:00", "profile_applied_month": 7, "profile_applied_hour": 16, "clear_sky_factor": 0.32, "generation_pct_of_capacity": 32.0, "ghi_w_per_m2": 319.7, "estimated_solar_kw_by_feeder": {"FDR-0001": 3439.8, "FDR-0002": 1678.8, "FDR-0003": 2639.5}}

3. get_load_snapshot({"timestamp": "2020-07-08 16:00"})

{"requested": "2020-07-08 16:00", "by_feeder": {"FDR-0001": {"timestamp": "2020-07-15T00:00:00", "load_mw": 6.268, "load_mvar": 2.708, "voltage_pu": 1.0252, "power_factor": 0.918}, "FDR-0002": {"timestamp": "2020-07-15T00:00:00", "load_mw": 6.993, "load_mvar": 3.083, "voltage_pu": 1.043, "power_factor": 0.915}, "FDR-0003": {"timestamp": "2020-07-15T00:00:00", "load_mw": 5.877, "load_mvar": 1.932, "voltage_pu": 0.967, "power_factor": 0.95}}}

4. get_hosting_capacity({})

{"total_binding_kw": 48865.799999999996, "total_existing_der_kw": 27787.899999999998, "by_feeder": [{"feeder_id": "FDR-0001", "transformers": 364, "thermal_kw": 32749.9, "voltage_kw": 22975.5, "binding_kw": 22780.8, "existing_der_kw": 12331.7}, {"feeder_id": "FDR-0002", "transformers": 157, "thermal_kw": 16129.9, "voltage_kw": 10980.6, "binding_kw": 10930.8, "existing_der_kw": 5963.3}, {"feeder_id": "FDR-0003", "transformers": 286, "thermal_kw": 22792.2, "voltage_kw": 15368.9, "binding_kw": 1515…

Takeaway. Fast solar transients are where a reasoning loop beats a setpoint table. The agent can propose a 'hold-and-ride-through' posture for the storm window that a static VVO algorithm can't express — and it can defend the choice in plain English to the operator who has to sign off.


Weather outage on FDR-0003 — single-feeder restoration¶

OUT-00016 is a weather-driven outage on FDR-0003 (the long south feeder) from September 2020: an overhead-line event took out 1,007 customers for 4 hours 45 minutes. Riverside has 3 breakers, 6 sectionalizers, and 3 open-point switches — enough topology to do partial restoration via tie switching while the faulted section is cleared. The agent is asked to draft a restoration sequence.

In [5]:
import json
from scripts.scenarios import by_id
scenario = by_id('04-single-feeder-restoration')
for label, fn in scenario.preamble:
    print(f'--- {label} ---')
    print(json.dumps(fn(), indent=2, default=str)[:1500])
    print()
--- outage_OUT-00016 ---
{
  "outage_id": "OUT-00016",
  "start_time": "2020-12-20T20:00:00",
  "end_time": "2020-12-21T00:45:00",
  "cause": "weather",
  "customers_affected": 1007,
  "feeder_id": "FDR-0003",
  "equipment_involved": "overhead line",
  "duration_hours": 4.75,
  "weather_related": true,
  "substation_id": "SUB-001"
}

--- topology ---
{
  "nodes": {
    "total": 1647,
    "by_feeder": {
      "FDR-0001": 739,
      "FDR-0002": 325,
      "FDR-0003": 583
    },
    "by_equipment_class": {
      "distribution_transformer": 807,
      "fuse": 807,
      "pole_top": 19,
      "sectionalizer": 6,
      "breaker": 3,
      "open_point": 3,
      "padmount": 2
    }
  },
  "edges": {
    "total": 1647,
    "by_feeder": {
      "FDR-0001": 739,
      "FDR-0002": 325,
      "FDR-0003": 583
    },
    "by_edge_type": {
      "lateral_overhead": 1228,
      "lateral_underground": 386,
      "primary_overhead": 26,
      "primary_underground": 4,
      "bus_tie": 3
    }
  }
}

--- feeders ---
{
  "feeders": [
    {
      "feeder_id": "FDR-0001",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 1",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.528095,
      "longitude_tail": -112.190379,
      "direction": "W",
      "length_miles": 2.7,
      "conductor_type": "795 ACSR",
      "rated_capacity_mw": 18.5,
      "peak_load_mw": 14.8,
      "num_customers": 2372,
      "status": "active"
    },
    {
      "feeder_id": "FDR-0002",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 2",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.577361,
      "longitude_tail": -112.14348,
      "direction": "N",
      "length_miles": 3.4,
      "conductor_type": "477 ACSR",
      "rated_capacity_mw": 18.5,
      "peak_load_mw": 15.8,
      "num_customers": 1028,
      "status": "active"
    },
    {
      "feeder_id": "FDR-0003",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 3",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.435359,
      "longitude_tail": -112.14348,
      "direction": "S",
      "length_miles": 6.4,
      "conductor_type": "336 ACSR",
      "rated_capacity_mw": 19.7,
      "peak_load_mw": 12.9,
      "num_customers": 1897,
      "status": "active"
    }
  ]
}

--- hosting_capacity ---
{
  "total_binding_kw": 48865.799999999996,
  "total_existing_der_kw": 27787.899999999998,
  "by_feeder": [
    {
      "feeder_id": "FDR-0001",
      "transformers": 364,
      "thermal_kw": 32749.9,
      "voltage_kw": 22975.5,
      "binding_kw": 22780.8,
      "existing_der_kw": 12331.7
    },
    {
      "feeder_id": "FDR-0002",
      "transformers": 157,
      "thermal_kw": 16129.9,
      "voltage_kw": 10980.6,
      "binding_kw": 10930.8,
      "existing_der_kw": 5963.3
    },
    {
      "feeder_id": "FDR-0003",
      "transformers": 286,
      "thermal_kw": 22792.2,
      "voltage_kw": 15368.9,
      "binding_kw": 15154.2,
      "existing_der_kw": 9492.9
    }
  ],
  "limiting_factors": {
    "voltage": 766,
    "thermal": 41
  }
}

Agent transcript¶

Query. Draft a restoration plan for OUT-00016 (FDR-0003 overhead-line fault, weather-driven, ~1000 customers out). Use what the topology gives you — sectionalizers, open points, and any tie-switch paths to adjacent feeders. Call out what requires operator judgment.

Recorded against ollama / gemma4:e4b in 52.4 s, 2 tool calls.

Hermes.

Draft Restoration Plan for OUT-00016 (FDR-0003)

This draft outlines a sequence for restoring power to FDR-0003 following a weather-driven overhead-line fault. This plan is proposed at Rung 2 for operator review.

Assumptions:

  1. The faulted section of FDR-0003 has been isolated and deemed safe for testing/repair.
  2. The substation breaker at SUB-001 is the primary point of control.

Topology Reference: The network includes 6 sectionalizers and 3 open points across the feeders. These devices will be critical for sectionalizing the fault and managing subsequent re-energization.


Restoration Sequence Draft¶

Phase 1: Fault Verification & Isolation (Requires Crew On-Site)

  1. Verify Fault Clearance: Confirm the overhead-line fault on FDR-0003 is cleared and the line is de-energized at the work site.
  2. Sectionalize: Remotely operate the sectionalizers on FDR-0003 upstream of the fault location to isolate the damaged segment.
  3. Open Point Confirmation: Verify all necessary protective devices (open points) are set to isolate the faulty section, preventing backfeed.

Phase 2: Power Restoration (Recommendation)

  • Primary Attempt (Normal Restoration): If the repair is localized, re-energize FDR-0003 by closing the main substation breaker at SUB-001, followed by sequential closure of the sectionalizers/reclosers downstream until the healthy load is reached.
  • Contingency Attempt (Tie-Switch Path): If the fault is extensive and cannot be cleared, assess the topology for a viable tie-switch path from FDR-0001 or FDR-0002.
    • Recommendation: Check tie-switch status and voltage compatibility for temporarily feeding the critical load area of FDR-0003 from an adjacent feeder, if available and safe.

Phase 3: Finalization

  1. Monitor voltage and load flow across the restored sections of FDR-0003.
  2. Once stability is confirmed, restore all open points and remove all temporary sectionalizing measures to restore normal system configuration.

⚠ Operator Judgment Required Actions:

  • Fault Location Pinpointing: The exact coordination of sectionalizers and open points relative to the fault location must be determined by field crews.
  • Re-energization Sequence: The precise, step-by-step opening/closing sequence of sectionalizers and switches requires detailed confirmation against the fault-end status and local protection coordination plans.
  • Tie-Switch Feasibility: Any proposal to utilize a tie-switch path to FDR-0001 or FDR-0002 is conditional upon real-time substation breaker status, load levels on the supplying feeder, and clearance from the protection engineer.

Tool trace¶

1. get_outage({"outage_id": "OUT-00016"})

{"outage_id": "OUT-00016", "start_time": "2020-12-20T20:00:00", "end_time": "2020-12-21T00:45:00", "cause": "weather", "customers_affected": 1007, "feeder_id": "FDR-0003", "equipment_involved": "overhead line", "duration_hours": 4.75, "weather_related": true, "substation_id": "SUB-001"}

2. get_topology({})

{"nodes": {"total": 1647, "by_feeder": {"FDR-0001": 739, "FDR-0002": 325, "FDR-0003": 583}, "by_equipment_class": {"distribution_transformer": 807, "fuse": 807, "pole_top": 19, "sectionalizer": 6, "breaker": 3, "open_point": 3, "padmount": 2}}, "edges": {"total": 1647, "by_feeder": {"FDR-0001": 739, "FDR-0002": 325, "FDR-0003": 583}, "by_edge_type": {"lateral_overhead": 1228, "lateral_underground": 386, "primary_overhead": 26, "primary_underground": 4, "bus_tie": 3}}}

Takeaway. Restoration reasoning is the cleanest fit for agentic assistance — the decision surface is well-defined (isolate, transfer, restore) and the evidence comes from the outage ticket, the topology, and the adjacent-feeder load margin. A shadow-mode agent logging its recommended sequence alongside the operator's actions builds an audit-grade evidence base for Rung-3 authorization without changing anything the operator does today.


Multi-feeder event — Luke AFB microgrid islanding¶

OUT-00009 hit FDR-0002 in May 2020 — a weather-driven fuse operation took the feeder down for 8 hours and affected 806 customers. FDR-0002 hosts Luke AFB Annex (MG-0005), a military microgrid with 4 MW solar + 3 MW / 12 MWh BESS + 2.5 MW CHP, capable of 2.3 hours of islanded operation against 5.16 MW of critical load. The agent is asked when and how to island and resync.

In [6]:
import json
from scripts.scenarios import by_id
scenario = by_id('05-microgrid-island')
for label, fn in scenario.preamble:
    print(f'--- {label} ---')
    print(json.dumps(fn(), indent=2, default=str)[:1500])
    print()
--- outage_OUT-00009 ---
{
  "outage_id": "OUT-00009",
  "start_time": "2020-08-03T14:00:00",
  "end_time": "2020-08-03T22:07:00",
  "cause": "weather",
  "customers_affected": 806,
  "feeder_id": "FDR-0002",
  "equipment_involved": "fuse",
  "duration_hours": 8.116666666666667,
  "weather_related": true,
  "substation_id": "SUB-001"
}

--- microgrid ---
{
  "microgrid_id": "MG-0005",
  "feeder_id": "FDR-0002",
  "substation_id": "SUB-001",
  "latitude": 33.556571,
  "longitude": -112.143475,
  "facility_type": "military",
  "facility_name": "Luke AFB Annex",
  "solar_capacity_mw": 4.0,
  "battery_power_mw": 3.0,
  "battery_energy_mwh": 12.0,
  "chp_capacity_mw": 2.5,
  "total_generation_mw": 6.5,
  "peak_load_mw": 11.35,
  "critical_load_mw": 5.16,
  "can_island": true,
  "island_duration_hours": 2.3,
  "interconnection_date": "2022-06-01T00:00:00",
  "status": "active"
}

--- topology ---
{
  "nodes": {
    "total": 1647,
    "by_feeder": {
      "FDR-0001": 739,
      "FDR-0002": 325,
      "FDR-0003": 583
    },
    "by_equipment_class": {
      "distribution_transformer": 807,
      "fuse": 807,
      "pole_top": 19,
      "sectionalizer": 6,
      "breaker": 3,
      "open_point": 3,
      "padmount": 2
    }
  },
  "edges": {
    "total": 1647,
    "by_feeder": {
      "FDR-0001": 739,
      "FDR-0002": 325,
      "FDR-0003": 583
    },
    "by_edge_type": {
      "lateral_overhead": 1228,
      "lateral_underground": 386,
      "primary_overhead": 26,
      "primary_underground": 4,
      "bus_tie": 3
    }
  }
}

--- feeders ---
{
  "feeders": [
    {
      "feeder_id": "FDR-0001",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 1",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.528095,
      "longitude_tail": -112.190379,
      "direction": "W",
      "length_miles": 2.7,
      "conductor_type": "795 ACSR",
      "rated_capacity_mw": 18.5,
      "peak_load_mw": 14.8,
      "num_customers": 2372,
      "status": "active"
    },
    {
      "feeder_id": "FDR-0002",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 2",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.577361,
      "longitude_tail": -112.14348,
      "direction": "N",
      "length_miles": 3.4,
      "conductor_type": "477 ACSR",
      "rated_capacity_mw": 18.5,
      "peak_load_mw": 15.8,
      "num_customers": 1028,
      "status": "active"
    },
    {
      "feeder_id": "FDR-0003",
      "substation_id": "SUB-001",
      "name": "Riverside Fdr 3",
      "voltage_kv": 12.47,
      "latitude_head": 33.528095,
      "longitude_head": -112.14348,
      "latitude_tail": 33.435359,
      "longitude_tail": -112.14348,
      "direction": "S",
      "length_miles": 6.4,
      "conductor_type": "336 ACSR",
      "rated_capacity_mw": 19.7,
      "peak_load_mw": 12.9,
      "num_customers": 1897,
      "status": "active"
    }
  ]
}

Agent transcript¶

Query. OUT-00009: weather-driven fuse operation, FDR-0002, 8-hour outage. FDR-0002 hosts the Luke AFB Annex microgrid (MG-0005). Walk me through whether to island, when, and the resync conditions. Be explicit about what happens after the 2.3-hour battery window if the feeder is still out.

Recorded against ollama / gemma4:e4b in 111.2 s, 4 tool calls.

Hermes.

This response provides a sequence of recommended actions for $\text{OUT-00009}$ on $\text{FDR-0002}$, prioritizing asset protection and load continuity based on the $\text{MG-0005}$ operational limits.

System Context:

  • Outage: $\text{OUT-00009}$ (Weather-driven, $\text{FDR-0002}$, approx. 8+ hours).
  • Asset: $\text{MG-0005}$ (Luke AFB Annex).
  • Constraint: $\text{MG-0005}$ has a battery runtime limit of 2.3 hours.

⚡️ Proposed Restoration Sequence (Rung 2)¶

Phase 1: Initiation of Islanding (Recommended Immediately)¶

When: As soon as the outage confirmation is stable and the initial utility supply interruption is confirmed, $\text{MG-0005}$ must transition to island mode to utilize its $\text{BESS}$ reserves. Delaying this risks an unscheduled shutdown.

How (Action Proposal):

  1. Confirm Boundaries: Verify the exact $\text{PCC}$ connection points for $\text{MG-0005}$ relative to the $\text{SUB-001}$ bus structure.
  2. Isolate $\text{MG-0005}$: Propose opening the appropriate upstream $\text{breaker}$ or $\text{sectionalizer}$ protecting $\text{MG-0005}$ on $\text{FDR-0002}$ to isolate it from the utility source.
  3. Stabilization: Allow the microgrid controls to confirm self-sustained operation using $\text{Solar}$ (4.0 MW), $\text{BESS}$ (3.0 MW), and $\text{CHP}$ (2.5 MW).

Phase 2: Operation During Islanding (0 to 2.3 Hours)¶

Goal: Maintain critical loads ($\text{Critical Load} = 5.16 \text{ MW}$) while maximizing $\text{BESS}$ utilization.

  • Monitoring: Continuously monitor $\text{BESS}$ State-of-Charge ($\text{SoC}$), $\text{CHP}$ output vs. $\text{Solar}$ output, and load shed status.
  • Action: Shed all non-critical load immediately to extend $\text{BESS}$ reserve past the 2.3-hour threshold, prioritizing the $\text{Critical Load}$.

Phase 3: Post-Battery Depletion Contingency (After 2.3 Hours)¶

Condition: If the main grid remains out past the 2.3-hour mark, $\text{MG-0005}$ will enter a critical state and will be unable to support the full $\text{Critical Load}$ through $\text{BESS}$ alone.

Action Plan (Requires Operator Judgment):

  1. Load Shedding: Immediately implement pre-determined emergency load shedding protocols for non-essential $\text{MG-0005}$ assets to conserve remaining $\text{BESS}$ energy.
  2. Request External Support: $\text{Requires operator judgment}$: Simultaneously request confirmation from SP&L operations for an expected restoration timeline or deployment of supplemental generation (e.g., external $\text{diesel}$ support).
  3. Sustain Mode: Operate in a severely restricted mode, only powering the absolute life-safety critical loads until external support or system restoration is confirmed.

Phase 4: Resynchronization (Requires Utilities Confirmation)¶

Prerequisite: This phase can only begin when the primary substation $\text{SUB-001}$ feeder $\text{FDR-0002}$ is confirmed stable, within acceptable voltage/frequency bands, and the initial weather event has passed.

Sequence:

  1. Utility Stabilization Check: Verify substation voltage ($\text{pu}$) and frequency match $\text{MG-0005}$ output tolerances.
  2. Internal System Confirmation: Confirm $\text{MG-0005}$ is stable and prepared to accept external power.
  3. Resync: Propose closing the isolated upstream $\text{breaker}$ in a controlled sequence, allowing the $\text{MG-0005}$ control system to manage the controlled reconnection.

Summary of Dependence: The entire plan hinges on the 2.3-hour battery window. After this time, operational focus shifts entirely to load shedding and external resource coordination until the main utility source is restored.

Tool trace¶

1. get_outage({"outage_id": "OUT-00009"})

{"outage_id": "OUT-00009", "start_time": "2020-08-03T14:00:00", "end_time": "2020-08-03T22:07:00", "cause": "weather", "customers_affected": 806, "feeder_id": "FDR-0002", "equipment_involved": "fuse", "duration_hours": 8.116666666666667, "weather_related": true, "substation_id": "SUB-001"}

2. get_microgrid({"microgrid_id": "MG-0005"})

{"error": "bad arguments to get_microgrid: get_microgrid() got an unexpected keyword argument 'microgrid_id'"}

3. get_microgrid({})

{"microgrid_id": "MG-0005", "feeder_id": "FDR-0002", "substation_id": "SUB-001", "latitude": 33.556571, "longitude": -112.143475, "facility_type": "military", "facility_name": "Luke AFB Annex", "solar_capacity_mw": 4.0, "battery_power_mw": 3.0, "battery_energy_mwh": 12.0, "chp_capacity_mw": 2.5, "total_generation_mw": 6.5, "peak_load_mw": 11.35, "critical_load_mw": 5.16, "can_island": true, "island_duration_hours": 2.3, "interconnection_date": "2022-06-01T00:00:00", "status": "active"}

4. get_topology({})

{"nodes": {"total": 1647, "by_feeder": {"FDR-0001": 739, "FDR-0002": 325, "FDR-0003": 583}, "by_equipment_class": {"distribution_transformer": 807, "fuse": 807, "pole_top": 19, "sectionalizer": 6, "breaker": 3, "open_point": 3, "padmount": 2}}, "edges": {"total": 1647, "by_feeder": {"FDR-0001": 739, "FDR-0002": 325, "FDR-0003": 583}, "by_edge_type": {"lateral_overhead": 1228, "lateral_underground": 386, "primary_overhead": 26, "primary_underground": 4, "bus_tie": 3}}}

Takeaway. Microgrid islanding decisions live at the intersection of protection coordination, battery state-of-charge, and a customer the utility cannot afford to drop. An agent that pulls the microgrid's published envelopes, the outage ETR, and the CHP fuel status into one reasoning pass — and explains the tradeoffs in plain English — is the shortest path from 'the operator manually thinks through this' to 'the operator confirms a recommendation in 30 seconds.'


What this doesn't show¶

  • Anything actuating. By design. Rung 2 means recommendation only.
  • Live customer data. The SP&L set is synthetic.
  • Claude-tier reasoning. These traces were produced by Gemma 4 E4B on consumer hardware, which matches the story in the Agentic Epoch article (open weights, edge-feasible). Recording the same scenarios against Claude over a VPC endpoint would produce tighter prose and occasionally catch context the smaller model misses. The scripts/record_traces.py CLI supports both — point HERMES_LLM_PROVIDER at bedrock and re-run, after you've satisfied the compliance gate in docs/SECURITY.md.

What a utility would do next¶

  • Swap the SP&L adapter for the utility's own historian, CMMS, and one-line source of truth (the contract: hermes/data/spl.py is the only place that reads files)
  • Record months of shadow-mode traces alongside live operator actions
  • Wire the tool-trace stream into the utility SIEM for CIP-007 evidence
  • When shadow-mode credibility is built, graduate to Rung 3 with human confirmation gates

The repo: github.com/SGridworks/hermes-riverside-poc