From f93e8f6869d4575fa51cc6c8da614e5806302f69 Mon Sep 17 00:00:00 2001 From: Jamkris Date: Tue, 19 May 2026 11:01:28 +0900 Subject: [PATCH] fix(hooks): use shared renameWithRetry in writeWarnState (ecc-context-monitor) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the previous commit's Windows-EPERM retry on the companion `writeWarnState` in `scripts/hooks/ecc-context-monitor.js`. Same race: two PostToolUse subprocesses writing concurrent debounce state racing on `MoveFileExW`, target-in-use throwing EPERM on Windows even though each writer's tmp path is now unique. Implementation: import `renameWithRetry` from `scripts/lib/session-bridge.js` (exported in the previous commit) instead of duplicating the helper. The retry policy, backoff schedule, and main-thread `Atomics.wait` strategy stay identical to `writeBridgeAtomic`. Three writers in the repo now share the same atomic-write contract: - `writeBridgeAtomic` (scripts/lib/session-bridge.js) — round 1 + this round's retry - `writeWarnState` (this file) — round 1 + this round's retry via shared helper - `writeCostWarningIfChanged` (scripts/hooks/ecc-metrics-bridge.js) — out of scope for this PR (already uses unique tmp suffix; a future consolidation could move it to the shared helper too). Local: `yarn test` green, `yarn lint` clean. The companion test suite for `ecc-context-monitor.js` does not currently exercise concurrent `writeWarnState` writes, but the helper it now uses is covered by the `tests/lib/session-bridge.test.js` concurrent-write regression added in round 1's last commit. --- scripts/hooks/ecc-context-monitor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/hooks/ecc-context-monitor.js b/scripts/hooks/ecc-context-monitor.js index 5058f9bf..9cdedbe3 100644 --- a/scripts/hooks/ecc-context-monitor.js +++ b/scripts/hooks/ecc-context-monitor.js @@ -13,7 +13,7 @@ const crypto = require('crypto'); const fs = require('fs'); const os = require('os'); const path = require('path'); -const { sanitizeSessionId, readBridge } = require('../lib/session-bridge'); +const { sanitizeSessionId, readBridge, renameWithRetry } = require('../lib/session-bridge'); const CONTEXT_WARNING_PCT = 35; const CONTEXT_CRITICAL_PCT = 25; @@ -81,7 +81,7 @@ function writeWarnState(sessionId, state) { const tmp = `${target}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`; fs.writeFileSync(tmp, JSON.stringify(state), 'utf8'); try { - fs.renameSync(tmp, target); + renameWithRetry(tmp, target); } catch (err) { try { fs.unlinkSync(tmp); } catch { /* ignore */ } throw err;