Skip to content

Commit 77d264f

Browse files
fix(storage): keep store inventory off the resolve path
1 parent 5e9e49e commit 77d264f

2 files changed

Lines changed: 26 additions & 75 deletions

File tree

src/storage.rs

Lines changed: 14 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -447,13 +447,11 @@ pub(crate) fn matching_legacy_profile_layouts(
447447
project_root: &Path,
448448
profile_root: &Path,
449449
excluded_project_id: Option<&str>,
450-
selected_layout_is_authoritative: bool,
451450
) -> Result<(Vec<StoreLayout>, bool)> {
452451
matching_legacy_profile_layouts_with_git_resolver(
453452
project_root,
454453
profile_root,
455454
excluded_project_id,
456-
selected_layout_is_authoritative,
457455
crate::worktree::is_detached_linked_worktree,
458456
crate::worktree::git_common_dir,
459457
)
@@ -463,7 +461,6 @@ fn matching_legacy_profile_layouts_with_git_resolver<D, G>(
463461
project_root: &Path,
464462
profile_root: &Path,
465463
excluded_project_id: Option<&str>,
466-
selected_layout_is_authoritative: bool,
467464
mut is_detached_linked_worktree: D,
468465
mut git_common_dir: G,
469466
) -> Result<(Vec<StoreLayout>, bool)>
@@ -503,17 +500,12 @@ where
503500

504501
// A linked worktree may have its own profile shard while sharing a Git
505502
// common directory with every sibling checkout. A non-excluded exact
506-
// manifest overrides the selected identity. A healthy and populated
507-
// marker/registry-selected layout is authoritative only when its manifest
508-
// names this exact checkout; a sibling-root selection must retain the
509-
// shared-Git recovery path.
503+
// manifest overrides the selected identity. Otherwise the shared-Git
504+
// recovery path still runs, and the caller decides whether a selected
505+
// identity naming this exact checkout outranks what it finds.
510506
let selected_is_sole_exact_root =
511507
selected_manifest_matches_exact_root && exact_manifests.is_empty();
512-
let matching_manifests = if !exact_manifests.is_empty()
513-
|| (selected_layout_is_authoritative && selected_manifest_matches_exact_root)
514-
{
515-
exact_manifests
516-
} else {
508+
let matching_manifests = if exact_manifests.is_empty() {
517509
let project_git_common_dir = (!is_detached_linked_worktree(project_root))
518510
.then(|| git_common_dir(project_root))
519511
.flatten();
@@ -536,6 +528,8 @@ where
536528
})
537529
})
538530
.collect()
531+
} else {
532+
exact_manifests
539533
};
540534
let mut layouts = Vec::new();
541535
for (manifest_path, manifest) in matching_manifests {
@@ -1321,7 +1315,7 @@ mod tests {
13211315
use std::sync::{Arc, Barrier};
13221316

13231317
#[test]
1324-
fn exact_root_authority_controls_shared_git_discovery() {
1318+
fn exact_root_manifest_overrides_shared_git_discovery() {
13251319
fn write_manifest(profile_root: &Path, project_id: &str, project_root: &Path) {
13261320
let data_root = profile_root.join("projects").join(project_id);
13271321
fs::create_dir_all(&data_root).unwrap();
@@ -1357,7 +1351,6 @@ mod tests {
13571351
&project_root,
13581352
&profile_root,
13591353
None,
1360-
false,
13611354
|_| false,
13621355
|root| {
13631356
resolver_calls.borrow_mut().push(root.to_path_buf());
@@ -1382,28 +1375,6 @@ mod tests {
13821375
&project_root,
13831376
&profile_root,
13841377
Some("proj_exact"),
1385-
true,
1386-
|_| false,
1387-
|root| {
1388-
resolver_calls.borrow_mut().push(root.to_path_buf());
1389-
Some(dir.path().join("shared.git"))
1390-
},
1391-
)
1392-
.unwrap();
1393-
assert!(layouts.is_empty());
1394-
assert!(selected_is_sole_exact_root);
1395-
assert!(
1396-
resolver_calls.borrow().is_empty(),
1397-
"an authoritative selected exact root must not invoke shared-Git discovery"
1398-
);
1399-
1400-
resolver_calls.borrow_mut().clear();
1401-
let (layouts, selected_is_sole_exact_root) =
1402-
matching_legacy_profile_layouts_with_git_resolver(
1403-
&project_root,
1404-
&profile_root,
1405-
Some("proj_exact"),
1406-
false,
14071378
|_| false,
14081379
|root| {
14091380
resolver_calls.borrow_mut().push(root.to_path_buf());
@@ -1416,11 +1387,14 @@ mod tests {
14161387
layouts[0].identity.project_id.as_deref(),
14171388
Some("proj_unrelated")
14181389
);
1419-
assert!(selected_is_sole_exact_root);
1390+
assert!(
1391+
selected_is_sole_exact_root,
1392+
"the caller decides whether the selected exact root outranks recovery"
1393+
);
14201394
assert_eq!(
14211395
resolver_calls.borrow().as_slice(),
14221396
[project_root, unrelated_root],
1423-
"a non-authoritative selected exact root must retain shared-Git recovery"
1397+
"an excluded selected exact root must retain shared-Git recovery"
14241398
);
14251399
}
14261400

@@ -1452,7 +1426,6 @@ mod tests {
14521426
&project_root,
14531427
&profile_root,
14541428
None,
1455-
false,
14561429
|_| false,
14571430
|_| None,
14581431
)
@@ -1461,7 +1434,7 @@ mod tests {
14611434
}
14621435

14631436
#[test]
1464-
fn authoritative_non_exact_identity_retains_historical_git_discovery() {
1437+
fn non_exact_identity_retains_historical_git_discovery() {
14651438
fn write_manifest(profile_root: &Path, project_id: &str, project_root: &Path) {
14661439
let data_root = profile_root.join("projects").join(project_id);
14671440
fs::create_dir_all(&data_root).unwrap();
@@ -1499,7 +1472,6 @@ mod tests {
14991472
&worktree_root,
15001473
&profile_root,
15011474
Some("proj_selected"),
1502-
true,
15031475
|_| false,
15041476
|root| {
15051477
resolver_calls.borrow_mut().push(root.to_path_buf());
@@ -1510,30 +1482,10 @@ mod tests {
15101482

15111483
assert_eq!(layouts.len(), 1);
15121484
assert!(!selected_is_sole_exact_root);
1513-
assert_eq!(
1514-
resolver_calls.borrow().as_slice(),
1515-
[worktree_root.clone(), historical_root.clone()],
1516-
"a healthy selected identity from a sibling root must retain shared-Git recovery"
1517-
);
1518-
1519-
resolver_calls.borrow_mut().clear();
1520-
let (layouts, _) = matching_legacy_profile_layouts_with_git_resolver(
1521-
&worktree_root,
1522-
&profile_root,
1523-
Some("proj_selected"),
1524-
false,
1525-
|_| false,
1526-
|root| {
1527-
resolver_calls.borrow_mut().push(root.to_path_buf());
1528-
Some(dir.path().join("shared.git"))
1529-
},
1530-
)
1531-
.unwrap();
1532-
assert_eq!(layouts.len(), 1);
15331485
assert_eq!(
15341486
resolver_calls.borrow().as_slice(),
15351487
[worktree_root, historical_root],
1536-
"an unhealthy or pristine selection must retain shared-Git recovery"
1488+
"a selected identity from a sibling root must retain shared-Git recovery"
15371489
);
15381490
}
15391491

src/tracedecay/lifecycle.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -205,18 +205,12 @@ impl TraceDecay {
205205
let selected_id = selected
206206
.as_ref()
207207
.and_then(|layout| layout.identity.project_id.as_deref());
208-
let selected_layout_is_authoritative = if let Some(selected) = selected.as_ref() {
209-
let inventory = store_identity_inventory(selected).await;
210-
inventory.is_healthy() && !inventory.is_pristine()
211-
} else {
212-
false
213-
};
214-
let (candidates, selected_is_sole_exact_root) = storage::matching_legacy_profile_layouts(
215-
project_root,
216-
&profile_root,
217-
selected_id,
218-
selected_layout_is_authoritative,
219-
)?;
208+
// Store inventory opens the graph and sessions databases, so it must
209+
// stay behind the rare paths that actually compare stores. Resolving a
210+
// layout is on every open, including fail-closed clients that must not
211+
// touch the store at all.
212+
let (candidates, selected_is_sole_exact_root) =
213+
storage::matching_legacy_profile_layouts(project_root, &profile_root, selected_id)?;
220214
Self::choose_identity_layout(
221215
project_root,
222216
selected,
@@ -238,7 +232,12 @@ impl TraceDecay {
238232
selected_is_sole_exact_root: bool,
239233
allow_repair: bool,
240234
) -> Result<Option<StoreLayout>> {
241-
if selected_is_sole_exact_root && let Some(selected) = selected.as_ref() {
235+
// With no competing candidate the selected layout wins regardless, so
236+
// skip the inventory rather than opening the store to confirm it.
237+
if selected_is_sole_exact_root
238+
&& !candidates.is_empty()
239+
&& let Some(selected) = selected.as_ref()
240+
{
242241
let selected_inventory = store_identity_inventory(selected).await;
243242
if selected_inventory.is_healthy() && !selected_inventory.is_pristine() {
244243
return Ok(Some(selected.clone()));

0 commit comments

Comments
 (0)