Files
multica/server/migrations/047_audit_extended_reserved_slugs.up.sql
Naiyuan Qing 6d6bc5a6f2 fix(routing): rename /new-workspace to /workspaces/new + extend reserved slug list (#1188)
* fix(routing): rename /new-workspace to /workspaces/new + extend reserved slug list

Two related changes:

1. Rename the global workspace-creation route from /new-workspace to
   /workspaces/new. The hyphenated word-group `new-workspace` is a
   common user workspace name (last deploy was blocked by a real user
   with exactly this slug). Industry consensus from auditing Linear,
   Vercel, Notion, Slack, GitHub: zero major SaaS uses hyphenated
   word-group root routes — they all use single words or `/{noun}/{verb}`
   pairs. Reserving the noun `workspaces` automatically protects the
   entire `/workspaces/*` subtree, so future workspace-related routes
   (`/workspaces/{id}/edit`, `/workspaces/{id}/billing`, etc.) need no
   additional reserved slugs or audit migrations.

2. Extend the reserved slug list to cover the minimal set recommended by
   the URL-design audit: full auth flow vocab, RFC 2142 mailbox names
   (postmaster, abuse, noreply...), hostname confusables (mail, ftp,
   static, cdn...), and likely-future platform routes (docs, support,
   status, legal, privacy, terms, security, etc.). Production data
   audit confirmed zero conflicts for every newly added slug, so
   migration 047 (the safety net) passes cleanly.

Slugs intentionally NOT added despite being in scope of the audit:
admin, multica, new, setup, www. Each has one production workspace
already using it; adding them now would block deploy. They will be
handled in a follow-up PR via owner outreach + targeted rename.

Also adds a CLAUDE.md convention rule: new global routes MUST use a
single word or `/{noun}/{verb}` pair, never hyphenated word groups.
This prevents the pattern from regenerating itself.

This PR does NOT resolve the currently-blocked prd deploy — that requires
the existing `slug='new-workspace'` workspace (owner: Dhruv Raina) to be
renamed by ops. After that workspace is renamed and migration 046 passes,
this PR's migration 047 will also pass on its first run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* review: drop migration 046, sweep stale comments, drive reserved test from map

Address code review on PR #1188:

1. Delete migration 046 (audit_new_workspace_slug). It audits "new-workspace"
   which is no longer a reserved slug after this PR's rename. Removing 046
   has an unexpected upside: it directly unblocks the currently-stuck prd
   deploy. Migration 046 had never successfully applied (it was the source
   of the deploy block); the audit-only nature means down-rollback is a
   no-op. The user workspace previously caught by 046 (slug='new-workspace',
   owner: Dhruv Raina) is now safe — `new-workspace` is no longer reserved,
   so the slug correctly resolves to that workspace and the global route
   `/workspaces/new` doesn't shadow it.

2. Refactor workspace_test.go to drive its reserved-slug list from the
   reservedSlugs map directly via `for slug := range reservedSlugs`. The
   previous hand-copied list was already drifting (40-ish entries vs 58 in
   the map). Now drift is impossible.

3. Sweep ~10 stale `/new-workspace` references in code comments to
   `/workspaces/new`. Comments only — runtime unchanged. The references
   in reserved-slugs.ts/workspace_reserved_slugs.go and CLAUDE.md are
   intentionally kept as anti-pattern examples ("don't add hyphenated
   word-group root routes like /new-workspace").

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:21:20 +08:00

66 lines
2.8 KiB
SQL

-- Audit existing workspace slugs against the extended reserved-slug list.
--
-- This PR (1) renames the global workspace-creation route from /new-workspace
-- to /workspaces/new, which moves the reserved name from "new-workspace" to
-- "workspaces", and (2) expands the reserved slug list to cover the broader
-- set recommended by the URL-design audit (auth flow words, RFC 2142 mailbox
-- names, hostname confusables, common platform routes).
--
-- Migration 046 was REMOVED in the same PR. It was auditing "new-workspace"
-- which is no longer reserved, so it had become dead code AND was actively
-- blocking prd deploy on a real-user workspace that no longer needs renaming
-- (the workspace is now safe under the new route — `new-workspace` slug
-- resolves to its workspace, no longer shadowed by the global route which
-- moved to /workspaces/new). Removing 046 is forward-only safe: 046 had
-- never successfully applied in prd (it was the source of the deploy
-- block), and the audit-only nature means down-rollback is a no-op.
--
-- The data audit was performed before this migration was written and confirmed
-- ZERO conflicts for every slug listed below in production. This migration
-- exists as a safety net: if a workspace with one of these slugs slips into
-- prod between audit and deploy, the migration will fail loudly rather than
-- silently shadowing the workspace behind a system route.
--
-- Slugs INTENTIONALLY OMITTED from this audit despite being in the reserved
-- list: 'admin', 'multica', 'new', 'setup', 'www'. These already have one
-- conflicting workspace each in production. They will be handled in a
-- follow-up PR (rename via owner outreach + targeted migration), not blocked
-- on this deploy.
--
-- Keep this slug list aligned with:
-- - server/internal/handler/workspace_reserved_slugs.go
-- - packages/core/paths/reserved-slugs.ts
DO $$
DECLARE
conflict_count INT;
conflict_list TEXT;
BEGIN
SELECT
COUNT(*),
string_agg(slug, ', ' ORDER BY slug)
INTO conflict_count, conflict_list
FROM workspace
WHERE slug IN (
-- Auth flow (newly added)
'signin', 'signout', 'oauth', 'callback', 'verify', 'reset', 'password',
-- Platform routes (newly added)
'docs', 'support', 'status', 'legal', 'privacy', 'terms', 'security',
'contact', 'blog', 'careers', 'press', 'download',
-- Workspace/team segments (newly added — replaces 'new-workspace')
'workspaces', 'teams',
-- RFC 2142 mailboxes (newly added)
'postmaster', 'abuse', 'noreply', 'webmaster', 'hostmaster',
-- Hostname confusables (newly added)
'mail', 'ftp', 'static', 'cdn', 'assets', 'public', 'files', 'uploads'
);
IF conflict_count > 0 THEN
RAISE EXCEPTION 'Found % workspace(s) with slugs that collide with extended reserved list: %. Rename or delete before deploying.', conflict_count, conflict_list;
END IF;
END $$;