diff --git a/src/paired-arbiter-request.ts b/src/paired-arbiter-request.ts new file mode 100644 index 0000000..92741a5 --- /dev/null +++ b/src/paired-arbiter-request.ts @@ -0,0 +1,74 @@ +import { isArbiterEnabled } from './config.js'; +import { logger } from './logger.js'; +import { transitionPairedTaskStatus } from './paired-execution-context-shared.js'; +import type { PairedTaskStatus } from './types.js'; + +export function requestArbiterOrEscalate(args: { + taskId: string; + currentStatus: PairedTaskStatus; + expectedUpdatedAt: string; + now: string; + arbiterLogMessage: string; + escalateLogMessage: string; + logContext?: Record; + patch?: { + title?: string | null; + source_ref?: string | null; + plan_notes?: string | null; + review_requested_at?: string | null; + round_trip_count?: number; + owner_failure_count?: number; + owner_step_done_streak?: number; + finalize_step_done_count?: number; + task_done_then_user_reopen_count?: number; + empty_step_done_streak?: number; + arbiter_verdict?: string | null; + arbiter_requested_at?: string | null; + completion_reason?: string | null; + }; +}): boolean { + const { + taskId, + currentStatus, + expectedUpdatedAt, + now, + arbiterLogMessage, + escalateLogMessage, + logContext, + } = args; + if (isArbiterEnabled()) { + const transitioned = transitionPairedTaskStatus({ + taskId, + currentStatus, + nextStatus: 'arbiter_requested', + expectedUpdatedAt, + updatedAt: now, + patch: { + ...args.patch, + arbiter_requested_at: now, + }, + }); + if (transitioned) { + logger.info(logContext ?? { taskId }, arbiterLogMessage); + } + return transitioned; + } + + const transitioned = transitionPairedTaskStatus({ + taskId, + currentStatus, + nextStatus: 'completed', + expectedUpdatedAt, + updatedAt: now, + patch: { + ...args.patch, + completion_reason: 'escalated', + }, + }); + if (transitioned) { + logger.info(logContext ?? { taskId }, escalateLogMessage); + } + return transitioned; +} + +export type RequestArbiterOrEscalateFn = typeof requestArbiterOrEscalate; diff --git a/src/paired-execution-context-owner.ts b/src/paired-execution-context-owner.ts index 95d3bf6..cb72009 100644 --- a/src/paired-execution-context-owner.ts +++ b/src/paired-execution-context-owner.ts @@ -9,9 +9,9 @@ import { } from './db.js'; import { logger } from './logger.js'; import { markPairedTaskReviewReady } from './paired-workspace-manager.js'; +import { requestArbiterOrEscalate } from './paired-arbiter-request.js'; import { applyPairedTaskPatch, - requestArbiterOrEscalate, transitionPairedTaskStatus, } from './paired-execution-context-shared.js'; import { resolveOwnerCompletionSignal } from './paired-completion-signals.js'; diff --git a/src/paired-execution-context-reviewer.ts b/src/paired-execution-context-reviewer.ts index 693a1ae..934d84d 100644 --- a/src/paired-execution-context-reviewer.ts +++ b/src/paired-execution-context-reviewer.ts @@ -1,10 +1,8 @@ import { ARBITER_DEADLOCK_THRESHOLD } from './config.js'; import { getPairedWorkspace } from './db.js'; import { logger } from './logger.js'; -import { - requestArbiterOrEscalate, - transitionPairedTaskStatus, -} from './paired-execution-context-shared.js'; +import { requestArbiterOrEscalate } from './paired-arbiter-request.js'; +import { transitionPairedTaskStatus } from './paired-execution-context-shared.js'; import { resolveReviewerCompletionSignal, resolveReviewerFailureSignal, diff --git a/src/paired-execution-context-shared.ts b/src/paired-execution-context-shared.ts index 06b9e5c..7b80b35 100644 --- a/src/paired-execution-context-shared.ts +++ b/src/paired-execution-context-shared.ts @@ -1,8 +1,9 @@ -import { isArbiterEnabled } from './config.js'; import { updatePairedTaskIfUnchanged } from './db.js'; import { logger } from './logger.js'; import type { PairedTaskStatus } from './types.js'; +export { requestArbiterOrEscalate } from './paired-arbiter-request.js'; +export type { RequestArbiterOrEscalateFn } from './paired-arbiter-request.js'; export { resolveOwnerCompletionSignal, resolveReviewerCompletionSignal, @@ -152,74 +153,5 @@ export function applyPairedTaskPatch(args: { return updated; } -export function requestArbiterOrEscalate(args: { - taskId: string; - currentStatus: PairedTaskStatus; - expectedUpdatedAt: string; - now: string; - arbiterLogMessage: string; - escalateLogMessage: string; - logContext?: Record; - patch?: { - title?: string | null; - source_ref?: string | null; - plan_notes?: string | null; - review_requested_at?: string | null; - round_trip_count?: number; - owner_failure_count?: number; - owner_step_done_streak?: number; - finalize_step_done_count?: number; - task_done_then_user_reopen_count?: number; - empty_step_done_streak?: number; - arbiter_verdict?: string | null; - arbiter_requested_at?: string | null; - completion_reason?: string | null; - }; -}): boolean { - const { - taskId, - currentStatus, - expectedUpdatedAt, - now, - arbiterLogMessage, - escalateLogMessage, - logContext, - } = args; - if (isArbiterEnabled()) { - const transitioned = transitionPairedTaskStatus({ - taskId, - currentStatus, - nextStatus: 'arbiter_requested', - expectedUpdatedAt, - updatedAt: now, - patch: { - ...args.patch, - arbiter_requested_at: now, - }, - }); - if (transitioned) { - logger.info(logContext ?? { taskId }, arbiterLogMessage); - } - return transitioned; - } - - const transitioned = transitionPairedTaskStatus({ - taskId, - currentStatus, - nextStatus: 'completed', - expectedUpdatedAt, - updatedAt: now, - patch: { - ...args.patch, - completion_reason: 'escalated', - }, - }); - if (transitioned) { - logger.info(logContext ?? { taskId }, escalateLogMessage); - } - return transitioned; -} - export type TransitionPairedTaskStatusFn = typeof transitionPairedTaskStatus; export type ApplyPairedTaskPatchFn = typeof applyPairedTaskPatch; -export type RequestArbiterOrEscalateFn = typeof requestArbiterOrEscalate; diff --git a/src/paired-execution-context.ts b/src/paired-execution-context.ts index 24236ed..5d72135 100644 --- a/src/paired-execution-context.ts +++ b/src/paired-execution-context.ts @@ -48,9 +48,9 @@ import { } from './paired-execution-context-reviewer.js'; import { applyPairedTaskPatch, - requestArbiterOrEscalate, transitionPairedTaskStatus, } from './paired-execution-context-shared.js'; +import { requestArbiterOrEscalate } from './paired-arbiter-request.js'; import { resolveCanonicalSourceRef } from './paired-source-ref.js'; import { isOwnerWorkspaceRepairNeededError,