omni-dev 0.46.0

AI-powered git commit rewriter, PR generator, and MCP server for Jira, Confluence, Datadog, Gmail, and Drive.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
//! Shared test-only helpers.
//!
//! These utilities are consumed by unit tests across the crate and must
//! stay in sync between shim-writing sites — see issue #642.

#![allow(clippy::unwrap_used, clippy::expect_used)]

/// Process-wide mutex serialising every test in the crate that mutates the
/// global `HOME` environment variable, or any credential env var whose
/// resolution depends on it (`dirs::home_dir()` /
/// `Settings::get_settings_path()`).
///
/// Every HOME-mutating test-support module (Atlassian, Datadog, Gmail, the
/// `ai_chat`/`cli::ai::chat` provider tests, …) aliases this **one** static
/// rather than declaring its own `Mutex<()>`. Independent per-module mutexes
/// provide no mutual exclusion against each other — each only serialises its
/// own module's tests — while `HOME` itself is shared process-wide state, so
/// two modules' tests can still interleave and race on it. That exact
/// pattern caused the flaky race fixed for Atlassian in issue #950, and
/// resurfaced as a Gmail-vs-Datadog race (both mutating `HOME` under their
/// own independent mutex) in issue #1465.
pub(crate) static HOME_ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());

/// Redirects the audit log into an isolated tempdir for the life of one
/// test — for this thread only, through `request_log::TEST_AUDIT_ROUTE`,
/// not the process-global `OMNI_DEV_AUDIT_LOG_FILE` — so it needs no lock
/// and tests holding one run fully in parallel.
///
/// Every Drive lease-check/acquire test that reaches a live lease or a
/// refusal triggers a best-effort or write-ahead audit write (ADR-0080
/// §11) as a side effect of calling production code, whether or not the
/// test cares about its content. A test that doesn't redirect lands in
/// `request_log`'s shared scratch file — never the real machine's
/// `audit.jsonl`, and never another test's env override either, since an
/// un-opted thread does not consult the env var at all; a test that wants
/// to read its *own* records back takes this guard. The writes this guard
/// observes must therefore happen on the test's own thread (a
/// `#[tokio::test]` body does; a `spawn_blocking` closure does not).
///
/// [`Self::records`] reads back what production code wrote, so a test
/// asserting on the audit trail needs no private line-parsing helper.
pub(crate) struct AuditLogGuard {
    path: std::path::PathBuf,
}

impl AuditLogGuard {
    pub(crate) fn redirect(dir: &std::path::Path) -> Self {
        let path = dir.join("audit.jsonl");
        crate::request_log::TEST_AUDIT_ROUTE.with(|slot| *slot.borrow_mut() = Some(path.clone()));
        Self { path }
    }

    /// Every record written so far, in order. An audit file that was never
    /// created (nothing wrote) reads as empty rather than panicking, so a
    /// "must write nothing" assertion is `assert!(guard.records().is_empty())`.
    pub(crate) fn records(&self) -> Vec<crate::request_log::LogRecord> {
        match std::fs::read_to_string(&self.path) {
            Ok(text) => text
                .lines()
                .map(|line| serde_json::from_str(line).unwrap())
                .collect(),
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => Vec::new(),
            Err(err) => panic!("failed to read {}: {err}", self.path.display()),
        }
    }

    /// The `verdict` of every record written so far, in order — the
    /// assertion almost every audit-trail test makes.
    pub(crate) fn verdicts(&self) -> Vec<String> {
        self.records()
            .iter()
            .map(|record| record.context.get("verdict").cloned().unwrap_or_default())
            .collect()
    }
}

impl Drop for AuditLogGuard {
    fn drop(&mut self) {
        crate::request_log::TEST_AUDIT_ROUTE.with(|slot| *slot.borrow_mut() = None);
    }
}

#[cfg(test)]
mod audit_log_guard_tests {
    use super::AuditLogGuard;

    /// Direct cover for the "nothing wrote" branch of [`AuditLogGuard::records`]
    /// (`ErrorKind::NotFound` reads as empty rather than panicking): every
    /// other caller in the crate redirects and then triggers a write before
    /// reading records back, so this path is otherwise never exercised.
    #[test]
    fn records_reads_as_empty_before_anything_writes() {
        let dir = tempfile::tempdir().unwrap();
        let guard = AuditLogGuard::redirect(dir.path());
        assert!(guard.records().is_empty());
    }

    /// Direct cover for the panic branch of [`AuditLogGuard::records`]: a
    /// read failure other than `NotFound` must not read as "nothing wrote
    /// yet" — it must panic instead, the same fail-loud contract every
    /// other test-support helper in this file follows. A directory in
    /// place of the audit file forces that non-`NotFound` failure, the
    /// same trick `drive::lease::check`'s own fail-closed test uses.
    #[test]
    fn records_panics_when_the_read_fails_for_a_reason_other_than_not_found() {
        let dir = tempfile::tempdir().unwrap();
        let guard = AuditLogGuard::redirect(dir.path());
        std::fs::create_dir(dir.path().join("audit.jsonl")).unwrap();

        let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| guard.records()));
        assert!(result.is_err());
    }
}

/// Thread-scoped log buffer backing [`capture_at`].
#[derive(Clone, Default)]
struct CaptureWriter(std::sync::Arc<std::sync::Mutex<Vec<u8>>>);

impl std::io::Write for CaptureWriter {
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.0.lock().unwrap().extend_from_slice(buf);
        Ok(buf.len())
    }
    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}

impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for CaptureWriter {
    type Writer = Self;
    fn make_writer(&'a self) -> Self::Writer {
        self.clone()
    }
}

/// Keeps one extra dispatcher registered for the life of the process, so that
/// [`capture_at`] and [`capture_future_at`] cannot lose events to a race.
///
/// `tracing-core` caches each callsite's interest. While only *one* dispatcher is
/// registered it recomputes that interest against the *calling thread's* default
/// instead of every registered dispatcher. In the parallel test binary that
/// lets a thread with no subscriber installed — the first to reach a callsite
/// while another thread's capture subscriber is the only one live — cache
/// "never" for it, and the capturing thread's event is then silently dropped:
/// a flake in the capturing test, and a macro line that reports uncovered. A
/// second, never-dropped registration keeps `tracing-core` on the path that
/// consults every live dispatcher. It is never installed as a default, so it
/// changes what no thread logs.
fn keep_every_dispatcher_consulted() {
    static KEEP: std::sync::OnceLock<tracing::Dispatch> = std::sync::OnceLock::new();
    KEEP.get_or_init(|| tracing::Dispatch::new(tracing::subscriber::NoSubscriber::default()));
}

/// Runs `f` under a thread-local subscriber that captures every event at
/// `level` or above, and returns everything it logged. `f` must be fully
/// synchronous on this thread. The one shared home for this capture
/// pattern (issue #1744); per-module `capture_info`/`capture_warnings`
/// helpers are thin aliases over it.
pub(crate) fn capture_at(level: tracing::Level, f: impl FnOnce()) -> String {
    keep_every_dispatcher_consulted();
    let writer = CaptureWriter::default();
    let subscriber = tracing_subscriber::fmt()
        .with_max_level(level)
        .with_ansi(false)
        .with_writer(writer.clone())
        .finish();
    tracing::subscriber::with_default(subscriber, f);
    let logs = String::from_utf8_lossy(&writer.0.lock().unwrap()).into_owned();
    logs
}

/// Captures events from an async future on every poll, even across worker threads.
/// Spawned child tasks still need their own subscriber; this does not install a
/// global subscriber or hold a thread-local guard across an await.
pub(crate) async fn capture_future_at<F: std::future::Future>(
    level: tracing::Level,
    future: F,
) -> (F::Output, String) {
    use tracing::instrument::WithSubscriber;
    keep_every_dispatcher_consulted();
    let writer = CaptureWriter::default();
    let subscriber = tracing_subscriber::fmt()
        .with_max_level(level)
        .with_ansi(false)
        .with_writer(writer.clone())
        .finish();
    let result = future.with_subscriber(subscriber).await;
    let logs = String::from_utf8_lossy(&writer.0.lock().unwrap()).into_owned();
    (result, logs)
}

pub(crate) mod failing_io {
    //! Writer fixture that always returns `ErrorKind::Other` from
    //! `write` and `flush`. Used to drive `?`-propagation Err branches
    //! in destructive-command tests where the prompt/preview write or
    //! the post-API-success writeln is expected to fail.
    pub(crate) struct FailingWriter;

    impl std::io::Write for FailingWriter {
        fn write(&mut self, _: &[u8]) -> std::io::Result<usize> {
            Err(std::io::Error::other("simulated write failure"))
        }
        fn flush(&mut self) -> std::io::Result<()> {
            Err(std::io::Error::other("simulated flush failure"))
        }
    }

    #[cfg(test)]
    mod tests {
        use super::*;
        use std::io::Write;

        /// Direct cover for `FailingWriter::flush`. The destructive-command
        /// tests fail at the prior `write!` so flush never fires; this
        /// asserts its body still returns the expected error.
        #[test]
        fn flush_returns_error() {
            let mut w = FailingWriter;
            let err = w.flush().unwrap_err();
            assert!(err.to_string().contains("simulated flush failure"));
        }
    }
}

pub(crate) mod env {
    //! Pure in-memory [`EnvSource`](crate::utils::env::EnvSource) for tests.
    //!
    //! `MapEnv` lets env-parsing boundaries be tested without mutating the
    //! process-global environment: a test builds its own map and passes
    //! `&map` to the seam's `*_with(&impl EnvSource, …)` entry point. Because
    //! the map is an owned value with no shared state, such tests need no
    //! lock and run fully in parallel (issue #1030 / #821).
    use std::collections::HashMap;

    /// An [`EnvSource`](crate::utils::env::EnvSource) backed by an in-memory
    /// map — the test counterpart to
    /// [`SystemEnv`](crate::utils::env::SystemEnv).
    #[derive(Debug, Default, Clone)]
    pub(crate) struct MapEnv(HashMap<String, String>);

    impl MapEnv {
        /// Creates an empty environment (every lookup returns `None`).
        pub(crate) fn new() -> Self {
            Self::default()
        }

        /// Inserts `key = value` and returns `self`, for builder-style setup.
        pub(crate) fn with(mut self, key: &str, value: &str) -> Self {
            self.0.insert(key.to_string(), value.to_string());
            self
        }
    }

    impl crate::utils::env::EnvSource for MapEnv {
        fn var(&self, key: &str) -> Option<String> {
            self.0.get(key).cloned()
        }
    }

    /// Writes `contents` to an owner-only (`0600`) file in a fresh temp dir,
    /// for tests of a secret's `<NAME>_FILE` companion (issue #2006). Returns
    /// the dir (keep it alive) and the file's absolute path.
    #[allow(clippy::unwrap_used)]
    pub(crate) fn secret_file(contents: &str) -> (tempfile::TempDir, String) {
        let dir = tempfile::tempdir().unwrap();
        let path = dir.path().join("secret");
        std::fs::write(&path, contents).unwrap();
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600)).unwrap();
        }
        let path = path.to_str().unwrap().to_string();
        (dir, path)
    }
}

pub(crate) mod atlassian_env {
    //! In-process Atlassian env-var guard for tests that drive
    //! `cli::atlassian::helpers::create_client()` end-to-end.
    //!
    //! Mirrors `tests/mcp_integration_test.rs::AtlassianEnvGuard` (which lives
    //! in a *separate* integration-test process and so keeps its own lock).
    //! Within the lib-test process every guard that mutates the Atlassian
    //! credential env vars **must serialise on the one canonical mutex**
    //! [`crate::atlassian::auth::test_util::AUTH_ENV_MUTEX`] — independent
    //! mutexes over the same process-global vars provide no mutual exclusion
    //! and caused the flaky env race in issue #950.
    //!
    //! This is transitional scaffolding: as the remaining `*Command` tests
    //! migrate to the [`create_client_from`] dependency-injection seam (and
    //! stop mutating env entirely), their use of this guard — and eventually
    //! the guard itself — can be removed.
    //!
    //! [`create_client_from`]: crate::cli::atlassian::helpers::create_client_from
    use std::sync::MutexGuard;

    pub(crate) struct AtlassianEnvGuard {
        _guard: MutexGuard<'static, ()>,
        prev_home: Option<String>,
        prev_xdg: Option<String>,
        prev_url: Option<String>,
        prev_email: Option<String>,
        prev_token: Option<String>,
        _tmp: tempfile::TempDir,
    }

    impl AtlassianEnvGuard {
        /// Repoints HOME at an empty tempdir and sets the Atlassian env
        /// vars so `create_client()` produces a client targeting the
        /// supplied URL with the supplied credentials.
        pub(crate) fn new(instance_url: &str, email: &str, token: &str) -> Self {
            let guard = crate::atlassian::auth::test_util::AUTH_ENV_MUTEX
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let tmp = tempfile::tempdir().unwrap();
            let prev_home = std::env::var("HOME").ok();
            let prev_xdg = std::env::var("XDG_CONFIG_HOME").ok();
            let prev_url = std::env::var("ATLASSIAN_INSTANCE_URL").ok();
            let prev_email = std::env::var("ATLASSIAN_EMAIL").ok();
            let prev_token = std::env::var("ATLASSIAN_API_TOKEN").ok();
            std::env::set_var("HOME", tmp.path());
            std::env::set_var("XDG_CONFIG_HOME", tmp.path().join("xdg"));
            std::env::set_var("ATLASSIAN_INSTANCE_URL", instance_url);
            std::env::set_var("ATLASSIAN_EMAIL", email);
            std::env::set_var("ATLASSIAN_API_TOKEN", token);
            Self {
                _guard: guard,
                prev_home,
                prev_xdg,
                prev_url,
                prev_email,
                prev_token,
                _tmp: tmp,
            }
        }
    }

    impl Drop for AtlassianEnvGuard {
        fn drop(&mut self) {
            restore("HOME", self.prev_home.as_deref());
            restore("XDG_CONFIG_HOME", self.prev_xdg.as_deref());
            restore("ATLASSIAN_INSTANCE_URL", self.prev_url.as_deref());
            restore("ATLASSIAN_EMAIL", self.prev_email.as_deref());
            restore("ATLASSIAN_API_TOKEN", self.prev_token.as_deref());
        }
    }

    fn restore(key: &str, prev: Option<&str>) {
        match prev {
            Some(v) => std::env::set_var(key, v),
            None => std::env::remove_var(key),
        }
    }
}

#[cfg(unix)]
pub(crate) mod shim {
    //! Helpers for tests that write an executable shim and then `execve` it.
    //!
    //! Writing a file and immediately running it races every other thread in
    //! the test binary that `fork()`s — every `Command::spawn` does. The child
    //! inherits a *duplicate* of our still-open writable FD, and because
    //! `O_CLOEXEC` closes only on `execve` (not on a bare `fork`), the kernel
    //! refuses our own `execve` of that file with `ETXTBSY` ("Text file busy")
    //! until the child execs and the duplicate closes. The window is
    //! microscopic but real under high parallelism (`cargo llvm-cov`); it fired
    //! on the v0.36.0 release CI. See issues #642 and #1348.
    //!
    //! [`write_exec_script`] holds the writable FD open for as short as
    //! possible (one open, `sync_all`, explicit drop) but cannot make the
    //! window zero. [`retry_on_etxtbsy`] closes it for good: re-run the exec a
    //! few times, since the child releases the inherited FD the instant it
    //! execs. The retry lives only in the test harness — `ETXTBSY` here is an
    //! artifact of writing the very binary we then run, which never happens to
    //! a real `gh`/`claude`, so production keeps failing loudly on it. (The
    //! `claude-cli` backend does the equivalent at its own spawn boundary with
    //! [`spawn_with_etxtbsy_retry`](crate::claude::ai::claude_cli).)
    //!
    //! [`retry_on_etxtbsy_async`] is the same retry for a test that drives a
    //! production `async fn` end-to-end (e.g. a `WorktreesService` op that
    //! `spawn_blocking`s the shim exec internally) rather than calling the
    //! sync `Command`-spawning function directly — a plain `shim_lock` guard
    //! bounds concurrent shim subprocesses but does **not** retry the exec
    //! race, so a test that only takes the lock can still flake. See #1348 for
    //! the sync case and the `merge_queue_with_*` tests in
    //! `daemon::services::worktrees` for the async one.
    //!
    //! Separately, [`shim_lock`] serialises tests that spawn a subprocess from a
    //! freshly-written shim. This is **not** the `ETXTBSY` fix (the retry above
    //! is) — the offending `fork()` comes from unrelated tests that never take
    //! this lock. What it does buy is bounding how many such subprocesses run at
    //! once: without it, high parallelism (e.g. `cargo test` on a many-core
    //! host) starves timing-sensitive subprocess tests — a freshly-spawned shim
    //! scheduled too late to write its state before a short run timeout reaps
    //! it. See `claude::ai::claude_cli::tests::timeout_reaps_full_process_group`.
    //!
    //! Neither of those fixes the *first exec* of a freshly-written file being
    //! slow: page-in plus, on macOS, the quarantine/Gatekeeper check. Measured
    //! at 150-600 ms cold against ~7-19 ms warm on a loaded machine, and it is
    //! paid per file, so every test pays it. A test whose run timeout is
    //! shorter than that never lets the shim do its work (#1598).
    //! [`write_warmed_exec_script`] moves that cost out of the timed region.
    use std::path::Path;
    use std::sync::{Mutex, MutexGuard};

    /// The errno the kernel returns when a process execs a file that some
    /// (possibly other) process still holds open for writing. `26` on both
    /// Linux and macOS.
    const ETXTBSY: i32 = 26;

    /// Serialises tests that spawn a subprocess from a freshly-written shim, so
    /// concurrent subprocess load stays bounded (see the module docs — this is
    /// the starvation guard, **not** the `ETXTBSY` fix).
    static SHIM_LOCK: Mutex<()> = Mutex::new(());

    /// Acquires the shim serialisation lock, recovering from poisoning so an
    /// intentional panic in one shim test doesn't cascade into the rest of the
    /// suite.
    pub(crate) fn shim_lock() -> MutexGuard<'static, ()> {
        SHIM_LOCK
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner)
    }

    /// Retries `f` while it fails with [`ETXTBSY`], backing off with bounded
    /// exponential delay. Success and every *non*-`ETXTBSY` error return
    /// immediately — so a test that expects a different failure (a non-zero
    /// exit, unparseable output) still sees exactly that, never a spuriously
    /// retried success.
    ///
    /// Wrap any call that ultimately `execve`s a freshly-written shim. See the
    /// module docs for why the race exists and why the retry is test-only.
    pub(crate) fn retry_on_etxtbsy<T>(
        mut f: impl FnMut() -> anyhow::Result<T>,
    ) -> anyhow::Result<T> {
        use std::time::Duration;
        const MAX_ATTEMPTS: u32 = 8;
        let mut backoff = Duration::from_millis(5);
        for _ in 1..MAX_ATTEMPTS {
            match f() {
                Err(e) if is_etxtbsy(&e) => {
                    std::thread::sleep(backoff);
                    backoff = backoff.saturating_mul(2);
                }
                // Success, or a non-ETXTBSY error: hand it straight back.
                other => return other,
            }
        }
        // Budget exhausted: return the final attempt's result, ETXTBSY or not,
        // so the caller fails loudly rather than looping forever.
        f()
    }

    /// Async counterpart to [`retry_on_etxtbsy`], for a test that awaits a
    /// production `async fn` that shells out to a freshly-written shim
    /// (rather than calling the sync spawn function directly, which
    /// [`retry_on_etxtbsy`] already covers). Same retry budget and backoff,
    /// but sleeps via `tokio::time::sleep` so it can run inside a
    /// `#[tokio::test]` without blocking the runtime thread.
    pub(crate) async fn retry_on_etxtbsy_async<T, F, Fut>(mut f: F) -> anyhow::Result<T>
    where
        F: FnMut() -> Fut,
        Fut: std::future::Future<Output = anyhow::Result<T>>,
    {
        use std::time::Duration;
        const MAX_ATTEMPTS: u32 = 8;
        let mut backoff = Duration::from_millis(5);
        for _ in 1..MAX_ATTEMPTS {
            match f().await {
                Err(e) if is_etxtbsy(&e) => {
                    tokio::time::sleep(backoff).await;
                    backoff = backoff.saturating_mul(2);
                }
                other => return other,
            }
        }
        // Budget exhausted: return the final attempt's result, ETXTBSY or not,
        // so the caller fails loudly rather than looping forever.
        f().await
    }

    /// Whether any error in `err`'s chain is an [`std::io::Error`] carrying
    /// [`ETXTBSY`]. Walks the whole chain because the exec failure is usually
    /// wrapped in caller `.context(..)` by the time it reaches a test.
    fn is_etxtbsy(err: &anyhow::Error) -> bool {
        err.chain().any(|cause| {
            cause
                .downcast_ref::<std::io::Error>()
                .and_then(std::io::Error::raw_os_error)
                == Some(ETXTBSY)
        })
    }

    /// Writes an executable script at `path`, flushes it to disk, and
    /// explicitly drops the writable FD before returning. Setting mode
    /// via `OpenOptions` avoids a second open-for-write that
    /// `chmod`-after-`fs::write` would cause. Pair the exec of the written
    /// shim with [`retry_on_etxtbsy`] to absorb the residual `fork`/`exec`
    /// race.
    pub(crate) fn write_exec_script(path: &Path, script: &str) {
        use std::io::Write;
        use std::os::unix::fs::OpenOptionsExt;
        let mut file = std::fs::OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .mode(0o755)
            .open(path)
            .unwrap();
        file.write_all(script.as_bytes()).unwrap();
        file.sync_all().unwrap();
        drop(file);
    }

    /// Environment variable that makes a shim written by
    /// [`write_warmed_exec_script`] exit immediately; set only for the warm-up
    /// exec.
    const WARMUP_ENV: &str = "OMNI_DEV_SHIM_WARMUP";

    /// Like [`write_exec_script`], but then execs the shim once, to
    /// completion, so the cold-exec cost (page-in plus the macOS
    /// quarantine/Gatekeeper check, 150-600 ms on a loaded machine) is paid
    /// here rather than inside the caller's timed region (#1598).
    ///
    /// `script` must start with a `#!` line. A guard that exits when
    /// [`WARMUP_ENV`] is set is inserted right after it, so the warm-up exec
    /// has no side effects: the body never runs. Use this instead of
    /// widening a timeout when the test asserts *what the shim did* before a
    /// short timeout fired, so the timeout can stay tight and a genuine
    /// regression still surfaces quickly.
    ///
    /// The warm-up exec goes through [`retry_on_etxtbsy`], like any exec of a
    /// freshly-written shim.
    pub(crate) fn write_warmed_exec_script(path: &Path, script: &str) {
        let (shebang, body) = script
            .split_once('\n')
            .expect("a warmed shim script must start with a shebang line");
        assert!(
            shebang.starts_with("#!"),
            "a warmed shim script must start with a shebang line, got {shebang:?}"
        );
        write_exec_script(
            path,
            &format!("{shebang}\n[ -n \"${WARMUP_ENV}\" ] && exit 0\n{body}"),
        );
        retry_on_etxtbsy(|| {
            let status = std::process::Command::new(path)
                .env(WARMUP_ENV, "1")
                .status()?;
            anyhow::ensure!(status.success(), "shim warm-up exited with {status}");
            Ok(())
        })
        .expect("shim warm-up exec");
    }

    #[cfg(test)]
    mod tests {
        use super::*;

        #[test]
        fn warmed_script_exits_on_warmup_without_running_its_body() {
            let tmp = tempfile::TempDir::new().unwrap();
            let marker = tmp.path().join("ran");
            let shim = tmp.path().join("shim");
            write_warmed_exec_script(&shim, &format!("#!/bin/sh\n: > '{}'\n", marker.display()));
            assert!(
                !marker.exists(),
                "the warm-up exec must not run the script body"
            );
        }

        #[test]
        fn warmed_script_runs_its_body_on_a_normal_exec() {
            let tmp = tempfile::TempDir::new().unwrap();
            let marker = tmp.path().join("ran");
            let shim = tmp.path().join("shim");
            write_warmed_exec_script(&shim, &format!("#!/bin/sh\n: > '{}'\n", marker.display()));
            // The guard is keyed on the env var alone, so a plain exec runs it.
            let status =
                retry_on_etxtbsy(|| Ok(std::process::Command::new(&shim).status()?)).unwrap();
            assert!(status.success());
            assert!(marker.exists(), "a normal exec must run the script body");
        }

        #[test]
        #[should_panic(expected = "shebang")]
        fn warmed_script_without_a_shebang_is_rejected() {
            let tmp = tempfile::TempDir::new().unwrap();
            write_warmed_exec_script(&tmp.path().join("shim"), "echo hi\n");
        }

        /// An `ETXTBSY` `io::Error` wrapped in `.context(..)`, matching how the
        /// exec failure reaches a test through a caller's error chain.
        fn etxtbsy_error() -> anyhow::Error {
            anyhow::Error::new(std::io::Error::from_raw_os_error(ETXTBSY))
                .context("failed to run the shim")
        }

        #[test]
        fn is_etxtbsy_sees_a_wrapped_text_file_busy() {
            assert!(is_etxtbsy(&etxtbsy_error()));
        }

        #[test]
        fn is_etxtbsy_rejects_other_errors() {
            // ENOENT is a spawn error too, but not the race we retry.
            let enoent = anyhow::Error::new(std::io::Error::from_raw_os_error(2))
                .context("failed to run the shim");
            assert!(!is_etxtbsy(&enoent));
            assert!(!is_etxtbsy(&anyhow::anyhow!("plain error, no io source")));
        }

        #[test]
        fn retry_on_etxtbsy_succeeds_after_transient_failures() {
            let mut calls = 0;
            let out = retry_on_etxtbsy(|| {
                calls += 1;
                if calls <= 3 {
                    Err(etxtbsy_error())
                } else {
                    Ok(calls)
                }
            })
            .unwrap();
            assert_eq!(out, 4, "should succeed on the 4th attempt");
            assert_eq!(calls, 4);
        }

        #[test]
        fn retry_on_etxtbsy_returns_a_non_etxtbsy_error_without_retrying() {
            let mut calls = 0;
            let result: anyhow::Result<()> = retry_on_etxtbsy(|| {
                calls += 1;
                Err(anyhow::anyhow!("a real failure"))
            });
            assert!(result.is_err());
            assert_eq!(calls, 1, "a non-ETXTBSY error must not be retried");
        }

        #[test]
        fn retry_on_etxtbsy_gives_up_after_the_budget_and_returns_the_last_error() {
            // Persistent ETXTBSY: exhaust the retry budget and surface the final
            // error rather than looping forever. Covers the give-up path.
            let mut calls = 0;
            let result: anyhow::Result<()> = retry_on_etxtbsy(|| {
                calls += 1;
                Err(etxtbsy_error())
            });
            assert!(is_etxtbsy(&result.unwrap_err()));
            assert_eq!(calls, 8, "should try MAX_ATTEMPTS times, then give up");
        }

        #[tokio::test]
        async fn retry_on_etxtbsy_async_succeeds_after_transient_failures() {
            let mut calls = 0;
            let out = retry_on_etxtbsy_async(|| {
                calls += 1;
                let calls = calls;
                async move {
                    if calls <= 3 {
                        Err(etxtbsy_error())
                    } else {
                        Ok(calls)
                    }
                }
            })
            .await
            .unwrap();
            assert_eq!(out, 4, "should succeed on the 4th attempt");
            assert_eq!(calls, 4);
        }

        #[tokio::test]
        async fn retry_on_etxtbsy_async_returns_a_non_etxtbsy_error_without_retrying() {
            let mut calls = 0;
            let result: anyhow::Result<()> = retry_on_etxtbsy_async(|| {
                calls += 1;
                async { Err(anyhow::anyhow!("a real failure")) }
            })
            .await;
            assert!(result.is_err());
            assert_eq!(calls, 1, "a non-ETXTBSY error must not be retried");
        }

        #[tokio::test]
        async fn retry_on_etxtbsy_async_gives_up_after_the_budget_and_returns_the_last_error() {
            let mut calls = 0;
            let result: anyhow::Result<()> = retry_on_etxtbsy_async(|| {
                calls += 1;
                async { Err(etxtbsy_error()) }
            })
            .await;
            assert!(is_etxtbsy(&result.unwrap_err()));
            assert_eq!(calls, 8, "should try MAX_ATTEMPTS times, then give up");
        }
    }
}

pub(crate) mod git_repo {
    //! A throwaway git repository with one commit, for tests that read `HEAD`.
    use std::process::Command;

    /// Creates a repository whose single commit holds `files` (path, bytes).
    ///
    /// Bytes rather than `&str` so a test can commit a non-UTF-8 file.
    pub(crate) fn commit_files(files: &[(&str, &[u8])]) -> tempfile::TempDir {
        let dir = tempfile::tempdir().unwrap();
        let run = |args: &[&str]| {
            let out = Command::new("git")
                .arg("-C")
                .arg(dir.path())
                .args(args)
                .output()
                .unwrap();
            let stderr = String::from_utf8_lossy(&out.stderr);
            assert!(out.status.success(), "{stderr}");
        };
        run(&["init", "-q"]);
        for (path, bytes) in files {
            std::fs::write(dir.path().join(path), bytes).unwrap();
        }
        run(&["add", "."]);
        run(&[
            "-c",
            "user.name=Fixture",
            "-c",
            "user.email=fixture@example.com",
            "commit",
            "-qm",
            "test: fixture",
        ]);
        dir
    }
}