galaxyproject / galaxyproject/loom

Orbit: process monitor goes empty while detached background work is running (nohup/disown reparents out of agent tree)

Open
#70 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
14
Forks
12
Avg merge
6d 5h
Merged PRs (30d)
17

Description

## Symptom

Brain backgrounds long work (downloads, parallel `fastp`, etc.) with `nohup ... & disown` so it survives an agent restart. The processes keep running fine — files appear in cwd, logs grow — but Orbit's process monitor panel shows "No subprocesses running" the whole time. From the user's perspective, nothing is happening.

Concrete example from a Pv4 ENA-download session:

```
$ ps -eo pid,ppid,comm,args | grep curl
2975097 2975094 bash bash -c mkdir -p fastq/... && curl -fSLC ...
2975115 2975097 curl curl -fSLC -o fastq/ERR027119/...
... (16+ similar)
```

PPID chain ends at `2975092` (a session leader under the user's systemd-user `2647`), not under the Electron agent (`2961887`). Process monitor sees nothing.

## Why

`ProcMonitor.collectDescendants` (`app/src/main/proc-monitor.ts:82`) walks descendants of `agentPid` only. Anything reparented via `nohup` / `disown` / `setsid` falls out of the agent's process tree and is invisible by design. The tradeoff is intentional for survival across brain restart, but the UI side never compensates — the panel just looks idle.

## Proposed fix

Brain explicitly tells shell which detached PIDs to monitor, via a new `extension_ui_request` method (same channel as `setStatus`, handled in `app/src/main/agent.ts:441`):

```
{method: "trackProcess", tag: "fastp-pv4", pids: [12345, 12346, ...], label: "fastp QC"}
{method: "untrackProcess", tag: "fastp-pv4"}
```

**Where each piece lives:**

1. **Brain (`extensions/loom/context.ts` UI bridge):** add `ctx.ui.trackProcess({tag, pids, label})` / `untrackProcess({tag})`. The bash-tool wrapper calls these whenever it detects `nohup`/`disown`/`setsid`.

2. **Main (`agent.ts:441`):** intercept these methods like the MCP-bootstrap swallow above; route to `ProcMonitor` instead of forwarding to renderer.

3. **`proc-monitor.ts`:** add `Map, label: string}>`. In `tick()`:
- Walk descendants as today.
- For each tracked tag, look up each pid in the same `ps -ax` output already being parsed. Pid missing → drop from set. Set empty → drop tag.
- Emit one merged `ProcInfo[]` with new optional `external?: boolean` and `tag?: string`.

4. **Renderer (`app.ts:2623` `renderProcs`):** group external rows under a "(detached)" subheader with the label; muted style. Empty-state hint becomes context-aware: *"Running detached. PIDs X, Y, Z"* when only the tracked set is non-empty.

**MVP path:** skip tag/label sugar — `trackPids(pids)` / `untrackPids(pids)` flat. ProcMonitor gets a single `Set`, renderer needs zero changes.

## Lifecycle

- Do **not** clear the tracked map on agent stop/restart — those PIDs are exactly what survive a restart. The per-pid `ps -p` liveness sweep cleans up naturally.
- Brain forgetting `untrackProcess` is fine; sweep drops dead pids and eventually the tag.
- Per-tag groups so concurrent download/QC/alignment batches render distinctly.

## Files

- `app/src/main/agent.ts` — handler branch for `trackProcess` / `untrackProcess`.
- `app/src/main/proc-monitor.ts` — tracked map + sweep + merged emission.
- `app/src/renderer/app.ts:2623` — render external rows; context-aware empty state.
- `extensions/loom/context.ts` — `ctx.ui.trackProcess` / `untrackProcess` on the UI bridge.
- Bash-tool wrapper in extensions/loom (caller side) — detect detach idioms, register PIDs.

## Edge cases

- Brain detaches without registering: invisible (status quo). Acceptable; brain is the only thing that knows it just detached.
- Pid reuse before sweep: vanishingly unlikely in 2.5s window; not worth defending against.
- Brain registers a pid that was never the brain's child: monitor still shows it. Fine — that's the point.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by tracing ProcMonitor.collectDescendants in app/src/main/proc-monitor.ts and the extension_ui_request handling in app/src/main/agent.ts, then inspect the UI bridge in extensions/loom/context.ts and renderProcs in app/src/renderer/app.ts. The work is done when registered detached PIDs remain visible across agent restarts, are removed when dead, and the renderer shows their labels and context-aware empty state.

Written by the indexing model from the issue text.

Assessment

Tech stack
bash, electron, typescript
Domain
devtools
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.