Matter access-contacts read — who to ask for access
Story-2677 refuses an unstaffed firm editor entry to a matter's detail page
(403 pramaan.matter.not_on_team), and the app shows a
not-authorized modal. But that editor had no way to learn who could
grant access: the roster read is admin-or-staffed only, and the matter list
item carries no lead. This story adds one minimal read that names exactly the
people who can help — the matter's lead counsel and the firm's active
admins — and nothing else.
The New Endpoint
GET /app/matters/{matter_id}/access-contacts
{
"lead": { "userId": "...", "name": "...", "email": "..." } | null,
"admins": [ { "userId": "...", "name": "...", "email": "..." }, ... ]
}
lead is the current lead counsel, or null in the
accepted no-lead runtime state. admins is every active firm
admin of the matter's firm. The payload carries only
userId, name, and email for those
people — no matter content, no full roster, no team-role or
membership-status internals.
Authorization — Firm Isolation Only
The read is gated on firm isolation alone: the matter must belong to the caller's firm. It deliberately does not reuse the admin-or-staffed roster gate or the detail-entry gate, so the very editor that Story-2677 refused entry is allowed to read the access contacts — that is the whole point. Firm admins and staffed users pass as a superset of "same firm". A matter outside the caller's firm is simply not found.
| Caller | Result |
|---|---|
| Unstaffed same-firm firm editor | 200 — lead + admins (the entry-gate 403 does not apply here) |
| Firm admin, staffed editor, staffed member | 200 — superset of "same firm" |
| Matter with no lead assigned | 200 — lead: null, admins still named |
| Caller in a different firm | 404 — no cross-firm existence leak |
What Changed
matter_access_contacts(matter_id):
matter = load matter (RLS + firm predicate) # not in firm -> 404
lead = lead-counsel assignment -> user # null in no-lead state
admins = active firm_admin memberships -> users # one firm-scoped join
return { lead, admins } # names/emails only
The lead comes from the same lead-counsel assignment the roster disclosure uses. Admins are resolved in one firm-scoped join over active firm memberships with the admin capability — no per-admin lookup. The read emits no domain events and writes nothing.
Deliberate Minimal Disclosure
This is an approved product decision: it intentionally reveals the matter's lead identity and firm admins to same-firm editors so a blocked editor knows whom to ask for access. The disclosure is bounded to names and emails of the lead and admins; nothing about the matter's substance crosses the boundary.