ECC/scripts/lib
Jamkris 28548f67ba fix(lib): use unique tmp suffix in writeBridgeAtomic to eliminate ENOENT race
`writeBridgeAtomic` wrote to a fixed `${target}.tmp` path before
calling `renameSync`. When two processes write to the same session
bridge concurrently (e.g. PostToolUse `ecc-metrics-bridge` + the
background `ecc-statusline`, both calling `writeBridgeAtomic(sessionId, ...)`),
the canonical atomic-rename race fires:

  1. Process A: writeFileSync(target.tmp, JSON_A) — tmp file exists.
  2. Process B: writeFileSync(target.tmp, JSON_B) — tmp file overwritten.
  3. Process A: renameSync(target.tmp, target) — succeeds; target = JSON_B
     (A's payload silently corrupted en-route).
  4. Process B: renameSync(target.tmp, target) — throws ENOENT (the
     rename consumed the file).

Every caller in the repo wraps `writeBridgeAtomic` in `try {} catch {}`,
so the ENOENT exception is swallowed and the user-visible symptom is
just "the bridge file occasionally contains the wrong process's
payload" with no diagnostic.

Reproduced before this commit:

  $ # two concurrent writers, each calling writeBridgeAtomic 500 times
  $ # against the same session ID
  [A] errors=244   # 244 ENOENT exceptions swallowed
  [B] errors=248   # ditto

After this commit the same workload reports 0 errors in both
subprocesses: tmp paths no longer collide.

Fix: change `${target}.tmp` to
`${target}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`,
matching the pattern already used by `writeCostWarningIfChanged` in
`scripts/hooks/ecc-metrics-bridge.js` (commit 9b1d8918). The pid +
4-byte nonce gives each writer process a distinct tmp path, so step 2
above no longer overwrites step 1's payload and step 4 no longer
races step 3.

Also added: on `renameSync` failure, attempt `fs.unlinkSync(tmp)` so
a writer that fails (disk full, permission, parent dir gone) does
not leak its tmp file. The cleanup is best-effort and the original
error is still re-thrown.

**Scope clarification.** This commit closes the atomic-rename
primitive's race only. The *read-modify-write* race in callers —
two writers each read the same bridge state, increment, and write
back, the second clobbering the first — is a separate concern that
needs locking or per-writer logs, and is intentionally out of scope
for this PR. The cost-tracker / metrics-bridge callers tolerate
last-writer-wins on their cumulative aggregates today and this
commit does not change that contract.

The companion `writeWarnState` in `ecc-context-monitor.js` has the
same fixed-suffix pattern and the same race; that fix lands in the
next commit so each can be reviewed against its own diff.
2026-05-19 04:57:10 -04:00
..
install fix(installer): harden locale docs install 2026-05-17 20:46:04 -04:00
install-targets feat(installer): add --locale flag for translated docs installation 2026-05-17 20:32:52 -04:00
session-adapters feat(session): add worker health alongside state in ecc.session.v1 (#751) 2026-03-22 15:39:51 -07:00
skill-evolution feat(design): skill health dashboard mockup (#518) 2026-03-16 14:01:41 -07:00
skill-improvement fix: port safe ci cleanup from backlog 2026-04-01 16:09:54 -07:00
state-store feat: add work items CLI 2026-05-11 12:24:12 -04:00
agent-compress.js feat: agent description compression with lazy loading (#696) 2026-03-20 03:53:22 -07:00
cost-estimate.js feat: add ECC statusline observability hooks 2026-05-11 23:44:06 -04:00
cursor-agent-names.js fix: namespace cursor agent installs 2026-04-30 02:19:57 -04:00
ecc_dashboard_runtime.py fix: port hook session and dashboard safety fixes 2026-05-11 02:53:28 -04:00
github-discussions.js fix: skip disabled discussion queries in platform audit 2026-05-17 20:32:09 -04:00
harness-adapter-compliance.js feat: add zed install target 2026-05-17 07:06:49 -04:00
hook-flags.js feat: deliver v1.8.0 harness reliability and parity updates 2026-03-04 14:48:06 -08:00
inspection.js feat: agent compression, inspection logic, governance hooks (#491, #485, #482) (#688) 2026-03-20 01:38:13 -07:00
install-executor.js fix(installer): harden locale docs install 2026-05-17 20:46:04 -04:00
install-lifecycle.js fix(scripts): add os.homedir() fallback for Windows compatibility 2026-03-28 11:28:12 +08:00
install-manifests.js fix(installer): harden locale docs install 2026-05-17 20:46:04 -04:00
install-state.js feat: strengthen install lifecycle and target adapters (#512) 2026-03-15 21:47:31 -07:00
mcp-config.js feat: support disabling bundled mcp servers 2026-04-05 14:37:28 -07:00
observer-sessions.js fix: port continuous-learning observer fixes 2026-05-11 03:35:42 -04:00
orchestration-session.js feat: orchestration harness, selective install, observer improvements 2026-03-14 12:55:25 -07:00
package-manager.d.ts fix: Windows path support, error handling, and dedup in validators 2026-02-12 15:57:20 -08:00
package-manager.js Fix failing workflows: trim action in getCommandPattern and remove broken AgentShield scan 2026-02-18 08:06:25 +00:00
project-detect.js docs: salvage F# agent and language guidance 2026-05-11 21:43:17 -04:00
resolve-ecc-root.js fix: salvage remaining stale queue fixes (#1754) 2026-05-11 16:41:08 -04:00
resolve-formatter.js refactor: deduplicate config lists and unify resolveFormatterBin branches 2026-03-11 10:45:28 +09:00
session-aliases.d.ts fix: 6 bugs fixed, 67 tests added for session-manager and session-aliases 2026-02-12 15:50:04 -08:00
session-aliases.js fix: make saveAliases atomic on Unix by skipping unnecessary unlink before rename 2026-02-13 04:23:22 -08:00
session-bridge.js fix(lib): use unique tmp suffix in writeBridgeAtomic to eliminate ENOENT race 2026-05-19 04:57:10 -04:00
session-manager.d.ts fix: fold session manager blockers into one candidate 2026-03-24 23:08:27 -04:00
session-manager.js fix: finish blocker lane hook and install regressions 2026-03-25 04:00:50 -04:00
shell-split.js fix(hooks): allow tmux-wrapped dev server commands (#321) 2026-03-07 14:47:49 -08:00
shell-substitution.js fix(hooks): close grouped command bypasses in gateguard (#1912) 2026-05-15 01:39:15 -04:00
tmux-worktree-orchestrator.js fix: resolve 8 test failures on main (install pipeline, orchestrator, repair) (#564) 2026-03-18 03:03:31 -07:00
utils.d.ts fix: fold session manager blockers into one candidate 2026-03-24 23:08:27 -04:00
utils.js fix: respect home overrides in hook utilities 2026-04-02 18:22:07 -07:00