Phase 3: link projects/chats/reviews/workflows to workspaces
Stamps every workspace-scoped resource with its workspace_id and teaches the create paths to populate it, the list paths (projects only for now) to filter by it. Schema (migration 0014_workspace_links.sql): - ALTER ... ADD COLUMN workspace_id uuid REFERENCES workspaces(id) ON DELETE CASCADE on projects, chats, tabular_reviews, workflows. - Backfill UPDATE per table: projects use the owner's Personal workspace; chats and tabular_reviews inherit the parent project's workspace when set, else Personal; workflows go to the author's Personal (or NULL when user_id IS NULL for system workflows). - ALTER COLUMN ... SET NOT NULL on projects, chats, tabular_reviews. workflows stays nullable because system workflows have no owner. - Helper function personal_workspace_id(uuid) for the backfill UPDATE and any future helpers. Marked STABLE. - Indexes on workspace_id for the typical list filter; workflows uses a partial index keyed on the not-null subset. Helpers (lib/workspaces.ts): - getPersonalWorkspaceId, resolveWorkspaceForUser (verifies caller is a member; falls back to Personal when null), and listAccessibleWorkspaceIds for future scope-by-membership queries. Routes: - POST /projects reads optional workspace_id and uses resolveWorkspaceForUser. 404 on non-member. - POST /workflows same shape. - POST /tabular-review prefers the parent project's workspace when project_id is set; otherwise resolveWorkspaceForUser. - POST /chat/create and POST /chat (streaming) likewise inherit when the chat is project-scoped. - POST /projects/:id/chat (projectChat.ts) always inherits the project's workspace; refuses if the project somehow has no workspace. - GET /projects accepts ?workspace_id= and scopes both branches (own + shared-by-email). Unknown ids return [] without error to avoid leaking existence. End-to-end verified against a fresh Postgres 16: - Project without workspace_id stamps Personal. - Project with explicit workspace_id stamps that workspace. - Project with non-member workspace_id returns 404. - GET /projects with no filter returns both; filtered returns the right subset; unknown returns []. - Chat under project inherits the project's workspace; standalone chat goes to Personal. - Workflow respects workspace_id parameter. - Tabular review standalone goes to Personal; under a project inherits the project's workspace. - DB shows every row in projects, chats, tabular_reviews, and workflows has workspace_id populated. Next sub-phase: replace projects.shared_with JSONB with a proper project_members table joined on user_id, and add a frontend workspace switcher.
| Repository | cpatpa/PIP |
|---|---|
| Author | Claude <noreply@anthropic.com> |
| Authored | |
| Parents | 61dda046 |
| Stats | 8 files changed , +400 , -42 |
| Part of | Phase 3 - workspaces (team-scope layer) |
Capture this commit into my fork
Download a Markdown prompt that tells Claude how to port this
exact commit into your working tree. Run it via
claude -p < capture-commit-171d88d4.md
from inside the repo you want the change in.