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
//! The one polling primitive the end-to-end tests wait on, bounded by
//! **silence** rather than by wall time.
//!
//! Every e2e assertion about a detached driver (§2.11) is made against
//! disk: the test owns no handle on the process, so it watches the
//! workspace exactly as a frontend does (§3.5) until the artifact it
//! expects shows up. The pass path is satisfied by observable state
//! however slowly it arrives — so a wall-clock bound on it is not a
//! property of the code under test, it is a property of the machine. A
//! deadline measured on a loaded box reports the load, which is the same
//! call `docs/ARCHITECTURE.md` §2.9 already makes for stop's leader-pgid
//! re-read: *"a retry **count**, not a wall-clock deadline: this race only
//! appears under load, and a deadline measured under load reports the
//! load."* A 120s bound here was outrun anyway (bl-2bf0) on a box running
//! three test suites at once — the chain was slow, not stuck, and the
//! bound could not tell those apart.
//!
//! So the bound counts **consecutive probes that observed no activity in
//! the workspace**. Activity is any change in the tree — a file appearing,
//! growing, being replaced or removed — which is what a live driver emits
//! continuously (delivery commits, transcript entries, streamed
//! `response.json` event lines, worktree churn) and what a wedged or dead
//! one emits none of. An arbitrarily slow machine therefore only makes the
//! pass path slower, never redder; the only thing that fails is genuine
//! silence, which is the hang the bound was always there to diagnose.
use fs;
use ;
use ;
use thread;
use ;
/// Cadence between probes — backoff, never a verdict.
const PROBE_INTERVAL: Duration = from_millis;
/// The bound: consecutive probes that saw a wholly motionless workspace.
/// It is *not* a budget for the work — the work resets it every time it
/// touches disk — so it is sized against the longest plausible gap
/// between two writes of a chain that is still going (process spawn and
/// exec, a git object read, a model-call round trip against a local mock),
/// with orders of magnitude to spare. Nothing on the pass path approaches
/// it, so the number costs only how long a genuine hang takes to report.
const SILENT_PROBES: u32 = 600;
/// Probe until it yields a value, or the workspace falls silent for
/// [`SILENT_PROBES`] straight probes. `None` is the stall verdict — the
/// caller panics with its own diagnostics, since only the caller knows
/// what it was watching and what else is worth dumping (a child's stderr,
/// the ref list). The caller's mutable borrows end with this call, which
/// is why the diagnostics are not a second closure taken here.
/// How long [`until`] stays with a motionless workspace, for diagnostics.
/// A fingerprint of every path under `root` — an order-independent sum
/// over (path, length, mtime), so it moves on any create, append,
/// replace, rename or delete and holds still while nothing runs.
/// Symlinks are fingerprinted, never followed.