| name | nus-orchestrate |
| description | Daily pipeline orchestration for the NUS campus energy management system. Coordinates all 8 specialist agents in the correct 7-phase sequence. Use when running the full daily pipeline, triggering a partial re-run after recalibration, or deciding which buildings need action. |
| metadata | {"openclaw":{"emoji":"๐ฏ"}} |
NUS Orchestrate Skill
Overview
This skill defines the full daily pipeline sequence, handoff payloads between agents,
decision rules, and fallback behaviour. The Orchestrator never does analysis itself โ
it delegates and stitches.
The Orchestrator is fronted by an LLM (default model: openai/gpt-5.1). It must convert
natural-language user commands into structured intents, then drive the pipeline
accordingly.
Intent Parsing (LLM Front-End)
When the user talks to the Orchestrator (via ACP, Slack, or web UI), the LLM must
normalize their request into a small JSON intent. Use this as the contract:
{
"action": "run_workflow",
"building_id": "FOE1",
"mode": "full",
"months": "latest"
}
Natural Language โ Intent Rules
-
If the user says:
- "test the pipeline for FOE3"
- "test the pipeline for FOE3 end-to-end"
- "run FOE3 end-to-end"
- "end-to-end test for FOE3"
Then parse as:
{
"action": "run_workflow",
"building_id": "FOE3",
"mode": "full",
"months": "latest"
}
-
If the user says:
- "run workflow for FOE1"
- "single-building run for FOE1"
- "run FOE1"
Then parse as:
{
"action": "run_workflow",
"building_id": "FOE1",
"mode": "full",
"months": "latest"
}
-
If the user says:
- "simulate FOE1 only"
- "just simulate FOE1 for 2025-01"
Then parse as:
{
"action": "run_workflow",
"building_id": "FOE1",
"mode": "simulate_only",
"months": ["2025-01"]
}
-
If the user says:
- "calibrate FOE1"
- "re-run calibration for FOE1"
Then parse as:
{
"action": "run_workflow",
"building_id": "FOE1",
"mode": "calibration_only",
"months": "latest"
}
-
If the user says:
- "run interventions for FOE1"
- "carbon scenarios for FOE1"
Then parse as:
{
"action": "run_workflow",
"building_id": "FOE1",
"mode": "interventions_only",
"months": "latest"
}
LLM Behaviour
- Always infer the intent JSON from the user message.
- Echo the intent back in a short confirmation message, e.g.:
- "Intent: run_workflow for FOE1, mode=full, months=latest. Proceeding."
- Hand this intent to the runner script (
run_pipeline.py) by translating it to
the appropriate CLI call (see below).
Intent โ CLI Mapping
For action == "run_workflow":
-
Resolve month:
- If
months == "latest", use the most recent month that has ground-truth and
weather available (or default to the current calendar month).
- If
months is a list, use the last entry.
-
Compute CLI flags:
base_cmd = "python3 ~/.openclaw/workspace-ORCHESTRATOR/skills/nus-orchestrate/scripts/run_pipeline.py"
# Single-building run
if intent.building_id is not null:
buildings_part = f"--buildings {intent.building_id}"
else:
buildings_part = "" # campus-wide
month_part = f"--month {month}"
# Mode handling
if intent.mode == "full":
extra = "" # no extra flags
elif intent.mode == "simulate_only":
extra = "--skip-weather --skip-simulation" # Or adjust when dedicated entrypoints exist
elif intent.mode == "calibration_only":
extra = "--resume-from diagnosis"
elif intent.mode == "interventions_only":
extra = "--resume-from intervene"
else:
extra = ""
final_command = f"{base_cmd} {month_part} {buildings_part} {extra}".strip()
- The orchestrator agent (in ACP) should execute
final_command on the host
shell and stream logs back to the user.
Runner Script
The pipeline can be driven end-to-end from a single script:
python3 ~/.openclaw/workspace-orchestrator/skills/nus-orchestrate/scripts/run_pipeline.py \
--month 2024-08
CLI Arguments
| Argument | Default | Description |
|---|
--month YYYY-MM | (required) | Target simulation month |
--buildings B1 B2 โฆ | all 23 GT buildings | Limit run to specific buildings |
--skip-weather | off | Skip Phase 1a; use existing calibrated EPW or TMY fallback |
--skip-simulation | off | Skip Phase 2; use existing parsed CSVs for detection |
--dry-run | off | Print what would run without executing anything |
--workers N | 4 | Parallel EnergyPlus workers (Forge batch) |
--resume-from PHASE | (none) | Resume from a specific phase, skipping all earlier ones. Choices: weather, simulation, detection, diagnosis, report, notify, intervene |
Environment
| Variable | Default | Description |
|---|
NUS_PROJECT_DIR | /Users/ye/nus-energy | Root of the NUS energy project tree |
What each phase does
| Phase | Name | Agent(s) | What runs |
|---|
| 0 | Setup | โ | Creates output dirs; loads/creates pipeline_state.json; opens log file |
| 1a | Weather | Nimbus | fetch_weather.py โ validate_weather.py โ build_epw.py; sets epw_source |
| 1b | Ground truth prep | Radar | prepare_ground_truth.py (skipped if sentinel CSV already exists) |
| 2 | Simulation | Forge | simulate.py batch (or per-building with --idf when --buildings used) |
| 3 | Detection gate | Radar | Reads simulation_summary.csv; classifies each building (calibrated/warning/critical/no_data); collects needs_diagnosis list |
| 4 | Diagnosis loop | Lens โธ โ Chisel โ Slack | Writes lens_input.json per building and pauses (Lens is LLM-only, no script). On resume: reads lens_output.json, runs Chisel dry-run diff, waits for slack_approval.json, then applies patch + re-runs simulation if approved |
| 5 | Report | Ledger | report.py โ generates daily/weekly report to outputs/reports/ |
| 6 | Notification | Signal | pipeline_trigger.py โ delivers results to Slack; prints decarbonisation decision gate prompt |
| 7 | Intervention | Compass + Oracle | carbon_scenarios.py per building; query.py to enable Oracle Q&A mode |
Lens pause/resume pattern (Phase 4)
Lens (DiagnosisAgent) is a pure LLM agent โ it has no callable script. The pipeline handles this by:
- Writing
outputs/{BUILDING}/lens_input.json with CVRMSE, NMBE, file paths, and iteration count
- Printing a
โธ PIPELINE PAUSED banner and exiting cleanly
- Waiting for the human/AI to run Lens and write
outputs/{BUILDING}/lens_output.json
- Resuming the pipeline with
--resume-from diagnosis
Similarly, Slack approval is handled via outputs/{BUILDING}/slack_approval.json:
{"approved": true}
After approval, Chisel patches the IDF and Forge re-runs for just that building.
Intervention approval is via outputs/intervention_approved.json:
{"approved": true, "buildings": ["all"]}
(Defaults to all GT buildings if the file is absent.)
Typical resume sequence
python3 run_pipeline.py --month 2024-08
python3 run_pipeline.py --month 2024-08 --resume-from diagnosis
python3 run_pipeline.py --month 2024-08 --resume-from intervene
Log & state output
- Log file:
$NUS_PROJECT_DIR/outputs/pipeline_{MONTH}.log
- State file:
$NUS_PROJECT_DIR/outputs/pipeline_state.json
Detection classifications (Phase 3)
| Status | Condition |
|---|
calibrated | CV(RMSE) โค 15% and |NMBE| โค 5% |
warning | CV(RMSE) 15โ30% or |NMBE| 5โ10% |
critical | CV(RMSE) > 30% or |NMBE| > 10% |
no_data | No entry in simulation_summary.csv |
error | Forge failed for this building |
Agent Directory
| Agent | Nickname | Trust | Role | Workspace |
|---|
| WeatherAgent | Nimbus ๐ฆ๏ธ | T1 | Fetch live weather, build site-calibrated EPW | workspace-weatheragent |
| SimulationAgent | Forge โก | T3 | Run EnergyPlus simulations | workspace-simulationagent |
| AnomalyAgent | Radar ๐ | T2 | Parse outputs, compute ASHRAE metrics, detection gate | workspace-anomalyagent |
| DiagnosisAgent | Lens ๐ฉบ | T2 | Root cause analysis | workspace-diagnosisagent |
| RecalibrationAgent | Chisel ๐ง | T3 | Propose + apply IDF patches (human approval required) | workspace-recalibrationagent |
| ReportAgent | Ledger ๐ | T3 | Daily/weekly reports, archives Calibration_log.md | workspace-reportagent |
| SlackNotificationAgent | Signal ๐ฃ | T2 | Slack delivery + decarbonisation decision gate | workspace-slacknotificationagent |
| InterventionAgent | Compass ๐งญ | T2 | Carbon reduction scenarios (shallow/medium/deep) | workspace-interventionagent |
| QueryAgent | Oracle ๐ฎ | T2 | Clarification during approval (Phase 4) + stakeholder Q&A (Phase 7) | workspace-queryagent |
Full Daily Pipeline (7 Phases)
Phase 1 โ Input
โโ Gather: GT CSV + building registry JSON + IDF files + campus shapefile
Phase 1a โ Weather (Nimbus ๐ฆ๏ธ, T1) [parallel to Phase 1b]
โโ Fetch hourly observations โ priority: (1) NUS localized API MET_E1A, (2) NUS onsite stations, (3) data.gov.sg S121
โโ Validate: spike detection + gap-fill + quality flag
โโ Build site-calibrated EPW for simulation month
โโ Notify Orchestrator: calibrated_epw_ready โ Forge uses it via --month flag
โโ If unavailable: Forge falls back to base TMY (log warning to Slack)
Phase 1b โ Registry Validation (Forge โก, pre-flight)
โโ Check building exists in building_registry.json; auto-populate if missing
Phase 2 โ Simulation (Forge โก, T3)
โโ Run EnergyPlus on IDF files at monthly resolution
โโ Uses calibrated EPW from Nimbus if available, else base TMY
โโ GT CSV also flows directly to Radar alongside simulation output
Phase 3 โ Detection (Radar ๐, T2) โ DECISION GATE
โโ CV(RMSE) โค 15% AND NMBE โค ยฑ5% โ โ
model accurate โ Phase 5 (Report)
โโ Thresholds not met โ โ model inaccurate โ Phase 4 (Diagnosis loop)
Phase 4 โ Diagnosis loop (Lens ๐ฉบ โ Chisel ๐ง โ Human approval)
โโ Lens: root cause hypotheses
โโ Chisel: proposes IDF parameter changes (bounded by parameter_bounds.json)
โโ Signal: sends parameter proposal to engineer via Slack
โโ Oracle: available to answer engineer's clarifying questions during review
โโ APPROVED โ updated IDFs โ back to Forge (Phase 2, loop)
โโ REJECTED โ fall through to Phase 5
Phase 5 โ Report (Ledger ๐, T3)
โโ Archives all findings to Calibration_log.md
โโ Documents root-cause hypotheses from Lens
โโ Generates report regardless of diagnosis loop outcome
Phase 6 โ Notification (Signal ๐ฃ, T2) โ DECISION GATE
โโ Delivers calibration results to Slack
โโ Decarbonisation required? โ YES โ Phase 7
โโ No โ workflow ends
Phase 7 โ Intervention (Compass ๐งญ + Oracle ๐ฎ, both T2)
โโ Compass: generates decarbonisation strategy
- ๐ข Shallow โ quick wins (setpoints, scheduling)
- ๐ก Medium โ moderate retrofit (LED, glazing)
- ๐ด Deep โ major retrofit (HVAC, PV, envelope)
โโ Oracle: concurrently answers stakeholder questions via Slack
Decision Rules
When to run Forge
- Full run: every morning (cron) or on manual trigger
- Targeted re-run: after Chisel patches an IDF โ run only that building
- Skip: if
outputs/{building}/parsed/{building}_monthly.csv exists and is from today (check mtime)
Detection thresholds (ASHRAE Guideline 14, current detector basis)
- CV(RMSE) โค 15% โ pass
- NMBE โค ยฑ5% โ pass
- Both must pass; either failing โ Diagnosis loop
Iteration cap enforcement (Diagnosis loop)
Before spawning Lens on a building:
- Read
$NUS_PROJECT_DIR/calibration_log.md
- Count existing iterations for that building
- If โฅ 3: skip Lens, set status =
engineer_review_required, notify Signal in #private
When to run Compass
- Only for GT buildings with current ground truth coverage:
FOE6, FOE9, FOE13, FOE18, FOS43, FOS46, FOE1, FOE3, FOE5, FOE10, FOE11, FOE12, FOE15, FOE16, FOE19, FOE20, FOE23, FOE24, FOE26, FOS26, FOS35, FOS41, FOS44
- Triggered by Signal's decarbonisation decision gate (Phase 6)
- Run Compass on all GT buildings regardless of calibration status โ scenarios are always useful for planning
- For uncalibrated buildings, Compass uses the available (potentially inaccurate) baseline and adds a
โ Uncalibrated baseline flag to the output so stakeholders know the numbers are indicative only
When to alert immediately (before daily report)
- Any building fails CV(RMSE) > 30% or NMBE > 15%
- Any Chisel IDF patch awaiting approval
- Compass finds
shallow or medium scenario > 40% reduction potential
- Any agent fails or returns an error
Handoff Payloads
Orchestrator โ Nimbus (Phase 1a โ before Forge)
{
"task": "build_calibrated_epw",
"month": "2024-08",
"base_epw": "/Users/ye/nus-energy/weather/SGP_Singapore.486980_IWEC.epw",
"out": "/Users/ye/nus-energy/weather/calibrated/2024-08_site_calibrated.epw"
}
Orchestrator โ Forge
{
"task": "simulate",
"buildings": ["FOE13"],
"idf_dir": "/Users/ye/nus-energy/idfs/",
"month": "2024-08",
"weather_file": "/Users/ye/nus-energy/weather/calibrated/2024-08_site_calibrated.epw",
"weather_fallback": "/Users/ye/nus-energy/weather/SGP_Singapore.486980_IWEC.epw",
"gt_dir": "/Users/ye/nus-energy/ground_truth/parsed/",
"outputs_dir": "/Users/ye/nus-energy/outputs/"
}
Orchestrator โ Radar
{
"task": "anomaly_check",
"buildings": ["FOE6", "FOE9", "FOE13", "FOE18", "FOS43", "FOS46"],
"outputs_dir": "/Users/ye/nus-energy/outputs/",
"gt_dir": "/Users/ye/nus-energy/ground_truth/parsed/",
"thresholds": {"cvrmse_pct": 15, "nmbe_pct": 5}
}
Orchestrator โ Lens
{
"task": "diagnose",
"building": "FOE13",
"cvrmse": 18.3,
"nmbe": 6.2,
"monthly_csv": "/Users/ye/nus-energy/outputs/FOE13/parsed/FOE13_monthly.csv",
"gt_csv": "/Users/ye/nus-energy/ground_truth/parsed/FOE13_ground_truth.csv",
"prepared_idf": "/Users/ye/nus-energy/outputs/FOE13/prepared/FOE13_prepared.idf",
"calibration_log": "/Users/ye/nus-energy/calibration_log.md",
"iteration_count": 1
}
Orchestrator โ Chisel
{
"task": "recalibrate",
"building": "FOE13",
"diagnosis": { "/* full Lens output JSON */": true },
"parameter_bounds": "/Users/ye/nus-energy/parameter_bounds.json",
"approval_channel": "#openclaw-alerts",
"iteration": 2
}
Orchestrator โ Oracle (Phase 4 โ clarification during approval)
{
"task": "clarify_approval",
"building": "FOE13",
"chisel_proposal": { "/* Chisel parameter proposal JSON */": true },
"lens_diagnosis": { "/* Lens output JSON */": true },
"slack_thread": "#openclaw-alerts",
"context": "Engineer reviewing Chisel's IDF parameter proposal โ answer any questions"
}
Orchestrator โ Ledger
{
"task": "daily_report",
"date": "2026-03-27",
"buildings_checked": 23,
"results": [
{"building": "FOE6", "cvrmse": 8.1, "nmbe": 1.2, "status": "ok", "carbon_scenario": null},
{"building": "FOE13", "cvrmse": 18.3, "nmbe": 6.2, "status": "warning", "carbon_scenario": null},
{"building": "FOS43", "cvrmse": 35.8, "nmbe": 12.1,"status": "critical", "carbon_scenario": null}
],
"recalibration_today": [
{"building": "FOE6", "parameter": "Cooling_Setpoint_C", "old": 25.0, "new": 23.0, "iteration": 1}
],
"calibration_log": "/Users/ye/nus-energy/calibration_log.md",
"critical_flags": []
}
Orchestrator โ Signal (Phase 6 notification)
{
"task": "notify_calibration_results",
"report_path": "/Users/ye/nus-energy/reports/NUS_Campus_Summary_Report.pdf",
"summary": "/* Ledger summary text */",
"channel": "#openclaw-alerts",
"decarbonisation_check": true
}
Orchestrator โ Signal (immediate alert)
{
"type": "anomaly_alert",
"urgency": "critical",
"building": "FOS43",
"data": {"cvrmse": 35.8, "nmbe": 12.1, "worst_months": ["Jun", "Jul"]},
"action_required": true,
"action_prompt": "Lens diagnosis in progress"
}
Orchestrator โ Compass (Phase 7)
{
"task": "carbon_scenarios",
"buildings": ["FOE6", "FOE9", "FOE13", "FOE18", "FOS43", "FOS46"],
"scenario_types": ["shallow", "medium", "deep"],
"outputs_dir": "/Users/ye/nus-energy/outputs/",
"idfs_dir": "/Users/ye/nus-energy/idfs/"
}
Orchestrator โ Oracle (Phase 7 โ stakeholder Q&A)
{
"task": "stakeholder_qa",
"context": "Compass has generated decarbonisation scenarios โ answer stakeholder questions via Slack",
"carbon_outputs_dir": "/Users/ye/nus-energy/outputs/",
"slack_channel": "#openclaw-alerts"
}
Pipeline State Tracking
Write pipeline run state to $NUS_PROJECT_DIR/outputs/pipeline_state.json:
{
"run_date": "2026-03-27",
"run_started": "2026-03-27T10:00:00+08:00",
"phases_complete": ["input", "simulate", "detect"],
"phases_pending": ["diagnose", "report", "notify", "intervene"],
"buildings": {
"FOE6": {"status": "ok", "cvrmse": 8.1, "nmbe": 1.2, "action": null},
"FOE13": {"status": "warning", "cvrmse": 18.3, "nmbe": 6.2, "action": "diagnosis_pending"},
"FOS43": {"status": "critical", "cvrmse": 35.8, "nmbe": 12.1, "action": "diagnosis_pending"}
},
"errors": []
}
Update this file after each phase completes. Other agents can read it to understand current pipeline state.
Error Handling
| Failure | Action |
|---|
| Forge fails for a building | Log error in pipeline_state.json; skip that building; continue pipeline |
| Radar returns no output | Notify Signal: "Pipeline error โ parse failed for {building}" |
Lens returns engineer_review_required: true | Notify Signal in #private; do not spawn Chisel |
| Chisel exceeds 3 iterations | Notify Signal in #private; halt recalibration for that building |
| Compass fails | Log error; still run Ledger + Signal with note "carbon scenarios unavailable" |
| Compass runs on uncalibrated building | Add โ Uncalibrated baseline โ treat savings as indicative to the Slack notification and Ledger report |
| Oracle fails | Log error; continue pipeline; human engineer can query manually |
| Signal fails | Log to pipeline_state.json; do not retry indefinitely |
Key Paths (all agents share these)
| Resource | Path |
|---|
| Project root | /Users/ye/nus-energy/ |
| IDF files | /Users/ye/nus-energy/idfs/ |
| Base TMY EPW (fallback) | /Users/ye/nus-energy/weather/SGP_Singapore.486980_IWEC.epw |
| Calibrated EPW dir (Nimbus) | /Users/ye/nus-energy/weather/calibrated/ |
| Latest conditions (Nimbus) | /Users/ye/nus-energy/weather/observed/latest_conditions.json |
| Building registry | /Users/ye/nus-energy/building_registry.json |
| Campus shapefile | /Users/ye/nus-energy/QGISFIle/MasterFile_241127.shp |
| Outputs | /Users/ye/nus-energy/outputs/ |
| Ground truth (canonical) | /Users/ye/nus-energy/ground_truth/ground-truth.csv |
| Ground truth (parsed fallback) | /Users/ye/nus-energy/ground_truth/parsed/{BUILDING}_ground_truth.csv |
| Calibration log | /Users/ye/nus-energy/calibration_log.md |
| Parameter bounds | /Users/ye/nus-energy/parameter_bounds.json |
| Pipeline state | /Users/ye/nus-energy/outputs/pipeline_state.json |
| Reports | /Users/ye/nus-energy/reports/ |
Ground-Truth Buildings
FOE6, FOE9, FOE13, FOE18, FOS43, FOS46, FOE1, FOE3, FOE5, FOE10, FOE11, FOE12, FOE15, FOE16, FOE19, FOE20, FOE23, FOE24, FOE26, FOS26, FOS35, FOS41, FOS44 โ these can have ASHRAE metrics computed under the current detector setup.