A service is always in exactly one of six states, and the whole supervisor is really a machine that moves services between them. Today you name all six and give each a readable label, so every later spec can assert a state by a value you can print and compare.
Define the six lifecycle states and a String label for each.
The lifecycle of a supervised service is small but exact. Stopped means not running and not wanted right now. Starting means the process has been spawned but has not yet reported ready. Running is the healthy steady state. Stopping means we asked it to shut down and are waiting for it to exit. Exited means it ended on its own. Failed is the terminal give-up state the supervisor moves a service to when restarting it is hopeless.
Giving each state a printable name is not cosmetic: every spec from here on asserts state by comparing these labels, and the final status report prints them. Because a supervisor is fundamentally a state machine, naming its states precisely is the groundwork for the transition rules you pin down next.
type State intconst (Stopped State = iotaStartingRunningStoppingExitedFailed)// fill in the label for each; String() drives every assertion laterfunc (s State) String() string { /* map each constant to its name */ }