Stabilize onboarding e2e cleanup paths

This commit is contained in:
dotta 2026-04-07 18:20:35 -05:00
parent cb705c9856
commit b0b85e6ba3
4 changed files with 215 additions and 16 deletions

View file

@ -1,5 +1,5 @@
import { createHash, randomBytes } from "node:crypto";
import { and, desc, eq, gte, inArray, lt, ne, sql } from "drizzle-orm";
import { and, desc, eq, gte, inArray, lt, ne, or, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import {
agents,
@ -8,9 +8,13 @@ import {
agentRuntimeState,
agentTaskSessions,
agentWakeupRequests,
activityLog,
costEvents,
heartbeatRunEvents,
heartbeatRuns,
issueExecutionDecisions,
issues,
issueComments,
} from "@paperclipai/db";
import { isUuidLike, normalizeAgentUrlKey } from "@paperclipai/shared";
import { conflict, notFound, unprocessable } from "../errors.js";
@ -474,8 +478,20 @@ export function agentService(db: Db) {
return db.transaction(async (tx) => {
await tx.update(agents).set({ reportsTo: null }).where(eq(agents.reportsTo, id));
await tx
.update(issues)
.set({ assigneeAgentId: null, createdByAgentId: null })
.where(or(eq(issues.assigneeAgentId, id), eq(issues.createdByAgentId, id)));
await tx.delete(heartbeatRunEvents).where(eq(heartbeatRunEvents.agentId, id));
await tx.delete(agentTaskSessions).where(eq(agentTaskSessions.agentId, id));
await tx.delete(activityLog).where(
or(
eq(activityLog.agentId, id),
sql`${activityLog.runId} in (select ${heartbeatRuns.id} from ${heartbeatRuns} where ${heartbeatRuns.agentId} = ${id})`,
),
);
await tx.delete(issueExecutionDecisions).where(eq(issueExecutionDecisions.actorAgentId, id));
await tx.delete(issueComments).where(eq(issueComments.authorAgentId, id));
await tx.delete(heartbeatRuns).where(eq(heartbeatRuns.agentId, id));
await tx.delete(agentWakeupRequests).where(eq(agentWakeupRequests.agentId, id));
await tx.delete(agentApiKeys).where(eq(agentApiKeys.agentId, id));

View file

@ -17,6 +17,7 @@ import {
heartbeatRunEvents,
costEvents,
financeEvents,
issueReadStates,
approvalComments,
approvals,
activityLog,
@ -25,6 +26,7 @@ import {
invites,
principalPermissionGrants,
companyMemberships,
companySkills,
} from "@paperclipai/db";
import { notFound, unprocessable } from "../errors.js";
@ -260,6 +262,7 @@ export function companyService(db: Db) {
// Delete from child tables in dependency order
await tx.delete(heartbeatRunEvents).where(eq(heartbeatRunEvents.companyId, id));
await tx.delete(agentTaskSessions).where(eq(agentTaskSessions.companyId, id));
await tx.delete(activityLog).where(eq(activityLog.companyId, id));
await tx.delete(heartbeatRuns).where(eq(heartbeatRuns.companyId, id));
await tx.delete(agentWakeupRequests).where(eq(agentWakeupRequests.companyId, id));
await tx.delete(agentApiKeys).where(eq(agentApiKeys.companyId, id));
@ -274,13 +277,14 @@ export function companyService(db: Db) {
await tx.delete(invites).where(eq(invites.companyId, id));
await tx.delete(principalPermissionGrants).where(eq(principalPermissionGrants.companyId, id));
await tx.delete(companyMemberships).where(eq(companyMemberships.companyId, id));
await tx.delete(companySkills).where(eq(companySkills.companyId, id));
await tx.delete(issueReadStates).where(eq(issueReadStates.companyId, id));
await tx.delete(issues).where(eq(issues.companyId, id));
await tx.delete(companyLogos).where(eq(companyLogos.companyId, id));
await tx.delete(assets).where(eq(assets.companyId, id));
await tx.delete(goals).where(eq(goals.companyId, id));
await tx.delete(projects).where(eq(projects.companyId, id));
await tx.delete(agents).where(eq(agents.companyId, id));
await tx.delete(activityLog).where(eq(activityLog.companyId, id));
const rows = await tx
.delete(companies)
.where(eq(companies.id, id))