Asking to start a service that is already up must not launch a second copy. A supervisor is declarative - "I want web running" - so a redundant start is simply satisfied already. Today you make Start a no-op when the service is not Stopped.
Make Start on an already-running service do nothing - no second process is spawned.
A supervisor expresses desired state, not one-off commands. When you say a
service should run, asking twice does not mean “run two copies” - it means the same
thing said again. So Start on a service that is already Running (or Starting)
must do nothing: no new process, no new handle, no state change. Spawning a second
copy would leak a process the supervisor has lost track of, because a service holds
exactly one handle.
The transition guard from lesson 4 gives you this almost for free: Starting is
only reachable from Stopped or Exited, so a service that is Running simply
fails the CanTransition check and Start returns early. This idempotence is the
quiet foundation of the reconcile loop much later, where the supervisor repeatedly
calls Start on everything that should be up and relies on the already-up ones
staying untouched.
func (s *Supervisor) Start(name string) {svc, _ := s.Get(name)// guard: a start is only meaningful from a stopped/exited serviceif !CanTransition(svc.State, Starting) {return // already up (or coming up); nothing to do}svc.Handle = s.rt.Start(svc.Command)svc.State = Starting}