build-a-process-supervisor / lesson-03.md
Lesson 03 · The service model and state machine

A supervisor of several services

One service is not a supervisor. The supervisor owns a set of services, each under a unique name, and every command it will ever run addresses a service by that name. Today you build the container that holds them and looks one up.

The goal

Build a supervisor that registers services by name and returns them on lookup.

Start here - the target
TO DO
Scenario: The supervisor registers and finds services by name
Givena new Supervisor with Add(NewService("web", "run-web")) and Add(NewService("db", "run-db"))
WhenGet("db") is called
Thenit returns the db service (Command "run-db"), and Get("missing") reports not found
AndNames() returns the registered names ["web", "db"] in insertion order
Background

A real supervisor manages many services at once - a database, a web server, a worker or two - and it addresses each by a unique name. That name is the handle every operation uses: Start("web"), Stop("db"), “reap the process that was worker”. So the supervisor is, at heart, a name-to-service map.

Keep a separate slice of names in insertion order. A plain map has no stable iteration order, and later lessons - the status report, topological start order, reverse-order shutdown - all need to walk services predictably. Establishing a deterministic order now saves every one of those lessons from flakiness.

Make it work
type Supervisor struct {
services map[string]*Service
order []string // preserves insertion order for Names()
}
func NewSupervisor() *Supervisor { /* init both fields */ }
func (s *Supervisor) Add(svc *Service) { /* store + append name */ }
func (s *Supervisor) Get(name string) (*Service, bool) { /* map lookup */ }
CheckpointDONE
The supervisor registers services by name and looks them up. Commit and stop here.