diff --git a/CLAUDE.md b/CLAUDE.md index cda4cd7..7762c02 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -409,6 +409,16 @@ Confirmation is driven by the `confirmHeadline` parameter rather than a count te `RestartSessionsAsync` runs its targets **strictly sequentially** — `RestartSessionAsync` waits for each Claude process to exit before starting its replacement, and awaiting those in parallel would put several `claude.exe` instances back on the shared config file at once, which is the race the wait exists to prevent. A `_restartInProgress` flag drops (rather than queues) an overlapping invocation for the same reason — see the guard note above. That sequencing is also why the bulk entry points confirm: restarting a fleet of Claude sessions is a minute-scale operation, not an instant one. Each target is re-resolved per iteration, since every restart replaces the `SessionViewModel` for that id and an earlier one in the loop may have failed into dormant. +**The rail, and why it is not the shutdown board.** A bulk restart drives `RestartRail` (2px, docked under `RestoreRail`) plus a `RestartPill` counter in the toolbar's right stack, via `SetRestartProgress`. That is the *restore* pattern, deliberately, and the choice is forced: `ShutdownOverlay` only works because `OnClosing` collapses `TerminalGrid` before showing it — WebView2 is an `HwndHost` and composites over any WPF overlay — and collapsing the grid here would black out every live pane for the whole run. A bulk restart does not block the user, so it gets the quiet rail; shutdown does, so it gets the board. Both are hidden for a single target: the toolbar ↻ restarts in seconds and a rail that appears and vanishes is noise. + +The restart pair is **separate from the restore pair rather than a shared rail** — two unrelated loops with unrelated lifetimes, and one shared rail would need a precedence rule to no benefit. Peach (`#fab387`, the shutdown board's "closing…" colour) rather than the restore blue. + +**A restart refuses to start while the restore loop is running** (`_restoreInProgress`, dropped with a toast like `_restartInProgress`). Both loops spawn `claude.exe` and neither one's stagger can see the other's launches, so overlapping them is exactly the unlocked read-modify-write on `~/.claude.json` that `ClaudeLaunchStaggerMs` exists to prevent. The `OnLoaded` restore loop awaits per session and leaves the quick menu clickable throughout, so this is an ordinary click on a 55-session start, not a corner case. An earlier version of this section cited that overlap as the *reason* for separate rails — writing a config race down as a layout question. Both single-session `RestartSessionAsync` and bulk `RestartSessionsAsync` carry the guard; one session is enough to lose the race. + +**Stop means "after the current one".** The `⏹` on the pill sets `_restartCancelRequested`, read at the **top** of each loop iteration. It cannot abandon the session already mid-restart: by that point the VM is out of `_vm.Sessions` and the PTY is down, so returning early would strand it as dormant — the failure path, not a stop. Targets not yet reached keep their live terminals untouched, and a toast reports how many were skipped. The flag clears in the same `finally` as `_restartInProgress`. + +**The confirmation quotes a duration**, built by the WPF-free `Services/BulkRestartEstimate` (tested in `BulkRestartEstimateTests`) so the wording is unit-testable at all. It costs a Claude target at 4–8s (exit wait 2.3–4.7s + stagger + ~1.4s relaunch) and a plain shell at 1–2s, reports a range rather than false precision, and switches to minutes only once the *optimistic* bound passes 60s. At or above `ScaleWarningThreshold` (10 targets) it also says the restarts are sequential and names how many are Claude — that count is what makes the estimate look earned rather than arbitrary. `MessageBox` cannot emphasise anything, so scale is carried by the words; making the warning *look* like one would mean a themed window instead. + ## Editing a Session's Configuration Any existing session can be reconfigured through the **same form used to create one** — diff --git a/src/CodeShellManager/MainWindow.xaml b/src/CodeShellManager/MainWindow.xaml index 8ef7615..c324731 100644 --- a/src/CodeShellManager/MainWindow.xaml +++ b/src/CodeShellManager/MainWindow.xaml @@ -113,6 +113,46 @@ + + + + +