Separate Ci and Runs, extract CI_FNL constant
Ci only holds repo_path for eval/validation. Runs owns runs_base
for the run lifecycle. Ci::runs() takes runs_base as an argument.
The .quire/ci.fnl path is now CI_FNL so it is not repeated.

Assisted-by: GLM-5.1 via pi
change lpvuklzznnolvmlzrpuvkwusyrrpyyts
commit 982a4680b336a1b3b809dbdda7c605bab7ad2418
author Alpha Chen <alpha@kejadlen.dev>
date
parent plryxxxt
diff --git a/src/ci/mod.rs b/src/ci/mod.rs
index f35e580..59255d4 100644
--- a/src/ci/mod.rs
+++ b/src/ci/mod.rs
@@ -4,39 +4,41 @@ pub mod graph;
 pub mod run;
 
 pub use graph::{EvalResult, JobDef, ValidationError, eval_ci, validate};
-pub use run::{Run, RunMeta, RunState, RunTimes};
+pub use run::{Run, RunMeta, RunState, RunTimes, Runs};
 
 use std::path::{Path, PathBuf};
 
 use crate::Result;
 use crate::event::{PushEvent, PushRef};
-use crate::fennel::Fennel;
 use crate::quire::Repo;
 
+/// Path to the CI config within a bare repo, relative to the repo root.
+pub const CI_FNL: &str = ".quire/ci.fnl";
+
 /// Access to CI operations for a single repo.
 ///
-/// Owns the base path for runs (`runs/<repo>/`) and provides eval,
-/// validation, and run lifecycle methods. Obtain one via `Repo::ci()`.
+/// Provides eval and validation methods scoped to a bare repo.
+/// Obtain one via `Repo::ci()`. Run lifecycle is on `Runs`, obtainable
+/// via `Repo::runs()`.
 pub struct Ci {
     repo_path: PathBuf,
-    runs_base: PathBuf,
 }
 
 impl Ci {
-    pub(crate) fn new(repo_path: PathBuf, runs_base: PathBuf) -> Self {
-        Self {
-            repo_path,
-            runs_base,
-        }
+    pub(crate) fn new(repo_path: PathBuf) -> Self {
+        Self { repo_path }
     }
 
-    // --- Eval & validation ---
+    /// Access CI runs for this repo.
+    pub fn runs(&self, runs_base: PathBuf) -> Runs {
+        Runs::new(runs_base)
+    }
 
     /// Evaluate ci.fnl at a given SHA and return the registration table.
     pub fn eval(&self, sha: &str) -> Result<EvalResult> {
         let source = self.ci_fnl_source(sha)?;
-        let fennel = Fennel::new()?;
-        let name = format!("{sha}:.quire/ci.fnl");
+        let fennel = crate::fennel::Fennel::new()?;
+        let name = format!("{sha}:{CI_FNL}");
         let result = eval_ci(&fennel, &source, &name)?;
         Ok(result)
     }
@@ -52,7 +54,7 @@ impl Ci {
     pub fn validate_file(path: &Path) -> Result<EvalResult> {
         let source = fs_err::read_to_string(path)?;
         let name = path.display().to_string();
-        let fennel = Fennel::new()?;
+        let fennel = crate::fennel::Fennel::new()?;
         let result = eval_ci(&fennel, &source, &name)?;
         validate(&result.jobs)?;
         Ok(result)
@@ -60,7 +62,7 @@ impl Ci {
 
     /// Check whether this bare repo has `.quire/ci.fnl` at a given commit SHA.
     fn has_ci_fnl(&self, sha: &str) -> bool {
-        self.git(&["show", &format!("{sha}:.quire/ci.fnl")])
+        self.git(&["show", &format!("{sha}:{CI_FNL}")])
             .stdout(std::process::Stdio::null())
             .stderr(std::process::Stdio::null())
             .status()
@@ -71,7 +73,7 @@ impl Ci {
     /// Read the contents of `.quire/ci.fnl` at a given commit SHA.
     fn ci_fnl_source(&self, sha: &str) -> Result<String> {
         let output = self
-            .git(&["show", &format!("{sha}:.quire/ci.fnl")])
+            .git(&["show", &format!("{sha}:{CI_FNL}")])
             .stdout(std::process::Stdio::piped())
             .stderr(std::process::Stdio::piped())
             .output()?;
@@ -79,7 +81,7 @@ impl Ci {
         if !output.status.success() {
             let stderr = String::from_utf8_lossy(&output.stderr);
             return Err(crate::Error::Git(format!(
-                "failed to read ci.fnl at {sha}: {stderr}"
+                "failed to read {CI_FNL} at {sha}: {stderr}"
             )));
         }
 
@@ -92,126 +94,6 @@ impl Ci {
         cmd.args(args).current_dir(&self.repo_path);
         cmd
     }
-
-    // --- Run lifecycle ---
-
-    /// Create a new run record in the `pending` state.
-    ///
-    /// Writes `meta.yml` and `times.yml` atomically (temp dir + rename).
-    pub fn create_run(&self, meta: &RunMeta) -> Result<Run> {
-        let pending_dir = self.runs_base.join(RunState::Pending.dir_name());
-        let id = uuid::Uuid::now_v7().to_string();
-
-        fs_err::create_dir_all(&pending_dir)?;
-
-        let tmp_dir = pending_dir.join(format!(".tmp-{id}"));
-        fs_err::create_dir_all(&tmp_dir)?;
-
-        run::write_yaml(&tmp_dir.join("meta.yml"), meta)?;
-        run::write_yaml(&tmp_dir.join("times.yml"), &RunTimes::default())?;
-
-        let final_dir = pending_dir.join(&id);
-        fs_err::rename(&tmp_dir, &final_dir)?;
-
-        Run::open(self.runs_base.clone(), RunState::Pending, id)
-    }
-
-    /// Scan for orphaned runs in `pending/` and `active/` directories.
-    pub fn scan_orphans(&self) -> Result<Vec<Run>> {
-        let mut orphans = Vec::new();
-
-        for &state in &[RunState::Pending, RunState::Active] {
-            let state_path = self.runs_base.join(state.dir_name());
-            let entries = match fs_err::read_dir(&state_path) {
-                Ok(entries) => entries,
-                Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
-                Err(e) => return Err(e.into()),
-            };
-
-            for entry in entries {
-                let entry = entry?;
-                let name = match entry.file_name().to_str() {
-                    Some(n) => n.to_string(),
-                    None => continue,
-                };
-
-                if name.starts_with('.') {
-                    continue;
-                }
-
-                match Run::open(self.runs_base.clone(), state, name.clone()) {
-                    Ok(run) => orphans.push(run),
-                    Err(e) => {
-                        tracing::warn!(
-                            state = ?state,
-                            run_id = %name,
-                            %e,
-                            "quarantining unreadable run to failed/"
-                        );
-                        self.quarantine(&state_path.join(&name), &name)?;
-                    }
-                }
-            }
-        }
-
-        Ok(orphans)
-    }
-
-    /// Move a broken run directory into `failed/`.
-    fn quarantine(&self, src: &Path, id: &str) -> Result<()> {
-        let failed_dir = self.runs_base.join(RunState::Failed.dir_name());
-        fs_err::create_dir_all(&failed_dir)?;
-        fs_err::rename(src, failed_dir.join(id))?;
-        Ok(())
-    }
-
-    /// Reconcile orphaned runs from a previous server instance.
-    pub fn reconcile_orphans(&self) -> Result<()> {
-        let orphans = self.scan_orphans()?;
-        for orphan in &orphans {
-            tracing::warn!(
-                run_id = %orphan.id(),
-                state = ?orphan.state(),
-                "found orphaned run"
-            );
-        }
-
-        for mut orphan in orphans {
-            match orphan.state() {
-                RunState::Pending => {
-                    tracing::warn!(
-                        run_id = %orphan.id(),
-                        "completing orphaned pending run"
-                    );
-                    if let Err(e) = orphan.transition(RunState::Complete) {
-                        tracing::error!(
-                            run_id = %orphan.id(),
-                            %e,
-                            "failed to transition orphaned pending run"
-                        );
-                    }
-                }
-                RunState::Active => {
-                    tracing::warn!(
-                        run_id = %orphan.id(),
-                        "marking orphaned active run as failed"
-                    );
-                    if let Err(e) = orphan.transition(RunState::Failed) {
-                        tracing::error!(
-                            run_id = %orphan.id(),
-                            %e,
-                            "failed to transition orphaned active run to failed"
-                        );
-                    }
-                }
-                RunState::Complete | RunState::Failed => {
-                    unreachable!("scan_orphans only returns pending/active")
-                }
-            }
-        }
-
-        Ok(())
-    }
 }
 
 /// Trigger CI for a push event: scan each updated ref for `.quire/ci.fnl`,
@@ -255,7 +137,7 @@ fn trigger_ref(repo: &Repo, pushed_at: jiff::Timestamp, push_ref: &PushRef) -> R
         pushed_at,
     };
 
-    let mut run = ci.create_run(&meta)?;
+    let mut run = ci.runs(repo.runs_base()).create(&meta)?;
 
     tracing::info!(
         run_id = %run.id(),
diff --git a/src/ci/run.rs b/src/ci/run.rs
index 4fe7a37..4bca1bb 100644
--- a/src/ci/run.rs
+++ b/src/ci/run.rs
@@ -55,6 +55,153 @@ pub struct RunTimes {
     pub finished_at: Option<Timestamp>,
 }
 
+/// Access to CI runs for a single repo.
+///
+/// Owns the base path (`runs/<repo>/`) and provides run creation
+/// and orphan reconciliation. Obtain one via `Ci::runs()`.
+#[derive(Debug)]
+pub struct Runs {
+    base: PathBuf,
+}
+
+impl Runs {
+    pub fn new(base: PathBuf) -> Self {
+        Self { base }
+    }
+
+    /// Create a new run record in the `pending` state.
+    ///
+    /// Writes `meta.yml` and `times.yml` atomically (temp dir + rename).
+    pub fn create(&self, meta: &RunMeta) -> Result<Run> {
+        let pending_dir = self.base.join(RunState::Pending.dir_name());
+        let id = uuid::Uuid::now_v7().to_string();
+
+        fs_err::create_dir_all(&pending_dir)?;
+
+        let tmp_dir = pending_dir.join(format!(".tmp-{id}"));
+        fs_err::create_dir_all(&tmp_dir)?;
+
+        write_yaml(&tmp_dir.join("meta.yml"), meta)?;
+        write_yaml(&tmp_dir.join("times.yml"), &RunTimes::default())?;
+
+        let final_dir = pending_dir.join(&id);
+        fs_err::rename(&tmp_dir, &final_dir)?;
+
+        Run::open(self.base.clone(), RunState::Pending, id)
+    }
+
+    /// Scan for orphaned runs in `pending/` and `active/` directories.
+    ///
+    /// Entries that cannot be opened (missing/unreadable `meta.yml` or
+    /// `times.yml`) are quarantined to `failed/` so they don't stay
+    /// stuck in pending/active forever.
+    ///
+    /// The caller decides how to reconcile the returned runs:
+    /// - `pending/` entries should be re-enqueued.
+    /// - `active/` entries with no live runner should be marked failed.
+    pub fn scan_orphans(&self) -> Result<Vec<Run>> {
+        let mut orphans = Vec::new();
+
+        for &state in &[RunState::Pending, RunState::Active] {
+            let state_path = self.base.join(state.dir_name());
+            let entries = match fs_err::read_dir(&state_path) {
+                Ok(entries) => entries,
+                Err(e) if e.kind() == std::io::ErrorKind::NotFound => continue,
+                Err(e) => return Err(e.into()),
+            };
+
+            for entry in entries {
+                let entry = entry?;
+                let name = match entry.file_name().to_str() {
+                    Some(n) => n.to_string(),
+                    None => continue,
+                };
+
+                if name.starts_with('.') {
+                    continue;
+                }
+
+                match Run::open(self.base.clone(), state, name.clone()) {
+                    Ok(run) => orphans.push(run),
+                    Err(e) => {
+                        tracing::warn!(
+                            state = ?state,
+                            run_id = %name,
+                            %e,
+                            "quarantining unreadable run to failed/"
+                        );
+                        self.quarantine(&state_path.join(&name), &name)?;
+                    }
+                }
+            }
+        }
+
+        Ok(orphans)
+    }
+
+    /// Move a broken run directory into `failed/` so it stops blocking
+    /// pending/active. The contents may be unreadable; we only care
+    /// about getting it out of the active state buckets.
+    fn quarantine(&self, src: &Path, id: &str) -> Result<()> {
+        let failed_dir = self.base.join(RunState::Failed.dir_name());
+        fs_err::create_dir_all(&failed_dir)?;
+        fs_err::rename(src, failed_dir.join(id))?;
+        Ok(())
+    }
+
+    /// Reconcile orphaned runs from a previous server instance.
+    ///
+    /// - `pending/` orphans are moved to `complete/` (will be re-enqueued when
+    ///   the runner exists; for now, immediately completed).
+    /// - `active/` orphans are moved to `failed/` (no live runner).
+    pub fn reconcile_orphans(&self) -> Result<()> {
+        let orphans = self.scan_orphans()?;
+        for orphan in &orphans {
+            tracing::warn!(
+                run_id = %orphan.id(),
+                state = ?orphan.state(),
+                "found orphaned run"
+            );
+        }
+
+        for mut orphan in orphans {
+            match orphan.state() {
+                RunState::Pending => {
+                    tracing::warn!(
+                        run_id = %orphan.id(),
+                        "completing orphaned pending run"
+                    );
+                    if let Err(e) = orphan.transition(RunState::Complete) {
+                        tracing::error!(
+                            run_id = %orphan.id(),
+                            %e,
+                            "failed to transition orphaned pending run"
+                        );
+                    }
+                }
+                RunState::Active => {
+                    tracing::warn!(
+                        run_id = %orphan.id(),
+                        "marking orphaned active run as failed"
+                    );
+                    if let Err(e) = orphan.transition(RunState::Failed) {
+                        tracing::error!(
+                            run_id = %orphan.id(),
+                            %e,
+                            "failed to transition orphaned active run to failed"
+                        );
+                    }
+                }
+                RunState::Complete | RunState::Failed => {
+                    unreachable!("scan_orphans only returns pending/active")
+                }
+            }
+        }
+
+        Ok(())
+    }
+}
+
 /// A CI run on disk.
 ///
 /// Owns the path to the run directory. Tracks current state so that
@@ -177,7 +324,6 @@ fn read_yaml<T: serde::de::DeserializeOwned>(path: &Path) -> Result<T> {
 mod tests {
     use super::*;
     use crate::Quire;
-    use crate::ci::Ci;
 
     fn tmp_quire() -> (tempfile::TempDir, Quire) {
         let dir = tempfile::tempdir().expect("tempdir");
@@ -185,11 +331,8 @@ mod tests {
         (dir, quire)
     }
 
-    fn test_ci(quire: &Quire) -> Ci {
-        Ci::new(
-            quire.repos_dir().join("test.git"),
-            quire.base_dir().join("runs").join("test.git"),
-        )
+    fn test_runs(quire: &Quire) -> Runs {
+        Runs::new(quire.base_dir().join("runs").join("test.git"))
     }
 
     fn test_meta() -> RunMeta {
@@ -211,8 +354,8 @@ mod tests {
     #[test]
     fn create_generates_uuidv7_id() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let run = runs.create(&test_meta()).expect("create");
         let parsed = uuid::Uuid::parse_str(run.id()).expect("should be valid UUID");
         assert_eq!(parsed.get_version(), Some(uuid::Version::SortRand));
     }
@@ -220,8 +363,8 @@ mod tests {
     #[test]
     fn create_writes_files_in_pending() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let run = runs.create(&test_meta()).expect("create");
 
         let path = run.path();
         assert!(path.exists(), "run directory should exist");
@@ -240,8 +383,8 @@ mod tests {
     #[test]
     fn transition_moves_directory() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let mut run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let mut run = runs.create(&test_meta()).expect("create");
         let id = run.id().to_string();
 
         let old_path = run.path();
@@ -262,8 +405,8 @@ mod tests {
     #[test]
     fn transition_stamps_started_at_on_active() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let mut run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let mut run = runs.create(&test_meta()).expect("create");
 
         run.transition(RunState::Active).expect("to active");
         let times = run.read_times().expect("read state");
@@ -274,9 +417,9 @@ mod tests {
     #[test]
     fn transition_stamps_finished_at_on_complete_and_failed() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
+        let runs = test_runs(&quire);
 
-        let mut completed = ci.create_run(&test_meta()).expect("create");
+        let mut completed = runs.create(&test_meta()).expect("create");
         completed.transition(RunState::Active).expect("to active");
         completed
             .transition(RunState::Complete)
@@ -284,7 +427,7 @@ mod tests {
         let times = completed.read_times().expect("read state");
         assert!(times.finished_at.is_some());
 
-        let mut failed = ci.create_run(&test_meta()).expect("create");
+        let mut failed = runs.create(&test_meta()).expect("create");
         failed.transition(RunState::Active).expect("to active");
         failed.transition(RunState::Failed).expect("to failed");
         let failed_times = failed.read_times().expect("read state");
@@ -294,14 +437,14 @@ mod tests {
     #[test]
     fn transition_rejects_invalid_transitions() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
+        let runs = test_runs(&quire);
 
         // Pending -> Failed is not allowed (must go via Active).
-        let mut run = ci.create_run(&test_meta()).expect("create");
+        let mut run = runs.create(&test_meta()).expect("create");
         assert!(run.transition(RunState::Failed).is_err());
 
         // Terminal -> anything is not allowed.
-        let mut completed = ci.create_run(&test_meta()).expect("create");
+        let mut completed = runs.create(&test_meta()).expect("create");
         completed.transition(RunState::Active).expect("to active");
         completed
             .transition(RunState::Complete)
@@ -313,8 +456,8 @@ mod tests {
     #[test]
     fn transition_preserves_started_at_through_completion() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let mut run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let mut run = runs.create(&test_meta()).expect("create");
 
         run.transition(RunState::Active).expect("to active");
         let active_times = run.read_times().expect("read state");
@@ -328,8 +471,8 @@ mod tests {
     #[test]
     fn transition_full_lifecycle() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let mut run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let mut run = runs.create(&test_meta()).expect("create");
 
         run.transition(RunState::Active).expect("to active");
         run.transition(RunState::Complete).expect("to complete");
@@ -353,10 +496,10 @@ mod tests {
     #[test]
     fn scan_orphans_finds_pending() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let run = runs.create(&test_meta()).expect("create");
 
-        let orphans = ci.scan_orphans().expect("scan");
+        let orphans = runs.scan_orphans().expect("scan");
         assert_eq!(orphans.len(), 1);
         assert_eq!(orphans[0].id(), run.id());
         assert_eq!(orphans[0].state(), RunState::Pending);
@@ -365,11 +508,11 @@ mod tests {
     #[test]
     fn scan_orphans_finds_active() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let mut run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let mut run = runs.create(&test_meta()).expect("create");
         run.transition(RunState::Active).expect("transition");
 
-        let orphans = ci.scan_orphans().expect("scan");
+        let orphans = runs.scan_orphans().expect("scan");
         assert_eq!(orphans.len(), 1);
         assert_eq!(orphans[0].state(), RunState::Active);
     }
@@ -377,28 +520,28 @@ mod tests {
     #[test]
     fn scan_orphans_skips_complete() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let mut run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let mut run = runs.create(&test_meta()).expect("create");
         run.transition(RunState::Complete).expect("transition");
 
-        let orphans = ci.scan_orphans().expect("scan");
+        let orphans = runs.scan_orphans().expect("scan");
         assert!(orphans.is_empty(), "complete runs are not orphans");
     }
 
     #[test]
     fn scan_orphans_quarantines_unreadable_runs() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
+        let base = quire.base_dir().join("runs").join("test.git");
+        let runs = Runs::new(base.clone());
 
         // Create a run, then break it by removing meta.yml.
-        let run = ci.create_run(&test_meta()).expect("create");
+        let run = runs.create(&test_meta()).expect("create");
         let id = run.id().to_string();
         fs_err::remove_file(run.path().join("meta.yml")).expect("remove meta");
 
-        let orphans = ci.scan_orphans().expect("scan");
+        let orphans = runs.scan_orphans().expect("scan");
         assert!(orphans.is_empty(), "broken run should not be returned");
 
-        let base = quire.base_dir().join("runs").join("test.git");
         let pending = base.join(RunState::Pending.dir_name()).join(&id);
         assert!(!pending.exists(), "broken run should leave pending/");
 
@@ -409,15 +552,15 @@ mod tests {
     #[test]
     fn scan_orphans_empty_when_no_runs_dir() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        assert!(ci.scan_orphans().expect("scan").is_empty());
+        let runs = test_runs(&quire);
+        assert!(runs.scan_orphans().expect("scan").is_empty());
     }
 
     #[test]
     fn write_times_updates_in_place() {
         let (_dir, quire) = tmp_quire();
-        let ci = test_ci(&quire);
-        let run = ci.create_run(&test_meta()).expect("create");
+        let runs = test_runs(&quire);
+        let run = runs.create(&test_meta()).expect("create");
 
         let started: Timestamp = "2026-04-28T12:00:01Z".parse().expect("parse");
         run.write_times(&RunTimes {
diff --git a/src/quire.rs b/src/quire.rs
index 2e11cd5..ef87ecf 100644
--- a/src/quire.rs
+++ b/src/quire.rs
@@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};
 
 use miette::{Context, IntoDiagnostic, Result, ensure};
 
-use crate::ci::Ci;
+use crate::ci::{Ci, Runs};
 use crate::fennel::Fennel;
 use crate::secret::SecretString;
 use crate::{Error, Result as AppResult};
@@ -151,7 +151,17 @@ impl Repo {
 
     /// Access CI operations for this repo.
     pub fn ci(&self) -> Ci {
-        Ci::new(self.path(), self.base_dir.join("runs").join(&self.name))
+        Ci::new(self.path())
+    }
+
+    /// The base directory for CI runs (`runs/<repo>/`).
+    pub fn runs_base(&self) -> PathBuf {
+        self.base_dir.join("runs").join(&self.name)
+    }
+
+    /// Access CI runs for this repo.
+    pub fn runs(&self) -> Runs {
+        Runs::new(self.runs_base())
     }
 
     /// Push `main` to the configured mirror, injecting the GitHub token via
diff --git a/src/server.rs b/src/server.rs
index a036e89..d307f0a 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -42,7 +42,7 @@ pub async fn run(quire: &Quire) -> Result<()> {
 
     // Scan for orphaned runs from a previous server instance.
     for repo in quire.repos().context("failed to list repos")? {
-        repo.ci().reconcile_orphans()?;
+        repo.runs().reconcile_orphans()?;
     }
 
     let quire_handle = quire.clone();