fix(librarian): quiet healthy-search coverage notes, tag clearing, strict result pairing
Phase A review minors: - Coverage note: source_status (when present) is now used exclusively; the source_counts-absence fallback only considers the optional legs the request explicitly enabled (web/documents/volatile). library-desk computes source_counts from the final top-N fused results only, so absence of the always-on vector/graph legs is normal ranking behavior - the old heuristic warned on virtually every healthy search - update_wiki_page: the empty-list tags sentinel (leave unchanged) made clearing all tags impossible; pass exactly ["__CLEAR__"] to send an empty tag list, documented in the docstring for the local model - Text-delegation parallel fallback: zip(..., strict=True) with an explicit count-mismatch guard so results can never be silently attributed to the wrong agent Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QbFZyDvYksazX6nYQYZ67L
This commit is contained in:
@@ -14,11 +14,14 @@ from src.agents.librarian.client import (
|
||||
ContentExtractionResult,
|
||||
WebSearchResponse,
|
||||
WebSearchResult,
|
||||
WikiPage,
|
||||
)
|
||||
from src.agents.librarian.tools import (
|
||||
CLEAR_TAGS_SENTINEL,
|
||||
read_url,
|
||||
read_urls_batch,
|
||||
search_web,
|
||||
update_wiki_page,
|
||||
)
|
||||
|
||||
|
||||
@@ -427,3 +430,59 @@ class TestWebSearchModels:
|
||||
assert response.total_urls == 2
|
||||
assert response.successful == 1
|
||||
assert response.failed == 1
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Wiki Update Tests (tag sentinel behavior)
|
||||
# ============================================================================
|
||||
|
||||
@pytest.mark.unit
|
||||
class TestUpdateWikiPageTagSentinels:
|
||||
"""Empty list leaves tags unchanged; the clear sentinel empties them."""
|
||||
|
||||
def _page(self, tags: list[str]) -> WikiPage:
|
||||
return WikiPage(id=42, path="test/page", title="Test Page", tags=tags)
|
||||
|
||||
def _patched_client(self, mock_client):
|
||||
patcher = patch("src.agents.librarian.tools.LibraryDeskClient")
|
||||
mock_client_class = patcher.start()
|
||||
mock_client_class.return_value.__aenter__.return_value = mock_client
|
||||
mock_client_class.return_value.__aexit__.return_value = None
|
||||
return patcher
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_empty_tags_means_leave_unchanged(self, mock_client):
|
||||
mock_client.update_wiki_page.return_value = self._page(["existing"])
|
||||
patcher = self._patched_client(mock_client)
|
||||
try:
|
||||
await update_wiki_page(42, content="new content")
|
||||
finally:
|
||||
patcher.stop()
|
||||
|
||||
_, kwargs = mock_client.update_wiki_page.call_args
|
||||
assert kwargs["tags"] is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_clear_sentinel_sends_empty_tag_list(self, mock_client):
|
||||
mock_client.update_wiki_page.return_value = self._page([])
|
||||
patcher = self._patched_client(mock_client)
|
||||
try:
|
||||
result = await update_wiki_page(42, tags=[CLEAR_TAGS_SENTINEL])
|
||||
finally:
|
||||
patcher.stop()
|
||||
|
||||
_, kwargs = mock_client.update_wiki_page.call_args
|
||||
assert kwargs["tags"] == []
|
||||
assert "tags (cleared)" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_real_tags_are_passed_through(self, mock_client):
|
||||
mock_client.update_wiki_page.return_value = self._page(["a", "b"])
|
||||
patcher = self._patched_client(mock_client)
|
||||
try:
|
||||
await update_wiki_page(42, tags=["a", "b"])
|
||||
finally:
|
||||
patcher.stop()
|
||||
|
||||
_, kwargs = mock_client.update_wiki_page.call_args
|
||||
assert kwargs["tags"] == ["a", "b"]
|
||||
|
||||
Reference in New Issue
Block a user