"""Systems become findable by meaning — the charter as an ANSWER (#4251). A System's `description` is a charter: several hundred words saying what belongs in that area and what does not. It is the answer to "where does this go?", and there was no semantic path to it — `list_systems` enumerates and `search(system_id=…)` uses a System as a FILTER over notes, so a System could narrow a search and could never be the answer to one. These pin the three halves: the document shape, the search's scoping and best-chunk contract, and that a charter edited through any door is re-indexed. """ from unittest.mock import AsyncMock, MagicMock, patch import pytest from scribe.services import embeddings as emb from scribe.services import systems as systems_svc from tests.helpers import make_mock_session def _system(system_id=3, name="Retrieval & recall", description="the charter"): s = MagicMock(id=system_id, project_id=2, description=description) s.name = name # never via the constructor — see note #2833 return s # --- the document shape ------------------------------------------------------ def test_the_document_is_the_name_and_the_charter(): assert emb.system_document("Retrieval", "what belongs here") == ( "Retrieval", "what belongs here" ) def test_a_system_with_no_charter_yet_degrades_to_its_name(): """It still embeds, just weakly. That is an argument for writing the charter, not for padding the document with whatever is to hand.""" assert emb.system_document("Retrieval", None) == ("Retrieval", None) assert emb.system_document("Retrieval", " ") == ("Retrieval", None) assert emb.chunk_document(*emb.system_document("Retrieval", None)) == ["Retrieval"] def test_an_empty_system_produces_no_document_at_all(): """Callers gate on falsiness to clear vectors rather than embed nothing.""" assert emb.system_document("", "") == (None, None) assert emb.chunk_document(*emb.system_document("", "")) == [] # --- the search -------------------------------------------------------------- def _rows_ctx(rows): session = MagicMock() result = MagicMock() result.all.return_value = rows session.execute = AsyncMock(return_value=result) ctx = MagicMock() ctx.__aenter__ = AsyncMock(return_value=session) ctx.__aexit__ = AsyncMock(return_value=False) return session, ctx @pytest.mark.asyncio async def test_the_search_collapses_to_best_chunk_and_reports_which_one_won(): """A charter is long, and a result shows its NAME — so the paragraph that actually decides where a record belongs has to come back with it, or the caller judges from two words that cannot say (#4243). Published from the start here rather than retrofitted, which is what #4251 asked for.""" a, b = _system(3), _system(4, name="Data Model") rows = [ (a, 0.10, 2, "retrieval telemetry belongs here"), (b, 0.22, 0, "the persistence layer"), (a, 0.40, 0, "a weaker paragraph of the same charter"), ] _session, ctx = _rows_ctx(rows) report: dict = {} with ( patch.object(emb, "async_session", return_value=ctx), patch.object(emb, "get_embedding", AsyncMock(return_value=[0.0] * 384)), patch.object(emb, "can_read_project", AsyncMock(return_value=True)), ): out = await emb.semantic_search_systems( 7, "where does telemetry go?", project_id=2, threshold=0.0, report=report, ) assert [s.id for _score, s in out] == [3, 4] assert out[0][0] == 1.0 - 0.10 # the BEST chunk's score assert report["best_chunk"][3] == { "index": 2, "text": "retrieval telemetry belongs here", } @pytest.mark.asyncio async def test_a_project_the_caller_cannot_read_returns_nothing(): """Rule 78 — the charter of a project someone was not given is not an answer to any question they are entitled to ask.""" _session, ctx = _rows_ctx([]) with ( patch.object(emb, "async_session", return_value=ctx), patch.object(emb, "get_embedding", AsyncMock(return_value=[0.0] * 384)), patch.object(emb, "can_read_project", AsyncMock(return_value=False)), ): assert await emb.semantic_search_systems(7, "q", project_id=99) == [] @pytest.mark.asyncio async def test_an_empty_query_never_reaches_the_embedder(): with patch.object(emb, "get_embedding", AsyncMock()) as embed: assert await emb.semantic_search_systems(7, " ") == [] embed.assert_not_awaited() @pytest.mark.asyncio async def test_a_search_that_fails_returns_nothing_rather_than_raising(): """A recall aid must never break the call it serves.""" with patch.object(emb, "get_embedding", AsyncMock(side_effect=RuntimeError)): assert await emb.semantic_search_systems(7, "q") == [] # --- staying current --------------------------------------------------------- @pytest.mark.asyncio async def test_creating_a_system_indexes_its_charter(): session = make_mock_session() with ( patch.object(systems_svc, "async_session", return_value=session), patch.object(systems_svc.access, "can_write_project", AsyncMock(return_value=True)), patch.object(systems_svc, "embed_system") as embed, ): await systems_svc.create_system(7, 2, "Retrieval", description="the charter") embed.assert_called_once() @pytest.mark.asyncio async def test_editing_a_charter_re_indexes_it(): """A charter edited and not re-indexed stays findable by what it used to say — the stale-vector case, and the one that matters most for a record whose whole job is to say where things belong now.""" system = _system() session = make_mock_session() session.get = AsyncMock(return_value=system) system.deleted_at = None with ( patch.object(systems_svc, "async_session", return_value=session), patch.object(systems_svc.access, "can_write_project", AsyncMock(return_value=True)), patch.object(systems_svc, "embed_system") as embed, ): await systems_svc.update_system(7, 3, description="a new charter") embed.assert_called_once_with(system) def test_embedding_a_system_never_breaks_the_write_that_saved_it(): """A System that saved must not fail on its index refresh. ValueError rather than RuntimeError deliberately: RuntimeError is also what "no running loop" raises, which is an ordinary sync caller and has its own branch — this has to land in the general swallow to prove it exists.""" with patch( "scribe.services.embeddings.upsert_system_embedding", side_effect=ValueError("boom"), ): systems_svc.embed_system(_system()) # returns quietly, does not raise