Story-1481 EPIC-030 first migration pramaan-admin

Firms list/create now uses hydrated generated hooks.

The firms list keeps the same dense admin UI, but its data path now matches the generated TanStack Query template that staff and support access will copy next.

What changed

Hydrated list

firms/page.tsx creates a server query client, spreads adminListFirmsQueryOptions for the canonical key, overrides the query function with firmsGateway.list, then renders HydrationBoundary.

Generated client hook

FirmsClient reads the same params through useAdminListFirms. The URL-driven nuqs table state remains the source for filters, sort, and pagination.

Server-confirmed create

The create sheet provisions Clerk through a server action, submits the generated FirmCreateRequest through useAdminCreateFirm, and invalidates only after success.

Why no optimistic mutation

Firm lifecycle writes are privileged and audited. The admin operator sees server-confirmed state only: no onMutate, no speculative queryClient.setQueryData, and no form reset on rejection.

Template gaps closed

Diagnostics headers

The firms BFF now forwards x-pramaan-* upstream identity headers so browser diagnostics can merge the live backend identity.

Shared helpers

resolveRequestTraceId, validation errors, and header forwarding live in src/app/api/internal/_helpers.ts for #1482 and #1483 to reuse.

Invalidation tests

src/lib/query/invalidation.test.ts proves exact-key invalidation and firms collection invalidation by generated key head.

Retired path

listFirms no longer lives in the "use server" action file. The firms-list Next fetch tag was removed; firm detail tags are untouched for the later firm-detail migration.