Skip to content

Bug #17: multi_edit Misleading Success Counter

Status: RESOLVED in v3.16.0 Category: Correctness / Verification Severity: Medium-High (misleading output forces wasteful re-reads to verify) Resolution Date: 2026-03-02

multi_edit reported ambiguous results. In real-world usage:

OK: 1/2 edits, 193 lines [backup:20260302-165154-27ead86d9eb8ef82]

The message indicated that only 1 of 2 edits was applied — but reading the file confirmed both changes were present. The only way to verify was a manual read_file_range call on the edited zone, wasting tokens.

When Edit 1’s replacement subsumes Edit 2’s intended change (overlapping edits), Edit 2’s oldText no longer exists in currentContent after Edit 1 is applied. The edit loop marked it as “failed” even though the file was already in the correct state.

Example:

  1. Edit 1: Replace entire function body (includes the line Edit 2 targets)
  2. Edit 2: Replace just one line within that function → oldText gone → “failed”
  3. Result: “1/2 edits” but file is correct

Comparing EditFile() vs MultiEdit(), MultiEdit was missing:

FeatureEditFileMultiEdit (before fix)
Risk assessmentCalculateChangeImpact()Missing
CRITICAL blockingShouldBlockOperation()Missing
Risk warningFormatRiskNotice()Missing
Context validationvalidateEditContext()Missing
Pre/Post hooksHookPreEdit / HookPostEditMissing
Per-edit detailN/A (single edit)Missing
  • Misleading output: “1/2 edits” implies failure when the file is correct
  • Wasted tokens: Forced read_file_range verification calls after every multi_edit
  • No risk protection: CRITICAL risk rewrites via multi_edit bypassed all safety checks
  • No hooks: Custom validation logic could not intervene in batch edits

When performIntelligentEdit fails for an edit, check:

  • Is newText already present in currentContent?
  • Is oldText absent from currentContent?

If both true → the edit was subsumed by a prior edit in the batch. Count as already_present (not failed).

New EditDetail struct captures the outcome of each individual edit:

type EditDetailStatus string // "applied", "already_present", "failed"
type EditDetail struct {
Index int
Status EditDetailStatus
OldTextSnippet string
NewTextSnippet string
MatchConfidence string
Error string
}
FeatureImplementation
Context validationValidates all edits against original content; only hard-blocks if NO edit passes
Risk assessmentSimulates all edits to compute aggregate impact on final vs original content
CRITICAL blockingShouldBlockOperation(force) — only >=90% blocks without force: true
Risk warningFormatRiskNotice() for MEDIUM/HIGH appended to response
Pre/Post hooksHookPreEdit before loop, HookPostEdit after write
Skip unnecessary writeIf all edits are already_present, no file I/O occurs

Compact mode:

OK: 2 edits (1 applied, 1 already present), 193 lines [backup:xxx]

Verbose mode:

Multi-edit completed on /path/to/file
Total edits: 2
Applied: 1
Already present: 1
Lines affected: 193
Confidence: high
Edit details:
edit 1: applied (confidence: high)
edit 2: already present (subsumed by prior edit)
FileChange
core/edit_operations.goEditDetailStatus, EditDetail types; MultiEditResult extended with SkippedEdits + EditDetails; MultiEdit() rewritten with context validation, risk assessment, hooks, already_present detection; calculateMultiEditImpact() + truncateText() helpers
main.gomulti_edit handler response formatting for compact and verbose modes
tests/bug17_test.go9 regression tests
tests/bug16_test.goTest content enlarged to avoid CRITICAL threshold on small files
  • 9 new regression tests in tests/bug17_test.go:
    • TestBug17_OverlappingEditsNotMisreported — Edit 1 subsumes Edit 2 → SkippedEdits=1, FailedEdits=0
    • TestBug17_AllEditsApplied — 2 independent edits → SuccessfulEdits=2
    • TestBug17_GenuineFailureStillReported — missing oldText → FailedEdits=1
    • TestBug17_RiskAssessmentCriticalBlocks — ~100% rewrite without force → OPERATION BLOCKED
    • TestBug17_RiskAssessmentCriticalForceProceeds — force=true bypasses CRITICAL
    • TestBug17_EditDetailsPopulated — 3 mixed edits → EditDetails with 3 entries
    • TestBug17_BackwardCompatibility — original fields (TotalEdits, SuccessfulEdits, etc.) still work
    • TestBug17_AllAlreadyPresent — all edits already present → no write, SkippedEdits=2
    • TestBug17_MixedOverlapAndIndependent — batch with overlap + independent edits
  • Build: go build passes
  • Full suite: go test ./... all pass