feat(edit): show closest candidate lines on oldText mismatch (#97032)#97305
feat(edit): show closest candidate lines on oldText mismatch (#97032)#97305ianalloway wants to merge 3 commits into
Conversation
When the edit tool can't match `oldText`, it now appends up to 3 of the closest lines from the file, each with its line number and a note on the likely cause (indentation difference, or a near-miss content difference such as an escaped vs literal character). Strings are JSON-escaped so whitespace and backslashes are visible. Purely enhances the error output — the matching algorithm is unchanged and the diagnostics only run on the existing not-found error path, so there are no side effects on successful edits. - add findClosestLines / describeClosestLines to edit-diff.ts (pure, tested) - appendMismatchHint targets the specific unmatched edit's oldText - keep the existing "Current file contents" hint - unit tests + an integration test for the indentation case Co-Authored-By: Claude Opus 4.8 <[email protected]>
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close as superseded: this branch is a useful candidate for edit mismatch diagnostics, but it still leaves candidate line text uncapped, while #97512 covers the same linked request with line, storage, and byte scan bounds plus proof. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Canonical path: Use #97512 as the canonical bounded implementation for the linked diagnostics request, then close or supersede the overlapping candidate branches. So I’m closing this here and keeping the remaining discussion on #97512. Review detailsBest possible solution: Use #97512 as the canonical bounded implementation for the linked diagnostics request, then close or supersede the overlapping candidate branches. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection shows current main lacks candidate diagnostics, and PR head shows the remaining edge case where raw candidate line text is rendered without a storage/output cap. Is this the best way to solve the issue? No for this branch. The edit-tool mismatch path is the right owner boundary, but #97512 is the safer implementation because it includes the same feature with bounded candidate collection and no-newline regression coverage. Security review: Security review cleared: The diff only changes edit-tool diagnostics and tests; it does not touch dependencies, workflows, secrets, permissions, install scripts, or package resolution. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against 6cb82eaab865. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4b1c47268
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }; | ||
| } | ||
| if (normalizedError.message.includes(EDIT_MISMATCH_MESSAGE)) { | ||
| throw appendMismatchHint(normalizedError, currentContent); | ||
| throw appendMismatchHint( | ||
| normalizedError, |
There was a problem hiding this comment.
Include closest-line hints for batched edit misses
When a call has more than one real edit and one oldText is missing, applyEditsToNormalizedContent() throws the multi-edit form from getNotFoundError() (Could not find edits[i] ...), which does not contain EDIT_MISMATCH_MESSAGE. This branch is therefore skipped and the newly added closest-line diagnostic never appears for batched edit calls, even though the tool prompt encourages batching multiple edits; match that error form or use a typed error before adding the hint.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Added EDIT_MISMATCH_MESSAGE_BATCH = "Could not find edits[" and extended the mismatch check to match both the single-edit and multi-edit error forms. Also changed pickUnmatchedOldText to parse the failing edit index directly from the error message (e.g. Could not find edits[1]) rather than re-doing a substring check — this correctly targets the edit that actually failed regardless of fuzzy-match semantics on earlier edits in the batch.
🤖 Addressed by Claude Code
| if (lineTrimmed.length === 0) { | ||
| continue; | ||
| } | ||
| const distance = levenshtein(lineTrimmed, anchorTrimmed); |
There was a problem hiding this comment.
Bound closest-line scans on large files
On any single-edit mismatch, describeClosestLines() now runs this Levenshtein calculation for every nonblank line in the full file before returning the already capped 800-byte content snippet. For large/generated files or long minified lines, a typo in oldText can spend seconds in diagnostics (the DP is capped per line but not by file size) and stall the agent instead of promptly returning the mismatch; add a file/line cap or cheap prefilter before computing distance.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Added CLOSEST_LINE_FILE_SEARCH_CAP = 500 and an early return in findClosestLines when lines.length > 500. A test was added that creates a 501-line file, triggers a mismatch, and asserts the error contains Current file contents: but not Closest line(s).
🤖 Addressed by Claude Code
Two follow-up fixes for the closest-line diagnostic: 1. File-size cap: `findClosestLines` now returns early when the file has more than 500 lines, avoiding a full O(n×m) Levenshtein walk on large or generated files that would stall the agent on a failed edit. 2. Batch-edit targeting: `pickUnmatchedOldText` previously used an exact-substring check that didn't match the fuzzy-match semantics of the edit applier. It now parses the failing edit index directly from the "Could not find edits[i]" error message, so the diagnostic always points at the edit that actually failed — not an earlier edit that fuzzy-matched. The mismatch check also recognises the batched error form so the hint fires for multi-edit calls. Tests added: batched-miss diagnostic and large-file scan skip. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Replace global `parseInt` with `Number.parseInt` and `new Array(n)` with
`Array.from({ length: n })` to pass the unicorn lint rules.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
|
ClawSweeper applied the proposed close for this PR.
|
Problem
When an edit tool call fails because
oldTextdoesn't match, the error only showed the raw file contents — no hint about why it didn't match. Agents had to re-read the file and guess whether the problem was wrong indentation, an escaped character, or a typo.Fix
Adds
findClosestLines/describeClosestLinestoedit-diff.ts. On a mismatch, the error message now includes the closest matching lines with a diagnostic note (indentation mismatch, content diff, etc.).Proof (real terminal output)
Triggering a mismatch where the file uses 4-space indent but the edit requests 8 spaces:
Tests
New tests cover:
Closes #97032
🤖 Generated with Claude Code