Commit Graph
27 Commits
Author SHA1 Message Date
Neil 1075dce185 perf(memory): prune claimed PTY subtrees (#17450) 2026-08-30 23:03:59 -07:00
NeilandOrcaWin 3457acb647 fix(memory): hydrate retained PTYs before diagnostics (#17308)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-08-29 19:42:56 -07:00
Neil 7ee8b5e1a6 Refactor lower max-lines modules (#16760) 2026-08-27 16:10:51 -07:00
Neil 64c992cd56 fix(memory): report the Windows number that predicts paging, not just resident pages (#16211) (#16589)
* fix(memory): report Windows commit charge, not just working set (#16211)

On Windows the per-process figure was working set — resident pages only.
An agent whose pages Windows has trimmed to the pagefile shrinks its
working set while still holding the commit that pushes the host into
paging, so Resource Manager and `orca diagnostics memory` understated an
owned tree by 10-40x (9 codex.exe: 1.4 GB working set, 13.4 GB private)
and could not warn before the host was already thrashing.

Add committed private bytes as a second, separately-labelled quantity
rather than redefining the existing one:

- CIM sweep gains one property (PageFileUsage, UInt32 KB); the typeperf
  fallback gains one counter (\Process(*)\Private Bytes). Both ride the
  sweep that already runs.
- MemorySnapshot gains optional `privateMemory` per app/worktree/session
  plus `processCommitMetric` and `totalPrivateMemory`. Rule 1 additive
  optional fields: old clients ignore them, and absence reads as "not
  measured", never as zero — Unix hosts and older hosts send nothing.
- `totalMemory` and `processMemoryMetric` keep their exact meaning, so
  the "shared pages may repeat" copy stays true; the working-set copy now
  also says paged-out memory is not counted.
- Resource Manager shows "Σ Private" beside "Σ WS", and tints the badge
  yellow/red once tracked commit passes 60/80% of physical RAM — the same
  thresholds `usageTextColorClass` already uses for host usage. Tint and
  tooltip only; no toast, and the badge number is unchanged.

The parsers move to windows-process-sample-parsing.ts and the Windows
sweep tests to their own file to stay under max-lines.

Not migrating the collector to windows-process-table.ts: the native
snapshot exposes no commit figure and no CPU times, and truncates
WorkingSetSize through a DWORD. Documented in the enumeration reference.

* fix(memory): derive the typeperf field cap from the counter list

The fallback parser's 8192-field cap was sized for three `\Process(*)`
counters. Adding `Private Bytes` cut the parsable process count from ~2730
to ~2047, and overrun is a blackout (`parseTypeperfCsvLine` returns `[]`, so
the whole sweep reports nothing) rather than a truncation. The counter list
now lives beside the decoder that reads those names back out of the PDH
header, and the cap is derived from it.

Also collapses the four spellings of "omit privateMemory when unmeasured"
in collector.ts onto one `commitField` helper, drops the unread parameter
and the never-rendered `columnLabel` from `getResourceCommitMetricCopy`,
folds `getCommitPressurePercent` into the only function that called it, and
reverts unrelated Prettier churn in the Windows enumeration doc.

The commit tint's doc comment no longer claims to predict host paging: it
measures Orca's own share of physical RAM. Host commit charge / commit
limit stays a follow-up (#16211).
2026-08-26 15:43:02 -07:00
Neil 0bbc6c80e8 refactor(host): route app paths and version through an AppEnvironment port (#16019)
* refactor(host): route app paths and version through an AppEnvironment port

`app.getPath('userData')` is the single largest Electron coupling in the main
process — 37 call sites — and it is one of the things stopping the Orca runtime
from booting on plain Node. Give it the same treatment as SecretStore.

- `src/shared/app-environment.ts` — the port plus a settable registry, covering
  the members the runtime's module graph actually reads: paths, app path,
  version, packaged flag, shutdown hook, exit, and Chromium process metrics.
  `getAppEnvironment()` throws until installed, for the same reason the secret
  store does: a silent default resolves `userData` to the wrong directory and the
  caller writes real state there before anyone notices. No `node:` imports,
  because `src/shared/**` is in the web build graph.
- `src/main/host/electron-app-environment.ts` — the desktop adapter, a
  pass-through to `electron.app`.
- 9 modules migrated: telemetry, opencode/mimo/pi hook services,
  terminal-history-paths, terminal-scrollback-snapshots, cli-installer,
  clipboard-image-temp-file, memory/collector.

Deliberately NOT migrated: `src/main/browser/**`. That cluster is Chromium-
adjacent by nature — cookie jars, download destinations, offscreen pages — and a
Node backend does not ship it at all, so porting it buys nothing and churns
heavily-mocked suites. Also left alone for now: the call sites that additionally
touch `app.asar` path literals or `app.setName`, which need more than a
mechanical swap.

`getAppMetrics` stays on the port rather than being injected because
memory/collector.ts is its only caller and reads it from module scope; a Node
host returns [], having no Chromium processes to measure.

Test wiring: the secret-store setup file becomes `vitest-host-ports-setup.ts` and
installs both ports, exporting `fakeAppEnvironment`/`installFakeAppEnvironment`
so suites needing one specific member state only that instead of restating all
seven — which is boilerplate, and had pushed one suite past the max-lines budget.

Verified: 159 files / 1651 tests pass across every touched area; `tsc` clean on
both the node and web projects; `oxlint` clean.

* fix(typecheck): list the vitest host-ports setup in the node project

Three suites import `installFakeAppEnvironment` from config/scripts, but that
directory is outside tsconfig.node.json's include list, so composite typecheck
failed with TS6307. Listing the one file matches how this config already pins
individual files it needs.

Local `tsc --composite false` does not reproduce this — only `pnpm typecheck`
does, which is what CI runs.

* refactor(host): drop two unused AppEnvironment exports

hasAppEnvironment() and resetAppEnvironmentForTests() had zero callers. The
secret-store equivalents are used, so these were mirror-symmetry rather than
need; add them back when something actually needs them.

* test(terminal-history): install the AppEnvironment fake instead of mocking electron

These three suites mocked `electron.app.getPath` to point at a fixture dir. The
production module now reads the port, so the mock was inert and the global test
default's temp dir won — which broke the WSL path assertions and every deletion
count.

Found by a full-suite run, not by the targeted checks around the migrated modules,
which is the argument for running the whole suite on a refactor this wide.

* test(host-ports): remove the per-environment temp dir on teardown

The setup allocated a mkdtemp directory at module scope, which vitest evaluates
once per test *environment* — one per test file, not one per worker. Nothing
removed them, so a full 6,000-file run left thousands behind.

Proven: with an isolated TMPDIR, a three-file run previously added directories and
now leaves zero.

* fix(app-environment): anchor the installed environment to a realm global

Same reason as the SecretStore: vi.resetModules() rebuilds the module registry,
and an environment installed before the reset read back as uninstalled.
2026-08-22 21:12:23 -07:00
Neil b7e79b7ca6 fix(windows): one chokepoint for every child process (#15746)
* feat(process): add the Windows-correct child-process chokepoint

Six decisions have to be made every time Orca starts a child process --
console visibility, argument quoting, .cmd interpretation, binary
resolution, timeout policy, and how the tree is later terminated. POSIX
forgives all six. Windows punishes each differently, and made per-call
site across 172 files they were right in some and wrong in others.

runProcess/spawnProcess make them once:
- windowsHide unconditionally, shell:false unconditionally (shell:true
  concatenates argv unescaped and silently disables windowsHide)
- .cmd/.bat routed through cmd.exe /d /v:off /s /c with a verbatim line,
  because Node refuses to spawn them otherwise (EINVAL)

The encoding was derived by measurement on Windows 11, not from the
docs. An embedded quote is written "" rather than \" so cmd's naive
quote count stays even -- with \" the parity flips and every later &
| < > on the line stops being data. Measured before the fix, argv
["a b", 'c"d', "e%F%g", "h&i", "j^k"] arrived as
["a b", 'c"d', "e^%F^%g", "h"]: the & truncated the argument and
ran its remainder as a command. Each % is broken out of the quoted run
as "^%" because %VAR% expands even inside quotes.

The import-boundary test is a ratchet seeded at today's 172 files; it
only shrinks.

* fix(process): route the console-flashing spawn sites through the chokepoint

The ssh -G config probe fires on every connect and reconnect, and ssh.exe
is console-subsystem, so a GUI-subsystem parent gets a fresh visible
conhost that takes foreground -- keystrokes typed into an Orca terminal
at that moment go into the black box (#10488, #14543). Same for the
ProxyJump tunnel, the ProxyCommand cmd.exe wrapper, the font enumeration
and the DPAPI cookie decrypt.

Also stops spawning powershell by bare name: PATH under Electron is not
the user's, so where policy has pruned the System32 entry the spawn fails
and the font picker silently reports five hardcoded families rather than
an error (#11771).

Deletes system-fonts' 40-line bespoke execFileText -- timeout, output cap
and kill are the chokepoint's job now. Adds runProcessSync so the sync
callers have a compliant path; without one the ratchet could never
reach zero.

The three suites that mocked child_process directly now mock runProcess,
which is the point: how a process gets started is no longer each
module's business. Ratchet 173 -> 170.

* fix(process): do not report a deliberately killed child as timed out

runProcessSync inferred a timeout from signal === 'SIGTERM'. Measured:
a real timeout sets error.code ETIMEDOUT and kills with SIGTERM, but so
does anything else that terminates the child -- and those cases set no
error at all. Reading the signal alone reports a process someone stopped
on purpose as having timed out, which callers retry.

* refactor(process): hold the ratchet as data and migrate the pwsh probes

The allowlist and the adversarial argument corpus are read only by tests,
so they were production modules in name only; they move to __fixtures__.

pwsh.ts carried isTimeoutError() purely to reconcile two spellings of the
same event -- execFileSync reports a timeout as ETIMEDOUT, the execFile
callback as a SIGTERM kill with no code. runProcess reports one timedOut
flag, so the helper and the reasoning behind it both go.

Its sync probe also spawned without windowsHide, which flashes a console
and steals foreground on every cold cache read.

* refactor(process): migrate five more spawn sites onto the chokepoint

Each one deletes a hand-rolled promise/timeout/kill wrapper and stops
re-deciding console visibility for itself. Ratchet 170 -> 164.

Two things this surfaced, both kept:

runProcess now accepts string chunks as well as buffers. A stream someone
called setEncoding on emits strings, and concatenating those as buffers
throws inside a data handler -- where the rejection has nowhere to go and
the caller simply hangs rather than failing.

ProcessSpec keeps its AbortSignal. I had removed it as unused; the macOS
PAM preflight passes one through from its own caller.

ipc/app.ts is deliberately NOT migrated. Its probe spawns a three-stage
 pipeline detached so a timeout can reap the group with one
negative-pid SIGKILL; runProcess kills only the root, which would orphan
the plutil stages. Migrating it needs the chokepoint to own POSIX
process-group termination first -- the same guarantee job objects give on
Windows. Reverted and left on the ratchet.

* test(process): do not assert a POSIX signal on Windows

Windows has no signals, so the same deliberate kill reports an exit code
there and a signal on POSIX. What has to hold on both is that neither
shape reads as a timeout. Caught by running the suite on Windows.

(cherry picked from commit 0a6e9902a22a369a0e85e113ea8d87b726f82e1f)

* fix(process): settle a timed-out run even when the child ignores the kill

close only fires once the child is actually gone, so a child that traps
SIGTERM never emits it and the promise outlives its own deadline
forever. That is the same wedge shape just fixed for the process table,
and it is worse here: pwsh.ts and the snapshot reader both cache an
in-flight probe, so one unkillable child hands every later caller the
same dead promise.

After the deadline it now escalates to SIGKILL and settles regardless,
reporting timedOut with whatever output arrived.

(cherry picked from commit 78ac169197c4e6faee1b9310a7186029cc11acbc)

* fix(process): escalate an aborted child too, not just a timed-out one

The grace escalation I added covered the timeout path and left abort on
the old one, so an aborted caller with an unkillable child still waited
forever -- the same defect, one path over. The macOS PAM preflight is a
real caller that passes an AbortSignal.

Both paths now share one stop-and-settle, and the result reports
timedOut honestly: false when the caller aborted.

(cherry picked from commit 7e9523a9e31172bb8183661b56f04c3ab6a03d0d)

* fix(windows): stop percent escaping from forging an escaped quote

escapePercentForCmd ran as a post-pass over the quoted string, so it
inserted a quote wherever a percent was -- including straight after a
backslash. CommandLineToArgvW reads backslash-quote as an escaped quote,
so C:\Users\%USERNAME%\x arrived corrupted. That is about as common as
Windows paths get, and my 20-case corpus had no backslash-before-percent
entry to catch it.

Percent handling is now part of the quoting loop, where the backslash
run is known and can be doubled before the inserted quote. Two corpus
cases cover the shape.

The program path gets the same treatment. It was quoted but not
percent-escaped, so a launcher under C:\Users\%USERNAME%\ had its own
path expanded on the cmd hop.

quoteWindowsArgument no longer takes a boolean. Passing it to
values.map() handed map's index in as the flag -- which is how the first
version of this fix was written, and the corpus test caught it.

Separately: an AbortSignal that was already aborted never fires the
event, so runProcess ran the child to its full timeout for a caller who
had already given up.

(cherry picked from commit f7e2e56b1ee1f27ab6d1035dde4501b38f95b374)
2026-08-21 21:05:24 -07:00
Neil 77f23b013f refactor(shared): drop the shared/types barrel and import from the real modules (#14447)
#14397 split `shared/types.ts` into 46 per-domain modules but kept the path as
a re-export barrel so the import sites did not have to change. This removes
the barrel: every consumer now imports from the module that actually declares
the type, and `src/shared/types.ts` is deleted.

Barrels hide where a type lives, make every consumer look like it depends on
the whole domain, and let an unrelated edit invalidate a module that ~2,000
files transitively import.

2,323 import declarations across 2,321 files. Rewritten mechanically: each
specifier was resolved to an absolute path via the TypeScript AST and
recomputed, rather than string-substituted, so alias forms (`@/../../shared/
types`) and per-specifier `type` modifiers survive.

Four cases the mechanical pass had to handle, each found by a gate rather than
by reading the diff:

- Modules inside `src/shared` import the barrel as `./types`, not
  `shared/types`. A pre-filter on the latter string skipped 176 of them and
  left imports dangling at a deleted file, which surfaced as confusing
  `Property 'x' is optional in type 'Repo' but required in Pick<Repo, ...>`
  errors rather than "module not found".
- The barrel RENAMED one type on the way through
  (`WorkspaceSource as WorkspaceCreateTelemetrySource`), so the original name
  in the owning module has to be re-aliased at each consumer.
- Three test files put `;(globalThis as ...)` on the line after the import.
  TypeScript parses that `;` as the import statement's terminator, so
  replacing through `statement.getEnd()` deletes it and breaks ASI. The
  rewrite now stops at the module specifier.
- A file that already imported directly from a module got a SECOND import
  from it, because the barrel re-exported those same names — which trips
  `import/no-duplicates` under `--deny-warnings`. A post-pass merges
  declarations sharing a specifier and type-only-ness; the `import type` plus
  `import` pair from one module is left alone, since that form is allowed.

Splitting one barrel import into several genuinely adds lines, which pushed
`terminal-layout-pty-ownership.ts` to 301 counted lines: its 107-character
import must wrap, and neither local type collapses onto one line (101 and 116
characters). Rather than contort a type declaration to fit a line budget,
`collectLeafIds` and `pruneLeaves` move to `terminal-pane-layout-tree.ts` —
they are pure structural operations on the layout tree and independent of PTY
ownership. `visible-worktrees.ts` similarly loses its own mini-barrel
re-export of `isDefaultBranchWorkspace`, with the four real consumers
repointed at the declaring module. No `max-lines` bypass added.

Verified: cold `tsc --noEmit` green on node, cli, and web (buildinfo deleted
first — these projects are `composite: true` and reuse stale caches); the full
`pnpm lint` green, not just bare oxlint — the narrower local check is what let
the duplicate imports reach CI; max-lines ratchet OK at 344.
2026-08-13 22:48:24 -07:00
Neil 583ab1601b refactor(shared): group worktree, github, and linear modules into folders (#14437)
`src/shared` is a flat directory of ~1,150 entries. The worktree, github, and
linear domains accounted for 71 of them, so finding the module you wanted meant
scanning a wall of same-prefixed filenames.

Move each domain into its own folder and drop the now-redundant prefix:

    src/shared/github-pr-types.ts    -> src/shared/github/pull-request-types.ts
    src/shared/worktree-id.ts        -> src/shared/worktree/id.ts
    src/shared/linear-links.ts       -> src/shared/linear/links.ts

This follows the existing `network/` and `new-workspace/` convention in the
same directory, which also drop the prefix inside the folder.

Whole clusters move, including tests. Foldering only part of a domain would be
worse than flat: a reader would have to check both `github/` and the flat
directory, and `github-auth-types.ts` / `github-project-types.ts` are type
modules that belong with the rest. No files with these prefixes remain flat.

Import specifiers were rewritten by resolving each one to an absolute path and
recomputing it, not by string substitution, so the `@/../../shared/...` alias
forms are handled correctly. 501 specifiers across 298 files.

Two things `tsc` cannot catch, handled explicitly:

- `github-project-types.ts` carries its own `max-lines` bypass, so its baseline
  entry is REPOINTED to the new path rather than pruned. Pruning would drop the
  bypass and then flag the new path as a fresh violation. Ratchet stays at 345.
- `mobile/` is outside `pnpm typecheck` and cannot be typechecked here
  (`mobile/node_modules` is empty). Instead every relative specifier in the repo
  was resolved against the filesystem: 174 unresolved before this change and 174
  after — identical, so nothing broke in mobile either.

The pinned `tests/e2e/.cross-version-checkouts` fixtures are deliberately NOT
rewritten; they are a snapshot of an older release and still reference the old
paths.

Verified: cold `tsc --noEmit` green on node, cli, and web (buildinfo deleted
first — these projects are `composite: true` and reuse stale caches).
2026-08-13 20:44:16 -07:00
Neil 991a3fe963 chore(lint): update oxlint to 1.77 and enable no-op cleanup rules (#13901)
Enable eleven oxlint rules that simplify code without changing behavior, and fix
every existing violation. Each candidate was gated on measured cost rather than
assumption, so rules that regressed runtime performance or type checking were
dropped instead of suppressed.

typescript/no-redundant-type-constituents is the largest addition: 113 sites, no
autofix. Dead constituents are deleted. Where the redundant literal existed to
document intent (`string | 'all'`), it is preserved as `(string & {})`, which
keeps the autocomplete hint the original code was reaching for instead of
flattening it away. The rule also caught a broken import —
remote-shared-control-retirement-probe.ts pulled RuntimeStatus from
src/shared/types, which does not export it, so the type silently degraded to
`any`; no tsconfig covers that file, so tsc never saw it.

oxlint stays at 1.77.0 rather than 1.78.0 because .npmrc sets
minimum-release-age=4320 and 1.78.0 is younger than that window.

Rules evaluated and rejected, with what disqualified each:
- prefer-string-raw: String.raw is a runtime call, not a literal (184x slower)
- prefer-string-replace-all: 26% slower
- text-encoding-identifier-case: ~5% slower, reproducible
- prefer-spread: [...str] is 110% slower than split('') and differs on surrogates
- no-implicit-coercion: `!!x` narrows types and `Boolean(x)` does not (22 tsc errors)
- prefer-arrow-callback: arrows are not constructible, breaking `new` on mocks
- object-shorthand: rewrites source text asserted by a tracked reliability gate
- switch-case-braces: pushes ten files past max-lines, which cannot be suppressed
- no-useless-switch-case: drops `case undefined:` that switch-exhaustiveness-check needs
- arrow-body-style: 115 violations have no fix, and it breaks max-lines
- newline-after-import: false-positives on the leading-semicolon ASI idiom

electron-vite-output-contract asserted on the literal
Object.prototype.hasOwnProperty.call text; retarget it to Object.hasOwn, which
rejects inherited keys identically.
2026-08-11 18:19:43 -07:00
Jinjing 05206046f6 chore: condense code comments (#12008)
* chore: condense code comments

* chore: shorten more code comments

* clarify PTY agent session descendant cleanup behavior

Refine the comment on ptyAgentSessionIds to more accurately describe
when agent sessions sweep their descendant process trees and note the
exception on immediate Windows shutdown.
2026-08-01 14:24:31 -07:00
Neil 6b16c20796 fix(memory): clarify Resource Manager accounting (#10821) 2026-07-26 19:46:29 -07:00
NeilandOrca aab112933e Revert "fix(memory): bound OOM-prone accumulators (#10179)" (#10255)
Co-authored-by: Orca <help@stably.ai>
2026-07-23 18:35:31 -07:00
Neil 8f40ddf328 fix(memory): bound OOM-prone accumulators (#10179) 2026-07-23 06:22:56 -07:00
Cal 4877d0b65c Fix Windows Resource Manager memory and CPU attribution (#8845)
Use a single CIM sample for Windows process memory and CPU attribution, with bounded Typeperf memory fallback and safe CPU delta recovery.
2026-07-21 02:04:37 -04:00
NeilandOrca 98b00d3a64 refactor(comments): slim verbose comments in main core (runtime/ipc/daemon/pty) (#9542)
Collapse multi-line explanatory comment blocks into single-line "why" statements
per AGENTS.md ("Document the Why, Briefly"): drop restatements of the code and
mechanism narration; keep the non-obvious reason, external refs, and directives.

Comments-only — verified no code changed via a Babel/esbuild comment-strip
token-equality gate against origin/main; typecheck and oxlint clean.

Area: main — core runtime, ipc, daemon, pty, providers. 73 files changed, 3206 insertions(+), 10475 deletions(-).

Co-authored-by: Orca <help@stably.ai>
2026-07-20 03:18:24 -07:00
NeilandOrca e825df8218 perf(memory): skip unrelated boot worktree scans (#8204)
* perf(memory): scope boot worktree scans to live sessions

* fix(memory): refresh daemon hydration snapshots

* fix(memory): rescan repos referenced only by the re-read snapshot

Repo selection previously came solely from the first daemon listing, so a
session that only became visible on the post-enumeration re-read (e.g. a
briefly unreachable adapter) could never be registered for the life of the
process. Loop enumeration until the latest daemon snapshot references no
unresolved repos, so selection and registration always share a snapshot.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-07-10 20:10:08 -07:00
Neil 220d5cb7fe Prevent and recover from degraded terminal daemon spawns
Adds daemon PTY-spawn health classification, POSIX daemon cwd repair before terminal spawn, and degraded-daemon fallback routing so fresh terminals keep opening without killing preserved sessions.
2026-06-29 20:17:46 -07:00
Jinwoo Hong 972078f2c4 Fix paste ownership, input bounds, and IPC validation
Supersedes #5745, #5746, and #5747.
2026-06-19 17:14:55 -07:00
0ec3882cb8 Add project Windows runtime selection (#5519)
* Add project Windows runtime selection

* Fix project Windows runtime selection

Co-authored-by: Orca <help@stably.ai>

* fix: preserve WSL shell variables

---------

Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Neil <neil@stably.ai>
2026-06-17 16:08:14 -07:00
Neil f7fb6ef686 Expose memory diagnostics through CLI (#4408) 2026-06-01 15:14:15 -07:00
Neil 9c37bab97a Reduce hidden browser and terminal memory churn
Reduce hidden browser webview retention, correct Resource Usage app memory attribution, and normalize terminal TUI redraw previews. Includes docs/renderer-memory-profile-2026-06-01.md with the profiling evidence.
2026-06-01 14:16:18 -07:00
Neil 20d4bbd791 perf: skip ssh repos during pty hydration 2026-05-31 09:55:19 -07:00
Neil 23686c825e perf: handle large pty registry hydration lists (#3789) 2026-05-30 10:18:49 -07:00
Jinjing c93dd901a3 fix: address review findings (#2695) 2026-05-23 12:33:49 -07:00
Brennan BensonandOrca 29e8eae70e perf(session): prune local terminal scrollback from persisted sessions (#1753)
Co-authored-by: Orca <help@stably.ai>
2026-05-13 16:34:44 -07:00
Brennan BensonandOrca 1f43346c5f fix(resource-usage): hydrate pty-registry at boot; render · remote only for SSH repos (#1667)
* WIP: Changes before auto-review fixes

Co-authored-by: Orca <help@stably.ai>

* fix: address auto-review-fix-multi-agent findings

- Replace local ORCA_WORKTREE_ID_SEPARATOR with shared WORKTREE_ID_SEPARATOR
- Make hydrateLocalPtyRegistryAtBoot idempotent (one-shot per process,
  but stays retry-eligible until daemon provider is available)
- Strengthen daemon-pty-adapter strict-parser test to actually exercise
  the new short-circuit (test would have passed under the old loose
  parser too without the change)
- Add eslint-disable max-lines directive to oversized merge test file

Co-authored-by: Orca <help@stably.ai>

* chore: archive auto-review context to .context/

Co-authored-by: Orca <help@stably.ai>

* fix: address auto-review-fix findings

Drop the destructive reconcileOnStartup call from boot-time PTY registry
hydration: a transient listRepoWorktrees failure (returns [] and only
warns) would otherwise let the reconcile pass kill live local sessions.
The boot path is now read-only against the daemon — listSessions() only.

Also: tighten parsePtySessionId to reject degenerate `::` halves; replace
stale pty.ts:1005 references and a misleading local-unknown comment in
the hydrate module; narrow Store dependency to Pick<Store, 'getRepos'>;
log adapter listSessions failures instead of silently swallowing them;
re-anchor design-doc references on stable symbols and align §1b/§1c/§1d
with the implementation.

Co-authored-by: Orca <help@stably.ai>

* docs(resource-usage): update remote badge spec

Co-authored-by: Orca <help@stably.ai>

* test(resource-usage): cover boot hydration failure modes + warm-reattach e2e

Adds the regression coverage flagged in PR #1667's test plan that wasn't
already locked down.

vitest (`hydrate-local-pty-registry.test.ts`):
  - daemon offline at first call → no-op, hasHydrated stays false so a
    later macOS dock re-activation can retry.
  - listSessions rejection caught and logged, does not throw.
  - pid-write ordering: a pre-existing registry entry with pid=12345 is
    not clobbered by a stale `pid: null` from listSessions (§1d).
  - SSH-gate: a session whose repo has a non-null connectionId stays out
    of the registry, mirroring the spawn-time gate in pty.ts.
  - Happy-path: a local session is registered with the daemon's pid.

Playwright e2e (`resource-usage-warm-reattach.spec.ts`):
  Full quit→relaunch cycle against the same userDataDir; asserts that
  on the second launch the snapshot includes the warm-reattached PTY
  with a real pid before any pane mount, and that the seeded repo
  resolves as local (no connectionId). Mirrors the existing
  terminal-restart-persistence pattern.

Co-authored-by: Orca <help@stably.ai>

* fix(test): satisfy Pick<Store, 'getRepos'> in hydrator vitest

CI typecheck failed because FakeStore's getRepos returned objects missing
Repo's required fields (path, displayName, badgeColor, addedAt). Fill with
placeholder values; the hydrator only reads id + connectionId, but the
type signature still has to line up.

Co-authored-by: Orca <help@stably.ai>

* chore(resource-usage): drop bug-doc files; strip dead doc refs from comments

Remove docs/resource-usage-remote-mislabel.md (new in this PR) and revert
docs/resource-usage-merge-spec.md to the PR-base state. Strip the
matching `docs/...md §N` pointers from code/test comments, keeping the
surrounding "why" explanations intact so readers still get the
warm-reattach mislabel context.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-05-10 17:17:52 -07:00
Brennan Benson 8b87acd72d feat(memory): add Memory & CPU status-bar popover (#1146) 2026-04-26 21:42:06 -07:00