THE CRITICAL ONE. The server ends every SSO login by redirecting to `#/login?sso=1`
(claim the session) or `#/login?sso_error=<code>` (say what went wrong). The router
compared the hash EXACTLY against '#/login' in three places, so an unauthenticated
browser — the only kind that ever arrives there — had the hash rewritten to a bare
'#/login' and the query was gone before the login view ran.
- a user who authenticated perfectly at their IdP landed back on a clean login page,
still signed out, with no message: /api/auth/sso/claim was never called
- all 16 error codes rendered SILENCE — not a raw key, not "undefined", nothing to
report or search for
- it took the pre-existing ?verified=1 email-verification toast with it
The comment above the reset-password exclusion describes this exact bug class and was
never extended to the login route. It is now, in all three places: the auth redirect,
the render dispatch, and the no-workspace guard.
Verified in real Chrome: 16/16 codes render a real sentence, and ?sso=1 now reaches
POST /api/auth/sso/claim.
Also, on a server with NO SSO configured, confirmed in the browser that the login page
is exactly what it was before any of this work: email, password, Sign In, Forgot
password, zero SSO buttons, no single sign-on wording, plain local login issues a
session, no page errors.
And fixes MY preflight, which pruned devDependencies as a side effect of BOOTING:
`npm install --omit=dev` reconciles the whole tree, so merely starting the server
deleted socket.io-client, puppeteer-core and js-yaml and broke `npm test`. A reviewer
watched it happen. It now installs only the named missing packages, with --no-save —
a boot-time repair that quietly removes packages is worse than the failure it fixes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bvjey4FNam49MN7ybjcq6A
scripts/upgrade.sh already runs `npm ci`, so this is not for the normal path. It is
for the ways a box ends up with the wrong node_modules, both of which present as
"server will not start" with an error naming a file rather than the action needed:
ROLLBACK checking out an older tag to back out a bad release restores that
tag's package.json but not its packages. This branch removes
google-auth-library, so a rollback to main would not boot — and you
are rolling back because something else already broke.
NODE UPGRADE better-sqlite3 is compiled against one ABI. Upgrading Node makes every
boot fail with NODE_MODULE_VERSION, which reads like database
corruption and is not.
Runs as the FIRST statement in server.js, before any dependency is required, and uses
only Node builtins — anything it imported could be the thing that is missing. Repairs
with `npm install --omit=dev` (never `ci` on a partly-populated tree, which would
delete a working node_modules to fix one package) or `npm rebuild better-sqlite3`, and
exits with the command to run if it cannot. ST_SKIP_DEP_PREFLIGHT=1 opts out.
⚠️ The first version of the native check was WRONG and I caught it only by running it
under a real version mismatch: better-sqlite3's entry point is plain JavaScript that
loads the compiled binding lazily, so `require()` succeeds under a Node the binary was
never built for. It reported a genuinely broken install as healthy. It now opens an
in-memory database, which is what actually pulls the binding in. A test pins that,
because the failure is invisible — the check keeps passing on every machine where
nothing is wrong.
Verified: a deleted dependency is detected, installed and the server boots (200); the
ABI mismatch is detected under Node 18 against a module built for Node 20 and reported
clean under Node 20; a healthy tree costs 8ms and touches no network.
1609 tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bvjey4FNam49MN7ybjcq6A