fix: keep Monitoring consistent in blocker summaries and recents

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-11-02 11:08:15 -06:00
1 parent fa9afcc466
commit d003ea672d
4 files changed
+22 -1

No files matched your search

@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
import { eq, sql } from "drizzle-orm";
import { expect, it } from "vitest";
import request from "supertest";
import { agents, heartbeatRuns, issueRecoveryActions, issues, statusCards } from "@paperclipai/db";
import { agents, heartbeatRuns, issueRecoveryActions, issueRelations, issues, statusCards } from "@paperclipai/db";
import { companySearchQuerySchema, createIssueSchema, PROVIDER_QUOTA_MONITOR_SERVICE_NAME } from "@paperclipai/shared";
import { issueDisplayStatusSql } from "../services/issue-display-status.js";
import { issueService } from "../services/issues.js";
@@ -69,6 +69,17 @@ describeEmbeddedPostgres("Monitoring task presentation", () => {
expect(await svc.count(f.companyId, { status: "in_progress" })).toBe(2);
});
it("shows the same wait indicator in list blocker summaries and task detail relations", async () => {
const f = await fixture();
const [dependent] = await ctx.db.insert(issues).values({ companyId: f.companyId, title: "Ship after deployment", status: "blocked" }).returning();
await ctx.db.insert(issueRelations).values({ companyId: f.companyId, issueId: f.issueId, relatedIssueId: dependent!.id, type: "blocks" });
const svc = issueService(ctx.db);
const list = await svc.list(f.companyId, { includeBlockedBy: true });
expect(list.find(row => row.id === dependent!.id)?.blockedBy).toEqual([expect.objectContaining({ id: f.issueId, displayStatus: "monitoring" })]);
const detail = await request(routeApp(ctx.db, f.actor, issueRoutes)).get(`/api/issues/${dependent!.id}`).expect(200);
expect(detail.body.blockedBy).toEqual([expect.objectContaining({ id: f.issueId, displayStatus: "monitoring" })]);
});
it("returns to In Progress during the check and Monitoring after another yield, then Done", async () => {
const f = await fixture();
const runId = randomUUID();
+1
View File
@@ -5286,6 +5286,7 @@ async function blockedByMapForIssues(
identifier: issues.identifier,
title: issues.title,
status: issues.status,
displayStatus: issueDisplayStatusSql(),
priority: issues.priority,
assigneeAgentId: issues.assigneeAgentId,
assigneeUserId: issues.assigneeUserId,
+8
View File
@@ -102,6 +102,14 @@ describe("recent task persistence", () => {
window.removeEventListener(RECENT_TASKS_UPDATED_EVENT, listener);
});
it("records a new display status without a task edit or newer activity", () => {
const task = { ...issue("1"), status: "in_progress" as const, displayStatus: "monitoring" as const };
recordRecentTask(task, "user-1");
recordRecentTask({ ...task, displayStatus: "in_progress" }, "user-1");
const storageKey = getRecentTasksStorageKey("company-1", "user-1");
expect(readRecentTasks(storageKey, "company-1")[0]?.displayStatus).toBe("in_progress");
});
it("prunes unavailable tasks and refreshes stored snapshots", () => {
recordRecentTask(issue("1"), "user-1", 1);
recordRecentTask(issue("2"), "user-1", 2);
+1
View File
@@ -166,6 +166,7 @@ export function recordRecentTask(
&& existing.title === entry.title
&& existing.identifier === entry.identifier
&& existing.status === entry.status
&& existing.displayStatus === entry.displayStatus
&& existing.externalConversationState === entry.externalConversationState
&& existing.recordedAt === entry.recordedAt
&& existing.snapshotUpdatedAt === entry.snapshotUpdatedAt