The Planning Loop

This page owns the planning workflow: how the AI planning agent receives context, writes a bounded plan, journals the hypothesis, and lets the dispatcher hand safe setpoints to the ESP32. It should not become the tunable catalog, the climate dashboard, or the safety page.

The exact runtime contract for triggers, payloads, accepted outputs, tunable writes, midnight review, and automatic site publishing lives on Planner Contract and AI Tunables.

At solar milestones, band transitions, forecast deltas, and deviation checks, the AI planning agent receives a structured greenhouse context window. The prompt carries a trigger ID, planner-instance stamp, MCP tool contract, current scorecard context, recent lessons, and the greenhouse state needed for that trigger.

Crop profiles define the target bands. The dispatcher derives the whole-house firmware band. The AI planning agent chooses bounded tactics inside that envelope. MCP validates the write. The ingestor dispatcher pushes changed values. The ESP32 firmware owns the physical relay state machine.

Live Planner State

The live panels here answer two narrow questions: is the planner healthy, and what plan is active now?

The Closed Loop

Gather

Assemble context from sensors, forecast, crop bands, equipment, lessons, scorecards, prior plans, and static greenhouse documentation.

Validate

Score the previous plan and extract a lesson when reality diverged from the hypothesis.

Route

Deliver each trigger with the same MCP allowlist, audit banner, and trigger-scoped context.

Reason

Balance temperature, VPD, water, fog, heat, light, cost, lessons, and retrieved context against the next 72 hours.

Plan

Write time-based tactical waypoints, each with a hypothesis and expected outcome.

Dispatch

Every 5 minutes, push changed setpoints to the ESP32; firmware enforces physical state locally.

Brief

Post the operator-facing Slack summary: what changed, why it changed, and what a human should watch.

Learn

Logged outcomes become scorecard data and validated lessons for the next planning cycle.

Left: plan outcome scores over 30 days — each dot is a cycle's self-assessment. Right: recent plan history with hypotheses and outcomes.

What The AI Planning Agent Reads

The planner assembles a bounded context packet rather than a generic prompt:

Current State

Zone climate, VPD, humidity, CO2, lux, outdoor weather, equipment state, active mode, alerts, and tunable readbacks.

Forward View

Forecast pressure, solar context, crop bands, firmware-band projection, and expected stress windows.

Memory

Previous plans, scorecard outcomes, validated lessons, recent failures, and operator notes.

Static Context

Curated site pages for structure, equipment, crops, safety, physical constraints, operating rules, and build notes.

The crop target source lives on Crop Profiles. Current climate panels live on Climate Control.

Context Window And Prompts

The AI planning agent does not plan from a generic greenhouse prompt. Every planning event gets a generated context window that describes the current room, the active plan, recent outcomes, forecast pressure, crop targets, equipment state, lessons, and data-health caveats.

The context window is built before the AI planning agent plans. MCP tools validate writes; the ESP32 enforces the physical state machine.

Dynamic Data Window

scripts/gather-plan-context.sh builds the live greenhouse packet. It reads TimescaleDB views, Home Assistant state where needed, and current ESP32/forecast data. The packet is plain text so the same context can move through both local and cloud planner routes.

Representative sections include:

SectionPlanning roleData
System healthTrust inputs or degradeData-health and dependency completeness.
Active planAvoid stale postureFuture transitions and Tier 1 values.
Zone conditionsDiagnose current stressTemperature, RH, VPD, CO2, outdoor weather, water, and enthalpy.
ESP32 stateSeparate intent from modeCurrent firmware state when available.
24h hourly patternGround forecast reasoningTemperature, VPD, RH, CO2, and lux over the prior day.
Planner scorecardConnect decisions to outcomesToday’s score and recent trend.
ComplianceFind binding failure mode24h zone band percentages for temperature and VPD.
DIF and DLIAvoid sensor overreachDay/night differential and light estimates with known limitations.
Hydro and irrigationKeep water context visibleWater quality, schedules, and history.
Equipment runtimeSpot churn and cost driversRelay/equipment hours and transitions.
Energy and waterMake tradeoffs visiblekWh, therms, watts, and water trend.
Crops and observationsTie goals to plantsActive crops and growing context.
Previous plan reviewGrade the last hypothesisGoverning plan, outcome fields, and validation state.
Structured hypothesisCompare belief to realityTyped predicted conditions and stress windows.
Tunable constraintsPrevent impossible writesFirmware min/max/step readbacks.
Forecast calibrationSeparate bias from regime changeBias-corrected weather priors and recent forecast accuracy.
Active lessonsCarry durable learningDeduplicated high-value lessons.
Current setpointsFill unchanged waypointsLatest effective values.
Forecast alertsTurn weather into tacticsHeat, VPD, frost, overcast, cold-front, and stress-window signals.
Guardrail-aware auditsKeep nudges inside rulesRecent transitions, stress windows, and constraint checks.
Context completenessExpose missing dataReachability and staleness of dependencies.

Static Site Context

scripts/gather-static-context.sh builds /srv/verdify/state/planner-static-context.md. It concatenates public Markdown pages from the site, excluding generated daily plans, dashboards, static assets, and scripts. The planner context packet embeds that file with a SHA-256 digest, byte count, and line count, so the public site contract and the planner payload are tied to the same source tree.

Generated daily plan pages are handled by source records instead of copied HTML or Markdown. The site renders plan_journal, plan_delivery_log, setpoint-plan rows, scorecards, lessons, and daily summaries into /plans/YYYY-MM-DD; the planner receives those same records directly in the dynamic context packet.

This gives the AI planning agent the greenhouse documentation in the same form public readers see it:

  • structure, equipment, zones, crops, and physical constraints
  • safety architecture and data model
  • planning loop and AI Tunables Traceability
  • resource accounting, evidence, and build notes
  • lessons and FAQ language that constrain public claims

Static context keeps the planner anchored to this greenhouse’s actual hardware and physical limitations.

Prompt Family

ingestor/iris_planner.py has a tailored prompt builder for each planning event:

TriggerRoleExpected output
SUNRISEMorning tactical planFull set_plan with 5-8 compact waypoints for the next daylight and overnight period.
SUNSETOvernight postureFull set_plan with 3-5 overnight waypoints for cold and disease control.
MIDNIGHTEnd-of-day review and resetRequired plan_evaluate pass for completed prior-day plans, followed by a fresh set_plan to start the new local day.
SOLAR_MAXSolar-noon checkpointset_tunable or acknowledge_trigger before the afternoon dry window.
TRANSITIONStress checkpointset_tunable or acknowledge_trigger at peak-stress or decline points.
FORECAST_DEVIATIONForecast missImmediate bounded correction or explanation after a sigma-gated observed-vs-forecast miss.
MANUALOperator requestWhatever the operator asked, still within MCP bounds.

Each event gets the same safety contract, the same writable tunable limits, and the same audit headers. The difference is how much change the event is expected to make. DEVIATION remains as a backward-compatible alias for old in-flight deliveries, but new trigger paths use FORECAST_DEVIATION.

Prompt Contract

The stable prompt contract is public-safe to summarize:

READ: scorecard, climate, forecast, active plan, crop bands, lessons, alerts.
DIAGNOSE: identify the binding compliance axis and stress type.
DECIDE: apply safety first, then validated lessons, then forecast reasoning.
ACT: write bounded tunables or a full plan through MCP tools only.
REPORT: summarize decisions and tradeoffs for the operator.

The standing directives also require:

  • use MCP tools only
  • do not run shell, direct SQL, or Docker commands
  • include trigger_id and planner_instance on every write
  • do not acknowledge required MIDNIGHT/SUNRISE/SUNSET full-plan cycles unless validation mode is active
  • call acknowledge_trigger for routine no-op cycles so SLA monitoring can close them honestly
  • write compact plans and avoid broad read tools during routine no-op or transition checks

Context Trim Policy

The planner route is designed for bounded, event-scoped context. Trigger-scoped sessions prevent chat history from growing without limit, but the generated packet still needs an explicit trim policy so required full plans stay focused.

Safety and write contractKeep

Firmware safety split, tunable bounds, audit headers, and current setpoints. Never trim from required planning events.

Live stateKeep latest compact rows

Current climate, equipment state, active alerts, forecast pressure, and active plan; avoid broad historical dumps.

Recent outcomesKeep summaries

Last plan hypothesis, scorecard, stress windows, and resource use, with links to plan/archive rows.

LessonsCollapse duplicates

Active curated lessons and high-confidence constraints.

Static site contextPrefer summaries

Architecture, safety, known limits, crops, zones, and equipment summaries with canonical links.

Long historyTrim first

Old plans, raw extraction rows, and full dashboard prose unless the event specifically needs them.

The practical rule is: routine events should receive enough context to make a bounded tactical decision, not a full copy of the website and history. Larger reviews can carry more context, but they still use the same MCP write boundary.

Audit Header

Every delivered prompt ends with an audit banner:

trigger_id=<uuid>
planner_instance="local" | "opus"

The planner must pass those values into set_plan, set_tunable, or acknowledge_trigger. That is how Verdify correlates:

plan_delivery_log -> planner delivery -> MCP write -> plan_journal/setpoint_changes -> public archive

If any link is missing, the failure should be visible. Missed cycles remain in the planning archive because planner availability is part of the system being audited.

What The AI Planning Agent Writes

The AI planning agent writes tactical intent, not direct actuator commands. In production that means two MCP write paths:

set_planMulti-waypoint plan

Writes future rows to setpoint_plan, deactivates older future plan waypoints, and records a hypothesis in plan_journal.

set_tunableOne-shot adjustment

Writes a current tactical row for a planner-pushable parameter when a trigger needs a narrow correction.

ValidationMCP boundary

Parameter names, bounds, trigger IDs, planner instance, structured hypotheses, and required routine-plan fields are checked before DB writes land.

The generated AI Tunables Traceability page owns the exact parameter names, bounds, defaults, current values, readbacks, and firmware references. This page intentionally does not duplicate that table.

Dispatch And Readback

The ingestor runs the setpoint dispatcher every 300 seconds, with an immediate reconciliation path after ESP32 reconnect. The dispatcher reads the active plan, resolves the latest applicable value for each parameter, compares it with current readbacks, and pushes only changed values through the ESPHome API. Final writes are logged in setpoint_changes; setpoint_snapshot stores the firmware cfg_* readbacks that confirm what the controller actually accepted.

Between dispatcher runs, the ESP32 keeps running locally from its current configured values. Failure behavior and relay ownership live on Safety Architecture.

Journal And Learning

Every plan is a hypothesis. plan_journal stores the plan ID, trigger metadata, hypothesis, experiment, expected outcome, conditions summary, and changed parameters. Later evaluation writes the outcome and score back to the same record.

Useful findings become rows in planner_lessons; they are not model self-modification. Future prompts can read validated lessons as ordinary database memory.

Left: forecast temperature bias over 30 days. Right: setpoint write rate and oscillation count.

Planner outcome proof lives on the Scorecard. The generated lesson list lives on Lessons Learned.

Page Boundaries

Plain-English overviewAI Greenhouse Control

The public story: crop targets, AI-assisted tactics, ESP32 control, scorecards, and why the testbed is useful.

Exact writable surfaceAI Tunables Traceability

Generated registry, bounds, readbacks, ownership, and firmware references.

Runtime planner contractPlanner Contract and AI Tunables

Trigger schedule, payload shape, accepted outputs, tunable writes, midnight review, automatic site publishing, and reliability checks.

Measured outcomesScorecard

Scorecards, compliance, stress hours, forecast-vs-band-vs-actual, and lessons live with the public proof layer.

Climate behaviorClimate Control

Live temperature, VPD, forecast, zone, and firmware-band evidence.

Operating rulesOperations

Lighting, biological activity, direct-wet windows, irrigation, and fertigation policy.

Resource and cost proofResource Use

Utility timing, rates, daily cost, seasonal cost, and solar-alignment accounting.

Safety boundarySafety Architecture

ESP32 relay ownership, hard rails, and failure behavior.

Operator surfaceSlack operator surface

Human-readable briefs, forecast-deviation messages, reminders, and the operator task queue.

Full dashboards: Controller ↗ · Planning ↗