docs(explore-notes): document the kill-on-detach gotcha and ConPtyPaneHost reparenting

This commit is contained in:
mika kuns
2026-08-06 12:57:42 +02:00
parent aac84e48b7
commit 0d1e3b9a6f
+19 -1
View File
@@ -1,7 +1,7 @@
# ConPTY interactive sessions & launch specs # ConPTY interactive sessions & launch specs
> **Explore-note — verify before trusting.** Distilled map of a subsystem, not authoritative. > **Explore-note — verify before trusting.** Distilled map of a subsystem, not authoritative.
> Last verified against commit `bdee731` (2026-08-05). > Last verified against commit `aac84e4` (2026-08-06).
> Drift check: `git log --oneline bdee731..HEAD -- src/ClaudeDo.Worker/Planning src/ClaudeDo.Worker/Hub src/ClaudeDo.Worker/Runner/ClaudeArgsBuilder.cs src/ClaudeDo.Ui/ViewModels/MissionControlViewModel.cs src/ClaudeDo.Ui/Views/InteractiveTerminalView.axaml` > Drift check: `git log --oneline bdee731..HEAD -- src/ClaudeDo.Worker/Planning src/ClaudeDo.Worker/Hub src/ClaudeDo.Worker/Runner/ClaudeArgsBuilder.cs src/ClaudeDo.Ui/ViewModels/MissionControlViewModel.cs src/ClaudeDo.Ui/Views/InteractiveTerminalView.axaml`
> Stable structure only (no line numbers). See docs/explore-notes/README.md. > Stable structure only (no line numbers). See docs/explore-notes/README.md.
@@ -94,6 +94,24 @@ the host wires handlers and then calls `Start()`. So the tile appears **immediat
spinner while the worker is still preparing the worktree. A failed launch keeps the tile with an spinner while the worker is still preparing the worktree. A failed launch keeps the tile with an
inline error banner instead of the tile never appearing. inline error banner instead of the tile never appearing.
### ⚠️ Gotcha: the terminal library kills its child on visual-tree detach
`Iciclecreek.Avalonia.Terminal`'s `TerminalView.OnDetachedFromLogicalTree` calls
`CleanupProcess()` (kills the PTY child) unless `BeginReparent()` suppressed it — and Mission
Control detaches pane views routinely (`RebuildOverviewGrid` recreates everything on any pane
add/remove/column change; focus-mode tab switches re-present content). Two-part defense (since
`aac84e4`):
1. `PtyTerminalSession.StartAsync` puts the control in **permanent reparent mode** right after
`LaunchProcess()``EndReparent` is deliberately never called. Teardown is explicit only:
`ConPtyPaneViewModel.Dispose``Terminal.Kill()` (pane close, VM disposal via DI on exit).
2. `ConPtyPaneHost` (the DataTemplate content for a pane) reparents **one long-lived
`ConPtyPaneView` per pane VM** (`ConditionalWeakTable`, view pins its own `DataContext`)
instead of letting the template instantiate a fresh view — a fresh view would render a dead,
empty terminal because the running session is bound to the original `TerminalControl`.
Hosts only steal the view while `IsEffectivelyVisible`; the layout toggle posts a reclaim
pass (`MissionControlView.ReclaimVisiblePaneHosts`) so the now-visible layout re-steals.
`Ellipse.spinner` (IslandStyles) is the shared indeterminate spinner — used for a starting pane `Ellipse.spinner` (IslandStyles) is the shared indeterminate spinner — used for a starting pane
(`InteractiveTerminalViewModel.IsStarting`) and in place of the refine button while (`InteractiveTerminalViewModel.IsStarting`) and in place of the refine button while
`TaskRowViewModel.IsRefining`. `TaskRowViewModel.IsRefining`.