fix(ralph-loop): skip handleFailedVerification when oracle dispatch is in flight (#4256)

When verification_pending is true and the agent has dispatched an Oracle
verification (verification_attempt_id is set), session.idle events that
arrive before tool-execute-after stores the Oracle session ID
(verification_session_id still undefined) caused handlePendingVerification
to fall through to handleFailedVerification. This injected a duplicate
'verification failed' continuation prompt, spawning a second Oracle.

The fix adds a guard in handlePendingVerification: when
verification_attempt_id is set but verification_session_id is not, Oracle
dispatch is in flight and the handler returns early instead of declaring
failure. The pending wake will retry on the next session.idle.

Regression test added in given/when/then style proving the race sequence:
  1. ULW loop detects DONE, enters verification_pending
  2. Oracle dispatch stamps verification_attempt_id (tool-execute-before)
  3. Second session.idle fires before tool-execute-after stores session ID
  4. Handler must NOT call handleFailedVerification

RED (before fix): 2 prompt injections (duplicate Oracle)
GREEN (after fix): 1 prompt injection (correct)

Fixes #4256
Fixes #4019
This commit is contained in:
YeonGyu-Kim
2026-05-24 15:00:26 +09:00
parent 3523dab0b1
commit 9a1dd75608
2 changed files with 148 additions and 0 deletions
@@ -137,6 +137,15 @@ export async function handlePendingVerification(
}
}
if (state.verification_attempt_id && !state.verification_session_id) {
log(`[${HOOK_NAME}] Skipped verification failure: oracle dispatch in flight`, {
sessionID,
verificationAttemptId: state.verification_attempt_id,
iteration: state.iteration,
})
return
}
const restarted = await handleFailedVerification(ctx, {
state,
loopState,