mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 01:50:39 +09:00
Fix stale issue live-run state
This commit is contained in:
parent
2172476e84
commit
ab5eeca94e
3 changed files with 65 additions and 2 deletions
45
ui/src/lib/issueActiveRun.test.ts
Normal file
45
ui/src/lib/issueActiveRun.test.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import type { Issue } from "@paperclipai/shared";
|
||||||
|
import type { ActiveRunForIssue } from "../api/heartbeats";
|
||||||
|
import { resolveIssueActiveRun, shouldTrackIssueActiveRun } from "./issueActiveRun";
|
||||||
|
|
||||||
|
describe("issueActiveRun", () => {
|
||||||
|
const makeIssue = (
|
||||||
|
overrides: Partial<Pick<Issue, "status" | "executionRunId">>,
|
||||||
|
): Pick<Issue, "status" | "executionRunId"> => ({
|
||||||
|
status: "todo",
|
||||||
|
executionRunId: null,
|
||||||
|
...overrides,
|
||||||
|
});
|
||||||
|
|
||||||
|
it("tracks active runs while an issue is still in progress", () => {
|
||||||
|
expect(shouldTrackIssueActiveRun(makeIssue({ status: "in_progress" }))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("tracks active runs while an execution run id is still attached", () => {
|
||||||
|
expect(shouldTrackIssueActiveRun(makeIssue({ status: "done", executionRunId: "run-123" }))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("drops stale cached active runs once the issue is closed and unlocked", () => {
|
||||||
|
const staleActiveRun: ActiveRunForIssue = {
|
||||||
|
id: "run-123",
|
||||||
|
status: "running",
|
||||||
|
invocationSource: "assignment",
|
||||||
|
triggerDetail: "system",
|
||||||
|
startedAt: "2026-04-13T01:29:00.000Z",
|
||||||
|
finishedAt: null,
|
||||||
|
createdAt: "2026-04-13T01:29:00.000Z",
|
||||||
|
agentId: "agent-1",
|
||||||
|
agentName: "Builder",
|
||||||
|
adapterType: "codex_local",
|
||||||
|
issueId: "issue-1",
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
resolveIssueActiveRun(
|
||||||
|
makeIssue({ status: "done" }),
|
||||||
|
staleActiveRun,
|
||||||
|
),
|
||||||
|
).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
15
ui/src/lib/issueActiveRun.ts
Normal file
15
ui/src/lib/issueActiveRun.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import type { Issue } from "@paperclipai/shared";
|
||||||
|
import type { ActiveRunForIssue } from "../api/heartbeats";
|
||||||
|
|
||||||
|
export function shouldTrackIssueActiveRun(
|
||||||
|
issue: Pick<Issue, "status" | "executionRunId"> | null | undefined,
|
||||||
|
): boolean {
|
||||||
|
return Boolean(issue && (issue.status === "in_progress" || issue.executionRunId));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveIssueActiveRun(
|
||||||
|
issue: Pick<Issue, "status" | "executionRunId"> | null | undefined,
|
||||||
|
activeRun: ActiveRunForIssue | null | undefined,
|
||||||
|
): ActiveRunForIssue | null {
|
||||||
|
return shouldTrackIssueActiveRun(issue) ? (activeRun ?? null) : null;
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ import {
|
||||||
readIssueDetailHeaderSeed,
|
readIssueDetailHeaderSeed,
|
||||||
rememberIssueDetailLocationState,
|
rememberIssueDetailLocationState,
|
||||||
} from "../lib/issueDetailBreadcrumb";
|
} from "../lib/issueDetailBreadcrumb";
|
||||||
|
import { resolveIssueActiveRun, shouldTrackIssueActiveRun } from "../lib/issueActiveRun";
|
||||||
import {
|
import {
|
||||||
hasBlockingShortcutDialog,
|
hasBlockingShortcutDialog,
|
||||||
resolveIssueDetailGoKeyAction,
|
resolveIssueDetailGoKeyAction,
|
||||||
|
|
@ -471,13 +472,15 @@ export function IssueDetail() {
|
||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: activeRun, isLoading: activeRunLoading } = useQuery({
|
const shouldPollActiveRun = shouldTrackIssueActiveRun(issue);
|
||||||
|
const { data: rawActiveRun, isLoading: activeRunLoading } = useQuery({
|
||||||
queryKey: queryKeys.issues.activeRun(issueId!),
|
queryKey: queryKeys.issues.activeRun(issueId!),
|
||||||
queryFn: () => heartbeatsApi.activeRunForIssue(issueId!),
|
queryFn: () => heartbeatsApi.activeRunForIssue(issueId!),
|
||||||
enabled: !!issueId && (!!issue?.executionRunId || issue?.status === "in_progress"),
|
enabled: !!issueId && shouldPollActiveRun,
|
||||||
refetchInterval: (liveRuns?.length ?? 0) > 0 ? false : 3000,
|
refetchInterval: (liveRuns?.length ?? 0) > 0 ? false : 3000,
|
||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
});
|
});
|
||||||
|
const activeRun = resolveIssueActiveRun(issue, rawActiveRun);
|
||||||
|
|
||||||
const hasLiveRuns = (liveRuns ?? []).length > 0 || !!activeRun;
|
const hasLiveRuns = (liveRuns ?? []).length > 0 || !!activeRun;
|
||||||
const runningIssueRun = useMemo(
|
const runningIssueRun = useMemo(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue