# How Orca Handles Memory Management: Architecture, Collection, and Attribution

> Discover how Orca manages memory. Learn about its architecture, resource collection, attribution to work-trees, and exposed metrics for efficient resource tracking.

- Repository: [Stably/orca](https://github.com/stablyai/orca)
- Tags: architecture
- Published: 2026-05-25

---

**Orca uses a dedicated collector in the Electron main process that snapshots OS-level process data, attributes resource usage to work-trees via PTY registration, and exposes aggregated metrics through a type-safe IPC layer.**

Orca is an Electron-based terminal workspace that tracks per-project resource consumption in real-time. Understanding how **Orca memory management** works requires examining its single-purpose collector architecture, which bridges raw operating system metrics with the renderer's React UI.

## The Collector Architecture

The memory management system lives entirely in the main process within [`src/main/memory/collector.ts`](https://github.com/stablyai/orca/blob/main/src/main/memory/collector.ts). It operates on a polling model that aggregates data from three sources: operating system processes, Electron's internal metrics API, and a local registry of pseudo-terminal (PTY) sessions.

### PTY Registration (pty-registry.ts)

Before the collector can attribute memory to specific projects, every locally-spawned terminal must register itself. In [`src/main/memory/pty-registry.ts`](https://github.com/stablyai/orca/blob/main/src/main/memory/pty-registry.ts), PTYs store their **work-tree ID**, **session ID**, **pane key**, and **PID** in a side-table.

This registry enables downstream attribution. When the collector runs, it maps system PIDs back to specific work-trees using this registration data.

```typescript
// Register a PTY when spawning a terminal (main process)
import { registerPty } from './memory/pty-registry';

registerPty({
  ptyId: terminalId,
  worktreeId: currentWorktreeId,
  sessionId: sessionId,
  paneKey: paneKey,
  pid: child.pid,               // may be null for a brief moment
});

```

### Process Enumeration (enumerateUnix/enumerateWindows)

On each snapshot cycle, the collector executes platform-specific commands to build a `ProcIndex` map. **`enumerateUnix()`** parses `ps` output on macOS and Linux, while **`enumerateWindows()`** queries `wmic` on Windows.

Both functions return a flat list of all OS processes including PID, PPID, CPU percentage, and RSS (Resident Set Size). These values populate the `ProcIndex` used for subsequent tree walking.

### Electron Process Bucketing (bucketElectronMetrics)

The collector retrieves Electron-specific metrics via `app.getAppMetrics()`. The **`bucketElectronMetrics()`** function categorizes these into **main**, **renderer**, and **other** buckets, converting working-set sizes from kilobytes to bytes for consistency with OS-level data.

## Work-Tree Attribution and Aggregation

Raw process data remains useless until mapped to user projects. Orca solves this by walking process trees and aggregating resource consumption into `WorktreeBucket` objects.

### Tree Walking and Deduplication (collectSubtree)

The **`collectSubtree`** function in [`src/main/memory/collector.ts`](https://github.com/stablyai/orca/blob/main/src/main/memory/collector.ts) recursively walks the process tree for each registered PTY. It sums CPU and memory for every descendant PID while tracking a `claimed` set to prevent double-counting shared child processes.

```typescript
// Excerpt from collectMemorySnapshot in collector.ts
export async function collectMemorySnapshot(store: Store): Promise<MemorySnapshot> {
  const procIndex = await enumerateProcesses();           // ps / wmic
  const appBuckets = bucketElectronMetrics();            // Electron metrics
  const ptys = listRegisteredPtys();                     // PTY side-table

  // Walk each PTY's subtree, sum CPU / memory, avoid double-counting
  for (const pty of ptys) { … }

  // Assemble final snapshot
  return {
    app: { … },
    worktrees,
    host: hostMetrics(),
    collectedAt: Date.now(),
  };
}

```

### Ring Buffer History (HISTORY_CAPACITY)

To support UI sparklines without unbounded growth, the collector maintains time-series data in ring buffers. **`HISTORY_CAPACITY`** is set to 60 samples per key, with stale entries purged after **`HISTORY_STALE_MS`** (10 minutes) via `sweepStaleHistory`.

The `pushHistorySample` function updates these buffers during each snapshot, while `readHistory` retrieves the current window for the renderer.

## IPC and Renderer Integration

The collector exposes data through a strict IPC boundary, preventing the renderer from directly accessing Node APIs.

### Main Process IPC Handler (memory:getSnapshot)

In [`src/main/ipc/memory.ts`](https://github.com/stablyai/orca/blob/main/src/main/ipc/memory.ts), the **`memory:getSnapshot`** channel handler invokes `collectMemorySnapshot(store)` and returns the serialized `MemorySnapshot` type defined in [`src/shared/types.ts`](https://github.com/stablyai/orca/blob/main/src/shared/types.ts).

### Preload Bridge (window.api.memory.getSnapshot)

The preload script at [`src/preload/index.ts`](https://github.com/stablyai/orca/blob/main/src/preload/index.ts) (around line 2786) wraps the IPC handler, exposing `window.api.memory.getSnapshot()` to the isolated renderer context. This maintains Electron's security model while providing type-safe access.

```typescript
// Request a memory snapshot from the renderer (e.g. in a React hook)
await window.api.memory.getSnapshot(); // returns a MemorySnapshot

```

### Zustand State Slice (createMemorySlice)

The renderer caches snapshots in a Zustand slice located at [`src/renderer/src/store/slices/memory.ts`](https://github.com/stablyai/orca/blob/main/src/renderer/src/store/slices/memory.ts). The **`createMemorySlice`** function deduplicates concurrent requests, manages loading states, and surfaces errors to the UI.

```typescript
// The slice that performs the request (simplified)
const fetchMemorySnapshot = async () => {
  const snap = await window.api.memory.getSnapshot(); // IPC call
  set({ memorySnapshot: snap, memorySnapshotError: null });
};

```

## Host-Level Metrics

Beyond per-work-tree attribution, the collector records machine-wide statistics via the **`hostMetrics()`** function. This uses Node's built-in `os` module to capture total system memory and CPU load, providing context for the relative percentages displayed in Orca's status bar.

UI components in [`src/renderer/src/components/status-bar/mergeSnapshotAndSessions.ts`](https://github.com/stablyai/orca/blob/main/src/renderer/src/components/status-bar/mergeSnapshotAndSessions.ts) merge this snapshot data with session information to render the final resource indicators.

## Summary

- **Orca's memory management** relies on a main-process collector that snapshots OS processes via `ps` or `wmic` and Electron's `app.getAppMetrics()`.
- **PTY registration** in [`pty-registry.ts`](https://github.com/stablyai/orca/blob/main/pty-registry.ts) enables attribution of arbitrary child processes to specific work-trees through PID tracking.
- **Tree-walking logic** in `collectSubtree` aggregates resource usage while preventing double-counting through a `claimed` set.
- **Ring buffers** with automatic expiration (60 samples, 10-minute staleness) provide trend data without memory leaks.
- **IPC isolation** via `memory:getSnapshot` and the preload bridge ensures security while exposing data through `window.api.memory.getSnapshot()`.
- **Zustand slices** handle caching and error states, feeding React components that display per-project memory usage in real-time.

## Frequently Asked Questions

### How does Orca prevent memory leaks in its history tracking?

Orca uses constant-size ring buffers (`HISTORY_CAPACITY = 60`) and automatic stale entry removal (`HISTORY_STALE_MS = 10 minutes`). The `sweepStaleHistory` function runs during each collection cycle, ensuring history data never grows unbounded regardless of uptime.

### Can Orca track memory for processes spawned outside of its terminals?

No. Attribution requires PTY registration via `registerPty()` in [`src/main/memory/pty-registry.ts`](https://github.com/stablyai/orca/blob/main/src/main/memory/pty-registry.ts). Only processes descending from registered PTYs (and their children) appear in work-tree buckets. Host-level metrics capture system-wide totals, but per-project attribution depends on terminal registration.

### Why does the collector run in the Electron main process instead of the renderer?

The collector requires Node.js APIs (`child_process` for `ps`/`wmic`, `os` for host metrics) and direct access to `app.getAppMetrics()`. Running in the main process maintains Electron's security boundaries while the preload script exposes only the specific `getSnapshot()` method to the renderer via IPC channels.

### What happens if the snapshot request fails?

The Zustand slice in [`src/renderer/src/store/slices/memory.ts`](https://github.com/stablyai/orca/blob/main/src/renderer/src/store/slices/memory.ts) catches IPC failures and stores the error in `memorySnapshotError`. The UI can then display fallback states or retry logic without crashing the application, with the last successful snapshot remaining available for reference.