CI: FAILURE radicle-artifact

Table of Contents

Run log

Plan, inside VM without network

plan: Executor starts
After 0.00 seconds at 2026-04-28 10:45:29ZProgram: ambient-execute-plan
Version: 0.14.0@c37ec71
plan: Runnable plan
After 0.00 seconds at 2026-04-28 10:45:29Z
steps:
- action: mkdir
  pathname: /ci
- action: mkdir
  pathname: /ci/artifacts
- action: tar_extract
  archive: /dev/vdc
  directory: /ci/src
- action: tar_extract
  archive: /dev/vdf
  directory: /ci/deps
- action: tar_extract
  archive: /dev/vde
  directory: /ci/cache
- action: shell
  shell: ln -sf /ci /workspace
- action: shell
  shell: git config --global user.name 'Ambient CI'
- action: shell
  shell: git config --global user.email ambient@example.com
- action: cargo_fmt
- action: cargo_clippy
- action: cargo_test
- action: tar_create
  archive: /dev/vde
  directory: /ci/cache
- action: tar_create
  archive: /dev/vdd
  directory: /ci/artifacts
executor_drive: /dev/vdb
source_drive: /dev/vdc
artifact_drive: /dev/vdd
cache_drive: /dev/vde
deps_drive: /dev/vdf
workspace_dir: /ci
source_dir: /ci/src
deps_dir: /ci/deps
cache_dir: /ci/cache
artifacts_dir: /ci/artifacts
envs: {}
plan: Successful action mkdir: /ci
After 0.02 seconds at 2026-04-28 10:45:29Z
  • plan: Start action mkdir: /ci
    After 0.00 seconds at 2026-04-28 10:45:29Z
    Mkdir(
        Mkdir {
            pathname: "/ci",
        },
    )
  • plan: Action succeeded mkdir: /ci
    After 0.00 seconds at 2026-04-28 10:45:29Z
    Mkdir(
        Mkdir {
            pathname: "/ci",
        },
    )
plan: Successful action mkdir: /ci/artifacts
After 0.03 seconds at 2026-04-28 10:45:29Z
  • plan: Start action mkdir: /ci/artifacts
    After 0.00 seconds at 2026-04-28 10:45:29Z
    Mkdir(
        Mkdir {
            pathname: "/ci/artifacts",
        },
    )
  • plan: Action succeeded mkdir: /ci/artifacts
    After 0.00 seconds at 2026-04-28 10:45:29Z
    Mkdir(
        Mkdir {
            pathname: "/ci/artifacts",
        },
    )
plan: Successful action tar_extract
After 0.03 seconds at 2026-04-28 10:45:29Z
  • plan: Start action tar_extract
    After 0.00 seconds at 2026-04-28 10:45:29Z
    TarExtract(
        TarExtract {
            archive: "/dev/vdc",
            directory: "/ci/src",
        },
    )
  • plan: Action succeeded tar_extract
    After 0.00 seconds at 2026-04-28 10:45:29Z
    TarExtract(
        TarExtract {
            archive: "/dev/vdc",
            directory: "/ci/src",
        },
    )
plan: Successful action tar_extract
After 0.05 seconds at 2026-04-28 10:45:29Z
  • plan: Start action tar_extract
    After 0.00 seconds at 2026-04-28 10:45:29Z
    TarExtract(
        TarExtract {
            archive: "/dev/vdf",
            directory: "/ci/deps",
        },
    )
  • plan: Action succeeded tar_extract
    After 0.00 seconds at 2026-04-28 10:45:32Z
    TarExtract(
        TarExtract {
            archive: "/dev/vdf",
            directory: "/ci/deps",
        },
    )
plan: Successful action tar_extract
After 2.71 seconds at 2026-04-28 10:45:32Z
  • plan: Start action tar_extract
    After 0.00 seconds at 2026-04-28 10:45:32Z
    TarExtract(
        TarExtract {
            archive: "/dev/vde",
            directory: "/ci/cache",
        },
    )
  • plan: Action succeeded tar_extract
    After 0.00 seconds at 2026-04-28 10:45:37Z
    TarExtract(
        TarExtract {
            archive: "/dev/vde",
            directory: "/ci/cache",
        },
    )
plan: Successful action shell: ln -sf /ci /workspace
After 8.05 seconds at 2026-04-28 10:45:37Z
  • plan: Start action shell: ln -sf /ci /workspace
    After 0.00 seconds at 2026-04-28 10:45:37Z
    Shell(
        Shell {
            shell: "ln -sf /ci /workspace",
        },
    )
  • plan: Start program bash
    After 0.00 seconds at 2026-04-28 10:45:37Z
    • bash
    • -c
    • set -xeuo pipefail ln -sf /ci /workspace
  • plan: Program succeeded
    After 0.00 seconds at 2026-04-28 10:45:37Z
    Exit code: 0
    Stderr:
    + ln -sf /ci /workspace
    
  • plan: Action succeeded shell: ln -sf /ci /workspace
    After 0.00 seconds at 2026-04-28 10:45:37Z
    Shell(
        Shell {
            shell: "ln -sf /ci /workspace",
        },
    )
plan: Successful action shell: git config --global user.name 'Ambient CI'
After 8.06 seconds at 2026-04-28 10:45:37Z
  • plan: Start action shell: git config --global user.name 'Ambient CI'
    After 0.00 seconds at 2026-04-28 10:45:37Z
    Shell(
        Shell {
            shell: "git config --global user.name 'Ambient CI'",
        },
    )
  • plan: Start program bash
    After 0.00 seconds at 2026-04-28 10:45:37Z
    • bash
    • -c
    • set -xeuo pipefail git config --global user.name 'Ambient CI'
  • plan: Program succeeded
    After 0.00 seconds at 2026-04-28 10:45:37Z
    Exit code: 0
    Stderr:
    + git config --global user.name 'Ambient CI'
    
  • plan: Action succeeded shell: git config --global user.name 'Ambient CI'
    After 0.00 seconds at 2026-04-28 10:45:37Z
    Shell(
        Shell {
            shell: "git config --global user.name 'Ambient CI'",
        },
    )
plan: Successful action shell: git config --global user.email ambient@example.com
After 8.08 seconds at 2026-04-28 10:45:37Z
  • plan: Start action shell: git config --global user.email ambient@example.com
    After 0.00 seconds at 2026-04-28 10:45:37Z
    Shell(
        Shell {
            shell: "git config --global user.email ambient@example.com",
        },
    )
  • plan: Start program bash
    After 0.00 seconds at 2026-04-28 10:45:37Z
    • bash
    • -c
    • set -xeuo pipefail git config --global user.email ambient@example.com
  • plan: Program succeeded
    After 0.00 seconds at 2026-04-28 10:45:37Z
    Exit code: 0
    Stderr:
    + git config --global user.email ambient@example.com
    
  • plan: Action succeeded shell: git config --global user.email ambient@example.com
    After 0.00 seconds at 2026-04-28 10:45:37Z
    Shell(
        Shell {
            shell: "git config --global user.email ambient@example.com",
        },
    )
plan: Start action cargo_fmt
After 8.09 seconds at 2026-04-28 10:45:37Z
CargoFmt(
    CargoFmt,
)
plan: Start program cargo
After 8.09 seconds at 2026-04-28 10:45:37Z
  • cargo
  • --version
plan: Program succeeded
After 8.16 seconds at 2026-04-28 10:45:37Z
Exit code: 0
Stdout:
cargo 1.94.0 (85eff7c80 2026-01-15)
plan: Start program cargo
After 8.16 seconds at 2026-04-28 10:45:37Z
  • cargo
  • clippy
  • --version
plan: Program succeeded
After 8.26 seconds at 2026-04-28 10:45:37Z
Exit code: 0
Stdout:
clippy 0.1.94 (4a4ef493e3 2026-03-02)
plan: Start program rustc
After 8.26 seconds at 2026-04-28 10:45:37Z
  • rustc
  • --version
plan: Program succeeded
After 8.31 seconds at 2026-04-28 10:45:37Z
Exit code: 0
Stdout:
rustc 1.94.0 (4a4ef493e 2026-03-02)
plan: Start program cargo
After 8.31 seconds at 2026-04-28 10:45:37Z
  • cargo
  • fmt
  • --check
plan: ERROR: Program failed
After 8.52 seconds at 2026-04-28 10:45:37Z
Exit code: 1
Stdout:
Diff in /ci/src/src/bin/rad-artifact.rs:175:
             let signer = profile.signer().map_err(error::Signer)?;
             match loc.command {
                 LocationCommand::Add(cmd) => {
-                    location_add(
-                        cmd,
-                        args.no_input,
-                        &mut releases,
-                        &repo,
-                        &profile,
-                        &signer,
-                    )?;
+                    location_add(cmd, args.no_input, &mut releases, &repo, &profile, &signer)?;
                 }
                 LocationCommand::Remove(cmd) => {
-                    location_remove(
-                        cmd,
-                        args.no_input,
-                        &mut releases,
-                        &repo,
-                        &profile,
-                        &signer,
-                    )?;
+                    location_remove(cmd, args.no_input, &mut releases, &repo, &profile, &signer)?;
                 }
             }
             if !args.no_sync {
Diff in /ci/src/src/bin/rad-artifact.rs:201:
         }
         Command::Attest(cmd) => {
             let signer = profile.signer().map_err(error::Signer)?;
-            attest_artifact(
-                cmd,
-                args.no_input,
-                &mut releases,
-                &repo,
-                &profile,
-                &signer,
-            )?;
+            attest_artifact(cmd, args.no_input, &mut releases, &repo, &profile, &signer)?;
             if !args.no_sync {
                 announce(&profile, repo.id)?;
             }
Diff in /ci/src/src/bin/rad-artifact.rs:215:
         }
         Command::Redact(cmd) => {
             let signer = profile.signer().map_err(error::Signer)?;
-            redact_artifact(
-                cmd,
-                args.no_input,
-                &mut releases,
-                &repo,
-                &profile,
-                &signer,
-            )?;
+            redact_artifact(cmd, args.no_input, &mut releases, &repo, &profile, &signer)?;
             if !args.no_sync {
                 announce(&profile, repo.id)?;
             }
Diff in /ci/src/src/bin/rad-artifact.rs:441:
     let (id, cid) = match (release, revision, cid) {
         (Some(_), _, Some(cid)) | (_, Some(_), Some(cid)) => {
             let id = resolve_target_release(
-                release,
-                revision,
-                releases,
-                repo,
-                &delegates,
-                no_input,
-                aliases,
+                release, revision, releases, repo, &delegates, no_input, aliases,
             )?;
             (id, cid)
         }
Diff in /ci/src/src/bin/rad-artifact.rs:493:
     let (id, cid) = match (release, revision, cid) {
         (Some(_), _, Some(cid)) | (_, Some(_), Some(cid)) => {
             let id = resolve_target_release(
-                release,
-                revision,
-                releases,
-                repo,
-                &delegates,
-                no_input,
-                aliases,
+                release, revision, releases, repo, &delegates, no_input, aliases,
             )?;
             (id, cid)
         }
Diff in /ci/src/src/bin/rad-artifact.rs:603:
                 let id = parse_release_id(s, repo)?;
                 let r = releases
                     .get(&id)
-                    .map_err(|err| error::Find::LookupId { release_id: id, err })?
+                    .map_err(|err| error::Find::LookupId {
+                        release_id: id,
+                        err,
+                    })?
                     .ok_or(error::Find::NoReleaseId(id))?;
                 vec![(id, r)]
             }
Diff in /ci/src/src/bin/rad-artifact.rs:628:
         all_authors,
         local: Some(local),
     };
-    let shown =
-        display::Releases::new(candidates.into_iter(), aliases, filters, true, repo, repo);
+    let shown = display::Releases::new(candidates.into_iter(), aliases, filters, true, repo, repo);
     if use_pretty(pretty, json) {
         print!("{}", shown.pretty(verbose));
     } else {
Diff in /ci/src/src/bin/rad-artifact.rs:1007:
         repo: &Repository,
         aliases: &impl AliasStore,
     ) -> Result<ReleaseId, String> {
-        select_release(candidates, None, repo, aliases)?
-            .ok_or_else(|| "no release selected".into())
+        select_release(candidates, None, repo, aliases)?.ok_or_else(|| "no release selected".into())
     }
 
     /// Show a multi-release picker. Returns `Some(id)` when the user
Diff in /ci/src/src/bin/rad-artifact.rs:1295:
                     name, commit_oid, ..
                 } => {
                     let short = &commit_oid.to_string()[..7];
-                    let title =
-                        display::CommitTitle::title(repo, commit_oid).unwrap_or_default();
+                    let title = display::CommitTitle::title(repo, commit_oid).unwrap_or_default();
                     format!("{name} -> {short}  {title}")
                 }
                 Entry::Commit { oid } => {
Diff in /ci/src/src/bin/rad-artifact.rs:1422:
             match releases.get(&id) {
                 Ok(Some(_)) => Ok(id),
                 Ok(None) => Err(error::Find::NoReleaseId(id).into()),
-                Err(err) => Err(error::Find::LookupId { release_id: id, err }.into()),
+                Err(err) => Err(error::Find::LookupId {
+                    release_id: id,
+                    err,
+                }
+                .into()),
             }
         }
         (None, Some(rev)) => {
Diff in /ci/src/src/bin/rad-artifact.rs:1450:
 /// `git revparse_single` accepts short OIDs, full OIDs, and any other
 /// ref name that points at the COB.
 fn parse_release_id(s: &str, repo: &Repository) -> Result<ReleaseId, error::Resolve> {
-    let object = repo.raw().revparse_single(s).map_err(|err| error::Resolve {
-        revision: s.to_owned(),
-        err,
-    })?;
+    let object = repo
+        .raw()
+        .revparse_single(s)
+        .map_err(|err| error::Resolve {
+            revision: s.to_owned(),
+            err,
+        })?;
     Ok(cob::ObjectId::from(object.id()).into())
 }
 
Diff in /ci/src/src/bin/rad-artifact.rs:1467:
     })?;
     if object.kind() == Some(ObjectType::Tag) {
         let tag_oid: Oid = object.id().into();
-        let peeled = object.peel(ObjectType::Commit).map_err(|err| error::Resolve {
-            revision: rev.to_owned(),
-            err,
-        })?;
+        let peeled = object
+            .peel(ObjectType::Commit)
+            .map_err(|err| error::Resolve {
+                revision: rev.to_owned(),
+                err,
+            })?;
         Ok(ResolvedRef {
             commit: peeled.id().into(),
             tag: Some(tag_oid),
Diff in /ci/src/src/lib.rs:712:
             store: self,
         })
     }
-
 }
 
 /// A `ReleaseMut` is a [`Release`] where the underlying `Release` can be
Diff in /ci/src/src/lib.rs:972:
 
     /// Collect the delegate set for a Radicle storage repository for use
     /// with `find_unique_by_commit`.
-    fn delegates(
-        repo: &radicle::storage::git::Repository,
-    ) -> std::collections::BTreeSet<Did> {
+    fn delegates(repo: &radicle::storage::git::Repository) -> std::collections::BTreeSet<Did> {
         use radicle::prelude::ReadRepository;
         repo.delegates().unwrap().into_iter().collect()
     }
Diff in /ci/src/src/lib.rs:2002:
             node: _alice, repo, ..
         } = test::setup::NodeWithRepo::default();
         let test::setup::NodeWithRepo { node: bob, .. } = test::setup::NodeWithRepo::default();
-        let test::setup::NodeWithRepo { node: carol, .. } =
-            test::setup::NodeWithRepo::default();
+        let test::setup::NodeWithRepo { node: carol, .. } = test::setup::NodeWithRepo::default();
         let oid = commit(&repo.backend, "Test Commit");
         let delegates = delegates(&repo);
         let mut releases = Releases::open(&*repo).unwrap();
plan: Action failed: cargo_fmt
After 8.63 seconds at 2026-04-28 10:45:38Z
CargoFmt(
    CargoFmt,
)

Raw log messages for Ambient troubleshooting

Raw log messages

These raw log messages are meant to help Ambient developers figure out problems. You can ignore them.

  1. {
      "type": "executor_starts",
      "name": "ambient-execute-plan",
      "version": "0.14.0@c37ec71",
      "timestamp": {
        "secs_since_epoch": 1777373129,
        "nanos_since_epoch": 459454896
      },
      "log_source": "Plan"
    }
  2. {
      "type": "runnable_plan",
      "steps": [
        {
          "action": "mkdir",
          "pathname": "/ci"
        },
        {
          "action": "mkdir",
          "pathname": "/ci/artifacts"
        },
        {
          "action": "tar_extract",
          "archive": "/dev/vdc",
          "directory": "/ci/src"
        },
        {
          "action": "tar_extract",
          "archive": "/dev/vdf",
          "directory": "/ci/deps"
        },
        {
          "action": "tar_extract",
          "archive": "/dev/vde",
          "directory": "/ci/cache"
        },
        {
          "action": "shell",
          "shell": "ln -sf /ci /workspace"
        },
        {
          "action": "shell",
          "shell": "git config --global user.name 'Ambient CI'"
        },
        {
          "action": "shell",
          "shell": "git config --global user.email ambient@example.com"
        },
        {
          "action": "cargo_fmt"
        },
        {
          "action": "cargo_clippy"
        },
        {
          "action": "cargo_test"
        },
        {
          "action": "tar_create",
          "archive": "/dev/vde",
          "directory": "/ci/cache"
        },
        {
          "action": "tar_create",
          "archive": "/dev/vdd",
          "directory": "/ci/artifacts"
        }
      ],
      "executor_drive": "/dev/vdb",
      "source_drive": "/dev/vdc",
      "artifact_drive": "/dev/vdd",
      "cache_drive": "/dev/vde",
      "deps_drive": "/dev/vdf",
      "workspace_dir": "/ci",
      "source_dir": "/ci/src",
      "deps_dir": "/ci/deps",
      "cache_dir": "/ci/cache",
      "artifacts_dir": "/ci/artifacts",
      "envs": {},
      "timestamp": {
        "secs_since_epoch": 1777373129,
        "nanos_since_epoch": 462123103
      },
      "log_source": "Plan"
    }
  3. {
      "type": "execute_action",
      "action": "mkdir",
      "pathname": "/ci",
      "timestamp": {
        "secs_since_epoch": 1777373129,
        "nanos_since_epoch": 477011239
      },
      "log_source": "Plan"
    }
  4. {
      "type": "action_succeeded",
      "action": "mkdir",
      "pathname": "/ci",
      "timestamp": {
        "secs_since_epoch": 1777373129,
        "nanos_since_epoch": 488329253
      },
      "log_source": "Plan"
    }
  5. {
      "type": "execute_action",
      "action": "mkdir",
      "pathname": "/ci/artifacts",
      "timestamp": {
        "secs_since_epoch": 1777373129,
        "nanos_since_epoch": 488352259
      },
      "log_source": "Plan"
    }
  6. {
      "type": "action_succeeded",
      "action": "mkdir",
      "pathname": "/ci/artifacts",
      "timestamp": {
        "secs_since_epoch": 1777373129,
        "nanos_since_epoch": 488387987
      },
      "log_source": "Plan"
    }
  7. {
      "type": "execute_action",
      "action": "tar_extract",
      "archive": "/dev/vdc",
      "directory": "/ci/src",
      "timestamp": {
        "secs_since_epoch": 1777373129,
        "nanos_since_epoch": 488565945
      },
      "log_source": "Plan"
    }
  8. {
      "type": "action_succeeded",
      "action": "tar_extract",
      "archive": "/dev/vdc",
      "directory": "/ci/src",
      "timestamp": {
        "secs_since_epoch": 1777373129,
        "nanos_since_epoch": 508312535
      },
      "log_source": "Plan"
    }
  9. {
      "type": "execute_action",
      "action": "tar_extract",
      "archive": "/dev/vdf",
      "directory": "/ci/deps",
      "timestamp": {
        "secs_since_epoch": 1777373129,
        "nanos_since_epoch": 510983625
      },
      "log_source": "Plan"
    }
  10. {
      "type": "action_succeeded",
      "action": "tar_extract",
      "archive": "/dev/vdf",
      "directory": "/ci/deps",
      "timestamp": {
        "secs_since_epoch": 1777373132,
        "nanos_since_epoch": 168127080
      },
      "log_source": "Plan"
    }
  11. {
      "type": "execute_action",
      "action": "tar_extract",
      "archive": "/dev/vde",
      "directory": "/ci/cache",
      "timestamp": {
        "secs_since_epoch": 1777373132,
        "nanos_since_epoch": 170853328
      },
      "log_source": "Plan"
    }
  12. {
      "type": "action_succeeded",
      "action": "tar_extract",
      "archive": "/dev/vde",
      "directory": "/ci/cache",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 504808603
      },
      "log_source": "Plan"
    }
  13. {
      "type": "execute_action",
      "action": "shell",
      "shell": "ln -sf /ci /workspace",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 504845137
      },
      "log_source": "Plan"
    }
  14. {
      "type": "start_program",
      "argv": [
        {
          "Unix": [
            98,
            97,
            115,
            104
          ]
        },
        {
          "Unix": [
            45,
            99
          ]
        },
        {
          "Unix": [
            115,
            101,
            116,
            32,
            45,
            120,
            101,
            117,
            111,
            32,
            112,
            105,
            112,
            101,
            102,
            97,
            105,
            108,
            10,
            108,
            110,
            32,
            45,
            115,
            102,
            32,
            47,
            99,
            105,
            32,
            47,
            119,
            111,
            114,
            107,
            115,
            112,
            97,
            99,
            101,
            10
          ]
        }
      ],
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 504857341
      },
      "log_source": "Plan"
    }
  15. {
      "type": "program_succeeded",
      "exit_code": 0,
      "stdout": "",
      "stderr": "+ ln -sf /ci /workspace\n",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 516506887
      },
      "log_source": "Plan"
    }
  16. {
      "type": "action_succeeded",
      "action": "shell",
      "shell": "ln -sf /ci /workspace",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 519179036
      },
      "log_source": "Plan"
    }
  17. {
      "type": "execute_action",
      "action": "shell",
      "shell": "git config --global user.name 'Ambient CI'",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 521488645
      },
      "log_source": "Plan"
    }
  18. {
      "type": "start_program",
      "argv": [
        {
          "Unix": [
            98,
            97,
            115,
            104
          ]
        },
        {
          "Unix": [
            45,
            99
          ]
        },
        {
          "Unix": [
            115,
            101,
            116,
            32,
            45,
            120,
            101,
            117,
            111,
            32,
            112,
            105,
            112,
            101,
            102,
            97,
            105,
            108,
            10,
            103,
            105,
            116,
            32,
            99,
            111,
            110,
            102,
            105,
            103,
            32,
            45,
            45,
            103,
            108,
            111,
            98,
            97,
            108,
            32,
            117,
            115,
            101,
            114,
            46,
            110,
            97,
            109,
            101,
            32,
            39,
            65,
            109,
            98,
            105,
            101,
            110,
            116,
            32,
            67,
            73,
            39,
            10
          ]
        }
      ],
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 524039131
      },
      "log_source": "Plan"
    }
  19. {
      "type": "program_succeeded",
      "exit_code": 0,
      "stdout": "",
      "stderr": "+ git config --global user.name 'Ambient CI'\n",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 535069539
      },
      "log_source": "Plan"
    }
  20. {
      "type": "action_succeeded",
      "action": "shell",
      "shell": "git config --global user.name 'Ambient CI'",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 535092884
      },
      "log_source": "Plan"
    }
  21. {
      "type": "execute_action",
      "action": "shell",
      "shell": "git config --global user.email ambient@example.com",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 535098153
      },
      "log_source": "Plan"
    }
  22. {
      "type": "start_program",
      "argv": [
        {
          "Unix": [
            98,
            97,
            115,
            104
          ]
        },
        {
          "Unix": [
            45,
            99
          ]
        },
        {
          "Unix": [
            115,
            101,
            116,
            32,
            45,
            120,
            101,
            117,
            111,
            32,
            112,
            105,
            112,
            101,
            102,
            97,
            105,
            108,
            10,
            103,
            105,
            116,
            32,
            99,
            111,
            110,
            102,
            105,
            103,
            32,
            45,
            45,
            103,
            108,
            111,
            98,
            97,
            108,
            32,
            117,
            115,
            101,
            114,
            46,
            101,
            109,
            97,
            105,
            108,
            32,
            97,
            109,
            98,
            105,
            101,
            110,
            116,
            64,
            101,
            120,
            97,
            109,
            112,
            108,
            101,
            46,
            99,
            111,
            109,
            10
          ]
        }
      ],
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 535314724
      },
      "log_source": "Plan"
    }
  23. {
      "type": "program_succeeded",
      "exit_code": 0,
      "stdout": "",
      "stderr": "+ git config --global user.email ambient@example.com\n",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 550579231
      },
      "log_source": "Plan"
    }
  24. {
      "type": "action_succeeded",
      "action": "shell",
      "shell": "git config --global user.email ambient@example.com",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 550599854
      },
      "log_source": "Plan"
    }
  25. {
      "type": "execute_action",
      "action": "cargo_fmt",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 550604882
      },
      "log_source": "Plan"
    }
  26. {
      "type": "start_program",
      "argv": [
        {
          "Unix": [
            99,
            97,
            114,
            103,
            111
          ]
        },
        {
          "Unix": [
            45,
            45,
            118,
            101,
            114,
            115,
            105,
            111,
            110
          ]
        }
      ],
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 551013090
      },
      "log_source": "Plan"
    }
  27. {
      "type": "program_succeeded",
      "exit_code": 0,
      "stdout": "cargo 1.94.0 (85eff7c80 2026-01-15)\n",
      "stderr": "",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 615194938
      },
      "log_source": "Plan"
    }
  28. {
      "type": "start_program",
      "argv": [
        {
          "Unix": [
            99,
            97,
            114,
            103,
            111
          ]
        },
        {
          "Unix": [
            99,
            108,
            105,
            112,
            112,
            121
          ]
        },
        {
          "Unix": [
            45,
            45,
            118,
            101,
            114,
            115,
            105,
            111,
            110
          ]
        }
      ],
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 617962151
      },
      "log_source": "Plan"
    }
  29. {
      "type": "program_succeeded",
      "exit_code": 0,
      "stdout": "clippy 0.1.94 (4a4ef493e3 2026-03-02)\n",
      "stderr": "",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 723095496
      },
      "log_source": "Plan"
    }
  30. {
      "type": "start_program",
      "argv": [
        {
          "Unix": [
            114,
            117,
            115,
            116,
            99
          ]
        },
        {
          "Unix": [
            45,
            45,
            118,
            101,
            114,
            115,
            105,
            111,
            110
          ]
        }
      ],
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 723135881
      },
      "log_source": "Plan"
    }
  31. {
      "type": "program_succeeded",
      "exit_code": 0,
      "stdout": "rustc 1.94.0 (4a4ef493e 2026-03-02)\n",
      "stderr": "",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 770643290
      },
      "log_source": "Plan"
    }
  32. {
      "type": "start_program",
      "argv": [
        {
          "Unix": [
            99,
            97,
            114,
            103,
            111
          ]
        },
        {
          "Unix": [
            102,
            109,
            116
          ]
        },
        {
          "Unix": [
            45,
            45,
            99,
            104,
            101,
            99,
            107
          ]
        }
      ],
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 770685325
      },
      "log_source": "Plan"
    }
  33. {
      "type": "program_failed",
      "exit_code": 1,
      "stdout": "Diff in /ci/src/src/bin/rad-artifact.rs:175:\n             let signer = profile.signer().map_err(error::Signer)?;\n             match loc.command {\n                 LocationCommand::Add(cmd) => {\n-                    location_add(\n-                        cmd,\n-                        args.no_input,\n-                        &mut releases,\n-                        &repo,\n-                        &profile,\n-                        &signer,\n-                    )?;\n+                    location_add(cmd, args.no_input, &mut releases, &repo, &profile, &signer)?;\n                 }\n                 LocationCommand::Remove(cmd) => {\n-                    location_remove(\n-                        cmd,\n-                        args.no_input,\n-                        &mut releases,\n-                        &repo,\n-                        &profile,\n-                        &signer,\n-                    )?;\n+                    location_remove(cmd, args.no_input, &mut releases, &repo, &profile, &signer)?;\n                 }\n             }\n             if !args.no_sync {\nDiff in /ci/src/src/bin/rad-artifact.rs:201:\n         }\n         Command::Attest(cmd) => {\n             let signer = profile.signer().map_err(error::Signer)?;\n-            attest_artifact(\n-                cmd,\n-                args.no_input,\n-                &mut releases,\n-                &repo,\n-                &profile,\n-                &signer,\n-            )?;\n+            attest_artifact(cmd, args.no_input, &mut releases, &repo, &profile, &signer)?;\n             if !args.no_sync {\n                 announce(&profile, repo.id)?;\n             }\nDiff in /ci/src/src/bin/rad-artifact.rs:215:\n         }\n         Command::Redact(cmd) => {\n             let signer = profile.signer().map_err(error::Signer)?;\n-            redact_artifact(\n-                cmd,\n-                args.no_input,\n-                &mut releases,\n-                &repo,\n-                &profile,\n-                &signer,\n-            )?;\n+            redact_artifact(cmd, args.no_input, &mut releases, &repo, &profile, &signer)?;\n             if !args.no_sync {\n                 announce(&profile, repo.id)?;\n             }\nDiff in /ci/src/src/bin/rad-artifact.rs:441:\n     let (id, cid) = match (release, revision, cid) {\n         (Some(_), _, Some(cid)) | (_, Some(_), Some(cid)) => {\n             let id = resolve_target_release(\n-                release,\n-                revision,\n-                releases,\n-                repo,\n-                &delegates,\n-                no_input,\n-                aliases,\n+                release, revision, releases, repo, &delegates, no_input, aliases,\n             )?;\n             (id, cid)\n         }\nDiff in /ci/src/src/bin/rad-artifact.rs:493:\n     let (id, cid) = match (release, revision, cid) {\n         (Some(_), _, Some(cid)) | (_, Some(_), Some(cid)) => {\n             let id = resolve_target_release(\n-                release,\n-                revision,\n-                releases,\n-                repo,\n-                &delegates,\n-                no_input,\n-                aliases,\n+                release, revision, releases, repo, &delegates, no_input, aliases,\n             )?;\n             (id, cid)\n         }\nDiff in /ci/src/src/bin/rad-artifact.rs:603:\n                 let id = parse_release_id(s, repo)?;\n                 let r = releases\n                     .get(&id)\n-                    .map_err(|err| error::Find::LookupId { release_id: id, err })?\n+                    .map_err(|err| error::Find::LookupId {\n+                        release_id: id,\n+                        err,\n+                    })?\n                     .ok_or(error::Find::NoReleaseId(id))?;\n                 vec![(id, r)]\n             }\nDiff in /ci/src/src/bin/rad-artifact.rs:628:\n         all_authors,\n         local: Some(local),\n     };\n-    let shown =\n-        display::Releases::new(candidates.into_iter(), aliases, filters, true, repo, repo);\n+    let shown = display::Releases::new(candidates.into_iter(), aliases, filters, true, repo, repo);\n     if use_pretty(pretty, json) {\n         print!(\"{}\", shown.pretty(verbose));\n     } else {\nDiff in /ci/src/src/bin/rad-artifact.rs:1007:\n         repo: &Repository,\n         aliases: &impl AliasStore,\n     ) -> Result<ReleaseId, String> {\n-        select_release(candidates, None, repo, aliases)?\n-            .ok_or_else(|| \"no release selected\".into())\n+        select_release(candidates, None, repo, aliases)?.ok_or_else(|| \"no release selected\".into())\n     }\n \n     /// Show a multi-release picker. Returns `Some(id)` when the user\nDiff in /ci/src/src/bin/rad-artifact.rs:1295:\n                     name, commit_oid, ..\n                 } => {\n                     let short = &commit_oid.to_string()[..7];\n-                    let title =\n-                        display::CommitTitle::title(repo, commit_oid).unwrap_or_default();\n+                    let title = display::CommitTitle::title(repo, commit_oid).unwrap_or_default();\n                     format!(\"{name} -> {short}  {title}\")\n                 }\n                 Entry::Commit { oid } => {\nDiff in /ci/src/src/bin/rad-artifact.rs:1422:\n             match releases.get(&id) {\n                 Ok(Some(_)) => Ok(id),\n                 Ok(None) => Err(error::Find::NoReleaseId(id).into()),\n-                Err(err) => Err(error::Find::LookupId { release_id: id, err }.into()),\n+                Err(err) => Err(error::Find::LookupId {\n+                    release_id: id,\n+                    err,\n+                }\n+                .into()),\n             }\n         }\n         (None, Some(rev)) => {\nDiff in /ci/src/src/bin/rad-artifact.rs:1450:\n /// `git revparse_single` accepts short OIDs, full OIDs, and any other\n /// ref name that points at the COB.\n fn parse_release_id(s: &str, repo: &Repository) -> Result<ReleaseId, error::Resolve> {\n-    let object = repo.raw().revparse_single(s).map_err(|err| error::Resolve {\n-        revision: s.to_owned(),\n-        err,\n-    })?;\n+    let object = repo\n+        .raw()\n+        .revparse_single(s)\n+        .map_err(|err| error::Resolve {\n+            revision: s.to_owned(),\n+            err,\n+        })?;\n     Ok(cob::ObjectId::from(object.id()).into())\n }\n \nDiff in /ci/src/src/bin/rad-artifact.rs:1467:\n     })?;\n     if object.kind() == Some(ObjectType::Tag) {\n         let tag_oid: Oid = object.id().into();\n-        let peeled = object.peel(ObjectType::Commit).map_err(|err| error::Resolve {\n-            revision: rev.to_owned(),\n-            err,\n-        })?;\n+        let peeled = object\n+            .peel(ObjectType::Commit)\n+            .map_err(|err| error::Resolve {\n+                revision: rev.to_owned(),\n+                err,\n+            })?;\n         Ok(ResolvedRef {\n             commit: peeled.id().into(),\n             tag: Some(tag_oid),\nDiff in /ci/src/src/lib.rs:712:\n             store: self,\n         })\n     }\n-\n }\n \n /// A `ReleaseMut` is a [`Release`] where the underlying `Release` can be\nDiff in /ci/src/src/lib.rs:972:\n \n     /// Collect the delegate set for a Radicle storage repository for use\n     /// with `find_unique_by_commit`.\n-    fn delegates(\n-        repo: &radicle::storage::git::Repository,\n-    ) -> std::collections::BTreeSet<Did> {\n+    fn delegates(repo: &radicle::storage::git::Repository) -> std::collections::BTreeSet<Did> {\n         use radicle::prelude::ReadRepository;\n         repo.delegates().unwrap().into_iter().collect()\n     }\nDiff in /ci/src/src/lib.rs:2002:\n             node: _alice, repo, ..\n         } = test::setup::NodeWithRepo::default();\n         let test::setup::NodeWithRepo { node: bob, .. } = test::setup::NodeWithRepo::default();\n-        let test::setup::NodeWithRepo { node: carol, .. } =\n-            test::setup::NodeWithRepo::default();\n+        let test::setup::NodeWithRepo { node: carol, .. } = test::setup::NodeWithRepo::default();\n         let oid = commit(&repo.backend, \"Test Commit\");\n         let delegates = delegates(&repo);\n         let mut releases = Releases::open(&*repo).unwrap();\n",
      "stderr": "",
      "timestamp": {
        "secs_since_epoch": 1777373137,
        "nanos_since_epoch": 976060276
      },
      "log_source": "Plan"
    }
  34. {
      "type": "action_failed",
      "action": "cargo_fmt",
      "timestamp": {
        "secs_since_epoch": 1777373138,
        "nanos_since_epoch": 86283791
      },
      "log_source": "Plan"
    }
  35. {
      "type": "executor_ends_in_failure",
      "exit_code": 1,
      "timestamp": {
        "secs_since_epoch": 1777373138,
        "nanos_since_epoch": 89799356
      },
      "log_source": "Plan"
    }

Trigger message

{
  "request": "trigger",
  "version": 1,
  "event_type": "patch",
  "repository": {
    "id": "rad:z4VYyJ9KuwMNkXGQnmKuGPGKw3inv",
    "name": "radicle-artifact",
    "description": "A Radicle Collaborative Object (COB) for content-addressed release artifacts and their discovery locations",
    "private": false,
    "default_branch": "main",
    "delegates": [
      "did:key:z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm"
    ]
  },
  "action": "Updated",
  "patch": {
    "id": "38c7fa789850450f8936febdad50dfe59d89648f",
    "author": {
      "id": "did:key:z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm",
      "alias": "2color"
    },
    "title": "Add support for tags in releases",
    "state": {
      "status": "open",
      "conflicts": []
    },
    "before": "1af9b4fc79a1e5816b92292bab422287c2dc65a3",
    "after": "1e72006266445d1db954f39c24c9979b78f61de8",
    "commits": [
      "1e72006266445d1db954f39c24c9979b78f61de8",
      "c86237d54fecf046d6dc1721c5a2e2b3be86f63b",
      "e2e9e721eae37358fffe4e18da3bc60a35fe96cf",
      "b6c4673e57a15769fe40804b239b03eda9c15419",
      "b9ed5aeaa4286301484782485668b25099c032b4",
      "98e9c91a275c40b43c387def0fdfec63595e7341",
      "41ab3b0563f37823fd25443b3250731cd327f1b7",
      "4e7289ff8b89b55a9b6203e86620687860ab2b56",
      "b98035f10aba068726bec077ded55ac78e514486",
      "88e573e3df4c403fd1d251605c6943c98e97c34a",
      "55899e0beea3b673c11a48c7debd60cbce7feee4",
      "9876154252f0bfa228a6684fa6873cf9e156c7a4",
      "09680ca0635cc64ebdae618c63b798af78b17c88",
      "d8d7319f181c4f640a65a2e1ada5824e4903be96",
      "35a619ce9659d3d971c179942f4e57f68dccf626",
      "58dd5a7c4f6be9236bd656be066e5f64f3bccafa",
      "479cd44bbe4721492964549b784806c071ca609a",
      "acf7cc89bfd393f9ba4d3660c4fe5f5a4147081a",
      "c0e2958fa068ac9c32ffaca65600b5a69269281b",
      "b04d6befebf153c845a3e7b553cd65cf33b893ed",
      "6796786e4047995656d118008aa5f576b4c60ac1"
    ],
    "target": "1af9b4fc79a1e5816b92292bab422287c2dc65a3",
    "labels": [],
    "assignees": [],
    "revisions": [
      {
        "id": "38c7fa789850450f8936febdad50dfe59d89648f",
        "author": {
          "id": "did:key:z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm",
          "alias": "2color"
        },
        "description": "This patch adds support for linking artifacts to a specific annotated tags. \n\nImplemented with an optional tag OID to the release schema and changes both read/write semantics relating to releases. \n\nThe complexity introduced by this patch is offset by some UX improvements to help the user pick the release either interactively or with a new `—release` flag \n\n## Before\n- Users only had to think about artifact CIDs linked to commits, even though they were grouped by a release COB\n- Releases were transparent to the user. Commits with multiple releases due to concurrency were also transparent, relying on a tie-breaker algorithm to deterministically resolve the release\n- no way to associate releases and artifacts to a specific annotated tag\n- Release authors didn’t matter, authorship only mattered on the artifact level\n\n## After\n\n- Releases now have an optional tag OID field, allowing linking artifacts to a specific annotated tag. (Lightweight tags are not supported)\n- The `COMMIT` argument has been renamed `REVISION` to better reflect that it can be either a commit, tag or abbreviated OID.\n- The same commit can have more than one release. Release IDs are surfaced to the user: \n  - `rad-artifact show|attest/redact/location add/location remove` now accept a `—release`  flag to target a specific release. \n  - `rad-artifact show <REVISION>` will return every release for a given revision\n- New interactive release picker for all write commands, e.g. `add, attest, redact, location add, location remove` ",
        "base": "1af9b4fc79a1e5816b92292bab422287c2dc65a3",
        "oid": "09680ca0635cc64ebdae618c63b798af78b17c88",
        "timestamp": 1777298029
      },
      {
        "id": "0d4d7667473c019f0e6b0cf9dfd53a3114fa04ba",
        "author": {
          "id": "did:key:z6MktwkohCx8aHZ1QCjVZUiLmX92oPZFxRiFZkbq32Tk5Tkm",
          "alias": "2color"
        },
        "description": "refactor: drop find_or_create_by_oid; rename to by_commit\n\n- Drop Releases::find_or_create_by_oid. The CLI is the only caller\n  and will own disambiguation directly, so the library no longer\n  needs a combined lookup-or-create with delegate-bootstrap rules.\n- Rename find_unique_by_oid -> find_unique_by_commit and the\n  iterator pair find_by_oid -> find_by_commit (FindByOid ->\n  FindByCommit). \"Commit\" is more precise than \"oid\", which is\n  also used for release ids.\n- Add find_by_release_id for symmetry with the by-commit lookups.\n- Update CLI add: compose find_unique_by_commit + create instead.\n  Ambiguous lookups now surface as errors rather than being\n  silently resolved, which is the right semantic for the upcoming\n  picker-based disambiguation.\n\nWhy: find_or_create_by_oid silently discarded the caller's tag\nwhen an existing release was returned, so promoting an RC commit\nto a final release dropped the new tag. Pushing disambiguation\nto the CLI lets the user choose explicitly between reusing a\nrelease and creating a new one with the new tag.\n\n\nfeat: add --release flag and disambiguation picker\n\n- Add --release <id> to `add`, mutually exclusive with --revision.\n  Targets an existing release directly, skipping commit/tag\n  resolution and any disambiguation. Required to script add against\n  a specific release when multiple exist for the same commit.\n- When --revision is given (or interactively picked) and multiple\n  releases exist for the resolved commit, prompt the user to pick\n  among them or to create a new release. The \"create new\" entry\n  carries the resolved tag's short OID so the RC-promotion case\n  (existing RC release on the same commit, new tag for the final\n  release) is obvious in the picker.\n- In --no-input mode, ambiguity is a hard error that lists the\n  candidate ids and tells the user to pass --release <id>. Single\n  existing release is still reused silently; zero releases creates\n  one with the resolved tag.\n\nWhy: the previous flow silently reused the first release found for\na commit and discarded the caller's tag, which corrupted the\nRC-promotion case (RC release inheriting a different final tag).\nPushing disambiguation up to the CLI lets the user choose\nexplicitly, and --release gives scripts an unambiguous target.\n\n\nfeat: prompt on single-release tag mismatch\n\nPreviously the picker only ran for >1 releases. A single existing\nrelease with a tag different from the supplied revision's tag (e.g.\nan RC release on the same commit, user runs --revision <final-tag>)\nsilently reused the RC release and dropped the new tag — the\noriginal RC-promotion footgun.\n\nNow also disambiguate when one release exists and its recorded tag\ndiffers from the supplied tag. The user can pick the existing\nrelease (accepting the tag as-is) or create a new release with the\nnew tag. When the user supplied no tag (bare commit), still reuse\nsilently — they didn't assert anything to contradict.\n\nIn --no-input mode this surfaces the same disambiguation error as\nthe multi-release case; rename AmbiguousReleases to\nNeedsDisambiguation since the message now covers single-mismatch\ntoo.\n\n\nfeat: surface tag name and creator in display\n\nPretty output for show/list now reads the annotated-tag name (the\nname field embedded in the tag object) and renders it as\n'tag v1.0 -> commit <short>' instead of 'tag <short-oid> -> ...',\nfalling back to the short OID when the tag object isn't present\nlocally. JSON output gains an optional `tag_name` field alongside\nthe existing `tag` OID.\n\nEach release now also shows its creator DID (with alias when\nknown) — meaningful now that the new picker UX lets multiple\nreleases per commit coexist, and the creator is the primary\ndisambiguator alongside the tag association.\n\nImplementation: parallel TagName trait next to CommitTitle, with\nimpls for Repository (reads tag name out of the tag object\ndirectly) and (). display::Release gains creator,\ncreator_alias, and tag_name fields; Releases::new picks up a\nTagName resolver alongside the existing CommitTitle one.\n\n\nrefactor: drop creation date from list/show output\n\nThe release header was getting noisy with id, ref, creator, date,\nand title; the creation timestamp adds little signal next to the\ntag/commit/creator triple and is still available in JSON via\ncreated_at. Removing it also drops the chrono dependency.\n\n\nfix: prefix release IDs with release\n\n\nfeat: extend --release flag to show/attest/redact/location\n\nMirrors the disambiguation hatch already present on `add` to every\nsubcommand that targets a single release. Each accepts either\n<revision> (the existing path; commit/tag resolution + delegate-\npriority canonicalization) or --release <id> (skips resolution and\naddresses the COB directly).\n\nWhen multiple non-delegate releases exist for a commit and there is\nno delegate-authored canonical, find_unique_by_commit returns\nAmbiguous; the error message now points the user at --release <id>.\n\nTwo CLI breaking changes for symmetry:\n- show <revision> stays positional but is now optional, paired with\n  --release. Clap requires exactly one.\n- location add/remove move <revision> from a positional to a\n  --revision flag. Required to coexist with the optional <url>\n  positional under clap's positional-ordering constraints.\n\nInternals: a single resolve_target_release helper handles both\npaths, returning a unified ResolveTarget error that wraps the\nexisting Resolve and Find variants. Find gains NoReleaseId and\nLookupId for the by-id miss/IO cases.\n\n\nfeat: prompt to pick release on ambiguous revision lookup\n\nExtends the interactive picker UX to every revision-targeting\nsubcommand (show, attest, redact, location add, location remove).\nPreviously these commands surfaced find_unique_by_commit's\nAmbiguous error and required the user to re-run with --release\n<id>; now they list the candidate releases (id, tag, creator,\ntitle) and let the user pick inline.\n\nThe prompt only fires when stdin is a TTY and --no-input is not\nset; non-interactive callers still get the explicit error\npointing at --release <id>. The picker has no \"create new\" entry\nsince these commands act on existing releases.\n\nPlumbing: a new prompt::pick_existing_release reuses the\nformat_candidate helper from add's picker.\nresolve_target_release picks up no_input + AliasStore params and\nthe dispatch threads args.no_input + &profile through to each\ncaller.\n\n\nfeat: show every release for a revision\n\n`show <revision>` now returns every release recorded against the\nresolved commit instead of picking one canonical release. Multiple\nreleases per commit are now first-class (via the picker UX in\nadd) and the previous behaviour silently hid non-canonical COBs\nbehind delegate-priority.\n\nThe three-tier model is now:\n- list                = every release in the repo\n- show <revision>     = every release for this commit/tag\n- show --release <id> = this one release in detail\n\nJSON always emits an array, even with --release (single-element).\nPredictable shape over slight verbosity — consumers don't branch\non which input flag was used. Pretty output renders each release\nin turn, separated by a blank line.\n\nThe Ambiguous picker no longer fires on show since there's\nnothing to disambiguate; revision lookup just returns all hits.\n\n\nfix: accept short release ids on --release flag\n\nClap was parsing --release through ReleaseId's FromStr, which\ndelegates to ObjectId's hex parser and requires a full 40-char\nSHA-1; a 7-char prefix would fail at clap parse time before any\ngit lookup could resolve it.\n\nThe COB's first commit is a real git object, so we can lean on\nrevparse_single to disambiguate prefixes the same way users\nalready abbreviate commit OIDs. Switch every --release clap field\nto a String, route it through a new parse_release_id helper that\ncalls revparse_single on the repository's git backend, then turn\nthe resulting full Oid into a ReleaseId via ObjectId::from.\n\n\nrefactor: simplify\n\n- Drop Releases::find_by_release_id; it was a thin alias of get.\n  Switch the three callers (add, show, resolve_target_release) to\n  call get directly.\n- Fold Add::FindByCommit/Lookup/ReleaseNotFound into the existing\n  Find error via Add::Find(#[from] Find). The three Add-specific\n  variants duplicated Find::Lookup/LookupId/NoReleaseId variant\n  for variant.\n- Pull resolve and format_did up to pub in display so the prompt\n  module's format_candidate can reuse the existing DID rendering\n  instead of inlining its own truncation. Same output, fewer\n  divergent code paths.\n- Unify the two release pickers behind a private select_release\n  helper. pick_release_or_create just appends an extra \"create\n  new\" entry and remaps the index; pick_existing_release calls\n  through with no extra entry. Removes the duplicated TTY check,\n  label-build loop, and \"pass --release <id>\" error string.\n- Route show through display::Releases::new + pretty(). The inline\n  loop in show duplicated the existing Releases construction; an\n  into_inner() accessor on Releases lets show emit a bare-array\n  JSON shape while reusing Releases::pretty for the pretty path.\n- Eliminate the find + get_mut double load in add --release.\n  get_mut already errors NotFound; map that to Find::NoReleaseId\n  inline.\n- Standardise on &impl AliasStore across the CLI command fns.\n  add_artifact was the odd one out taking &Profile only for its\n  AliasStore impl.\n\n\nfeat: show local user's artifacts and full CIDs\n\n- exempt the local user from the delegate-only author filter so users\n  see their own contributions without passing --all-authors\n- render CIDs in full in show/list pretty output; verbose now only\n  controls DID truncation, since CIDs are the primary copy target",
        "base": "1af9b4fc79a1e5816b92292bab422287c2dc65a3",
        "oid": "1e72006266445d1db954f39c24c9979b78f61de8",
        "timestamp": 1777373116
      }
    ]
  }
}

Ambient stdout

executor from config: /usr/bin/ambient-execute-plan
executor from PATH: /usr/bin/ambient-execute-plan
run CI for rad:z4VYyJ9KuwMNkXGQnmKuGPGKw3inv
ERROR: CI run failed inside QEMU

Ambient stderr

<empty log>