Found while working objectui#2794 (the /setup deep link). Not fixed there — that card is about who reaches /setup, this is about where the wizard sends you once it is done. Filed unassigned.
What
apps/console/src/pages/auth/SetupPage.tsx finishes the first-run owner bootstrap with a full-page navigation to the site root, twice:
- line 88 — the already-signed-in bounce:
window.location.assign('/')
- line 148 — the success path, after
signUp() and the bootstrap-org rename: window.location.assign('/')
window.location.assign bypasses React Router's basename. The console resolves its basename from an injected base href (App.tsx:resolveBasename), and the framework CLI injects one for every embedded deployment — so on a console served at /_console/, both calls land on the ORIGIN root rather than the console root.
Why this is a real class, not a nitpick
apps/console/src/pages/auth/LoginPage.tsx already carries the fix for exactly this, as a named helper with a comment that states the rule:
function withConsoleBase(path: string): string
window.location.assign bypasses React Router's basename, so a path produced by the router […] or a literal like /organizations would resolve to http://host/settings, missing the /_console mount and 404-ing.
SetupPage makes the same call and does not use that helper. Same defect the helper exists to prevent, one file over.
Severity / reachability
Low reach, and that is why it is filed rather than smuggled into #2794's PR:
- it only fires on a deployment that has no owner yet, i.e. genuinely first-run;
- under the default
/ mount the two spellings coincide, so a standalone os dev run cannot see it — this needs an embedded / base href-mounted console to reproduce, which is why nothing has hit it.
The outcome, when it does fire, is a completed bootstrap that lands the brand-new owner outside the SPA — the worst possible moment for a dead end, since it is the first screen after creating the account.
Suggested fix
Route both calls through withConsoleBase (or lift that helper somewhere both pages can import it — it is currently module-private to LoginPage). Worth a quick grep for other window.location.assign / window.location.href = producers in apps/console at the same time; this one was found by reading, not by a sweep, so the set may be larger.
Generated by Claude Code
Found while working objectui#2794 (the
/setupdeep link). Not fixed there — that card is about who reaches/setup, this is about where the wizard sends you once it is done. Filed unassigned.What
apps/console/src/pages/auth/SetupPage.tsxfinishes the first-run owner bootstrap with a full-page navigation to the site root, twice:window.location.assign('/')signUp()and the bootstrap-org rename:window.location.assign('/')window.location.assignbypasses React Router'sbasename. The console resolves its basename from an injectedbase href(App.tsx:resolveBasename), and the framework CLI injects one for every embedded deployment — so on a console served at/_console/, both calls land on the ORIGIN root rather than the console root.Why this is a real class, not a nitpick
apps/console/src/pages/auth/LoginPage.tsxalready carries the fix for exactly this, as a named helper with a comment that states the rule:SetupPagemakes the same call and does not use that helper. Same defect the helper exists to prevent, one file over.Severity / reachability
Low reach, and that is why it is filed rather than smuggled into #2794's PR:
/mount the two spellings coincide, so a standaloneos devrun cannot see it — this needs an embedded /base href-mounted console to reproduce, which is why nothing has hit it.The outcome, when it does fire, is a completed bootstrap that lands the brand-new owner outside the SPA — the worst possible moment for a dead end, since it is the first screen after creating the account.
Suggested fix
Route both calls through
withConsoleBase(or lift that helper somewhere both pages can import it — it is currently module-private toLoginPage). Worth a quick grep for otherwindow.location.assign/window.location.href =producers inapps/consoleat the same time; this one was found by reading, not by a sweep, so the set may be larger.Generated by Claude Code