Phase 3: review_members replaces tabular_reviews.shared_with JSONB
Mirrors the project_members migration from the previous commit.
Standalone tabular reviews now share via the new review_members
table joined on user_id; the legacy
tabular_reviews.shared_with JSONB column stays as a rollback
safety net but is no longer read or written by application code.
Schema (migration 0016_review_members.sql):
- review_members(id, review_id FK, user_id FK,
role CHECK in ('member','editor'), invited_by FK, joined_at,
UNIQUE (review_id, user_id)).
- Indexes on review_id and user_id. RLS forced; anon/authenticated
grants revoked.
- Backfill DO block walks every tabular_reviews.shared_with JSONB
list, matches emails to active users, INSERTs review_members
rows, skips owner-matches, reports unmatched count via NOTICE.
Helpers (lib/reviewMembers.ts) match projectMembers:
- REVIEW_SELECT_COLUMNS: canonical SELECT including a computed
shared_with array derived from review_members + users so the
response shape stays stable.
- listReviewMembers: user_id, email, display_name, role.
- setReviewMembers: transactional replace with matched/unmatched
email reporting; skips owner; deletes-then-upserts.
Access helpers (lib/access.ts):
- ensureReviewAccess signature now requires review.id so it can
look up review_members directly. Falls back to
checkProjectAccess for project-scoped reviews. The legacy
shared_with field on the review object is accepted but ignored.
Routes (routes/tabular.ts):
- GET / direct-share branch -> JOIN review_members.
- GET, GET /:id, PATCH all use REVIEW_SELECT_COLUMNS so the
response carries a derived shared_with array.
- PATCH routes the supplied shared_with through setReviewMembers
instead of writing the legacy JSONB.
- /people returns owner + listReviewMembers with display_name and
role.
End-to-end verified against Postgres 16 across 8 scenarios:
- Standalone review starts with shared_with=[].
- PATCH shared_with with valid + unknown email -> unknown silently
dropped; only valid in review_members.
- Direct member can GET the review and sees it in their list.
- /people returns owner + members with display_name + role.
- Non-owner PATCH shared_with -> 403.
- Empty shared_with revokes access; ex-member 404s.
| Repository | cpatpa/PIP |
|---|---|
| Author | Claude <noreply@anthropic.com> |
| Authored | |
| Parents | 6e9bf945 |
| Stats | 5 files changed , +310 , -85 |
| Part of | Phase 3 - granular sharing (project_members, review_members) |
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-510d7e86.md
from inside the repo you want the change in.