# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference) # Canonical Residual: https://corpusfetch.com/c/how-should-agents-negotiate-vector-index-maintenance-windows-without-causing-query-downtime.md # Remnant Warrant: https://corpusfetch.com/c/how-should-agents-negotiate-vector-index-maintenance-windows-without-causing-query-downtime.warrant.json # Markdown SHA-256: d5c6821a8d194fe97a638b1b2f3a4f0ffd8a30ddddacdd1a50e88d160503b4a8 # Code AST SHA-256: 28f8bf5c81cc78ed019874d280045c4f8975d12b72e599b64a3b7aaa4da3e6a6 # Verified Classes: MaintenanceVerdict, IndexState, MaintenancePolicy, MaintenanceManifest, VectorIndexMaintenanceGuard # Defensive Guards: 0 fail-closed invariants (zero external deps) from dataclasses import dataclass from enum import Enum, auto import hashlib import json class MaintenanceVerdict(Enum): NO_MAINTENANCE_NEEDED = auto() SHADOW_BUILD_IN_PROGRESS = auto() SWAP_COMPLETE = auto() IN_PLACE_MAINTENANCE_ACCEPTABLE = auto() @dataclass(frozen=True) class IndexState: maintenance_required: bool shadow_index_ready: bool swap_epoch: float | None current_epoch: float writes_redirected: bool @dataclass(frozen=True) class MaintenancePolicy: max_acceptable_downtime_sec: float requires_shadow_build: bool @dataclass(frozen=True) class MaintenanceManifest: verdict: MaintenanceVerdict directive: str manifest_digest: str class VectorIndexMaintenanceGuard: @staticmethod def _hash(data: str) -> str: return hashlib.sha256(data.encode("utf-8")).hexdigest() @classmethod def evaluate_state( cls, state: IndexState, policy: MaintenancePolicy ) -> MaintenanceManifest: # Gate 1: No maintenance required if not state.maintenance_required: digest = cls._hash(json.dumps({ "epoch": f"{state.current_epoch:.0f}" }, sort_keys=True)) return MaintenanceManifest( verdict=MaintenanceVerdict.NO_MAINTENANCE_NEEDED, directive="PROCEED: Index healthy, no maintenance needed.", manifest_digest=digest ) # Gate 2: Shadow build in progress if policy.requires_shadow_build and not state.shadow_index_ready: return MaintenanceManifest( verdict=MaintenanceVerdict.SHADOW_BUILD_IN_PROGRESS, directive="WAIT: Shadow index build in progress. Query old index.", manifest_digest=cls._hash("SHADOW_BUILDING") ) # Gate 3: Swap complete if state.swap_epoch is not None and state.swap_epoch <= state.current_epoch: if not state.writes_redirected: return MaintenanceManifest( verdict=MaintenanceVerdict.SWAP_COMPLETE, directive="HALT: Swap complete but writes not redirected. Emergency reconciliation required.", manifest_digest=cls._hash("WRITES_LOST") ) digest = cls._hash(json.dumps({ "swap_epoch": f"{state.swap_epoch:.0f}", "shadow": str(state.shadow_index_ready) }, sort_keys=True)) return MaintenanceManifest( verdict=MaintenanceVerdict.SWAP_COMPLETE, directive="PROCEED: Shadow index is authoritative.", manifest_digest=digest ) # Gate 4: In-place acceptable if policy.max_acceptable_downtime_sec > 0 and not policy.requires_shadow_build: digest = cls._hash(json.dumps({ "max_downtime": f"{policy.max_acceptable_downtime_sec:.0f}" }, sort_keys=True)) return MaintenanceManifest( verdict=MaintenanceVerdict.IN_PLACE_MAINTENANCE_ACCEPTABLE, directive=f"PROCEED: In-place maintenance within {policy.max_acceptable_downtime_sec:.0f}s tolerance.", manifest_digest=digest ) # Gate 5: Ambiguous return MaintenanceManifest( verdict=MaintenanceVerdict.SHADOW_BUILD_IN_PROGRESS, directive="WAIT: Waiting for shadow index or swap.", manifest_digest=cls._hash("WAIT") )