diff --git a/crates/tracedecay-mcp/src/handlers/hook_runtime/admission/tests.rs b/crates/tracedecay-mcp/src/handlers/hook_runtime/admission/tests.rs index 25d074e122..51c9a46022 100644 --- a/crates/tracedecay-mcp/src/handlers/hook_runtime/admission/tests.rs +++ b/crates/tracedecay-mcp/src/handlers/hook_runtime/admission/tests.rs @@ -80,7 +80,9 @@ fn restart_ledger(data_root: &std::path::Path) { #[test] fn producer_work_commits_with_its_admission_and_redrives_until_completed() { let data_root = tempfile::tempdir().unwrap(); - let now = UtcMicros(1_000); + // Settlement reopens an evicted ledger at the wall clock, and parallel + // tests can evict this root, so admit inside the real retention window. + let now = hook_now(); let envelope = admission_test_envelope(31, 7); let staged = stage_hook_v2_admission( data_root.path(), diff --git a/crates/tracedecay-session-runtime/src/session_sync/git_topology.rs b/crates/tracedecay-session-runtime/src/session_sync/git_topology.rs index 39285e392a..27ec0dd156 100644 --- a/crates/tracedecay-session-runtime/src/session_sync/git_topology.rs +++ b/crates/tracedecay-session-runtime/src/session_sync/git_topology.rs @@ -62,6 +62,11 @@ impl SessionSyncProjectContext { } }; let Some(runtime) = project_sessions.project_graph_runtime() else { + tracing::warn!( + event = "git_topology_publication_unavailable", + stage = "project graph runtime", + "git topology publication failed" + ); return GitTopologySyncOutcome::Finished(Err(GitTopologySyncFailure::Unavailable)); }; let runtime: Arc = Arc::new(runtime.clone()); @@ -113,6 +118,22 @@ impl GraphCancellation for GitTopologySyncCancellation { } } +/// Keeps the operator-facing failure code stable while recording which step +/// failed and why, which the code alone cannot say. +fn unavailable( + stage: &'static str, +) -> impl FnOnce(E) -> GitTopologySyncFailure { + move |error| { + tracing::warn!( + event = "git_topology_publication_unavailable", + stage, + ?error, + "git topology publication failed" + ); + GitTopologySyncFailure::Unavailable + } +} + pub fn publish_native_topology( runtime: Arc, project_root: PathBuf, @@ -122,23 +143,23 @@ pub fn publish_native_topology( cancelled: Arc, ) -> Result<(), GitTopologySyncFailure> { let identity = git_topology_projection_identity( - git_topology_namespace(&repository).map_err(|_| GitTopologySyncFailure::Unavailable)?, + git_topology_namespace(&repository).map_err(unavailable("projection namespace"))?, ) - .map_err(|_| GitTopologySyncFailure::Unavailable)?; + .map_err(unavailable("projection identity"))?; let read_cancelled = Arc::clone(&cancelled); let current = runtime .verified_snapshot( &identity, FactReadControl::new(Arc::new(move || read_cancelled.load(Ordering::Acquire))), ) - .map_err(|_| GitTopologySyncFailure::Unavailable)?; + .map_err(unavailable("verified snapshot read"))?; let (branch_stacks, worktree_occupancies) = match current { Some(snapshot) => { let store = GitTopologyProjectionStore::from_verified_snapshot_verified( snapshot, Arc::new(GitTopologySyncCancellation(Arc::clone(&cancelled))), ) - .map_err(|_| GitTopologySyncFailure::Unavailable)?; + .map_err(unavailable("retained projection load"))?; validate_retained_declared_topology( &repository, &project_root, @@ -156,11 +177,11 @@ pub fn publish_native_topology( let adapter = NativeGitIntelligence::new(project_root, repository.clone(), worktree); let projection = adapter .topology_projection(GIT_HISTORY_MAX_COUNT_LIMIT) - .map_err(|_| GitTopologySyncFailure::Unavailable)? + .map_err(unavailable("git topology read"))? .with_declared_topology(branch_stacks, worktree_occupancies) - .map_err(|_| GitTopologySyncFailure::Unavailable)?; + .map_err(unavailable("declared topology merge"))?; let revision = GraphProjectorRevision::try_from(GIT_TOPOLOGY_PROJECTOR_REVISION_V1.to_owned()) - .map_err(|_| GitTopologySyncFailure::Unavailable)?; + .map_err(unavailable("projector revision"))?; let check = || { if cancelled.load(Ordering::Relaxed) { Err(GraphDbError::Cancelled) @@ -169,12 +190,12 @@ pub fn publish_native_topology( } }; let manifest = build_git_topology_manifest_checked(identity, &projection, &revision, &check) - .map_err(|_| GitTopologySyncFailure::Unavailable)?; + .map_err(unavailable("manifest build"))?; let idempotency = git_topology_idempotency_key(&projection, &revision) - .map_err(|_| GitTopologySyncFailure::Unavailable)?; + .map_err(unavailable("idempotency key"))?; runtime .publish_verified_manifest(&manifest, idempotency, cancelled) - .map_err(|_| GitTopologySyncFailure::Unavailable)?; + .map_err(unavailable("manifest publication"))?; Ok(()) }