Skip to content

Preserve step-installed skills during base-branch restore; allow Claude's Skill tool - #62419

Merged
pelikhan merged 20 commits into
mainfrom
copilot/fix-claude-engine-base-folder-restore
Sep 22, 2026
Merged

pelikhan merged 20 commits into
mainfrom
copilot/fix-claude-engine-base-folder-restore

Conversation

Copilot AI commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

With engine: claude and an APM import, skills unpacked into .claude/skills/ were deleted before the agent started: the Restore agent config folders from base branch step either overwrote .claude from the base snapshot or rm -rf'd it when the base branch had no .claude/. ADR-28290 addressed this for pre-agent-steps, but the canonical shared/apm.md declares its restore under frontmatter steps:, which is emitted before Checkout PR branch, so the ordering invariant doesn't apply to it.

Compiled order for a pull_request workflow importing shared/apm.md:

- name: Restore APM packages            # steps: from the import
- name: Checkout PR branch
- name: Restore agent config folders from base branch   # rm -rf .claude

Changes

  • actions/setup/sh/restore_base_github_folders.sh — removes only the git-tracked files under each agent folder/root file, then overlays the base snapshot, instead of rm -rf + copy. After git checkout -B <branch> origin/pr-head everything the PR branch controls is tracked, so fork-injection protection is unchanged while untracked files written earlier in the same job (APM packages) survive. Collisions resolve in favor of the base-branch version.
  • Fallbacks — full rm -rf when the workspace is not a git worktree or when git ls-files fails (corrupt index); per-entry rm -rf so submodule gitlinks are removable. Tracked paths are read NUL-separated from a temp file since command substitution drops NULs.
  • pkg/workflow/claude_tools.go — adds Skill to Claude's default --allowed-tools. Previously any Skill call returned permission_denied unless the workflow used permission-mode: bypassPermissions. Claude only exposes the tool when a skill is registered, so this is inert otherwise.
  • Tests — three new cases in restore_base_github_folders_test.sh (untracked APM skills survive a base restore; folder absent from base keeps untracked files; ls-files failure falls back to full removal). Existing tests use non-git temp workspaces and therefore cover the fallback path.
  • Regenerated — Claude allowed-tools expectations, wasm golden, lock files; plus a changeset and a "Pull request runs" note in reference/dependencies.md.

Notes for review

The security property now rests on "PR-branch content ⟺ git-tracked". The one way to violate it would be attacker-controlled untracked files surviving into the workspace, which actions/checkout + git checkout -B does not produce.


pr-sous-chef
Run: https://github.com/github/gh-aw/actions/runs/35656487865

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 27.7 AIC · ⊞ 9.6K · ◷
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 21.9 AIC · ⊞ 9.6K · ◷
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 25.2 AIC · ⊞ 9.1K · ◷
Comment /souschef to run again


pr-sous-chef run https://github.com/github/gh-aw/actions/runs/35743734337

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • github.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "github.com"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 46.4 AIC · ⌖ 8.66 AIC · ⊞ 9.6K · ◷
Comment /souschef to run again

Copilot AI and others added 3 commits September 21, 2026 18:16
…laude Skill tool

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix base folder restore deleting APM-installed skills Preserve step-installed skills during base-branch restore; allow Claude's Skill tool Sep 21, 2026
Copilot AI requested a review from pelikhan September 21, 2026 18:25
@pelikhan
pelikhan marked this pull request as ready for review September 21, 2026 19:57
Copilot AI balanced review requested due to automatic review settings September 21, 2026 19:57
@github-actions

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

Warning

Threat Detection Engine Failure — The analysis engine could not complete. This is a tooling failure, not a security finding.

What happened

The threat detection engine failed to produce results.

Review the workflow run logs for details.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Lean already. Ship.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • ab.chatgpt.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

Generated by Ponytail Reviewer for #62419

@github-actions

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Security scanning failed for Design Decision Gate 🏗️. Review the logs for details.

No ADR enforcement needed: PR does not have the implementation label and has ≤100 new lines of code in business logic directories (53 additions).

🏗️ ADR gate enforced by Design Decision Gate 🏗️

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Preserving all untracked agent files permits earlier steps executing PR-controlled code to persist malicious skills past the security restore.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Low severity

Open (2)
What changed in this PR

This PR preserves step-installed skills during base-branch restoration and enables Claude’s Skill tool by default.

Changes:

  • Preserves untracked files while replacing Git-tracked agent configuration.
  • Adds restoration tests, documentation, and release notes.
  • Regenerates Claude tool allowlists in workflow fixtures and lock files.
File Description
actions/​setup/​sh/​restore_base_github_folders.sh Preserves untracked agent files during restore.
actions/​setup/​sh/​restore_base_github_folders_test.sh Tests tracked-file removal and fallbacks.
pkg/​workflow/​claude_tools.go Allows Claude’s Skill tool.
pkg/​workflow/​testdata/​TestWasmGolden_AllEngines/​claude.golden Updates Claude golden output.
docs/​src/​content/​docs/​reference/​dependencies.md Documents PR restoration behavior.
.changeset/​claude-skills-survive-base-restore.md Adds patch release note.
.github/​workflows/​step-name-alignment.lock.yml Regenerates Claude allowlist.
.github/​workflows/​static-analysis-report.lock.yml Regenerates Claude allowlist.
.github/​workflows/​smoke-github-claude.lock.yml Regenerates Claude allowlist.
.github/​workflows/​smoke-claude-on-copilot.lock.yml Regenerates Claude allowlist.
.github/​workflows/​smoke-agent-public-none.lock.yml Regenerates Claude allowlist.
.github/​workflows/​smoke-agent-public-approved.lock.yml Regenerates Claude allowlist.
.github/​workflows/​smoke-agent-all-none.lock.yml Regenerates Claude allowlist.
.github/​workflows/​smoke-agent-all-merged.lock.yml Regenerates Claude allowlist.
.github/​workflows/​sergo.lock.yml Regenerates Claude allowlist.
.github/​workflows/​semantic-function-refactor.lock.yml Regenerates Claude allowlist.
.github/​workflows/​scout.lock.yml Regenerates Claude allowlist.
.github/​workflows/​safe-output-health.lock.yml Regenerates Claude allowlist.
.github/​workflows/​ruflo-backed-task.lock.yml Regenerates Claude allowlist.
.github/​workflows/​prompt-clustering-analysis.lock.yml Regenerates Claude allowlist.
.github/​workflows/​portfolio-analyst.lock.yml Regenerates Claude allowlist.
.github/​workflows/​lockfile-stats.lock.yml Regenerates Claude allowlist.
.github/​workflows/​instructions-janitor.lock.yml Regenerates Claude allowlist.
.github/​workflows/​go-pattern-detector.lock.yml Regenerates Claude allowlist.
.github/​workflows/​go-fan.lock.yml Regenerates Claude allowlist.
.github/​workflows/​github-mcp-tools-report.lock.yml Regenerates Claude allowlist.
.github/​workflows/​github-mcp-structural-analysis.lock.yml Regenerates Claude allowlist.
.github/​workflows/​example-workflow-analyzer.lock.yml Regenerates Claude allowlist.
.github/​workflows/​deepsec-security-scan.lock.yml Regenerates Claude allowlist.
.github/​workflows/​daily-vulnhunter-scan.lock.yml Regenerates Claude allowlist.
.github/​workflows/​daily-harness-experiment-proposer.lock.yml Regenerates Claude allowlist.
.github/​workflows/​daily-elixir-credo-snippet-audit.lock.yml Regenerates Claude allowlist.
.github/​workflows/​daily-choice-test.lock.yml Regenerates Claude allowlist.
.github/​workflows/​daily-astrostylelite-markdown-spellcheck.lock.yml Regenerates Claude allowlist.
.github/​workflows/​aw-failure-investigator.lock.yml Regenerates Claude allowlist.

Comment on lines +20 to +24
# PR-branch content is removed by deleting the git-tracked files under each item
# rather than the whole directory. Everything the PR branch can control is
# tracked in git, so untracked files are necessarily produced by earlier steps of
# the agent job itself (for example the APM package restore, which unpacks skills
# into .claude/skills/ or .github/skills/). Those files are trusted and must

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 3cf8635: restored full removal of agent configuration folders, removed the tracked/untracked trust distinction, and removed the tests and release-note claims that relied on it.


## Pull request runs

On pull request triggers the agent job restores agent config folders (`.agents`, `.github`, and the engine folder such as `.claude`) from a base-branch snapshot so a fork PR cannot inject skill or instruction files. This restore removes only the files tracked by git in the PR branch, so package files that APM unpacked into `.claude/skills/` or `.github/skills/` earlier in the job are preserved.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 3cf8635: the paragraph now begins “For pull request triggers,” and describes the restored full-folder replacement behavior.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — no blocking issues found.

📋 Key Themes & Highlights

Analysis

The fix correctly reframes the security invariant from "path == PR-owned" to "path is git-tracked == PR-owned," which precisely matches how actions/checkout + git checkout -B populate the workspace. This is the right root-cause fix rather than a narrower one-off patch for APM skills specifically — it will transparently cover any future trusted step that writes untracked files into agent folders.

Test coverage (/tdd)

  • Test 7 exercises the primary regression scenario (untracked APM skill under .claude/skills/ survives while tracked PR content is replaced/removed) — directly reproduces the reported bug (#62167) before the fix and demonstrates it's fixed after.
  • Test 8 covers the "folder absent from base" branch with untracked files present.
  • Test 9 covers the git ls-files failure fallback (corrupt index), verifying the safe "remove everything" fallback still fires — this is the correct conservative choice since an unreadable index means tracked/untracked status can't be trusted.
  • All 25 assertions in restore_base_github_folders_test.sh pass locally, and go build ./pkg/workflow/... compiles cleanly with the updated claude_engine_tools_test.go expectations.

Design notes (/diagnosing-bugs)

  • Root cause (ordering of steps:-declared restores vs. Checkout PR branch vs. Restore agent config folders) is clearly explained in the PR description and the script's own header comment — good self-documentation for future maintainers.
  • The empty-directory cleanup (find "${dest}" -type d -empty -delete) after removing tracked files is a nice touch to avoid leaving stray empty dirs when a PR-tracked directory becomes empty.
  • Minor (non-blocking) observation: the NUL-separated temp-file approach for git ls-files -z is a robust way to avoid the classic word-splitting/newline pitfalls of command substitution — good attention to shell correctness here.

Positive Highlights

  • ✅ Root-cause fix, not a special-case patch for APM alone
  • ✅ Conservative fallbacks in both non-git and git-failure scenarios preserve the original "wipe everything" safety property
  • ✅ Regression tests added before/alongside the fix, covering the exact reported scenario plus edge cases
  • ✅ Documentation (dependencies.md) and changeset updated to reflect the new behavior
  • ✅ Skill allowed-tool addition is well-scoped and is a no-op when no skills are registered (per Claude's own tool-exposure behavior)

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 80.1 AIC · ⌖ 14.4 AIC · ⊞ 10.1K
Comment /matt to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Impeccable Review — bug_fix (harden, audit)

Reviewed the security-sensitive restore script rewrite and the Claude Skill allowed-tool addition.

What I checked

  • remove_pr_content() correctly scopes deletion to git ls-files-tracked paths, falls back to full rm -rf when the workspace isn't a git worktree or git ls-files fails (corrupt index), and cleans up now-empty directories — the invariant "PR-branch content ⟺ git-tracked" holds given checkout + git checkout -B.
  • Base-snapshot restore now uses cp -R "${SNAPSHOT}/." "${DEST}/" after clearing tracked content, so base-branch files win collisions as documented, while untracked (trusted, step-installed) files survive.
  • New tests (7–9) cover: untracked APM skills surviving a restore, tracked-only removal when a folder is absent from base, and fallback-to-full-removal on a corrupted git index. Golden/test-expectation updates for the Skill allowed-tool addition are consistent with the code change.
  • claude_tools.go: adding Skill to the default allowed-tools list is inert unless a skill is registered by Claude, matching the stated rationale.

No blocking issues found in the changed lines. Nice defensive coding — the NUL-separated temp-file handling for git ls-files -z output correctly avoids the command-substitution NUL-stripping pitfall.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · copilot · sonnet50 · 54.6 AIC · ⌖ 13.1 AIC · ⊞ 8.1K

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

pr-sous-chef
@copilot This PR still needs forward progress before a maintainer can merge it.

Please address the open review follow-ups and then run the pr-finisher skill.

Open review follow-ups (newest first):

I also requested a branch refresh for this PR.
Run: https://github.com/github/gh-aw/actions/runs/35656487865

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 27.7 AIC · ⊞ 9.6K · ◷
Comment /souschef to run again

Comment on lines +82 to +91
while IFS= read -r -d '' tracked; do
# -r handles gitlink entries (submodule directories)
rm -rf "${WORKSPACE:?}/${tracked:?}"
done <"${tracked_list}"
rm -f "${tracked_list}"

# Drop directories that only held PR-branch files
if [ -d "${dest}" ]; then
find "${dest}" -type d -empty -delete
fi

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This finding was raised against 203b4c6, which deleted only git-tracked paths. That behavior was reverted in 3cf8635: remove_pr_content no longer exists and the script again does a full rm -rf of each agent config folder (and rm -f of each root instruction file) before overlaying the base snapshot, removing entries that the base branch doesn't have at all. No untracked-equals-trusted assumption remains, so the fork-PR injection boundary is back to "remove the whole agent config folder".

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

pr-sous-chef
@copilot This PR still needs forward progress before a maintainer can merge it.
...

Ran the pr-finisher pass; no new code changes were needed.

  • Reviews — the two findings in the referenced overview were raised against d50ef09 and were already addressed in 3cf8635 (full agent-folder replacement restored; "For pull request triggers," wording fixed). The related GHAS finding about treating untracked files as trusted is moot for the same reason — the tracked/untracked trust distinction no longer exists in actions/setup/sh/restore_base_github_folders.sh. Generalization to all engines landed in 3972819.
  • Checks (local) — make fmt, make lint, make recompile (299/299, no drift) and the PR-scoped tests (pkg/workflow Claude tool / pre-agent-step / wasm golden tests, and restore_base_github_folders_test.sh, 16/16) all pass. TEST_UNIT_RUN_FULL=1 make test-unit has two failures — TestGetContainerPin_DefaultMCPImagesArePinned and TestCompileWorkflow_AllManifestContainersArePinned — both caused by the unpinned ghcr.io/github/github-mcp-server:v1.12.2 bump on main (Update default CLI and service versions #62497). I reproduced both on a clean origin/main worktree, so they are pre-existing and unrelated to this PR.
  • Mergeable — branch is already merged up to origin/main (f533506), nothing unpushed.

CI on the current HEAD is stale and needs a maintainer re-trigger (close/reopen or push); I cannot trigger it from here.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot update compiler threat specification

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

👏 Excellent work on fixing the APM skill preservation issue! This PR addresses a critical security and functional gap in the base-branch restore process while properly enabling Claude's Skill tool capability.

The approach is solid:

  • Preserves git-tracked semantics (PR branch content ⟺ tracked files)
  • Adds fallback logic for non-git or corrupt index scenarios
  • Includes comprehensive test coverage
  • Well-documented with security properties and review notes

This looks ready for review and merge.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • api.github.com

[!TIP]
api.github.com is blocked because GitHub API access uses the built-in GitHub tools by default. Instead of adding api.github.com to network.allowed, use tools.github.mode: gh-proxy for direct pre-authenticated GitHub CLI access without requiring network access to api.github.com:

tools:
  github:
    mode: gh-proxy

See GitHub Tools for more information on gh-proxy mode.

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "api.github.com"

See Network Configuration for more information.

Generated by ✅ Contribution Check · copilot · auto · 53.5 AIC · ⌖ 7.79 AIC · ⊞ 9.2K · ◷

Copilot AI commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

@copilot update compiler threat specification

Updated in 6d95c8c.

Added CTR-028 Agent Configuration Restore Provenance to specs/compiler-threat-detection-spec.md: for pull-request triggers, the generated agent job must emit the base-branch restore of every engine's agent configuration folders and root instruction files after the PR checkout and before any step that installs agent content or executes the agent.

  • Section 3 now states that sandbox bypass covers the provenance of the agent's own configuration, since head-branch agent config is attacker-controlled until the base snapshot replaces it.
  • Section 5.1 catalog entry, Section 7.1 mapping (pkg/workflow/pr.go, save_base_github_folders.sh, restore_base_github_folders.sh → compiler_pre_agent_steps_test.go, restore_base_github_folders_test.sh), and Section 8.1 test row T-CTR-043.
  • Compliance map CTR-028 → T-CTR-043 plus the non-sequential-ID note, spec version bumped to 1.0.37 with a Section 2 sync row, and a changelog entry.

TestFormal_* bijection/coverage/deprecation conformance tests, make fmt, make lint, make recompile (no drift), and restore_base_github_folders_test.sh all pass. Specification-only change — no compiler behavior was modified.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please refresh this branch from the base branch, address any remaining review feedback, and then run the pr-finisher skill before handing back to maintainers.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • github.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "github.com"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 22.9 AIC · ⊞ 9.6K · ◷
Comment /souschef to run again

Copilot AI and others added 3 commits September 22, 2026 05:08
…gine-base-folder-restore

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please refresh this branch from the base branch, address any remaining review feedback, and then run the pr-finisher skill before handing back to maintainers.

[!WARNING]...

Completed in 58501770. Merged current main via 66d074a, reviewed the remaining feedback and prior CI logs, ran the pr-finisher sequence, and pushed the linter-compatible Claude tool-name check. PR-specific tests and final pre-PR validation pass; the full suite only reports the two pre-existing github-mcp-server:v1.12.2 pin failures inherited from main.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

pr-sous-chef
@copilot This PR still needs forward progress before a maintainer can merge it.

Please address the remaining review feedback below, inspect the failing checks, refresh the branch if needed, and then run the pr-finisher skill.

Open review follow-ups (newest first):

Failed checks:

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • github.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "github.com"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 46.4 AIC · ⌖ 8.66 AIC · ⊞ 9.6K · ◷
Comment /souschef to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
@pelikhan
pelikhan merged commit d39baa4 into main Sep 22, 2026
35 checks passed
@pelikhan
pelikhan deleted the copilot/fix-claude-engine-base-folder-restore branch September 22, 2026 17:15
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.89.20

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

claude engine: base-folder restore deletes APM-installed .claude/skills before the agent starts

5 participants