Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

console: the first-run setup wizard's completion redirect bypasses the console basename #4181

Closed

Description

@yinlianghui

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

Activity

  1. self-assigned this
    on Aug 10, 2026
  2. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    ContributorAuthor

    Claim: PM loop round 6 (rolling slot refill; graded bug — concrete on every framework-CLI deployment, which injects the base href automatically; invisible only under bare / mounts)
    Session: session_017Qqyix2QcnpUC9XeYVDzx3 · Branch: claude/issue-4181-setup-exit-basename · Worktree: objectui-issue-4181
    File surface: apps/console/src/pages/auth/SetupPage.tsx (:88 and :148, the two window.location.assign('/') exits) + wherever withConsoleBase() gets lifted from LoginPage's module-private scope so both callers share one implementation + tests. Serial constraints: PR #4180 (armed, same app) touched App.tsx/components but deliberately NOT SetupPage — branch off main; rebase if #4180's landing moves shared files.
    Container & model: S, mode:subagent, model: opus

    Direction (the filing names its own fix): route both exits through the withConsoleBase() treatment LoginPage already carries — lift the helper to a shared module rather than duplicating it (two module-private copies of a basename fix is the next drift), convert the exits to router navigation if the surrounding code allows it (a full-page assign may be deliberate post-bootstrap — READ why it reloads before deciding; if the reload is load-bearing, keep the assign but base-prefix it and say why), and pin the base-href case red→green (the #4168/#4180 basename test idiom is the worked precedent).


    Generated by Claude Code

  3. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    ContributorAuthor

    ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4186.

    The dispatch's "read why it reloads before deciding" clause earned its keep: the reload is load-bearing for a measured reason (MetadataProvider mounts on auth RESOLUTION and re-keys on language alone, so a router navigation would hand the new owner an appless console) — both exits stay assigns, base-prefixed, with the rationale pinned on the constant so the next refactorer inherits the WHY. Two card corrections reported straight: the third private copy of the helper in RegisterPage (the predicted drift had already happened — the lift covers three sites), and the verification that the helper's mechanism actually holds (BASE_URL vs injected base href are different sources that agree because Vite 8 resolves ./ literally — documented, because without that fact the fix would be theatre). The test design matches the mechanism: targets resolved against document.baseURI as location.assign itself resolves them, across all three real mount configurations. Reverse verification predicted 4-red exactly with the lift proven behavior-neutral by the seven untouched pins. The location.assign sweep's per-site verdicts (OAuth targets external by design; the storage URL server-prefixed) recorded in #4190 so nobody re-chases them, with #4190's own submitBehavior.url contract question correctly held for a ruling.

    First-run owners on mounted consoles now land inside the SPA. Flipping ready + auto-merge.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions