v0.8.1
v0.8.0 does not boot. Use this instead. If you pulled latest or v0.8.0 in the last hour, the backend crash-loops; replacing the image is the whole fix, no data or config changes needed.
What was broken
mcp-endpoint.controller read the application version with require('../../package.json') in order to report it in the MCP handshake. That relative path is correct under src/, and wrong everywhere the file actually runs: tsc emits to dist/src/, and the runtime image keeps the manifest at /app/backend/package.json while the compiled file sits at /app/backend/dist/src/mcp-server/. Node threw MODULE_NOT_FOUND at import time, so the backend died before it ever listened.
The container restarted in a loop, but the frontend in the same container started normally, so http://localhost:3000 served a login page and the instance looked healthy from the outside. Only /health on port 4000 told the truth.
The version is now resolved by walking up from __dirname for the nearest package.json carrying a name and a version, and it degrades to 0.0.0 instead of throwing. A string that appears in one handshake field cannot take the process down.
Also fixed: a failed migration no longer starts the backend anyway
start.sh ran prisma migrate deploy and ignored the result. When migrations failed, the backend came up against an empty or half-applied schema and died on the first query with relation "public.site_settings" does not exist, which points at the schema rather than at the migration that never ran.
It now stops at the migration step, where the error still describes the actual problem. Docker's restart policy retries, which is also the right behaviour when the database is simply not accepting connections yet. Both shipped compose files gate on a healthy Postgres, so this was only reachable when running the image by hand.
Why CI did not catch the first one
The Docker build job only ran on pushes to main, and only built the image. Building was never the problem: the image assembled perfectly and every test passed. CI now starts the built image against a throwaway Postgres and waits for /health, printing the container log when it does not come up.
That check found both of the issues above, plus two gaps in its own setup, within three runs.
Full diff: v0.8.0...v0.8.1