Skip to content

feat: apply_last_dry_run — commit a cached dry-run result without resending payload (Option C) #2

Description

@loerei

Problem

The current dry-run workflow doubles token usage:

patch_file(dry_run=true, search_content=<big>, replace_content=<big>) → diff preview
# model inspects diff, looks good…
patch_file(dry_run=false, search_content=<big>, replace_content=<big>) → apply
# identical payload sent twice ≈ 2× tokens

Proposed Solution — Option C: run_id flow

When dry_run=true, the server caches the computed patched_content in-memory (keyed by a short random run_id) and returns that run_id alongside the diff preview.

A new tool apply_last_dry_run commits the cached result using only the run_id — no payload repetition.

patch_file(dry_run=true, ...) → { diff: "...", run_id: "a1b2c3", expires_in: 300 }
apply_last_dry_run(run_id: "a1b2c3")  → { applied: true }

Implementation Scope

New module: src/patchitright_mcp/run_cache.py

  • Thread-safe in-memory dict { run_id: RunEntry }
  • Each RunEntry stores: target_path, patched_content, original_hash, created_at
  • TTL: 300 seconds (hard default, no persistence across restarts)
  • RunCache.store(target_path, patched_content, original_content) → run_id
  • RunCache.consume(run_id) → RunEntry | None (pops entry — single-use)

Changes: patch_file() in patch_file.py

  • On dry_run=True: after computing patched_content, store in RunCache and include run_id + expires_in in the response dict.

Changes: batch_patch_files() in patch_file.py

  • Same: on dry_run=True, cache each file's patched result under a single shared run_id, return it.

New tool: apply_last_dry_run in server.py

  • Input: run_id: str
  • Looks up entry in RunCache.consume(run_id)
  • Guards: original file hash must match (file unchanged since dry-run)
  • Writes patched content to disk
  • Returns standard success dict

Schema update: instructions.md + MCP schema JSON

  • Document run_id field in patch_file / batch_patch_files responses
  • Document apply_last_dry_run tool

Acceptance Criteria

  • patch_file(dry_run=True) returns run_id and expires_in in its response
  • batch_patch_files(dry_run=True) returns a single run_id covering all files
  • apply_last_dry_run(run_id=...) applies the cached patch and returns success
  • apply_last_dry_run fails with a clear error if run_id is unknown or expired
  • apply_last_dry_run fails with a clear error if the file was modified after the dry-run
  • Existing patch_file / batch_patch_files behavior unchanged when dry_run=False
  • All acceptance criteria covered by tests (TDD)

Token Savings

For a typical patch call with a 200-token search_content + replace_content, this eliminates the second full payload entirely. For batch_patch_files with N files, savings scale linearly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions