Firm detail moves to generated hooks without touching grant work
This execution follows the latest verified issue comment: only the conflict-free firm-detail read and non-grant mutations move to the BFF plus generated-hook architecture. Grant create/revoke, support-access, and disabled lifecycle stubs remain deferred.
What changed
The firm detail page now prefetches the generated useAdminGetFirmByRef query key server-side, delegates the query function to firmDetailGateway.getByRef, and hydrates the client with dehydrate plus HydrationBoundary.
/api/internal/firms/by-ref/[firmRef] validates generated params, calls firmDetailGateway, forwards x-pramaan-* headers, and keeps the service key server-side through src/lib/gateway.ts.
CSM reassign, seat adjust, plan change, and support-roster add/remove now run through generated mutation hooks. There is no onMutate or cache flip; success invalidates/refetches the firm detail and firms list queries.
The CSM, roster, and provisioning dialogs keep pending controls disabled and render returned form or field errors persistently, preserving entered values for retry.
Deferred work
Grant create/revoke stays on the existing server action because the governing comment ties it to EPIC-026 #1393 and #1397. The broader support-access architecture migration stays deferred until #1396-#1399 settle. Suspend/archive remain disabled stubs and are not wired here.
The current generated contract exposes plan and seat writes as useAdminChangePlan and useAdminAdjustSeats. This implementation uses those generated hooks rather than inventing unsupported fields on useAdminUpdateFirmMetadata or regenerating src/gen.
Verification proof
Static migration coverage pins the hydration pattern and asserts the migrated mutation hooks have no onMutate or setQueryData. Existing contract tests now target firmDetailGateway for read, activity, grants-list folding, and subscription mutation parsing.
Observability remains on the pramaan-docs ADR-104 bridge: every migrated route flows through /api/internal/firms/* into src/lib/gateway.ts; no raw tracer APIs, new OTel SDK, or extra middleware were added.
Local gate: pnpm run lint, pnpm exec tsc --noEmit, pnpm test, NEXT_PUBLIC_ENV=local pnpm build, pnpm build-storybook, and git diff --check.