github / github/gh-aw

Base-branch config-folder restore silently reverts changes that race the agent run (trusted triggers)

Open
#55,318 0 comments 0 reactions 0 assignees View on GitHub
community
Dominant language
Go
Stars
5.1k
Forks
541
Avg merge
5h 46m
Merged PRs (30d)
760

Description

### Summary

The base-branch config-folder snapshot/restore ([#26380](https://github.com/github/gh-aw/pull/26380)) silently **reverts legitimate changes** to `.github` / `.claude` / etc. in the PR it produces, whenever the base branch advances during the agent run. The snapshot is taken at *activation* time but the PR is committed against a *later* base tip, so the force-restore writes stale config back over changes that landed in between — and these deletions are attributed to the agent PR.

This is noted as intentional ("protects against concurrent base branch mutations during execution"), but for **trusted, non-fork** triggers (`workflow_dispatch`, `command`, scheduled) it produces incorrect, confusing diffs rather than protecting anything, and it silently defeats the `create_pull_request` `allowed_files` scoping (the restore runs outside that filter).

### Repro (observed in production)

1. A `command`-triggered workflow with `safe-outputs.create_pull_request.allowed_files` scoped to a single file, and `protect_top_level_dot_folders: true`.
2. Activation job checks out base (`master`) at SHA `A` and snapshots `GH_AW_AGENT_FOLDERS` (incl. `.github`) into `/tmp/gh-aw/base`.
3. During the ~2-minute agent run, an unrelated human PR merges to `master` (SHA `B`), adding a step to `.github/workflows/publish.yml`.
4. `create_pull_request` commits the agent's change on top of `B`; `restore_base_github_folders.sh` overwrites `.github` with the SHA-`A` snapshot.
5. Resulting PR diff: the intended one-line change **plus** a spurious deletion of the step added in `B` — even though the agent never touched `.github` and it isn't in `allowed_files`.

Confirmed at the git level: the workflow blob in the agent's commit is byte-identical to `^` (pre-change base), while the commit's parent already contains the change.

### Impact

- Silent reverts of workflow/config changes that raced the run — a correctness and, for `.github`/`.mcp.json`, a supply-chain-adjacent concern.
- `allowed_files` gives false confidence: it does not bound what the PR actually changes.
- Noisy PRs that reviewers must manually clean up (or worse, merge).

### Suggested fixes (either)

1. **Re-snapshot against the base tip at PR-creation time** (re-fetch base right before `restore_base_github_folders.sh`) so the restore reflects the actual PR base, not the activation-time base.
2. **Scope the force-restore to untrusted contexts only** (fork PRs / `pull_request_target` / `issue_comment`), where the threat model applies. Trusted `workflow_dispatch`/`command`/schedule runs don't need it.
3. At minimum, extend the `allowed_files` guard to the restore step so config-folder writes outside the declared scope are dropped rather than committed.

### Environment

- setup action `v0.77.5`, firewall/agent images `0.25.58`.
- Scripts `actions/setup/sh/{save,restore}_base_github_folders.sh` unchanged on `main` at time of filing.

Contributor guide

Open the contributing guide

Research direction

Start with actions/setup/sh/save_base_github_folders.sh and restore_base_github_folders.sh, then trace the create_pull_request path that invokes the restore. Reproduce the race with a base change during an agent run and inspect the resulting diff. Done means trusted runs no longer revert intervening base changes or bypass allowed_files, while the intended protection remains covered by tests or a documented verification.

Written by the indexing model from the issue text.

Assessment

Tech stack
git, github-actions, shell
Domain
ci-cd, devops, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.