fix(service): make expected_version mandatory on update_note and edit
Both guarded with `if expected_version and ...`, so an empty string skipped the version check entirely and silently overwrote the note. Empty/missing expected_version is now a structured INVALID error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0139vHnNpS2sLNELjrAHX5Cu
This commit is contained in:
1 parent
de752d126b
commit
dbe9acf155
2 files changed
+27
-2
No files matched your search
@@ -426,7 +426,9 @@ class MemoryService:
|
||||
"""Active notes are never edited in place by an agent: the edit becomes a
|
||||
proposed revision in the inbox (§2.4, §9)."""
|
||||
note = self.get_note(note_id)
|
||||
if expected_version and expected_version != note.file_hash:
|
||||
if not expected_version:
|
||||
raise Invalid("expected_version is required; read the note first and pass its version", id=note_id)
|
||||
if expected_version != note.file_hash:
|
||||
raise VersionConflict(note_id, expected_version, note.file_hash)
|
||||
if note.state == "archive":
|
||||
raise Archived(note_id)
|
||||
@@ -587,7 +589,9 @@ class MemoryService:
|
||||
) -> dict[str, Any]:
|
||||
"""Direct edit from the UI: the owner may change an active note in place."""
|
||||
note = self.get_note(note_id)
|
||||
if expected_version and expected_version != note.file_hash:
|
||||
if not expected_version:
|
||||
raise Invalid("expected_version is required; read the note first and pass its version", id=note_id)
|
||||
if expected_version != note.file_hash:
|
||||
raise VersionConflict(note_id, expected_version, note.file_hash)
|
||||
if frontmatter:
|
||||
for k, v in frontmatter.items():
|
||||
|
||||
@@ -32,3 +32,24 @@ async def test_update_note_on_a_rejected_archived_note_is_refused(svc):
|
||||
unchanged = svc.get_note(w["id"])
|
||||
assert unchanged.body.strip() == "alt"
|
||||
assert unchanged.get("rejected") is True
|
||||
|
||||
|
||||
# -- finding 4: expected_version must be mandatory, not just "truthy" ---------
|
||||
async def test_update_note_with_empty_expected_version_is_rejected(svc):
|
||||
w = await svc.write_note(title="T", body="original", type="note")
|
||||
|
||||
with pytest.raises(Invalid):
|
||||
await svc.update_note(w["id"], "", body="entfuehrt")
|
||||
|
||||
unchanged = svc.get_note(w["id"])
|
||||
assert unchanged.body.strip() == "original"
|
||||
|
||||
|
||||
async def test_edit_with_empty_expected_version_is_rejected(svc):
|
||||
w = await svc.write_note(title="T", body="original", type="note")
|
||||
|
||||
with pytest.raises(Invalid):
|
||||
await svc.edit(w["id"], body="entfuehrt", expected_version="")
|
||||
|
||||
unchanged = svc.get_note(w["id"])
|
||||
assert unchanged.body.strip() == "original"
|
||||
Reference in new issue
Block a user