mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-27 02:44:50 +01:00
fix(routines): preserve coalesced supersession history
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
6e688bde82
commit
1de5798039
2 files changed
+69
-1
No files matched your search
@@ -404,6 +404,73 @@ describeEmbeddedPostgres("routine service live-execution coalescing", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves coalesced run history when the owning execution is superseded", async () => {
|
||||
const { agentId, companyId, issueSvc, routine, svc } = await seedFixture();
|
||||
const optedInRoutine = await svc.update(routine.id, { lifecyclePolicy: "latest_success_wins" }, {});
|
||||
const revisionId = optedInRoutine!.latestRevisionId!;
|
||||
const owner = await createExecutionInstance({
|
||||
companyId,
|
||||
agentId,
|
||||
routine: optedInRoutine!,
|
||||
issueSvc,
|
||||
triggeredAt: new Date("2026-08-23T10:00:00.000Z"),
|
||||
routineRevisionId: revisionId,
|
||||
});
|
||||
const coalescedRunId = randomUUID();
|
||||
const coalescedAt = new Date("2026-08-23T10:15:00.000Z");
|
||||
await db.insert(routineRuns).values({
|
||||
id: coalescedRunId,
|
||||
companyId,
|
||||
routineId: optedInRoutine!.id,
|
||||
routineRevisionId: revisionId,
|
||||
source: "manual",
|
||||
status: "coalesced",
|
||||
triggeredAt: coalescedAt,
|
||||
completedAt: coalescedAt,
|
||||
linkedIssueId: owner.issue.id,
|
||||
coalescedIntoRunId: owner.runId,
|
||||
});
|
||||
const winner = await createExecutionInstance({
|
||||
companyId,
|
||||
agentId,
|
||||
routine: optedInRoutine!,
|
||||
issueSvc,
|
||||
triggeredAt: new Date("2026-08-23T11:00:00.000Z"),
|
||||
routineRevisionId: revisionId,
|
||||
});
|
||||
|
||||
await issueSvc.update(winner.issue.id, { status: "done" });
|
||||
await svc.syncRunStatusForIssue(winner.issue.id);
|
||||
|
||||
const runRows = await db
|
||||
.select()
|
||||
.from(routineRuns)
|
||||
.where(inArray(routineRuns.id, [owner.runId, coalescedRunId]));
|
||||
expect(runRows).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: owner.runId,
|
||||
status: "superseded",
|
||||
supersededByRunId: winner.runId,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: coalescedRunId,
|
||||
status: "coalesced",
|
||||
coalescedIntoRunId: owner.runId,
|
||||
supersededByRunId: null,
|
||||
}),
|
||||
]));
|
||||
const supersededActivities = await db
|
||||
.select()
|
||||
.from(activityLog)
|
||||
.where(eq(activityLog.action, "issue.superseded"));
|
||||
expect(supersededActivities).toEqual([
|
||||
expect.objectContaining({
|
||||
entityId: owner.issue.id,
|
||||
details: expect.objectContaining({ supersededRunId: owner.runId }),
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("preserves an older instance with a live grandchild while superseding instances without live descendants", async () => {
|
||||
const { agentId, companyId, issueSvc, routine, svc } = await seedFixture();
|
||||
const optedInRoutine = await svc.update(routine.id, { lifecyclePolicy: "latest_success_wins" }, {});
|
||||
|
||||
@@ -1650,7 +1650,7 @@ export function routineService(
|
||||
.innerJoin(
|
||||
routineRuns,
|
||||
and(
|
||||
eq(routineRuns.linkedIssueId, issues.id),
|
||||
sql`${routineRuns.id}::text = ${issues.originRunId}`,
|
||||
eq(routineRuns.companyId, issues.companyId),
|
||||
),
|
||||
)
|
||||
@@ -1769,6 +1769,7 @@ export function routineService(
|
||||
eq(routineRuns.companyId, input.companyId),
|
||||
eq(routineRuns.routineId, input.routineId),
|
||||
eq(routineRuns.linkedIssueId, candidate.supersededByIssueId),
|
||||
eq(routineRuns.status, "completed"),
|
||||
),
|
||||
)
|
||||
.then((rows) => rows[0] ?? null);
|
||||
|
||||
Reference in new issue
Block a user