Repository navigation
Description
Copying an invitation link from the workspace Invitations tab yields a URL that cannot be opened. The recipient of an invitation therefore has no working way in.
Repro
- Multi-org runtime, zh console, sign up and create a workspace (owner).
- Users → Invite user → invite
lisi@acme-test.comasmember. - Workspace switcher → Manage members → Invitations tab → click Copy invitation link.
- Paste the clipboard content.
Actual
http://localhost:8080./accept-invitation/bgn1XAfRuCQUS1l9NBqOMGgJDUYm5l56
Two defects in one string:
- trailing-dot host —
localhost:8080.(the port is followed by.); - the console mount is missing — the console is served under
/_console/, andGET /accept-invitation/<id>(dot removed) is answered by the server with{"error":"Not found"}.
The only URL that renders the accept page is http://localhost:8080/_console/accept-invitation/<id> — verified working, the page loads and the invitation can be accepted.
Expected
The copied link is the one the recipient can open: <origin><console mount>accept-invitation/<id>.
Cause
buildAcceptUrl rebuilds the URL from import.meta.env.BASE_URL, which is '.' in a portable (base: './') build:
// packages/app-shell/src/console/organizations/manage/InvitationsPage.tsx:32
function buildAcceptUrl(invitationId: string): string {
const base = (import.meta as any).env?.BASE_URL ?? '/';
const normalized = base.endsWith('/') ? base : `${base}/`;
return `${window.location.origin}${normalized}accept-invitation/${invitationId}`;
}Duplicated verbatim in packages/app-shell/src/console/organizations/manage/InviteMemberDialog.tsx:39 (the "share link" view of the invite dialog), so the freshly-created-invitation link is broken the same way.
This is the exact failure mode resolveHomeUrl.ts was written to retire — its header documents it:
${origin}${import.meta.env.BASE_URL}homebroke portable builds (base: './'), producinghttps://host./home— trailing-dot host.
Suggested fix
Drop both local buildAcceptUrl copies and call the existing mount-aware helper:
import { resolveConsoleUrl } from '../resolveHomeUrl';
const url = resolveConsoleUrl(`accept-invitation/${invitationId}`);resolveConsoleUrl resolves against <base href> (the deployment mount), which is what both WorkspaceSwitcher and OrganizationsPage already use for full-page navigations. A regression test asserting the copied URL contains no :<port>. and starts with the console mount would pin it.
Environment
Local dev server http://localhost:8080, multi-org enabled, zh locale, observed 2026-08-12. The server was started by the maintainer and its exact commit is not verified; the source quoted above is the current objectui main checkout.
Activity
baozhoutao commentedon Aug 12, 2026
Found in the same multi-org walkthrough (sign-up → create workspace → invite → second user accepts → second workspace → switch → isolation/authz probes): #8090, #8091, #8092, #8093, #8094.
baozhoutao commentedon Aug 12, 2026
Two further findings from the same pass, filed for the platform to rule on: #8095, #8096.
hotlong commentedon Aug 12, 2026
Moved to objectstack-ai/objectui#4472 (rebuilt verbatim + provenance header; native transfer is not available to this seat). Closing here as moved — authority: the repo-routing rule "Issue 住在修复落地的仓" (maintainer ruling 2026-08-10, recorded in the pm-dispatch skill); both defect sites (
InvitationsPage.tsx,InviteMemberDialog.tsx) are inobjectui, and the body stands with objectstack removed, so this is file-at-destination, not a seam card. Originalpriority:p1grading is recorded in the new card's header. 本评论来自分诊座位 Routine。Generated by Claude Code
fix(pm): check-half-states reads every `Blocked-by:` directive on a l…