diff --git a/tests/bug-1736-local-install-commands.test.cjs b/tests/bug-1736-local-install-commands.test.cjs index 969376435..5dcf6550c 100644 --- a/tests/bug-1736-local-install-commands.test.cjs +++ b/tests/bug-1736-local-install-commands.test.cjs @@ -46,7 +46,7 @@ describe('#1736: local Claude install populates .claude/commands/gsd/', () => { }); afterEach(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); + fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); }); test('local install creates .claude/commands/gsd/ directory', (t) => { diff --git a/tests/bug-2248-local-install-statusline.test.cjs b/tests/bug-2248-local-install-statusline.test.cjs index a8bea399a..55da484dd 100644 --- a/tests/bug-2248-local-install-statusline.test.cjs +++ b/tests/bug-2248-local-install-statusline.test.cjs @@ -47,7 +47,7 @@ describe('#2248: local Claude install does not clobber profile-level statusLine' }); afterEach(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); + fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); }); test('local install does not write statusLine to .claude/settings.json', (t) => { diff --git a/tests/bug-2698-crlf-install.test.cjs b/tests/bug-2698-crlf-install.test.cjs index 51f56b424..3e5afe648 100644 --- a/tests/bug-2698-crlf-install.test.cjs +++ b/tests/bug-2698-crlf-install.test.cjs @@ -60,7 +60,7 @@ describe('#2698: CRLF stale gsd-update-check block is removed on Codex reinstall }); afterEach(() => { - fs.rmSync(tmpDir, { recursive: true, force: true }); + fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); }); // Helper: pre-populate .codex/config.toml with a GSD marker + stale hooks block diff --git a/tests/bug-2838-summary-rescue-gitignored-planning.test.cjs b/tests/bug-2838-summary-rescue-gitignored-planning.test.cjs index fde78bbc3..1a90ed3eb 100644 --- a/tests/bug-2838-summary-rescue-gitignored-planning.test.cjs +++ b/tests/bug-2838-summary-rescue-gitignored-planning.test.cjs @@ -163,7 +163,7 @@ ${rescueBlock} } function cleanup(tmp) { - try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (_) {} + try { fs.rmSync(tmp, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); } catch (_) {} } describe('bug-2838: SUMMARY rescue handles gitignored .planning/', () => { diff --git a/tests/bug-2839-review-fix-transactional-cleanup.test.cjs b/tests/bug-2839-review-fix-transactional-cleanup.test.cjs index 1a6aa9cd2..1bd2c5f05 100644 --- a/tests/bug-2839-review-fix-transactional-cleanup.test.cjs +++ b/tests/bug-2839-review-fix-transactional-cleanup.test.cjs @@ -30,19 +30,9 @@ const assert = require('node:assert/strict'); const fs = require('fs'); const path = require('path'); -const SENTINEL_NAME = '.review-fix-recovery-pending.json'; +const { parseFrontmatter } = require('./helpers.cjs'); -function parseFrontmatter(content) { - const match = content.match(/^---\n([\s\S]*?)\n---/); - if (!match) return null; - const body = match[1]; - const out = {}; - for (const line of body.split('\n')) { - const m = line.match(/^([a-zA-Z_]+):\s*(.*)$/); - if (m) out[m[1]] = m[2].trim(); - } - return out; -} +const SENTINEL_NAME = '.review-fix-recovery-pending.json'; function extractStep(content, stepName) { const re = new RegExp(`([\\s\\S]*?)`); diff --git a/tests/helpers.cjs b/tests/helpers.cjs index 7bb14585c..2e9843fb4 100644 --- a/tests/helpers.cjs +++ b/tests/helpers.cjs @@ -104,7 +104,10 @@ function createTempGitProject(prefix = 'gsd-test-') { } function cleanup(tmpDir) { - fs.rmSync(tmpDir, { recursive: true, force: true }); + // maxRetries/retryDelay absorbs transient Windows EBUSY where AV scanners, + // file-indexers, or just-exited child processes still hold handles when + // teardown runs. On POSIX the retry loop is a no-op (rmSync succeeds first try). + fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); } /**