A timeline of the workspace migration, slice by slice. Use this as the canonical record of "why is the repo shaped this way?" — every awkward seam has a reason.
7588932)#pnpm-workspace.yaml declaring apps/* and packages/* as members.turbo.json with build, lint, type:check, dev pipelines.packages/auth with the email allowlist (isAllowedEmail,
pickAllowedEmail, ALLOWED_EMAIL_DOMAINS).lib/auth/email-allowlist.ts pointing to
@matters/auth/email-allowlist, so all six existing consumers continued
working without code changes.apps/workspace as an empty but deployable Next.js skeleton: own
proxy.ts (Clerk middleware + allowlist), own next.config.ts, placeholder
page, login route.Why this shape? A path-of-least-resistance carve-out: the workspace app exists and deploys, but doesn't actually serve the real workspace routes yet. Lets us prove the deploy pipeline before moving production code.
30716fd)#packages/lib with errors/exceptions, errors/logger, config/env,
config/sentry, utils/cn. Re-export shims at every original lib/ path
so root consumers don't need touching.packages/ui scaffold (empty for now — Radix primitives still
in root components/ui/).packages/config with shared tsconfig.base.json and
tsconfig.next.json./workspace/* and /auth/* redirects in root
next.config.ts, controlled by WORKSPACE_SUBDOMAIN_URL.Why re-export shims? They keep the diff to slice 2 itself small (only
the source files inside packages/). Future code can import directly from
@matters/lib while existing code keeps working — the shim is a one-line
re-export that can be deleted later when every caller has migrated.
8250bb3 + Phase C 7812d94)#The big move. ~210 files relocated via git mv (history preserved):
app/workspace/** → apps/workspace/app/(workspace)/**app/auth/** → apps/workspace/app/auth/**app/api/{workspace,admin,settings,user,whitepapers/admin,careers/settings} →
apps/workspace/app/api/**Supporting changes:
apps/workspace/tsconfig.json — added path bridge so @/lib, @/components,
@/hooks still resolve to repo root. Zero source-file import rewrites.apps/workspace/next.config.ts — outputFileTracingRoot + transpilePackages.apps/workspace/tailwind.config.ts re-exports root config and extends
content to scan both repo root and workspace files.proxy.ts — protected-route matcher cleared.lib/downloads-config.ts — extracted the WordPress-reading logic that was
previously imported across app boundaries from lib/downloads-cache.ts and
apps/workspace/app/api/settings/downloads/route.ts. Both call the shared
helper now; no cross-app route-handler imports remain in production code.Why the tsconfig path bridge? Forcing the moved workspace files to use
@matters/lib/... and @matters/ui/... would have meant rewriting hundreds
of imports inside the carve commit, ballooning the diff and making review
impossible. The path bridge lets the imports stay @/lib/... etc., and the
bridge resolves to repo root until each module graduates into a shared
package — at which point the corresponding alias entry gets dropped.
3dd76d5 → b60eb50)#After the structural carve, a hardening pass:
3dd76d5 — Closed 2 critical RBAC gaps:
/api/workspace/webinars/event-logs (was unprotected; added
checkRole("org:webinars:manage"))./api/workspace/sales/export (accepted any MongoDB collection name from
the request body — now enum-bound via Zod, full body validated, error
responses don't leak error.message).12a25d4 — Stripped /workspace/... prefix from every internal link
inside the workspace app (login redirect, 26 page hrefs, lib/workspace-nav.ts).
On the subdomain, the dashboard is mounted at /, not /workspace.3d39b3b — Added lib/workspace-url.ts so public-site cross-app links
go through an env-aware helper instead of hard-coded /workspace paths.6b3a09e — Added apps/webdocs Next.js docs app (this site) with seed
content covering overview, getting-started, architecture, monorepo,
workspace, RBAC, env, deployment, security.b60eb50 — Added PROD_CHECKLIST.md at repo root: the single
source-of-truth deployment checklist with tickable items for code, infra,
CI, smoke tests, and post-launch polish.6729662 — Added lib/security/api-guards.ts (enforceRateLimit
auditMutation helpers) and wired them into the 5 highest-risk routes
(admin/users PUT, sales/export, whitepapers/leads/export, bulk-invite,
sync-salesforce).ccb39d3 — Dead-code cleanup: removed Require2FA, removed unreachable
Clerk auth-branch from root proxy.ts, hardened the
createApiHandler({ requireAuth: true }) placeholder into an explicit
error pointing to the canonical Clerk + RBAC pattern.apps/web/ rename — root currently doubles as the public website AND the
monorepo root. Cleaner end state would be moving it into apps/web/, but
the migration cost (~200 mechanical file moves) outweighs the immediate
value. Track in PROD_CHECKLIST.md § 6.components/ui/ → packages/ui/src/ extraction.lib/services, lib/db, lib/schemas → packages/lib/src/ extraction.MIGRATION.md at the repo root — the operator-facing
version of this history.PROD_CHECKLIST.md at the repo root — the launch
checklist.