fix(ralph-loop): harden oracle verification flow

Capture oracle verification sessions more reliably and accept parent-session VERIFIED evidence so ULW loops do not retry after successful review.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-03-18 17:45:59 +09:00
parent 23c0ff60f2
commit ce8957e1e1
6 changed files with 95 additions and 14 deletions
@@ -25,6 +25,8 @@ You already emitted <promise>{{INITIAL_PROMISE}}</promise>. This does NOT finish
REQUIRED NOW:
- Call Oracle using task(subagent_type="oracle", load_skills=[], run_in_background=false, ...)
- Ask Oracle to verify whether the original task is actually complete
- Include the original task in the Oracle request
- Explicitly tell Oracle to review skeptically and critically, and to look for reasons the task may still be incomplete or wrong
- The system will inspect the Oracle session directly for the verification result
- If Oracle does not verify, continue fixing the task and do not consider it complete
@@ -40,6 +42,7 @@ REQUIRED NOW:
- Oracle does not lie. Treat the verification result as ground truth
- Do not claim completion early or argue with the failed verification
- After fixing the remaining issues, request Oracle review again using task(subagent_type="oracle", load_skills=[], run_in_background=false, ...)
- Include the original task in the Oracle request and tell Oracle to review skeptically and critically
- Only when the work is ready for review again, output: <promise>{{PROMISE}}</promise>
Original task:
@@ -87,7 +87,7 @@ export function createRalphLoopEventHandler(
return
}
const completionSessionID = verificationSessionID ?? (state.verification_pending ? undefined : sessionID)
const completionSessionID = verificationSessionID ?? sessionID
const transcriptPath = completionSessionID ? options.getTranscriptPath(completionSessionID) : undefined
const completionViaTranscript = completionSessionID
? detectCompletionInTranscript(
@@ -107,7 +107,13 @@ export function createRalphLoopEventHandler(
sinceMessageIndex: undefined,
})
: state.verification_pending
? false
? await detectCompletionInSessionMessages(ctx, {
sessionID,
promise: state.completion_promise,
apiTimeoutMs: options.apiTimeoutMs,
directory: options.directory,
sinceMessageIndex: state.message_count_at_start,
})
: await detectCompletionInSessionMessages(ctx, {
sessionID,
promise: state.completion_promise,
@@ -366,7 +366,7 @@ describe("ulw-loop verification", () => {
expect(toastCalls.some((toast) => toast.title === "ULTRAWORK LOOP COMPLETE!")).toBe(false)
})
test("#given parent session emits VERIFIED #when oracle session is not tracked #then ulw loop continues instead of completing", async () => {
test("#given parent session emits VERIFIED #when oracle session is not tracked #then ulw loop completes from parent session evidence", async () => {
const hook = createRalphLoopHook(createMockPluginInput(), {
getTranscriptPath: (sessionID) => sessionID === "ses-oracle" ? oracleTranscriptPath : parentTranscriptPath,
})
@@ -379,17 +379,13 @@ describe("ulw-loop verification", () => {
await hook.event({ event: { type: "session.idle", properties: { sessionID: "session-123" } } })
writeFileSync(
parentTranscriptPath,
`${JSON.stringify({ type: "tool_result", timestamp: new Date().toISOString(), tool_output: { output: "done <promise>DONE</promise>" } })}\n${JSON.stringify({ type: "tool_result", timestamp: new Date().toISOString(), tool_output: { output: `bad parent leak <promise>${ULTRAWORK_VERIFICATION_PROMISE}</promise>` } })}\n`,
`${JSON.stringify({ type: "tool_result", timestamp: new Date().toISOString(), tool_output: { output: "done <promise>DONE</promise>" } })}\n${JSON.stringify({ type: "tool_result", timestamp: new Date().toISOString(), tool_output: { output: `verified <promise>${ULTRAWORK_VERIFICATION_PROMISE}</promise>` } })}\n`,
)
await hook.event({ event: { type: "session.idle", properties: { sessionID: "session-123" } } })
expect(hook.getState()).not.toBeNull()
expect(hook.getState()?.iteration).toBe(2)
expect(hook.getState()?.completion_promise).toBe("DONE")
expect(hook.getState()?.verification_pending).toBeUndefined()
expect(promptCalls).toHaveLength(2)
expect(promptCalls[1]?.text).toContain("Verification failed")
expect(hook.getState()).toBeNull()
expect(toastCalls.some((toast) => toast.title === "ULTRAWORK LOOP COMPLETE!")).toBe(true)
})
test("#given oracle verification fails #when loop restarts #then old oracle session is aborted", async () => {
+14 -3
View File
@@ -6,6 +6,17 @@ import { readState, writeState } from "../hooks/ralph-loop/storage"
const VERIFICATION_ATTEMPT_PATTERN = /<ulw_verification_attempt_id>(.*?)<\/ulw_verification_attempt_id>/i
function getMetadataString(metadata: Record<string, unknown> | undefined, keys: string[]): string | undefined {
for (const key of keys) {
const value = metadata?.[key]
if (typeof value === "string") {
return value
}
}
return undefined
}
function getPluginDirectory(ctx: PluginContext): string | null {
if (typeof ctx === "object" && ctx !== null && "directory" in ctx && typeof ctx.directory === "string") {
return ctx.directory
@@ -43,9 +54,9 @@ export function createToolExecuteAfterHandler(args: {
if (input.tool === "task") {
const directory = getPluginDirectory(ctx)
const sessionId = typeof output.metadata?.sessionId === "string" ? output.metadata.sessionId : undefined
const agent = typeof output.metadata?.agent === "string" ? output.metadata.agent : undefined
const prompt = typeof output.metadata?.prompt === "string" ? output.metadata.prompt : undefined
const sessionId = getMetadataString(output.metadata, ["sessionId", "sessionID", "session_id"])
const agent = getMetadataString(output.metadata, ["agent"])
const prompt = getMetadataString(output.metadata, ["prompt"])
const verificationAttemptId = prompt?.match(VERIFICATION_ATTEMPT_PATTERN)?.[1]?.trim()
const loopState = directory ? readState(directory) : null
const isVerificationContext =
+25 -1
View File
@@ -20,6 +20,26 @@ export function createToolExecuteBeforeHandler(args: {
) => Promise<void> {
const { ctx, hooks } = args
function buildUltraworkOracleVerificationPrompt(prompt: string, originalTask: string, verificationAttemptId: string): string {
const verificationPrompt = [
"You are verifying the active ULTRAWORK loop result for this session.",
"",
"Original task:",
originalTask,
"",
"Review the work skeptically and critically.",
"Assume it may be incomplete, misleading, or subtly broken until the evidence proves otherwise.",
"Look for missing scope, weak verification, process violations, hidden regressions, and any reason the task should NOT be considered complete.",
"",
`If the work is fully complete, end your response with <promise>${ULTRAWORK_VERIFICATION_PROMISE}</promise>.`,
"If the work is not complete, explain the blocking issues clearly and DO NOT emit that promise.",
"",
`<ulw_verification_attempt_id>${verificationAttemptId}</ulw_verification_attempt_id>`,
].join("\n")
return `${prompt ? `${prompt}\n\n` : ""}${verificationPrompt}`
}
return async (input, output): Promise<void> => {
await hooks.writeExistingFileGuard?.["tool.execute.before"]?.(input, output)
await hooks.questionLabelTruncator?.["tool.execute.before"]?.(input, output)
@@ -91,7 +111,11 @@ export function createToolExecuteBeforeHandler(args: {
verification_session_id: undefined,
})
argsObject.run_in_background = false
argsObject.prompt = `${prompt ? `${prompt}\n\n` : ""}You are verifying the active ULTRAWORK loop result for this session. Review whether the original task is truly complete: ${loopState.prompt}\n\nIf the work is fully complete, end your response with <promise>${ULTRAWORK_VERIFICATION_PROMISE}</promise>. If the work is not complete, explain the blocking issues clearly and DO NOT emit that promise.\n\n<ulw_verification_attempt_id>${verificationAttemptId}</ulw_verification_attempt_id>`
argsObject.prompt = buildUltraworkOracleVerificationPrompt(
prompt,
loopState.prompt,
verificationAttemptId,
)
}
}
@@ -65,7 +65,9 @@ describe("tool.execute.before ultrawork oracle verification", () => {
expect(readState(directory)?.verification_attempt_id).toBeTruthy()
expect(output.args.run_in_background).toBe(false)
expect(output.args.prompt).toContain("Original task:")
expect(output.args.prompt).toContain("Ship feature")
expect(output.args.prompt).toContain("Review the work skeptically and critically")
expect(output.args.prompt).toContain(`<promise>${ULTRAWORK_VERIFICATION_PROMISE}</promise>`)
clearState(directory)
@@ -171,6 +173,45 @@ describe("tool.execute.before ultrawork oracle verification", () => {
rmSync(directory, { recursive: true, force: true })
})
test("#given ulw loop is awaiting verification #when oracle metadata uses sessionID #then oracle session id is stored", async () => {
const directory = join(tmpdir(), `tool-after-ulw-sessionid-${Date.now()}`)
mkdirSync(directory, { recursive: true })
writeState(directory, {
active: true,
iteration: 3,
completion_promise: ULTRAWORK_VERIFICATION_PROMISE,
initial_completion_promise: "DONE",
started_at: new Date().toISOString(),
prompt: "Ship feature",
session_id: "ses-main",
ultrawork: true,
verification_pending: true,
})
const handler = createToolExecuteAfterHandler({
ctx: createCtx(directory) as unknown as Parameters<typeof createToolExecuteAfterHandler>[0]["ctx"],
hooks: {} as Parameters<typeof createToolExecuteAfterHandler>[0]["hooks"],
})
await handler(
{ tool: "task", sessionID: "ses-main", callID: "call-1" },
{
title: "oracle task",
output: "done",
metadata: {
agent: "oracle",
sessionID: "ses-oracle-alt",
sync: true,
},
},
)
expect(readState(directory)?.verification_session_id).toBe("ses-oracle-alt")
clearState(directory)
rmSync(directory, { recursive: true, force: true })
})
test("#given newer oracle attempt exists #when older oracle task finishes #then old session does not overwrite active verification", async () => {
const directory = join(tmpdir(), `tool-race-ulw-${Date.now()}`)
mkdirSync(directory, { recursive: true })