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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//! Tests for [`super`] — the OS-level "is anything standing in here?" gate (#4311).
//!
//! Why: this gate's whole value is its fail direction. A wrong implementation
//! that returned `None` on a probe it could not run would read as "free" and
//! hand a live directory to `git worktree remove --force`, so the arms that
//! must refuse get the coverage, not the happy path.
//! Test: this file.
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::{Duration, Instant};
use super::{process_holding, run_cwd_probe, scan_probe};
/// One `lsof -F pcn` process set.
fn set(pid: &str, command: &str, cwd: &str) -> String {
format!("p{pid}\nc{command}\nn{cwd}\n")
}
/// The self-visibility proof every non-refusing listing must carry: this
/// process's own pid with a working directory.
fn self_set() -> String {
set(&std::process::id().to_string(), "test", "/")
}
/// A process whose cwd is the candidate itself is named, with pid and command.
#[test]
fn liveness_reports_a_process_standing_in_the_directory() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let listing = format!(
"{}{}",
set("100", "zsh", "/Users/bob"),
set("7762", "trusty-memory", "/r/.claude/worktrees/agent-x")
);
let reason = scan_probe(&listing, &root).expect("a process in the tree must refuse removal");
assert!(reason.contains("7762"), "must name the pid: {reason}");
assert!(
reason.contains("trusty-memory"),
"must name the command: {reason}"
);
}
/// A cwd nested BELOW the candidate still counts — the 2026-08-15 shape.
#[test]
fn liveness_reports_a_process_nested_below_the_directory() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let listing = set(
"7762",
"cargo",
"/r/.claude/worktrees/agent-x/crates/trusty-mpm",
);
assert!(
scan_probe(&listing, &root).is_some(),
"a process one level down is still inside the tree"
);
}
/// A sibling worktree is not this worktree, and a prefix that is not a path
/// boundary is not a match either.
///
/// Why: a naive `str::starts_with` would read `…/agent-x2` as inside
/// `…/agent-x` and refuse to reap a directory nothing is using, permanently.
#[test]
fn liveness_ignores_a_sibling_directory() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let listing = format!(
"{}{}{}",
self_set(),
set("100", "zsh", "/r/.claude/worktrees/agent-y"),
set("101", "zsh", "/r/.claude/worktrees/agent-x2")
);
assert_eq!(
scan_probe(&listing, &root),
None,
"neither sibling is inside the candidate"
);
}
/// #4311 REGRESSION: a listing that cannot see THIS process is not a negative.
///
/// Why: without root, `lsof` reports only the caller's own processes and exits
/// 0 either way — measured on this machine, `lsof -w -d cwd -F pcn` returned
/// 429 of 667 processes and ZERO of the 229 owned by other users. The
/// pre-review check was a single "did any `n` line appear" flag, which that
/// partial listing satisfies, so blindness to the process that matters read as
/// a clean negative — a PERMIT on `git worktree remove --force`. This listing
/// is non-empty and names a real cwd, so it passes the old flag; it must still
/// refuse, because the probe never reported the process that ran it.
#[test]
fn liveness_treats_a_listing_that_cannot_see_this_process_as_in_use() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let listing = set("100", "zsh", "/Users/bob");
let reason =
scan_probe(&listing, &root).expect("a listing blind to this process rules nothing out");
assert!(
reason.contains(&std::process::id().to_string()),
"the refusal must name the pid it could not find: {reason}"
);
}
/// #4311 REGRESSION: a probe that could not be run reads as IN USE, never free.
///
/// Why (ADR-0045): an unanswerable question on a destructive path is
/// UNDETERMINABLE, not ABSENT. Returning `None` here would let a
/// `git worktree remove --force` proceed on a machine where the check never
/// ran at all — the exact shape of #4470's empty-`lsof` defect, one subsystem
/// over.
#[test]
fn liveness_treats_a_missing_lsof_as_in_use() {
let err = run_cwd_probe(
Command::new("trusty-mpm-no-such-probe-binary"),
Duration::from_secs(5),
)
.expect_err("an absent probe binary must not yield a listing");
assert!(
err.contains("could not run"),
"must say which step failed: {err}"
);
}
/// #7540 critic round REGRESSION: a probe that outlives its ceiling is killed,
/// reaped, and reads as IN USE.
///
/// Why: an unbounded `lsof` stalled on a hung network mount held the reclaim
/// tick, and every later sweep, forever. The stand-in records its own pid and
/// then blocks well past the ceiling; the probe must return inside the ceiling
/// with a refusal, and the pid must be gone.
#[cfg(unix)]
#[test]
fn liveness_kills_a_probe_that_outlives_its_ceiling() {
let tmp = tempfile::tempdir().expect("tempdir");
let pidfile = tmp.path().join("probe.pid");
let mut hung = Command::new("sh");
hung.args([
"-c",
&format!("echo $$ > {}; exec sleep 20", pidfile.display()),
]);
let started = Instant::now();
let err = run_cwd_probe(hung, Duration::from_millis(500))
.expect_err("a probe past its ceiling must not yield a listing");
assert!(
started.elapsed() < Duration::from_secs(10),
"the ceiling must fire; took {:?}",
started.elapsed()
);
assert!(err.contains("timed out after"), "{err}");
let pid: i32 = std::fs::read_to_string(&pidfile)
.expect("the stand-in must have recorded its pid")
.trim()
.parse()
.expect("a pid");
// SAFETY: signal 0 only probes for existence; `pid` is this test's child.
let alive = unsafe { libc::kill(pid, 0) } == 0;
if alive {
// SAFETY: as above; never leave the stand-in behind, whatever the verdict.
unsafe { libc::kill(pid, libc::SIGKILL) };
}
assert!(!alive, "the stalled probe (pid {pid}) was left running");
}
/// A listing carrying no working directory at all rules nothing out.
#[test]
fn liveness_treats_an_empty_probe_as_in_use() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
let reason = scan_probe("", &root).expect("an empty listing observed nothing");
assert!(reason.contains("ADR-0045"), "{reason}");
}
/// A listing in a shape the parser does not recognise is likewise in use.
#[test]
fn liveness_treats_an_unparsable_probe_as_in_use() {
let root = PathBuf::from("/r/.claude/worktrees/agent-x");
assert!(
scan_probe("COMMAND PID USER FD\nzsh 100 bob cwd\n", &root).is_some(),
"a human-readable table carries no `n` field — the probe answered in a \
shape this cannot read, which is not evidence of freedom"
);
}
/// A path that does not exist cannot be compared against a resolved cwd, so the
/// answer is in use rather than free.
#[test]
fn liveness_treats_an_uncanonicalizable_path_as_in_use() {
let reason = process_holding(Path::new("/nonexistent/trusty-mpm/agent-x"))
.expect("an unresolvable path must not read as free");
assert!(reason.contains("canonicalize"), "{reason}");
}
/// The real probe runs on this machine and clears an empty temp directory.
///
/// Why: the parser tests all run against fixed text, so without this nothing
/// proves the flags actually work against the `lsof` that is installed — the
/// gate could fail-closed forever and every other test would still pass.
#[test]
fn liveness_clears_an_unused_directory_against_the_real_probe() {
let dir = tempfile::tempdir().expect("tempdir");
match process_holding(dir.path()) {
None => {}
// A machine with no `lsof` refuses by design; that is this gate's
// stated cost, not a test failure. Assert it refused for THAT reason
// and not because the probe ran and found something in a fresh
// directory, which would be a real defect.
Some(reason) => assert!(
reason.contains("could not run") || reason.contains("exited"),
"a fresh temp directory holds no process; the only acceptable \
refusal is an unavailable probe, got: {reason}"
),
}
}