trusty-mpm 1.8.2

trusty-mpm: unified multi-agent orchestration platform (core, daemon, CLI, TUI, Telegram)
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
//! Keep a slot directory from serving one worktree's build to another (#8261).
//!
//! Why: cargo hashes a path package's identity RELATIVE to its workspace root
//! and records dep-info paths package-relative, so two worktrees of one
//! repository map to the SAME unit names in a target directory, and cargo
//! decides freshness by comparing mtimes. When a slot's previous holder built
//! worktree A and the next holder builds worktree B, B's sources are often OLDER
//! than A's outputs — cargo then reports every path crate "Fresh" and the test
//! binary contains none of B's changes (observed on a pooled slot, 2026-09-24).
//! A silently stale verdict is worse than a slow one.
//!
//! What: each slot directory carries [`LAST_CHECKOUT_MARKER`], naming the
//! checkout root that last built in it. [`invalidate_if_checkout_changed`]
//! compares it with the incoming holder's checkout; on a change — or when the
//! marker is missing, since a slot from the retired dispatch-time pool has an
//! unknown history — it deletes the `.fingerprint` entries of the checkout's
//! own workspace packages, so cargo rebuilds exactly those crates and their
//! dependents while every registry dependency stays warm. When the workspace
//! package list cannot be read, it deletes EVERY fingerprint: a cold build is
//! the price of certainty. The slot's flock is held throughout, so no build
//! is running in the directory while it is edited.
//!
//! Cost (#9045): the guard lists the slot root and nothing below it. Cargo's
//! layout puts `.cargo-lock` and `.fingerprint` at `<profile>/` and
//! `<triple>/<profile>/`, so both are probed by direct path. A slot's `deps`,
//! `build` and `incremental` grow without bound — one held 458,794 entries and
//! a readdir of it stalled `tm build-lease` for 30+ minutes.
//! Test: `stale_guard_tests.rs`.

use std::ffi::OsString;
use std::fs::FileType;
use std::io;
use std::path::{Path, PathBuf};

/// The marker file inside a slot directory naming its last checkout.
pub const LAST_CHECKOUT_MARKER: &str = ".trusty-slot-last-checkout";

/// The lock cargo holds on a profile directory for a build's life.
const CARGO_LOCK: &str = ".cargo-lock";

/// The per-profile directory of cargo's freshness records.
const FINGERPRINT_DIR: &str = ".fingerprint";

/// The filesystem calls the guard makes — a seam so tests can count them.
///
/// Why (#9045): the regression test must measure what the guard visits, not
/// wall time. What: [`RealFs`] is the only production implementation.
/// Test: `the_lock_guard_lists_only_the_slot_root`.
trait SlotFs {
    /// Names of the directories directly inside `dir` — one `read_dir`.
    fn child_dirs(&self, dir: &Path) -> io::Result<Vec<OsString>>;
    /// The type of `path`, following symlinks.
    fn file_type(&self, path: &Path) -> io::Result<FileType>;
    /// Try `flock(LOCK_EX | LOCK_NB)` on `lock`; `Ok(true)` when taken (and
    /// released at once), `Ok(false)` when another description holds it.
    fn try_lock(&self, lock: &Path) -> io::Result<bool>;
}

/// [`SlotFs`] over the real filesystem.
struct RealFs;

impl SlotFs for RealFs {
    fn child_dirs(&self, dir: &Path) -> io::Result<Vec<OsString>> {
        // #9045: only this open error may say "absent"; entry errors go
        // through `dirs_in`, which never reports one as absent.
        let read = std::fs::read_dir(dir)?;
        dirs_in(read.map(|e| e.map(|e| (e.file_name(), e.file_type()))))
    }

    fn file_type(&self, path: &Path) -> io::Result<FileType> {
        std::fs::metadata(path).map(|m| m.file_type())
    }

    fn try_lock(&self, lock: &Path) -> io::Result<bool> {
        use std::os::fd::AsRawFd;
        let file = std::fs::File::open(lock)?;
        // SAFETY: `file` owns a valid descriptor for both calls.
        let rc = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) };
        if rc != 0 {
            return Ok(false);
        }
        // SAFETY: as above; releases the probe lock at once.
        unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_UN) };
        Ok(true)
    }
}

/// The directory names in a listing, failing closed on a per-entry error.
///
/// Why (#9045): an entry error carrying `NotFound` must not reach
/// [`is_absent`], where it would read as "slot does not exist" and so free.
/// What: a directory is kept; a non-directory is skipped; an entry whose type
/// lookup says `NotFound` vanished mid-listing and is skipped; every other
/// entry error fails the listing as `ErrorKind::Other`.
/// Test: `a_per_entry_listing_error_is_never_absent`.
fn dirs_in(
    entries: impl Iterator<Item = io::Result<(OsString, io::Result<FileType>)>>,
) -> io::Result<Vec<OsString>> {
    let entry_error = |err: io::Error| io::Error::other(format!("slot entry unreadable: {err}"));
    let mut out = Vec::new();
    for entry in entries {
        let (name, kind) = entry.map_err(entry_error)?;
        match kind {
            Ok(kind) if kind.is_dir() => out.push(name),
            Ok(_) => {}
            Err(err) if err.kind() == io::ErrorKind::NotFound => {}
            Err(err) => return Err(entry_error(err)),
        }
    }
    Ok(out)
}

/// Whether `err` means the path is not there (as opposed to unreadable).
fn is_absent(err: &io::Error) -> bool {
    matches!(
        err.kind(),
        io::ErrorKind::NotFound | io::ErrorKind::NotADirectory
    )
}

/// What [`invalidate_if_checkout_changed`] did.
///
/// Test: every test below.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Invalidation {
    /// The slot last built this same checkout; nothing was touched.
    SameCheckout,
    /// The slot last built another checkout (or an unknown one); this many
    /// fingerprint entries were removed.
    Cleared {
        /// The previous checkout, or `None` when the marker was missing.
        previous: Option<String>,
        /// Fingerprint entries removed.
        removed: usize,
        /// Whether every fingerprint went because the package list was unreadable.
        all: bool,
    },
}

/// The root of the git checkout containing `cwd`, or `cwd` itself.
///
/// What: the nearest ancestor holding a `.git` entry — a directory in a main
/// checkout, a file in a linked worktree.
/// Test: `the_checkout_root_is_found_from_a_subdirectory`.
#[must_use]
pub fn checkout_root(cwd: &Path) -> PathBuf {
    cwd.ancestors()
        .find(|dir| dir.join(".git").exists())
        .unwrap_or(cwd)
        .to_path_buf()
}

/// Invalidate `slot_dir`'s workspace fingerprints if its last checkout differs.
///
/// What: see the module doc. `packages` lists the checkout's own workspace
/// package names; it is only called when a clear is needed.
///
/// # Errors
///
/// A description when a matching fingerprint entry could not be removed or the
/// fingerprints could not be enumerated (the marker is then left unchanged, and
/// the caller must not build in the slot, #8261 round 3, #9045), or when the
/// marker could not be written.
///
/// Test: `a_different_checkout_clears_only_workspace_fingerprints`,
/// `the_same_checkout_touches_nothing`, `a_missing_marker_clears`,
/// `an_unreadable_package_list_clears_every_fingerprint`,
/// `a_failed_removal_is_an_error_and_keeps_the_marker`,
/// `a_fingerprint_enumeration_error_keeps_the_marker`.
pub fn invalidate_if_checkout_changed(
    slot_dir: &Path,
    checkout: &Path,
    packages: impl FnOnce() -> Result<Vec<String>, String>,
) -> Result<Invalidation, String> {
    let marker = slot_dir.join(LAST_CHECKOUT_MARKER);
    let current = checkout.display().to_string();
    let previous = std::fs::read_to_string(&marker)
        .ok()
        .map(|s| s.trim().to_string());
    if previous.as_deref() == Some(current.as_str()) {
        return Ok(Invalidation::SameCheckout);
    }
    let names = packages();
    let all = names.is_err();
    let names = names.unwrap_or_default();
    let mut removed = 0;
    // #9045: an enumeration error must not advance the marker, or the next
    // holder would skip a clear that never finished.
    let dirs = fingerprint_dirs(&RealFs, slot_dir).map_err(|err| {
        format!(
            "could not list fingerprints in {}: {err}; the slot is not used",
            slot_dir.display()
        )
    })?;
    for dir in dirs {
        let entries = match std::fs::read_dir(&dir) {
            Ok(entries) => entries,
            Err(err) if is_absent(&err) => continue,
            Err(err) => {
                return Err(format!(
                    "could not list {}: {err}; the slot is not used",
                    dir.display()
                ));
            }
        };
        for entry in entries {
            let entry = entry.map_err(|err| {
                format!(
                    "could not list {}: {err}; the slot is not used",
                    dir.display()
                )
            })?;
            let name = entry.file_name().to_string_lossy().into_owned();
            if !(all || names.iter().any(|pkg| is_unit_of(&name, pkg))) {
                continue;
            }
            // #8261 round 3: a fingerprint left behind can serve a stale
            // build, so a failed removal fails the whole invalidation.
            std::fs::remove_dir_all(entry.path()).map_err(|err| {
                format!(
                    "could not clear stale fingerprint {}: {err}; the slot is not used",
                    entry.path().display()
                )
            })?;
            removed += 1;
        }
    }
    std::fs::write(&marker, &current)
        .map_err(|err| format!("could not write {}: {err}", marker.display()))?;
    Ok(Invalidation::Cleared {
        previous,
        removed,
        all,
    })
}

/// Whether a cargo build is running in `slot_dir` right now.
///
/// Why (#8261 round 3): a SIGKILLed `tm build-lease` frees its slot's flock at
/// once while the build it spawned keeps running in the slot's directory. Cargo
/// holds `flock(LOCK_EX)` on `<profile>/.cargo-lock` for a build's life, so that
/// lock — not the slot file — says whether the directory is still in use.
/// What: `true` when a `<profile>/.cargo-lock` or `<triple>/<profile>/.cargo-lock`
/// is locked by another open file description. Profiles are the slot root's
/// child directories holding a `.cargo-lock`; cargo locks the host profile on
/// every build, `--target` builds included. A slot that does not exist is free.
/// Fail-closed (#9045): a slot root that cannot be listed, or a lock file that
/// cannot be stat'ed or opened, counts as busy — its state is unknown.
/// Cost (#9045): one `read_dir` of the slot root and direct-path probes; no
/// directory below the root is listed.
/// Test: `a_held_cargo_lock_marks_the_directory_busy`,
/// `a_held_cross_target_lock_marks_the_directory_busy`,
/// `the_lock_guard_lists_only_the_slot_root`, `a_lock_inside_deps_is_never_probed`,
/// `an_unlistable_slot_counts_as_held`, `an_unstatable_cargo_lock_counts_as_held`,
/// `an_unopenable_cargo_lock_counts_as_held`, `a_missing_slot_reads_free`.
#[must_use]
pub fn cargo_lock_held(slot_dir: &Path) -> bool {
    cargo_lock_held_in(&RealFs, slot_dir)
}

/// [`cargo_lock_held`] over any [`SlotFs`].
fn cargo_lock_held_in(fs: &impl SlotFs, slot_dir: &Path) -> bool {
    // #9045: the slot root is the only directory listed; it holds profile,
    // triple and tool directories, never per-unit files.
    let children = match fs.child_dirs(slot_dir) {
        Ok(children) => children,
        Err(err) if is_absent(&err) => return false,
        Err(_) => return true,
    };
    let mut profiles = Vec::new();
    for child in &children {
        match probe_lock(fs, &slot_dir.join(child).join(CARGO_LOCK)) {
            LockProbe::Absent => {}
            LockProbe::Free => profiles.push(child),
            LockProbe::Busy => return true,
        }
    }
    // #9045: a `--target` layout reuses the host profile names, so each
    // `<triple>/<profile>/.cargo-lock` is a direct path, never a listing.
    children.iter().any(|child| {
        profiles.iter().any(|profile| {
            let lock = slot_dir.join(child).join(profile).join(CARGO_LOCK);
            probe_lock(fs, &lock) == LockProbe::Busy
        })
    })
}

/// The state of one `.cargo-lock` path.
#[derive(Debug, PartialEq, Eq)]
enum LockProbe {
    /// No lock file at the path (or a non-file, which no cargo locks).
    Absent,
    /// A lock file nobody holds.
    Free,
    /// Held by a live build, or its state could not be read.
    Busy,
}

/// Probe `lock` by direct path.
fn probe_lock(fs: &impl SlotFs, lock: &Path) -> LockProbe {
    match fs.file_type(lock) {
        Err(err) if is_absent(&err) => LockProbe::Absent,
        // #9045: EACCES or similar — unknown state, so never reported free.
        Err(_) => LockProbe::Busy,
        Ok(kind) if !kind.is_file() => LockProbe::Absent,
        Ok(_) => match fs.try_lock(lock) {
            Ok(true) => LockProbe::Free,
            Ok(false) | Err(_) => LockProbe::Busy,
        },
    }
}

/// Whether a fingerprint entry `<pkg>-<16 hex>` belongs to package `pkg`.
fn is_unit_of(entry: &str, pkg: &str) -> bool {
    entry
        .strip_prefix(pkg)
        .and_then(|rest| rest.strip_prefix('-'))
        .is_some_and(|hash| hash.len() == 16 && hash.chars().all(|c| c.is_ascii_hexdigit()))
}

/// Every `<profile>/.fingerprint` and `<triple>/<profile>/.fingerprint` in `root`.
///
/// What: profiles are the root's child directories holding a `.fingerprint`;
/// the triple layouts reuse their names.
///
/// # Errors
///
/// #9045: an unlistable root, or a `.fingerprint` stat failing for any reason
/// but absence, is an error — never an empty answer, which would read as
/// "nothing to clear".
///
/// Cost (#9045): one `read_dir` of `root` and direct-path stats; no `deps`,
/// `build` or `incremental` listing.
/// Test: `fingerprint_search_lists_only_the_slot_root`,
/// `a_fingerprint_enumeration_error_keeps_the_marker`.
fn fingerprint_dirs(fs: &impl SlotFs, root: &Path) -> io::Result<Vec<PathBuf>> {
    let children = fs.child_dirs(root)?;
    let fingerprints = |dir: PathBuf| -> io::Result<Option<PathBuf>> {
        let fp = dir.join(FINGERPRINT_DIR);
        match fs.file_type(&fp) {
            Ok(kind) => Ok(kind.is_dir().then_some(fp)),
            Err(err) if is_absent(&err) => Ok(None),
            Err(err) => Err(err),
        }
    };
    let mut profiles: Vec<&OsString> = Vec::new();
    let mut out: Vec<PathBuf> = Vec::new();
    for child in &children {
        if let Some(fp) = fingerprints(root.join(child))? {
            profiles.push(child);
            out.push(fp);
        }
    }
    for child in &children {
        for profile in &profiles {
            out.extend(fingerprints(root.join(child).join(profile))?);
        }
    }
    Ok(out)
}

/// The checkout's own workspace package names, via `cargo metadata --no-deps`.
///
/// # Errors
///
/// A description when cargo could not be run or its output not parsed.
///
/// Test: `workspace_packages_lists_this_workspace`.
pub fn workspace_packages(checkout: &Path) -> Result<Vec<String>, String> {
    let out = std::process::Command::new("cargo")
        .args([
            "metadata",
            "--no-deps",
            "--format-version",
            "1",
            "--offline",
        ])
        .current_dir(checkout)
        .output()
        .map_err(|err| format!("could not run cargo metadata: {err}"))?;
    if !out.status.success() {
        return Err(format!(
            "cargo metadata exited {}: {}",
            out.status,
            String::from_utf8_lossy(&out.stderr).trim()
        ));
    }
    let meta: serde_json::Value = serde_json::from_slice(&out.stdout)
        .map_err(|err| format!("cargo metadata output did not parse: {err}"))?;
    Ok(meta["packages"]
        .as_array()
        .map(|pkgs| {
            pkgs.iter()
                .filter_map(|p| p["name"].as_str().map(str::to_string))
                .collect()
        })
        .unwrap_or_default())
}

#[cfg(test)]
#[path = "stale_guard_tests.rs"]
mod tests;