C-H2: гонка stopping-флага в обработчике выхода `RoxDesignRuntimeManager.start()`/`stop()`
## Что это
## Резюме (RU)
C-H2: гонка stopping-флага в обработчике выхода `RoxDesignRuntimeManager.start()`/`stop()`
*Полный перевод тела — в работе. Английский оригинал ниже.*
---
# Original (EN)
## Objective
Eliminate the race condition in `RoxDesignRuntimeManager` where the `child.once("exit")` handler fires during an intentional `stop()` call and spuriously emits `rox-design:sidecar-exited` IPC to the renderer.
**Audit source:** [2026-05-20 PR #268 Release-Readiness Audit]() — Finding **C-H2** (HIGH)
**Suggested branch:** `fix/rox-design-runtime-stopping-flag-race-t543-c-h2`
---
## Source Links
* Audit: [https://github.com/agisota/rox.one/blob/main/docs/audits/2026-05-20-pr268-release-readiness-audit.md]()
* File: `packages/electron/src/main/rox-design/RoxDesignRuntimeManager.ts`
---
## Current State
`child.once("exit")` is registered unconditionally inside `start()`. When `stop()` calls `child.kill()`, the exit handler fires and emits `rox-design:sidecar-exited` to the renderer — identical to a crash path — causing the UI to show an unexpected error banner during clean shutdown.
---
## Requirements
1. Introduce a `private stopping = false` flag on `RoxDesignRuntimeManager`.
2. Set `stopping = true` at the top of `stop()` before any `child.kill()` call.
3. Guard the `child.once("exit")` handler: if `this.stopping` is `true`, return early without emitting `rox-design:sidecar-exited`.
4. Reset `stopping = false` after `stop()` fully completes (child confirmed dead or timeout).
---
## Specification
```typescript
class RoxDesignRuntimeManager {
private stopping = false;
async stop(): Promise {
this.stopping = true;
// ... kill child, await exit ...
this.stopping = false;
}
private registerExitHandler(child: ChildProcess): void {
child.once("exit", (code, signal) => {
if (this.stopping) return; // intentional shutdown — do not emit
this.emit("rox-design:sidecar-exited", { code, signal });
});
}
}
```
---
## Acceptance Criteria
- [ ] `stop()` sets `stopping = true` before `child.kill()`.
- [ ] `exit` handler short-circuits when `stopping === true`.
- [ ] `rox-design:sidecar-exited` is NOT emitted during normal `stop()`.
- [ ] `rox-design:sidecar-exited` IS still emitted on unex...
## Статус
Это задача из текущего backlog'а ROX.ONE (Linear). Текущий статус в Linear: `На ревью — In Review`. Метки: `Баг` / `Bug`.
## 🔗 Linear
- [PZD-105](https://linear.app/kuhjie/issue/PZD-105/c-h2-gonka-stopping-flaga-v-obrabotchike-vyhoda) — backing ticket
- Parent epic: [PZD-119](https://linear.app/kuhjie/issue/PZD-119)