fix(clawsweeper): address review for automerge-openclaw-openclaw-82891 (2)

This commit is contained in:
clawsweeper
2026-05-17 11:42:53 +00:00
parent fd2fe46e98
commit 4c6dd7ed6e
2 changed files with 54 additions and 3 deletions

View File

@@ -78,6 +78,46 @@ describe("embedded attempt session lock lifecycle", () => {
expect(events).toEqual(["prep-release", "post-write", "post-release"]);
});
it("reuses its active post-prompt lock for nested session writes", async () => {
const events: string[] = [];
const sessionFile = await createTempSessionFile();
const acquireSessionWriteLock = vi
.fn()
.mockResolvedValueOnce({ release: vi.fn(async () => events.push("prep-release")) })
.mockResolvedValueOnce({ release: vi.fn(async () => events.push("post-release")) })
.mockRejectedValueOnce(
new SessionWriteLockTimeoutError({
timeoutMs: lockOptions.timeoutMs,
owner: "pid=789",
lockPath: `${sessionFile}.lock`,
}),
);
const controller = await createEmbeddedAttemptSessionLockController({
acquireSessionWriteLock,
lockOptions: { ...lockOptions, sessionFile },
});
await controller.releaseForPrompt();
await controller.withSessionWriteLock(async () => {
events.push("outer-start");
await fs.appendFile(sessionFile, '{"type":"message","id":"local"}\n', "utf8");
await controller.withSessionWriteLock(async () => {
events.push("inner-write");
});
events.push("outer-end");
});
expect(acquireSessionWriteLock).toHaveBeenCalledTimes(2);
expect(events).toEqual([
"prep-release",
"outer-start",
"inner-write",
"outer-end",
"post-release",
]);
});
it("drains queued Pi session events before reacquiring for cleanup", async () => {
const events: string[] = [];
let resolveQueue!: () => void;

View File

@@ -1,3 +1,4 @@
import { AsyncLocalStorage } from "node:async_hooks";
import fs from "node:fs/promises";
import { isSessionWriteLockTimeoutError } from "../../session-write-lock-error.js";
import type { acquireSessionWriteLock } from "../../session-write-lock.js";
@@ -257,6 +258,7 @@ export async function createEmbeddedAttemptSessionLockController(params: {
});
let heldLock: SessionLock | undefined = await acquireLock();
const activeWriteLock = new AsyncLocalStorage<SessionLock>();
let fenceFingerprint: SessionFileFingerprint | undefined;
let fenceActive = false;
let takeoverDetected = false;
@@ -310,12 +312,21 @@ export async function createEmbeddedAttemptSessionLockController(params: {
if (takeoverDetected) {
throw new EmbeddedAttemptSessionTakeoverError(params.lockOptions.sessionFile);
}
if (activeWriteLock.getStore()) {
return await run();
}
const { lock, owned } = await acquireWriteLock();
try {
await assertSessionFileFence();
const result = await run();
await refreshSessionFileFence();
return result;
const runWithLock = async () => {
const result = await run();
await refreshSessionFileFence();
return result;
};
if (owned) {
return await activeWriteLock.run(lock, runWithLock);
}
return await runWithLock();
} finally {
if (owned) {
await lock.release();