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
//! Where a project's `.trusty-mpm/` harness state lives (#4832).
//!
//! Why: the owner ruling (2026-08-04) is that a worktree holds the mutable,
//! tracked working set — code, config, docs — and nothing else. Harness state
//! belongs to the PROJECT, not to the branch that happens to be checked out.
//! Every writer used to join `.trusty-mpm` onto whatever directory it was
//! handed, so one project accumulated a `.trusty-mpm/` per worktree, a bare `tm`
//! run from a subdirectory seeded one there, and a launch outside any repository
//! scattered one wherever the operator's shell happened to be standing.
//! What: [`harness_root_for`] answers "which checkout owns this project's
//! harness state" from git itself, then [`harness_dir`], [`framework_dir`] and
//! [`session_dir`] compose the three directories the ruling defines —
//! `framework/` (project-stable config), `sessions/<id>/` (per-session output),
//! and `logs/`. [`session_scope`] resolves the `<id>` segment.
//! Test: `harness_root_is_the_main_checkout_for_a_worktree`,
//! `harness_root_maps_a_base_clone_back_to_the_project`,
//! `harness_root_for_is_none_outside_a_git_repo`,
//! `session_dir_is_per_session_under_the_harness_root`.
use ;
use crategit_command;
/// The project-local harness directory name.
///
/// Why: one literal, so a writer cannot spell it differently from a reader.
/// What: `.trusty-mpm`.
/// Test: `harness_dir_is_under_the_harness_root`.
pub const HARNESS_DIR: &str = ".trusty-mpm";
/// Subdirectory holding project-stable framework config (`manifest.toml`).
///
/// Why: the ruling separates project-stable config from per-session output;
/// this is the config half, and an operator MAY track it.
/// What: `framework`.
/// Test: `framework_dir_is_under_the_harness_dir`.
pub const FRAMEWORK_DIR: &str = "framework";
/// Subdirectory holding per-session output.
///
/// Why: compiled instructions are session-scoped, not project-scoped — that is
/// what removes the concurrent-session overwrite by construction.
/// What: `sessions`.
/// Test: `session_dir_is_per_session_under_the_harness_root`.
pub const SESSIONS_DIR: &str = "sessions";
/// The shared bare clone trusty-mpm provisioned inside a managed project
/// before #4270.
///
/// Why: `provisioner::workspace` used to clone the shared base into
/// `<project>/.base` and add every managed session's worktree from THERE, so
/// those worktrees' git common dir is the bare clone — a directory INSIDE the
/// project, not the project. #4270 retired that store: provisioning now clones
/// the base into `<project>` itself and puts worktrees at
/// `<project>/.worktrees/<id>`, matching the in-project path. Existing `.base`
/// stores were deliberately NOT migrated, so this mapping stays load-bearing
/// for every worktree already under one, and the name is also what
/// `provisioner::workspace`'s own guard checks before refusing to clone over a
/// live legacy store.
/// What: `.base`. The name alone is never sufficient — see
/// [`map_base_clone_to_project`], which also requires the directory to be a
/// BARE repository before rewriting it.
/// Test: `harness_root_maps_a_base_clone_back_to_the_project`,
/// `harness_root_for_a_non_bare_repo_named_base_is_itself`,
/// `migrate_old_layout_aside_refuses_a_dir_holding_a_dot_base_store`.
pub const BASE_CLONE_DIRNAME: &str = ".base";
/// Name the protected state `dir` holds, or `None` when it holds none.
///
/// Why (#4270): two call sites decide whether a directory may be renamed aside
/// — `provisioner::workspace`'s stale-base recovery hint and
/// `daemon::managed_routes::inproject::migrate_old_layout_aside`. Both used to
/// ask a narrower question (is there a `.base`? is there a `.git`?) and both
/// were wrong in the same way: the thing that must never be moved is any
/// directory git or trusty-mpm keeps real state in. Renaming one orphans every
/// worktree beneath it, which is the #3605 failure with the project directory
/// as its target. One predicate means the two guards cannot drift apart.
///
/// The probe is deliberately fail-SAFE rather than accurate. `Path::exists`
/// answers `false` for a directory it lacks permission to stat, so an EACCES
/// blip on `.git` reads exactly like a project with no git directory at all —
/// and the caller then renames live work. Anything that is not a definite
/// `NotFound` therefore counts as present: the cost of a false positive is a
/// refusal the operator can act on, the cost of a false negative is lost work.
/// What: returns the first of `.git`, `.base`, the CONFIGURED worktree base, or
/// the built-in `.worktrees` found under `dir`, so the caller's message can name
/// what it found; `None` only when all are definitely absent.
///
/// #5204 widened this from a fixed triple to include a retargeted worktree base.
/// Both the configured name and `.worktrees` are probed: a checkout that held
/// worktrees before the retarget still holds them after, and moving it aside
/// would orphan them just the same. Returns `String` rather than `&'static str`
/// because the configured name is resolved at runtime.
/// Test: `protected_state_in_names_each_protected_entry`,
/// `protected_state_in_is_none_for_a_foreign_directory`,
/// `protected_state_in_treats_an_unreadable_entry_as_present`.
pub
/// Environment variable carrying the managed session id inside a tm pane.
///
/// // #8233: `pub` so `runtime::launch_spec` sets the same name it is read from
/// here. The pane-shell `export` that used to publish it is now an `env` entry
/// on the spawned process, and a second spelling of the name would silently
/// break the in-place relaunch (#2023 component C).
pub const MANAGED_SESSION_ID_ENV: &str = "TM_MANAGED_SESSION_ID";
/// Session-directory segment for a launch with no managed session identity.
///
/// Why: three of the six production `prepare_session` call sites (in-place
/// start, standalone load, deploy-validate repair) run BEFORE any session id
/// exists, and `tm connect` never mints one at all. They still need a
/// deterministic, project-local directory, and inventing a fresh id per call
/// would strew unreachable directories that no later reader could match to a
/// session.
/// What: `local` — one bucket per project for unmanaged launches, which is
/// still strictly narrower than the single per-project file it replaces.
/// Test: `session_scope_falls_back_to_the_unmanaged_bucket`.
pub const UNMANAGED_SESSION_SCOPE: &str = "local";
/// The checkout that owns `dir`'s harness state, or `None` outside git.
///
/// Why: a worktree must never accumulate harness state, and the only
/// authoritative answer to "which checkout is this worktree a worktree OF" is
/// git's own shared git directory. `None` is the load-bearing result: it means
/// `dir` is not inside any git working tree, which is the condition
/// `tm session start` refuses on rather than scattering `.trusty-mpm/` into an
/// arbitrary shell directory (#4832 defect 5).
/// What: probes git through the hardened [`git_command`] (which strips the
/// environment variables that could point git at a different repository) and
/// branches on the ONE distinction that matters — whether `dir` sits in a
/// linked worktree, i.e. whether `--git-dir` differs from `--git-common-dir`.
///
/// - Not a linked worktree, not bare: `dir` is in a working tree that owns its
/// own state, so the answer is `--show-toplevel`. This is deliberately NOT
/// derived from the git directory, because for a submodule
/// (`<super>/.git/modules/<name>`) and for a `--separate-git-dir` checkout
/// (`<store>.git`) the git directory is not inside the working tree at all.
/// - Not a linked worktree, bare: there is no working tree, so the repository
/// directory is the root — modulo [`map_base_clone_to_project`].
/// - A linked worktree: git's SHARED directory identifies the checkout it
/// belongs to. `<main>/.git` → the main checkout; anything else is a
/// repository directory, again modulo [`map_base_clone_to_project`].
///
/// Any failed probe returns `None`, never a guess.
/// Test: `harness_root_is_the_main_checkout_for_a_worktree`,
/// `harness_root_maps_a_base_clone_back_to_the_project`,
/// `harness_root_for_is_none_outside_a_git_repo`,
/// `harness_root_is_the_repo_root_from_a_subdirectory`,
/// `harness_root_for_a_non_bare_repo_named_base_is_itself`,
/// `harness_root_for_a_submodule_is_the_submodule_checkout`,
/// `harness_root_for_a_separate_git_dir_checkout_is_the_working_tree`.
/// The checkout `dir`'s LINKED worktree belongs to, or `None` when `dir` is not
/// in a linked worktree (#7673).
///
/// Why: the one worktree detector in the crate. `harness_root_for` answers a
/// wider question — it also resolves plain checkouts, subdirectories and bare
/// repos to their toplevel — and a caller that only needs "is this a linked
/// worktree, and of what" must not re-parse `.git` files to find out.
/// What: `None` unless `--git-dir` differs from `--git-common-dir` (a submodule
/// and an independent clone both have them equal); otherwise the same owner
/// [`harness_root_for`] reports for a linked worktree. Any failed probe is
/// `None`.
/// Test: `linked_worktree_owner_is_the_main_checkout_only_for_a_linked_worktree`.
/// The checkout that owns a linked worktree's shared git directory.
///
/// What: `<main>/.git` → `<main>`; any other directory through
/// [`map_base_clone_to_project`].
/// Test: see [`linked_worktree_owner`].
/// Rewrite a bare [`BASE_CLONE_DIRNAME`] clone to the project that contains it.
///
/// Why: `<project>/.base` is trusty-mpm's own provisioning artifact — a
/// directory INSIDE the project, not the project — so harness state resolved
/// through it belongs one level up. Bareness is part of the test, not an
/// assumption: an ordinary checkout that merely happens to be named `.base`
/// owns its own state and must resolve to ITSELF, or a plain repository would
/// write its harness state outside itself and possibly into a different
/// repository (#4841 review).
/// What: returns `repo_dir`'s parent only when `repo_dir` is named `.base` AND
/// `git rev-parse --is-bare-repository` confirms it is bare; otherwise
/// `repo_dir` unchanged. An unobservable bareness probe does not remap.
/// Test: `harness_root_maps_a_base_clone_back_to_the_project`,
/// `harness_root_for_a_non_bare_repo_named_base_is_itself`.
/// [`harness_root_for`], falling back to `dir` itself.
///
/// Why: the library must still produce a deterministic, project-local path for
/// a caller that is legitimately outside a repository — hermetic tests, and the
/// `tm connect`/standalone paths that operate on a directory the operator
/// named. The fallback is `dir` and never a global location: a non-git launch
/// is REFUSED at the CLI boundary (`tm session start`), so this is a
/// last-resort default, not a silent success path.
/// What: `harness_root_for(dir).unwrap_or_else(|| dir.to_path_buf())`.
/// Test: `harness_root_falls_back_to_the_given_dir_outside_git`.
/// `<harness-root>/.trusty-mpm`.
///
/// Why: every project-local harness write resolves its directory here, so a
/// worktree cannot grow one.
/// What: [`harness_root`] joined with [`HARNESS_DIR`].
/// Test: `harness_dir_is_under_the_harness_root`.
/// `<harness-root>/.trusty-mpm/framework`.
///
/// Why: the project-stable config layer — today the operator's `manifest.toml`
/// override. Deliberately NOT where per-session output lands.
/// What: [`harness_dir`] joined with [`FRAMEWORK_DIR`].
/// Test: `framework_dir_is_under_the_harness_dir`.
/// `<harness-root>/.trusty-mpm/sessions/<session_id>`.
///
/// Why: per-session output cannot collide with a concurrent session's by
/// construction, which is what retires the shared per-project compiled prompt.
/// What: [`harness_dir`] joined with [`SESSIONS_DIR`] and the session segment.
/// Test: `session_dir_is_per_session_under_the_harness_root`.
/// Resolve the `<session-id>` directory segment for this launch.
///
/// Why: the writers reach this seam with three different amounts of knowledge —
/// a managed spawn HAS the session id, an in-place relaunch runs inside a pane
/// that exports it, and a fresh in-place start has none yet. One resolver keeps
/// the three from picking different segments for the same session.
/// What: `explicit` when it is a usable segment, else
/// [`MANAGED_SESSION_ID_ENV`] from the process environment, else
/// [`UNMANAGED_SESSION_SCOPE`]. A candidate is usable only when it is a
/// non-empty run of `[A-Za-z0-9._-]` that is not `.` or `..` — the environment
/// is operator-writable and this value becomes a path component, so a `../`
/// escape must never reach [`session_dir`].
/// Test: exercised through [`session_scope_from`] —
/// `session_scope_prefers_the_explicit_id`,
/// `session_scope_reads_the_managed_env_var`,
/// `session_scope_falls_back_to_the_unmanaged_bucket`,
/// `session_scope_rejects_a_path_traversal_segment`.
/// [`session_scope`] with the ambient managed id passed in.
///
/// Why: the resolution ORDER and the path-component validation are the parts
/// worth testing, and testing them through `std::env` would mean mutating
/// process-global state from a parallel test binary — the exact race this
/// split avoids. `session_scope` is then a two-line adapter with nothing left
/// to get wrong.
/// What: first usable of `explicit`, then `ambient`, else
/// [`UNMANAGED_SESSION_SCOPE`].
/// Test: `session_scope_prefers_the_explicit_id`,
/// `session_scope_reads_the_managed_env_var`,
/// `session_scope_falls_back_to_the_unmanaged_bucket`,
/// `session_scope_rejects_a_path_traversal_segment`.
/// Is `raw` safe to use as a single path component?
///
/// Why/What: see [`session_scope`]. Returns the owned segment when it is a
/// non-empty run of ASCII alphanumerics, `.`, `_` or `-` and is neither `.` nor
/// `..`; `None` otherwise.
/// Test: `session_scope_rejects_a_path_traversal_segment`.
/// One absolute path from a `git rev-parse` path selector, or `None`.
///
/// Why: an empty stdout, a non-zero exit, or a missing `git` must all read as
/// "could not observe" rather than as a fact about `dir`. `--show-toplevel`
/// legitimately fails inside a bare repository, which is exactly why the caller
/// establishes bareness first.
/// What: `git -C <dir> rev-parse --path-format=absolute <selector>` through the
/// hardened [`git_command`].
/// Test: covered by every `harness_root_*` test.
/// Is `dir` inside a bare repository? `None` when the probe is unobservable.
///
/// Why: bareness is what separates "no working tree owns this, so the
/// repository directory is the root" from "a working tree owns this". Anything
/// other than a clean `true`/`false` is `None` so a caller can refuse to guess.
/// What: `git -C <dir> rev-parse --is-bare-repository` through [`git_command`].
/// Test: `harness_root_maps_a_base_clone_back_to_the_project`,
/// `harness_root_for_a_non_bare_repo_named_base_is_itself`.