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
//! Handing an [`Alert`] to the desktop (§6 as amended, bl-e160): one child per
//! alert, and the argv it becomes.
//!
//! **Zero new dependencies** (AGENTS.md rule 6). A desktop notification on
//! Linux is a D-Bus call to `org.freedesktop.Notifications`, and every Rust
//! crate that makes one (`notify-rust` and its `zbus`/`dbus` stack) is a new
//! dependency yog is not allowed to take. The freedesktop spec's own reference
//! client is a binary every desktop already ships — libnotify's `notify-send` —
//! and yog is a program whose whole substrate is spawned binaries. So this is
//! the spawn discipline yog already has, pointed one process further out.
//!
//! **Not through [`Cli`](crate::cli_outbound::Cli), deliberately.** Every `Cli`
//! spawn folds yog's composed world onto the child (§16.2) so the substrate
//! nests. The notifier is not substrate — it is the operator's own desktop
//! session, reached through the session-bus address yog itself inherited — so it
//! takes the bare [`git_env::command`](crate::git_env::command) constructor: the
//! ambient environment minus git's leaked `GIT_DIR`/`GIT_INDEX_FILE`, which is
//! what every child gets and no more.
//!
//! **Synchronous here, off-thread at the seat.** This blocks on each child, and
//! the frame must never block (bl-ee0a), so the window's one call site runs it
//! on a thread of its own. Keeping the wait here rather than hiding a spawn
//! inside is what lets a test drive it deterministically.
//!
//! **Every failure is silent.** A desktop with no notifier, a refused bus, a
//! non-zero exit: none is an event to record. `ops.jsonl` is the log of what
//! yog *did to the world* (§4.2) and a notification changes nothing — it is
//! render output that happens to leave the window. A notifier that is absent
//! renders nothing, exactly as the strip renders nothing when nothing stirs.
use Path;
use Stdio;
use Alert;
/// The desktop notifier yog spends — libnotify's reference client, resolved on
/// `PATH` like every other binary yog names by word rather than by path.
pub const NOTIFIER: &str = "notify-send";
/// How yog identifies itself to the notification daemon, so an operator's
/// desktop can filter or theme yog's notifications as yog's.
const APP_NAME: &str = "yog";
/// The argv one alert becomes: `notify-send -a yog <summary> <body>`.
///
/// Summary and body ride last and unescaped because they are exactly the two
/// positional operands libnotify takes, and they are yog-composed sentences —
/// a workspace leaf (§3.1-validated), a §3.3 display name, and the fixed rule
/// wording ([`AttentionKind::says`](crate::attention::AttentionKind::says)).
/// Nothing here is a shell string: the child is spawned directly, so there is
/// no word splitting to defend against.
/// Announce each alert, blocking on each child. `notifier` is the program to
/// run — [`NOTIFIER`] in the window, a stub in a test.
///
/// stdio is bound to null in all three directions: a notifier's chatter is not
/// yog's to carry, and a child inheriting the frame's stdout would interleave
/// with nothing anyone reads.