Tighten runtime feedback and interaction catchup

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-05-19 16:30:47 -05:00
1 parent 133457c2f4
commit 686e06c8ea
3 files changed
+30 -7

No files matched your search

@@ -15,4 +15,10 @@ describe("feedback export flush error classification", () => {
it("does not classify ordinary feedback upload failures as database outages", () => {
expect(isDatabaseConnectionUnavailableError(new Error("upstream returned 500"))).toBe(false);
});
it("does not trust unrelated error messages that mention ECONNREFUSED", () => {
expect(isDatabaseConnectionUnavailableError(
new Error("feedback upload payload mentioned ECONNREFUSED in user content"),
)).toBe(false);
});
});
-2
View File
@@ -86,8 +86,6 @@ const VITE_DEV_STATIC_PATHS = new Set([
export function isDatabaseConnectionUnavailableError(err: unknown): boolean {
const error = err as { code?: unknown; message?: unknown; cause?: unknown };
if (error?.code === "ECONNREFUSED") return true;
const message = typeof error?.message === "string" ? error.message : "";
if (message.includes("ECONNREFUSED")) return true;
return Boolean(error?.cause && isDatabaseConnectionUnavailableError(error.cause));
}
@@ -1068,6 +1068,13 @@ export function issueThreadInteractionService(db: Db) {
const now = new Date();
const expired: IssueThreadInteraction[] = [];
const supersededByComment = new Map<
string,
{
comment: (typeof comments)[number];
rowIds: string[];
}
>();
for (const row of rows) {
const interaction = hydrateInteraction(row) as RequestConfirmationInteraction;
if (!shouldSupersedeRequestConfirmationOnUserComment(interaction)) continue;
@@ -1078,26 +1085,38 @@ export function issueThreadInteractionService(db: Db) {
}));
if (!supersedingComment) continue;
const [updated] = await db
const group = supersededByComment.get(supersedingComment.id);
if (group) {
group.rowIds.push(row.id);
} else {
supersededByComment.set(supersedingComment.id, {
comment: supersedingComment,
rowIds: [row.id],
});
}
}
for (const { comment, rowIds } of supersededByComment.values()) {
const updatedRows = await db
.update(issueThreadInteractions)
.set({
status: "expired",
result: {
version: 1,
outcome: "superseded_by_comment",
commentId: supersedingComment.id,
commentId: comment.id,
},
resolvedByAgentId: null,
resolvedByUserId: supersedingComment.authorUserId,
resolvedByUserId: comment.authorUserId,
resolvedAt: now,
updatedAt: now,
})
.where(and(
eq(issueThreadInteractions.id, row.id),
inArray(issueThreadInteractions.id, rowIds),
eq(issueThreadInteractions.status, "pending"),
))
.returning();
if (updated) expired.push(hydrateInteraction(updated));
expired.push(...updatedRows.map(hydrateInteraction));
}
if (expired.length > 0) {