Cancel stale queued heartbeats when issue graph changes (PAP-2314) (#4534)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Dotta
2026-04-26 21:17:38 -05:00
committed by GitHub
parent 868d08903e
commit 82e257c7ba
21 changed files with 1991 additions and 238 deletions
@@ -4,7 +4,9 @@ import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest
import {
activityLog,
agents,
budgetPolicies,
companies,
costEvents,
createDb,
executionWorkspaces,
heartbeatRuns,
@@ -191,7 +193,7 @@ describeEmbeddedPostgres("heartbeat issue graph liveness escalation", () => {
type: "blocks",
});
return { companyId, managerId, blockedIssueId, blockerIssueId };
return { companyId, managerId, coderId, blockedIssueId, blockerIssueId };
}
it("keeps liveness findings advisory when auto recovery is disabled", async () => {
@@ -342,6 +344,71 @@ describeEmbeddedPostgres("heartbeat issue graph liveness escalation", () => {
expect(events.some((event) => event.action === "issue.blockers.updated")).toBe(true);
});
it("skips budget-blocked direct owners and assigns recovery to the manager fallback", async () => {
await enableAutoRecovery();
const { companyId, managerId, coderId, blockedIssueId, blockerIssueId } = await seedBlockedChain();
const issueTimestamp = new Date(Date.now() - 25 * 60 * 60 * 1000);
await db
.update(issues)
.set({
status: "in_review",
assigneeAgentId: coderId,
updatedAt: issueTimestamp,
})
.where(eq(issues.id, blockerIssueId));
await db.insert(budgetPolicies).values({
companyId,
scopeType: "agent",
scopeId: coderId,
metric: "billed_cents",
windowKind: "calendar_month_utc",
amount: 1,
hardStopEnabled: true,
isActive: true,
});
await db.insert(costEvents).values({
companyId,
agentId: coderId,
issueId: blockerIssueId,
provider: "test",
biller: "test",
billingType: "tokens",
model: "test-model",
costCents: 1,
occurredAt: new Date(),
});
const result = await heartbeatService(db).reconcileIssueGraphLiveness();
expect(result.escalationsCreated).toBe(1);
const escalations = await db
.select()
.from(issues)
.where(and(eq(issues.companyId, companyId), eq(issues.originKind, "harness_liveness_escalation")));
expect(escalations).toHaveLength(1);
expect(escalations[0]).toMatchObject({
parentId: blockerIssueId,
assigneeAgentId: managerId,
originId: [
"harness_liveness",
companyId,
blockedIssueId,
"in_review_without_action_path",
blockerIssueId,
].join(":"),
});
const events = await db.select().from(activityLog).where(eq(activityLog.companyId, companyId));
const createdEvent = events.find((event) => event.action === "issue.harness_liveness_escalation_created");
expect(createdEvent?.details).toMatchObject({
ownerSelection: {
selectedAgentId: managerId,
selectedReason: "assignee_reporting_chain",
budgetBlockedCandidateAgentIds: [coderId],
},
});
});
it("parents recovery under the leaf blocker without inheriting dependent or blocker execution state for manager-owned recovery", async () => {
await enableAutoRecovery();
await instanceSettingsService(db).updateExperimental({ enableIsolatedWorkspaces: true });