Follow-up to PRI-13. Current supervisor test coverage is only the pure functions (next_backoff, SidecarStatus serialization, default config constants). The state machine itself — spawn → run → terminate → backoff → respawn, healthy-uptime backoff reset — is only validated by manual E2E.
Scope
Introduce a SidecarLauncher trait inside sidecar_supervisor.rs:
trait SidecarLauncher: Send + Sync + 'static {
async fn launch(&self) -> Result<LaunchedChild, io::Error>;
}
struct LaunchedChild {
events: tokio::sync::mpsc::Receiver<CommandEvent>,
writer: Box<dyn StdinWrite + Send>,
}
Production impl wraps app.shell().sidecar(SIDECAR_BINARY)?.spawn() as today. Test impl is a scripted launcher that:
- Emits a configurable sequence of
CommandEvents into the rx channel
- Terminates on cue to drive the respawn branch
- Records bootstrap + twitch_connect writes so tests can assert them
Tests to add:
- Healthy run resets backoff: seed a launcher that stays alive past the healthy threshold, then terminates → next
run_once starts at initial_backoff
- Unhealthy run grows backoff: seed a launcher that terminates immediately twice → second backoff is 2× first
- Bootstrap + twitch_connect replay on every respawn
sidecar_status events are emitted in the expected order per iteration
Out of scope
- Real cross-process respawn (this lives in manual E2E and the release smoke test)
- Mocking the ring buffer — drain loop stays out of these tests; focus is the supervisor lifecycle
Depends on
Follow-up to PRI-13. Current supervisor test coverage is only the pure functions (
next_backoff,SidecarStatusserialization, default config constants). The state machine itself — spawn → run → terminate → backoff → respawn, healthy-uptime backoff reset — is only validated by manual E2E.Scope
Introduce a
SidecarLaunchertrait insidesidecar_supervisor.rs:Production impl wraps
app.shell().sidecar(SIDECAR_BINARY)?.spawn()as today. Test impl is a scripted launcher that:CommandEvents into the rx channelTests to add:
run_oncestarts atinitial_backoffsidecar_statusevents are emitted in the expected order per iterationOut of scope
Depends on