← Back to PRs

#21728: fix(boot): deduplicate startup notification within restart cycle

by irchelper open 2026-02-20 08:59 View on GitHub →
gateway size: S
## Problem When the gateway recovers from a repeated config-invalid restart loop, multiple boot sessions may have been queued (one per restart cycle). All of them can complete in a short window, and each independently sends the startup notification defined in `BOOT.md`. The user ends up receiving duplicate online messages. **Log evidence** (2026-02-20 08:10–08:15 config-invalid loop, recovered at 08:16): - `boot-2026-02-20_08-16-21-094-3b733d68` → sent notification ✓ - `boot-2026-02-20_08-16-58-928-b27bd200` → sent duplicate notification ✗ ## Root Cause `startGatewaySidecars` is called on every gateway restart. Each call fires a `gateway:startup` hook event that is handled by `runBootChecklist` → `runBootOnce`. There was no guard to prevent `runBootOnce` from executing more than once per OS-process lifetime. ## Fix Approach Added a module-level boolean flag `_bootNotificationSent` in `src/gateway/boot.ts`. - The flag is checked **synchronously** at the top of `runBootOnce`. If it is already set, the function returns immediately with `{ status: "skipped", reason: "already-ran" }`. - The flag is set **synchronously** (before any `await`) once we decide to proceed, making the read-then-write atomic at the JavaScript level (Node.js is single-threaded). This prevents race conditions from concurrent event-loop ticks. - Because the flag lives in module scope it is **automatically reset** when the gateway daemon process exits and restarts — the right granularity for this dedup. - A test-only `_resetBootDedup()` export allows unit tests to reset the flag between cases. **Files changed:** | File | Change | |------|--------| | `src/gateway/boot.ts` | Add dedup flag + guard in `runBootOnce`; update `BootRunResult` type | | `src/gateway/boot.test.ts` | Add `_resetBootDedup` to `beforeEach`; add 3 dedup-specific test cases | ## Testing Unit tests added in `src/gateway/boot.test.ts` under a new `startup notification deduplication` describe block: 1. **First run sends notification** — `agentCommand` is called once. 2. **Second run within same process is skipped** — `agentCommand` is **not** called a second time; result is `{ status: "skipped", reason: "already-ran" }`. 3. **`_resetBootDedup` restores normal behaviour** — simulates a fresh OS-process restart; the next run is allowed through. All 10 tests pass (`pnpm vitest run src/gateway/boot.test.ts`). `pnpm check` (format + type-check + lint) exits clean. <!-- greptile_comment --> <h3>Greptile Summary</h3> Prevents duplicate startup notifications when the gateway recovers from repeated restart cycles by adding a module-level deduplication flag. The flag is checked synchronously at the top of `runBootOnce` and set before any async operations, ensuring atomicity in Node.js's single-threaded event loop. The flag naturally resets on full process restart, which is the correct granularity for this dedup. Implementation includes comprehensive test coverage for first run, subsequent runs within the same process, and reset behavior. <h3>Confidence Score: 5/5</h3> - Safe to merge - implements targeted fix with comprehensive test coverage - The fix correctly addresses the duplicate notification issue with a simple, atomic deduplication mechanism. The flag is set synchronously before any async operations, preventing race conditions. Test coverage is thorough, including reset behavior for unit tests. The module-level scope ensures automatic cleanup on process restart, which is the correct granularity for this use case. - No files require special attention <sub>Last reviewed commit: d0a3502</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs