Diagram — Availability State Machine¶
Referenced from docs/technical/ARCHITECTURE.md and
docs/availability-contract.md. This is the published heartbeat
state machine (AvailabilityDetectorLogic.Evaluate, hysteresis applied) — the hard gate
(AvailabilityGate.Evaluate) is memory-free and re-evaluates fresh signals on every job, so it has
no state machine of its own; see the note at the bottom.
stateDiagram-v2
[*] --> Available: WorkerAvailability.InitialAvailable
Available --> Gaming: Steam RunningAppID != 0<br/>or allowlisted process seen
Available --> Streaming: obs64/obs32 process seen
Available --> ManualUnavailable: override file = "unavailable" (active)
Gaming --> Streaming: OBS starts while gaming<br/>(streaming outranks gaming — blockLevel: all)
Gaming --> GamingGrace: last gaming signal clears
GamingGrace --> Gaming: gaming signal re-trips<br/>(timer resets to zero, not extended)
GamingGrace --> Available: 90s continuous quiet elapsed
Streaming --> StreamingGrace: OBS process exits
StreamingGrace --> Streaming: streaming signal re-trips<br/>(timer resets to zero, not extended)
StreamingGrace --> Available: 120s continuous quiet elapsed
Available --> ManualAvailable: override file = "available" (active)
Gaming --> ManualAvailable: override file = "available" (active, wins over live signal)
Streaming --> ManualAvailable: override file = "available" (active, wins over live signal)
ManualUnavailable --> Available: override expires or flips to "auto"
ManualAvailable --> Available: override expires or flips to "auto"
ManualUnavailable --> ManualAvailable: override flips "unavailable" → "available"
ManualAvailable --> ManualUnavailable: override flips "available" → "unavailable"
note right of GamingGrace
blockLevel: heavy during this state
(trivial + control still run)
Grace = 90s per AvailabilityDetectorLogic.GamingGrace
end note
note right of StreamingGrace
blockLevel: all during this state
(only control still runs)
Grace = 120s per AvailabilityDetectorLogic.StreamingGrace
end note
note left of ManualUnavailable
blockLevel: all.
Wins over every live signal —
checked BEFORE Steam/OBS reads.
end note
Important asymmetry: this diagram is the detector's, not the gate's¶
AvailabilityDetectorLogic (this diagram) exists only to smooth the published /health
availability object so a consumer like Mission Control never sees available: true flicker on
in the half-second between "alt-tabbed out" and "actually still playing." It is advisory.
AvailabilityGate.Evaluate — the function that actually decides whether a job is allowed to
run — has no states and no memory at all. Every single job re-reads live signals
(IAvailabilitySignalsProvider.GetSignals()) fresh and applies the same six ordered rules (see
ARCHITECTURE.md § Gate rules)
from scratch, every time. If the detector's published state is stale or wrong for a few seconds —
which the 90s/120s grace windows above guarantee it sometimes will be — the gate is never fooled
by that staleness, because it never looks at the detector's state at all.