Skip to content

Fix 11 memory leaks and add 35 tests#8

Open
vsumner wants to merge 2 commits into
tintinweb:masterfrom
vsumner:fix/memory-leaks-and-test-coverage
Open

Fix 11 memory leaks and add 35 tests#8
vsumner wants to merge 2 commits into
tintinweb:masterfrom
vsumner:fix/memory-leaks-and-test-coverage

Conversation

@vsumner

@vsumner vsumner commented Mar 18, 2026

Copy link
Copy Markdown

Summary

  • Fix 11 resource leak / memory management issues (P0–P3) found via code review
  • Add 35 new tests across 4 files covering all changed code paths
  • All 226 tests pass (4 pre-existing failures in custom-agents.test.ts are environment-specific — test doesn't isolate from real ~/.pi/agent/agents/)

Fixes

P0 — Session shutdown cleanup (index.ts)

  • session_shutdown now disposes groupJoin, widget, clears agentActivity, batchFinalizeTimer, currentBatchAgents, and calls resetDefaults()

P0 — outputCleanup in dispose (agent-manager.ts)

  • dispose() and removeRecord() call record.outputCleanup?.() before session.dispose()

P0 — AbortController nulling (agent-manager.ts)

  • .then() and .catch() handlers null record.abortController after completion

P1 — ResourceLoader disposal (agent-runner.ts)

  • finally block calls (loader as any).dispose?.() to release file watchers

P1 — maxBuffer on execFileSync (worktree.ts)

  • Added 10MB maxBuffer to all git status/add/commit/worktree-add calls

P2 — finishedTurnAge pruning (agent-widget.ts)

  • onTurnStart() prunes entries exceeding ERROR_LINGER_TURNS

P2 — Stale tool descriptions (index.ts)

  • Removed cached const typeListText, calls buildTypeListText() inline

P2 — currentCtx on session_switch (index.ts)

  • session_switch handler accepts and updates ctx if provided

P3 — waitForAll timeout (agent-manager.ts)

  • Added timeoutMs parameter (default 5 minutes) with Promise.race

P3 — Async output writes (output-file.ts)

  • Normal flushes use async appendFile with batching; final cleanup uses sync for safety

P3 — Global state reset (agent-runner.ts)

  • Added resetDefaults() export, called from shutdown handler

New tests (35 total)

File Tests Covers
test/agent-runner.test.ts 8 resetDefaults(), get/set maxTurns, get/set graceTurns, min-clamping
test/group-join.test.ts 12 registerGroup, isGrouped, onAgentComplete (pass/held/delivered), timeout, stragglers, dispose
test/output-file.test.ts 8 createOutputFilePath, writeInitialEntry, sync/async flush, no-duplication, toolResult
test/agent-manager.test.ts 7 (added) outputCleanup in dispose/clearCompleted, error swallowing, abortController lifecycle, waitForAll

Test plan

  • npx tsc --noEmit — clean typecheck
  • npx vitest run — 226 passed, 4 failed (pre-existing, unrelated)
  • Manual smoke test with background agents to verify no regressions in agent lifecycle

vsumner added 2 commits March 18, 2026 15:01
Resource leaks fixed:
- Session shutdown now disposes groupJoin, widget, agentActivity, batch timer
- outputCleanup called in dispose() and removeRecord() before session disposal
- AbortController nulled after completion/error to prevent retention
- DefaultResourceLoader disposed in agent-runner finally block
- execFileSync maxBuffer (10MB) added to all git calls in worktree.ts
- finishedTurnAge entries pruned when exceeding ERROR_LINGER_TURNS
- Stale typeListText cache replaced with inline buildTypeListText()
- session_switch now updates currentCtx when provided
- waitForAll gets timeout parameter (default 5min) with Promise.race
- output-file rewritten to use async writes with batching
- resetDefaults() added to agent-runner for global state cleanup

Test coverage added:
- agent-runner: resetDefaults, get/set for maxTurns and graceTurns
- group-join: registration, completion flow, timeout, stragglers, dispose
- output-file: JSONL format, async/sync flush, no-duplication, toolResult
- agent-manager: outputCleanup, abortController lifecycle, waitForAll
P1 fixes:
- Extract releaseRecordResources() helper to deduplicate 4x cleanup pattern
- Replace dynamic require("node:fs") with static appendFileSync import
- Clear timeout timer in waitForAll when Promise.allSettled wins the race

P2 fixes:
- Fix asyncFlush/syncFlush race: advance writtenCount only after successful write
- Narrow (loader as any) cast to { dispose?(): void }
- Replace flaky 50ms sleeps in tests with deterministic file-size polling
- Expand inline batchFinalizeTimer cleanup to multi-line

P3 fixes:
- Remove redundant _INITIAL suffix from constants (DEFAULT_MAX_TURNS, GRACE_TURNS)
- Shorten over-verbose widget prune comment to inline
- Use queueMicrotask for asyncFlush re-entry instead of direct recursion
@vsumner
vsumner force-pushed the fix/memory-leaks-and-test-coverage branch from 8754b53 to ea19241 Compare March 18, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant