On this page
Process supervision for ``fastware dev``: pre-spawn gates, health-gated readiness, backend and Vite topology ordering, and graceful process-group teardown.
#src.fastware.supervise
#src.fastware.supervise
Process supervision for the fastware dev CLI.
Provides :class:DevProcess (a single supervised child in its own process group, with graceful-then-kill teardown) and the orchestration that turns a validated :class:~fastware.devconfig.DevConfig into a running dev environment:
- pre-spawn gates (must pass before anything spawns),
- ordered, health-gated aux services,
- the topology-ordered vite + backend pair with readiness probes,
- a supervise loop that, on any component death, tears the rest down in reverse
order and exits non-zero naming the dead component.
Daemon mode detaches the whole supervisor and registers it in the Phase 11 instance registry (entry name "<app-name>-dev"), which dev status and dev stop read.
#DevRunError
Base class for dev-run failures.
#PreSpawnGateError
A pre-spawn gate did not pass; nothing was spawned.
#ReadinessError
A component did not become ready within its timeout.
#PortCollisionError
A required port is already in use.
#SWModeConflictError
The resolved in-process app is configured with sw_mode='cache'.
#ComponentDied
A supervised component exited; the environment was torn down.
#_http_answers
def _http_answers(url: str, timeout: float) -> boolTrue if url answers at the HTTP level (any status), else False.
#run_pre_spawn_gates
def run_pre_spawn_gates(cfg: DevConfig) -> NoneRun every pre-spawn gate; raise :class:PreSpawnGateError on the first failure.
#DevProcess
A supervised child process running in its own process group.
#start
def start(self) -> None#pid
def pid(self) -> int | None#is_running
def is_running(self) -> bool#returncode
def returncode(self) -> int | None#forward_signal
def forward_signal(self, sig: int) -> NoneForward sig to the child's process group (if still running).
#stop
def stop(self, grace_s: float | None=None) -> NoneGraceful-then-kill: SIGTERM the group, wait grace_s, then SIGKILL.
#teardown
def teardown(components: list[DevProcess], grace_s: float=GRACE_DEFAULT) -> NoneStop components in reverse start order.
#supervise_loop
def supervise_loop(started: list[DevProcess], stop_flag: dict, poll_s: float=0.2) -> DevProcess | NoneBlock until a component dies or stop_flag['flag'] is set.
Returns the first dead :class:DevProcess, or None when a stop was requested (clean shutdown). Does not tear anything down -- the caller owns teardown so ordering stays in one place.
#_serve_in_process
def _serve_in_process(app_path: str, host: str, port: int, vite_port: int) -> NoneSubprocess entry point: import an app, wrap with ViteDevProxy, serve it.
#_start_ordered
def _start_ordered(cfg: DevConfig, grace_s: float, started: list[DevProcess]) -> NoneStart vite + backend in the order dictated by topology, readying each.
#resolve_backend_sw_mode
def resolve_backend_sw_mode(cfg: DevConfig) -> str | NoneImport the in-process app and return its resolved sw_mode (or None).
Only meaningful for the app backend form; returns None for cmd-form backends (whose sw_mode cannot be introspected without running them).
#check_sw_mode
def check_sw_mode(cfg: DevConfig) -> NoneHard-error if the resolved in-process app uses sw_mode='cache'.
cmd-form backends run as opaque subprocesses whose sw_mode cannot be introspected without executing them, so the guard cannot verify them. Rather than skip silently, emit a one-line stderr notice so the operator knows the guard did not run for this backend.
#dev_pid_path
def dev_pid_path(cfg: DevConfig, pid_dir: Path | str | None=None) -> Path#list_dev_instances
def list_dev_instances(cfg: DevConfig, pid_dir: Path | str | None=None)Return live registered dev instances (registry entries with the dev role).
#stop_dev_instances
def stop_dev_instances(cfg: DevConfig, pid_dir: Path | str | None=None, grace_s: float=GRACE_DEFAULT)Terminate every registered dev instance gracefully-then-kill. Returns them.
#_spawn_daemon
def _spawn_daemon(cfg: DevConfig, pid_dir: Path | str | None) -> intSpawn a detached fastware dev run --no-daemon and wait for it to register.
#run_dev
def run_dev(cfg: DevConfig, *, daemon: bool, grace_s: float=GRACE_DEFAULT, pid_dir: Path | str | None=None) -> intRun the dev environment. Foreground blocks; daemon detaches and returns.
Always performs the sw_mode='cache' hard-error check up front.