GitHub Repository Checkout
The checkout: frontmatter field controls how actions/checkout is invoked in the agent job. Configure custom checkout settings, check out multiple repositories, or disable checkout entirely.
By default, the agent checks out the repository where the workflow is running with a shallow fetch (fetch-depth: 1). If triggered by a pull_request event, it also checks out the PR’s selected immutable commit, as described in PR Revision Binding. For pull_request_target events, automatic PR checkout is disabled by default; pull-request-floating-head does not enable it. For most workflows, this default checkout is sufficient and no checkout: configuration is necessary.
Before the agent job, activation separately checks out workflow configuration to load runtime imports and skills. On pull_request, pull_request_review, and pull_request_review_comment events, this sparse checkout uses the PR base SHA, not the PR head; on other events it retains the normal activation ref. For reusable workflow_call workflows, it uses the resolved callee SHA when there is no active PR event, and skips cross-repository checkout if authentication may fall back to the caller’s repository-scoped GITHUB_TOKEN. The checkout: options below describe the agent job and do not override this activation checkout.
Use checkout: when you need to check out additional branches, check out multiple repositories, or to disable checkout entirely for workflows that don’t need to access code or can access code dynamically through the GitHub Tools.
PR Revision Binding
Section titled “PR Revision Binding”By default, the compiler-generated “Checkout PR branch” step fetches a specific commit SHA from the current repository’s origin, then verifies the checked-out HEAD before reporting success or recording a patch baseline. It does not resolve a moving refs/pull/N/head during fetch.
| Trigger | Selected revision by default | Head movement |
|---|---|---|
pull_request, except labeled and unlabeled | Event-recorded PR head SHA | Retains the event revision, even if the branch moves or is deleted. |
pull_request label events | Event-recorded PR head SHA | Must match the live head before and after fetch. |
pull_request_review / pull_request_review_comment | Review or review-comment commit_id | Must match the live head before and after fetch. |
Revision-bound workflow_dispatch | Supplied aw_context.pr_head_sha | Must match the live head before and after fetch. |
issue_comment, routed comment commands, or manual PR dispatch without revision evidence | Current API head SHA | Pins the selected head and rejects a detected change during fetch; does not establish revision approval. |
These rules apply only when the compiler generates automatic PR checkout. The floating-head opt-in replaces event/review revision selection with current-head selection.
Centralized command/label dispatch propagates event-recorded revision evidence in aw_context.pr_head_sha. A revision-bound dispatch must provide a full 40-character commit SHA matching the current PR head. Missing required evidence, malformed PR context, API failures, and detected head changes stop checkout rather than continuing on the previous workspace.
Issue comments contain no historical PR head SHA. Comment commands therefore authorize current-head execution, not approval of a particular revision. They require valid comment creation/edit and PR update timestamps and warn when the PR was updated after the triggering comment; the resolved head is pinned and checked again after fetch. Timestamps cannot prove which revision was reviewed: unrelated activity, including the router’s status comment, also updates the PR. Manual PR dispatch without command/label markers or a supplied revision also selects the current head; it is not revision approval. Workflows requiring revision approval should use a revision-bearing PR/review event or disable automatic PR checkout with checkout: { pull-request: false }. A comment command alone is not suitable for approving privileged execution of PR code.
These checks preserve the existing actor permission floor and same-repository bot restrictions. A matching SHA proves commit identity, not that PR code is safe to execute with credentials. Fetching still requires credentials available to the checkout step; the helper does not persist credentials, widen sparse paths, or unshallow a repository.
After successful checkout, the unified session records a workflow.pr_checkout_revision event from runtime/pr-checkout with prNumber and the full, lowercase selectedSha. This records commit identity without adding repository content or credentials; checkout log messages use 12-character SHA prefixes.
Allow Floating Head
Section titled “Allow Floating Head”To select the current PR head at execution time instead of the triggering event or review revision, explicitly opt in:
checkout: pull-request-floating-head: trueThe default is false. The value must be a literal boolean; strings such as "true" and expressions are rejected. With true, checkout ignores event/review SHA and comment freshness evidence, resolves the current head through the PR API, and uses the current commit count for fetch depth. It still fetches that specific SHA, rechecks the head after fetch, and verifies actual HEAD. A head change during fetch fails; this option does not restore mutable-ref fetching or bypass the server’s SHA-fetch requirements.
Actor permissions, fork/bot restrictions, dispatch repository/shape validation, and safe-output lifecycle checks remain enforced. The setting applies workflow-wide when any entry in a checkout: list enables it; it does not enable checkout when checkout: false, pull-request: false, or the compiler’s PR-target policy suppresses the PR checkout step. Recompile the workflow with the updated compiler/runtime to apply the setting.
Custom Checkout Settings
Section titled “Custom Checkout Settings”You can use checkout: to override default checkout settings (e.g., fetch depth, sparse checkout) without needing to define a custom job:
checkout: fetch-depth: 0 # Full git history github-token: ${{ secrets.MY_TOKEN }} # Custom authenticationOr use GitHub App authentication:
checkout: fetch-depth: 0 github-app: client-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }}You can also use checkout: to check out additional repositories alongside the main repository:
checkout: - fetch-depth: 0 - repository: owner/other-repo path: ./libs/other ref: main github-token: ${{ secrets.CROSS_REPO_PAT }}Configuration Options
Section titled “Configuration Options”| Field | Type | Description |
|---|---|---|
pull-request | boolean | Set false to disable automatic PR checkout and its paired base-config snapshot/restore steps. The normal repository checkout remains enabled. |
pull-request-floating-head | boolean | Select the current API head instead of event/review revision evidence. Defaults to false; literal boolean only. Applies workflow-wide if any checkout entry enables it. See Allow Floating Head. |
repository | string | Explicit repository in owner/repo format, or a newline-separated list with multi: true. Without multi, expressions must resolve to exactly one repository and need no allowlist. Does not support wildcards. Defaults to the current repository. |
repo | string | Alias for repository; specify only one. Does not support wildcards. |
multi | boolean | Explicitly enables runtime repository-list expansion and its restrictions. Defaults to false; must be a literal boolean. |
allowed-repos | string | string[] | Mandatory allowlist for multi: true; does not enable multi mode by itself. Accepts newline-separated patterns, an array, or an expression; supports scoped patterns such as owner/* and */repo, but not unrestricted "*" or "*/*". Ignored in single-repository mode. |
max-repos | integer | Maximum unique repositories per multi: true checkout entry. Requires allowed-repos in multi mode; defaults to 10. Must be a positive literal integer. Ignored in single-repository mode. |
allow-private-repos | boolean | With multi: true, allow private/internal targets in a runtime list hosted by a private/internal repository. Requires allowed-repos in multi mode; defaults to false and must be a literal boolean. Public hosts always reject non-public multi targets, even when true. Ignored in single-repository mode. |
ref | string | Branch, tag, or SHA to checkout. Defaults to the triggering ref. |
path | string | Path within GITHUB_WORKSPACE to place the checkout. Defaults to workspace root. |
github-token | string | Token for authentication. Use ${{ secrets.MY_TOKEN }} syntax. |
github-app | object | GitHub App credentials (client-id or app-id (deprecated), private-key, optional owner, repositories). Mutually exclusive with github-token. app is a deprecated alias for the field name. Run gh aw fix to auto-migrate app-id to client-id. |
safe-outputs-github-app | object | Optional per-checkout GitHub App credentials used exclusively for safe_outputs git operations on this checkout target (client-id or app-id (deprecated), private-key, optional owner, repositories). Does not change agent/activation checkout auth. See Cross-Organization safe_outputs Authentication for cross-org usage. |
fetch-depth | integer | Commits to fetch. 0 = full history, 1 = shallow clone (default). |
fetch | string | string[] | Additional Git refs to fetch after checkout. See Fetching Additional Refs. |
sparse-checkout | string | Newline-separated patterns for sparse checkout (e.g., .github/\nsrc/). |
submodules | string/bool | Submodule handling: "recursive", "true", or "false". |
lfs | boolean | Download Git LFS objects. |
current | boolean | Marks this checkout as the primary working repository. The agent uses this as the default target for all GitHub operations. Only one checkout may set current: true; the compiler rejects workflows where multiple checkouts enable it. |
force-clean-git-credentials | boolean | When true, the checkout step is generated with persist-credentials: true and followed by a dedicated cleanup step that scrubs both repo and submodule git credentials. Use this for submodule-heavy or sparse checkouts where the default persist-credentials: false post-step cleanup fails. See Cleaning Submodule Credentials. |
Runtime Repository Lists
Section titled “Runtime Repository Lists”An entry with multi: true expands repository (or repo) at runtime into one checkout per newline-separated owner/repo slug. It requires allowed-repos. This allows scheduled workers to process a changing repository set supplied through variables, inputs, or earlier step outputs.
Without multi: true (including multi: false), the runtime requires exactly one owner/repo slug. Repository expressions do not require allowed-repos or a non-root path prefix. Normal single-checkout authentication, private-repository access, LFS, submodules, current, and wiki behavior applies; none of the multi-repo restrictions below apply. Merely specifying allowed-repos does not enable expansion or constrain a single checkout.
checkout: repo: ${{ inputs.repo }} path: target current: truerepository and repo select explicit repository names, not wildcard patterns. Their expressions must resolve to concrete owner/repo slugs. Wildcards are supported only in allowed-repos, which filters the selected list and never discovers or selects repositories.
allowed-repos policies may use literal patterns or standalone references to repository or organization variables, such as ${{ vars.ALLOWED_WORKER_REPOS }}. Caller-controlled inputs, step or job outputs, computed expressions, and expressions combined with literal text are rejected at compilation. Only the selected repository list may come from caller-controlled data.
on: schedule: dailycheckout: - multi: true repository: ${{ vars.WORKER_REPOS }} path: targets allowed-repos: ${{ vars.ALLOWED_WORKER_REPOS }} max-repos: 5 github-token: ${{ secrets.CROSS_REPO_PAT }}For WORKER_REPOS containing org/api and org/web on separate lines, the checkouts are placed at targets/org/api and targets/org/web. With multi: true, path must be a literal, non-root prefix and allowed-repos is mandatory, even if the selector is literal or resolves to just the workflow repository. The allowlist may contain scoped wildcard patterns such as org/* or */api. Unrestricted patterns such as * and */* are rejected, including when an expression resolves to them at runtime. Matching and deduplication are case-insensitive; an empty selected list produces no additional checkouts, not all allowlisted repositories.
Every selected repository must pass the allowlist and GitHub API metadata checks before the checkout action is generated. Redirected repository names are checked against the allowlist again. Runtime lists allow only public targets by default. Set allow-private-repos: true on an entry to also allow private/internal targets when the hosting repository is private/internal. Public workflow repositories cannot check out private or internal repositories, even when this flag is true and the supplied token can access them. The flag requires allowed-repos and a literal boolean; it does not change ordinary static checkouts. Both checkout jobs enforce the same policy, and merged entries permit non-public targets only if every entry explicitly opts in. Omission or false prevents an imported entry from broadening access. Failed or incomplete visibility checks stop the job.
Enabled submodules are rejected at compilation and runtime for all repository lists because repository-controlled submodule URLs can bypass allowed-repos using the checkout token. Generated checkouts and follow-up fetches disable submodule recursion without removing repository-controlled .gitmodules files. Omit submodules or set it to false; static checkout submodule support is unchanged.
max-repos caps each runtime entry at 10 unique selected slugs by default, after case-insensitive deduplication and before metadata requests or checkout. Oversized lists fail the entire batch; they are not truncated. Set a positive literal integer to change the cap. Entries merged for the same repository expression and path use the smallest effective cap, including the default when omitted. Separate runtime entries have independent caps.
A disallowed repository fails the entire checkout step before any selected repository is cloned; it is never silently skipped. The rejection is included in the workflow’s agent failure issue, subject to the existing safe-outputs.report-failure-as-issue settings. Invalid runtime selectors or allowlists, unsafe paths, missing credentials, failed visibility checks, and exceeded repository limits also report their preparation failure through this path. Unexpected dependency errors report a generic preparation failure rather than exposing raw dependency diagnostics in the issue. Failures in the agent and safe-output checkout jobs both use this reporting path.
Runtime entries support the normal ref, history, sparse-checkout, fetch, and authentication options. lfs: true is rejected at compilation and runtime for all repository lists: repository-controlled LFS endpoints could download content outside allowed-repos using the checkout token. Static checkouts continue to support LFS. GitHub App authentication requires explicit owner and repositories scoping. current: true and wiki: true are not supported for repository lists. Existing destination directories and symlinked path ancestors are rejected rather than cleaned or followed.
The agent and PR safe-output jobs each build a checkout manifest recording the expanded repositories, their exact workspace paths, and their default branches. Safe-output handlers use that manifest to locate repositories even under deep prefixes. Before agent execution, finalization explicitly cleans and verifies every expanded checkout’s git config and submodule configs using the validated paths, regardless of prefix depth. Missing configs, unavailable cleanup helpers, and residual credentials stop the job. PR safe-output jobs use the same expansion and directory layout, retaining their effective PR checkout credentials, including global safe-output GitHub App authentication unless an entry specifies its own credentials; the agent receives credential-free checkouts.
Fetching Additional Refs
Section titled “Fetching Additional Refs”By default, actions/checkout performs a shallow clone (fetch-depth: 1) of a single ref. For workflows that need to work with other branches — for example, a scheduled workflow that must push changes to open pull-request branches — use the fetch: option to retrieve additional refs after the checkout step.
A dedicated git fetch step is emitted after the actions/checkout step. Authentication re-uses the checkout token (or falls back to github.token) via a transient http.extraheader credential — no credentials are persisted to disk, consistent with the enforced persist-credentials: false policy.
Each fetch: pattern is checked and fetched independently, for both static and runtime-selected repositories. Patterns that match no complete remote ref names are skipped without preventing other patterns from being fetched; suffix-only matches do not count. Authentication, network, and other discovery or fetch errors still fail the step. Additional fetches use the checkout’s effective fetch-depth (1 by default; 0 for full history). Expression-valued patterns are passed as data, not interpolated into shell scripts.
For example, fetch: ["main", "my-prefix/*"] fetches main even when no branches match my-prefix/*. A missing literal branch is also skipped; an unmatched pattern does not create a local ref.
| Value | Description |
|---|---|
"*" | All remote branches. |
"refs/pulls/open/*" | All open pull-request head refs (GH-AW shorthand). |
"main" | A specific branch name. |
"feature/*" | A glob pattern matching branch names. |
checkout: - fetch: ["*"] # fetch all branches (default checkout) fetch-depth: 0 # fetch full history to ensure we can see all commits and PR detailscheckout: - repository: githubnext/gh-aw-side-repo github-token: ${{ secrets.GH_AW_SIDE_REPO_PAT }} fetch: ["refs/pulls/open/*"] # fetch all open PR refs after checkout fetch-depth: 0 # fetch full history to ensure we can see all commits and PR detailscheckout: - repository: org/target-repo github-token: ${{ secrets.CROSS_REPO_PAT }} fetch: ["main", "feature/*"] # fetch specific branches fetch-depth: 0 # fetch full history to ensure we can see all commits and PR detailsGit Credentials After Checkout
Section titled “Git Credentials After Checkout”The generated checkout step uses persist-credentials: false, so the git credentials that actions/checkout used are removed once checkout completes. The agent then runs without credentials for the checked-out repository, and any git operation that must authenticate to the remote fails. In private repositories this includes:
git fetch,git pull,git clone, and directgit push- Checking out or switching to a remote branch that was not already fetched
- Deepening a shallow clone (
git fetch --unshallow) - On-demand blob fetches in partial (blobless) clones — operations on files absent from the initial checkout
Fetch everything the workflow needs at checkout time using fetch-depth and fetch:, and write changes through safe-output tools such as push-to-pull-request-branch rather than a direct git push. The agent is instructed not to configure credential helpers or run git credential fill, because authentication cannot succeed; credential errors are reported as a limitation instead of worked around.
Disabling Checkout (checkout: false)
Section titled “Disabling Checkout (checkout: false)”Set checkout: false to suppress both the default actions/checkout step and the PR-specific “Checkout PR branch” step entirely. Use this for workflows that access repositories through MCP servers or other mechanisms that do not require a local clone:
checkout: falseThis is equivalent to omitting the checkout step from the agent job. Custom dev-mode steps (such as “Checkout actions folder”) are unaffected.
PR Head Checkout and Branch Deletion
Section titled “PR Head Checkout and Branch Deletion”PR head checkout fetches the immutable commit selected by the
revision-binding rules, including for same-repository
PRs. Source-branch deletion does not change the selected revision, but checkout
still requires that commit to be available from origin. A queued review
rejects a detected head change instead of checking out a later revision.
The local branch keeps the PR’s source branch name; checkout does not recreate
the remote branch or fall back to the base branch.
A failed PR checkout stops the agent job; it never continues on the previous
base checkout. If an originally open PR closes and fetching the selected commit
fails with a missing-ref or upload-pack: not our ref <sha> error, a live closure check records lifecycle invalidation
separately from ordinary checkout errors. Authentication, permission, network,
unknown errors, and unavailable state evidence remain failures. Workflows triggered
on an already closed or merged PR still require a successful checkout of the
selected commit.
For work triggered while a PR was open, the safe-output manager checks live PR state before each handler, deferred retry, and buffered review submission. If the PR has closed, remaining effects are recorded as skipped, not applied. Closed-event workflows and staged previews are preserved. Configured safe-output tokens need read access to the triggering PR; a failed state lookup is an error, not proof of closure. PR-branch pushes also refuse to recreate deleted target branches. These checks cannot make a PR-state lookup and a later GitHub write atomic.
For body-only review loops, a bot explicitly listed in on.bots can trigger PR
checkout through pull_request: edited without pushing a commit. The event sender
must be a Bot whose login matches the triggering actor, and the runtime, PR head,
and PR base repository IDs must match. Unlisted bots and fork PRs still require
write-or-higher collaborator permission. Filter body edits with an if: condition
on github.event.changes.body to avoid re-reviewing unrelated edits.
Disabling the PR Branch Checkout
Section titled “Disabling the PR Branch Checkout”To keep the normal repository checkout but suppress the compiler-generated “Checkout PR branch” step and its associated base-config snapshot/restore steps, set pull-request: false under checkout:
checkout: pull-request: falseTarget-Only Checkout (permissions.contents: none)
Section titled “Target-Only Checkout (permissions.contents: none)”Sidecar (MultiRepoOps) workflows often need to operate on a target repository without checking out the repository that hosts the workflow itself. Setting permissions.contents: none suppresses only the default workflow-repository checkout and the “Checkout PR branch” step; any other explicitly configured checkout: entries (such as a target repository) are still checked out normally:
permissions: contents: nonecheckout: - repository: octo-org/target-repository path: target github-app: client-id: ${{ vars.TARGET_APP_CLIENT_ID }} private-key: ${{ secrets.TARGET_APP_PRIVATE_KEY }} owner: octo-org repositories: - target-repositoryThis differs from checkout: false, which disables checkout entirely (including any additional configured repositories). With permissions.contents: none, only the workflow’s own repository is skipped.
Safe-output patch and bundle generation is local-only. It never fetches
missing refs or objects from the agent-writable checkout, even when safe-output
API credentials are configured. All Git transports, including external remote
helpers and partial-clone lazy fetches, are disabled during artifact generation.
Repository-configured external diffs, text conversion, hooks, fsmonitor, and
signing are also disabled. Filtered artifacts are synthesized from committed
objects with index-only plumbing, without checkout or clean/smudge filters.
Raw repo-memory staging pins parent-directory descriptors before reading files;
it requires Linux descriptor-relative paths and fails closed on other platforms.
Prepare the required refs with checkout.fetch and sufficient history with
checkout.fetch-depth before agent execution; partial clones must also have the
required blobs locally. Incremental updates require the pre-agent PR-head baseline
recorded by the PR checkout step or an existing origin/<branch> tracking ref.
Missing baselines are reported rather than replaced with a broader diff.
Full-mode generation can use the local base branch when its origin tracking
ref is absent, including cross-repository checkouts. Filtered bundles preserve
the checkout’s shallow boundaries; deeper history is only needed when the
required baseline cannot be resolved locally. Failure messages retain fixed
checkout and workflow-compilation guidance without exposing raw Git stderr,
checkout paths, or caller-supplied ref names.
When local generation cannot resolve required refs or objects, non-staged PR
outputs can instead return result: "queued" with recovery: "object-pack".
This is not publication success. The agent packages available local objects
without fetching; the trusted safe-outputs job fetches the authorized baseline
into a new isolated bare repository, validates the objects, and regenerates the
patch and bundle before applying the existing file, size, and publication policies.
Agent repository configuration, remotes, hooks, and alternates are never copied.
The fallback selects objects reachable from the pinned tip, using available local baseline refs as exclusion hints. It omits unrelated branches, tags, unreachable history, and objects already reachable from those hints. Hints reduce transfer size but never authorize a publisher baseline or a remote request. The default 100,000-object and 64 MiB uncompressed limits apply to the selected objects, not the entire monorepo. The existing patch artifact previews every packed object. Recovery rejects redacted or mismatched previews. After redaction and before upload, the artifact guard also blocks recovery envelopes whose paired previews changed or disappeared. Publisher rejection alone cannot protect bytes already available in a downloadable artifact.
By default, the trusted publisher starts with a depth-one, treeless fetch of the authorized baseline. If required commit ancestry is absent, it increases depth geometrically through 16, 64, 256, 1,024, 4,096, 16,384, and at most 65,536 commits, keeping the first resolved revision pinned. Only the required trusted baseline/boundary tree snapshots are then fetched without blobs, rather than downloading every intermediate monorepo tree. It compares trees before importing agent objects and fetches only required old/new blobs whose identities were independently read from trusted fetched trees. Unchanged large files are not hydrated; missing agent-created bytes are rejected rather than requested from upload hints. Servers without filtering support fail explicitly instead of retrying an unfiltered clone. Authentication and other fetch failures are not retried.
All fetching finishes before agent objects are imported. By default, requests have a 60-second command deadline and share a 180-second network-operation deadline. Tree inspection is limited to one million entries, required baseline blob hydration to 64 MiB, and the uploaded pack/import to 80 MiB. Hydration size is checked after transfer; these limits are not a downloaded-byte budget.
Repository-configured recovery limits
Section titled “Repository-configured recovery limits”GitHub Actions repository configuration variables (Settings > Secrets and variables > Actions > Variables) configure the following budgets without
changing workflow frontmatter. The compiler binds them explicitly into the agent
runtime, nested safe-outputs MCP container, upload-validation steps, and trusted
publisher. Upload validation and publishing independently use repository values;
uploaded metadata and agent-written environment files cannot override their limits.
Existing compiled workflows require recompilation with the matching setup runtime.
| Repository variable | Default | Controls |
|---|---|---|
GH_AW_OBJECT_PACK_MAX_BYTES | 67108864 | Uncompressed objects, hydration, and derived pack/preview/header/output-buffer bounds |
GH_AW_OBJECT_PACK_MAX_OBJECTS | 100000 | Selected/parsed objects and derived tree/scan entry and fetch-batch budgets |
GH_AW_OBJECT_PACK_TIMEOUT_MS | 60000 | Each command and the derived shared network deadline |
GH_AW_OBJECT_PACK_FETCH_DEPTHS | 1,16,64,256,1024,4096,16384,65536 | Finite, strictly increasing history search depths |
For byte budget B, pack/import and Git graph buffers are ceil(5B/4), previews
are 2B, total envelope headers are max(1, floor(B/16384)), diagnostic buffers
are max(1, floor(B/64)), and nested regeneration Git buffers are ceil(25B/16).
Uncompressed object and post-transfer hydration budgets are each B.
For object budget N, tree and scan entry budgets are each 10N, and fetch
batches contain at most max(1, ceil(256N/100000)) IDs. The shared network deadline
is three times the command timeout. These ratios preserve all existing defaults:
80 MiB packs, 128 MiB previews, 4 KiB headers, 1 MiB diagnostic buffers, 100 MiB
nested Git buffers, one million tree/scan entries, 256-ID batches, and 180 seconds
for network operations.
Empty or unset variables retain defaults. Numeric settings accept positive decimal integers only; zero does not disable a limit. Depths accept a comma-separated strictly increasing list of positive integers. Invalid settings fail closed instead of being ignored or clamped. Node.js buffer/string and subprocess-timer capacities, Git’s depth range, and the pack format’s object-count range still apply. Configured budgets must also keep every derived buffer and timer representable.
For example, gh variable set GH_AW_OBJECT_PACK_MAX_BYTES --body 134217728
raises the byte budget to 128 MiB, packs to 160 MiB, and previews to 256 MiB.
Larger budgets can increase
runner memory, CPU, and network use; no setting relaxes repository authorization,
preview validation, native object validation, or the fetch-before-import boundary.
Incremental recovery requires the triggering PR’s exact pre-agent head SHA and
an authorized head repository; an uploaded baseline or a newer remote head cannot
replace it. Dynamic PR updates without that trusted event baseline, staged
outputs, larger selected changes, exhausted recovery budgets, and missing agent-created content still
require checkout provisioning. Full recovery uses the authorized base branch and
preserves the trusted dispatch-SHA baseline for same-repository feature runs.
Publisher credentials must authorize the fetched repository independently of any
credentials previously used by actions/checkout.
Fallback issues keep the original object-pack uploads immutable. Their recovery
commands require a clean, complete clone and reconstruct a binary patch from the
publisher-resolved baseline and tip, retaining configured exclusions. Object-pack
envelopes and previews cannot be applied directly with git fetch or git am.
GitHub Actions debug logging includes [object_pack_transport] resolution
stages, decision booleans, and object/byte counts. These traces omit repository
identities, refs, commit IDs, filesystem paths, credentials, object contents, and
raw subprocess diagnostics.
pull_request_target Checkout
Section titled “pull_request_target Checkout”For pull_request_target workflows, the “Checkout PR branch” step is auto-disabled when no checkout: key is present in frontmatter. This prevents hard failures when the PR head branch is deleted (merged/closed events) or inaccessible (fork PRs).
To check out a trusted ref such as the base SHA, opt in explicitly with a checkout: mapping:
on: pull_request_target: types: [opened, synchronize]checkout: repository: ${{ github.repository }} ref: ${{ github.event.pull_request.base.sha }}This produces a configured checkout pointing at the safe base ref; the “Checkout PR branch” step remains disabled for pull_request_target. Do not use github.event.pull_request.head.sha or refs/pull/.../head in a privileged pull_request_target job — that would check out untrusted code in a context with write permissions.
Explicit Trusted Checkout Policy
Section titled “Explicit Trusted Checkout Policy”Strict mode rejects pull_request_target checkouts unless checkout is explicitly disabled, targets the supported base repository/ref, or matches a literal pair in on.pull_request_target.allowed-checkouts. Declare this policy in the main workflow’s frontmatter; it is not inherited from imports. The default restrictions remain unchanged when the policy is omitted.
on: pull_request_target: types: [closed] branches: [main] forks: ["dotnet/*"] acknowledge-risk: true allowed-checkouts: - repository: dotnet/AspNetCore.Docs ref: mainif: github.event.pull_request.merged == true
checkout: - repository: dotnet/AspNetCore.Docs ref: main path: . current: trueon.pull_request_target.allowed-checkouts defaults to no additional trusted checkouts. Each entry requires a literal owner/repository and a non-empty literal branch, tag, or commit SHA. Matching is exact and case-sensitive; repository/ref pairs cannot be combined across entries. Expressions, wildcards, and refs/pull/... refs are rejected in the policy. Every configured checkout must independently satisfy the policy or the existing base-checkout rules. Do not configure checkout.fetch for these checkouts: follow-up fetches can add refs beyond the trusted checkout, so they do not satisfy the policy. External repositories with omitted or PR-dependent refs remain rejected. Wiki checkouts are not covered by this allowlist.
on.pull_request_target.acknowledge-risk defaults to false. Setting it to true suppresses only the strict-mode dangerous-trigger warning, including for checkout: false or a supported base checkout. It neither allows other checkouts nor bypasses fork-pattern validation, actor authorization, the unrestricted-forks warning, other strict validation, or safe-update checks. An allowlist without acknowledgment still emits the trigger warning.
Marking a Primary Repository (current: true)
Section titled “Marking a Primary Repository (current: true)”When a workflow running from a central repository targets a different repository, use current: true to tell the agent which repository to treat as its primary working target. The agent uses this as the default for all GitHub operations (creating issues, opening PRs, reading content) unless the prompt instructs otherwise. When omitted, the agent defaults to the repository where the workflow is running.
checkout: - repository: org/target-repo path: ./target github-token: ${{ secrets.CROSS_REPO_PAT }} current: true # agent's primary targetCleaning Submodule Credentials
Section titled “Cleaning Submodule Credentials”By default, generated checkout steps set persist-credentials: false, which causes actions/checkout to remove credentials in its post-step. In repositories with submodules or sparse checkouts, that post-step can fail with missing submodule URL or path errors.
Set force-clean-git-credentials: true on a checkout target to opt into an explicit cleanup step instead. The compiler emits the checkout with persist-credentials: true, then injects a Clean git credentials after checkout step immediately after it. The cleanup removes the credential helper and http.*.extraheader entries from both .git/config and any .git/modules/*/config, including nested submodules.
Cleanup is fail-closed: a failed helper, invalid git configuration, or residual credential helper, authentication header, or authenticated URL stops the job before agent execution. A separate read-only verification checks the effective configuration, including included config files, without logging token values. The final pre-agent credential cleanup has the same verification requirement. With no checkout or git config files, cleanup and verification are no-ops.
checkout: - repository: org/monorepo-with-submodules submodules: recursive force-clean-git-credentials: trueCross-Organization safe_outputs Authentication (safe-outputs-github-app)
Section titled “Cross-Organization safe_outputs Authentication (safe-outputs-github-app)”By default, the safe_outputs job uses GITHUB_TOKEN to check out repositories. The only exception is the default workspace checkout when the global safe-outputs.github-app serves create-pull-request or push-to-pull-request-branch for the workflow repository (no target-repo/head-repo, no per-handler app or token): it persists the minted App token so pushes run as the App and trigger workflows. Additional checkouts, per-handler apps and cross-repository targets never inherit the global App token for checkout, which prevents cross-org token confusion when the app is scoped to a different organization.
For workflows that target a different organization, use safe-outputs-github-app on the relevant checkout entry to supply a checkout token for the safe_outputs job:
checkout: - repository: OrgB/target-repo github-app: client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} owner: OrgB safe-outputs-github-app: client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} owner: OrgB current: true
safe-outputs: github-app: client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} owner: OrgB create-pull-request: target-repo: OrgB/target-repoIn this configuration:
- The agent job checks out
OrgB/target-repousing the app token scoped toOrgB. - The safe_outputs job checks out
OrgB/target-repousing thesafe-outputs-github-apptoken. - The safe_outputs job checks out the workflow repository using
GITHUB_TOKEN(the default), not theOrgB-scoped app token — avoiding the cross-org authentication failure. safe-outputs.github-appis used only for PR creation and push operations, not for checkout.
Checkout Merging
Section titled “Checkout Merging”Multiple checkout: configurations can target the same path and repository. This is useful for monorepos where different parts of the repository must be merged into the same workspace directory with different settings (e.g., sparse checkout for some paths, full checkout for others).
When multiple checkout: entries target the same repository and path, their configurations are merged with the following rules:
- Fetch depth: Deepest value wins (
0= full history always takes precedence) - Fetch refs: Merged (union of all patterns; duplicates are removed)
- Sparse patterns: Merged (union of all patterns)
- LFS: OR-ed (if any config enables
lfs, the merged configuration enables it) - Submodules: First non-empty value wins for each
(repository, path); once set, later values are ignored - Ref/Token/App: First-seen wins
Learn More
Section titled “Learn More”- Cross-Repository Operations - Reading and writing across multiple repositories
- Authentication Reference - PAT and GitHub App setup
- Multi-Repository Examples - Complete working examples
- Checkout Behavior Specification - Formal checkout semantics across activation, agent, and safe_outputs jobs