fix: align apply-patch failure flow with codex

This commit is contained in:
Codex
2026-05-30 04:09:09 +00:00
parent 531d435777
commit ce519dbc0c
4 changed files with 5 additions and 17 deletions
+1 -12
View File
@@ -26,11 +26,10 @@ export interface PatchUpdateResult {
export interface ApplyPatchV2Outcome {
hunk: number;
action: PatchHunk["kind"] | "move";
status: "applied" | "failed" | "pending";
status: "applied" | "failed";
path: string;
targetPath?: string;
change?: string;
reason?: string;
partialChanges?: string[];
error?: {
name?: string;
@@ -296,18 +295,10 @@ async function applyPatchV2Hunks(executor: ApplyPatchV2Executor, hunks: PatchHun
...(partialChanges.length > 0 ? { partialChanges } : {}),
error: errorSummary(error),
});
for (let pendingIndex = index + 1; pendingIndex < hunks.length; pendingIndex += 1) {
outcomes.push({
...outcomeBase(hunks[pendingIndex] as PatchHunk, pendingIndex),
status: "pending",
reason: "skipped_after_previous_failure",
});
}
throw new ApplyPatchV2Error(error instanceof Error ? error.message : String(error), {
partialChanges: changed,
outcomes,
failed: outcomes.find((item) => item.status === "failed") ?? null,
pending: outcomes.filter((item) => item.status === "pending"),
cause: error instanceof ApplyPatchV2Error ? error.details : undefined,
});
}
@@ -342,7 +333,6 @@ function formatApplyPatchFailure(error: unknown): string {
lines.push("Patch status:");
appendOutcomeSection(lines, "Applied before failure:", outcomes.filter((item) => item.status === "applied"));
appendOutcomeSection(lines, "Failed:", outcomes.filter((item) => item.status === "failed"));
appendOutcomeSection(lines, "Pending after failure:", outcomes.filter((item) => item.status === "pending"));
}
return `${lines.join("\n")}\n`;
}
@@ -359,7 +349,6 @@ function formatOutcome(outcome: ApplyPatchV2Outcome): string {
const target = outcome.targetPath === undefined ? outcome.path : `${outcome.path} -> ${outcome.targetPath}`;
const base = `hunk ${outcome.hunk} ${outcome.action} ${target}`;
if (outcome.status === "applied") return outcome.change === undefined ? base : `${outcome.change} (${base})`;
if (outcome.status === "pending") return `${base} (pending)`;
const error = outcome.error?.message ?? "failed";
const partial = outcome.partialChanges === undefined || outcome.partialChanges.length === 0
? ""
+1 -1
View File
@@ -191,7 +191,7 @@ export function sshHelp(): unknown {
"When a one-line shell command is easier to type through the script path, `script -- '<command && command>'` runs that single string through the remote shell without waiting for stdin. When `script --` is followed by multiple tokens, it stays a direct argv form for commands such as `tran D601:/work script -- sed -n '1,20p' file`.",
"script and shell helper modes inject a tiny POSIX-compatible printf wrapper before user shell text, so portable printf headings such as `printf \"--- section ---\\n\"` work consistently under dash/sh and bash. Direct argv commands are unchanged.",
"For arbitrary stdin streams into a workload command, use a workload route plus `exec --stdin -- <command> ...`; this keeps the route as location-only and avoids heredoc/base64/tar shell wrapping.",
"`apply-patch` is the default remote text patch entry and uses the v2 local line-based patch engine with remote read/write operations, including Windows routes such as `D601:win/c/test`, so long Unicode/Chinese lines and pure insertion hunks avoid the legacy remote shell hunk parser. Its stdout/stderr follows Codex apply_patch text output rather than UniDesk JSON output; on multi-file failure, stderr lists applied, failed and pending hunks.",
"`apply-patch` is the default remote text patch entry and uses the v2 local line-based patch engine with remote read/write operations, including Windows routes such as `D601:win/c/test`, so long Unicode/Chinese lines and pure insertion hunks avoid the legacy remote shell hunk parser. Its stdout/stderr follows Codex apply_patch text output rather than UniDesk JSON output; on multi-file failure, stderr lists applied hunks before the first failed hunk and the failed hunk, then stops like Codex apply_patch.",
"`upload` and `download` are the default whole-file transfer entries for non-text and generated files. They write through remote temp files, verify byte count and SHA-256 on both sides, and fall back from a single stdin payload to bounded client-side chunks before treating provider-gateway limits as a server-side problem.",
"`apply-patch-v1` is the only legacy fallback entry: it rejects low-context update hunks by default, reports the matched file:line for each hunk on stderr, and only accepts --allow-loose when the caller has manually reviewed an intentionally ambiguous insertion.",
"script defaults to target /bin/sh and inherits provider proxy variables such as HTTP_PROXY/HTTPS_PROXY/ALL_PROXY/NO_PROXY; use --shell bash only for bash syntax such as pipefail, arrays, or [[ ... ]], not as a proxy workaround.",
+2 -3
View File
@@ -772,9 +772,8 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
&& failedCompoundVisibleV2.stderr.includes("M first.txt")
&& failedCompoundVisibleV2.stderr.includes("Failed:")
&& failedCompoundVisibleV2.stderr.includes("hunk 2 update second.txt")
&& failedCompoundVisibleV2.stderr.includes("Pending after failure:")
&& failedCompoundVisibleV2.stderr.includes("hunk 3 update third.txt"),
"v2 failed CLI path should print Codex-style stderr plus per-file applied/failed/pending summary",
&& !failedCompoundVisibleV2.stderr.includes("third.txt"),
"v2 failed CLI path should print Codex-style stderr plus applied/failed summary and stop before later hunks",
failedCompoundVisibleV2.stderr,
);