fix(coding-agent): corrected off-by-one range errors in hashline edits

- Added auto-correction logic for off-by-one range start errors in hashline edits.
- Added safety check to prevent false-positive auto-correction when position already includes boundary.
- Added test coverage for off-by-one range correction and duplicate leading line handling.
- Extracted turn-aborted-guidance prompt to external markdown file for better maintainability.
This commit is contained in:
can1357
2026-03-05 02:03:37 +01:00
parent 06a6b96efe
commit b278ae85d1
5 changed files with 74 additions and 19 deletions
+26 -3
View File
@@ -458,6 +458,18 @@ function maybeWarnSuspiciousUnicodeEscapePlaceholder(edits: HashlineEdit[], warn
// Edit Application
// ═══════════════════════════════════════════════════════════════════════════
const MIN_AUTOCORRECT_LENGTH = 2;
function shouldAutocorrect(line: string, otherLine: string): boolean {
if (!line || line !== otherLine) return false;
line = line.trim();
if (line.length < MIN_AUTOCORRECT_LENGTH) {
// if brace, we allow
return line.endsWith("}") || line.endsWith(")");
}
return true;
}
/**
* Apply an array of hashline edits to file content.
*
@@ -630,9 +642,7 @@ export function applyHashlineEdits(
const trailingReplacementLine = newLines[newLines.length - 1]?.trimEnd();
const nextSurvivingLine = fileLines[edit.end.line]?.trimEnd();
if (
trailingReplacementLine &&
nextSurvivingLine &&
trailingReplacementLine === nextSurvivingLine &&
shouldAutocorrect(trailingReplacementLine, nextSurvivingLine) &&
// Safety: only correct when end-line content differs from the duplicate.
// If end already points to the boundary, matching next line is coincidence.
fileLines[edit.end.line - 1]?.trimEnd() !== trailingReplacementLine
@@ -642,6 +652,19 @@ export function applyHashlineEdits(
`Auto-corrected range replace ${edit.pos.line}#${edit.pos.hash}-${edit.end.line}#${edit.end.hash}: removed trailing replacement line "${trailingReplacementLine}" that duplicated next surviving line`,
);
}
const leadingReplacementLine = newLines[0]?.trimEnd();
const prevSurvivingLine = fileLines[edit.pos.line - 2]?.trimEnd();
if (
shouldAutocorrect(leadingReplacementLine, prevSurvivingLine) &&
// Safety: only correct when pos-line content differs from the duplicate.
// If pos already points to the boundary, matching prev line is coincidence.
fileLines[edit.pos.line - 1]?.trimEnd() !== leadingReplacementLine
) {
newLines.shift();
warnings.push(
`Auto-corrected range replace ${edit.pos.line}#${edit.pos.hash}-${edit.end.line}#${edit.end.hash}: removed leading replacement line "${leadingReplacementLine}" that duplicated preceding surviving line`,
);
}
fileLines.splice(edit.pos.line - 1, count, ...newLines);
trackFirstChanged(edit.pos.line);
}