Bug #16: Edit Risk Model — Only CRITICAL Blocks
Status: RESOLVED in v3.15.1 Category: Risk Model / Agentic Workflow Severity: High (wasted token round-trips on every moderate edit) Resolution Date: 2026-03-02
Problem
Section titled “Problem”When an edit changed more than 30% of a file, the server blocked the operation and asked the AI to retry with force: true. In agentic workflows (Claude Code, Claude Desktop), this caused a wasteful extra round-trip for every moderate edit — even though the change was perfectly safe.
What Happened
Section titled “What Happened”- Claude called
edit_file(path, old_text, new_text)to replace a block (~35% of file) - Server returned:
OPERATION BLOCKED - MEDIUM risk ... add "force": true - Claude retried with
force: true— the exact same edit, wasting tokens - The edit succeeded on retry, but the backup was never created for the blocked attempt
Root Cause
Section titled “Root Cause”Two issues in core/edit_operations.go:
-
Wrong blocking condition:
EditFile()usedimpact.IsRisky && !forceto decide whether to block. SinceIsRiskyistruefor MEDIUM (>=30%), HIGH (>=50%), and CRITICAL (>=90%), any edit changing more than 30% of a file was blocked. The methodShouldBlockOperation()inimpact_analyzer.gocorrectly only blocked HIGH/CRITICAL, but was never called fromEditFile(). -
Backup created too late: The backup creation (line 107) happened AFTER the blocking check (line 75). When the operation was blocked, no backup existed — so if the user had already made destructive changes, there was no safety net.
Why It Matters
Section titled “Why It Matters”- Token waste: Every MEDIUM risk edit (very common in refactoring) cost 2 tool calls instead of 1
- No backup on block: The safety net was missing exactly when it was most needed
- Agentic friction: AI agents follow instructions literally — the “add force: true” retry is pure overhead when the operation is safe
Solution
Section titled “Solution”New Risk Model
Section titled “New Risk Model”| Risk Level | % Change | Behavior |
|---|---|---|
| LOW | < 20% | Proceed silently |
| MEDIUM | 20–74% | Auto-backup + proceed + risk notice in response |
| HIGH | 75–89% | Auto-backup + proceed + prominent warning in response |
| CRITICAL | >= 90% | Auto-backup + block (require force: true) |
Key Changes
Section titled “Key Changes”-
Backup BEFORE block: Backup is now created before any blocking decision, so even CRITICAL-blocked operations have a safety net
-
ShouldBlockOperation()now used:EditFile()callsimpact.ShouldBlockOperation(force)instead of the rawimpact.IsRisky && !forcecheck. Only"critical"risk blocks. -
Non-blocking risk notice: MEDIUM and HIGH risk edits succeed immediately. The response includes the backup ID and a risk notice so the user can restore if needed:
Risk notice [medium]40.0% of file changed (1 occurrence(s), ~200 chars)Auto-backup: 20260302-143022-abc123Restore with: restore_backup(backup_id) -
Updated thresholds: MEDIUM raised from 30% to 20%, HIGH raised from 50% to 75% to better match real-world editing patterns
-
MultiEditgetsforceparameter: Previously missing, now supportsforce: truefor CRITICAL batch edits
Updated Tool Descriptions
Section titled “Updated Tool Descriptions”All edit tools (edit_file, mcp_edit, intelligent_edit, smart_edit_file, multi_edit) now document:
- “Only blocks CRITICAL risk (>=90% file rewrite)”
forceparameter: “Force even if CRITICAL risk”
Files Changed
Section titled “Files Changed”| File | Change |
|---|---|
core/impact_analyzer.go | Thresholds 20%/75%, ShouldBlockOperation() CRITICAL-only, new FormatRiskNotice() |
core/edit_operations.go | EditResult.RiskWarning, backup before block, ShouldBlockOperation() call, MultiEdit force param + persistent backup |
core/streaming_operations.go | SmartEditFile + streamingEditLargeFile get force param + risk assessment |
core/claude_optimizer.go | Pass force through to SmartEditFile |
core/engine.go | Fallback defaults 20.0/75.0 |
core/pipeline.go | Pass force to MultiEdit |
main.go | CLI defaults, tool descriptions, handler responses, force params for all edit tools |
tests/bug16_test.go | 10 regression tests covering all risk levels |
CHANGELOG.md | v3.15.1 entry |
Testing
Section titled “Testing”- 10 new regression tests in
tests/bug16_test.go:TestBug16_UpdatedThresholds— verify new default valuesTestBug16_ShouldBlockOnlyCritical— table-driven test for all 4 risk levelsTestBug16_MediumRiskAutoProceeds— 40% change succeeds without forceTestBug16_HighRiskAutoProceeds— 80% change succeeds without forceTestBug16_CriticalRiskBlocked— full rewrite blocked without forceTestBug16_CriticalRiskForceProceeds— force=true overrides CRITICALTestBug16_LowRiskNoWarning— small edit has empty RiskWarningTestBug16_BackupCreatedBeforeBlock— blocked error contains backup IDTestBug16_MultiEditWithForce— MultiEdit accepts force, returns BackupIDTestBug16_FormatRiskNotice— notice contains backup ID and restore instructions
- Build:
go buildpasses - Full suite:
go test ./tests/... ./core/...all pass - Race detector:
go test -race ./...clean