Skip to content

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

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.

  1. Claude called edit_file(path, old_text, new_text) to replace a block (~35% of file)
  2. Server returned: OPERATION BLOCKED - MEDIUM risk ... add "force": true
  3. Claude retried with force: true — the exact same edit, wasting tokens
  4. The edit succeeded on retry, but the backup was never created for the blocked attempt

Two issues in core/edit_operations.go:

  1. Wrong blocking condition: EditFile() used impact.IsRisky && !force to decide whether to block. Since IsRisky is true for MEDIUM (>=30%), HIGH (>=50%), and CRITICAL (>=90%), any edit changing more than 30% of a file was blocked. The method ShouldBlockOperation() in impact_analyzer.go correctly only blocked HIGH/CRITICAL, but was never called from EditFile().

  2. 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.

  • 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
Risk Level% ChangeBehavior
LOW< 20%Proceed silently
MEDIUM20–74%Auto-backup + proceed + risk notice in response
HIGH75–89%Auto-backup + proceed + prominent warning in response
CRITICAL>= 90%Auto-backup + block (require force: true)
  1. Backup BEFORE block: Backup is now created before any blocking decision, so even CRITICAL-blocked operations have a safety net

  2. ShouldBlockOperation() now used: EditFile() calls impact.ShouldBlockOperation(force) instead of the raw impact.IsRisky && !force check. Only "critical" risk blocks.

  3. 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-abc123
    Restore with: restore_backup(backup_id)
  4. Updated thresholds: MEDIUM raised from 30% to 20%, HIGH raised from 50% to 75% to better match real-world editing patterns

  5. MultiEdit gets force parameter: Previously missing, now supports force: true for CRITICAL batch edits

All edit tools (edit_file, mcp_edit, intelligent_edit, smart_edit_file, multi_edit) now document:

  • “Only blocks CRITICAL risk (>=90% file rewrite)”
  • force parameter: “Force even if CRITICAL risk”
FileChange
core/impact_analyzer.goThresholds 20%/75%, ShouldBlockOperation() CRITICAL-only, new FormatRiskNotice()
core/edit_operations.goEditResult.RiskWarning, backup before block, ShouldBlockOperation() call, MultiEdit force param + persistent backup
core/streaming_operations.goSmartEditFile + streamingEditLargeFile get force param + risk assessment
core/claude_optimizer.goPass force through to SmartEditFile
core/engine.goFallback defaults 20.0/75.0
core/pipeline.goPass force to MultiEdit
main.goCLI defaults, tool descriptions, handler responses, force params for all edit tools
tests/bug16_test.go10 regression tests covering all risk levels
CHANGELOG.mdv3.15.1 entry
  • 10 new regression tests in tests/bug16_test.go:
    • TestBug16_UpdatedThresholds — verify new default values
    • TestBug16_ShouldBlockOnlyCritical — table-driven test for all 4 risk levels
    • TestBug16_MediumRiskAutoProceeds — 40% change succeeds without force
    • TestBug16_HighRiskAutoProceeds — 80% change succeeds without force
    • TestBug16_CriticalRiskBlocked — full rewrite blocked without force
    • TestBug16_CriticalRiskForceProceeds — force=true overrides CRITICAL
    • TestBug16_LowRiskNoWarning — small edit has empty RiskWarning
    • TestBug16_BackupCreatedBeforeBlock — blocked error contains backup ID
    • TestBug16_MultiEditWithForce — MultiEdit accepts force, returns BackupID
    • TestBug16_FormatRiskNotice — notice contains backup ID and restore instructions
  • Build: go build passes
  • Full suite: go test ./tests/... ./core/... all pass
  • Race detector: go test -race ./... clean