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: REQUIRED NOW:
- Call Oracle using task(subagent_type="oracle", load_skills=[], run_in_background=false, ...) - Call Oracle using task(subagent_type="oracle", load_skills=[], run_in_background=false, ...)
- Ask Oracle to verify whether the original task is actually complete - 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 - 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 - 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 - Oracle does not lie. Treat the verification result as ground truth
- Do not claim completion early or argue with the failed verification - 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, ...) - 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> - Only when the work is ready for review again, output: <promise>{{PROMISE}}</promise>
Original task: Original task:
@@ -87,7 +87,7 @@ export function createRalphLoopEventHandler(
return return
} }
const completionSessionID = verificationSessionID ?? (state.verification_pending ? undefined : sessionID) const completionSessionID = verificationSessionID ?? sessionID
const transcriptPath = completionSessionID ? options.getTranscriptPath(completionSessionID) : undefined const transcriptPath = completionSessionID ? options.getTranscriptPath(completionSessionID) : undefined
const completionViaTranscript = completionSessionID const completionViaTranscript = completionSessionID
? detectCompletionInTranscript( ? detectCompletionInTranscript(
@@ -107,7 +107,13 @@ export function createRalphLoopEventHandler(
sinceMessageIndex: undefined, sinceMessageIndex: undefined,
}) })
: state.verification_pending : 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, { : await detectCompletionInSessionMessages(ctx, {
sessionID, sessionID,
promise: state.completion_promise, promise: state.completion_promise,
@@ -366,7 +366,7 @@ describe("ulw-loop verification", () => {
expect(toastCalls.some((toast) => toast.title === "ULTRAWORK LOOP COMPLETE!")).toBe(false) 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(), { const hook = createRalphLoopHook(createMockPluginInput(), {
getTranscriptPath: (sessionID) => sessionID === "ses-oracle" ? oracleTranscriptPath : parentTranscriptPath, 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" } } }) await hook.event({ event: { type: "session.idle", properties: { sessionID: "session-123" } } })
writeFileSync( writeFileSync(
parentTranscriptPath, 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" } } }) await hook.event({ event: { type: "session.idle", properties: { sessionID: "session-123" } } })
expect(hook.getState()).not.toBeNull() expect(hook.getState()).toBeNull()
expect(hook.getState()?.iteration).toBe(2) expect(toastCalls.some((toast) => toast.title === "ULTRAWORK LOOP COMPLETE!")).toBe(true)
expect(hook.getState()?.completion_promise).toBe("DONE")
expect(hook.getState()?.verification_pending).toBeUndefined()
expect(promptCalls).toHaveLength(2)
expect(promptCalls[1]?.text).toContain("Verification failed")
}) })
test("#given oracle verification fails #when loop restarts #then old oracle session is aborted", async () => { 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 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 { function getPluginDirectory(ctx: PluginContext): string | null {
if (typeof ctx === "object" && ctx !== null && "directory" in ctx && typeof ctx.directory === "string") { if (typeof ctx === "object" && ctx !== null && "directory" in ctx && typeof ctx.directory === "string") {
return ctx.directory return ctx.directory
@@ -43,9 +54,9 @@ export function createToolExecuteAfterHandler(args: {
if (input.tool === "task") { if (input.tool === "task") {
const directory = getPluginDirectory(ctx) const directory = getPluginDirectory(ctx)
const sessionId = typeof output.metadata?.sessionId === "string" ? output.metadata.sessionId : undefined const sessionId = getMetadataString(output.metadata, ["sessionId", "sessionID", "session_id"])
const agent = typeof output.metadata?.agent === "string" ? output.metadata.agent : undefined const agent = getMetadataString(output.metadata, ["agent"])
const prompt = typeof output.metadata?.prompt === "string" ? output.metadata.prompt : undefined const prompt = getMetadataString(output.metadata, ["prompt"])
const verificationAttemptId = prompt?.match(VERIFICATION_ATTEMPT_PATTERN)?.[1]?.trim() const verificationAttemptId = prompt?.match(VERIFICATION_ATTEMPT_PATTERN)?.[1]?.trim()
const loopState = directory ? readState(directory) : null const loopState = directory ? readState(directory) : null
const isVerificationContext = const isVerificationContext =
+25 -1
View File
@@ -20,6 +20,26 @@ export function createToolExecuteBeforeHandler(args: {
) => Promise<void> { ) => Promise<void> {
const { ctx, hooks } = args 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> => { return async (input, output): Promise<void> => {
await hooks.writeExistingFileGuard?.["tool.execute.before"]?.(input, output) await hooks.writeExistingFileGuard?.["tool.execute.before"]?.(input, output)
await hooks.questionLabelTruncator?.["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, verification_session_id: undefined,
}) })
argsObject.run_in_background = false 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(readState(directory)?.verification_attempt_id).toBeTruthy()
expect(output.args.run_in_background).toBe(false) 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("Ship feature")
expect(output.args.prompt).toContain("Review the work skeptically and critically")
expect(output.args.prompt).toContain(`<promise>${ULTRAWORK_VERIFICATION_PROMISE}</promise>`) expect(output.args.prompt).toContain(`<promise>${ULTRAWORK_VERIFICATION_PROMISE}</promise>`)
clearState(directory) clearState(directory)
@@ -171,6 +173,45 @@ describe("tool.execute.before ultrawork oracle verification", () => {
rmSync(directory, { recursive: true, force: true }) 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 () => { 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()}`) const directory = join(tmpdir(), `tool-race-ulw-${Date.now()}`)
mkdirSync(directory, { recursive: true }) mkdirSync(directory, { recursive: true })