Files
PaperClipAI/server/src/services/merged-pr-confirmation-work.ts
T
DottaandPaperclip 7edb163a80 fix(server): cancel merge observation HTTP requests on shutdown
Bound GitHub object and repository probes, keep readiness independent of external reads, and page pending cards through the owning parser.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-30 16:49:17 -05:00

39 lines
2.2 KiB
TypeScript

import type { createDeliveryWorkCoordinator } from "./delivery-work-coordinator.js";
import { DELIVERY_QUEUES } from "./delivery-work-notifications.js";
import type { issueThreadInteractionService } from "./issue-thread-interactions.js";
import type { MergeConfirmationScope } from "./merged-pr-confirmation-notifications.js";
import { registerScopedDeadlineWork } from "./scoped-deadline-work.js";
export const MERGE_CONFIRMATION_RECONCILE_MS = 5 * 60_000;
export function registerMergeConfirmationWork(
coordinator: ReturnType<typeof createDeliveryWorkCoordinator>,
service: Pick<ReturnType<typeof issueThreadInteractionService>,
"mergedPullRequestConfirmationCandidates" | "mergedPullRequestConfirmationPage" | "sweepMergedPullRequestConfirmations">,
canRun: () => boolean,
) {
const cursors = new Map<string, string>();
return registerScopedDeadlineWork(coordinator, {
queue: DELIVERY_QUEUES.mergedPullRequestConfirmations,
decodeScope: hint => hint === "*" ? undefined : JSON.parse(hint) as MergeConfirmationScope,
matches: (entry, scope?: MergeConfirmationScope) => !scope || (entry.companyId === scope.companyId &&
(!scope.issueId || entry.issueId === scope.issueId) && (!scope.interactionId || entry.id === scope.interactionId)),
load: async (scope?: MergeConfirmationScope) => {
const key = scope ? JSON.stringify(scope) : "*";
const page = await service.mergedPullRequestConfirmationPage(scope, cursors.get(key));
if (page.complete) cursors.delete(key);
else cursors.set(key, page.after!);
return { complete: page.complete, rows: page.rows.map(({ interaction }) => ({
id: interaction.id, companyId: interaction.companyId, issueId: interaction.issueId, dueAt: Date.now(),
})) };
},
check: async (entry, signal) => {
const scope = { companyId: entry.companyId, issueId: entry.issueId, interactionId: entry.id };
await service.sweepMergedPullRequestConfirmations([], scope, signal);
if (signal?.aborted) return entry;
const remaining = await service.mergedPullRequestConfirmationCandidates(scope);
return remaining.length ? { ...entry, dueAt: Date.now() + MERGE_CONFIRMATION_RECONCILE_MS } : null;
},
}, canRun);
}