Remove mirroring from CI
Server-side mirroring already runs on every push, so a pipeline `(mirror ...)` job pushes the same refs twice — the CI path is a duplicate, not a mechanism. Drops M.mirror and its helpers from quire.stdlib, the .quire/ci.fnl job, the docs section, and the tests, plus the git_dir plumbing through Bootstrap, the runtime push table, and store_bootstrap_data that only existed to feed it.

Assisted-by: GLM-5.2 via pi
change mqonymuqrrkmumtyzqzknrusqqvqtvnu
commit 3b54ac6e3e8fa9251dd5bdd00d98197f5a04eec8
author Alpha Chen <alpha@kejadlen.dev>
date
parent zvxortqx
diff --git a/.quire/ci.fnl b/.quire/ci.fnl
index d5943dd..0bf5ff9 100644
--- a/.quire/ci.fnl
+++ b/.quire/ci.fnl
@@ -1,15 +1,3 @@
 (local {: job} (require :quire.ci))
-(local {: mirror} (require :quire.stdlib))
 
 ; (job :test [:quire/push] (fn [{: sh}] (sh [:cargo :test])))
-
-; (job :mirror [:quire/push]
-;      (fn [{: jobs : secret}]
-;        (let [push (jobs :quire/push)]
-;          (when (= push.ref :refs/heads/main)
-;            (mirror {:url "https://github.com/kejadlen/quire.git"
-;                     :auth-header (secret :github_auth_header)
-;                     :sha push.sha
-;                     :tag (.. :v (os.date "!%Y-%m-%d") "-" (push.sha:sub 1 8))
-;                     :git-dir push.git-dir
-;                     :refs [:refs/heads/main]})))))
diff --git a/docs/CI-FENNEL.md b/docs/CI-FENNEL.md
index 9cbcae1..6d3ea86 100644
--- a/docs/CI-FENNEL.md
+++ b/docs/CI-FENNEL.md
@@ -221,33 +221,6 @@ The execute VM is sandboxed (no `io`/`os`/`debug`), so `runtime.sh` is the docum
 
 `runtime` is also reachable as a module: `(let [{: sh : secret} (require :quire.runtime)] …)`. Same table, same closures — useful for library code that wants its dependencies explicit.
 
-## Stdlib (`quire.stdlib`)
-
-Helpers that compose runtime primitives into common recipes. Embedded into the binary; available via `(require :quire.stdlib)` from any run-fn.
-
-The kernel (`sh`/`secret`/`jobs`) stays small. Higher-level operations like tag-and-push live in Fennel where they're easier to read and evolve.
-
-```
-(local {: mirror} (require :quire.stdlib))
-
-(ci.job :mirror [:quire/push :test]
-  (fn [{: jobs : secret}]
-    (let [push (jobs :quire/push)
-          auth (secret :github_auth_header)]
-      (mirror {:url         "https://github.com/example/repo.git"
-               :auth-header auth
-               :sha         push.sha
-               :tag         (.. "quire-" (string.sub push.sha 1 8))
-               :git-dir     (. push :git-dir)
-               :refs        ["refs/heads/main"]}))))
-```
-
-Available helpers:
-
-* `(mirror opts)` — tag a commit and push it (plus optional refs) to a remote. `opts.url`, `opts.auth-header`, `opts.sha`, `opts.tag`, and `opts.git-dir` are required; `opts.refs` defaults to `[]`. The caller resolves the credential (typically via `runtime.secret`) and passes the full HTTP header line as `:auth-header`; mirror passes it to git via `GIT_CONFIG_*` env vars rather than `-c http.extraHeader=…` in argv, so it doesn't appear in `ps` listings. Returns `{:tag :pushed_refs}`. Raises on missing required opts or non-zero git exits.
-
-Use the stdlib form to mirror conditionally or as part of a larger run-fn.
-
 ## A worked example
 
 ```
diff --git a/docs/CI-STATE.md b/docs/CI-STATE.md
index bd74d04..6faadb2 100644
--- a/docs/CI-STATE.md
+++ b/docs/CI-STATE.md
@@ -160,7 +160,7 @@ sequenceDiagram
     Run->>CI: spawn (QUIRE__SERVER_URL, QUIRE__RUN_TOKEN, --events, --out-dir)
     CI->>Bootstrap: GET /api/run/bootstrap (bearer token)
     Bootstrap->>DB: UPDATE runs SET state='active', started_at_ms=now
-    Bootstrap-->>CI: git_dir, meta, sentry_trace_id
+    Bootstrap-->>CI: meta, sentry_trace_id
     CI->>CI: compile .quire/ci.fnl
     loop per job in topo order
       CI->>CI: enter_job / run-fn / leave_job
@@ -234,7 +234,6 @@ Two things in `CI.md` that the code does *not* yet implement at this layer:
 | `started_at_ms` | `transition(Active)`, also stamped as fallback in `Succeeded/Failed/Canceled` | `read_started_at`, web handlers |
 | `finished_at_ms` | `transition(Succeeded/Failed/Canceled)` | `read_finished_at`, web handlers |
 | `run_token` | `Runs::create` (API sessions only) | `verify_run_token` middleware |
-| `git_dir` | `Run::store_bootstrap_data` (API sessions only) | bootstrap endpoint |
 | `traceparent` | `Run::store_bootstrap_data` (API sessions only) | bootstrap endpoint |
 
 Migration 0007 dropped eight columns that carried no live data with the Process executor: `container_id`, `workspace_path`, `image_tag`, `build_started_at_ms`, `build_finished_at_ms`, `container_started_at_ms`, `container_stopped_at_ms`, and `sentry_trace_id`. The first five were Docker-executor placeholders; `workspace_path` was written at create time but reconstructable from `<base_dir>/<run_id>/workspace`; `sentry_trace_id` was added in migration 0004 and superseded by `traceparent` before it was ever used.
diff --git a/quire-ci/src/main.rs b/quire-ci/src/main.rs
index b66093e..b2739ed 100644
--- a/quire-ci/src/main.rs
+++ b/quire-ci/src/main.rs
@@ -240,12 +240,11 @@ impl RunClient {
     ///
     /// One-shot: the server marks the bootstrap as fetched after the first
     /// successful call and returns 410 on any subsequent call.
-    fn fetch_bootstrap(&self) -> Result<(PathBuf, RunMeta, TelemetryContext)> {
+    fn fetch_bootstrap(&self) -> Result<(RunMeta, TelemetryContext)> {
         let bootstrap: Bootstrap =
             (|| -> reqwest::Result<_> { self.get("bootstrap")?.error_for_status()?.json() })()
                 .into_diagnostic()?;
         Ok((
-            bootstrap.git_dir,
             bootstrap.meta,
             TelemetryContext {
                 traceparent: bootstrap.traceparent,
@@ -352,7 +351,7 @@ fn main() -> Result<()> {
             // before the Sentry client flushes to the server.
             let _guard = telemetry::init_telemetry(miette_layer, FmtMode::Plain, None, VERSION)?;
 
-            let (git_dir, meta, sentry_ctx) = if local {
+            let (meta, sentry_ctx) = if local {
                 let Some(git_dir) = git_dir else {
                     bail!("--git-dir is required for local runs");
                 };
@@ -363,7 +362,7 @@ fn main() -> Result<()> {
                     r#ref: git_ref,
                     pushed_at: jiff::Timestamp::now(),
                 };
-                (git_dir, meta, TelemetryContext::default())
+                (meta, TelemetryContext::default())
             } else {
                 client.fetch_bootstrap().inspect_err(|e| {
                     tracing::error!(error = %e, "bootstrap fetch failed");
@@ -386,7 +385,7 @@ fn main() -> Result<()> {
 
             let registry = SecretRegistry::new(move |name| client.fetch_secret(name));
 
-            run_pipeline(workspace, sink, log_dir, git_dir, meta, registry)
+            run_pipeline(workspace, sink, log_dir, meta, registry)
         }
     }
 }
@@ -493,7 +492,6 @@ fn run_pipeline(
     workspace: PathBuf,
     mut sink: Box<dyn EventSink>,
     log_dir: PathBuf,
-    git_dir: PathBuf,
     meta: RunMeta,
     registry: SecretRegistry,
 ) -> Result<()> {
@@ -535,9 +533,7 @@ fn run_pipeline(
 
     let sink: Rc<RefCell<Box<dyn EventSink>>> = Rc::new(RefCell::new(sink));
 
-    let runtime = Rc::new(Runtime::new(
-        pipeline, registry, &meta, &git_dir, workspace, log_dir,
-    ));
+    let runtime = Rc::new(Runtime::new(pipeline, registry, &meta, workspace, log_dir));
 
     // Active job pointer, shared between the main loop and the
     // runtime callback. The callback translates RuntimeEvent into
diff --git a/quire-core/src/ci/bootstrap.rs b/quire-core/src/ci/bootstrap.rs
index 56bbc81..9f254dd 100644
--- a/quire-core/src/ci/bootstrap.rs
+++ b/quire-core/src/ci/bootstrap.rs
@@ -5,23 +5,19 @@
 //! token. Local runs pass `--local --git-dir <path>` and derive the
 //! commit SHA and ref directly from the git dir.
 
-use std::path::PathBuf;
-
 use serde::{Deserialize, Serialize};
 
 use crate::ci::run::RunMeta;
 
 /// Inputs the orchestrator supplies to a quire-ci subprocess.
 ///
-/// `git_dir` is the bare repo the run is scoped to. quire-ci surfaces
-/// it via `(jobs :quire/push).git-dir`, which the mirror job's run-fn
-/// passes to git as `GIT_DIR`. The materialized workspace is a flat
-/// `git archive` extract with no `.git` inside, so quire-ci has no
-/// way to recover this path on its own.
+/// Carries push facts (`meta`), run identity (`repo`, `run_id`), and
+/// the trace context (`traceparent`). The bare repo the run is scoped
+/// to stays server-side — quire-ci runs against the materialized
+/// workspace and has no need for it.
 #[derive(Debug, Serialize, Deserialize)]
 pub struct Bootstrap {
     pub meta: RunMeta,
-    pub git_dir: PathBuf,
     /// The repo this run is scoped to (matches the `runs.repo`
     /// column). quire-ci tags Sentry events with it.
     pub repo: String,
diff --git a/quire-core/src/ci/runtime.rs b/quire-core/src/ci/runtime.rs
index 4aa0410..aea1808 100644
--- a/quire-core/src/ci/runtime.rs
+++ b/quire-core/src/ci/runtime.rs
@@ -142,7 +142,6 @@ impl Runtime {
         pipeline: Pipeline,
         registry: SecretRegistry,
         meta: &RunMeta,
-        git_dir: &std::path::Path,
         workspace: std::path::PathBuf,
         log_dir: std::path::PathBuf,
     ) -> Self {
@@ -155,11 +154,6 @@ impl Runtime {
         push.set("ref", meta.r#ref.as_str()).expect("set ref");
         push.set("pushed-at", meta.pushed_at.to_string().as_str())
             .expect("set pushed-at");
-        // `git-dir` is environmental rather than a fact about the push;
-        // it may belong on an ambient context alongside `sh`/`secret`
-        // instead of on this table.
-        push.set("git-dir", git_dir.to_string_lossy().as_ref())
-            .expect("set git-dir");
         let push_value = push.into_lua(lua).expect("push table to value");
 
         // Build per-job input views from transitive reachability.
@@ -1007,115 +1001,4 @@ mod tests {
             "expected type error, got: {msg}"
         );
     }
-
-    // --- quire.stdlib mirror tests ---
-
-    /// Run `git` once with the standard test env. Asserts success and
-    /// returns stdout. Used by the mirror fixture below to set up the
-    /// source and target bare repos.
-    fn git(args: &[&str], cwd: &std::path::Path) -> String {
-        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 out = std::process::Command::new("git")
-            .args(args)
-            .current_dir(cwd)
-            .envs(env_vars)
-            .output()
-            .expect("git");
-        assert!(
-            out.status.success(),
-            "git {:?} failed: {}",
-            args,
-            String::from_utf8_lossy(&out.stderr)
-        );
-        String::from_utf8(out.stdout).expect("utf8")
-    }
-
-    /// Build a source bare repo with one commit and an empty target
-    /// bare repo in the same tempdir. Returns (tempdir, source bare,
-    /// target bare, head 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");
-        git(&["init", "-b", "main"], &work);
-        git(&["commit", "--allow-empty", "-m", "initial"], &work);
-        let sha = git(&["rev-parse", "HEAD"], &work).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)
-    }
-
-    #[test]
-    fn stdlib_mirror_tags_and_pushes() {
-        let (_dir, bare, target, sha) = bare_repo_with_target();
-
-        let mut secrets = HashMap::new();
-        secrets.insert(
-            "github_token".to_string(),
-            SecretString::from("Authorization: Bearer test-token"),
-        );
-
-        let source = format!(
-            r#"(local {{: job : runtime}} (require :quire.ci))
-(local {{: mirror}} (require :quire.stdlib))
-(job :go [:quire/push]
-  (fn []
-    (let [auth (runtime.secret :github_token)]
-      (mirror {{:url "{url}"
-               :auth-header auth
-               :sha "{sha}"
-               :tag "v1"
-               :git-dir "{git_dir}"}}))))"#,
-            url = format!("file://{}", target.display()),
-            sha = sha,
-            git_dir = bare.display(),
-        );
-
-        let (runtime, run_fn, _guard) = rt(&source, secrets);
-        let _: mlua::Value = call_fn(&runtime, &run_fn).expect("mirror should succeed");
-
-        // Tag landed in the target repo, pointing at the head SHA.
-        let resolved = git(&["rev-parse", "refs/tags/v1"], &target);
-        assert_eq!(resolved.trim(), sha);
-    }
-
-    #[test]
-    fn stdlib_mirror_errors_on_missing_required_opt() {
-        let source = r#"(local {: job} (require :quire.ci))
-(local {: mirror} (require :quire.stdlib))
-(job :go [:quire/push]
-  (fn []
-    (mirror {:auth-header "x" :sha "x" :tag "v1" :git-dir "/tmp"})))"#;
-        let (_runtime, run_fn, _guard) = rt(source, HashMap::new());
-        let err = run_fn.call::<mlua::Value>(()).unwrap_err();
-        let msg = err.to_string();
-        assert!(
-            msg.contains("Missing argument url"),
-            "expected missing-:url error, got: {msg}"
-        );
-    }
 }
diff --git a/quire-core/src/ci/stdlib.fnl b/quire-core/src/ci/stdlib.fnl
index c851c79..744dbf1 100644
--- a/quire-core/src/ci/stdlib.fnl
+++ b/quire-core/src/ci/stdlib.fnl
@@ -5,55 +5,4 @@
 
 (local M {})
 
-(fn trim [s]
-  (string.gsub s "%s+$" ""))
-
-(fn cat [...]
-  ;; Concatenate sequence tables into a fresh sequence.
-  (let [out []]
-    (each [_ t (ipairs [...])]
-      (each [_ x (ipairs t)]
-        (table.insert out x)))
-    out))
-
-;; (mirror opts)
-;;
-;; Tag a commit and push the tag (plus optional refs) to a remote.
-;;
-;; opts: {:url         — remote URL (required)
-;;        :auth-header — full HTTP header line passed to git as
-;;                       `http.extraHeader`; resolve via
-;;                       `runtime.secret` at the call site (required)
-;;        :sha         — commit to tag (required)
-;;        :tag         — tag name (required)
-;;        :git-dir     — bare git directory the run is scoped to (required)
-;;        :refs        — extra refs to push alongside the tag
-;;                       (optional, default [])}
-;;
-;; Returns {:tag :pushed_refs}. Raises on missing required opts or
-;; non-zero git exits. `lambda` checks the required bindings for nil
-;; at the call site.
-(λ M.mirror [{: url : auth-header : sha : tag : git-dir :refs ?refs}]
-  (let [{: sh} (. (require :quire.ci) :runtime)
-        refs (or ?refs [])
-        ;; Pass http.extraHeader via GIT_CONFIG_* env (git 2.31+)
-        ;; instead of `-c http.extraHeader=…` in argv. Keeps the auth
-        ;; header out of `ps` and out of any argv logging we add
-        ;; later; runtime.sh's redact pass on stdout/stderr remains as
-        ;; defense in depth.
-        sh-opts {:env {:GIT_DIR git-dir
-                       :GIT_CONFIG_COUNT :1
-                       :GIT_CONFIG_KEY_0 :http.extraHeader
-                       :GIT_CONFIG_VALUE_0 auth-header}}
-        tag-result (sh [:git :tag tag sha] sh-opts)]
-    (when (not= 0 tag-result.exit)
-      (error (.. "git tag failed: " (trim tag-result.stderr))))
-    (let [push-args (cat [:git :push :--porcelain url]
-                         refs
-                         [(.. :refs/tags/ tag)])
-          push-result (sh push-args sh-opts)]
-      (when (not= 0 push-result.exit)
-        (error (.. "git push failed: " (trim push-result.stderr))))
-      {: tag :pushed_refs refs})))
-
 M
diff --git a/quire-core/src/fennel.rs b/quire-core/src/fennel.rs
index aceb5ad..52fe7d5 100644
--- a/quire-core/src/fennel.rs
+++ b/quire-core/src/fennel.rs
@@ -537,12 +537,14 @@ mod tests {
     #[test]
     fn stdlib_module_preloaded_at_construction() {
         let f = fennel();
-        let module: mlua::Table = f
+        // `quire.stdlib` is the home for run-fn helpers composed from
+        // runtime primitives. The module loads even when it ships no
+        // helpers, so a future addition needs no loader changes.
+        let _: mlua::Table = f
             .lua()
             .load(r#"return require("quire.stdlib")"#)
             .eval()
             .expect("require quire.stdlib");
-        let _: mlua::Function = module.get("mirror").expect("mirror should be a function");
     }
 
     #[test]
diff --git a/quire-server/src/ci/run.rs b/quire-server/src/ci/run.rs
index bbd1806..5b81bf8 100644
--- a/quire-server/src/ci/run.rs
+++ b/quire-server/src/ci/run.rs
@@ -278,7 +278,7 @@ impl Run {
                 cmd.arg("--local").arg("--git-dir").arg(git_dir);
             }
             Some(s) => {
-                self.store_bootstrap_data(git_dir, traceparent)?;
+                self.store_bootstrap_data(traceparent)?;
                 cmd.env("QUIRE__SERVER_URL", &s.server_url);
                 cmd.env("QUIRE__RUN_TOKEN", &s.run_token);
             }
@@ -425,17 +425,11 @@ impl Run {
     /// Called by `execute` when the API transport is active, before spawning
     /// quire-ci. quire-ci fetches this via `GET /api/runs/:id/bootstrap`
     /// instead of reading a file.
-    fn store_bootstrap_data(&self, git_dir: &Path, traceparent: Option<&str>) -> Result<()> {
-        let git_dir_str = git_dir.to_str().ok_or_else(|| {
-            std::io::Error::new(
-                std::io::ErrorKind::InvalidData,
-                "git_dir path is not valid UTF-8",
-            )
-        })?;
+    fn store_bootstrap_data(&self, traceparent: Option<&str>) -> Result<()> {
         let db = crate::db::open(&self.db_path)?;
         db.execute(
-            "UPDATE runs SET git_dir = ?1, traceparent = ?2 WHERE id = ?3",
-            rusqlite::params![git_dir_str, traceparent, &self.id],
+            "UPDATE runs SET traceparent = ?1 WHERE id = ?2",
+            rusqlite::params![traceparent, &self.id],
         )?;
         Ok(())
     }
diff --git a/quire-server/src/quire/web/api.rs b/quire-server/src/quire/web/api.rs
index 79695a0..95ad4c6 100644
--- a/quire-server/src/quire/web/api.rs
+++ b/quire-server/src/quire/web/api.rs
@@ -5,8 +5,6 @@
 //! is created and stored in `runs.run_token`. The bearer token itself
 //! identifies the run — no run ID appears in the path.
 
-use std::path::PathBuf;
-
 use serde::Deserialize;
 
 use axum::extract::{FromRequestParts, State};
@@ -137,11 +135,8 @@ async fn verify_run_token(
 ///
 /// Returns the bootstrap payload for a run. One-shot: the server marks
 /// bootstrap as fetched on the first successful read and returns 410 on
-/// any subsequent call. Auth is handled by [`verify_run_token`] middleware.
-///
-/// Returns 404 if the run does not have API bootstrap data (e.g. the run
-/// was created with filesystem transport and `store_bootstrap_data` was
-/// never called).
+/// any subsequent call. Auth is handled by [`verify_run_token`] middleware,
+/// which resolves the run from the bearer token before this handler runs.
 async fn get_bootstrap(
     State(quire): State<Quire>,
     axum::Extension(run_id): axum::Extension<String>,
@@ -155,7 +150,6 @@ async fn get_bootstrap(
                 sha: String,
                 ref_name: String,
                 pushed_at_ms: i64,
-                git_dir: Option<String>,
                 traceparent: Option<String>,
                 dispatched_at: Option<i64>,
                 repo: String,
@@ -163,7 +157,7 @@ async fn get_bootstrap(
 
             let row: RunRow = db
                 .prepare(
-                    "SELECT sha, ref_name, pushed_at_ms, git_dir, traceparent, dispatched_at, repo
+                    "SELECT sha, ref_name, pushed_at_ms, traceparent, dispatched_at, repo
                      FROM runs WHERE id = ?1",
                 )?
                 .query_and_then(rusqlite::params![run_id], serde_rusqlite::from_row)?
@@ -174,8 +168,6 @@ async fn get_bootstrap(
                 return Err(ApiError::Gone);
             }
 
-            let git_dir: PathBuf = row.git_dir.map(PathBuf::from).ok_or(ApiError::NotFound)?;
-
             let meta = RunMeta {
                 sha: row.sha,
                 r#ref: row.ref_name,
@@ -191,7 +183,6 @@ async fn get_bootstrap(
 
             Ok(Bootstrap {
                 meta,
-                git_dir,
                 repo: row.repo,
                 run_id,
                 traceparent: row.traceparent,
@@ -295,7 +286,6 @@ mod tests {
     async fn create_run_with_bootstrap(
         env: &TestEnv,
         session: &ApiSession,
-        git_dir: &str,
         traceparent: Option<&str>,
     ) -> String {
         let run = env
@@ -306,8 +296,8 @@ mod tests {
 
         let db = crate::db::open(&env.quire.db_path()).expect("db open");
         db.execute(
-            "UPDATE runs SET git_dir = ?1, traceparent = ?2 WHERE id = ?3",
-            rusqlite::params![git_dir, traceparent, &run_id],
+            "UPDATE runs SET traceparent = ?1 WHERE id = ?2",
+            rusqlite::params![traceparent, &run_id],
         )
         .expect("update bootstrap data");
         run_id
@@ -317,7 +307,7 @@ mod tests {
     async fn bootstrap_returns_401_without_auth() {
         let env = TestEnv::new();
         let session = ApiSession::new(3000);
-        create_run_with_bootstrap(&env, &session, "/repos/test.git", None).await;
+        create_run_with_bootstrap(&env, &session, None).await;
 
         let resp = get(env.app(), "/run/bootstrap", None).await;
         assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
@@ -335,7 +325,7 @@ mod tests {
     async fn bootstrap_returns_payload_on_first_fetch() {
         let env = TestEnv::new();
         let session = ApiSession::new(3000);
-        let run_id = create_run_with_bootstrap(&env, &session, "/repos/test.git", None).await;
+        let run_id = create_run_with_bootstrap(&env, &session, None).await;
 
         let resp = get(env.app(), "/run/bootstrap", Some(&session.run_token)).await;
         assert_eq!(resp.status(), StatusCode::OK);
@@ -343,7 +333,6 @@ mod tests {
         use http_body_util::BodyExt;
         let body = resp.into_body().collect().await.unwrap().to_bytes();
         let parsed: serde_json::Value = serde_json::from_slice(&body).expect("json body");
-        assert_eq!(parsed["git_dir"], "/repos/test.git");
         assert_eq!(parsed["repo"], "test.git");
         assert_eq!(parsed["run_id"], run_id);
     }
@@ -352,7 +341,7 @@ mod tests {
     async fn bootstrap_returns_410_on_second_fetch() {
         let env = TestEnv::new();
         let session = ApiSession::new(3000);
-        create_run_with_bootstrap(&env, &session, "/repos/test.git", None).await;
+        create_run_with_bootstrap(&env, &session, None).await;
         let token = &session.run_token;
 
         let first = get(env.app(), "/run/bootstrap", Some(token)).await;