fix(core): preserve git-memory formatting and enforce lock conflicts (#9451)

* test(core): strengthen git-memory system prompt stability integration coverage

Switch git-memory HTTP integration tests to OpenAI model handles and add assertions that system prompt content remains stable after normal turns and direct block value updates until explicit recompilation or reset.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix(core): preserve git-memory formatting and enforce lock conflicts

Preserve existing markdown frontmatter formatting on block updates while still ensuring required metadata fields exist, and make post-push git sync propagate memory-repo lock conflicts as 409 responses. Also enable slash-containing core-memory block labels in route params and add regression coverage.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix(memfs): fail closed on memory repo lock contention

Make memfs git commits fail closed when the per-agent Redis lock cannot be acquired, return 409 MEMORY_REPO_BUSY from the memfs files write API, and map that 409 back to core MemoryRepoBusyError so API callers receive consistent busy conflicts.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* chore(core): minimize git-memory fix scope to memfs lock and frontmatter paths

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* chore: drop unrelated changes and keep memfs-focused scope

Revert branch-only changes that are not required for the memfs lock contention and frontmatter-preservation fix so the PR contains only issue-relevant files.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix(memfs): lock push sync path and improve nested sync diagnostics

Serialize memfs push-to-GCS sync with the same per-agent Redis lock key used by API commits, and add targeted post-push nested-block diagnostics plus a focused nested-label sync regression test for _sync_after_push.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

---------

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Sarah Wooders
2026-02-11 22:29:29 -08:00
committed by Caren Thomas
parent d7793a4474
commit 05073ba837
4 changed files with 180 additions and 5 deletions

View File

@@ -1,7 +1,10 @@
from unittest.mock import patch
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
import letta.server.rest_api.routers.v1.git_http as git_http_router
from letta.log_context import get_log_context
from letta.server.rest_api.middleware import LoggingMiddleware
@@ -35,6 +38,64 @@ def client(app):
class TestLogContextMiddleware:
@pytest.mark.asyncio
async def test_sync_after_push_syncs_nested_block_labels_to_postgres(self, monkeypatch):
"""Regression test: nested labels (e.g., system/human) are synced from git files."""
synced_calls = []
class DummyActor:
id = "user-123"
organization_id = "org-123"
class DummyGit:
async def get_files(self, agent_id, org_id, ref):
assert ref == "HEAD"
return {
"system/human.md": "---\ndescription: human\nlimit: 20000\n---\nname: sarah",
"system/persona.md": "---\ndescription: persona\nlimit: 20000\n---\nbe helpful",
}
class DummyMemoryRepoManager:
git = DummyGit()
class DummyBlockManager:
async def _sync_block_to_postgres(self, **kwargs):
synced_calls.append(kwargs)
class DummyAgentManager:
async def list_agent_blocks_async(self, **kwargs):
return []
class DummyUserManager:
async def get_actor_by_id_async(self, actor_id):
return DummyActor()
class DummyServer:
user_manager = DummyUserManager()
memory_repo_manager = DummyMemoryRepoManager()
block_manager = DummyBlockManager()
agent_manager = DummyAgentManager()
class DummyGitEnabledBlockManager(DummyBlockManager):
pass
dummy_server = DummyServer()
dummy_server.block_manager = DummyGitEnabledBlockManager()
monkeypatch.setattr(git_http_router, "_server_instance", dummy_server)
from letta.settings import settings as core_settings
monkeypatch.setattr(core_settings, "memfs_service_url", "http://memfs.test")
with patch("letta.services.block_manager_git.GitEnabledBlockManager", DummyGitEnabledBlockManager):
await git_http_router._sync_after_push(actor_id="user-123", agent_id="agent-123")
labels = {call["label"] for call in synced_calls}
assert "system/human" in labels
assert "system/persona" in labels
def test_extracts_actor_id_from_headers(self, client):
response = client.get("/v1/agents/agent-123e4567-e89b-42d3-8456-426614174000", headers={"user_id": "user-abc123"})
assert response.status_code == 200