Workspace slug url refactor
Skill kjuhwa/skills-hub/skills/architecture/workspace-slug-url-refactor
Self-correcting knowledge corpus for Claude Code — 9 stable shape clusters, bias-correction pipeline baked into contribution flow. 47 papers, 45 techniques, 1.1k skills.
npx -y skills add kjuhwa/skills-hub --skill workspace-slug-url-refactorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Migrate a multi-tenant SPA from header-based workspace identity to URL-based (/{slug}/...) to fix shareable links, mobile switching, multi-tab leakage, and mutation side-effect races.
SKILL.md
3.5 KB, as published. Nobody here has run it
When to use
- Your app's workspace identity is carried by
X-Workspace-IDheader + Zustand store + localStorage. - You see: shareable URLs opening wrong workspace, mobile has no switcher, multi-tab tabs clobber each other, mutation
onSuccessraces. - Product is pre-launch or you're willing to break existing bookmarks.
Steps
- Audit what the data layer already supports:
- DB has
workspace.slug TEXT UNIQUE NOT NULL? Good. - Backend has
GetWorkspaceBySlug? Good. - TS
Workspacetype hasslug? Good. - If any of these are missing, land them first (separate PRs).
- DB has
- Reorganize routes to carry the slug:
- Next.js web:
apps/web/app/(dashboard)/*→apps/web/app/(dashboard)/[workspaceSlug]/*(or/(dashboard)/ws/[slug]/if you want a prefix). - Desktop react-router: add
/:workspaceSlugprefix to every session route.
- Next.js web:
- Write a
NavigationAdapterin each app'splatform/layer that auto-prepends the current slug topush(path), so shared views calluseNavigation().push('/issues')and the adapter resolves to/my-team/issues. - Replace all hardcoded path strings with a
paths.*builder:export const paths = { issues: () => `/issues`, issue: (id: string) => `/issues/${id}`, settings: () => `/settings`, // ... }; - Delete the imperative
switchWorkspace/hydrateWorkspaceactions. Switching becomes pure navigation:
This single change eliminates a family of mutation-// Before <button onClick={() => { push('/issues'); switchWorkspace(ws); }}> // After <Link href={paths.issues({ slug: ws.slug })}>onSuccessrace bugs (create-workspace flash, delete-workspace-no-nav, accept-invite-no-switch). - Delete the global
multica_workspace_idlocalStorage key. Per-workspace-scoped persist stores switch to a workspace-aware storage adapter (${key}:${slug}). - Add a server-side slug-first resolver with UUID fallback so CLI/daemon don't break:
1. X-Workspace-Slug header → GetWorkspaceBySlug → UUID 2. ?workspace_slug query → same 3. X-Workspace-ID header (legacy / CLI compat) 4. ?workspace_id query (legacy / CLI compat) - Ship all of these in one PR — intermediate states don't run because the URL shape is an atomic change.
- Update E2E tests to assert on URLs with slugs. Internal markdown links (
[foo](/issues/abc)) must auto-prepend the current slug when dispatched.
Example
Before: /issues/abc123 (wrong workspace when shared).
After: /my-team/issues/abc123 (unambiguous, shareable, bookmarkable, multi-tab safe).
Caveats
- Existing bookmarks break. Acceptable if pre-launch; otherwise add
/issues/:id→ lookup-by-id-and-redirect path for a grace period. - Reserved-slug list must be complete and enforced server-side before launch; see the
reserved-slugs-coordinated-listskill. - Email invite links (
/invite/:id) and auth callback URLs remain workspace-agnostic — they're pre-workspace flows.