WattifOperations · M&V companion

Methodology · Continuous control policies

How Wattif delivers continuous control policies end to end

A Wattif strategy is a continuous control loop attached to one piece of customer equipment. It reads telemetry, decides what to do, and either acts or recommends — every minute, indefinitely, with the authority the operator granted and not a millivolt more. This document describes how that loop is born from a diagnosis, previewed against the customer's own history, commissioned under an explicit risk contract, and settled against an IPMVP-grade baseline.

The engineering moves that make it work: a polymorphic entity model so the same shape covers assets, spaces, and processes; a pure decision function so every tick is replayable; an append-only stream so attribution survives audit; a state-machine hook so commissioning is a side effect of contract approval, not a separate workflow. The measurement engine is shared with /overview/mv-option-b-method; this document covers everything upstream of the ledger.

Prepared by Wattif Technologies Pte. Ltd. · May 2026Singapore
01What the engine assumes

Working principles

The scope and the runtime are separate objects.

The Intervention holds the scope and the M&V plan. The strategy is the runtime that delivers it. Keeping them apart lets a hardware retrofit verify under the same scope with no strategy at all, lets a partner algorithm replace a Wattif-written one without re-papering, and lets a strategy be paused or retuned without touching the verification record. One scope; zero, one, or many runtimes.

One control surface across three entity kinds.

A strategy binds to an asset (a fan-coil), a space (a refrigerated zone), or a process (a CIP step). We model these as three columns on the same row, with a CHECK constraint enforcing exactly one is set, instead of three parallel tables. The lifecycle, the audit trail, the authority ladder, and the executor pipeline all gain one implementation to maintain instead of three.

Authority belongs to the strategy, not the building.

A control loop's risk profile is local to the loop. The same site can run an FCU setback at Dispatch — fully autonomous — and a chiller-plant sequencer at Recommend, because the cost of a wrong move differs by orders of magnitude. The authority level lives on the strategy row and is the only thing standing between a Decision and equipment movement.

Replay precedes commissioning.

Before a strategy reaches Enabled, the operator sees the last thirty days of their telemetry re-played through it — every Decision it would have emitted, the energy delta against the baseline, the comfort excursions. The executor is a pure function with no side effects, so the replay uses the exact same code path as live operation. The preview is faithful, not approximate, and trust transfers against evidence the customer already owns.

The decision stream is the audit trail.

Every executor tick writes one row to control_strategy_decisions — including the ticks that decided to do nothing, so gaps in the stream mean cron incidents, not silent ambiguity. Each row carries the state it was evaluated against and forward links to the Case or control_command it produced. The table is append-only at the database level; corrections come from new rows, never from edits.

Approving the contract creates the strategy.

When the customer accepts an Intervention's scope, the state-machine transition fires a commissioning hook in the same call. The hook walks target_entities, looks up matching policies in the registry, and writes one disabled control_strategies row per eligible pair with default config and an intervention back-link. The operator never files paperwork to enable a strategy — they choose when to flip a row that's already pre-configured and waiting.

02How an Intervention delivers its savings

Three delivery modes

Every Intervention delivers its savings through exactly one of three modes. Picking the right mode is upstream of every downstream decision in the lifecycle — it determines whether a strategy row comes into existence, whose code runs the loop, and which artifacts the verifier will read at settlement. The Intervention's category and delivery mode together encode the choice; the commissioning hook in lib/control-strategies/commissioning.ts acts on it without anyone having to remember.

Delivery modeWhat deliversHow it shows up in the system
platformWattif's own algorithmA control_strategies row of a registered platform strategy_type. The decision function is code Wattif owns, audits, and versions. fcu_occupancy_optimizer is the canonical example. The full pipeline — executor, replay, authority, dispatch, stream, verification — is end-to-end ours.
partnerA partner's algorithm inside Wattif's perimeterA partner ships their intelligence — model weights, an inference container, a runtime contract — into Wattif's infrastructure and registers it as a strategy_type. Customer telemetry never leaves Wattif; the partner brings IP and gets nothing about the customer beyond what's needed for inference. From the operator's seat, a partner strategy looks identical to a platform strategy: same Strategy tab, same authority ladder, same decision stream, same verification. The difference is who wrote the decision function. Resync, BrainBox, GridBeyond fit here.
physical_retrofitHardware install — no software strategyThe intervention's savings come from new equipment rather than smarter use of existing equipment. Adaptive-flow VAV terminal boxes, LED fixtures, EC fan motors, heat-pump retrofits. The commissioning hook is a clean no-op — no control_strategies row, no decision stream — but the Intervention is still verified against the boundary meter through the same Option B engine. Equipment is passed through at cost; the savings come from the kWh delta the hardware produces.

The platform–partner boundary deserves a sentence. The distinction is not who pays for the algorithm; it's where the algorithm runs and whose data it sees. A partner brings their intelligence into Wattif's perimeter, registers it as a strategy_type, and reads telemetry through the same fetcher our own executors use. The customer's data never leaves us. That is the deal — partners get access to a market they otherwise couldn't reach with the trust profile it requires; Wattif gets algorithms it didn't have to build and retains the customer relationship, the boundary meter, and the settlement record. Physical retrofit is its own mode because no software loop is involved — replacing a pressure-dependent VAV with an adaptive-flow VAV is a hardware change, and the savings come from the new equipment's behaviour rather than from a strategy operating it.

03entity_type

What a strategy binds to

A strategy attaches to exactly one entity. The entity is the thing the strategy controls and the thing whose telemetry the executor reads. The schema is polymorphic across three kinds; the device_channels CHECK constraint enforces that any single channel binds to exactly one of asset, space, or process.

asset

A single piece of equipment — fan-coil unit, exhaust fan with VFD, single compressor, single refrigeration case. The most common binding. Used when the strategy's measurement boundary is one sub-circuit.

space

A zone, room, or floor — refrigerated warehouse rack, hotel guest room, retail trading floor. Used when a single sensor + actuator combination governs a multi-asset comfort or storage outcome.

process

A process_flows row — a clean-in-place loop, a batch step, a production line. Used in food processing and other industrial contexts where the relevant control point is a process, not an asset.

04From diagnosis to archive

The eight-stage lifecycle

A strategy progresses through a fixed sequence of stages, each anchored to a transition on the Intervention it serves. The shape is deliberate: every stage has a single responsible owner, an artifact it produces, and a precondition the next stage checks before it begins. Running and Verified overlap in calendar time — a long-lived strategy is in Running every minute and in Verified once a cadence — but they remain distinct in the audit record so each can be queried independently.

Scoped
Approved
Run-up
Replay
Enabled
Running
Verified
Archived
Strategy emits decisions Setup, commissioning, settlementArchived can occur from any stage if the Intervention retires.

Scoped

A diagnosis writes an Intervention row with the category, the entities it would attach to, and a sized opportunity. The strategy doesn't exist yet — only the contract sketch does. Owner: diagnostic agent.

Approved

The customer accepts scope. The state-machine transition fires synchronously; the commissioning hook in the same call reads the registry, walks the target entities, and writes one disabled control_strategies row per eligible pair with default config and an intervention_id back-link. Owner: customer.

Commissioning (run-up)

Telemetry accumulates against the boundary meter while the strategy sits disabled. The baseline window is strategy-specific — fourteen days for an FCU loop, twenty-eight for whole-facility — and the Strategy tab refuses the disabled-to-enabled flip until that window is met and an mv_baselines row is built. The customer doesn't need to know this is happening; the platform tracks the gate. Owner: platform.

Replay review

The operator opens the Strategy tab. The executor is invoked in dry-mode over the last thirty days of state, producing a hypothetical decision stream and the energy delta against the baseline. Sliders for the config parameters re-run the replay under a second. Commissioning happens only when the operator's judgement and the replay agree. Owner: operator.

Enabled

The operator flips status to enabled and chooses an initial authority — Recommend by default. The platform stamps commissioned_at. From this timestamp forward the reporting period is running and every tick is part of the settlement record. Owner: operator.

Running

The executor cron evaluates the strategy every minute. Each tick reads state through the same fetcher the replay used, calls the pure decision function, routes the decision by authority, and appends a row to the stream. Per-strategy failures never block other policies; per-tick failures preserve the strategy's vacancy memory so the next tick recovers without manual intervention. Owner: strategy executor.

Verified (per cadence)

On the Intervention's M&V cadence, the verifier cron reconstructs the period: the boundary meter, the baseline model, the decision stream cross-referenced with telemetry to confirm the strategy caused what it claimed to cause. The result is an immutable intervention_verifications row, hash-chained, with a method-version stamp. Settlement reads from here. Owner: verifier (agent or human).

Archived

Operator-initiated retirement or Intervention end-of-life. decommissioned_at is stamped; the stream stays. Ticks stop, but every prior decision remains queryable for audit and for the next pass at the strategy if the customer comes back. Owner: operator.

05Finding → Intervention → Strategy → Check

How the operator sees it

Five screens, one per operator-facing stage of the lifecycle. The data is taken from the end-to-end smoke test on the Acme test facility — every figure traces to a real row, including the finding identifier, the intervention prefix, the savings band, the decisions table, and the verifier method version. The point of showing them in sequence is to make visible that the operator never has to leave the platform's primary surfaces to move a control loop from observation to settlement.

step 1Finding · the Operator agent surfaces an opportunityTelemetry pattern detected; estimated savings sized; eligible strategy named.
wattif.ai/facility/acme/findings/f-2026-05-fcu-occupancy
FindingsFCU L3-East occupancy gap
RecommendationOperator agent · 2026-05-29

FCU L3-East runs 24/7 in a zone vacant 09:00–18:00 weekdays

Estimated annual savings

AUD 540

at current tariff · ±18%

Eligible strategy

fcu_occupancy_optimizer

delivery_mode: platform

Investment

AUD 0

no capex · uses existing controls

Evidence · 30-day occupancy vs setpoint trace

26 °C23 °C20 °Coccupancysetpoint trace

The zone holds 22 °C continuously while occupancy data shows it is vacant ~9 h on weekdays and the full weekend. Raising the setpoint by 1.5 °C during vacant windows recovers 51 kWh/month with no comfort impact.

Anchor: finding f-2026-05-fcu-occupancyConfidence: high · 28 days of clean telemetry
step 2Intervention · the scope the customer approvesScope, target entities, M&V plan. Approve fires the commissioning hook.
wattif.ai/facility/acme/interventions/iv-136c8884
InterventionsFCU occupancy setback — L3-East
scopedcategory · operational_optimization

FCU occupancy setback — L3-East

Customer economics

Expected savings · annualAUD 540Equipment · at costAUD 0Customer keepsAUD 540

M&V plan

IPMVP optionB · retrofit isolationBoundary meterbilling-class sub-meterBaseline window28 daysVerification cadencemonthly

target_entities

EntityTypeCategory
FCU L3-Eastassetfcu

On approve, the commissioning hook will materialise 1 control_strategies row of type fcu_occupancy_optimizer in status disabled.

Origin: finding f-2026-05-fcu-occupancyState transitions: scoped → approved → deploying → live → measuring → verified
step 3Strategy tab · replay-first commissioningThe replay shows what the strategy would have done over the past 30 days. The operator tunes and commissions.
wattif.ai/facility/acme/assets/fcu-l3-east?tab=strategy
AssetsFCU L3-EastStrategy

FCU L3-East · asset

category · fcu · floor 3 · east wing · linked to iv-136c8884

device · simulator_fcu · last uplink 12s ago
ProfileStrategyTelemetryHistory
fcu_occupancy_optimizerdisabledbaseline ready · 28/28 days

Replay · last 30 days

what actually happened what the strategy would have done
25 °C23 °C21 °C42 setback events · 42 restore events

Replay result

Avoided energy51 kWh / monthEstimated savingsAUD 14.10 / monthComfort excursions0Confidence± 9 % (Annex F lite)

Configuration

Vacancy window15 min
Setback delta+1.5 °C
Base setpoint23 °C
Restore hysteresis0.25 °C

Tune any slider · replay re-runs in under one second. When the configuration matches your judgment, commission at Recommend authority; promote to Dispatch after observing a few cycles.

step 4Strategy tab · running at DispatchLive state at the top, recent Decisions below. Authority is per-strategy; pause and baseline reference are one click away.
wattif.ai/facility/acme/assets/fcu-l3-east?tab=strategy
AssetsFCU L3-EastStrategy

FCU L3-East · asset

linked to iv-136c8884 · commissioned 2026-05-29 · 12 days running

enableddispatch
ProfileStrategyTelemetryHistory

Room temp

24.49 °C

↑ 0.04 last 15 min

Setpoint

24.50 °C

raised by strategy 14:22

Occupied

false

vacant 1 h 12 min

Valve

92 %

active cooling

Recent decisions

live · refreshes every minute
TimeActionReasonRouted toResult
14:48nonevacant 1.1 min — under 15 min windowlog
14:22set_point → 24.5 °CVacant 15.2 min ≥ 15 min — raising setpointcontrol_commandapplied
09:04set_point → 23.0 °COccupied — restoring setpoint from 24.5 °Ccontrol_commandapplied
09:03noneoccupied and setpoint already at baselog
08:18set_point → 24.5 °CVacant 645.1 min ≥ 15 min — raising setpointcontrol_commandapplied
21:33 (prev)set_point → 24.5 °CVacant 15.0 min ≥ 15 min — raising setpointcontrol_commandapplied
Authority
observerecommendadjustdispatch
step 5Verification · monthly settlement checkAdjusted baseline, reporting period, verified savings, attribution evidence, verifier identity, split.
wattif.ai/facility/acme/interventions/iv-136c8884/verifications/v-2026-06
Verificationsiv-136c8884June 2026
verifiedhash-chained · settlement scheduled 2026-07-05

FCU occupancy setback — June 2026 verification

Reporting period: 2026-06-01 → 2026-06-30 · IPMVP Option B

Adjusted baseline energy

142 kWh

from mv_baselines · normalised for ambient

Reporting period energy

91 kWh

from boundary meter · 99.4% coverage

Verified savings

AUD 14.10

51 kWh · ± AUD 2.30 (90% CI)

Attribution evidence

Decisions in window43,200· setback set_points22· restore set_points22· logged-only ticks43,156Coverage99.4 %Outlier ticks0

Verifier identity

kindagentnameAccountantmethod versionmv@1.4.0uncertainty band± 9 %baseline refmvb-7c91e2row hash0x4a92e8…

Verified saving

Customer · AUD 14.10

The whole of it. Wattif takes no percentage of a verified saving; the fee for the work is fixed and invoiced separately.

06What the strategy may do unsupervised

The authority ladder

Authority is the explicit risk contract between the operator and the strategy. It exists because algorithmic confidence and operational consequence aren't the same axis: a strategy can be statistically excellent and still wrong about a single freezer at 03:00. The ladder lets the operator grant autonomy where mistakes are recoverable, withhold it where they aren't, and change their mind without code changes. The default for every new strategy is Recommend; promotion is an explicit operator action recorded on the decision stream alongside the decisions themselves. The registry imposes ceilings: a strategy whose action touches a HACCP-monitored variable, a safety boundary, or any quantity where overshoot has real consequence is refused promotion past Recommend by the platform, not by convention.

observe

inform

report

recommend

adjust

dispatch

emergency

log only creates Case writes control_command bypasses operator delay

Authority is set per strategy. The default for every new strategy is Recommend. Promotion past the registry-defined ceiling for the strategy's safety class is refused.

observe

The executor runs, decisions are logged, nothing is surfaced and nothing is dispatched. Used during shadow commissioning when the operator wants telemetry-driven sanity-checking before the strategy becomes visible to anyone else.

inform

Decisions land in the operator's feed but never become Cases or commands. Useful when the operator wants a passive stream of what the strategy is thinking without a queue to triage.

report

Decisions roll up into daily or weekly reports rather than appearing interactively. Suitable for policies whose decisions are advisory by design — tariff-window strategy notes, monthly comfort-band reviews.

recommend

The default for every new strategy. Each non-trivial decision creates a Case for operator review; nothing reaches the equipment until the operator approves. Equivalent to a human technician proposing a change in person.

adjust

Decisions write control_commands within explicit guard-rails declared on the registry entry (setpoint may move at most 2.0 °C from base, fan_mode may not jump more than one step). The dispatch worker delivers commands to the bound device's transport — but the strategy can't escape its declared box.

dispatch

Decisions write control_commands without per-decision guard-rail clamping. The registry's hard limits still apply, but inside those limits the strategy is autonomous. Reached only after the operator has watched a few cycles match intent at the lower levels.

emergency

Reserved for safety- and grid-critical interventions — demand-response dispatch, equipment-protective shutdowns. Decisions execute immediately and notify the operator after. The registry restricts which strategy types can be promoted here; for most loops this level is not user-toggleable at all.

07The trust mechanism

Replay-first commissioning

The hardest moment in deploying any algorithm into customer equipment is the leap of faith between "we ran a model" and "we trust it on your chiller." Replay closes that gap by paying it in evidence rather than reputation: the operator sees the past thirty days of their own telemetry re-played through the strategy, with every Decision the strategy would have emitted, the energy delta against the baseline model, and the comfort excursions that would have resulted. The engineering move that makes this clean is the executor's purity — a (state, config, now) → Decision function with no side effects, so live and replay share the exact same code path.

Last 30 days of telemetryPre-commissioning data the customer already ownsExecutorSame pure functionHypothetical Decision StreamEstimated kWh and dollar deltaoperator tunes config · replay re-runs in < 1s · commission when satisfied

Purity is the precondition

The executor is a pure function: (state, config, now) → Decision. No I/O, no clocks except the one passed in, no hidden state. Purity is what lets the same code that decides live at 14:22 decide the same way again when replayed in dry-mode at midnight against the state of 14:22 — which is what makes replay a faithful preview rather than an approximation.

State as a time series, not a snapshot

The replay reconstructs the state the executor would have seen at each tick. For simulator-backed entities that's a read from simulator_devices.state at one-minute resolution. For real devices it's an interpolation across load_telemetry and context_telemetry. Either way, the executor sees the same shape of input it sees live, which is the only way to trust the output.

Output is the same shape the operator will live with

Replay emits a list of Decision objects identical in shape to control_strategy_decisions rows. The operator inspects the same data structure they will see in the Recent Decisions table once the strategy is enabled, including the action='none' ticks. There is no separate 'preview format' to learn.

Energy delta through the baseline model

Each replay decision is run through the same baseline model the live verifier uses, producing a per-tick kWh delta. The summed delta is the figure the operator approves against; the confidence band scales the baseline's residual error by tick count and is honest about how much of the headline number is signal versus noise.

Sub-second tuning

Configuration sliders re-run the replay under a second. The interaction is closer to scrubbing a clip than to filling a form: the operator drags vacancy_window from fifteen to ten minutes, the timeline redraws with more setback events, the savings tile updates, the comfort excursion count is still zero — keep, or revert. Convergence on a config that matches operator judgement takes minutes, not weeks.

Replay becomes the first verification's anchor

On commissioning, the chosen replay result is captured as the predicted savings the first live verification will be measured against. Predicted-versus-actual ratio is one of the cheapest sanity checks for an algorithm changing behaviour: if the first month diverges from replay by more than the model's residual band, something material changed and the verifier surfaces it before settlement.

The typical alternative is a two-to-four-week pilot where the algorithm runs live and the customer evaluates results. Replay-first compresses that to seconds against data the customer already owns, with no commands sent and no risk taken. The operator's first commission is a decision they've already validated.

08lib/control-strategies/registry.ts

The strategy registry

Every strategy_type the platform can deliver is declared once, in code, with the fields the commissioning hook, the replay engine, and the Strategy tab all read. Adding a new strategy is a registry entry plus an executor function — every other surface picks it up automatically.

entity_types

Which entity kinds the strategy may bind to. fcu_occupancy_optimizer accepts entity_type='asset' only; cip_schedule_optimizer accepts 'process' only; zone_setback accepts 'space'.

applies_to_*_types

Sub-filter by category. fcu_occupancy_optimizer.applies_to_asset_types = [fcu, vav, split_ac, minisplit, ahu]. The commissioning hook refuses to attach a strategy to an entity whose category is outside this list.

intervention_category_ids

Which intervention_categories.id values trigger this strategy on commissioning. fcu_occupancy_optimizer registers operational_optimization. A new strategy that delivers electrification adds 'electrification' here and the commissioning hook picks it up without further wiring.

telemetry_required

The columns the executor's state fetcher expects non-null. Used by the replay engine to refuse policies whose state cannot be reconstructed from history.

baseline_min_days

Minimum days of pre-commissioning telemetry needed for a defensible mv_baselines row. The Strategy tab will not allow the flip from Disabled to Enabled until this window is met.

default_config

Sensible starting parameters. Merged over by intervention-specific tuning at commissioning. Visible to the operator in the Strategy tab; editable before commissioning.

default_authority

The authority a new strategy commissions at. Every entry in the registry today defaults to 'recommend'. Promotion to Adjust or Dispatch is an explicit operator action recorded on the Decision Stream.

09From Approved to Disabled rows

The commissioning hook

When transitionIntervention fires Scoped → Approved, the commissioning hook in lib/control-strategies/commissioning.ts runs synchronously inside the same call. It is idempotent: re-running for the same Intervention is a clean no-op against existing rows. Failures are logged and do not roll back the Intervention transition.

1

Read the intervention

Pulls category_id, target_entities, facility_id. Refuses if category_id is null (no strategy can be selected) or target_entities is empty (no entity to bind to).

2

Look up matching policies

strategiesForCategory(category_id) returns every StrategyMetadata whose intervention_category_ids includes this category. Empty list means the category is fully external (e.g. building_envelope) — commissioning is a clean no-op.

3

Filter target entities by strategy applicability

For each matching strategy, iterate target_entities. Skip entries whose type is not in strategy.entity_types or whose category is not in the appropriate applies_to_* list. The skip is recorded in the commissioning result for audit.

4

Upsert one control_strategies row per eligible pair

Insert with status='disabled', authority_level=strategy.default_authority, config=strategy.default_config, intervention_id=intervention.id. The UNIQUE (entity_type, entity_id, strategy_type) constraint makes re-runs safe; existing rows are left untouched (operator tuning is never clobbered).

5

Return per-pair audit

The commissioning function returns a CommissioningResult with created and skipped arrays. Failures of one entity do not block others; each failure carries its reason string.

10Per-strategy IPMVP option

The measurement boundary

Every shipped strategy declares its measurement boundary and the IPMVP option under which its savings are settled. Mixing options within a single Intervention is not supported; bundled work is split into separate Interventions with their own boundaries.

Strategy typeIPMVP OptionMeasurement boundary
fcu_occupancy_optimizerOption BOne asset (the FCU). Electrical demand at the sub-meter or estimated from setpoint trace and valve position; thermal effect at the room sensor.
kitchen_exhaust_demand_controlOption BOne asset (the VFD-driven fan). Fan kW measured directly at the VFD output; hood temperature at the canopy sensor.
refrigerated_zone_setbackOption BOne space (the refrigerated zone). Compressor demand at the sub-meter; rack temperature at the zone probe.
cip_schedule_optimizerOption BOne process (the CIP loop). Steam, hot water, and electrical demand measured at the loop's boundary meter set.
peak_demand_shaverOption CWhole facility. Interval demand at the utility meter. Adjusted Baseline Demand normalised for weather and occupancy. Reporting against the half-hour peak rather than energy total.
chiller_plant_sequencerOption B (partner-delivered)Plant boundary meter measured by Wattif; the optimisation logic is a partner algorithm running inside our perimeter as a registered strategy_type. Verified savings settle to the Intervention; partner revenue is a contractual share, not a data exchange.

Option B boundaries are documented exhaustively in /overview/mv-option-b-method. Option C and partner-attributed boundaries follow the same engine with method-specific calculation paths.

11control_strategy_decisions

The decision stream

Every executor tick writes one row. Over a year of running, a single strategy emits half a million rows; over a hundred policies, fifty million. The stream is what the operator's recent-decisions timeline reads from, what the verifier reconstructs the period from, and what an auditor reaches for when asked to defend a settlement years after the fact. Three properties make it useful: every tick is captured (including the unremarkable ones), every row carries the state it was evaluated against (so the audit doesn't depend on retained telemetry), and the table is append-only at the database level (so a corrupted row can't quietly become a different row).

One tick, one row, no gaps

The executor writes to control_strategy_decisions every minute for every enabled strategy. The action='none' ticks are written too — that's deliberate, because the absence of a row means the cron didn't run, not that the strategy decided nothing. Gaps surface as cron incidents in monitoring, not as silent uncertainty in the audit trail.

Frozen state snapshot per row

Each row carries the state the executor evaluated against — temperature, setpoint, occupied, vacancy_since — copied into the row, not referenced. Re-running the executor against historical rows produces the same Decision the cron made; the audit doesn't depend on the original telemetry still being available at original resolution.

Bidirectional links to side effects

When a Decision routed to a Case, case_id on the decision row points to it. When it routed to a control_command, command_id points to that. The graph of (Decision → Case → operator action) and (Decision → control_command → device transport) is fully queryable in both directions — useful when an auditor asks how a specific equipment movement came about, and useful when a verifier asks which Decisions actually reached the equipment.

Append-only by RLS, not by convention

The table grants no UPDATE strategy to any role; service-role inserts only. Corrections are appends with supersedes metadata, never mutations. A row that lands in the stream is in the audit trail forever — the engineering equivalent of a financial journal's ink.

Attribution evidence for verification

The verifier cron reads the stream filtered by intervention_id and the verification window. The presence and count of action-bearing decisions inside the window is the primary evidence that the strategy did the work — distinguishing 'savings appeared and we ran' from 'savings appeared because we ran'. The two are not the same and a defensible settlement requires we tell them apart.

12One engine, two delivery shapes

How verified savings settle

A common failure mode in M&V systems is to bolt a second verification pipeline onto a new delivery shape and end up with two slightly different definitions of 'savings.' We don't: strategy-delivered Interventions settle through the same ledger, the same baseline builder, the same uncertainty engine, and the same crons that settle hardware-retrofit Interventions. What the strategy contributes is the decision stream, which gives the verifier attribution evidence that a meter alone cannot — the contemporaneous record that the savings followed the strategy's actions rather than coinciding with them.

One verification engine, two delivery shapes

Hardware-retrofit Interventions and strategy-delivered Interventions settle through the same ledger writer, the same uncertainty calculation, the same baseline builder, and the same hourly verifier cron. There is no parallel pathway, no second M&V code path to maintain, and no second method version to keep aligned with the first. A reviewer who has accepted the Option B engine has accepted ours.

Decision stream becomes attribution evidence

Baselines tell you what should have happened. Boundary meters tell you what did happen. Neither tells you why — and 'why' is what attribution requires. The decision stream provides the contemporaneous record of the strategy's actions, so the verifier can prove the savings followed the strategy's behaviour rather than coinciding with it. For setpoint-class policies this is decisive: the setpoint trace itself is the action proof.

Boundary coherence

The mv_baselines row for a strategy-delivered Intervention is built against the same meter that defines the strategy's measurement boundary. This sounds obvious; it's a frequent source of error in less disciplined M&V systems where the baseline is built from one signal and the verification reads another. The platform enforces the equality at the schema level so the mismatch can't happen.

Tick count is not the point

What the customer is shown is intervention_verifications.verified_savings_amount on the ledger. Not how many ticks a strategy emitted, not how many control_commands it wrote, not any platform usage metric. Wattif charges a fixed fee for the work and takes no percentage of the saving, so the whole of it belongs to the customer and the ledger exists to prove the number rather than to bill against it.

13Who does what

Roles

Every role is named. Wattif's AI is the diagnostic and the executor; the customer is the operator. Where an ecosystem partner is involved, the partner provides optimisation logic and Wattif retains the M&V boundary and the customer relationship.

Diagnostic agent (Wattif AI)

Identifies opportunities, scopes Interventions, populates target_entities, writes the Brief or Report.

Customer (operator)

Approves Intervention scope. Opens the Strategy tab. Reviews replay. Tunes configuration. Sets initial authority. Promotes, pauses, or archives policies as the relationship matures.

Strategy executor (Wattif platform)

Runs every minute. Emits Decisions. Routes per authority. Never exceeds authority. Writes the Decision Stream.

Dispatch worker (Wattif platform)

Delivers commands from policies at Adjust+ authority to the bound device's transport (LoRaWAN, MQTT, Modbus, BACnet). Until Phase 4 lands, the simulator stands in for this layer for development.

Verification cron (Wattif platform)

Settles on the Intervention's cadence by reading the Decision Stream and the boundary meter through the engine documented in /overview/mv-option-b-method.

Ecosystem partner

Ships an algorithm — model weights, an inference container, a runtime contract — into Wattif's infrastructure and registers it as a strategy_type. Does not receive customer telemetry; reads through Wattif's fetcher inside our perimeter. Wattif retains the M&V boundary, the boundary meter, and the customer relationship.

14Scheduled writers

The crons that drive the engine

Four scheduled writers move a strategy through its lifecycle. All are registered in vercel.json. Per-strategy failures never block other policies; per-Intervention failures never block other Interventions.

strategy-executor

Every minute. Walks every control_strategies WHERE status='enabled', fetches state, calls the executor, routes the decision per authority, writes the decision stream row. Per-strategy failures never block other policies.

normalize (simulator hook)

Every minute. Ticks any active simulator_devices before its channel loop. Until Phase 4 of the build sequence lands, the simulator drains pending control_commands inline and applies downlinks; that integration retires when the dispatch worker is generalised.

mv-baseline-builder

Nightly at 02:30 SGT. Builds the pre-commissioning baseline for any Intervention in Deploying or Live without an mv_baseline_id. Documented in /overview/mv-option-b-method.

mv-verifier

Hourly. Settles the Intervention's verification cadence by reading the Decision Stream and the boundary meter. Documented in /overview/mv-option-b-method.

15When the engine refuses

Failure modes

Every failure mode is named, returns a structured error or skipped-row reason, and either writes a Case or returns to the cron for the next pass. The system never silently degrades a strategy or skips a tick without an audit record.

missing_target_entities

Intervention approved with empty target_entities. Commissioning records the failure on its result; the Intervention stays Approved without any strategy rows. Operator action: add entities and re-fire the commissioning hook, or manually create the strategy from the Strategy tab.

no_platform_strategy_for_category

Intervention's category_id has no entry in any StrategyMetadata.intervention_category_ids. This is not a bug — it is the case for delivery_mode='external' categories. Commissioning is a clean no-op.

strategy_not_applicable_to_entity_category

Target entity's category is outside the strategy's applies_to_* list (e.g. a 'chiller' entity targeted by fcu_occupancy_optimizer). The pair is skipped with a recorded reason; other eligible pairs in the same intervention still commission.

baseline_min_days_unmet

Operator attempts to flip status from Disabled to Enabled before baseline_min_days of pre-commissioning telemetry has accumulated. The Strategy tab refuses the transition; commissioned_at remains unstamped.

executor_crash

Per-strategy failure during a tick. The cron continues to the next strategy; the failed strategy's tick is recorded as routed_to='skipped' with the error string. The vacancy_since memory is preserved so the next tick recovers.

authority_exceeded_attempt

Operator attempts to promote authority past the registry's hard ceiling for that strategy type (e.g. promoting an HACCP-class strategy to Dispatch). The Strategy tab refuses; the attempt is recorded as a Case for audit.

16Audit chain

How a reviewer reconstructs a settlement

Any strategy-delivered settlement can be reconstructed from five reads against the database, left to right. The first three reads cover the strategy lifecycle; the right two cover the M&V engine documented in /overview/mv-option-b-method.

interventions

the anchor

control_strategies

one row per (entity, type) pair

control_strategy_decisions

append-only stream

mv_baselines

the reference

intervention_verifications

settlement ledger

Five reads, left to right. Every strategy-delivered settlement is reconstructible from these five tables. The /overview/mv-option-b-method document covers the right two in detail; this document covers the left three.

  1. Find the intervention. interventions.id is the anchor.
  2. Read the target entities. interventions.target_entities lists the assets/spaces/processes the policies bind to.
  3. Read the policies. control_strategies WHERE intervention_id = <id> returns one row per (entity, strategy_type) pair the commissioning hook created. Each row carries entity_type, entity_id, config, authority_level, commissioned_at, decommissioned_at, and last_decision.
  4. Read the decision stream. control_strategy_decisions WHERE strategy_id IN (...) AND decided_at BETWEEN baseline_end AND verification_end is the full record of every tick during the verification window.
  5. Read the baseline. mv_baselines WHERE intervention_id = <id> is the reference any verification row was measured against. Documented in /overview/mv-option-b-method.
  6. Read the verifications. intervention_verifications WHERE intervention_id = <id> ordered by created_at is the append-only settlement ledger. Documented in /overview/mv-option-b-method.
17Honest open work

What is not yet in scope

The methodology document is honest about its boundaries. The following items are deliberately deferred and are documented here so a reviewer is not surprised.

  • Partner-mode first claim. The DeliveryMode union now includes 'partner' but no INTERVENTION_CATEGORIES entry currently maps to it — the first claim happens when the first partner algorithm (Resync chiller-plant model, BrainBox HVAC agent) lands in the registry. Today every category is either platform (operational_optimization, flexibility_dr) or physical_retrofit (everything else).
  • Partner algorithm sandbox. The runtime contract for a partner-shipped executor — image format, fetcher API, decision schema, resource limits, secrets handling — is being specified jointly with the first partner. Until that contract is stable we register partner policies as platform-typed shims that proxy to a partner endpoint.
  • Replay-first commissioning UI. The replay engine specification and the Strategy tab design exist (project_strategy_replay_first_creation). The component and the dry-mode executor wrapper are unbuilt.
  • Dispatch worker. Phase 4 of the control-strategies-and-bidirectional-devices.md build sequence. The simulator's inline command-drain is the stand-in; the generalised worker that resolves asset_id → bound device → adapter.buildControlPayload → transport publish is unbuilt.
  • Segment-aware authority defaults and class-based promotion locks. The registry records default_authority but does not yet enforce class-based promotion ceilings for HACCP, life-safety, or grid-critical classes.
  • Real device adapters. McLimate FCT-LRW (LoRaWAN FCU controller) ships in Phase 6. VFD adapter (Modbus) ships in Phase 7. The contract for both is the controlPoints + buildControlPayload interface added in Phase 1.
  • Verification cron's Decision-Stream pathway. The cron exists for boundary-meter Option B settlements; the path that reads control_strategy_decisions to enrich the attribution evidence is scoped but not yet wired.
  • Strategy-level Recent Decisions timeline UI. Last-N decisions with intent → observed effect → verification result render on the Strategy tab. Phase 5 of the build sequence.
  • External CMVP review of the methodology. Scheduled for Q3 2026 jointly with the Option B engine. Until then alignment claims here are the engineering team's, not an independent reviewer's.
18Sources

Standards alignment

The Wattif control-strategies engine settles under the same standards that govern the Option B ledger. The Q3 2026 CMVP review will formally validate the methodology mapping; until then the alignment claims here are the engineering team's, not an independent reviewer's.

IPMVP Core Concepts (Volume I, 2022). Options A through D defined; Annex F treats uncertainty. Strategy-delivered Interventions settle under Option B (entity-scoped) or Option C (whole-facility demand).

Efficiency Valuation Organization

ASHRAE Guideline 14-2014: Measurement of Energy, Demand, and Water Savings. Informs the platform's coverage rule and uncertainty conventions.

ASHRAE

ISO 50001: Energy management systems. The platform's strategy lifecycle is aligned with the plan-do-check-act loop; Wattif Pte Ltd self-certification scheduled for late 2026.

ISO