galaxyproject / galaxyproject/loom
Orbit: process monitor goes empty while detached background work is running (nohup/disown reparents out of agent tree)
- 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