Files
orca/src
Neil e95d247be1 perf(terminal): cheap-tier process inspection for anchored local agent panes (#18780)
* perf(terminal): cheap-tier process inspection for anchored local agent panes

Every idle local pane's completion cadence ran a full whole-host `ps` (with
`tty=` and `command=`, 0.34-0.50s on a 1,900-process Mac, 1.15s on Linux)
purely to build `foregroundProcessEvidence` that the renderer then discards
for local ids. Add a cheap tier (same job-control columns, no tty/command,
0.03s) gated so that it introduces no user-facing trade-off:

- Only a pane whose last FULL capture proved a recognized agent may take the
  cheap tier. Panes with no anchor always take the full capture, so start
  discovery keeps today's exact behaviour.
- The cheap tick compares a per-pane fingerprint (root shell pid+start, tpgid,
  every descendant's pid+start+pgid+job-control state). Any change, a changed
  node-pty foreground name, an unreadable capture, or an incarnation mismatch
  escalates to the full capture. A recognized agent's exit is always a pid
  vanishing, which the fingerprint always sees.
- A cheap answer OMITS evidence rather than fabricating a tty-less fence.
  Remote/restore consumers never send `steadyState`, so they keep the full
  capture unchanged.
- `steadyState` is a new optional request field; an old daemon ignores it and
  answers with the full capture.

Measured (8 idle panes, 60s, idle cadence, forks counted by column set):
30 full -> 1 full + 29 cheap.

* fix(terminal): route the cheap ps capture through runProcess

The cheap-tier reader imported node:child_process directly, which the
child-process import-boundary and windowsHide ratchet tests reject (CI shards
1/8 and 3/8). Use Orca's single spawn entry point instead; it pins windowsHide
and encodes argv. Map its result onto the capture-error vocabulary:
outputTruncated -> capture_truncated, timedOut -> capture_timeout, non-zero
exit -> ps_exit_<code>. Tests mock at the runProcess seam.

* fix(perf): refuse a pane fingerprint when any descendant start marker is missing

`buildPaneProcessFingerprint` rejected only a missing root start marker; a missing descendant
marker was stamped as `?`. Two captures that both failed to read the same descendant therefore
compared equal, which removes the pid-reuse protection the fingerprint exists to provide: a
recycled pid could make a vanished agent look unchanged, and the cheap tier would keep serving
its name instead of escalating.

Reachable on Linux, where `readLinuxProcStartTime` legitimately returns null when a process
exits between the `ps` capture and the `/proc/<pid>/stat` read.

Every subtree member now needs a start marker or the fingerprint is refused, which sends the
caller to the full capture — the same conservative default every other uncertain path takes.

Reported by CodeRabbit on #18780. The two new tests fail against the previous code with
`expected '4242@2400#4300:|4300@?:4300:+' to be null`.
2026-09-05 00:57:02 -07:00
..