mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-27 02:44:50 +01:00
fix: inherit run scope for scheduled retries
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
67044f91a6
commit
75ac8b4dbb
3 files changed
+38
-2
No files matched your search
@@ -142,10 +142,21 @@ describeEmbeddedPostgres("activity service", () => {
|
||||
permissions: {},
|
||||
});
|
||||
|
||||
await db.insert(issues).values({
|
||||
id: issueId,
|
||||
companyId,
|
||||
title: "Summarize a completed run",
|
||||
status: "done",
|
||||
priority: "medium",
|
||||
assigneeAgentId: agentId,
|
||||
});
|
||||
|
||||
await db.insert(heartbeatRuns).values({
|
||||
id: runId,
|
||||
companyId,
|
||||
agentId,
|
||||
scopeKind: "issue",
|
||||
issueId,
|
||||
invocationSource: "assignment",
|
||||
status: "succeeded",
|
||||
contextSnapshot: { issueId },
|
||||
@@ -256,6 +267,8 @@ describeEmbeddedPostgres("activity service", () => {
|
||||
id: runId,
|
||||
companyId,
|
||||
agentId,
|
||||
scopeKind: "issue",
|
||||
issueId,
|
||||
invocationSource: "assignment",
|
||||
status: "succeeded",
|
||||
startedAt: new Date("2026-04-18T20:00:00.000Z"),
|
||||
@@ -350,6 +363,8 @@ describeEmbeddedPostgres("activity service", () => {
|
||||
id: runId,
|
||||
companyId,
|
||||
agentId,
|
||||
scopeKind: "issue",
|
||||
issueId,
|
||||
invocationSource: "assignment",
|
||||
status: "succeeded",
|
||||
startedAt: new Date("2026-04-18T20:00:00.000Z"),
|
||||
@@ -365,6 +380,8 @@ describeEmbeddedPostgres("activity service", () => {
|
||||
id: otherRunId,
|
||||
companyId,
|
||||
agentId,
|
||||
scopeKind: "issue",
|
||||
issueId,
|
||||
invocationSource: "assignment",
|
||||
status: "succeeded",
|
||||
startedAt: new Date("2026-04-18T20:05:00.000Z"),
|
||||
@@ -472,6 +489,8 @@ describeEmbeddedPostgres("activity service", () => {
|
||||
id: runId,
|
||||
companyId,
|
||||
agentId,
|
||||
scopeKind: "issue",
|
||||
issueId,
|
||||
invocationSource: "assignment",
|
||||
status: "succeeded",
|
||||
startedAt: new Date("2026-04-18T20:10:00.000Z"),
|
||||
|
||||
@@ -380,6 +380,11 @@ describeEmbeddedPostgres("heartbeat bounded retry scheduling", () => {
|
||||
identifier: `${issuePrefix}-1`,
|
||||
});
|
||||
|
||||
// Complete the circular issue/run fixture after both FK targets exist.
|
||||
await db.update(heartbeatRuns)
|
||||
.set({ scopeKind: "issue", issueId })
|
||||
.where(eq(heartbeatRuns.id, runId));
|
||||
|
||||
return { companyId, agentId, issueId, runId, now };
|
||||
}
|
||||
|
||||
@@ -1755,6 +1760,9 @@ describeEmbeddedPostgres("heartbeat bounded retry scheduling", () => {
|
||||
issueNumber: 1,
|
||||
identifier: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}-2`,
|
||||
});
|
||||
await db.update(heartbeatRuns)
|
||||
.set({ scopeKind: "issue", issueId })
|
||||
.where(eq(heartbeatRuns.id, sourceRunId));
|
||||
|
||||
const scheduled = await heartbeat.scheduleBoundedRetry(sourceRunId, {
|
||||
now,
|
||||
@@ -1856,6 +1864,9 @@ describeEmbeddedPostgres("heartbeat bounded retry scheduling", () => {
|
||||
issueNumber: 1,
|
||||
identifier: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}-3`,
|
||||
});
|
||||
await db.update(heartbeatRuns)
|
||||
.set({ scopeKind: "issue", issueId })
|
||||
.where(eq(heartbeatRuns.id, sourceRunId));
|
||||
|
||||
const scheduled = await heartbeat.scheduleBoundedRetry(sourceRunId, {
|
||||
now,
|
||||
@@ -1966,6 +1977,9 @@ describeEmbeddedPostgres("heartbeat bounded retry scheduling", () => {
|
||||
issueNumber: 1,
|
||||
identifier: `T${companyId.replace(/-/g, "").slice(0, 6).toUpperCase()}-3`,
|
||||
});
|
||||
await db.update(heartbeatRuns)
|
||||
.set({ scopeKind: "issue", issueId })
|
||||
.where(eq(heartbeatRuns.id, sourceRunId));
|
||||
|
||||
const scheduled = await heartbeat.scheduleBoundedRetry(sourceRunId, {
|
||||
now,
|
||||
|
||||
@@ -10511,7 +10511,10 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
: null;
|
||||
const transientRetryNotBefore = transientRecovery?.retryNotBefore ?? null;
|
||||
const contextSnapshot = parseObject(run.contextSnapshot);
|
||||
const issueId = readNonEmptyString(contextSnapshot.issueId);
|
||||
// A retry inherits the durable authorization scope of its source run. Do
|
||||
// not promote an untrusted or legacy contextSnapshot.issueId into a new
|
||||
// issue binding: that could either violate the FK or misclassify history.
|
||||
const issueId = run.scopeKind === "issue" ? run.issueId : null;
|
||||
|
||||
if (!baseSchedule) {
|
||||
await appendRunEvent(run, await nextRunEventSeq(run.id), {
|
||||
@@ -10901,7 +10904,7 @@ export function heartbeatService(db: Db, options: HeartbeatServiceOptions = {})
|
||||
.values({
|
||||
companyId: run.companyId,
|
||||
agentId: run.agentId,
|
||||
scopeKind: issueId ? "issue" : "company",
|
||||
scopeKind: run.scopeKind,
|
||||
issueId,
|
||||
invocationSource: "automation",
|
||||
triggerDetail: "system",
|
||||
|
||||
Reference in new issue
Block a user