From 0ea41f918ab280e44b909be56a7ad4fe049eeddc Mon Sep 17 00:00:00 2001 From: brokemac79 Date: Sat, 25 Jul 2026 05:47:48 +0100 Subject: [PATCH 1/2] fix(exact-review): snapshot durable tuple before publication --- src/repair/event-record-store.ts | 14 +++++--- src/repair/publish-event-result.ts | 7 ++-- test/repair/event-record-store.test.ts | 47 ++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/repair/event-record-store.ts b/src/repair/event-record-store.ts index be2acc4153..f5407ecaf1 100644 --- a/src/repair/event-record-store.ts +++ b/src/repair/event-record-store.ts @@ -96,12 +96,16 @@ export function resetEventSnapshot(store: EventRecordStore): void { fs.mkdirSync(store.snapshotDir, { recursive: true }); } -export function captureEventBaseSnapshot(store: EventRecordStore): EventRecordPaths { +export function captureEventBaseSnapshot( + store: EventRecordStore, + options: { sourceRoot?: string } = {}, +): EventRecordPaths { const paths = eventRecordPaths(store); - copyIfExists(paths.itemRecord, paths.snapshotBaseItem); - copyIfExists(paths.closedRecord, paths.snapshotBaseClosed); - copyIfExists(paths.planRecord, paths.snapshotBasePlan); - copyIfExists(paths.decisionPacket, paths.snapshotBaseDecisionPacket); + const sourceRoot = options.sourceRoot ?? "."; + copyIfExists(path.resolve(sourceRoot, paths.itemRecord), paths.snapshotBaseItem); + copyIfExists(path.resolve(sourceRoot, paths.closedRecord), paths.snapshotBaseClosed); + copyIfExists(path.resolve(sourceRoot, paths.planRecord), paths.snapshotBasePlan); + copyIfExists(path.resolve(sourceRoot, paths.decisionPacket), paths.snapshotBaseDecisionPacket); return paths; } diff --git a/src/repair/publish-event-result.ts b/src/repair/publish-event-result.ts index 088901a542..59fbf00538 100644 --- a/src/repair/publish-event-result.ts +++ b/src/repair/publish-event-result.ts @@ -151,9 +151,13 @@ async function publishEventResult(options: EventOptions): Promise { itemNumber: options.itemNumber, snapshotDir: options.snapshotDir, }; + const stateRoot = publishRoot(); resetEventSnapshot(recordStore); - const recordPaths = captureEventBaseSnapshot(recordStore); + const recordPaths = captureEventBaseSnapshot( + recordStore, + stateRoot ? { sourceRoot: stateRoot } : {}, + ); fs.rmSync(options.reportPath, { force: true }); runClawsweeper(options, [ @@ -174,7 +178,6 @@ async function publishEventResult(options: EventOptions): Promise { captureEventSnapshot(recordStore); hardResetToRemoteMain(); const stateBaseCommit = captureStatePublishBaseline(); - const stateRoot = publishRoot(); const preflightResult = applyEventSnapshotIfCurrent( recordPaths, stateRoot ? { remoteRoot: stateRoot } : {}, diff --git a/test/repair/event-record-store.test.ts b/test/repair/event-record-store.test.ts index 5cca079f26..e125b90a2d 100644 --- a/test/repair/event-record-store.test.ts +++ b/test/repair/event-record-store.test.ts @@ -33,6 +33,53 @@ test("event record directories stay inside the isolated worker root", () => { }); }); +test("event base snapshot uses durable state when the isolated worker starts empty", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "clawsweeper-event-records-")); + const workerRoot = path.join(root, "worker"); + const stateRoot = path.join(root, "state"); + const store = { + targetRepo: "openclaw/openclaw", + itemNumber: "93584", + snapshotDir: path.join(workerRoot, "snapshot"), + }; + const paths = eventRecordPaths(store); + fs.mkdirSync(workerRoot, { recursive: true }); + fs.mkdirSync(stateRoot, { recursive: true }); + + withCwd(stateRoot, () => { + writeEventTuple(paths, { + marker: "durable previous review", + reviewedAt: "2026-07-24T22:16:08.206Z", + itemUpdatedAt: "2026-07-24T22:14:34Z", + }); + }); + + withCwd(workerRoot, () => { + resetEventSnapshot(store); + captureEventBaseSnapshot(store); + writeEventTuple(paths, { + marker: "fresh exact review", + reviewedAt: "2026-07-25T03:12:20.195Z", + itemUpdatedAt: "2026-07-25T03:10:15Z", + }); + captureEventSnapshot(store); + assert.throws( + () => applyEventSnapshot(paths, { remoteRoot: stateRoot }), + /missing comparable state-mutation timestamp/, + ); + + resetEventSnapshot(store); + captureEventBaseSnapshot(store, { sourceRoot: stateRoot }); + writeEventTuple(paths, { + marker: "fresh exact review", + reviewedAt: "2026-07-25T03:12:20.195Z", + itemUpdatedAt: "2026-07-25T03:10:15Z", + }); + captureEventSnapshot(store); + assert.equal(applyEventSnapshot(paths, { remoteRoot: stateRoot }), "open"); + }); +}); + test("event snapshot match follows the final tuple winner, not only its action", () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "clawsweeper-event-records-")); const store = { From 0b4839236a1c2568beb060ad6f6bc027eaf0023f Mon Sep 17 00:00:00 2001 From: brokemac79 Date: Sat, 25 Jul 2026 06:03:28 +0100 Subject: [PATCH 2/2] test(exact-review): mock verified pull admission --- test/dashboard-worker.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/dashboard-worker.test.ts b/test/dashboard-worker.test.ts index b2b14751cd..542fdb1d4e 100644 --- a/test/dashboard-worker.test.ts +++ b/test/dashboard-worker.test.ts @@ -7331,6 +7331,11 @@ test("exact-review queue retains a synchronize update after an unclassified 422" return jsonResponse({ id: 999 }); if (/^\/repos\/openclaw\/(?:clawsweeper|gogcli)\/issues\/\d+$/.test(url.pathname)) return jsonResponse({ state: "open" }); + if (url.pathname === "/repos/openclaw/clawsweeper/pulls/842") + return jsonResponse({ + state: "open", + head: { sha: "81e4abc894e7d3ec1fddbd856378b6aadb4392f3" }, + }); if (url.pathname === "/app/installations/999/access_tokens") return jsonResponse({ token: "dispatch-token" }); if (url.pathname === "/repos/openclaw/clawsweeper/dispatches") {