fix: route BLOCKED/NEEDS_CONTEXT through arbiter before escalating
BLOCKED and NEEDS_CONTEXT verdicts went straight to completed, bypassing the arbiter entirely. Now when arbiter is enabled, these verdicts trigger arbiter intervention first — the arbiter may resolve the block or decide to escalate to the user (ESCALATE).
This commit is contained in:
@@ -67,9 +67,16 @@ function classifyReviewerVerdict(
|
|||||||
// Arbiter verdict detection
|
// Arbiter verdict detection
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
type ArbiterVerdictResult = 'proceed' | 'revise' | 'reset' | 'escalate' | 'unknown';
|
type ArbiterVerdictResult =
|
||||||
|
| 'proceed'
|
||||||
|
| 'revise'
|
||||||
|
| 'reset'
|
||||||
|
| 'escalate'
|
||||||
|
| 'unknown';
|
||||||
|
|
||||||
function classifyArbiterVerdict(summary: string | null | undefined): ArbiterVerdictResult {
|
function classifyArbiterVerdict(
|
||||||
|
summary: string | null | undefined,
|
||||||
|
): ArbiterVerdictResult {
|
||||||
if (!summary) return 'unknown';
|
if (!summary) return 'unknown';
|
||||||
const cleaned = summary.replace(/<internal>[\s\S]*?<\/internal>/g, '').trim();
|
const cleaned = summary.replace(/<internal>[\s\S]*?<\/internal>/g, '').trim();
|
||||||
if (!cleaned) return 'unknown';
|
if (!cleaned) return 'unknown';
|
||||||
@@ -522,12 +529,25 @@ export function completePairedExecutionContext(args: {
|
|||||||
|
|
||||||
case 'blocked':
|
case 'blocked':
|
||||||
case 'needs_context':
|
case 'needs_context':
|
||||||
// Needs user decision — stop ping-pong, leave task active
|
// If arbiter is enabled, let arbiter judge before escalating to user.
|
||||||
updatePairedTask(taskId, { status: 'completed', updated_at: now });
|
// Arbiter may resolve the block (e.g. owner can fix it, info exists in context).
|
||||||
logger.info(
|
if (isArbiterEnabled()) {
|
||||||
{ taskId, verdict, summary: args.summary?.slice(0, 100) },
|
updatePairedTask(taskId, {
|
||||||
'Reviewer escalated to user — ping-pong stopped',
|
status: 'arbiter_requested',
|
||||||
);
|
arbiter_requested_at: now,
|
||||||
|
updated_at: now,
|
||||||
|
});
|
||||||
|
logger.info(
|
||||||
|
{ taskId, verdict, summary: args.summary?.slice(0, 100) },
|
||||||
|
'Reviewer blocked/needs_context — requesting arbiter before escalating',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
updatePairedTask(taskId, { status: 'completed', updated_at: now });
|
||||||
|
logger.info(
|
||||||
|
{ taskId, verdict, summary: args.summary?.slice(0, 100) },
|
||||||
|
'Reviewer escalated to user — ping-pong stopped',
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'done_with_concerns':
|
case 'done_with_concerns':
|
||||||
@@ -597,10 +617,7 @@ export function completePairedExecutionContext(args: {
|
|||||||
arbiter_verdict: 'escalate',
|
arbiter_verdict: 'escalate',
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
});
|
});
|
||||||
logger.info(
|
logger.info({ taskId }, 'Arbiter escalated to user — task completed');
|
||||||
{ taskId },
|
|
||||||
'Arbiter escalated to user — task completed',
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Unknown verdict — treat as escalate
|
// Unknown verdict — treat as escalate
|
||||||
|
|||||||
Reference in New Issue
Block a user