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
//! Handler for `trusty-memory start` — boots the daemon in the background.
//!
//! Why: the trusty-* daemons historically had diverging `start` / `serve` /
//! `stop` semantics. `trusty-memory start` mirrors `trusty-search start`: it
//! spawns a detached `serve --foreground`. A second `start` while the daemon is
//! already serving is a no-op rather than a second instance racing the first for
//! redb's write lock.
//!
//! What (#6286): probes the socket and, on a miss, goes through
//! [`crate::commands::daemon_guard::ensure_daemon_running`] — which takes the
//! same [`start_lock_path`] lock the `serve --stdio` bridge takes, so a `start`
//! racing a bridge still cannot produce two daemons. It used to probe an
//! `http_addr` file plus `GET /health` and delegate to
//! `trusty_mcp::ensure_daemon_up_single_flight`; that helper is built around a
//! health URL this daemon no longer has.
//! Test: `start_lock_lives_beside_the_socket`; the exclusion itself in
//! `crate::commands::daemon_guard::tests`.
use Result;
use Colorize;
/// Path to the exclusive lock that serialises daemon starts (#5267).
///
/// Why: `handle_start` and the `serve --stdio` bridge both start the daemon.
/// They must contend for the SAME lock file or the exclusion is only within each
/// path and a `start` racing a bridge still yields two daemons — #1152's failure
/// mode. One derivation, used by both, is what makes that impossible.
/// What: returns `{resolve_data_dir("trusty-memory")}/start.lock`, under the
/// same (test-overridable) data dir the socket is derived from. Returns `None`
/// when the data dir cannot be resolved.
/// Test: `start_lock_lives_beside_the_socket`.
pub
/// Boot the trusty-memory daemon in the background.
///
/// Why: the daemon must outlive the invoking shell, so it runs detached rather
/// than tied to the controlling terminal (which broke shell profiles, tmux
/// panes, and `make`-driven dev loops). Since #5267 this also waits for
/// readiness before returning: the previous fire-and-forget return released the
/// start lock before the daemon was listening, which let the next contender
/// re-probe a dead endpoint and start a second daemon (#1152).
///
/// What: delegates to [`crate::commands::daemon_guard::ensure_daemon_running`],
/// which fast-paths a live socket, then starts `serve --foreground` at most once
/// across all processes and waits for it to answer. Fails closed if it never
/// does.
///
/// Test: `start_lock_lives_beside_the_socket`; the exclusion itself in
/// `crate::commands::daemon_guard::tests`.
pub async