diff --git a/src/clawsweeper.ts b/src/clawsweeper.ts index b6fec4d4a6..18778a86c8 100644 --- a/src/clawsweeper.ts +++ b/src/clawsweeper.ts @@ -18047,6 +18047,8 @@ async function applyDecisionsCommand(args: Args): Promise { } catch (error) { const detail = error instanceof Error ? error.message : String(error); const reason = `invalid maintainer_decision: ${detail}`; + markdown = replaceFrontMatterValue(markdown, "apply_checked_at", new Date().toISOString()); + if (!dryRun) writeFileSync(path, markdown, "utf8"); results.push({ number, action: "kept_open", reason }); processedCount += 1; maybeLogProgress(`skipped #${number}: ${reason}`); diff --git a/test/clawsweeper.test.ts b/test/clawsweeper.test.ts index 75b9450ddb..80e549450b 100644 --- a/test/clawsweeper.test.ts +++ b/test/clawsweeper.test.ts @@ -446,17 +446,31 @@ test("apply-decisions makes no GitHub mutation for malformed maintainer decision implementedCloseReport({ maintainer_decision: "{" }), "utf8", ); + writeFileSync( + join(itemsDir, "322.md"), + implementedCloseReport({ number: 322, maintainer_decision: "{" }), + "utf8", + ); const ghMock = ` console.error("malformed maintainer decision reached GitHub"); process.exit(1); `; withMockGh(root, ghMock, () => { - runApplyDecisionsForTest({ itemsDir, closedDir, plansDir, reportPath }); + runApplyDecisionsForTest({ + itemsDir, + closedDir, + plansDir, + reportPath, + extraArgs: ["--processed-limit", "1"], + }); }); assert.equal(existsSync(join(itemsDir, "321.md")), true); assert.equal(existsSync(join(closedDir, "321.md")), false); + const firstUpdatedReport = readFileSync(join(itemsDir, "321.md"), "utf8"); + assert.match(firstUpdatedReport, /^apply_checked_at: /m); + assert.doesNotMatch(readFileSync(join(itemsDir, "322.md"), "utf8"), /^apply_checked_at: /m); assert.deepEqual(JSON.parse(readFileSync(reportPath, "utf8")), [ { number: 321, @@ -464,6 +478,25 @@ process.exit(1); reason: "invalid maintainer_decision: maintainer_decision must contain valid JSON", }, ]); + + withMockGh(root, ghMock, () => { + runApplyDecisionsForTest({ + itemsDir, + closedDir, + plansDir, + reportPath, + extraArgs: ["--processed-limit", "1"], + }); + }); + + assert.match(readFileSync(join(itemsDir, "322.md"), "utf8"), /^apply_checked_at: /m); + assert.deepEqual(JSON.parse(readFileSync(reportPath, "utf8")), [ + { + number: 322, + action: "kept_open", + reason: "invalid maintainer_decision: maintainer_decision must contain valid JSON", + }, + ]); } finally { rmSync(root, { recursive: true, force: true }); }