diff --git a/docs/reference/memory-leak-audit-final-comb.md b/docs/reference/memory-leak-audit-final-comb.md new file mode 100644 index 000000000..0608705f1 --- /dev/null +++ b/docs/reference/memory-leak-audit-final-comb.md @@ -0,0 +1,61 @@ +# Memory Leak Audit Final Comb + +Started: 2026-05-29 PDT + +Final tracker branch: `nwparker/mem-leak-final-comb-tracker`, based on `origin/main` at `869661cbba` after the final-comb fix PRs merged. + +Objective: run one more complete pass across the current Orca tree for missed listener, timer, observer, worker, socket, watcher, subscription, abort, and disposable leaks; open scoped PRs for confirmed stranglers; include risk level and merge low-risk PRs. + +## Codebase Inventory + +Tracked code files, from `git ls-files`: + +| Bucket | Files | Status | +| ----------------- | ----: | ------- | +| `src/renderer` | 1584 | Checked | +| `src/main` | 856 | Checked | +| `src/shared` | 245 | Checked | +| `mobile/src` | 100 | Checked | +| `tests/e2e` | 72 | Checked | +| `src/relay` | 65 | Checked | +| `src/cli` | 65 | Checked | +| `config/scripts` | 33 | Checked | +| `mobile/app` | 16 | Checked | +| `other-code` | 5 | Checked | +| `src/preload` | 6 | Checked | +| `mobile/packages` | 5 | Checked | +| Total | 3052 | Checked | + +## Scan Log + +- 2026-05-29: Created fresh final-comb branch from current `origin/main`. +- 2026-05-29: Fast-forwarded the final-comb branch as `origin/main` moved; final tracker is based on `869661cbba`. +- 2026-05-29: Counted 3341 broad risk-pattern hits across `src`, `mobile`, `tests`, `config`, and `native` on the final post-fix tree. +- 2026-05-29: Re-ran heuristic buckets after fixes: + - `addEventListener` without same-file `removeEventListener`: 37 remaining. Reviewed as React Native subscriptions with `.remove()`, injected WebView document-lifetime scripts, `{ once: true }` image load handlers, IPC abort-signal test listeners, singleton image-cache invalidation, owned pane-divider DOM, and test fixture listeners. + - `setInterval` without `clearInterval`: 1 remaining, a test comment string. + - `setTimeout` without same-file `clearTimeout`: 233 remaining. Reviewed remaining main-process hits as sleep helpers, intentional app relaunch/exit delays, socket idle timeout ownership, usage-scanner yield points, and startup force-exit behavior. Confirmed final-comb timer findings are fixed in PRs listed below. + - Observers without `disconnect`: 0. + - Workers without `terminate`: 0. + - Abort controllers without `abort`: 1 test-only controller passed through the abortable API under test. + - Watchers without close/unwatch: no production misses; remaining heuristic hits are provider type/comment references. +- 2026-05-29: Re-ran React effect scan and spot-checked candidates. Remaining hits had cleanup returns (`clearTimeout`, `clearInterval`, `removeEventListener`, IPC unsubscribe) or were short one-shot UI focus/open delays. + +## Findings + +| PR | Risk | Status | Finding | Resolution | +| -- | ---- | ------ | ------- | ---------- | +| [#3305](https://github.com/stablyai/orca/pull/3305) | LOW | Merged | Browser reload fallback timer retained the reload promise closure, webContents reference, and listeners until the 10s fallback fired even after `did-finish-load`/`did-fail-load`. | Clear the fallback timer on early settle, guard duplicate cleanup, and test with fake timers. | +| [#3306](https://github.com/stablyai/orca/pull/3306) | LOW | Merged | Native notification retention timers stayed scheduled after close/click cleanup, retaining notification closures until fallback expiry. | Clear notification fallback timers on release, detach native listeners, and cover dispatch/startup/accessibility notification paths. | +| [#3307](https://github.com/stablyai/orca/pull/3307) | LOW | Merged | Local-network permission UDP prompt kept its 1s fallback timer and socket error listener after the send callback settled first. | Clear the fallback timer, remove the error listener, and test the `developerPermissions:request` local-network path. | + +No higher-risk findings remained after the final pass, so no second higher-risk mitigation pass was needed. + +## Validation + +- `pnpm vitest run --config config/vitest.config.ts src/main/browser/agent-browser-bridge.test.ts` +- `pnpm vitest run --config config/vitest.config.ts src/main/browser/agent-browser-bridge.test.ts src/main/ipc/notifications.test.ts src/main/computer/permissions.test.ts src/main/ipc/developer-permissions.test.ts` +- `pnpm vitest run --config config/vitest.config.ts src/main/ipc/notifications.test.ts src/main/computer/permissions.test.ts` +- `pnpm vitest run --config config/vitest.config.ts src/main/ipc/developer-permissions.test.ts` +- `pnpm exec oxlint src/main/browser/agent-browser-bridge.ts src/main/browser/agent-browser-bridge.test.ts src/main/ipc/notifications.ts src/main/ipc/notifications.test.ts src/main/computer/permissions.ts src/main/computer/permissions.test.ts src/main/ipc/developer-permissions.ts src/main/ipc/developer-permissions.test.ts` +- `pnpm run typecheck:node`