From 52002791a47024589efd15745d8b7c886110dd9b Mon Sep 17 00:00:00 2001 From: Dudu-0223 Date: Mon, 27 Jul 2026 15:36:17 +0800 Subject: [PATCH] test(acp): stabilize continuable subagent snapshot --- .../fixtures/subagent-durability-failure.ts | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/examples/acp-agent/tests/fixtures/subagent-durability-failure.ts b/examples/acp-agent/tests/fixtures/subagent-durability-failure.ts index 5d0137911d..47f96c0b80 100644 --- a/examples/acp-agent/tests/fixtures/subagent-durability-failure.ts +++ b/examples/acp-agent/tests/fixtures/subagent-durability-failure.ts @@ -1,9 +1,47 @@ import type { Context } from 'cordis' export const name = 'subagent-durability-failure' +export const inject = ['sessionPersistence', 'tasks'] -/** Fail a continuable child's provider-owned final durability confirmation. */ +const UNKNOWN_CHILD_ID = '22222222-2222-4222-8222-222222222222' +const FOLLOW_UP_TASK_ID = 'subagent-2' + +/** Fail the child checkpoint and stabilize the authored follow-up failure ordering. */ export function apply(ctx: Context): void { + const thirdStepEnded = Promise.withResolvers() + const followUpSettled = Promise.withResolvers() + const persistence = ctx.sessionPersistence + const load = persistence.load.bind(persistence) + + // The unavailable-child lookup is real asynchronous I/O. Fence it between + // the authored step boundaries so runner speed cannot reorder the exact log. + persistence.load = async (id) => { + if (id === UNKNOWN_CHILD_ID) await thirdStepEnded.promise + return load.call(persistence, id) + } + ctx.effect(() => () => { + persistence.load = load + thirdStepEnded.resolve(undefined) + followUpSettled.resolve(undefined) + }, 'subagent snapshot ordering') + + ctx.on('session/event', (session, event) => { + if (session.header.parentSession === undefined + && event.type === 'step/end' + && event.data.turn === 1 + && event.data.step === 3) { + thirdStepEnded.resolve(undefined) + } + }) + ctx.tasks.onTaskDone((snapshot) => { + if (snapshot.id === FOLLOW_UP_TASK_ID) followUpSettled.resolve(undefined) + }) + ctx.on('agent/step', async (agent, turn, step) => { + if (agent.session.header.parentSession === undefined && turn === 1 && step === 4) { + await followUpSettled.promise + } + }) + const flushedTurnEnds = new WeakSet() ctx.on('session/flush', (session) => { if (session.header.parentSession === undefined) return