From 4bcabe6da463fc74716d9f4222bb981e998e69a0 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 7 Sep 2026 19:20:39 +0800 Subject: [PATCH] test: pair spill cleanup roots with concurrent run results --- ...2026-09-06-pr-ci-runner-temporary-storage.i18n.yaml | 4 ++-- .../2026-09-06-pr-ci-runner-temporary-storage.md | 2 +- .../2026-09-06-pr-ci-runner-temporary-storage.zh.md | 2 +- scripts/snapshot-spill-locators.spec.ts | 10 +++++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.i18n.yaml b/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.i18n.yaml index 87f49001ec..dccefc4520 100644 --- a/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.i18n.yaml +++ b/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.md -2026-09-06-pr-ci-runner-temporary-storage.md: 53e5ac8498200b82bde1c31393df6a38327db8ef -2026-09-06-pr-ci-runner-temporary-storage.zh.md: 931ab5fd4da363ad350159cd3ebfe9ab1d093158 +2026-09-06-pr-ci-runner-temporary-storage.md: 59d609c08e1b228ae9a9705140f2832305dfead5 +2026-09-06-pr-ci-runner-temporary-storage.zh.md: f6765212af2424f2080063946f9104125993b9d5 diff --git a/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.md b/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.md index 53e5ac8498..59d609c08e 100644 --- a/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.md +++ b/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.md @@ -32,7 +32,7 @@ The reference-composer fixture maps the known home-abbreviated workspace display The advanced Python snapshot pauses only its matching workflow child’s first pre-step until the parent’s durable workflow membership event is observed. The fixture supports either event-arrival order and cancels pending waits on abort or disposal. This pins the scenario’s cross-session ordering without sorting notifications or changing production scheduling. -The queue snapshot moves the pointer away from the Stop/Send control and waits for its Send tooltip to close before capture. Workspace-management tests select the sole non-blank Session by its actions affordance, not row position, and select that Session before asserting that archiving it removes the empty Ungrouped bucket. Hover behavior, queue contents, durable archive identity, and reload assertions remain unchanged. +The queue snapshot moves the pointer away from the Stop/Send control and waits for its Send tooltip to close before capture. Workspace-management tests select the sole non-blank Session by its actions affordance, not row position, and select that Session before asserting that archiving it removes the empty Ungrouped bucket. Hover behavior, queue contents, durable archive identity, and reload assertions remain unchanged. The concurrent spill isolation test keeps each root paired with its run result rather than assuming filesystem allocation completion order matches input order. ## Alternatives considered diff --git a/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.zh.md b/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.zh.md index 931ab5fd4d..f6765212af 100644 --- a/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.zh.md +++ b/.agents/notes/implemented/testing/2026-09-06-pr-ci-runner-temporary-storage.zh.md @@ -32,7 +32,7 @@ Reference-composer 夹具将已知的 home 缩写 workspace 显示映射到既 高级 Python 快照仅暂停其匹配的 workflow 子进程首次 pre-step,直到观察到父 Session 的持久化 workflow 成员事件。夹具支持事件先到或等待先建立两种顺序,并在取消或销毁时结束未完成等待。这固定了场景的跨 Session 顺序,而不排序通知或改变生产调度。 -Queue 快照在捕获前将指针移离 Stop/Send 控件,并等待其 Send tooltip 关闭。Workspace-management 测试通过操作按钮定位唯一非空 Session,而不依赖行位置;在断言归档会移除空的 Ungrouped 分组前,先选中该 Session。Hover 行为、队列内容、持久化归档身份及重载断言保持不变。 +Queue 快照在捕获前将指针移离 Stop/Send 控件,并等待其 Send tooltip 关闭。Workspace-management 测试通过操作按钮定位唯一非空 Session,而不依赖行位置;在断言归档会移除空的 Ungrouped 分组前,先选中该 Session。Hover 行为、队列内容、持久化归档身份及重载断言保持不变。并发 spill 隔离测试将每个根目录与其运行结果关联,而不假设文件系统分配完成顺序与输入顺序一致。 ## 考虑过的替代方案 diff --git a/scripts/snapshot-spill-locators.spec.ts b/scripts/snapshot-spill-locators.spec.ts index 03584d8172..6bd0f2ef81 100644 --- a/scripts/snapshot-spill-locators.spec.ts +++ b/scripts/snapshot-spill-locators.spec.ts @@ -9,13 +9,16 @@ import { join, resolve } from 'node:path' import { expect, it } from 'vitest' import * as locators from './snapshot-spill-locators.ts' -it('keeps concurrent physical spill files private while retaining logical locator length and bytes', async () => { +it.each([false, true])('keeps concurrent physical spill files private while retaining logical locator length and bytes (reverse allocation=%s)', async (reverseAllocation) => { const roots: string[] = [] const disposers: (() => Promise)[] = [] + const secondAllocated = Promise.withResolvers() try { const runs = await Promise.all([0, 1].map(async (index) => { + if (reverseAllocation && index === 0) await secondAllocated.promise const root = await mkdtemp(join(tmpdir(), 'snapshot-locator-')) roots.push(root) + if (index === 1) secondAllocated.resolve(undefined) const ctx = new Context() const storeFiber = ctx.plugin(LocalSpillStore, { root, cleanupPeriodDays: 0 }) disposers.push(() => storeFiber.dispose()) @@ -51,11 +54,12 @@ it('keeps concurrent physical spill files private while retaining logical locato const restored = await ctx.fs.resolve(ref.locator) expect(ctx.fs.processPath(restored)).not.toBe(physicalPath) expect(await ctx.fs.stat(restored)).toBeUndefined() - return { physicalPath, locator: ref.locator, content } + return { root, physicalPath, locator: ref.locator, content } })) expect(runs[0]?.physicalPath).not.toBe(runs[1]?.physicalPath) expect(runs[0]?.locator.length).toBe(runs[1]?.locator.length) - await rm(roots[0] as string, { recursive: true, force: true }) + // Promise.all preserves input order; allocation completion order may differ. + await rm(runs[0]!.root, { recursive: true, force: true }) expect(await readFile(runs[1]?.physicalPath as string, 'utf8')).toBe(runs[1]?.content) } finally { for (const dispose of disposers.reverse()) await dispose()