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
//! What:
//! The PID namespace an owner record names, and the rule for judging an owner that lives in another one.
//! Why:
//! A PID means something only inside its PID namespace. A wrapper in a container that probes a host owner's PID
//! finds no process (or an unrelated one), so it would judge a live owner dead and recover or retire what the owner
//! still holds (issues 1781 and 1784). A record therefore names its namespace, and an owner of another namespace is
//! judged by the age of its record instead of by the PID.
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! const sameNamespace = readlinkSync('/proc/self/ns/pid') === record.ownerPidNamespace;
//! ```
/// The longest a merge waits for a foreign program's `index.lock`, which the patience is derived from.
use crateLOCK_CEILING;
use Path;
use ;
/// How long an owner of another PID namespace counts as alive, measured from when its record was written.
/// A commit or a lock hold lasts seconds to minutes. Past this age a record is neither alive nor dead: the command that finds it stops,
/// changes nothing and names it (`ExpiredForeignRecord`; owner, 2026-11-04, "Refuse and name it (Recommended)"), because the age proves no
/// death, and a crashed foreign owner's record is removed by the person with the `rm` the error gives.
/// Three kinds of record are judged by this age. A merge's intent record (`merge_isolation_journal.rs`, `write_merge_intent`,
/// written once and synced per merge) and a claim file (`single_owner_claim.rs`, `write_private`, written once) are not
/// rewritten while the owner works, so the age is the owner's whole hold; the item path of a worktree add is judged by its own
/// modification time (`worktree_copy_owner.rs`, `is_alive`), which the add's work can move, so its age is at most the add's
/// length. The patience must outlast the longest such hold.
/// Basis: twice `LOCK_CEILING` (10 minutes, `wait_ceilings.rs`), the longest a merge waits for a foreign `index.lock` with its
/// record already written, so 20 minutes. It is derived, not written as a number, so the two cannot drift apart again: it was
/// an hour because it was twice the 30 minute live lock wait of `wait_limit.rs`, a file that no longer exists, and the owner's
/// rule (2026-11-03, "nothing should ever wait forever or indefinitely") bounded that wait at `LOCK_CEILING`. Measured: the
/// longest worktree add in the add timing log (79 adds, 2026-11-03T08:55:46Z to 2026-11-04T13:53:17Z, `totalMs`) took 539.7 s
/// and the longest commit in the commit timing log (1,838 commits to 2026-11-04T14:08:47Z) 750.6 s, so the patience is 1.6
/// times the longest command measured and 2.2 times the longest add. The logs hold commands of the host's own PID namespace, so
/// no foreign-namespace hold was observed directly. If `LOCK_CEILING` changes, this value follows it; revisit the margin then
/// (`doc/planning/cli-git-shipped-limits-audit.md`, "Foreign-owner patience").
pub const FOREIGN_OWNER_PATIENCE: Duration = from_secs;
/// What:
/// The identity of this process's PID namespace, such as `pid:[4026531836]`, or nothing where the system has none.
/// Why:
/// The kernel names a namespace by an inode; two processes share a PID space exactly when the names are equal.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// readlinkSync('/proc/self/ns/pid')
/// ```
/// What:
/// Whether a recorded namespace and the reader's namespace are both known and differ.
/// Why:
/// Without both names nothing proves the PID space differs, so the PID probe keeps its old meaning; a record
/// written before this field existed carries no name.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// recorded !== undefined && current !== undefined && recorded !== current
/// ```
/// What:
/// The age of a record file, or nothing when it cannot be measured.
/// Why:
/// The record file's modification time stands for when the owner started; a record that cannot be measured keeps its owner alive,
/// because the wrapper never retires what it cannot prove dead.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// Date.now() - statSync(recordPath).mtimeMs
/// ```
/// What:
/// Whether a record of this age has outlived the patience; exactly the patience still counts as alive.
/// Why:
/// The boundary is a fact of the rule, so it is separated from the clock to be checked exactly.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// age > FOREIGN_OWNER_PATIENCE_MS
/// ```
/// What:
/// A record of another PID namespace that is older than the patience: neither alive nor dead.
/// Why:
/// Owner, 2026-11-04, "Refuse and name it (Recommended)": expiry no longer authorizes a command to treat a foreign owner as dead
/// and to recover, take over or remove what it holds. The age is a length that nothing bounds (a write that cannot be run twice may
/// run past any number, and a step the wrapper left running outlives the wrapper), so it proves no death. The command stops, changes
/// nothing, and says which record it found and what a person checks before removing it.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// type ExpiredForeignRecord = { record: string; namespace: string; age: number };
/// ```
/// What:
/// How a record is judged when it names another PID namespace: alive while it is younger than the patience, refused after it, and
/// nothing for a record of this namespace or of none, which the PID decides.
/// Why:
/// Callers share one rule: an owner of another namespace is alive until its record outlives the patience, and then the command
/// stops (`ExpiredForeignRecord`) and changes nothing.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// isOtherNamespace(recorded, current) ? (ageOutlivesPatience(age(recordPath)) ? refused : alive) : undefined
/// ```
/// Namespace controls stay out of the release executable.