-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Describe the bug
Permission prompts in git worktrees show the main repo path instead of the worktree path. Approving permissions enters an infinite loop — each new session or sub-agent re-prompts because the stored approval key doesn't match the actual working directory.
Affected version
0.0.410
Steps to reproduce the behavior
- Set
"experimental": truein~/.copilot/config.json - Create a worktree:
git worktree add ../my-feature feature-branch - Start Copilot CLI in the worktree
- Trigger a permission prompt (edit, bash, etc.)
- Prompt says "in this repo (
my-repo)" instead ofmy-feature - Approve → reprompted again even in same session
Expected behavior
No need to approve again what was marked as safe to do in current directory
Additional context
Copilot investigation:
Bug: PERSISTED_PERMISSIONS breaks git worktree support — location key resolves to wrong path
Description
The PERSISTED_PERMISSIONS feature (introduced in v0.0.407) breaks permission handling in git worktrees. When enabled (via "experimental": true in config), the permission dialog only offers "approve permanently for this location" — but the location key is resolved via git rev-parse --git-common-dir, which returns the main repository path instead of the worktree path.
This causes:
- Permission prompts display the wrong directory
- Approvals are stored under the wrong path in
permissions-config.json - Subsequent sessions in the same worktree cannot find the stored approvals (they look up using the main repo key, but the file paths they're checking are under the worktree)
- Sub-agents (task tool) that start fresh permission contexts re-prompt for already-approved permissions
Before v0.0.407, permissions were session-scoped only and worked correctly in worktrees because they didn't rely on filesystem path matching.
Environment
- Copilot CLI: 0.0.410
- OS: Ubuntu 20.04 (Linux 6.0.12)
- Node.js: v18.20.4
- Config:
"experimental": true(enablesPERSISTED_PERMISSIONSfeature flag) - Git: worktree setup with multiple parallel workspaces
Steps to Reproduce
- Ensure
"experimental": truein~/.copilot/config.json - Have a git repository cloned at
/home/user/my-repo - Create a worktree:
git worktree add /home/user/my-repo-feature feature-branch
cd /home/user/my-repo-feature- Start a Copilot CLI session:
copilot - Trigger any action that requires permission (e.g., edit a file, run bash)
- Notice the prompt says "in this repo (
my-repo)" — wrong path - Approve the permission
- Start a new session or trigger a sub-agent → re-prompted for the same permission
Expected Behavior
- Permission prompt shows the worktree path: "in this repo (
my-repo-feature)" - Approval is stored under the worktree path
- New sessions and sub-agents in the same worktree find the stored approval
Actual Behavior
- Prompt shows main repo: "in this repo (
my-repo)" - Approval stored under
/home/user/my-repoinpermissions-config.json - New sessions/sub-agents resolve to the same wrong key but file paths don't match → re-prompt
Regression Timeline
| Version | Date | Behavior |
|---|---|---|
| ≤ 0.0.406 | ≤ Feb 10 | Session-scoped permissions only → worktrees worked fine |
| 0.0.407 | Feb 12 | PERSISTED_PERMISSIONS added (staff-or-experimental) |
| 0.0.410 | Feb 16 | First location-based permission log entries; worktrees broken |
The PERSISTED_PERMISSIONS feature flag gates "staff-or-experimental", so this affects users with "experimental": true or staff accounts.
Root Cause: Source-Level Trace
The location key resolver function (qme in minified index.js):
async function qme(cwd) {
// Step 1: Get worktree-aware path (CORRECT)
let toplevel = exec("git", ["rev-parse", "--show-toplevel"], {cwd}).stdout.trim();
// Step 2: Get common git dir (points to MAIN repo for worktrees)
let commonDir = exec("git", ["rev-parse", "--git-common-dir"], {cwd}).stdout.trim();
// Step 3: BUG — prefers dirname(commonDir) over toplevel
return isAbsolute(commonDir)
? { locationKey: dirname(commonDir) } // ← WRONG for worktrees
: { locationKey: toplevel }; // ← correct, but only used as fallback
}For a normal repo, --git-common-dir returns .git (relative) → falls through to toplevel → works.
For a worktree, --git-common-dir returns /home/user/my-repo/.git (absolute) → uses dirname() → returns main repo path.
Why the dialog doesn't offer session-scoped approval as fallback
When PERSISTED_PERMISSIONS is enabled, the permission dialog replaces the session-scoped option:
if (featureFlags?.PERSISTED_PERMISSIONS && locationKey) {
// ONLY option: "Yes, and don't ask again for ... in this repo (my-repo)"
choices.push({kind: "approve-for-location", ...});
} else {
// Fallback: "Yes, and approve ... for the rest of the session"
choices.push({kind: "approve-for-session", ...});
}So users with experimental: true cannot fall back to session-scoped permissions.
Suggested Fix
Replace dirname(commonDir) with toplevel in the location key resolver:
// Before (buggy for worktrees):
return isAbsolute(commonDir)
? { locationKey: normalize(dirname(commonDir)), locationType: "repo" }
: { locationKey: normalize(toplevel), locationType: "repo" };
// After (worktree-aware):
return { locationKey: normalize(toplevel), locationType: "repo" };--show-toplevel is already computed and is worktree-aware. For normal repos it returns the same value as dirname(.git). For worktrees it returns the correct worktree root.
Workaround
Manually duplicate the permission entry in ~/.copilot/permissions-config.json for each worktree path:
{
"locations": {
"/home/user/my-repo": { "tool_approvals": [...] },
"/home/user/my-repo-feature": { "tool_approvals": [...] }
}
}Or disable the feature flag by setting "experimental": false in ~/.copilot/config.json (but this disables other experimental features too).
Impact
- Breaks git worktree workflows for all users with
experimental: trueor staff accounts - No session-scoped fallback — the feature replaces rather than supplements the old approval flow
- Sub-agents re-prompt — agents spawned via task tool get fresh permission contexts and can't find stored approvals
- Blocks autonomous workflows — parallel development with multiple worktrees requires manual config editing per worktree