EVM control as a cron job
Every pmcontrols Result exposes r.ok, which is True only when no
indicator breaches its threshold. That one boolean turns earned value
control from a status report into an automatable gate.
import sys
import pmcontrols as pm
r = pm.evm("pmb.json", ev=current_ev, ac=current_ac, at=current_period)
print(r.summary())
sys.exit(0 if r.ok else 1)
A weekly job reads the latest actuals, evaluates them against the frozen
PMB, prints a plain-language verdict, and exits nonzero the moment CPI or
SPI(t) drops below the threshold. Anything that watches process exit codes
(cron with MAILTO, a CI step, a systemd timer, an agent) now alerts on
schedule and cost the same way a failing test alerts on code.
pmcontrols evm - 2026-06-15T09:14:02+00:00
cpi 0.8571
spi_t 0.7500
ieac_t 13.3333
Alerts:
! cpi=0.857 breaches 0.9 - below threshold
! spi_t=0.750 breaches 0.9 - below threshold
Verdict: ATTENTION - indicators breach thresholds.
Thresholds
The defaults flag CPI or SPI(t) below 0.90. Override per run:
r = pm.evm("pmb.json", ev=ev, ac=ac, at=at,
thresholds={"cpi": 0.95, "spi_t": 0.95})
Any indicator name in r.stats can be a threshold key, and the alert fires
when the value falls below it. r.alerts is a tuple of structured Alert
records (indicator, value, threshold, note), so a downstream system
can route them rather than parse text.
Why this is safe to automate
The PMB is frozen and committed, so the baseline a job compares against is
the same one signed off at planning time. Limits do not drift because
someone recomputed them on this week's data. And r.to_dict() gives a
JSON-safe, versioned payload with the library version, an input hash, and a
timestamp, so every alert is reproducible six months later.