Compare commits

...

2 Commits

Author SHA1 Message Date
Dotta
b04f4ad5d5 test: cover active run comment queue guards 2026-05-01 10:17:54 -05:00
Dotta
a8ea1e07fd Surface live run comment context
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-01 10:09:04 -05:00
7 changed files with 72 additions and 0 deletions

View File

@@ -187,6 +187,8 @@ describe("agent live run routes", () => {
status: "running",
invocationSource: "on_demand",
triggerDetail: "manual",
contextCommentId: "comment-1",
contextWakeCommentId: "comment-1",
startedAt: new Date("2026-04-10T09:30:00.000Z"),
finishedAt: null,
createdAt: new Date("2026-04-10T09:29:59.000Z"),
@@ -224,6 +226,8 @@ describe("agent live run routes", () => {
status: "running",
invocationSource: "on_demand",
triggerDetail: "manual",
contextCommentId: "comment-1",
contextWakeCommentId: "comment-1",
startedAt: "2026-04-10T09:30:00.000Z",
finishedAt: null,
createdAt: "2026-04-10T09:29:59.000Z",

View File

@@ -2853,6 +2853,8 @@ export function agentRoutes(
status: heartbeatRuns.status,
invocationSource: heartbeatRuns.invocationSource,
triggerDetail: heartbeatRuns.triggerDetail,
contextCommentId: sql<string | null>`${heartbeatRuns.contextSnapshot} ->> 'commentId'`.as("contextCommentId"),
contextWakeCommentId: sql<string | null>`${heartbeatRuns.contextSnapshot} ->> 'wakeCommentId'`.as("contextWakeCommentId"),
startedAt: heartbeatRuns.startedAt,
finishedAt: heartbeatRuns.finishedAt,
createdAt: heartbeatRuns.createdAt,
@@ -3089,6 +3091,8 @@ export function agentRoutes(
status: heartbeatRuns.status,
invocationSource: heartbeatRuns.invocationSource,
triggerDetail: heartbeatRuns.triggerDetail,
contextCommentId: sql<string | null>`${heartbeatRuns.contextSnapshot} ->> 'commentId'`.as("contextCommentId"),
contextWakeCommentId: sql<string | null>`${heartbeatRuns.contextSnapshot} ->> 'wakeCommentId'`.as("contextWakeCommentId"),
startedAt: heartbeatRuns.startedAt,
finishedAt: heartbeatRuns.finishedAt,
createdAt: heartbeatRuns.createdAt,

View File

@@ -744,6 +744,8 @@ const heartbeatRunIssueSummaryColumns = {
status: heartbeatRuns.status,
invocationSource: heartbeatRuns.invocationSource,
triggerDetail: heartbeatRuns.triggerDetail,
contextCommentId: sql<string | null>`${heartbeatRuns.contextSnapshot} ->> 'commentId'`.as("contextCommentId"),
contextWakeCommentId: sql<string | null>`${heartbeatRuns.contextSnapshot} ->> 'wakeCommentId'`.as("contextWakeCommentId"),
startedAt: heartbeatRuns.startedAt,
finishedAt: heartbeatRuns.finishedAt,
createdAt: heartbeatRuns.createdAt,

View File

@@ -19,6 +19,8 @@ export interface ActiveRunForIssue {
status: string;
invocationSource: string;
triggerDetail: string | null;
contextCommentId?: string | null;
contextWakeCommentId?: string | null;
startedAt: string | Date | null;
finishedAt: string | Date | null;
createdAt: string | Date;
@@ -41,6 +43,8 @@ export interface LiveRunForIssue {
status: string;
invocationSource: string;
triggerDetail: string | null;
contextCommentId?: string | null;
contextWakeCommentId?: string | null;
startedAt: string | null;
finishedAt: string | null;
createdAt: string;

View File

@@ -726,14 +726,61 @@ describe("optimistic issue comments", () => {
expect(
isQueuedIssueComment({
comment: {
id: "comment-2",
createdAt: new Date("2026-03-28T16:20:05.000Z"),
},
activeRunStartedAt: new Date("2026-03-28T16:20:00.000Z"),
activeRunWakeCommentId: "comment-1",
runId: null,
}),
).toBe(true);
});
it("does not mark the comment that triggered the active run as queued", () => {
expect(
isQueuedIssueComment({
comment: {
id: "comment-1",
createdAt: new Date("2026-03-28T16:20:05.000Z"),
},
activeRunStartedAt: new Date("2026-03-28T16:20:00.000Z"),
activeRunCommentId: "comment-1",
activeRunWakeCommentId: "comment-1",
runId: null,
}),
).toBe(false);
});
it("does not mark the active run context comment as queued", () => {
expect(
isQueuedIssueComment({
comment: {
id: "context-comment",
createdAt: new Date("2026-03-28T16:20:05.000Z"),
},
activeRunStartedAt: new Date("2026-03-28T16:20:00.000Z"),
activeRunCommentId: "context-comment",
activeRunWakeCommentId: "wake-comment",
runId: null,
}),
).toBe(false);
});
it("does not mark the active run wake comment as queued", () => {
expect(
isQueuedIssueComment({
comment: {
id: "wake-comment",
createdAt: new Date("2026-03-28T16:20:05.000Z"),
},
activeRunStartedAt: new Date("2026-03-28T16:20:00.000Z"),
activeRunCommentId: "context-comment",
activeRunWakeCommentId: "wake-comment",
runId: null,
}),
).toBe(false);
});
it("does not mark comments with an associated run as queued", () => {
expect(
isQueuedIssueComment({

View File

@@ -70,15 +70,24 @@ export function createOptimisticIssueComment(params: {
export function isQueuedIssueComment(params: {
comment: Pick<IssueTimelineComment, "createdAt"> &
Partial<Pick<OptimisticIssueComment, "clientStatus">> & {
id?: string;
authorAgentId?: string | null;
};
activeRunStartedAt?: Date | string | null;
activeRunAgentId?: string | null;
activeRunCommentId?: string | null;
activeRunWakeCommentId?: string | null;
runId?: string | null;
interruptedRunId?: string | null;
}) {
if (params.runId) return false;
if (params.interruptedRunId) return false;
if (
params.comment.id &&
(params.comment.id === params.activeRunWakeCommentId || params.comment.id === params.activeRunCommentId)
) {
return false;
}
if (params.comment.authorAgentId && params.activeRunAgentId && params.comment.authorAgentId === params.activeRunAgentId) {
return false;
}

View File

@@ -778,6 +778,8 @@ const IssueDetailChatTab = memo(function IssueDetailChatTab({
comment: nextComment,
activeRunStartedAt,
activeRunAgentId: runningIssueRun?.agentId ?? null,
activeRunCommentId: runningIssueRun?.contextCommentId ?? null,
activeRunWakeCommentId: runningIssueRun?.contextWakeCommentId ?? null,
runId: meta?.runId ?? nextComment.runId ?? null,
interruptedRunId: meta?.interruptedRunId ?? nextComment.interruptedRunId ?? null,
})