Comfy-Org / Comfy-Org/Comfy-Desktop
Unify in-flight operation tracking so all windows reflect every operation (dashboard hydration)
- Dominant language
- TypeScript
- Stars
- 458
- Forks
- 59
- Avg merge
- 22h 18m
- Merged PRs (30d)
- 45
Description
## Summary
Newly-opened windows (and the dashboard generally) do not see in-flight **operations** (update / migrate / delete / copy / restore) or several lifecycle states started elsewhere. We should unify operation tracking so any window reflects every in-flight op, reusing the registry the title-bar picker popup already relies on rather than building a parallel system.
Spun out of the dashboard status-pill work (PR #874), where we added hydration for `runningInstances` and `launchingInstances` but deliberately stopped at the bigger operation-tracking change.
## Background — two execution models
Operations run through **two different paths** today:
- **Renderer-owned ops (dashboard / panel):** the initiating renderer's `progressStore.showProgress` drives `window.api.runAction(...)`. Main reports progress **point-to-point** via `sender.send('install-progress', ...)` to that one window, and registers only the abort handle in `_operationAborts`. No rich status is recorded globally.
- **Main-owned ops (title-bar picker popup):** the popup is ephemeral, so main runs the op via `pickerRunBackgroundOp(...)` and tracks full status in `_activeOperationStatus` (`Map`), pushing it to the popup via the picker snapshot.
`_activeOperationStatus` is therefore **only populated for picker-initiated ops** (`_activeOperationStatus.set` lives exclusively inside `pickerRunBackgroundOp` in `src/main/index.ts`). If the dashboard naively consumed it, it would show picker ops and miss every dashboard/panel-initiated op — a misleading half-view. That's why the dashboard doesn't currently read it.
## Current hydration matrix
| State | Live to all windows | Hydrated on a new window's `init()` |
|---|---|---|
| `runningInstances` | yes (broadcast) | yes (`get-running-instances`) |
| `launchingInstances` | yes (broadcast) | yes (`get-launching-instances`, added in #874) |
| `stoppingInstances` | yes (broadcast) | no (main keeps no queryable stopping set) |
| error — crash | yes (broadcast) | per-install pull (`getLastCrashError`) in the instance window only; dashboard not bulk-hydrated |
| error — op failure | no (initiator only) | no |
| operations / progress bars | no (`sender.send` initiator only) | no |
## Proposed work
**Small, low-risk gaps (same pattern as the launching fix):**
1. Retain a stopping set in main + a `get-stopping-instances` snapshot so a window opened mid-stop shows "Stopping…".
2. Add a bulk `get-error-instances` snapshot so a fresh dashboard shows red tiles for crashes/op-failures that occurred before it opened.
**Operation-registry unification (the real fix):**
3. Make the renderer-owned `runAction` flow also write into `_activeOperationStatus` (hook via `makeSendProgress`), so the registry is the single source of truth for all ops.
4. Broadcast `install-progress` to all windows (or emit a snapshot-changed signal like the picker loop) so progress is shared.
5. Add `get-active-operations` so a newly-opened window hydrates in-flight ops into its `progressStore` (mirrors `get-launching-instances`).
## Risks / open questions
- **Cancel & ownership semantics.** A renderer-owned op is today implicitly tied to its initiating window's `ProgressModal`. Once shared, we must define: what does "cancel from another window" do, and what happens to the op (and its modal) when the initiating window closes? `_operationAborts` is already global/main-side, so the op itself survives window close — only the progress UI is window-bound today.
- Avoid double-counting between the existing picker path and the new shared path.
## References
- `src/main/lib/ipc/shared.ts` — `_activeOperationStatus`, `_runningSessions`, `_getLaunchingInstances`
- `src/main/index.ts` — `pickerRunBackgroundOp` (only writer of `_activeOperationStatus`)
- `src/main/lib/ipc/broadcast.ts` — `_broadcastToRenderer` (all windows) vs `sender.send` (initiator only)
- `src/renderer/src/stores/progressStore.ts` — renderer-owned op lifecycle
- `src/renderer/src/stores/sessionStore.ts` — `init()` hydration
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.