Phase 3: review_members replaces tabular_reviews.shared_with JSONB

↗ view on GitHub · Claude · 2026-05-15 · 510d7e86

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.

⬇ Download capture-commit-510d7e86.md