Shadow Deployment of Controllers
A shadow controller runs on live machine data and computes what it would command, but its outputs go nowhere near an actuator — pure observation before authority.
Watching without touching
Before any controller or predictive model earns authority over the breeder or burner, it runs in shadow: it receives the same live state vector as the production controller, computes its full command trajectory each cycle, and logs it, but its outputs are discarded. Shadow answers the essential question would this model have done something sane, on real conditions, with zero risk.
Shadow is more informative than offline backtesting because it runs against the true, live data path including real latency, real sensor noise, and real missing-data handling. It also exposes the model to conditions no offline test set contained, since campaigns generate novel operating points continuously.
What shadow measures
- Agreement with the incumbent controller on nominal operation
- Divergence magnitude and its timing relative to events
- Frequency of would-be envelope or rate-limit violations
- Inference latency and jitter on the real edge budget
- Behavior during disturbances and near-fault conditions
def shadow_cycle(state, prod_ctrl, shadow_ctrl, log):
u_prod = prod_ctrl.act(state) # applied to machine
u_shadow = shadow_ctrl.act(state) # DISCARDED, logged only
log.record(state.t, u_prod, u_shadow,
div=norm(u_prod - u_shadow),
violations=check_envelope(u_shadow, state))
apply(u_prod) # only prod acts
Persistent large divergence is not automatically bad; the shadow model may be better. It is a signal to investigate, using the twin to replay both trajectories. A model that survives shadow with well-understood divergence and no envelope violations becomes eligible for canary. Shadow divergence tooling overlaps with divergence detection in the twin layer.