Route both pipeline compile callers through Ci::compile
Ci::pipeline and trigger_ref each called source() then
pipeline::compile(&source, CI_FNL) directly, so the two paths could
drift on validation invariants. Add Ci::compile as the single chokepoint
and have both callers go through it.

Assisted-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
change qzzvvllzlooprzkysymysomtxotnttvz
commit 0cde0faee1cba1732b2aa2328968be41d625c393
author Alpha Chen <alpha@kejadlen.dev>
date
parent yvvtyotx
diff --git a/src/ci/mod.rs b/src/ci/mod.rs
index 9fe2d73..f96b851 100644
--- a/src/ci/mod.rs
+++ b/src/ci/mod.rs
@@ -68,9 +68,15 @@ impl Ci {
         let Some(source) = self.source(&commit.sha)? else {
             return Ok(None);
         };
-        let name = CI_FNL.to_string();
-        let pipeline = pipeline::compile(&source, &name)?;
-        Ok(Some(pipeline))
+        Ok(Some(self.compile(&source)?))
+    }
+
+    /// Compile `.quire/ci.fnl` source into a validated [`Pipeline`].
+    ///
+    /// Single chokepoint for compile + structural validation, used by
+    /// [`Ci::pipeline`] and `trigger_ref` so the two paths can't drift.
+    fn compile(&self, source: &str) -> error::Result<Pipeline> {
+        pipeline::compile(source, CI_FNL)
     }
 
     /// Read the contents of `.quire/ci.fnl` at a given commit SHA.
@@ -168,7 +174,7 @@ fn trigger_ref(
         "created CI run"
     );
 
-    let pipeline = match pipeline::compile(&source, CI_FNL) {
+    let pipeline = match ci.compile(&source) {
         Ok(p) => p,
         Err(e) => {
             run.transition(RunState::Active)?;