diff --git a/crates/tracedecay-runtime-core/src/db/access/path_layout.rs b/crates/tracedecay-runtime-core/src/db/access/path_layout.rs index 0f6d0b68c7..2d3d2e98b4 100644 --- a/crates/tracedecay-runtime-core/src/db/access/path_layout.rs +++ b/crates/tracedecay-runtime-core/src/db/access/path_layout.rs @@ -46,14 +46,13 @@ pub(super) fn database_lock_root(database_path: &Path, fallback_parent: &Path) - fn profile_project_root(database_path: &Path) -> Option<&Path> { let parent = database_path.parent()?; - let data_root = if parent.file_name().is_some_and(|name| name == "branches") { - parent.parent()? - } else if parent - .file_name() - .is_some_and(|name| name == ".consolidation-input") - && database_path + let data_root = if parent.file_name().is_some_and(|name| name == "branches") + || (parent .file_name() - .is_some_and(|name| name == "source-sessions.db" || name == "target-sessions.db") + .is_some_and(|name| name == ".consolidation-input") + && database_path + .file_name() + .is_some_and(|name| name == "source-sessions.db" || name == "target-sessions.db")) { parent.parent()? } else { diff --git a/src/daemon/scheduler.rs b/src/daemon/scheduler.rs index 1e6e62c03d..998c5e83d4 100644 --- a/src/daemon/scheduler.rs +++ b/src/daemon/scheduler.rs @@ -321,7 +321,7 @@ impl DaemonEngine { } pub(super) async fn shutdown_automation_schedulers_with_deadline(&self, deadline: Duration) { - let scheduler_handles: Vec> = match timeout( + let Ok(scheduler_handles) = timeout( deadline, self.store_administration.with_writer(|| async { let mut schedulers = self @@ -329,16 +329,16 @@ impl DaemonEngine { .automation_schedulers() .lock() .await; - schedulers.drain().map(|(_, handle)| handle.task).collect() + schedulers + .drain() + .map(|(_, handle)| handle.task) + .collect::>>() }), ) .await - { - Ok(handles) => handles, - Err(_) => { - log_daemon_event("daemon_shutdown", &[("outcome", "timeout".to_string())]); - return; - } + else { + log_daemon_event("daemon_shutdown", &[("outcome", "timeout".to_string())]); + return; }; let _child_shutdown = crate::sessions::codex_app_server::begin_codex_app_server_shutdown(); for handle in &scheduler_handles { diff --git a/src/migrate/consolidate/tests.rs b/src/migrate/consolidate/tests.rs index 0191624606..d3326fca26 100644 --- a/src/migrate/consolidate/tests.rs +++ b/src/migrate/consolidate/tests.rs @@ -402,6 +402,8 @@ async fn synthesized_branch_metadata_change_invalidates_confirmation_token() { #[tokio::test] async fn hook_analytics_append_after_plan_preserves_bytes_without_invalidating_confirmation() { + use std::io::Write; + let fixture = fixture().await; let source = layout_for_id(&fixture.project, &fixture.profile, &fixture.source_id).unwrap(); let telemetry_path = source.data_root.join("hook_analytics.jsonl"); @@ -411,7 +413,6 @@ async fn hook_analytics_append_after_plan_preserves_bytes_without_invalidating_c let options = fixture.options(); let planned = plan(&options).await.unwrap(); - use std::io::Write; fs::OpenOptions::new() .append(true) .open(&telemetry_path) diff --git a/src/tracedecay/lifecycle.rs b/src/tracedecay/lifecycle.rs index 5d650b3724..b3342782d2 100644 --- a/src/tracedecay/lifecycle.rs +++ b/src/tracedecay/lifecycle.rs @@ -229,11 +229,7 @@ impl TraceDecay { // stay behind the rare paths that actually compare stores. Resolving a // layout is on every open, including fail-closed clients that must not // touch the store at all. - let ( - candidates, - selected_manifest_matches_exact_root, - candidates_match_exact_root, - ) = + let (candidates, selected_manifest_matches_exact_root, candidates_match_exact_root) = storage::matching_legacy_profile_layouts(project_root, &profile_root, selected_id)?; if selected.is_some() && !candidates.is_empty() @@ -265,6 +261,9 @@ impl TraceDecay { ) } + // The four flags mirror the resolver evidence computed by the sole + // caller; a params struct would outlive this soon-to-move code. + #[allow(clippy::fn_params_excessive_bools)] async fn choose_identity_layout( project_root: &Path, selected: Option, @@ -280,14 +279,27 @@ impl TraceDecay { // This resolver uses bounded presence probes only; the subsequent // serving open performs full integrity validation and fails closed. // Legacy duplicates stay untouched, while an empty or unreadable - // selected store still reaches the fail-closed diagnostics. + // selected store still reaches the fail-closed diagnostics. An + // exact-root candidate with real content is a genuine identity + // cutover: it keeps the fail-closed conflict diagnostics instead of + // being silently outranked. if (selected_manifest_matches_exact_root || (selected_via_exact_registry_alias && !candidates_match_exact_root)) && !candidates.is_empty() - && let Some(selected) = selected.as_ref() + && let Some(selected_layout) = selected.as_ref() + && store_identity_has_bounded_population_evidence(selected_layout).await { - if store_identity_has_bounded_population_evidence(selected).await { - return Ok(Some(selected.clone())); + let mut exact_candidate_is_populated = false; + if candidates_match_exact_root { + for candidate in &candidates { + if store_identity_has_bounded_population_evidence(candidate).await { + exact_candidate_is_populated = true; + break; + } + } + } + if !exact_candidate_is_populated { + return Ok(Some(selected_layout.clone())); } } if candidates.len() > 1 { @@ -1567,9 +1579,8 @@ async fn store_identity_has_bounded_population_evidence(layout: &StoreLayout) -> tree_has_files(&layout.lcm_payload_root), tree_has_files(&layout.response_handle_root), ); - let (automation_files, payload_files, response_files) = match tree_presence { - (Ok(automation), Ok(payloads), Ok(responses)) => (automation, payloads, responses), - _ => return false, + let (Ok(automation_files), Ok(payload_files), Ok(response_files)) = tree_presence else { + return false; }; graph_is_populated @@ -1591,11 +1602,10 @@ fn branch_inventory(data_root: &Path) -> std::result::Result { let path = data_root.join(storage::BRANCH_META_FILENAME); match std::fs::symlink_metadata(&path) { Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(0), - Err(_) => Err(()), Ok(metadata) if metadata.file_type().is_file() => branch_meta::load_branch_meta(data_root) .map(|meta| meta.branches.len()) .ok_or(()), - Ok(_) => Err(()), + Err(_) | Ok(_) => Err(()), } } diff --git a/tests/storage_suite/storage_resolver_test.rs b/tests/storage_suite/storage_resolver_test.rs index 5e38771ace..d72b387c58 100644 --- a/tests/storage_suite/storage_resolver_test.rs +++ b/tests/storage_suite/storage_resolver_test.rs @@ -1902,9 +1902,7 @@ async fn linked_worktree_exact_registry_alias_ignores_duplicate_shared_legacy_ma store_kind: "code_project".to_string(), storage_mode: "profile_sharded".to_string(), store_relpath: format!("projects/{project_id}"), - manifest_relpath: Some(format!( - "projects/{project_id}/{STORE_MANIFEST_FILENAME}" - )), + manifest_relpath: Some(format!("projects/{project_id}/{STORE_MANIFEST_FILENAME}")), last_verified_at: None, last_write_at: None, }) @@ -2033,7 +2031,12 @@ async fn linked_worktree_exact_manifest_overrides_canonical_exact_registry_alias let canonical = TraceDecay::init(&project).await.unwrap(); canonical.index_all().await.unwrap(); - let canonical_project_id = canonical.store_layout().identity.project_id.clone().unwrap(); + let canonical_project_id = canonical + .store_layout() + .identity + .project_id + .clone() + .unwrap(); canonical.close(); git(