Description
Annotates the four interactive `prisma.$transaction` call sites in
`apps/apollo/src/` that were still relying on the Postgres default
isolation level (effectively ReadCommitted, but implicit).
This rebuilds the work from #9628 (auto-closed by the stale-PR bot on
2026-05-12 without merging) and folds in a new transaction introduced
since then by PR #9842 (`fix: cascade soft-delete consumer resources on
org deletion`).
| File | Old | New | Why |
|---|
| `lib/org/inviteCode/joinOrgWInviteCode.ts:223` | implicit | Serializable + `withSerializableRetry` | Enforces "one owned clanker per (user, org)" across multiple unclaimed clankers. The inner CAS (`claimIfUnownedTx`) only protects the row it targets; two parallel claims of different unclaimed clankers by the same user both commit at ReadCommitted. Only wraps the retry when this code opens its own tx — outer txns keep their isolation. |
| `lib/toolRouterV2/features/session/dbUtils/patchToolRouterSession.ts:50` | implicit | ReadCommitted | Application-level OCC via `configVersion` CAS in the `updateMany` WHERE — the DB enforces the version check, callers retry on `count === 0`. Serializable would only add spurious P2034 conflicts on hot sessions. |
| `lib/consumer/dbUtils/connected_account_tool_permissions.ts:232` | wrapped in `withSerializableRetry` but no isolation level — dead retry | ReadCommitted + drop the no-op wrapper | Pure batched upsert loop on the `(connectedAccountId, toolSlug)` unique constraint. Each upsert is `INSERT ... ON CONFLICT DO UPDATE` (row-level atomic at any isolation level), the tx is only for batch atomicity. The previous `withSerializableRetry` was dead code because P2034 only fires at Serializable. |
| `lib/org/dbUtils/deleteOrg.ts:207` (new) | implicit | ReadCommitted | Soft-deletes the org row and cascades to `userOrgMapping` rows. Both writes are idempotent (admin re-asserted in `where`; `deletedAt: null` guard makes a second delete a no-op) and neither depends on a value read inside the tx — batch atomicity only. |
How did I test this PR
- Scoped vitest: `pnpm with-env vitest run src/lib/org/dbUtils/deleteOrg.unit.test.ts src/lib/toolRouterV2/features/session/dbUtils/getToolRouterSession.test.ts` — 9/9 passed.
- `pnpm check-types` (tsgo) — clean.
- `pnpm lint` — 0 errors, 1 pre-existing warning in unrelated `composio_actions/utils/schemas.ts:1178`.
No behavior change at runtime: the four transactions previously ran at the Postgres default (effectively ReadCommitted). The clanker-claim site is the one true behavior change — it now correctly serializes the "one owned clanker per (user, org)" invariant under concurrent claims of distinct clankers, with bounded retry on P2034. The other three sites are pinned to ReadCommitted explicitly so future reviewers don't have to re-derive whether Serializable is required.
Triggered by: rahul.lingala@composio.dev | Source: cron-9fe6365ec672
Session: https://zen-api-production-4c98.up.railway.app/dashboard/#/chat/zen-cron-e0ef4da21cbf
Origin: cron-9fe6365ec672 / zen-cron-e0ef4da21cbf