Measured while folding AiChatPage's publicShareBase into the shared resolver (#4482, PR #5677). Observation-class: nothing reachable is broken today, and no fix is proposed here.
What
consoleRoot() in packages/app-shell/src/console/organizations/resolveHomeUrl.ts is the single mount source for resolveHomeUrl / resolveRootUrl / resolveConsoleUrl, and now also for the share-link base. It resolves relative paths against the <base href> verbatim:
constbaseHref=document.querySelector('base')?.getAttribute('href');returnbaseHref ? newURL(baseHref,window.location.origin) : newURL('/',window.location.origin);Every one of those helpers therefore silently depends on the injected href ending in /. Given <base href="/_console"> (no trailing slash), new URL('home', 'https://host/_console') resolves to https://host/home — HTML base-URL semantics drop the last segment — so org switching, the /home landing, resolveConsoleUrl('apps/…') and the share base would all leave the console mount at once.
Nothing in the tree asserts or normalizes that trailing slash.
Why it is only an observation
The single injector is packages/cli/src/utils/console.ts, which writes `<base href="${CONSOLE_PATH}/">` — the slash is a literal in the template, so today the invariant holds by construction. objectui ships no <base> tag in any HTML of its own (checked across apps/** and packages/**), and the CLI's injection is idempotent: it bails if the build already shipped one, which is the seam where a differently-shaped href could arrive from a host.
Note also that a slashless href would already break the SPA's relative asset URLs (./assets/…), so such a deployment is broken before it reaches these helpers. That is the argument for leaving consoleRoot() alone rather than adding normalization — the resolver following browser semantics is arguably correct, and "correct" here is a judgement call, not an obvious repair.
Two smaller shapes in the same family, recorded for completeness:
What could be done, if triage decides it is worth anything
Cheapest option is a one-line normalization in consoleRoot() (append / when the resolved pathname lacks one), which would make the family robust to a host-injected href of either shape. That is a behaviour change to /home and org-switch navigation, which is why PR #5677 did not smuggle it in under a share-link card. The alternative is to leave the code alone and write the invariant down where the CLI injects it.
Filed unassigned for triage.
Generated by Claude Code
Generated by Claude Code
Measured while folding
AiChatPage'spublicShareBaseinto the shared resolver (#4482, PR #5677). Observation-class: nothing reachable is broken today, and no fix is proposed here.What
consoleRoot()inpackages/app-shell/src/console/organizations/resolveHomeUrl.tsis the single mount source forresolveHomeUrl/resolveRootUrl/resolveConsoleUrl, and now also for the share-link base. It resolves relative paths against the<base href>verbatim:Every one of those helpers therefore silently depends on the injected href ending in
/. Given<base href="/_console">(no trailing slash),new URL('home', 'https://host/_console')resolves tohttps://host/home— HTML base-URL semantics drop the last segment — so org switching, the/homelanding,resolveConsoleUrl('apps/…')and the share base would all leave the console mount at once.Nothing in the tree asserts or normalizes that trailing slash.
Why it is only an observation
The single injector is
packages/cli/src/utils/console.ts, which writes`<base href="${CONSOLE_PATH}/">`— the slash is a literal in the template, so today the invariant holds by construction. objectui ships no<base>tag in any HTML of its own (checked acrossapps/**andpackages/**), and the CLI's injection is idempotent: it bails if the build already shipped one, which is the seam where a differently-shaped href could arrive from a host.Note also that a slashless href would already break the SPA's relative asset URLs (
./assets/…), so such a deployment is broken before it reaches these helpers. That is the argument for leavingconsoleRoot()alone rather than adding normalization — the resolver following browser semantics is arguably correct, and "correct" here is a judgement call, not an obvious repair.Two smaller shapes in the same family, recorded for completeness:
<base href>makes the share link point at that origin. The builder deleted in PR refactor(app-shell): resolve AiChatPage's share base through the one console-mount resolver #5677 forcedwindow.location.origininstead. Also unreachable — the CLI emits a path-only href."null"(afile://or sandboxed-iframe document)consoleRoot()throws.WorkspaceSwitcherandOrganizationsPagealready reach it there, so this is pre-existing and consistent, not new.What could be done, if triage decides it is worth anything
Cheapest option is a one-line normalization in
consoleRoot()(append/when the resolved pathname lacks one), which would make the family robust to a host-injected href of either shape. That is a behaviour change to/homeand org-switch navigation, which is why PR #5677 did not smuggle it in under a share-link card. The alternative is to leave the code alone and write the invariant down where the CLI injects it.Filed unassigned for triage.
Generated by Claude Code
Generated by Claude Code