---
name: foundations-control-theory
description: Control-theory primitives for PID, MPC, Kalman, stability, anti-windup, dead-time, breakers, and limits. Use when tuning autoscaling, retries, or agent loops.
compatibility: Portable core only.
version: "1.2"
last_validated: 2026-08-14
---

# Control Theory Foundations

12 applied control-theory primitives for feedback control and dynamical systems, backed by a formal theory map. Each primitive owns a specific failure mode in any system that must reach and hold a target state despite disturbances, delays, noise, or nonlinearities. Primitives are domain-agnostic: the same PID loop that controls CPU utilization controls budget pacing and retry rates; the same circuit breaker that isolates a failing database isolates a failing LLM tool.

## Contents

- [Quick Reference](#quick-reference)
- [Primitive Index](#primitive-index)
- [Formal Supporting Theory](#formal-supporting-theory)
- [Anti-Patterns](#anti-patterns)
- [Misuse Boundaries](#misuse-boundaries)
- [Expert Judgment](#expert-judgment)
- [Decision Checklist](#decision-checklist)
- [Composition Recipes](#composition-recipes)
- [Workflow](#workflow)
- [ASCII Flow](#ascii-flow)
- [Navigation](#navigation)
- [Fact-Checking](#fact-checking)

---

## Quick Reference

| Primitive | Problem It Solves | Key Parameters |
|-----------|------------------|----------------|
| [PID Control](#1-pid-control) | Drive output to setpoint despite steady-state error and disturbances | Kp, Ki, Kd; tuned via Ziegler-Nichols |
| [Feedback vs. Feedforward](#2-feedback-vs-feedforward) | Reactive-only loops ignore predictable disturbances | Plant model accuracy; disturbance measurability |
| [Observability & Controllability](#3-observability--controllability) | States you cannot see or reach make the loop fail silently | Controllability matrix rank; observability matrix rank |
| [Lyapunov Stability](#4-lyapunov-stability) | No proof that a loop converges; may oscillate or diverge | Lyapunov function V(x); dV/dt < 0 condition |
| [MPC](#5-model-predictive-control-mpc) | One-step control ignores future constraints and couplings | Horizon N; cost matrices Q, R; constraint bounds |
| [Kalman Filter](#6-kalman-filter) | Noisy measurements degrade controller and monitoring accuracy | Process noise Q; measurement noise R; model (A, B, C) |
| [Dead-Time Compensation](#7-dead-time-compensation-smith-predictor) | Transport lag causes oscillation or instability | Dead time L; plant model (delay-free) |
| [Anti-Windup](#8-anti-windup) | Integrator saturates during limit-clamping → overshoot on release | Actuator min/max; tracking constant T_t |
| [Gain Scheduling](#9-gain-scheduling) | Single fixed-gain controller fails across operating regimes | Scheduling variable σ; per-regime gain tables |
| [Circuit Breaker & Backpressure](#10-circuit-breaker--backpressure) | Cascading failure; unbounded queue growth | Failure threshold; timeout; half-open probe logic |
| [Rate Limiting / Token Bucket](#11-rate-limiting--token-bucket) | Bursts and retry storms overload downstream; 429s cascade | Fill rate r; burst capacity b; per-request cost s |
| [DeePC / Behavioral Systems](#12-deepc--behavioral-systems) | MPC without a plant model — unknown dynamics make model-based prediction impossible | Hankel matrix T (data length); regularization λ_g, λ_y; persistency-of-excitation order |

---

## When to Apply

**Apply control-theory when:**
- A measurable variable must track a setpoint over time (autoscaler latency target, rate limiter throughput, retry pacing)
- Feedback loop with measurable lag — current state shapes the next action
- Oscillation or overshoot is observed (system swings around target instead of settling)
- Anti-windup needed — integral term must be clamped during saturation
- Plant has dead-time or transport delay (e.g., pod startup ~45 s)

**Skip and use simpler alternatives when:**
- One-shot decision, no feedback, no setpoint — use foundations-decision-theory
- Static threshold rule that doesn't need to adapt — a constant or hysteresis band is simpler
- Capacity sizing question, not feedback question — use foundations-queueing-theory
- Plant model is unknown and you can't measure error reliably — fix observability first
- Dead-time > 30% of desired settling time — PID alone is insufficient; consider Smith Predictor or MPC
- System is unstable open-loop and you don't know why — diagnose root cause before adding feedback

---

## Primitive Index

Each primitive is summarized here, expanded in [`references/primitives-overview.md`](references/primitives-overview.md), and covered by standalone playbooks under [`assets/templates/control-theory/`](assets/templates/control-theory/). Use [`references/formal-theory-map.md`](references/formal-theory-map.md) when the task needs stability assumptions, state-space reasoning, or robustness boundaries.

| # | Mechanism | Failure Mode Addressed |
|---|-----------|----------------------|
| 1 | PID Control | Uncontrolled oscillation or steady-state error in a closed loop |
| 2 | Feedback vs. Feedforward | Reactive-only control cannot anticipate predictable disturbances |
| 3 | Observability & Controllability | Controlling or monitoring states that cannot be reached or seen |
| 4 | Lyapunov Stability | No convergence proof; loop may diverge without warning |
| 5 | MPC | Constraint violations; myopic one-step control |
| 6 | Kalman Filter | State estimation errors from noisy sensors degrade controller performance |
| 7 | Dead-Time Compensation | Transport lag causes oscillation or instability in feedback loops |
| 8 | Anti-Windup | Integrator saturation during actuator clamping -> post-release overshoot |
| 9 | Gain Scheduling | Fixed gains inadequate outside the design operating point |
| 10 | Circuit Breaker & Backpressure | Cascading failure from downstream service failures; unbounded queues |
| 11 | Rate Limiting / Token Bucket | Burst overload and retry storms after failure recovery |
| 12 | DeePC / Behavioral Systems | MPC-level optimization when no plant model is available; unknown or hard-to-identify dynamics |

---

## Formal Supporting Theory

| Theory Area | Use When | Applied Primitives It Grounds |
|---|---|---|
| State-space systems | Need A/B/C/D models, poles, modes, controllability, observability | #3, #4, #5, #6 |
| Classical feedback | Need loop shaping, root locus, Bode/Nyquist, margins, PID tuning | #1, #2, #7, #8 |
| Stability theory | Need Lyapunov, input-to-state stability, passivity, bounded-input bounded-output | #4, #10, #11 |
| Optimal control | Need LQR/LQG, dynamic programming, constrained optimization, MPC | #5, #6 |
| Robust control | Need uncertainty margins, H-infinity, mu-synthesis, delay robustness | #4, #7, #9 |
| Adaptive/nonlinear control | Need parameter drift, operating regimes, saturation, nonlinear dynamics | #8, #9 |
| Stochastic estimation | Need Kalman assumptions, noise covariance, filtering vs smoothing | #6 |
| Networked/distributed control | Need queueing, backpressure, admission control, cascading-failure boundaries | #10, #11 |
| Safe RL with certificates | Need hard safety constraints in a learned/RL-controlled system at deployment | #4 |
| Online convex optimization / adaptive control | Need regret bounds for algorithms that update controller parameters online | #5 (adaptive MPC) |
| Behavioral systems / Willems' Fundamental Lemma | Need MPC without a parametric model; replace explicit prediction with data-driven Hankel matrix; unknown or nonlinear plant | #12 (DeePC) |
| Advanced regulatory control (ARC) | Need to decompose a multi-loop or multi-agent system into scoped elements with deterministic conflict resolution (selectors, split-range) rather than negotiation | #4, #5, #9 |

---

## Anti-Patterns

| Anti-Pattern | Control Theory Diagnosis | Fix |
|-------------|------------------------|-----|
| P-only autoscaler oscillates around target | Underdamped proportional-only control; no derivative damping | Add derivative term (Kd); tune with Ziegler-Nichols (#1) |
| Integrator windup at actuator limit | Integral accumulates during saturation; releases as large overshoot | Anti-windup on every PID with bounded actuator (#8) — this is always required |
| Reactive controller ignores predictable patterns (load spikes, business hours) | Feedback-only; no model of known disturbances | Add feedforward schedule component (#2) |
| Observability gap: slow-changing state invisible to aggregate metric | Unobservable state mode in measurement design | Observability rank test (#3); add direct sensor or redesign C matrix |
| MPC tuned on stale system model | Model–reality mismatch degrades constraint handling and prediction | Retrain model online; add Kalman filter for state estimation (#6) |
| Dead time treated as additional plant gain | Transport lag misidentified → wrong tuning; oscillation | Smith Predictor (#7); estimate L via step test; never increase Kp to compensate |
| Retry storm after circuit re-closes | All blocked callers retry simultaneously; re-triggers failure | Token bucket with jitter (#11); stagger retries; half-open probe first (#10) |
| Agent loop with no convergence proof | No potential function that decreases per step | Define Lyapunov potential (e.g., remaining uncertainty); add hard step limit as fallback (#4) |
| Gain scheduled without bumpless transfer | Abrupt gain switch causes transient at boundary | Interpolate gains smoothly (#9); transfer integral state during switch |
| Backpressure signal not honored by producer | Queue grows despite signal | Enforce at ingress; drop or block if producer ignores signal (#10) |
| Agent loop passes safety tests in simulation but fails on hardware | CBF applied only at inference, not baked into training; policy never internalized the constraint | CBF-RL: embed CBF as training-time safety filter so policy internalizes constraint before deployment (#4) |
| Multi-agent system resolves constraint conflicts by LLM negotiation | Conflict resolution delegated to a stochastic component; no deterministic priority order | Structural priority: MIN/MAX selectors for competing controlled variables, split-range for competing actuators; orchestrator resolves deterministically regardless of model output (#4, #5) |

---

## Misuse Boundaries

| Misuse | Why It Is Wrong | Required Correction |
|---|---|---|
| Tuning PID by folklore constants | Ziegler-Nichols is an aggressive starting point, not a guarantee | Test margins and re-tune on the actual plant |
| Ignoring actuator saturation | Integral windup creates overshoot and instability | Add anti-windup to bounded actuators |
| Treating delay as lower gain | Dead time changes phase and can destabilize loops | Estimate delay and use compensation or lower bandwidth |
| Claiming Kalman optimality outside assumptions | Kalman is optimal for linear Gaussian systems | Use EKF/UKF/particle filters with caveats |
| Applying MPC without model validation | Bad model makes constrained optimization confidently wrong | Validate model error and add robust margins. When plant model is unknown: use DeePC (#12) for a model-free alternative, Koopman MPC if a stability certificate is required (Schimperna et al. 2025), or physics-informed sysid if partial domain knowledge exists (Sivaranjani et al. 2025, arXiv:2512.06315). |
| Applying deterministic CBF with noisy measurements | Classical CBFs guarantee forward-invariance only for noise-free dynamics; sensor noise violates the invariance condition | Use stochastic/probabilistic CBF (Echigo et al. 2026, arXiv:2604.08831) or add an explicit safety margin to the CBF constraint. |
| Using circuit breakers without backpressure | Fail-fast alone can shift overload elsewhere | Pair breakers with queues, rate limits, and admission control |
| Calling an agent loop stable because it has max steps | Max steps bound cost, not convergence | Define a potential function or monotone progress metric |

---

## Expert Judgment

What separates a control-theory-literate engineer from someone reading the tables above: recognizing failure before the dashboard shows it, and knowing when the discipline does not apply.

### Recognizing an unstable loop before it visibly oscillates

Visible oscillation is the *late* symptom. By the time replica count or bid multiplier is swinging, margin was gone cycles earlier. Earlier signals, roughly in order of how early they appear:

- **Growing control-signal variance at constant error variance.** If `u` (replicas added/removed, bid delta) is getting noisier while the error it's responding to is not, gain margin is shrinking — the loop is amplifying noise it used to damp. This shows up in `stddev(Δu)` well before it shows up in the tracked metric.
- **Settling time creeping up release over release.** Each correction takes a bit longer to return to setpoint than the previous one. A single slow cycle is noise; a multi-week upward trend is phase margin eroding, usually from an added dependency, a slower downstream, or a metrics pipeline that got an extra aggregation stage.
- **Widening lag between command and effect.** Cross-correlate the actuator command timestamp against the measured-effect timestamp on a rolling window. Dead time is supposed to be a constant you compensate for once; if the cross-correlation lag is trending up, something upstream (queueing, batching, an added retry layer) is adding delay the control loop was never tuned against.
- **Two independently-stable loops sharing an actuator or measurement surface.** An autoscaler and a load balancer's outlier-ejection logic both write to "how many pods serve traffic." Each can pass isolated load-testing and still destabilize the composite system, because neither loop's model includes the other's action. Before declaring a loop stable, ask what else reads or writes the same actuator and measurement.
- **The absence of a plant model is itself a signal.** If nobody on the team can say "here is the transfer function, or here is the step response we measured," any stability claim is a guess. Silence on this question is the earliest warning of all — it means the loop was tuned by trial and error under one traffic pattern and has no basis for extrapolation to another.

### Measurement-delay traps

Dead time is not just physical (network RTT, pod boot, replication lag). The measurement pipeline adds its own, and it is the one teams forget to model:

- A rolling average over a `W`-second window adds roughly `W/2` seconds of effective dead time on top of the true delay — a 60 s Prometheus rate() window plus a 30 s scrape interval can add 45–60 s of lag the PID was never told about.
- Dashboards built for humans (5-minute buckets, smoothed lines) are the wrong signal to feed a controller — they look clean specifically because they've been low-pass filtered, which is delay in another form.
- Symptom of this trap: a loop tuned against historical/backtested data (already aggregated, already lagged) oscillates in production against the live, less-delayed-but-noisier raw signal, or vice versa — tuned against raw data and unstable once someone "cleans it up" with a wider aggregation window.
- Fix: state the total loop dead time as one number — scrape interval + aggregation window + decision latency + actuation latency + propagation time — and compensate (or gain-reduce) against that total, not the physical component alone.

### Why most software "controllers" fail

It is rarely bad arithmetic. Nearly every production PID-like autoscaler, pacer, or admission controller that misbehaves is missing one or more of three universal preconditions, and most are missing all three at once:

1. **Delay** (dead time) is not modeled — see above.
2. **Noise** is fed to the controller raw — an unfiltered P99 or a jittery per-second rate drives Kd (derivative) into "derivative kick," amplifying sensor noise into actuator chatter.
3. **Actuator saturation** has no anti-windup — the moment the loop hits a hard limit (max replicas, bid cap, rate-limit ceiling), the integral term keeps accumulating against a wall, then overshoots on release.

The fix for each is well-known (Kalman/low-pass filtering, Smith Predictor, anti-windup) and documented in this skill — the expert judgment is diagnosing *which* of the three is actually dominant before reaching for a fix, since applying the wrong one (e.g., adding derivative gain to a problem that is really unmodeled dead time) makes the loop worse.

### When open-loop beats closed-loop

Closed-loop control is not free — it costs a measurement, a delay, and a risk of instability. Prefer open-loop (feedforward-only, or a scheduled/static policy) when:

- The dominant disturbance is fully predictable (diurnal traffic, a scheduled batch job) **and** dead time is a large fraction of the desired response time — feedback correction physically cannot arrive before the disturbance has already passed. See the Predictive Autoscaler recipe below; empirically this beat reactive HPA/KEDA by roughly 6–20x median latency in one measured case (Tymoshenko, Maraschi & Collina 2026, arXiv:2604.19705 — Node.js/Kubernetes-specific, not verified to generalize).
- The measurement itself is slow, expensive, or destabilizing — e.g., a business metric only available T+1 day, or a metric whose own collection changes the system being measured. Closed-loop control against a badly-delayed proxy signal can be strictly worse than a static policy tuned offline from historical data.
- The actuator is high-consequence and effectively irreversible on the timescale of one control cycle (a schema migration, a pricing change, a one-way data deletion). "Act, observe, correct" is not a viable strategy when the correction cannot undo the action — get it right open-loop, using simulation and backtesting, not live feedback.
- As a rule of thumb: if `dead_time / desired_settling_time > ~0.5`, or if the disturbance is highly predictable and the loop's only job is to react to something already known in advance, feedback control is fighting a battle it starts already behind. Feedforward-first, feedback-as-trim is the correct architecture, not "add more gain."

---

## Decision Checklist

- [ ] **Setpoint tracking**: System must reach and hold a target value under disturbances? → PID (#1)
- [ ] **Predictable disturbances**: Known patterns (time of day, schedule, traffic shape) available? → Feedforward (#2)
- [ ] **State visibility**: Can all relevant states be inferred from available sensors? → Observability rank test (#3)
- [ ] **Actuator reachability**: Can all target states be reached by available actuators? → Controllability rank test (#3)
- [ ] **Convergence proof required**: Must prove loop terminates or converges by design? → Lyapunov function (#4)
- [ ] **Constraints exist**: Actuator limits, safety bounds, or resource caps that must never be violated? → MPC (#5) or anti-windup (#8) in PID
- [ ] **Noisy measurements**: Sensor output too noisy for direct use in controller? → Kalman filter (#6)
- [ ] **Transport lag**: Action-to-effect delay > 30% of dominant time constant? → Dead-time compensation (#7)
- [ ] **Bounded actuator**: Control output has hard min/max? → Anti-windup (#8) — include by default
- [ ] **Regime variation**: System dynamics differ significantly across load or operating conditions? → Gain scheduling (#9)
- [ ] **External service dependency**: Calling a downstream service that can fail? → Circuit breaker (#10)
- [ ] **Producer-consumer queue**: Queue can grow unboundedly under sustained load? → Backpressure (#10)
- [ ] **Bursty arrivals or retry risk**: Traffic or retries can spike beyond downstream capacity? → Token bucket (#11)
- [ ] **Unknown plant + MPC desired**: Need MPC-level constraint handling but have no plant model and system identification is impractical? → DeePC (#12) — collect persistently-exciting offline data first
- [ ] **Predictable load pattern + dominant startup lag**: Load is 