fix(agents): clear heartbeat refs before removal

This commit is contained in:
Dotta committed 2026-04-10 11:26:22 -05:00
1 parent 47f767d4a9
commit d82963ce87
2 files changed
+55 -1

No files matched your search

@@ -7,6 +7,7 @@ import {
companies,
companySkills,
createDb,
heartbeatRunEvents,
heartbeatRuns,
issueComments,
issueExecutionDecisions,
@@ -44,6 +45,7 @@ describeEmbeddedPostgres("cleanup removal services", () => {
await db.delete(issueComments);
await db.delete(issueExecutionDecisions);
await db.delete(companySkills);
await db.delete(heartbeatRunEvents);
await db.delete(heartbeatRuns);
await db.delete(issues);
await db.delete(agents);
@@ -99,6 +101,15 @@ describeEmbeddedPostgres("cleanup removal services", () => {
contextSnapshot: { issueId },
});
await db
.update(issues)
.set({
executionRunId: runId,
executionAgentNameKey: "codexcoder",
executionLockedAt: new Date(),
})
.where(eq(issues.id, issueId));
return { agentId, companyId, issueId, runId };
}
@@ -137,13 +148,29 @@ describeEmbeddedPostgres("cleanup removal services", () => {
createdByRunId: runId,
});
await db.insert(heartbeatRunEvents).values({
companyId,
runId,
agentId,
seq: 1,
eventType: "status",
message: "Run finished",
});
const removed = await agentService(db).remove(agentId);
expect(removed?.id).toBe(agentId);
await expect(db.select().from(agents).where(eq(agents.id, agentId))).resolves.toHaveLength(0);
await expect(db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, runId))).resolves.toHaveLength(0);
await expect(db.select().from(heartbeatRunEvents).where(eq(heartbeatRunEvents.runId, runId))).resolves.toHaveLength(0);
await expect(db.select().from(issueComments).where(eq(issueComments.issueId, issueId))).resolves.toHaveLength(0);
await expect(db.select().from(activityLog).where(eq(activityLog.companyId, companyId))).resolves.toHaveLength(0);
await expect(
db
.select({ executionRunId: issues.executionRunId })
.from(issues)
.where(eq(issues.id, issueId)),
).resolves.toEqual([{ executionRunId: null }]);
});
it("removes issue read states and activity rows before deleting the company", async () => {
+28 -1
View File
@@ -477,11 +477,38 @@ export function agentService(db: Db) {
if (!existing) return null;
return db.transaction(async (tx) => {
const agentRunIds = await tx
.select({ id: heartbeatRuns.id })
.from(heartbeatRuns)
.where(eq(heartbeatRuns.agentId, id))
.then((rows) => rows.map((row) => row.id));
await tx.update(agents).set({ reportsTo: null }).where(eq(agents.reportsTo, id));
await tx
.update(issues)
.set({ assigneeAgentId: null, createdByAgentId: null })
.set({
assigneeAgentId: null,
createdByAgentId: null,
...(agentRunIds.length > 0
? {
executionRunId: null,
executionAgentNameKey: null,
executionLockedAt: null,
}
: {}),
})
.where(or(eq(issues.assigneeAgentId, id), eq(issues.createdByAgentId, id)));
if (agentRunIds.length > 0) {
await tx
.update(issues)
.set({
executionRunId: null,
executionAgentNameKey: null,
executionLockedAt: null,
})
.where(inArray(issues.executionRunId, agentRunIds));
await tx.delete(heartbeatRunEvents).where(inArray(heartbeatRunEvents.runId, agentRunIds));
}
await tx.delete(heartbeatRunEvents).where(eq(heartbeatRunEvents.agentId, id));
await tx.delete(agentTaskSessions).where(eq(agentTaskSessions.agentId, id));
await tx.delete(activityLog).where(