-
Notifications
You must be signed in to change notification settings - Fork 3.4k
fix: prevent worktree reuse across different local clones #1186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
f9d54ac
9f38502
e92ee2d
e2af7a2
14c57f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -423,7 +423,37 @@ export async function workflowRunCommand( | |
| ? await isolationDb.findActiveByWorkflow(codebase.id, 'task', options.branchName) | ||
| : undefined; | ||
|
|
||
| if (existingEnv && (await provider.healthCheck(existingEnv.working_path))) { | ||
| // Guard: skip reuse if the existing environment was created from a different | ||
| // local clone of the same remote (GH-1183). Two clones share one codebase_id | ||
| // (derived from the remote URL), so findActiveByWorkflow can return an | ||
| // environment that belongs to a sibling checkout. Compare the repo root that | ||
| // was recorded at creation time with the current working directory's repo root. | ||
| // Resolve repo root lazily: needed both for the reuse guard and for metadata storage | ||
| const currentRepoRoot = await git.findRepoRoot(cwd); | ||
| const envSourceRoot = | ||
| existingEnv && typeof existingEnv.metadata?.source_repo_root === 'string' | ||
| ? existingEnv.metadata.source_repo_root | ||
| : undefined; | ||
|
|
||
| // Intentional: when currentRepoRoot or envSourceRoot is unavailable, fall back | ||
| // to allowing reuse. This preserves backward compat for pre-existing envs and | ||
| // avoids blocking workflows when git is temporarily unavailable. Risk: could | ||
| // allow cross-checkout reuse in edge cases — logged as a warning below. | ||
| const reuseSameCheckout = | ||
| !existingEnv || !envSourceRoot || !currentRepoRoot || envSourceRoot === currentRepoRoot; | ||
|
Comment on lines
+432
to
+454
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "workflow.ts changed block:"
sed -n '426,536p' packages/cli/src/commands/workflow.ts
echo
echo "findRepoRoot() implementation:"
sed -n '14,40p' packages/git/src/repo.ts
echo
echo "getCanonicalRepoPath() implementation:"
sed -n '232,257p' packages/git/src/worktree.tsRepository: coleam00/Archon Length of output: 6635 Resolve the repo root before canonicalizing it. Line 434 calls Call 🛠️ Proposed fix let currentRepoRoot: string | null = null;
try {
- currentRepoRoot = await git.getCanonicalRepoPath(cwd);
- } catch {
- // Non-fatal: cwd may not be a git repo (handled later) or git unavailable
+ const repoRoot = await git.findRepoRoot(cwd);
+ currentRepoRoot = repoRoot ? await git.getCanonicalRepoPath(repoRoot) : null;
+ } catch (error) {
+ const err = error as Error;
+ getLog().error({ err, cwd }, 'worktree.reuse_root_detection_failed');
+ throw new Error(`Failed to resolve repository root for ${cwd}: ${err.message}`);
}Violates: "Never silently swallow errors related to unsupported or unsafe states — throw early with clear error messages." Also applies to: 535 🤖 Prompt for AI Agents |
||
|
|
||
| if (existingEnv && envSourceRoot && !currentRepoRoot) { | ||
| getLog().warn( | ||
| { path: existingEnv.working_path, envSourceRoot }, | ||
| 'worktree.reuse_root_undetectable' | ||
| ); | ||
| } | ||
|
|
||
| if ( | ||
| existingEnv && | ||
| reuseSameCheckout && | ||
| (await provider.healthCheck(existingEnv.working_path)) | ||
| ) { | ||
| if (options.fromBranch) { | ||
| getLog().warn( | ||
| { path: existingEnv.working_path, fromBranch: options.fromBranch }, | ||
|
|
@@ -463,6 +493,14 @@ export async function workflowRunCommand( | |
| workingCwd = existingEnv.working_path; | ||
| isolationEnvId = existingEnv.id; | ||
| } else { | ||
| // Log when skipping reuse due to cross-checkout mismatch (GH-1183) | ||
| if (existingEnv && !reuseSameCheckout) { | ||
| getLog().warn( | ||
| { path: existingEnv.working_path, envSourceRoot, currentRepoRoot }, | ||
| 'worktree.reuse_different_checkout' | ||
| ); | ||
| } | ||
|
|
||
| // Create new worktree | ||
| getLog().info( | ||
| { branch: branchIdentifier, fromBranch: options.fromBranch }, | ||
|
|
@@ -489,7 +527,7 @@ export async function workflowRunCommand( | |
| working_path: isolatedEnv.workingPath, | ||
| branch_name: isolatedEnv.branchName, | ||
| created_by_platform: 'cli', | ||
| metadata: {}, | ||
| metadata: { ...(currentRepoRoot ? { source_repo_root: currentRepoRoot } : {}) }, | ||
| }); | ||
|
|
||
| workingCwd = isolatedEnv.workingPath; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.