TextCanvas.apply_patch accepts stale hunks without validating context lines
Describe the bug
TextCanvas.apply_patch() says it applies hunks with context-line validation. A unified diff prepared from an older revision is nevertheless applied to the latest revision when its removed/context line no longer matches.
The call returns PATCH APPLIED, creates a new revision, and makes the stale edit the latest content.
Reproduction
import asyncio
import difflib
from autogen_core import CancellationToken
from autogen_ext.memory.canvas import TextCanvasMemory
BASE = "service=payments\nregion=Singapore\nstatus=active\n"
EDIT_A = "service=payments\nregion=Tokyo\nstatus=active\n"
EDIT_B = "service=payments\nregion=London\nstatus=active\n"
def make_diff(old: str, new: str) -> str:
return "".join(
difflib.unified_diff(
old.splitlines(keepends=True),
new.splitlines(keepends=True),
fromfile="state.txt",
tofile="state.txt",
)
)
async def main() -> None:
memory = TextCanvasMemory()
update = memory.get_update_file_tool()
apply_patch = memory.get_apply_patch_tool()
token = CancellationToken()
await update.run_json(
{"filename": "state.txt", "new_content": BASE}, token
)
# Writer A prepares a patch against revision 1.
stale_patch = make_diff(BASE, EDIT_A)
# Writer B changes the same line, producing revision 2.
await update.run_json(
{"filename": "state.txt", "new_content": EDIT_B}, token
)
result = await apply_patch.run_json(
{"filename": "state.txt", "patch_text": stale_patch}, token
)
print(result.status)
print(memory.canvas.list_files())
print(memory.canvas.get_latest_content("state.txt"), end="")
asyncio.run(main())
Run with:
pip install "autogen-ext[canvas]==0.7.5"
python repro.py
Observed output:
PATCH APPLIED
{'state.txt': 3}
service=payments
region=Tokyo
status=active
Expected behavior
The patch should be rejected because its source hunk removes region=Singapore, while the latest revision contains region=London. Revision 2 should remain latest.
As a control, a patch generated from the actual revision-2 content applies successfully.
Environment and validation
autogen-core==0.7.5autogen-ext==0.7.5unidiff==0.7.5- Python 3.13.13
- Ubuntu
- Also reproduced against current main commit
027ecf0a379bcc1d09956d46d12d44a3ad9cee14 - Repeated 5/5 times on the release and 5/5 times on main
- No model provider or embedding service is involved
I also independently reproduced the release case in a fresh environment.
Implementation observation
PatchSet parses the diff, but the current loop constructs a replacement block and assigns it to working_lines[start:end] without comparing the hunk's source/context lines to that slice.
TextCanvas is experimental, and the previous revision remains in revision history. The reported behavior is therefore limited to a stale edit silently becoming the current/latest revision; this report does not claim unrecoverable data loss.
Source: microsoft/autogen