Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/basic_memory/indexing/accepted_note_mutation_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,7 @@ async def _run_accepted_note_move(
actor_user_profile_id=request.actor.user_profile_id,
actor_kind=request.actor.kind,
actor_name=request.actor.name,
previous_file_path=existing_file_path,
cleanup_after_write=persisted.previous_file_delete,
fallback_source=request.source,
)
Expand Down
3 changes: 3 additions & 0 deletions src/basic_memory/runtime/job_payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class RuntimeNoteMaterializationJobPayload(BaseModel):
actor_kind: str | None = None
actor_name: str | None = None
source: str | None = None
previous_file_path: str | None = None
cleanup_file_path: str | None = None
cleanup_file_checksum: str | None = None

Expand Down Expand Up @@ -125,6 +126,7 @@ def from_runtime_request(cls, request: RuntimeNoteMaterializationJobRequest) ->
actor_kind=request.actor_kind,
actor_name=request.actor_name,
source=request.source,
previous_file_path=request.previous_file_path,
cleanup_file_path=request.cleanup_file_path,
cleanup_file_checksum=request.cleanup_file_checksum,
)
Expand All @@ -140,6 +142,7 @@ def to_runtime_request(self) -> RuntimeNoteMaterializationJobRequest:
actor_kind=self.actor_kind,
actor_name=self.actor_name,
source=self.source,
previous_file_path=self.previous_file_path,
cleanup_file_path=self.cleanup_file_path,
cleanup_file_checksum=self.cleanup_file_checksum,
)
Expand Down
9 changes: 9 additions & 0 deletions src/basic_memory/runtime/note_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,7 @@ class RuntimePendingNoteMaterialization:
actor_kind: RuntimeNoteActorKind | None = None
actor_name: RuntimeNoteActorName | None = None
source: RuntimeNoteChangeSource | None = None
previous_file_path: RuntimeFilePath | None = None
cleanup_after_write: RuntimePendingNoteFileDelete | None = None


Expand All @@ -720,6 +721,7 @@ def plan_pending_note_materialization(
actor_user_profile_id: UUID | None = None,
actor_kind: RuntimeNoteActorKind | None = None,
actor_name: RuntimeNoteActorName | None = None,
previous_file_path: RuntimeFilePath | None = None,
cleanup_after_write: RuntimePendingNoteFileDelete | None = None,
) -> RuntimePendingNoteMaterialization:
"""Build the queued materialization marker from accepted note_content state."""
Expand All @@ -733,6 +735,7 @@ def plan_pending_note_materialization(
actor_kind=actor_kind,
actor_name=actor_name,
source=str(source) if source else None,
previous_file_path=previous_file_path,
cleanup_after_write=cleanup_after_write,
)

Expand All @@ -749,6 +752,7 @@ class RuntimeNoteMaterializationJobRequest:
actor_kind: RuntimeNoteActorKind | None = None
actor_name: RuntimeNoteActorName | None = None
source: RuntimeNoteChangeSource | None = None
previous_file_path: RuntimeFilePath | None = None
cleanup_file_path: RuntimeFilePath | None = None
cleanup_file_checksum: RuntimeFileChecksum | None = None

Expand Down Expand Up @@ -805,6 +809,7 @@ def plan_note_materialization_job_request(
actor_kind=materialization.actor_kind,
actor_name=materialization.actor_name,
source=materialization.source,
previous_file_path=materialization.previous_file_path,
cleanup_file_path=cleanup.file_path if cleanup is not None else None,
cleanup_file_checksum=cleanup.file_checksum if cleanup is not None else None,
)
Expand Down Expand Up @@ -864,6 +869,7 @@ def plan_accepted_note_materialization_change[PayloadT](
actor_user_profile_id: UUID | None = None,
actor_kind: RuntimeNoteActorKind | None = None,
actor_name: RuntimeNoteActorName | None = None,
previous_file_path: RuntimeFilePath | None = None,
cleanup_after_write: RuntimePendingNoteFileDelete | None = None,
) -> RuntimeAcceptedNoteChange[PayloadT]:
"""Build an accepted-note response plus materialization follow-up marker."""
Expand All @@ -878,6 +884,7 @@ def plan_accepted_note_materialization_change[PayloadT](
actor_user_profile_id=actor_user_profile_id,
actor_kind=actor_kind,
actor_name=actor_name,
previous_file_path=previous_file_path,
cleanup_after_write=cleanup_after_write,
),
)
Expand Down Expand Up @@ -956,6 +963,7 @@ def plan_accepted_note_write_change(
actor_user_profile_id: UUID | None = None,
actor_kind: RuntimeNoteActorKind | None = None,
actor_name: RuntimeNoteActorName | None = None,
previous_file_path: RuntimeFilePath | None = None,
cleanup_after_write: RuntimePendingNoteFileDelete | None = None,
) -> RuntimeAcceptedNoteChange[RuntimeAcceptedNoteResponse]:
"""Build the accepted-note response plus the materialization follow-up marker."""
Expand All @@ -973,6 +981,7 @@ def plan_accepted_note_write_change(
actor_user_profile_id=actor_user_profile_id,
actor_kind=actor_kind,
actor_name=actor_name,
previous_file_path=previous_file_path,
cleanup_after_write=cleanup_after_write,
)

Expand Down
19 changes: 15 additions & 4 deletions tests/indexing/test_accepted_note_mutation_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ async def test_run_accepted_note_create_persists_prepared_markdown() -> None:
assert change.materialization.actor_user_profile_id == _ACTOR_ID
assert change.materialization.actor_kind == "user"
assert change.materialization.actor_name == "Ada"
assert change.materialization.previous_file_path is None


@pytest.mark.asyncio
Expand Down Expand Up @@ -597,6 +598,7 @@ async def test_run_accepted_note_update_replaces_existing_note_content() -> None
assert change.payload.title == "Replacement"
assert change.materialization is not None
assert change.materialization.db_version == 2
assert change.materialization.previous_file_path is None


@pytest.mark.asyncio
Expand Down Expand Up @@ -964,13 +966,17 @@ async def test_run_accepted_note_edit_applies_patch_against_db_content() -> None


@pytest.mark.asyncio
async def test_run_accepted_note_move_uses_policy_and_carries_cleanup() -> None:
@pytest.mark.parametrize("file_checksum", ["file-checksum", None])
async def test_run_accepted_note_move_carries_previous_path_and_materialized_cleanup(
file_checksum: str | None,
) -> None:
session = _MutationSession()
project = _project()
prepared = _prepared_replacement()
prepared_move = _prepared_move()
entity = _entity(file_path="notes/accepted.md")
note_content = _note_content(entity)
note_content.file_checksum = file_checksum
project_repository = _ProjectRepository(project)
entity_lookup_repository = _EntityLookupRepository(by_external_id=entity)
note_content_lookup_repository = _NoteContentLookupRepository(note_content)
Expand Down Expand Up @@ -1015,9 +1021,14 @@ async def test_run_accepted_note_move_uses_policy_and_carries_cleanup() -> None:
assert entity.permalink == "archive/accepted"
assert change.status_code == 200
assert change.materialization is not None
assert change.materialization.cleanup_after_write is not None
assert change.materialization.cleanup_after_write.file_path == "notes/accepted.md"
assert change.materialization.cleanup_after_write.file_checksum == "file-checksum"
assert change.materialization.previous_file_path == "notes/accepted.md"
cleanup = change.materialization.cleanup_after_write
if file_checksum is None:
assert cleanup is None
else:
assert cleanup is not None
assert cleanup.file_path == "notes/accepted.md"
assert cleanup.file_checksum == "file-checksum"


@pytest.mark.asyncio
Expand Down
35 changes: 35 additions & 0 deletions tests/runtime/test_pending_note_materialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

from basic_memory.runtime.note_content import (
RuntimeAcceptedNoteContentWritePlan,
RuntimeNoteMaterializationJobRequest,
RuntimePendingNoteFileDelete,
RuntimePendingNoteMaterialization,
next_runtime_note_content_version,
plan_accepted_note_content_write,
plan_accepted_note_materialization_change,
plan_note_materialization_job_request,
plan_pending_note_materialization,
)

Expand Down Expand Up @@ -40,6 +42,7 @@ def test_plan_pending_note_materialization_uses_fallback_source_when_missing() -
fallback_source="api",
actor_kind="mcp_client",
actor_name="Claude Code",
previous_file_path="notes/old.md",
cleanup_after_write=cleanup,
)

Expand All @@ -51,6 +54,7 @@ def test_plan_pending_note_materialization_uses_fallback_source_when_missing() -
actor_kind="mcp_client",
actor_name="Claude Code",
source="api",
previous_file_path="notes/old.md",
cleanup_after_write=cleanup,
)

Expand Down Expand Up @@ -87,6 +91,37 @@ def test_plan_pending_note_materialization_prefers_note_source() -> None:
assert materialization.source == "mcp"


def test_plan_note_materialization_job_request_preserves_previous_move_path() -> None:
cleanup = RuntimePendingNoteFileDelete(
project_id=7,
entity_id=42,
file_path="notes/old.md",
file_checksum="old-checksum",
)
materialization = RuntimePendingNoteMaterialization(
project_id=7,
entity_id=42,
db_version=4,
db_checksum="db-checksum",
source="api",
previous_file_path="notes/old.md",
cleanup_after_write=cleanup,
)

assert plan_note_materialization_job_request(
materialization
) == RuntimeNoteMaterializationJobRequest(
project_id=7,
entity_id=42,
db_version=4,
db_checksum="db-checksum",
source="api",
previous_file_path="notes/old.md",
cleanup_file_path="notes/old.md",
cleanup_file_checksum="old-checksum",
)


def test_next_runtime_note_content_version_starts_new_rows_at_one() -> None:
assert next_runtime_note_content_version(None) == 1

Expand Down
1 change: 1 addition & 0 deletions tests/runtime/test_runtime_job_payloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def test_runtime_note_materialization_job_payload_round_trips_runtime_request()
actor_kind=NOTE_OBJECT_ACTOR_KIND_MCP_CLIENT,
actor_name="Claude Code",
source="mcp",
previous_file_path="notes/previous.md",
cleanup_file_path="notes/old.md",
cleanup_file_checksum="old-file-sum",
)
Expand Down
Loading