Story-1391: Active Support Grants Contract
This walkthrough documents the producer-side contract change for
GET /admin/support_grants/active. The endpoint now gives
the admin support-access monitor enough server-side data to render and
filter the cross-firm active-grant table without joining through the
firms list.
What Changed
-
ActiveGrantListItemnow includesfirmRef, derived from the joined firm'sfirmNumberthrough the canonical formatter. -
ActiveGrantListRequestnow acceptsfirmId,staffUserId, andqquery parameters. - Filters are applied before cursor pagination, so filtered admin views are not limited to whatever happened to be on the first page.
-
qsearches firm name, staff display name, and staff email case-insensitively.
Contract Shape
The active grant row now has the firm link data inline:
{
"id": "...",
"staffUserId": "...",
"staffDisplayName": "Support Engineer",
"firmId": "...",
"firmRef": "FIRM-00042",
"firmName": "Example LLP",
"firmNumber": 42,
"grantedAt": "2026-06-18T00:00:00Z",
"expiresAt": "2026-06-25T00:00:00Z",
"reason": "Customer support investigation",
"grantedByUserId": "..."
}
Verification
-
Route coverage validates the full
ActiveGrantListResponseDTO round-trip. -
A real admin request against a zero-row active-list state still
returns
{"items": [], "nextCursor": null}. -
Route coverage proves
firmId,staffUserId, andqfilters. -
Route coverage seeds more than 200 active rows for one firm and
proves the second page is reachable with the existing
beforecursor. -
openapi/admin-api.jsonwas regenerated for the producer contract.
Consumer Impact
pramaan-admin must regenerate src/gen/ from
the new admin OpenAPI snapshot, consume firmRef directly
from listActiveGrants, and remove the separate
/admin/firms join currently used only to build firm
detail links.