test(snapshot): retain inbox wait timeout diagnostics
This commit is contained in:
1 parent
534acc5952
commit
22f034bd76
5 files changed
+27
-20
No files matched your search
@@ -30,8 +30,8 @@
|
||||
en: 64666ea2894acc4a
|
||||
zh: 0e78df32bcfd3633
|
||||
/deepseek-ai-dsh-session-snapshot/use-this-package/what-can-go-wrong:
|
||||
en: 9e596b474f37950a
|
||||
zh: caef58074f332819
|
||||
en: 6c5e9a1f97153cd0
|
||||
zh: e41bb04558fff2ed
|
||||
/deepseek-ai-dsh-session-snapshot/understand-the-implementation:
|
||||
en: 18ff76b440fe40da
|
||||
zh: 4dbf2eb5f9ee98a2
|
||||
|
||||
@@ -92,7 +92,7 @@ A scenario requiring a non-Windows host declares `posixOnly`, which skips its ru
|
||||
|
||||
### What can go wrong
|
||||
|
||||
- **A turn-end wait fails** — `waitForTurnEnd` and `waitForSubagentTurnEnd` identify the session or child and deadline even when the first log harvest exceeds that deadline. Child waits also name the requested turn. Both retain the underlying failure as the error cause.
|
||||
- **A durable wait fails** — `waitForTurnEnd`, `waitForSubagentTurnEnd`, and `waitForInboxMessage` identify the session or child and deadline even when the first log harvest exceeds that deadline. Child waits also name the requested turn. All retain the underlying failure as the error cause.
|
||||
- **A fixture guard rejects the committed files** — orphan scenario dirs, missing files, multiple pins for one header class, duplicate sidecar content, unscrubbed prompt text or tool schemas, a `request/header` with no preceding `system/message`, and malformed pinning headers all fail the suite before comparisons run.
|
||||
- **The session harvest needs raw JSONL mode** — snapshot configs set the JSONL backend's `compression: 'none'`; compressed JSONL has no snapshot-harvest path.
|
||||
- **Built mode needs current artifacts** — run `pnpm run build` before selecting `DSH_EXAMPLE_MODE=lib`; source mode remains the zero-build path.
|
||||
|
||||
@@ -92,7 +92,7 @@ Headless/ACP 与 SDK 适配器在规范化之前,将原始目录中的子创
|
||||
|
||||
### 可能出什么问题
|
||||
|
||||
- **轮次结束等待失败**——即使首次日志收集就超过期限,`waitForTurnEnd` 和 `waitForSubagentTurnEnd` 也会指出会话或子会话及等待期限。子会话等待还会指出目标轮次。两者都通过错误的 cause 保留底层失败。
|
||||
- **持久记录等待失败**——即使首次日志收集就超过期限,`waitForTurnEnd`、`waitForSubagentTurnEnd` 和 `waitForInboxMessage` 也会指出会话或子会话及等待期限。子会话等待还会指出目标轮次。三者都通过错误的 cause 保留底层失败。
|
||||
- **fixture 保护拒绝已提交文件**——遗留场景目录、缺失文件、一个 header 类别包含多个 pin、重复的伴随文件内容、未擦除的提示文本或工具 schema、没有前置 `system/message` 的 `request/header`,以及格式错误的 pin header 都会在比较运行前使套件失败。
|
||||
- **会话收集需要原始 JSONL mode**——快照配置使用 JSONL 后端的 `compression: 'none'`;压缩 JSONL 没有快照收集路径。
|
||||
- **构建 mode 需要当前产物**——选择 `DSH_EXAMPLE_MODE=lib` 前先运行 `pnpm run build`;源 mode 仍是零构建路径。
|
||||
|
||||
@@ -65,6 +65,8 @@ const WAIT_POLL_INTERVAL_MS = 10
|
||||
* selected session's latest complete raw-JSONL turn boundary is `turn/end`.
|
||||
* `waitForGoalPhase` waits for the latest durable goal snapshot to reach one phase.
|
||||
* `waitForInboxMessage` waits for inserted inbox text containing a scenario marker.
|
||||
* Its failures identify the session and deadline even if the first log read is
|
||||
* still pending; the underlying failure is retained as cause.
|
||||
* `waitForSubagentTurnEnd` waits until one background child has persisted a
|
||||
* closed model-work turn after its own descriptor; child progress has no ACP
|
||||
* update to wait on. Failures identify the child, turn, and deadline even if
|
||||
@@ -663,22 +665,25 @@ async function waitForPersistedInboxMessage(
|
||||
text: string,
|
||||
timeoutMs = DEFAULT_WAIT_TIMEOUT_MS,
|
||||
): Promise<void> {
|
||||
await vi.waitFor(async () => {
|
||||
const log = (await harvestSessionLogs(root)).find(candidate => candidate.id === sessionId)
|
||||
const matched = log?.content.split('\n').some((line) => {
|
||||
if (line.length === 0) return false
|
||||
const record = JSON.parse(line) as {
|
||||
type?: unknown
|
||||
data?: { inserted?: Array<{ content?: Array<{ type?: unknown; text?: unknown }> }> }
|
||||
}
|
||||
return record.type === 'agent/inbox/spliced' && record.data?.inserted?.some(message =>
|
||||
message.content?.some(block => block.type === 'text'
|
||||
&& typeof block.text === 'string' && block.text.includes(text))) === true
|
||||
}) ?? false
|
||||
if (!matched) {
|
||||
throw new Error(`snapshot-harness: session "${sessionId}" did not persist expected inbox message within ${timeoutMs}ms`)
|
||||
}
|
||||
}, { interval: WAIT_POLL_INTERVAL_MS, timeout: timeoutMs })
|
||||
const message = `snapshot-harness: session "${sessionId}" did not persist expected inbox message within ${timeoutMs}ms`
|
||||
try {
|
||||
await vi.waitFor(async () => {
|
||||
const log = (await harvestSessionLogs(root)).find(candidate => candidate.id === sessionId)
|
||||
const matched = log?.content.split('\n').some((line) => {
|
||||
if (line.length === 0) return false
|
||||
const record = JSON.parse(line) as {
|
||||
type?: unknown
|
||||
data?: { inserted?: Array<{ content?: Array<{ type?: unknown; text?: unknown }> }> }
|
||||
}
|
||||
return record.type === 'agent/inbox/spliced' && record.data?.inserted?.some(message =>
|
||||
message.content?.some(block => block.type === 'text'
|
||||
&& typeof block.text === 'string' && block.text.includes(text))) === true
|
||||
}) ?? false
|
||||
if (!matched) throw new Error(message)
|
||||
}, { interval: WAIT_POLL_INTERVAL_MS, timeout: timeoutMs })
|
||||
} catch (cause) {
|
||||
throw new Error(message, { cause })
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether a child log contains model work after its own descriptor event. */
|
||||
|
||||
@@ -1002,6 +1002,8 @@ describe('runScenario', () => {
|
||||
|
||||
it.each([
|
||||
{ label: 'session', step: { op: 'waitForTurnEnd', timeoutMs: 20 }, expected: 'did not persist turn/end within 20ms' },
|
||||
{ label: 'inbox', step: { op: 'waitForInboxMessage', text: 'missing', timeoutMs: 20 },
|
||||
expected: 'did not persist expected inbox message within 20ms' },
|
||||
{ label: 'child', step: { op: 'waitForSubagentTurnEnd', child: 2, timeoutMs: 20 },
|
||||
expected: 'subagent child #2 did not persist closed turn 1 within 20ms' },
|
||||
] satisfies { label: string; step: InputStep; expected: string }[])
|
||||
|
||||
Reference in new issue
Block a user