Log mirror failures at their origin
trigger returned an aggregate MirrorError that the caller logged in one
shot, so sentry-tracing saw a single exception and the per-target source
chains were unreachable. Logging each failure where it happens emits one
tracing event per target, so each reaches Sentry as its own exception
with cause intact — and a scrub of one no longer takes the rest.

Assisted-by: Claude Opus 4.8 via Claude Code
change uvzkvluwmwvqxkmqstsuvrwwqvtmzmwo
commit 326f590a7b81f68d60e17631539112f93b75d925
author Alpha Chen <alpha@kejadlen.dev>
date
parent stpnunxv
diff --git a/quire-server/src/bin/quire/server.rs b/quire-server/src/bin/quire/server.rs
index 7f4f83c..5dcbdb4 100644
--- a/quire-server/src/bin/quire/server.rs
+++ b/quire-server/src/bin/quire/server.rs
@@ -151,7 +151,5 @@ async fn handle_event_connection(mut stream: tokio::net::UnixStream, quire: Quir
     }
 
     ci::trigger(&quire, &event);
-    if let Err(e) = mirror::trigger(&quire, &event) {
-        tracing::error!(repo = %event.repo, error = &e as &(dyn std::error::Error + 'static), "mirror failed");
-    }
+    mirror::trigger(&quire, &event);
 }
diff --git a/quire-server/src/mirror.rs b/quire-server/src/mirror.rs
index b3e9fb1..51648cc 100644
--- a/quire-server/src/mirror.rs
+++ b/quire-server/src/mirror.rs
@@ -10,19 +10,6 @@ use thiserror::Error;
 
 use crate::quire::{Quire, Repo};
 
-/// A mirror failure: either we couldn't get started, or one or more targets
-/// failed after we did.
-#[derive(Debug, Error)]
-pub enum MirrorError {
-    /// Couldn't resolve the repo from the push event — nothing was attempted.
-    #[error(transparent)]
-    Repo(#[from] crate::Error),
-
-    /// One or more mirror targets failed; the rest may have succeeded.
-    #[error("mirror: {} target(s) failed", errors.len())]
-    Targets { errors: Vec<TargetError> },
-}
-
 /// A failure mirroring one ref, carrying where it happened.
 #[derive(Debug, Error)]
 pub enum TargetError {
@@ -60,27 +47,48 @@ pub enum PushError {
 ///
 /// For each updated ref, reads `.quire/config.fnl` at the new SHA to obtain
 /// the `:mirrors` table. Each target names a global `:secrets` entry holding
-/// its push token. Repos with no mirrors are skipped; a target naming a
-/// missing secret produces an error.
-pub fn trigger(quire: &Quire, event: &PushEvent) -> Result<(), MirrorError> {
-    let repo = quire.repo(&event.repo)?;
+/// its push token. Repos with no mirrors are skipped.
+///
+/// Failures are logged here rather than returned: each failed target is
+/// emitted as its own `tracing` error event, so it reaches Sentry as an
+/// individual exception with its `#[source]` chain intact instead of being
+/// flattened into one aggregate.
+pub fn trigger(quire: &Quire, event: &PushEvent) {
+    let repo = match quire.repo(&event.repo) {
+        Ok(repo) => repo,
+        Err(error) => {
+            tracing::error!(
+                repo = %event.repo,
+                error = &error as &(dyn std::error::Error + 'static),
+                "mirror: resolving repo failed",
+            );
+            return;
+        }
+    };
     let mirror = Mirror {
         repo: &repo,
         secrets: &quire.config.secrets,
     };
 
-    let (pushes, mut errors) = mirror.plan(event);
+    let (pushes, errors) = mirror.plan(event);
+    for error in errors {
+        log_target_failure(&event.repo, &error);
+    }
     for push in pushes {
         if let Err(cause) = mirror.run(&push) {
-            errors.push(TargetError::Push { push, cause });
+            log_target_failure(&event.repo, &TargetError::Push { push, cause });
         }
     }
+}
 
-    if errors.is_empty() {
-        Ok(())
-    } else {
-        Err(MirrorError::Targets { errors })
-    }
+/// Emit one mirror target failure as a `tracing` error so sentry-tracing
+/// captures it as an individual exception, source chain and all.
+fn log_target_failure(repo: impl std::fmt::Display, error: &TargetError) {
+    tracing::error!(
+        %repo,
+        error = error as &(dyn std::error::Error + 'static),
+        "mirror: target failed",
+    );
 }
 
 /// One mirror push to perform: a ref pushed to a remote, authenticated with