Version Lock¶
The supervisor refuses to talk to a mismatched backend. This guards against shipping a build where the renderer expects API shapes the backend doesn't provide (or vice versa).
The three-way contract¶
When a backend change requires a new build, all three must move together:
| # | File | Field |
|---|---|---|
| 1 | backend-api/main.py |
BACKEND_VERSION |
| 2 | src/shared/constants.ts |
EXPECTED_BACKEND_VERSION |
| 3 | package.json |
version |
backend.ts hits /version on startup; if the running backend's version
doesn't match EXPECTED_BACKEND_VERSION, it kills the port and respawns
its own bundled binary.
What happens on mismatch¶
- The supervisor detects that the backend on its resolved port reports
BACKEND_VERSION = "0.1.9". - The renderer build was compiled against
EXPECTED_BACKEND_VERSION = "0.2.0". - The supervisor terminates that backend and spawns the version it shipped with.
- The renderer retries and connects to the matching backend.
This is the same code path that recovers from stale uvicorn processes left over from a previous dev session.
The splash enforces it too¶
The version lock is checked in two places, not one. Besides the supervisor
(main process), the renderer's startup splash (useBackendReady) polls
/version and only treats the backend as ready when the reported version
equals EXPECTED_BACKEND_VERSION. A 200 carrying a different version (a
stale or incompatible backend adopted on the resolved port) is not accepted — the
splash stays in its "Starting backend…" state while the supervisor kills and
respawns the bundled binary, then flips to ready once the matching version
answers. Without this, the UI could go live against a backend the supervisor
is in the middle of replacing, and /api/* calls would fail silently after
the splash had already dismissed.
Tagging a release¶
bash
git tag vX.Y.Z
git push origin vX.Y.Z
The release.yml workflow signs and notarizes the macOS app, builds for
Windows and Linux, and publishes a GitHub Release (published, not a draft —
electron-updater needs it that way to detect the update). Both the bundled backend
and the renderer reference vX.Y.Z, so the supervisor's check passes by
construction.
When you can skip a backend rebuild¶
If a change only touches src/renderer/, the renderer's
EXPECTED_BACKEND_VERSION doesn't change and you can ship a renderer-only
update. In practice this only matters for hotfixes — the normal flow is to
bump all three together.