mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
Distinguish failed Git inspection from missing worktree registration (#15525)
Apply the reviewed change for Distinguish failed Git inspection from missing worktree registration. Validation: required local typecheck and tests, passing CI, Greptile 5/5, and independent review of the exact source head. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
ed0c6958f8
commit
1d19f9b562
6 files changed
+235
-13
No files matched your search
@@ -67,6 +67,11 @@ import { isRuntimeOwnedGitBranch } from "./execution-workspace-branch-ownership.
|
||||
import { logActivity } from "./activity-log.js";
|
||||
import { readProjectWorkspaceRuntimeConfig } from "./project-workspace-runtime-config.js";
|
||||
import { workspaceGitOperationScheduler } from "./workspace-git-operation-scheduler.js";
|
||||
import {
|
||||
MANAGED_GIT_WORKTREE_REASON_CODES,
|
||||
readManagedGitInspectionDiagnostic,
|
||||
type ManagedGitInspectionDiagnostic,
|
||||
} from "./workspace-validation-diagnostics.js";
|
||||
import {
|
||||
cleanupWorktreeInstanceArtifacts,
|
||||
deriveWorktreeInstanceId,
|
||||
@@ -2606,13 +2611,8 @@ type GitWorktreeListEntry = {
|
||||
export type ManagedGitWorktreeBranchInspection = {
|
||||
valid: boolean;
|
||||
reason: string | null;
|
||||
reasonCode:
|
||||
| "missing_worktree"
|
||||
| "not_a_git_checkout"
|
||||
| "not_registered"
|
||||
| "wrong_repository_root"
|
||||
| "branch_mismatch"
|
||||
| null;
|
||||
reasonCode: typeof MANAGED_GIT_WORKTREE_REASON_CODES[number] | null;
|
||||
inspectionDiagnostic?: ManagedGitInspectionDiagnostic;
|
||||
repoRoot: string | null;
|
||||
worktreePath: string;
|
||||
expectedBranchName: string | null;
|
||||
@@ -2735,16 +2735,33 @@ async function resolvePathForWorktreeComparison(value: string): Promise<string>
|
||||
return fs.realpath(resolved).then((realPath) => path.resolve(realPath)).catch(() => resolved);
|
||||
}
|
||||
|
||||
async function listLinkedGitWorktreePaths(repoRoot: string): Promise<Set<string>> {
|
||||
const output = await runGit(["worktree", "list", "--porcelain"], repoRoot);
|
||||
async function listLinkedGitWorktreePaths(repoRoot: string): Promise<
|
||||
{ paths: Set<string>; diagnostic?: never } | { paths?: never; diagnostic: ManagedGitInspectionDiagnostic }
|
||||
> {
|
||||
let proc: Awaited<ReturnType<typeof executeProcess>>;
|
||||
try {
|
||||
proc = await executeProcess({ command: "git", args: ["worktree", "list", "--porcelain"], cwd: repoRoot });
|
||||
} catch (error) {
|
||||
return { diagnostic: readManagedGitInspectionDiagnostic({
|
||||
command: "worktree_list", failure: "spawn_failed", errorCode: (error as NodeJS.ErrnoException)?.code,
|
||||
})! };
|
||||
}
|
||||
if (proc.code !== 0) {
|
||||
return { diagnostic: readManagedGitInspectionDiagnostic({
|
||||
command: "worktree_list", failure: "nonzero_exit", exitCode: proc.code,
|
||||
})! };
|
||||
}
|
||||
if (proc.stdoutTruncated) {
|
||||
return { diagnostic: { command: "worktree_list", failure: "output_truncated" } };
|
||||
}
|
||||
const paths = new Set<string>();
|
||||
for (const line of output.split("\n")) {
|
||||
for (const line of proc.stdout.split("\n")) {
|
||||
if (!line.startsWith("worktree ")) continue;
|
||||
const worktree = line.slice("worktree ".length).trim();
|
||||
if (!worktree) continue;
|
||||
paths.add(await resolvePathForWorktreeComparison(worktree));
|
||||
}
|
||||
return paths;
|
||||
return { paths };
|
||||
}
|
||||
|
||||
export async function inspectManagedGitWorktreeBranch(input: {
|
||||
@@ -2783,8 +2800,18 @@ export async function inspectManagedGitWorktreeBranch(input: {
|
||||
};
|
||||
}
|
||||
|
||||
const listedWorktrees = await listLinkedGitWorktreePaths(repoRoot).catch(() => null);
|
||||
if (!listedWorktrees?.has(worktreePath)) {
|
||||
const listedWorktrees = await listLinkedGitWorktreePaths(repoRoot);
|
||||
if (listedWorktrees.diagnostic) {
|
||||
return {
|
||||
...base,
|
||||
valid: false,
|
||||
reason: "could not inspect the complete git worktree registration list",
|
||||
reasonCode: "git_inspection_failed",
|
||||
repoRoot,
|
||||
inspectionDiagnostic: listedWorktrees.diagnostic,
|
||||
};
|
||||
}
|
||||
if (!listedWorktrees.paths.has(worktreePath)) {
|
||||
return {
|
||||
...base,
|
||||
valid: false,
|
||||
@@ -2841,6 +2868,7 @@ async function validateLinkedGitWorktree(input: {
|
||||
reason: string;
|
||||
reasonCode: Exclude<ManagedGitWorktreeBranchInspection["reasonCode"], null>;
|
||||
actualBranchName?: string | null;
|
||||
inspectionDiagnostic?: ManagedGitInspectionDiagnostic;
|
||||
}
|
||||
> {
|
||||
const inspection = await inspectManagedGitWorktreeBranch({
|
||||
@@ -2855,6 +2883,7 @@ async function validateLinkedGitWorktree(input: {
|
||||
reason: inspection.reason ?? "unknown git worktree mismatch",
|
||||
reasonCode: inspection.reasonCode ?? "not_a_git_checkout",
|
||||
actualBranchName: inspection.actualBranchName,
|
||||
...(inspection.inspectionDiagnostic ? { inspectionDiagnostic: inspection.inspectionDiagnostic } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2867,6 +2896,7 @@ export function formatManagedGitWorktreeBranchInspection(input: ManagedGitWorktr
|
||||
worktreePath: input.worktreePath,
|
||||
expectedBranchName: input.expectedBranchName,
|
||||
actualBranchName: input.actualBranchName,
|
||||
...(input.inspectionDiagnostic ? { inspectionDiagnostic: input.inspectionDiagnostic } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3756,6 +3786,7 @@ export async function ensurePersistedExecutionWorkspaceAvailable(input: {
|
||||
reasonCode: validation.reasonCode,
|
||||
worktreePath: reuseWorktreePath,
|
||||
executionWorkspaceId: input.workspace.id ?? null,
|
||||
...(validation.inspectionDiagnostic ? { inspectionDiagnostic: validation.inspectionDiagnostic } : {}),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user