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:
2026-07-14 11:59:15 +02:00
co-authored by Claude Fable 5
parent 31b948a748
commit 8cf3609948
5 changed files with 119 additions and 12 deletions
+59
View File
@@ -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"]