# Remote-safe Finance Dashboard restart pattern

Use this when FinanceManager's System Controls or a remote/Tailscale browser can restart the FastAPI backend and Vue frontend.

## Problem pattern

- The restart request is served by the backend that may be killed by the restart itself.
- If the backend process is stopped synchronously, the browser may see a network error before the HTTP response is flushed.
- A remote/Tailscale user can interpret that as “restart failed” or “dashboard unreachable”, even if the shell script eventually starts services.
- Multiple stale Vite/Uvicorn listeners can also leave PID files pointing at wrapper processes rather than the actual port owner.

## Durable fix pattern

1. Keep restart actions allowlisted only; never accept free-form commands.
2. For backend/frontend/dashboard restarts, schedule a detached worker instead of running the script synchronously from the request handler:
   - create runtime log dir;
   - write worker stdout/stderr to `runtime/logs/restart_<action>.log`;
   - run via `subprocess.Popen([...], start_new_session=True)`;
   - include a short `sleep 1` before executing the restart script so the HTTP response reaches remote clients before the process is killed.
3. Return a deterministic API response immediately:
   - `status: scheduled`;
   - `worker_pid`;
   - `worker_log`;
   - no raw stdout/stderr in the JSON response.
4. Frontend System Controls must treat both `ok` and `scheduled` as successful restart initiation.
5. After scheduling, poll `/api/system/status`; for full dashboard restart, reload the page after a short delay.
6. Restart scripts should still do the real safety work:
   - stop PID-file process if present;
   - kill stale listeners on backend/frontend ports;
   - start backend on `0.0.0.0:8000`;
   - start frontend on `0.0.0.0:5173`;
   - set `VITE_API_BASE_URL` to the Tailscale backend URL;
   - verify local backend, local frontend, and Tailscale frontend health.

## Tests to keep

- Backend unit test: restart schedules a detached worker and does not call synchronous `subprocess.run`.
- Backend unit test: ops log records `worker_pid` / `worker_log` and does not expose long/raw logs.
- Frontend unit test: System Controls accepts `scheduled` without showing “failed”.
- Runtime script text test: detached restart uses `sleep 1`, `subprocess.Popen`, and `start_new_session=True`.

## Live verification checklist

- Trigger `POST /api/system/restart-dashboard` and confirm JSON response arrives with `status=scheduled`.
- During restart, brief 8000/5173 downtime is acceptable.
- Confirm final health:
  - `http://127.0.0.1:8000/api/health`
  - `http://127.0.0.1:5173`
  - `http://<tailscale-ip>:5173`
  - `http://<tailscale-ip>:8000/api/health`
- Open dashboard through Tailscale in browser and check console for JS errors.
