Repository: cli/cli

Issue:
`gh run rerun --failed` fails with "its workflow file may be broken" when workflow not finished yet
### Describe the bug

When `gh run rerun --failed` is invoked on a workflow that did not complete yet (and thus is not re-runnable at least in the web UI), the command prints "run .... cannot be rerun; its workflow file may be broken", which does not indicate actual reason of the failure

```
$ gh --version
gh version 2.50.0 (2024-05-29)
https://github.com/cli/cli/releases/tag/v2.50.0
```



### Steps to reproduce the behavior

1. Create a workflow with multiple jobs so that one fails and one continues to run longer
2. Create a PR, wait for the to-be-failed job to fail
3. Obtain status checks for this PR e.g. with `gh pr checks "${pr_link}" --json 'link,bucket' --jq 'map(. | select(.bucket == "fail")'` -- the job is reported as failed
4. Obtain the run ID -- either from web UI or with something like `gh pr checks "${pr_link}" --json 'link,bucket' --jq 'map(. | select(.bucket == "fail") | .link | sub(".*/runs/(?<id>[0-9]+)/job/[0-9]+$"; "\(.id)") ) | unique | .[]'`
5. Attempt re-running the failed jobs: `gh run rerun --failed "${run_id}"`

### Expected vs actual behavior

Expected: `gh run rerun` to print something indicating that workflow/run cannot be rerun before it finishes
Actual: message not indicating what the actual problem is


Discussion:
@andyfeller: Thanks for opening this bug report, @findepi! 🤗 

Using the following workflow, I was able to reproduce this without involving a pull request:

```yaml
on:
  workflow_dispatch:
jobs:
  long-running:
    runs-on: ubuntu-latest
    steps:
      - name: Delay
        run: |
          sleep 300
  short-failing:
    runs-on: ubuntu-latest
    steps:
      - name: Delay
        run: |
          sleep 60
      - name: Fail
        run: |
          exit 1
```

which resulted in:

```shell

< HTTP/2.0 403 Forbidden
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Tue, 30 Jul 2024 18:54:02 GMT
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Oauth-Scopes: 
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Api-Version-Selected: 2022-11-28
< X-Github-Media-Type: github.v3; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: E3D3:ED6FF:6FAA10:D07726:66A936CA
< X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
< X-Oauth-Scopes: codespace, gist, project, read:org, repo, workflow
< X-Ratelimit-Limit: 15000
< X-Ratelimit-Remaining: 14982
< X-Ratelimit-Reset: 1722369142
< X-Ratelimit-Resource: core
< X-Ratelimit-Used: 18
< X-Xss-Protection: 0

{
  "message": "This workflow is already running",
  "documentation_url": "https://docs.github.com/rest/actions/workflow-runs#re-run-failed-jobs-from-a-workflow-run",
  "status": "403"
}

* Request took 118.392833ms
run 10168088985 cannot be rerun; its workflow file may be broken
```

<details>
<summary>
  <b>Full debug output from rerunning failed jobs</b>
</summary>
<p>

```shell
andyfeller@Andys-MBP:tinyfists/actions-experiments ‹main›$ GH_DEBUG=api gh run rerun 10168088985 --failed
[git remote -v]
[git config --get-regexp ^remote\..*\.gh-resolved$]
⣾* Request at 2024-07-30 14:54:01.934144 -0400 EDT m=+0.083731709
* Request to https://api.github.com/repos/tinyfists/actions-experiments/actions/runs/10168088985?exclude_pull_requests=true
> GET /repos/tinyfists/actions-experiments/actions/runs/10168088985?exclude_pull_requests=true HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Type: application/json; charset=utf-8
> Time-Zone: America/New_York
> User-Agent: GitHub CLI 2.52.0

⣽< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Cache-Control: private, max-age=60, s-maxage=60
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Tue, 30 Jul 2024 18:54:02 GMT
< Etag: W/"b4d532226b977680761313d8e5eeed26e89a2d779efc0cce6f4f39519b920a23"
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Oauth-Scopes: 
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Api-Version-Selected: 2022-11-28
< X-Github-Media-Type: github.v3; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: E3D3:ED6FF:6FA8FF:D07546:66A936CA
< X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
< X-Oauth-Scopes: codespace, gist, project, read:org, repo, workflow
< X-Ratelimit-Limit: 15000
< X-Ratelimit-Remaining: 14984
< X-Ratelimit-Reset: 1722369142
< X-Ratelimit-Resource: core
< X-Ratelimit-Used: 16
< X-Xss-Protection: 0

{
  "id": 10168088985,
  "name": ".github/workflows/cli-9221.yml",
  "node_id": "WFR_kwLOIVGots8AAAACXhC5mQ",
  "head_branch": "main",
  "head_sha": "f868aa71d5ef0d60dc692b6a73891887ec2dc624",
  "path": ".github/workflows/cli-9221.yml",
  "display_title": ".github/workflows/cli-9221.yml",
  "run_number": 1,
  "event": "workflow_dispatch",
  "status": "in_progress",
  "conclusion": null,
  "workflow_id": 109990972,
  "check_suite_id": 26606595224,
  "check_suite_node_id": "CS_kwDOIVGots8AAAAGMeBsmA",
  "url": "https://api.github.com/repos/tinyfists/actions-experiments/actions/runs/10168088985",
  "html_url": "https://github.com/tinyfists/actions-experiments/actions/runs/10168088985",
  "pull_requests": [],
  "created_at": "2024-07-30T18:51:31Z",
  "updated_at": "2024-07-30T18:51:42Z",
  "actor": {
    "login": "andyfeller",
    "id": 2089743,
    "node_id": "MDQ6VXNlcjIwODk3NDM=",
    "avatar_url": "https://avatars.githubusercontent.com/u/2089743?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/andyfeller",
    "html_url": "https://github.com/andyfeller",
    "followers_url": "https://api.github.com/users/andyfeller/followers",
    "following_url": "https://api.github.com/users/andyfeller/following{/other_user}",
    "gists_url": "https://api.github.com/users/andyfeller/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/andyfeller/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/andyfeller/subscriptions",
    "organizations_url": "https://api.github.com/users/andyfeller/orgs",
    "repos_url": "https://api.github.com/users/andyfeller/repos",
    "events_url": "https://api.github.com/users/andyfeller/events{/privacy}",
    "received_events_url": "https://api.github.com/users/andyfeller/received_events",
    "type": "User",
    "site_admin": true
  },
  "run_attempt": 1,
  "referenced_workflows": [],
  "run_started_at": "2024-07-30T18:51:31Z",
  "triggering_actor": {
    "login": "andyfeller",
    "id": 2089743,
    "node_id": "MDQ6VXNlcjIwODk3NDM=",
    "avatar_url": "https://avatars.githubusercontent.com/u/2089743?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/andyfeller",
    "html_url": "https://github.com/andyfeller",
    "followers_url": "https://api.github.com/users/andyfeller/followers",
    "following_url": "https://api.github.com/users/andyfeller/following{/other_user}",
    "gists_url": "https://api.github.com/users/andyfeller/gists{/gist_id}",
    "starred_url": "https://api.github.com/users/andyfeller/starred{/owner}{/repo}",
    "subscriptions_url": "https://api.github.com/users/andyfeller/subscriptions",
    "organizations_url": "https://api.github.com/users/andyfeller/orgs",
    "repos_url": "https://api.github.com/users/andyfeller/repos",
    "events_url": "https://api.github.com/users/andyfeller/events{/privacy}",
    "received_events_url": "https://api.github.com/users/andyfeller/received_events",
    "type": "User",
    "site_admin": true
  },
  "jobs_url": "https://api.github.com/repos/tinyfists/actions-experiments/actions/runs/10168088985/jobs",
  "logs_url": "https://api.github.com/repos/tinyfists/actions-experiments/actions/runs/10168088985/logs",
  "check_suite_url": "https://api.github.com/repos/tinyfists/actions-experiments/check-suites/26606595224",
  "artifacts_url": "https://api.github.com/repos/tinyfists/actions-experiments/actions/runs/10168088985/artifacts",
  "cancel_url": "https://api.github.com/repos/tinyfists/actions-experiments/actions/runs/10168088985/cancel",
  "rerun_url": "https://api.github.com/repos/tinyfists/actions-experiments/actions/runs/10168088985/rerun",
  "previous_attempt_url": null,
  "workflow_url": "https://api.github.com/repos/tinyfists/actions-experiments/actions/workflows/109990972",
  "head_commit": {
    "id": "f868aa71d5ef0d60dc692b6a73891887ec2dc624",
    "tree_id": "3621b7f9d3aca60b87dc82715da5e11fd324f883",
    "message": "Create cli-9221.yml",
    "timestamp": "2024-07-30T18:50:57Z",
    "author": {
      "name": "Andy Feller",
      "email": "andyfeller@github.com"
    },
    "committer": {
      "name": "GitHub",
      "email": "noreply@github.com"
    }
  },
  "repository": {
    "id": 558999734,
    "node_id": "R_kgDOIVGotg",
    "name": "actions-experiments",
    "full_name": "tinyfists/actions-experiments",
    "private": true,
    "owner": {
      "login": "tinyfists",
      "id": 90800543,
      "node_id": "MDEyOk9yZ2FuaXphdGlvbjkwODAwNTQz",
      "avatar_url": "https://avatars.githubusercontent.com/u/90800543?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/tinyfists",
      "html_url": "https://github.com/tinyfists",
      "followers_url": "https://api.github.com/users/tinyfists/followers",
      "following_url": "https://api.github.com/users/tinyfists/following{/other_user}",
      "gists_url": "https://api.github.com/users/tinyfists/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/tinyfists/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/tinyfists/subscriptions",
      "organizations_url": "https://api.github.com/users/tinyfists/orgs",
      "repos_url": "https://api.github.com/users/tinyfists/repos",
      "events_url": "https://api.github.com/users/tinyfists/events{/privacy}",
      "received_events_url": "https://api.github.com/users/tinyfists/received_events",
      "type": "Organization",
      "site_admin": false
    },
    "html_url": "https://github.com/tinyfists/actions-experiments",
    "description": "Collection of GitHub Action workflows for experimenting with various techniques.",
    "fork": false,
    "url": "https://api.github.com/repos/tinyfists/actions-experiments",
    "forks_url": "https://api.github.com/repos/tinyfists/actions-experiments/forks",
    "keys_url": "https://api.github.com/repos/tinyfists/actions-experiments/keys{/key_id}",
    "collaborators_url": "https://api.github.com/repos/tinyfists/actions-experiments/collaborators{/collaborator}",
    "teams_url": "https://api.github.com/repos/tinyfists/actions-experiments/teams",
    "hooks_url": "https://api.github.com/repos/tinyfists/actions-experiments/hooks",
    "issue_events_url": "https://api.github.com/repos/tinyfists/actions-experiments/issues/events{/number}",
    "events_url": "https://api.github.com/repos/tinyfists/actions-experiments/events",
    "assignees_url": "https://api.github.com/repos/tinyfists/actions-experiments/assignees{/user}",
    "branches_url": "https://api.github.com/repos/tinyfists/actions-experiments/branches{/branch}",
    "tags_url": "https://api.github.com/repos/tinyfists/actions-experiments/tags",
    "blobs_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/blobs{/sha}",
    "git_tags_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/tags{/sha}",
    "git_refs_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/refs{/sha}",
    "trees_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/trees{/sha}",
    "statuses_url": "https://api.github.com/repos/tinyfists/actions-experiments/statuses/{sha}",
    "languages_url": "https://api.github.com/repos/tinyfists/actions-experiments/languages",
    "stargazers_url": "https://api.github.com/repos/tinyfists/actions-experiments/stargazers",
    "contributors_url": "https://api.github.com/repos/tinyfists/actions-experiments/contributors",
    "subscribers_url": "https://api.github.com/repos/tinyfists/actions-experiments/subscribers",
    "subscription_url": "https://api.github.com/repos/tinyfists/actions-experiments/subscription",
    "commits_url": "https://api.github.com/repos/tinyfists/actions-experiments/commits{/sha}",
    "git_commits_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/commits{/sha}",
    "comments_url": "https://api.github.com/repos/tinyfists/actions-experiments/comments{/number}",
    "issue_comment_url": "https://api.github.com/repos/tinyfists/actions-experiments/issues/comments{/number}",
    "contents_url": "https://api.github.com/repos/tinyfists/actions-experiments/contents/{+path}",
    "compare_url": "https://api.github.com/repos/tinyfists/actions-experiments/compare/{base}...{head}",
    "merges_url": "https://api.github.com/repos/tinyfists/actions-experiments/merges",
    "archive_url": "https://api.github.com/repos/tinyfists/actions-experiments/{archive_format}{/ref}",
    "downloads_url": "https://api.github.com/repos/tinyfists/actions-experiments/downloads",
    "issues_url": "https://api.github.com/repos/tinyfists/actions-experiments/issues{/number}",
    "pulls_url": "https://api.github.com/repos/tinyfists/actions-experiments/pulls{/number}",
    "milestones_url": "https://api.github.com/repos/tinyfists/actions-experiments/milestones{/number}",
    "notifications_url": "https://api.github.com/repos/tinyfists/actions-experiments/notifications{?since,all,participating}",
    "labels_url": "https://api.github.com/repos/tinyfists/actions-experiments/labels{/name}",
    "releases_url": "https://api.github.com/repos/tinyfists/actions-experiments/releases{/id}",
    "deployments_url": "https://api.github.com/repos/tinyfists/actions-experiments/deployments"
  },
  "head_repository": {
    "id": 558999734,
    "node_id": "R_kgDOIVGotg",
    "name": "actions-experiments",
    "full_name": "tinyfists/actions-experiments",
    "private": true,
    "owner": {
      "login": "tinyfists",
      "id": 90800543,
      "node_id": "MDEyOk9yZ2FuaXphdGlvbjkwODAwNTQz",
      "avatar_url": "https://avatars.githubusercontent.com/u/90800543?v=4",
      "gravatar_id": "",
      "url": "https://api.github.com/users/tinyfists",
      "html_url": "https://github.com/tinyfists",
      "followers_url": "https://api.github.com/users/tinyfists/followers",
      "following_url": "https://api.github.com/users/tinyfists/following{/other_user}",
      "gists_url": "https://api.github.com/users/tinyfists/gists{/gist_id}",
      "starred_url": "https://api.github.com/users/tinyfists/starred{/owner}{/repo}",
      "subscriptions_url": "https://api.github.com/users/tinyfists/subscriptions",
      "organizations_url": "https://api.github.com/users/tinyfists/orgs",
      "repos_url": "https://api.github.com/users/tinyfists/repos",
      "events_url": "https://api.github.com/users/tinyfists/events{/privacy}",
      "received_events_url": "https://api.github.com/users/tinyfists/received_events",
      "type": "Organization",
      "site_admin": false
    },
    "html_url": "https://github.com/tinyfists/actions-experiments",
    "description": "Collection of GitHub Action workflows for experimenting with various techniques.",
    "fork": false,
    "url": "https://api.github.com/repos/tinyfists/actions-experiments",
    "forks_url": "https://api.github.com/repos/tinyfists/actions-experiments/forks",
    "keys_url": "https://api.github.com/repos/tinyfists/actions-experiments/keys{/key_id}",
    "collaborators_url": "https://api.github.com/repos/tinyfists/actions-experiments/collaborators{/collaborator}",
    "teams_url": "https://api.github.com/repos/tinyfists/actions-experiments/teams",
    "hooks_url": "https://api.github.com/repos/tinyfists/actions-experiments/hooks",
    "issue_events_url": "https://api.github.com/repos/tinyfists/actions-experiments/issues/events{/number}",
    "events_url": "https://api.github.com/repos/tinyfists/actions-experiments/events",
    "assignees_url": "https://api.github.com/repos/tinyfists/actions-experiments/assignees{/user}",
    "branches_url": "https://api.github.com/repos/tinyfists/actions-experiments/branches{/branch}",
    "tags_url": "https://api.github.com/repos/tinyfists/actions-experiments/tags",
    "blobs_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/blobs{/sha}",
    "git_tags_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/tags{/sha}",
    "git_refs_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/refs{/sha}",
    "trees_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/trees{/sha}",
    "statuses_url": "https://api.github.com/repos/tinyfists/actions-experiments/statuses/{sha}",
    "languages_url": "https://api.github.com/repos/tinyfists/actions-experiments/languages",
    "stargazers_url": "https://api.github.com/repos/tinyfists/actions-experiments/stargazers",
    "contributors_url": "https://api.github.com/repos/tinyfists/actions-experiments/contributors",
    "subscribers_url": "https://api.github.com/repos/tinyfists/actions-experiments/subscribers",
    "subscription_url": "https://api.github.com/repos/tinyfists/actions-experiments/subscription",
    "commits_url": "https://api.github.com/repos/tinyfists/actions-experiments/commits{/sha}",
    "git_commits_url": "https://api.github.com/repos/tinyfists/actions-experiments/git/commits{/sha}",
    "comments_url": "https://api.github.com/repos/tinyfists/actions-experiments/comments{/number}",
    "issue_comment_url": "https://api.github.com/repos/tinyfists/actions-experiments/issues/comments{/number}",
    "contents_url": "https://api.github.com/repos/tinyfists/actions-experiments/contents/{+path}",
    "compare_url": "https://api.github.com/repos/tinyfists/actions-experiments/compare/{base}...{head}",
    "merges_url": "https://api.github.com/repos/tinyfists/actions-experiments/merges",
    "archive_url": "https://api.github.com/repos/tinyfists/actions-experiments/{archive_format}{/ref}",
    "downloads_url": "https://api.github.com/repos/tinyfists/actions-experiments/downloads",
    "issues_url": "https://api.github.com/repos/tinyfists/actions-experiments/issues{/number}",
    "pulls_url": "https://api.github.com/repos/tinyfists/actions-experiments/pulls{/number}",
    "milestones_url": "https://api.github.com/repos/tinyfists/actions-experiments/milestones{/number}",
    "notifications_url": "https://api.github.com/repos/tinyfists/actions-experiments/notifications{?since,all,participating}",
    "labels_url": "https://api.github.com/repos/tinyfists/actions-experiments/labels{/name}",
    "releases_url": "https://api.github.com/repos/tinyfists/actions-experiments/releases{/id}",
    "deployments_url": "https://api.github.com/repos/tinyfists/actions-experiments/deployments"
  }
}

* Request took 198.621417ms
⣻* Request at 2024-07-30 14:54:02.174297 -0400 EDT m=+0.323885209
* Request to https://api.github.com/repos/tinyfists/actions-experiments/actions/workflows/109990972
> GET /repos/tinyfists/actions-experiments/actions/workflows/109990972 HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Type: application/json; charset=utf-8
> Time-Zone: America/New_York
> User-Agent: GitHub CLI 2.52.0

⢿< HTTP/2.0 200 OK
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Cache-Control: private, max-age=60, s-maxage=60
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Tue, 30 Jul 2024 18:54:02 GMT
< Etag: W/"9a90c525307654e4889f544427d7871fda0d09e345d2dcca932c25e11b7190cc"
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Oauth-Scopes: 
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Api-Version-Selected: 2022-11-28
< X-Github-Media-Type: github.v3; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: E3D3:ED6FF:6FA994:D0763D:66A936CA
< X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
< X-Oauth-Scopes: codespace, gist, project, read:org, repo, workflow
< X-Ratelimit-Limit: 15000
< X-Ratelimit-Remaining: 14983
< X-Ratelimit-Reset: 1722369142
< X-Ratelimit-Resource: core
< X-Ratelimit-Used: 17
< X-Xss-Protection: 0

{
  "id": 109990972,
  "node_id": "W_kwDOIVGots4GjlQ8",
  "name": ".github/workflows/cli-9221.yml",
  "path": ".github/workflows/cli-9221.yml",
  "state": "active",
  "created_at": "2024-07-30T14:50:58.000-04:00",
  "updated_at": "2024-07-30T14:50:58.000-04:00",
  "url": "https://api.github.com/repos/tinyfists/actions-experiments/actions/workflows/109990972",
  "html_url": "https://github.com/tinyfists/actions-experiments/blob/main/.github/workflows/cli-9221.yml",
  "badge_url": "https://github.com/tinyfists/actions-experiments/workflows/.github/workflows/cli-9221.yml/badge.svg"
}

* Request took 115.547917ms
* Request at 2024-07-30 14:54:02.336669 -0400 EDT m=+0.486256959
* Request to https://api.github.com/repos/tinyfists/actions-experiments/actions/runs/10168088985/rerun-failed-jobs
> POST /repos/tinyfists/actions-experiments/actions/runs/10168088985/rerun-failed-jobs HTTP/1.1
> Host: api.github.com
> Accept: application/vnd.github.merge-info-preview+json, application/vnd.github.nebula-preview
> Authorization: token ████████████████████
> Content-Type: application/json; charset=utf-8
> Time-Zone: America/New_York
> User-Agent: GitHub CLI 2.52.0

< HTTP/2.0 403 Forbidden
< Access-Control-Allow-Origin: *
< Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset
< Content-Security-Policy: default-src 'none'
< Content-Type: application/json; charset=utf-8
< Date: Tue, 30 Jul 2024 18:54:02 GMT
< Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
< Server: github.com
< Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
< Vary: Accept-Encoding, Accept, X-Requested-With
< X-Accepted-Oauth-Scopes: 
< X-Content-Type-Options: nosniff
< X-Frame-Options: deny
< X-Github-Api-Version-Selected: 2022-11-28
< X-Github-Media-Type: github.v3; param=merge-info-preview.nebula-preview; format=json
< X-Github-Request-Id: E3D3:ED6FF:6FAA10:D07726:66A936CA
< X-Oauth-Client-Id: 178c6fc778ccc68e1d6a
< X-Oauth-Scopes: codespace, gist, project, read:org, repo, workflow
< X-Ratelimit-Limit: 15000
< X-Ratelimit-Remaining: 14982
< X-Ratelimit-Reset: 1722369142
< X-Ratelimit-Resource: core
< X-Ratelimit-Used: 18
< X-Xss-Protection: 0

{
  "message": "This workflow is already running",
  "documentation_url": "https://docs.github.com/rest/actions/workflow-runs#re-run-failed-jobs-from-a-workflow-run",
  "status": "403"
}

* Request took 118.392833ms
run 10168088985 cannot be rerun; its workflow file may be broken
```

</p>
</details> 

I believe the issue can be tracked back to the following code snippet; I think the `message` from the response should be used / provided to the user:

https://github.com/cli/cli/blob/b05bddc210a1e45a25b76f9b320239e4c36f03ee/pkg/cmd/run/rerun/rerun.go#L199-L208@artemisart: I had the same issue, see https://github.com/orgs/community/discussions/67654#discussioncomment-8038649 for a workaround: calling a second workflow to wait and rerun the first one.@williammartin: @andyfeller this looks very similar to https://github.com/cli/cli/commit/b41b77165bc8d0d6cdf7a031c62d2e09c3160aa7, we should probably just remove the:

```
 fmt.Errorf("run %d cannot be rerun; its workflow file may be broken", run.ID)
```

And return whatever the API does. This seems like a recipe for confusion as is.

Solution patch:
diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml
index 850cc19b7..d5cc67ec9 100644
--- a/.github/workflows/deployment.yml
+++ b/.github/workflows/deployment.yml
@@ -384,7 +384,7 @@ jobs:
             git diff --name-status @{upstream}..
           fi
       - name: Bump homebrew-core formula
-        uses: mislav/bump-homebrew-formula-action@942e550c6344cfdb9e1ab29b9bb9bf0c43efa19b
+        uses: mislav/bump-homebrew-formula-action@8e2baa47daaa8db10fcdeb04105dfa6850eb0d68
         if: inputs.environment == 'production' && !contains(inputs.tag_name, '-')
         with:
           formula-name: gh
diff --git a/.github/workflows/homebrew-bump.yml b/.github/workflows/homebrew-bump.yml
index 228f1a345..0b42803aa 100644
--- a/.github/workflows/homebrew-bump.yml
+++ b/.github/workflows/homebrew-bump.yml
@@ -17,7 +17,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Bump homebrew-core formula
-        uses: mislav/bump-homebrew-formula-action@942e550c6344cfdb9e1ab29b9bb9bf0c43efa19b
+        uses: mislav/bump-homebrew-formula-action@8e2baa47daaa8db10fcdeb04105dfa6850eb0d68
         if: inputs.environment == 'production' && !contains(inputs.tag_name, '-')
         with:
           formula-name: gh
diff --git a/README.md b/README.md
index cefe1abb0..8257cf566 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ For [installation options see below](#installation), for usage instructions [see
 
 ## Contributing
 
-If anything feels off, or if you feel that some functionality is missing, please check out the [contributing page][contributing]. There you will find instructions for sharing your feedback, building the tool locally, and submitting pull requests to the project.
+If anything feels off or if you feel that some functionality is missing, please check out the [contributing page][contributing]. There you will find instructions for sharing your feedback, building the tool locally, and submitting pull requests to the project.
 
 If you are a hubber and are interested in shipping new commands for the CLI, check out our [doc on internal contributions][intake-doc].
 
@@ -58,7 +58,7 @@ Additional Conda installation options available on the [gh-feedstock page](https
 | ----------------------------------- | ---------------- |
 | `curl -sS https://webi.sh/gh \| sh` | `webi gh@stable` |
 
-For more information about the Webi installer see [its homepage](https://webinstall.dev/).
+For more information about the Webi installer, see [its homepage](https://webinstall.dev/).
 
 #### Flox
 
@@ -127,9 +127,9 @@ Download packaged binaries from the [releases page][].
 
 #### Verification of binaries
 
-Since version 2.50.0 `gh` has been producing [Build Provenance Attestation](https://github.blog/changelog/2024-06-25-artifact-attestations-is-generally-available/) enabling a cryptographically verifiable paper-trail back to the origin GitHub repository, git revision and build instructions used. The build provenance attestations are signed and relies on Public Good [Sigstore](https://www.sigstore.dev/) for PKI.
+Since version 2.50.0, `gh` has been producing [Build Provenance Attestation](https://github.blog/changelog/2024-06-25-artifact-attestations-is-generally-available/), enabling a cryptographically verifiable paper-trail back to the origin GitHub repository, git revision, and build instructions used. The build provenance attestations are signed and rely on Public Good [Sigstore](https://www.sigstore.dev/) for PKI.
 
-There are two common ways to verify a downloaded release, depending if `gh` is already installed or not. If `gh` is installed, it's trivial to verify a new release:
+There are two common ways to verify a downloaded release, depending on whether `gh` is already installed or not. If `gh` is installed, it's trivial to verify a new release:
 
 - **Option 1: Using `gh` if already installed:**
 
diff --git a/api/queries_issue.go b/api/queries_issue.go
index f09360152..24e0b4f4c 100644
--- a/api/queries_issue.go
+++ b/api/queries_issue.go
@@ -38,6 +38,7 @@ type Issue struct {
 	Comments         Comments
 	Author           Author
 	Assignees        Assignees
+	AssignedActors   AssignedActors
 	Labels           Labels
 	ProjectCards     ProjectCards
 	ProjectItems     ProjectItems
@@ -91,6 +92,61 @@ func (a Assignees) Logins() []string {
 	return logins
 }
 
+type AssignedActors struct {
+	Nodes      []Actor
+	TotalCount int
+}
+
+func (a AssignedActors) Logins() []string {
+	logins := make([]string, len(a.Nodes))
+	for i, a := range a.Nodes {
+		logins[i] = a.Login
+	}
+	return logins
+}
+
+// DisplayNames returns a list of display names for the assigned actors.
+func (a AssignedActors) DisplayNames() []string {
+	// These display names are used for populating the "default" assigned actors
+	// from the AssignedActors type. But, this is only one piece of the puzzle
+	// as later, other queries will fetch the full list of possible assignable
+	// actors from the repository, and the two lists will be reconciled.
+	//
+	// It's important that the display names are the same between the defaults
+	// (the values returned here) and the full list (the values returned by
+	// other repository queries). Any discrepancy would result in an
+	// "invalid default", which means an assigned actor will not be matched
+	// to an assignable actor and not presented as a "default" selection.
+	// Not being presented as a default would cause the actor to be potentially
+	// unassigned if the edits were submitted.
+	//
+	// To prevent this, we need shared logic to look up an actor's display name.
+	// However, our API types between assignedActors and the full list of
+	// assignableActors are different. So, as an attempt to maintain
+	// consistency we convert the assignedActors to the same types as the
+	// repository's assignableActors, treating the assignableActors DisplayName
+	// methods as the sources of truth.
+	// TODO KW: make this comment less of a wall of text if needed.
+	var displayNames []string
+	for _, a := range a.Nodes {
+		if a.TypeName == "User" {
+			u := NewAssignableUser(
+				a.ID,
+				a.Login,
+				a.Name,
+			)
+			displayNames = append(displayNames, u.DisplayName())
+		} else if a.TypeName == "Bot" {
+			b := NewAssignableBot(
+				a.ID,
+				a.Login,
+			)
+			displayNames = append(displayNames, b.DisplayName())
+		}
+	}
+	return displayNames
+}
+
 type Labels struct {
 	Nodes      []IssueLabel
 	TotalCount int
diff --git a/api/queries_pr.go b/api/queries_pr.go
index 5b941bb42..1d394e864 100644
--- a/api/queries_pr.go
+++ b/api/queries_pr.go
@@ -84,15 +84,26 @@ type PullRequest struct {
 	}
 
 	Assignees      Assignees
-	Labels         Labels
-	ProjectCards   ProjectCards
-	ProjectItems   ProjectItems
-	Milestone      *Milestone
-	Comments       Comments
-	ReactionGroups ReactionGroups
-	Reviews        PullRequestReviews
-	LatestReviews  PullRequestReviews
-	ReviewRequests ReviewRequests
+	AssignedActors AssignedActors
+	// AssignedActorsUsed is a GIGANTIC hack to carry around whether we expected AssignedActors to be requested
+	// on this PR. This is required because the Feature Detection of support for AssignedActors occurs inside the
+	// PR Finder, but knowledge of support is required at the command level. However, we can't easily construct
+	// the feature detector at the command level because it needs knowledge of the BaseRepo, which is only available
+	// inside the PR Finder. This is bad and we should feel bad.
+	//
+	// The right solution is to extract argument parsing from the PR Finder into each command, so that we have access
+	// to the BaseRepo and can construct the feature detector there. This is what happens in the issue commands with
+	// `shared.ParseIssueFromArg`.
+	AssignedActorsUsed bool
+	Labels             Labels
+	ProjectCards       ProjectCards
+	ProjectItems       ProjectItems
+	Milestone          *Milestone
+	Comments           Comments
+	ReactionGroups     ReactionGroups
+	Reviews            PullRequestReviews
+	LatestReviews      PullRequestReviews
+	ReviewRequests     ReviewRequests
 
 	ClosingIssuesReferences ClosingIssuesReferences
 }
diff --git a/api/queries_repo.go b/api/queries_repo.go
index 93a32d80c..efbcfcb19 100644
--- a/api/queries_repo.go
+++ b/api/queries_repo.go
@@ -146,6 +146,18 @@ type GitHubUser struct {
 	Name  string `json:"name"`
 }
 
+// Actor is a superset of User and Bot, among others.
+// At the time of writing, some of these fields
+// are not directly supported by the Actor type and
+// instead are only available on the User or Bot types
+// directly.
+type Actor struct {
+	ID       string `json:"id"`
+	Login    string `json:"login"`
+	Name     string `json:"name"`
+	TypeName string `json:"__typename"`
+}
+
 // BranchRef is the branch name in a GitHub repository
 type BranchRef struct {
 	Name string `json:"name"`
@@ -674,13 +686,14 @@ func RepoFindForks(client *Client, repo ghrepo.Interface, limit int) ([]*Reposit
 }
 
 type RepoMetadataResult struct {
-	CurrentLogin    string
-	AssignableUsers []RepoAssignee
-	Labels          []RepoLabel
-	Projects        []RepoProject
-	ProjectsV2      []ProjectV2
-	Milestones      []RepoMilestone
-	Teams           []OrgTeam
+	CurrentLogin     string
+	AssignableUsers  []AssignableUser
+	AssignableActors []AssignableActor
+	Labels           []RepoLabel
+	Projects         []RepoProject
+	ProjectsV2       []ProjectV2
+	Milestones       []RepoMilestone
+	Teams            []OrgTeam
 }
 
 func (m *RepoMetadataResult) MembersToIDs(names []string) ([]string, error) {
@@ -688,12 +701,27 @@ func (m *RepoMetadataResult) MembersToIDs(names []string) ([]string, error) {
 	for _, assigneeLogin := range names {
 		found := false
 		for _, u := range m.AssignableUsers {
-			if strings.EqualFold(assigneeLogin, u.Login) {
-				ids = append(ids, u.ID)
+			if strings.EqualFold(assigneeLogin, u.Login()) {
+				ids = append(ids, u.ID())
+				found = true
+				break
+			}
+		}
+
+		// Look for ID in assignable actors if not found in assignable users
+		for _, a := range m.AssignableActors {
+			if strings.EqualFold(assigneeLogin, a.Login()) {
+				ids = append(ids, a.ID())
+				found = true
+				break
+			}
+			if strings.EqualFold(assigneeLogin, a.DisplayName()) {
+				ids = append(ids, a.ID())
 				found = true
 				break
 			}
 		}
+
 		if !found {
 			return nil, fmt.Errorf("'%s' not found", assigneeLogin)
 		}
@@ -885,12 +913,13 @@ func (m *RepoMetadataResult) Merge(m2 *RepoMetadataResult) {
 }
 
 type RepoMetadataInput struct {
-	Assignees  bool
-	Reviewers  bool
-	Labels     bool
-	ProjectsV1 bool
-	ProjectsV2 bool
-	Milestones bool
+	Assignees      bool
+	ActorAssignees bool
+	Reviewers      bool
+	Labels         bool
+	ProjectsV1     bool
+	ProjectsV2     bool
+	Milestones     bool
 }
 
 // RepoMetadata pre-fetches the metadata for attaching to issues and pull requests
@@ -899,14 +928,37 @@ func RepoMetadata(client *Client, repo ghrepo.Interface, input RepoMetadataInput
 	var g errgroup.Group
 
 	if input.Assignees || input.Reviewers {
-		g.Go(func() error {
-			users, err := RepoAssignableUsers(client, repo)
-			if err != nil {
-				err = fmt.Errorf("error fetching assignees: %w", err)
-			}
-			result.AssignableUsers = users
-			return err
-		})
+		if input.ActorAssignees {
+			g.Go(func() error {
+				actors, err := RepoAssignableActors(client, repo)
+				if err != nil {
+					return fmt.Errorf("error fetching assignable actors: %w", err)
+				}
+				result.AssignableActors = actors
+
+				// Filter actors for users to use for pull request reviewers,
+				// skip retrieving the same info through RepoAssignableUsers().
+				var users []AssignableUser
+				for _, a := range actors {
+					if _, ok := a.(AssignableUser); !ok {
+						continue
+					}
+					users = append(users, a.(AssignableUser))
+				}
+				result.AssignableUsers = users
+				return nil
+			})
+		} else {
+			// Not using Actors, fetch legacy assignable users.
+			g.Go(func() error {
+				users, err := RepoAssignableUsers(client, repo)
+				if err != nil {
+					err = fmt.Errorf("error fetching assignable users: %w", err)
+				}
+				result.AssignableUsers = users
+				return err
+			})
+		}
 	}
 
 	if input.Reviewers {
@@ -1070,12 +1122,16 @@ func RepoResolveMetadataIDs(client *Client, repo ghrepo.Interface, input RepoRes
 				result.Teams = append(result.Teams, t)
 			}
 		default:
-			user := RepoAssignee{}
+			user := struct {
+				Id    string
+				Login string
+				Name  string
+			}{}
 			err := json.Unmarshal(v, &user)
 			if err != nil {
 				return result, err
 			}
-			result.AssignableUsers = append(result.AssignableUsers, user)
+			result.AssignableUsers = append(result.AssignableUsers, NewAssignableUser(user.Id, user.Login, user.Name))
 		}
 	}
 
@@ -1127,26 +1183,99 @@ func RepoProjects(client *Client, repo ghrepo.Interface) ([]RepoProject, error)
 	return projects, nil
 }
 
-type RepoAssignee struct {
-	ID    string
-	Login string
-	Name  string
+// Expected login for Copilot when retrieved as an Actor
+// This is returned from assignable actors and issue/pr assigned actors.
+// We use this to check if the actor is Copilot.
+const CopilotActorLogin = "copilot-swe-agent"
+
+type AssignableActor interface {
+	DisplayName() string
+	ID() string
+	Login() string
+
+	sealedAssignableActor()
+}
+
+// Always a user
+type AssignableUser struct {
+	id    string
+	login string
+	name  string
+}
+
+func NewAssignableUser(id, login, name string) AssignableUser {
+	return AssignableUser{
+		id:    id,
+		login: login,
+		name:  name,
+	}
 }
 
 // DisplayName returns a formatted string that uses Login and Name to be displayed e.g. 'Login (Name)' or 'Login'
-func (ra RepoAssignee) DisplayName() string {
-	if ra.Name != "" {
-		return fmt.Sprintf("%s (%s)", ra.Login, ra.Name)
+func (u AssignableUser) DisplayName() string {
+	if u.name != "" {
+		return fmt.Sprintf("%s (%s)", u.login, u.name)
+	}
+	return u.login
+}
+
+func (u AssignableUser) ID() string {
+	return u.id
+}
+
+func (u AssignableUser) Login() string {
+	return u.login
+}
+
+func (u AssignableUser) Name() string {
+	return u.name
+}
+
+func (u AssignableUser) sealedAssignableActor() {}
+
+type AssignableBot struct {
+	id    string
+	login string
+}
+
+func NewAssignableBot(id, login string) AssignableBot {
+	return AssignableBot{
+		id:    id,
+		login: login,
+	}
+}
+
+func (b AssignableBot) DisplayName() string {
+	if b.login == CopilotActorLogin {
+		return "Copilot (AI)"
 	}
-	return ra.Login
+	return b.Login()
+}
+
+func (b AssignableBot) ID() string {
+	return b.id
 }
 
+func (b AssignableBot) Login() string {
+	return b.login
+}
+
+func (b AssignableBot) Name() string {
+	return ""
+}
+
+func (b AssignableBot) sealedAssignableActor() {}
+
 // RepoAssignableUsers fetches all the assignable users for a repository
-func RepoAssignableUsers(client *Client, repo ghrepo.Interface) ([]RepoAssignee, error) {
+func RepoAssignableUsers(client *Client, repo ghrepo.Interface) ([]AssignableUser, error) {
 	type responseData struct {
 		Repository struct {
 			AssignableUsers struct {
-				Nodes    []RepoAssignee
+				Nodes []struct {
+					ID    string
+					Login string
+					Name  string
+				}
 				PageInfo struct {
 					HasNextPage bool
 					EndCursor   string
@@ -1161,7 +1290,7 @@ func RepoAssignableUsers(client *Client, repo ghrepo.Interface) ([]RepoAssignee,
 		"endCursor": (*githubv4.String)(nil),
 	}
 
-	var users []RepoAssignee
+	var users []AssignableUser
 	for {
 		var query responseData
 		err := client.Query(repo.RepoHost(), "RepositoryAssignableUsers", &query, variables)
@@ -1169,7 +1298,15 @@ func RepoAssignableUsers(client *Client, repo ghrepo.Interface) ([]RepoAssignee,
 			return nil, err
 		}
 
-		users = append(users, query.Repository.AssignableUsers.Nodes...)
+		for _, node := range query.Repository.AssignableUsers.Nodes {
+			user := AssignableUser{
+				id:    node.ID,
+				login: node.Login,
+				name:  node.Name,
+			}
+
+			users = append(users, user)
+		}
 		if !query.Repository.AssignableUsers.PageInfo.HasNextPage {
 			break
 		}
@@ -1179,6 +1316,72 @@ func RepoAssignableUsers(client *Client, repo ghrepo.Interface) ([]RepoAssignee,
 	return users, nil
 }
 
+// RepoAssignableActors fetches all the assignable actors for a repository on
+// GitHub hosts that support Actor assignees.
+func RepoAssignableActors(client *Client, repo ghrepo.Interface) ([]AssignableActor, error) {
+	type responseData struct {
+		Repository struct {
+			SuggestedActors struct {
+				Nodes []struct {
+					User struct {
+						ID       string
+						Login    string
+						Name     string
+						TypeName string `graphql:"__typename"`
+					} `graphql:"... on User"`
+					Bot struct {
+						ID       string
+						Login    string
+						TypeName string `graphql:"__typename"`
+					} `graphql:"... on Bot"`
+				}
+				PageInfo struct {
+					HasNextPage bool
+					EndCursor   string
+				}
+			} `graphql:"suggestedActors(first: 100, after: $endCursor, capabilities: CAN_BE_ASSIGNED)"`
+		} `graphql:"repository(owner: $owner, name: $name)"`
+	}
+
+	variables := map[string]interface{}{
+		"owner":     githubv4.String(repo.RepoOwner()),
+		"name":      githubv4.String(repo.RepoName()),
+		"endCursor": (*githubv4.String)(nil),
+	}
+
+	var actors []AssignableActor
+	for {
+		var query responseData
+		err := client.Query(repo.RepoHost(), "RepositoryAssignableActors", &query, variables)
+		if err != nil {
+			return nil, err
+		}
+
+		for _, node := range query.Repository.SuggestedActors.Nodes {
+			if node.User.TypeName == "User" {
+				actor := AssignableUser{
+					id:    node.User.ID,
+					login: node.User.Login,
+					name:  node.User.Name,
+				}
+				actors = append(actors, actor)
+			} else if node.Bot.TypeName == "Bot" {
+				actor := AssignableBot{
+					id:    node.Bot.ID,
+					login: node.Bot.Login,
+				}
+				actors = append(actors, actor)
+			}
+		}
+
+		if !query.Repository.SuggestedActors.PageInfo.HasNextPage {
+			break
+		}
+		variables["endCursor"] = githubv4.String(query.Repository.SuggestedActors.PageInfo.EndCursor)
+	}
+	return actors, nil
+}
+
 type RepoLabel struct {
 	ID   string
 	Name string
diff --git a/api/query_builder.go b/api/query_builder.go
index 47fb4c225..a2432673b 100644
--- a/api/query_builder.go
+++ b/api/query_builder.go
@@ -20,6 +20,25 @@ func shortenQuery(q string) string {
 	return strings.Map(squeeze, q)
 }
 
+var assignedActors = shortenQuery(`
+	assignedActors(first: 10) {
+		nodes {
+			...on User {
+				id,
+				login,
+				name,
+				__typename
+			}
+			...on Bot {
+				id,
+				login,
+				__typename
+			}
+		},
+		totalCount
+	}
+`)
+
 var issueComments = shortenQuery(`
 	comments(first: 100) {
 		nodes {
@@ -366,6 +385,8 @@ func IssueGraphQL(fields []string) string {
 			q = append(q, `headRepository{id,name}`)
 		case "assignees":
 			q = append(q, `assignees(first:100){nodes{id,login,name},totalCount}`)
+		case "assignedActors":
+			q = append(q, assignedActors)
 		case "labels":
 			q = append(q, `labels(first:100){nodes{id,name,description,color},totalCount}`)
 		case "projectCards":
diff --git a/go.mod b/go.mod
index bf8b033fe..289fd83a5 100644
--- a/go.mod
+++ b/go.mod
@@ -10,10 +10,11 @@ require (
 	github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2
 	github.com/briandowns/spinner v1.18.1
 	github.com/cenkalti/backoff/v4 v4.3.0
+	github.com/cenkalti/backoff/v5 v5.0.2
 	github.com/charmbracelet/glamour v0.9.2-0.20250319212134-549f544650e3
 	github.com/charmbracelet/huh v0.7.0
 	github.com/charmbracelet/lipgloss v1.1.1-0.20250319133953-166f707985bc
-	github.com/cli/go-gh/v2 v2.12.0
+	github.com/cli/go-gh/v2 v2.12.1
 	github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24
 	github.com/cli/oauth v1.1.1
 	github.com/cli/safeexec v1.0.1
@@ -21,7 +22,7 @@ require (
 	github.com/creack/pty v1.1.24
 	github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7
 	github.com/distribution/reference v0.6.0
-	github.com/gabriel-vasile/mimetype v1.4.8
+	github.com/gabriel-vasile/mimetype v1.4.9
 	github.com/gdamore/tcell/v2 v2.5.4
 	github.com/golang/snappy v0.0.4
 	github.com/google/go-cmp v0.7.0
@@ -43,17 +44,19 @@ require (
 	github.com/opentracing/opentracing-go v1.2.0
 	github.com/rivo/tview v0.0.0-20221029100920-c4a7e501810d
 	github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc
-	github.com/sigstore/protobuf-specs v0.4.1
-	github.com/sigstore/sigstore-go v0.7.2
+	github.com/sigstore/protobuf-specs v0.4.3
+	github.com/sigstore/sigstore-go v1.0.0
 	github.com/spf13/cobra v1.9.1
 	github.com/spf13/pflag v1.0.6
 	github.com/stretchr/testify v1.10.0
+	github.com/theupdateframework/go-tuf/v2 v2.1.1
+	github.com/yuin/goldmark v1.7.12
 	github.com/zalando/go-keyring v0.2.5
-	golang.org/x/crypto v0.37.0
-	golang.org/x/sync v0.13.0
-	golang.org/x/term v0.31.0
-	golang.org/x/text v0.24.0
-	google.golang.org/grpc v1.71.1
+	golang.org/x/crypto v0.38.0
+	golang.org/x/sync v0.14.0
+	golang.org/x/term v0.32.0
+	golang.org/x/text v0.25.0
+	google.golang.org/grpc v1.72.2
 	google.golang.org/protobuf v1.36.6
 	gopkg.in/h2non/gock.v1 v1.1.2
 	gopkg.in/yaml.v3 v3.0.1
@@ -154,9 +157,9 @@ require (
 	github.com/shibumi/go-pathspec v1.3.0 // indirect
 	github.com/shopspring/decimal v1.4.0 // indirect
 	github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect
-	github.com/sigstore/rekor v1.3.9 // indirect
-	github.com/sigstore/sigstore v1.9.1 // indirect
-	github.com/sigstore/timestamp-authority v1.2.5 // indirect
+	github.com/sigstore/rekor v1.3.10 // indirect
+	github.com/sigstore/sigstore v1.9.4 // indirect
+	github.com/sigstore/timestamp-authority v1.2.7 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/sourcegraph/conc v0.3.0 // indirect
 	github.com/spf13/afero v1.12.0 // indirect
@@ -165,27 +168,25 @@ require (
 	github.com/stretchr/objx v0.5.2 // indirect
 	github.com/subosito/gotenv v1.6.0 // indirect
 	github.com/theupdateframework/go-tuf v0.7.0 // indirect
-	github.com/theupdateframework/go-tuf/v2 v2.0.2 // indirect
 	github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e // indirect
 	github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
 	github.com/transparency-dev/merkle v0.0.2 // indirect
 	github.com/vbatts/tar-split v0.11.6 // indirect
 	github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
-	github.com/yuin/goldmark v1.7.8 // indirect
 	github.com/yuin/goldmark-emoji v1.0.5 // indirect
 	go.mongodb.org/mongo-driver v1.14.0 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
-	go.opentelemetry.io/otel v1.34.0 // indirect
-	go.opentelemetry.io/otel/metric v1.34.0 // indirect
-	go.opentelemetry.io/otel/trace v1.34.0 // indirect
+	go.opentelemetry.io/otel v1.35.0 // indirect
+	go.opentelemetry.io/otel/metric v1.35.0 // indirect
+	go.opentelemetry.io/otel/trace v1.35.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.27.0 // indirect
-	golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
+	golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
 	golang.org/x/mod v0.24.0 // indirect
-	golang.org/x/net v0.38.0 // indirect
-	golang.org/x/sys v0.32.0 // indirect
+	golang.org/x/net v0.40.0 // indirect
+	golang.org/x/sys v0.33.0 // indirect
 	golang.org/x/tools v0.29.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
 	k8s.io/klog/v2 v2.130.1 // indirect
 )
diff --git a/go.sum b/go.sum
index e0ecad6a7..95f7ed75d 100644
--- a/go.sum
+++ b/go.sum
@@ -1,17 +1,17 @@
-cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME=
-cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc=
-cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps=
-cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8=
-cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
-cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
+cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
+cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
+cloud.google.com/go/auth v0.16.0 h1:Pd8P1s9WkcrBE2n/PhAwKsdrR35V3Sg2II9B+ndM3CU=
+cloud.google.com/go/auth v0.16.0/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
+cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
+cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
 cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
 cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
-cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM=
-cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM=
-cloud.google.com/go/kms v1.21.1 h1:r1Auo+jlfJSf8B7mUnVw5K0fI7jWyoUy65bV53VjKyk=
-cloud.google.com/go/kms v1.21.1/go.mod h1:s0wCyByc9LjTdCjG88toVs70U9W+cc6RKFc8zAqX7nE=
-cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q=
-cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY=
+cloud.google.com/go/iam v1.5.0 h1:QlLcVMhbLGOjRcGe6VTGGTyQib8dRLK2B/kYNV0+2xs=
+cloud.google.com/go/iam v1.5.0/go.mod h1:U+DOtKQltF/LxPEtcDLoobcsZMilSRwR7mgNL7knOpo=
+cloud.google.com/go/kms v1.21.2 h1:c/PRUSMNQ8zXrc1sdAUnsenWWaNXN+PzTXfXOcSFdoE=
+cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqjuWcI6w=
+cloud.google.com/go/longrunning v0.6.6 h1:XJNDo5MUfMM05xK3ewpbSdmt7R2Zw+aQEMbdQR65Rbw=
+cloud.google.com/go/longrunning v0.6.6/go.mod h1:hyeGJUrPHcx0u2Uu1UFSoYZLn4lkMrccJig0t4FI7yw=
 dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
 dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
@@ -20,18 +20,18 @@ github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjq
 github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM=
 github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
 github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w=
 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14=
 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
 github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
 github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
 github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
@@ -58,10 +58,10 @@ github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk
 github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
 github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
 github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
-github.com/aws/aws-sdk-go-v2/config v1.29.10 h1:yNjgjiGBp4GgaJrGythyBXg2wAs+Im9fSWIUwvi1CAc=
-github.com/aws/aws-sdk-go-v2/config v1.29.10/go.mod h1:A0mbLXSdtob/2t59n1X0iMkPQ5d+YzYZB4rwu7SZ7aA=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.63 h1:rv1V3kIJ14pdmTu01hwcMJ0WAERensSiD9rEWEBb1Tk=
-github.com/aws/aws-sdk-go-v2/credentials v1.17.63/go.mod h1:EJj+yDf0txT26Ulo0VWTavBl31hOsaeuMxIHu2m0suY=
+github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM=
+github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
 github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
@@ -74,14 +74,14 @@ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b
 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
 github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
-github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 h1:tecq7+mAav5byF+Mr+iONJnCBf4B4gon8RSp4BrweSc=
-github.com/aws/aws-sdk-go-v2/service/kms v1.38.1/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk=
-github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0=
-github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.2 h1:wK8O+j2dOolmpNVY1EWIbLgxrGCHJKVPm08Hv/u80M8=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.2/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc=
-github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
+github.com/aws/aws-sdk-go-v2/service/kms v1.38.3 h1:RivOtUH3eEu6SWnUMFHKAW4MqDOzWn1vGQ3S38Y5QMg=
+github.com/aws/aws-sdk-go-v2/service/kms v1.38.3/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk=
+github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
+github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
 github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
 github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
 github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
@@ -100,6 +100,8 @@ github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY=
 github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
+github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
@@ -135,8 +137,8 @@ github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJn
 github.com/cli/browser v1.0.0/go.mod h1:IEWkHYbLjkhtjwwWlwTHW2lGxeS5gezEQBMLTwDHf5Q=
 github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
 github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
-github.com/cli/go-gh/v2 v2.12.0 h1:PIurZ13fXbWDbr2//6ws4g4zDbryO+iDuTpiHgiV+6k=
-github.com/cli/go-gh/v2 v2.12.0/go.mod h1:+5aXmEOJsH9fc9mBHfincDwnS02j2AIA/DsTH0Bk5uw=
+github.com/cli/go-gh/v2 v2.12.1 h1:SVt1/afj5FRAythyMV3WJKaUfDNsxXTIe7arZbwTWKA=
+github.com/cli/go-gh/v2 v2.12.1/go.mod h1:+5aXmEOJsH9fc9mBHfincDwnS02j2AIA/DsTH0Bk5uw=
 github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24 h1:QDrhR4JA2n3ij9YQN0u5ZeuvRIIvsUGmf5yPlTS0w8E=
 github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24/go.mod h1:rr9GNING0onuVw8MnracQHn7PcchnFlP882Y0II2KZk=
 github.com/cli/oauth v1.1.1 h1:459gD3hSjlKX9B1uXBuiAMdpXBUQ9QGf/NDcCpoQxPs=
@@ -192,8 +194,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
 github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
 github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
-github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
+github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
+github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
 github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
 github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
 github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k=
@@ -227,8 +229,8 @@ github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZ
 github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
 github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
 github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
-github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
-github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
+github.com/go-sql-driver/mysql v1.9.1 h1:FrjNGn/BsJQjVRuSa8CBrM5BWA9BWoXXat3KrtSb/iI=
+github.com/go-sql-driver/mysql v1.9.1/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
 github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
 github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
 github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
@@ -253,8 +255,6 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
 github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
-github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w=
-github.com/google/tink/go v1.7.0/go.mod h1:GAUOd+QE3pgj9q8VKIGTCP33c/B7eb4NhxLcgTJZStM=
 github.com/google/trillian v1.7.1 h1:+zX8jLM3524bAMPS+VxaDIDgsMv3/ty6DuLWerHXcek=
 github.com/google/trillian v1.7.1/go.mod h1:E1UMAHqpZCA8AQdrKdWmHmtUfSeiD0sDWD1cv00Xa+c=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -415,8 +415,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
-github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
+github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
+github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
 github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
 github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
@@ -455,24 +455,24 @@ github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc h1:vH0NQbIDk+mJL
 github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8=
 github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
 github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
-github.com/sigstore/protobuf-specs v0.4.1 h1:5SsMqZbdkcO/DNHudaxuCUEjj6x29tS2Xby1BxGU7Zc=
-github.com/sigstore/protobuf-specs v0.4.1/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc=
-github.com/sigstore/rekor v1.3.9 h1:sUjRpKVh/hhgqGMs0t+TubgYsksArZ6poLEC3MsGAzU=
-github.com/sigstore/rekor v1.3.9/go.mod h1:xThNUhm6eNEmkJ/SiU/FVU7pLY2f380fSDZFsdDWlcM=
-github.com/sigstore/sigstore v1.9.1 h1:bNMsfFATsMPaagcf+uppLk4C9rQZ2dh5ysmCxQBYWaw=
-github.com/sigstore/sigstore v1.9.1/go.mod h1:zUoATYzR1J3rLNp3jmp4fzIJtWdhC3ZM6MnpcBtnsE4=
-github.com/sigstore/sigstore-go v0.7.2 h1:CN4xPasChSEb0QBMxMW5dLcXdA9KD4QiRyVnMkhXj6U=
-github.com/sigstore/sigstore-go v0.7.2/go.mod h1:AIRj4I3LC82qd07VFm3T2zXYiddxeBV1k/eoS8nTz0E=
-github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1 h1:/YcNq687WnXpIRXl04nLfJX741G4iW+w+7Nem2Zy0f4=
-github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1/go.mod h1:ApL9RpKsi7gkSYN0bMNdm/3jZ9EefxMmfYHfUmq2ZYM=
-github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1 h1:FnusXyTIInnwfIOzzl5PFilRm1I97dxMSOcCkZBu9Kc=
-github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1/go.mod h1:d5m5LOa/69a+t2YC9pDPwS1n2i/PhqB4cUKbpVDlKKE=
-github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1 h1:LFiYK1DEWQ6Hf/nroFzBMM+s5rVSjVL45Alpb5Ctl5A=
-github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1/go.mod h1:GFyFmDsE2wDuIHZD+4+JErGpA0S4zJsKNz5l2JVJd8s=
-github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1 h1:sIW6xe4yU5eIMH8fve2C78d+r29KmHnIb+7po+80bsY=
-github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1/go.mod h1:3pNf99GnK9eu3XUa5ebHzgEQSVYf9hqAoPFwbwD6O6M=
-github.com/sigstore/timestamp-authority v1.2.5 h1:W22JmwRv1Salr/NFFuP7iJuhytcZszQjldoB8GiEdnw=
-github.com/sigstore/timestamp-authority v1.2.5/go.mod h1:gWPKWq4HMWgPCETre0AakgBzcr9DRqHrsgbrRqsigOs=
+github.com/sigstore/protobuf-specs v0.4.3 h1:kRgJ+ciznipH9xhrkAbAEHuuxD3GhYnGC873gZpjJT4=
+github.com/sigstore/protobuf-specs v0.4.3/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc=
+github.com/sigstore/rekor v1.3.10 h1:/mSvRo4MZ/59ECIlARhyykAlQlkmeAQpvBPlmJtZOCU=
+github.com/sigstore/rekor v1.3.10/go.mod h1:JvryKJ40O0XA48MdzYUPu0y4fyvqt0C4iSY7ri9iu3A=
+github.com/sigstore/sigstore v1.9.4 h1:64+OGed80+A4mRlNzRd055vFcgBeDghjZw24rPLZgDU=
+github.com/sigstore/sigstore v1.9.4/go.mod h1:Q7tGTC3gbtK7c3jcxEmGc2MmK4rRpIRzi3bxRFWKvEY=
+github.com/sigstore/sigstore-go v1.0.0 h1:4N07S2zLxf09nTRwaPKyAxbKzpM8WJYUS8lWWaYxneU=
+github.com/sigstore/sigstore-go v1.0.0/go.mod h1:UYsZ/XHE4eltv1o1Lu+n6poW1Z5to3f0+emvfXNxIN8=
+github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.4 h1:kQqUJ1VuWdJltMkinFXAHTlJrzMRPoNgL+dy6WyJ/dA=
+github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.4/go.mod h1:9miLz7c69vj/7VH7UpCKHDia41HCTIDJWJWf4Ex5yUk=
+github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.4 h1:MHRm7YQuF4zFyoXRLgUdLaNxqVO6JlLGnkDUI9fm9ow=
+github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.4/go.mod h1:899VNYSSnQ0QtcuhkW0gznzxn0cqhowTL3nzc/xnym8=
+github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.4 h1:C2nSyTmTxpuamUmLCWWZwz+0Y1IQIig9XwAJ4UAn/SI=
+github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.4/go.mod h1:vjDahU0sEw/WMkKkygZNH72EMg86iaFNLAaJFXhItXU=
+github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.4 h1:t9yfb6yteIDv8CNRT6OHdqgTV6TSj+CdOtZP9dVhpsQ=
+github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.4/go.mod h1:m7sQxVJmDa+rsmS1m6biQxaLX83pzNS7ThUEyjOqkCU=
+github.com/sigstore/timestamp-authority v1.2.7 h1:HP/VT4wnL4uzP0fVo3eHXlt0reuNgW3PLt78+BV0I5I=
+github.com/sigstore/timestamp-authority v1.2.7/go.mod h1:te4ThQ3Q/CX1bzVsf5mMN0K7Z/cgc2OcoEGxAJiFqqI=
 github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
 github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
@@ -499,16 +499,18 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
 github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=
 github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug=
-github.com/theupdateframework/go-tuf/v2 v2.0.2 h1:PyNnjV9BJNzN1ZE6BcWK+5JbF+if370jjzO84SS+Ebo=
-github.com/theupdateframework/go-tuf/v2 v2.0.2/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA=
+github.com/theupdateframework/go-tuf/v2 v2.1.1 h1:OWcoHItwsGO+7m0wLa7FDWPR4oB1cj0zOr1kosE4G+I=
+github.com/theupdateframework/go-tuf/v2 v2.1.1/go.mod h1:V675cQGhZONR0OGQ8r1feO0uwtsTBYPDWHzAAPn5rjE=
 github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8=
 github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI=
 github.com/tink-crypto/tink-go-awskms/v2 v2.1.0 h1:N9UxlsOzu5mttdjhxkDLbzwtEecuXmlxZVo/ds7JKJI=
 github.com/tink-crypto/tink-go-awskms/v2 v2.1.0/go.mod h1:PxSp9GlOkKL9rlybW804uspnHuO9nbD98V/fDX4uSis=
 github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0=
 github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw=
-github.com/tink-crypto/tink-go/v2 v2.3.0 h1:4/TA0lw0lA/iVKBL9f8R5eP7397bfc4antAMXF5JRhs=
-github.com/tink-crypto/tink-go/v2 v2.3.0/go.mod h1:kfPOtXIadHlekBTeBtJrHWqoGL+Fm3JQg0wtltPuxLU=
+github.com/tink-crypto/tink-go-hcvault/v2 v2.3.0 h1:6nAX1aRGnkg2SEUMwO5toB2tQkP0Jd6cbmZ/K5Le1V0=
+github.com/tink-crypto/tink-go-hcvault/v2 v2.3.0/go.mod h1:HOC5NWW1wBI2Vke1FGcRBvDATkEYE7AUDiYbXqi2sBw=
+github.com/tink-crypto/tink-go/v2 v2.4.0 h1:8VPZeZI4EeZ8P/vB6SIkhlStrJfivTJn+cQ4dtyHNh0=
+github.com/tink-crypto/tink-go/v2 v2.4.0/go.mod h1:l//evrF2Y3MjdbpNDNGnKgCpo5zSmvUvnQ4MU+yE2sw=
 github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
 github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
 github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4=
@@ -519,8 +521,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
-github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
-github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
+github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
+github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
 github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
 github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
 github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8=
@@ -529,22 +531,22 @@ go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd
 go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
-go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
-go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
-go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
-go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
 go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
 go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
 go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
 go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
-go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
-go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
-go.step.sm/crypto v0.60.0 h1:UgSw8DFG5xUOGB3GUID17UA32G4j1iNQ4qoMhBmsVFw=
-go.step.sm/crypto v0.60.0/go.mod h1:Ep83Lv818L4gV0vhFTdPWRKnL6/5fRMpi8SaoP5ArSw=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
+go.step.sm/crypto v0.63.0 h1:U1QGELQqJ85oDfeNFE2V52cow1rvy0m3MekG3wFmyXY=
+go.step.sm/crypto v0.63.0/go.mod h1:aj3LETmCZeSil1DMq3BlbhDBcN86+mmKrHZtXWyc0L4=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -553,24 +555,24 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
 go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
-golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
-golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
-golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
+golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
+golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
+golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
+golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
 golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
-golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
-golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
-golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
+golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
+golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
+golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
+golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
-golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
+golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -581,19 +583,19 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
-golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
+golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
-golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
+golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
+golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
-golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
+golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
+golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
 golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
 golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -602,16 +604,16 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
 golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
 golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc=
-google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ=
+google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM=
+google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=
 google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
 google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
-google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
-google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
-google.golang.org/grpc v1.71.1 h1:ffsFWr7ygTUscGPI0KKK6TLrGz0476KUvvsbqWK0rPI=
-google.golang.org/grpc v1.71.1/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
+google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e h1:UdXH7Kzbj+Vzastr5nVfccbmFsmYNygVLSPk1pEfDoY=
+google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go.mod h1:085qFyf2+XaZlRdCgKNCIZ3afY2p4HHZdoIRpId8F4A=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
+google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
+google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
 google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
 google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/internal/docs/markdown.go b/internal/docs/markdown.go
index 05d8686b8..7ae8c6862 100644
--- a/internal/docs/markdown.go
+++ b/internal/docs/markdown.go
@@ -142,8 +142,7 @@ func genMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string)
 		fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.UseLine())
 	}
 	if hasLong {
-		longWithEscapedPipe := strings.ReplaceAll(cmd.Long, "|", "&#124;")
-		fmt.Fprintf(w, "%s\n\n", longWithEscapedPipe)
+		fmt.Fprintf(w, "%s\n\n", cmd.Long)
 	}
 
 	for _, g := range root.GroupedCommands(cmd) {
diff --git a/internal/featuredetection/feature_detection.go b/internal/featuredetection/feature_detection.go
index fba317f58..a2f34a60b 100644
--- a/internal/featuredetection/feature_detection.go
+++ b/internal/featuredetection/feature_detection.go
@@ -18,11 +18,13 @@ type Detector interface {
 }
 
 type IssueFeatures struct {
-	StateReason bool
+	StateReason       bool
+	ActorIsAssignable bool
 }
 
 var allIssueFeatures = IssueFeatures{
-	StateReason: true,
+	StateReason:       true,
+	ActorIsAssignable: true,
 }
 
 type PullRequestFeatures struct {
@@ -70,7 +72,8 @@ func (d *detector) IssueFeatures() (IssueFeatures, error) {
 	}
 
 	features := IssueFeatures{
-		StateReason: false,
+		StateReason:       false,
+		ActorIsAssignable: false, // replaceActorsForAssignable GraphQL mutation unavailable on GHES
 	}
 
 	var featureDetection struct {
diff --git a/internal/ghcmd/cmd.go b/internal/ghcmd/cmd.go
index bd901b78e..9fc5bcefe 100644
--- a/internal/ghcmd/cmd.go
+++ b/internal/ghcmd/cmd.go
@@ -29,14 +29,6 @@ import (
 	"github.com/spf13/cobra"
 )
 
-// updaterEnabled is a statically linked build property set in gh formula within homebrew/homebrew-core
-// used to control whether users are notified of newer GitHub CLI releases.
-// This needs to be set to 'cli/cli' as it affects where update.CheckForUpdate() checks for releases.
-// It is unclear whether this means that only homebrew builds will check for updates or not.
-// Development builds leave this empty as impossible to determine if newer or not.
-// For more information, <https://github.com/Homebrew/homebrew-core/blob/master/Formula/g/gh.rb>.
-var updaterEnabled = ""
-
 type exitCode int
 
 const (
diff --git a/internal/ghcmd/update_disabled.go b/internal/ghcmd/update_disabled.go
new file mode 100644
index 000000000..3d9fa4e57
--- /dev/null
+++ b/internal/ghcmd/update_disabled.go
@@ -0,0 +1,6 @@
+//go:build !updateable
+
+package ghcmd
+
+// See update_enabled.go comment for more information.
+var updaterEnabled = ""
diff --git a/internal/ghcmd/update_enabled.go b/internal/ghcmd/update_enabled.go
new file mode 100644
index 000000000..3eb9eba4f
--- /dev/null
+++ b/internal/ghcmd/update_enabled.go
@@ -0,0 +1,18 @@
+//go:build updateable
+
+package ghcmd
+
+// `updateable` is a build tag set in the gh formula within homebrew/homebrew-core
+// and is used to control whether users are notified of newer GitHub CLI releases.
+//
+// Currently, updaterEnabled needs to be set to 'cli/cli' as it affects where
+// update.CheckForUpdate() checks for releases. It is unclear to what extent
+// this updaterEnabled is being used by unofficial forks or builds, so we decided
+// to leave it available for injection as a string variable for now.
+//
+// Development builds do not generate update messages by default.
+//
+// For more information, see:
+// - the Homebrew formula for gh: <https://github.com/Homebrew/homebrew-core/blob/master/Formula/g/gh.rb>.
+// - a discussion about adding this build tag: <https://github.com/cli/cli/pull/11024#discussion_r2107597618>.
+var updaterEnabled = "cli/cli"
diff --git a/pkg/cmd/config/config.go b/pkg/cmd/config/config.go
index 66051f83a..5f8242d2a 100644
--- a/pkg/cmd/config/config.go
+++ b/pkg/cmd/config/config.go
@@ -16,14 +16,14 @@ import (
 func NewCmdConfig(f *cmdutil.Factory) *cobra.Command {
 	longDoc := strings.Builder{}
 	longDoc.WriteString("Display or change configuration settings for gh.\n\n")
-	longDoc.WriteString("Current respected settings:\n\n")
+	longDoc.WriteString("Current respected settings:\n")
 	for _, co := range config.Options {
 		longDoc.WriteString(fmt.Sprintf("- `%s`: %s", co.Key, co.Description))
 		if len(co.AllowedValues) > 0 {
-			longDoc.WriteString(fmt.Sprintf(" {%s}", strings.Join(co.AllowedValues, "|")))
+			longDoc.WriteString(fmt.Sprintf(" `{%s}`", strings.Join(co.AllowedValues, " | ")))
 		}
 		if co.DefaultValue != "" {
-			longDoc.WriteString(fmt.Sprintf(" (default %s)", co.DefaultValue))
+			longDoc.WriteString(fmt.Sprintf(" (default `%s`)", co.DefaultValue))
 		}
 		longDoc.WriteRune('\n')
 	}
diff --git a/pkg/cmd/gist/edit/edit.go b/pkg/cmd/gist/edit/edit.go
index e0d6e3b84..9e74adf62 100644
--- a/pkg/cmd/gist/edit/edit.go
+++ b/pkg/cmd/gist/edit/edit.go
@@ -236,6 +236,8 @@ func editRun(opts *EditOptions) error {
 		if filename == "" {
 			if len(candidates) == 1 {
 				filename = candidates[0]
+			} else if len(candidates) == 0 {
+				return errors.New("no file in the gist")
 			} else {
 				if !opts.IO.CanPrompt() {
 					return errors.New("unsure what file to edit; either specify --filename or run interactively")
diff --git a/pkg/cmd/gist/shared/shared.go b/pkg/cmd/gist/shared/shared.go
index fc63f56ce..99a5524ee 100644
--- a/pkg/cmd/gist/shared/shared.go
+++ b/pkg/cmd/gist/shared/shared.go
@@ -44,6 +44,9 @@ func (g Gist) Filename() string {
 	for fn := range g.Files {
 		filenames = append(filenames, fn)
 	}
+	if len(filenames) == 0 {
+		return ""
+	}
 	sort.Strings(filenames)
 	return filenames[0]
 }
diff --git a/pkg/cmd/issue/edit/edit.go b/pkg/cmd/issue/edit/edit.go
index 8386cbcfa..b207a96fd 100644
--- a/pkg/cmd/issue/edit/edit.go
+++ b/pkg/cmd/issue/edit/edit.go
@@ -60,11 +60,17 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
 
 			Editing issues' projects requires authorization with the %[1]sproject%[1]s scope.
 			To authorize, run %[1]sgh auth refresh -s project%[1]s.
+
+			The %[1]s--add-assignee%[1]s and %[1]s--remove-assignee%[1]s flags both support
+			the following special values:
+			- %[1]s@me%[1]s: assign or unassign yourself
+			- %[1]s@copilot%[1]s: assign or unassign Copilot (not supported on GitHub Enterprise Server)
 		`, "`"),
 		Example: heredoc.Doc(`
 			$ gh issue edit 23 --title "I found a bug" --body "Nothing works"
 			$ gh issue edit 23 --add-label "bug,help wanted" --remove-label "core"
 			$ gh issue edit 23 --add-assignee "@me" --remove-assignee monalisa,hubot
+			$ gh issue edit 23 --add-assignee "@copilot"
 			$ gh issue edit 23 --add-project "Roadmap" --remove-project v1,v2
 			$ gh issue edit 23 --milestone "Version 1"
 			$ gh issue edit 23 --remove-milestone
@@ -165,8 +171,8 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
 	cmd.Flags().StringVarP(&opts.Editable.Title.Value, "title", "t", "", "Set the new title.")
 	cmd.Flags().StringVarP(&opts.Editable.Body.Value, "body", "b", "", "Set the new body.")
 	cmd.Flags().StringVarP(&bodyFile, "body-file", "F", "", "Read body text from `file` (use \"-\" to read from standard input)")
-	cmd.Flags().StringSliceVar(&opts.Editable.Assignees.Add, "add-assignee", nil, "Add assigned users by their `login`. Use \"@me\" to assign yourself.")
-	cmd.Flags().StringSliceVar(&opts.Editable.Assignees.Remove, "remove-assignee", nil, "Remove assigned users by their `login`. Use \"@me\" to unassign yourself.")
+	cmd.Flags().StringSliceVar(&opts.Editable.Assignees.Add, "add-assignee", nil, "Add assigned users by their `login`. Use \"@me\" to assign yourself, or \"@copilot\" to assign Copilot.")
+	cmd.Flags().StringSliceVar(&opts.Editable.Assignees.Remove, "remove-assignee", nil, "Remove assigned users by their `login`. Use \"@me\" to unassign yourself, or \"@copilot\" to unassign Copilot.")
 	cmd.Flags().StringSliceVar(&opts.Editable.Labels.Add, "add-label", nil, "Add labels by `name`")
 	cmd.Flags().StringSliceVar(&opts.Editable.Labels.Remove, "remove-label", nil, "Remove labels by `name`")
 	cmd.Flags().StringSliceVar(&opts.Editable.Projects.Add, "add-project", nil, "Add the issue to projects by `title`")
@@ -197,9 +203,24 @@ func editRun(opts *EditOptions) error {
 		}
 	}
 
+	if opts.Detector == nil {
+		cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
+		opts.Detector = fd.NewDetector(cachedClient, baseRepo.RepoHost())
+	}
+
+	issueFeatures, err := opts.Detector.IssueFeatures()
+	if err != nil {
+		return err
+	}
+
 	lookupFields := []string{"id", "number", "title", "body", "url"}
 	if editable.Assignees.Edited {
-		lookupFields = append(lookupFields, "assignees")
+		if issueFeatures.ActorIsAssignable {
+			editable.Assignees.ActorAssignees = true
+			lookupFields = append(lookupFields, "assignedActors")
+		} else {
+			lookupFields = append(lookupFields, "assignees")
+		}
 	}
 	if editable.Labels.Edited {
 		lookupFields = append(lookupFields, "labels")
@@ -207,11 +228,6 @@ func editRun(opts *EditOptions) error {
 	if editable.Projects.Edited {
 		// TODO projectsV1Deprecation
 		// Remove this section as we should no longer add projectCards
-		if opts.Detector == nil {
-			cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
-			opts.Detector = fd.NewDetector(cachedClient, baseRepo.RepoHost())
-		}
-
 		projectsV1Support := opts.Detector.ProjectsV1()
 		if projectsV1Support == gh.ProjectsV1Supported {
 			lookupFields = append(lookupFields, "projectCards")
@@ -254,7 +270,14 @@ func editRun(opts *EditOptions) error {
 
 		editable.Title.Default = issue.Title
 		editable.Body.Default = issue.Body
-		editable.Assignees.Default = issue.Assignees.Logins()
+		// We use Actors as the default assignees if Actors are assignable
+		// on this GitHub host.
+		if editable.Assignees.ActorAssignees {
+			editable.Assignees.Default = issue.AssignedActors.DisplayNames()
+			editable.Assignees.DefaultLogins = issue.AssignedActors.Logins()
+		} else {
+			editable.Assignees.Default = issue.Assignees.Logins()
+		}
 		editable.Labels.Default = issue.Labels.Names()
 		editable.Projects.Default = append(issue.ProjectCards.ProjectNames(), issue.ProjectItems.ProjectTitles()...)
 		projectItems := map[string]string{}
diff --git a/pkg/cmd/pr/create/create.go b/pkg/cmd/pr/create/create.go
index 1d980b68d..705f46023 100644
--- a/pkg/cmd/pr/create/create.go
+++ b/pkg/cmd/pr/create/create.go
@@ -201,6 +201,8 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
 		Long: heredoc.Docf(`
 			Create a pull request on GitHub.
 
+			Upon success, the URL of the created pull request will be printed.
+
 			When the current branch isn't fully pushed to a git remote, a prompt will ask where
 			to push the branch and offer an option to fork the base repository. Use %[1]s--head%[1]s to
 			explicitly skip any forking or pushing behavior.
diff --git a/pkg/cmd/pr/edit/edit.go b/pkg/cmd/pr/edit/edit.go
index cc5eefb9e..becbfce47 100644
--- a/pkg/cmd/pr/edit/edit.go
+++ b/pkg/cmd/pr/edit/edit.go
@@ -20,15 +20,14 @@ import (
 type EditOptions struct {
 	HttpClient func() (*http.Client, error)
 	IO         *iostreams.IOStreams
-	// TODO projectsV1Deprecation
-	// Remove this detector since it is only used for test validation.
-	Detector fd.Detector
 
 	Finder          shared.PRFinder
 	Surveyor        Surveyor
 	Fetcher         EditableOptionsFetcher
 	EditorRetriever EditorRetriever
 	Prompter        shared.EditPrompter
+	Detector        fd.Detector
+	BaseRepo        func() (ghrepo.Interface, error)
 
 	SelectorArg string
 	Interactive bool
@@ -60,12 +59,21 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
 
 			Editing a pull request's projects requires authorization with the %[1]sproject%[1]s scope.
 			To authorize, run %[1]sgh auth refresh -s project%[1]s.
+
+			The %[1]s--add-assignee%[1]s and %[1]s--remove-assignee%[1]s flags both support
+			the following special values:
+			- %[1]s@me%[1]s: assign or unassign yourself
+			- %[1]s@copilot%[1]s: assign or unassign Copilot (not supported on GitHub Enterprise Server)
+
+			The %[1]s--add-reviewer%[1]s and %[1]s--remove-reviewer%[1]s flags do not support
+			these special values.
 		`, "`"),
 		Example: heredoc.Doc(`
 			$ gh pr edit 23 --title "I found a bug" --body "Nothing works"
 			$ gh pr edit 23 --add-label "bug,help wanted" --remove-label "core"
 			$ gh pr edit 23 --add-reviewer monalisa,hubot  --remove-reviewer myorg/team-name
 			$ gh pr edit 23 --add-assignee "@me" --remove-assignee monalisa,hubot
+			$ gh pr edit 23 --add-assignee "@copilot"
 			$ gh pr edit 23 --add-project "Roadmap" --remove-project v1,v2
 			$ gh pr edit 23 --milestone "Version 1"
 			$ gh pr edit 23 --remove-milestone
@@ -73,6 +81,7 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
 		Args: cobra.MaximumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			opts.Finder = shared.NewFinder(f)
+			opts.BaseRepo = f.BaseRepo
 
 			if len(args) > 0 {
 				opts.SelectorArg = args[0]
@@ -161,8 +170,8 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
 	cmd.Flags().StringVarP(&opts.Editable.Base.Value, "base", "B", "", "Change the base `branch` for this pull request")
 	cmd.Flags().StringSliceVar(&opts.Editable.Reviewers.Add, "add-reviewer", nil, "Add reviewers by their `login`.")
 	cmd.Flags().StringSliceVar(&opts.Editable.Reviewers.Remove, "remove-reviewer", nil, "Remove reviewers by their `login`.")
-	cmd.Flags().StringSliceVar(&opts.Editable.Assignees.Add, "add-assignee", nil, "Add assigned users by their `login`. Use \"@me\" to assign yourself.")
-	cmd.Flags().StringSliceVar(&opts.Editable.Assignees.Remove, "remove-assignee", nil, "Remove assigned users by their `login`. Use \"@me\" to unassign yourself.")
+	cmd.Flags().StringSliceVar(&opts.Editable.Assignees.Add, "add-assignee", nil, "Add assigned users by their `login`. Use \"@me\" to assign yourself, or \"@copilot\" to assign Copilot.")
+	cmd.Flags().StringSliceVar(&opts.Editable.Assignees.Remove, "remove-assignee", nil, "Remove assigned users by their `login`. Use \"@me\" to unassign yourself, or \"@copilot\" to unassign Copilot.")
 	cmd.Flags().StringSliceVar(&opts.Editable.Labels.Add, "add-label", nil, "Add labels by `name`")
 	cmd.Flags().StringSliceVar(&opts.Editable.Labels.Remove, "remove-label", nil, "Remove labels by `name`")
 	cmd.Flags().StringSliceVar(&opts.Editable.Projects.Add, "add-project", nil, "Add the pull request to projects by `title`")
@@ -196,9 +205,15 @@ func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Comman
 func editRun(opts *EditOptions) error {
 	findOptions := shared.FindOptions{
 		Selector: opts.SelectorArg,
-		Fields:   []string{"id", "url", "title", "body", "baseRefName", "reviewRequests", "assignees", "labels", "projectCards", "projectItems", "milestone"},
+		Fields:   []string{"id", "url", "title", "body", "baseRefName", "reviewRequests", "labels", "projectCards", "projectItems", "milestone", "assignees"},
 		Detector: opts.Detector,
 	}
+
+	httpClient, err := opts.HttpClient()
+	if err != nil {
+		return err
+	}
+
 	pr, repo, err := opts.Finder.Find(findOptions)
 	if err != nil {
 		return err
@@ -210,7 +225,13 @@ func editRun(opts *EditOptions) error {
 	editable.Body.Default = pr.Body
 	editable.Base.Default = pr.BaseRefName
 	editable.Reviewers.Default = pr.ReviewRequests.Logins()
-	editable.Assignees.Default = pr.Assignees.Logins()
+	if pr.AssignedActorsUsed {
+		editable.Assignees.ActorAssignees = true
+		editable.Assignees.Default = pr.AssignedActors.DisplayNames()
+		editable.Assignees.DefaultLogins = pr.AssignedActors.Logins()
+	} else {
+		editable.Assignees.Default = pr.Assignees.Logins()
+	}
 	editable.Labels.Default = pr.Labels.Names()
 	editable.Projects.Default = append(pr.ProjectCards.ProjectNames(), pr.ProjectItems.ProjectTitles()...)
 	projectItems := map[string]string{}
@@ -229,10 +250,6 @@ func editRun(opts *EditOptions) error {
 		}
 	}
 
-	httpClient, err := opts.HttpClient()
-	if err != nil {
-		return err
-	}
 	apiClient := api.NewClientFromHTTP(httpClient)
 
 	opts.IO.StartProgressIndicator()
diff --git a/pkg/cmd/pr/list/list.go b/pkg/cmd/pr/list/list.go
index 0b86d5e11..7188df1a5 100644
--- a/pkg/cmd/pr/list/list.go
+++ b/pkg/cmd/pr/list/list.go
@@ -64,6 +64,9 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
 			# List PRs authored by you
 			$ gh pr list --author "@me"
 
+			# List PRs with a specific head branch name
+			$ gh pr list --head "typo"
+
 			# List only PRs with all of the given labels
 			$ gh pr list --label bug --label "priority 1"
 
@@ -102,7 +105,7 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
 	cmd.Flags().IntVarP(&opts.LimitResults, "limit", "L", 30, "Maximum number of items to fetch")
 	cmdutil.StringEnumFlag(cmd, &opts.State, "state", "s", "open", []string{"open", "closed", "merged", "all"}, "Filter by state")
 	cmd.Flags().StringVarP(&opts.BaseBranch, "base", "B", "", "Filter by base branch")
-	cmd.Flags().StringVarP(&opts.HeadBranch, "head", "H", "", "Filter by head branch")
+	cmd.Flags().StringVarP(&opts.HeadBranch, "head", "H", "", `Filter by head branch ("<owner>:<branch>" syntax not supported)`)
 	cmd.Flags().StringSliceVarP(&opts.Labels, "label", "l", nil, "Filter by label")
 	cmd.Flags().StringVarP(&opts.Author, "author", "A", "", "Filter by author")
 	cmd.Flags().StringVar(&appAuthor, "app", "", "Filter by GitHub App author")
diff --git a/pkg/cmd/pr/shared/completion.go b/pkg/cmd/pr/shared/completion.go
index e07abc5a7..c1296be71 100644
--- a/pkg/cmd/pr/shared/completion.go
+++ b/pkg/cmd/pr/shared/completion.go
@@ -21,13 +21,13 @@ func RequestableReviewersForCompletion(httpClient *http.Client, repo ghrepo.Inte
 
 	results := []string{}
 	for _, user := range metadata.AssignableUsers {
-		if strings.EqualFold(user.Login, metadata.CurrentLogin) {
+		if strings.EqualFold(user.Login(), metadata.CurrentLogin) {
 			continue
 		}
-		if user.Name != "" {
-			results = append(results, fmt.Sprintf("%s\t%s", user.Login, user.Name))
+		if user.Name() != "" {
+			results = append(results, fmt.Sprintf("%s\t%s", user.Login(), user.Name()))
 		} else {
-			results = append(results, user.Login)
+			results = append(results, user.Login())
 		}
 	}
 	for _, team := range metadata.Teams {
diff --git a/pkg/cmd/pr/shared/editable.go b/pkg/cmd/pr/shared/editable.go
index e73b3c294..2f51f2ae8 100644
--- a/pkg/cmd/pr/shared/editable.go
+++ b/pkg/cmd/pr/shared/editable.go
@@ -14,7 +14,7 @@ type Editable struct {
 	Body      EditableString
 	Base      EditableString
 	Reviewers EditableSlice
-	Assignees EditableSlice
+	Assignees EditableAssignees
 	Labels    EditableSlice
 	Projects  EditableProjects
 	Milestone EditableString
@@ -38,6 +38,14 @@ type EditableSlice struct {
 	Allowed bool
 }
 
+// EditableAssignees is a special case of EditableSlice.
+// It contains a flag to indicate whether the assignees are actors or not.
+type EditableAssignees struct {
+	EditableSlice
+	ActorAssignees bool
+	DefaultLogins  []string // For disambiguating actors from display names
+}
+
 // ProjectsV2 mutations require a mapping of an item ID to a project ID.
 // Keep that map along with standard EditableSlice data.
 type EditableProjects struct {
@@ -105,21 +113,56 @@ func (e Editable) AssigneeIds(client *api.Client, repo ghrepo.Interface) (*[]str
 	if !e.Assignees.Edited {
 		return nil, nil
 	}
+
+	// If assignees came in from command line flags, we need to
+	// curate the final list of assignees from the default list.
 	if len(e.Assignees.Add) != 0 || len(e.Assignees.Remove) != 0 {
 		meReplacer := NewMeReplacer(client, repo.RepoHost())
-		s := set.NewStringSet()
-		s.AddValues(e.Assignees.Default)
-		add, err := meReplacer.ReplaceSlice(e.Assignees.Add)
+		copilotReplacer := NewCopilotReplacer()
+
+		replaceSpecialAssigneeNames := func(value []string) ([]string, error) {
+			replaced, err := meReplacer.ReplaceSlice(value)
+			if err != nil {
+				return nil, err
+			}
+
+			// Only suppported for actor assignees.
+			if e.Assignees.ActorAssignees {
+				replaced = copilotReplacer.ReplaceSlice(replaced)
+			}
+
+			return replaced, nil
+		}
+
+		assigneeSet := set.NewStringSet()
+
+		// This check below is required because in a non-interactive flow,
+		// the user gives us a login and not the DisplayName, and when
+		// we have actor assignees e.Assignees.Default will contain
+		// DisplayNames and not logins (this is to accommodate special actor
+		// display names in the interactive flow).
+		// So, we need to add the default logins here instead of the DisplayNames.
+		// Otherwise, the value the user provided won't be found in the
+		// set to be added or removed, causing unexpected behavior.
+		if e.Assignees.ActorAssignees {
+			assigneeSet.AddValues(e.Assignees.DefaultLogins)
+		} else {
+			assigneeSet.AddValues(e.Assignees.Default)
+		}
+
+		add, err := replaceSpecialAssigneeNames(e.Assignees.Add)
 		if err != nil {
 			return nil, err
 		}
-		s.AddValues(add)
-		remove, err := meReplacer.ReplaceSlice(e.Assignees.Remove)
+		assigneeSet.AddValues(add)
+
+		remove, err := replaceSpecialAssigneeNames(e.Assignees.Remove)
 		if err != nil {
 			return nil, err
 		}
-		s.RemoveValues(remove)
-		e.Assignees.Value = s.ToSlice()
+		assigneeSet.RemoveValues(remove)
+
+		e.Assignees.Value = assigneeSet.ToSlice()
 	}
 	a, err := e.Metadata.MembersToIDs(e.Assignees.Value)
 	return &a, err
@@ -245,6 +288,14 @@ func (es *EditableSlice) clone() EditableSlice {
 	return cpy
 }
 
+func (ea *EditableAssignees) clone() EditableAssignees {
+	return EditableAssignees{
+		EditableSlice:  ea.EditableSlice.clone(),
+		ActorAssignees: ea.ActorAssignees,
+		DefaultLogins:  ea.DefaultLogins,
+	}
+}
+
 func (ep *EditableProjects) clone() EditableProjects {
 	return EditableProjects{
 		EditableSlice: ep.EditableSlice.clone(),
@@ -378,12 +429,13 @@ func FieldsToEditSurvey(p EditPrompter, editable *Editable) error {
 
 func FetchOptions(client *api.Client, repo ghrepo.Interface, editable *Editable) error {
 	input := api.RepoMetadataInput{
-		Reviewers:  editable.Reviewers.Edited,
-		Assignees:  editable.Assignees.Edited,
-		Labels:     editable.Labels.Edited,
-		ProjectsV1: editable.Projects.Edited,
-		ProjectsV2: editable.Projects.Edited,
-		Milestones: editable.Milestone.Edited,
+		Reviewers:      editable.Reviewers.Edited,
+		Assignees:      editable.Assignees.Edited,
+		ActorAssignees: editable.Assignees.ActorAssignees,
+		Labels:         editable.Labels.Edited,
+		ProjectsV1:     editable.Projects.Edited,
+		ProjectsV2:     editable.Projects.Edited,
+		Milestones:     editable.Milestone.Edited,
 	}
 	metadata, err := api.RepoMetadata(client, repo, input)
 	if err != nil {
@@ -392,7 +444,11 @@ func FetchOptions(client *api.Client, repo ghrepo.Interface, editable *Editable)
 
 	var users []string
 	for _, u := range metadata.AssignableUsers {
-		users = append(users, u.Login)
+		users = append(users, u.Login())
+	}
+	var actors []string
+	for _, a := range metadata.AssignableActors {
+		actors = append(actors, a.DisplayName())
 	}
 	var teams []string
 	for _, t := range metadata.Teams {
@@ -416,7 +472,11 @@ func FetchOptions(client *api.Client, repo ghrepo.Interface, editable *Editable)
 
 	editable.Metadata = *metadata
 	editable.Reviewers.Options = append(users, teams...)
-	editable.Assignees.Options = users
+	if editable.Assignees.ActorAssignees {
+		editable.Assignees.Options = actors
+	} else {
+		editable.Assignees.Options = users
+	}
 	editable.Labels.Options = labels
 	editable.Projects.Options = projects
 	editable.Milestone.Options = milestones
diff --git a/pkg/cmd/pr/shared/editable_http.go b/pkg/cmd/pr/shared/editable_http.go
index fcc30095a..8cd51c349 100644
--- a/pkg/cmd/pr/shared/editable_http.go
+++ b/pkg/cmd/pr/shared/editable_http.go
@@ -60,25 +60,78 @@ func UpdateIssue(httpClient *http.Client, repo ghrepo.Interface, id string, isPR
 
 	if dirtyExcludingLabels(options) {
 		wg.Go(func() error {
-			return replaceIssueFields(httpClient, repo, id, isPR, options)
+			// updateIssue mutation does not support Actors so assignment needs to
+			// be in a separate request when our assignees are Actors.
+			// Note: this is intentionally done synchronously with updating
+			// other issue fields to ensure consistency with how legacy
+			// user assignees are handled.
+			// https://github.com/cli/cli/pull/10960#discussion_r2086725348
+			if options.Assignees.Edited && options.Assignees.ActorAssignees {
+				apiClient := api.NewClientFromHTTP(httpClient)
+				assigneeIds, err := options.AssigneeIds(apiClient, repo)
+				if err != nil {
+					return err
+				}
+
+				err = replaceActorAssigneesForEditable(apiClient, repo, id, assigneeIds)
+				if err != nil {
+					return err
+				}
+			}
+			err := replaceIssueFields(httpClient, repo, id, isPR, options)
+			if err != nil {
+				return err
+			}
+
+			return nil
 		})
 	}
 
 	return wg.Wait()
 }
 
-func replaceIssueFields(httpClient *http.Client, repo ghrepo.Interface, id string, isPR bool, options Editable) error {
-	apiClient := api.NewClientFromHTTP(httpClient)
-	assigneeIds, err := options.AssigneeIds(apiClient, repo)
+func replaceActorAssigneesForEditable(apiClient *api.Client, repo ghrepo.Interface, id string, assigneeIds *[]string) error {
+	type ReplaceActorsForAssignableInput struct {
+		AssignableID githubv4.ID   `json:"assignableId"`
+		ActorIDs     []githubv4.ID `json:"actorIds"`
+	}
+
+	params := ReplaceActorsForAssignableInput{
+		AssignableID: githubv4.ID(id),
+		ActorIDs:     *ghIds(assigneeIds),
+	}
+
+	var mutation struct {
+		ReplaceActorsForAssignable struct {
+			TypeName string `graphql:"__typename"`
+		} `graphql:"replaceActorsForAssignable(input: $input)"`
+	}
+
+	variables := map[string]interface{}{"input": params}
+	err := apiClient.Mutate(repo.RepoHost(), "ReplaceActorsForAssignable", &mutation, variables)
 	if err != nil {
 		return err
 	}
 
+	return nil
+}
+
+func replaceIssueFields(httpClient *http.Client, repo ghrepo.Interface, id string, isPR bool, options Editable) error {
+	apiClient := api.NewClientFromHTTP(httpClient)
+
 	projectIds, err := options.ProjectIds()
 	if err != nil {
 		return err
 	}
 
+	var assigneeIds *[]string
+	if !options.Assignees.ActorAssignees {
+		assigneeIds, err = options.AssigneeIds(apiClient, repo)
+		if err != nil {
+			return err
+		}
+	}
+
 	milestoneId, err := options.MilestoneId()
 	if err != nil {
 		return err
diff --git a/pkg/cmd/pr/shared/finder.go b/pkg/cmd/pr/shared/finder.go
index b8d23c978..a19c69669 100644
--- a/pkg/cmd/pr/shared/finder.go
+++ b/pkg/cmd/pr/shared/finder.go
@@ -242,6 +242,33 @@ func (f *finder) Find(opts FindOptions) (*api.PullRequest, ghrepo.Interface, err
 		}
 	}
 
+	// Ok this is super, super horrible so bear with me.
+	// The `assignees` field on a Pull Request exposes users that are assigned. It is also possible for bots to be
+	// assigned, but they only appear under the `assignedActors` field. Ideally, the caller of `Find` would determine
+	// the correct field to use based on the `fd.Detector` that is passed in, but they can't construct a detector
+	// because the BaseRepo is only determined within this function. The more correct solution is to do what I did with
+	// the issue commands and decouple argument parsing from API lookup. See PR #10811 for example.
+	var actorAssigneesUsed bool
+	if fields.Contains("assignees") {
+		if opts.Detector == nil {
+			cachedClient := api.NewCachedHTTPClient(httpClient, time.Hour*24)
+			opts.Detector = fd.NewDetector(cachedClient, f.baseRefRepo.RepoHost())
+		}
+
+		issueFeatures, err := opts.Detector.IssueFeatures()
+		if err != nil {
+			return nil, nil, fmt.Errorf("error detecting issue features: %v", err)
+		}
+
+		// If actors are assignable on this host then we additionally request the `assignedActors` field.
+		// Note that we don't remove the `assignees` field because some commands (`pr view`) do not display actor
+		// assignees yet, so we have to have both sets of data.
+		if issueFeatures.ActorIsAssignable {
+			fields.Add("assignedActors")
+			actorAssigneesUsed = true
+		}
+	}
+
 	var pr *api.PullRequest
 	if f.prNumber > 0 {
 		// If we have a PR number, let's look it up
@@ -297,6 +324,10 @@ func (f *finder) Find(opts FindOptions) (*api.PullRequest, ghrepo.Interface, err
 		})
 	}
 
+	if actorAssigneesUsed {
+		pr.AssignedActorsUsed = true
+	}
+
 	return pr, f.baseRefRepo, g.Wait()
 }
 
diff --git a/pkg/cmd/pr/shared/params.go b/pkg/cmd/pr/shared/params.go
index 08968939d..1fa45652a 100644
--- a/pkg/cmd/pr/shared/params.go
+++ b/pkg/cmd/pr/shared/params.go
@@ -312,3 +312,26 @@ func (r *MeReplacer) ReplaceSlice(handles []string) ([]string, error) {
 	}
 	return res, nil
 }
+
+// CopilotReplacer resolves usages of `@copilot` to Copilot's login.
+type CopilotReplacer struct{}
+
+func NewCopilotReplacer() *CopilotReplacer {
+	return &CopilotReplacer{}
+}
+
+func (r *CopilotReplacer) replace(handle string) string {
+	if strings.EqualFold(handle, "@copilot") {
+		return api.CopilotActorLogin
+	}
+	return handle
+}
+
+// ReplaceSlice replaces usages of `@copilot` in a slice with Copilot's login.
+func (r *CopilotReplacer) ReplaceSlice(handles []string) []string {
+	res := make([]string, len(handles))
+	for i, h := range handles {
+		res[i] = r.replace(h)
+	}
+	return res
+}
diff --git a/pkg/cmd/pr/shared/survey.go b/pkg/cmd/pr/shared/survey.go
index bf4476ca1..b6c927a2d 100644
--- a/pkg/cmd/pr/shared/survey.go
+++ b/pkg/cmd/pr/shared/survey.go
@@ -192,7 +192,7 @@ func MetadataSurvey(p Prompt, io *iostreams.IOStreams, baseRepo ghrepo.Interface
 
 	var reviewers []string
 	for _, u := range metadataResult.AssignableUsers {
-		if u.Login != metadataResult.CurrentLogin {
+		if u.Login() != metadataResult.CurrentLogin {
 			reviewers = append(reviewers, u.DisplayName())
 		}
 	}
diff --git a/pkg/cmd/preview/preview.go b/pkg/cmd/preview/preview.go
new file mode 100644
index 000000000..791c100b6
--- /dev/null
+++ b/pkg/cmd/preview/preview.go
@@ -0,0 +1,25 @@
+package preview
+
+import (
+	"github.com/MakeNowJust/heredoc"
+	cmdPrompter "github.com/cli/cli/v2/pkg/cmd/preview/prompter"
+	"github.com/cli/cli/v2/pkg/cmdutil"
+	"github.com/spf13/cobra"
+)
+
+func NewCmdPreview(f *cmdutil.Factory) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "preview <command>",
+		Short: "Execute previews for gh features",
+		Long: heredoc.Doc(`
+			Preview commands are for testing, demonstrative, and development purposes only.
+			They should be considered unstable and can change at any time.
+		`),
+	}
+
+	cmdutil.DisableAuthCheck(cmd)
+
+	cmd.AddCommand(cmdPrompter.NewCmdPrompter(f, nil))
+
+	return cmd
+}
diff --git a/pkg/cmd/preview/prompter/prompter.go b/pkg/cmd/preview/prompter/prompter.go
new file mode 100644
index 000000000..5b44a5cbf
--- /dev/null
+++ b/pkg/cmd/preview/prompter/prompter.go
@@ -0,0 +1,236 @@
+package prompter
+
+import (
+	"fmt"
+
+	"github.com/MakeNowJust/heredoc"
+	"github.com/cli/cli/v2/internal/gh"
+	"github.com/cli/cli/v2/internal/prompter"
+	"github.com/cli/cli/v2/pkg/cmdutil"
+	"github.com/cli/cli/v2/pkg/iostreams"
+	"github.com/spf13/cobra"
+)
+
+type prompterOptions struct {
+	IO     *iostreams.IOStreams
+	Config func() (gh.Config, error)
+
+	PromptsToRun []func(prompter.Prompter, *iostreams.IOStreams) error
+}
+
+func NewCmdPrompter(f *cmdutil.Factory, runF func(*prompterOptions) error) *cobra.Command {
+	opts := &prompterOptions{
+		IO:     f.IOStreams,
+		Config: f.Config,
+	}
+
+	const (
+		selectPrompt          = "select"
+		multiSelectPrompt     = "multi-select"
+		inputPrompt           = "input"
+		passwordPrompt        = "password"
+		confirmPrompt         = "confirm"
+		authTokenPrompt       = "auth-token"
+		confirmDeletionPrompt = "confirm-deletion"
+		inputHostnamePrompt   = "input-hostname"
+		markdownEditorPrompt  = "markdown-editor"
+	)
+
+	prompterTypeFuncMap := map[string]func(prompter.Prompter, *iostreams.IOStreams) error{
+		selectPrompt:          runSelect,
+		multiSelectPrompt:     runMultiSelect,
+		inputPrompt:           runInput,
+		passwordPrompt:        runPassword,
+		confirmPrompt:         runConfirm,
+		authTokenPrompt:       runAuthToken,
+		confirmDeletionPrompt: runConfirmDeletion,
+		inputHostnamePrompt:   runInputHostname,
+		markdownEditorPrompt:  runMarkdownEditor,
+	}
+
+	allPromptsOrder := []string{
+		selectPrompt,
+		multiSelectPrompt,
+		inputPrompt,
+		passwordPrompt,
+		confirmPrompt,
+		authTokenPrompt,
+		confirmDeletionPrompt,
+		inputHostnamePrompt,
+		markdownEditorPrompt,
+	}
+
+	cmd := &cobra.Command{
+		Use:   "prompter [prompt type]",
+		Short: "Execute a test program to preview the prompter",
+		Long: heredoc.Doc(`
+			Execute a test program to preview the prompter.
+			Without an argument, all prompts will be run.
+
+			Available prompt types:
+			- select
+			- multi-select
+			- input
+			- password
+			- confirm
+			- auth-token
+			- confirm-deletion
+			- input-hostname
+			- markdown-editor
+		`),
+		Args: cobra.MaximumNArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if runF != nil {
+				return runF(opts)
+			}
+
+			if len(args) == 0 {
+				// All prompts, in a fixed order
+				for _, promptType := range allPromptsOrder {
+					f := prompterTypeFuncMap[promptType]
+					opts.PromptsToRun = append(opts.PromptsToRun, f)
+				}
+			} else {
+				// Only the one specified
+				for _, arg := range args {
+					f, ok := prompterTypeFuncMap[arg]
+					if !ok {
+						return fmt.Errorf("unknown prompter type: %q", arg)
+					}
+					opts.PromptsToRun = append(opts.PromptsToRun, f)
+				}
+			}
+
+			return prompterRun(opts)
+		},
+	}
+
+	return cmd
+}
+
+func prompterRun(opts *prompterOptions) error {
+	editor, err := cmdutil.DetermineEditor(opts.Config)
+	if err != nil {
+		return err
+	}
+
+	p := prompter.New(editor, opts.IO)
+
+	for _, f := range opts.PromptsToRun {
+		if err := f(p, opts.IO); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func runSelect(p prompter.Prompter, io *iostreams.IOStreams) error {
+	fmt.Fprintln(io.Out, "Demonstrating Single Select")
+	cuisines := []string{"Italian", "Greek", "Indian", "Japanese", "American"}
+	favorite, err := p.Select("Favorite cuisine?", "Italian", cuisines)
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(io.Out, "Favorite cuisine: %s\n", cuisines[favorite])
+	return nil
+}
+
+func runMultiSelect(p prompter.Prompter, io *iostreams.IOStreams) error {
+	fmt.Fprintln(io.Out, "Demonstrating Multi Select")
+	cuisines := []string{"Italian", "Greek", "Indian", "Japanese", "American"}
+	favorites, err := p.MultiSelect("Favorite cuisines?", []string{}, cuisines)
+	if err != nil {
+		return err
+	}
+	for _, f := range favorites {
+		fmt.Fprintf(io.Out, "Favorite cuisine: %s\n", cuisines[f])
+	}
+	return nil
+}
+
+func runInput(p prompter.Prompter, io *iostreams.IOStreams) error {
+	fmt.Fprintln(io.Out, "Demonstrating Text Input")
+	text, err := p.Input("Favorite meal?", "Breakfast")
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(io.Out, "You typed: %s\n", text)
+	return nil
+}
+
+func runPassword(p prompter.Prompter, io *iostreams.IOStreams) error {
+	fmt.Fprintln(io.Out, "Demonstrating Password Input")
+	safeword, err := p.Password("Safe word?")
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(io.Out, "Safe word: %s\n", safeword)
+	return nil
+}
+
+func runConfirm(p prompter.Prompter, io *iostreams.IOStreams) error {
+	fmt.Fprintln(io.Out, "Demonstrating Confirmation")
+	confirmation, err := p.Confirm("Are you sure?", true)
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(io.Out, "Confirmation: %t\n", confirmation)
+	return nil
+}
+
+func runAuthToken(p prompter.Prompter, io *iostreams.IOStreams) error {
+	fmt.Fprintln(io.Out, "Demonstrating Auth Token (can't be blank)")
+	token, err := p.AuthToken()
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(io.Out, "Auth token: %s\n", token)
+	return nil
+}
+
+func runConfirmDeletion(p prompter.Prompter, io *iostreams.IOStreams) error {
+	fmt.Fprintln(io.Out, "Demonstrating Deletion Confirmation")
+	err := p.ConfirmDeletion("delete-me")
+	if err != nil {
+		return err
+	}
+	fmt.Fprintln(io.Out, "Item deleted")
+	return nil
+}
+
+func runInputHostname(p prompter.Prompter, io *iostreams.IOStreams) error {
+	fmt.Fprintln(io.Out, "Demonstrating Hostname")
+	hostname, err := p.InputHostname()
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(io.Out, "Hostname: %s\n", hostname)
+	return nil
+}
+
+func runMarkdownEditor(p prompter.Prompter, io *iostreams.IOStreams) error {
+	defaultText := "default text value"
+
+	fmt.Fprintln(io.Out, "Demonstrating Markdown Editor with blanks allowed and default text")
+	editorText, err := p.MarkdownEditor("Edit your text:", defaultText, true)
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(io.Out, "Returned text: %s\n\n", editorText)
+
+	fmt.Fprintln(io.Out, "Demonstrating Markdown Editor with blanks disallowed and default text")
+	editorText2, err := p.MarkdownEditor("Edit your text:", defaultText, false)
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(io.Out, "Returned text: %s\n\n", editorText2)
+
+	fmt.Fprintln(io.Out, "Demonstrating Markdown Editor with blanks disallowed and no default text")
+	editorText3, err := p.MarkdownEditor("Edit your text:", "", false)
+	if err != nil {
+		return err
+	}
+	fmt.Fprintf(io.Out, "Returned text: %s\n", editorText3)
+	return nil
+}
diff --git a/pkg/cmd/release/shared/fetch.go b/pkg/cmd/release/shared/fetch.go
index 8db7e502a..4c0a014b9 100644
--- a/pkg/cmd/release/shared/fetch.go
+++ b/pkg/cmd/release/shared/fetch.go
@@ -71,6 +71,7 @@ type ReleaseAsset struct {
 	Name   string
 	Label  string
 	Size   int64
+	Digest *string
 	State  string
 	APIURL string `json:"url"`
 
@@ -107,6 +108,7 @@ func (rel *Release) ExportData(fields []string) map[string]interface{} {
 					"name":          a.Name,
 					"label":         a.Label,
 					"size":          a.Size,
+					"digest":        a.Digest,
 					"state":         a.State,
 					"createdAt":     a.CreatedAt,
 					"updatedAt":     a.UpdatedAt,
diff --git a/pkg/cmd/release/view/view.go b/pkg/cmd/release/view/view.go
index c9030f299..59a19b4e5 100644
--- a/pkg/cmd/release/view/view.go
+++ b/pkg/cmd/release/view/view.go
@@ -154,10 +154,14 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error {
 
 	if len(release.Assets) > 0 {
 		fmt.Fprintln(w, cs.Bold("Assets"))
-		//nolint:staticcheck // SA1019: Showing NAME|SIZE headers adds nothing to table.
-		table := tableprinter.New(io, tableprinter.NoHeader)
+		table := tableprinter.New(io, tableprinter.WithHeader("Name", "Digest", "Size"))
 		for _, a := range release.Assets {
 			table.AddField(a.Name)
+			if a.Digest == nil {
+				table.AddField("")
+			} else {
+				table.AddField(*a.Digest)
+			}
 			table.AddField(humanFileSize(a.Size))
 			table.EndRow()
 		}
diff --git a/pkg/cmd/root/root.go b/pkg/cmd/root/root.go
index 8cf30db1b..6a709c336 100644
--- a/pkg/cmd/root/root.go
+++ b/pkg/cmd/root/root.go
@@ -26,6 +26,7 @@ import (
 	labelCmd "github.com/cli/cli/v2/pkg/cmd/label"
 	orgCmd "github.com/cli/cli/v2/pkg/cmd/org"
 	prCmd "github.com/cli/cli/v2/pkg/cmd/pr"
+	previewCmd "github.com/cli/cli/v2/pkg/cmd/preview"
 	projectCmd "github.com/cli/cli/v2/pkg/cmd/project"
 	releaseCmd "github.com/cli/cli/v2/pkg/cmd/release"
 	repoCmd "github.com/cli/cli/v2/pkg/cmd/repo"
@@ -141,6 +142,7 @@ func NewCmdRoot(f *cmdutil.Factory, version, buildDate string) (*cobra.Command,
 	cmd.AddCommand(statusCmd.NewCmdStatus(f, nil))
 	cmd.AddCommand(codespaceCmd.NewCmdCodespace(f))
 	cmd.AddCommand(projectCmd.NewCmdProject(f))
+	cmd.AddCommand(previewCmd.NewCmdPreview(f))
 
 	// below here at the commands that require the "intelligent" BaseRepo resolver
 	repoResolvingCmdFactory := *f
diff --git a/pkg/cmd/run/rerun/rerun.go b/pkg/cmd/run/rerun/rerun.go
index 66d5d6f64..8777e0a8a 100644
--- a/pkg/cmd/run/rerun/rerun.go
+++ b/pkg/cmd/run/rerun/rerun.go
@@ -202,10 +202,7 @@ func rerunRun(client *api.Client, repo ghrepo.Interface, run *shared.Run, onlyFa
 	if err != nil {
 		var httpError api.HTTPError
 		if errors.As(err, &httpError) && httpError.StatusCode == 403 {
-			if httpError.Message == "Unable to retry this workflow run because it was created over a month ago" {
-				return fmt.Errorf("run %d cannot be rerun; %s", run.ID, httpError.Message)
-			}
-			return fmt.Errorf("run %d cannot be rerun; its workflow file may be broken", run.ID)
+			return fmt.Errorf("run %d cannot be rerun; %s", run.ID, httpError.Message)
 		}
 		return fmt.Errorf("failed to rerun: %w", err)
 	}
diff --git a/pkg/cmd/run/shared/presentation.go b/pkg/cmd/run/shared/presentation.go
index a3556d743..983979d8a 100644
--- a/pkg/cmd/run/shared/presentation.go
+++ b/pkg/cmd/run/shared/presentation.go
@@ -47,6 +47,48 @@ func RenderJobs(cs *iostreams.ColorScheme, jobs []Job, verbose bool) string {
 	return strings.Join(lines, "\n")
 }
 
+func RenderJobsCompact(cs *iostreams.ColorScheme, jobs []Job) string {
+	lines := []string{}
+	for _, job := range jobs {
+		elapsed := job.CompletedAt.Sub(job.StartedAt)
+		elapsedStr := fmt.Sprintf(" in %s", elapsed)
+		if elapsed < 0 {
+			elapsedStr = ""
+		}
+		symbol, symbolColor := Symbol(cs, job.Status, job.Conclusion)
+		id := cs.Cyanf("%d", job.ID)
+		lines = append(lines, fmt.Sprintf("%s %s%s (ID %s)", symbolColor(symbol), cs.Bold(job.Name), elapsedStr, id))
+
+		if job.Status == Completed && job.Conclusion == Success {
+			continue
+		}
+
+		var inProgressStepLine string
+		var failedStepLines []string
+
+		for _, step := range job.Steps {
+			stepSymbol, stepSymColor := Symbol(cs, step.Status, step.Conclusion)
+			stepLine := fmt.Sprintf("  %s %s", stepSymColor(stepSymbol), step.Name)
+
+			if IsFailureState(step.Conclusion) {
+				failedStepLines = append(failedStepLines, stepLine)
+			}
+
+			if step.Status == InProgress {
+				inProgressStepLine = stepLine
+			}
+		}
+
+		lines = append(lines, failedStepLines...)
+
+		if inProgressStepLine != "" {
+			lines = append(lines, inProgressStepLine)
+		}
+	}
+
+	return strings.Join(lines, "\n")
+}
+
 func RenderAnnotations(cs *iostreams.ColorScheme, annotations []Annotation) string {
 	lines := []string{}
 
diff --git a/pkg/cmd/run/watch/watch.go b/pkg/cmd/run/watch/watch.go
index 57be01f64..a73a91e1a 100644
--- a/pkg/cmd/run/watch/watch.go
+++ b/pkg/cmd/run/watch/watch.go
@@ -28,6 +28,7 @@ type WatchOptions struct {
 	RunID      string
 	Interval   int
 	ExitStatus bool
+	Compact    bool
 
 	Prompt bool
 
@@ -48,6 +49,9 @@ func NewCmdWatch(f *cmdutil.Factory, runF func(*WatchOptions) error) *cobra.Comm
 		Long: heredoc.Docf(`
 			Watch a run until it completes, showing its progress.
 
+			By default, all steps are displayed. The %[1]s--compact%[1]s option can be used to only
+			show the relevant/failed steps.
+
 			This command does not support authenticating via fine grained PATs
 			as it is not currently possible to create a PAT with the %[1]schecks:read%[1]s permission.
 		`, "`"),
@@ -55,6 +59,9 @@ func NewCmdWatch(f *cmdutil.Factory, runF func(*WatchOptions) error) *cobra.Comm
 			# Watch a run until it's done
 			$ gh run watch
 
+			# Watch a run in compact mode
+			$ gh run watch --compact
+
 			# Run some other command when the run is finished
 			$ gh run watch && notify-send 'run is done!'
 		`),
@@ -78,6 +85,7 @@ func NewCmdWatch(f *cmdutil.Factory, runF func(*WatchOptions) error) *cobra.Comm
 		},
 	}
 	cmd.Flags().BoolVar(&opts.ExitStatus, "exit-status", false, "Exit with non-zero status if run fails")
+	cmd.Flags().BoolVar(&opts.Compact, "compact", false, "Show only relevant/failed steps")
 	cmd.Flags().IntVarP(&opts.Interval, "interval", "i", defaultInterval, "Refresh interval in seconds")
 
 	return cmd
@@ -252,8 +260,11 @@ func renderRun(out io.Writer, opts WatchOptions, client *api.Client, repo ghrepo
 	}
 
 	fmt.Fprintln(out, cs.Bold("JOBS"))
-
-	fmt.Fprintln(out, shared.RenderJobs(cs, jobs, true))
+	if opts.Compact {
+		fmt.Fprintln(out, shared.RenderJobsCompact(cs, jobs))
+	} else {
+		fmt.Fprintln(out, shared.RenderJobs(cs, jobs, true))
+	}
 
 	if missingAnnotationsPermissions {
 		fmt.Fprintln(out)
diff --git a/script/build.go b/script/build.go
index d7085f590..2ea0088af 100644
--- a/script/build.go
+++ b/script/build.go
@@ -53,7 +53,15 @@ var tasks = map[string]func(string) error{
 			ldflags = fmt.Sprintf("-X github.com/cli/cli/v2/internal/authflow.oauthClientID=%s %s", os.Getenv("GH_OAUTH_CLIENT_ID"), ldflags)
 		}
 
-		return run("go", "build", "-trimpath", "-ldflags", ldflags, "-o", exe, "./cmd/gh")
+		buildTags, _ := os.LookupEnv("GO_BUILDTAGS")
+
+		args := []string{"go", "build", "-trimpath"}
+		if buildTags != "" {
+			args = append(args, "-tags", buildTags)
+		}
+		args = append(args, "-ldflags", ldflags, "-o", exe, "./cmd/gh")
+
+		return run(args...)
 	},
 	"manpages": func(_ string) error {
 		return run("go", "run", "./cmd/gen-docs", "--man-page", "--doc-path", "./share/man/man1/")



Test patch:
diff --git a/api/queries_repo_test.go b/api/queries_repo_test.go
index 01fc7a4c7..9040a0018 100644
--- a/api/queries_repo_test.go
+++ b/api/queries_repo_test.go
@@ -526,17 +526,17 @@ func Test_RepoMilestones(t *testing.T) {
 func TestDisplayName(t *testing.T) {
 	tests := []struct {
 		name     string
-		assignee RepoAssignee
+		assignee AssignableUser
 		want     string
 	}{
 		{
 			name:     "assignee with name",
-			assignee: RepoAssignee{"123", "octocat123", "Octavious Cath"},
+			assignee: AssignableUser{"123", "octocat123", "Octavious Cath"},
 			want:     "octocat123 (Octavious Cath)",
 		},
 		{
 			name:     "assignee without name",
-			assignee: RepoAssignee{"123", "octocat123", ""},
+			assignee: AssignableUser{"123", "octocat123", ""},
 			want:     "octocat123",
 		},
 	}
diff --git a/internal/featuredetection/feature_detection_test.go b/internal/featuredetection/feature_detection_test.go
index f1152da2c..2c7d19071 100644
--- a/internal/featuredetection/feature_detection_test.go
+++ b/internal/featuredetection/feature_detection_test.go
@@ -23,7 +23,8 @@ func TestIssueFeatures(t *testing.T) {
 			name:     "github.com",
 			hostname: "github.com",
 			wantFeatures: IssueFeatures{
-				StateReason: true,
+				StateReason:       true,
+				ActorIsAssignable: true,
 			},
 			wantErr: false,
 		},
@@ -31,7 +32,8 @@ func TestIssueFeatures(t *testing.T) {
 			name:     "ghec data residency (ghe.com)",
 			hostname: "stampname.ghe.com",
 			wantFeatures: IssueFeatures{
-				StateReason: true,
+				StateReason:       true,
+				ActorIsAssignable: true,
 			},
 			wantErr: false,
 		},
@@ -42,7 +44,8 @@ func TestIssueFeatures(t *testing.T) {
 				`query Issue_fields\b`: `{"data": {}}`,
 			},
 			wantFeatures: IssueFeatures{
-				StateReason: false,
+				StateReason:       false,
+				ActorIsAssignable: false,
 			},
 			wantErr: false,
 		},
diff --git a/pkg/cmd/attestation/inspect/inspect.go b/pkg/cmd/attestation/inspect/inspect.go
index b571eee01..9a2bb5d3f 100644
--- a/pkg/cmd/attestation/inspect/inspect.go
+++ b/pkg/cmd/attestation/inspect/inspect.go
@@ -77,13 +77,18 @@ func NewInspectCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command
 				opts.Hostname, _ = ghauth.DefaultHost()
 			}
 
-			err := auth.IsHostSupported(opts.Hostname)
+			if err := auth.IsHostSupported(opts.Hostname); err != nil {
+				return err
+			}
+
+			hc, err := f.HttpClient()
 			if err != nil {
 				return err
 			}
 
 			config := verification.SigstoreConfig{
-				Logger: opts.Logger,
+				HttpClient: hc,
+				Logger:     opts.Logger,
 			}
 
 			if ghauth.IsTenancy(opts.Hostname) {
diff --git a/pkg/cmd/attestation/inspect/inspect_integration_test.go b/pkg/cmd/attestation/inspect/inspect_integration_test.go
new file mode 100644
index 000000000..6c56461af
--- /dev/null
+++ b/pkg/cmd/attestation/inspect/inspect_integration_test.go
@@ -0,0 +1,45 @@
+//go:build integration
+
+package inspect
+
+import (
+	"bytes"
+	"fmt"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/cli/cli/v2/pkg/cmdutil"
+	"github.com/cli/cli/v2/pkg/iostreams"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewInspectCmd_PrintOutputJSONFormat(t *testing.T) {
+	testIO, _, _, _ := iostreams.Test()
+	f := &cmdutil.Factory{
+		IOStreams: testIO,
+		HttpClient: func() (*http.Client, error) {
+			return http.DefaultClient, nil
+		},
+	}
+
+	t.Run("Print output in JSON format", func(t *testing.T) {
+		var opts *Options
+		cmd := NewInspectCmd(f, func(o *Options) error {
+			opts = o
+			return nil
+		})
+
+		argv := strings.Split(fmt.Sprintf("%s --format json", bundlePath), " ")
+		cmd.SetArgs(argv)
+		cmd.SetIn(&bytes.Buffer{})
+		cmd.SetOut(&bytes.Buffer{})
+		cmd.SetErr(&bytes.Buffer{})
+		_, err := cmd.ExecuteC()
+		assert.NoError(t, err)
+
+		assert.Equal(t, bundlePath, opts.BundlePath)
+		assert.NotNil(t, opts.Logger)
+		assert.NotNil(t, opts.exporter)
+	})
+}
diff --git a/pkg/cmd/attestation/inspect/inspect_test.go b/pkg/cmd/attestation/inspect/inspect_test.go
index 1e0c1305e..c94e80ad2 100644
--- a/pkg/cmd/attestation/inspect/inspect_test.go
+++ b/pkg/cmd/attestation/inspect/inspect_test.go
@@ -1,11 +1,7 @@
 package inspect
 
 import (
-	"bytes"
 	"encoding/json"
-	"fmt"
-	"net/http"
-	"strings"
 	"testing"
 
 	"github.com/cli/cli/v2/pkg/cmd/attestation/artifact/oci"
@@ -14,7 +10,6 @@ import (
 	"github.com/cli/cli/v2/pkg/cmd/attestation/verification"
 	"github.com/cli/cli/v2/pkg/cmdutil"
 
-	"github.com/cli/cli/v2/pkg/httpmock"
 	"github.com/cli/cli/v2/pkg/iostreams"
 
 	"github.com/stretchr/testify/assert"
@@ -26,66 +21,7 @@ const (
 	SigstoreSanRegex = "^https://github.com/sigstore/sigstore-js/"
 )
 
-var (
-	bundlePath = test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json")
-)
-
-func TestNewInspectCmd(t *testing.T) {
-	testIO, _, _, _ := iostreams.Test()
-	f := &cmdutil.Factory{
-		IOStreams: testIO,
-		HttpClient: func() (*http.Client, error) {
-			reg := &httpmock.Registry{}
-			client := &http.Client{}
-			httpmock.ReplaceTripper(client, reg)
-			return client, nil
-		},
-	}
-
-	testcases := []struct {
-		name          string
-		cli           string
-		wants         Options
-		wantsErr      bool
-		wantsExporter bool
-	}{
-		{
-			name: "Prints output in JSON format",
-			cli:  fmt.Sprintf("%s --format json", bundlePath),
-			wants: Options{
-				BundlePath:       bundlePath,
-				SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
-			},
-			wantsExporter: true,
-		},
-	}
-
-	for _, tc := range testcases {
-		t.Run(tc.name, func(t *testing.T) {
-			var opts *Options
-			cmd := NewInspectCmd(f, func(o *Options) error {
-				opts = o
-				return nil
-			})
-
-			argv := strings.Split(tc.cli, " ")
-			cmd.SetArgs(argv)
-			cmd.SetIn(&bytes.Buffer{})
-			cmd.SetOut(&bytes.Buffer{})
-			cmd.SetErr(&bytes.Buffer{})
-			_, err := cmd.ExecuteC()
-			if tc.wantsErr {
-				assert.Error(t, err)
-				return
-			}
-			assert.NoError(t, err)
-
-			assert.Equal(t, tc.wants.BundlePath, opts.BundlePath)
-			assert.NotNil(t, opts.Logger)
-			assert.Equal(t, tc.wantsExporter, opts.exporter != nil)
-		})
-	}
-}
+var bundlePath = test.NormalizeRelativePath("../test/data/sigstore-js-2.1.0-bundle.json")
 
 func TestRunInspect(t *testing.T) {
 	opts := Options{
diff --git a/pkg/cmd/attestation/trustedroot/trustedroot.go b/pkg/cmd/attestation/trustedroot/trustedroot.go
index 4e55e27ab..a347b64b2 100644
--- a/pkg/cmd/attestation/trustedroot/trustedroot.go
+++ b/pkg/cmd/attestation/trustedroot/trustedroot.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
+	"net/http"
 	"os"
 
 	"github.com/cli/cli/v2/pkg/cmd/attestation/api"
@@ -68,6 +69,10 @@ func NewTrustedRootCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Com
 				return err
 			}
 
+			hc, err := f.HttpClient()
+			if err != nil {
+				return err
+			}
 			if ghauth.IsTenancy(opts.Hostname) {
 				c, err := f.Config()
 				if err != nil {
@@ -77,11 +82,6 @@ func NewTrustedRootCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Com
 				if !c.Authentication().HasActiveToken(opts.Hostname) {
 					return fmt.Errorf("not authenticated with %s", opts.Hostname)
 				}
-
-				hc, err := f.HttpClient()
-				if err != nil {
-					return err
-				}
 				logger := io.NewHandler(f.IOStreams)
 				apiClient := api.NewLiveClient(hc, opts.Hostname, logger)
 				td, err := apiClient.GetTrustDomain()
@@ -95,7 +95,7 @@ func NewTrustedRootCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Com
 				return runF(opts)
 			}
 
-			if err := getTrustedRoot(tuf.New, opts); err != nil {
+			if err := getTrustedRoot(tuf.New, opts, hc); err != nil {
 				return fmt.Errorf("Failed to verify the TUF repository: %w", err)
 			}
 
@@ -118,11 +118,11 @@ type tufConfig struct {
 	targets    []string
 }
 
-func getTrustedRoot(makeTUF tufClientInstantiator, opts *Options) error {
+func getTrustedRoot(makeTUF tufClientInstantiator, opts *Options, hc *http.Client) error {
 	var tufOptions []tufConfig
 	var defaultTR = "trusted_root.json"
 
-	tufOpt := verification.DefaultOptionsWithCacheSetting(o.None[string]())
+	tufOpt := verification.DefaultOptionsWithCacheSetting(o.None[string](), hc)
 	// Disable local caching, so we get up-to-date response from TUF repository
 	tufOpt.CacheValidity = 0
 
@@ -151,7 +151,7 @@ func getTrustedRoot(makeTUF tufClientInstantiator, opts *Options) error {
 			targets:    []string{defaultTR},
 		})
 
-		tufOpt = verification.GitHubTUFOptions(o.None[string]())
+		tufOpt = verification.GitHubTUFOptions(o.None[string](), hc)
 		tufOpt.CacheValidity = 0
 		tufOptions = append(tufOptions, tufConfig{
 			tufOptions: tufOpt,
diff --git a/pkg/cmd/attestation/trustedroot/trustedroot_test.go b/pkg/cmd/attestation/trustedroot/trustedroot_test.go
index c4a259436..0d67c4445 100644
--- a/pkg/cmd/attestation/trustedroot/trustedroot_test.go
+++ b/pkg/cmd/attestation/trustedroot/trustedroot_test.go
@@ -28,6 +28,12 @@ func TestNewTrustedRootCmd(t *testing.T) {
 		Config: func() (gh.Config, error) {
 			return &ghmock.ConfigMock{}, nil
 		},
+		HttpClient: func() (*http.Client, error) {
+			reg := &httpmock.Registry{}
+			client := &http.Client{}
+			httpmock.ReplaceTripper(client, reg)
+			return client, nil
+		},
 	}
 
 	testcases := []struct {
@@ -113,6 +119,7 @@ func TestNewTrustedRootWithTenancy(t *testing.T) {
 					},
 				}, nil
 			},
+			HttpClient: httpClientFunc,
 		}
 
 		cmd := NewTrustedRootCmd(f, func(_ *Options) error {
@@ -171,15 +178,19 @@ func TestGetTrustedRoot(t *testing.T) {
 		TufRootPath: root,
 	}
 
+	reg := &httpmock.Registry{}
+	client := &http.Client{}
+	httpmock.ReplaceTripper(client, reg)
+
 	t.Run("failed to create TUF root", func(t *testing.T) {
-		err := getTrustedRoot(newTUFErrClient, opts)
+		err := getTrustedRoot(newTUFErrClient, opts, client)
 		require.Error(t, err)
 		require.ErrorContains(t, err, "failed to create TUF client")
 	})
 
 	t.Run("fails because the root cannot be found", func(t *testing.T) {
 		opts.TufRootPath = test.NormalizeRelativePath("./does/not/exist/root.json")
-		err := getTrustedRoot(tuf.New, opts)
+		err := getTrustedRoot(tuf.New, opts, client)
 		require.Error(t, err)
 		require.ErrorContains(t, err, "failed to read root file")
 	})
diff --git a/pkg/cmd/attestation/verification/sigstore.go b/pkg/cmd/attestation/verification/sigstore.go
index 190ea5c0f..95dc2fb9c 100644
--- a/pkg/cmd/attestation/verification/sigstore.go
+++ b/pkg/cmd/attestation/verification/sigstore.go
@@ -6,6 +6,7 @@ import (
 	"crypto/x509"
 	"errors"
 	"fmt"
+	"net/http"
 	"os"
 
 	"github.com/cli/cli/v2/pkg/cmd/attestation/api"
@@ -33,6 +34,7 @@ type SigstoreConfig struct {
 	TrustedRoot  string
 	Logger       *io.Handler
 	NoPublicGood bool
+	HttpClient   *http.Client
 	// If tenancy mode is not used, trust domain is empty
 	TrustDomain string
 	// TUFMetadataDir
@@ -46,9 +48,9 @@ type SigstoreVerifier interface {
 type LiveSigstoreVerifier struct {
 	Logger       *io.Handler
 	NoPublicGood bool
-	PublicGood   *verify.SignedEntityVerifier
-	GitHub       *verify.SignedEntityVerifier
-	Custom       map[string]*verify.SignedEntityVerifier
+	PublicGood   *verify.Verifier
+	GitHub       *verify.Verifier
+	Custom       map[string]*verify.Verifier
 }
 
 var ErrNoAttestationsVerified = errors.New("no attestations were verified")
@@ -71,13 +73,13 @@ func NewLiveSigstoreVerifier(config SigstoreConfig) (*LiveSigstoreVerifier, erro
 		return liveVerifier, nil
 	}
 	if !config.NoPublicGood {
-		publicGoodVerifier, err := newPublicGoodVerifier(config.TUFMetadataDir)
+		publicGoodVerifier, err := newPublicGoodVerifier(config.TUFMetadataDir, config.HttpClient)
 		if err != nil {
 			return nil, err
 		}
 		liveVerifier.PublicGood = publicGoodVerifier
 	}
-	github, err := newGitHubVerifier(config.TrustDomain, config.TUFMetadataDir)
+	github, err := newGitHubVerifier(config.TrustDomain, config.TUFMetadataDir, config.HttpClient)
 	if err != nil {
 		return nil, err
 	}
@@ -86,13 +88,13 @@ func NewLiveSigstoreVerifier(config SigstoreConfig) (*LiveSigstoreVerifier, erro
 	return liveVerifier, nil
 }
 
-func createCustomVerifiers(trustedRoot string, noPublicGood bool) (map[string]*verify.SignedEntityVerifier, error) {
+func createCustomVerifiers(trustedRoot string, noPublicGood bool) (map[string]*verify.Verifier, error) {
 	customTrustRoots, err := os.ReadFile(trustedRoot)
 	if err != nil {
 		return nil, fmt.Errorf("unable to read file %s: %v", trustedRoot, err)
 	}
 
-	verifiers := make(map[string]*verify.SignedEntityVerifier)
+	verifiers := make(map[string]*verify.Verifier)
 	reader := bufio.NewReader(bytes.NewReader(customTrustRoots))
 	var line []byte
 	var readError error
@@ -189,7 +191,7 @@ func getBundleIssuer(b *bundle.Bundle) (string, error) {
 	return leafCert.Issuer.Organization[0], nil
 }
 
-func (v *LiveSigstoreVerifier) chooseVerifier(issuer string) (*verify.SignedEntityVerifier, error) {
+func (v *LiveSigstoreVerifier) chooseVerifier(issuer string) (*verify.Verifier, error) {
 	// if no custom trusted root is set, return either the Public Good or GitHub verifier
 	// If the chosen verifier has not yet been created, create it as a LiveSigstoreVerifier field for use in future calls
 	if v.Custom != nil {
@@ -291,7 +293,7 @@ func (v *LiveSigstoreVerifier) Verify(attestations []*api.Attestation, policy ve
 	return results, nil
 }
 
-func newCustomVerifier(trustedRoot *root.TrustedRoot) (*verify.SignedEntityVerifier, error) {
+func newCustomVerifier(trustedRoot *root.TrustedRoot) (*verify.Verifier, error) {
 	// All we know about this trust root is its configuration so make some
 	// educated guesses as to what the policy should be.
 	verifierConfig := []verify.VerifierOption{}
@@ -306,7 +308,7 @@ func newCustomVerifier(trustedRoot *root.TrustedRoot) (*verify.SignedEntityVerif
 		verifierConfig = append(verifierConfig, verify.WithTransparencyLog(1))
 	}
 
-	gv, err := verify.NewSignedEntityVerifier(trustedRoot, verifierConfig...)
+	gv, err := verify.NewVerifier(trustedRoot, verifierConfig...)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create custom verifier: %v", err)
 	}
@@ -314,10 +316,10 @@ func newCustomVerifier(trustedRoot *root.TrustedRoot) (*verify.SignedEntityVerif
 	return gv, nil
 }
 
-func newGitHubVerifier(trustDomain string, tufMetadataDir o.Option[string]) (*verify.SignedEntityVerifier, error) {
+func newGitHubVerifier(trustDomain string, tufMetadataDir o.Option[string], hc *http.Client) (*verify.Verifier, error) {
 	var tr string
 
-	opts := GitHubTUFOptions(tufMetadataDir)
+	opts := GitHubTUFOptions(tufMetadataDir, hc)
 	client, err := tuf.New(opts)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create TUF client: %v", err)
@@ -339,8 +341,8 @@ func newGitHubVerifier(trustDomain string, tufMetadataDir o.Option[string]) (*ve
 	return newGitHubVerifierWithTrustedRoot(trustedRoot)
 }
 
-func newGitHubVerifierWithTrustedRoot(trustedRoot *root.TrustedRoot) (*verify.SignedEntityVerifier, error) {
-	gv, err := verify.NewSignedEntityVerifier(trustedRoot, verify.WithSignedTimestamps(1))
+func newGitHubVerifierWithTrustedRoot(trustedRoot *root.TrustedRoot) (*verify.Verifier, error) {
+	gv, err := verify.NewVerifier(trustedRoot, verify.WithSignedTimestamps(1))
 	if err != nil {
 		return nil, fmt.Errorf("failed to create GitHub verifier: %v", err)
 	}
@@ -348,8 +350,8 @@ func newGitHubVerifierWithTrustedRoot(trustedRoot *root.TrustedRoot) (*verify.Si
 	return gv, nil
 }
 
-func newPublicGoodVerifier(tufMetadataDir o.Option[string]) (*verify.SignedEntityVerifier, error) {
-	opts := DefaultOptionsWithCacheSetting(tufMetadataDir)
+func newPublicGoodVerifier(tufMetadataDir o.Option[string], hc *http.Client) (*verify.Verifier, error) {
+	opts := DefaultOptionsWithCacheSetting(tufMetadataDir, hc)
 	client, err := tuf.New(opts)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create TUF client: %v", err)
@@ -362,8 +364,8 @@ func newPublicGoodVerifier(tufMetadataDir o.Option[string]) (*verify.SignedEntit
 	return newPublicGoodVerifierWithTrustedRoot(trustedRoot)
 }
 
-func newPublicGoodVerifierWithTrustedRoot(trustedRoot *root.TrustedRoot) (*verify.SignedEntityVerifier, error) {
-	sv, err := verify.NewSignedEntityVerifier(trustedRoot, verify.WithSignedCertificateTimestamps(1), verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1))
+func newPublicGoodVerifierWithTrustedRoot(trustedRoot *root.TrustedRoot) (*verify.Verifier, error) {
+	sv, err := verify.NewVerifier(trustedRoot, verify.WithSignedCertificateTimestamps(1), verify.WithTransparencyLog(1), verify.WithObserverTimestamps(1))
 	if err != nil {
 		return nil, fmt.Errorf("failed to create Public Good verifier: %v", err)
 	}
diff --git a/pkg/cmd/attestation/verification/sigstore_integration_test.go b/pkg/cmd/attestation/verification/sigstore_integration_test.go
index 2a2d3beea..d37b94fc8 100644
--- a/pkg/cmd/attestation/verification/sigstore_integration_test.go
+++ b/pkg/cmd/attestation/verification/sigstore_integration_test.go
@@ -3,6 +3,7 @@
 package verification
 
 import (
+	"net/http"
 	"testing"
 
 	"github.com/cli/cli/v2/pkg/cmd/attestation/api"
@@ -51,6 +52,7 @@ func TestLiveSigstoreVerifier(t *testing.T) {
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
 			verifier, err := NewLiveSigstoreVerifier(SigstoreConfig{
+				HttpClient:     http.DefaultClient,
 				Logger:         io.NewTestHandler(),
 				TUFMetadataDir: o.Some(t.TempDir()),
 			})
@@ -71,6 +73,7 @@ func TestLiveSigstoreVerifier(t *testing.T) {
 
 	t.Run("with 2/3 verified attestations", func(t *testing.T) {
 		verifier, err := NewLiveSigstoreVerifier(SigstoreConfig{
+			HttpClient:     http.DefaultClient,
 			Logger:         io.NewTestHandler(),
 			TUFMetadataDir: o.Some(t.TempDir()),
 		})
@@ -89,6 +92,7 @@ func TestLiveSigstoreVerifier(t *testing.T) {
 
 	t.Run("fail with 0/2 verified attestations", func(t *testing.T) {
 		verifier, err := NewLiveSigstoreVerifier(SigstoreConfig{
+			HttpClient:     http.DefaultClient,
 			Logger:         io.NewTestHandler(),
 			TUFMetadataDir: o.Some(t.TempDir()),
 		})
@@ -114,6 +118,7 @@ func TestLiveSigstoreVerifier(t *testing.T) {
 		attestations := getAttestationsFor(t, "../test/data/github_provenance_demo-0.0.12-py3-none-any-bundle.jsonl")
 
 		verifier, err := NewLiveSigstoreVerifier(SigstoreConfig{
+			HttpClient:     http.DefaultClient,
 			Logger:         io.NewTestHandler(),
 			TUFMetadataDir: o.Some(t.TempDir()),
 		})
@@ -128,6 +133,7 @@ func TestLiveSigstoreVerifier(t *testing.T) {
 		attestations := getAttestationsFor(t, "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl")
 
 		verifier, err := NewLiveSigstoreVerifier(SigstoreConfig{
+			HttpClient:     http.DefaultClient,
 			Logger:         io.NewTestHandler(),
 			TrustedRoot:    test.NormalizeRelativePath("../test/data/trusted_root.json"),
 			TUFMetadataDir: o.Some(t.TempDir()),
diff --git a/pkg/cmd/attestation/verification/tuf.go b/pkg/cmd/attestation/verification/tuf.go
index dcfdd0b32..b88b15547 100644
--- a/pkg/cmd/attestation/verification/tuf.go
+++ b/pkg/cmd/attestation/verification/tuf.go
@@ -2,12 +2,15 @@ package verification
 
 import (
 	_ "embed"
+	"net/http"
 	"os"
 	"path/filepath"
 
+	"github.com/cenkalti/backoff/v5"
 	o "github.com/cli/cli/v2/pkg/option"
 	"github.com/cli/go-gh/v2/pkg/config"
 	"github.com/sigstore/sigstore-go/pkg/tuf"
+	"github.com/theupdateframework/go-tuf/v2/metadata/fetcher"
 )
 
 //go:embed embed/tuf-repo.github.com/root.json
@@ -15,7 +18,7 @@ var githubRoot []byte
 
 const GitHubTUFMirror = "https://tuf-repo.github.com"
 
-func DefaultOptionsWithCacheSetting(tufMetadataDir o.Option[string]) *tuf.Options {
+func DefaultOptionsWithCacheSetting(tufMetadataDir o.Option[string], hc *http.Client) *tuf.Options {
 	opts := tuf.DefaultOptions()
 
 	// The CODESPACES environment variable will be set to true in a Codespaces workspace
@@ -32,11 +35,18 @@ func DefaultOptionsWithCacheSetting(tufMetadataDir o.Option[string]) *tuf.Option
 	// Allow TUF cache for 1 day
 	opts.CacheValidity = 1
 
+	// configure fetcher timeout and retry
+	f := fetcher.NewDefaultFetcher()
+	f.SetHTTPClient(hc)
+	retryOptions := []backoff.RetryOption{backoff.WithMaxTries(3)}
+	f.SetRetryOptions(retryOptions...)
+	opts.WithFetcher(f)
+
 	return opts
 }
 
-func GitHubTUFOptions(tufMetadataDir o.Option[string]) *tuf.Options {
-	opts := DefaultOptionsWithCacheSetting(tufMetadataDir)
+func GitHubTUFOptions(tufMetadataDir o.Option[string], hc *http.Client) *tuf.Options {
+	opts := DefaultOptionsWithCacheSetting(tufMetadataDir, hc)
 
 	opts.Root = githubRoot
 	opts.RepositoryBaseURL = GitHubTUFMirror
diff --git a/pkg/cmd/attestation/verification/tuf_test.go b/pkg/cmd/attestation/verification/tuf_test.go
index e8b6ecf98..41f766ac9 100644
--- a/pkg/cmd/attestation/verification/tuf_test.go
+++ b/pkg/cmd/attestation/verification/tuf_test.go
@@ -12,7 +12,7 @@ import (
 
 func TestGitHubTUFOptionsNoMetadataDir(t *testing.T) {
 	os.Setenv("CODESPACES", "true")
-	opts := GitHubTUFOptions(o.None[string]())
+	opts := GitHubTUFOptions(o.None[string](), nil)
 
 	require.Equal(t, GitHubTUFMirror, opts.RepositoryBaseURL)
 	require.NotNil(t, opts.Root)
@@ -21,6 +21,6 @@ func TestGitHubTUFOptionsNoMetadataDir(t *testing.T) {
 }
 
 func TestGitHubTUFOptionsWithMetadataDir(t *testing.T) {
-	opts := GitHubTUFOptions(o.Some("anything"))
+	opts := GitHubTUFOptions(o.Some("anything"), nil)
 	require.Equal(t, "anything", opts.CachePath)
 }
diff --git a/pkg/cmd/attestation/verify/attestation_integration_test.go b/pkg/cmd/attestation/verify/attestation_integration_test.go
index 73452c425..ec3eb271c 100644
--- a/pkg/cmd/attestation/verify/attestation_integration_test.go
+++ b/pkg/cmd/attestation/verify/attestation_integration_test.go
@@ -3,6 +3,7 @@
 package verify
 
 import (
+	"net/http"
 	"testing"
 
 	"github.com/cli/cli/v2/pkg/cmd/attestation/api"
@@ -26,6 +27,7 @@ func getAttestationsFor(t *testing.T, bundlePath string) []*api.Attestation {
 
 func TestVerifyAttestations(t *testing.T) {
 	sgVerifier, err := verification.NewLiveSigstoreVerifier(verification.SigstoreConfig{
+		HttpClient:     http.DefaultClient,
 		Logger:         io.NewTestHandler(),
 		TUFMetadataDir: o.Some(t.TempDir()),
 	})
diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go
index b8debc529..90cc5643c 100644
--- a/pkg/cmd/attestation/verify/verify.go
+++ b/pkg/cmd/attestation/verify/verify.go
@@ -186,9 +186,10 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command
 			opts.APIClient = api.NewLiveClient(hc, opts.Hostname, opts.Logger)
 
 			config := verification.SigstoreConfig{
-				TrustedRoot:  opts.TrustedRoot,
+				HttpClient:   hc,
 				Logger:       opts.Logger,
 				NoPublicGood: opts.NoPublicGood,
+				TrustedRoot:  opts.TrustedRoot,
 			}
 
 			// Prepare for tenancy if detected
diff --git a/pkg/cmd/attestation/verify/verify_integration_test.go b/pkg/cmd/attestation/verify/verify_integration_test.go
index 92864f78e..d77f21f70 100644
--- a/pkg/cmd/attestation/verify/verify_integration_test.go
+++ b/pkg/cmd/attestation/verify/verify_integration_test.go
@@ -3,6 +3,7 @@
 package verify
 
 import (
+	"net/http"
 	"testing"
 
 	"github.com/cli/cli/v2/pkg/cmd/attestation/api"
@@ -20,6 +21,7 @@ func TestVerifyIntegration(t *testing.T) {
 	logger := io.NewTestHandler()
 
 	sigstoreConfig := verification.SigstoreConfig{
+		HttpClient:     http.DefaultClient,
 		Logger:         logger,
 		TUFMetadataDir: o.Some(t.TempDir()),
 	}
@@ -136,6 +138,7 @@ func TestVerifyIntegrationCustomIssuer(t *testing.T) {
 	logger := io.NewTestHandler()
 
 	sigstoreConfig := verification.SigstoreConfig{
+		HttpClient:     http.DefaultClient,
 		Logger:         logger,
 		TUFMetadataDir: o.Some(t.TempDir()),
 	}
@@ -209,6 +212,7 @@ func TestVerifyIntegrationReusableWorkflow(t *testing.T) {
 	logger := io.NewTestHandler()
 
 	sigstoreConfig := verification.SigstoreConfig{
+		HttpClient:     http.DefaultClient,
 		Logger:         logger,
 		TUFMetadataDir: o.Some(t.TempDir()),
 	}
@@ -301,6 +305,7 @@ func TestVerifyIntegrationReusableWorkflowSignerWorkflow(t *testing.T) {
 	logger := io.NewTestHandler()
 
 	sigstoreConfig := verification.SigstoreConfig{
+		HttpClient:     http.DefaultClient,
 		Logger:         logger,
 		TUFMetadataDir: o.Some(t.TempDir()),
 	}
diff --git a/pkg/cmd/gist/edit/edit_test.go b/pkg/cmd/gist/edit/edit_test.go
index 728f6e894..12cdf8169 100644
--- a/pkg/cmd/gist/edit/edit_test.go
+++ b/pkg/cmd/gist/edit/edit_test.go
@@ -557,6 +557,30 @@ func Test_editRun(t *testing.T) {
 			},
 			wantErr: "gist ID or URL required when not running interactively",
 		},
+		{
+			name: "edit no-file gist (#10626)",
+			opts: &EditOptions{
+				Selector: "1234",
+			},
+			mockGist: &shared.Gist{
+				ID:    "1234",
+				Files: map[string]*shared.GistFile{},
+				Owner: &shared.GistOwner{Login: "octocat"},
+			},
+			wantErr: "no file in the gist",
+		},
+		{
+			name: "edit no-file gist, nil map (#10626)",
+			opts: &EditOptions{
+				Selector: "1234",
+			},
+			mockGist: &shared.Gist{
+				ID:    "1234",
+				Files: nil,
+				Owner: &shared.GistOwner{Login: "octocat"},
+			},
+			wantErr: "no file in the gist",
+		},
 	}
 
 	for _, tt := range tests {
diff --git a/pkg/cmd/gist/shared/shared_test.go b/pkg/cmd/gist/shared/shared_test.go
index 15b14a939..0bc1e1f11 100644
--- a/pkg/cmd/gist/shared/shared_test.go
+++ b/pkg/cmd/gist/shared/shared_test.go
@@ -163,6 +163,33 @@ func TestPromptGists(t *testing.T) {
 			response: `{ "data": { "viewer": { "gists": { "nodes": [] } } } }`,
 			wantOut:  Gist{},
 		},
+		{
+			name: "prompt list contains no-file gist (#10626)",
+			prompterStubs: func(pm *prompter.MockPrompter) {
+				pm.RegisterSelect("Select a gist",
+					[]string{"  about 6 hours ago", "gistfile0.txt  about 6 hours ago"},
+					func(_, _ string, opts []string) (int, error) {
+						return prompter.IndexFor(opts, "  about 6 hours ago")
+					})
+			},
+			response: `{ "data": { "viewer": { "gists": { "nodes": [
+							{
+								"name": "1234",
+								"files": [],
+								"description": "",
+								"updatedAt": "%[1]v",
+								"isPublic": true
+							},
+							{
+								"name": "5678",
+								"files": [{ "name": "gistfile0.txt" }],
+								"description": "",
+								"updatedAt": "%[1]v",
+								"isPublic": true
+							}
+						] } } } }`,
+			wantOut: Gist{ID: "1234", Files: map[string]*GistFile{}, UpdatedAt: sixHoursAgo, Public: true},
+		},
 	}
 
 	ios, _, _, _ := iostreams.Test()
diff --git a/pkg/cmd/issue/edit/edit_test.go b/pkg/cmd/issue/edit/edit_test.go
index c9aa4c409..d14b2f462 100644
--- a/pkg/cmd/issue/edit/edit_test.go
+++ b/pkg/cmd/issue/edit/edit_test.go
@@ -118,9 +118,11 @@ func TestNewCmdEdit(t *testing.T) {
 			output: EditOptions{
 				IssueNumbers: []int{23},
 				Editable: prShared.Editable{
-					Assignees: prShared.EditableSlice{
-						Add:    []string{"monalisa", "hubot"},
-						Edited: true,
+					Assignees: prShared.EditableAssignees{
+						EditableSlice: prShared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Edited: true,
+						},
 					},
 				},
 			},
@@ -132,9 +134,11 @@ func TestNewCmdEdit(t *testing.T) {
 			output: EditOptions{
 				IssueNumbers: []int{23},
 				Editable: prShared.Editable{
-					Assignees: prShared.EditableSlice{
-						Remove: []string{"monalisa", "hubot"},
-						Edited: true,
+					Assignees: prShared.EditableAssignees{
+						EditableSlice: prShared.EditableSlice{
+							Remove: []string{"monalisa", "hubot"},
+							Edited: true,
+						},
 					},
 				},
 			},
@@ -354,10 +358,12 @@ func Test_editRun(t *testing.T) {
 						Value:  "new body",
 						Edited: true,
 					},
-					Assignees: prShared.EditableSlice{
-						Add:    []string{"monalisa", "hubot"},
-						Remove: []string{"octocat"},
-						Edited: true,
+					Assignees: prShared.EditableAssignees{
+						EditableSlice: prShared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Remove: []string{"octocat"},
+							Edited: true,
+						},
 					},
 					Labels: prShared.EditableSlice{
 						Add:    []string{"feature", "TODO", "bug"},
@@ -388,6 +394,7 @@ func Test_editRun(t *testing.T) {
 				mockIssueProjectItemsGet(t, reg)
 				mockRepoMetadata(t, reg)
 				mockIssueUpdate(t, reg)
+				mockIssueUpdateActorAssignees(t, reg)
 				mockIssueUpdateLabels(t, reg)
 				mockProjectV2ItemUpdate(t, reg)
 			},
@@ -399,10 +406,12 @@ func Test_editRun(t *testing.T) {
 				IssueNumbers: []int{456, 123},
 				Interactive:  false,
 				Editable: prShared.Editable{
-					Assignees: prShared.EditableSlice{
-						Add:    []string{"monalisa", "hubot"},
-						Remove: []string{"octocat"},
-						Edited: true,
+					Assignees: prShared.EditableAssignees{
+						EditableSlice: prShared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Remove: []string{"octocat"},
+							Edited: true,
+						},
 					},
 					Labels: prShared.EditableSlice{
 						Add:    []string{"feature", "TODO", "bug"},
@@ -433,6 +442,8 @@ func Test_editRun(t *testing.T) {
 				mockIssueProjectItemsGet(t, reg)
 				mockIssueUpdate(t, reg)
 				mockIssueUpdate(t, reg)
+				mockIssueUpdateActorAssignees(t, reg)
+				mockIssueUpdateActorAssignees(t, reg)
 				mockIssueUpdateLabels(t, reg)
 				mockIssueUpdateLabels(t, reg)
 				mockProjectV2ItemUpdate(t, reg)
@@ -449,10 +460,12 @@ func Test_editRun(t *testing.T) {
 				IssueNumbers: []int{123, 9999},
 				Interactive:  false,
 				Editable: prShared.Editable{
-					Assignees: prShared.EditableSlice{
-						Add:    []string{"monalisa", "hubot"},
-						Remove: []string{"octocat"},
-						Edited: true,
+					Assignees: prShared.EditableAssignees{
+						EditableSlice: prShared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Remove: []string{"octocat"},
+							Edited: true,
+						},
 					},
 					Labels: prShared.EditableSlice{
 						Add:    []string{"feature", "TODO", "bug"},
@@ -494,10 +507,12 @@ func Test_editRun(t *testing.T) {
 				IssueNumbers: []int{123, 456},
 				Interactive:  false,
 				Editable: prShared.Editable{
-					Assignees: prShared.EditableSlice{
-						Add:    []string{"monalisa", "hubot"},
-						Remove: []string{"octocat"},
-						Edited: true,
+					Assignees: prShared.EditableAssignees{
+						EditableSlice: prShared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Remove: []string{"octocat"},
+							Edited: true,
+						},
 					},
 					Milestone: prShared.EditableString{
 						Value:  "GA",
@@ -509,14 +524,14 @@ func Test_editRun(t *testing.T) {
 			httpStubs: func(t *testing.T, reg *httpmock.Registry) {
 				// Should only be one fetch of metadata.
 				reg.Register(
-					httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+					httpmock.GraphQL(`query RepositoryAssignableActors\b`),
 					httpmock.StringResponse(`
-					{ "data": { "repository": { "assignableUsers": {
+					{ "data": { "repository": { "suggestedActors": {
 						"nodes": [
-							{ "login": "hubot", "id": "HUBOTID" },
-							{ "login": "MonaLisa", "id": "MONAID" }
+							{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
+							{ "login": "MonaLisa", "id": "MONAID", "__typename": "User" }
 						],
-						"pageInfo": { "hasNextPage": false }
+						"pageInfo": { "hasNextPage": false, "endCursor": "Mg" }
 					} } } }
 					`))
 				reg.Register(
@@ -534,6 +549,14 @@ func Test_editRun(t *testing.T) {
 				mockIssueNumberGet(t, reg, 123)
 				mockIssueNumberGet(t, reg, 456)
 				// Updating 123 should succeed.
+				reg.Register(
+					httpmock.GraphQLMutationMatcher(`mutation ReplaceActorsForAssignable\b`, func(m map[string]interface{}) bool {
+						return m["assignableId"] == "123"
+					}),
+					httpmock.GraphQLMutation(`
+					{ "data": { "replaceActorsForAssignable": { "__typename": "" } } }`,
+						func(inputs map[string]interface{}) {}),
+				)
 				reg.Register(
 					httpmock.GraphQLMutationMatcher(`mutation IssueUpdate\b`, func(m map[string]interface{}) bool {
 						return m["id"] == "123"
@@ -544,8 +567,8 @@ func Test_editRun(t *testing.T) {
 				)
 				// Updating 456 should fail.
 				reg.Register(
-					httpmock.GraphQLMutationMatcher(`mutation IssueUpdate\b`, func(m map[string]interface{}) bool {
-						return m["id"] == "456"
+					httpmock.GraphQLMutationMatcher(`mutation ReplaceActorsForAssignable\b`, func(m map[string]interface{}) bool {
+						return m["assignableId"] == "456"
 					}),
 					httpmock.GraphQLMutation(`
 							{ "errors": [ { "message": "test error" } ] }`,
@@ -591,11 +614,130 @@ func Test_editRun(t *testing.T) {
 				mockIssueProjectItemsGet(t, reg)
 				mockRepoMetadata(t, reg)
 				mockIssueUpdate(t, reg)
+				mockIssueUpdateActorAssignees(t, reg)
 				mockIssueUpdateLabels(t, reg)
 				mockProjectV2ItemUpdate(t, reg)
 			},
 			stdout: "https://github.com/OWNER/REPO/issue/123\n",
 		},
+		{
+			name: "interactive prompts with actor assignee display names when actors available",
+			input: &EditOptions{
+				IssueNumbers: []int{123},
+				Interactive:  true,
+				FieldsToEditSurvey: func(p prShared.EditPrompter, eo *prShared.Editable) error {
+					eo.Assignees.Edited = true
+					return nil
+				},
+				EditFieldsSurvey: func(p prShared.EditPrompter, eo *prShared.Editable, _ string) error {
+					// Checking that the display name is being used in the prompt.
+					require.Equal(t, []string{"hubot"}, eo.Assignees.Default)
+					require.Equal(t, []string{"hubot"}, eo.Assignees.DefaultLogins)
+
+					// Adding MonaLisa as PR assignee, should preserve hubot.
+					eo.Assignees.Value = []string{"hubot", "MonaLisa (Mona Display Name)"}
+					return nil
+				},
+				FetchOptions:    prShared.FetchOptions,
+				DetermineEditor: func() (string, error) { return "vim", nil },
+			},
+			httpStubs: func(t *testing.T, reg *httpmock.Registry) {
+				mockIsssueNumberGetWithAssignedActors(t, reg, 123)
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableActors\b`),
+					httpmock.StringResponse(`
+					{ "data": { "repository": { "suggestedActors": {
+						"nodes": [
+							{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
+							{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
+						],
+						"pageInfo": { "hasNextPage": false }
+					} } } }
+					`))
+				mockIssueUpdate(t, reg)
+				reg.Register(
+					httpmock.GraphQL(`mutation ReplaceActorsForAssignable\b`),
+					httpmock.GraphQLMutation(`
+					{ "data": { "replaceActorsForAssignable": { "__typename": "" } } }`,
+						func(inputs map[string]interface{}) {
+							// Checking that despite the display name being returned
+							// from the EditFieldsSurvey, the ID is still
+							// used in the mutation.
+							require.Subset(t, inputs["actorIds"], []string{"MONAID", "HUBOTID"})
+						}),
+				)
+			},
+			stdout: "https://github.com/OWNER/REPO/issue/123\n",
+		},
+		{
+			name: "interactive prompts with user assignee logins when actors unavailable",
+			input: &EditOptions{
+				IssueNumbers: []int{123},
+				Interactive:  true,
+				FieldsToEditSurvey: func(p prShared.EditPrompter, eo *prShared.Editable) error {
+					eo.Assignees.Edited = true
+					return nil
+				},
+				EditFieldsSurvey: func(p prShared.EditPrompter, eo *prShared.Editable, _ string) error {
+					// Checking that only the login is used in the prompt (no display name)
+					require.Equal(t, eo.Assignees.Default, []string{"hubot", "MonaLisa"})
+
+					// Mocking a selection of only MonaLisa in the prompt.
+					eo.Assignees.Value = []string{"MonaLisa"}
+					return nil
+				},
+				FetchOptions:    prShared.FetchOptions,
+				DetermineEditor: func() (string, error) { return "vim", nil },
+				Detector:        &fd.DisabledDetectorMock{},
+			},
+			httpStubs: func(t *testing.T, reg *httpmock.Registry) {
+				reg.Register(
+					httpmock.GraphQL(`query IssueByNumber\b`),
+					httpmock.StringResponse(fmt.Sprintf(`
+                        { "data": { "repository": { "hasIssuesEnabled": true, "issue": {
+							"id": "%[1]d",
+							"number": %[1]d,
+                            "url": "https://github.com/OWNER/REPO/issue/123",
+                            "assignees": {
+                                "nodes": [
+                                    {
+                                        "id": "HUBOTID",
+                                        "login": "hubot",
+										"name": ""
+                                    },
+                                    {
+                                        "id": "MONAID",
+                                        "login": "MonaLisa",
+										"name": "Mona Display Name"
+                                    }
+                                ],
+                                "totalCount": 2
+                            }
+                        } } } }`, 123)),
+				)
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+					httpmock.StringResponse(`
+					{ "data": { "repository": { "assignableUsers": {
+						"nodes": [
+							{ "login": "hubot", "id": "HUBOTID", "name": "" },
+							{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name" }
+						],
+						"pageInfo": { "hasNextPage": false }
+					} } } }
+					`))
+				reg.Register(
+					httpmock.GraphQL(`mutation IssueUpdate\b`),
+					httpmock.GraphQLMutation(`
+								{ "data": { "updateIssue": { "__typename": "" } } }`,
+						func(inputs map[string]interface{}) {
+							// Checking that we still assigned the expected ID.
+							require.Contains(t, inputs["assigneeIds"], "MONAID")
+						}),
+				)
+			},
+			stdout: "https://github.com/OWNER/REPO/issue/123\n",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -654,6 +796,28 @@ func mockIssueNumberGet(_ *testing.T, reg *httpmock.Registry, number int) {
 	)
 }
 
+func mockIsssueNumberGetWithAssignedActors(_ *testing.T, reg *httpmock.Registry, number int) {
+	reg.Register(
+		httpmock.GraphQL(`query IssueByNumber\b`),
+		httpmock.StringResponse(fmt.Sprintf(`
+			{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
+				"id": "%[1]d",
+				"number": %[1]d,
+				"url": "https://github.com/OWNER/REPO/issue/%[1]d",
+				"assignedActors": {
+					"nodes": [
+						{
+							"id": "HUBOTID",
+							"login": "hubot",
+							"__typename": "Bot"
+						}
+					],
+					"totalCount": 1
+				}
+			} } } }`, number)),
+	)
+}
+
 func mockIssueProjectItemsGet(_ *testing.T, reg *httpmock.Registry) {
 	reg.Register(
 		httpmock.GraphQL(`query IssueProjectItems\b`),
@@ -670,16 +834,17 @@ func mockIssueProjectItemsGet(_ *testing.T, reg *httpmock.Registry) {
 
 func mockRepoMetadata(_ *testing.T, reg *httpmock.Registry) {
 	reg.Register(
-		httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+		httpmock.GraphQL(`query RepositoryAssignableActors\b`),
 		httpmock.StringResponse(`
-		{ "data": { "repository": { "assignableUsers": {
+		{ "data": { "repository": { "suggestedActors": {
 			"nodes": [
-				{ "login": "hubot", "id": "HUBOTID" },
-				{ "login": "MonaLisa", "id": "MONAID" }
+				{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
+				{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
 			],
 			"pageInfo": { "hasNextPage": false }
 		} } } }
 		`))
+
 	reg.Register(
 		httpmock.GraphQL(`query RepositoryLabelList\b`),
 		httpmock.StringResponse(`
@@ -767,6 +932,15 @@ func mockIssueUpdate(t *testing.T, reg *httpmock.Registry) {
 	)
 }
 
+func mockIssueUpdateActorAssignees(t *testing.T, reg *httpmock.Registry) {
+	reg.Register(
+		httpmock.GraphQL(`mutation ReplaceActorsForAssignable\b`),
+		httpmock.GraphQLMutation(`
+		{ "data": { "replaceActorsForAssignable": { "__typename": "" } } }`,
+			func(inputs map[string]interface{}) {}),
+	)
+}
+
 func mockIssueUpdateLabels(t *testing.T, reg *httpmock.Registry) {
 	reg.Register(
 		httpmock.GraphQL(`mutation LabelAdd\b`),
@@ -791,6 +965,85 @@ func mockProjectV2ItemUpdate(t *testing.T, reg *httpmock.Registry) {
 	)
 }
 
+func TestActorIsAssignable(t *testing.T) {
+	t.Run("when actors are assignable, query includes assignedActors", func(t *testing.T) {
+		ios, _, _, _ := iostreams.Test()
+
+		reg := &httpmock.Registry{}
+		reg.Register(
+			httpmock.GraphQL(`assignedActors`),
+			// Simulate a GraphQL error to early exit the test.
+			httpmock.StatusStringResponse(500, ""),
+		)
+
+		_, cmdTeardown := run.Stub()
+		defer cmdTeardown(t)
+
+		// Ignore the error because we don't care.
+		_ = editRun(&EditOptions{
+			IO: ios,
+			HttpClient: func() (*http.Client, error) {
+				return &http.Client{Transport: reg}, nil
+			},
+			BaseRepo: func() (ghrepo.Interface, error) {
+				return ghrepo.New("OWNER", "REPO"), nil
+			},
+			Detector:     &fd.EnabledDetectorMock{},
+			IssueNumbers: []int{123},
+			Editable: prShared.Editable{
+				Assignees: prShared.EditableAssignees{
+					EditableSlice: prShared.EditableSlice{
+						Add:    []string{"monalisa", "octocat"},
+						Edited: true,
+					},
+				},
+			},
+		})
+
+		reg.Verify(t)
+	})
+
+	t.Run("when actors are not assignable, query includes assignees instead", func(t *testing.T) {
+		ios, _, _, _ := iostreams.Test()
+
+		reg := &httpmock.Registry{}
+		// This test should NOT include assignedActors in the query
+		reg.Exclude(t, httpmock.GraphQL(`assignedActors`))
+		// It should include the regular assignees field
+		reg.Register(
+			httpmock.GraphQL(`assignees`),
+			// Simulate a GraphQL error to early exit the test.
+			httpmock.StatusStringResponse(500, ""),
+		)
+
+		_, cmdTeardown := run.Stub()
+		defer cmdTeardown(t)
+
+		// Ignore the error because we're not really interested in it.
+		_ = editRun(&EditOptions{
+			IO: ios,
+			HttpClient: func() (*http.Client, error) {
+				return &http.Client{Transport: reg}, nil
+			},
+			BaseRepo: func() (ghrepo.Interface, error) {
+				return ghrepo.New("OWNER", "REPO"), nil
+			},
+			Detector:     &fd.DisabledDetectorMock{},
+			IssueNumbers: []int{123},
+			Editable: prShared.Editable{
+				Assignees: prShared.EditableAssignees{
+					EditableSlice: prShared.EditableSlice{
+						Add:    []string{"monalisa", "octocat"},
+						Edited: true,
+					},
+				},
+			},
+		})
+
+		reg.Verify(t)
+	})
+}
+
 // TODO projectsV1Deprecation
 // Remove this test.
 func TestProjectsV1Deprecation(t *testing.T) {
diff --git a/pkg/cmd/pr/edit/edit_test.go b/pkg/cmd/pr/edit/edit_test.go
index 63e380486..374625912 100644
--- a/pkg/cmd/pr/edit/edit_test.go
+++ b/pkg/cmd/pr/edit/edit_test.go
@@ -166,9 +166,11 @@ func TestNewCmdEdit(t *testing.T) {
 			output: EditOptions{
 				SelectorArg: "23",
 				Editable: shared.Editable{
-					Assignees: shared.EditableSlice{
-						Add:    []string{"monalisa", "hubot"},
-						Edited: true,
+					Assignees: shared.EditableAssignees{
+						EditableSlice: shared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Edited: true,
+						},
 					},
 				},
 			},
@@ -180,9 +182,11 @@ func TestNewCmdEdit(t *testing.T) {
 			output: EditOptions{
 				SelectorArg: "23",
 				Editable: shared.Editable{
-					Assignees: shared.EditableSlice{
-						Remove: []string{"monalisa", "hubot"},
-						Edited: true,
+					Assignees: shared.EditableAssignees{
+						EditableSlice: shared.EditableSlice{
+							Remove: []string{"monalisa", "hubot"},
+							Edited: true,
+						},
 					},
 				},
 			},
@@ -337,9 +341,11 @@ func Test_editRun(t *testing.T) {
 		{
 			name: "non-interactive",
 			input: &EditOptions{
+				Detector:    &fd.EnabledDetectorMock{},
 				SelectorArg: "123",
 				Finder: shared.NewMockFinder("123", &api.PullRequest{
-					URL: "https://github.com/OWNER/REPO/pull/123",
+					URL:                "https://github.com/OWNER/REPO/pull/123",
+					AssignedActorsUsed: true,
 				}, ghrepo.New("OWNER", "REPO")),
 				Interactive: false,
 				Editable: shared.Editable{
@@ -360,10 +366,12 @@ func Test_editRun(t *testing.T) {
 						Remove: []string{"dependabot"},
 						Edited: true,
 					},
-					Assignees: shared.EditableSlice{
-						Add:    []string{"monalisa", "hubot"},
-						Remove: []string{"octocat"},
-						Edited: true,
+					Assignees: shared.EditableAssignees{
+						EditableSlice: shared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Remove: []string{"octocat"},
+							Edited: true,
+						},
 					},
 					Labels: shared.EditableSlice{
 						Add:    []string{"feature", "TODO", "bug"},
@@ -387,6 +395,7 @@ func Test_editRun(t *testing.T) {
 			httpStubs: func(reg *httpmock.Registry) {
 				mockRepoMetadata(reg, false)
 				mockPullRequestUpdate(reg)
+				mockPullRequestUpdateActorAssignees(reg)
 				mockPullRequestReviewersUpdate(reg)
 				mockPullRequestUpdateLabels(reg)
 				mockProjectV2ItemUpdate(reg)
@@ -396,9 +405,11 @@ func Test_editRun(t *testing.T) {
 		{
 			name: "non-interactive skip reviewers",
 			input: &EditOptions{
+				Detector:    &fd.EnabledDetectorMock{},
 				SelectorArg: "123",
 				Finder: shared.NewMockFinder("123", &api.PullRequest{
-					URL: "https://github.com/OWNER/REPO/pull/123",
+					URL:                "https://github.com/OWNER/REPO/pull/123",
+					AssignedActorsUsed: true,
 				}, ghrepo.New("OWNER", "REPO")),
 				Interactive: false,
 				Editable: shared.Editable{
@@ -414,10 +425,12 @@ func Test_editRun(t *testing.T) {
 						Value:  "base-branch-name",
 						Edited: true,
 					},
-					Assignees: shared.EditableSlice{
-						Add:    []string{"monalisa", "hubot"},
-						Remove: []string{"octocat"},
-						Edited: true,
+					Assignees: shared.EditableAssignees{
+						EditableSlice: shared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Remove: []string{"octocat"},
+							Edited: true,
+						},
 					},
 					Labels: shared.EditableSlice{
 						Add:    []string{"feature", "TODO", "bug"},
@@ -441,6 +454,7 @@ func Test_editRun(t *testing.T) {
 			httpStubs: func(reg *httpmock.Registry) {
 				mockRepoMetadata(reg, true)
 				mockPullRequestUpdate(reg)
+				mockPullRequestUpdateActorAssignees(reg)
 				mockPullRequestUpdateLabels(reg)
 				mockProjectV2ItemUpdate(reg)
 			},
@@ -449,9 +463,11 @@ func Test_editRun(t *testing.T) {
 		{
 			name: "non-interactive remove all reviewers",
 			input: &EditOptions{
+				Detector:    &fd.EnabledDetectorMock{},
 				SelectorArg: "123",
 				Finder: shared.NewMockFinder("123", &api.PullRequest{
-					URL: "https://github.com/OWNER/REPO/pull/123",
+					URL:                "https://github.com/OWNER/REPO/pull/123",
+					AssignedActorsUsed: true,
 				}, ghrepo.New("OWNER", "REPO")),
 				Interactive: false,
 				Editable: shared.Editable{
@@ -471,10 +487,12 @@ func Test_editRun(t *testing.T) {
 						Remove: []string{"OWNER/core", "OWNER/external", "monalisa", "hubot", "dependabot"},
 						Edited: true,
 					},
-					Assignees: shared.EditableSlice{
-						Add:    []string{"monalisa", "hubot"},
-						Remove: []string{"octocat"},
-						Edited: true,
+					Assignees: shared.EditableAssignees{
+						EditableSlice: shared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Remove: []string{"octocat"},
+							Edited: true,
+						},
 					},
 					Labels: shared.EditableSlice{
 						Add:    []string{"feature", "TODO", "bug"},
@@ -500,6 +518,7 @@ func Test_editRun(t *testing.T) {
 				mockPullRequestUpdate(reg)
 				mockPullRequestReviewersUpdate(reg)
 				mockPullRequestUpdateLabels(reg)
+				mockPullRequestUpdateActorAssignees(reg)
 				mockProjectV2ItemUpdate(reg)
 			},
 			stdout: "https://github.com/OWNER/REPO/pull/123\n",
@@ -507,18 +526,44 @@ func Test_editRun(t *testing.T) {
 		{
 			name: "interactive",
 			input: &EditOptions{
+				Detector:    &fd.EnabledDetectorMock{},
 				SelectorArg: "123",
 				Finder: shared.NewMockFinder("123", &api.PullRequest{
-					URL: "https://github.com/OWNER/REPO/pull/123",
+					URL:                "https://github.com/OWNER/REPO/pull/123",
+					AssignedActorsUsed: true,
 				}, ghrepo.New("OWNER", "REPO")),
-				Interactive:     true,
-				Surveyor:        testSurveyor{},
+				Interactive: true,
+				Surveyor: testSurveyor{
+					fieldsToEdit: func(e *shared.Editable) error {
+						e.Title.Edited = true
+						e.Body.Edited = true
+						e.Reviewers.Edited = true
+						e.Assignees.Edited = true
+						e.Labels.Edited = true
+						e.Projects.Edited = true
+						e.Milestone.Edited = true
+						return nil
+					},
+					editFields: func(e *shared.Editable, _ string) error {
+						e.Title.Value = "new title"
+						e.Body.Value = "new body"
+						e.Reviewers.Value = []string{"monalisa", "hubot", "OWNER/core", "OWNER/external"}
+						e.Assignees.Value = []string{"monalisa", "hubot"}
+						e.Labels.Value = []string{"feature", "TODO", "bug"}
+						e.Labels.Add = []string{"feature", "TODO", "bug"}
+						e.Labels.Remove = []string{"docs"}
+						e.Projects.Value = []string{"Cleanup", "CleanupV2"}
+						e.Milestone.Value = "GA"
+						return nil
+					},
+				},
 				Fetcher:         testFetcher{},
 				EditorRetriever: testEditorRetriever{},
 			},
 			httpStubs: func(reg *httpmock.Registry) {
 				mockRepoMetadata(reg, false)
 				mockPullRequestUpdate(reg)
+				mockPullRequestUpdateActorAssignees(reg)
 				mockPullRequestReviewersUpdate(reg)
 				mockPullRequestUpdateLabels(reg)
 				mockProjectV2ItemUpdate(reg)
@@ -528,18 +573,42 @@ func Test_editRun(t *testing.T) {
 		{
 			name: "interactive skip reviewers",
 			input: &EditOptions{
+				Detector:    &fd.EnabledDetectorMock{},
 				SelectorArg: "123",
 				Finder: shared.NewMockFinder("123", &api.PullRequest{
-					URL: "https://github.com/OWNER/REPO/pull/123",
+					URL:                "https://github.com/OWNER/REPO/pull/123",
+					AssignedActorsUsed: true,
 				}, ghrepo.New("OWNER", "REPO")),
-				Interactive:     true,
-				Surveyor:        testSurveyor{skipReviewers: true},
+				Interactive: true,
+				Surveyor: testSurveyor{
+					fieldsToEdit: func(e *shared.Editable) error {
+						e.Title.Edited = true
+						e.Body.Edited = true
+						e.Assignees.Edited = true
+						e.Labels.Edited = true
+						e.Projects.Edited = true
+						e.Milestone.Edited = true
+						return nil
+					},
+					editFields: func(e *shared.Editable, _ string) error {
+						e.Title.Value = "new title"
+						e.Body.Value = "new body"
+						e.Assignees.Value = []string{"monalisa", "hubot"}
+						e.Labels.Value = []string{"feature", "TODO", "bug"}
+						e.Labels.Add = []string{"feature", "TODO", "bug"}
+						e.Labels.Remove = []string{"docs"}
+						e.Projects.Value = []string{"Cleanup", "CleanupV2"}
+						e.Milestone.Value = "GA"
+						return nil
+					},
+				},
 				Fetcher:         testFetcher{},
 				EditorRetriever: testEditorRetriever{},
 			},
 			httpStubs: func(reg *httpmock.Registry) {
 				mockRepoMetadata(reg, true)
 				mockPullRequestUpdate(reg)
+				mockPullRequestUpdateActorAssignees(reg)
 				mockPullRequestUpdateLabels(reg)
 				mockProjectV2ItemUpdate(reg)
 			},
@@ -548,12 +617,37 @@ func Test_editRun(t *testing.T) {
 		{
 			name: "interactive remove all reviewers",
 			input: &EditOptions{
+				Detector:    &fd.EnabledDetectorMock{},
 				SelectorArg: "123",
 				Finder: shared.NewMockFinder("123", &api.PullRequest{
-					URL: "https://github.com/OWNER/REPO/pull/123",
+					URL:                "https://github.com/OWNER/REPO/pull/123",
+					AssignedActorsUsed: true,
 				}, ghrepo.New("OWNER", "REPO")),
-				Interactive:     true,
-				Surveyor:        testSurveyor{removeAllReviewers: true},
+				Interactive: true,
+				Surveyor: testSurveyor{
+					fieldsToEdit: func(e *shared.Editable) error {
+						e.Title.Edited = true
+						e.Body.Edited = true
+						e.Reviewers.Edited = true
+						e.Assignees.Edited = true
+						e.Labels.Edited = true
+						e.Projects.Edited = true
+						e.Milestone.Edited = true
+						return nil
+					},
+					editFields: func(e *shared.Editable, _ string) error {
+						e.Title.Value = "new title"
+						e.Body.Value = "new body"
+						e.Reviewers.Remove = []string{"monalisa", "hubot", "OWNER/core", "OWNER/external", "dependabot"}
+						e.Assignees.Value = []string{"monalisa", "hubot"}
+						e.Labels.Value = []string{"feature", "TODO", "bug"}
+						e.Labels.Add = []string{"feature", "TODO", "bug"}
+						e.Labels.Remove = []string{"docs"}
+						e.Projects.Value = []string{"Cleanup", "CleanupV2"}
+						e.Milestone.Value = "GA"
+						return nil
+					},
+				},
 				Fetcher:         testFetcher{},
 				EditorRetriever: testEditorRetriever{},
 			},
@@ -561,11 +655,115 @@ func Test_editRun(t *testing.T) {
 				mockRepoMetadata(reg, false)
 				mockPullRequestUpdate(reg)
 				mockPullRequestReviewersUpdate(reg)
+				mockPullRequestUpdateActorAssignees(reg)
 				mockPullRequestUpdateLabels(reg)
 				mockProjectV2ItemUpdate(reg)
 			},
 			stdout: "https://github.com/OWNER/REPO/pull/123\n",
 		},
+		{
+			name: "interactive prompts with actor assignee display names when actors available",
+			input: &EditOptions{
+				Detector:    &fd.EnabledDetectorMock{},
+				SelectorArg: "123",
+				Finder: shared.NewMockFinder("123", &api.PullRequest{
+					URL:                "https://github.com/OWNER/REPO/pull/123",
+					AssignedActorsUsed: true,
+					AssignedActors: api.AssignedActors{
+						Nodes: []api.Actor{
+							{
+								ID:       "HUBOTID",
+								Login:    "hubot",
+								TypeName: "Bot",
+							},
+						},
+						TotalCount: 1,
+					},
+				}, ghrepo.New("OWNER", "REPO")),
+				Interactive: true,
+				Surveyor: testSurveyor{
+					fieldsToEdit: func(e *shared.Editable) error {
+						e.Assignees.Edited = true
+						return nil
+					},
+					editFields: func(e *shared.Editable, _ string) error {
+						// Checking that the display name is being used in the prompt.
+						require.Equal(t, []string{"hubot"}, e.Assignees.Default)
+						require.Equal(t, []string{"hubot"}, e.Assignees.DefaultLogins)
+
+						// Adding MonaLisa as PR assignee, should preserve hubot.
+						e.Assignees.Value = []string{"hubot", "MonaLisa (Mona Display Name)"}
+						return nil
+					},
+				},
+				Fetcher:         testFetcher{},
+				EditorRetriever: testEditorRetriever{},
+			},
+			httpStubs: func(reg *httpmock.Registry) {
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableActors\b`),
+					httpmock.StringResponse(`
+					{ "data": { "repository": { "suggestedActors": {
+						"nodes": [
+							{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
+							{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
+						],
+						"pageInfo": { "hasNextPage": false }
+					} } } }
+					`))
+				mockPullRequestUpdate(reg)
+				reg.Register(
+					httpmock.GraphQL(`mutation ReplaceActorsForAssignable\b`),
+					httpmock.GraphQLMutation(`
+					{ "data": { "replaceActorsForAssignable": { "__typename": "" } } }`,
+						func(inputs map[string]interface{}) {
+							// Checking that despite the display name being returned
+							// from the EditFieldsSurvey, the ID is still
+							// used in the mutation.
+							require.Subset(t, inputs["actorIds"], []string{"MONAID", "HUBOTID"})
+						}),
+				)
+			},
+			stdout: "https://github.com/OWNER/REPO/pull/123\n",
+		},
+		{
+			name: "Legacy assignee users are fetched and updated on unsupported GitHub Hosts",
+			input: &EditOptions{
+				Detector:    &fd.DisabledDetectorMock{},
+				SelectorArg: "123",
+				Finder: shared.NewMockFinder("123", &api.PullRequest{
+					URL: "https://github.com/OWNER/REPO/pull/123",
+				}, ghrepo.New("OWNER", "REPO")),
+				Interactive: false,
+				Editable: shared.Editable{
+					Assignees: shared.EditableAssignees{
+						EditableSlice: shared.EditableSlice{
+							Add:    []string{"monalisa", "hubot"},
+							Remove: []string{"octocat"},
+							Edited: true,
+						},
+					},
+				},
+				Fetcher: testFetcher{},
+			},
+			httpStubs: func(reg *httpmock.Registry) {
+				// Notice there is no call to mockReplaceActorsForAssignable()
+				// and no GraphQL call to RepositoryAssignableActors below.
+				reg.Register(
+					httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+					httpmock.StringResponse(`
+					{ "data": { "repository": { "assignableUsers": {
+						"nodes": [
+							{ "login": "hubot", "id": "HUBOTID" },
+							{ "login": "MonaLisa", "id": "MONAID" }
+						],
+						"pageInfo": { "hasNextPage": false }
+					} } } }
+					`))
+				mockPullRequestUpdate(reg)
+			},
+			stdout: "https://github.com/OWNER/REPO/pull/123\n",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -579,9 +777,11 @@ func Test_editRun(t *testing.T) {
 			tt.httpStubs(reg)
 
 			httpClient := func() (*http.Client, error) { return &http.Client{Transport: reg}, nil }
+			baseRepo := func() (ghrepo.Interface, error) { return ghrepo.New("OWNER", "REPO"), nil }
 
 			tt.input.IO = ios
 			tt.input.HttpClient = httpClient
+			tt.input.BaseRepo = baseRepo
 
 			err := editRun(tt.input)
 			assert.NoError(t, err)
@@ -593,16 +793,16 @@ func Test_editRun(t *testing.T) {
 
 func mockRepoMetadata(reg *httpmock.Registry, skipReviewers bool) {
 	reg.Register(
-		httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
+		httpmock.GraphQL(`query RepositoryAssignableActors\b`),
 		httpmock.StringResponse(`
-		{ "data": { "repository": { "assignableUsers": {
-			"nodes": [
-				{ "login": "hubot", "id": "HUBOTID" },
-				{ "login": "MonaLisa", "id": "MONAID" }
-			],
-			"pageInfo": { "hasNextPage": false }
-		} } } }
-		`))
+			{ "data": { "repository": { "suggestedActors": {
+				"nodes": [
+					{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
+					{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
+				],
+				"pageInfo": { "hasNextPage": false }
+			} } } }
+			`))
 	reg.Register(
 		httpmock.GraphQL(`query RepositoryLabelList\b`),
 		httpmock.StringResponse(`
@@ -705,6 +905,15 @@ func mockPullRequestUpdate(reg *httpmock.Registry) {
 		httpmock.StringResponse(`{}`))
 }
 
+func mockPullRequestUpdateActorAssignees(reg *httpmock.Registry) {
+	reg.Register(
+		httpmock.GraphQL(`mutation ReplaceActorsForAssignable\b`),
+		httpmock.GraphQLMutation(`
+		{ "data": { "replaceActorsForAssignable": { "__typename": "" } } }`,
+			func(inputs map[string]interface{}) {}),
+	)
+}
+
 func mockPullRequestReviewersUpdate(reg *httpmock.Registry) {
 	reg.Register(
 		httpmock.GraphQL(`mutation PullRequestUpdateRequestReviews\b`),
@@ -736,48 +945,26 @@ func mockProjectV2ItemUpdate(reg *httpmock.Registry) {
 }
 
 type testFetcher struct{}
-type testSurveyor struct {
-	skipReviewers      bool
-	removeAllReviewers bool
-}
-type testEditorRetriever struct{}
 
 func (f testFetcher) EditableOptionsFetch(client *api.Client, repo ghrepo.Interface, opts *shared.Editable) error {
 	return shared.FetchOptions(client, repo, opts)
 }
 
+type testSurveyor struct {
+	fieldsToEdit func(e *shared.Editable) error
+	editFields   func(e *shared.Editable, editorCmd string) error
+}
+
 func (s testSurveyor) FieldsToEdit(e *shared.Editable) error {
-	e.Title.Edited = true
-	e.Body.Edited = true
-	if !s.skipReviewers {
-		e.Reviewers.Edited = true
-	}
-	e.Assignees.Edited = true
-	e.Labels.Edited = true
-	e.Projects.Edited = true
-	e.Milestone.Edited = true
-	return nil
+	return s.fieldsToEdit(e)
 }
 
-func (s testSurveyor) EditFields(e *shared.Editable, _ string) error {
-	e.Title.Value = "new title"
-	e.Body.Value = "new body"
-	if !s.skipReviewers {
-		if s.removeAllReviewers {
-			e.Reviewers.Remove = []string{"monalisa", "hubot", "OWNER/core", "OWNER/external", "dependabot"}
-		} else {
-			e.Reviewers.Value = []string{"monalisa", "hubot", "OWNER/core", "OWNER/external"}
-		}
-	}
-	e.Assignees.Value = []string{"monalisa", "hubot"}
-	e.Labels.Value = []string{"feature", "TODO", "bug"}
-	e.Labels.Add = []string{"feature", "TODO", "bug"}
-	e.Labels.Remove = []string{"docs"}
-	e.Projects.Value = []string{"Cleanup", "CleanupV2"}
-	e.Milestone.Value = "GA"
-	return nil
+func (s testSurveyor) EditFields(e *shared.Editable, editorCmd string) error {
+	return s.editFields(e, editorCmd)
 }
 
+type testEditorRetriever struct{}
+
 func (t testEditorRetriever) Retrieve() (string, error) {
 	return "vim", nil
 }
diff --git a/pkg/cmd/pr/shared/finder_test.go b/pkg/cmd/pr/shared/finder_test.go
index abc754d1a..0fd96e09b 100644
--- a/pkg/cmd/pr/shared/finder_test.go
+++ b/pkg/cmd/pr/shared/finder_test.go
@@ -9,6 +9,7 @@ import (
 
 	ghContext "github.com/cli/cli/v2/context"
 	"github.com/cli/cli/v2/git"
+	fd "github.com/cli/cli/v2/internal/featuredetection"
 	"github.com/cli/cli/v2/internal/ghrepo"
 	"github.com/cli/cli/v2/pkg/httpmock"
 	"github.com/stretchr/testify/require"
@@ -705,6 +706,81 @@ func TestFind(t *testing.T) {
 	}
 }
 
+func TestFindAssignableActors(t *testing.T) {
+	t.Run("given actors are not assignable, do nothing special", func(t *testing.T) {
+		reg := &httpmock.Registry{}
+		defer reg.Verify(t)
+
+		// Ensure we never request assignedActors
+		reg.Exclude(t, httpmock.GraphQL(`assignedActors`))
+		reg.Register(
+			httpmock.GraphQL(`query PullRequestByNumber\b`),
+			httpmock.StringResponse(`{"data":{"repository":{
+						"pullRequest":{"number":13}
+					}}}`))
+
+		f := finder{
+			httpClient: func() (*http.Client, error) {
+				return &http.Client{Transport: reg}, nil
+			},
+		}
+
+		pr, _, err := f.Find(FindOptions{
+			Detector: &fd.DisabledDetectorMock{},
+			Fields:   []string{"assignees"},
+			Selector: "https://github.com/cli/cli/pull/13",
+		})
+		require.NoError(t, err)
+
+		require.False(t, pr.AssignedActorsUsed, "expected PR not to have assigned actors used")
+	})
+
+	t.Run("given actors are assignable, request assignedActors and indicate that on the returned PR", func(t *testing.T) {
+		reg := &httpmock.Registry{}
+		defer reg.Verify(t)
+
+		// Ensure that we only respond if assignedActors is requested
+		reg.Register(
+			httpmock.GraphQL(`assignedActors`),
+			httpmock.StringResponse(`{"data":{"repository":{
+						"pullRequest":{
+						    "number":13, 
+							"assignedActors": {
+								"nodes": [
+									{
+										"id": "HUBOTID",
+										"login": "hubot",
+										"__typename": "Bot"
+									},
+									{
+										"id": "MONAID",
+										"login": "MonaLisa",
+										"name": "Mona Display Name",
+										"__typename": "User"
+									}
+								],
+								"totalCount": 2
+							}}
+					}}}`))
+
+		f := finder{
+			httpClient: func() (*http.Client, error) {
+				return &http.Client{Transport: reg}, nil
+			},
+		}
+
+		pr, _, err := f.Find(FindOptions{
+			Detector: &fd.EnabledDetectorMock{},
+			Fields:   []string{"assignees"},
+			Selector: "https://github.com/cli/cli/pull/13",
+		})
+		require.NoError(t, err)
+
+		require.Equal(t, []string{"hubot", "MonaLisa"}, pr.AssignedActors.Logins())
+		require.True(t, pr.AssignedActorsUsed, "expected PR to have assigned actors used")
+	})
+}
+
 func stubBranchConfig(branchConfig git.BranchConfig, err error) func(context.Context, string) (git.BranchConfig, error) {
 	return func(_ context.Context, branch string) (git.BranchConfig, error) {
 		return branchConfig, err
diff --git a/pkg/cmd/pr/shared/params_test.go b/pkg/cmd/pr/shared/params_test.go
index 15f00ca4f..53eb6328f 100644
--- a/pkg/cmd/pr/shared/params_test.go
+++ b/pkg/cmd/pr/shared/params_test.go
@@ -187,6 +187,67 @@ func TestMeReplacer_Replace(t *testing.T) {
 	}
 }
 
+func TestCopilotReplacer_ReplaceSlice(t *testing.T) {
+	type args struct {
+		handles []string
+	}
+	tests := []struct {
+		name string
+		args args
+		want []string
+	}{
+		{
+			name: "replaces @copilot with copilot-swe-agent",
+			args: args{
+				handles: []string{"monalisa", "@copilot", "hubot"},
+			},
+			want: []string{"monalisa", "copilot-swe-agent", "hubot"},
+		},
+		{
+			name: "handles no @copilot mentions",
+			args: args{
+				handles: []string{"monalisa", "user", "hubot"},
+			},
+			want: []string{"monalisa", "user", "hubot"},
+		},
+		{
+			name: "replaces multiple @copilot mentions",
+			args: args{
+				handles: []string{"@copilot", "user", "@copilot"},
+			},
+			want: []string{"copilot-swe-agent", "user", "copilot-swe-agent"},
+		},
+		{
+			name: "handles @copilot case-insensitively",
+			args: args{
+				handles: []string{"@Copilot", "user", "@CoPiLoT"},
+			},
+			want: []string{"copilot-swe-agent", "user", "copilot-swe-agent"},
+		},
+		{
+			name: "handles nil slice",
+			args: args{
+				handles: nil,
+			},
+			want: []string{},
+		},
+		{
+			name: "handles empty slice",
+			args: args{
+				handles: []string{},
+			},
+			want: []string{},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := NewCopilotReplacer()
+			got := r.ReplaceSlice(tt.args.handles)
+			require.Equal(t, tt.want, got)
+		})
+	}
+}
+
 func Test_QueryHasStateClause(t *testing.T) {
 	tests := []struct {
 		searchQuery string
diff --git a/pkg/cmd/pr/shared/survey_test.go b/pkg/cmd/pr/shared/survey_test.go
index 6895b52ac..7097d0761 100644
--- a/pkg/cmd/pr/shared/survey_test.go
+++ b/pkg/cmd/pr/shared/survey_test.go
@@ -28,9 +28,9 @@ func TestMetadataSurvey_selectAll(t *testing.T) {
 
 	fetcher := &metadataFetcher{
 		metadataResult: &api.RepoMetadataResult{
-			AssignableUsers: []api.RepoAssignee{
-				{Login: "hubot"},
-				{Login: "monalisa"},
+			AssignableUsers: []api.AssignableUser{
+				api.NewAssignableUser("", "hubot", ""),
+				api.NewAssignableUser("", "monalisa", ""),
 			},
 			Labels: []api.RepoLabel{
 				{Name: "help wanted"},
diff --git a/pkg/cmd/release/view/view_test.go b/pkg/cmd/release/view/view_test.go
index 8ca4f14c8..be345b186 100644
--- a/pkg/cmd/release/view/view_test.go
+++ b/pkg/cmd/release/view/view_test.go
@@ -150,8 +150,9 @@ func Test_viewRun(t *testing.T) {
 				
 				
 				Assets
-				windows.zip  12 B
-				linux.tgz    34 B
+				NAME         DIGEST           SIZE
+				windows.zip  sha256:deadc0de  12 B
+				linux.tgz                     34 B
 				
 				View on GitHub: https://github.com/OWNER/REPO/releases/tags/v1.2.3
 			`),
@@ -174,8 +175,9 @@ func Test_viewRun(t *testing.T) {
 
 
 				Assets
-				windows.zip  12 B
-				linux.tgz    34 B
+				NAME         DIGEST           SIZE
+				windows.zip  sha256:deadc0de  12 B
+				linux.tgz                     34 B
 
 				View on GitHub: https://github.com/OWNER/REPO/releases/tags/v1.2.3
 			`),
@@ -248,8 +250,8 @@ func Test_viewRun(t *testing.T) {
 				"published_at": "%[1]s",
 				"html_url": "https://github.com/OWNER/REPO/releases/tags/v1.2.3",
 				"assets": [
-					{ "name": "windows.zip", "size": 12 },
-					{ "name": "linux.tgz", "size": 34 }
+					{ "name": "windows.zip", "size": 12, "digest": "sha256:deadc0de" },
+					{ "name": "linux.tgz", "size": 34, "digest": null }
 				]
 			}`, tt.releasedAt.Format(time.RFC3339), tt.releaseBody))
 
diff --git a/pkg/cmd/root/help_test.go b/pkg/cmd/root/help_test.go
index d24a84a0d..40f333159 100644
--- a/pkg/cmd/root/help_test.go
+++ b/pkg/cmd/root/help_test.go
@@ -1,7 +1,20 @@
 package root
 
 import (
+	"fmt"
 	"testing"
+
+	"github.com/cli/cli/v2/internal/browser"
+	"github.com/cli/cli/v2/internal/config"
+	"github.com/cli/cli/v2/internal/gh"
+	"github.com/cli/cli/v2/pkg/cmdutil"
+	"github.com/cli/cli/v2/pkg/extensions"
+	"github.com/cli/cli/v2/pkg/iostreams"
+	"github.com/spf13/cobra"
+	"github.com/stretchr/testify/require"
+	"github.com/yuin/goldmark"
+	"github.com/yuin/goldmark/ast"
+	"github.com/yuin/goldmark/text"
 )
 
 func TestDedent(t *testing.T) {
@@ -44,3 +57,68 @@ func TestDedent(t *testing.T) {
 		}
 	}
 }
+
+// Since our online docs website renders pages by using the kramdown (a superset
+// of Markdown) engine, we have to check against some known quirks of the
+// syntax.
+func TestKramdownCompatibleDocs(t *testing.T) {
+	ios, _, _, _ := iostreams.Test()
+	f := &cmdutil.Factory{
+		IOStreams: ios,
+		Config:    func() (gh.Config, error) { return config.NewBlankConfig(), nil },
+		Browser:   &browser.Stub{},
+		ExtensionManager: &extensions.ExtensionManagerMock{
+			ListFunc: func() []extensions.Extension {
+				return nil
+			},
+		},
+	}
+
+	cmd, err := NewCmdRoot(f, "N/A", "")
+	require.NoError(t, err)
+
+	var walk func(*cobra.Command)
+	walk = func(cmd *cobra.Command) {
+		name := fmt.Sprintf("%q: test pipes are in code blocks", cmd.UseLine())
+		t.Run(name, func(t *testing.T) {
+			assertPipesAreInCodeBlocks(t, cmd)
+		})
+		for _, child := range cmd.Commands() {
+			walk(child)
+		}
+	}
+
+	walk(cmd)
+}
+
+// If not in a code block or a code span, kramdown treats pipes ("|") as table
+// column separators, even if there's no table header, or left/right table row
+// borders (i.e. lines starting and ending with a pipe).
+//
+// We need to assert there's no pipe in the text unless it's in a code-block or
+// code-span.
+//
+// (See https://github.com/cli/cli/issues/10348)
+func assertPipesAreInCodeBlocks(t *testing.T, cmd *cobra.Command) {
+	md := goldmark.New()
+	reader := text.NewReader([]byte(cmd.Long))
+	doc := md.Parser().Parse(reader)
+
+	var checkNode func(node ast.Node)
+	checkNode = func(node ast.Node) {
+		if node.Kind() == ast.KindCodeSpan || node.Kind() == ast.KindCodeBlock {
+			return
+		}
+
+		if node.Kind() == ast.KindText {
+			text := string(node.(*ast.Text).Segment.Value(reader.Source()))
+			require.NotContains(t, text, "|", `found pipe ("|") in plain text in %q docs`, cmd.CommandPath())
+		}
+
+		for child := node.FirstChild(); child != nil; child = child.NextSibling() {
+			checkNode(child)
+		}
+	}
+
+	checkNode(doc)
+}
diff --git a/pkg/cmd/run/rerun/rerun_test.go b/pkg/cmd/run/rerun/rerun_test.go
index 0dc74129e..77f0a922f 100644
--- a/pkg/cmd/run/rerun/rerun_test.go
+++ b/pkg/cmd/run/rerun/rerun_test.go
@@ -14,6 +14,7 @@ import (
 	"github.com/cli/cli/v2/pkg/cmdutil"
 	"github.com/cli/cli/v2/pkg/httpmock"
 	"github.com/cli/cli/v2/pkg/iostreams"
+	"github.com/cli/go-gh/v2/pkg/api"
 	"github.com/google/shlex"
 	"github.com/stretchr/testify/assert"
 )
@@ -374,7 +375,7 @@ func TestRerun(t *testing.T) {
 			errOut:  "no recent runs have failed; please specify a specific `<run-id>`",
 		},
 		{
-			name: "unrerunnable",
+			name: "API error (403)",
 			tty:  true,
 			opts: &RerunOptions{
 				RunID: "3",
@@ -392,10 +393,42 @@ func TestRerun(t *testing.T) {
 					}))
 				reg.Register(
 					httpmock.REST("POST", "repos/OWNER/REPO/actions/runs/3/rerun"),
-					httpmock.StatusStringResponse(403, "no"))
+					httpmock.JSONErrorResponse(403, api.HTTPError{
+						StatusCode: 403,
+						Message:    "blah blah",
+					}),
+				)
 			},
 			wantErr: true,
-			errOut:  "run 3 cannot be rerun; its workflow file may be broken",
+			errOut:  "run 3 cannot be rerun; blah blah",
+		},
+		{
+			name: "API error (non-403)",
+			tty:  true,
+			opts: &RerunOptions{
+				RunID: "3",
+			},
+			httpStubs: func(reg *httpmock.Registry) {
+				reg.Register(
+					httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/3"),
+					httpmock.JSONResponse(shared.SuccessfulRun))
+				reg.Register(
+					httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
+					httpmock.JSONResponse(workflowShared.WorkflowsPayload{
+						Workflows: []workflowShared.Workflow{
+							shared.TestWorkflow,
+						},
+					}))
+				reg.Register(
+					httpmock.REST("POST", "repos/OWNER/REPO/actions/runs/3/rerun"),
+					httpmock.JSONErrorResponse(500, api.HTTPError{
+						StatusCode: 500,
+						Message:    "blah blah",
+					}),
+				)
+			},
+			wantErr: true,
+			errOut:  "failed to rerun: HTTP 500: blah blah (https://api.github.com/repos/OWNER/REPO/actions/runs/3/rerun)",
 		},
 	}
 
diff --git a/pkg/cmd/run/shared/presentation_test.go b/pkg/cmd/run/shared/presentation_test.go
new file mode 100644
index 000000000..f91e38907
--- /dev/null
+++ b/pkg/cmd/run/shared/presentation_test.go
@@ -0,0 +1,398 @@
+package shared
+
+import (
+	"testing"
+	"time"
+
+	"github.com/MakeNowJust/heredoc"
+	"github.com/cli/cli/v2/pkg/iostreams"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestRenderJobs(t *testing.T) {
+	startedAt, err := time.Parse(time.RFC3339, "2009-03-19T00:00:00Z")
+	require.NoError(t, err)
+	completedAt, err := time.Parse(time.RFC3339, "2009-03-19T00:01:00Z")
+	require.NoError(t, err)
+
+	tests := []struct {
+		name        string
+		jobs        []Job
+		wantVerbose string
+		wantNormal  string
+		wantCompact string
+	}{
+		{
+			name: "nil jobs",
+			jobs: nil,
+		},
+		{
+			name: "empty jobs",
+			jobs: []Job{},
+		},
+		{
+			// This is not a real-world case, but nevertheless the code should
+			// be able to handle that without error/panic.
+			name: "in-progress job without steps",
+			jobs: []Job{
+				{
+					Name:      "foo",
+					ID:        999,
+					StartedAt: startedAt,
+					Status:    InProgress,
+				},
+			},
+			wantCompact: heredoc.Doc(`
+				* foo (ID 999)`),
+			wantNormal: heredoc.Doc(`
+				* foo (ID 999)`),
+			wantVerbose: heredoc.Doc(`
+				* foo (ID 999)`),
+		},
+		{
+			// This is not a real-world case, but nevertheless the code should
+			// be able to handle that without error/panic.
+			name: "successful job without steps",
+			jobs: []Job{
+				{
+					Name:        "foo",
+					ID:          999,
+					StartedAt:   startedAt,
+					CompletedAt: completedAt,
+					Status:      Completed,
+					Conclusion:  Success,
+				},
+			},
+			wantCompact: heredoc.Doc(`
+				✓ foo in 1m0s (ID 999)`),
+			wantNormal: heredoc.Doc(`
+				✓ foo in 1m0s (ID 999)`),
+			wantVerbose: heredoc.Doc(`
+				✓ foo in 1m0s (ID 999)`),
+		},
+		{
+			// This is not a real-world case, but nevertheless the code should
+			// be able to handle that without error/panic.
+			name: "failed job without steps",
+			jobs: []Job{
+				{
+					Name:        "foo",
+					ID:          999,
+					StartedAt:   startedAt,
+					CompletedAt: completedAt,
+					Status:      Completed,
+					Conclusion:  Failure,
+				},
+			},
+			wantCompact: heredoc.Doc(`
+				X foo in 1m0s (ID 999)`),
+			wantNormal: heredoc.Doc(`
+				X foo in 1m0s (ID 999)`),
+			wantVerbose: heredoc.Doc(`
+				X foo in 1m0s (ID 999)`),
+		},
+		{
+			name: "in-progress job with various step status values",
+			jobs: []Job{
+				{
+					Name:      "foo",
+					ID:        999,
+					StartedAt: startedAt,
+					Status:    InProgress,
+					Steps: []Step{
+						{
+							Name:        "passed",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Success,
+							Number:      1,
+						},
+						{
+							Name:       "skipped",
+							Status:     Completed,
+							Conclusion: Skipped,
+							Number:     2,
+						},
+						{
+							Name:        "failed 1",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Failure,
+							Number:      3,
+						},
+						{
+							Name:        "failed 2",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Failure,
+							Number:      4,
+						},
+						{
+							Name:      "in-progress",
+							StartedAt: startedAt,
+							Status:    InProgress,
+							Number:    5,
+						},
+						{
+							Name:   "pending",
+							Status: Pending,
+							Number: 6,
+						},
+					},
+				},
+			},
+			wantCompact: heredoc.Doc(`
+				* foo (ID 999)
+				  X failed 1
+				  X failed 2
+				  * in-progress`),
+			wantNormal: heredoc.Doc(`
+				* foo (ID 999)`),
+			wantVerbose: heredoc.Doc(`
+				* foo (ID 999)
+				  ✓ passed
+				  - skipped
+				  X failed 1
+				  X failed 2
+				  * in-progress
+				  * pending`),
+		},
+		{
+			// As of my observations (babakks) when there is a failed step, the
+			// job run is marked as failed. In other words, a successful job run
+			// cannot have any failed steps. That's why there's no failed steps
+			// in this test case.
+			name: "successful job with various step status values",
+			jobs: []Job{
+				{
+					Name:        "foo",
+					ID:          999,
+					StartedAt:   startedAt,
+					CompletedAt: completedAt,
+					Status:      Completed,
+					Conclusion:  Success,
+					Steps: []Step{
+						{
+							Name:        "passed 1",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Success,
+							Number:      1,
+						},
+						{
+							Name:       "skipped",
+							Status:     Completed,
+							Conclusion: Skipped,
+							Number:     2,
+						},
+						{
+							Name:        "passed 2",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Success,
+							Number:      3,
+						},
+					},
+				},
+			},
+			wantCompact: heredoc.Doc(`
+				✓ foo in 1m0s (ID 999)`),
+			wantNormal: heredoc.Doc(`
+				✓ foo in 1m0s (ID 999)`),
+			wantVerbose: heredoc.Doc(`
+				✓ foo in 1m0s (ID 999)
+				  ✓ passed 1
+				  - skipped
+				  ✓ passed 2`),
+		},
+		{
+			name: "failed job with various step status values",
+			jobs: []Job{
+				{
+					Name:        "foo",
+					ID:          999,
+					StartedAt:   startedAt,
+					CompletedAt: completedAt,
+					Status:      Completed,
+					Conclusion:  Failure,
+					Steps: []Step{
+						{
+							Name:        "passed 1",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Success,
+							Number:      1,
+						},
+						{
+							Name:       "skipped",
+							Status:     Completed,
+							Conclusion: Skipped,
+							Number:     2,
+						},
+						{
+							Name:        "failed 1",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Failure,
+							Number:      3,
+						},
+						{
+							Name:        "failed 2",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Failure,
+							Number:      4,
+						},
+						{
+							Name:        "passed 2",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Success,
+							Number:      5,
+						},
+					},
+				},
+			},
+			wantCompact: heredoc.Doc(`
+				X foo in 1m0s (ID 999)
+				  X failed 1
+				  X failed 2`),
+			wantNormal: heredoc.Doc(`
+				X foo in 1m0s (ID 999)
+				  ✓ passed 1
+				  - skipped
+				  X failed 1
+				  X failed 2
+				  ✓ passed 2`),
+			wantVerbose: heredoc.Doc(`
+				X foo in 1m0s (ID 999)
+				  ✓ passed 1
+				  - skipped
+				  X failed 1
+				  X failed 2
+				  ✓ passed 2`),
+		},
+		{
+			name: "multiple jobs",
+			jobs: []Job{
+				{
+					Name:      "in-progress",
+					ID:        999,
+					StartedAt: startedAt,
+					Status:    InProgress,
+					Steps: []Step{
+						{
+							Name:        "passed",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Success,
+							Number:      1,
+						},
+						{
+							Name:      "in-progress",
+							StartedAt: startedAt,
+							Status:    InProgress,
+							Number:    2,
+						},
+					},
+				},
+				{
+					Name:        "successful",
+					ID:          999,
+					StartedAt:   startedAt,
+					CompletedAt: completedAt,
+					Status:      Completed,
+					Conclusion:  Success,
+					Steps: []Step{
+						{
+							Name:        "passed",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Success,
+							Number:      1,
+						},
+						{
+							Name:       "skipped",
+							Status:     Completed,
+							Conclusion: Skipped,
+							Number:     2,
+						},
+					},
+				},
+				{
+					Name:        "failed",
+					ID:          999,
+					StartedAt:   startedAt,
+					CompletedAt: completedAt,
+					Status:      Completed,
+					Conclusion:  Failure,
+					Steps: []Step{
+						{
+							Name:        "passed",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Success,
+							Number:      1,
+						},
+						{
+							Name:        "failed",
+							StartedAt:   startedAt,
+							CompletedAt: completedAt,
+							Status:      Completed,
+							Conclusion:  Failure,
+							Number:      2,
+						},
+					},
+				},
+			},
+			wantCompact: heredoc.Doc(`
+				* in-progress (ID 999)
+				  * in-progress
+				✓ successful in 1m0s (ID 999)
+				X failed in 1m0s (ID 999)
+				  X failed`),
+			wantNormal: heredoc.Doc(`
+				* in-progress (ID 999)
+				✓ successful in 1m0s (ID 999)
+				X failed in 1m0s (ID 999)
+				  ✓ passed
+				  X failed`),
+			wantVerbose: heredoc.Doc(`
+				* in-progress (ID 999)
+				  ✓ passed
+				  * in-progress
+				✓ successful in 1m0s (ID 999)
+				  ✓ passed
+				  - skipped
+				X failed in 1m0s (ID 999)
+				  ✓ passed
+				  X failed`),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotCompact := RenderJobsCompact(&iostreams.ColorScheme{}, tt.jobs)
+			assert.Equal(t, tt.wantCompact, gotCompact, "unexpected compact mode output")
+
+			gotNormal := RenderJobs(&iostreams.ColorScheme{}, tt.jobs, false)
+			assert.Equal(t, tt.wantNormal, gotNormal, "unexpected normal mode output")
+
+			gotVerbose := RenderJobs(&iostreams.ColorScheme{}, tt.jobs, true)
+			assert.Equal(t, tt.wantVerbose, gotVerbose, "unexpected verbose mode output")
+		})
+	}
+}
diff --git a/pkg/cmd/run/watch/watch_test.go b/pkg/cmd/run/watch/watch_test.go
index 49e56217b..1471f64cf 100644
--- a/pkg/cmd/run/watch/watch_test.go
+++ b/pkg/cmd/run/watch/watch_test.go
@@ -57,6 +57,15 @@ func TestNewCmdWatch(t *testing.T) {
 				ExitStatus: true,
 			},
 		},
+		{
+			name: "compact status",
+			cli:  "1234 --compact",
+			wants: WatchOptions{
+				Interval: defaultInterval,
+				RunID:    "1234",
+				Compact:  true,
+			},
+		},
 	}
 
 	for _, tt := range tests {