Graceful backend shutdown handshake for the desktop shell
Today the Tauri shell (#78) tears the backend down hard on quit: best-effort
child.kill() plus, on Windows, the Job Object's kill-on-close reaping the whole
tree (python + device-layer grandchild). That guarantee is the right floor — no
orphaned device layer holding COM ports even on a crash — but there is no graceful
step before it. desktop/README.md and the design spec already list the handshake
as deferred future work.
Why it now matters concretely
Session replay (#84) records UI events and flushes batches to the backend. On app
quit, the final pagehide beacon races the backend's death, so the last seconds of
a session can be lost — and in a crash postmortem those are exactly the seconds that
matter. #84 bounds the loss (short flush interval, ≤ one batch) but cannot eliminate
it from the page side.
There are also the shutdown-sequence ideas from the launcher design that need the
same hook: guided housekeeping steps on exit (e.g. prompting the operator to reset
the F drive) require the backend to be asked to stop, not just killed.
Shape of the fix
On window-close: shell calls a backend shutdown endpoint (drain replay ingest, stop
the device layer via DeviceLayerSupervisor's existing graceful path, flush state),
waits briefly with a hard deadline, then proceeds with the existing kill + job-close
as the unchanged fallback. The Job Object floor stays — the handshake only adds a
polite first attempt in front of it.
Refs: #78 (where the kill lives, desktop/src-tauri/src/main.rs exit handling),
#84 (first consumer), docs/superpowers/specs/2026-07-02-unified-launcher-design.md.
Graceful backend shutdown handshake for the desktop shell
Today the Tauri shell (#78) tears the backend down hard on quit: best-effort
child.kill()plus, on Windows, the Job Object's kill-on-close reaping the wholetree (python + device-layer grandchild). That guarantee is the right floor — no
orphaned device layer holding COM ports even on a crash — but there is no graceful
step before it.
desktop/README.mdand the design spec already list the handshakeas deferred future work.
Why it now matters concretely
Session replay (#84) records UI events and flushes batches to the backend. On app
quit, the final
pagehidebeacon races the backend's death, so the last seconds ofa session can be lost — and in a crash postmortem those are exactly the seconds that
matter. #84 bounds the loss (short flush interval, ≤ one batch) but cannot eliminate
it from the page side.
There are also the shutdown-sequence ideas from the launcher design that need the
same hook: guided housekeeping steps on exit (e.g. prompting the operator to reset
the F drive) require the backend to be asked to stop, not just killed.
Shape of the fix
On window-close: shell calls a backend shutdown endpoint (drain replay ingest, stop
the device layer via
DeviceLayerSupervisor's existing graceful path, flush state),waits briefly with a hard deadline, then proceeds with the existing kill + job-close
as the unchanged fallback. The Job Object floor stays — the handshake only adds a
polite first attempt in front of it.
Refs: #78 (where the kill lives,
desktop/src-tauri/src/main.rsexit handling),#84 (first consumer),
docs/superpowers/specs/2026-07-02-unified-launcher-design.md.