The Source Control panel mounted every non-collapsed file row, so large
changesets (big refactors, lockfiles, branch compare with hundreds of
entries) created hundreds to thousands of DOM rows and re-rendered them
all on every 3s git-status refresh (`STA-351`, `STA-1280`).
Window each per-section list (uncommitted tree/list, committed-on-branch
tree/list) with @tanstack/react-virtual against the panel's shared
scroller, extracted into source-control-virtual-file-list.tsx. Sections
under 50 rows keep the exact pre-virtualization DOM so small changesets
see zero change; larger sections mount only viewport + overscan rows.
Fixed-height estimates with element measurement keep identical refreshes
from moving the scroll position, and stable row keys carry item identity
across status polls.
Co-authored-by: Orca <help@stably.ai>
oxlint already fails any file over max-lines that is not suppressed, so the
only way to grow past the budget is to add an eslint/oxlint-disable max-lines
comment or a per-file max-lines bump in mobile/.oxlintrc.json. This adds a CI
gate that freezes the current set of suppressions (config/max-lines-baseline.txt,
355 grandfathered entries) and fails the build when a NEW one appears — with a
loud, actionable message pointing at 'split the file'. Existing oversized files
are untouched; the baseline may only shrink (pnpm check:max-lines-ratchet --prune).
Wired into the root lint script and as a dedicated pr.yml step. Unit-tested
(15 cases) and verified against all three failure paths + clean-tree pass.
Co-authored-by: Orca <help@stably.ai>
retainedAgentsByPaneKey snapshots a completed agent (a full AgentStatusEntry
— up to ~24KB of prompt/message text — plus a TerminalTab) per ephemeral
paneKey. paneKeys never recur, and the map is pruned only on worktree removal
or manual dismissal, so a long-lived worktree in a busy multi-agent /
orchestration session grows it without bound. This is the dominant
large-payload driver of the renderer JS-heap OOM seen in the Windows crash
bundles (heap climbing to the 3586 MB V8 old-space limit under continuous
multi-agent work).
Cap the map with insertion-order (== retention-order) FIFO eviction, mirroring
capRecordByInsertionOrder in github.ts: the newest completions survive; the
oldest simply stop showing in the recently-completed overlay. retainAgents is
the only path that grows the map, so capping there fully bounds it.
Adds a leak regression test that drives the production retainAgents path with
distinct ephemeral paneKeys and asserts the map stays bounded — fails before
this change (grows to N), passes after (capped at MAX_RETAINED_AGENTS).
Co-authored-by: Neil <neil@stably.ai>
* fix(renderer): raise renderer V8 heap toward the 4GB pointer-compression cage
Renderer OOM ('renderer crashed'/'oom', exit 5 / 0xE0000008 / SIGTRAP) is the
dominant crash in the crash channel: the renderer JS heap reaches Chromium's
default V8 old-space ceiling (~RAM/4) and V8 aborts. Two adversarial leak hunts
(13 agents across every renderer subsystem) found no unbounded GB-scale leak, so
this is a capacity ceiling, not a leak.
Chromium sizes the renderer heap at ~RAM/4, leaving 8-15GB machines well under
V8's ~4GB pointer-compression cage (an 8GB machine caps near 2.2GB). Reclaim that
unused headroom via --max-old-space-size in a focused startup module, gated on
physical RAM (>=8GB, ~40% of RAM, floor 3072MB, capped at the real 4096MB cage).
16GB+ machines are already at the cage so this is a no-op for them; low-RAM
machines keep the default to avoid trading a clean OOM for OS memory-pressure
kills.
Overridable with ORCA_RENDERER_HEAP_MB (number to force, default/off/0 to opt
out). Verified on Electron 42.3.3: the main-process js-flags switch propagates to
the renderer V8 and is honored up to the 4096MB cage (5000/12288 -> 4096).
Co-authored-by: Orca <help@stably.ai>
* fix(renderer): address CodeRabbit — floor-to-0 override + Linux 8GB gate
- parseRendererHeapOverrideMb: a fractional override in (0,1) floored to 0 and
emitted an invalid --max-old-space-size=0; treat floored-to-0 as an opt-out.
- Lower the RAM gate from 8 to 7.5 GiB: os.totalmem() on Linux reports MemTotal
(excludes kernel/firmware-reserved RAM), so a real 8 GB box reports ~7.7 GiB
and was wrongly excluded from the headroom — the exact crashing population.
7.5 still cleanly excludes 6 GB machines (report ~5.7 GiB).
Co-authored-by: Orca <help@stably.ai>
---------
Co-authored-by: Orca <help@stably.ai>
110 files carried an eslint/oxlint-disable max-lines directive but are
already under the default max-lines budget (300 .ts / 400 .tsx / 600 .mjs
/ 800 test), so the suppression is dead. Removing it restores real
max-lines coverage on these files with zero behavior change.
Each removed directive had max-lines as its only rule; verified via a
full oxlint run (0 max-lines violations, 0 new errors). Diff is pure
deletions (200 lines, 0 additions) — no code touched.
Co-authored-by: Orca <help@stably.ai>
Bump marketing version 0.0.22 -> 0.0.24 and Android versionCode 4 -> 5.
The 0.0.22 base was never committed after prior releases, so the Jul 6
builds carrying the show-all-worktrees fix (#7500) regressed below the
0.0.23 already on TestFlight (iOS) and collided with the existing
0.0.22/versionCode 4 APK (Android, no upgrade signal). Committing the
bump makes app.json authoritative again so 0.0.24 supersedes both.
Co-authored-by: Orca <help@stably.ai>
Two independent pieces, no behavior change to terminal handling:
1. Daemon lifecycle file log. The detached daemon runs with stdio
ignored, so field failures have zero daemon-side evidence. The daemon
now writes rotated NDJSON lifecycle events (startup/ready/hello
accept+reject/session create/attach/exit/kill/shutdown/uncaught
exceptions) to logs/daemon.log via a new optional --log-file fork arg.
Fail-open (any fs error disables logging), adoption-neutral (old
daemons without the arg keep working, protocol untouched), and the
diagnostic bundle collector now includes the file, bounded by the same
lookback window as trace spans.
2. tools/win-update-e2e: a packaged NSIS update proof harness. Installs
version N, drives the installed app (isolated userData), plants a
canary marker session, silently updates to N+1, relaunches, and
asserts an explicit expectations profile: --expect cold-restore
(today's behavior) or --expect survival (the Phase 1 target). Window
flashes are detected by baseline-diffed window enumeration with
canary-title attribution; daemons are identified by command-line
marker, never exe name. Refuses to run when a pre-existing Orca app is
running or (without --allow-existing-install) installed, and only
uninstalls an install it fully owns.
Ensure agent CLI startup and draft launch commands use the correct quoting
format based on the user's configured local Windows shell (e.g., cmd.exe).
This avoids using host settings for remote/SSH targets where local shell
preferences do not apply.