1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Async action dispatch for the `tm projects` TUI (#2118).
//!
//! Why: [`super::events::handle_key`] stays pure (no IO) and returns a
//! [`PendingAction`] describing what the operator asked for; this module is
//! the one place that awaits the matching [`DaemonClient`] call and folds the
//! result into a notice, mirroring `tui::coordinator`'s
//! `dispatch_submitted_line` split between pure routing and async dispatch.
//!
//! **Launch is a stub, by design** (documented per the issue's request):
//! spawning a managed session requires a `task` description and a `git_ref`
//! (see [`crate::client::ManagedSpawnRequest`]) that a 4-pane skeleton has no
//! text-entry surface to collect — building that form is explicitly out of
//! scope here. Rather than silently doing nothing, `l` shows a notice
//! pointing at the existing `tm sessions new` CLI verb, which already does
//! the real work. Config (`c`) is NO LONGER a stub — #2120 replaced it with a
//! real fixed-field form; see [`PendingAction::SubmitConfig`]'s handling
//! below.
//!
//! **Decommission has no confirmation step**, matching the issue's keybinding
//! table verbatim (`d` → decommission, no modal). This is a permanent,
//! destructive action (the workspace is deleted); a confirmation modal would
//! be a reasonable fast-follow but is out of scope for this skeleton.
//!
//! What: [`dispatch`] matches a [`PendingAction`] to its `DaemonClient` call
//! and calls [`ProjectCtlState::set_notice`] with the outcome, requesting an
//! immediate re-poll after any action that changed the fleet.
//! Test: the pure notice text is not independently unit-tested (it is a
//! terminal string, not branch logic); the routing itself is covered by
//! `events::tests` (which `PendingAction` a key produces).
//! `project_ctl_decommission_prunes_stale_worktree_bookkeeping` drives
//! `Decommission` end to end against a stub daemon and a real git worktree.
//! The remaining live HTTP calls are exercised manually / by
//! `tests/session_manager_mvp.rs`'s coverage of the underlying `DaemonClient`
//! methods.
use crate;
use PendingAction;
use ProjectCtlState;
/// Route one [`PendingAction`] to its `DaemonClient` call and update `state`.
///
/// Why: the single async seam the run loop calls after `handle_key` returns
/// `Some(action)`.
/// What: `Launch` is a stub — see the module doc — that sets an explanatory
/// notice with no daemon call. `Kill`/`Resume` call the matching mutating
/// endpoint directly; `Decommission` goes through
/// [`CommandExecutor::decommission_managed_id`], the shared implementation
/// that also repairs the base repo's worktree bookkeeping (#5913). All three,
/// on success, request an immediate re-poll
/// ([`ProjectCtlState::request_repoll`]) so the fleet reflects the change
/// now. `Attach` fetches and displays the tmux attach command (read-only —
/// no repoll). `SubmitConfig` (DOC-35 §6, #2120) PATCHes the config form's
/// built args: on success, CLOSES the form (`close_config_form`), sets a
/// success notice, and requests a repoll (matching the other mutating
/// verbs); on failure, the form STAYS OPEN and the error renders INLINE in
/// it (`set_config_form_error`) rather than as a transient notice — the
/// explicit #2120 requirement that a rejected submit never discards the
/// operator's other unsaved edits.
pub async
/// Fold a mutating endpoint's result into a notice, requesting a repoll on success.
///
/// Why: `Kill`/`Resume`/`Decommission` share the identical
/// result-to-notice-plus-repoll shape; factoring it out keeps [`dispatch`]'s
/// match arms one line each.
/// What: on `Ok`, sets a `"{verb} {name} — now {state}"` notice and calls
/// [`ProjectCtlState::request_repoll`]; on `Err`, sets a `"{verb} failed: {e}"`
/// notice with no repoll.