Part of #5. Small, and it makes the app feel unsafe to touch.
What is there today
POST /api/settings ends with await restartWatching() for every change, including ones that cannot
affect a running task: the allowed handles, pause, and the open-at-login checkbox
(app/Uplink/desktop-agent.ts#L336).
restartWatching calls uplink.stop() (app/Uplink/desktop-agent.ts#L207), which cancels every active
run and sets stoppedByUser = true (app/Uplink/uplink.ts#L150). And finish() deliberately sends no
reply when stoppedByUser is true (app/Uplink/uplink.ts#L368), because that flag normally means the
person texted stop and already knows.
So somebody tidying their setup while a task runs, ticking "Open at login" or adding a second handle, silently kills the task. The person who texted receives nothing at all.
Proposal
- Only restart the watcher for changes that require it.
openAtLoginwrites a plist and needs no restart at all (app/Uplink/desktop-agent.ts#L331). The allowed handles are re-read every poll byrefreshHandles(app/Uplink/uplink.ts#L190), so they may not need one either. - When a restart really is needed, wait for the active run to finish first. The from-source watcher
already does exactly this for
.envedits: it restarts "once no run is in progress" (app/Commands/Uplink.ts#L110). Reuse that discipline. - Separate "the user texted stop" from "we cancelled it ourselves". Only the first should be silent. A run cancelled by the app deserves a text saying so.
Requirements
- Never cancel a run without telling the person who asked for it, unless they are the one who cancelled it.
- Pausing is the one case where stopping immediately is correct, because that is what pause means. Say so in the reply.
- Settings must still take effect. Deferring a restart is not dropping it.
Acceptance
- Ticking "Open at login" during a run does not interrupt it.
- Pausing during a run stops it and texts the person that it was stopped.
- A setting that needs a restart applies as soon as the run finishes.