refactor(search): address PR #750 round 5 review feedback
Tightens verifier consistency, closes test gaps, hardens the fire-and-forget eviction snapshot, and routes the new concurrency knob through Settings. - Pre-flight ``int()`` guard in ``_verify_notes`` mirrors ``_verify_deck_cards``, so a non-numeric note id produces a type-specific log line instead of falling through to the generic "unexpected error" branch. - Adds explicit 403 tests for the file and news verifiers (symmetry with the existing notes/deck 403 tests) plus a ``non_numeric_id_keeps`` test. - ``AppContext`` and ``OAuthAppContext`` no longer snapshot ``_vector_sync_state.eviction_task_group`` at lifespan-yield time. Both expose it as a ``@property`` that reads the singleton dynamically, removing the order-sensitive race where a future startup-ordering change could silently degrade fire-and-forget eviction to inline forever. - Adds ``verification_concurrency`` (env var ``VERIFICATION_CONCURRENCY``, default 20) to ``Settings`` with a dynaconf validator; ``verify_search_results`` resolves the cap lazily from settings when the caller doesn't override it. - Enriches the news verifier TODO to call out that ``batch_size=-1`` is intentional — a numeric ceiling would silently break correctness because any item beyond the cap would be missing from ``present_ids`` and dropped. - Updates ``Optional[TaskGroup]`` to ``TaskGroup | None`` per project style. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
926722b09d
commit
ffcca23a7b
@@ -323,7 +323,7 @@ class VectorSyncState:
|
||||
# Long-lived task group used for fire-and-forget background work spawned
|
||||
# from the request path (e.g. ADR-019 verify-on-read eviction). Set by the
|
||||
# starlette lifespan after entering its task group; cleared on shutdown.
|
||||
eviction_task_group: Optional[TaskGroup] = None
|
||||
eviction_task_group: TaskGroup | None = None
|
||||
|
||||
|
||||
# Module-level singleton for vector sync state
|
||||
@@ -340,7 +340,15 @@ class AppContext:
|
||||
document_receive_stream: Optional[MemoryObjectReceiveStream] = None
|
||||
shutdown_event: Optional[anyio.Event] = None
|
||||
scanner_wake_event: Optional[anyio.Event] = None
|
||||
eviction_task_group: Optional[TaskGroup] = None
|
||||
|
||||
@property
|
||||
def eviction_task_group(self) -> TaskGroup | None:
|
||||
# Read dynamically from the module-level singleton instead of
|
||||
# snapshotting at lifespan-yield time. Snapshotting is order-sensitive:
|
||||
# if the FastMCP server lifespan ever runs before the Starlette
|
||||
# lifespan assigns the task group, every session for the life of the
|
||||
# process would see ``None`` and fall back to inline eviction.
|
||||
return _vector_sync_state.eviction_task_group
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -359,7 +367,11 @@ class OAuthAppContext:
|
||||
document_receive_stream: Optional[MemoryObjectReceiveStream] = None
|
||||
shutdown_event: Optional[anyio.Event] = None
|
||||
scanner_wake_event: Optional[anyio.Event] = None
|
||||
eviction_task_group: Optional[TaskGroup] = None
|
||||
|
||||
@property
|
||||
def eviction_task_group(self) -> TaskGroup | None:
|
||||
# See AppContext.eviction_task_group for rationale.
|
||||
return _vector_sync_state.eviction_task_group
|
||||
|
||||
|
||||
class BasicAuthMiddleware:
|
||||
@@ -576,7 +588,8 @@ async def app_lifespan_basic(server: FastMCP) -> AsyncIterator[AppContext]:
|
||||
document_receive_stream=_vector_sync_state.document_receive_stream,
|
||||
shutdown_event=_vector_sync_state.shutdown_event,
|
||||
scanner_wake_event=_vector_sync_state.scanner_wake_event,
|
||||
eviction_task_group=_vector_sync_state.eviction_task_group,
|
||||
# eviction_task_group is exposed via @property (reads
|
||||
# _vector_sync_state at access time, not snapshot).
|
||||
)
|
||||
finally:
|
||||
logger.info("Shutting down BasicAuth session")
|
||||
@@ -1197,7 +1210,8 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None =
|
||||
document_receive_stream=_vector_sync_state.document_receive_stream,
|
||||
shutdown_event=_vector_sync_state.shutdown_event,
|
||||
scanner_wake_event=_vector_sync_state.scanner_wake_event,
|
||||
eviction_task_group=_vector_sync_state.eviction_task_group,
|
||||
# eviction_task_group is exposed via @property (reads
|
||||
# _vector_sync_state at access time, not snapshot).
|
||||
)
|
||||
finally:
|
||||
logger.info("Shutting down MCP server")
|
||||
|
||||
Reference in New Issue
Block a user