Cloud runs
Repositories and pull requests
A cloud run given a GitHub repository and ref works on that code. The run's agent does the git and the GitHub itself, as a developer would: it commits, pushes a branch, opens a pull request when the task calls for one, watches its checks, reads the logs of a failed run, fixes, and pushes again. workbenches.dev does none of that for it, and never publishes anything on its own when a run ends.
Connect GitHub#
Connect GitHub for the repository's owner from your organization's sources page in the dashboard. It installs the workbenches.dev GitHub App, or links an installation you already have, to your organization.
- Linking an installation lets every member of the organization run on its repositories, private ones included, so you choose each installation.
- You need authority over the GitHub account: on a personal account it must be your own, and on a GitHub organization you must be an active admin of it.
- An installation already linked to an organization you do not belong to cannot be linked. One linked to another of your organizations can be reused.
The installation must grant contents, pull requests, workflows, issues, and actions (write), and checks and statuses (read). Without them GitHub refuses the requests the agent makes.
| GitHub connected for the owner | Not connected | |
|---|---|---|
| Public repository | Clone, push its branch, open and update a pull request | Clones anonymously. Cannot push. |
| Private repository | Clone, push its branch, open and update a pull request | Fails: The repository could not be fetched. If it is private, connect GitHub for <owner> and run again. |
A public repository outside the installation's repository selection is cloned anonymously, as if there were no installation.
Start a repository run#
Pass repository (owner/name) and ref together, and optionally branch:
{
"tool": "run",
"arguments": {
"workbench": "acme/code-review",
"task": "Fix the failing migration test and open a draft pull request.",
"repository": "acme/api",
"ref": "main",
"branch": "fix-migration-test"
}
}In POST /v1/runs these go in workspace: { "repository": "acme/api", "ref": "main", "branch": "fix-migration-test" }. ref is resolved to a commit before the sandbox is created, so a ref GitHub refuses fails the run before anything is billed.
The run's branch#
The agent may push exactly one branch:
workbenches/<branch>when you passbranch,- otherwise
workbenches/<session id>.
Every other branch, every tag, a signed push, and a compressed push are refused. Force pushes and deleting the run's own branch are allowed. Agents cannot push a name the task asks for, so name the branch with branch, not in the task. The agent is told which branch to push and to mention any requested name in the pull request.
A branch name is one segment, up to 100 characters, of ASCII letters, digits, and ., _, -. It follows git check-ref-format: it cannot be empty, start with - or ., end with . or .lock, contain .., @{, spaces, control characters, or any of ~ ^ : ? * [ \, be @, or start with wbs_ in any case. A name that breaks a rule is a 400 that says which.
A branch cannot be used by two unfinished runs of the same repository in one organization: the second is refused with 409 branch_in_use, naming the branch. Once the earlier run ends, a later run may name the same branch, which is how a follow-up carries on the work and its pull request. The repository is compared without regard to case and the branch with case, so fix-Shared and fix-shared differ.
The effective branch, prefix included, is branch on the run.
How the agent reaches GitHub#
No GitHub token ever enters the sandbox, not even to read. The sandbox holds one run credential that works only against workbenches.dev, which forwards each request to GitHub with an installation token scoped to the one repository. The credential is bound to the run, its organization, its repository, and its branch, and is revoked the moment the runner stops.
The runner's environment has GH_HOST, GH_ENTERPRISE_TOKEN, GH_PROMPT_DISABLED=1, GH_REPO, and WORKBENCH_BRANCH, so ordinary git and gh commands work. An image without gh gets a pinned release, downloaded from GitHub and checked against its published checksum before the runner starts. It goes in ~/.local/bin, which comes last on the runner's PATH, so it needs no root and never replaces a command the image has. When the install cannot work, as in an image with no way to download files, the run still goes, repository.ready and the MCP wait result carry a warning that says why, and the agent can push its branch but cannot open or watch a pull request.
The checkout is on a local branch named for the run's branch, cut from the base commit. Commits are authored as Workbenches <noreply@workbenches.dev>; the person who started the run is not named.
What the agent can do on GitHub#
- Read anything in the repository.
- Open a pull request whose head is its own branch, and update, comment on, mark ready, or convert to draft that pull request.
- Re-run GitHub Actions runs and jobs of its own branch.
Everything else is refused: merges, writes to contents, refs, and commits through the API, releases, settings, branch protection, hooks, secrets, workflow dispatches, cancelling or approving workflow runs, re-running other branches' CI, comments on issues or other pull requests, and requests outside the repository. A run may make 600 requests a minute; past that GitHub requests get 429 with Retry-After. To run CI on new code, the agent pushes a commit to its branch.
Protect your default branch#
A run's branch can run GitHub Actions workflows, including workflows the agent adds, with the repository's secrets and a GITHUB_TOKEN the workflow may elevate. The rule that the agent can push only its own branch is not a boundary against Actions. Protect the default branch and tags with rulesets, and keep secrets in environments that require approval.
The pull request#
When the agent opens or updates a pull request from its branch, workbenches.dev reads it back from GitHub and records it on the run as delivery:
{
"state": "published",
"branch": "workbenches/fix-migration-test",
"base_branch": "main",
"commit": "4f2c...",
"pull_request": {
"number": 128,
"url": "https://github.com/acme/api/pull/128"
}
}The run keeps the latest, and a delivery.completed event is added once per pull request and head commit. A run whose agent opened none has no delivery. MCP wait reports the pull request when the run ends.
The change#
When the runner stops, workbenches.dev stages everything in the checkout (git add -A, so .gitignore applies) and measures it against the base commit, including commits the agent made. The patch is kept in the run's outcome and returned by GET /v1/runs/:id/changes as text/x-diff, whatever happened on GitHub, for cancelled and failed runs too. Apply it with git apply. A patch over 10 MB is not kept, and the outcome carries a changes_too_large warning. The MCP changes tool shows the files with lines added and removed, and the diff.
Anonymous runs#
A run without an installation for the owner clones with no credential. It emits only repository.ready, records its baseline, and keeps its change in the outcome like any other, but it cannot push and has no delivery.
If the GitHub connection for the owner was unlinked or replaced after the run was created, the run fails with The GitHub connection for <owner> is no longer the one this run started with. Connect GitHub for <owner> and run again.