Address operator QoL review feedback

This commit is contained in:
Dotta
2026-06-05 03:40:46 +00:00
parent fb13e1f6a9
commit af0c43b205
3 changed files with 54 additions and 7 deletions
@@ -845,6 +845,12 @@ describeEmbeddedPostgres("issueThreadInteractionService", () => {
userId: "local-board",
});
expect(created).toMatchObject({
payload: {
supersedeOnUserComment: true,
},
});
const expired = await interactionsSvc.expireRequestConfirmationsSupersededByComment({
id: issueId,
companyId,
@@ -903,6 +909,40 @@ describeEmbeddedPostgres("issueThreadInteractionService", () => {
expect(rows[0]?.status).toBe("pending");
});
it("keeps legacy request confirmations pending when comment supersede was not stored", async () => {
const { companyId, issueId } = await seedConfirmationIssue("Legacy confirmation without comment supersede flag");
await db.insert(issueThreadInteractions).values({
id: randomUUID(),
companyId,
issueId,
kind: "request_confirmation",
status: "pending",
continuationPolicy: { kind: "none" },
payload: {
version: 1,
prompt: "Proceed with the current draft?",
},
createdByUserId: "local-board",
});
const expired = await interactionsSvc.expireRequestConfirmationsSupersededByComment({
id: issueId,
companyId,
}, {
id: randomUUID(),
createdAt: new Date(Date.now() + 1_000),
authorUserId: "local-board",
}, {
userId: "local-board",
});
expect(expired).toHaveLength(0);
const rows = await db.select().from(issueThreadInteractions);
expect(rows).toHaveLength(1);
expect(rows[0]?.status).toBe("pending");
});
it("does not supersede request confirmations for agent, system, or older user comments", async () => {
const { companyId, issueId } = await seedConfirmationIssue("Comment supersede exclusions");
@@ -159,7 +159,18 @@ function shouldReturnAcceptedConfirmationToCreatorAgent(args: {
}
function shouldSupersedeRequestConfirmationOnUserComment(interaction: RequestConfirmationInteraction) {
return interaction.payload.supersedeOnUserComment !== false;
return interaction.payload.supersedeOnUserComment === true;
}
function normalizeCreateInteractionInput(input: CreateIssueThreadInteraction): CreateIssueThreadInteraction {
if (input.kind !== "request_confirmation") return input;
return {
...input,
payload: {
...input.payload,
supersedeOnUserComment: input.payload.supersedeOnUserComment ?? true,
},
};
}
function isCommentAtOrAfterInteraction(args: {
@@ -673,7 +684,7 @@ export function issueThreadInteractionService(db: Db) {
input: CreateIssueThreadInteraction,
actor: InteractionActor,
) => {
const data = createIssueThreadInteractionSchema.parse(input);
const data = normalizeCreateInteractionInput(createIssueThreadInteractionSchema.parse(input));
if (data.idempotencyKey) {
const existing = await getIdempotentInteraction({
+1 -5
View File
@@ -1,6 +1,6 @@
// @vitest-environment jsdom
import type { AnchorHTMLAttributes, ReactNode } from "react";
import { act, type AnchorHTMLAttributes, type ReactNode } from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { Issue, RoutineListItem } from "@paperclipai/shared";
@@ -237,10 +237,6 @@ vi.mock("../components/AgentIconPicker", () => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
async function act(callback: () => void | Promise<void>) {
await callback();
}
function createRoutine(overrides: Partial<RoutineListItem>): RoutineListItem {
return {
id: "routine-1",