mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-14 13:49:18 +02:00
* fix(desktop): new tab inherits current workspace + guard against malformed tab paths
Three layered fixes for the same root cause: tab URLs were being
constructed without a workspace slug in some code paths, triggering
NoAccessPage whenever the router interpreted the first segment as a
(non-existent) workspace slug.
## Layer 1 — tab-bar "+" button now inherits current workspace
The handler had a hardcoded `path = "/issues"` left over from before
the slug URL refactor. Without a workspace prefix, the router saw
`workspaceSlug = "issues"` and rendered NoAccessPage. Read
`getCurrentSlug()` and build `/{slug}/issues` instead. Falls back to
"/" (→ IndexRedirect) when there is no current workspace.
This matches terminal/IDE new-tab semantics: new tab opens in the
same workspace as the active tab, not in `wsList[0]`.
## Layer 2 — validateWorkspaceSlugs runs synchronously
PR #1178 added startup validation of persisted tab slugs against the
current workspace list, but ran it in a useEffect. useEffect fires
AFTER commit, so the initial render would briefly show NoAccessPage
on a stale slug before the effect reset the tab path. Moving the call
into render phase eliminates that flash; zustand supports setState in
render, and the validator is idempotent (early-returns if nothing
changed) so this doesn't loop.
## Layer 3 — tab store rejects malformed paths at construction
Any path whose first segment is a reserved slug (e.g. "/issues",
"/login") clearly lacks a workspace prefix and is a caller bug.
sanitizeTabPath catches these at makeTab time, rewrites to "/", and
logs a console.warn naming the offending path so the bug can be fixed
at source. Any future new-tab entry point that forgets the slug will
not reach NoAccessPage.
Net effect: NoAccessPage is reserved for its legitimate purpose —
users navigating to URLs they genuinely don't have access to — and
can no longer be triggered by system bugs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* review: read new-tab workspace from active tab + unify sanitize + add tests
Three follow-ups from self-review of PR #1198:
1. Resolve the current workspace from the active tab's path instead of
from getCurrentSlug(). With N tabs mounted under <Activity>, every
WorkspaceRouteLayout calls setCurrentWorkspace() in render — the
singleton ends up holding "whichever tab rendered last", which is
non-deterministic. activeTabId is the unambiguous source of truth
for "which workspace is the user actually looking at right now".
2. Unify the persist merge's stale-path detection with sanitizeTabPath.
The merge previously checked ROUTE_ICONS (dashboard segments only);
sanitizeTabPath uses isReservedSlug (dashboard + auth + platform +
RFC 2142 + hostname confusables). Same code path now, wider
coverage, and one source of truth.
3. Add unit tests for sanitizeTabPath: root pass-through, global paths,
valid workspace-scoped paths, malformed paths (reserved first
segment) rejected with console.warn, and user slugs that happen to
look path-like but aren't reserved.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>