fix: strengthen apply-patch v2 mxcx hints
This commit is contained in:
@@ -126,7 +126,8 @@ export function applyPatchV2HelpPayload() {
|
||||
"A blank line in Add File is a line containing only +.",
|
||||
"Update File uses @@ or @@ context markers, followed by context lines starting with one extra space prefix and changed lines starting with - or +; for a column-0 source line `const x`, write ` const x`, and for a two-space-indented source line write three spaces total. Unified-diff line-range headers are accepted with hints for MiniMax compatibility.",
|
||||
"Prefer `trans <route> apply-patch < /tmp/patch.diff` for long patches, Windows paths, or quoting-sensitive content.",
|
||||
"MiniMax compatibility: stray @@ or unprefixed content inside Add File, unprefixed Update File context lines, and extra hunk/body lines after Delete File, are accepted with stderr hints."
|
||||
"MiniMax compatibility: stray @@ or unprefixed content inside Add File, unprefixed Update File context lines, and extra hunk/body lines after Delete File, are accepted with stderr hints.",
|
||||
"MiniMax/MXCX concatenated patch compatibility: repeated nested Begin Patch / End Patch markers are accepted with stderr hints, but the CLI still uses only the v2 engine and never auto-falls back to apply-patch-v1."
|
||||
],
|
||||
examples: {
|
||||
addFile: [
|
||||
@@ -151,6 +152,7 @@ export function applyPatchV2HelpPayload() {
|
||||
commonPitfalls: [
|
||||
"Do not put @@ after `*** Add File:`; @@ is only for Update File.",
|
||||
"Prefer canonical @@ or @@ context over unified diff headers such as `@@ -1,3 +1,4 @@`; v2 accepts those headers with a hint.",
|
||||
"If multiple printf/heredoc fragments were concatenated, keep one outer Begin/End envelope or include complete nested envelopes; v2 will hint on nested markers instead of trying the legacy helper.",
|
||||
"Do not use remote Python/Perl/sed heredocs for text patches when `trans <route> apply-patch` is available."
|
||||
],
|
||||
note: "apply-patch reads patch text from stdin and uses the v2 engine by default. Use `apply-patch-v1` only for the legacy helper."
|
||||
@@ -420,6 +422,7 @@ function formatApplyPatchFailure(error: unknown): string {
|
||||
const details = error instanceof ApplyPatchV2Error ? error.details : {};
|
||||
const outcomes = Array.isArray(details.outcomes) ? details.outcomes as ApplyPatchV2Outcome[] : [];
|
||||
const lines = [`${message.trimEnd()}`];
|
||||
appendParserFailureHint(lines, message, details);
|
||||
appendExpectedLinesFailureHint(lines, details);
|
||||
if (outcomes.length > 1 || outcomes.some((item) => item.status === "applied")) {
|
||||
lines.push("Patch status:");
|
||||
@@ -429,6 +432,21 @@ function formatApplyPatchFailure(error: unknown): string {
|
||||
return `${lines.join("\n")}\n`;
|
||||
}
|
||||
|
||||
function appendParserFailureHint(lines: string[], message: string, details: Record<string, unknown>): void {
|
||||
const text = typeof details.text === "string" ? details.text : "";
|
||||
const parserLine = typeof details.line === "number" ? `line ${details.line}` : "";
|
||||
if (message === "invalid hunk header") {
|
||||
const shown = text.length === 0 ? "" : ` (${JSON.stringify(text)})`;
|
||||
lines.push(`Hint: unexpected patch body ${parserLine}${shown}. For concatenated MiniMax/MXCX fragments, keep exactly one outer *** Begin Patch / *** End Patch envelope or make each nested fragment a complete Begin/End pair.`);
|
||||
lines.push("Hint: apply-patch remains the v2 engine only; fix the patch text or run `trans <route> apply-patch --help`, do not retry by switching to apply-patch-v1.");
|
||||
return;
|
||||
}
|
||||
if (message.startsWith("invalid patch:")) {
|
||||
lines.push("Hint: apply-patch expects a single stdin patch whose first non-wrapper line is *** Begin Patch and last line is *** End Patch. Quoted heredocs such as `cat <<'PATCH' > /tmp/patch.diff` preserve the required leading spaces.");
|
||||
lines.push("Hint: apply-patch remains the v2 engine only; v1 is never auto-selected for malformed MiniMax/MXCX patch envelopes.");
|
||||
}
|
||||
}
|
||||
|
||||
function appendExpectedLinesFailureHint(lines: string[], details: Record<string, unknown>): void {
|
||||
const cause = recordValue(details.cause) ?? details;
|
||||
const expected = typeof cause.expected === "string" ? cause.expected : "";
|
||||
@@ -439,6 +457,7 @@ function appendExpectedLinesFailureHint(lines: string[], details: Record<string,
|
||||
appendQuotedBlock(lines, expected, 20, 1600);
|
||||
appendExpectedLineDiagnostics(lines, cause);
|
||||
lines.push("Hint: re-read the target file around this hunk. In Update File hunks, every context line needs a leading space prefix; for a column-0 source line like `]);`, write ` ]);`.");
|
||||
lines.push("Hint: this is still handled by apply-patch v2. Correct the hunk context or prefixes; do not switch the same patch to apply-patch-v1.");
|
||||
}
|
||||
|
||||
function appendExpectedLineDiagnostics(lines: string[], cause: Record<string, unknown>): void {
|
||||
|
||||
@@ -1071,11 +1071,37 @@ export async function runSshArgvGuidanceContract(): Promise<JsonRecord> {
|
||||
missingPlusLargeInsertVisibleV2.stderr.includes("First expected line appears near target line(s): 2, 5")
|
||||
&& missingPlusLargeInsertVisibleV2.stderr.includes("Best partial context match: 2 expected line(s) matched")
|
||||
&& missingPlusLargeInsertVisibleV2.stderr.includes("large insertion whose new lines were written as context")
|
||||
&& missingPlusLargeInsertVisibleV2.stderr.includes("regenerate the patch instead of editing it with sed"),
|
||||
&& missingPlusLargeInsertVisibleV2.stderr.includes("regenerate the patch instead of editing it with sed")
|
||||
&& missingPlusLargeInsertVisibleV2.stderr.includes("handled by apply-patch v2")
|
||||
&& missingPlusLargeInsertVisibleV2.stderr.includes("do not switch the same patch to apply-patch-v1"),
|
||||
"v2 missing-plus failure should diagnose the MiniMax sed-regression pattern explicitly",
|
||||
missingPlusLargeInsertVisibleV2.stderr,
|
||||
);
|
||||
|
||||
const fragmentedEnvelopeFailureV2 = await applyPatchV2FixtureAttempt([
|
||||
"*** Begin Patch",
|
||||
"*** Update File: fragment.txt",
|
||||
"@@",
|
||||
"-alpha",
|
||||
"+ALPHA",
|
||||
"*** End Patch",
|
||||
"printf '%s\\n' '*** Begin Patch'",
|
||||
"*** End Patch",
|
||||
"",
|
||||
].join("\n"), {
|
||||
"fragment.txt": "alpha\n",
|
||||
}, { stderrOutput: true });
|
||||
assertCondition(fragmentedEnvelopeFailureV2.exitCode === 1 && fragmentedEnvelopeFailureV2.error === null, "v2 should reject non-patch shell fragments with a parser hint", fragmentedEnvelopeFailureV2);
|
||||
assertCondition(
|
||||
fragmentedEnvelopeFailureV2.stderr.includes("invalid hunk header")
|
||||
&& fragmentedEnvelopeFailureV2.stderr.includes("concatenated MiniMax/MXCX fragments")
|
||||
&& fragmentedEnvelopeFailureV2.stderr.includes("exactly one outer *** Begin Patch / *** End Patch envelope")
|
||||
&& fragmentedEnvelopeFailureV2.stderr.includes("v2 engine only")
|
||||
&& fragmentedEnvelopeFailureV2.stderr.includes("do not retry by switching to apply-patch-v1"),
|
||||
"v2 parser failure should hint the MXCX printf/heredoc envelope fix without falling back to v1",
|
||||
fragmentedEnvelopeFailureV2.stderr,
|
||||
);
|
||||
|
||||
const nestedEnvelopeV2 = await applyPatchV2FixtureAttempt([
|
||||
"*** Begin Patch",
|
||||
"*** Update File: nested-envelope.txt",
|
||||
|
||||
Reference in New Issue
Block a user