Remove ci.mirror shim
The (ci.mirror …) registration helper and its entire Rust
implementation (mirror.rs, InvalidMirrorCall error variant, 11 tests)
are gone. Pipelines now register their own quire/mirror job directly
using stdlib.mirror, as the .quire/ci.fnl migration in the prior
commit demonstrates.

Assisted-by: GLM-5.1 via pi
change xyyxupnzxuopypwymnlmpmurotonsvsp
commit 1c35d09f1addafcd40e08343c59541cff9254e39
author Alpha Chen <alpha@kejadlen.dev>
date
parent xlwlrwuw
diff --git a/quire-core/src/ci/mirror.rs b/quire-core/src/ci/mirror.rs
deleted file mode 100644
index 43dfa04..0000000
--- a/quire-core/src/ci/mirror.rs
+++ /dev/null
@@ -1,603 +0,0 @@
-//! `(ci.mirror url opts)`: registers the singleton `quire/mirror`
-//! job, whose Lua run-fn delegates to `(require :quire.stdlib).mirror`
-//! for the tag-and-push at execute time.
-//!
-//! This shim handles the `ci.mirror`-specific concerns: resolving
-//! the `:secret` name into an auth header, invoking the `:tag`
-//! callback, and gating on `:refs`. The actual git plumbing lives
-//! in `stdlib.fnl`.
-
-use std::rc::Rc;
-
-use mlua::{Lua, LuaSerdeExt};
-
-use super::pipeline::{self, DefinitionError, Job, RunFn};
-use super::registration::Registration;
-
-/// Body of `(ci.mirror url opts)`. Validates opts and registers an
-/// internal job at `quire/mirror` whose Lua run-fn delegates to
-/// `stdlib.mirror` at execute time. Singleton-ness is enforced by
-/// generic id uniqueness in `Registration::add_job`.
-pub fn register(lua: &Lua, (url, opts): (String, mlua::Table)) -> mlua::Result<()> {
-    let r = lua
-        .app_data_ref::<Registration>()
-        .ok_or_else(|| mlua::Error::external("quire.ci registration not installed on Lua VM"))?;
-    let line = lua
-        .inspect_stack(1, |d| d.current_line())
-        .flatten()
-        .map(|l| l as u32)
-        .unwrap_or(0);
-    let span = || pipeline::span_for_line(&r.source, line);
-    let invalid = |msg: String, s: _| DefinitionError::InvalidMirrorCall {
-        message: msg,
-        span: s,
-    };
-
-    // :tag — required function.
-    let tag: mlua::Function = match opts.get::<mlua::Value>("tag")? {
-        mlua::Value::Function(f) => f,
-        mlua::Value::Nil => {
-            r.errors.borrow_mut().push(invalid(
-                ":tag is required (a function returning the tag name)".into(),
-                span(),
-            ));
-            return Ok(());
-        }
-        other => {
-            r.errors.borrow_mut().push(invalid(
-                format!(":tag must be a function, got {}", other.type_name()),
-                span(),
-            ));
-            return Ok(());
-        }
-    };
-
-    // :secret — required string.
-    let secret: String = match opts.get::<mlua::Value>("secret")? {
-        mlua::Value::String(s) => s.to_str()?.to_string(),
-        mlua::Value::Nil => {
-            r.errors
-                .borrow_mut()
-                .push(invalid("missing field `secret`".into(), span()));
-            return Ok(());
-        }
-        other => {
-            r.errors.borrow_mut().push(invalid(
-                format!(":secret must be a string, got {}", other.type_name()),
-                span(),
-            ));
-            return Ok(());
-        }
-    };
-
-    // :refs, :after — optional string lists.
-    let refs: Vec<String> = opts.get::<Option<Vec<String>>>("refs")?.unwrap_or_default();
-    let after: Vec<String> = opts
-        .get::<Option<Vec<String>>>("after")?
-        .unwrap_or_default();
-
-    // Reject unknown keys.
-    for pair in opts.pairs::<String, mlua::Value>() {
-        let (k, _) = pair?;
-        if !matches!(k.as_str(), "tag" | "secret" | "refs" | "after") {
-            r.errors
-                .borrow_mut()
-                .push(invalid(format!("unknown field `{k}`"), span()));
-            return Ok(());
-        }
-    }
-
-    // Build the Lua run-fn. Closes over registration-time values;
-    // at execute time the ambient runtime provides push data and
-    // secret resolution.
-    let url = Rc::new(url);
-    let secret = Rc::new(secret);
-    let refs = Rc::new(refs);
-
-    let run_fn = lua.create_function(move |lua, ()| {
-        let loaded: mlua::Table = lua
-            .globals()
-            .get::<mlua::Table>("package")?
-            .get::<mlua::Table>("loaded")?;
-        let mirror: mlua::Function = loaded.get::<mlua::Table>("quire.stdlib")?.get("mirror")?;
-
-        let runtime: mlua::Table = loaded.get::<mlua::Table>("quire.ci")?.get("runtime")?;
-        let push: mlua::Table = runtime.get::<mlua::Function>("jobs")?.call("quire/push")?;
-        let pushed_ref: String = push.get("ref")?;
-
-        // Gate: skip if :refs is set and trigger ref doesn't match.
-        if !refs.is_empty() && !refs.iter().any(|r| r == &pushed_ref) {
-            return Ok(mlua::Value::Nil);
-        }
-
-        let tag_name: String = tag.call(push.clone())?;
-        let auth_header: String = runtime
-            .get::<mlua::Function>("secret")?
-            .call(secret.as_str())?;
-
-        let push_refs = if refs.is_empty() {
-            vec![pushed_ref]
-        } else {
-            refs.to_vec()
-        };
-
-        let mopts = lua.create_table()?;
-        mopts.set("url", url.as_str())?;
-        mopts.set("auth-header", auth_header)?;
-        mopts.set("sha", push.get::<String>("sha")?)?;
-        mopts.set("tag", tag_name)?;
-        mopts.set("git-dir", push.get::<String>("git-dir")?)?;
-        mopts.set("refs", lua.to_value(&push_refs)?)?;
-
-        mirror.call(mopts)
-    })?;
-
-    let mut inputs = vec!["quire/push".to_string()];
-    inputs.extend(after);
-    match Job::new(
-        "quire/mirror".into(),
-        inputs,
-        RunFn::Lua(run_fn),
-        line,
-        &r.source,
-    ) {
-        Ok(job) => r.add_job(job, line),
-        Err(e) => r.errors.borrow_mut().push(e),
-    }
-    Ok(())
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    use std::collections::HashMap;
-
-    use crate::ci::pipeline::{Diagnostic, compile};
-    use crate::ci::run::RunMeta;
-    use crate::ci::runtime::RuntimeHandle;
-    use crate::secret::SecretString;
-
-    /// Set up a bare git repo with one commit. Returns the tempdir,
-    /// the bare repo path, and the head SHA.
-    fn bare_repo() -> (tempfile::TempDir, std::path::PathBuf, String) {
-        let dir = tempfile::tempdir().expect("tempdir");
-        let work = dir.path().join("work");
-        let bare = dir.path().join("repo.git");
-
-        fs_err::create_dir_all(&work).expect("mkdir work");
-        let env_vars: [(&str, &str); 6] = [
-            ("GIT_AUTHOR_NAME", "test"),
-            ("GIT_AUTHOR_EMAIL", "test@test"),
-            ("GIT_COMMITTER_NAME", "test"),
-            ("GIT_COMMITTER_EMAIL", "test@test"),
-            ("GIT_CONFIG_GLOBAL", "/dev/null"),
-            ("GIT_CONFIG_SYSTEM", "/dev/null"),
-        ];
-        let output = std::process::Command::new("git")
-            .args(["init", "-b", "main"])
-            .current_dir(&work)
-            .envs(env_vars)
-            .output()
-            .expect("git init");
-        assert!(output.status.success());
-
-        let output = std::process::Command::new("git")
-            .args(["commit", "--allow-empty", "-m", "initial"])
-            .current_dir(&work)
-            .envs(env_vars)
-            .output()
-            .expect("git commit");
-        assert!(output.status.success());
-
-        let output = std::process::Command::new("git")
-            .args([
-                "clone",
-                "--bare",
-                work.to_str().unwrap(),
-                bare.to_str().unwrap(),
-            ])
-            .current_dir(dir.path())
-            .output()
-            .expect("git clone --bare");
-        assert!(output.status.success());
-
-        let sha_output = std::process::Command::new("git")
-            .args(["rev-parse", "HEAD"])
-            .current_dir(&bare)
-            .output()
-            .expect("git rev-parse");
-        let sha = String::from_utf8(sha_output.stdout)
-            .expect("utf8")
-            .trim()
-            .to_string();
-
-        (dir, bare, sha)
-    }
-
-    /// Build a source bare repo with one commit and an empty target
-    /// bare repo. Returns (tempdir, source bare, target bare, sha).
-    fn bare_repo_with_target() -> (
-        tempfile::TempDir,
-        std::path::PathBuf,
-        std::path::PathBuf,
-        String,
-    ) {
-        let dir = tempfile::tempdir().expect("tempdir");
-        let work = dir.path().join("work");
-        let bare = dir.path().join("repo.git");
-        let target = dir.path().join("target.git");
-
-        fs_err::create_dir_all(&work).expect("mkdir work");
-        let env_vars: [(&str, &str); 6] = [
-            ("GIT_AUTHOR_NAME", "test"),
-            ("GIT_AUTHOR_EMAIL", "test@test"),
-            ("GIT_COMMITTER_NAME", "test"),
-            ("GIT_COMMITTER_EMAIL", "test@test"),
-            ("GIT_CONFIG_GLOBAL", "/dev/null"),
-            ("GIT_CONFIG_SYSTEM", "/dev/null"),
-        ];
-        let git = |args: &[&str], cwd: &std::path::Path| {
-            let out = std::process::Command::new("git")
-                .args(args)
-                .current_dir(cwd)
-                .envs(env_vars)
-                .output()
-                .expect("git");
-            assert!(out.status.success(), "git {:?} failed", args);
-            out
-        };
-        git(&["init", "-b", "main"], &work);
-        git(&["commit", "--allow-empty", "-m", "initial"], &work);
-        let sha = String::from_utf8(git(&["rev-parse", "HEAD"], &work).stdout)
-            .expect("utf8")
-            .trim()
-            .to_string();
-        git(
-            &[
-                "clone",
-                "--bare",
-                work.to_str().unwrap(),
-                bare.to_str().unwrap(),
-            ],
-            dir.path(),
-        );
-        git(&["init", "--bare", target.to_str().unwrap()], dir.path());
-
-        (dir, bare, target, sha)
-    }
-
-    /// Pull the mirror job's inputs from a compiled pipeline.
-    fn mirror_job_inputs(source: &str) -> Vec<String> {
-        let pipeline = compile(source, "ci.fnl").expect("compile should succeed");
-        pipeline
-            .jobs()
-            .iter()
-            .find(|j| j.id == "quire/mirror")
-            .expect("mirror job should be registered")
-            .inputs
-            .clone()
-    }
-
-    #[test]
-    fn mirror_registers_quire_mirror_job_with_push_input() {
-        let inputs = mirror_job_inputs(
-            r#"(local ci (require :quire.ci))
-(ci.mirror "https://github.com/example/repo.git"
-  {:secret :github_token :tag (fn [_] "v1")})"#,
-        );
-        assert_eq!(inputs, vec!["quire/push".to_string()]);
-    }
-
-    #[test]
-    fn mirror_after_appends_to_inputs() {
-        let source = r#"(local ci (require :quire.ci))
-(ci.job :build [:quire/push] (fn [] nil))
-(ci.mirror "https://github.com/example/repo.git"
-  {:secret :github_token :tag (fn [_] "v1") :after [:build]})"#;
-        let inputs = mirror_job_inputs(source);
-        assert_eq!(inputs, vec!["quire/push".to_string(), "build".to_string()]);
-    }
-
-    #[test]
-    fn mirror_duplicate_call_errors_via_id_collision() {
-        let source = r#"(local ci (require :quire.ci))
-(ci.mirror "https://github.com/example/repo.git" {:secret :a :tag (fn [_] "v1")})
-(ci.mirror "https://github.com/example/other.git" {:secret :b :tag (fn [_] "v1")})"#;
-        let Err(err) = compile(source, "ci.fnl") else {
-            panic!("expected error");
-        };
-        let crate::ci::pipeline::CompileError::Pipeline(pe) = err else {
-            panic!("expected PipelineError, got {err:?}");
-        };
-        assert!(
-            pe.diagnostics.iter().any(|d| matches!(
-                d,
-                Diagnostic::Definition(DefinitionError::DuplicateJob { job_id, .. })
-                    if job_id == "quire/mirror"
-            )),
-            "expected DuplicateJob('quire/mirror') in: {:?}",
-            pe.diagnostics
-        );
-    }
-
-    /// Compile, expect a single `InvalidMirrorCall` diagnostic, return its message.
-    fn invalid_mirror_message(source: &str) -> String {
-        let Err(err) = compile(source, "ci.fnl") else {
-            panic!("expected error");
-        };
-        let crate::ci::pipeline::CompileError::Pipeline(pe) = err else {
-            panic!("expected PipelineError, got {err:?}");
-        };
-        pe.diagnostics
-            .iter()
-            .find_map(|d| match d {
-                Diagnostic::Definition(DefinitionError::InvalidMirrorCall { message, .. }) => {
-                    Some(message.clone())
-                }
-                _ => None,
-            })
-            .unwrap_or_else(|| panic!("expected InvalidMirrorCall in: {:?}", pe.diagnostics))
-    }
-
-    #[test]
-    fn mirror_unknown_opt_key_errors() {
-        let msg = invalid_mirror_message(
-            r#"(local ci (require :quire.ci))
-(ci.mirror "https://github.com/example/repo.git"
-  {:secret :a :tag (fn [_] "v1") :tagPrefix "v"})"#,
-        );
-        assert!(
-            msg.contains("tagPrefix") && msg.contains("unknown field"),
-            "expected unknown-field error mentioning the typo, got: {msg}"
-        );
-    }
-
-    #[test]
-    fn mirror_requires_secret() {
-        let msg = invalid_mirror_message(
-            r#"(local ci (require :quire.ci))
-(ci.mirror "https://github.com/example/repo.git" {:tag (fn [_] "v1")})"#,
-        );
-        assert!(
-            msg.contains("missing field") && msg.contains("secret"),
-            "expected missing-secret error, got: {msg}"
-        );
-    }
-
-    #[test]
-    fn mirror_requires_tag() {
-        let msg = invalid_mirror_message(
-            r#"(local ci (require :quire.ci))
-(ci.mirror "https://github.com/example/repo.git" {:secret :a})"#,
-        );
-        assert!(
-            msg.contains(":tag is required"),
-            "expected missing-tag error, got: {msg}"
-        );
-    }
-
-    #[test]
-    fn mirror_tag_must_be_function() {
-        let msg = invalid_mirror_message(
-            r#"(local ci (require :quire.ci))
-(ci.mirror "https://github.com/example/repo.git" {:secret :a :tag "v1"})"#,
-        );
-        assert!(
-            msg.contains("must be a function"),
-            "expected tag-shape error, got: {msg}"
-        );
-    }
-
-    /// Compile a mirror source and return the runtime and the mirror
-    /// job's Lua run-fn ready to be invoked with the ambient runtime
-    /// installed.
-    fn mirror_run_fn(
-        source: &str,
-        secrets: HashMap<String, SecretString>,
-        meta: &RunMeta,
-        git_dir: &std::path::Path,
-    ) -> (
-        Rc<crate::ci::runtime::Runtime>,
-        mlua::Function,
-        RuntimeHandle,
-    ) {
-        use crate::ci::runtime::Runtime;
-        let pipeline = compile(source, "ci.fnl").expect("compile should succeed");
-        let run_fn = match pipeline
-            .jobs()
-            .iter()
-            .find(|j| j.id == "quire/mirror")
-            .expect("mirror job should exist")
-            .run_fn
-            .clone()
-        {
-            RunFn::Lua(f) => f,
-            RunFn::Rust(_) => panic!("mirror should register a RunFn::Lua"),
-        };
-        let log_dir = tempfile::tempdir().expect("tempdir for mirror logs").keep();
-        let runtime = Rc::new(Runtime::new(
-            pipeline,
-            secrets,
-            meta,
-            git_dir,
-            std::env::current_dir().expect("cwd"),
-            log_dir,
-        ));
-        let guard =
-            RuntimeHandle::install(runtime.clone(), runtime.lua()).expect("install runtime");
-        (runtime, run_fn, guard)
-    }
-
-    #[test]
-    fn mirror_executes_tag_callback_and_pushes() {
-        let (_dir, bare, target, sha) = bare_repo_with_target();
-        let pushed_at: jiff::Timestamp = "2026-05-01T12:00:00Z".parse().unwrap();
-        let meta = RunMeta {
-            sha: sha.clone(),
-            r#ref: "refs/heads/main".to_string(),
-            pushed_at,
-        };
-
-        let mut secrets = HashMap::new();
-        secrets.insert(
-            "github_token".to_string(),
-            SecretString::from("Authorization: Bearer test-token"),
-        );
-
-        let source = format!(
-            r#"(local ci (require :quire.ci))
-(ci.mirror "{url}"
-  {{:secret :github_token
-   :tag (fn [push] (.. "release-" (string.sub push.sha 1 8)))}})"#,
-            url = format!("file://{}", target.display()),
-        );
-        let (runtime, run_fn, _guard) = mirror_run_fn(&source, secrets, &meta, &bare);
-
-        runtime.enter_job("quire/mirror");
-        let _: mlua::Value = run_fn.call(()).expect("mirror should succeed");
-        runtime.leave_job();
-
-        // Tag landed in the target repo via the push.
-        let expected_tag = format!("release-{}", &sha[..8]);
-        let tag_output = std::process::Command::new("git")
-            .args(["tag", "-l"])
-            .current_dir(&target)
-            .output()
-            .expect("git tag -l");
-        let tags = String::from_utf8(tag_output.stdout).expect("utf8");
-        assert!(
-            tags.contains(&expected_tag),
-            "tag should exist in target repo: {tags}"
-        );
-
-        // Tag and push outputs were recorded.
-        let outputs = runtime.take_outputs();
-        let recorded = outputs
-            .get("quire/mirror")
-            .expect("mirror outputs recorded");
-        assert_eq!(recorded.len(), 2, "expected tag + push outputs");
-    }
-
-    #[test]
-    fn mirror_pushes_listed_refs_when_trigger_ref_matches() {
-        let (_dir, bare, target, sha) = bare_repo_with_target();
-        // Create a release branch so git can push it.
-        std::process::Command::new("git")
-            .args(["branch", "release"])
-            .current_dir(&bare)
-            .output()
-            .expect("git branch release");
-
-        let pushed_at: jiff::Timestamp = "2026-05-01T12:00:00Z".parse().unwrap();
-        let meta = RunMeta {
-            sha,
-            r#ref: "refs/heads/main".to_string(),
-            pushed_at,
-        };
-
-        let mut secrets = HashMap::new();
-        secrets.insert(
-            "github_token".to_string(),
-            SecretString::from("Authorization: Bearer test-token"),
-        );
-
-        // :refs is set and the trigger ref matches, so the mirror
-        // should push the listed refs verbatim.
-        let source = format!(
-            r#"(local ci (require :quire.ci))
-(ci.mirror "{url}"
-  {{:secret :github_token
-   :tag (fn [_] "v1")
-   :refs ["refs/heads/main" "refs/heads/release"]}})"#,
-            url = format!("file://{}", target.display()),
-        );
-        let (runtime, run_fn, _guard) = mirror_run_fn(&source, secrets, &meta, &bare);
-
-        runtime.enter_job("quire/mirror");
-        let _: mlua::Value = run_fn.call(()).expect("mirror should succeed");
-        runtime.leave_job();
-
-        let outputs = runtime.take_outputs();
-        let recorded = outputs.get("quire/mirror").expect("recorded");
-        // Tag step records first; push step second.
-        let push = recorded.last().expect("push output");
-        let cmd = &push.cmd;
-        assert!(
-            cmd.contains("refs/heads/main") && cmd.contains("refs/heads/release"),
-            "push cmd should list configured refs, got: {cmd}"
-        );
-    }
-
-    #[test]
-    fn mirror_skips_when_trigger_ref_not_in_refs() {
-        let (_dir, bare, _sha) = bare_repo();
-        let pushed_at: jiff::Timestamp = "2026-05-01T12:00:00Z".parse().unwrap();
-        let meta = RunMeta {
-            sha: "abc123".to_string(),
-            r#ref: "refs/heads/feature".to_string(),
-            pushed_at,
-        };
-
-        let mut secrets = HashMap::new();
-        secrets.insert(
-            "github_token".to_string(),
-            SecretString::from("Authorization: Bearer test-token"),
-        );
-
-        // Trigger ref is feature, but :refs only lists main — mirror
-        // should be a no-op.
-        let source = r#"(local ci (require :quire.ci))
-(ci.mirror "https://github.com/example/repo.git"
-  {:secret :github_token
-   :tag (fn [_] "v1")
-   :refs ["refs/heads/main"]})"#;
-        let (runtime, run_fn, _guard) = mirror_run_fn(source, secrets, &meta, &bare);
-
-        runtime.enter_job("quire/mirror");
-        let _: mlua::Value = run_fn.call(()).expect("mirror should succeed (no-op)");
-        runtime.leave_job();
-
-        let outputs = runtime.take_outputs();
-        assert_eq!(
-            outputs.get("quire/mirror").map(|v| v.len()).unwrap_or(0),
-            0,
-            "no outputs should be recorded for a skipped mirror"
-        );
-
-        // No tag should have been created.
-        let tag_output = std::process::Command::new("git")
-            .args(["tag", "-l"])
-            .current_dir(&bare)
-            .output()
-            .expect("git tag -l");
-        let tags = String::from_utf8(tag_output.stdout).expect("utf8");
-        assert!(tags.trim().is_empty(), "no tags should exist: {tags}");
-    }
-
-    #[test]
-    fn mirror_errors_for_unknown_secret_at_runtime() {
-        let (_dir, bare, sha) = bare_repo();
-        let pushed_at: jiff::Timestamp = "2026-05-01T12:00:00Z".parse().unwrap();
-        let meta = RunMeta {
-            sha,
-            r#ref: "refs/heads/main".to_string(),
-            pushed_at,
-        };
-
-        let source = r#"(local ci (require :quire.ci))
-(ci.mirror "https://github.com/example/repo.git"
-  {:secret :missing :tag (fn [_] "v1")})"#;
-        let (runtime, run_fn, _guard) = mirror_run_fn(source, HashMap::new(), &meta, &bare);
-
-        runtime.enter_job("quire/mirror");
-        let err = run_fn.call::<mlua::Value>(()).unwrap_err();
-        runtime.leave_job();
-
-        let msg = err.to_string();
-        assert!(
-            msg.contains("missing"),
-            "expected UnknownSecret(\"missing\"), got: {msg}"
-        );
-    }
-}
diff --git a/quire-core/src/ci/mod.rs b/quire-core/src/ci/mod.rs
index 34c4aff..7b454cf 100644
--- a/quire-core/src/ci/mod.rs
+++ b/quire-core/src/ci/mod.rs
@@ -8,7 +8,6 @@
 pub mod dispatch;
 pub mod event;
 pub mod logs;
-pub mod mirror;
 pub mod pipeline;
 pub mod registration;
 pub mod run;
diff --git a/quire-core/src/ci/pipeline.rs b/quire-core/src/ci/pipeline.rs
index 140fb12..67fc196 100644
--- a/quire-core/src/ci/pipeline.rs
+++ b/quire-core/src/ci/pipeline.rs
@@ -47,13 +47,6 @@ pub enum DefinitionError {
         span: SourceSpan,
     },
 
-    #[error("ci.mirror: {message}")]
-    InvalidMirrorCall {
-        message: String,
-        #[label("here")]
-        span: SourceSpan,
-    },
-
     #[error(
         "Job '{job_id}' run-fn takes an argument — run-fns must be zero-arg `(fn [] …)`; bind the runtime via `(local runtime (require :quire.runtime))` or destructure it from `(require :quire.ci)` instead of destructuring a handle."
     )]
@@ -128,8 +121,8 @@ pub type RustRunFn =
 /// `Lua` is the user case: a Fennel function the executor calls
 /// through the Lua VM, passing the runtime handle table. `Rust` is
 /// the built-in case: a closure that receives the runtime directly,
-/// used by helpers (e.g. `(ci.mirror …)`) that do their work in
-/// plain Rust without round-tripping through Lua.
+/// used by helpers that do their work in plain Rust without
+/// round-tripping through Lua.
 ///
 /// Both variants are `Clone` so the executor can take an owned copy
 /// before invoking — `mlua::Function` is cheap to clone (a registry
@@ -137,7 +130,7 @@ pub type RustRunFn =
 #[derive(Clone)]
 pub enum RunFn {
     Lua(mlua::Function),
-    #[allow(dead_code)] // Wired up by `(ci.mirror …)` and friends.
+    #[allow(dead_code)] // Wired up by built-in helpers.
     Rust(RustRunFn),
 }
 
diff --git a/quire-core/src/ci/registration.rs b/quire-core/src/ci/registration.rs
index ee84ead..2086cf8 100644
--- a/quire-core/src/ci/registration.rs
+++ b/quire-core/src/ci/registration.rs
@@ -14,7 +14,6 @@ use mlua::{IntoLua, Lua};
 
 use miette::NamedSource;
 
-use super::mirror;
 use super::pipeline::{
     self, CompileResult, DefinitionError, Diagnostic, Job, PipelineError, RunFn,
 };
@@ -105,7 +104,6 @@ impl IntoLua for Registration {
         let table = lua.create_table()?;
         table.set("job", lua.create_function(register_job)?)?;
         table.set("image", lua.create_function(register_image)?)?;
-        table.set("mirror", lua.create_function(mirror::register)?)?;
         // Carry forward the runtime stub from the placeholder
         // `quire.ci` table seeded by `Fennel::new`. `register_module`
         // overwrites `package.loaded["quire.ci"]` with the value we
@@ -178,7 +176,7 @@ fn register_image(lua: &Lua, (name,): (String,)) -> mlua::Result<()> {
 /// from the Lua debug stack so per-job validation errors carry a span
 /// pointing back at the user's source. Enforces the user-facing
 /// reserved-slash rule: ids may not contain `/`, since the `quire/`
-/// namespace is reserved for built-in helpers (see `mirror::register_mirror`).
+/// namespace is reserved for built-in helpers.
 fn register_job(
     lua: &Lua,
     (id, inputs, run_fn): (String, Vec<String>, mlua::Function),
diff --git a/quire-core/src/fennel.rs b/quire-core/src/fennel.rs
index 4511675..67269d4 100644
--- a/quire-core/src/fennel.rs
+++ b/quire-core/src/fennel.rs
@@ -87,7 +87,7 @@ impl Fennel {
         // `runtime` stub. The stub is the canonical runtime table:
         // `RuntimeHandle::install` mutates it in place and `uninstall`
         // clears it. `registration::register` overwrites the rest of
-        // `quire.ci` (job/image/mirror) but carries this same stub
+        // `quire.ci` (job/image) but carries this same stub
         // forward as the new module's `runtime` field, so references
         // captured before, during, and after registration all point at
         // the same Lua table. There is no `quire.runtime` module — all
diff --git a/quire-server/src/ci/mod.rs b/quire-server/src/ci/mod.rs
index b083802..a449e35 100644
--- a/quire-server/src/ci/mod.rs
+++ b/quire-server/src/ci/mod.rs
@@ -11,7 +11,7 @@ pub use quire_core::ci::pipeline::{
     DefinitionError, Diagnostic, Job, Pipeline, PipelineError, StructureError,
 };
 pub use quire_core::ci::run::RunMeta;
-pub use quire_core::ci::{mirror, pipeline, registration, runtime};
+pub use quire_core::ci::{pipeline, registration, runtime};
 pub use run::{Executor, Run, RunState, Runs, materialize_workspace, reconcile_orphans};
 
 /// A resolved commit reference.