Restructure dev seeder around nested structs and deltas
Prior form scattered runs, jobs, and sh_events into three parallel tuple
arrays with absolute timestamps like `base_ms - 600_000 + 3000`, leaving
the parent/child timing relationships implicit and easy to break. Nesting
jobs/events under their run and storing `started_delta + duration` makes
the structure obvious. Real v7 UUIDs replace the `aaaaaaaa-...`
placeholders.

Assisted-by: Claude Opus 4.7 via Claude Code
change zmumopuutqvuysylpxpsxorpxwwsppwp
commit ff28104cc245082930ab8975aecf3d9b9d5f45e9
author Alpha Chen <alpha@kejadlen.dev>
date
parent 0e3f37a9
diff --git a/quire-server/src/bin/quire/commands/dev.rs b/quire-server/src/bin/quire/commands/dev.rs
index 3ec815e..14a64be 100644
--- a/quire-server/src/bin/quire/commands/dev.rs
+++ b/quire-server/src/bin/quire/commands/dev.rs
@@ -2,15 +2,10 @@
 
 use miette::{Context, IntoDiagnostic, Result};
 use rusqlite::params;
+use uuid::Uuid;
 
 use quire::Quire;
 
-/// Anchor timestamp for seed data: captured at seed time so relative
-/// displays like "3m ago" stay realistic regardless of wall clock.
-fn base_ms() -> i64 {
-    jiff::Timestamp::now().as_millisecond()
-}
-
 /// Seed a tempdir with realistic CI run data and return a `Quire` pointing at it.
 ///
 /// Creates a fresh tempdir under `std::env::temp_dir()`, inserts a fixed corpus
@@ -18,510 +13,445 @@ fn base_ms() -> i64 {
 /// superseded) with matching on-disk log artifacts. Idempotent — same input,
 /// same output.
 pub fn seed() -> Result<Quire> {
-    let dir = tempfile::tempdir()
-        .into_diagnostic()
-        .context("failed to create tempdir")?;
-
-    // Leak the TempDir so it outlives the function. The server will
-    // clean up on shutdown, or the OS will when the process exits.
-    let base_dir = dir.keep();
-    tracing::info!(path = %base_dir.display(), "seeded tempdir");
+    Seeder::new()?.run()
+}
 
-    let quire = Quire::new(base_dir);
+/// One run with its jobs. `pushed_delta_ms` is offset from "now" at seed time;
+/// `started_delta_ms` is offset from `pushed`; `duration_ms` is how long the
+/// run ran after starting.
+struct SeedRun {
+    state: &'static str,
+    sha: &'static str,
+    ref_name: &'static str,
+    pushed_delta_ms: i64,
+    started_delta_ms: Option<i64>,
+    duration_ms: Option<i64>,
+    jobs: Vec<SeedJob>,
+}
 
-    // Create the repos dir + a bare repo so the web view resolves the repo.
-    let bare_repo = quire.repos_dir().join("example.git");
-    fs_err::create_dir_all(&bare_repo)
-        .into_diagnostic()
-        .context("failed to create bare repo dir")?;
-    let db_path = quire.db_path();
+/// `started_delta_ms` is offset from the run's start; `duration_ms` is how long
+/// the job ran (None if still active).
+struct SeedJob {
+    job_id: &'static str,
+    state: &'static str,
+    exit_code: Option<i32>,
+    started_delta_ms: i64,
+    duration_ms: Option<i64>,
+    events: Vec<SeedShEvent>,
+}
 
-    // Open and migrate.
-    let mut db = quire::db::open(&db_path)
-        .into_diagnostic()
-        .context("failed to open database")?;
-    quire::db::migrate(&mut db)
-        .into_diagnostic()
-        .context("failed to run migrations")?;
+/// `started_delta_ms` is offset from the job's start.
+struct SeedShEvent {
+    started_delta_ms: i64,
+    duration_ms: i64,
+    exit_code: i32,
+    cmd: &'static str,
+    log: Option<&'static str>,
+}
 
-    insert_runs(&db)?;
-    insert_jobs(&db)?;
-    insert_sh_events(&db)?;
-    write_log_artifacts(&quire)?;
+struct Seeder {
+    quire: Quire,
+    db: rusqlite::Connection,
+    base_ms: i64,
+    runs: Vec<SeedRun>,
+}
 
-    let run_count: i64 = db
-        .query_row("SELECT count(*) FROM runs", [], |row| row.get(0))
-        .into_diagnostic()?;
+impl Seeder {
+    fn new() -> Result<Self> {
+        let dir = tempfile::tempdir()
+            .into_diagnostic()
+            .context("failed to create tempdir")?;
 
-    tracing::info!(%run_count, "seeded database");
-    Ok(quire)
-}
+        // Leak the TempDir so it outlives the function. The server will
+        // clean up on shutdown, or the OS will when the process exits.
+        let base_dir = dir.keep();
+        tracing::info!(path = %base_dir.display(), "seeded tempdir");
 
-fn insert_runs(db: &rusqlite::Connection) -> Result<()> {
-    let repo = "example.git";
-    let workspace = "/tmp/quire-seed";
+        let quire = Quire::new(base_dir);
 
-    let runs = [
-        // Complete run — all jobs passed.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000001",
-            "complete",
-            "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
-            "refs/heads/main",
-            base_ms(),
-            Some(base_ms() + 1000),
-            Some(base_ms() + 5000),
-        ),
-        // Failed run — one job failed.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000002",
-            "failed",
-            "cafebabecafebabecafebabecafebabecafebabe",
-            "refs/heads/main",
-            base_ms() - 600_000,
-            Some(base_ms() - 600_000 + 1000),
-            Some(base_ms() - 600_000 + 8000),
-        ),
-        // Superseded run — pushed then rebased.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000003",
-            "superseded",
-            "1111111111111111111111111111111111111111",
-            "refs/heads/feature",
-            base_ms() - 1_200_000,
-            Some(base_ms() - 1_200_000 + 1000),
-            Some(base_ms() - 1_200_000 + 2000),
-        ),
-        // Active run — still running.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000004",
-            "active",
-            "2222222222222222222222222222222222222222",
-            "refs/heads/main",
-            base_ms() - 5000,
-            Some(base_ms() - 4000),
-            None,
-        ),
-        // Pending run — queued but not started.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000005",
-            "pending",
-            "3333333333333333333333333333333333333333",
-            "refs/heads/main",
-            base_ms() - 1000,
-            None,
-            None,
-        ),
-        // Complete run on a different branch with multiple jobs.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000006",
-            "complete",
-            "4444444444444444444444444444444444444444",
-            "refs/heads/v2",
-            base_ms() - 3_600_000,
-            Some(base_ms() - 3_600_000 + 2000),
-            Some(base_ms() - 3_600_000 + 12000),
-        ),
-        // Failed run — orphaned (container died).
-        (
-            "aaaaaaaa-0000-0000-0000-000000000007",
-            "failed",
-            "5555555555555555555555555555555555555555",
-            "refs/heads/main",
-            base_ms() - 7_200_000,
-            Some(base_ms() - 7_200_000 + 1000),
-            Some(base_ms() - 7_200_000 + 60000),
-        ),
-    ];
+        // Create the repos dir + a bare repo so the web view resolves the repo.
+        let bare_repo = quire.repos_dir().join("example.git");
+        fs_err::create_dir_all(&bare_repo)
+            .into_diagnostic()
+            .context("failed to create bare repo dir")?;
 
-    let mut stmt = db.prepare(
-        "INSERT INTO runs (id, repo, ref_name, sha, pushed_at_ms, state, failure_kind,
-                           queued_at_ms, started_at_ms, finished_at_ms,
-                           container_id, image_tag, build_started_at_ms, build_finished_at_ms,
-                           container_started_at_ms, container_stopped_at_ms, workspace_path)
-         VALUES (?1, ?2, ?3, ?4, ?5, ?6, NULL, ?7, ?8, ?9, NULL, NULL, NULL, NULL, NULL, NULL, ?10)"
-    ).into_diagnostic()?;
+        let mut db = quire::db::open(&quire.db_path())
+            .into_diagnostic()
+            .context("failed to open database")?;
+        quire::db::migrate(&mut db)
+            .into_diagnostic()
+            .context("failed to run migrations")?;
 
-    for (id, state, sha, ref_name, pushed_at_ms, started_at_ms, finished_at_ms) in &runs {
-        stmt.execute(params![
-            id,
-            repo,
-            ref_name,
-            sha,
-            pushed_at_ms,
-            state,
-            pushed_at_ms, // queued_at_ms = pushed_at_ms
-            started_at_ms,
-            finished_at_ms,
-            workspace,
-        ])
-        .into_diagnostic()?;
+        Ok(Self {
+            quire,
+            db,
+            base_ms: jiff::Timestamp::now().as_millisecond(),
+            runs: build_runs(),
+        })
     }
 
-    Ok(())
-}
-
-fn insert_jobs(db: &rusqlite::Connection) -> Result<()> {
-    let jobs = [
-        // Run 1 (complete): two passing jobs.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000001",
-            "build",
-            "complete",
-            Some(0),
-            base_ms() + 1000,
-            Some(base_ms() + 3000),
-        ),
-        (
-            "aaaaaaaa-0000-0000-0000-000000000001",
-            "test",
-            "complete",
-            Some(0),
-            base_ms() + 3000,
-            Some(base_ms() + 5000),
-        ),
-        // Run 2 (failed): one pass, one fail.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000002",
-            "build",
-            "complete",
-            Some(0),
-            base_ms() - 600_000 + 1000,
-            Some(base_ms() - 600_000 + 3000),
-        ),
-        (
-            "aaaaaaaa-0000-0000-0000-000000000002",
-            "test",
-            "failed",
-            Some(1),
-            base_ms() - 600_000 + 3000,
-            Some(base_ms() - 600_000 + 8000),
-        ),
-        // Run 3 (superseded): one job started then cancelled.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000003",
-            "build",
-            "complete",
-            Some(0),
-            base_ms() - 1_200_000 + 1000,
-            Some(base_ms() - 1_200_000 + 2000),
-        ),
-        // Run 4 (active): build running.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000004",
-            "build",
-            "active",
-            None,
-            base_ms() - 4000,
-            None,
-        ),
-        // Run 5 (pending): nothing started.
-        // (no jobs yet)
-        // Run 6 (complete, multi-job): lint + build + test.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000006",
-            "lint",
-            "complete",
-            Some(0),
-            base_ms() - 3_600_000 + 2000,
-            Some(base_ms() - 3_600_000 + 4000),
-        ),
-        (
-            "aaaaaaaa-0000-0000-0000-000000000006",
-            "build",
-            "complete",
-            Some(0),
-            base_ms() - 3_600_000 + 4000,
-            Some(base_ms() - 3_600_000 + 8000),
-        ),
-        (
-            "aaaaaaaa-0000-0000-0000-000000000006",
-            "test",
-            "complete",
-            Some(0),
-            base_ms() - 3_600_000 + 8000,
-            Some(base_ms() - 3_600_000 + 12000),
-        ),
-        // Run 7 (failed, orphaned): build passed, test was running when container died.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000007",
-            "build",
-            "complete",
-            Some(0),
-            base_ms() - 7_200_000 + 1000,
-            Some(base_ms() - 7_200_000 + 4000),
-        ),
-        (
-            "aaaaaaaa-0000-0000-0000-000000000007",
-            "test",
-            "failed",
-            Some(137),
-            base_ms() - 7_200_000 + 4000,
-            Some(base_ms() - 7_200_000 + 60000),
-        ),
-    ];
+    fn run(self) -> Result<Quire> {
+        for run in &self.runs {
+            self.insert_run(run)?;
+        }
 
-    let mut stmt = db
-        .prepare(
-            "INSERT INTO jobs (run_id, job_id, state, exit_code, started_at_ms, finished_at_ms)
-         VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
-        )
-        .into_diagnostic()?;
+        let run_count: i64 = self
+            .db
+            .query_row("SELECT count(*) FROM runs", [], |row| row.get(0))
+            .into_diagnostic()?;
 
-    for (run_id, job_id, state, exit_code, started_at_ms, finished_at_ms) in &jobs {
-        stmt.execute(params![
-            run_id,
-            job_id,
-            state,
-            exit_code,
-            started_at_ms,
-            finished_at_ms
-        ])
-        .into_diagnostic()?;
+        tracing::info!(%run_count, "seeded database");
+        Ok(self.quire)
     }
 
-    Ok(())
-}
-
-fn insert_sh_events(db: &rusqlite::Connection) -> Result<()> {
-    let events = [
-        // Run 1, build job.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000001",
-            "build",
-            base_ms() + 1000,
-            base_ms() + 2500,
-            0,
-            "cargo build --release",
-        ),
-        (
-            "aaaaaaaa-0000-0000-0000-000000000001",
-            "build",
-            base_ms() + 2500,
-            base_ms() + 3000,
-            0,
-            "cargo clippy -- -D warnings",
-        ),
-        // Run 1, test job.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000001",
-            "test",
-            base_ms() + 3000,
-            base_ms() + 4800,
-            0,
-            "cargo test --workspace",
-        ),
-        // Run 2, build job.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000002",
-            "build",
-            base_ms() - 600_000 + 1000,
-            base_ms() - 600_000 + 3000,
-            0,
-            "cargo build --release",
-        ),
-        // Run 2, test job — fails.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000002",
-            "test",
-            base_ms() - 600_000 + 3000,
-            base_ms() - 600_000 + 5000,
-            0,
-            "cargo test --workspace",
-        ),
-        (
-            "aaaaaaaa-0000-0000-0000-000000000002",
-            "test",
-            base_ms() - 600_000 + 5000,
-            base_ms() - 600_000 + 8000,
-            1,
-            "cargo test -- --ignored",
-        ),
-        // Run 3, build job.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000003",
-            "build",
-            base_ms() - 1_200_000 + 1000,
-            base_ms() - 1_200_000 + 2000,
-            0,
-            "cargo build --release",
-        ),
-        // Run 4, active build.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000004",
-            "build",
-            base_ms() - 4000,
-            base_ms() - 2000,
-            0,
-            "cargo build --release",
-        ),
-        (
-            "aaaaaaaa-0000-0000-0000-000000000004",
-            "build",
-            base_ms() - 2000,
-            base_ms() - 1000,
-            0,
-            "cargo clippy -- -D warnings",
-        ),
-        // Run 6, lint job.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000006",
-            "lint",
-            base_ms() - 3_600_000 + 2000,
-            base_ms() - 3_600_000 + 4000,
-            0,
-            "cargo fmt --check",
-        ),
-        // Run 6, build job.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000006",
-            "build",
-            base_ms() - 3_600_000 + 4000,
-            base_ms() - 3_600_000 + 8000,
-            0,
-            "cargo build --release",
-        ),
-        // Run 6, test job.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000006",
-            "test",
-            base_ms() - 3_600_000 + 8000,
-            base_ms() - 3_600_000 + 12000,
-            0,
-            "cargo test --workspace",
-        ),
-        // Run 7, build job.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000007",
-            "build",
-            base_ms() - 7_200_000 + 1000,
-            base_ms() - 7_200_000 + 4000,
-            0,
-            "cargo build --release",
-        ),
-        // Run 7, test job — container died mid-run.
-        (
-            "aaaaaaaa-0000-0000-0000-000000000007",
-            "test",
-            base_ms() - 7_200_000 + 4000,
-            base_ms() - 7_200_000 + 60000,
-            137,
-            "cargo test --workspace",
-        ),
-    ];
+    fn insert_run(&self, run: &SeedRun) -> Result<()> {
+        let repo = "example.git";
+        let workspace = "/tmp/quire-seed";
+        // v7 UUIDs so the IDs are time-sortable in addition to unique.
+        let run_id = Uuid::now_v7().to_string();
 
-    let mut stmt = db
-        .prepare(
-            "INSERT INTO sh_events (run_id, job_id, started_at_ms, finished_at_ms, exit_code, cmd)
-         VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
-        )
-        .into_diagnostic()?;
+        let pushed_at = self.base_ms + run.pushed_delta_ms;
+        let started_at = run.started_delta_ms.map(|d| pushed_at + d);
+        let finished_at = started_at.zip(run.duration_ms).map(|(s, d)| s + d);
 
-    for (run_id, job_id, started_at_ms, finished_at_ms, exit_code, cmd) in &events {
-        stmt.execute(params![
-            run_id,
-            job_id,
-            started_at_ms,
-            finished_at_ms,
-            exit_code,
-            cmd
-        ])
-        .into_diagnostic()?;
-    }
+        self.db.execute(
+            "INSERT INTO runs (id, repo, ref_name, sha, pushed_at_ms, state, failure_kind,
+                               queued_at_ms, started_at_ms, finished_at_ms,
+                               container_id, image_tag, build_started_at_ms, build_finished_at_ms,
+                               container_started_at_ms, container_stopped_at_ms, workspace_path)
+             VALUES (?1, ?2, ?3, ?4, ?5, ?6, NULL, ?7, ?8, ?9, NULL, NULL, NULL, NULL, NULL, NULL, ?10)",
+            params![
+                run_id,
+                repo,
+                run.ref_name,
+                run.sha,
+                pushed_at,
+                run.state,
+                pushed_at, // queued_at_ms = pushed_at_ms
+                started_at,
+                finished_at,
+                workspace,
+            ],
+        ).into_diagnostic()?;
 
-    Ok(())
-}
+        let Some(run_started_at) = started_at else {
+            return Ok(()); // pending run; no jobs to insert.
+        };
 
-fn write_log_artifacts(quire: &Quire) -> Result<()> {
-    let b = quire.base_dir().join("runs").join("example.git");
+        let logs_base = self
+            .quire
+            .base_dir()
+            .join("runs")
+            .join("example.git")
+            .join(&run_id);
 
-    // Run 1 — complete, clean build output.
-    write_sh_log(
-        &b,
-        run_id(1),
-        "build",
-        1,
-        include_str!("fixtures/run-1-build-1.log"),
-    );
-    write_sh_log(
-        &b,
-        run_id(1),
-        "build",
-        2,
-        include_str!("fixtures/run-1-build-2.log"),
-    );
-    write_sh_log(
-        &b,
-        run_id(1),
-        "test",
-        1,
-        include_str!("fixtures/run-1-test-1.log"),
-    );
+        for job in &run.jobs {
+            let job_started_at = run_started_at + job.started_delta_ms;
+            let job_finished_at = job.duration_ms.map(|d| job_started_at + d);
 
-    // Run 2 — failed, test output with failures.
-    write_sh_log(
-        &b,
-        run_id(2),
-        "build",
-        1,
-        include_str!("fixtures/run-2-build-1.log"),
-    );
-    write_sh_log(
-        &b,
-        run_id(2),
-        "test",
-        1,
-        include_str!("fixtures/run-2-test-1.log"),
-    );
-    write_sh_log(
-        &b,
-        run_id(2),
-        "test",
-        2,
-        include_str!("fixtures/run-2-test-2.log"),
-    );
+            self.db
+                .execute(
+                    "INSERT INTO jobs (run_id, job_id, state, exit_code, started_at_ms, finished_at_ms)
+                     VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
+                    params![
+                        run_id,
+                        job.job_id,
+                        job.state,
+                        job.exit_code,
+                        job_started_at,
+                        job_finished_at,
+                    ],
+                )
+                .into_diagnostic()?;
 
-    // Run 7 — orphaned, long output that was interrupted.
-    write_sh_log(
-        &b,
-        run_id(7),
-        "build",
-        1,
-        include_str!("fixtures/run-7-build-1.log"),
-    );
-    write_sh_log(
-        &b,
-        run_id(7),
-        "test",
-        1,
-        include_str!("fixtures/run-7-test-1.log"),
-    );
+            for (idx, event) in job.events.iter().enumerate() {
+                let started_at = job_started_at + event.started_delta_ms;
+                let finished_at = started_at + event.duration_ms;
+                self.db
+                    .execute(
+                        "INSERT INTO sh_events (run_id, job_id, started_at_ms, finished_at_ms, exit_code, cmd)
+                         VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
+                        params![
+                            run_id,
+                            job.job_id,
+                            started_at,
+                            finished_at,
+                            event.exit_code,
+                            event.cmd,
+                        ],
+                    )
+                    .into_diagnostic()?;
 
-    Ok(())
-}
+                if let Some(content) = event.log {
+                    let dir = logs_base.join("jobs").join(job.job_id);
+                    fs_err::create_dir_all(&dir)
+                        .into_diagnostic()
+                        .context("failed to create log dir")?;
+                    fs_err::write(dir.join(format!("sh-{}.log", idx + 1)), content)
+                        .into_diagnostic()
+                        .context("failed to write log")?;
+                }
+            }
+        }
 
-/// Short-hand for the seed run UUIDs (1-indexed).
-fn run_id(n: u8) -> &'static str {
-    match n {
-        1 => "aaaaaaaa-0000-0000-0000-000000000001",
-        2 => "aaaaaaaa-0000-0000-0000-000000000002",
-        3 => "aaaaaaaa-0000-0000-0000-000000000003",
-        4 => "aaaaaaaa-0000-0000-0000-000000000004",
-        5 => "aaaaaaaa-0000-0000-0000-000000000005",
-        6 => "aaaaaaaa-0000-0000-0000-000000000006",
-        7 => "aaaaaaaa-0000-0000-0000-000000000007",
-        _ => panic!("no seed run with index {n}"),
+        Ok(())
     }
 }
 
-fn write_sh_log(
-    runs_base: &std::path::Path,
-    run_id: &str,
-    job_id: &str,
-    sh_n: usize,
-    content: &str,
-) {
-    let dir = runs_base.join(run_id).join("jobs").join(job_id);
-    fs_err::create_dir_all(&dir).expect("failed to create log dir");
-    fs_err::write(dir.join(format!("sh-{sh_n}.log")), content).expect("failed to write log");
+fn build_runs() -> Vec<SeedRun> {
+    vec![
+        // Run 1 — complete, all jobs passed.
+        SeedRun {
+            state: "complete",
+            sha: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
+            ref_name: "refs/heads/main",
+            pushed_delta_ms: 0,
+            started_delta_ms: Some(1000),
+            duration_ms: Some(4000),
+            jobs: vec![
+                SeedJob {
+                    job_id: "build",
+                    state: "complete",
+                    exit_code: Some(0),
+                    started_delta_ms: 0,
+                    duration_ms: Some(2000),
+                    events: vec![
+                        SeedShEvent {
+                            started_delta_ms: 0,
+                            duration_ms: 1500,
+                            exit_code: 0,
+                            cmd: "cargo build --release",
+                            log: Some(include_str!("fixtures/run-1-build-1.log")),
+                        },
+                        SeedShEvent {
+                            started_delta_ms: 1500,
+                            duration_ms: 500,
+                            exit_code: 0,
+                            cmd: "cargo clippy -- -D warnings",
+                            log: Some(include_str!("fixtures/run-1-build-2.log")),
+                        },
+                    ],
+                },
+                SeedJob {
+                    job_id: "test",
+                    state: "complete",
+                    exit_code: Some(0),
+                    started_delta_ms: 2000,
+                    duration_ms: Some(2000),
+                    events: vec![SeedShEvent {
+                        started_delta_ms: 0,
+                        duration_ms: 1800,
+                        exit_code: 0,
+                        cmd: "cargo test --workspace",
+                        log: Some(include_str!("fixtures/run-1-test-1.log")),
+                    }],
+                },
+            ],
+        },
+        // Run 2 — failed, one job failed.
+        SeedRun {
+            state: "failed",
+            sha: "cafebabecafebabecafebabecafebabecafebabe",
+            ref_name: "refs/heads/main",
+            pushed_delta_ms: -600_000,
+            started_delta_ms: Some(1000),
+            duration_ms: Some(7000),
+            jobs: vec![
+                SeedJob {
+                    job_id: "build",
+                    state: "complete",
+                    exit_code: Some(0),
+                    started_delta_ms: 0,
+                    duration_ms: Some(2000),
+                    events: vec![SeedShEvent {
+                        started_delta_ms: 0,
+                        duration_ms: 2000,
+                        exit_code: 0,
+                        cmd: "cargo build --release",
+                        log: Some(include_str!("fixtures/run-2-build-1.log")),
+                    }],
+                },
+                SeedJob {
+                    job_id: "test",
+                    state: "failed",
+                    exit_code: Some(1),
+                    started_delta_ms: 2000,
+                    duration_ms: Some(5000),
+                    events: vec![
+                        SeedShEvent {
+                            started_delta_ms: 0,
+                            duration_ms: 2000,
+                            exit_code: 0,
+                            cmd: "cargo test --workspace",
+                            log: Some(include_str!("fixtures/run-2-test-1.log")),
+                        },
+                        SeedShEvent {
+                            started_delta_ms: 2000,
+                            duration_ms: 3000,
+                            exit_code: 1,
+                            cmd: "cargo test -- --ignored",
+                            log: Some(include_str!("fixtures/run-2-test-2.log")),
+                        },
+                    ],
+                },
+            ],
+        },
+        // Run 3 — superseded, pushed then rebased.
+        SeedRun {
+            state: "superseded",
+            sha: "1111111111111111111111111111111111111111",
+            ref_name: "refs/heads/feature",
+            pushed_delta_ms: -1_200_000,
+            started_delta_ms: Some(1000),
+            duration_ms: Some(1000),
+            jobs: vec![SeedJob {
+                job_id: "build",
+                state: "complete",
+                exit_code: Some(0),
+                started_delta_ms: 0,
+                duration_ms: Some(1000),
+                events: vec![SeedShEvent {
+                    started_delta_ms: 0,
+                    duration_ms: 1000,
+                    exit_code: 0,
+                    cmd: "cargo build --release",
+                    log: None,
+                }],
+            }],
+        },
+        // Run 4 — active, still running.
+        SeedRun {
+            state: "active",
+            sha: "2222222222222222222222222222222222222222",
+            ref_name: "refs/heads/main",
+            pushed_delta_ms: -5000,
+            started_delta_ms: Some(1000),
+            duration_ms: None,
+            jobs: vec![SeedJob {
+                job_id: "build",
+                state: "active",
+                exit_code: None,
+                started_delta_ms: 0,
+                duration_ms: None,
+                events: vec![
+                    SeedShEvent {
+                        started_delta_ms: 0,
+                        duration_ms: 2000,
+                        exit_code: 0,
+                        cmd: "cargo build --release",
+                        log: None,
+                    },
+                    SeedShEvent {
+                        started_delta_ms: 2000,
+                        duration_ms: 1000,
+                        exit_code: 0,
+                        cmd: "cargo clippy -- -D warnings",
+                        log: None,
+                    },
+                ],
+            }],
+        },
+        // Run 5 — pending, queued but not started.
+        SeedRun {
+            state: "pending",
+            sha: "3333333333333333333333333333333333333333",
+            ref_name: "refs/heads/main",
+            pushed_delta_ms: -1000,
+            started_delta_ms: None,
+            duration_ms: None,
+            jobs: vec![],
+        },
+        // Run 6 — complete, multi-job: lint + build + test.
+        SeedRun {
+            state: "complete",
+            sha: "4444444444444444444444444444444444444444",
+            ref_name: "refs/heads/v2",
+            pushed_delta_ms: -3_600_000,
+            started_delta_ms: Some(2000),
+            duration_ms: Some(10_000),
+            jobs: vec![
+                SeedJob {
+                    job_id: "lint",
+                    state: "complete",
+                    exit_code: Some(0),
+                    started_delta_ms: 0,
+                    duration_ms: Some(2000),
+                    events: vec![SeedShEvent {
+                        started_delta_ms: 0,
+                        duration_ms: 2000,
+                        exit_code: 0,
+                        cmd: "cargo fmt --check",
+                        log: None,
+                    }],
+                },
+                SeedJob {
+                    job_id: "build",
+                    state: "complete",
+                    exit_code: Some(0),
+                    started_delta_ms: 2000,
+                    duration_ms: Some(4000),
+                    events: vec![SeedShEvent {
+                        started_delta_ms: 0,
+                        duration_ms: 4000,
+                        exit_code: 0,
+                        cmd: "cargo build --release",
+                        log: None,
+                    }],
+                },
+                SeedJob {
+                    job_id: "test",
+                    state: "complete",
+                    exit_code: Some(0),
+                    started_delta_ms: 6000,
+                    duration_ms: Some(4000),
+                    events: vec![SeedShEvent {
+                        started_delta_ms: 0,
+                        duration_ms: 4000,
+                        exit_code: 0,
+                        cmd: "cargo test --workspace",
+                        log: None,
+                    }],
+                },
+            ],
+        },
+        // Run 7 — failed, orphaned (container died mid-run).
+        SeedRun {
+            state: "failed",
+            sha: "5555555555555555555555555555555555555555",
+            ref_name: "refs/heads/main",
+            pushed_delta_ms: -7_200_000,
+            started_delta_ms: Some(1000),
+            duration_ms: Some(59_000),
+            jobs: vec![
+                SeedJob {
+                    job_id: "build",
+                    state: "complete",
+                    exit_code: Some(0),
+                    started_delta_ms: 0,
+                    duration_ms: Some(3000),
+                    events: vec![SeedShEvent {
+                        started_delta_ms: 0,
+                        duration_ms: 3000,
+                        exit_code: 0,
+                        cmd: "cargo build --release",
+                        log: Some(include_str!("fixtures/run-7-build-1.log")),
+                    }],
+                },
+                SeedJob {
+                    job_id: "test",
+                    state: "failed",
+                    exit_code: Some(137),
+                    started_delta_ms: 3000,
+                    duration_ms: Some(56_000),
+                    events: vec![SeedShEvent {
+                        started_delta_ms: 0,
+                        duration_ms: 56_000,
+                        exit_code: 137,
+                        cmd: "cargo test --workspace",
+                        log: Some(include_str!("fixtures/run-7-test-1.log")),
+                    }],
+                },
+            ],
+        },
+    ]
 }