Work with repositories
An agent frequently needs source code that is not its recipe: the service it
reviews, the monorepo it migrates, the docs site it edits. A task boots with an
empty workspace/repos/, and nothing is cloned until you say so.
Access is split across four layers, each owning exactly one decision. Nothing below it can widen what a layer above it allows.
| Layer | Who sets it | Decides |
|---|---|---|
| GitHub App installation | A GitHub organization admin | What Introspection can see |
| Project repositories | An Introspection member | What is registered |
runtime.github in the recipe manifest | The recipe author | The grant: what this runtime may clone |
repositories on the task | The caller | The trigger: what this task does clone, and at what ref |
The last two are the split to keep in mind. A grant clones nothing on its own: it names the set a runtime is allowed to reach and how far its credential reaches. A task then picks the subset it actually wants, and the ref to clone it at.
This guide assumes the organization has a GitHub connection, the recipe already has a runtime you can deploy, and the Introspection CLI is signed in.
1. Register the repository to the project
A grant names repositories that are already registered to the recipe’s project, never arbitrary URLs. Registration is what binds a repository to the organization’s GitHub integration, and it is the reason a grant cannot reach a repository the project does not own.
The repository holding your recipe is registered for you: creating a recipe from
it, which introspection runtimes create and the dashboard’s new-runtime flow
both do, registers it against the organization’s GitHub integration as a side
effect. If the repository your agent needs to work on is also the one your recipe
lives in — a common shape for an agent that maintains its own service — you have
nothing to do here.
A repository that only ever appears as a workspace checkout has to be registered explicitly. Registration is authorized against the App installation, so a repository the installation cannot see is refused here rather than failing later at clone time.
introspection repositories add acme/api-service
introspection repositories listA full GitHub URL works too. The command is idempotent, so running it twice returns the existing registration. The dashboard and API provide the same operation; use the GitHub reference when you need those surfaces or the integration details.
Registering needs member authentication. repositories:write is a
member-role scope, so a project API key cannot register a repository — keys top
out at repositories:read. Use the dashboard, or the CLI signed in with
introspection login. Reading the list is fine with an API key.
Registering grants nothing by itself. It makes a repository addressable: the
token a sandbox mints is requested by repository id
(POST /v1/repositories/{id}/access-token takes no slug), so an unregistered
repository cannot be named in any request at all. What a runtime may reach is
still the grant in the next step.
2. Grant the repository to the runtime
Declare the grant in the runtime manifest, under runtime.github:
# .introspection/coding-agent.yaml
name: coding-agent
description: Reviews and patches the API service
path: apps/coding-agent
runtime:
github:
repositories:
- acme/api-service
- acme/webEach entry is an owner/name slug for a registered repository. The grant
carries nothing else — no ref, no depth, no destination folder — because those
are per-task decisions, and a checkout always lands at
workspace/repos/<repository name>.
Validate before deploying. introspection check mirrors the same grammar the
server enforces, so a typo surfaces locally instead of at deploy:
introspection checkThe grant derives from the runtime. A deploy never widens an existing one:
editing runtime.github in the manifest produces a new runtime version. A
running task’s checkout list is fixed at launch, so what it has already cloned
cannot change underneath it. Its token ceiling is read from the runtime on
each mint, so an explicit PATCH /v1/runtimes/{id} that rewrites the grant does
reach a task already in flight — treat that route as the privileged one it is.
It replaces config_json wholesale rather than merging, so send the runtime’s
current config with github edited into it.
A granted repository that is not registered to the project resolves to
nothing: it is never cloned and never mintable. This degrades quietly by
design, so that manifests and registration can be done in either order — but
it does mean a repository missing from step 1 looks like a grant that silently
does nothing. Case, at least, is not a way to trip on this: slugs match
case-insensitively, the way GitHub itself treats them, so acme/API-Service
and acme/api-service resolve to the same registration.
3. Choose the permission ceiling
By default the grant is read-only. permissions raises the ceiling on what a
token minted for these repositories may do:
runtime:
github:
repositories:
- acme/api-service
permissions:
contents: write
pull-requests: writeThe block mirrors GitHub Actions permissions:. Every supported read is
implicit, so declare only write elevations. Unknown or privileged capabilities
fail rather than silently widening access. The complete allow-list and the
hyphenated/underscored key rules live in the
GitHub permission reference.
Three ceilings nest, and the narrowest wins:
requested (one operation) ⊆ runtime.github.permissions ⊆ GitHub App installationA request beyond the runtime’s map is refused, never silently narrowed, so a permission missing from the manifest surfaces as a clear denial instead of an unexplained GitHub error later in the run.
Deploy the new runtime version once the manifest is right. Runtime sync audits the declared permissions against the App installation and logs a warning for anything the installation has not been approved for — a warning rather than a failure, because it checks external state a GitHub admin controls.
4. Clone the repositories in a task
The grant says what may be cloned. The task says what is:
CLI
introspection tasks create \
--runtime coding-agent \
--environment production \
--repository acme/api-service@main \
--prompt "Fix the flaky checkout test and open a pull request"--repository OWNER/NAME[@REF][:DEPTH] is repeatable, with no count limit.
The JavaScript and Python SDKs expose the same repositories task field. Use
their task-start references when the caller is an application rather than an
operator at the CLI.
ref accepts a branch, tag, or full 40-character commit sha. Omit it and the
clone takes the repository’s default branch, whatever it is named — nothing is
stored to make that work, so it cannot go stale when a repository is renamed or
its default branch changes. An abbreviated sha is not recognised as a pin: it
is passed to git as a branch name, so the clone fails and the repository is
dropped. depth defaults to a shallow clone; pass 0 for
full history when the agent needs to read it, such as for git log analysis or
a bisect.
A full commit sha ignores depth and always clones full history. Branches and
tags are cloned directly at the ref, but a raw sha has to be reached by checking
it out after the clone, which needs the history containing it. Pin to a sha when
you want an exact tree; expect the slower boot that comes with it.
An entry outside the runtime’s grant is dropped by the server, not treated
as a launch failure. So is one that fails to clone. A workspace repository is a
convenience, not a launch precondition — a task with other work to do still
starts. Malformed input is different: a bad slug, a ref with shell
metacharacters, the same repository named twice, or two repositories that would
land in the same folder are rejected with a 422 at task creation.
Clones run concurrently with the rest of boot and are joined before the first
turn, so the agent never sees a half-populated workspace. While they run, the
task reports a cloning_repos phase.
5. Work with the checkout
The checkout lands at workspace/repos/<repository name> — the repository name
only, never the owner, and not configurable. Two granted repositories may not
share a name for exactly this reason, and introspection check catches the
collision at authoring time.
Inside the sandbox, ordinary Git and GitHub tooling works against the repositories this task cloned, without special handling:
cd workspace/repos/api-service
git checkout -b fix/flaky-checkout-test
# ... the agent edits files ...
git commit -am "Fix flaky checkout test"
git push -u origin fix/flaky-checkout-test
gh pr create --fillA credential helper is installed on the sandbox’s Git configuration, so git,
gh, and anything built on them authenticate on their own — for the repositories
this task requested, which is what the in-sandbox manifest lists. A granted
repository the task did not ask for has no credential here, so add it to the
task’s repositories rather than expecting the grant alone to reach it. For a tool that does not go through Git’s credential path — curl, an
Octokit script, your own tooling — run it under the wrapper instead:
with-github-token -- ./scripts/sync-labels.sh
with-github-token --repo acme/api-service --permissions contents:write -- ./scripts/triage.mjs--repo is optional when the task requested exactly one repository, and
required when it requested several — the in-sandbox manifest lists what was
requested, including a repository whose clone failed, so a task that requested
two and cloned one still needs --repo. --permissions asks for more than the
operation would otherwise imply, still capped to the runtime’s ceiling; asking
past it is refused, not narrowed.
The wrapper puts a real token in that one child process’s environment, which is why it is the escape hatch rather than the default route: the command and anything it spawns can read it. Credentials are minted per operation, cover a single repository, carry only that operation’s permissions, and expire within an hour. Nothing long-lived is written to disk, and each concurrent clone mints its own.
6. Ship the change through review
An agent with contents: write and pull-requests: write can push a branch and
open a pull request. Treat that as the same capability you would grant a human
contributor, and gate it the same way.
GitHub grants merge to any token holding both permissions, so branch protection is the boundary that stops an agent merging its own work. Require review on the branches that matter before enabling write on a runtime that handles untrusted input. Prompt injection reaching an agent with push access is a code-execution path into your repository, and the review requirement is what makes it a proposal rather than a deploy.
Where the agent’s pull request goes next is your existing process, unchanged: review it, and merge it through Git. If the change is to the recipe itself, the agent development lifecycle covers how a merged commit becomes a production runtime version.
An agent’s recipe is a repository like any other, so listing it in the grant
and elevating contents gives the agent write access to its own instructions —
the pattern behind agents that fix themselves. The capability, the monorepo case
where you reach it without setting out to, and the two controls that keep a
person in the path are covered in self-improving
agents.
For registration errors, missing checkouts, credential failures, and the bounds on grants and clones — count limits, clone latency, baked-image exclusions, and why personal access tokens are not a supported substitute — see Troubleshooting.
Common failure pattern: granting the repository instead of the task
A runtime grant sets the maximum access a task may request; it does not place every granted repository into every sandbox. Request only the repositories the task needs, and keep the grant narrow enough that a prompt-injected agent cannot reach unrelated source. A missing checkout should lead you to inspect both the runtime grant and the task request, not to broaden permissions by default.
Done when
- The repository is registered to the project and appears in
GET /v1/repositories. introspection checkpasses on the manifest carrying the grant.- A task requesting the repository shows it checked out at
workspace/repos/<name>. - The agent’s credential reaches exactly as far as the work requires, and no further.
- Branch protection requires review on any branch the agent can push to.
Next
- Agent development lifecycle: move the recipe change that uses this grant through to production.
- Learn from production: turn what the agent got wrong on real code into a focused fix.
Related
- GitHub: the App installation, recipe source, and lifecycle events behind the connection.
- Runtime manifest: the manifest that carries the grant, and how versions pin it.
- Security: sandbox isolation, the credential boundary, and self-improving agents.
- CLI → Tasks: every option on
tasks create, including--repository.