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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! The launch seam (ARCH §2.11 *A deposit into a quiescent agent starts
//! a driver*): who may launch a driver, what a launch actually does, and
//! the probe that decides whether one is warranted.
//!
//! Split out of [`super`] at bl-6a7c along the axis its tests were
//! already split on ([`super::tests::launcher`] and
//! [`super::tests::probe`]). The module above holds the inbox's paths and
//! its deposit vocabulary; this one holds the decision to run something,
//! and [`super::cli`] the verb that orchestrates the two.
use ;
use cratestep;
use io;
use ;
/// Launches a driver for a quiescent agent — the one launch seam shared
/// by the writer's post-deposit probe, the `litany scan` flush, and the
/// exit protocol's self-directed launch (§2.11). Kept as a trait so
/// every launch decision is testable with the spawn injected, and so the
/// production launch target can change without touching the callers. No
/// launcher ever decides whether launching is warranted; warrant is
/// decided by the launched driver under the lock (§2.11).
/// The production launcher: detach-spawns `litany advance <workspace>
/// <agent>` (§6), the workflow-chain driver that takes the lease,
/// rematerializes the worktree, drains the inbox, and steps (its
/// own-branch entry is [`crate::prompt::dispatch::driver::drive`]).
///
/// The spawn is **detached per §2.11**: `setsid` in the child (its own
/// session and process group — a §2.9 stop cascade against the launching
/// process never reaches the driver, and the driver outlives a launcher
/// running inside another agent's tool subprocess or a user's script),
/// stdin and stdout bound to null (a driver reads nothing and, by the
/// §3.4 one-product convention, says nothing on stdout), **stderr
/// captured** to the agent's [`step::DRIVER_LOG_FILE`]
/// ([`driver_log`] — §2.11; the driver's declines are operator-facing
/// and a `setsid` child has no terminal to inherit), and
/// [`baton::LOCK_FD_ENV`] scrubbed (a launched driver *acquires*; only
/// an exec'd successor adopts, §6). Fire-and-forget: the child is never
/// waited on — a launcher is short-lived by design, and the unreaped
/// driver reparents to init when the launcher exits.
/// Open the append sink for a detached driver's stderr:
/// `<workspace>/steps/<agent-id>/driver.log` (§2.11,
/// [`step::DRIVER_LOG_FILE`]). The path is **derived** from the two
/// arguments every launch already carries, so capture costs no config
/// and admits no second home for the fact (`docs/PRINCIPLES.md` Single
/// source of truth): a driver's diagnostics land in the diagnostic tree
/// its step records land in (§2.3). Created with the agent's step
/// directory, since a launch may precede that agent's first step.
///
/// A failure here **declines the launch** rather than falling back to
/// null (`docs/PRINCIPLES.md` *Decline illegal operations* — silent
/// degradation is never preferable to a loud refusal): a workspace whose
/// `steps/` tree cannot be written is one the driver could not have
/// recorded a step in either, and the refusal reaches the caller's own
/// stderr, where the failure it would have swallowed is legible.
/// The driver spawn's between-fork-and-exec hook: detach the child from
/// the launching agent's process group so a §2.9 cascade against the
/// parent never reaps the driver. `setsid` failure (caller already a
/// group leader) is ignored — the spawn proceeds grouped, which only
/// widens the cascade. Called in-process by its test: counters
/// incremented in the forked child die with the `exec`.
pub
/// Outcome of the post-deposit probe (§2.11 *A deposit into a quiescent
/// agent starts a driver*).
/// Probe the executor lock for `agent_id` and, finding it quiescent,
/// release the probe and launch a driver (§2.11). A non-blocking
/// try-acquire whose *success* means nobody is driving: on success the
/// lease is dropped immediately — launching is not driving — before the
/// driver is launched, so the driver can win the acquire.