Neil cc9e9ed65f fix(crash-reporting): sample system memory before the process is gone (#18356)
* fix(crash-reporting): sample system memory before the renderer dies

* fix(crash-reporting): make the pre-gone host sample decisive, not just present

Round-1 review said the shipped field set could not decide G4-oom. Fixed.

Decisive field (blocking #1). The investigation's own win-lowspec repro
falsified "low available commit kills": at a 127 MB commit floor Windows grew
the pagefile to 2029 MB and nothing died, and it named the missing datum —
pagefile-growth headroom / system-drive free space. `getSystemMemoryInfo()`
gives neither. Added `swap-volume-free-space.ts`: one `fs.statfs` on the volume
backing the pagefile (SystemRoot on Windows, the root fs elsewhere, resolved
via `path.parse().root`), published as `systemMemoryPreGoneSwapVolumeFreeMB`.
Together with the already-emitted commit limit that separates "commit was low"
from "commit was refused". Pagefile *max* size needs a registry read; skipped
deliberately — per-operation interpreter spawning is exactly what
docs/reference/windows-edr-posture.md says not to add for telemetry.

Darwin honesty (blocking #2). Every reading now carries
`systemMemoryPressureSignal`: `available-commit` on Windows (swapFree is
ullAvailPageFile), `mem-available` on Linux when MemAvailable is present,
`none` otherwise — which is always on darwin. A future analyst cannot now table
`freeMB: 272` from a healthy Mac as evidence of exhaustion, because the same
record says the platform gave no pressure verdict. Partial rebuttal on the
suggested reuse: `host-memory.ts:86` was considered and rejected as a periodic
source. It spawns `/usr/bin/memory_pressure` per call, and the sampler this PR
needs runs every 10 s for the app's lifetime; a subprocess at that cadence is
worse than the gap it closes. The reviewer conceded this tradeoff is arguable —
what was not acceptable was shipping the darwin gap silently, so it is now in
the data, not only in a comment.

Staleness (blocking #3). Confirmed the measurement: four of five G4 reports
carried a ~37 s-old sample (4872/36796/37332/38017/39715 ms). Host memory no
longer rides the 60 s process-metrics sweep; `pre-gone-host-memory.ts` samples
it on its own 10 s timer with its own `systemMemoryPreGoneSampleAgeMs`. One
GlobalMemoryStatusEx-class call plus one statfs is cheap enough at that rate. A
refusal shorter than the interval stays invisible and the module comment says
so — no polling cadence fixes that.

Non-blocking, all taken: renamed `gone-time-system-memory.ts` ->
`system-memory-details.ts` with the now-false "reads AFTER the crash" framing
scoped to the gone-time caller; pre-gone host keys moved out of the
`processMetrics` namespace to `systemMemoryPreGone*`, so the string-surgery
`preGoneDetailKey` helper is gone and a `systemMemory` prefix scan sees both
reads; the bare catch no longer spans both halves of the sample, and a test
pins that a throwing host read leaves the process-metric sample intact; the
inert second test is replaced by three that go red without this change
(verified: swap-volume, pressure-signal and cadence assertions all fail when
the production hunks are reverted).

Rebuttal, non-blocking #5 (duplicated electron mock across two test files):
declined. `vi.mock` is hoisted per file, so the mock cannot be shared without a
setup module, and this directory already has 26 focused test files that each
re-declare it. Splitting by concern is the local convention.

`startPreGoneProcessMetricsSampling` is renamed `startPreGoneCrashSampling`
since it now starts two samplers.

* fix(crash-reporting): test the arming, gate the swap volume, unblock the host read

Round-2 review blocked on four items. All four addressed.

WHAT THIS BRANCH ACTUALLY DOES, AT HEAD (blocking #4). The commit-1 message
("13 lines, 1 production file, no new module, new optional numeric fields
only", `processMetricsPreGoneSystemMemory*` keys, a `preGoneDetailKey` helper,
a `pre-gone-system-memory.test.ts`) describes a superseded revision; every one
of those claims is false now, so it must not be used as the PR description.
The change against origin/main is: 3 new production modules
(`pre-gone-host-memory.ts`, `system-memory-details.ts`,
`swap-volume-free-space.ts`), 1 deleted (`gone-time-system-memory.ts`), plus
edits to `process-gone-diagnostics.ts` and `main-process-ready-runtime.ts` and
2 test files. It adds a second main-process interval timer that runs for the
life of the app: every 10 s one synchronous GlobalMemoryStatusEx-class read,
and on win32/darwin one `fs.statfs` on the swap-backing volume. Details are
`systemMemoryPreGone*`, and two of them are STRINGS, not numbers:
`systemMemoryPreGonePressureSignal` (enum) and `systemMemoryPreGoneSwapVolume`
(a drive label, separator-trimmed so it is not a path). Both are assigned after
`sanitizeCrashReportDetails`; neither carries user content.

Arming is now tested (blocking #1). The reviewer deleted
`startPreGoneSystemMemorySampling(...)` from `startPreGoneCrashSampling` and
all 264 tests stayed green — confirmed and fixed. `pre-gone-host-memory.test.ts`
now calls `startPreGoneCrashSampling()` with production defaults and asserts
both `setInterval` calls, their literal periods `[60_000, 10_000]`, that both
timers are unref'd, and that advancing 10 s takes a fresh host sample that
reaches `buildProcessGoneCrashDetails` with `SampleAgeMs: 0`. Verified red on
revert: deleting the arming line -> 1 failure; changing the interval constant
to 30_000 -> 1 failure (the old assertion compared the constant to itself and
caught neither). The tautological `10_000 < 60_000 / 2` test is gone,
superseded by this one.

Swap volume is win32/darwin only (blocking #2). On Linux swap is a fixed
partition, a fixed-size swapfile, or zram; none grow into root-fs free space,
so `SwapVolumeFreeMB: 380000` beside `SwapFreeMB: 0` would have invited exactly
the wrong verdict on the two Linux cluster members. `swapVolumeAnchor` returns
undefined off win32/darwin, so no field and no statfs at all. The comment
claiming "elsewhere swap is on the root fs" was wrong and is gone. The Windows
anchor is still the DEFAULT pagefile volume, so the measured volume now ships
with the number (`systemMemoryPreGoneSwapVolume: 'C:'`) instead of being
implied. The honesty label covers it: win32 reads `available-commit` only when
the volume datum is present, and `available-commit-unqualified` otherwise —
which also fixes non-blocking #5, where the synchronous gone-time read claimed
a verdict its own fields could not support.

Host read no longer waits on statfs (blocking #3). `samplePreGoneSystemMemory`
now commits the synchronous memory reading first and merges volume free space
in afterwards, so the cadence is 10 s regardless of disk-metadata latency and a
hung volume can no longer stop host sampling — precisely the paging-storm case
this exists for. The in-flight latch now guards only the statfs. Verified red
on revert to the serialized shape (2 failures). A stale-but-slow-moving volume
value merging into a newer memory sample is deliberate and commented.

Non-blocking #3 (reset does not invalidate an in-flight sample): fixed with a
generation counter bumped by `resetPreGoneSystemMemorySamplingForTest`, so a
late statfs cannot repopulate a reset sample. Separately, the volume read now
only runs after a host sample committed, which removes the real `statfs('/')`
side effect from `process-gone-diagnostics.test.ts` entirely.

REBUTTAL, darwin `memory_pressure` reuse (non-blocking #2): declined, with
evidence. `readDarwinAvailableMemory` at src/main/memory/host-memory.ts:87 is
reached only via `collectHostMemory` <- `runSnapshot` <- `collectMemorySnapshot`,
whose only callers are the `memory:getSnapshot` IPC handler and
orca-runtime-pty-foreground-process-reads.ts:170 — both on demand. There is no
periodic snapshot, so there is no cached reading to reuse for free; adopting it
means spawning `/usr/bin/memory_pressure` on a main-process timer for the life
of the app, and its module-global `darwinAvailabilitySupported` latch is shared
with the memory UI. The gap is not hidden: darwin ships
`PressureSignal: 'none'` in the data, and the module comment now cites the
existing reader and why it is not used here rather than claiming Orca lacks
one.

Verified: `vitest src/main/crash-reporting src/main/startup src/main/memory` =
769 passed / 6 skipped (crash-reporting re-run 5x, no flake);
`tsc --noEmit -p config/tsconfig.node.json` 0; `oxlint` 0; `oxfmt --check` 0.

* fix(crash-reporting): stop a stale statfs qualifying the commit verdict

Round-3 adversarial review, 2 blocking. Both fixed with mutation-verified
tests.

1. `mergeSwapVolumeFreeSpace` merged the volume reading into whatever sample
   was current at RESOLUTION time, and `pressureSignal` then upgraded win32
   from `available-commit-unqualified` to the decisive `available-commit` on
   the strength of it. The `swapVolumeReadInFlight` latch makes every
   intervening tick skip the merge, so the lag is as old as the last STARTED
   statfs, not the last tick — and no age field exposed it, because
   `systemMemoryPreGoneSampleAgeMs` describes only the synchronous memory read.

   Reviewer's executed scenario: a statfs issued at t=0 on a healthy host
   (40 GB free) resolving at t=20 s of commit pressure emitted
   `SwapFreeMB: 200` beside `SwapVolumeFreeMB: 40000`, labelled
   `available-commit`, with `SampleAgeMs: 0`. That reads as "the pagefile had
   room, so this was not a commit refusal" — the opposite conclusion, wearing
   the branch's highest-confidence label, on exactly the win32 G4-oom reports
   this exists to decide.

   The datum still ships (it is the only pagefile-expandability signal there
   is), but now:
   - the sample carries `swapVolumeSampledAtMs` — the tick that ISSUED the
     statfs, never the one it resolved on — surfaced as
     `systemMemoryPreGoneSwapVolumeAgeMs`;
   - only a statfs that answers on its own tick may qualify the verdict.
     `withSwapVolumeFreeSpace` takes `coTimed`; false keeps
     `available-commit-unqualified`.
   The next tick issues a fresh statfs, so the verdict recovers on its own.

2. The branch's sole production entry point — `startPreGoneCrashSampling()` at
   main-process-ready-runtime.ts:128 — was untested. Deleting it left 691
   tests across crash-reporting/ and startup/ green, while a comment in the
   new test file claimed that gap was why the test was written. This is pure
   instrumentation, so that one line is the whole of its value in the shipped
   app. Added a source-level wiring test (the pattern this repo already uses
   for arm-once ready-phase lines) that pins the import, exactly one call, the
   call at statement indent, and that `main-process-ready.ts` awaits the
   function it lives in. The misleading comment is gone.

Mutation-verified — each goes red alone:
  coTimed -> always true                     1 failed (verdict)
  drop swapVolumeSampledAtMs age             1 failed (verdict test)
  delete startPreGoneCrashSampling()         1 failed (wiring)
  wrap it in `if (!is.dev) { ... }`          1 failed (wiring)

Verified: tsc -p config/tsconfig.node.json exit 0; oxlint
src/main/crash-reporting src/main/startup exit 0; 268 tests in
crash-reporting/ pass. Across crash-reporting/ + startup/ + memory/: 769
passed, 2 failed — both environment-dependent and failing identically on the
unmodified tree (Xvfb rebind, and a whole-repo glob census that times out).

* fix(crash-reporting): stop free disk standing in for pagefile growability

The win32 reading was promoted to the decisive `available-commit` whenever a
co-timed volume number merely existed, which the data cannot support: a fixed
or disabled pagefile grows into no amount of empty disk, its maximum is
unreadable here, and the measured volume is only the DEFAULT pagefile drive. A
host with 180 MB of available commit, a commit limit at RAM and 812 GB free
read as "the pagefile had room" — the opposite conclusion, under the branch's
most confident label.

The volume datum is now named for what it is (`available-commit-volume-cotimed`,
context beside the commit number), and the one decisive win32 case — a commit
limit at or below RAM, i.e. no pagefile behind it — gets its own label.

Also: carry the last volume reading onto the sample that replaces it, aged and
non-qualifying, so a statfs slower than one tick no longer makes the field
vanish from the reports it exists for; don't commit a reading whose every
memory field failed, which shipped an age and a disk-free number with no host
memory beside them; and move the startup wiring test beside the file it pins,
scoped to the ready-phase entry's own body so the call cannot satisfy it from
a sibling export nothing calls.

* fix(crash-reporting): co-time the statfs by tick, not sample identity

A tick whose host read fails leaves the pre-gone sample object in place, so
the identity check still read a 25 s-late statfs as co-timed.
2026-09-04 00:53:37 -07:00
2026-09-03 17:32:59 -07:00
2026-05-04 20:42:03 -07:00
2026-03-16 22:27:51 -07:00
2026-03-28 10:19:14 -07:00

Orca Orca

GitHub stars Total downloads across all releases License: MIT Join the Orca Discord Follow Orca on X Supported platforms: macOS, Windows, and Linux

中文 · 日本語 · 한국어 · Español · Français · Português

The AI Orchestrator for 100x builders.
Run Codex, ClaudeCode, OpenCode or Pi side-by-side — each in its own worktree, tracked in one place.

Download Orca

Orca desktop app running agents in parallel worktrees, with the Orca mobile companion app in the corner

Features

Mobile Companion

Monitor and steer your agents from your phone — get notified when an agent finishes and send follow-ups from anywhere.

iOS App Store · TestFlight · Android APK 0.0.47 · Docs →

Orca desktop with the mobile companion app

Parallel Worktrees

Fan one prompt across five agents, each in its own isolated git worktree — compare the results and merge the winner.

Docs →

Parallel worktree orchestration

Terminal Splits

Ghostty-class terminals with WebGL rendering, infinite splits, and scrollback that survives restarts.

Docs →

Terminal splits

Design Mode

Click any UI element in a real Chromium window to send its HTML, CSS, and a cropped screenshot straight into your agent's prompt.

Docs →

Embedded browser and Design Mode

GitHub & Linear, Native

Browse PRs, issues, and project boards in-app — open a worktree from any task and review without a context switch.

Docs →

GitHub and Linear task workflows in Orca

SSH Worktrees

Run agents on a beefy remote box with full file editing, git, and terminals — auto-reconnect and port forwarding included.

Docs →

Remote worktrees over SSH

Annotate AI Diffs

Drop comments on any diff line and ship them back to the agent — review, edit, and commit without leaving Orca.

Docs →

Annotate AI-generated diffs

Drag Files to Agents

VS Code's editor with autosave everywhere — drag files or images straight into an agent prompt.

Docs →

Drag files and images into an agent prompt

Orca CLI

Agents drive Orca too — script every workflow with orca worktree create, snapshot, click, and fill.

Docs →

Script Orca from the CLI

Also in the box:

  • Quick open — Search across worktrees, files, agents, commands, and repo context without leaving your flow.
  • Account switcher & usage tracking — See Claude and Codex usage and rate-limit resets, and hot-swap accounts without re-logging in.
  • Rich repo previews — Preview Markdown, images, PDFs, and repo docs in the workspace.
  • Computer Use — Let agents operate desktop apps and visible UI when a workflow needs real interaction.
  • Notifications and unread state — Know when an agent finishes or needs attention, then mark threads unread to come back later.
  • And many, many more — we ship daily, so this list is perpetually behind. The changelog is the real feature list.

Supported Agents

Works with any CLI agent — if it runs in a terminal, it runs in Orca.

Claude Code logo Claude Code   Codex logo Codex   Grok logo Grok   Cursor logo Cursor   GitHub Copilot logo GitHub Copilot   OpenCode logo OpenCode   MiMo Code logo MiMo Code   Amp logo Amp   OpenClaude logo OpenClaude   Antigravity logo Antigravity   Pi logo Pi   oh-my-pi logo oh-my-pi   Hermes Agent logo Hermes Agent   Devin logo Devin   Goose logo Goose   Auggie logo Auggie   Autohand Code logo Autohand Code   Charm logo Charm   Cline logo Cline   Codebuff logo Codebuff   Command Code logo Command Code   Continue logo Continue   Droid logo Droid   Kilocode logo Kilocode   Kimi logo Kimi   Kiro logo Kiro   Mistral Vibe logo Mistral Vibe   Qwen Code logo Qwen Code   Rovo Dev logo Rovo Dev   + any CLI agent


Install

Desktop — macOS, Windows, Linux

Or via a package manager:

# macOS (Homebrew)
brew install --cask stablyai/orca/orca

# Arch Linux (AUR) — or stably-orca-git to build from source
yay -S stably-orca-bin

Mobile Companion — iOS, Android

Pair with your desktop app to monitor and steer your agents from your phone.


Community & Support

  • Discord: Join the community on Discord.

  • Twitter / X: Follow @orca_build for updates and announcements.

  • WeChat: Scan to join the Orca community WeChat group 8.

    WeChat group 8 QR code for the Orca community
  • Feedback & Ideas: We ship fast. Missing something? Request a new feature.

  • Privacy: See the privacy & telemetry docs for what anonymous usage data Orca collects and how to opt out.

  • Show Support: Star this repo to follow along with our daily ships.


Developing

Want to contribute or run locally? See our CONTRIBUTING.md guide.

The relay that pairs the mobile app with a desktop host is also in this repository under cloud/, with a separate pnpm workspace and setup guide.

Orca contributors

GitHub star history chart for stablyai/orca

Signed Builds

Windows code signing sponored/provided by SignPath.io, certificate by SignPath Foundation.

License

Orca is free and open source under the MIT License.

S
Description
Orca is the ADE for working with a fleet of parallel agents. Run any coding agent with your own subscription. Available on desktop, mobile and remote runtime.
Readme MIT
1.4 GiB
Languages
TypeScript 95.2%
JavaScript 4.1%
Swift 0.2%
CSS 0.1%
HCL 0.1%