fix(ulw-loop): add fallback for Oracle verification session tracking
The verification_session_id was never reliably set because the prompt-based attempt_id matching in tool-execute-after depends on metadata.prompt surviving the delegate-task execution chain. When this fails silently, the loop never detects Oracle's VERIFIED emission. Add a fallback: when exact attempt_id matching fails but oracle agent + verification_pending state match, still set the session ID. Add diagnostic logging to trace verification flow failures. Add integration test covering the full verification chain.
This commit is contained in:
@@ -48,22 +48,50 @@ export function createToolExecuteAfterHandler(args: {
|
||||
const prompt = typeof output.metadata?.prompt === "string" ? output.metadata.prompt : undefined
|
||||
const verificationAttemptId = prompt?.match(VERIFICATION_ATTEMPT_PATTERN)?.[1]?.trim()
|
||||
const loopState = directory ? readState(directory) : null
|
||||
|
||||
if (
|
||||
const isVerificationContext =
|
||||
agent === "oracle"
|
||||
&& sessionId
|
||||
&& verificationAttemptId
|
||||
&& directory
|
||||
&& !!sessionId
|
||||
&& !!directory
|
||||
&& loopState?.active === true
|
||||
&& loopState.ultrawork === true
|
||||
&& loopState.verification_pending === true
|
||||
&& loopState.session_id === input.sessionID
|
||||
|
||||
log("[tool-execute-after] ULW verification tracking check", {
|
||||
tool: input.tool,
|
||||
agent,
|
||||
parentSessionID: input.sessionID,
|
||||
oracleSessionID: sessionId,
|
||||
hasPromptInMetadata: typeof prompt === "string",
|
||||
extractedVerificationAttemptId: verificationAttemptId,
|
||||
})
|
||||
|
||||
if (
|
||||
isVerificationContext
|
||||
&& verificationAttemptId
|
||||
&& loopState.verification_attempt_id === verificationAttemptId
|
||||
) {
|
||||
writeState(directory, {
|
||||
...loopState,
|
||||
verification_session_id: sessionId,
|
||||
})
|
||||
log("[tool-execute-after] Stored oracle verification session via attempt match", {
|
||||
parentSessionID: input.sessionID,
|
||||
oracleSessionID: sessionId,
|
||||
verificationAttemptId,
|
||||
})
|
||||
} else if (isVerificationContext && !verificationAttemptId) {
|
||||
writeState(directory, {
|
||||
...loopState,
|
||||
verification_session_id: sessionId,
|
||||
})
|
||||
log("[tool-execute-after] Fallback: stored oracle verification session without attempt match", {
|
||||
parentSessionID: input.sessionID,
|
||||
oracleSessionID: sessionId,
|
||||
hasPromptInMetadata: typeof prompt === "string",
|
||||
expectedAttemptId: loopState.verification_attempt_id,
|
||||
extractedAttemptId: verificationAttemptId,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,12 @@ export function createToolExecuteBeforeHandler(args: {
|
||||
|
||||
if (shouldInjectOracleVerification) {
|
||||
const verificationAttemptId = randomUUID()
|
||||
log("[tool-execute-before] Injecting ULW oracle verification attempt", {
|
||||
sessionID: input.sessionID,
|
||||
callID: input.callID,
|
||||
verificationAttemptId,
|
||||
loopSessionID: loopState.session_id,
|
||||
})
|
||||
writeState(ctx.directory, {
|
||||
...loopState,
|
||||
verification_attempt_id: verificationAttemptId,
|
||||
|
||||
@@ -19,6 +19,27 @@ describe("tool.execute.before ultrawork oracle verification", () => {
|
||||
}
|
||||
}
|
||||
|
||||
function createOracleTaskArgs(prompt: string): Record<string, unknown> {
|
||||
return {
|
||||
subagent_type: "oracle",
|
||||
run_in_background: true,
|
||||
prompt,
|
||||
}
|
||||
}
|
||||
|
||||
function createSyncTaskMetadata(
|
||||
args: Record<string, unknown>,
|
||||
sessionId: string,
|
||||
): Record<string, unknown> {
|
||||
return {
|
||||
prompt: args.prompt,
|
||||
agent: "oracle",
|
||||
run_in_background: args.run_in_background,
|
||||
sessionId,
|
||||
sync: true,
|
||||
}
|
||||
}
|
||||
|
||||
test("#given ulw loop is awaiting verification #when oracle task runs #then oracle prompt is enforced and sync", async () => {
|
||||
const directory = join(tmpdir(), `tool-before-ulw-${Date.now()}`)
|
||||
mkdirSync(directory, { recursive: true })
|
||||
@@ -38,13 +59,7 @@ describe("tool.execute.before ultrawork oracle verification", () => {
|
||||
ctx: createCtx(directory) as unknown as Parameters<typeof createToolExecuteBeforeHandler>[0]["ctx"],
|
||||
hooks: {} as Parameters<typeof createToolExecuteBeforeHandler>[0]["hooks"],
|
||||
})
|
||||
const output = {
|
||||
args: {
|
||||
subagent_type: "oracle",
|
||||
run_in_background: true,
|
||||
prompt: "Check it",
|
||||
} as Record<string, unknown>,
|
||||
}
|
||||
const output = { args: createOracleTaskArgs("Check it") }
|
||||
|
||||
await handler({ tool: "task", sessionID: "ses-main", callID: "call-1" }, output)
|
||||
|
||||
@@ -64,13 +79,7 @@ describe("tool.execute.before ultrawork oracle verification", () => {
|
||||
ctx: createCtx(directory) as unknown as Parameters<typeof createToolExecuteBeforeHandler>[0]["ctx"],
|
||||
hooks: {} as Parameters<typeof createToolExecuteBeforeHandler>[0]["hooks"],
|
||||
})
|
||||
const output = {
|
||||
args: {
|
||||
subagent_type: "oracle",
|
||||
run_in_background: true,
|
||||
prompt: "Check it",
|
||||
} as Record<string, unknown>,
|
||||
}
|
||||
const output = { args: createOracleTaskArgs("Check it") }
|
||||
|
||||
await handler({ tool: "task", sessionID: "ses-main", callID: "call-1" }, output)
|
||||
|
||||
@@ -80,7 +89,7 @@ describe("tool.execute.before ultrawork oracle verification", () => {
|
||||
rmSync(directory, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test("#given ulw loop is awaiting verification #when oracle task finishes #then oracle session id is stored", async () => {
|
||||
test("#given ulw loop is awaiting verification #when oracle sync task metadata is persisted #then oracle session id is stored", async () => {
|
||||
const directory = join(tmpdir(), `tool-after-ulw-${Date.now()}`)
|
||||
mkdirSync(directory, { recursive: true })
|
||||
writeState(directory, {
|
||||
@@ -99,14 +108,44 @@ describe("tool.execute.before ultrawork oracle verification", () => {
|
||||
ctx: createCtx(directory) as unknown as Parameters<typeof createToolExecuteBeforeHandler>[0]["ctx"],
|
||||
hooks: {} as Parameters<typeof createToolExecuteBeforeHandler>[0]["hooks"],
|
||||
})
|
||||
const beforeOutput = {
|
||||
args: {
|
||||
subagent_type: "oracle",
|
||||
run_in_background: true,
|
||||
prompt: "Check it",
|
||||
} as Record<string, unknown>,
|
||||
}
|
||||
const beforeOutput = { args: createOracleTaskArgs("Check it") }
|
||||
await beforeHandler({ tool: "task", sessionID: "ses-main", callID: "call-1" }, beforeOutput)
|
||||
const metadataFromSyncTask = createSyncTaskMetadata(beforeOutput.args, "ses-oracle")
|
||||
|
||||
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: metadataFromSyncTask,
|
||||
},
|
||||
)
|
||||
|
||||
expect(readState(directory)?.verification_session_id).toBe("ses-oracle")
|
||||
|
||||
clearState(directory)
|
||||
rmSync(directory, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test("#given ulw loop is awaiting verification #when oracle metadata prompt is missing #then oracle session fallback is stored", async () => {
|
||||
const directory = join(tmpdir(), `tool-after-ulw-fallback-${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"],
|
||||
@@ -120,13 +159,13 @@ describe("tool.execute.before ultrawork oracle verification", () => {
|
||||
output: "done",
|
||||
metadata: {
|
||||
agent: "oracle",
|
||||
prompt: String(beforeOutput.args.prompt),
|
||||
sessionId: "ses-oracle",
|
||||
sessionId: "ses-oracle-fallback",
|
||||
sync: true,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
expect(readState(directory)?.verification_session_id).toBe("ses-oracle")
|
||||
expect(readState(directory)?.verification_session_id).toBe("ses-oracle-fallback")
|
||||
|
||||
clearState(directory)
|
||||
rmSync(directory, { recursive: true, force: true })
|
||||
@@ -156,23 +195,11 @@ describe("tool.execute.before ultrawork oracle verification", () => {
|
||||
hooks: {} as Parameters<typeof createToolExecuteAfterHandler>[0]["hooks"],
|
||||
})
|
||||
|
||||
const firstOutput = {
|
||||
args: {
|
||||
subagent_type: "oracle",
|
||||
run_in_background: true,
|
||||
prompt: "Check it",
|
||||
} as Record<string, unknown>,
|
||||
}
|
||||
const firstOutput = { args: createOracleTaskArgs("Check it") }
|
||||
await beforeHandler({ tool: "task", sessionID: "ses-main", callID: "call-1" }, firstOutput)
|
||||
const firstAttemptId = readState(directory)?.verification_attempt_id
|
||||
|
||||
const secondOutput = {
|
||||
args: {
|
||||
subagent_type: "oracle",
|
||||
run_in_background: true,
|
||||
prompt: "Check it again",
|
||||
} as Record<string, unknown>,
|
||||
}
|
||||
const secondOutput = { args: createOracleTaskArgs("Check it again") }
|
||||
await beforeHandler({ tool: "task", sessionID: "ses-main", callID: "call-2" }, secondOutput)
|
||||
const secondAttemptId = readState(directory)?.verification_attempt_id
|
||||
|
||||
|
||||
Reference in New Issue
Block a user