
























@@ -68,6 +68,45 @@ describe("embedded attempt session lock lifecycle", () => {
6868expect(releases).toEqual(["prep", "cleanup"]);
6969});
707071+it("releases the eagerly-held attempt lock on dispose when cleanup is skipped (#86014)", async () => {
72+const releases: string[] = [];
73+const acquireSessionWriteLock = vi
74+.fn()
75+.mockResolvedValueOnce({ release: vi.fn(async () => releases.push("held")) });
76+77+const controller = await createEmbeddedAttemptSessionLockController({
78+ acquireSessionWriteLock,
79+ lockOptions,
80+});
81+82+// An exception on the post-prompt path skips acquireForCleanup; the run's outer finally
83+// must still release the eagerly-held lock or it leaks to the live process.
84+await controller.dispose();
85+await controller.dispose(); // idempotent
86+87+expect(acquireSessionWriteLock).toHaveBeenCalledTimes(1);
88+expect(releases).toEqual(["held"]);
89+});
90+91+it("dispose does not double-release a lock already handed to cleanup", async () => {
92+const releases: string[] = [];
93+const acquireSessionWriteLock = vi
94+.fn()
95+.mockResolvedValueOnce({ release: vi.fn(async () => releases.push("held")) });
96+97+const controller = await createEmbeddedAttemptSessionLockController({
98+ acquireSessionWriteLock,
99+ lockOptions,
100+});
101+102+const cleanupLock = await controller.acquireForCleanup();
103+await cleanupLock.release();
104+await controller.dispose();
105+106+expect(acquireSessionWriteLock).toHaveBeenCalledTimes(1);
107+expect(releases).toEqual(["held"]);
108+});
109+71110it("runs post-prompt transcript writes under a short reacquired lock", async () => {
72111const events: string[] = [];
73112const acquireSessionWriteLock = vi
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。