diff --git a/.axiom/semantic_index/index.json b/.axiom/semantic_index/index.json index c88b167a..4f323e7d 100644 --- a/.axiom/semantic_index/index.json +++ b/.axiom/semantic_index/index.json @@ -1,8 +1,8 @@ { - "generated_at": "2026-05-15T18:40:14.207110454Z", + "generated_at": "2026-05-17T09:53:01.679154203Z", "root_path": "/home/busya/dev/ss-tools", - "contract_count": 2479, - "edge_count": 1776, + "contract_count": 2483, + "edge_count": 1759, "file_count": 535, "contracts": [ { @@ -18429,7 +18429,7 @@ "contract_type": "Module", "file_path": "backend/src/api/routes/llm.py", "start_line": 1, - "end_line": 425, + "end_line": 430, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -18484,7 +18484,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region LlmRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, llm, api, provider]\n# @BRIEF API routes for LLM provider configuration and management.\n# @LAYER: UI (API)\n# @RELATION DEPENDS_ON -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n# @RELATION DEPENDS_ON -> [get_current_user]\n# @RELATION DEPENDS_ON -> [get_db]\n\n\nfrom fastapi import APIRouter, Depends, HTTPException, status\nfrom pydantic import BaseModel, Field\nfrom sqlalchemy.orm import Session\n\nfrom ...core.database import get_db\nfrom ...core.logger import logger\nfrom ...dependencies import get_current_user as get_current_active_user\nfrom ...plugins.llm_analysis.models import LLMProviderConfig, LLMProviderType\nfrom ...schemas.auth import User\nfrom ...services.llm_provider import LLMProviderService\n\n\n# #region FetchModelsRequest [C:1] [TYPE Class]\n# @BRIEF Pydantic request model for the fetch-models endpoint.\nclass FetchModelsRequest(BaseModel):\n base_url: str | None = Field(None, description=\"LLM provider base URL\")\n provider_type: str | None = Field(None, description=\"Provider type (openai, anthropic, etc)\")\n api_key: str | None = Field(None, description=\"Direct API key (takes precedence over provider_id)\")\n provider_id: str | None = Field(None, description=\"Saved provider ID to look up stored API key\")\n\n\n# #endregion FetchModelsRequest\n\n# #region router [TYPE Global]\n# @BRIEF APIRouter instance for LLM routes.\nrouter = APIRouter(tags=[\"LLM\"])\n# #endregion router\n\n\n# #region _is_valid_runtime_api_key [TYPE Function]\n# @BRIEF Validate decrypted runtime API key presence/shape.\n# @PRE: value can be None.\n# @POST: Returns True only for non-placeholder key.\n# @RELATION BINDS_TO -> [LlmRoutes]\ndef _is_valid_runtime_api_key(value: str | None) -> bool:\n key = (value or \"\").strip()\n if not key:\n return False\n if key in {\"********\", \"EMPTY_OR_NONE\"}:\n return False\n return len(key) >= 16\n\n\n# #endregion _is_valid_runtime_api_key\n\n\n# #region get_providers [TYPE Function]\n# @BRIEF Retrieve all LLM provider configurations.\n# @PRE: User is authenticated.\n# @POST: Returns list of LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.get(\"/providers\", response_model=list[LLMProviderConfig])\nasync def get_providers(\n current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db)\n):\n \"\"\"\n Get all LLM provider configurations.\n \"\"\"\n logger.reason(\n f\"Fetching providers for user: {current_user.username}\",\n extra={\"src\": \"llm_routes.get_providers\"},\n )\n service = LLMProviderService(db)\n providers = service.get_all_providers()\n return [\n LLMProviderConfig(\n id=p.id,\n provider_type=LLMProviderType(p.provider_type),\n name=p.name,\n base_url=p.base_url,\n api_key=\"********\",\n default_model=p.default_model,\n is_active=p.is_active,\n )\n for p in providers\n ]\n\n\n# #endregion get_providers\n\n\n# #region fetch_models [TYPE Function]\n# @BRIEF Fetch available models from an LLM provider by base_url+provider_type, or by provider_id.\n# @PRE: User is authenticated. Either provider_id or base_url+provider_type must be provided.\n# @POST: Returns a list of available model IDs.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION CALLS -> [LLMClient]\n@router.post(\"/providers/fetch-models\")\nasync def fetch_models(\n payload: FetchModelsRequest,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n from ...plugins.llm_analysis.models import LLMProviderType\n from ...plugins.llm_analysis.service import LLMClient\n\n base_url = (payload.base_url or \"\").strip()\n provider_type_str = (payload.provider_type or \"\").strip()\n api_key = payload.api_key or \"\"\n provider_id = payload.provider_id or \"\"\n\n # Early validation: at least one of api_key or provider_id is required\n if not api_key and not provider_id:\n raise HTTPException(\n status_code=400,\n detail=\"api_key or provider_id is required\",\n )\n\n # Resolve provider_id to stored credentials if no direct api_key given\n if not api_key and provider_id:\n service = LLMProviderService(db)\n db_provider = service.get_provider(provider_id)\n if not db_provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n base_url = base_url or db_provider.base_url\n provider_type_str = provider_type_str or db_provider.provider_type\n stored_key = service.get_decrypted_api_key(provider_id)\n if stored_key:\n api_key = stored_key\n\n if not base_url:\n raise HTTPException(status_code=400, detail=\"base_url is required\")\n if not provider_type_str:\n raise HTTPException(status_code=400, detail=\"provider_type is required\")\n\n try:\n provider_type = LLMProviderType(provider_type_str)\n except ValueError:\n raise HTTPException(status_code=400, detail=f\"Invalid provider_type: {provider_type_str}\")\n\n # Release DB connection before the network call to avoid idle-in-transaction\n # blocking other queries while we wait for the LLM API response.\n db.close()\n\n client = LLMClient(\n provider_type=provider_type,\n api_key=api_key or \"sk-placeholder\",\n base_url=base_url,\n default_model=\"\",\n )\n\n try:\n models = await client.fetch_models()\n return {\"models\": models}\n except Exception as e:\n logger.error(\n f\"[llm_routes.fetch_models] Failed to fetch models: {e}\",\n extra={\"src\": \"llm_routes.fetch_models\"},\n )\n raise HTTPException(status_code=502, detail=str(e))\n\n\n# #endregion fetch_models\n\n\n# #region get_llm_status [TYPE Function]\n# @BRIEF Returns whether LLM runtime is configured for dashboard validation.\n# @PRE: User is authenticated.\n# @POST: configured=true only when an active provider with valid decrypted key exists.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION CALLS -> [_is_valid_runtime_api_key]\n@router.get(\"/status\")\nasync def get_llm_status(\n current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db)\n):\n service = LLMProviderService(db)\n providers = service.get_all_providers()\n active_provider = next((p for p in providers if p.is_active), None)\n\n if not providers:\n return {\n \"configured\": False,\n \"reason\": \"no_providers_configured\",\n \"provider_count\": 0,\n \"active_provider_count\": 0,\n }\n\n if not active_provider:\n return {\n \"configured\": False,\n \"reason\": \"no_active_provider\",\n \"provider_count\": len(providers),\n \"active_provider_count\": 0,\n \"providers\": [\n {\n \"id\": provider.id,\n \"name\": provider.name,\n \"provider_type\": provider.provider_type,\n \"is_active\": bool(provider.is_active),\n }\n for provider in providers\n ],\n }\n\n api_key = service.get_decrypted_api_key(active_provider.id)\n if not _is_valid_runtime_api_key(api_key):\n return {\n \"configured\": False,\n \"reason\": \"invalid_api_key\",\n \"provider_count\": len(providers),\n \"active_provider_count\": len(\n [provider for provider in providers if provider.is_active]\n ),\n \"provider_id\": active_provider.id,\n \"provider_name\": active_provider.name,\n \"provider_type\": active_provider.provider_type,\n \"default_model\": active_provider.default_model,\n }\n\n return {\n \"configured\": True,\n \"reason\": \"ok\",\n \"provider_count\": len(providers),\n \"active_provider_count\": len(\n [provider for provider in providers if provider.is_active]\n ),\n \"provider_id\": active_provider.id,\n \"provider_name\": active_provider.name,\n \"provider_type\": active_provider.provider_type,\n \"default_model\": active_provider.default_model,\n }\n\n\n# #endregion get_llm_status\n\n\n# #region create_provider [TYPE Function]\n# @BRIEF Create a new LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns the created LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.post(\n \"/providers\", response_model=LLMProviderConfig, status_code=status.HTTP_201_CREATED\n)\nasync def create_provider(\n config: LLMProviderConfig,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Create a new LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n provider = service.create_provider(config)\n return LLMProviderConfig(\n id=provider.id,\n provider_type=LLMProviderType(provider.provider_type),\n name=provider.name,\n base_url=provider.base_url,\n api_key=\"********\",\n default_model=provider.default_model,\n is_active=provider.is_active,\n )\n\n\n# #endregion create_provider\n\n\n# #region update_provider [TYPE Function]\n# @BRIEF Update an existing LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns the updated LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.put(\"/providers/{provider_id}\", response_model=LLMProviderConfig)\nasync def update_provider(\n provider_id: str,\n config: LLMProviderConfig,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Update an existing LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n provider = service.update_provider(provider_id, config)\n if not provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n\n return LLMProviderConfig(\n id=provider.id,\n provider_type=LLMProviderType(provider.provider_type),\n name=provider.name,\n base_url=provider.base_url,\n api_key=\"********\",\n default_model=provider.default_model,\n is_active=provider.is_active,\n )\n\n\n# #endregion update_provider\n\n\n# #region delete_provider [TYPE Function]\n# @BRIEF Delete an LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns success status.\n# @RELATION CALLS -> [LLMProviderService]\n@router.delete(\"/providers/{provider_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_provider(\n provider_id: str,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Delete an LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n if not service.delete_provider(provider_id):\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n return\n\n\n# #endregion delete_provider\n\n\n# #region test_connection [TYPE Function]\n# @BRIEF Test connection to an LLM provider.\n# @PRE: User is authenticated.\n# @POST: Returns success status and message.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMClient]\n@router.post(\"/providers/{provider_id}/test\")\nasync def test_connection(\n provider_id: str,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n logger.reason(\n f\"Testing connection for provider_id: {provider_id}\",\n extra={\"src\": \"llm_routes.test_connection\"},\n )\n \"\"\"\n Test connection to an LLM provider.\n \"\"\"\n from ...plugins.llm_analysis.service import LLMClient\n\n service = LLMProviderService(db)\n db_provider = service.get_provider(provider_id)\n if not db_provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n\n api_key = service.get_decrypted_api_key(provider_id)\n\n # Check if API key was successfully decrypted\n if not api_key:\n logger.error(\n f\"[llm_routes][test_connection] Failed to decrypt API key for provider {provider_id}\"\n )\n raise HTTPException(\n status_code=500,\n detail=\"Failed to decrypt API key. The provider may have been encrypted with a different encryption key. Please update the provider with a new API key.\",\n )\n\n client = LLMClient(\n provider_type=LLMProviderType(db_provider.provider_type),\n api_key=api_key,\n base_url=db_provider.base_url,\n default_model=db_provider.default_model,\n )\n\n try:\n await client.test_runtime_connection()\n return {\"success\": True, \"message\": \"Connection successful\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\n\n# #endregion test_connection\n\n\n# #region test_provider_config [TYPE Function]\n# @BRIEF Test connection with a provided configuration (not yet saved).\n# @PRE: User is authenticated.\n# @POST: Returns success status and message.\n# @RELATION DEPENDS_ON -> [LLMClient]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.post(\"/providers/test\")\nasync def test_provider_config(\n config: LLMProviderConfig, current_user: User = Depends(get_current_active_user)\n):\n \"\"\"\n Test connection with a provided configuration.\n \"\"\"\n from ...plugins.llm_analysis.service import LLMClient\n\n logger.reason(\n f\"Testing config for {config.name}\",\n extra={\"src\": \"llm_routes.test_provider_config\"},\n )\n\n # Check if API key is provided\n if not config.api_key or config.api_key == \"********\":\n raise HTTPException(\n status_code=400, detail=\"API key is required for testing connection\"\n )\n\n client = LLMClient(\n provider_type=config.provider_type,\n api_key=config.api_key,\n base_url=config.base_url,\n default_model=config.default_model,\n )\n\n try:\n await client.test_runtime_connection()\n return {\"success\": True, \"message\": \"Connection successful\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\n\n# #endregion test_provider_config\n\n# #endregion LlmRoutes\n" + "body": "# #region LlmRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, llm, api, provider]\n# @BRIEF API routes for LLM provider configuration and management.\n# @LAYER: UI (API)\n# @RELATION DEPENDS_ON -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n# @RELATION DEPENDS_ON -> [get_current_user]\n# @RELATION DEPENDS_ON -> [get_db]\n\n\nfrom fastapi import APIRouter, Depends, HTTPException, status\nfrom pydantic import BaseModel, Field\nfrom sqlalchemy.orm import Session\n\nfrom ...core.database import get_db\nfrom ...core.logger import logger\nfrom ...dependencies import get_current_user as get_current_active_user\nfrom ...plugins.llm_analysis.models import LLMProviderConfig, LLMProviderType\nfrom ...schemas.auth import User\nfrom ...services.llm_provider import LLMProviderService, is_masked_or_placeholder, mask_api_key\n\n\n# #region FetchModelsRequest [C:1] [TYPE Class]\n# @BRIEF Pydantic request model for the fetch-models endpoint.\nclass FetchModelsRequest(BaseModel):\n base_url: str | None = Field(None, description=\"LLM provider base URL\")\n provider_type: str | None = Field(None, description=\"Provider type (openai, anthropic, etc)\")\n api_key: str | None = Field(None, description=\"Direct API key (takes precedence over provider_id)\")\n provider_id: str | None = Field(None, description=\"Saved provider ID to look up stored API key\")\n\n\n# #endregion FetchModelsRequest\n\n# #region router [TYPE Global]\n# @BRIEF APIRouter instance for LLM routes.\nrouter = APIRouter(tags=[\"LLM\"])\n# #endregion router\n\n\n# #region _is_valid_runtime_api_key [C:4] [TYPE Function]\n# @BRIEF Validate decrypted runtime API key presence/shape.\n# @PRE: value can be None.\n# @POST: Returns True only for non-placeholder key.\n# @RELATION BINDS_TO -> [LlmRoutes]\ndef _is_valid_runtime_api_key(value: str | None) -> bool:\n key = (value or \"\").strip()\n if not key:\n return False\n if key in {\"********\", \"EMPTY_OR_NONE\"}:\n return False\n return len(key) >= 16\n\n\n# #endregion _is_valid_runtime_api_key\n\n\n# #region get_providers [C:4] [TYPE Function]\n# @BRIEF Retrieve all LLM provider configurations.\n# @PRE: User is authenticated.\n# @POST: Returns list of LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.get(\"/providers\", response_model=list[LLMProviderConfig])\nasync def get_providers(\n current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db)\n):\n \"\"\"\n Get all LLM provider configurations.\n \"\"\"\n logger.reason(\n f\"Fetching providers for user: {current_user.username}\",\n extra={\"src\": \"llm_routes.get_providers\"},\n )\n service = LLMProviderService(db)\n providers = service.get_all_providers()\n return [\n LLMProviderConfig(\n id=p.id,\n provider_type=LLMProviderType(p.provider_type),\n name=p.name,\n base_url=p.base_url,\n api_key=mask_api_key(service.get_decrypted_api_key(p.id)) if p.api_key else \"\",\n default_model=p.default_model,\n is_active=p.is_active,\n )\n for p in providers\n ]\n\n\n# #endregion get_providers\n\n\n# #region fetch_models [C:4] [TYPE Function]\n# @BRIEF Fetch available models from an LLM provider by base_url+provider_type, or by provider_id.\n# @PRE: User is authenticated. Either provider_id or base_url+provider_type must be provided.\n# @POST: Returns a list of available model IDs.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION CALLS -> [LLMClient]\n@router.post(\"/providers/fetch-models\")\nasync def fetch_models(\n payload: FetchModelsRequest,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n from ...plugins.llm_analysis.models import LLMProviderType\n from ...plugins.llm_analysis.service import LLMClient\n\n base_url = (payload.base_url or \"\").strip()\n provider_type_str = (payload.provider_type or \"\").strip()\n api_key = payload.api_key or \"\"\n provider_id = payload.provider_id or \"\"\n\n # Treat masked/placeholder API keys as if no api_key was provided;\n # they are display-only and cannot be used for real API calls.\n if is_masked_or_placeholder(api_key):\n api_key = \"\"\n\n # Early validation: at least one of api_key or provider_id is required\n if not api_key and not provider_id:\n raise HTTPException(\n status_code=400,\n detail=\"api_key or provider_id is required\",\n )\n\n # Resolve provider_id to stored credentials if no direct api_key given\n if not api_key and provider_id:\n service = LLMProviderService(db)\n db_provider = service.get_provider(provider_id)\n if not db_provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n base_url = base_url or db_provider.base_url\n provider_type_str = provider_type_str or db_provider.provider_type\n stored_key = service.get_decrypted_api_key(provider_id)\n if stored_key:\n api_key = stored_key\n\n if not base_url:\n raise HTTPException(status_code=400, detail=\"base_url is required\")\n if not provider_type_str:\n raise HTTPException(status_code=400, detail=\"provider_type is required\")\n\n try:\n provider_type = LLMProviderType(provider_type_str)\n except ValueError:\n raise HTTPException(status_code=400, detail=f\"Invalid provider_type: {provider_type_str}\")\n\n # Release DB connection before the network call to avoid idle-in-transaction\n # blocking other queries while we wait for the LLM API response.\n db.close()\n\n client = LLMClient(\n provider_type=provider_type,\n api_key=api_key or \"sk-placeholder\",\n base_url=base_url,\n default_model=\"\",\n )\n\n try:\n models = await client.fetch_models()\n return {\"models\": models}\n except Exception as e:\n logger.error(\n f\"[llm_routes.fetch_models] Failed to fetch models: {e}\",\n extra={\"src\": \"llm_routes.fetch_models\"},\n )\n raise HTTPException(status_code=502, detail=str(e))\n\n\n# #endregion fetch_models\n\n\n# #region get_llm_status [C:4] [TYPE Function]\n# @BRIEF Returns whether LLM runtime is configured for dashboard validation.\n# @PRE: User is authenticated.\n# @POST: configured=true only when an active provider with valid decrypted key exists.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION CALLS -> [_is_valid_runtime_api_key]\n@router.get(\"/status\")\nasync def get_llm_status(\n current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db)\n):\n service = LLMProviderService(db)\n providers = service.get_all_providers()\n active_provider = next((p for p in providers if p.is_active), None)\n\n if not providers:\n return {\n \"configured\": False,\n \"reason\": \"no_providers_configured\",\n \"provider_count\": 0,\n \"active_provider_count\": 0,\n }\n\n if not active_provider:\n return {\n \"configured\": False,\n \"reason\": \"no_active_provider\",\n \"provider_count\": len(providers),\n \"active_provider_count\": 0,\n \"providers\": [\n {\n \"id\": provider.id,\n \"name\": provider.name,\n \"provider_type\": provider.provider_type,\n \"is_active\": bool(provider.is_active),\n }\n for provider in providers\n ],\n }\n\n api_key = service.get_decrypted_api_key(active_provider.id)\n if not _is_valid_runtime_api_key(api_key):\n return {\n \"configured\": False,\n \"reason\": \"invalid_api_key\",\n \"provider_count\": len(providers),\n \"active_provider_count\": len(\n [provider for provider in providers if provider.is_active]\n ),\n \"provider_id\": active_provider.id,\n \"provider_name\": active_provider.name,\n \"provider_type\": active_provider.provider_type,\n \"default_model\": active_provider.default_model,\n }\n\n return {\n \"configured\": True,\n \"reason\": \"ok\",\n \"provider_count\": len(providers),\n \"active_provider_count\": len(\n [provider for provider in providers if provider.is_active]\n ),\n \"provider_id\": active_provider.id,\n \"provider_name\": active_provider.name,\n \"provider_type\": active_provider.provider_type,\n \"default_model\": active_provider.default_model,\n }\n\n\n# #endregion get_llm_status\n\n\n# #region create_provider [C:4] [TYPE Function]\n# @BRIEF Create a new LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns the created LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.post(\n \"/providers\", response_model=LLMProviderConfig, status_code=status.HTTP_201_CREATED\n)\nasync def create_provider(\n config: LLMProviderConfig,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Create a new LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n provider = service.create_provider(config)\n return LLMProviderConfig(\n id=provider.id,\n provider_type=LLMProviderType(provider.provider_type),\n name=provider.name,\n base_url=provider.base_url,\n api_key=mask_api_key(config.api_key),\n default_model=provider.default_model,\n is_active=provider.is_active,\n )\n\n\n# #endregion create_provider\n\n\n# #region update_provider [C:4] [TYPE Function]\n# @BRIEF Update an existing LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns the updated LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.put(\"/providers/{provider_id}\", response_model=LLMProviderConfig)\nasync def update_provider(\n provider_id: str,\n config: LLMProviderConfig,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Update an existing LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n provider = service.update_provider(provider_id, config)\n if not provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n\n return LLMProviderConfig(\n id=provider.id,\n provider_type=LLMProviderType(provider.provider_type),\n name=provider.name,\n base_url=provider.base_url,\n api_key=mask_api_key(service.get_decrypted_api_key(provider.id)) if provider.api_key else \"\",\n default_model=provider.default_model,\n is_active=provider.is_active,\n )\n\n\n# #endregion update_provider\n\n\n# #region delete_provider [C:4] [TYPE Function]\n# @BRIEF Delete an LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns success status.\n# @RELATION CALLS -> [LLMProviderService]\n@router.delete(\"/providers/{provider_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_provider(\n provider_id: str,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Delete an LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n if not service.delete_provider(provider_id):\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n return\n\n\n# #endregion delete_provider\n\n\n# #region test_connection [C:4] [TYPE Function]\n# @BRIEF Test connection to an LLM provider.\n# @PRE: User is authenticated.\n# @POST: Returns success status and message.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMClient]\n@router.post(\"/providers/{provider_id}/test\")\nasync def test_connection(\n provider_id: str,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n logger.reason(\n f\"Testing connection for provider_id: {provider_id}\",\n extra={\"src\": \"llm_routes.test_connection\"},\n )\n \"\"\"\n Test connection to an LLM provider.\n \"\"\"\n from ...plugins.llm_analysis.service import LLMClient\n\n service = LLMProviderService(db)\n db_provider = service.get_provider(provider_id)\n if not db_provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n\n api_key = service.get_decrypted_api_key(provider_id)\n\n # Check if API key was successfully decrypted\n if not api_key:\n logger.error(\n f\"[llm_routes][test_connection] Failed to decrypt API key for provider {provider_id}\"\n )\n raise HTTPException(\n status_code=500,\n detail=\"Failed to decrypt API key. The provider may have been encrypted with a different encryption key. Please update the provider with a new API key.\",\n )\n\n client = LLMClient(\n provider_type=LLMProviderType(db_provider.provider_type),\n api_key=api_key,\n base_url=db_provider.base_url,\n default_model=db_provider.default_model,\n )\n\n try:\n await client.test_runtime_connection()\n return {\"success\": True, \"message\": \"Connection successful\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\n\n# #endregion test_connection\n\n\n# #region test_provider_config [C:4] [TYPE Function]\n# @BRIEF Test connection with a provided configuration (not yet saved).\n# @PRE: User is authenticated.\n# @POST: Returns success status and message.\n# @RELATION DEPENDS_ON -> [LLMClient]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.post(\"/providers/test\")\nasync def test_provider_config(\n config: LLMProviderConfig, current_user: User = Depends(get_current_active_user)\n):\n \"\"\"\n Test connection with a provided configuration.\n \"\"\"\n from ...plugins.llm_analysis.service import LLMClient\n\n logger.reason(\n f\"Testing config for {config.name}\",\n extra={\"src\": \"llm_routes.test_provider_config\"},\n )\n\n # Check if API key is provided (reject empty, placeholder, or masked keys)\n if is_masked_or_placeholder(config.api_key):\n raise HTTPException(\n status_code=400, detail=\"API key is required for testing connection\"\n )\n\n client = LLMClient(\n provider_type=config.provider_type,\n api_key=config.api_key,\n base_url=config.base_url,\n default_model=config.default_model,\n )\n\n try:\n await client.test_runtime_connection()\n return {\"success\": True, \"message\": \"Connection successful\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\n\n# #endregion test_provider_config\n\n# #endregion LlmRoutes\n" }, { "contract_id": "FetchModelsRequest", @@ -18555,9 +18555,10 @@ "file_path": "backend/src/api/routes/llm.py", "start_line": 39, "end_line": 53, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns True only for non-placeholder key.", "PRE": "value can be None.", "PURPOSE": "Validate decrypted runtime API key presence/shape." @@ -18572,36 +18573,18 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region _is_valid_runtime_api_key [TYPE Function]\n# @BRIEF Validate decrypted runtime API key presence/shape.\n# @PRE: value can be None.\n# @POST: Returns True only for non-placeholder key.\n# @RELATION BINDS_TO -> [LlmRoutes]\ndef _is_valid_runtime_api_key(value: str | None) -> bool:\n key = (value or \"\").strip()\n if not key:\n return False\n if key in {\"********\", \"EMPTY_OR_NONE\"}:\n return False\n return len(key) >= 16\n\n\n# #endregion _is_valid_runtime_api_key\n" + "body": "# #region _is_valid_runtime_api_key [C:4] [TYPE Function]\n# @BRIEF Validate decrypted runtime API key presence/shape.\n# @PRE: value can be None.\n# @POST: Returns True only for non-placeholder key.\n# @RELATION BINDS_TO -> [LlmRoutes]\ndef _is_valid_runtime_api_key(value: str | None) -> bool:\n key = (value or \"\").strip()\n if not key:\n return False\n if key in {\"********\", \"EMPTY_OR_NONE\"}:\n return False\n return len(key) >= 16\n\n\n# #endregion _is_valid_runtime_api_key\n" }, { "contract_id": "get_providers", @@ -18609,9 +18592,10 @@ "file_path": "backend/src/api/routes/llm.py", "start_line": 56, "end_line": 89, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns list of LLMProviderConfig.", "PRE": "User is authenticated.", "PURPOSE": "Retrieve all LLM provider configurations." @@ -18632,46 +18616,29 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_providers [TYPE Function]\n# @BRIEF Retrieve all LLM provider configurations.\n# @PRE: User is authenticated.\n# @POST: Returns list of LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.get(\"/providers\", response_model=list[LLMProviderConfig])\nasync def get_providers(\n current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db)\n):\n \"\"\"\n Get all LLM provider configurations.\n \"\"\"\n logger.reason(\n f\"Fetching providers for user: {current_user.username}\",\n extra={\"src\": \"llm_routes.get_providers\"},\n )\n service = LLMProviderService(db)\n providers = service.get_all_providers()\n return [\n LLMProviderConfig(\n id=p.id,\n provider_type=LLMProviderType(p.provider_type),\n name=p.name,\n base_url=p.base_url,\n api_key=\"********\",\n default_model=p.default_model,\n is_active=p.is_active,\n )\n for p in providers\n ]\n\n\n# #endregion get_providers\n" + "body": "# #region get_providers [C:4] [TYPE Function]\n# @BRIEF Retrieve all LLM provider configurations.\n# @PRE: User is authenticated.\n# @POST: Returns list of LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.get(\"/providers\", response_model=list[LLMProviderConfig])\nasync def get_providers(\n current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db)\n):\n \"\"\"\n Get all LLM provider configurations.\n \"\"\"\n logger.reason(\n f\"Fetching providers for user: {current_user.username}\",\n extra={\"src\": \"llm_routes.get_providers\"},\n )\n service = LLMProviderService(db)\n providers = service.get_all_providers()\n return [\n LLMProviderConfig(\n id=p.id,\n provider_type=LLMProviderType(p.provider_type),\n name=p.name,\n base_url=p.base_url,\n api_key=mask_api_key(service.get_decrypted_api_key(p.id)) if p.api_key else \"\",\n default_model=p.default_model,\n is_active=p.is_active,\n )\n for p in providers\n ]\n\n\n# #endregion get_providers\n" }, { "contract_id": "fetch_models", "contract_type": "Function", "file_path": "backend/src/api/routes/llm.py", "start_line": 92, - "end_line": 163, - "tier": "TIER_1", - "complexity": 1, + "end_line": 168, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns a list of available model IDs.", "PRE": "User is authenticated. Either provider_id or base_url+provider_type must be provided.", "PURPOSE": "Fetch available models from an LLM provider by base_url+provider_type, or by provider_id." @@ -18692,46 +18659,29 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region fetch_models [TYPE Function]\n# @BRIEF Fetch available models from an LLM provider by base_url+provider_type, or by provider_id.\n# @PRE: User is authenticated. Either provider_id or base_url+provider_type must be provided.\n# @POST: Returns a list of available model IDs.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION CALLS -> [LLMClient]\n@router.post(\"/providers/fetch-models\")\nasync def fetch_models(\n payload: FetchModelsRequest,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n from ...plugins.llm_analysis.models import LLMProviderType\n from ...plugins.llm_analysis.service import LLMClient\n\n base_url = (payload.base_url or \"\").strip()\n provider_type_str = (payload.provider_type or \"\").strip()\n api_key = payload.api_key or \"\"\n provider_id = payload.provider_id or \"\"\n\n # Early validation: at least one of api_key or provider_id is required\n if not api_key and not provider_id:\n raise HTTPException(\n status_code=400,\n detail=\"api_key or provider_id is required\",\n )\n\n # Resolve provider_id to stored credentials if no direct api_key given\n if not api_key and provider_id:\n service = LLMProviderService(db)\n db_provider = service.get_provider(provider_id)\n if not db_provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n base_url = base_url or db_provider.base_url\n provider_type_str = provider_type_str or db_provider.provider_type\n stored_key = service.get_decrypted_api_key(provider_id)\n if stored_key:\n api_key = stored_key\n\n if not base_url:\n raise HTTPException(status_code=400, detail=\"base_url is required\")\n if not provider_type_str:\n raise HTTPException(status_code=400, detail=\"provider_type is required\")\n\n try:\n provider_type = LLMProviderType(provider_type_str)\n except ValueError:\n raise HTTPException(status_code=400, detail=f\"Invalid provider_type: {provider_type_str}\")\n\n # Release DB connection before the network call to avoid idle-in-transaction\n # blocking other queries while we wait for the LLM API response.\n db.close()\n\n client = LLMClient(\n provider_type=provider_type,\n api_key=api_key or \"sk-placeholder\",\n base_url=base_url,\n default_model=\"\",\n )\n\n try:\n models = await client.fetch_models()\n return {\"models\": models}\n except Exception as e:\n logger.error(\n f\"[llm_routes.fetch_models] Failed to fetch models: {e}\",\n extra={\"src\": \"llm_routes.fetch_models\"},\n )\n raise HTTPException(status_code=502, detail=str(e))\n\n\n# #endregion fetch_models\n" + "body": "# #region fetch_models [C:4] [TYPE Function]\n# @BRIEF Fetch available models from an LLM provider by base_url+provider_type, or by provider_id.\n# @PRE: User is authenticated. Either provider_id or base_url+provider_type must be provided.\n# @POST: Returns a list of available model IDs.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION CALLS -> [LLMClient]\n@router.post(\"/providers/fetch-models\")\nasync def fetch_models(\n payload: FetchModelsRequest,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n from ...plugins.llm_analysis.models import LLMProviderType\n from ...plugins.llm_analysis.service import LLMClient\n\n base_url = (payload.base_url or \"\").strip()\n provider_type_str = (payload.provider_type or \"\").strip()\n api_key = payload.api_key or \"\"\n provider_id = payload.provider_id or \"\"\n\n # Treat masked/placeholder API keys as if no api_key was provided;\n # they are display-only and cannot be used for real API calls.\n if is_masked_or_placeholder(api_key):\n api_key = \"\"\n\n # Early validation: at least one of api_key or provider_id is required\n if not api_key and not provider_id:\n raise HTTPException(\n status_code=400,\n detail=\"api_key or provider_id is required\",\n )\n\n # Resolve provider_id to stored credentials if no direct api_key given\n if not api_key and provider_id:\n service = LLMProviderService(db)\n db_provider = service.get_provider(provider_id)\n if not db_provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n base_url = base_url or db_provider.base_url\n provider_type_str = provider_type_str or db_provider.provider_type\n stored_key = service.get_decrypted_api_key(provider_id)\n if stored_key:\n api_key = stored_key\n\n if not base_url:\n raise HTTPException(status_code=400, detail=\"base_url is required\")\n if not provider_type_str:\n raise HTTPException(status_code=400, detail=\"provider_type is required\")\n\n try:\n provider_type = LLMProviderType(provider_type_str)\n except ValueError:\n raise HTTPException(status_code=400, detail=f\"Invalid provider_type: {provider_type_str}\")\n\n # Release DB connection before the network call to avoid idle-in-transaction\n # blocking other queries while we wait for the LLM API response.\n db.close()\n\n client = LLMClient(\n provider_type=provider_type,\n api_key=api_key or \"sk-placeholder\",\n base_url=base_url,\n default_model=\"\",\n )\n\n try:\n models = await client.fetch_models()\n return {\"models\": models}\n except Exception as e:\n logger.error(\n f\"[llm_routes.fetch_models] Failed to fetch models: {e}\",\n extra={\"src\": \"llm_routes.fetch_models\"},\n )\n raise HTTPException(status_code=502, detail=str(e))\n\n\n# #endregion fetch_models\n" }, { "contract_id": "get_llm_status", "contract_type": "Function", "file_path": "backend/src/api/routes/llm.py", - "start_line": 166, - "end_line": 234, - "tier": "TIER_1", - "complexity": 1, + "start_line": 171, + "end_line": 239, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "configured=true only when an active provider with valid decrypted key exists.", "PRE": "User is authenticated.", "PURPOSE": "Returns whether LLM runtime is configured for dashboard validation." @@ -18752,46 +18702,29 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_llm_status [TYPE Function]\n# @BRIEF Returns whether LLM runtime is configured for dashboard validation.\n# @PRE: User is authenticated.\n# @POST: configured=true only when an active provider with valid decrypted key exists.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION CALLS -> [_is_valid_runtime_api_key]\n@router.get(\"/status\")\nasync def get_llm_status(\n current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db)\n):\n service = LLMProviderService(db)\n providers = service.get_all_providers()\n active_provider = next((p for p in providers if p.is_active), None)\n\n if not providers:\n return {\n \"configured\": False,\n \"reason\": \"no_providers_configured\",\n \"provider_count\": 0,\n \"active_provider_count\": 0,\n }\n\n if not active_provider:\n return {\n \"configured\": False,\n \"reason\": \"no_active_provider\",\n \"provider_count\": len(providers),\n \"active_provider_count\": 0,\n \"providers\": [\n {\n \"id\": provider.id,\n \"name\": provider.name,\n \"provider_type\": provider.provider_type,\n \"is_active\": bool(provider.is_active),\n }\n for provider in providers\n ],\n }\n\n api_key = service.get_decrypted_api_key(active_provider.id)\n if not _is_valid_runtime_api_key(api_key):\n return {\n \"configured\": False,\n \"reason\": \"invalid_api_key\",\n \"provider_count\": len(providers),\n \"active_provider_count\": len(\n [provider for provider in providers if provider.is_active]\n ),\n \"provider_id\": active_provider.id,\n \"provider_name\": active_provider.name,\n \"provider_type\": active_provider.provider_type,\n \"default_model\": active_provider.default_model,\n }\n\n return {\n \"configured\": True,\n \"reason\": \"ok\",\n \"provider_count\": len(providers),\n \"active_provider_count\": len(\n [provider for provider in providers if provider.is_active]\n ),\n \"provider_id\": active_provider.id,\n \"provider_name\": active_provider.name,\n \"provider_type\": active_provider.provider_type,\n \"default_model\": active_provider.default_model,\n }\n\n\n# #endregion get_llm_status\n" + "body": "# #region get_llm_status [C:4] [TYPE Function]\n# @BRIEF Returns whether LLM runtime is configured for dashboard validation.\n# @PRE: User is authenticated.\n# @POST: configured=true only when an active provider with valid decrypted key exists.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION CALLS -> [_is_valid_runtime_api_key]\n@router.get(\"/status\")\nasync def get_llm_status(\n current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db)\n):\n service = LLMProviderService(db)\n providers = service.get_all_providers()\n active_provider = next((p for p in providers if p.is_active), None)\n\n if not providers:\n return {\n \"configured\": False,\n \"reason\": \"no_providers_configured\",\n \"provider_count\": 0,\n \"active_provider_count\": 0,\n }\n\n if not active_provider:\n return {\n \"configured\": False,\n \"reason\": \"no_active_provider\",\n \"provider_count\": len(providers),\n \"active_provider_count\": 0,\n \"providers\": [\n {\n \"id\": provider.id,\n \"name\": provider.name,\n \"provider_type\": provider.provider_type,\n \"is_active\": bool(provider.is_active),\n }\n for provider in providers\n ],\n }\n\n api_key = service.get_decrypted_api_key(active_provider.id)\n if not _is_valid_runtime_api_key(api_key):\n return {\n \"configured\": False,\n \"reason\": \"invalid_api_key\",\n \"provider_count\": len(providers),\n \"active_provider_count\": len(\n [provider for provider in providers if provider.is_active]\n ),\n \"provider_id\": active_provider.id,\n \"provider_name\": active_provider.name,\n \"provider_type\": active_provider.provider_type,\n \"default_model\": active_provider.default_model,\n }\n\n return {\n \"configured\": True,\n \"reason\": \"ok\",\n \"provider_count\": len(providers),\n \"active_provider_count\": len(\n [provider for provider in providers if provider.is_active]\n ),\n \"provider_id\": active_provider.id,\n \"provider_name\": active_provider.name,\n \"provider_type\": active_provider.provider_type,\n \"default_model\": active_provider.default_model,\n }\n\n\n# #endregion get_llm_status\n" }, { "contract_id": "create_provider", "contract_type": "Function", "file_path": "backend/src/api/routes/llm.py", - "start_line": 237, - "end_line": 267, - "tier": "TIER_1", - "complexity": 1, + "start_line": 242, + "end_line": 272, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the created LLMProviderConfig.", "PRE": "User is authenticated and has admin permissions.", "PURPOSE": "Create a new LLM provider configuration." @@ -18812,46 +18745,29 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region create_provider [TYPE Function]\n# @BRIEF Create a new LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns the created LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.post(\n \"/providers\", response_model=LLMProviderConfig, status_code=status.HTTP_201_CREATED\n)\nasync def create_provider(\n config: LLMProviderConfig,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Create a new LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n provider = service.create_provider(config)\n return LLMProviderConfig(\n id=provider.id,\n provider_type=LLMProviderType(provider.provider_type),\n name=provider.name,\n base_url=provider.base_url,\n api_key=\"********\",\n default_model=provider.default_model,\n is_active=provider.is_active,\n )\n\n\n# #endregion create_provider\n" + "body": "# #region create_provider [C:4] [TYPE Function]\n# @BRIEF Create a new LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns the created LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.post(\n \"/providers\", response_model=LLMProviderConfig, status_code=status.HTTP_201_CREATED\n)\nasync def create_provider(\n config: LLMProviderConfig,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Create a new LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n provider = service.create_provider(config)\n return LLMProviderConfig(\n id=provider.id,\n provider_type=LLMProviderType(provider.provider_type),\n name=provider.name,\n base_url=provider.base_url,\n api_key=mask_api_key(config.api_key),\n default_model=provider.default_model,\n is_active=provider.is_active,\n )\n\n\n# #endregion create_provider\n" }, { "contract_id": "update_provider", "contract_type": "Function", "file_path": "backend/src/api/routes/llm.py", - "start_line": 270, - "end_line": 302, - "tier": "TIER_1", - "complexity": 1, + "start_line": 275, + "end_line": 307, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the updated LLMProviderConfig.", "PRE": "User is authenticated and has admin permissions.", "PURPOSE": "Update an existing LLM provider configuration." @@ -18872,46 +18788,29 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region update_provider [TYPE Function]\n# @BRIEF Update an existing LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns the updated LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.put(\"/providers/{provider_id}\", response_model=LLMProviderConfig)\nasync def update_provider(\n provider_id: str,\n config: LLMProviderConfig,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Update an existing LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n provider = service.update_provider(provider_id, config)\n if not provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n\n return LLMProviderConfig(\n id=provider.id,\n provider_type=LLMProviderType(provider.provider_type),\n name=provider.name,\n base_url=provider.base_url,\n api_key=\"********\",\n default_model=provider.default_model,\n is_active=provider.is_active,\n )\n\n\n# #endregion update_provider\n" + "body": "# #region update_provider [C:4] [TYPE Function]\n# @BRIEF Update an existing LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns the updated LLMProviderConfig.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.put(\"/providers/{provider_id}\", response_model=LLMProviderConfig)\nasync def update_provider(\n provider_id: str,\n config: LLMProviderConfig,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Update an existing LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n provider = service.update_provider(provider_id, config)\n if not provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n\n return LLMProviderConfig(\n id=provider.id,\n provider_type=LLMProviderType(provider.provider_type),\n name=provider.name,\n base_url=provider.base_url,\n api_key=mask_api_key(service.get_decrypted_api_key(provider.id)) if provider.api_key else \"\",\n default_model=provider.default_model,\n is_active=provider.is_active,\n )\n\n\n# #endregion update_provider\n" }, { "contract_id": "delete_provider", "contract_type": "Function", "file_path": "backend/src/api/routes/llm.py", - "start_line": 305, - "end_line": 325, - "tier": "TIER_1", - "complexity": 1, + "start_line": 310, + "end_line": 330, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns success status.", "PRE": "User is authenticated and has admin permissions.", "PURPOSE": "Delete an LLM provider configuration." @@ -18926,46 +18825,29 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region delete_provider [TYPE Function]\n# @BRIEF Delete an LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns success status.\n# @RELATION CALLS -> [LLMProviderService]\n@router.delete(\"/providers/{provider_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_provider(\n provider_id: str,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Delete an LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n if not service.delete_provider(provider_id):\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n return\n\n\n# #endregion delete_provider\n" + "body": "# #region delete_provider [C:4] [TYPE Function]\n# @BRIEF Delete an LLM provider configuration.\n# @PRE: User is authenticated and has admin permissions.\n# @POST: Returns success status.\n# @RELATION CALLS -> [LLMProviderService]\n@router.delete(\"/providers/{provider_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_provider(\n provider_id: str,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n \"\"\"\n Delete an LLM provider configuration.\n \"\"\"\n service = LLMProviderService(db)\n if not service.delete_provider(provider_id):\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n return\n\n\n# #endregion delete_provider\n" }, { "contract_id": "test_connection", "contract_type": "Function", "file_path": "backend/src/api/routes/llm.py", - "start_line": 328, - "end_line": 380, - "tier": "TIER_1", - "complexity": 1, + "start_line": 333, + "end_line": 385, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns success status and message.", "PRE": "User is authenticated.", "PURPOSE": "Test connection to an LLM provider." @@ -18986,46 +18868,29 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region test_connection [TYPE Function]\n# @BRIEF Test connection to an LLM provider.\n# @PRE: User is authenticated.\n# @POST: Returns success status and message.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMClient]\n@router.post(\"/providers/{provider_id}/test\")\nasync def test_connection(\n provider_id: str,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n logger.reason(\n f\"Testing connection for provider_id: {provider_id}\",\n extra={\"src\": \"llm_routes.test_connection\"},\n )\n \"\"\"\n Test connection to an LLM provider.\n \"\"\"\n from ...plugins.llm_analysis.service import LLMClient\n\n service = LLMProviderService(db)\n db_provider = service.get_provider(provider_id)\n if not db_provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n\n api_key = service.get_decrypted_api_key(provider_id)\n\n # Check if API key was successfully decrypted\n if not api_key:\n logger.error(\n f\"[llm_routes][test_connection] Failed to decrypt API key for provider {provider_id}\"\n )\n raise HTTPException(\n status_code=500,\n detail=\"Failed to decrypt API key. The provider may have been encrypted with a different encryption key. Please update the provider with a new API key.\",\n )\n\n client = LLMClient(\n provider_type=LLMProviderType(db_provider.provider_type),\n api_key=api_key,\n base_url=db_provider.base_url,\n default_model=db_provider.default_model,\n )\n\n try:\n await client.test_runtime_connection()\n return {\"success\": True, \"message\": \"Connection successful\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\n\n# #endregion test_connection\n" + "body": "# #region test_connection [C:4] [TYPE Function]\n# @BRIEF Test connection to an LLM provider.\n# @PRE: User is authenticated.\n# @POST: Returns success status and message.\n# @RELATION CALLS -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [LLMClient]\n@router.post(\"/providers/{provider_id}/test\")\nasync def test_connection(\n provider_id: str,\n current_user: User = Depends(get_current_active_user),\n db: Session = Depends(get_db),\n):\n logger.reason(\n f\"Testing connection for provider_id: {provider_id}\",\n extra={\"src\": \"llm_routes.test_connection\"},\n )\n \"\"\"\n Test connection to an LLM provider.\n \"\"\"\n from ...plugins.llm_analysis.service import LLMClient\n\n service = LLMProviderService(db)\n db_provider = service.get_provider(provider_id)\n if not db_provider:\n raise HTTPException(status_code=404, detail=\"Provider not found\")\n\n api_key = service.get_decrypted_api_key(provider_id)\n\n # Check if API key was successfully decrypted\n if not api_key:\n logger.error(\n f\"[llm_routes][test_connection] Failed to decrypt API key for provider {provider_id}\"\n )\n raise HTTPException(\n status_code=500,\n detail=\"Failed to decrypt API key. The provider may have been encrypted with a different encryption key. Please update the provider with a new API key.\",\n )\n\n client = LLMClient(\n provider_type=LLMProviderType(db_provider.provider_type),\n api_key=api_key,\n base_url=db_provider.base_url,\n default_model=db_provider.default_model,\n )\n\n try:\n await client.test_runtime_connection()\n return {\"success\": True, \"message\": \"Connection successful\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\n\n# #endregion test_connection\n" }, { "contract_id": "test_provider_config", "contract_type": "Function", "file_path": "backend/src/api/routes/llm.py", - "start_line": 383, - "end_line": 423, - "tier": "TIER_1", - "complexity": 1, + "start_line": 388, + "end_line": 428, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns success status and message.", "PRE": "User is authenticated.", "PURPOSE": "Test connection with a provided configuration (not yet saved)." @@ -19046,36 +18911,18 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region test_provider_config [TYPE Function]\n# @BRIEF Test connection with a provided configuration (not yet saved).\n# @PRE: User is authenticated.\n# @POST: Returns success status and message.\n# @RELATION DEPENDS_ON -> [LLMClient]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.post(\"/providers/test\")\nasync def test_provider_config(\n config: LLMProviderConfig, current_user: User = Depends(get_current_active_user)\n):\n \"\"\"\n Test connection with a provided configuration.\n \"\"\"\n from ...plugins.llm_analysis.service import LLMClient\n\n logger.reason(\n f\"Testing config for {config.name}\",\n extra={\"src\": \"llm_routes.test_provider_config\"},\n )\n\n # Check if API key is provided\n if not config.api_key or config.api_key == \"********\":\n raise HTTPException(\n status_code=400, detail=\"API key is required for testing connection\"\n )\n\n client = LLMClient(\n provider_type=config.provider_type,\n api_key=config.api_key,\n base_url=config.base_url,\n default_model=config.default_model,\n )\n\n try:\n await client.test_runtime_connection()\n return {\"success\": True, \"message\": \"Connection successful\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\n\n# #endregion test_provider_config\n" + "body": "# #region test_provider_config [C:4] [TYPE Function]\n# @BRIEF Test connection with a provided configuration (not yet saved).\n# @PRE: User is authenticated.\n# @POST: Returns success status and message.\n# @RELATION DEPENDS_ON -> [LLMClient]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\n@router.post(\"/providers/test\")\nasync def test_provider_config(\n config: LLMProviderConfig, current_user: User = Depends(get_current_active_user)\n):\n \"\"\"\n Test connection with a provided configuration.\n \"\"\"\n from ...plugins.llm_analysis.service import LLMClient\n\n logger.reason(\n f\"Testing config for {config.name}\",\n extra={\"src\": \"llm_routes.test_provider_config\"},\n )\n\n # Check if API key is provided (reject empty, placeholder, or masked keys)\n if is_masked_or_placeholder(config.api_key):\n raise HTTPException(\n status_code=400, detail=\"API key is required for testing connection\"\n )\n\n client = LLMClient(\n provider_type=config.provider_type,\n api_key=config.api_key,\n base_url=config.base_url,\n default_model=config.default_model,\n )\n\n try:\n await client.test_runtime_connection()\n return {\"success\": True, \"message\": \"Connection successful\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\n\n# #endregion test_provider_config\n" }, { "contract_id": "MappingsApi", @@ -21868,52 +21715,55 @@ "contract_type": "Module", "file_path": "backend/src/api/routes/translate/__init__.py", "start_line": 1, - "end_line": 37, + "end_line": 40, "tier": "TIER_2", "complexity": 4, "metadata": { "COMPLEXITY": 4, - "LAYER": "UI (API)", + "LAYER": "API", + "POST": "Translation job, run, dictionary, correction, preview, and schedule CRUD operations are executed.", + "PRE": "API server is running, user is authenticated, database is accessible.", "PURPOSE": "API routes for LLM-based SQL/dashboard translation management including terminology dictionary CRUD and import.", "RATIONALE": "Snapshot isolation — in-progress runs use config snapshot; config edits affect future runs only.", - "REJECTED": "Invalidating in-progress runs on config edit would break scheduled run continuity." + "REJECTED": "Invalidating in-progress runs on config edit would break scheduled run continuity.", + "SIDE_EFFECT": "Creates/modifies DB rows; triggers background translation runs; queries Superset API." }, "relations": [ { "source_id": "TranslateRoutes", "relation_type": "DEPENDS_ON", - "target_id": "TranslateJobResponse", - "target_ref": "[TranslateJobResponse]" + "target_id": "SupersetClient", + "target_ref": "[SupersetClient]" }, { "source_id": "TranslateRoutes", "relation_type": "DEPENDS_ON", - "target_id": "DictionaryManager:Class", - "target_ref": "[DictionaryManager:Class]" + "target_id": "SupersetClient", + "target_ref": "[SupersetClient]" }, { "source_id": "TranslateRoutes", "relation_type": "DEPENDS_ON", - "target_id": "get_current_user", - "target_ref": "[get_current_user]" + "target_id": "SupersetClient", + "target_ref": "[SupersetClient]" }, { "source_id": "TranslateRoutes", "relation_type": "DEPENDS_ON", - "target_id": "get_db", - "target_ref": "[get_db]" + "target_id": "SupersetClient", + "target_ref": "[SupersetClient]" }, { "source_id": "TranslateRoutes", "relation_type": "DEPENDS_ON", - "target_id": "has_permission", - "target_ref": "[has_permission]" + "target_id": "SupersetClient", + "target_ref": "[SupersetClient]" }, { "source_id": "TranslateRoutes", "relation_type": "DEPENDS_ON", - "target_id": "ConfigManager", - "target_ref": "[ConfigManager]" + "target_id": "SupersetClient", + "target_ref": "[SupersetClient]" }, { "source_id": "TranslateRoutes", @@ -21922,62 +21772,17 @@ "target_ref": "[SupersetClient]" } ], - "schema_warnings": [ - { - "code": "invalid_enum_value", - "tag": "LAYER", - "message": "@LAYER value 'UI (API)' is not in allowed enum", - "detail": { - "allowed": [ - "Core", - "Domain", - "API", - "UI", - "Service", - "Infrastructure", - "Plugin" - ], - "value": "UI (API)" - } - }, - { - "code": "missing_required_tag", - "tag": "POST", - "message": "@POST is required for contract type 'Module' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Module" - } - }, - { - "code": "missing_required_tag", - "tag": "PRE", - "message": "@PRE is required for contract type 'Module' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Module" - } - }, - { - "code": "missing_required_tag", - "tag": "SIDE_EFFECT", - "message": "@SIDE_EFFECT is required for contract type 'Module' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Module" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateRoutes [C:4] [TYPE Module] [SEMANTICS translate, api, package, schedule, dashboard, llm]\n# @BRIEF API routes for LLM-based SQL/dashboard translation management including terminology dictionary CRUD and import.\n# @LAYER: UI (API)\n# @RELATION DEPENDS_ON -> [TranslateJobResponse]\n# @RELATION DEPENDS_ON -> [DictionaryManager:Class]\n# @RELATION DEPENDS_ON -> [get_current_user]\n# @RELATION DEPENDS_ON -> [get_db]\n# @RELATION DEPENDS_ON -> [has_permission]\n# @RELATION DEPENDS_ON -> [ConfigManager]\n# @RELATION DEPENDS_ON -> [SupersetClient]\n# @RATIONALE: Snapshot isolation — in-progress runs use config snapshot; config edits affect future runs only.\n# @REJECTED: Invalidating in-progress runs on config edit would break scheduled run continuity.\n\n\"\"\"\nTranslate routes package.\n\nRe-exports the shared router and all route handler modules.\nAll sub-modules register their handlers on the router at import time.\n\"\"\"\n\nfrom . import (\n _correction_routes, # noqa: F401 — registers correction handlers\n _dictionary_routes, # noqa: F401 — registers dictionary handlers\n _job_routes, # noqa: F401 — registers job handlers\n _metrics_routes, # noqa: F401 — registers metrics handlers\n _preview_routes, # noqa: F401 — registers preview handlers\n _run_list_routes, # noqa: F401 — registers run list/detail/csv handlers\n _run_routes, # noqa: F401 — registers run handlers\n _schedule_routes, # noqa: F401 — registers schedule handlers\n)\nfrom ._router import router\n\n__all__ = [\n \"router\",\n]\n\n# #endregion TranslateRoutes\n" + "body": "# #region TranslateRoutes [C:4] [TYPE Module] [SEMANTICS translate, api, package, schedule, dashboard, llm]\n# @BRIEF API routes for LLM-based SQL/dashboard translation management including terminology dictionary CRUD and import.\n# @LAYER API\n# @RELATION DEPENDS_ON -> [SupersetClient]\n# @RELATION DEPENDS_ON -> [SupersetClient]\n# @RELATION DEPENDS_ON -> [SupersetClient]\n# @RELATION DEPENDS_ON -> [SupersetClient]\n# @RELATION DEPENDS_ON -> [SupersetClient]\n# @RELATION DEPENDS_ON -> [SupersetClient]\n# @RELATION DEPENDS_ON -> [SupersetClient]\n# @RATIONALE Snapshot isolation — in-progress runs use config snapshot; config edits affect future runs only.\n# @REJECTED Invalidating in-progress runs on config edit would break scheduled run continuity.\n# @POST Translation job, run, dictionary, correction, preview, and schedule CRUD operations are executed.\n# @PRE API server is running, user is authenticated, database is accessible.\n# @SIDE_EFFECT Creates/modifies DB rows; triggers background translation runs; queries Superset API.\n\n\"\"\"\nTranslate routes package.\n\nRe-exports the shared router and all route handler modules.\nAll sub-modules register their handlers on the router at import time.\n\"\"\"\n\nfrom . import (\n _correction_routes, # noqa: F401 — registers correction handlers\n _dictionary_routes, # noqa: F401 — registers dictionary handlers\n _job_routes, # noqa: F401 — registers job handlers\n _metrics_routes, # noqa: F401 — registers metrics handlers\n _preview_routes, # noqa: F401 — registers preview handlers\n _run_list_routes, # noqa: F401 — registers run list/detail/csv handlers\n _run_routes, # noqa: F401 — registers run handlers\n _schedule_routes, # noqa: F401 — registers schedule handlers\n)\nfrom ._router import router\n\n__all__ = [\n \"router\",\n]\n\n# #endregion TranslateRoutes\n" }, { "contract_id": "TranslateCorrectionRoutesModule", "contract_type": "Module", "file_path": "backend/src/api/routes/translate/_correction_routes.py", "start_line": 1, - "end_line": 94, + "end_line": 100, "tier": "TIER_1", "complexity": 2, "metadata": { @@ -21989,90 +21794,70 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateCorrectionRoutesModule [C:2] [TYPE Module] [SEMANTICS fastapi, translate, api, search]\n# @BRIEF Term correction submission endpoints.\n# @LAYER: API\n\nfrom fastapi import Depends, HTTPException, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_current_user, has_permission\nfrom ....plugins.translate.dictionary import DictionaryManager\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n TermCorrectionBulkSubmit,\n TermCorrectionSubmit,\n)\nfrom ._router import router\n\n# ============================================================\n# Corrections\n# ============================================================\n\n# #region submit_correction [TYPE Function]\n# @BRIEF Submit a term correction from a run result review.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Correction applied with conflict detection.\n@router.post(\"/corrections\")\nasync def submit_correction(\n payload: TermCorrectionSubmit,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Submit a term correction from a run result review.\"\"\"\n logger.reason(f\"submit_correction — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n if not payload.dictionary_id:\n raise HTTPException(status_code=422, detail=\"dictionary_id is required\")\n try:\n result = DictionaryManager.submit_correction(\n db,\n dict_id=payload.dictionary_id,\n source_term=payload.source_term,\n incorrect_target_term=payload.incorrect_target_term,\n corrected_target_term=payload.corrected_target_term,\n origin_run_id=payload.origin_run_id,\n origin_row_key=payload.origin_row_key,\n origin_user_id=current_user.username,\n context_data=payload.context_data,\n usage_notes=payload.usage_notes,\n keep_context=payload.keep_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion submit_correction\n\n\n# #region submit_bulk_corrections [TYPE Function]\n# @BRIEF Submit multiple term corrections atomically.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: All corrections applied or none with conflict list.\n@router.post(\"/corrections/bulk\")\nasync def submit_bulk_corrections(\n payload: TermCorrectionBulkSubmit,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Submit multiple term corrections atomically.\"\"\"\n logger.reason(f\"submit_bulk_corrections — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n corr_list = []\n for c in payload.corrections:\n corr_list.append({\n \"source_term\": c.source_term,\n \"incorrect_target_term\": c.incorrect_target_term,\n \"corrected_target_term\": c.corrected_target_term,\n \"origin_run_id\": c.origin_run_id,\n \"origin_row_key\": c.origin_row_key,\n })\n result = DictionaryManager.submit_bulk_corrections(\n db,\n dict_id=payload.dictionary_id,\n corrections=corr_list,\n origin_user_id=current_user.username,\n )\n if result.get(\"status\") == \"conflicts\":\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=result)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion submit_bulk_corrections\n\n# #endregion TranslateCorrectionRoutesModule\n" + "body": "# #region TranslateCorrectionRoutesModule [C:2] [TYPE Module] [SEMANTICS fastapi, translate, api, search]\n# @BRIEF Term correction submission endpoints.\n# @LAYER: API\n\nfrom fastapi import Depends, HTTPException, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_current_user, has_permission\nfrom ....plugins.translate.dictionary import DictionaryManager\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n TermCorrectionBulkSubmit,\n TermCorrectionSubmit,\n)\nfrom ._router import router\n\n# ============================================================\n# Corrections\n# ============================================================\n\n# #region submit_correction [C:4] [TYPE Function]\n# @BRIEF Submit a term correction from a run result review.\n# @PRE User has translate.dictionary.edit permission.\n# @POST Correction applied with conflict detection.\n# @SIDE_EFFECT Creates/updates DictionaryEntry row; commits.\n# @RELATION DEPENDS_ON -> [DictionaryManager]\n@router.post(\"/corrections\")\nasync def submit_correction(\n payload: TermCorrectionSubmit,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Submit a term correction from a run result review.\"\"\"\n logger.reason(f\"submit_correction — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n if not payload.dictionary_id:\n raise HTTPException(status_code=422, detail=\"dictionary_id is required\")\n try:\n result = DictionaryManager.submit_correction(\n db,\n dict_id=payload.dictionary_id,\n source_term=payload.source_term,\n incorrect_target_term=payload.incorrect_target_term,\n corrected_target_term=payload.corrected_target_term,\n origin_run_id=payload.origin_run_id,\n origin_row_key=payload.origin_row_key,\n origin_user_id=current_user.username,\n context_data=payload.context_data,\n usage_notes=payload.usage_notes,\n keep_context=payload.keep_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion submit_correction\n\n\n# #region submit_bulk_corrections [C:4] [TYPE Function]\n# @BRIEF Submit multiple term corrections atomically.\n# @PRE User has translate.dictionary.edit permission.\n# @POST All corrections applied or none with conflict list.\n# @SIDE_EFFECT Creates/updates DictionaryEntry rows; commits once.\n# @RELATION DEPENDS_ON -> [DictionaryManager]\n# @COMPLEXITY 4\n\n@router.post(\"/corrections/bulk\")\nasync def submit_bulk_corrections(\n payload: TermCorrectionBulkSubmit,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Submit multiple term corrections atomically.\"\"\"\n logger.reason(f\"submit_bulk_corrections — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n corr_list = []\n for c in payload.corrections:\n corr_list.append({\n \"source_term\": c.source_term,\n \"incorrect_target_term\": c.incorrect_target_term,\n \"corrected_target_term\": c.corrected_target_term,\n \"origin_run_id\": c.origin_run_id,\n \"origin_row_key\": c.origin_row_key,\n })\n result = DictionaryManager.submit_bulk_corrections(\n db,\n dict_id=payload.dictionary_id,\n corrections=corr_list,\n origin_user_id=current_user.username,\n )\n if result.get(\"status\") == \"conflicts\":\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=result)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion submit_bulk_corrections\n\n# #endregion TranslateCorrectionRoutesModule\n" }, { "contract_id": "submit_correction", "contract_type": "Function", "file_path": "backend/src/api/routes/translate/_correction_routes.py", "start_line": 23, - "end_line": 55, - "tier": "TIER_1", - "complexity": 1, + "end_line": 57, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Correction applied with conflict detection.", "PRE": "User has translate.dictionary.edit permission.", - "PURPOSE": "Submit a term correction from a run result review." + "PURPOSE": "Submit a term correction from a run result review.", + "SIDE_EFFECT": "Creates/updates DictionaryEntry row; commits." }, - "relations": [], - "schema_warnings": [ + "relations": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } + "source_id": "submit_correction", + "relation_type": "DEPENDS_ON", + "target_id": "DictionaryManager", + "target_ref": "[DictionaryManager]" } ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region submit_correction [TYPE Function]\n# @BRIEF Submit a term correction from a run result review.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Correction applied with conflict detection.\n@router.post(\"/corrections\")\nasync def submit_correction(\n payload: TermCorrectionSubmit,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Submit a term correction from a run result review.\"\"\"\n logger.reason(f\"submit_correction — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n if not payload.dictionary_id:\n raise HTTPException(status_code=422, detail=\"dictionary_id is required\")\n try:\n result = DictionaryManager.submit_correction(\n db,\n dict_id=payload.dictionary_id,\n source_term=payload.source_term,\n incorrect_target_term=payload.incorrect_target_term,\n corrected_target_term=payload.corrected_target_term,\n origin_run_id=payload.origin_run_id,\n origin_row_key=payload.origin_row_key,\n origin_user_id=current_user.username,\n context_data=payload.context_data,\n usage_notes=payload.usage_notes,\n keep_context=payload.keep_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion submit_correction\n" + "body": "# #region submit_correction [C:4] [TYPE Function]\n# @BRIEF Submit a term correction from a run result review.\n# @PRE User has translate.dictionary.edit permission.\n# @POST Correction applied with conflict detection.\n# @SIDE_EFFECT Creates/updates DictionaryEntry row; commits.\n# @RELATION DEPENDS_ON -> [DictionaryManager]\n@router.post(\"/corrections\")\nasync def submit_correction(\n payload: TermCorrectionSubmit,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Submit a term correction from a run result review.\"\"\"\n logger.reason(f\"submit_correction — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n if not payload.dictionary_id:\n raise HTTPException(status_code=422, detail=\"dictionary_id is required\")\n try:\n result = DictionaryManager.submit_correction(\n db,\n dict_id=payload.dictionary_id,\n source_term=payload.source_term,\n incorrect_target_term=payload.incorrect_target_term,\n corrected_target_term=payload.corrected_target_term,\n origin_run_id=payload.origin_run_id,\n origin_row_key=payload.origin_row_key,\n origin_user_id=current_user.username,\n context_data=payload.context_data,\n usage_notes=payload.usage_notes,\n keep_context=payload.keep_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion submit_correction\n" }, { "contract_id": "submit_bulk_corrections", "contract_type": "Function", "file_path": "backend/src/api/routes/translate/_correction_routes.py", - "start_line": 58, - "end_line": 92, - "tier": "TIER_1", - "complexity": 1, + "start_line": 60, + "end_line": 98, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "All corrections applied or none with conflict list.", "PRE": "User has translate.dictionary.edit permission.", - "PURPOSE": "Submit multiple term corrections atomically." + "PURPOSE": "Submit multiple term corrections atomically.", + "SIDE_EFFECT": "Creates/updates DictionaryEntry rows; commits once." }, - "relations": [], - "schema_warnings": [ + "relations": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } + "source_id": "submit_bulk_corrections", + "relation_type": "DEPENDS_ON", + "target_id": "DictionaryManager", + "target_ref": "[DictionaryManager]" } ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region submit_bulk_corrections [TYPE Function]\n# @BRIEF Submit multiple term corrections atomically.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: All corrections applied or none with conflict list.\n@router.post(\"/corrections/bulk\")\nasync def submit_bulk_corrections(\n payload: TermCorrectionBulkSubmit,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Submit multiple term corrections atomically.\"\"\"\n logger.reason(f\"submit_bulk_corrections — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n corr_list = []\n for c in payload.corrections:\n corr_list.append({\n \"source_term\": c.source_term,\n \"incorrect_target_term\": c.incorrect_target_term,\n \"corrected_target_term\": c.corrected_target_term,\n \"origin_run_id\": c.origin_run_id,\n \"origin_row_key\": c.origin_row_key,\n })\n result = DictionaryManager.submit_bulk_corrections(\n db,\n dict_id=payload.dictionary_id,\n corrections=corr_list,\n origin_user_id=current_user.username,\n )\n if result.get(\"status\") == \"conflicts\":\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=result)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion submit_bulk_corrections\n" + "body": "# #region submit_bulk_corrections [C:4] [TYPE Function]\n# @BRIEF Submit multiple term corrections atomically.\n# @PRE User has translate.dictionary.edit permission.\n# @POST All corrections applied or none with conflict list.\n# @SIDE_EFFECT Creates/updates DictionaryEntry rows; commits once.\n# @RELATION DEPENDS_ON -> [DictionaryManager]\n# @COMPLEXITY 4\n\n@router.post(\"/corrections/bulk\")\nasync def submit_bulk_corrections(\n payload: TermCorrectionBulkSubmit,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Submit multiple term corrections atomically.\"\"\"\n logger.reason(f\"submit_bulk_corrections — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n corr_list = []\n for c in payload.corrections:\n corr_list.append({\n \"source_term\": c.source_term,\n \"incorrect_target_term\": c.incorrect_target_term,\n \"corrected_target_term\": c.corrected_target_term,\n \"origin_run_id\": c.origin_run_id,\n \"origin_row_key\": c.origin_row_key,\n })\n result = DictionaryManager.submit_bulk_corrections(\n db,\n dict_id=payload.dictionary_id,\n corrections=corr_list,\n origin_user_id=current_user.username,\n )\n if result.get(\"status\") == \"conflicts\":\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=result)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion submit_bulk_corrections\n" }, { "contract_id": "TranslateDictionaryRoutesModule", "contract_type": "Module", "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 1, - "end_line": 389, + "end_line": 391, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -22094,7 +21879,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateDictionaryRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, search]\n# @BRIEF Terminology Dictionary CRUD, entries management, and import routes.\n# @LAYER: API\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.database import get_db\nfrom ....core.logger import belief_scope, logger\nfrom ....dependencies import get_current_user, has_permission\nfrom ....plugins.translate.dictionary import DictionaryManager\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n DictionaryCreate,\n DictionaryEntryCreate,\n DictionaryImport,\n)\nfrom ._helpers import _dict_to_response, _get_dictionary_entry_counts\nfrom ._router import router\n\n# ============================================================\n# Terminology Dictionaries\n# ============================================================\n\n# #region list_dictionaries [TYPE Function]\n# @BRIEF List all terminology dictionaries.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns list of dictionaries with total count.\n@router.get(\"/dictionaries\")\nasync def list_dictionaries(\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List all terminology dictionaries.\"\"\"\n with belief_scope(\"list_dictionaries\"):\n logger.reason(f\"User: {current_user.username}\")\n dicts, total = DictionaryManager.list_dictionaries(db, page=page, page_size=page_size)\n dict_ids = [d.id for d in dicts]\n counts = _get_dictionary_entry_counts(db, dict_ids) if dict_ids else {}\n items = [_dict_to_response(d, counts.get(d.id, 0)) for d in dicts]\n logger.reflect(\"Dictionaries listed\", {\"total\": total, \"returned\": len(items)})\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n# #endregion list_dictionaries\n\n\n# #region get_dictionary [TYPE Function]\n# @BRIEF Get a single terminology dictionary by ID.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns the dictionary with entry_count.\n@router.get(\"/dictionaries/{dictionary_id}\")\nasync def get_dictionary(\n dictionary_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get a terminology dictionary by ID.\"\"\"\n with belief_scope(\"get_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n d = DictionaryManager.get_dictionary(db, dictionary_id)\n from ....models.translate import DictionaryEntry\n entry_count = db.query(DictionaryEntry.id).filter(DictionaryEntry.dictionary_id == dictionary_id).count()\n logger.reflect(\"Dictionary found\", {\"id\": dictionary_id})\n return _dict_to_response(d, entry_count)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_dictionary\n\n\n# #region create_dictionary [TYPE Function]\n# @BRIEF Create a new terminology dictionary.\n# @PRE: User has translate.dictionary.create permission.\n# @POST: Returns the created dictionary.\n@router.post(\"/dictionaries\", status_code=status.HTTP_201_CREATED)\nasync def create_dictionary(\n payload: DictionaryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"CREATE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Create a new terminology dictionary.\"\"\"\n with belief_scope(\"create_dictionary\"):\n logger.reason(f\"User: {current_user.username}\")\n d = DictionaryManager.create_dictionary(\n db,\n name=payload.name,\n source_dialect=payload.source_dialect,\n target_dialect=payload.target_dialect,\n created_by=current_user.username,\n description=payload.description,\n is_active=payload.is_active,\n )\n logger.reflect(\"Dictionary created\", {\"id\": d.id})\n return _dict_to_response(d, 0)\n# #endregion create_dictionary\n\n\n# #region update_dictionary [TYPE Function]\n# @BRIEF Update an existing terminology dictionary.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Returns the updated dictionary.\n@router.put(\"/dictionaries/{dictionary_id}\")\nasync def update_dictionary(\n dictionary_id: str,\n payload: DictionaryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Update a terminology dictionary.\"\"\"\n with belief_scope(\"update_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n d = DictionaryManager.update_dictionary(\n db,\n dict_id=dictionary_id,\n name=payload.name,\n description=payload.description,\n source_dialect=payload.source_dialect,\n target_dialect=payload.target_dialect,\n is_active=payload.is_active,\n )\n from ....models.translate import DictionaryEntry\n entry_count = db.query(DictionaryEntry.id).filter(DictionaryEntry.dictionary_id == dictionary_id).count()\n logger.reflect(\"Dictionary updated\", {\"id\": dictionary_id})\n return _dict_to_response(d, entry_count)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion update_dictionary\n\n\n# #region delete_dictionary [TYPE Function]\n# @BRIEF Delete a terminology dictionary, blocked if attached to active/scheduled jobs.\n# @PRE: User has translate.dictionary.delete permission.\n# @POST: Dictionary is deleted.\n@router.delete(\"/dictionaries/{dictionary_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_dictionary(\n dictionary_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"DELETE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Delete a terminology dictionary.\"\"\"\n with belief_scope(\"delete_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n DictionaryManager.delete_dictionary(db, dictionary_id)\n logger.reflect(\"Dictionary deleted\", {\"id\": dictionary_id})\n return None\n except ValueError as e:\n if \"active/scheduled\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_dictionary\n\n\n# ============================================================\n# Dictionary Entries CRUD\n# ============================================================\n\n# #region list_dictionary_entries [TYPE Function]\n# @BRIEF List entries for a dictionary, optionally filtered by language pair.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns paginated list of entries with language pair fields.\n@router.get(\"/dictionaries/{dictionary_id}/entries\")\nasync def list_dictionary_entries(\n dictionary_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(50, ge=1, le=500),\n source_language: str | None = Query(None, description=\"Filter by BCP-47 source language\"),\n target_language: str | None = Query(None, description=\"Filter by BCP-47 target language\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List entries for a dictionary.\"\"\"\n with belief_scope(\"list_dictionary_entries\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n entries, total = DictionaryManager.list_entries(db, dictionary_id, page=page, page_size=page_size)\n # Apply language pair filtering\n if source_language or target_language:\n filtered = []\n for e in entries:\n if source_language and e.source_language != source_language and e.source_language != \"und\":\n continue\n if target_language and e.target_language != target_language:\n continue\n filtered.append(e)\n entries = filtered\n total = len(filtered)\n items = [\n {\n \"id\": e.id,\n \"dictionary_id\": e.dictionary_id,\n \"source_term\": e.source_term,\n \"source_term_normalized\": e.source_term_normalized,\n \"target_term\": e.target_term,\n \"source_language\": e.source_language,\n \"target_language\": e.target_language,\n \"context_notes\": e.context_notes,\n \"context_data\": e.context_data,\n \"usage_notes\": e.usage_notes,\n \"has_context\": e.has_context,\n \"context_source\": e.context_source,\n \"origin_source_language\": e.origin_source_language,\n \"created_at\": e.created_at,\n \"updated_at\": e.updated_at,\n }\n for e in entries\n ]\n logger.reflect(\"Entries listed\", {\"dict_id\": dictionary_id, \"total\": total, \"source_language\": source_language, \"target_language\": target_language})\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion list_dictionary_entries\n\n\n# #region add_dictionary_entry [TYPE Function]\n# @BRIEF Add a new entry to a dictionary.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is created.\n@router.post(\"/dictionaries/{dictionary_id}/entries\", status_code=status.HTTP_201_CREATED)\nasync def add_dictionary_entry(\n dictionary_id: str,\n payload: DictionaryEntryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Add a new entry to a dictionary.\"\"\"\n with belief_scope(\"add_dictionary_entry\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n entry = DictionaryManager.add_entry(\n db, dictionary_id,\n source_term=payload.source_term,\n target_term=payload.target_term,\n context_notes=payload.context_notes,\n source_language=payload.source_language,\n target_language=payload.target_language,\n )\n logger.reflect(\"Entry added\", {\"entry_id\": entry.id})\n return {\n \"id\": entry.id,\n \"dictionary_id\": entry.dictionary_id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"origin_source_language\": entry.origin_source_language,\n \"created_at\": entry.created_at,\n \"updated_at\": entry.updated_at,\n }\n except ValueError as e:\n if \"already exists\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion add_dictionary_entry\n\n\n# #region edit_dictionary_entry [TYPE Function]\n# @BRIEF Update an existing dictionary entry.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is updated.\n@router.put(\"/dictionaries/{dictionary_id}/entries/{entry_id}\")\nasync def edit_dictionary_entry(\n dictionary_id: str,\n entry_id: str,\n payload: DictionaryEntryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Update an existing dictionary entry.\"\"\"\n with belief_scope(\"edit_dictionary_entry\"):\n logger.reason(f\"Entry: {entry_id}, User: {current_user.username}\")\n try:\n entry = DictionaryManager.edit_entry(\n db, entry_id,\n source_term=payload.source_term,\n target_term=payload.target_term,\n context_notes=payload.context_notes,\n source_language=payload.source_language,\n target_language=payload.target_language,\n )\n logger.reflect(\"Entry updated\", {\"entry_id\": entry_id})\n return {\n \"id\": entry.id,\n \"dictionary_id\": entry.dictionary_id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"origin_source_language\": entry.origin_source_language,\n \"created_at\": entry.created_at,\n \"updated_at\": entry.updated_at,\n }\n except ValueError as e:\n if \"already exists\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion edit_dictionary_entry\n\n\n# #region delete_dictionary_entry [TYPE Function]\n# @BRIEF Delete a dictionary entry.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is deleted.\n@router.delete(\"/dictionaries/{dictionary_id}/entries/{entry_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_dictionary_entry(\n dictionary_id: str,\n entry_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Delete a dictionary entry.\"\"\"\n with belief_scope(\"delete_dictionary_entry\"):\n logger.reason(f\"Entry: {entry_id}, User: {current_user.username}\")\n try:\n DictionaryManager.delete_entry(db, entry_id)\n logger.reflect(\"Entry deleted\", {\"entry_id\": entry_id})\n return None\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_dictionary_entry\n\n\n# #region import_dictionary_entries [TYPE Function]\n# @BRIEF Import entries into a terminology dictionary from CSV/TSV content.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entries are imported per conflict mode.\n@router.post(\"/dictionaries/{dictionary_id}/import\")\nasync def import_dictionary_entries(\n dictionary_id: str,\n payload: DictionaryImport,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Import entries into a terminology dictionary from CSV/TSV content.\"\"\"\n with belief_scope(\"import_dictionary_entries\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n\n if payload.on_conflict not in (\"overwrite\", \"keep_existing\", \"cancel\"):\n raise HTTPException(\n status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,\n detail=\"on_conflict must be 'overwrite', 'keep_existing', or 'cancel'\",\n )\n\n try:\n result = DictionaryManager.import_entries(\n db,\n dict_id=dictionary_id,\n content=payload.content,\n delimiter=payload.delimiter,\n on_conflict=payload.on_conflict,\n preview_only=payload.preview_only,\n default_source_language=payload.default_source_language,\n default_target_language=payload.default_target_language,\n )\n logger.reflect(\"Import completed\", {\n \"dict_id\": dictionary_id,\n \"total\": result[\"total\"],\n \"errors\": len(result[\"errors\"]),\n })\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion import_dictionary_entries\n\n# #endregion TranslateDictionaryRoutesModule\n" + "body": "# #region TranslateDictionaryRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, search]\n# @BRIEF Terminology Dictionary CRUD, entries management, and import routes.\n# @LAYER: API\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.database import get_db\nfrom ....core.logger import belief_scope, logger\nfrom ....dependencies import get_current_user, has_permission\nfrom ....plugins.translate.dictionary import DictionaryManager\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n DictionaryCreate,\n DictionaryEntryCreate,\n DictionaryImport,\n)\nfrom ._helpers import _dict_to_response, _get_dictionary_entry_counts\nfrom ._router import router\n\n# ============================================================\n# Terminology Dictionaries\n# ============================================================\n\n# #region list_dictionaries [C:4] [TYPE Function]\n# @BRIEF List all terminology dictionaries.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns list of dictionaries with total count.\n@router.get(\"/dictionaries\")\nasync def list_dictionaries(\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List all terminology dictionaries.\"\"\"\n with belief_scope(\"list_dictionaries\"):\n logger.reason(f\"User: {current_user.username}\")\n dicts, total = DictionaryManager.list_dictionaries(db, page=page, page_size=page_size)\n dict_ids = [d.id for d in dicts]\n counts = _get_dictionary_entry_counts(db, dict_ids) if dict_ids else {}\n items = [_dict_to_response(d, counts.get(d.id, 0)) for d in dicts]\n logger.reflect(\"Dictionaries listed\", {\"total\": total, \"returned\": len(items)})\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n# #endregion list_dictionaries\n\n\n# #region get_dictionary [C:4] [TYPE Function]\n# @BRIEF Get a single terminology dictionary by ID.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns the dictionary with entry_count.\n@router.get(\"/dictionaries/{dictionary_id}\")\nasync def get_dictionary(\n dictionary_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get a terminology dictionary by ID.\"\"\"\n with belief_scope(\"get_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n d = DictionaryManager.get_dictionary(db, dictionary_id)\n from ....models.translate import DictionaryEntry\n entry_count = db.query(DictionaryEntry.id).filter(DictionaryEntry.dictionary_id == dictionary_id).count()\n logger.reflect(\"Dictionary found\", {\"id\": dictionary_id})\n return _dict_to_response(d, entry_count)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_dictionary\n\n\n# #region create_dictionary [C:4] [TYPE Function]\n# @BRIEF Create a new terminology dictionary.\n# @PRE: User has translate.dictionary.create permission.\n# @POST: Returns the created dictionary.\n@router.post(\"/dictionaries\", status_code=status.HTTP_201_CREATED)\nasync def create_dictionary(\n payload: DictionaryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"CREATE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Create a new terminology dictionary.\"\"\"\n with belief_scope(\"create_dictionary\"):\n logger.reason(f\"User: {current_user.username}\")\n d = DictionaryManager.create_dictionary(\n db,\n name=payload.name,\n source_dialect=payload.source_dialect,\n target_dialect=payload.target_dialect,\n created_by=current_user.username,\n description=payload.description,\n is_active=payload.is_active,\n )\n logger.reflect(\"Dictionary created\", {\"id\": d.id})\n return _dict_to_response(d, 0)\n# #endregion create_dictionary\n\n\n# #region update_dictionary [C:4] [TYPE Function]\n# @BRIEF Update an existing terminology dictionary.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Returns the updated dictionary.\n@router.put(\"/dictionaries/{dictionary_id}\")\nasync def update_dictionary(\n dictionary_id: str,\n payload: DictionaryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Update a terminology dictionary.\"\"\"\n with belief_scope(\"update_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n d = DictionaryManager.update_dictionary(\n db,\n dict_id=dictionary_id,\n name=payload.name,\n description=payload.description,\n source_dialect=payload.source_dialect,\n target_dialect=payload.target_dialect,\n is_active=payload.is_active,\n )\n from ....models.translate import DictionaryEntry\n entry_count = db.query(DictionaryEntry.id).filter(DictionaryEntry.dictionary_id == dictionary_id).count()\n logger.reflect(\"Dictionary updated\", {\"id\": dictionary_id})\n return _dict_to_response(d, entry_count)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion update_dictionary\n\n\n# #region delete_dictionary [C:4] [TYPE Function]\n# @BRIEF Delete a terminology dictionary, blocked if attached to active/scheduled jobs.\n# @PRE: User has translate.dictionary.delete permission.\n# @POST: Dictionary is deleted.\n@router.delete(\"/dictionaries/{dictionary_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_dictionary(\n dictionary_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"DELETE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Delete a terminology dictionary.\"\"\"\n with belief_scope(\"delete_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n DictionaryManager.delete_dictionary(db, dictionary_id)\n logger.reflect(\"Dictionary deleted\", {\"id\": dictionary_id})\n return None\n except ValueError as e:\n if \"active/scheduled\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_dictionary\n\n\n# ============================================================\n# Dictionary Entries CRUD\n# ============================================================\n\n# #region list_dictionary_entries [C:4] [TYPE Function]\n# @BRIEF List entries for a dictionary, optionally filtered by language pair.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns paginated list of entries with language pair fields.\n@router.get(\"/dictionaries/{dictionary_id}/entries\")\nasync def list_dictionary_entries(\n dictionary_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(50, ge=1, le=500),\n source_language: str | None = Query(None, description=\"Filter by BCP-47 source language\"),\n target_language: str | None = Query(None, description=\"Filter by BCP-47 target language\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List entries for a dictionary.\"\"\"\n with belief_scope(\"list_dictionary_entries\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n entries, total = DictionaryManager.list_entries(db, dictionary_id, page=page, page_size=page_size)\n # Apply language pair filtering\n if source_language or target_language:\n filtered = []\n for e in entries:\n if source_language and e.source_language != source_language and e.source_language != \"und\":\n continue\n if target_language and e.target_language != target_language:\n continue\n filtered.append(e)\n entries = filtered\n total = len(filtered)\n items = [\n {\n \"id\": e.id,\n \"dictionary_id\": e.dictionary_id,\n \"source_term\": e.source_term,\n \"source_term_normalized\": e.source_term_normalized,\n \"target_term\": e.target_term,\n \"source_language\": e.source_language,\n \"target_language\": e.target_language,\n \"context_notes\": e.context_notes,\n \"context_data\": e.context_data,\n \"usage_notes\": e.usage_notes,\n \"has_context\": e.has_context,\n \"context_source\": e.context_source,\n \"origin_source_language\": e.origin_source_language,\n \"created_at\": e.created_at,\n \"updated_at\": e.updated_at,\n }\n for e in entries\n ]\n logger.reflect(\"Entries listed\", {\"dict_id\": dictionary_id, \"total\": total, \"source_language\": source_language, \"target_language\": target_language})\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion list_dictionary_entries\n\n\n# #region add_dictionary_entry [C:4] [TYPE Function]\n# @BRIEF Add a new entry to a dictionary.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is created.\n@router.post(\"/dictionaries/{dictionary_id}/entries\", status_code=status.HTTP_201_CREATED)\nasync def add_dictionary_entry(\n dictionary_id: str,\n payload: DictionaryEntryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Add a new entry to a dictionary.\"\"\"\n with belief_scope(\"add_dictionary_entry\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n entry = DictionaryManager.add_entry(\n db, dictionary_id,\n source_term=payload.source_term,\n target_term=payload.target_term,\n context_notes=payload.context_notes,\n source_language=payload.source_language,\n target_language=payload.target_language,\n )\n logger.reflect(\"Entry added\", {\"entry_id\": entry.id})\n return {\n \"id\": entry.id,\n \"dictionary_id\": entry.dictionary_id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"origin_source_language\": entry.origin_source_language,\n \"created_at\": entry.created_at,\n \"updated_at\": entry.updated_at,\n }\n except ValueError as e:\n if \"already exists\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion add_dictionary_entry\n\n\n# #region edit_dictionary_entry [C:4] [TYPE Function]\n# @BRIEF Update an existing dictionary entry.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is updated.\n@router.put(\"/dictionaries/{dictionary_id}/entries/{entry_id}\")\nasync def edit_dictionary_entry(\n dictionary_id: str,\n entry_id: str,\n payload: DictionaryEntryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Update an existing dictionary entry.\"\"\"\n with belief_scope(\"edit_dictionary_entry\"):\n logger.reason(f\"Entry: {entry_id}, User: {current_user.username}\")\n try:\n entry = DictionaryManager.edit_entry(\n db, entry_id,\n source_term=payload.source_term,\n target_term=payload.target_term,\n context_notes=payload.context_notes,\n source_language=payload.source_language,\n target_language=payload.target_language,\n )\n logger.reflect(\"Entry updated\", {\"entry_id\": entry_id})\n return {\n \"id\": entry.id,\n \"dictionary_id\": entry.dictionary_id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"origin_source_language\": entry.origin_source_language,\n \"created_at\": entry.created_at,\n \"updated_at\": entry.updated_at,\n }\n except ValueError as e:\n if \"already exists\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion edit_dictionary_entry\n\n\n# #region delete_dictionary_entry [C:4] [TYPE Function]\n# @BRIEF Delete a dictionary entry.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is deleted.\n@router.delete(\"/dictionaries/{dictionary_id}/entries/{entry_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_dictionary_entry(\n dictionary_id: str,\n entry_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Delete a dictionary entry.\"\"\"\n with belief_scope(\"delete_dictionary_entry\"):\n logger.reason(f\"Entry: {entry_id}, User: {current_user.username}\")\n try:\n DictionaryManager.delete_entry(db, entry_id)\n logger.reflect(\"Entry deleted\", {\"entry_id\": entry_id})\n return None\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_dictionary_entry\n\n\n# #region import_dictionary_entries [C:4] [TYPE Function]\n# @BRIEF Import entries into a terminology dictionary from CSV/TSV content.\n# @PRE User has translate.dictionary.edit permission.\n# @POST Entries are imported per conflict mode.\n# @COMPLEXITY 4\n\n@router.post(\"/dictionaries/{dictionary_id}/import\")\nasync def import_dictionary_entries(\n dictionary_id: str,\n payload: DictionaryImport,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Import entries into a terminology dictionary from CSV/TSV content.\"\"\"\n with belief_scope(\"import_dictionary_entries\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n\n if payload.on_conflict not in (\"overwrite\", \"keep_existing\", \"cancel\"):\n raise HTTPException(\n status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,\n detail=\"on_conflict must be 'overwrite', 'keep_existing', or 'cancel'\",\n )\n\n try:\n result = DictionaryManager.import_entries(\n db,\n dict_id=dictionary_id,\n content=payload.content,\n delimiter=payload.delimiter,\n on_conflict=payload.on_conflict,\n preview_only=payload.preview_only,\n default_source_language=payload.default_source_language,\n default_target_language=payload.default_target_language,\n )\n logger.reflect(\"Import completed\", {\n \"dict_id\": dictionary_id,\n \"total\": result[\"total\"],\n \"errors\": len(result[\"errors\"]),\n })\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion import_dictionary_entries\n\n# #endregion TranslateDictionaryRoutesModule\n" }, { "contract_id": "list_dictionaries", @@ -22102,9 +21887,10 @@ "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 25, "end_line": 46, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns list of dictionaries with total count.", "PRE": "User has translate.dictionary.view permission.", "PURPOSE": "List all terminology dictionaries." @@ -22112,27 +21898,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region list_dictionaries [TYPE Function]\n# @BRIEF List all terminology dictionaries.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns list of dictionaries with total count.\n@router.get(\"/dictionaries\")\nasync def list_dictionaries(\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List all terminology dictionaries.\"\"\"\n with belief_scope(\"list_dictionaries\"):\n logger.reason(f\"User: {current_user.username}\")\n dicts, total = DictionaryManager.list_dictionaries(db, page=page, page_size=page_size)\n dict_ids = [d.id for d in dicts]\n counts = _get_dictionary_entry_counts(db, dict_ids) if dict_ids else {}\n items = [_dict_to_response(d, counts.get(d.id, 0)) for d in dicts]\n logger.reflect(\"Dictionaries listed\", {\"total\": total, \"returned\": len(items)})\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n# #endregion list_dictionaries\n" + "body": "# #region list_dictionaries [C:4] [TYPE Function]\n# @BRIEF List all terminology dictionaries.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns list of dictionaries with total count.\n@router.get(\"/dictionaries\")\nasync def list_dictionaries(\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List all terminology dictionaries.\"\"\"\n with belief_scope(\"list_dictionaries\"):\n logger.reason(f\"User: {current_user.username}\")\n dicts, total = DictionaryManager.list_dictionaries(db, page=page, page_size=page_size)\n dict_ids = [d.id for d in dicts]\n counts = _get_dictionary_entry_counts(db, dict_ids) if dict_ids else {}\n items = [_dict_to_response(d, counts.get(d.id, 0)) for d in dicts]\n logger.reflect(\"Dictionaries listed\", {\"total\": total, \"returned\": len(items)})\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n# #endregion list_dictionaries\n" }, { "contract_id": "get_dictionary", @@ -22140,9 +21926,10 @@ "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 49, "end_line": 71, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the dictionary with entry_count.", "PRE": "User has translate.dictionary.view permission.", "PURPOSE": "Get a single terminology dictionary by ID." @@ -22150,27 +21937,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_dictionary [TYPE Function]\n# @BRIEF Get a single terminology dictionary by ID.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns the dictionary with entry_count.\n@router.get(\"/dictionaries/{dictionary_id}\")\nasync def get_dictionary(\n dictionary_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get a terminology dictionary by ID.\"\"\"\n with belief_scope(\"get_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n d = DictionaryManager.get_dictionary(db, dictionary_id)\n from ....models.translate import DictionaryEntry\n entry_count = db.query(DictionaryEntry.id).filter(DictionaryEntry.dictionary_id == dictionary_id).count()\n logger.reflect(\"Dictionary found\", {\"id\": dictionary_id})\n return _dict_to_response(d, entry_count)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_dictionary\n" + "body": "# #region get_dictionary [C:4] [TYPE Function]\n# @BRIEF Get a single terminology dictionary by ID.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns the dictionary with entry_count.\n@router.get(\"/dictionaries/{dictionary_id}\")\nasync def get_dictionary(\n dictionary_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get a terminology dictionary by ID.\"\"\"\n with belief_scope(\"get_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n d = DictionaryManager.get_dictionary(db, dictionary_id)\n from ....models.translate import DictionaryEntry\n entry_count = db.query(DictionaryEntry.id).filter(DictionaryEntry.dictionary_id == dictionary_id).count()\n logger.reflect(\"Dictionary found\", {\"id\": dictionary_id})\n return _dict_to_response(d, entry_count)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_dictionary\n" }, { "contract_id": "create_dictionary", @@ -22178,9 +21965,10 @@ "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 74, "end_line": 99, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the created dictionary.", "PRE": "User has translate.dictionary.create permission.", "PURPOSE": "Create a new terminology dictionary." @@ -22188,27 +21976,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region create_dictionary [TYPE Function]\n# @BRIEF Create a new terminology dictionary.\n# @PRE: User has translate.dictionary.create permission.\n# @POST: Returns the created dictionary.\n@router.post(\"/dictionaries\", status_code=status.HTTP_201_CREATED)\nasync def create_dictionary(\n payload: DictionaryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"CREATE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Create a new terminology dictionary.\"\"\"\n with belief_scope(\"create_dictionary\"):\n logger.reason(f\"User: {current_user.username}\")\n d = DictionaryManager.create_dictionary(\n db,\n name=payload.name,\n source_dialect=payload.source_dialect,\n target_dialect=payload.target_dialect,\n created_by=current_user.username,\n description=payload.description,\n is_active=payload.is_active,\n )\n logger.reflect(\"Dictionary created\", {\"id\": d.id})\n return _dict_to_response(d, 0)\n# #endregion create_dictionary\n" + "body": "# #region create_dictionary [C:4] [TYPE Function]\n# @BRIEF Create a new terminology dictionary.\n# @PRE: User has translate.dictionary.create permission.\n# @POST: Returns the created dictionary.\n@router.post(\"/dictionaries\", status_code=status.HTTP_201_CREATED)\nasync def create_dictionary(\n payload: DictionaryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"CREATE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Create a new terminology dictionary.\"\"\"\n with belief_scope(\"create_dictionary\"):\n logger.reason(f\"User: {current_user.username}\")\n d = DictionaryManager.create_dictionary(\n db,\n name=payload.name,\n source_dialect=payload.source_dialect,\n target_dialect=payload.target_dialect,\n created_by=current_user.username,\n description=payload.description,\n is_active=payload.is_active,\n )\n logger.reflect(\"Dictionary created\", {\"id\": d.id})\n return _dict_to_response(d, 0)\n# #endregion create_dictionary\n" }, { "contract_id": "update_dictionary", @@ -22216,9 +22004,10 @@ "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 102, "end_line": 133, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the updated dictionary.", "PRE": "User has translate.dictionary.edit permission.", "PURPOSE": "Update an existing terminology dictionary." @@ -22226,27 +22015,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region update_dictionary [TYPE Function]\n# @BRIEF Update an existing terminology dictionary.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Returns the updated dictionary.\n@router.put(\"/dictionaries/{dictionary_id}\")\nasync def update_dictionary(\n dictionary_id: str,\n payload: DictionaryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Update a terminology dictionary.\"\"\"\n with belief_scope(\"update_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n d = DictionaryManager.update_dictionary(\n db,\n dict_id=dictionary_id,\n name=payload.name,\n description=payload.description,\n source_dialect=payload.source_dialect,\n target_dialect=payload.target_dialect,\n is_active=payload.is_active,\n )\n from ....models.translate import DictionaryEntry\n entry_count = db.query(DictionaryEntry.id).filter(DictionaryEntry.dictionary_id == dictionary_id).count()\n logger.reflect(\"Dictionary updated\", {\"id\": dictionary_id})\n return _dict_to_response(d, entry_count)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion update_dictionary\n" + "body": "# #region update_dictionary [C:4] [TYPE Function]\n# @BRIEF Update an existing terminology dictionary.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Returns the updated dictionary.\n@router.put(\"/dictionaries/{dictionary_id}\")\nasync def update_dictionary(\n dictionary_id: str,\n payload: DictionaryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Update a terminology dictionary.\"\"\"\n with belief_scope(\"update_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n d = DictionaryManager.update_dictionary(\n db,\n dict_id=dictionary_id,\n name=payload.name,\n description=payload.description,\n source_dialect=payload.source_dialect,\n target_dialect=payload.target_dialect,\n is_active=payload.is_active,\n )\n from ....models.translate import DictionaryEntry\n entry_count = db.query(DictionaryEntry.id).filter(DictionaryEntry.dictionary_id == dictionary_id).count()\n logger.reflect(\"Dictionary updated\", {\"id\": dictionary_id})\n return _dict_to_response(d, entry_count)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion update_dictionary\n" }, { "contract_id": "delete_dictionary", @@ -22254,9 +22043,10 @@ "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 136, "end_line": 158, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Dictionary is deleted.", "PRE": "User has translate.dictionary.delete permission.", "PURPOSE": "Delete a terminology dictionary, blocked if attached to active/scheduled jobs." @@ -22264,27 +22054,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region delete_dictionary [TYPE Function]\n# @BRIEF Delete a terminology dictionary, blocked if attached to active/scheduled jobs.\n# @PRE: User has translate.dictionary.delete permission.\n# @POST: Dictionary is deleted.\n@router.delete(\"/dictionaries/{dictionary_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_dictionary(\n dictionary_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"DELETE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Delete a terminology dictionary.\"\"\"\n with belief_scope(\"delete_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n DictionaryManager.delete_dictionary(db, dictionary_id)\n logger.reflect(\"Dictionary deleted\", {\"id\": dictionary_id})\n return None\n except ValueError as e:\n if \"active/scheduled\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_dictionary\n" + "body": "# #region delete_dictionary [C:4] [TYPE Function]\n# @BRIEF Delete a terminology dictionary, blocked if attached to active/scheduled jobs.\n# @PRE: User has translate.dictionary.delete permission.\n# @POST: Dictionary is deleted.\n@router.delete(\"/dictionaries/{dictionary_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_dictionary(\n dictionary_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"DELETE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Delete a terminology dictionary.\"\"\"\n with belief_scope(\"delete_dictionary\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n DictionaryManager.delete_dictionary(db, dictionary_id)\n logger.reflect(\"Dictionary deleted\", {\"id\": dictionary_id})\n return None\n except ValueError as e:\n if \"active/scheduled\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_dictionary\n" }, { "contract_id": "list_dictionary_entries", @@ -22292,9 +22082,10 @@ "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 165, "end_line": 220, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns paginated list of entries with language pair fields.", "PRE": "User has translate.dictionary.view permission.", "PURPOSE": "List entries for a dictionary, optionally filtered by language pair." @@ -22302,27 +22093,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region list_dictionary_entries [TYPE Function]\n# @BRIEF List entries for a dictionary, optionally filtered by language pair.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns paginated list of entries with language pair fields.\n@router.get(\"/dictionaries/{dictionary_id}/entries\")\nasync def list_dictionary_entries(\n dictionary_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(50, ge=1, le=500),\n source_language: str | None = Query(None, description=\"Filter by BCP-47 source language\"),\n target_language: str | None = Query(None, description=\"Filter by BCP-47 target language\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List entries for a dictionary.\"\"\"\n with belief_scope(\"list_dictionary_entries\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n entries, total = DictionaryManager.list_entries(db, dictionary_id, page=page, page_size=page_size)\n # Apply language pair filtering\n if source_language or target_language:\n filtered = []\n for e in entries:\n if source_language and e.source_language != source_language and e.source_language != \"und\":\n continue\n if target_language and e.target_language != target_language:\n continue\n filtered.append(e)\n entries = filtered\n total = len(filtered)\n items = [\n {\n \"id\": e.id,\n \"dictionary_id\": e.dictionary_id,\n \"source_term\": e.source_term,\n \"source_term_normalized\": e.source_term_normalized,\n \"target_term\": e.target_term,\n \"source_language\": e.source_language,\n \"target_language\": e.target_language,\n \"context_notes\": e.context_notes,\n \"context_data\": e.context_data,\n \"usage_notes\": e.usage_notes,\n \"has_context\": e.has_context,\n \"context_source\": e.context_source,\n \"origin_source_language\": e.origin_source_language,\n \"created_at\": e.created_at,\n \"updated_at\": e.updated_at,\n }\n for e in entries\n ]\n logger.reflect(\"Entries listed\", {\"dict_id\": dictionary_id, \"total\": total, \"source_language\": source_language, \"target_language\": target_language})\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion list_dictionary_entries\n" + "body": "# #region list_dictionary_entries [C:4] [TYPE Function]\n# @BRIEF List entries for a dictionary, optionally filtered by language pair.\n# @PRE: User has translate.dictionary.view permission.\n# @POST: Returns paginated list of entries with language pair fields.\n@router.get(\"/dictionaries/{dictionary_id}/entries\")\nasync def list_dictionary_entries(\n dictionary_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(50, ge=1, le=500),\n source_language: str | None = Query(None, description=\"Filter by BCP-47 source language\"),\n target_language: str | None = Query(None, description=\"Filter by BCP-47 target language\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List entries for a dictionary.\"\"\"\n with belief_scope(\"list_dictionary_entries\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n entries, total = DictionaryManager.list_entries(db, dictionary_id, page=page, page_size=page_size)\n # Apply language pair filtering\n if source_language or target_language:\n filtered = []\n for e in entries:\n if source_language and e.source_language != source_language and e.source_language != \"und\":\n continue\n if target_language and e.target_language != target_language:\n continue\n filtered.append(e)\n entries = filtered\n total = len(filtered)\n items = [\n {\n \"id\": e.id,\n \"dictionary_id\": e.dictionary_id,\n \"source_term\": e.source_term,\n \"source_term_normalized\": e.source_term_normalized,\n \"target_term\": e.target_term,\n \"source_language\": e.source_language,\n \"target_language\": e.target_language,\n \"context_notes\": e.context_notes,\n \"context_data\": e.context_data,\n \"usage_notes\": e.usage_notes,\n \"has_context\": e.has_context,\n \"context_source\": e.context_source,\n \"origin_source_language\": e.origin_source_language,\n \"created_at\": e.created_at,\n \"updated_at\": e.updated_at,\n }\n for e in entries\n ]\n logger.reflect(\"Entries listed\", {\"dict_id\": dictionary_id, \"total\": total, \"source_language\": source_language, \"target_language\": target_language})\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion list_dictionary_entries\n" }, { "contract_id": "add_dictionary_entry", @@ -22330,9 +22121,10 @@ "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 223, "end_line": 269, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Entry is created.", "PRE": "User has translate.dictionary.edit permission.", "PURPOSE": "Add a new entry to a dictionary." @@ -22340,27 +22132,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region add_dictionary_entry [TYPE Function]\n# @BRIEF Add a new entry to a dictionary.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is created.\n@router.post(\"/dictionaries/{dictionary_id}/entries\", status_code=status.HTTP_201_CREATED)\nasync def add_dictionary_entry(\n dictionary_id: str,\n payload: DictionaryEntryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Add a new entry to a dictionary.\"\"\"\n with belief_scope(\"add_dictionary_entry\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n entry = DictionaryManager.add_entry(\n db, dictionary_id,\n source_term=payload.source_term,\n target_term=payload.target_term,\n context_notes=payload.context_notes,\n source_language=payload.source_language,\n target_language=payload.target_language,\n )\n logger.reflect(\"Entry added\", {\"entry_id\": entry.id})\n return {\n \"id\": entry.id,\n \"dictionary_id\": entry.dictionary_id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"origin_source_language\": entry.origin_source_language,\n \"created_at\": entry.created_at,\n \"updated_at\": entry.updated_at,\n }\n except ValueError as e:\n if \"already exists\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion add_dictionary_entry\n" + "body": "# #region add_dictionary_entry [C:4] [TYPE Function]\n# @BRIEF Add a new entry to a dictionary.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is created.\n@router.post(\"/dictionaries/{dictionary_id}/entries\", status_code=status.HTTP_201_CREATED)\nasync def add_dictionary_entry(\n dictionary_id: str,\n payload: DictionaryEntryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Add a new entry to a dictionary.\"\"\"\n with belief_scope(\"add_dictionary_entry\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n try:\n entry = DictionaryManager.add_entry(\n db, dictionary_id,\n source_term=payload.source_term,\n target_term=payload.target_term,\n context_notes=payload.context_notes,\n source_language=payload.source_language,\n target_language=payload.target_language,\n )\n logger.reflect(\"Entry added\", {\"entry_id\": entry.id})\n return {\n \"id\": entry.id,\n \"dictionary_id\": entry.dictionary_id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"origin_source_language\": entry.origin_source_language,\n \"created_at\": entry.created_at,\n \"updated_at\": entry.updated_at,\n }\n except ValueError as e:\n if \"already exists\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion add_dictionary_entry\n" }, { "contract_id": "edit_dictionary_entry", @@ -22368,9 +22160,10 @@ "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 272, "end_line": 319, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Entry is updated.", "PRE": "User has translate.dictionary.edit permission.", "PURPOSE": "Update an existing dictionary entry." @@ -22378,27 +22171,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region edit_dictionary_entry [TYPE Function]\n# @BRIEF Update an existing dictionary entry.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is updated.\n@router.put(\"/dictionaries/{dictionary_id}/entries/{entry_id}\")\nasync def edit_dictionary_entry(\n dictionary_id: str,\n entry_id: str,\n payload: DictionaryEntryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Update an existing dictionary entry.\"\"\"\n with belief_scope(\"edit_dictionary_entry\"):\n logger.reason(f\"Entry: {entry_id}, User: {current_user.username}\")\n try:\n entry = DictionaryManager.edit_entry(\n db, entry_id,\n source_term=payload.source_term,\n target_term=payload.target_term,\n context_notes=payload.context_notes,\n source_language=payload.source_language,\n target_language=payload.target_language,\n )\n logger.reflect(\"Entry updated\", {\"entry_id\": entry_id})\n return {\n \"id\": entry.id,\n \"dictionary_id\": entry.dictionary_id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"origin_source_language\": entry.origin_source_language,\n \"created_at\": entry.created_at,\n \"updated_at\": entry.updated_at,\n }\n except ValueError as e:\n if \"already exists\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion edit_dictionary_entry\n" + "body": "# #region edit_dictionary_entry [C:4] [TYPE Function]\n# @BRIEF Update an existing dictionary entry.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is updated.\n@router.put(\"/dictionaries/{dictionary_id}/entries/{entry_id}\")\nasync def edit_dictionary_entry(\n dictionary_id: str,\n entry_id: str,\n payload: DictionaryEntryCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Update an existing dictionary entry.\"\"\"\n with belief_scope(\"edit_dictionary_entry\"):\n logger.reason(f\"Entry: {entry_id}, User: {current_user.username}\")\n try:\n entry = DictionaryManager.edit_entry(\n db, entry_id,\n source_term=payload.source_term,\n target_term=payload.target_term,\n context_notes=payload.context_notes,\n source_language=payload.source_language,\n target_language=payload.target_language,\n )\n logger.reflect(\"Entry updated\", {\"entry_id\": entry_id})\n return {\n \"id\": entry.id,\n \"dictionary_id\": entry.dictionary_id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"origin_source_language\": entry.origin_source_language,\n \"created_at\": entry.created_at,\n \"updated_at\": entry.updated_at,\n }\n except ValueError as e:\n if \"already exists\" in str(e):\n raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(e))\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion edit_dictionary_entry\n" }, { "contract_id": "delete_dictionary_entry", @@ -22406,9 +22199,10 @@ "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 322, "end_line": 343, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Entry is deleted.", "PRE": "User has translate.dictionary.edit permission.", "PURPOSE": "Delete a dictionary entry." @@ -22416,37 +22210,38 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region delete_dictionary_entry [TYPE Function]\n# @BRIEF Delete a dictionary entry.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is deleted.\n@router.delete(\"/dictionaries/{dictionary_id}/entries/{entry_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_dictionary_entry(\n dictionary_id: str,\n entry_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Delete a dictionary entry.\"\"\"\n with belief_scope(\"delete_dictionary_entry\"):\n logger.reason(f\"Entry: {entry_id}, User: {current_user.username}\")\n try:\n DictionaryManager.delete_entry(db, entry_id)\n logger.reflect(\"Entry deleted\", {\"entry_id\": entry_id})\n return None\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_dictionary_entry\n" + "body": "# #region delete_dictionary_entry [C:4] [TYPE Function]\n# @BRIEF Delete a dictionary entry.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entry is deleted.\n@router.delete(\"/dictionaries/{dictionary_id}/entries/{entry_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_dictionary_entry(\n dictionary_id: str,\n entry_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Delete a dictionary entry.\"\"\"\n with belief_scope(\"delete_dictionary_entry\"):\n logger.reason(f\"Entry: {entry_id}, User: {current_user.username}\")\n try:\n DictionaryManager.delete_entry(db, entry_id)\n logger.reflect(\"Entry deleted\", {\"entry_id\": entry_id})\n return None\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_dictionary_entry\n" }, { "contract_id": "import_dictionary_entries", "contract_type": "Function", "file_path": "backend/src/api/routes/translate/_dictionary_routes.py", "start_line": 346, - "end_line": 387, - "tier": "TIER_1", - "complexity": 1, + "end_line": 389, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "Entries are imported per conflict mode.", "PRE": "User has translate.dictionary.edit permission.", "PURPOSE": "Import entries into a terminology dictionary from CSV/TSV content." @@ -22454,27 +22249,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region import_dictionary_entries [TYPE Function]\n# @BRIEF Import entries into a terminology dictionary from CSV/TSV content.\n# @PRE: User has translate.dictionary.edit permission.\n# @POST: Entries are imported per conflict mode.\n@router.post(\"/dictionaries/{dictionary_id}/import\")\nasync def import_dictionary_entries(\n dictionary_id: str,\n payload: DictionaryImport,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Import entries into a terminology dictionary from CSV/TSV content.\"\"\"\n with belief_scope(\"import_dictionary_entries\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n\n if payload.on_conflict not in (\"overwrite\", \"keep_existing\", \"cancel\"):\n raise HTTPException(\n status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,\n detail=\"on_conflict must be 'overwrite', 'keep_existing', or 'cancel'\",\n )\n\n try:\n result = DictionaryManager.import_entries(\n db,\n dict_id=dictionary_id,\n content=payload.content,\n delimiter=payload.delimiter,\n on_conflict=payload.on_conflict,\n preview_only=payload.preview_only,\n default_source_language=payload.default_source_language,\n default_target_language=payload.default_target_language,\n )\n logger.reflect(\"Import completed\", {\n \"dict_id\": dictionary_id,\n \"total\": result[\"total\"],\n \"errors\": len(result[\"errors\"]),\n })\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion import_dictionary_entries\n" + "body": "# #region import_dictionary_entries [C:4] [TYPE Function]\n# @BRIEF Import entries into a terminology dictionary from CSV/TSV content.\n# @PRE User has translate.dictionary.edit permission.\n# @POST Entries are imported per conflict mode.\n# @COMPLEXITY 4\n\n@router.post(\"/dictionaries/{dictionary_id}/import\")\nasync def import_dictionary_entries(\n dictionary_id: str,\n payload: DictionaryImport,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.dictionary\", \"EDIT\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Import entries into a terminology dictionary from CSV/TSV content.\"\"\"\n with belief_scope(\"import_dictionary_entries\"):\n logger.reason(f\"Dict: {dictionary_id}, User: {current_user.username}\")\n\n if payload.on_conflict not in (\"overwrite\", \"keep_existing\", \"cancel\"):\n raise HTTPException(\n status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,\n detail=\"on_conflict must be 'overwrite', 'keep_existing', or 'cancel'\",\n )\n\n try:\n result = DictionaryManager.import_entries(\n db,\n dict_id=dictionary_id,\n content=payload.content,\n delimiter=payload.delimiter,\n on_conflict=payload.on_conflict,\n preview_only=payload.preview_only,\n default_source_language=payload.default_source_language,\n default_target_language=payload.default_target_language,\n )\n logger.reflect(\"Import completed\", {\n \"dict_id\": dictionary_id,\n \"total\": result[\"total\"],\n \"errors\": len(result[\"errors\"]),\n })\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion import_dictionary_entries\n" }, { "contract_id": "TranslateHelpersModule", @@ -22510,7 +22305,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateHelpersModule [C:2] [TYPE Module] [SEMANTICS sqlalchemy, translate, helper, query, mapping]\n# @BRIEF Shared helper functions for translate route handlers.\n# @LAYER: API\n# @RELATION DEPENDS_ON -> [models.translate]\n\nfrom typing import Any\n\nfrom sqlalchemy.orm import Session\n\n\n# #region _run_to_response [C:2] [TYPE Function]\n# @BRIEF Convert TranslationRun ORM to response dict.\ndef _run_to_response(run: Any) -> dict:\n return {\n \"id\": run.id,\n \"job_id\": run.job_id,\n \"status\": run.status,\n \"trigger_type\": run.trigger_type,\n \"started_at\": run.started_at.isoformat() if run.started_at else None,\n \"completed_at\": run.completed_at.isoformat() if run.completed_at else None,\n \"error_message\": run.error_message,\n \"total_records\": run.total_records or 0,\n \"successful_records\": run.successful_records or 0,\n \"failed_records\": run.failed_records or 0,\n \"skipped_records\": run.skipped_records or 0,\n \"insert_status\": run.insert_status,\n \"superset_execution_id\": run.superset_execution_id,\n \"config_snapshot\": run.config_snapshot,\n \"key_hash\": run.key_hash,\n \"config_hash\": run.config_hash,\n \"dict_snapshot_hash\": run.dict_snapshot_hash,\n \"created_by\": run.created_by,\n \"created_at\": run.created_at.isoformat() if run.created_at else None,\n }\n# #endregion _run_to_response\n\n\n# #region _dict_to_response [C:2] [TYPE Function]\n# @BRIEF Convert a TerminologyDictionary ORM model to a response dict with computed entry_count.\ndef _dict_to_response(d: Any, entry_count: int = 0) -> dict:\n return {\n \"id\": d.id,\n \"name\": d.name,\n \"description\": d.description,\n \"source_dialect\": d.source_dialect,\n \"target_dialect\": d.target_dialect,\n \"is_active\": d.is_active,\n \"created_by\": d.created_by,\n \"created_at\": d.created_at,\n \"updated_at\": d.updated_at,\n \"entry_count\": entry_count,\n }\n# #endregion _dict_to_response\n\n\n# #region _get_dictionary_entry_counts [C:2] [TYPE Function]\n# @BRIEF Get entry counts for a list of dictionary IDs.\ndef _get_dictionary_entry_counts(db: Session, dict_ids: list[str]) -> dict[str, int]:\n from sqlalchemy import func\n\n from ....models.translate import DictionaryEntry\n counts = (\n db.query(DictionaryEntry.dictionary_id, func.count(DictionaryEntry.id))\n .filter(DictionaryEntry.dictionary_id.in_(dict_ids))\n .group_by(DictionaryEntry.dictionary_id)\n .all()\n )\n return {row[0]: row[1] for row in counts}\n# #endregion _get_dictionary_entry_counts\n\n# #endregion TranslateHelpersModule\n" + "body": "# #region TranslateHelpersModule [C:2] [TYPE Module] [SEMANTICS sqlalchemy, translate, helper, query, mapping]\n# @BRIEF Shared helper functions for translate route handlers.\n# @LAYER API\n# @RELATION DEPENDS_ON -> [models.translate]\n\nfrom typing import Any\n\nfrom sqlalchemy.orm import Session\n\n\n# #region _run_to_response [C:2] [TYPE Function]\n# @BRIEF Convert TranslationRun ORM to response dict.\ndef _run_to_response(run: Any) -> dict:\n return {\n \"id\": run.id,\n \"job_id\": run.job_id,\n \"status\": run.status,\n \"trigger_type\": run.trigger_type,\n \"started_at\": run.started_at.isoformat() if run.started_at else None,\n \"completed_at\": run.completed_at.isoformat() if run.completed_at else None,\n \"error_message\": run.error_message,\n \"total_records\": run.total_records or 0,\n \"successful_records\": run.successful_records or 0,\n \"failed_records\": run.failed_records or 0,\n \"skipped_records\": run.skipped_records or 0,\n \"insert_status\": run.insert_status,\n \"superset_execution_id\": run.superset_execution_id,\n \"config_snapshot\": run.config_snapshot,\n \"key_hash\": run.key_hash,\n \"config_hash\": run.config_hash,\n \"dict_snapshot_hash\": run.dict_snapshot_hash,\n \"created_by\": run.created_by,\n \"created_at\": run.created_at.isoformat() if run.created_at else None,\n }\n# #endregion _run_to_response\n\n\n# #region _dict_to_response [C:2] [TYPE Function]\n# @BRIEF Convert a TerminologyDictionary ORM model to a response dict with computed entry_count.\ndef _dict_to_response(d: Any, entry_count: int = 0) -> dict:\n return {\n \"id\": d.id,\n \"name\": d.name,\n \"description\": d.description,\n \"source_dialect\": d.source_dialect,\n \"target_dialect\": d.target_dialect,\n \"is_active\": d.is_active,\n \"created_by\": d.created_by,\n \"created_at\": d.created_at,\n \"updated_at\": d.updated_at,\n \"entry_count\": entry_count,\n }\n# #endregion _dict_to_response\n\n\n# #region _get_dictionary_entry_counts [C:2] [TYPE Function]\n# @BRIEF Get entry counts for a list of dictionary IDs.\ndef _get_dictionary_entry_counts(db: Session, dict_ids: list[str]) -> dict[str, int]:\n from sqlalchemy import func\n\n from ....models.translate import DictionaryEntry\n counts = (\n db.query(DictionaryEntry.dictionary_id, func.count(DictionaryEntry.id))\n .filter(DictionaryEntry.dictionary_id.in_(dict_ids))\n .group_by(DictionaryEntry.dictionary_id)\n .all()\n )\n return {row[0]: row[1] for row in counts}\n# #endregion _get_dictionary_entry_counts\n\n# #endregion TranslateHelpersModule\n" }, { "contract_id": "_run_to_response", @@ -22571,7 +22366,7 @@ "contract_type": "Module", "file_path": "backend/src/api/routes/translate/_job_routes.py", "start_line": 1, - "end_line": 248, + "end_line": 249, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -22593,7 +22388,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateJobRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, search]\n# @BRIEF Translation Job CRUD and datasource column routes.\n# @LAYER: API\n\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import get_db\nfrom ....core.logger import belief_scope, logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.service import (\n TranslateJobService,\n job_to_response,\n)\nfrom ....plugins.translate.service import (\n get_datasource_columns as fetch_datasource_columns_service,\n)\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n DatasourceColumnsResponse,\n DuplicateJobResponse,\n TranslateJobCreate,\n TranslateJobResponse,\n TranslateJobUpdate,\n)\nfrom ._router import router\n\n# ============================================================\n# Translation Job CRUD\n# ============================================================\n\n# #region list_jobs [TYPE Function]\n# @BRIEF List all translation jobs.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of translation jobs.\n@router.get(\"/jobs\", response_model=list[TranslateJobResponse])\nasync def list_jobs(\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n status: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"List all translation jobs.\"\"\"\n logger.reason(f\"list_jobs — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n total, jobs = service.list_jobs(\n page=page,\n page_size=page_size,\n status_filter=status,\n )\n results = []\n for job in jobs:\n dict_ids = service.get_job_dictionary_ids(job.id)\n results.append(job_to_response(job, dict_ids))\n return results\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion list_jobs\n\n\n# #region get_job [TYPE Function]\n# @BRIEF Get a single translation job by ID.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns the translation job.\n@router.get(\"/jobs/{job_id}\", response_model=TranslateJobResponse)\nasync def get_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get a translation job by ID.\"\"\"\n logger.reason(f\"get_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.get_job(job_id)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_job\n\n\n# #region create_job [TYPE Function]\n# @BRIEF Create a new translation job.\n# @PRE: User has translate.job.create permission.\n# @POST: Returns the created translation job.\n# @SIDE_EFFECT: Validates columns via SupersetClient; caches database_dialect.\n@router.post(\"/jobs\", response_model=TranslateJobResponse, status_code=status.HTTP_201_CREATED)\nasync def create_job(\n payload: TranslateJobCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"CREATE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Create a new translation job.\"\"\"\n logger.reason(f\"create_job — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.create_job(payload)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e))\n# #endregion create_job\n\n\n# #region update_job [TYPE Function]\n# @BRIEF Update an existing translation job.\n# @PRE: User has translate.job.edit permission.\n# @POST: Returns the updated translation job.\n# @SIDE_EFFECT: Re-detects database_dialect if datasource changed.\n@router.put(\"/jobs/{job_id}\", response_model=TranslateJobResponse)\nasync def update_job(\n job_id: str,\n payload: TranslateJobUpdate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EDIT\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Update a translation job.\"\"\"\n logger.reason(f\"update_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.update_job(job_id, payload)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion update_job\n\n\n# #region delete_job [TYPE Function]\n# @BRIEF Delete a translation job.\n# @PRE: User has translate.job.delete permission.\n# @POST: Job is deleted.\n@router.delete(\"/jobs/{job_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"DELETE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Delete a translation job.\"\"\"\n logger.reason(f\"delete_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n service.delete_job(job_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_job\n\n\n# #region duplicate_job [TYPE Function]\n# @BRIEF Duplicate a translation job.\n# @PRE: User has translate.job.create permission.\n# @POST: Returns the duplicated job with status DRAFT.\n@router.post(\"/jobs/{job_id}/duplicate\", response_model=DuplicateJobResponse, status_code=status.HTTP_201_CREATED)\nasync def duplicate_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"CREATE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Duplicate a translation job.\"\"\"\n logger.reason(f\"duplicate_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n new_job = service.duplicate_job(job_id)\n return DuplicateJobResponse(\n id=new_job.id,\n name=new_job.name,\n message=\"Job duplicated successfully\",\n )\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion duplicate_job\n\n\n# #region get_datasource_columns [TYPE Function]\n# @BRIEF Get column metadata and database dialect for a Superset datasource.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns column list with metadata and database_dialect.\n# @SIDE_EFFECT: Queries Superset API for dataset detail and database info.\n# @RATIONALE: Dialect detection from Superset connection cached on TranslationJob at save time.\n# @REJECTED: Hardcoding dialect list per database — would drift from actual Superset config.\n\n@router.get(\"/datasources\")\nasync def list_datasources(\n env_id: str = Query(..., description=\"Superset environment ID\"),\n search: str | None = Query(None, description=\"Search by table name\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"List all Superset datasets available for translation jobs.\"\"\"\n with belief_scope(\"list_datasources\"):\n try:\n service = TranslateJobService(db, config_manager)\n datasets = service.fetch_available_datasources(env_id, search)\n return datasets\n except Exception as e:\n logger.explore(\"list_datasources failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(e))\n\n@router.get(\"/datasources/{datasource_id}/columns\", response_model=DatasourceColumnsResponse)\nasync def get_datasource_columns(\n datasource_id: int,\n env_id: str = Query(..., description=\"Superset environment ID\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get column metadata and database dialect for a Superset datasource.\"\"\"\n logger.reason(\n f\"get_datasource_columns — Datasource: {datasource_id}, Env: {env_id}, User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"}\n )\n try:\n result = fetch_datasource_columns_service(\n datasource_id=datasource_id,\n env_id=env_id,\n config_manager=config_manager,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"get_datasource_columns failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(\n status_code=status.HTTP_502_BAD_GATEWAY,\n detail=f\"Failed to fetch datasource columns from Superset: {e}\",\n )\n# #endregion get_datasource_columns\n\n# #endregion TranslateJobRoutesModule\n" + "body": "# #region TranslateJobRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, search]\n# @BRIEF Translation Job CRUD and datasource column routes.\n# @LAYER: API\n\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import get_db\nfrom ....core.logger import belief_scope, logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.service import (\n TranslateJobService,\n job_to_response,\n)\nfrom ....plugins.translate.service import (\n get_datasource_columns as fetch_datasource_columns_service,\n)\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n DatasourceColumnsResponse,\n DuplicateJobResponse,\n TranslateJobCreate,\n TranslateJobResponse,\n TranslateJobUpdate,\n)\nfrom ._router import router\n\n# ============================================================\n# Translation Job CRUD\n# ============================================================\n\n# #region list_jobs [C:4] [TYPE Function]\n# @BRIEF List all translation jobs.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of translation jobs.\n@router.get(\"/jobs\", response_model=list[TranslateJobResponse])\nasync def list_jobs(\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n status: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"List all translation jobs.\"\"\"\n logger.reason(f\"list_jobs — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n total, jobs = service.list_jobs(\n page=page,\n page_size=page_size,\n status_filter=status,\n )\n results = []\n for job in jobs:\n dict_ids = service.get_job_dictionary_ids(job.id)\n results.append(job_to_response(job, dict_ids))\n return results\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion list_jobs\n\n\n# #region get_job [C:4] [TYPE Function]\n# @BRIEF Get a single translation job by ID.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns the translation job.\n@router.get(\"/jobs/{job_id}\", response_model=TranslateJobResponse)\nasync def get_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get a translation job by ID.\"\"\"\n logger.reason(f\"get_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.get_job(job_id)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_job\n\n\n# #region create_job [C:4] [TYPE Function]\n# @BRIEF Create a new translation job.\n# @PRE: User has translate.job.create permission.\n# @POST: Returns the created translation job.\n# @SIDE_EFFECT: Validates columns via SupersetClient; caches database_dialect.\n@router.post(\"/jobs\", response_model=TranslateJobResponse, status_code=status.HTTP_201_CREATED)\nasync def create_job(\n payload: TranslateJobCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"CREATE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Create a new translation job.\"\"\"\n logger.reason(f\"create_job — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.create_job(payload)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e))\n# #endregion create_job\n\n\n# #region update_job [C:4] [TYPE Function]\n# @BRIEF Update an existing translation job.\n# @PRE: User has translate.job.edit permission.\n# @POST: Returns the updated translation job.\n# @SIDE_EFFECT: Re-detects database_dialect if datasource changed.\n@router.put(\"/jobs/{job_id}\", response_model=TranslateJobResponse)\nasync def update_job(\n job_id: str,\n payload: TranslateJobUpdate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EDIT\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Update a translation job.\"\"\"\n logger.reason(f\"update_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.update_job(job_id, payload)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion update_job\n\n\n# #region delete_job [C:4] [TYPE Function]\n# @BRIEF Delete a translation job.\n# @PRE: User has translate.job.delete permission.\n# @POST: Job is deleted.\n@router.delete(\"/jobs/{job_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"DELETE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Delete a translation job.\"\"\"\n logger.reason(f\"delete_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n service.delete_job(job_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_job\n\n\n# #region duplicate_job [C:4] [TYPE Function]\n# @BRIEF Duplicate a translation job.\n# @PRE: User has translate.job.create permission.\n# @POST: Returns the duplicated job with status DRAFT.\n@router.post(\"/jobs/{job_id}/duplicate\", response_model=DuplicateJobResponse, status_code=status.HTTP_201_CREATED)\nasync def duplicate_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"CREATE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Duplicate a translation job.\"\"\"\n logger.reason(f\"duplicate_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n new_job = service.duplicate_job(job_id)\n return DuplicateJobResponse(\n id=new_job.id,\n name=new_job.name,\n message=\"Job duplicated successfully\",\n )\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion duplicate_job\n\n\n# #region get_datasource_columns [C:4] [TYPE Function]\n# @BRIEF Get column metadata and database dialect for a Superset datasource.\n# @PRE User has translate.job.view permission.\n# @POST Returns column list with metadata and database_dialect.\n# @SIDE_EFFECT Queries Superset API for dataset detail and database info.\n# @RATIONALE Dialect detection from Superset connection cached on TranslationJob at save time.\n# @REJECTED Hardcoding dialect list per database — would drift from actual Superset config.\n# @COMPLEXITY 4\n\n@router.get(\"/datasources\")\nasync def list_datasources(\n env_id: str = Query(..., description=\"Superset environment ID\"),\n search: str | None = Query(None, description=\"Search by table name\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"List all Superset datasets available for translation jobs.\"\"\"\n with belief_scope(\"list_datasources\"):\n try:\n service = TranslateJobService(db, config_manager)\n datasets = service.fetch_available_datasources(env_id, search)\n return datasets\n except Exception as e:\n logger.explore(\"list_datasources failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(e))\n\n@router.get(\"/datasources/{datasource_id}/columns\", response_model=DatasourceColumnsResponse)\nasync def get_datasource_columns(\n datasource_id: int,\n env_id: str = Query(..., description=\"Superset environment ID\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get column metadata and database dialect for a Superset datasource.\"\"\"\n logger.reason(\n f\"get_datasource_columns — Datasource: {datasource_id}, Env: {env_id}, User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"}\n )\n try:\n result = fetch_datasource_columns_service(\n datasource_id=datasource_id,\n env_id=env_id,\n config_manager=config_manager,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"get_datasource_columns failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(\n status_code=status.HTTP_502_BAD_GATEWAY,\n detail=f\"Failed to fetch datasource columns from Superset: {e}\",\n )\n# #endregion get_datasource_columns\n\n# #endregion TranslateJobRoutesModule\n" }, { "contract_id": "list_jobs", @@ -22601,9 +22396,10 @@ "file_path": "backend/src/api/routes/translate/_job_routes.py", "start_line": 34, "end_line": 64, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns list of translation jobs.", "PRE": "User has translate.job.view permission.", "PURPOSE": "List all translation jobs." @@ -22611,27 +22407,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region list_jobs [TYPE Function]\n# @BRIEF List all translation jobs.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of translation jobs.\n@router.get(\"/jobs\", response_model=list[TranslateJobResponse])\nasync def list_jobs(\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n status: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"List all translation jobs.\"\"\"\n logger.reason(f\"list_jobs — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n total, jobs = service.list_jobs(\n page=page,\n page_size=page_size,\n status_filter=status,\n )\n results = []\n for job in jobs:\n dict_ids = service.get_job_dictionary_ids(job.id)\n results.append(job_to_response(job, dict_ids))\n return results\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion list_jobs\n" + "body": "# #region list_jobs [C:4] [TYPE Function]\n# @BRIEF List all translation jobs.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of translation jobs.\n@router.get(\"/jobs\", response_model=list[TranslateJobResponse])\nasync def list_jobs(\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n status: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"List all translation jobs.\"\"\"\n logger.reason(f\"list_jobs — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n total, jobs = service.list_jobs(\n page=page,\n page_size=page_size,\n status_filter=status,\n )\n results = []\n for job in jobs:\n dict_ids = service.get_job_dictionary_ids(job.id)\n results.append(job_to_response(job, dict_ids))\n return results\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion list_jobs\n" }, { "contract_id": "get_job", @@ -22639,9 +22435,10 @@ "file_path": "backend/src/api/routes/translate/_job_routes.py", "start_line": 67, "end_line": 88, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the translation job.", "PRE": "User has translate.job.view permission.", "PURPOSE": "Get a single translation job by ID." @@ -22649,27 +22446,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_job [TYPE Function]\n# @BRIEF Get a single translation job by ID.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns the translation job.\n@router.get(\"/jobs/{job_id}\", response_model=TranslateJobResponse)\nasync def get_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get a translation job by ID.\"\"\"\n logger.reason(f\"get_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.get_job(job_id)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_job\n" + "body": "# #region get_job [C:4] [TYPE Function]\n# @BRIEF Get a single translation job by ID.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns the translation job.\n@router.get(\"/jobs/{job_id}\", response_model=TranslateJobResponse)\nasync def get_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get a translation job by ID.\"\"\"\n logger.reason(f\"get_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.get_job(job_id)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_job\n" }, { "contract_id": "create_job", @@ -22677,9 +22474,10 @@ "file_path": "backend/src/api/routes/translate/_job_routes.py", "start_line": 91, "end_line": 113, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the created translation job.", "PRE": "User has translate.job.create permission.", "PURPOSE": "Create a new translation job.", @@ -22688,36 +22486,18 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "SIDE_EFFECT", - "message": "@SIDE_EFFECT is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region create_job [TYPE Function]\n# @BRIEF Create a new translation job.\n# @PRE: User has translate.job.create permission.\n# @POST: Returns the created translation job.\n# @SIDE_EFFECT: Validates columns via SupersetClient; caches database_dialect.\n@router.post(\"/jobs\", response_model=TranslateJobResponse, status_code=status.HTTP_201_CREATED)\nasync def create_job(\n payload: TranslateJobCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"CREATE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Create a new translation job.\"\"\"\n logger.reason(f\"create_job — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.create_job(payload)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e))\n# #endregion create_job\n" + "body": "# #region create_job [C:4] [TYPE Function]\n# @BRIEF Create a new translation job.\n# @PRE: User has translate.job.create permission.\n# @POST: Returns the created translation job.\n# @SIDE_EFFECT: Validates columns via SupersetClient; caches database_dialect.\n@router.post(\"/jobs\", response_model=TranslateJobResponse, status_code=status.HTTP_201_CREATED)\nasync def create_job(\n payload: TranslateJobCreate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"CREATE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Create a new translation job.\"\"\"\n logger.reason(f\"create_job — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.create_job(payload)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e))\n# #endregion create_job\n" }, { "contract_id": "update_job", @@ -22725,9 +22505,10 @@ "file_path": "backend/src/api/routes/translate/_job_routes.py", "start_line": 116, "end_line": 139, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the updated translation job.", "PRE": "User has translate.job.edit permission.", "PURPOSE": "Update an existing translation job.", @@ -22736,36 +22517,18 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "SIDE_EFFECT", - "message": "@SIDE_EFFECT is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region update_job [TYPE Function]\n# @BRIEF Update an existing translation job.\n# @PRE: User has translate.job.edit permission.\n# @POST: Returns the updated translation job.\n# @SIDE_EFFECT: Re-detects database_dialect if datasource changed.\n@router.put(\"/jobs/{job_id}\", response_model=TranslateJobResponse)\nasync def update_job(\n job_id: str,\n payload: TranslateJobUpdate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EDIT\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Update a translation job.\"\"\"\n logger.reason(f\"update_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.update_job(job_id, payload)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion update_job\n" + "body": "# #region update_job [C:4] [TYPE Function]\n# @BRIEF Update an existing translation job.\n# @PRE: User has translate.job.edit permission.\n# @POST: Returns the updated translation job.\n# @SIDE_EFFECT: Re-detects database_dialect if datasource changed.\n@router.put(\"/jobs/{job_id}\", response_model=TranslateJobResponse)\nasync def update_job(\n job_id: str,\n payload: TranslateJobUpdate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EDIT\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Update a translation job.\"\"\"\n logger.reason(f\"update_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n job = service.update_job(job_id, payload)\n dict_ids = service.get_job_dictionary_ids(job.id)\n return job_to_response(job, dict_ids)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion update_job\n" }, { "contract_id": "delete_job", @@ -22773,9 +22536,10 @@ "file_path": "backend/src/api/routes/translate/_job_routes.py", "start_line": 142, "end_line": 161, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Job is deleted.", "PRE": "User has translate.job.delete permission.", "PURPOSE": "Delete a translation job." @@ -22783,27 +22547,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region delete_job [TYPE Function]\n# @BRIEF Delete a translation job.\n# @PRE: User has translate.job.delete permission.\n# @POST: Job is deleted.\n@router.delete(\"/jobs/{job_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"DELETE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Delete a translation job.\"\"\"\n logger.reason(f\"delete_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n service.delete_job(job_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_job\n" + "body": "# #region delete_job [C:4] [TYPE Function]\n# @BRIEF Delete a translation job.\n# @PRE: User has translate.job.delete permission.\n# @POST: Job is deleted.\n@router.delete(\"/jobs/{job_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"DELETE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Delete a translation job.\"\"\"\n logger.reason(f\"delete_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n service.delete_job(job_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_job\n" }, { "contract_id": "duplicate_job", @@ -22811,9 +22575,10 @@ "file_path": "backend/src/api/routes/translate/_job_routes.py", "start_line": 164, "end_line": 188, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the duplicated job with status DRAFT.", "PRE": "User has translate.job.create permission.", "PURPOSE": "Duplicate a translation job." @@ -22821,37 +22586,38 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region duplicate_job [TYPE Function]\n# @BRIEF Duplicate a translation job.\n# @PRE: User has translate.job.create permission.\n# @POST: Returns the duplicated job with status DRAFT.\n@router.post(\"/jobs/{job_id}/duplicate\", response_model=DuplicateJobResponse, status_code=status.HTTP_201_CREATED)\nasync def duplicate_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"CREATE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Duplicate a translation job.\"\"\"\n logger.reason(f\"duplicate_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n new_job = service.duplicate_job(job_id)\n return DuplicateJobResponse(\n id=new_job.id,\n name=new_job.name,\n message=\"Job duplicated successfully\",\n )\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion duplicate_job\n" + "body": "# #region duplicate_job [C:4] [TYPE Function]\n# @BRIEF Duplicate a translation job.\n# @PRE: User has translate.job.create permission.\n# @POST: Returns the duplicated job with status DRAFT.\n@router.post(\"/jobs/{job_id}/duplicate\", response_model=DuplicateJobResponse, status_code=status.HTTP_201_CREATED)\nasync def duplicate_job(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"CREATE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Duplicate a translation job.\"\"\"\n logger.reason(f\"duplicate_job — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n service = TranslateJobService(db, config_manager, current_user.username)\n new_job = service.duplicate_job(job_id)\n return DuplicateJobResponse(\n id=new_job.id,\n name=new_job.name,\n message=\"Job duplicated successfully\",\n )\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion duplicate_job\n" }, { "contract_id": "get_datasource_columns", "contract_type": "Function", "file_path": "backend/src/api/routes/translate/_job_routes.py", "start_line": 191, - "end_line": 246, - "tier": "TIER_1", - "complexity": 1, + "end_line": 247, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "Returns column list with metadata and database_dialect.", "PRE": "User has translate.job.view permission.", "PURPOSE": "Get column metadata and database dialect for a Superset datasource.", @@ -22862,43 +22628,25 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "SIDE_EFFECT", - "message": "@SIDE_EFFECT is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_datasource_columns [TYPE Function]\n# @BRIEF Get column metadata and database dialect for a Superset datasource.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns column list with metadata and database_dialect.\n# @SIDE_EFFECT: Queries Superset API for dataset detail and database info.\n# @RATIONALE: Dialect detection from Superset connection cached on TranslationJob at save time.\n# @REJECTED: Hardcoding dialect list per database — would drift from actual Superset config.\n\n@router.get(\"/datasources\")\nasync def list_datasources(\n env_id: str = Query(..., description=\"Superset environment ID\"),\n search: str | None = Query(None, description=\"Search by table name\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"List all Superset datasets available for translation jobs.\"\"\"\n with belief_scope(\"list_datasources\"):\n try:\n service = TranslateJobService(db, config_manager)\n datasets = service.fetch_available_datasources(env_id, search)\n return datasets\n except Exception as e:\n logger.explore(\"list_datasources failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(e))\n\n@router.get(\"/datasources/{datasource_id}/columns\", response_model=DatasourceColumnsResponse)\nasync def get_datasource_columns(\n datasource_id: int,\n env_id: str = Query(..., description=\"Superset environment ID\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get column metadata and database dialect for a Superset datasource.\"\"\"\n logger.reason(\n f\"get_datasource_columns — Datasource: {datasource_id}, Env: {env_id}, User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"}\n )\n try:\n result = fetch_datasource_columns_service(\n datasource_id=datasource_id,\n env_id=env_id,\n config_manager=config_manager,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"get_datasource_columns failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(\n status_code=status.HTTP_502_BAD_GATEWAY,\n detail=f\"Failed to fetch datasource columns from Superset: {e}\",\n )\n# #endregion get_datasource_columns\n" + "body": "# #region get_datasource_columns [C:4] [TYPE Function]\n# @BRIEF Get column metadata and database dialect for a Superset datasource.\n# @PRE User has translate.job.view permission.\n# @POST Returns column list with metadata and database_dialect.\n# @SIDE_EFFECT Queries Superset API for dataset detail and database info.\n# @RATIONALE Dialect detection from Superset connection cached on TranslationJob at save time.\n# @REJECTED Hardcoding dialect list per database — would drift from actual Superset config.\n# @COMPLEXITY 4\n\n@router.get(\"/datasources\")\nasync def list_datasources(\n env_id: str = Query(..., description=\"Superset environment ID\"),\n search: str | None = Query(None, description=\"Search by table name\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"List all Superset datasets available for translation jobs.\"\"\"\n with belief_scope(\"list_datasources\"):\n try:\n service = TranslateJobService(db, config_manager)\n datasets = service.fetch_available_datasources(env_id, search)\n return datasets\n except Exception as e:\n logger.explore(\"list_datasources failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=str(e))\n\n@router.get(\"/datasources/{datasource_id}/columns\", response_model=DatasourceColumnsResponse)\nasync def get_datasource_columns(\n datasource_id: int,\n env_id: str = Query(..., description=\"Superset environment ID\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get column metadata and database dialect for a Superset datasource.\"\"\"\n logger.reason(\n f\"get_datasource_columns — Datasource: {datasource_id}, Env: {env_id}, User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"}\n )\n try:\n result = fetch_datasource_columns_service(\n datasource_id=datasource_id,\n env_id=env_id,\n config_manager=config_manager,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"get_datasource_columns failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(\n status_code=status.HTTP_502_BAD_GATEWAY,\n detail=f\"Failed to fetch datasource columns from Superset: {e}\",\n )\n# #endregion get_datasource_columns\n" }, { "contract_id": "TranslateMetricsRoutesModule", "contract_type": "Module", "file_path": "backend/src/api/routes/translate/_metrics_routes.py", "start_line": 1, - "end_line": 63, + "end_line": 65, "tier": "TIER_1", "complexity": 2, "metadata": { @@ -22910,7 +22658,7 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateMetricsRoutesModule [C:2] [TYPE Module] [SEMANTICS fastapi, translate, api, search]\n# @BRIEF Translation Metrics endpoints.\n# @LAYER: API\n\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_current_user, has_permission\nfrom ....plugins.translate.metrics import TranslationMetrics\nfrom ....schemas.auth import User\nfrom ._router import router\n\n# ============================================================\n# Metrics\n# ============================================================\n\n# #region get_metrics [TYPE Function]\n# @BRIEF Get translation metrics, optionally filtered by job.\n# @PRE: User has translate.metrics.view permission.\n# @POST: Returns metrics data.\n@router.get(\"/metrics\")\nasync def get_metrics(\n job_id: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.metrics\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get translation metrics.\"\"\"\n logger.reason(f\"get_metrics — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n metrics_svc = TranslationMetrics(db)\n if job_id:\n return metrics_svc.get_job_metrics(job_id)\n return metrics_svc.get_all_metrics()\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_metrics\n\n\n# #region get_job_metrics [TYPE Function]\n# @BRIEF Get aggregated metrics for a specific job.\n# @PRE: User has translate.metrics.view permission.\n# @POST: Returns metrics dict.\n@router.get(\"/jobs/{job_id}/metrics\")\nasync def get_job_metrics(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.metrics\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get aggregated metrics for a specific job.\"\"\"\n logger.reason(f\"get_job_metrics — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n metrics_svc = TranslationMetrics(db)\n return metrics_svc.get_job_metrics(job_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_job_metrics\n\n# #endregion TranslateMetricsRoutesModule\n" + "body": "# #region TranslateMetricsRoutesModule [C:2] [TYPE Module] [SEMANTICS fastapi, translate, api, search]\n# @BRIEF Translation Metrics endpoints.\n# @LAYER: API\n\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_current_user, has_permission\nfrom ....plugins.translate.metrics import TranslationMetrics\nfrom ....schemas.auth import User\nfrom ._router import router\n\n# ============================================================\n# Metrics\n# ============================================================\n\n# #region get_metrics [C:4] [TYPE Function]\n# @BRIEF Get translation metrics, optionally filtered by job.\n# @PRE: User has translate.metrics.view permission.\n# @POST: Returns metrics data.\n@router.get(\"/metrics\")\nasync def get_metrics(\n job_id: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.metrics\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get translation metrics.\"\"\"\n logger.reason(f\"get_metrics — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n metrics_svc = TranslationMetrics(db)\n if job_id:\n return metrics_svc.get_job_metrics(job_id)\n return metrics_svc.get_all_metrics()\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_metrics\n\n\n# #region get_job_metrics [C:4] [TYPE Function]\n# @BRIEF Get aggregated metrics for a specific job.\n# @PRE User has translate.metrics.view permission.\n# @POST Returns metrics dict.\n# @COMPLEXITY 4\n\n@router.get(\"/jobs/{job_id}/metrics\")\nasync def get_job_metrics(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.metrics\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get aggregated metrics for a specific job.\"\"\"\n logger.reason(f\"get_job_metrics — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n metrics_svc = TranslationMetrics(db)\n return metrics_svc.get_job_metrics(job_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_job_metrics\n\n# #endregion TranslateMetricsRoutesModule\n" }, { "contract_id": "get_metrics", @@ -22918,9 +22666,10 @@ "file_path": "backend/src/api/routes/translate/_metrics_routes.py", "start_line": 20, "end_line": 40, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns metrics data.", "PRE": "User has translate.metrics.view permission.", "PURPOSE": "Get translation metrics, optionally filtered by job." @@ -22928,37 +22677,38 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_metrics [TYPE Function]\n# @BRIEF Get translation metrics, optionally filtered by job.\n# @PRE: User has translate.metrics.view permission.\n# @POST: Returns metrics data.\n@router.get(\"/metrics\")\nasync def get_metrics(\n job_id: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.metrics\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get translation metrics.\"\"\"\n logger.reason(f\"get_metrics — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n metrics_svc = TranslationMetrics(db)\n if job_id:\n return metrics_svc.get_job_metrics(job_id)\n return metrics_svc.get_all_metrics()\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_metrics\n" + "body": "# #region get_metrics [C:4] [TYPE Function]\n# @BRIEF Get translation metrics, optionally filtered by job.\n# @PRE: User has translate.metrics.view permission.\n# @POST: Returns metrics data.\n@router.get(\"/metrics\")\nasync def get_metrics(\n job_id: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.metrics\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get translation metrics.\"\"\"\n logger.reason(f\"get_metrics — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n metrics_svc = TranslationMetrics(db)\n if job_id:\n return metrics_svc.get_job_metrics(job_id)\n return metrics_svc.get_all_metrics()\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_metrics\n" }, { "contract_id": "get_job_metrics", "contract_type": "Function", "file_path": "backend/src/api/routes/translate/_metrics_routes.py", "start_line": 43, - "end_line": 61, - "tier": "TIER_1", - "complexity": 1, + "end_line": 63, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "Returns metrics dict.", "PRE": "User has translate.metrics.view permission.", "PURPOSE": "Get aggregated metrics for a specific job." @@ -22966,34 +22716,34 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_job_metrics [TYPE Function]\n# @BRIEF Get aggregated metrics for a specific job.\n# @PRE: User has translate.metrics.view permission.\n# @POST: Returns metrics dict.\n@router.get(\"/jobs/{job_id}/metrics\")\nasync def get_job_metrics(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.metrics\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get aggregated metrics for a specific job.\"\"\"\n logger.reason(f\"get_job_metrics — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n metrics_svc = TranslationMetrics(db)\n return metrics_svc.get_job_metrics(job_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_job_metrics\n" + "body": "# #region get_job_metrics [C:4] [TYPE Function]\n# @BRIEF Get aggregated metrics for a specific job.\n# @PRE User has translate.metrics.view permission.\n# @POST Returns metrics dict.\n# @COMPLEXITY 4\n\n@router.get(\"/jobs/{job_id}/metrics\")\nasync def get_job_metrics(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.metrics\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get aggregated metrics for a specific job.\"\"\"\n logger.reason(f\"get_job_metrics — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n metrics_svc = TranslationMetrics(db)\n return metrics_svc.get_job_metrics(job_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_job_metrics\n" }, { "contract_id": "TranslatePreviewRoutesModule", "contract_type": "Module", "file_path": "backend/src/api/routes/translate/_preview_routes.py", "start_line": 1, - "end_line": 192, + "end_line": 194, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -23015,7 +22765,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslatePreviewRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, preview, review, api, search]\n# @BRIEF Translation Preview session management routes.\n# @LAYER: API\n\n\nfrom fastapi import Depends, HTTPException, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.preview import TranslationPreview\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n PreviewRequest,\n PreviewRow,\n PreviewRowUpdate,\n)\nfrom ._router import router\n\n# ============================================================\n# Preview\n# ============================================================\n\n# #region preview_translation [TYPE Function]\n# @BRIEF Preview a translation before applying it.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns a preview session with records and cost estimation.\n# @SIDE_EFFECT: Fetches sample data from Superset; calls LLM provider; creates DB rows.\n@router.post(\"/jobs/{job_id}/preview\", status_code=status.HTTP_201_CREATED)\nasync def preview_translation(\n job_id: str,\n payload: PreviewRequest = None,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Preview a translation before applying it.\"\"\"\n logger.reason(f\"preview_translation — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n if payload is None:\n payload = PreviewRequest()\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.preview_rows(\n job_id=job_id,\n sample_size=payload.sample_size,\n prompt_template=payload.prompt_template,\n env_id=payload.env_id,\n )\n return result\n except ValueError as e:\n logger.explore(\"preview_translation validation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"preview_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Preview failed: {e}\")\n# #endregion preview_translation\n\n\n# #region update_preview_row [TYPE Function]\n# @BRIEF Approve, edit, or reject a preview row (optionally per language).\n# @PRE: User has translate.job.execute permission.\n# @POST: Preview row status is updated.\n@router.put(\"/jobs/{job_id}/preview/rows/{row_key}\")\nasync def update_preview_row(\n job_id: str,\n row_key: str,\n payload: PreviewRowUpdate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Approve, edit, or reject a preview row (optionally per language).\"\"\"\n logger.reason(f\"update_preview_row — Job: {job_id}, Row: {row_key}, Action: {payload.action}, Lang: {payload.language_code}\", extra={\"src\": \"translate_routes\"})\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.update_preview_row(\n job_id=job_id,\n row_id=row_key,\n action=payload.action,\n translation=payload.translation,\n feedback=payload.feedback,\n language_code=payload.language_code,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion update_preview_row\n\n\n# #region accept_preview_session [TYPE Function]\n# @BRIEF Accept a preview session, marking it as the quality gate for full execution.\n# @PRE: User has translate.job.execute permission. Job has an ACTIVE preview session.\n# @POST: Preview session is marked as APPLIED; full execution can proceed.\n@router.post(\"/jobs/{job_id}/preview/accept\")\nasync def accept_preview_session(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Accept a preview session, enabling full translation execution.\"\"\"\n logger.reason(f\"accept_preview_session — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.accept_preview_session(job_id=job_id)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion accept_preview_session\n\n\n# #region apply_preview [TYPE Function]\n# @BRIEF Apply a preview session (alias for accept when accepting at session level).\n# @PRE: User has translate.job.execute permission.\n# @POST: Preview is applied.\n@router.post(\"/preview/{session_id}/apply\")\nasync def apply_preview(\n session_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Apply a preview session by session ID.\"\"\"\n logger.reason(f\"apply_preview — Session: {session_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n # Find job_id from session\n from ....models.translate import TranslationPreviewSession\n session = db.query(TranslationPreviewSession).filter(TranslationPreviewSession.id == session_id).first()\n if not session:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f\"Preview session '{session_id}' not found\")\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.accept_preview_session(job_id=session.job_id)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion apply_preview\n\n\n# ============================================================\n# Preview Records\n# ============================================================\n\n# #region get_preview_records [TYPE Function]\n# @BRIEF Get records for a preview session.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of preview records.\n@router.get(\"/preview/{session_id}/records\", response_model=list[PreviewRow])\nasync def get_preview_records(\n session_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get records for a preview session.\"\"\"\n logger.reason(f\"get_preview_records — Session: {session_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationPreviewRecord, TranslationPreviewSession\n session = db.query(TranslationPreviewSession).filter(TranslationPreviewSession.id == session_id).first()\n if not session:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f\"Preview session '{session_id}' not found\")\n records = (\n db.query(TranslationPreviewRecord)\n .filter(TranslationPreviewRecord.session_id == session_id)\n .all()\n )\n return [\n PreviewRow(\n id=r.id,\n source_sql=r.source_sql,\n target_sql=r.target_sql,\n source_object_type=r.source_object_type,\n source_object_id=r.source_object_id,\n source_object_name=r.source_object_name,\n status=r.status,\n feedback=r.feedback,\n )\n for r in records\n ]\n except HTTPException:\n raise\n except Exception as e:\n logger.explore(\"get_preview_records failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion get_preview_records\n\n# #endregion TranslatePreviewRoutesModule\n" + "body": "# #region TranslatePreviewRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, preview, review, api, search]\n# @BRIEF Translation Preview session management routes.\n# @LAYER: API\n\n\nfrom fastapi import Depends, HTTPException, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.preview import TranslationPreview\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n PreviewRequest,\n PreviewRow,\n PreviewRowUpdate,\n)\nfrom ._router import router\n\n# ============================================================\n# Preview\n# ============================================================\n\n# #region preview_translation [C:4] [TYPE Function]\n# @BRIEF Preview a translation before applying it.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns a preview session with records and cost estimation.\n# @SIDE_EFFECT: Fetches sample data from Superset; calls LLM provider; creates DB rows.\n@router.post(\"/jobs/{job_id}/preview\", status_code=status.HTTP_201_CREATED)\nasync def preview_translation(\n job_id: str,\n payload: PreviewRequest = None,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Preview a translation before applying it.\"\"\"\n logger.reason(f\"preview_translation — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n if payload is None:\n payload = PreviewRequest()\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.preview_rows(\n job_id=job_id,\n sample_size=payload.sample_size,\n prompt_template=payload.prompt_template,\n env_id=payload.env_id,\n )\n return result\n except ValueError as e:\n logger.explore(\"preview_translation validation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"preview_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Preview failed: {e}\")\n# #endregion preview_translation\n\n\n# #region update_preview_row [C:4] [TYPE Function]\n# @BRIEF Approve, edit, or reject a preview row (optionally per language).\n# @PRE: User has translate.job.execute permission.\n# @POST: Preview row status is updated.\n@router.put(\"/jobs/{job_id}/preview/rows/{row_key}\")\nasync def update_preview_row(\n job_id: str,\n row_key: str,\n payload: PreviewRowUpdate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Approve, edit, or reject a preview row (optionally per language).\"\"\"\n logger.reason(f\"update_preview_row — Job: {job_id}, Row: {row_key}, Action: {payload.action}, Lang: {payload.language_code}\", extra={\"src\": \"translate_routes\"})\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.update_preview_row(\n job_id=job_id,\n row_id=row_key,\n action=payload.action,\n translation=payload.translation,\n feedback=payload.feedback,\n language_code=payload.language_code,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion update_preview_row\n\n\n# #region accept_preview_session [C:4] [TYPE Function]\n# @BRIEF Accept a preview session, marking it as the quality gate for full execution.\n# @PRE: User has translate.job.execute permission. Job has an ACTIVE preview session.\n# @POST: Preview session is marked as APPLIED; full execution can proceed.\n@router.post(\"/jobs/{job_id}/preview/accept\")\nasync def accept_preview_session(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Accept a preview session, enabling full translation execution.\"\"\"\n logger.reason(f\"accept_preview_session — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.accept_preview_session(job_id=job_id)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion accept_preview_session\n\n\n# #region apply_preview [C:4] [TYPE Function]\n# @BRIEF Apply a preview session (alias for accept when accepting at session level).\n# @PRE: User has translate.job.execute permission.\n# @POST: Preview is applied.\n@router.post(\"/preview/{session_id}/apply\")\nasync def apply_preview(\n session_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Apply a preview session by session ID.\"\"\"\n logger.reason(f\"apply_preview — Session: {session_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n # Find job_id from session\n from ....models.translate import TranslationPreviewSession\n session = db.query(TranslationPreviewSession).filter(TranslationPreviewSession.id == session_id).first()\n if not session:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f\"Preview session '{session_id}' not found\")\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.accept_preview_session(job_id=session.job_id)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion apply_preview\n\n\n# ============================================================\n# Preview Records\n# ============================================================\n\n# #region get_preview_records [C:4] [TYPE Function]\n# @BRIEF Get records for a preview session.\n# @PRE User has translate.job.view permission.\n# @POST Returns list of preview records.\n# @COMPLEXITY 4\n\n@router.get(\"/preview/{session_id}/records\", response_model=list[PreviewRow])\nasync def get_preview_records(\n session_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get records for a preview session.\"\"\"\n logger.reason(f\"get_preview_records — Session: {session_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationPreviewRecord, TranslationPreviewSession\n session = db.query(TranslationPreviewSession).filter(TranslationPreviewSession.id == session_id).first()\n if not session:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f\"Preview session '{session_id}' not found\")\n records = (\n db.query(TranslationPreviewRecord)\n .filter(TranslationPreviewRecord.session_id == session_id)\n .all()\n )\n return [\n PreviewRow(\n id=r.id,\n source_sql=r.source_sql,\n target_sql=r.target_sql,\n source_object_type=r.source_object_type,\n source_object_id=r.source_object_id,\n source_object_name=r.source_object_name,\n status=r.status,\n feedback=r.feedback,\n )\n for r in records\n ]\n except HTTPException:\n raise\n except Exception as e:\n logger.explore(\"get_preview_records failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion get_preview_records\n\n# #endregion TranslatePreviewRoutesModule\n" }, { "contract_id": "preview_translation", @@ -23023,9 +22773,10 @@ "file_path": "backend/src/api/routes/translate/_preview_routes.py", "start_line": 26, "end_line": 59, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns a preview session with records and cost estimation.", "PRE": "User has translate.job.execute permission.", "PURPOSE": "Preview a translation before applying it.", @@ -23034,36 +22785,18 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "SIDE_EFFECT", - "message": "@SIDE_EFFECT is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region preview_translation [TYPE Function]\n# @BRIEF Preview a translation before applying it.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns a preview session with records and cost estimation.\n# @SIDE_EFFECT: Fetches sample data from Superset; calls LLM provider; creates DB rows.\n@router.post(\"/jobs/{job_id}/preview\", status_code=status.HTTP_201_CREATED)\nasync def preview_translation(\n job_id: str,\n payload: PreviewRequest = None,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Preview a translation before applying it.\"\"\"\n logger.reason(f\"preview_translation — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n if payload is None:\n payload = PreviewRequest()\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.preview_rows(\n job_id=job_id,\n sample_size=payload.sample_size,\n prompt_template=payload.prompt_template,\n env_id=payload.env_id,\n )\n return result\n except ValueError as e:\n logger.explore(\"preview_translation validation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"preview_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Preview failed: {e}\")\n# #endregion preview_translation\n" + "body": "# #region preview_translation [C:4] [TYPE Function]\n# @BRIEF Preview a translation before applying it.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns a preview session with records and cost estimation.\n# @SIDE_EFFECT: Fetches sample data from Superset; calls LLM provider; creates DB rows.\n@router.post(\"/jobs/{job_id}/preview\", status_code=status.HTTP_201_CREATED)\nasync def preview_translation(\n job_id: str,\n payload: PreviewRequest = None,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Preview a translation before applying it.\"\"\"\n logger.reason(f\"preview_translation — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n if payload is None:\n payload = PreviewRequest()\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.preview_rows(\n job_id=job_id,\n sample_size=payload.sample_size,\n prompt_template=payload.prompt_template,\n env_id=payload.env_id,\n )\n return result\n except ValueError as e:\n logger.explore(\"preview_translation validation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"preview_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Preview failed: {e}\")\n# #endregion preview_translation\n" }, { "contract_id": "update_preview_row", @@ -23071,9 +22804,10 @@ "file_path": "backend/src/api/routes/translate/_preview_routes.py", "start_line": 62, "end_line": 91, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Preview row status is updated.", "PRE": "User has translate.job.execute permission.", "PURPOSE": "Approve, edit, or reject a preview row (optionally per language)." @@ -23081,27 +22815,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region update_preview_row [TYPE Function]\n# @BRIEF Approve, edit, or reject a preview row (optionally per language).\n# @PRE: User has translate.job.execute permission.\n# @POST: Preview row status is updated.\n@router.put(\"/jobs/{job_id}/preview/rows/{row_key}\")\nasync def update_preview_row(\n job_id: str,\n row_key: str,\n payload: PreviewRowUpdate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Approve, edit, or reject a preview row (optionally per language).\"\"\"\n logger.reason(f\"update_preview_row — Job: {job_id}, Row: {row_key}, Action: {payload.action}, Lang: {payload.language_code}\", extra={\"src\": \"translate_routes\"})\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.update_preview_row(\n job_id=job_id,\n row_id=row_key,\n action=payload.action,\n translation=payload.translation,\n feedback=payload.feedback,\n language_code=payload.language_code,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion update_preview_row\n" + "body": "# #region update_preview_row [C:4] [TYPE Function]\n# @BRIEF Approve, edit, or reject a preview row (optionally per language).\n# @PRE: User has translate.job.execute permission.\n# @POST: Preview row status is updated.\n@router.put(\"/jobs/{job_id}/preview/rows/{row_key}\")\nasync def update_preview_row(\n job_id: str,\n row_key: str,\n payload: PreviewRowUpdate,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Approve, edit, or reject a preview row (optionally per language).\"\"\"\n logger.reason(f\"update_preview_row — Job: {job_id}, Row: {row_key}, Action: {payload.action}, Lang: {payload.language_code}\", extra={\"src\": \"translate_routes\"})\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.update_preview_row(\n job_id=job_id,\n row_id=row_key,\n action=payload.action,\n translation=payload.translation,\n feedback=payload.feedback,\n language_code=payload.language_code,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion update_preview_row\n" }, { "contract_id": "accept_preview_session", @@ -23109,9 +22843,10 @@ "file_path": "backend/src/api/routes/translate/_preview_routes.py", "start_line": 94, "end_line": 114, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Preview session is marked as APPLIED; full execution can proceed.", "PRE": "User has translate.job.execute permission. Job has an ACTIVE preview session.", "PURPOSE": "Accept a preview session, marking it as the quality gate for full execution." @@ -23119,27 +22854,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region accept_preview_session [TYPE Function]\n# @BRIEF Accept a preview session, marking it as the quality gate for full execution.\n# @PRE: User has translate.job.execute permission. Job has an ACTIVE preview session.\n# @POST: Preview session is marked as APPLIED; full execution can proceed.\n@router.post(\"/jobs/{job_id}/preview/accept\")\nasync def accept_preview_session(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Accept a preview session, enabling full translation execution.\"\"\"\n logger.reason(f\"accept_preview_session — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.accept_preview_session(job_id=job_id)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion accept_preview_session\n" + "body": "# #region accept_preview_session [C:4] [TYPE Function]\n# @BRIEF Accept a preview session, marking it as the quality gate for full execution.\n# @PRE: User has translate.job.execute permission. Job has an ACTIVE preview session.\n# @POST: Preview session is marked as APPLIED; full execution can proceed.\n@router.post(\"/jobs/{job_id}/preview/accept\")\nasync def accept_preview_session(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Accept a preview session, enabling full translation execution.\"\"\"\n logger.reason(f\"accept_preview_session — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.accept_preview_session(job_id=job_id)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion accept_preview_session\n" }, { "contract_id": "apply_preview", @@ -23147,9 +22882,10 @@ "file_path": "backend/src/api/routes/translate/_preview_routes.py", "start_line": 117, "end_line": 142, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Preview is applied.", "PRE": "User has translate.job.execute permission.", "PURPOSE": "Apply a preview session (alias for accept when accepting at session level)." @@ -23157,37 +22893,38 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region apply_preview [TYPE Function]\n# @BRIEF Apply a preview session (alias for accept when accepting at session level).\n# @PRE: User has translate.job.execute permission.\n# @POST: Preview is applied.\n@router.post(\"/preview/{session_id}/apply\")\nasync def apply_preview(\n session_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Apply a preview session by session ID.\"\"\"\n logger.reason(f\"apply_preview — Session: {session_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n # Find job_id from session\n from ....models.translate import TranslationPreviewSession\n session = db.query(TranslationPreviewSession).filter(TranslationPreviewSession.id == session_id).first()\n if not session:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f\"Preview session '{session_id}' not found\")\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.accept_preview_session(job_id=session.job_id)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion apply_preview\n" + "body": "# #region apply_preview [C:4] [TYPE Function]\n# @BRIEF Apply a preview session (alias for accept when accepting at session level).\n# @PRE: User has translate.job.execute permission.\n# @POST: Preview is applied.\n@router.post(\"/preview/{session_id}/apply\")\nasync def apply_preview(\n session_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Apply a preview session by session ID.\"\"\"\n logger.reason(f\"apply_preview — Session: {session_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n # Find job_id from session\n from ....models.translate import TranslationPreviewSession\n session = db.query(TranslationPreviewSession).filter(TranslationPreviewSession.id == session_id).first()\n if not session:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f\"Preview session '{session_id}' not found\")\n try:\n preview_service = TranslationPreview(db, config_manager, current_user.username)\n result = preview_service.accept_preview_session(job_id=session.job_id)\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion apply_preview\n" }, { "contract_id": "get_preview_records", "contract_type": "Function", "file_path": "backend/src/api/routes/translate/_preview_routes.py", "start_line": 149, - "end_line": 190, - "tier": "TIER_1", - "complexity": 1, + "end_line": 192, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "Returns list of preview records.", "PRE": "User has translate.job.view permission.", "PURPOSE": "Get records for a preview session." @@ -23195,34 +22932,34 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_preview_records [TYPE Function]\n# @BRIEF Get records for a preview session.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of preview records.\n@router.get(\"/preview/{session_id}/records\", response_model=list[PreviewRow])\nasync def get_preview_records(\n session_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get records for a preview session.\"\"\"\n logger.reason(f\"get_preview_records — Session: {session_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationPreviewRecord, TranslationPreviewSession\n session = db.query(TranslationPreviewSession).filter(TranslationPreviewSession.id == session_id).first()\n if not session:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f\"Preview session '{session_id}' not found\")\n records = (\n db.query(TranslationPreviewRecord)\n .filter(TranslationPreviewRecord.session_id == session_id)\n .all()\n )\n return [\n PreviewRow(\n id=r.id,\n source_sql=r.source_sql,\n target_sql=r.target_sql,\n source_object_type=r.source_object_type,\n source_object_id=r.source_object_id,\n source_object_name=r.source_object_name,\n status=r.status,\n feedback=r.feedback,\n )\n for r in records\n ]\n except HTTPException:\n raise\n except Exception as e:\n logger.explore(\"get_preview_records failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion get_preview_records\n" + "body": "# #region get_preview_records [C:4] [TYPE Function]\n# @BRIEF Get records for a preview session.\n# @PRE User has translate.job.view permission.\n# @POST Returns list of preview records.\n# @COMPLEXITY 4\n\n@router.get(\"/preview/{session_id}/records\", response_model=list[PreviewRow])\nasync def get_preview_records(\n session_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get records for a preview session.\"\"\"\n logger.reason(f\"get_preview_records — Session: {session_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationPreviewRecord, TranslationPreviewSession\n session = db.query(TranslationPreviewSession).filter(TranslationPreviewSession.id == session_id).first()\n if not session:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f\"Preview session '{session_id}' not found\")\n records = (\n db.query(TranslationPreviewRecord)\n .filter(TranslationPreviewRecord.session_id == session_id)\n .all()\n )\n return [\n PreviewRow(\n id=r.id,\n source_sql=r.source_sql,\n target_sql=r.target_sql,\n source_object_type=r.source_object_type,\n source_object_id=r.source_object_id,\n source_object_name=r.source_object_name,\n status=r.status,\n feedback=r.feedback,\n )\n for r in records\n ]\n except HTTPException:\n raise\n except Exception as e:\n logger.explore(\"get_preview_records failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion get_preview_records\n" }, { "contract_id": "TranslateRouterModule", "contract_type": "Module", "file_path": "backend/src/api/routes/translate/_router.py", "start_line": 1, - "end_line": 12, + "end_line": 13, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -23234,14 +22971,14 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateRouterModule [C:1] [TYPE Module] [SEMANTICS fastapi, translate, api]\n# @BRIEF APIRouter instance for translate routes.\n# @LAYER: API\n\nfrom fastapi import APIRouter\n\n# #region translate_router [TYPE Global]\n# @BRIEF APIRouter instance for all translate sub-routes.\nrouter = APIRouter(prefix=\"/api/translate\", tags=[\"translate\"])\n# #endregion translate_router\n\n# #endregion TranslateRouterModule\n" + "body": "# #region TranslateRouterModule [C:1] [TYPE Module] [SEMANTICS fastapi, translate, api]\n# @BRIEF APIRouter instance for translate routes.\n# @LAYER: API\n\nfrom fastapi import APIRouter\n\n# #region translate_router [TYPE Global]\n# @BRIEF APIRouter instance for all translate sub-routes.\n\nrouter = APIRouter(prefix=\"/api/translate\", tags=[\"translate\"])\n# #endregion translate_router\n\n# #endregion TranslateRouterModule\n" }, { "contract_id": "translate_router", "contract_type": "Global", "file_path": "backend/src/api/routes/translate/_router.py", "start_line": 7, - "end_line": 10, + "end_line": 11, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -23279,14 +23016,14 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region translate_router [TYPE Global]\n# @BRIEF APIRouter instance for all translate sub-routes.\nrouter = APIRouter(prefix=\"/api/translate\", tags=[\"translate\"])\n# #endregion translate_router\n" + "body": "# #region translate_router [TYPE Global]\n# @BRIEF APIRouter instance for all translate sub-routes.\n\nrouter = APIRouter(prefix=\"/api/translate\", tags=[\"translate\"])\n# #endregion translate_router\n" }, { "contract_id": "TranslateRunListRoutesModule", "contract_type": "Module", "file_path": "backend/src/api/routes/translate/_run_list_routes.py", "start_line": 1, - "end_line": 223, + "end_line": 225, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -23308,7 +23045,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateRunListRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, download, search]\n# @BRIEF Translation Run listing, detail, and CSV download routes (cross-job).\n# @LAYER: API\n\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.events import TranslationEventLog\nfrom ....plugins.translate.orchestrator import TranslationOrchestrator\nfrom ....schemas.auth import User\nfrom ._router import router\n\n# ============================================================\n# List Runs (cross-job)\n# ============================================================\n\n# #region list_runs [TYPE Function]\n# @BRIEF List all runs with cross-job filtering and pagination.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns paginated list of runs.\n@router.get(\"/runs\")\nasync def list_runs(\n job_id: str | None = Query(None),\n run_status: str | None = Query(None),\n trigger_type: str | None = Query(None),\n created_by: str | None = Query(None),\n date_from: str | None = Query(None),\n date_to: str | None = Query(None),\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List all runs with cross-job filtering and pagination.\"\"\"\n logger.reason(f\"list_runs — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationRun\n query = db.query(TranslationRun)\n\n if job_id:\n query = query.filter(TranslationRun.job_id == job_id)\n if run_status:\n query = query.filter(TranslationRun.status == run_status)\n if trigger_type:\n query = query.filter(TranslationRun.trigger_type == trigger_type)\n if created_by:\n query = query.filter(TranslationRun.created_by == created_by)\n if date_from:\n try:\n from datetime import datetime\n dt_from = datetime.fromisoformat(date_from)\n query = query.filter(TranslationRun.created_at >= dt_from)\n except ValueError:\n pass\n if date_to:\n try:\n from datetime import datetime\n dt_to = datetime.fromisoformat(date_to)\n query = query.filter(TranslationRun.created_at <= dt_to)\n except ValueError:\n pass\n\n total = query.count()\n runs = (\n query.order_by(TranslationRun.created_at.desc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n\n items = []\n for r in runs:\n # Gather per-language statistics from the run's language_stats relationship\n lang_stats = r.language_stats or []\n target_languages = [ls.language_code for ls in lang_stats]\n total_translated = sum((ls.translated_rows or 0) for ls in lang_stats)\n language_stats = {\n ls.language_code: {\n \"total_rows\": ls.total_rows or 0,\n \"translated_rows\": ls.translated_rows or 0,\n \"failed_rows\": ls.failed_rows or 0,\n \"skipped_rows\": ls.skipped_rows or 0,\n \"token_count\": ls.token_count or 0,\n \"estimated_cost\": ls.estimated_cost or 0.0,\n }\n for ls in lang_stats\n }\n\n items.append({\n \"id\": r.id,\n \"job_id\": r.job_id,\n \"status\": r.status,\n \"trigger_type\": r.trigger_type,\n \"started_at\": r.started_at.isoformat() if r.started_at else None,\n \"completed_at\": r.completed_at.isoformat() if r.completed_at else None,\n \"error_message\": r.error_message,\n \"total_records\": r.total_records or 0,\n \"successful_records\": r.successful_records or 0,\n \"failed_records\": r.failed_records or 0,\n \"skipped_records\": r.skipped_records or 0,\n \"insert_status\": r.insert_status,\n \"superset_execution_id\": r.superset_execution_id,\n \"config_hash\": r.config_hash,\n \"dict_snapshot_hash\": r.dict_snapshot_hash,\n \"created_by\": r.created_by,\n \"created_at\": r.created_at.isoformat() if r.created_at else None,\n \"target_languages\": target_languages,\n \"total_translated\": total_translated,\n \"language_stats\": language_stats,\n })\n\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n except Exception as e:\n logger.explore(\"list_runs failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion list_runs\n\n\n# ============================================================\n# Run Detail\n# ============================================================\n\n# #region get_run_detail [TYPE Function]\n# @BRIEF Get detailed run info with config_snapshot, records, events.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns run detail with records and events.\n@router.get(\"/runs/{run_id}/detail\")\nasync def get_run_detail(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get detailed run info with config_snapshot, records, events.\"\"\"\n logger.reason(f\"get_run_detail — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n status_info = orch.get_run_status(run_id)\n records = orch.get_run_records(run_id, page=1, page_size=50)\n events = TranslationEventLog(db).query_events(run_id=run_id)\n event_summary = TranslationEventLog(db).get_run_event_summary(run_id)\n\n # Get config snapshot from run\n from ....models.translate import TranslationRun\n run = db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n config_snapshot = run.config_snapshot if run else None\n\n return {\n **status_info,\n \"config_snapshot\": config_snapshot,\n \"config_hash\": run.config_hash if run else None,\n \"records\": records,\n \"events\": events,\n \"event_invariants\": event_summary,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_detail\n\n\n# ============================================================\n# Download Skipped CSV\n# ============================================================\n\n# #region download_skipped_csv [TYPE Function]\n# @BRIEF Download a CSV of skipped translation records from a run.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns CSV file of skipped records.\n@router.get(\"/runs/{run_id}/skipped.csv\")\nasync def download_skipped_csv(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Download skipped translation records as CSV.\"\"\"\n logger.reason(f\"download_skipped_csv — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n import csv\n import io\n\n from fastapi.responses import StreamingResponse\n\n from ....models.translate import TranslationRecord\n\n records = (\n db.query(TranslationRecord)\n .filter(\n TranslationRecord.run_id == run_id,\n TranslationRecord.status == \"SKIPPED\",\n )\n .all()\n )\n\n output = io.StringIO()\n writer = csv.writer(output)\n writer.writerow([\"id\", \"source_sql\", \"target_sql\", \"source_object_type\",\n \"source_object_id\", \"source_object_name\", \"error_message\"])\n for rec in records:\n writer.writerow([\n rec.id, rec.source_sql, rec.target_sql, rec.source_object_type,\n rec.source_object_id, rec.source_object_name, rec.error_message,\n ])\n\n output.seek(0)\n return StreamingResponse(\n iter([output.getvalue()]),\n media_type=\"text/csv\",\n headers={\"Content-Disposition\": f\"attachment; filename=skipped_{run_id}.csv\"},\n )\n except Exception as e:\n logger.explore(\"download_skipped_csv failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion download_skipped_csv\n\n# #endregion TranslateRunListRoutesModule\n" + "body": "# #region TranslateRunListRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, download, search]\n# @BRIEF Translation Run listing, detail, and CSV download routes (cross-job).\n# @LAYER: API\n\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.events import TranslationEventLog\nfrom ....plugins.translate.orchestrator import TranslationOrchestrator\nfrom ....schemas.auth import User\nfrom ._router import router\n\n# ============================================================\n# List Runs (cross-job)\n# ============================================================\n\n# #region list_runs [C:4] [TYPE Function]\n# @BRIEF List all runs with cross-job filtering and pagination.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns paginated list of runs.\n@router.get(\"/runs\")\nasync def list_runs(\n job_id: str | None = Query(None),\n run_status: str | None = Query(None),\n trigger_type: str | None = Query(None),\n created_by: str | None = Query(None),\n date_from: str | None = Query(None),\n date_to: str | None = Query(None),\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List all runs with cross-job filtering and pagination.\"\"\"\n logger.reason(f\"list_runs — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationRun\n query = db.query(TranslationRun)\n\n if job_id:\n query = query.filter(TranslationRun.job_id == job_id)\n if run_status:\n query = query.filter(TranslationRun.status == run_status)\n if trigger_type:\n query = query.filter(TranslationRun.trigger_type == trigger_type)\n if created_by:\n query = query.filter(TranslationRun.created_by == created_by)\n if date_from:\n try:\n from datetime import datetime\n dt_from = datetime.fromisoformat(date_from)\n query = query.filter(TranslationRun.created_at >= dt_from)\n except ValueError:\n pass\n if date_to:\n try:\n from datetime import datetime\n dt_to = datetime.fromisoformat(date_to)\n query = query.filter(TranslationRun.created_at <= dt_to)\n except ValueError:\n pass\n\n total = query.count()\n runs = (\n query.order_by(TranslationRun.created_at.desc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n\n items = []\n for r in runs:\n # Gather per-language statistics from the run's language_stats relationship\n lang_stats = r.language_stats or []\n target_languages = [ls.language_code for ls in lang_stats]\n total_translated = sum((ls.translated_rows or 0) for ls in lang_stats)\n language_stats = {\n ls.language_code: {\n \"total_rows\": ls.total_rows or 0,\n \"translated_rows\": ls.translated_rows or 0,\n \"failed_rows\": ls.failed_rows or 0,\n \"skipped_rows\": ls.skipped_rows or 0,\n \"token_count\": ls.token_count or 0,\n \"estimated_cost\": ls.estimated_cost or 0.0,\n }\n for ls in lang_stats\n }\n\n items.append({\n \"id\": r.id,\n \"job_id\": r.job_id,\n \"status\": r.status,\n \"trigger_type\": r.trigger_type,\n \"started_at\": r.started_at.isoformat() if r.started_at else None,\n \"completed_at\": r.completed_at.isoformat() if r.completed_at else None,\n \"error_message\": r.error_message,\n \"total_records\": r.total_records or 0,\n \"successful_records\": r.successful_records or 0,\n \"failed_records\": r.failed_records or 0,\n \"skipped_records\": r.skipped_records or 0,\n \"insert_status\": r.insert_status,\n \"superset_execution_id\": r.superset_execution_id,\n \"config_hash\": r.config_hash,\n \"dict_snapshot_hash\": r.dict_snapshot_hash,\n \"created_by\": r.created_by,\n \"created_at\": r.created_at.isoformat() if r.created_at else None,\n \"target_languages\": target_languages,\n \"total_translated\": total_translated,\n \"language_stats\": language_stats,\n })\n\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n except Exception as e:\n logger.explore(\"list_runs failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion list_runs\n\n\n# ============================================================\n# Run Detail\n# ============================================================\n\n# #region get_run_detail [C:4] [TYPE Function]\n# @BRIEF Get detailed run info with config_snapshot, records, events.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns run detail with records and events.\n@router.get(\"/runs/{run_id}/detail\")\nasync def get_run_detail(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get detailed run info with config_snapshot, records, events.\"\"\"\n logger.reason(f\"get_run_detail — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n status_info = orch.get_run_status(run_id)\n records = orch.get_run_records(run_id, page=1, page_size=50)\n events = TranslationEventLog(db).query_events(run_id=run_id)\n event_summary = TranslationEventLog(db).get_run_event_summary(run_id)\n\n # Get config snapshot from run\n from ....models.translate import TranslationRun\n run = db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n config_snapshot = run.config_snapshot if run else None\n\n return {\n **status_info,\n \"config_snapshot\": config_snapshot,\n \"config_hash\": run.config_hash if run else None,\n \"records\": records,\n \"events\": events,\n \"event_invariants\": event_summary,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_detail\n\n\n# ============================================================\n# Download Skipped CSV\n# ============================================================\n\n# #region download_skipped_csv [C:4] [TYPE Function]\n# @BRIEF Download a CSV of skipped translation records from a run.\n# @PRE User has translate.job.view permission.\n# @POST Returns CSV file of skipped records.\n# @COMPLEXITY 4\n\n@router.get(\"/runs/{run_id}/skipped.csv\")\nasync def download_skipped_csv(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Download skipped translation records as CSV.\"\"\"\n logger.reason(f\"download_skipped_csv — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n import csv\n import io\n\n from fastapi.responses import StreamingResponse\n\n from ....models.translate import TranslationRecord\n\n records = (\n db.query(TranslationRecord)\n .filter(\n TranslationRecord.run_id == run_id,\n TranslationRecord.status == \"SKIPPED\",\n )\n .all()\n )\n\n output = io.StringIO()\n writer = csv.writer(output)\n writer.writerow([\"id\", \"source_sql\", \"target_sql\", \"source_object_type\",\n \"source_object_id\", \"source_object_name\", \"error_message\"])\n for rec in records:\n writer.writerow([\n rec.id, rec.source_sql, rec.target_sql, rec.source_object_type,\n rec.source_object_id, rec.source_object_name, rec.error_message,\n ])\n\n output.seek(0)\n return StreamingResponse(\n iter([output.getvalue()]),\n media_type=\"text/csv\",\n headers={\"Content-Disposition\": f\"attachment; filename=skipped_{run_id}.csv\"},\n )\n except Exception as e:\n logger.explore(\"download_skipped_csv failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion download_skipped_csv\n\n# #endregion TranslateRunListRoutesModule\n" }, { "contract_id": "list_runs", @@ -23316,9 +23053,10 @@ "file_path": "backend/src/api/routes/translate/_run_list_routes.py", "start_line": 22, "end_line": 122, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns paginated list of runs.", "PRE": "User has translate.history.view permission.", "PURPOSE": "List all runs with cross-job filtering and pagination." @@ -23326,27 +23064,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region list_runs [TYPE Function]\n# @BRIEF List all runs with cross-job filtering and pagination.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns paginated list of runs.\n@router.get(\"/runs\")\nasync def list_runs(\n job_id: str | None = Query(None),\n run_status: str | None = Query(None),\n trigger_type: str | None = Query(None),\n created_by: str | None = Query(None),\n date_from: str | None = Query(None),\n date_to: str | None = Query(None),\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List all runs with cross-job filtering and pagination.\"\"\"\n logger.reason(f\"list_runs — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationRun\n query = db.query(TranslationRun)\n\n if job_id:\n query = query.filter(TranslationRun.job_id == job_id)\n if run_status:\n query = query.filter(TranslationRun.status == run_status)\n if trigger_type:\n query = query.filter(TranslationRun.trigger_type == trigger_type)\n if created_by:\n query = query.filter(TranslationRun.created_by == created_by)\n if date_from:\n try:\n from datetime import datetime\n dt_from = datetime.fromisoformat(date_from)\n query = query.filter(TranslationRun.created_at >= dt_from)\n except ValueError:\n pass\n if date_to:\n try:\n from datetime import datetime\n dt_to = datetime.fromisoformat(date_to)\n query = query.filter(TranslationRun.created_at <= dt_to)\n except ValueError:\n pass\n\n total = query.count()\n runs = (\n query.order_by(TranslationRun.created_at.desc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n\n items = []\n for r in runs:\n # Gather per-language statistics from the run's language_stats relationship\n lang_stats = r.language_stats or []\n target_languages = [ls.language_code for ls in lang_stats]\n total_translated = sum((ls.translated_rows or 0) for ls in lang_stats)\n language_stats = {\n ls.language_code: {\n \"total_rows\": ls.total_rows or 0,\n \"translated_rows\": ls.translated_rows or 0,\n \"failed_rows\": ls.failed_rows or 0,\n \"skipped_rows\": ls.skipped_rows or 0,\n \"token_count\": ls.token_count or 0,\n \"estimated_cost\": ls.estimated_cost or 0.0,\n }\n for ls in lang_stats\n }\n\n items.append({\n \"id\": r.id,\n \"job_id\": r.job_id,\n \"status\": r.status,\n \"trigger_type\": r.trigger_type,\n \"started_at\": r.started_at.isoformat() if r.started_at else None,\n \"completed_at\": r.completed_at.isoformat() if r.completed_at else None,\n \"error_message\": r.error_message,\n \"total_records\": r.total_records or 0,\n \"successful_records\": r.successful_records or 0,\n \"failed_records\": r.failed_records or 0,\n \"skipped_records\": r.skipped_records or 0,\n \"insert_status\": r.insert_status,\n \"superset_execution_id\": r.superset_execution_id,\n \"config_hash\": r.config_hash,\n \"dict_snapshot_hash\": r.dict_snapshot_hash,\n \"created_by\": r.created_by,\n \"created_at\": r.created_at.isoformat() if r.created_at else None,\n \"target_languages\": target_languages,\n \"total_translated\": total_translated,\n \"language_stats\": language_stats,\n })\n\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n except Exception as e:\n logger.explore(\"list_runs failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion list_runs\n" + "body": "# #region list_runs [C:4] [TYPE Function]\n# @BRIEF List all runs with cross-job filtering and pagination.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns paginated list of runs.\n@router.get(\"/runs\")\nasync def list_runs(\n job_id: str | None = Query(None),\n run_status: str | None = Query(None),\n trigger_type: str | None = Query(None),\n created_by: str | None = Query(None),\n date_from: str | None = Query(None),\n date_to: str | None = Query(None),\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"List all runs with cross-job filtering and pagination.\"\"\"\n logger.reason(f\"list_runs — User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationRun\n query = db.query(TranslationRun)\n\n if job_id:\n query = query.filter(TranslationRun.job_id == job_id)\n if run_status:\n query = query.filter(TranslationRun.status == run_status)\n if trigger_type:\n query = query.filter(TranslationRun.trigger_type == trigger_type)\n if created_by:\n query = query.filter(TranslationRun.created_by == created_by)\n if date_from:\n try:\n from datetime import datetime\n dt_from = datetime.fromisoformat(date_from)\n query = query.filter(TranslationRun.created_at >= dt_from)\n except ValueError:\n pass\n if date_to:\n try:\n from datetime import datetime\n dt_to = datetime.fromisoformat(date_to)\n query = query.filter(TranslationRun.created_at <= dt_to)\n except ValueError:\n pass\n\n total = query.count()\n runs = (\n query.order_by(TranslationRun.created_at.desc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n\n items = []\n for r in runs:\n # Gather per-language statistics from the run's language_stats relationship\n lang_stats = r.language_stats or []\n target_languages = [ls.language_code for ls in lang_stats]\n total_translated = sum((ls.translated_rows or 0) for ls in lang_stats)\n language_stats = {\n ls.language_code: {\n \"total_rows\": ls.total_rows or 0,\n \"translated_rows\": ls.translated_rows or 0,\n \"failed_rows\": ls.failed_rows or 0,\n \"skipped_rows\": ls.skipped_rows or 0,\n \"token_count\": ls.token_count or 0,\n \"estimated_cost\": ls.estimated_cost or 0.0,\n }\n for ls in lang_stats\n }\n\n items.append({\n \"id\": r.id,\n \"job_id\": r.job_id,\n \"status\": r.status,\n \"trigger_type\": r.trigger_type,\n \"started_at\": r.started_at.isoformat() if r.started_at else None,\n \"completed_at\": r.completed_at.isoformat() if r.completed_at else None,\n \"error_message\": r.error_message,\n \"total_records\": r.total_records or 0,\n \"successful_records\": r.successful_records or 0,\n \"failed_records\": r.failed_records or 0,\n \"skipped_records\": r.skipped_records or 0,\n \"insert_status\": r.insert_status,\n \"superset_execution_id\": r.superset_execution_id,\n \"config_hash\": r.config_hash,\n \"dict_snapshot_hash\": r.dict_snapshot_hash,\n \"created_by\": r.created_by,\n \"created_at\": r.created_at.isoformat() if r.created_at else None,\n \"target_languages\": target_languages,\n \"total_translated\": total_translated,\n \"language_stats\": language_stats,\n })\n\n return {\"items\": items, \"total\": total, \"page\": page, \"page_size\": page_size}\n except Exception as e:\n logger.explore(\"list_runs failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion list_runs\n" }, { "contract_id": "get_run_detail", @@ -23354,9 +23092,10 @@ "file_path": "backend/src/api/routes/translate/_run_list_routes.py", "start_line": 129, "end_line": 165, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns run detail with records and events.", "PRE": "User has translate.history.view permission.", "PURPOSE": "Get detailed run info with config_snapshot, records, events." @@ -23364,37 +23103,38 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_run_detail [TYPE Function]\n# @BRIEF Get detailed run info with config_snapshot, records, events.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns run detail with records and events.\n@router.get(\"/runs/{run_id}/detail\")\nasync def get_run_detail(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get detailed run info with config_snapshot, records, events.\"\"\"\n logger.reason(f\"get_run_detail — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n status_info = orch.get_run_status(run_id)\n records = orch.get_run_records(run_id, page=1, page_size=50)\n events = TranslationEventLog(db).query_events(run_id=run_id)\n event_summary = TranslationEventLog(db).get_run_event_summary(run_id)\n\n # Get config snapshot from run\n from ....models.translate import TranslationRun\n run = db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n config_snapshot = run.config_snapshot if run else None\n\n return {\n **status_info,\n \"config_snapshot\": config_snapshot,\n \"config_hash\": run.config_hash if run else None,\n \"records\": records,\n \"events\": events,\n \"event_invariants\": event_summary,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_detail\n" + "body": "# #region get_run_detail [C:4] [TYPE Function]\n# @BRIEF Get detailed run info with config_snapshot, records, events.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns run detail with records and events.\n@router.get(\"/runs/{run_id}/detail\")\nasync def get_run_detail(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get detailed run info with config_snapshot, records, events.\"\"\"\n logger.reason(f\"get_run_detail — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n status_info = orch.get_run_status(run_id)\n records = orch.get_run_records(run_id, page=1, page_size=50)\n events = TranslationEventLog(db).query_events(run_id=run_id)\n event_summary = TranslationEventLog(db).get_run_event_summary(run_id)\n\n # Get config snapshot from run\n from ....models.translate import TranslationRun\n run = db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n config_snapshot = run.config_snapshot if run else None\n\n return {\n **status_info,\n \"config_snapshot\": config_snapshot,\n \"config_hash\": run.config_hash if run else None,\n \"records\": records,\n \"events\": events,\n \"event_invariants\": event_summary,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_detail\n" }, { "contract_id": "download_skipped_csv", "contract_type": "Function", "file_path": "backend/src/api/routes/translate/_run_list_routes.py", "start_line": 172, - "end_line": 221, - "tier": "TIER_1", - "complexity": 1, + "end_line": 223, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "Returns CSV file of skipped records.", "PRE": "User has translate.job.view permission.", "PURPOSE": "Download a CSV of skipped translation records from a run." @@ -23402,34 +23142,34 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region download_skipped_csv [TYPE Function]\n# @BRIEF Download a CSV of skipped translation records from a run.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns CSV file of skipped records.\n@router.get(\"/runs/{run_id}/skipped.csv\")\nasync def download_skipped_csv(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Download skipped translation records as CSV.\"\"\"\n logger.reason(f\"download_skipped_csv — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n import csv\n import io\n\n from fastapi.responses import StreamingResponse\n\n from ....models.translate import TranslationRecord\n\n records = (\n db.query(TranslationRecord)\n .filter(\n TranslationRecord.run_id == run_id,\n TranslationRecord.status == \"SKIPPED\",\n )\n .all()\n )\n\n output = io.StringIO()\n writer = csv.writer(output)\n writer.writerow([\"id\", \"source_sql\", \"target_sql\", \"source_object_type\",\n \"source_object_id\", \"source_object_name\", \"error_message\"])\n for rec in records:\n writer.writerow([\n rec.id, rec.source_sql, rec.target_sql, rec.source_object_type,\n rec.source_object_id, rec.source_object_name, rec.error_message,\n ])\n\n output.seek(0)\n return StreamingResponse(\n iter([output.getvalue()]),\n media_type=\"text/csv\",\n headers={\"Content-Disposition\": f\"attachment; filename=skipped_{run_id}.csv\"},\n )\n except Exception as e:\n logger.explore(\"download_skipped_csv failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion download_skipped_csv\n" + "body": "# #region download_skipped_csv [C:4] [TYPE Function]\n# @BRIEF Download a CSV of skipped translation records from a run.\n# @PRE User has translate.job.view permission.\n# @POST Returns CSV file of skipped records.\n# @COMPLEXITY 4\n\n@router.get(\"/runs/{run_id}/skipped.csv\")\nasync def download_skipped_csv(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Download skipped translation records as CSV.\"\"\"\n logger.reason(f\"download_skipped_csv — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n import csv\n import io\n\n from fastapi.responses import StreamingResponse\n\n from ....models.translate import TranslationRecord\n\n records = (\n db.query(TranslationRecord)\n .filter(\n TranslationRecord.run_id == run_id,\n TranslationRecord.status == \"SKIPPED\",\n )\n .all()\n )\n\n output = io.StringIO()\n writer = csv.writer(output)\n writer.writerow([\"id\", \"source_sql\", \"target_sql\", \"source_object_type\",\n \"source_object_id\", \"source_object_name\", \"error_message\"])\n for rec in records:\n writer.writerow([\n rec.id, rec.source_sql, rec.target_sql, rec.source_object_type,\n rec.source_object_id, rec.source_object_name, rec.error_message,\n ])\n\n output.seek(0)\n return StreamingResponse(\n iter([output.getvalue()]),\n media_type=\"text/csv\",\n headers={\"Content-Disposition\": f\"attachment; filename=skipped_{run_id}.csv\"},\n )\n except Exception as e:\n logger.explore(\"download_skipped_csv failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion download_skipped_csv\n" }, { "contract_id": "TranslateRunRoutesModule", "contract_type": "Module", "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 1, - "end_line": 552, + "end_line": 554, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -23451,7 +23191,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateRunRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, search, execution, history]\n# @BRIEF Translation Run execution, history, status, records and batches routes.\n# @LAYER: API\n\nfrom datetime import UTC, datetime\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import SessionLocal, get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.orchestrator import TranslationOrchestrator\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n BulkFindReplaceRequest,\n InlineEditRequest,\n OverrideLanguageRequest,\n)\nfrom ._helpers import _run_to_response\nfrom ._router import router\n\n# ============================================================\n# Translation Run / Execute\n# ============================================================\n\n# #region run_translation [TYPE Function]\n# @BRIEF Execute a translation job (trigger a run).\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the created translation run.\n@router.post(\"/jobs/{job_id}/run\", status_code=status.HTTP_201_CREATED)\ndef run_translation(\n job_id: str,\n full_translation: bool = Query(False, description=\"Translate ALL rows (skip new-key-only filter)\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Execute a translation job (trigger a run).\n\n - Normal run (default): translates only new/changed rows\n - Full translation (full_translation=true): re-translates ALL rows from the datasource\n \"\"\"\n logger.reason(\n f\"run_translation — Job: {job_id}, User: {current_user.username}, full: {full_translation}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.start_run(job_id=job_id, is_scheduled=False, full_translation=full_translation)\n # The request-scoped db session will be closed after this handler returns.\n # The background thread must use its OWN session to avoid operating on a\n # closed or expunged session.\n import threading\n\n def _background_execute():\n from ....models.translate import TranslationRun as TRModel\n bg_db = None\n try:\n bg_db = SessionLocal()\n bg_orch = TranslationOrchestrator(\n bg_db, config_manager,\n current_user.username if current_user else None,\n )\n # Re-fetch the run within the background session to get a fresh,\n # attached object\n bg_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()\n if bg_run is None:\n logger.explore(\n \"Background execute: run not found\",\n extra={\"src\": \"translate_routes\", \"run_id\": run.id},\n )\n return\n\n # execute_run internally calls self.db.commit()\n bg_orch.execute_run(bg_run)\n\n # ----- VERIFICATION -----\n # After execute_run the run object from bg_db is detached after\n # commit. Re-query to verify the status was persisted in the\n # database and is a terminal state.\n check_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()\n if check_run and check_run.status in (\"COMPLETED\", \"FAILED\", \"CANCELLED\"):\n logger.reason(\n \"Background execute verified\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"status\": check_run.status,\n },\n )\n else:\n # execute_run appeared to succeed but the run is still in a\n # non-terminal state — manually fail it so the frontend\n # polling can detect the terminal state and stop spinning.\n actual_status = check_run.status if check_run else \"NOT_FOUND\"\n logger.explore(\n \"Background execute: run not in terminal state after commit\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"status\": actual_status,\n },\n )\n if check_run:\n check_run.status = \"FAILED\"\n check_run.error_message = (\n \"Background execution did not reach terminal state \"\n f\"(was: {actual_status})\"\n )\n bg_db.commit()\n\n except Exception as bg_err:\n logger.explore(\n \"Background execute failed\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"error\": str(bg_err),\n },\n )\n # Mark the run as FAILED so the frontend polling can detect a\n # terminal state and stop spinning.\n try:\n fb_db = SessionLocal()\n try:\n fb_run = fb_db.query(TRModel).filter(TRModel.id == run.id).first()\n if fb_run:\n fb_run.status = \"FAILED\"\n fb_run.error_message = f\"Background execute error: {bg_err}\"\n fb_run.completed_at = datetime.now(UTC)\n fb_db.commit()\n logger.reason(\n \"Background execute: run marked FAILED\",\n extra={\"src\": \"translate_routes\", \"run_id\": run.id},\n )\n finally:\n fb_db.close()\n except Exception as fb_err:\n logger.explore(\n \"Background execute: unable to mark run FAILED\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"error\": str(fb_err),\n },\n )\n finally:\n if bg_db is not None:\n bg_db.close()\n\n threading.Thread(\n target=_background_execute,\n daemon=True,\n ).start()\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"run_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Run failed: {e}\")\n# #endregion run_translation\n\n\n# #region retry_run [TYPE Function]\n# @BRIEF Retry failed batches in a translation run.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the updated translation run.\n@router.post(\"/runs/{run_id}/retry\")\ndef retry_run(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Retry failed batches in a translation run.\"\"\"\n logger.reason(f\"retry_run — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.retry_failed_batches(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"retry_run failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Retry failed: {e}\")\n# #endregion retry_run\n\n\n# #region retry_insert [TYPE Function]\n# @BRIEF Retry the SQL insert phase for a completed run.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the updated run.\n@router.post(\"/runs/{run_id}/retry-insert\")\ndef retry_insert(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Retry the SQL insert phase for a completed run.\"\"\"\n logger.reason(f\"retry_insert — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.retry_insert(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"retry_insert failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Retry insert failed: {e}\")\n# #endregion retry_insert\n\n\n# #region cancel_run [TYPE Function]\n# @BRIEF Cancel a running translation.\n# @PRE: User has translate.job.execute permission.\n# @POST: Run is cancelled.\n@router.post(\"/runs/{run_id}/cancel\")\ndef cancel_run(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Cancel a running translation.\"\"\"\n logger.reason(f\"cancel_run — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.cancel_run(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion cancel_run\n\n\n# #region get_run_history [TYPE Function]\n# @BRIEF Get run history for a translation job.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns list of runs.\n@router.get(\"/jobs/{job_id}/runs\")\ndef get_run_history(\n job_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get run history for a translation job.\"\"\"\n logger.reason(f\"get_run_history — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n total, runs = orch.get_run_history(job_id, page=page, page_size=page_size)\n return {\"items\": runs, \"total\": total, \"page\": page, \"page_size\": page_size}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_history\n\n\n# #region get_run_status [TYPE Function]\n# @BRIEF Get status and statistics for a translation run.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns run details with statistics.\n@router.get(\"/runs/{run_id}\")\ndef get_run_status(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get status and statistics for a translation run.\"\"\"\n logger.reason(f\"get_run_status — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n return orch.get_run_status(run_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_status\n\n\n# #region get_run_records [TYPE Function]\n# @BRIEF Get paginated records for a translation run.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns paginated records.\n@router.get(\"/runs/{run_id}/records\")\ndef get_run_records(\n run_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(50, ge=1, le=500),\n status: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get paginated records for a translation run.\"\"\"\n logger.reason(f\"get_run_records — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n return orch.get_run_records(run_id, page=page, page_size=page_size, status_filter=status)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_records\n\n\n# ============================================================\n# Batches\n# ============================================================\n\n# #region get_batches [TYPE Function]\n# @BRIEF Get batches for a translation run.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of batches.\n@router.get(\"/runs/{run_id}/batches\")\ndef get_batches(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get batches for a translation run.\"\"\"\n logger.reason(f\"get_batches — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationBatch\n batches = (\n db.query(TranslationBatch)\n .filter(TranslationBatch.run_id == run_id)\n .order_by(TranslationBatch.batch_index.asc())\n .all()\n )\n return [\n {\n \"id\": b.id,\n \"run_id\": b.run_id,\n \"batch_index\": b.batch_index,\n \"status\": b.status,\n \"total_records\": b.total_records or 0,\n \"successful_records\": b.successful_records or 0,\n \"failed_records\": b.failed_records or 0,\n \"started_at\": b.started_at.isoformat() if b.started_at else None,\n \"completed_at\": b.completed_at.isoformat() if b.completed_at else None,\n \"created_at\": b.created_at.isoformat() if b.created_at else None,\n }\n for b in batches\n ]\n except Exception as e:\n logger.explore(\"get_batches failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion get_batches\n\n# ============================================================\n# Manual Language Override\n# ============================================================\n\n# #region override_detected_language [TYPE Function]\n# @BRIEF Manually override the detected source language for a specific translation language entry.\n# @PRE: User has translate.job.execute permission. Run, record, and language entry exist.\n# @POST: TranslationLanguage.source_language_detected is updated; language_overridden is set to True.\n# @SIDE_EFFECT: DB write.\n@router.put(\"/runs/{run_id}/records/{record_id}/languages/{language_code}/override-language\")\ndef override_detected_language(\n run_id: str,\n record_id: str,\n language_code: str,\n payload: OverrideLanguageRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Manually override the detected source language for a translation entry.\"\"\"\n logger.reason(f\"override_detected_language — Run: {run_id}, Record: {record_id}, Lang: {language_code}, Override: {payload.source_language}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationLanguage, TranslationRecord\n\n # Verify the record exists and belongs to the run\n record = (\n db.query(TranslationRecord)\n .filter(\n TranslationRecord.id == record_id,\n TranslationRecord.run_id == run_id,\n )\n .first()\n )\n if not record:\n raise HTTPException(\n status_code=status.HTTP_404_NOT_FOUND,\n detail=f\"Translation record '{record_id}' not found in run '{run_id}'\",\n )\n\n # Find the language entry\n lang_entry = (\n db.query(TranslationLanguage)\n .filter(\n TranslationLanguage.record_id == record_id,\n TranslationLanguage.language_code == language_code,\n )\n .first()\n )\n if not lang_entry:\n raise HTTPException(\n status_code=status.HTTP_404_NOT_FOUND,\n detail=f\"Language entry '{language_code}' not found for record '{record_id}'\",\n )\n\n # Update the language entry\n lang_entry.source_language_detected = payload.source_language\n lang_entry.language_overridden = True\n lang_entry.needs_review = False # Override clears the review flag\n db.commit()\n db.refresh(lang_entry)\n\n logger.reason(\"Language override applied\", {\n \"run_id\": run_id,\n \"record_id\": record_id,\n \"language_code\": language_code,\n \"new_source_language\": payload.source_language,\n \"overridden_by\": current_user.username,\n })\n\n from ....schemas.translate import TranslationLanguageResponse\n return TranslationLanguageResponse.model_validate(lang_entry)\n except HTTPException:\n raise\n except Exception as e:\n logger.explore(\"override_detected_language failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion override_detected_language\n\n\n# ============================================================\n# Inline Correction\n# ============================================================\n\n# #region inline_edit_translation [C:3] [TYPE Function] [SEMANTICS api,translate,correction]\n# @BRIEF Apply an inline correction to a translated value on a completed run result.\n# @PRE: User has translate.job.execute permission. Run, record, and language entry exist.\n# @POST: TranslationLanguage.final_value and user_edit are updated. Optional dictionary submission.\n@router.put(\"/runs/{run_id}/records/{record_id}/languages/{language_code}\")\ndef inline_edit_translation(\n run_id: str,\n record_id: str,\n language_code: str,\n payload: InlineEditRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Apply an inline correction to a translated value.\"\"\"\n logger.reason(\n f\"inline_edit_translation — Run: {run_id}, Record: {record_id}, \"\n f\"Lang: {language_code}, User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n from ....plugins.translate.service import InlineCorrectionService\n\n result = InlineCorrectionService.apply_inline_edit(\n db=db,\n run_id=run_id,\n record_id=record_id,\n language_code=language_code,\n final_value=payload.final_value,\n submit_to_dictionary=payload.submit_to_dictionary,\n dictionary_id=payload.dictionary_id,\n current_user=current_user.username,\n context_data_override=payload.context_data_override,\n usage_notes=payload.usage_notes,\n keep_context=payload.keep_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"inline_edit_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion inline_edit_translation\n\n\n# ============================================================\n# Bulk Find-and-Replace\n# ============================================================\n\n# #region bulk_find_replace [C:3] [TYPE Function] [SEMANTICS api,translate,bulk,replace]\n# @BRIEF Perform bulk find-and-replace on translated values within a run.\n# @PRE: User has translate.job.execute permission. Run exists.\n# @POST: If preview=false, matching translations are updated. Optional dictionary submission.\n@router.post(\"/runs/{run_id}/bulk-replace\")\ndef bulk_find_replace(\n run_id: str,\n payload: BulkFindReplaceRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Perform bulk find-and-replace on translated values within a run.\"\"\"\n logger.reason(\n f\"bulk_find_replace — Run: {run_id}, Pattern: '{payload.find_pattern}', \"\n f\"Regex: {payload.is_regex}, Lang: {payload.target_language}, \"\n f\"User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n from ....plugins.translate.service import BulkFindReplaceService\n\n if payload.preview:\n # Preview mode: return matches without applying\n preview = BulkFindReplaceService.preview(\n db=db,\n run_id=run_id,\n pattern=payload.find_pattern,\n is_regex=payload.is_regex,\n replacement_text=payload.replacement_text,\n target_language=payload.target_language,\n )\n return {\n \"rows_affected\": len(preview),\n \"corrections_submitted\": 0,\n \"preview\": preview,\n }\n\n # Apply mode: perform replacements\n result = BulkFindReplaceService.apply(\n db=db,\n run_id=run_id,\n pattern=payload.find_pattern,\n is_regex=payload.is_regex,\n replacement_text=payload.replacement_text,\n target_language=payload.target_language,\n submit_to_dictionary=payload.submit_to_dictionary,\n dictionary_id=payload.dictionary_id,\n usage_notes=payload.usage_notes,\n current_user=current_user.username,\n submit_to_dictionary_with_context=payload.submit_to_dictionary_with_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"bulk_find_replace failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion bulk_find_replace\n\n\n# #endregion TranslateRunRoutesModule\n" + "body": "# #region TranslateRunRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, search, execution, history]\n# @BRIEF Translation Run execution, history, status, records and batches routes.\n# @LAYER: API\n\nfrom datetime import UTC, datetime\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import SessionLocal, get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.orchestrator import TranslationOrchestrator\nfrom ....schemas.auth import User\nfrom ....schemas.translate import (\n BulkFindReplaceRequest,\n InlineEditRequest,\n OverrideLanguageRequest,\n)\nfrom ._helpers import _run_to_response\nfrom ._router import router\n\n# ============================================================\n# Translation Run / Execute\n# ============================================================\n\n# #region run_translation [C:4] [TYPE Function]\n# @BRIEF Execute a translation job (trigger a run).\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the created translation run.\n@router.post(\"/jobs/{job_id}/run\", status_code=status.HTTP_201_CREATED)\ndef run_translation(\n job_id: str,\n full_translation: bool = Query(False, description=\"Translate ALL rows (skip new-key-only filter)\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Execute a translation job (trigger a run).\n\n - Normal run (default): translates only new/changed rows\n - Full translation (full_translation=true): re-translates ALL rows from the datasource\n \"\"\"\n logger.reason(\n f\"run_translation — Job: {job_id}, User: {current_user.username}, full: {full_translation}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.start_run(job_id=job_id, is_scheduled=False, full_translation=full_translation)\n # The request-scoped db session will be closed after this handler returns.\n # The background thread must use its OWN session to avoid operating on a\n # closed or expunged session.\n import threading\n\n def _background_execute():\n from ....models.translate import TranslationRun as TRModel\n bg_db = None\n try:\n bg_db = SessionLocal()\n bg_orch = TranslationOrchestrator(\n bg_db, config_manager,\n current_user.username if current_user else None,\n )\n # Re-fetch the run within the background session to get a fresh,\n # attached object\n bg_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()\n if bg_run is None:\n logger.explore(\n \"Background execute: run not found\",\n extra={\"src\": \"translate_routes\", \"run_id\": run.id},\n )\n return\n\n # execute_run internally calls self.db.commit()\n bg_orch.execute_run(bg_run)\n\n # ----- VERIFICATION -----\n # After execute_run the run object from bg_db is detached after\n # commit. Re-query to verify the status was persisted in the\n # database and is a terminal state.\n check_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()\n if check_run and check_run.status in (\"COMPLETED\", \"FAILED\", \"CANCELLED\"):\n logger.reason(\n \"Background execute verified\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"status\": check_run.status,\n },\n )\n else:\n # execute_run appeared to succeed but the run is still in a\n # non-terminal state — manually fail it so the frontend\n # polling can detect the terminal state and stop spinning.\n actual_status = check_run.status if check_run else \"NOT_FOUND\"\n logger.explore(\n \"Background execute: run not in terminal state after commit\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"status\": actual_status,\n },\n )\n if check_run:\n check_run.status = \"FAILED\"\n check_run.error_message = (\n \"Background execution did not reach terminal state \"\n f\"(was: {actual_status})\"\n )\n bg_db.commit()\n\n except Exception as bg_err:\n logger.explore(\n \"Background execute failed\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"error\": str(bg_err),\n },\n )\n # Mark the run as FAILED so the frontend polling can detect a\n # terminal state and stop spinning.\n try:\n fb_db = SessionLocal()\n try:\n fb_run = fb_db.query(TRModel).filter(TRModel.id == run.id).first()\n if fb_run:\n fb_run.status = \"FAILED\"\n fb_run.error_message = f\"Background execute error: {bg_err}\"\n fb_run.completed_at = datetime.now(UTC)\n fb_db.commit()\n logger.reason(\n \"Background execute: run marked FAILED\",\n extra={\"src\": \"translate_routes\", \"run_id\": run.id},\n )\n finally:\n fb_db.close()\n except Exception as fb_err:\n logger.explore(\n \"Background execute: unable to mark run FAILED\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"error\": str(fb_err),\n },\n )\n finally:\n if bg_db is not None:\n bg_db.close()\n\n threading.Thread(\n target=_background_execute,\n daemon=True,\n ).start()\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"run_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Run failed: {e}\")\n# #endregion run_translation\n\n\n# #region retry_run [C:4] [TYPE Function]\n# @BRIEF Retry failed batches in a translation run.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the updated translation run.\n@router.post(\"/runs/{run_id}/retry\")\ndef retry_run(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Retry failed batches in a translation run.\"\"\"\n logger.reason(f\"retry_run — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.retry_failed_batches(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"retry_run failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Retry failed: {e}\")\n# #endregion retry_run\n\n\n# #region retry_insert [C:4] [TYPE Function]\n# @BRIEF Retry the SQL insert phase for a completed run.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the updated run.\n@router.post(\"/runs/{run_id}/retry-insert\")\ndef retry_insert(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Retry the SQL insert phase for a completed run.\"\"\"\n logger.reason(f\"retry_insert — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.retry_insert(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"retry_insert failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Retry insert failed: {e}\")\n# #endregion retry_insert\n\n\n# #region cancel_run [C:4] [TYPE Function]\n# @BRIEF Cancel a running translation.\n# @PRE: User has translate.job.execute permission.\n# @POST: Run is cancelled.\n@router.post(\"/runs/{run_id}/cancel\")\ndef cancel_run(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Cancel a running translation.\"\"\"\n logger.reason(f\"cancel_run — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.cancel_run(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion cancel_run\n\n\n# #region get_run_history [C:4] [TYPE Function]\n# @BRIEF Get run history for a translation job.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns list of runs.\n@router.get(\"/jobs/{job_id}/runs\")\ndef get_run_history(\n job_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get run history for a translation job.\"\"\"\n logger.reason(f\"get_run_history — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n total, runs = orch.get_run_history(job_id, page=page, page_size=page_size)\n return {\"items\": runs, \"total\": total, \"page\": page, \"page_size\": page_size}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_history\n\n\n# #region get_run_status [C:4] [TYPE Function]\n# @BRIEF Get status and statistics for a translation run.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns run details with statistics.\n@router.get(\"/runs/{run_id}\")\ndef get_run_status(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get status and statistics for a translation run.\"\"\"\n logger.reason(f\"get_run_status — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n return orch.get_run_status(run_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_status\n\n\n# #region get_run_records [C:4] [TYPE Function]\n# @BRIEF Get paginated records for a translation run.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns paginated records.\n@router.get(\"/runs/{run_id}/records\")\ndef get_run_records(\n run_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(50, ge=1, le=500),\n status: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get paginated records for a translation run.\"\"\"\n logger.reason(f\"get_run_records — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n return orch.get_run_records(run_id, page=page, page_size=page_size, status_filter=status)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_records\n\n\n# ============================================================\n# Batches\n# ============================================================\n\n# #region get_batches [C:4] [TYPE Function]\n# @BRIEF Get batches for a translation run.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of batches.\n@router.get(\"/runs/{run_id}/batches\")\ndef get_batches(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get batches for a translation run.\"\"\"\n logger.reason(f\"get_batches — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationBatch\n batches = (\n db.query(TranslationBatch)\n .filter(TranslationBatch.run_id == run_id)\n .order_by(TranslationBatch.batch_index.asc())\n .all()\n )\n return [\n {\n \"id\": b.id,\n \"run_id\": b.run_id,\n \"batch_index\": b.batch_index,\n \"status\": b.status,\n \"total_records\": b.total_records or 0,\n \"successful_records\": b.successful_records or 0,\n \"failed_records\": b.failed_records or 0,\n \"started_at\": b.started_at.isoformat() if b.started_at else None,\n \"completed_at\": b.completed_at.isoformat() if b.completed_at else None,\n \"created_at\": b.created_at.isoformat() if b.created_at else None,\n }\n for b in batches\n ]\n except Exception as e:\n logger.explore(\"get_batches failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion get_batches\n\n# ============================================================\n# Manual Language Override\n# ============================================================\n\n# #region override_detected_language [C:4] [TYPE Function]\n# @BRIEF Manually override the detected source language for a specific translation language entry.\n# @PRE: User has translate.job.execute permission. Run, record, and language entry exist.\n# @POST: TranslationLanguage.source_language_detected is updated; language_overridden is set to True.\n# @SIDE_EFFECT: DB write.\n@router.put(\"/runs/{run_id}/records/{record_id}/languages/{language_code}/override-language\")\ndef override_detected_language(\n run_id: str,\n record_id: str,\n language_code: str,\n payload: OverrideLanguageRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Manually override the detected source language for a translation entry.\"\"\"\n logger.reason(f\"override_detected_language — Run: {run_id}, Record: {record_id}, Lang: {language_code}, Override: {payload.source_language}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationLanguage, TranslationRecord\n\n # Verify the record exists and belongs to the run\n record = (\n db.query(TranslationRecord)\n .filter(\n TranslationRecord.id == record_id,\n TranslationRecord.run_id == run_id,\n )\n .first()\n )\n if not record:\n raise HTTPException(\n status_code=status.HTTP_404_NOT_FOUND,\n detail=f\"Translation record '{record_id}' not found in run '{run_id}'\",\n )\n\n # Find the language entry\n lang_entry = (\n db.query(TranslationLanguage)\n .filter(\n TranslationLanguage.record_id == record_id,\n TranslationLanguage.language_code == language_code,\n )\n .first()\n )\n if not lang_entry:\n raise HTTPException(\n status_code=status.HTTP_404_NOT_FOUND,\n detail=f\"Language entry '{language_code}' not found for record '{record_id}'\",\n )\n\n # Update the language entry\n lang_entry.source_language_detected = payload.source_language\n lang_entry.language_overridden = True\n lang_entry.needs_review = False # Override clears the review flag\n db.commit()\n db.refresh(lang_entry)\n\n logger.reason(\"Language override applied\", {\n \"run_id\": run_id,\n \"record_id\": record_id,\n \"language_code\": language_code,\n \"new_source_language\": payload.source_language,\n \"overridden_by\": current_user.username,\n })\n\n from ....schemas.translate import TranslationLanguageResponse\n return TranslationLanguageResponse.model_validate(lang_entry)\n except HTTPException:\n raise\n except Exception as e:\n logger.explore(\"override_detected_language failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion override_detected_language\n\n\n# ============================================================\n# Inline Correction\n# ============================================================\n\n# #region inline_edit_translation [C:4] [TYPE Function] [SEMANTICS api,translate,correction]\n# @BRIEF Apply an inline correction to a translated value on a completed run result.\n# @PRE: User has translate.job.execute permission. Run, record, and language entry exist.\n# @POST: TranslationLanguage.final_value and user_edit are updated. Optional dictionary submission.\n@router.put(\"/runs/{run_id}/records/{record_id}/languages/{language_code}\")\ndef inline_edit_translation(\n run_id: str,\n record_id: str,\n language_code: str,\n payload: InlineEditRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Apply an inline correction to a translated value.\"\"\"\n logger.reason(\n f\"inline_edit_translation — Run: {run_id}, Record: {record_id}, \"\n f\"Lang: {language_code}, User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n from ....plugins.translate.service import InlineCorrectionService\n\n result = InlineCorrectionService.apply_inline_edit(\n db=db,\n run_id=run_id,\n record_id=record_id,\n language_code=language_code,\n final_value=payload.final_value,\n submit_to_dictionary=payload.submit_to_dictionary,\n dictionary_id=payload.dictionary_id,\n current_user=current_user.username,\n context_data_override=payload.context_data_override,\n usage_notes=payload.usage_notes,\n keep_context=payload.keep_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"inline_edit_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion inline_edit_translation\n\n\n# ============================================================\n# Bulk Find-and-Replace\n# ============================================================\n\n# #region bulk_find_replace [C:4] [TYPE Function] [SEMANTICS api,translate,bulk,replace]\n# @BRIEF Perform bulk find-and-replace on translated values within a run.\n# @PRE User has translate.job.execute permission. Run exists.\n# @POST If preview=false, matching translations are updated. Optional dictionary submission.\n# @COMPLEXITY 4\n\n@router.post(\"/runs/{run_id}/bulk-replace\")\ndef bulk_find_replace(\n run_id: str,\n payload: BulkFindReplaceRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Perform bulk find-and-replace on translated values within a run.\"\"\"\n logger.reason(\n f\"bulk_find_replace — Run: {run_id}, Pattern: '{payload.find_pattern}', \"\n f\"Regex: {payload.is_regex}, Lang: {payload.target_language}, \"\n f\"User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n from ....plugins.translate.service import BulkFindReplaceService\n\n if payload.preview:\n # Preview mode: return matches without applying\n preview = BulkFindReplaceService.preview(\n db=db,\n run_id=run_id,\n pattern=payload.find_pattern,\n is_regex=payload.is_regex,\n replacement_text=payload.replacement_text,\n target_language=payload.target_language,\n )\n return {\n \"rows_affected\": len(preview),\n \"corrections_submitted\": 0,\n \"preview\": preview,\n }\n\n # Apply mode: perform replacements\n result = BulkFindReplaceService.apply(\n db=db,\n run_id=run_id,\n pattern=payload.find_pattern,\n is_regex=payload.is_regex,\n replacement_text=payload.replacement_text,\n target_language=payload.target_language,\n submit_to_dictionary=payload.submit_to_dictionary,\n dictionary_id=payload.dictionary_id,\n usage_notes=payload.usage_notes,\n current_user=current_user.username,\n submit_to_dictionary_with_context=payload.submit_to_dictionary_with_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"bulk_find_replace failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion bulk_find_replace\n\n\n# #endregion TranslateRunRoutesModule\n" }, { "contract_id": "run_translation", @@ -23459,9 +23199,10 @@ "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 28, "end_line": 164, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the created translation run.", "PRE": "User has translate.job.execute permission.", "PURPOSE": "Execute a translation job (trigger a run)." @@ -23469,27 +23210,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region run_translation [TYPE Function]\n# @BRIEF Execute a translation job (trigger a run).\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the created translation run.\n@router.post(\"/jobs/{job_id}/run\", status_code=status.HTTP_201_CREATED)\ndef run_translation(\n job_id: str,\n full_translation: bool = Query(False, description=\"Translate ALL rows (skip new-key-only filter)\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Execute a translation job (trigger a run).\n\n - Normal run (default): translates only new/changed rows\n - Full translation (full_translation=true): re-translates ALL rows from the datasource\n \"\"\"\n logger.reason(\n f\"run_translation — Job: {job_id}, User: {current_user.username}, full: {full_translation}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.start_run(job_id=job_id, is_scheduled=False, full_translation=full_translation)\n # The request-scoped db session will be closed after this handler returns.\n # The background thread must use its OWN session to avoid operating on a\n # closed or expunged session.\n import threading\n\n def _background_execute():\n from ....models.translate import TranslationRun as TRModel\n bg_db = None\n try:\n bg_db = SessionLocal()\n bg_orch = TranslationOrchestrator(\n bg_db, config_manager,\n current_user.username if current_user else None,\n )\n # Re-fetch the run within the background session to get a fresh,\n # attached object\n bg_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()\n if bg_run is None:\n logger.explore(\n \"Background execute: run not found\",\n extra={\"src\": \"translate_routes\", \"run_id\": run.id},\n )\n return\n\n # execute_run internally calls self.db.commit()\n bg_orch.execute_run(bg_run)\n\n # ----- VERIFICATION -----\n # After execute_run the run object from bg_db is detached after\n # commit. Re-query to verify the status was persisted in the\n # database and is a terminal state.\n check_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()\n if check_run and check_run.status in (\"COMPLETED\", \"FAILED\", \"CANCELLED\"):\n logger.reason(\n \"Background execute verified\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"status\": check_run.status,\n },\n )\n else:\n # execute_run appeared to succeed but the run is still in a\n # non-terminal state — manually fail it so the frontend\n # polling can detect the terminal state and stop spinning.\n actual_status = check_run.status if check_run else \"NOT_FOUND\"\n logger.explore(\n \"Background execute: run not in terminal state after commit\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"status\": actual_status,\n },\n )\n if check_run:\n check_run.status = \"FAILED\"\n check_run.error_message = (\n \"Background execution did not reach terminal state \"\n f\"(was: {actual_status})\"\n )\n bg_db.commit()\n\n except Exception as bg_err:\n logger.explore(\n \"Background execute failed\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"error\": str(bg_err),\n },\n )\n # Mark the run as FAILED so the frontend polling can detect a\n # terminal state and stop spinning.\n try:\n fb_db = SessionLocal()\n try:\n fb_run = fb_db.query(TRModel).filter(TRModel.id == run.id).first()\n if fb_run:\n fb_run.status = \"FAILED\"\n fb_run.error_message = f\"Background execute error: {bg_err}\"\n fb_run.completed_at = datetime.now(UTC)\n fb_db.commit()\n logger.reason(\n \"Background execute: run marked FAILED\",\n extra={\"src\": \"translate_routes\", \"run_id\": run.id},\n )\n finally:\n fb_db.close()\n except Exception as fb_err:\n logger.explore(\n \"Background execute: unable to mark run FAILED\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"error\": str(fb_err),\n },\n )\n finally:\n if bg_db is not None:\n bg_db.close()\n\n threading.Thread(\n target=_background_execute,\n daemon=True,\n ).start()\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"run_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Run failed: {e}\")\n# #endregion run_translation\n" + "body": "# #region run_translation [C:4] [TYPE Function]\n# @BRIEF Execute a translation job (trigger a run).\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the created translation run.\n@router.post(\"/jobs/{job_id}/run\", status_code=status.HTTP_201_CREATED)\ndef run_translation(\n job_id: str,\n full_translation: bool = Query(False, description=\"Translate ALL rows (skip new-key-only filter)\"),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Execute a translation job (trigger a run).\n\n - Normal run (default): translates only new/changed rows\n - Full translation (full_translation=true): re-translates ALL rows from the datasource\n \"\"\"\n logger.reason(\n f\"run_translation — Job: {job_id}, User: {current_user.username}, full: {full_translation}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.start_run(job_id=job_id, is_scheduled=False, full_translation=full_translation)\n # The request-scoped db session will be closed after this handler returns.\n # The background thread must use its OWN session to avoid operating on a\n # closed or expunged session.\n import threading\n\n def _background_execute():\n from ....models.translate import TranslationRun as TRModel\n bg_db = None\n try:\n bg_db = SessionLocal()\n bg_orch = TranslationOrchestrator(\n bg_db, config_manager,\n current_user.username if current_user else None,\n )\n # Re-fetch the run within the background session to get a fresh,\n # attached object\n bg_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()\n if bg_run is None:\n logger.explore(\n \"Background execute: run not found\",\n extra={\"src\": \"translate_routes\", \"run_id\": run.id},\n )\n return\n\n # execute_run internally calls self.db.commit()\n bg_orch.execute_run(bg_run)\n\n # ----- VERIFICATION -----\n # After execute_run the run object from bg_db is detached after\n # commit. Re-query to verify the status was persisted in the\n # database and is a terminal state.\n check_run = bg_db.query(TRModel).filter(TRModel.id == run.id).first()\n if check_run and check_run.status in (\"COMPLETED\", \"FAILED\", \"CANCELLED\"):\n logger.reason(\n \"Background execute verified\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"status\": check_run.status,\n },\n )\n else:\n # execute_run appeared to succeed but the run is still in a\n # non-terminal state — manually fail it so the frontend\n # polling can detect the terminal state and stop spinning.\n actual_status = check_run.status if check_run else \"NOT_FOUND\"\n logger.explore(\n \"Background execute: run not in terminal state after commit\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"status\": actual_status,\n },\n )\n if check_run:\n check_run.status = \"FAILED\"\n check_run.error_message = (\n \"Background execution did not reach terminal state \"\n f\"(was: {actual_status})\"\n )\n bg_db.commit()\n\n except Exception as bg_err:\n logger.explore(\n \"Background execute failed\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"error\": str(bg_err),\n },\n )\n # Mark the run as FAILED so the frontend polling can detect a\n # terminal state and stop spinning.\n try:\n fb_db = SessionLocal()\n try:\n fb_run = fb_db.query(TRModel).filter(TRModel.id == run.id).first()\n if fb_run:\n fb_run.status = \"FAILED\"\n fb_run.error_message = f\"Background execute error: {bg_err}\"\n fb_run.completed_at = datetime.now(UTC)\n fb_db.commit()\n logger.reason(\n \"Background execute: run marked FAILED\",\n extra={\"src\": \"translate_routes\", \"run_id\": run.id},\n )\n finally:\n fb_db.close()\n except Exception as fb_err:\n logger.explore(\n \"Background execute: unable to mark run FAILED\",\n extra={\n \"src\": \"translate_routes\",\n \"run_id\": run.id,\n \"error\": str(fb_err),\n },\n )\n finally:\n if bg_db is not None:\n bg_db.close()\n\n threading.Thread(\n target=_background_execute,\n daemon=True,\n ).start()\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"run_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Run failed: {e}\")\n# #endregion run_translation\n" }, { "contract_id": "retry_run", @@ -23497,9 +23238,10 @@ "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 167, "end_line": 190, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the updated translation run.", "PRE": "User has translate.job.execute permission.", "PURPOSE": "Retry failed batches in a translation run." @@ -23507,27 +23249,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region retry_run [TYPE Function]\n# @BRIEF Retry failed batches in a translation run.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the updated translation run.\n@router.post(\"/runs/{run_id}/retry\")\ndef retry_run(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Retry failed batches in a translation run.\"\"\"\n logger.reason(f\"retry_run — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.retry_failed_batches(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"retry_run failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Retry failed: {e}\")\n# #endregion retry_run\n" + "body": "# #region retry_run [C:4] [TYPE Function]\n# @BRIEF Retry failed batches in a translation run.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the updated translation run.\n@router.post(\"/runs/{run_id}/retry\")\ndef retry_run(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Retry failed batches in a translation run.\"\"\"\n logger.reason(f\"retry_run — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.retry_failed_batches(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"retry_run failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Retry failed: {e}\")\n# #endregion retry_run\n" }, { "contract_id": "retry_insert", @@ -23535,9 +23277,10 @@ "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 193, "end_line": 216, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the updated run.", "PRE": "User has translate.job.execute permission.", "PURPOSE": "Retry the SQL insert phase for a completed run." @@ -23545,27 +23288,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region retry_insert [TYPE Function]\n# @BRIEF Retry the SQL insert phase for a completed run.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the updated run.\n@router.post(\"/runs/{run_id}/retry-insert\")\ndef retry_insert(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Retry the SQL insert phase for a completed run.\"\"\"\n logger.reason(f\"retry_insert — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.retry_insert(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"retry_insert failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Retry insert failed: {e}\")\n# #endregion retry_insert\n" + "body": "# #region retry_insert [C:4] [TYPE Function]\n# @BRIEF Retry the SQL insert phase for a completed run.\n# @PRE: User has translate.job.execute permission.\n# @POST: Returns the updated run.\n@router.post(\"/runs/{run_id}/retry-insert\")\ndef retry_insert(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Retry the SQL insert phase for a completed run.\"\"\"\n logger.reason(f\"retry_insert — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.retry_insert(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"retry_insert failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f\"Retry insert failed: {e}\")\n# #endregion retry_insert\n" }, { "contract_id": "cancel_run", @@ -23573,9 +23316,10 @@ "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 219, "end_line": 239, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Run is cancelled.", "PRE": "User has translate.job.execute permission.", "PURPOSE": "Cancel a running translation." @@ -23583,27 +23327,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region cancel_run [TYPE Function]\n# @BRIEF Cancel a running translation.\n# @PRE: User has translate.job.execute permission.\n# @POST: Run is cancelled.\n@router.post(\"/runs/{run_id}/cancel\")\ndef cancel_run(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Cancel a running translation.\"\"\"\n logger.reason(f\"cancel_run — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.cancel_run(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion cancel_run\n" + "body": "# #region cancel_run [C:4] [TYPE Function]\n# @BRIEF Cancel a running translation.\n# @PRE: User has translate.job.execute permission.\n# @POST: Run is cancelled.\n@router.post(\"/runs/{run_id}/cancel\")\ndef cancel_run(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Cancel a running translation.\"\"\"\n logger.reason(f\"cancel_run — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n run = orch.cancel_run(run_id)\n return _run_to_response(run)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion cancel_run\n" }, { "contract_id": "get_run_history", @@ -23611,9 +23355,10 @@ "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 242, "end_line": 264, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns list of runs.", "PRE": "User has translate.history.view permission.", "PURPOSE": "Get run history for a translation job." @@ -23621,27 +23366,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_run_history [TYPE Function]\n# @BRIEF Get run history for a translation job.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns list of runs.\n@router.get(\"/jobs/{job_id}/runs\")\ndef get_run_history(\n job_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get run history for a translation job.\"\"\"\n logger.reason(f\"get_run_history — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n total, runs = orch.get_run_history(job_id, page=page, page_size=page_size)\n return {\"items\": runs, \"total\": total, \"page\": page, \"page_size\": page_size}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_history\n" + "body": "# #region get_run_history [C:4] [TYPE Function]\n# @BRIEF Get run history for a translation job.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns list of runs.\n@router.get(\"/jobs/{job_id}/runs\")\ndef get_run_history(\n job_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(20, ge=1, le=100),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get run history for a translation job.\"\"\"\n logger.reason(f\"get_run_history — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n total, runs = orch.get_run_history(job_id, page=page, page_size=page_size)\n return {\"items\": runs, \"total\": total, \"page\": page, \"page_size\": page_size}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_history\n" }, { "contract_id": "get_run_status", @@ -23649,9 +23394,10 @@ "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 267, "end_line": 286, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns run details with statistics.", "PRE": "User has translate.history.view permission.", "PURPOSE": "Get status and statistics for a translation run." @@ -23659,27 +23405,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_run_status [TYPE Function]\n# @BRIEF Get status and statistics for a translation run.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns run details with statistics.\n@router.get(\"/runs/{run_id}\")\ndef get_run_status(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get status and statistics for a translation run.\"\"\"\n logger.reason(f\"get_run_status — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n return orch.get_run_status(run_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_status\n" + "body": "# #region get_run_status [C:4] [TYPE Function]\n# @BRIEF Get status and statistics for a translation run.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns run details with statistics.\n@router.get(\"/runs/{run_id}\")\ndef get_run_status(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get status and statistics for a translation run.\"\"\"\n logger.reason(f\"get_run_status — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n return orch.get_run_status(run_id)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_status\n" }, { "contract_id": "get_run_records", @@ -23687,9 +23433,10 @@ "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 289, "end_line": 311, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns paginated records.", "PRE": "User has translate.history.view permission.", "PURPOSE": "Get paginated records for a translation run." @@ -23697,27 +23444,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_run_records [TYPE Function]\n# @BRIEF Get paginated records for a translation run.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns paginated records.\n@router.get(\"/runs/{run_id}/records\")\ndef get_run_records(\n run_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(50, ge=1, le=500),\n status: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get paginated records for a translation run.\"\"\"\n logger.reason(f\"get_run_records — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n return orch.get_run_records(run_id, page=page, page_size=page_size, status_filter=status)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_records\n" + "body": "# #region get_run_records [C:4] [TYPE Function]\n# @BRIEF Get paginated records for a translation run.\n# @PRE: User has translate.history.view permission.\n# @POST: Returns paginated records.\n@router.get(\"/runs/{run_id}/records\")\ndef get_run_records(\n run_id: str,\n page: int = Query(1, ge=1),\n page_size: int = Query(50, ge=1, le=500),\n status: str | None = Query(None),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.history\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get paginated records for a translation run.\"\"\"\n logger.reason(f\"get_run_records — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n orch = TranslationOrchestrator(db, config_manager, current_user.username)\n return orch.get_run_records(run_id, page=page, page_size=page_size, status_filter=status)\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_run_records\n" }, { "contract_id": "get_batches", @@ -23725,9 +23472,10 @@ "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 318, "end_line": 357, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns list of batches.", "PRE": "User has translate.job.view permission.", "PURPOSE": "Get batches for a translation run." @@ -23735,27 +23483,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_batches [TYPE Function]\n# @BRIEF Get batches for a translation run.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of batches.\n@router.get(\"/runs/{run_id}/batches\")\ndef get_batches(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get batches for a translation run.\"\"\"\n logger.reason(f\"get_batches — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationBatch\n batches = (\n db.query(TranslationBatch)\n .filter(TranslationBatch.run_id == run_id)\n .order_by(TranslationBatch.batch_index.asc())\n .all()\n )\n return [\n {\n \"id\": b.id,\n \"run_id\": b.run_id,\n \"batch_index\": b.batch_index,\n \"status\": b.status,\n \"total_records\": b.total_records or 0,\n \"successful_records\": b.successful_records or 0,\n \"failed_records\": b.failed_records or 0,\n \"started_at\": b.started_at.isoformat() if b.started_at else None,\n \"completed_at\": b.completed_at.isoformat() if b.completed_at else None,\n \"created_at\": b.created_at.isoformat() if b.created_at else None,\n }\n for b in batches\n ]\n except Exception as e:\n logger.explore(\"get_batches failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion get_batches\n" + "body": "# #region get_batches [C:4] [TYPE Function]\n# @BRIEF Get batches for a translation run.\n# @PRE: User has translate.job.view permission.\n# @POST: Returns list of batches.\n@router.get(\"/runs/{run_id}/batches\")\ndef get_batches(\n run_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"VIEW\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Get batches for a translation run.\"\"\"\n logger.reason(f\"get_batches — Run: {run_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationBatch\n batches = (\n db.query(TranslationBatch)\n .filter(TranslationBatch.run_id == run_id)\n .order_by(TranslationBatch.batch_index.asc())\n .all()\n )\n return [\n {\n \"id\": b.id,\n \"run_id\": b.run_id,\n \"batch_index\": b.batch_index,\n \"status\": b.status,\n \"total_records\": b.total_records or 0,\n \"successful_records\": b.successful_records or 0,\n \"failed_records\": b.failed_records or 0,\n \"started_at\": b.started_at.isoformat() if b.started_at else None,\n \"completed_at\": b.completed_at.isoformat() if b.completed_at else None,\n \"created_at\": b.created_at.isoformat() if b.created_at else None,\n }\n for b in batches\n ]\n except Exception as e:\n logger.explore(\"get_batches failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion get_batches\n" }, { "contract_id": "override_detected_language", @@ -23763,9 +23511,10 @@ "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 363, "end_line": 435, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "TranslationLanguage.source_language_detected is updated; language_overridden is set to True.", "PRE": "User has translate.job.execute permission. Run, record, and language entry exist.", "PURPOSE": "Manually override the detected source language for a specific translation language entry.", @@ -23774,36 +23523,18 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "SIDE_EFFECT", - "message": "@SIDE_EFFECT is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region override_detected_language [TYPE Function]\n# @BRIEF Manually override the detected source language for a specific translation language entry.\n# @PRE: User has translate.job.execute permission. Run, record, and language entry exist.\n# @POST: TranslationLanguage.source_language_detected is updated; language_overridden is set to True.\n# @SIDE_EFFECT: DB write.\n@router.put(\"/runs/{run_id}/records/{record_id}/languages/{language_code}/override-language\")\ndef override_detected_language(\n run_id: str,\n record_id: str,\n language_code: str,\n payload: OverrideLanguageRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Manually override the detected source language for a translation entry.\"\"\"\n logger.reason(f\"override_detected_language — Run: {run_id}, Record: {record_id}, Lang: {language_code}, Override: {payload.source_language}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationLanguage, TranslationRecord\n\n # Verify the record exists and belongs to the run\n record = (\n db.query(TranslationRecord)\n .filter(\n TranslationRecord.id == record_id,\n TranslationRecord.run_id == run_id,\n )\n .first()\n )\n if not record:\n raise HTTPException(\n status_code=status.HTTP_404_NOT_FOUND,\n detail=f\"Translation record '{record_id}' not found in run '{run_id}'\",\n )\n\n # Find the language entry\n lang_entry = (\n db.query(TranslationLanguage)\n .filter(\n TranslationLanguage.record_id == record_id,\n TranslationLanguage.language_code == language_code,\n )\n .first()\n )\n if not lang_entry:\n raise HTTPException(\n status_code=status.HTTP_404_NOT_FOUND,\n detail=f\"Language entry '{language_code}' not found for record '{record_id}'\",\n )\n\n # Update the language entry\n lang_entry.source_language_detected = payload.source_language\n lang_entry.language_overridden = True\n lang_entry.needs_review = False # Override clears the review flag\n db.commit()\n db.refresh(lang_entry)\n\n logger.reason(\"Language override applied\", {\n \"run_id\": run_id,\n \"record_id\": record_id,\n \"language_code\": language_code,\n \"new_source_language\": payload.source_language,\n \"overridden_by\": current_user.username,\n })\n\n from ....schemas.translate import TranslationLanguageResponse\n return TranslationLanguageResponse.model_validate(lang_entry)\n except HTTPException:\n raise\n except Exception as e:\n logger.explore(\"override_detected_language failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion override_detected_language\n" + "body": "# #region override_detected_language [C:4] [TYPE Function]\n# @BRIEF Manually override the detected source language for a specific translation language entry.\n# @PRE: User has translate.job.execute permission. Run, record, and language entry exist.\n# @POST: TranslationLanguage.source_language_detected is updated; language_overridden is set to True.\n# @SIDE_EFFECT: DB write.\n@router.put(\"/runs/{run_id}/records/{record_id}/languages/{language_code}/override-language\")\ndef override_detected_language(\n run_id: str,\n record_id: str,\n language_code: str,\n payload: OverrideLanguageRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Manually override the detected source language for a translation entry.\"\"\"\n logger.reason(f\"override_detected_language — Run: {run_id}, Record: {record_id}, Lang: {language_code}, Override: {payload.source_language}\", extra={\"src\": \"translate_routes\"})\n try:\n from ....models.translate import TranslationLanguage, TranslationRecord\n\n # Verify the record exists and belongs to the run\n record = (\n db.query(TranslationRecord)\n .filter(\n TranslationRecord.id == record_id,\n TranslationRecord.run_id == run_id,\n )\n .first()\n )\n if not record:\n raise HTTPException(\n status_code=status.HTTP_404_NOT_FOUND,\n detail=f\"Translation record '{record_id}' not found in run '{run_id}'\",\n )\n\n # Find the language entry\n lang_entry = (\n db.query(TranslationLanguage)\n .filter(\n TranslationLanguage.record_id == record_id,\n TranslationLanguage.language_code == language_code,\n )\n .first()\n )\n if not lang_entry:\n raise HTTPException(\n status_code=status.HTTP_404_NOT_FOUND,\n detail=f\"Language entry '{language_code}' not found for record '{record_id}'\",\n )\n\n # Update the language entry\n lang_entry.source_language_detected = payload.source_language\n lang_entry.language_overridden = True\n lang_entry.needs_review = False # Override clears the review flag\n db.commit()\n db.refresh(lang_entry)\n\n logger.reason(\"Language override applied\", {\n \"run_id\": run_id,\n \"record_id\": record_id,\n \"language_code\": language_code,\n \"new_source_language\": payload.source_language,\n \"overridden_by\": current_user.username,\n })\n\n from ....schemas.translate import TranslationLanguageResponse\n return TranslationLanguageResponse.model_validate(lang_entry)\n except HTTPException:\n raise\n except Exception as e:\n logger.explore(\"override_detected_language failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion override_detected_language\n" }, { "contract_id": "inline_edit_translation", @@ -23812,9 +23543,9 @@ "start_line": 442, "end_line": 484, "tier": "TIER_2", - "complexity": 3, + "complexity": 4, "metadata": { - "COMPLEXITY": 3, + "COMPLEXITY": 4, "POST": "TranslationLanguage.final_value and user_edit are updated. Optional dictionary submission.", "PRE": "User has translate.job.execute permission. Run, record, and language entry exist.", "PURPOSE": "Apply an inline correction to a translated value on a completed run result." @@ -23822,47 +23553,38 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C3", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 3, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C3", - "detail": { - "actual_complexity": 3, + "actual_complexity": 4, "contract_type": "Function" } }, { "code": "missing_required_tag", - "tag": "RELATION", - "message": "@RELATION is required for contract type 'Function' at C3", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 3, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region inline_edit_translation [C:3] [TYPE Function] [SEMANTICS api,translate,correction]\n# @BRIEF Apply an inline correction to a translated value on a completed run result.\n# @PRE: User has translate.job.execute permission. Run, record, and language entry exist.\n# @POST: TranslationLanguage.final_value and user_edit are updated. Optional dictionary submission.\n@router.put(\"/runs/{run_id}/records/{record_id}/languages/{language_code}\")\ndef inline_edit_translation(\n run_id: str,\n record_id: str,\n language_code: str,\n payload: InlineEditRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Apply an inline correction to a translated value.\"\"\"\n logger.reason(\n f\"inline_edit_translation — Run: {run_id}, Record: {record_id}, \"\n f\"Lang: {language_code}, User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n from ....plugins.translate.service import InlineCorrectionService\n\n result = InlineCorrectionService.apply_inline_edit(\n db=db,\n run_id=run_id,\n record_id=record_id,\n language_code=language_code,\n final_value=payload.final_value,\n submit_to_dictionary=payload.submit_to_dictionary,\n dictionary_id=payload.dictionary_id,\n current_user=current_user.username,\n context_data_override=payload.context_data_override,\n usage_notes=payload.usage_notes,\n keep_context=payload.keep_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"inline_edit_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion inline_edit_translation\n" + "body": "# #region inline_edit_translation [C:4] [TYPE Function] [SEMANTICS api,translate,correction]\n# @BRIEF Apply an inline correction to a translated value on a completed run result.\n# @PRE: User has translate.job.execute permission. Run, record, and language entry exist.\n# @POST: TranslationLanguage.final_value and user_edit are updated. Optional dictionary submission.\n@router.put(\"/runs/{run_id}/records/{record_id}/languages/{language_code}\")\ndef inline_edit_translation(\n run_id: str,\n record_id: str,\n language_code: str,\n payload: InlineEditRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Apply an inline correction to a translated value.\"\"\"\n logger.reason(\n f\"inline_edit_translation — Run: {run_id}, Record: {record_id}, \"\n f\"Lang: {language_code}, User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n from ....plugins.translate.service import InlineCorrectionService\n\n result = InlineCorrectionService.apply_inline_edit(\n db=db,\n run_id=run_id,\n record_id=record_id,\n language_code=language_code,\n final_value=payload.final_value,\n submit_to_dictionary=payload.submit_to_dictionary,\n dictionary_id=payload.dictionary_id,\n current_user=current_user.username,\n context_data_override=payload.context_data_override,\n usage_notes=payload.usage_notes,\n keep_context=payload.keep_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"inline_edit_translation failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion inline_edit_translation\n" }, { "contract_id": "bulk_find_replace", "contract_type": "Function", "file_path": "backend/src/api/routes/translate/_run_routes.py", "start_line": 491, - "end_line": 549, + "end_line": 551, "tier": "TIER_2", - "complexity": 3, + "complexity": 4, "metadata": { - "COMPLEXITY": 3, + "COMPLEXITY": "4", "POST": "If preview=false, matching translations are updated. Optional dictionary submission.", "PRE": "User has translate.job.execute permission. Run exists.", "PURPOSE": "Perform bulk find-and-replace on translated values within a run." @@ -23870,43 +23592,34 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C3", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 3, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C3", - "detail": { - "actual_complexity": 3, + "actual_complexity": 4, "contract_type": "Function" } }, { "code": "missing_required_tag", - "tag": "RELATION", - "message": "@RELATION is required for contract type 'Function' at C3", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 3, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region bulk_find_replace [C:3] [TYPE Function] [SEMANTICS api,translate,bulk,replace]\n# @BRIEF Perform bulk find-and-replace on translated values within a run.\n# @PRE: User has translate.job.execute permission. Run exists.\n# @POST: If preview=false, matching translations are updated. Optional dictionary submission.\n@router.post(\"/runs/{run_id}/bulk-replace\")\ndef bulk_find_replace(\n run_id: str,\n payload: BulkFindReplaceRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Perform bulk find-and-replace on translated values within a run.\"\"\"\n logger.reason(\n f\"bulk_find_replace — Run: {run_id}, Pattern: '{payload.find_pattern}', \"\n f\"Regex: {payload.is_regex}, Lang: {payload.target_language}, \"\n f\"User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n from ....plugins.translate.service import BulkFindReplaceService\n\n if payload.preview:\n # Preview mode: return matches without applying\n preview = BulkFindReplaceService.preview(\n db=db,\n run_id=run_id,\n pattern=payload.find_pattern,\n is_regex=payload.is_regex,\n replacement_text=payload.replacement_text,\n target_language=payload.target_language,\n )\n return {\n \"rows_affected\": len(preview),\n \"corrections_submitted\": 0,\n \"preview\": preview,\n }\n\n # Apply mode: perform replacements\n result = BulkFindReplaceService.apply(\n db=db,\n run_id=run_id,\n pattern=payload.find_pattern,\n is_regex=payload.is_regex,\n replacement_text=payload.replacement_text,\n target_language=payload.target_language,\n submit_to_dictionary=payload.submit_to_dictionary,\n dictionary_id=payload.dictionary_id,\n usage_notes=payload.usage_notes,\n current_user=current_user.username,\n submit_to_dictionary_with_context=payload.submit_to_dictionary_with_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"bulk_find_replace failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion bulk_find_replace\n" + "body": "# #region bulk_find_replace [C:4] [TYPE Function] [SEMANTICS api,translate,bulk,replace]\n# @BRIEF Perform bulk find-and-replace on translated values within a run.\n# @PRE User has translate.job.execute permission. Run exists.\n# @POST If preview=false, matching translations are updated. Optional dictionary submission.\n# @COMPLEXITY 4\n\n@router.post(\"/runs/{run_id}/bulk-replace\")\ndef bulk_find_replace(\n run_id: str,\n payload: BulkFindReplaceRequest,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.job\", \"EXECUTE\")),\n db: Session = Depends(get_db),\n):\n \"\"\"Perform bulk find-and-replace on translated values within a run.\"\"\"\n logger.reason(\n f\"bulk_find_replace — Run: {run_id}, Pattern: '{payload.find_pattern}', \"\n f\"Regex: {payload.is_regex}, Lang: {payload.target_language}, \"\n f\"User: {current_user.username}\",\n extra={\"src\": \"translate_routes\"},\n )\n try:\n from ....plugins.translate.service import BulkFindReplaceService\n\n if payload.preview:\n # Preview mode: return matches without applying\n preview = BulkFindReplaceService.preview(\n db=db,\n run_id=run_id,\n pattern=payload.find_pattern,\n is_regex=payload.is_regex,\n replacement_text=payload.replacement_text,\n target_language=payload.target_language,\n )\n return {\n \"rows_affected\": len(preview),\n \"corrections_submitted\": 0,\n \"preview\": preview,\n }\n\n # Apply mode: perform replacements\n result = BulkFindReplaceService.apply(\n db=db,\n run_id=run_id,\n pattern=payload.find_pattern,\n is_regex=payload.is_regex,\n replacement_text=payload.replacement_text,\n target_language=payload.target_language,\n submit_to_dictionary=payload.submit_to_dictionary,\n dictionary_id=payload.dictionary_id,\n usage_notes=payload.usage_notes,\n current_user=current_user.username,\n submit_to_dictionary_with_context=payload.submit_to_dictionary_with_context,\n )\n return result\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n except Exception as e:\n logger.explore(\"bulk_find_replace failed\", extra={\"src\": \"translate_routes\", \"error\": str(e)})\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion bulk_find_replace\n" }, { "contract_id": "TranslateScheduleRoutesModule", "contract_type": "Module", "file_path": "backend/src/api/routes/translate/_schedule_routes.py", "start_line": 1, - "end_line": 237, + "end_line": 239, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -23928,7 +23641,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateScheduleRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, schedule, search]\n# @BRIEF Translation Schedule management routes.\n# @LAYER: API\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.scheduler import TranslationScheduler\nfrom ....schemas.auth import User\nfrom ....schemas.translate import ScheduleConfig\nfrom ._router import router\n\n# ============================================================\n# Schedule\n# ============================================================\n\n# #region get_schedule [TYPE Function]\n# @BRIEF Get the schedule for a translation job.\n# @PRE: User has translate.schedule.view permission.\n# @POST: Returns the schedule configuration.\n@router.get(\"/jobs/{job_id}/schedule\")\nasync def get_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get schedule for a translation job.\"\"\"\n logger.reason(f\"get_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.get_schedule(job_id)\n return {\n \"id\": schedule.id,\n \"job_id\": schedule.job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n \"execution_mode\": schedule.execution_mode,\n \"last_run_at\": schedule.last_run_at.isoformat() if schedule.last_run_at else None,\n \"next_run_at\": schedule.next_run_at.isoformat() if schedule.next_run_at else None,\n \"created_by\": schedule.created_by,\n \"created_at\": schedule.created_at.isoformat() if schedule.created_at else None,\n \"updated_at\": schedule.updated_at.isoformat() if schedule.updated_at else None,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_schedule\n\n\n# #region set_schedule [TYPE Function]\n# @BRIEF Set or update the schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is created or updated.\n@router.put(\"/jobs/{job_id}/schedule\")\nasync def set_schedule(\n job_id: str,\n payload: ScheduleConfig,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Set or update schedule for a translation job.\"\"\"\n logger.reason(f\"set_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n # Check if schedule already exists\n from ....models.translate import TranslationSchedule\n existing = db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n execution_mode = getattr(payload, 'execution_mode', 'full')\n if existing:\n schedule = scheduler.update_schedule(\n job_id,\n cron_expression=payload.cron_expression,\n timezone_str=payload.timezone,\n is_active=payload.is_active,\n execution_mode=execution_mode,\n )\n else:\n schedule = scheduler.create_schedule(\n job_id,\n cron_expression=payload.cron_expression,\n timezone=payload.timezone,\n is_active=payload.is_active,\n execution_mode=execution_mode,\n )\n # Register with APScheduler via SchedulerService\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.add_translation_job(\n schedule_id=schedule.id,\n job_id=job_id,\n cron_expression=schedule.cron_expression,\n timezone=schedule.timezone,\n execution_mode=schedule.execution_mode,\n )\n return {\n \"id\": schedule.id,\n \"job_id\": schedule.job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n \"execution_mode\": schedule.execution_mode,\n \"last_run_at\": schedule.last_run_at.isoformat() if schedule.last_run_at else None,\n \"created_by\": schedule.created_by,\n \"created_at\": schedule.created_at.isoformat() if schedule.created_at else None,\n \"updated_at\": schedule.updated_at.isoformat() if schedule.updated_at else None,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion set_schedule\n\n\n# #region enable_schedule [TYPE Function]\n# @BRIEF Enable a schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is enabled.\n@router.post(\"/jobs/{job_id}/schedule/enable\")\nasync def enable_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Enable a schedule for a translation job.\"\"\"\n logger.reason(f\"enable_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.set_schedule_active(job_id, is_active=True)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.add_translation_job(\n schedule_id=schedule.id,\n job_id=job_id,\n cron_expression=schedule.cron_expression,\n timezone=schedule.timezone,\n execution_mode=schedule.execution_mode,\n )\n return {\"status\": \"enabled\", \"job_id\": job_id}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion enable_schedule\n\n\n# #region disable_schedule [TYPE Function]\n# @BRIEF Disable a schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is disabled.\n@router.post(\"/jobs/{job_id}/schedule/disable\")\nasync def disable_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Disable a schedule for a translation job.\"\"\"\n logger.reason(f\"disable_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n scheduler.set_schedule_active(job_id, is_active=False)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.remove_translation_job(schedule_id=job_id)\n return {\"status\": \"disabled\", \"job_id\": job_id}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion disable_schedule\n\n\n# #region delete_schedule [TYPE Function]\n# @BRIEF Delete the schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is removed.\n@router.delete(\"/jobs/{job_id}/schedule\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Delete schedule for a translation job.\"\"\"\n logger.reason(f\"delete_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n scheduler.delete_schedule(job_id)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.remove_translation_job(schedule_id=job_id)\n return None\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_schedule\n\n\n# #region get_next_executions [TYPE Function]\n# @BRIEF Preview next N executions for a job's schedule.\n# @PRE: User has translate.schedule.view permission.\n# @POST: Returns next execution times.\n@router.get(\"/jobs/{job_id}/schedule/next-executions\")\nasync def get_next_executions(\n job_id: str,\n n: int = Query(3, ge=1, le=10),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Preview next N executions for a job's schedule.\"\"\"\n logger.reason(f\"get_next_executions — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.get_schedule(job_id)\n next_times = TranslationScheduler.get_next_executions(\n schedule.cron_expression, schedule.timezone, n=n\n )\n return {\n \"job_id\": job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"next_executions\": next_times,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_next_executions\n\n# #endregion TranslateScheduleRoutesModule\n" + "body": "# #region TranslateScheduleRoutesModule [C:3] [TYPE Module] [SEMANTICS fastapi, translate, api, schedule, search]\n# @BRIEF Translation Schedule management routes.\n# @LAYER: API\n\nfrom fastapi import Depends, HTTPException, Query, status\nfrom sqlalchemy.orm import Session\n\nfrom ....core.config_manager import ConfigManager\nfrom ....core.database import get_db\nfrom ....core.logger import logger\nfrom ....dependencies import get_config_manager, get_current_user, has_permission\nfrom ....plugins.translate.scheduler import TranslationScheduler\nfrom ....schemas.auth import User\nfrom ....schemas.translate import ScheduleConfig\nfrom ._router import router\n\n# ============================================================\n# Schedule\n# ============================================================\n\n# #region get_schedule [C:4] [TYPE Function]\n# @BRIEF Get the schedule for a translation job.\n# @PRE: User has translate.schedule.view permission.\n# @POST: Returns the schedule configuration.\n@router.get(\"/jobs/{job_id}/schedule\")\nasync def get_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get schedule for a translation job.\"\"\"\n logger.reason(f\"get_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.get_schedule(job_id)\n return {\n \"id\": schedule.id,\n \"job_id\": schedule.job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n \"execution_mode\": schedule.execution_mode,\n \"last_run_at\": schedule.last_run_at.isoformat() if schedule.last_run_at else None,\n \"next_run_at\": schedule.next_run_at.isoformat() if schedule.next_run_at else None,\n \"created_by\": schedule.created_by,\n \"created_at\": schedule.created_at.isoformat() if schedule.created_at else None,\n \"updated_at\": schedule.updated_at.isoformat() if schedule.updated_at else None,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_schedule\n\n\n# #region set_schedule [C:4] [TYPE Function]\n# @BRIEF Set or update the schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is created or updated.\n@router.put(\"/jobs/{job_id}/schedule\")\nasync def set_schedule(\n job_id: str,\n payload: ScheduleConfig,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Set or update schedule for a translation job.\"\"\"\n logger.reason(f\"set_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n # Check if schedule already exists\n from ....models.translate import TranslationSchedule\n existing = db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n execution_mode = getattr(payload, 'execution_mode', 'full')\n if existing:\n schedule = scheduler.update_schedule(\n job_id,\n cron_expression=payload.cron_expression,\n timezone_str=payload.timezone,\n is_active=payload.is_active,\n execution_mode=execution_mode,\n )\n else:\n schedule = scheduler.create_schedule(\n job_id,\n cron_expression=payload.cron_expression,\n timezone=payload.timezone,\n is_active=payload.is_active,\n execution_mode=execution_mode,\n )\n # Register with APScheduler via SchedulerService\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.add_translation_job(\n schedule_id=schedule.id,\n job_id=job_id,\n cron_expression=schedule.cron_expression,\n timezone=schedule.timezone,\n execution_mode=schedule.execution_mode,\n )\n return {\n \"id\": schedule.id,\n \"job_id\": schedule.job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n \"execution_mode\": schedule.execution_mode,\n \"last_run_at\": schedule.last_run_at.isoformat() if schedule.last_run_at else None,\n \"created_by\": schedule.created_by,\n \"created_at\": schedule.created_at.isoformat() if schedule.created_at else None,\n \"updated_at\": schedule.updated_at.isoformat() if schedule.updated_at else None,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion set_schedule\n\n\n# #region enable_schedule [C:4] [TYPE Function]\n# @BRIEF Enable a schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is enabled.\n@router.post(\"/jobs/{job_id}/schedule/enable\")\nasync def enable_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Enable a schedule for a translation job.\"\"\"\n logger.reason(f\"enable_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.set_schedule_active(job_id, is_active=True)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.add_translation_job(\n schedule_id=schedule.id,\n job_id=job_id,\n cron_expression=schedule.cron_expression,\n timezone=schedule.timezone,\n execution_mode=schedule.execution_mode,\n )\n return {\"status\": \"enabled\", \"job_id\": job_id}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion enable_schedule\n\n\n# #region disable_schedule [C:4] [TYPE Function]\n# @BRIEF Disable a schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is disabled.\n@router.post(\"/jobs/{job_id}/schedule/disable\")\nasync def disable_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Disable a schedule for a translation job.\"\"\"\n logger.reason(f\"disable_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n scheduler.set_schedule_active(job_id, is_active=False)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.remove_translation_job(schedule_id=job_id)\n return {\"status\": \"disabled\", \"job_id\": job_id}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion disable_schedule\n\n\n# #region delete_schedule [C:4] [TYPE Function]\n# @BRIEF Delete the schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is removed.\n@router.delete(\"/jobs/{job_id}/schedule\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Delete schedule for a translation job.\"\"\"\n logger.reason(f\"delete_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n scheduler.delete_schedule(job_id)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.remove_translation_job(schedule_id=job_id)\n return None\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_schedule\n\n\n# #region get_next_executions [C:4] [TYPE Function]\n# @BRIEF Preview next N executions for a job's schedule.\n# @PRE User has translate.schedule.view permission.\n# @POST Returns next execution times.\n# @COMPLEXITY 4\n\n@router.get(\"/jobs/{job_id}/schedule/next-executions\")\nasync def get_next_executions(\n job_id: str,\n n: int = Query(3, ge=1, le=10),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Preview next N executions for a job's schedule.\"\"\"\n logger.reason(f\"get_next_executions — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.get_schedule(job_id)\n next_times = TranslationScheduler.get_next_executions(\n schedule.cron_expression, schedule.timezone, n=n\n )\n return {\n \"job_id\": job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"next_executions\": next_times,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_next_executions\n\n# #endregion TranslateScheduleRoutesModule\n" }, { "contract_id": "get_schedule", @@ -23936,9 +23649,10 @@ "file_path": "backend/src/api/routes/translate/_schedule_routes.py", "start_line": 21, "end_line": 53, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns the schedule configuration.", "PRE": "User has translate.schedule.view permission.", "PURPOSE": "Get the schedule for a translation job." @@ -23946,27 +23660,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_schedule [TYPE Function]\n# @BRIEF Get the schedule for a translation job.\n# @PRE: User has translate.schedule.view permission.\n# @POST: Returns the schedule configuration.\n@router.get(\"/jobs/{job_id}/schedule\")\nasync def get_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get schedule for a translation job.\"\"\"\n logger.reason(f\"get_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.get_schedule(job_id)\n return {\n \"id\": schedule.id,\n \"job_id\": schedule.job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n \"execution_mode\": schedule.execution_mode,\n \"last_run_at\": schedule.last_run_at.isoformat() if schedule.last_run_at else None,\n \"next_run_at\": schedule.next_run_at.isoformat() if schedule.next_run_at else None,\n \"created_by\": schedule.created_by,\n \"created_at\": schedule.created_at.isoformat() if schedule.created_at else None,\n \"updated_at\": schedule.updated_at.isoformat() if schedule.updated_at else None,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_schedule\n" + "body": "# #region get_schedule [C:4] [TYPE Function]\n# @BRIEF Get the schedule for a translation job.\n# @PRE: User has translate.schedule.view permission.\n# @POST: Returns the schedule configuration.\n@router.get(\"/jobs/{job_id}/schedule\")\nasync def get_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Get schedule for a translation job.\"\"\"\n logger.reason(f\"get_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.get_schedule(job_id)\n return {\n \"id\": schedule.id,\n \"job_id\": schedule.job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n \"execution_mode\": schedule.execution_mode,\n \"last_run_at\": schedule.last_run_at.isoformat() if schedule.last_run_at else None,\n \"next_run_at\": schedule.next_run_at.isoformat() if schedule.next_run_at else None,\n \"created_by\": schedule.created_by,\n \"created_at\": schedule.created_at.isoformat() if schedule.created_at else None,\n \"updated_at\": schedule.updated_at.isoformat() if schedule.updated_at else None,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_schedule\n" }, { "contract_id": "set_schedule", @@ -23974,9 +23688,10 @@ "file_path": "backend/src/api/routes/translate/_schedule_routes.py", "start_line": 56, "end_line": 119, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Schedule is created or updated.", "PRE": "User has translate.schedule.manage permission.", "PURPOSE": "Set or update the schedule for a translation job." @@ -23984,27 +23699,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region set_schedule [TYPE Function]\n# @BRIEF Set or update the schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is created or updated.\n@router.put(\"/jobs/{job_id}/schedule\")\nasync def set_schedule(\n job_id: str,\n payload: ScheduleConfig,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Set or update schedule for a translation job.\"\"\"\n logger.reason(f\"set_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n # Check if schedule already exists\n from ....models.translate import TranslationSchedule\n existing = db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n execution_mode = getattr(payload, 'execution_mode', 'full')\n if existing:\n schedule = scheduler.update_schedule(\n job_id,\n cron_expression=payload.cron_expression,\n timezone_str=payload.timezone,\n is_active=payload.is_active,\n execution_mode=execution_mode,\n )\n else:\n schedule = scheduler.create_schedule(\n job_id,\n cron_expression=payload.cron_expression,\n timezone=payload.timezone,\n is_active=payload.is_active,\n execution_mode=execution_mode,\n )\n # Register with APScheduler via SchedulerService\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.add_translation_job(\n schedule_id=schedule.id,\n job_id=job_id,\n cron_expression=schedule.cron_expression,\n timezone=schedule.timezone,\n execution_mode=schedule.execution_mode,\n )\n return {\n \"id\": schedule.id,\n \"job_id\": schedule.job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n \"execution_mode\": schedule.execution_mode,\n \"last_run_at\": schedule.last_run_at.isoformat() if schedule.last_run_at else None,\n \"created_by\": schedule.created_by,\n \"created_at\": schedule.created_at.isoformat() if schedule.created_at else None,\n \"updated_at\": schedule.updated_at.isoformat() if schedule.updated_at else None,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion set_schedule\n" + "body": "# #region set_schedule [C:4] [TYPE Function]\n# @BRIEF Set or update the schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is created or updated.\n@router.put(\"/jobs/{job_id}/schedule\")\nasync def set_schedule(\n job_id: str,\n payload: ScheduleConfig,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Set or update schedule for a translation job.\"\"\"\n logger.reason(f\"set_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n # Check if schedule already exists\n from ....models.translate import TranslationSchedule\n existing = db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n execution_mode = getattr(payload, 'execution_mode', 'full')\n if existing:\n schedule = scheduler.update_schedule(\n job_id,\n cron_expression=payload.cron_expression,\n timezone_str=payload.timezone,\n is_active=payload.is_active,\n execution_mode=execution_mode,\n )\n else:\n schedule = scheduler.create_schedule(\n job_id,\n cron_expression=payload.cron_expression,\n timezone=payload.timezone,\n is_active=payload.is_active,\n execution_mode=execution_mode,\n )\n # Register with APScheduler via SchedulerService\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.add_translation_job(\n schedule_id=schedule.id,\n job_id=job_id,\n cron_expression=schedule.cron_expression,\n timezone=schedule.timezone,\n execution_mode=schedule.execution_mode,\n )\n return {\n \"id\": schedule.id,\n \"job_id\": schedule.job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n \"execution_mode\": schedule.execution_mode,\n \"last_run_at\": schedule.last_run_at.isoformat() if schedule.last_run_at else None,\n \"created_by\": schedule.created_by,\n \"created_at\": schedule.created_at.isoformat() if schedule.created_at else None,\n \"updated_at\": schedule.updated_at.isoformat() if schedule.updated_at else None,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))\n# #endregion set_schedule\n" }, { "contract_id": "enable_schedule", @@ -24012,9 +23727,10 @@ "file_path": "backend/src/api/routes/translate/_schedule_routes.py", "start_line": 122, "end_line": 151, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Schedule is enabled.", "PRE": "User has translate.schedule.manage permission.", "PURPOSE": "Enable a schedule for a translation job." @@ -24022,27 +23738,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region enable_schedule [TYPE Function]\n# @BRIEF Enable a schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is enabled.\n@router.post(\"/jobs/{job_id}/schedule/enable\")\nasync def enable_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Enable a schedule for a translation job.\"\"\"\n logger.reason(f\"enable_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.set_schedule_active(job_id, is_active=True)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.add_translation_job(\n schedule_id=schedule.id,\n job_id=job_id,\n cron_expression=schedule.cron_expression,\n timezone=schedule.timezone,\n execution_mode=schedule.execution_mode,\n )\n return {\"status\": \"enabled\", \"job_id\": job_id}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion enable_schedule\n" + "body": "# #region enable_schedule [C:4] [TYPE Function]\n# @BRIEF Enable a schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is enabled.\n@router.post(\"/jobs/{job_id}/schedule/enable\")\nasync def enable_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Enable a schedule for a translation job.\"\"\"\n logger.reason(f\"enable_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.set_schedule_active(job_id, is_active=True)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.add_translation_job(\n schedule_id=schedule.id,\n job_id=job_id,\n cron_expression=schedule.cron_expression,\n timezone=schedule.timezone,\n execution_mode=schedule.execution_mode,\n )\n return {\"status\": \"enabled\", \"job_id\": job_id}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion enable_schedule\n" }, { "contract_id": "disable_schedule", @@ -24050,9 +23766,10 @@ "file_path": "backend/src/api/routes/translate/_schedule_routes.py", "start_line": 154, "end_line": 177, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Schedule is disabled.", "PRE": "User has translate.schedule.manage permission.", "PURPOSE": "Disable a schedule for a translation job." @@ -24060,27 +23777,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region disable_schedule [TYPE Function]\n# @BRIEF Disable a schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is disabled.\n@router.post(\"/jobs/{job_id}/schedule/disable\")\nasync def disable_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Disable a schedule for a translation job.\"\"\"\n logger.reason(f\"disable_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n scheduler.set_schedule_active(job_id, is_active=False)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.remove_translation_job(schedule_id=job_id)\n return {\"status\": \"disabled\", \"job_id\": job_id}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion disable_schedule\n" + "body": "# #region disable_schedule [C:4] [TYPE Function]\n# @BRIEF Disable a schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is disabled.\n@router.post(\"/jobs/{job_id}/schedule/disable\")\nasync def disable_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Disable a schedule for a translation job.\"\"\"\n logger.reason(f\"disable_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n scheduler.set_schedule_active(job_id, is_active=False)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.remove_translation_job(schedule_id=job_id)\n return {\"status\": \"disabled\", \"job_id\": job_id}\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion disable_schedule\n" }, { "contract_id": "delete_schedule", @@ -24088,9 +23805,10 @@ "file_path": "backend/src/api/routes/translate/_schedule_routes.py", "start_line": 180, "end_line": 203, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Schedule is removed.", "PRE": "User has translate.schedule.manage permission.", "PURPOSE": "Delete the schedule for a translation job." @@ -24098,37 +23816,38 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region delete_schedule [TYPE Function]\n# @BRIEF Delete the schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is removed.\n@router.delete(\"/jobs/{job_id}/schedule\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Delete schedule for a translation job.\"\"\"\n logger.reason(f\"delete_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n scheduler.delete_schedule(job_id)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.remove_translation_job(schedule_id=job_id)\n return None\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_schedule\n" + "body": "# #region delete_schedule [C:4] [TYPE Function]\n# @BRIEF Delete the schedule for a translation job.\n# @PRE: User has translate.schedule.manage permission.\n# @POST: Schedule is removed.\n@router.delete(\"/jobs/{job_id}/schedule\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_schedule(\n job_id: str,\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"MANAGE\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Delete schedule for a translation job.\"\"\"\n logger.reason(f\"delete_schedule — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n scheduler.delete_schedule(job_id)\n from ....dependencies import get_scheduler_service\n sched_svc = get_scheduler_service()\n sched_svc.remove_translation_job(schedule_id=job_id)\n return None\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion delete_schedule\n" }, { "contract_id": "get_next_executions", "contract_type": "Function", "file_path": "backend/src/api/routes/translate/_schedule_routes.py", "start_line": 206, - "end_line": 235, - "tier": "TIER_1", - "complexity": 1, + "end_line": 237, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "Returns next execution times.", "PRE": "User has translate.schedule.view permission.", "PURPOSE": "Preview next N executions for a job's schedule." @@ -24136,27 +23855,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_next_executions [TYPE Function]\n# @BRIEF Preview next N executions for a job's schedule.\n# @PRE: User has translate.schedule.view permission.\n# @POST: Returns next execution times.\n@router.get(\"/jobs/{job_id}/schedule/next-executions\")\nasync def get_next_executions(\n job_id: str,\n n: int = Query(3, ge=1, le=10),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Preview next N executions for a job's schedule.\"\"\"\n logger.reason(f\"get_next_executions — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.get_schedule(job_id)\n next_times = TranslationScheduler.get_next_executions(\n schedule.cron_expression, schedule.timezone, n=n\n )\n return {\n \"job_id\": job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"next_executions\": next_times,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_next_executions\n" + "body": "# #region get_next_executions [C:4] [TYPE Function]\n# @BRIEF Preview next N executions for a job's schedule.\n# @PRE User has translate.schedule.view permission.\n# @POST Returns next execution times.\n# @COMPLEXITY 4\n\n@router.get(\"/jobs/{job_id}/schedule/next-executions\")\nasync def get_next_executions(\n job_id: str,\n n: int = Query(3, ge=1, le=10),\n current_user: User = Depends(get_current_user),\n _ = Depends(has_permission(\"translate.schedule\", \"VIEW\")),\n db: Session = Depends(get_db),\n config_manager: ConfigManager = Depends(get_config_manager),\n):\n \"\"\"Preview next N executions for a job's schedule.\"\"\"\n logger.reason(f\"get_next_executions — Job: {job_id}, User: {current_user.username}\", extra={\"src\": \"translate_routes\"})\n try:\n scheduler = TranslationScheduler(db, config_manager, current_user.username)\n schedule = scheduler.get_schedule(job_id)\n next_times = TranslationScheduler.get_next_executions(\n schedule.cron_expression, schedule.timezone, n=n\n )\n return {\n \"job_id\": job_id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"next_executions\": next_times,\n }\n except ValueError as e:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))\n# #endregion get_next_executions\n" }, { "contract_id": "AppModule", @@ -47309,7 +47028,7 @@ "contract_type": "Module", "file_path": "backend/src/models/translate.py", "start_line": 1, - "end_line": 394, + "end_line": 397, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -47320,34 +47039,15 @@ "relations": [ { "source_id": "TranslateModels", - "relation_type": "INHERITS_FROM", + "relation_type": "INHERITS", "target_id": "MappingModels", - "target_ref": "MappingModels:Base" - } - ], - "schema_warnings": [ - { - "code": "invalid_relation_predicate", - "tag": "RELATION", - "message": "Predicate INHERITS_FROM is not in allowed_predicates", - "detail": { - "allowed": [ - "DEPENDS_ON", - "CALLS", - "INHERITS", - "IMPLEMENTS", - "DISPATCHES", - "BINDS_TO", - "CALLED_BY", - "VERIFIES" - ], - "predicate": "INHERITS_FROM" - } + "target_ref": "[MappingModels]" } ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateModels [C:3] [TYPE Module] [SEMANTICS sqlalchemy, translate, model, schema, dashboard, llm]\n# @BRIEF SQLAlchemy ORM models for LLM-based SQL/dashboard translation across dialects.\n# @LAYER: Domain\n# @RELATION INHERITS_FROM -> MappingModels:Base\n\nimport uuid\nfrom datetime import UTC, datetime\n\nfrom sqlalchemy import JSON, Boolean, Column, DateTime, Float, ForeignKey, Index, Integer, String, Text, UniqueConstraint\nfrom sqlalchemy.orm import relationship\n\nfrom .mapping import Base\n\n\ndef generate_uuid():\n return str(uuid.uuid4())\n\n\n# #region TranslationJob [TYPE Class]\n# @BRIEF A translation job representing a multi-dialect conversion task with column mappings, LLM config, and dictionary attachments.\n# @RATIONALE: Snapshot isolation — in-progress runs use config snapshot; config edits affect future runs only.\n# @REJECTED: Invalidating in-progress runs on config edit would break scheduled run continuity.\nclass TranslationJob(Base):\n __tablename__ = \"translation_jobs\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n name = Column(String, nullable=False)\n description = Column(Text, nullable=True)\n source_dialect = Column(String, nullable=False)\n target_dialect = Column(String, nullable=False)\n database_dialect = Column(String, nullable=True, comment=\"Detected dialect from Superset connection at save time\")\n status = Column(String, nullable=False, default=\"DRAFT\") # DRAFT, READY, RUNNING, COMPLETED, FAILED, CANCELLED\n\n # Datasource & target table configuration\n source_datasource_id = Column(String, nullable=True, comment=\"Superset datasource ID\")\n source_table = Column(String, nullable=True, comment=\"Source table name resolved from datasource\")\n target_schema = Column(String, nullable=True, comment=\"Target table schema\")\n target_table = Column(String, nullable=True, comment=\"Target table name\")\n\n # Column mapping\n source_key_cols = Column(JSON, nullable=True, comment=\"Source key column names for composite key\")\n target_key_cols = Column(JSON, nullable=True, comment=\"Target key column names for composite key\")\n translation_column = Column(String, nullable=True, comment=\"Source column whose values will be translated\")\n target_column = Column(String, nullable=True, comment=\"Target column for translated output (defaults to translation_column)\")\n target_language_column = Column(String, nullable=True, comment=\"Target column for language code (e.g. 'ru', 'en')\")\n target_source_column = Column(String, nullable=True, comment=\"Target column for source/original text\")\n target_source_language_column = Column(String, nullable=True, comment=\"Target column for detected source language (BCP-47)\")\n context_columns = Column(JSON, nullable=True, comment=\"Context column names included in LLM prompt\")\n\n # LLM & processing settings\n # source_language removed — deprecated, auto-detected per row by LLM; use TranslationLanguage.source_language_detected instead\n target_languages = Column(JSON, nullable=True, comment=\"List of BCP-47 target language codes (multi-language support)\")\n provider_id = Column(String, nullable=True, comment=\"LLM provider ID\")\n batch_size = Column(Integer, nullable=False, default=50, comment=\"Records per batch\")\n disable_reasoning = Column(Boolean, default=False, comment=\"If true, pass reasoning_effort:none to LLM to save output tokens\")\n upsert_strategy = Column(String, nullable=False, default=\"MERGE\", comment=\"MERGE, INSERT, UPDATE\")\n\n # Environment association\n environment_id = Column(String, nullable=True, comment=\"Superset environment ID for datasource access\")\n target_database_id = Column(String, nullable=True, comment=\"Superset database ID for SQL Lab insert target\")\n\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n updated_at = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))\n# #endregion TranslationJob\n\n\n# #region TranslationRun [TYPE Class]\n# @BRIEF Represents a single execution of a translation job, capturing status, timing, and Superset execution metadata.\nclass TranslationRun(Base):\n __tablename__ = \"translation_runs\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n status = Column(String, nullable=False, default=\"PENDING\") # PENDING, RUNNING, COMPLETED, FAILED, CANCELLED\n trigger_type = Column(String, nullable=True, comment=\"manual, scheduled, retry, baseline_expired\")\n started_at = Column(DateTime, nullable=True)\n completed_at = Column(DateTime, nullable=True)\n error_message = Column(Text, nullable=True)\n total_records = Column(Integer, default=0)\n successful_records = Column(Integer, default=0)\n failed_records = Column(Integer, default=0)\n skipped_records = Column(Integer, default=0)\n insert_status = Column(String, nullable=True, comment=\"Status of the Superset insert/update operation\")\n superset_execution_id = Column(String, nullable=True, comment=\"Superset execution/task ID\")\n superset_execution_log = Column(JSON, nullable=True, comment=\"Superset execution log output\")\n config_snapshot = Column(JSON, nullable=True, comment=\"Snapshot of job config at run creation time\")\n key_hash = Column(String, nullable=True, comment=\"Hash of source key fields for dedup\")\n config_hash = Column(String, nullable=True, comment=\"Hash of translation configuration state\")\n dict_snapshot_hash = Column(String, nullable=True, comment=\"Hash of dictionary state at run time\")\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n language_stats = relationship(\"TranslationRunLanguageStats\", back_populates=\"run\")\n# #endregion TranslationRun\n\n\n# #region TranslationBatch [TYPE Class]\n# @BRIEF Groups translation records within a run into manageable batches with timing and record counts.\nclass TranslationBatch(Base):\n __tablename__ = \"translation_batches\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=False, index=True)\n batch_index = Column(Integer, nullable=False)\n status = Column(String, nullable=False, default=\"PENDING\")\n total_records = Column(Integer, default=0)\n successful_records = Column(Integer, default=0)\n failed_records = Column(Integer, default=0)\n started_at = Column(DateTime, nullable=True)\n completed_at = Column(DateTime, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n# #endregion TranslationBatch\n\n\n# #region TranslationRecord [TYPE Class]\n# @BRIEF Individual translation result for a single SQL statement or dashboard element, tracking source, target, and error state.\nclass TranslationRecord(Base):\n __tablename__ = \"translation_records\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n batch_id = Column(String, ForeignKey(\"translation_batches.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=False, index=True)\n source_sql = Column(Text, nullable=True)\n target_sql = Column(Text, nullable=True)\n source_object_type = Column(String, nullable=True) # query, dashboard, chart, dataset\n source_object_id = Column(String, nullable=True)\n source_object_name = Column(String, nullable=True)\n source_data = Column(JSON, nullable=True, comment=\"Original source row key columns for upsert matching\")\n status = Column(String, nullable=False, default=\"PENDING\") # PENDING, SUCCESS, FAILED, SKIPPED\n error_message = Column(Text, nullable=True)\n token_count_input = Column(Integer, nullable=True)\n token_count_output = Column(Integer, nullable=True)\n translation_duration_ms = Column(Integer, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n languages = relationship(\"TranslationLanguage\", back_populates=\"record\")\n\n __table_args__ = (\n Index(\"ix_translation_records_run_status\", \"run_id\", \"status\"),\n )\n# #endregion TranslationRecord\n\n\n# #region TranslationEvent [TYPE Class]\n# @BRIEF Audit/event log for translation operations, with optional run_id for context.\nclass TranslationEvent(Base):\n __tablename__ = \"translation_events\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=True, index=True)\n event_type = Column(String, nullable=False) # JOB_CREATED, JOB_STARTED, JOB_COMPLETED, JOB_FAILED, etc.\n event_data = Column(JSON, nullable=True)\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n# #endregion TranslationEvent\n\n\n# #region TranslationPreviewSession [TYPE Class]\n# @BRIEF A preview session allowing users to review proposed translations before applying them.\nclass TranslationPreviewSession(Base):\n __tablename__ = \"translation_preview_sessions\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=True)\n status = Column(String, nullable=False, default=\"ACTIVE\") # ACTIVE, APPLIED, DISCARDED, EXPIRED\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n expires_at = Column(DateTime, nullable=True)\n# #endregion TranslationPreviewSession\n\n\n# #region TranslationPreviewRecord [TYPE Class]\n# @BRIEF Individual preview entry within a preview session, showing original and translated content side by side.\nclass TranslationPreviewRecord(Base):\n __tablename__ = \"translation_preview_records\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n session_id = Column(String, ForeignKey(\"translation_preview_sessions.id\"), nullable=False, index=True)\n source_sql = Column(Text, nullable=True)\n target_sql = Column(Text, nullable=True)\n source_object_type = Column(String, nullable=True)\n source_object_id = Column(String, nullable=True)\n source_object_name = Column(String, nullable=True)\n source_data = Column(JSON, nullable=True, comment=\"Original source row key columns for upsert matching\")\n status = Column(String, nullable=False, default=\"PENDING\") # PENDING, APPROVED, REJECTED\n feedback = Column(Text, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n languages = relationship(\"TranslationPreviewLanguage\", back_populates=\"preview_record\")\n# #endregion TranslationPreviewRecord\n\n\n# #region TerminologyDictionary [TYPE Class]\n# @BRIEF A named collection of terminology mappings used during translation to ensure consistent term translation.\nclass TerminologyDictionary(Base):\n __tablename__ = \"terminology_dictionaries\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n name = Column(String, nullable=False)\n description = Column(Text, nullable=True)\n source_dialect = Column(String, nullable=False)\n target_dialect = Column(String, nullable=False)\n is_active = Column(Boolean, default=True)\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n updated_at = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))\n# #endregion TerminologyDictionary\n\n\n# #region DictionaryEntry [TYPE Class]\n# @BRIEF A single term mapping entry with case-insensitive unique constraint on source_term_normalized and origin tracking.\nclass DictionaryEntry(Base):\n __tablename__ = \"dictionary_entries\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n dictionary_id = Column(String, ForeignKey(\"terminology_dictionaries.id\"), nullable=False, index=True)\n source_term = Column(String, nullable=False)\n source_term_normalized = Column(String, nullable=False)\n target_term = Column(String, nullable=False)\n source_language = Column(String, nullable=False, comment=\"BCP-47 source language code\")\n target_language = Column(String, nullable=False, comment=\"BCP-47 target language code\")\n context_notes = Column(Text, nullable=True)\n context_data = Column(JSON, nullable=True, comment=\"Structured context for term usage\")\n usage_notes = Column(Text, nullable=True, comment=\"Usage guidance for the term mapping\")\n has_context = Column(Boolean, default=False, comment=\"Whether context_data is populated\")\n context_source = Column(String, nullable=True, comment=\"auto|auto_with_edits|manual|bulk\")\n origin_source_language = Column(String, nullable=True, comment=\"Original source language of the term\")\n origin_run_id = Column(String, nullable=True, comment=\"Run ID from which this correction originated\")\n origin_row_key = Column(String, nullable=True, comment=\"Row key within the run that triggered this correction\")\n origin_user_id = Column(String, nullable=True, comment=\"User who submitted the correction\")\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n updated_at = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))\n\n __table_args__ = (\n UniqueConstraint(\n \"dictionary_id\", \"source_term_normalized\", \"source_language\", \"target_language\",\n name=\"uq_dict_source_term_lang\"\n ),\n Index(\"idx_dict_entry_lang\", \"source_language\", \"target_language\"),\n Index(\"idx_dict_has_context\", \"has_context\"),\n )\n# #endregion DictionaryEntry\n\n\n# #region TranslationSchedule [TYPE Class]\n# @BRIEF Defines a cron-based schedule for recurring translation jobs.\nclass TranslationSchedule(Base):\n __tablename__ = \"translation_schedules\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n cron_expression = Column(String, nullable=False)\n timezone = Column(String, nullable=False, default=\"UTC\")\n is_active = Column(Boolean, default=True)\n last_run_at = Column(DateTime, nullable=True)\n next_run_at = Column(DateTime, nullable=True)\n execution_mode = Column(String, nullable=False, default=\"full\", comment=\"full, new_key_only\")\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n updated_at = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))\n# #endregion TranslationSchedule\n\n\n# #region TranslationJobDictionary [TYPE Class]\n# @BRIEF Many-to-many association between translation jobs and terminology dictionaries.\nclass TranslationJobDictionary(Base):\n __tablename__ = \"translation_job_dictionaries\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n dictionary_id = Column(String, ForeignKey(\"terminology_dictionaries.id\"), nullable=False, index=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n __table_args__ = (\n UniqueConstraint(\n \"job_id\", \"dictionary_id\",\n name=\"uq_job_dictionary\"\n ),\n )\n# #endregion TranslationJobDictionary\n\n\n# #region MetricSnapshot [TYPE Class]\n# @BRIEF Captures translation performance metrics at a point in time with config/content/dictionary hash keys for aggregation.\nclass MetricSnapshot(Base):\n __tablename__ = \"translation_metric_snapshots\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=True, index=True)\n key_hash = Column(String, nullable=False, comment=\"Hash of dimension key fields for aggregation\")\n config_hash = Column(String, nullable=True, comment=\"Hash of translation configuration state\")\n dict_snapshot_hash = Column(String, nullable=True, comment=\"Hash of dictionary state at capture time\")\n covers_events_before = Column(DateTime, nullable=True, comment=\"Indicates snapshot covers events before this timestamp\")\n total_jobs = Column(Integer, default=0)\n total_runs = Column(Integer, default=0)\n total_records = Column(Integer, default=0)\n successful_records = Column(Integer, default=0)\n failed_records = Column(Integer, default=0)\n skipped_records = Column(Integer, default=0)\n avg_duration_ms = Column(Integer, nullable=True)\n p50_duration_ms = Column(Integer, nullable=True)\n p95_duration_ms = Column(Integer, nullable=True)\n p99_duration_ms = Column(Integer, nullable=True)\n per_language_metrics = Column(JSON, nullable=True, comment=\"Per-language cumulative metrics: {lang: {cumulative_tokens, cumulative_cost, runs}}\")\n snapshot_date = Column(DateTime, nullable=False, default=lambda: datetime.now(UTC))\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n __table_args__ = (\n Index(\"ix_metric_snapshots_job_date\", \"job_id\", \"snapshot_date\"),\n )\n# #endregion MetricSnapshot\n\n\n# #region TranslationLanguage [C:1] [TYPE Class]\n# @BRIEF Per-language translation result for a single record, supporting multi-language output.\nclass TranslationLanguage(Base):\n __tablename__ = \"translation_languages\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n record_id = Column(String, ForeignKey(\"translation_records.id\"), nullable=False)\n language_code = Column(String, nullable=False, comment=\"BCP-47 language code\")\n source_language_detected = Column(String, nullable=True, comment=\"BCP-47 or 'und' for undetermined\")\n translated_value = Column(Text, nullable=True, comment=\"LLM-generated translation\")\n user_edit = Column(Text, nullable=True, comment=\"User-edited translation\")\n final_value = Column(Text, nullable=True, comment=\"Final resolved value (translated or user edit)\")\n status = Column(String, default=\"pending\", comment=\"pending|translated|approved|edited|rejected|failed|skipped\")\n error_message = Column(Text, nullable=True)\n needs_review = Column(Boolean, default=False, comment=\"Flagged because source language could not be determined\")\n language_overridden = Column(Boolean, default=False, comment=\"Source language was manually overridden by user\")\n created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(UTC))\n\n record = relationship(\"TranslationRecord\", back_populates=\"languages\")\n\n __table_args__ = (\n UniqueConstraint(\"record_id\", \"language_code\", name=\"uq_record_language\"),\n Index(\"idx_tl_language\", \"language_code\"),\n Index(\"idx_tl_record_lang\", \"record_id\", \"language_code\"),\n )\n# #endregion TranslationLanguage\n\n\n# #region TranslationPreviewLanguage [C:1] [TYPE Class]\n# @BRIEF Per-language preview entry within a preview session.\nclass TranslationPreviewLanguage(Base):\n __tablename__ = \"translation_preview_languages\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n preview_record_id = Column(String, ForeignKey(\"translation_preview_records.id\"), nullable=False)\n language_code = Column(String, nullable=False, comment=\"BCP-47 language code\")\n source_language_detected = Column(String, nullable=True, comment=\"BCP-47 or 'und'\")\n translated_value = Column(Text, nullable=True)\n user_edit = Column(Text, nullable=True)\n final_value = Column(Text, nullable=True)\n status = Column(String, default=\"pending\", comment=\"pending|approved|edited|rejected\")\n needs_review = Column(Boolean, default=False, comment=\"Flagged because source language could not be determined\")\n created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(UTC))\n\n preview_record = relationship(\"TranslationPreviewRecord\", back_populates=\"languages\")\n\n __table_args__ = (\n UniqueConstraint(\"preview_record_id\", \"language_code\", name=\"uq_preview_record_language\"),\n )\n# #endregion TranslationPreviewLanguage\n\n\n# #region TranslationRunLanguageStats [C:1] [TYPE Class]\n# @BRIEF Per-language statistics for a translation run (row counts, tokens, cost).\nclass TranslationRunLanguageStats(Base):\n __tablename__ = \"translation_run_language_stats\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=False)\n language_code = Column(String, nullable=False, comment=\"BCP-47 language code\")\n total_rows = Column(Integer, default=0)\n translated_rows = Column(Integer, default=0)\n failed_rows = Column(Integer, default=0)\n skipped_rows = Column(Integer, default=0)\n token_count = Column(Integer, default=0)\n estimated_cost = Column(Float, default=0.0)\n\n run = relationship(\"TranslationRun\", back_populates=\"language_stats\")\n\n __table_args__ = (\n UniqueConstraint(\"run_id\", \"language_code\", name=\"uq_run_language\"),\n Index(\"idx_rls_run\", \"run_id\"),\n )\n# #endregion TranslationRunLanguageStats\n\n\n# #endregion TranslateModels\n" + "body": "# #region TranslateModels [C:3] [TYPE Module] [SEMANTICS sqlalchemy, translate, model, schema, dashboard, llm]\n# @BRIEF SQLAlchemy ORM models for LLM-based SQL/dashboard translation across dialects.\n# @LAYER: Domain\n# @RELATION INHERITS -> [MappingModels]\n\nimport uuid\nfrom datetime import UTC, datetime\n\nfrom sqlalchemy import JSON, Boolean, Column, DateTime, Float, ForeignKey, Index, Integer, String, Text, UniqueConstraint\nfrom sqlalchemy.orm import relationship\n\nfrom .mapping import Base\n\n\ndef generate_uuid():\n return str(uuid.uuid4())\n\n\n# #region TranslationJob [TYPE Class]\n# @BRIEF A translation job representing a multi-dialect conversion task with column mappings, LLM config, and dictionary attachments.\n# @RATIONALE: Snapshot isolation — in-progress runs use config snapshot; config edits affect future runs only.\n# @REJECTED: Invalidating in-progress runs on config edit would break scheduled run continuity.\nclass TranslationJob(Base):\n __tablename__ = \"translation_jobs\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n name = Column(String, nullable=False)\n description = Column(Text, nullable=True)\n source_dialect = Column(String, nullable=False)\n target_dialect = Column(String, nullable=False)\n database_dialect = Column(String, nullable=True, comment=\"Detected dialect from Superset connection at save time\")\n status = Column(String, nullable=False, default=\"DRAFT\") # DRAFT, READY, RUNNING, COMPLETED, FAILED, CANCELLED\n\n # Datasource & target table configuration\n source_datasource_id = Column(String, nullable=True, comment=\"Superset datasource ID\")\n source_table = Column(String, nullable=True, comment=\"Source table name resolved from datasource\")\n target_schema = Column(String, nullable=True, comment=\"Target table schema\")\n target_table = Column(String, nullable=True, comment=\"Target table name\")\n\n # Column mapping\n source_key_cols = Column(JSON, nullable=True, comment=\"Source key column names for composite key\")\n target_key_cols = Column(JSON, nullable=True, comment=\"Target key column names for composite key\")\n translation_column = Column(String, nullable=True, comment=\"Source column whose values will be translated\")\n target_column = Column(String, nullable=True, comment=\"Target column for translated output (defaults to translation_column)\")\n target_language_column = Column(String, nullable=True, comment=\"Target column for language code (e.g. 'ru', 'en')\")\n target_source_column = Column(String, nullable=True, comment=\"Target column for source/original text\")\n target_source_language_column = Column(String, nullable=True, comment=\"Target column for detected source language (BCP-47)\")\n context_columns = Column(JSON, nullable=True, comment=\"Context column names included in LLM prompt\")\n\n # LLM & processing settings\n # source_language removed — deprecated, auto-detected per row by LLM; use TranslationLanguage.source_language_detected instead\n target_languages = Column(JSON, nullable=True, comment=\"List of BCP-47 target language codes (multi-language support)\")\n provider_id = Column(String, nullable=True, comment=\"LLM provider ID\")\n batch_size = Column(Integer, nullable=False, default=50, comment=\"Records per batch\")\n disable_reasoning = Column(Boolean, default=False, comment=\"If true, pass reasoning_effort:none to LLM to save output tokens\")\n upsert_strategy = Column(String, nullable=False, default=\"MERGE\", comment=\"MERGE, INSERT, UPDATE\")\n\n # Environment association\n environment_id = Column(String, nullable=True, comment=\"Superset environment ID for datasource access\")\n target_database_id = Column(String, nullable=True, comment=\"Superset database ID for SQL Lab insert target\")\n\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n updated_at = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))\n# #endregion TranslationJob\n\n\n# #region TranslationRun [TYPE Class]\n# @BRIEF Represents a single execution of a translation job, capturing status, timing, and Superset execution metadata.\nclass TranslationRun(Base):\n __tablename__ = \"translation_runs\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n status = Column(String, nullable=False, default=\"PENDING\") # PENDING, RUNNING, COMPLETED, FAILED, CANCELLED\n trigger_type = Column(String, nullable=True, comment=\"manual, scheduled, retry, baseline_expired\")\n started_at = Column(DateTime, nullable=True)\n completed_at = Column(DateTime, nullable=True)\n error_message = Column(Text, nullable=True)\n total_records = Column(Integer, default=0)\n successful_records = Column(Integer, default=0)\n failed_records = Column(Integer, default=0)\n skipped_records = Column(Integer, default=0)\n insert_status = Column(String, nullable=True, comment=\"Status of the Superset insert/update operation\")\n superset_execution_id = Column(String, nullable=True, comment=\"Superset execution/task ID\")\n superset_execution_log = Column(JSON, nullable=True, comment=\"Superset execution log output\")\n config_snapshot = Column(JSON, nullable=True, comment=\"Snapshot of job config at run creation time\")\n key_hash = Column(String, nullable=True, comment=\"Hash of source key fields for dedup\")\n config_hash = Column(String, nullable=True, comment=\"Hash of translation configuration state\")\n dict_snapshot_hash = Column(String, nullable=True, comment=\"Hash of dictionary state at run time\")\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n language_stats = relationship(\"TranslationRunLanguageStats\", back_populates=\"run\")\n# #endregion TranslationRun\n\n\n# #region TranslationBatch [TYPE Class]\n# @BRIEF Groups translation records within a run into manageable batches with timing and record counts.\nclass TranslationBatch(Base):\n __tablename__ = \"translation_batches\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=False, index=True)\n batch_index = Column(Integer, nullable=False)\n status = Column(String, nullable=False, default=\"PENDING\")\n total_records = Column(Integer, default=0)\n successful_records = Column(Integer, default=0)\n failed_records = Column(Integer, default=0)\n started_at = Column(DateTime, nullable=True)\n completed_at = Column(DateTime, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n# #endregion TranslationBatch\n\n\n# #region TranslationRecord [TYPE Class]\n# @BRIEF Individual translation result for a single SQL statement or dashboard element, tracking source, target, and error state.\nclass TranslationRecord(Base):\n __tablename__ = \"translation_records\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n batch_id = Column(String, ForeignKey(\"translation_batches.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=False, index=True)\n source_sql = Column(Text, nullable=True)\n target_sql = Column(Text, nullable=True)\n source_object_type = Column(String, nullable=True) # query, dashboard, chart, dataset\n source_object_id = Column(String, nullable=True)\n source_object_name = Column(String, nullable=True)\n source_data = Column(JSON, nullable=True, comment=\"Original source row key columns for upsert matching\")\n status = Column(String, nullable=False, default=\"PENDING\") # PENDING, SUCCESS, FAILED, SKIPPED\n error_message = Column(Text, nullable=True)\n token_count_input = Column(Integer, nullable=True)\n token_count_output = Column(Integer, nullable=True)\n translation_duration_ms = Column(Integer, nullable=True)\n source_hash = Column(String, nullable=True,\n comment=\"SHA256(source_text+context+dict_snapshot_hash+config_hash) for cache dedup — excludes key/identifier columns\")\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n languages = relationship(\"TranslationLanguage\", back_populates=\"record\")\n\n __table_args__ = (\n Index(\"ix_translation_records_run_status\", \"run_id\", \"status\"),\n Index(\"ix_translation_records_source_hash_status\", \"source_hash\", \"status\"),\n )\n# #endregion TranslationRecord\n\n\n# #region TranslationEvent [TYPE Class]\n# @BRIEF Audit/event log for translation operations, with optional run_id for context.\nclass TranslationEvent(Base):\n __tablename__ = \"translation_events\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=True, index=True)\n event_type = Column(String, nullable=False) # JOB_CREATED, JOB_STARTED, JOB_COMPLETED, JOB_FAILED, etc.\n event_data = Column(JSON, nullable=True)\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n# #endregion TranslationEvent\n\n\n# #region TranslationPreviewSession [TYPE Class]\n# @BRIEF A preview session allowing users to review proposed translations before applying them.\nclass TranslationPreviewSession(Base):\n __tablename__ = \"translation_preview_sessions\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=True)\n status = Column(String, nullable=False, default=\"ACTIVE\") # ACTIVE, APPLIED, DISCARDED, EXPIRED\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n expires_at = Column(DateTime, nullable=True)\n# #endregion TranslationPreviewSession\n\n\n# #region TranslationPreviewRecord [TYPE Class]\n# @BRIEF Individual preview entry within a preview session, showing original and translated content side by side.\nclass TranslationPreviewRecord(Base):\n __tablename__ = \"translation_preview_records\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n session_id = Column(String, ForeignKey(\"translation_preview_sessions.id\"), nullable=False, index=True)\n source_sql = Column(Text, nullable=True)\n target_sql = Column(Text, nullable=True)\n source_object_type = Column(String, nullable=True)\n source_object_id = Column(String, nullable=True)\n source_object_name = Column(String, nullable=True)\n source_data = Column(JSON, nullable=True, comment=\"Original source row key columns for upsert matching\")\n status = Column(String, nullable=False, default=\"PENDING\") # PENDING, APPROVED, REJECTED\n feedback = Column(Text, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n languages = relationship(\"TranslationPreviewLanguage\", back_populates=\"preview_record\")\n# #endregion TranslationPreviewRecord\n\n\n# #region TerminologyDictionary [TYPE Class]\n# @BRIEF A named collection of terminology mappings used during translation to ensure consistent term translation.\nclass TerminologyDictionary(Base):\n __tablename__ = \"terminology_dictionaries\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n name = Column(String, nullable=False)\n description = Column(Text, nullable=True)\n source_dialect = Column(String, nullable=False)\n target_dialect = Column(String, nullable=False)\n is_active = Column(Boolean, default=True)\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n updated_at = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))\n# #endregion TerminologyDictionary\n\n\n# #region DictionaryEntry [TYPE Class]\n# @BRIEF A single term mapping entry with case-insensitive unique constraint on source_term_normalized and origin tracking.\nclass DictionaryEntry(Base):\n __tablename__ = \"dictionary_entries\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n dictionary_id = Column(String, ForeignKey(\"terminology_dictionaries.id\"), nullable=False, index=True)\n source_term = Column(String, nullable=False)\n source_term_normalized = Column(String, nullable=False)\n target_term = Column(String, nullable=False)\n source_language = Column(String, nullable=False, comment=\"BCP-47 source language code\")\n target_language = Column(String, nullable=False, comment=\"BCP-47 target language code\")\n context_notes = Column(Text, nullable=True)\n context_data = Column(JSON, nullable=True, comment=\"Structured context for term usage\")\n usage_notes = Column(Text, nullable=True, comment=\"Usage guidance for the term mapping\")\n has_context = Column(Boolean, default=False, comment=\"Whether context_data is populated\")\n context_source = Column(String, nullable=True, comment=\"auto|auto_with_edits|manual|bulk\")\n origin_source_language = Column(String, nullable=True, comment=\"Original source language of the term\")\n origin_run_id = Column(String, nullable=True, comment=\"Run ID from which this correction originated\")\n origin_row_key = Column(String, nullable=True, comment=\"Row key within the run that triggered this correction\")\n origin_user_id = Column(String, nullable=True, comment=\"User who submitted the correction\")\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n updated_at = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))\n\n __table_args__ = (\n UniqueConstraint(\n \"dictionary_id\", \"source_term_normalized\", \"source_language\", \"target_language\",\n name=\"uq_dict_source_term_lang\"\n ),\n Index(\"idx_dict_entry_lang\", \"source_language\", \"target_language\"),\n Index(\"idx_dict_has_context\", \"has_context\"),\n )\n# #endregion DictionaryEntry\n\n\n# #region TranslationSchedule [TYPE Class]\n# @BRIEF Defines a cron-based schedule for recurring translation jobs.\nclass TranslationSchedule(Base):\n __tablename__ = \"translation_schedules\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n cron_expression = Column(String, nullable=False)\n timezone = Column(String, nullable=False, default=\"UTC\")\n is_active = Column(Boolean, default=True)\n last_run_at = Column(DateTime, nullable=True)\n next_run_at = Column(DateTime, nullable=True)\n execution_mode = Column(String, nullable=False, default=\"full\", comment=\"full, new_key_only\")\n created_by = Column(String, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n updated_at = Column(DateTime, default=lambda: datetime.now(UTC), onupdate=lambda: datetime.now(UTC))\n# #endregion TranslationSchedule\n\n\n# #region TranslationJobDictionary [TYPE Class]\n# @BRIEF Many-to-many association between translation jobs and terminology dictionaries.\nclass TranslationJobDictionary(Base):\n __tablename__ = \"translation_job_dictionaries\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n dictionary_id = Column(String, ForeignKey(\"terminology_dictionaries.id\"), nullable=False, index=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n __table_args__ = (\n UniqueConstraint(\n \"job_id\", \"dictionary_id\",\n name=\"uq_job_dictionary\"\n ),\n )\n# #endregion TranslationJobDictionary\n\n\n# #region MetricSnapshot [TYPE Class]\n# @BRIEF Captures translation performance metrics at a point in time with config/content/dictionary hash keys for aggregation.\nclass MetricSnapshot(Base):\n __tablename__ = \"translation_metric_snapshots\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n job_id = Column(String, ForeignKey(\"translation_jobs.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=True, index=True)\n key_hash = Column(String, nullable=False, comment=\"Hash of dimension key fields for aggregation\")\n config_hash = Column(String, nullable=True, comment=\"Hash of translation configuration state\")\n dict_snapshot_hash = Column(String, nullable=True, comment=\"Hash of dictionary state at capture time\")\n covers_events_before = Column(DateTime, nullable=True, comment=\"Indicates snapshot covers events before this timestamp\")\n total_jobs = Column(Integer, default=0)\n total_runs = Column(Integer, default=0)\n total_records = Column(Integer, default=0)\n successful_records = Column(Integer, default=0)\n failed_records = Column(Integer, default=0)\n skipped_records = Column(Integer, default=0)\n avg_duration_ms = Column(Integer, nullable=True)\n p50_duration_ms = Column(Integer, nullable=True)\n p95_duration_ms = Column(Integer, nullable=True)\n p99_duration_ms = Column(Integer, nullable=True)\n per_language_metrics = Column(JSON, nullable=True, comment=\"Per-language cumulative metrics: {lang: {cumulative_tokens, cumulative_cost, runs}}\")\n snapshot_date = Column(DateTime, nullable=False, default=lambda: datetime.now(UTC))\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n __table_args__ = (\n Index(\"ix_metric_snapshots_job_date\", \"job_id\", \"snapshot_date\"),\n )\n# #endregion MetricSnapshot\n\n\n# #region TranslationLanguage [C:1] [TYPE Class]\n# @BRIEF Per-language translation result for a single record, supporting multi-language output.\nclass TranslationLanguage(Base):\n __tablename__ = \"translation_languages\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n record_id = Column(String, ForeignKey(\"translation_records.id\"), nullable=False)\n language_code = Column(String, nullable=False, comment=\"BCP-47 language code\")\n source_language_detected = Column(String, nullable=True, comment=\"BCP-47 or 'und' for undetermined\")\n translated_value = Column(Text, nullable=True, comment=\"LLM-generated translation\")\n user_edit = Column(Text, nullable=True, comment=\"User-edited translation\")\n final_value = Column(Text, nullable=True, comment=\"Final resolved value (translated or user edit)\")\n status = Column(String, default=\"pending\", comment=\"pending|translated|approved|edited|rejected|failed|skipped\")\n error_message = Column(Text, nullable=True)\n needs_review = Column(Boolean, default=False, comment=\"Flagged because source language could not be determined\")\n language_overridden = Column(Boolean, default=False, comment=\"Source language was manually overridden by user\")\n created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(UTC))\n\n record = relationship(\"TranslationRecord\", back_populates=\"languages\")\n\n __table_args__ = (\n UniqueConstraint(\"record_id\", \"language_code\", name=\"uq_record_language\"),\n Index(\"idx_tl_language\", \"language_code\"),\n Index(\"idx_tl_record_lang\", \"record_id\", \"language_code\"),\n )\n# #endregion TranslationLanguage\n\n\n# #region TranslationPreviewLanguage [C:1] [TYPE Class]\n# @BRIEF Per-language preview entry within a preview session.\nclass TranslationPreviewLanguage(Base):\n __tablename__ = \"translation_preview_languages\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n preview_record_id = Column(String, ForeignKey(\"translation_preview_records.id\"), nullable=False)\n language_code = Column(String, nullable=False, comment=\"BCP-47 language code\")\n source_language_detected = Column(String, nullable=True, comment=\"BCP-47 or 'und'\")\n translated_value = Column(Text, nullable=True)\n user_edit = Column(Text, nullable=True)\n final_value = Column(Text, nullable=True)\n status = Column(String, default=\"pending\", comment=\"pending|approved|edited|rejected\")\n needs_review = Column(Boolean, default=False, comment=\"Flagged because source language could not be determined\")\n created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(UTC))\n\n preview_record = relationship(\"TranslationPreviewRecord\", back_populates=\"languages\")\n\n __table_args__ = (\n UniqueConstraint(\"preview_record_id\", \"language_code\", name=\"uq_preview_record_language\"),\n )\n# #endregion TranslationPreviewLanguage\n\n\n# #region TranslationRunLanguageStats [C:1] [TYPE Class]\n# @BRIEF Per-language statistics for a translation run (row counts, tokens, cost).\nclass TranslationRunLanguageStats(Base):\n __tablename__ = \"translation_run_language_stats\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=False)\n language_code = Column(String, nullable=False, comment=\"BCP-47 language code\")\n total_rows = Column(Integer, default=0)\n translated_rows = Column(Integer, default=0)\n failed_rows = Column(Integer, default=0)\n skipped_rows = Column(Integer, default=0)\n token_count = Column(Integer, default=0)\n estimated_cost = Column(Float, default=0.0)\n\n run = relationship(\"TranslationRun\", back_populates=\"language_stats\")\n\n __table_args__ = (\n UniqueConstraint(\"run_id\", \"language_code\", name=\"uq_run_language\"),\n Index(\"idx_rls_run\", \"run_id\"),\n )\n# #endregion TranslationRunLanguageStats\n\n\n# #endregion TranslateModels\n" }, { "contract_id": "TranslationJob", @@ -47407,7 +47107,7 @@ "contract_type": "Class", "file_path": "backend/src/models/translate.py", "start_line": 116, - "end_line": 142, + "end_line": 145, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47417,14 +47117,14 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslationRecord [TYPE Class]\n# @BRIEF Individual translation result for a single SQL statement or dashboard element, tracking source, target, and error state.\nclass TranslationRecord(Base):\n __tablename__ = \"translation_records\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n batch_id = Column(String, ForeignKey(\"translation_batches.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=False, index=True)\n source_sql = Column(Text, nullable=True)\n target_sql = Column(Text, nullable=True)\n source_object_type = Column(String, nullable=True) # query, dashboard, chart, dataset\n source_object_id = Column(String, nullable=True)\n source_object_name = Column(String, nullable=True)\n source_data = Column(JSON, nullable=True, comment=\"Original source row key columns for upsert matching\")\n status = Column(String, nullable=False, default=\"PENDING\") # PENDING, SUCCESS, FAILED, SKIPPED\n error_message = Column(Text, nullable=True)\n token_count_input = Column(Integer, nullable=True)\n token_count_output = Column(Integer, nullable=True)\n translation_duration_ms = Column(Integer, nullable=True)\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n languages = relationship(\"TranslationLanguage\", back_populates=\"record\")\n\n __table_args__ = (\n Index(\"ix_translation_records_run_status\", \"run_id\", \"status\"),\n )\n# #endregion TranslationRecord\n" + "body": "# #region TranslationRecord [TYPE Class]\n# @BRIEF Individual translation result for a single SQL statement or dashboard element, tracking source, target, and error state.\nclass TranslationRecord(Base):\n __tablename__ = \"translation_records\"\n\n id = Column(String, primary_key=True, default=generate_uuid)\n batch_id = Column(String, ForeignKey(\"translation_batches.id\"), nullable=False, index=True)\n run_id = Column(String, ForeignKey(\"translation_runs.id\"), nullable=False, index=True)\n source_sql = Column(Text, nullable=True)\n target_sql = Column(Text, nullable=True)\n source_object_type = Column(String, nullable=True) # query, dashboard, chart, dataset\n source_object_id = Column(String, nullable=True)\n source_object_name = Column(String, nullable=True)\n source_data = Column(JSON, nullable=True, comment=\"Original source row key columns for upsert matching\")\n status = Column(String, nullable=False, default=\"PENDING\") # PENDING, SUCCESS, FAILED, SKIPPED\n error_message = Column(Text, nullable=True)\n token_count_input = Column(Integer, nullable=True)\n token_count_output = Column(Integer, nullable=True)\n translation_duration_ms = Column(Integer, nullable=True)\n source_hash = Column(String, nullable=True,\n comment=\"SHA256(source_text+context+dict_snapshot_hash+config_hash) for cache dedup — excludes key/identifier columns\")\n created_at = Column(DateTime, default=lambda: datetime.now(UTC))\n\n languages = relationship(\"TranslationLanguage\", back_populates=\"record\")\n\n __table_args__ = (\n Index(\"ix_translation_records_run_status\", \"run_id\", \"status\"),\n Index(\"ix_translation_records_source_hash_status\", \"source_hash\", \"status\"),\n )\n# #endregion TranslationRecord\n" }, { "contract_id": "TranslationEvent", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 145, - "end_line": 157, + "start_line": 148, + "end_line": 160, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47440,8 +47140,8 @@ "contract_id": "TranslationPreviewSession", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 160, - "end_line": 172, + "start_line": 163, + "end_line": 175, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47457,8 +47157,8 @@ "contract_id": "TranslationPreviewRecord", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 175, - "end_line": 193, + "start_line": 178, + "end_line": 196, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47474,8 +47174,8 @@ "contract_id": "TerminologyDictionary", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 196, - "end_line": 210, + "start_line": 199, + "end_line": 213, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47491,8 +47191,8 @@ "contract_id": "DictionaryEntry", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 213, - "end_line": 245, + "start_line": 216, + "end_line": 248, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47508,8 +47208,8 @@ "contract_id": "TranslationSchedule", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 248, - "end_line": 264, + "start_line": 251, + "end_line": 267, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47525,8 +47225,8 @@ "contract_id": "TranslationJobDictionary", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 267, - "end_line": 283, + "start_line": 270, + "end_line": 286, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47542,8 +47242,8 @@ "contract_id": "MetricSnapshot", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 286, - "end_line": 315, + "start_line": 289, + "end_line": 318, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47559,8 +47259,8 @@ "contract_id": "TranslationLanguage", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 318, - "end_line": 343, + "start_line": 321, + "end_line": 346, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47577,8 +47277,8 @@ "contract_id": "TranslationPreviewLanguage", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 346, - "end_line": 367, + "start_line": 349, + "end_line": 370, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -47595,8 +47295,8 @@ "contract_id": "TranslationRunLanguageStats", "contract_type": "Class", "file_path": "backend/src/models/translate.py", - "start_line": 370, - "end_line": 391, + "start_line": 373, + "end_line": 394, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -48126,7 +47826,7 @@ "contract_type": "Module", "file_path": "backend/src/plugins/llm_analysis/models.py", "start_line": 1, - "end_line": 64, + "end_line": 63, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -48140,31 +47840,19 @@ "relation_type": "DEPENDS_ON", "target_id": "pydantic", "target_ref": "pydantic" - }, - { - "source_id": "LLMAnalysisModels", - "relation_type": "DEPENDS_on", - "target_id": "pydantic", - "target_ref": "pydantic" - }, - { - "source_id": "LLMAnalysisModels", - "relation_type": "DEPENDs_on", - "target_id": "pydantic", - "target_ref": "pydantic" } ], "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region LLMAnalysisModels [C:3] [TYPE Module] [SEMANTICS pydantic, llm, plugin, model, provider-type]\n# @BRIEF Define Pydantic models for LLM Analysis plugin.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> pydantic\n# @RELATION DEPENDS_on -> pydantic\n# @RELATION DEPENDs_on -> pydantic\n\nfrom datetime import datetime\nfrom enum import Enum\n\nfrom pydantic import BaseModel, Field\n\n\n# #region LLMProviderType [TYPE Class]\n# @BRIEF Enum for supported LLM providers.\nclass LLMProviderType(str, Enum):\n OPENAI = \"openai\"\n OPENROUTER = \"openrouter\"\n KILO = \"kilo\"\n# #endregion LLMProviderType\n\n# #region LLMProviderConfig [TYPE Class]\n# @BRIEF Configuration for an LLM provider.\nclass LLMProviderConfig(BaseModel):\n id: str | None = None\n provider_type: LLMProviderType\n name: str\n base_url: str\n api_key: str | None = None\n default_model: str\n is_active: bool = True\n# #endregion LLMProviderConfig\n\n# #region ValidationStatus [TYPE Class]\n# @BRIEF Enum for dashboard validation status.\nclass ValidationStatus(str, Enum):\n PASS = \"PASS\"\n WARN = \"WARN\"\n FAIL = \"FAIL\"\n UNKNOWN = \"UNKNOWN\"\n# #endregion ValidationStatus\n\n# #region DetectedIssue [TYPE Class]\n# @BRIEF Model for a single issue detected during validation.\nclass DetectedIssue(BaseModel):\n severity: ValidationStatus\n message: str\n location: str | None = None\n# #endregion DetectedIssue\n\n# #region ValidationResult [TYPE Class]\n# @BRIEF Model for dashboard validation result.\nclass ValidationResult(BaseModel):\n id: str | None = None\n dashboard_id: str\n timestamp: datetime = Field(default_factory=datetime.utcnow)\n status: ValidationStatus\n screenshot_path: str | None = None\n issues: list[DetectedIssue]\n summary: str\n raw_response: str | None = None\n# #endregion ValidationResult\n\n# #endregion LLMAnalysisModels\n" + "body": "# #region LLMAnalysisModels [C:3] [TYPE Module] [SEMANTICS pydantic, llm, plugin, model, provider-type]\n# @BRIEF Define Pydantic models for LLM Analysis plugin.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> pydantic\n\nfrom datetime import datetime\nfrom enum import Enum\n\nfrom pydantic import BaseModel, Field\n\n\n# #region LLMProviderType [TYPE Class]\n# @BRIEF Enum for supported LLM providers.\nclass LLMProviderType(str, Enum):\n OPENAI = \"openai\"\n OPENROUTER = \"openrouter\"\n KILO = \"kilo\"\n LITELLM = \"litellm\"\n# #endregion LLMProviderType\n\n# #region LLMProviderConfig [TYPE Class]\n# @BRIEF Configuration for an LLM provider.\nclass LLMProviderConfig(BaseModel):\n id: str | None = None\n provider_type: LLMProviderType\n name: str\n base_url: str\n api_key: str | None = None\n default_model: str\n is_active: bool = True\n# #endregion LLMProviderConfig\n\n# #region ValidationStatus [TYPE Class]\n# @BRIEF Enum for dashboard validation status.\nclass ValidationStatus(str, Enum):\n PASS = \"PASS\"\n WARN = \"WARN\"\n FAIL = \"FAIL\"\n UNKNOWN = \"UNKNOWN\"\n# #endregion ValidationStatus\n\n# #region DetectedIssue [TYPE Class]\n# @BRIEF Model for a single issue detected during validation.\nclass DetectedIssue(BaseModel):\n severity: ValidationStatus\n message: str\n location: str | None = None\n# #endregion DetectedIssue\n\n# #region ValidationResult [TYPE Class]\n# @BRIEF Model for dashboard validation result.\nclass ValidationResult(BaseModel):\n id: str | None = None\n dashboard_id: str\n timestamp: datetime = Field(default_factory=datetime.utcnow)\n status: ValidationStatus\n screenshot_path: str | None = None\n issues: list[DetectedIssue]\n summary: str\n raw_response: str | None = None\n# #endregion ValidationResult\n\n# #endregion LLMAnalysisModels\n" }, { "contract_id": "LLMProviderType", "contract_type": "Class", "file_path": "backend/src/plugins/llm_analysis/models.py", - "start_line": 14, - "end_line": 20, + "start_line": 12, + "end_line": 19, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -48174,14 +47862,14 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region LLMProviderType [TYPE Class]\n# @BRIEF Enum for supported LLM providers.\nclass LLMProviderType(str, Enum):\n OPENAI = \"openai\"\n OPENROUTER = \"openrouter\"\n KILO = \"kilo\"\n# #endregion LLMProviderType\n" + "body": "# #region LLMProviderType [TYPE Class]\n# @BRIEF Enum for supported LLM providers.\nclass LLMProviderType(str, Enum):\n OPENAI = \"openai\"\n OPENROUTER = \"openrouter\"\n KILO = \"kilo\"\n LITELLM = \"litellm\"\n# #endregion LLMProviderType\n" }, { "contract_id": "LLMProviderConfig", "contract_type": "Class", "file_path": "backend/src/plugins/llm_analysis/models.py", - "start_line": 22, - "end_line": 32, + "start_line": 21, + "end_line": 31, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -48197,8 +47885,8 @@ "contract_id": "ValidationStatus", "contract_type": "Class", "file_path": "backend/src/plugins/llm_analysis/models.py", - "start_line": 34, - "end_line": 41, + "start_line": 33, + "end_line": 40, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -48214,8 +47902,8 @@ "contract_id": "DetectedIssue", "contract_type": "Class", "file_path": "backend/src/plugins/llm_analysis/models.py", - "start_line": 43, - "end_line": 49, + "start_line": 42, + "end_line": 48, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -48231,8 +47919,8 @@ "contract_id": "ValidationResult", "contract_type": "Class", "file_path": "backend/src/plugins/llm_analysis/models.py", - "start_line": 51, - "end_line": 62, + "start_line": 50, + "end_line": 61, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -48543,7 +48231,7 @@ "contract_type": "Module", "file_path": "backend/src/plugins/llm_analysis/service.py", "start_line": 1, - "end_line": 978, + "end_line": 981, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -48585,7 +48273,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region LLMAnalysisService [C:3] [TYPE Module] [SEMANTICS llm, screenshot, playwright, openai, tenacity]\n# @BRIEF Services for LLM interaction and dashboard screenshots.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> playwright\n# @RELATION DEPENDS_ON -> openai\n# @RELATION DEPENDS_ON -> tenacity\n# @INVARIANT: Screenshots must be 1920px width and capture full page height.\n\nimport asyncio\nimport base64\nimport io\nimport json\nimport os\nfrom typing import Any\nfrom urllib.parse import urlsplit\n\nimport httpx\nfrom openai import AsyncOpenAI, RateLimitError\nfrom openai import AuthenticationError as OpenAIAuthenticationError\nfrom PIL import Image\nfrom playwright.async_api import async_playwright\nfrom tenacity import retry, retry_if_exception, stop_after_attempt, wait_exponential\n\nfrom ...core.config_models import Environment\nfrom ...core.logger import belief_scope, logger\nfrom ...services.llm_prompt_templates import DEFAULT_LLM_PROMPTS, render_prompt\nfrom .models import LLMProviderType\n\n\n# #region ScreenshotService [TYPE Class]\n# @BRIEF Handles capturing screenshots of Superset dashboards.\nclass ScreenshotService:\n # region ScreenshotService.__init__ [TYPE Function]\n # @PURPOSE: Initializes the ScreenshotService with environment configuration.\n # @PRE: env is a valid Environment object.\n def __init__(self, env: Environment):\n self.env = env\n # endregion ScreenshotService.__init__\n\n # region ScreenshotService._find_first_visible_locator [TYPE Function]\n # @PURPOSE: Resolve the first visible locator from multiple Playwright locator strategies.\n # @PRE: candidates is a non-empty list of locator-like objects.\n # @POST: Returns a locator ready for interaction or None when nothing matches.\n async def _find_first_visible_locator(self, candidates) -> Any:\n for locator in candidates:\n try:\n match_count = await locator.count()\n for index in range(match_count):\n candidate = locator.nth(index)\n if await candidate.is_visible():\n return candidate\n except Exception:\n continue\n return None\n # endregion ScreenshotService._find_first_visible_locator\n\n # region ScreenshotService._iter_login_roots [TYPE Function]\n # @PURPOSE: Enumerate page and child frames where login controls may be rendered.\n # @PRE: page is a Playwright page-like object.\n # @POST: Returns ordered roots starting with main page followed by frames.\n def _iter_login_roots(self, page) -> list[Any]:\n roots = [page]\n page_frames = getattr(page, \"frames\", [])\n try:\n for frame in page_frames:\n if frame not in roots:\n roots.append(frame)\n except Exception:\n pass\n return roots\n # endregion ScreenshotService._iter_login_roots\n\n # region ScreenshotService._extract_hidden_login_fields [TYPE Function]\n # @PURPOSE: Collect hidden form fields required for direct login POST fallback.\n # @PRE: Login page is loaded.\n # @POST: Returns hidden input name/value mapping aggregated from page and child frames.\n async def _extract_hidden_login_fields(self, page) -> dict[str, str]:\n hidden_fields: dict[str, str] = {}\n for root in self._iter_login_roots(page):\n try:\n locator = root.locator(\"input[type='hidden'][name]\")\n count = await locator.count()\n for index in range(count):\n candidate = locator.nth(index)\n field_name = str(await candidate.get_attribute(\"name\") or \"\").strip()\n if not field_name or field_name in hidden_fields:\n continue\n hidden_fields[field_name] = str(await candidate.input_value()).strip()\n except Exception:\n continue\n return hidden_fields\n # endregion ScreenshotService._extract_hidden_login_fields\n\n # region ScreenshotService._extract_csrf_token [TYPE Function]\n # @PURPOSE: Resolve CSRF token value from main page or embedded login frame.\n # @PRE: Login page is loaded.\n # @POST: Returns first non-empty csrf token or empty string.\n async def _extract_csrf_token(self, page) -> str:\n hidden_fields = await self._extract_hidden_login_fields(page)\n return str(hidden_fields.get(\"csrf_token\") or \"\").strip()\n # endregion ScreenshotService._extract_csrf_token\n\n # region ScreenshotService._response_looks_like_login_page [TYPE Function]\n # @PURPOSE: Detect when fallback login POST returned the login form again instead of an authenticated page.\n # @PRE: response_text is normalized HTML or text from login POST response.\n # @POST: Returns True when login-page markers dominate the response body.\n def _response_looks_like_login_page(self, response_text: str) -> bool:\n normalized = str(response_text or \"\").strip().lower()\n if not normalized:\n return False\n\n markers = [\n \"enter your login and password below\",\n \"username:\",\n \"password:\",\n \"sign in\",\n 'name=\"csrf_token\"',\n ]\n return sum(marker in normalized for marker in markers) >= 3\n # endregion ScreenshotService._response_looks_like_login_page\n\n # region ScreenshotService._redirect_looks_authenticated [TYPE Function]\n # @PURPOSE: Treat non-login redirects after form POST as successful authentication without waiting for redirect target.\n # @PRE: redirect_location may be empty or relative.\n # @POST: Returns True when redirect target does not point back to login flow.\n def _redirect_looks_authenticated(self, redirect_location: str) -> bool:\n normalized = str(redirect_location or \"\").strip().lower()\n if not normalized:\n return True\n return \"/login\" not in normalized\n # endregion ScreenshotService._redirect_looks_authenticated\n\n # region ScreenshotService._submit_login_via_form_post [TYPE Function]\n # @PURPOSE: Fallback login path that submits credentials directly with csrf token.\n # @PRE: login_url is same-origin and csrf token can be read from DOM.\n # @POST: Browser context receives authenticated cookies when login succeeds.\n async def _submit_login_via_form_post(self, page, login_url: str) -> bool:\n hidden_fields = await self._extract_hidden_login_fields(page)\n csrf_token = str(hidden_fields.get(\"csrf_token\") or \"\").strip()\n if not csrf_token:\n logger.warning(\"[DEBUG] Direct form login fallback skipped: csrf_token not found\")\n return False\n\n try:\n request_context = page.context.request\n except Exception as context_error:\n logger.warning(f\"[DEBUG] Direct form login fallback skipped: request context unavailable: {context_error}\")\n return False\n\n parsed_url = urlsplit(login_url)\n origin = f\"{parsed_url.scheme}://{parsed_url.netloc}\" if parsed_url.scheme and parsed_url.netloc else login_url\n payload = dict(hidden_fields)\n payload[\"username\"] = self.env.username\n payload[\"password\"] = self.env.password\n logger.info(\n f\"[DEBUG] Attempting direct form login fallback via browser context request with hidden fields: {sorted(hidden_fields.keys())}\"\n )\n\n response = await request_context.post(\n login_url,\n form=payload,\n headers={\n \"Origin\": origin,\n \"Referer\": login_url,\n },\n timeout=10000,\n fail_on_status_code=False,\n max_redirects=0,\n )\n response_url = str(getattr(response, \"url\", \"\") or \"\")\n response_status = int(getattr(response, \"status\", 0) or 0)\n response_headers = dict(getattr(response, \"headers\", {}) or {})\n redirect_location = str(\n response_headers.get(\"location\")\n or response_headers.get(\"Location\")\n or \"\"\n ).strip()\n redirect_statuses = {301, 302, 303, 307, 308}\n if response_status in redirect_statuses:\n redirect_authenticated = self._redirect_looks_authenticated(redirect_location)\n logger.info(\n f\"[DEBUG] Direct form login fallback redirect response: status={response_status} url={response_url} location={redirect_location!r} authenticated={redirect_authenticated}\"\n )\n return redirect_authenticated\n\n response_text = await response.text()\n text_snippet = \" \".join(response_text.split())[:200]\n looks_like_login_page = self._response_looks_like_login_page(response_text)\n logger.info(\n f\"[DEBUG] Direct form login fallback response: status={response_status} url={response_url} login_markup={looks_like_login_page} snippet={text_snippet!r}\"\n )\n return not looks_like_login_page\n # endregion ScreenshotService._submit_login_via_form_post\n\n # region ScreenshotService._find_login_field_locator [TYPE Function]\n # @PURPOSE: Resolve login form input using semantic label text plus generic visible-input fallbacks.\n # @PRE: field_name is `username` or `password`.\n # @POST: Returns a locator for the corresponding input or None.\n async def _find_login_field_locator(self, page, field_name: str) -> Any:\n normalized = str(field_name or \"\").strip().lower()\n for root in self._iter_login_roots(page):\n if normalized == \"username\":\n input_candidates = [\n root.get_by_label(\"Username\", exact=False),\n root.get_by_label(\"Login\", exact=False),\n root.locator(\"label:text-matches('Username|Login', 'i')\").locator(\"xpath=following::input[1]\"),\n root.locator(\"text=/Username|Login/i\").locator(\"xpath=following::input[1]\"),\n root.locator(\"input[name='username']\"),\n root.locator(\"input#username\"),\n root.locator(\"input[placeholder*='Username']\"),\n root.locator(\"input[type='text']\"),\n root.locator(\"input:not([type='password'])\"),\n ]\n locator = await self._find_first_visible_locator(input_candidates)\n if locator:\n return locator\n\n if normalized == \"password\":\n input_candidates = [\n root.get_by_label(\"Password\", exact=False),\n root.locator(\"label:text-matches('Password', 'i')\").locator(\"xpath=following::input[1]\"),\n root.locator(\"text=/Password/i\").locator(\"xpath=following::input[1]\"),\n root.locator(\"input[name='password']\"),\n root.locator(\"input#password\"),\n root.locator(\"input[placeholder*='Password']\"),\n root.locator(\"input[type='password']\"),\n ]\n locator = await self._find_first_visible_locator(input_candidates)\n if locator:\n return locator\n\n return None\n # endregion ScreenshotService._find_login_field_locator\n\n # region ScreenshotService._find_submit_locator [TYPE Function]\n # @PURPOSE: Resolve login submit button from main page or embedded auth frame.\n # @PRE: page is ready for login interaction.\n # @POST: Returns visible submit locator or None.\n async def _find_submit_locator(self, page) -> Any:\n selectors = [\n lambda root: root.get_by_role(\"button\", name=\"Sign in\", exact=False),\n lambda root: root.get_by_role(\"button\", name=\"Login\", exact=False),\n lambda root: root.locator(\"button[type='submit']\"),\n lambda root: root.locator(\"button#submit\"),\n lambda root: root.locator(\".btn-primary\"),\n lambda root: root.locator(\"input[type='submit']\"),\n ]\n for root in self._iter_login_roots(page):\n locator = await self._find_first_visible_locator([factory(root) for factory in selectors])\n if locator:\n return locator\n return None\n # endregion ScreenshotService._find_submit_locator\n\n # region ScreenshotService._goto_resilient [TYPE Function]\n # @PURPOSE: Navigate without relying on networkidle for pages with long-polling or persistent requests.\n # @PRE: page is a valid Playwright page and url is non-empty.\n # @POST: Returns last navigation response or raises when both primary and fallback waits fail.\n async def _goto_resilient(\n self,\n page,\n url: str,\n primary_wait_until: str = \"domcontentloaded\",\n fallback_wait_until: str = \"load\",\n timeout: int = 60000,\n ):\n try:\n return await page.goto(url, wait_until=primary_wait_until, timeout=timeout)\n except Exception as primary_error:\n logger.warning(\n f\"[ScreenshotService._goto_resilient] Primary navigation wait '{primary_wait_until}' failed for {url}: {primary_error}\"\n )\n return await page.goto(url, wait_until=fallback_wait_until, timeout=timeout)\n # endregion ScreenshotService._goto_resilient\n\n # region ScreenshotService.capture_dashboard [TYPE Function]\n # @PURPOSE: Captures a full-page screenshot of a dashboard using Playwright and CDP.\n # @PRE: dashboard_id is a valid string, output_path is a writable path.\n # @POST: Returns True if screenshot is saved successfully.\n # @SIDE_EFFECT: Launches a browser, performs UI login, switches tabs, and writes a PNG file.\n # @UX_STATE: [Navigating] -> Loading dashboard UI\n # @UX_STATE: [TabSwitching] -> Iterating through dashboard tabs to trigger lazy loading\n # @UX_STATE: [CalculatingHeight] -> Determining dashboard dimensions\n # @UX_STATE: [Capturing] -> Executing CDP screenshot\n async def capture_dashboard(self, dashboard_id: str, output_path: str) -> bool:\n with belief_scope(\"capture_dashboard\", f\"dashboard_id={dashboard_id}\"):\n logger.info(f\"Capturing screenshot for dashboard {dashboard_id}\")\n async with async_playwright() as p:\n browser = await p.chromium.launch(\n headless=True,\n args=[\n \"--disable-blink-features=AutomationControlled\",\n \"--disable-infobars\",\n \"--no-sandbox\"\n ]\n )\n # Set a realistic user agent to avoid 403 Forbidden from OpenResty/WAF\n user_agent = \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\"\n # Construct base UI URL from environment (strip /api/v1 suffix)\n base_ui_url = self.env.url.rstrip(\"/\")\n if base_ui_url.endswith(\"/api/v1\"):\n base_ui_url = base_ui_url[:-len(\"/api/v1\")]\n\n # Create browser context with realistic headers\n context = await browser.new_context(\n viewport={'width': 1280, 'height': 720},\n user_agent=user_agent,\n extra_http_headers={\n \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\",\n \"Accept-Language\": \"ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7\",\n \"Upgrade-Insecure-Requests\": \"1\",\n \"Sec-Fetch-Dest\": \"document\",\n \"Sec-Fetch-Mode\": \"navigate\",\n \"Sec-Fetch-Site\": \"none\",\n \"Sec-Fetch-User\": \"?1\"\n }\n )\n logger.info(\"Browser context created successfully\")\n\n page = await context.new_page()\n # Bypass navigator.webdriver detection\n await page.add_init_script(\"delete Object.getPrototypeOf(navigator).webdriver\")\n\n # 1. Navigate to login page and authenticate\n login_url = f\"{base_ui_url.rstrip('/')}/login/\"\n logger.info(f\"[DEBUG] Navigating to login page: {login_url}\")\n\n response = await self._goto_resilient(\n page,\n login_url,\n primary_wait_until=\"domcontentloaded\",\n fallback_wait_until=\"load\",\n timeout=60000,\n )\n if response:\n logger.info(f\"[DEBUG] Login page response status: {response.status}\")\n\n # Wait for login form to be ready\n await page.wait_for_load_state(\"domcontentloaded\")\n\n # More exhaustive list of selectors for various Superset versions/themes\n selectors = {\n \"username\": ['input[name=\"username\"]', 'input#username', 'input[placeholder*=\"Username\"]', 'input[type=\"text\"]'],\n \"password\": ['input[name=\"password\"]', 'input#password', 'input[placeholder*=\"Password\"]', 'input[type=\"password\"]'],\n \"submit\": ['button[type=\"submit\"]', 'button#submit', '.btn-primary', 'input[type=\"submit\"]']\n }\n logger.info(\"[DEBUG] Attempting to find login form elements...\")\n\n try:\n used_direct_form_login = False\n # Find and fill username\n username_locator = await self._find_login_field_locator(page, \"username\")\n\n if not username_locator:\n roots = self._iter_login_roots(page)\n logger.info(f\"[DEBUG] Found {len(roots)} login roots including child frames\")\n for root_index, root in enumerate(roots[:5]):\n all_inputs = await root.locator('input').all()\n logger.info(f\"[DEBUG] Root {root_index}: found {len(all_inputs)} input fields\")\n for i, inp in enumerate(all_inputs[:5]): # Log first 5\n inp_type = await inp.get_attribute('type')\n inp_name = await inp.get_attribute('name')\n inp_id = await inp.get_attribute('id')\n logger.info(f\"[DEBUG] Root {root_index} input {i}: type={inp_type}, name={inp_name}, id={inp_id}\")\n used_direct_form_login = await self._submit_login_via_form_post(page, login_url)\n if not used_direct_form_login:\n raise RuntimeError(\"Could not find username input field on login page\")\n username_locator = None\n\n if username_locator is not None:\n logger.info(\"[DEBUG] Filling username field\")\n await username_locator.fill(self.env.username)\n\n # Find and fill password\n password_locator = await self._find_login_field_locator(page, \"password\") if username_locator is not None else None\n\n if username_locator is not None and not password_locator:\n raise RuntimeError(\"Could not find password input field on login page\")\n\n if password_locator is not None:\n logger.info(\"[DEBUG] Filling password field\")\n await password_locator.fill(self.env.password)\n\n # Click submit\n submit_locator = await self._find_submit_locator(page) if username_locator is not None else None\n\n if username_locator is not None and not submit_locator:\n raise RuntimeError(\"Could not find submit button on login page\")\n\n if submit_locator is not None:\n logger.info(\"[DEBUG] Clicking submit button\")\n await submit_locator.click()\n\n # Wait for navigation after login\n if not used_direct_form_login:\n try:\n await page.wait_for_load_state(\"load\", timeout=30000)\n except Exception as load_wait_error:\n logger.warning(f\"[DEBUG] Login post-submit load wait timed out: {load_wait_error}\")\n\n # Check if login was successful\n if not used_direct_form_login and \"/login\" in page.url:\n # Check for error messages on page\n error_msg = await page.locator(\".alert-danger, .error-message\").text_content() if await page.locator(\".alert-danger, .error-message\").count() > 0 else \"Unknown error\"\n logger.error(f\"[DEBUG] Login failed. Still on login page. Error: {error_msg}\")\n debug_path = output_path.replace(\".png\", \"_debug_failed_login.png\")\n await page.screenshot(path=debug_path)\n raise RuntimeError(f\"Login failed: {error_msg}. Debug screenshot saved to {debug_path}\")\n\n logger.info(f\"[DEBUG] Login successful. Current URL: {page.url}\")\n\n # Check cookies after successful login\n page_cookies = await context.cookies()\n logger.info(f\"[DEBUG] Cookies after login: {len(page_cookies)}\")\n for c in page_cookies:\n logger.info(f\"[DEBUG] Cookie: name={c['name']}, domain={c['domain']}, value={c.get('value', '')[:20]}...\")\n\n except Exception as e:\n page_title = await page.title()\n logger.error(f\"UI Login failed. Page title: {page_title}, URL: {page.url}, Error: {e!s}\")\n debug_path = output_path.replace(\".png\", \"_debug_failed_login.png\")\n await page.screenshot(path=debug_path)\n raise RuntimeError(f\"Login failed: {e!s}. Debug screenshot saved to {debug_path}\")\n\n # 2. Navigate to dashboard\n # @UX_STATE: [Navigating] -> Loading dashboard UI\n dashboard_url = f\"{base_ui_url.rstrip('/')}/superset/dashboard/{dashboard_id}/?standalone=true\"\n\n if base_ui_url.startswith(\"https://\") and dashboard_url.startswith(\"http://\"):\n dashboard_url = dashboard_url.replace(\"http://\", \"https://\")\n\n logger.info(f\"[DEBUG] Navigating to dashboard: {dashboard_url}\")\n\n # Dashboard pages can keep polling/network activity open indefinitely.\n response = await self._goto_resilient(\n page,\n dashboard_url,\n primary_wait_until=\"domcontentloaded\",\n fallback_wait_until=\"load\",\n timeout=60000,\n )\n\n if response:\n logger.info(f\"[DEBUG] Dashboard navigation response status: {response.status}, URL: {response.url}\")\n\n if \"/login\" in page.url:\n debug_path = output_path.replace(\".png\", \"_debug_failed_dashboard_auth.png\")\n await page.screenshot(path=debug_path)\n raise RuntimeError(\n f\"Dashboard navigation redirected to login page after authentication. Debug screenshot saved to {debug_path}\"\n )\n\n try:\n # Wait for the dashboard grid to be present\n await page.wait_for_selector('.dashboard-component, .dashboard-header, [data-test=\"dashboard-grid\"]', timeout=30000)\n logger.info(\"[DEBUG] Dashboard container loaded\")\n\n # Wait for charts to finish loading (Superset uses loading spinners/skeletons)\n # We wait until loading indicators disappear or a timeout occurs\n try:\n # Wait for loading indicators to disappear\n await page.wait_for_selector('.loading, .ant-skeleton, .spinner', state=\"hidden\", timeout=60000)\n logger.info(\"[DEBUG] Loading indicators hidden\")\n except Exception:\n logger.warning(\"[DEBUG] Timeout waiting for loading indicators to hide\")\n\n # Wait for charts to actually render their content (e.g., ECharts, NVD3)\n # We look for common chart containers that should have content\n try:\n await page.wait_for_selector('.chart-container canvas, .slice_container svg, .superset-chart-canvas, .grid-content .chart-container', timeout=60000)\n logger.info(\"[DEBUG] Chart content detected\")\n except Exception:\n logger.warning(\"[DEBUG] Timeout waiting for chart content\")\n\n # Additional check: wait for all chart containers to have non-empty content\n logger.info(\"[DEBUG] Waiting for all charts to have rendered content...\")\n await page.wait_for_function(\"\"\"() => {\n const charts = document.querySelectorAll('.chart-container, .slice_container');\n if (charts.length === 0) return true; // No charts to wait for\n \n // Check if all charts have rendered content (canvas, svg, or non-empty div)\n return Array.from(charts).every(chart => {\n const hasCanvas = chart.querySelector('canvas') !== null;\n const hasSvg = chart.querySelector('svg') !== null;\n const hasContent = chart.innerText.trim().length > 0 || chart.children.length > 0;\n return hasCanvas || hasSvg || hasContent;\n });\n }\"\"\", timeout=60000)\n logger.info(\"[DEBUG] All charts have rendered content\")\n\n # Scroll to bottom and back to top to trigger lazy loading of all charts\n logger.info(\"[DEBUG] Scrolling to trigger lazy loading...\")\n await page.evaluate(\"\"\"async () => {\n const delay = ms => new Promise(resolve => setTimeout(resolve, ms));\n for (let i = 0; i < document.body.scrollHeight; i += 500) {\n window.scrollTo(0, i);\n await delay(100);\n }\n window.scrollTo(0, 0);\n await delay(500);\n }\"\"\")\n\n except Exception as e:\n logger.warning(f\"[DEBUG] Dashboard content wait failed: {e}, proceeding anyway after delay\")\n\n # Final stabilization delay - increased for complex dashboards\n logger.info(\"[DEBUG] Final stabilization delay...\")\n await asyncio.sleep(15)\n\n # Logic to handle tabs and full-page capture\n try:\n # 1. Handle Tabs (Recursive switching)\n # @UX_STATE: [TabSwitching] -> Iterating through dashboard tabs to trigger lazy loading\n processed_tabs = set()\n\n async def switch_tabs(depth=0):\n if depth > 3:\n return # Limit recursion depth\n\n tab_selectors = [\n '.ant-tabs-nav-list .ant-tabs-tab',\n '.dashboard-component-tabs .ant-tabs-tab',\n '[data-test=\"dashboard-component-tabs\"] .ant-tabs-tab'\n ]\n\n found_tabs = []\n for selector in tab_selectors:\n found_tabs = await page.locator(selector).all()\n if found_tabs:\n break\n\n if found_tabs:\n logger.info(f\"[DEBUG][TabSwitching] Found {len(found_tabs)} tabs at depth {depth}\")\n for i, tab in enumerate(found_tabs):\n try:\n tab_text = (await tab.inner_text()).strip()\n tab_id = f\"{depth}_{i}_{tab_text}\"\n\n if tab_id in processed_tabs:\n continue\n\n if await tab.is_visible():\n logger.info(f\"[DEBUG][TabSwitching] Switching to tab: {tab_text}\")\n processed_tabs.add(tab_id)\n\n is_active = \"ant-tabs-tab-active\" in (await tab.get_attribute(\"class\") or \"\")\n if not is_active:\n await tab.click()\n await asyncio.sleep(2) # Wait for content to render\n\n await switch_tabs(depth + 1)\n except Exception as tab_e:\n logger.warning(f\"[DEBUG][TabSwitching] Failed to process tab {i}: {tab_e}\")\n\n try:\n first_tab = found_tabs[0]\n if \"ant-tabs-tab-active\" not in (await first_tab.get_attribute(\"class\") or \"\"):\n await first_tab.click()\n await asyncio.sleep(1)\n except Exception:\n pass\n\n await switch_tabs()\n\n # 2. Calculate full height for screenshot\n # @UX_STATE: [CalculatingHeight] -> Determining dashboard dimensions\n full_height = await page.evaluate(\"\"\"() => {\n const body = document.body;\n const html = document.documentElement;\n const dashboardContent = document.querySelector('.dashboard-content');\n \n return Math.max(\n body.scrollHeight, body.offsetHeight,\n html.clientHeight, html.scrollHeight, html.offsetHeight,\n dashboardContent ? dashboardContent.scrollHeight + 100 : 0\n );\n }\"\"\")\n logger.info(f\"[DEBUG] Calculated full height: {full_height}\")\n\n # DIAGNOSTIC: Count chart elements before resize\n chart_count_before = await page.evaluate(\"\"\"() => {\n return {\n chartContainers: document.querySelectorAll('.chart-container, .slice_container').length,\n canvasElements: document.querySelectorAll('canvas').length,\n svgElements: document.querySelectorAll('.chart-container svg, .slice_container svg').length,\n visibleCharts: document.querySelectorAll('.chart-container:visible, .slice_container:visible').length\n };\n }\"\"\")\n logger.info(f\"[DIAGNOSTIC] Chart elements BEFORE viewport resize: {chart_count_before}\")\n\n # DIAGNOSTIC: Capture pre-resize screenshot for comparison\n pre_resize_path = output_path.replace(\".png\", \"_preresize.png\")\n try:\n await page.screenshot(path=pre_resize_path, full_page=False, timeout=10000)\n import os\n pre_resize_size = os.path.getsize(pre_resize_path) if os.path.exists(pre_resize_path) else 0\n logger.info(f\"[DIAGNOSTIC] Pre-resize screenshot saved: {pre_resize_path} ({pre_resize_size} bytes)\")\n except Exception as pre_e:\n logger.warning(f\"[DIAGNOSTIC] Failed to capture pre-resize screenshot: {pre_e}\")\n\n logger.info(f\"[DIAGNOSTIC] Resizing viewport from current to 1920x{int(full_height)}\")\n await page.set_viewport_size({\"width\": 1920, \"height\": int(full_height)})\n\n # DIAGNOSTIC: Increased wait time and log timing\n logger.info(\"[DIAGNOSTIC] Waiting 10 seconds after viewport resize for re-render...\")\n await asyncio.sleep(10)\n logger.info(\"[DIAGNOSTIC] Wait completed\")\n\n # DIAGNOSTIC: Count chart elements after resize and wait\n chart_count_after = await page.evaluate(\"\"\"() => {\n return {\n chartContainers: document.querySelectorAll('.chart-container, .slice_container').length,\n canvasElements: document.querySelectorAll('canvas').length,\n svgElements: document.querySelectorAll('.chart-container svg, .slice_container svg').length,\n visibleCharts: document.querySelectorAll('.chart-container:visible, .slice_container:visible').length\n };\n }\"\"\")\n logger.info(f\"[DIAGNOSTIC] Chart elements AFTER viewport resize + wait: {chart_count_after}\")\n\n # DIAGNOSTIC: Check if any charts have error states\n chart_errors = await page.evaluate(\"\"\"() => {\n const errors = [];\n document.querySelectorAll('.chart-container, .slice_container').forEach((chart, i) => {\n const errorEl = chart.querySelector('.error, .alert-danger, .ant-alert-error');\n if (errorEl) {\n errors.push({index: i, text: errorEl.innerText.substring(0, 100)});\n }\n });\n return errors;\n }\"\"\")\n if chart_errors:\n logger.warning(f\"[DIAGNOSTIC] Charts with error states detected: {chart_errors}\")\n else:\n logger.info(\"[DIAGNOSTIC] No chart error states detected\")\n\n # 3. Take screenshot using CDP to bypass Playwright's font loading wait\n # @UX_STATE: [Capturing] -> Executing CDP screenshot\n logger.info(\"[DEBUG] Attempting full-page screenshot via CDP...\")\n cdp = await page.context.new_cdp_session(page)\n\n screenshot_data = await cdp.send(\"Page.captureScreenshot\", {\n \"format\": \"png\",\n \"fromSurface\": True,\n \"captureBeyondViewport\": True\n })\n\n image_data = base64.b64decode(screenshot_data[\"data\"])\n\n with open(output_path, 'wb') as f:\n f.write(image_data)\n\n # DIAGNOSTIC: Verify screenshot file\n import os\n final_size = os.path.getsize(output_path) if os.path.exists(output_path) else 0\n logger.info(f\"[DIAGNOSTIC] Final screenshot saved: {output_path}\")\n logger.info(f\"[DIAGNOSTIC] Final screenshot size: {final_size} bytes ({final_size / 1024:.2f} KB)\")\n\n # DIAGNOSTIC: Get image dimensions\n try:\n with Image.open(output_path) as final_img:\n logger.info(f\"[DIAGNOSTIC] Final screenshot dimensions: {final_img.width}x{final_img.height}\")\n except Exception as img_err:\n logger.warning(f\"[DIAGNOSTIC] Could not read final image dimensions: {img_err}\")\n\n logger.info(f\"Full-page screenshot saved to {output_path} (via CDP)\")\n except Exception as e:\n logger.error(f\"[DEBUG] Full-page/Tab capture failed: {e}\")\n try:\n await page.screenshot(path=output_path, full_page=True, timeout=10000)\n except Exception as e2:\n logger.error(f\"[DEBUG] Fallback screenshot also failed: {e2}\")\n await page.screenshot(path=output_path, timeout=5000)\n\n await browser.close()\n return True\n # endregion ScreenshotService.capture_dashboard\n# #endregion ScreenshotService\n\n# #region LLMClient [TYPE Class]\n# @BRIEF Wrapper for LLM provider APIs.\nclass LLMClient:\n # region LLMClient.__init__ [TYPE Function]\n # @PURPOSE: Initializes the LLMClient with provider settings.\n # @PRE: api_key, base_url, and default_model are non-empty strings.\n def __init__(self, provider_type: LLMProviderType, api_key: str, base_url: str, default_model: str):\n self.provider_type = provider_type\n normalized_key = (api_key or \"\").strip()\n if normalized_key.lower().startswith(\"bearer \"):\n normalized_key = normalized_key[7:].strip()\n self.api_key = normalized_key\n self.base_url = base_url\n self.default_model = default_model\n\n # DEBUG: Log initialization parameters (without exposing full API key)\n logger.info(\"[LLMClient.__init__] Initializing LLM client:\")\n logger.info(f\"[LLMClient.__init__] Provider Type: {provider_type}\")\n logger.info(f\"[LLMClient.__init__] Base URL: {base_url}\")\n logger.info(f\"[LLMClient.__init__] Default Model: {default_model}\")\n logger.info(f\"[LLMClient.__init__] API Key (first 8 chars): {self.api_key[:8] if self.api_key and len(self.api_key) > 8 else 'EMPTY_OR_NONE'}...\")\n logger.info(f\"[LLMClient.__init__] API Key Length: {len(self.api_key) if self.api_key else 0}\")\n\n # Some OpenAI-compatible gateways are strict about auth header naming.\n default_headers = {\"Authorization\": f\"Bearer {self.api_key}\"}\n if self.provider_type == LLMProviderType.OPENROUTER:\n default_headers[\"HTTP-Referer\"] = (\n os.getenv(\"OPENROUTER_SITE_URL\", \"\").strip()\n or os.getenv(\"APP_BASE_URL\", \"\").strip()\n or \"http://localhost:8000\"\n )\n default_headers[\"X-Title\"] = os.getenv(\"OPENROUTER_APP_NAME\", \"\").strip() or \"ss-tools\"\n if self.provider_type == LLMProviderType.KILO:\n default_headers[\"Authentication\"] = f\"Bearer {self.api_key}\"\n default_headers[\"X-API-Key\"] = self.api_key\n\n http_client = httpx.AsyncClient(headers=default_headers, timeout=120.0)\n self.client = AsyncOpenAI(\n api_key=self.api_key,\n base_url=base_url,\n default_headers=default_headers,\n http_client=http_client,\n )\n # endregion LLMClient.__init__\n\n # region LLMClient._supports_json_response_format [TYPE Function]\n # @PURPOSE: Detect whether provider/model is likely compatible with response_format=json_object.\n # @PRE: Client initialized with base_url and default_model.\n # @POST: Returns False for known-incompatible combinations to avoid avoidable 400 errors.\n def _supports_json_response_format(self) -> bool:\n base = (self.base_url or \"\").lower()\n model = (self.default_model or \"\").lower()\n\n # OpenRouter routes to many upstream providers; some models reject json_object mode.\n if \"openrouter.ai\" in base:\n incompatible_tokens = (\n \"stepfun/\",\n \"step-\",\n \":free\",\n )\n if any(token in model for token in incompatible_tokens):\n return False\n return True\n # endregion LLMClient._supports_json_response_format\n\n # region LLMClient.get_json_completion [TYPE Function]\n # @PURPOSE: Helper to handle LLM calls with JSON mode and fallback parsing.\n # @PRE: messages is a list of valid message dictionaries.\n # @POST: Returns a parsed JSON dictionary.\n # @SIDE_EFFECT: Calls external LLM API.\n def _should_retry(exception: Exception) -> bool:\n \"\"\"Custom retry predicate that excludes authentication errors.\"\"\"\n # Don't retry on authentication errors\n if isinstance(exception, OpenAIAuthenticationError):\n return False\n # Retry on rate limit errors and other exceptions\n return isinstance(exception, (RateLimitError, Exception))\n\n @retry(\n stop=stop_after_attempt(5),\n wait=wait_exponential(multiplier=2, min=5, max=60),\n retry=retry_if_exception(_should_retry),\n reraise=True\n )\n async def get_json_completion(self, messages: list[dict[str, Any]]) -> dict[str, Any]:\n with belief_scope(\"get_json_completion\"):\n response = None\n try:\n use_json_mode = self._supports_json_response_format()\n try:\n logger.info(\n f\"[get_json_completion] Attempting LLM call for model: {self.default_model} \"\n f\"(json_mode={'on' if use_json_mode else 'off'})\"\n )\n logger.info(f\"[get_json_completion] Base URL being used: {self.base_url}\")\n logger.info(f\"[get_json_completion] Number of messages: {len(messages)}\")\n logger.info(f\"[get_json_completion] API Key present: {bool(self.api_key and len(self.api_key) > 0)}\")\n\n if use_json_mode:\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages,\n response_format={\"type\": \"json_object\"}\n )\n else:\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages\n )\n except Exception as e:\n if use_json_mode and (\n \"JSON mode is not enabled\" in str(e)\n or \"json_object is not supported\" in str(e).lower()\n or \"response_format\" in str(e).lower()\n or \"400\" in str(e)\n ):\n logger.warning(f\"[get_json_completion] JSON mode failed or not supported: {e!s}. Falling back to plain text response.\")\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages\n )\n else:\n raise e\n\n logger.debug(f\"[get_json_completion] LLM Response: {response}\")\n except OpenAIAuthenticationError as e:\n logger.error(f\"[get_json_completion] Authentication error: {e!s}\")\n # Do not retry on auth errors - re-raise to stop retry\n raise\n except RateLimitError as e:\n logger.warning(f\"[get_json_completion] Rate limit hit: {e!s}\")\n\n # Extract retry_delay from error metadata if available\n retry_delay = 5.0 # Default fallback\n try:\n # Based on logs, the raw response is in e.body or e.response.json()\n # The logs show 'metadata': {'raw': '...'} which suggests a proxy or specific client wrapper\n # Let's try to find the 'retryDelay' in the error message or response\n import re\n\n # Try to find \"retryDelay\": \"XXs\" in the string representation of the error\n error_str = str(e)\n match = re.search(r'\"retryDelay\":\\s*\"(\\d+)s\"', error_str)\n if match:\n retry_delay = float(match.group(1))\n else:\n # Try to parse from response if it's a standard OpenAI-like error with body\n if hasattr(e, 'body') and isinstance(e.body, dict):\n # Some providers put it in details\n details = e.body.get('error', {}).get('details', [])\n for detail in details:\n if detail.get('@type') == 'type.googleapis.com/google.rpc.RetryInfo':\n delay_str = detail.get('retryDelay', '5s')\n retry_delay = float(delay_str.rstrip('s'))\n break\n except Exception as parse_e:\n logger.debug(f\"[get_json_completion] Failed to parse retry delay: {parse_e}\")\n\n # Add a small safety margin (0.5s) as requested\n wait_time = retry_delay + 0.5\n logger.info(f\"[get_json_completion] Waiting for {wait_time}s before retry...\")\n await asyncio.sleep(wait_time)\n raise\n except Exception as e:\n logger.error(f\"[get_json_completion] LLM call failed: {e!s}\")\n raise\n\n if not response or not hasattr(response, 'choices') or not response.choices:\n raise RuntimeError(f\"Invalid LLM response: {response}\")\n\n content = response.choices[0].message.content\n logger.debug(f\"[get_json_completion] Raw content to parse: {content}\")\n\n try:\n return json.loads(content)\n except json.JSONDecodeError:\n logger.warning(\"[get_json_completion] Failed to parse JSON directly, attempting to extract from code blocks\")\n if \"```json\" in content:\n json_str = content.split(\"```json\")[1].split(\"```\")[0].strip()\n return json.loads(json_str)\n elif \"```\" in content:\n json_str = content.split(\"```\")[1].split(\"```\")[0].strip()\n return json.loads(json_str)\n else:\n raise\n # endregion LLMClient.get_json_completion\n\n # region LLMClient.test_runtime_connection [TYPE Function]\n # @PURPOSE: Validate provider credentials using the same chat completions transport as runtime analysis.\n # @PRE: Client is initialized with provider credentials and default_model.\n # @POST: Returns lightweight JSON payload when runtime auth/model path is valid.\n # @SIDE_EFFECT: Calls external LLM API.\n async def test_runtime_connection(self) -> dict[str, Any]:\n with belief_scope(\"test_runtime_connection\"):\n messages = [\n {\n \"role\": \"user\",\n \"content\": 'Return exactly this JSON object and nothing else: {\"ok\": true}',\n }\n ]\n return await self.get_json_completion(messages)\n # endregion LLMClient.test_runtime_connection\n\n # region LLMClient.fetch_models [TYPE Function]\n # @PURPOSE: Fetch available models from the provider's API.\n # @PRE: Client is initialized with provider credentials.\n # @POST: Returns a list of model ID strings.\n # @SIDE_EFFECT: Calls external LLM API /v1/models endpoint.\n async def fetch_models(self) -> list[str]:\n with belief_scope(\"LLMClient.fetch_models\"):\n try:\n response = await self.client.models.list()\n model_ids = [m.id for m in response.data]\n model_ids.sort()\n logger.reason(\n f\"[LLMClient.fetch_models] Fetched {len(model_ids)} models from {self.base_url}\",\n extra={\"src\": \"LLMClient.fetch_models\"},\n )\n return model_ids\n except Exception as e:\n logger.warning(\n f\"[LLMClient.fetch_models] Failed to fetch models: {e}\",\n )\n raise\n # endregion LLMClient.fetch_models\n\n # region LLMClient.analyze_dashboard [TYPE Function]\n # @PURPOSE: Sends dashboard data (screenshot + logs) to LLM for health analysis.\n # @PRE: screenshot_path exists, logs is a list of strings.\n # @POST: Returns a structured analysis dictionary (status, summary, issues).\n # @SIDE_EFFECT: Reads screenshot file and calls external LLM API.\n async def analyze_dashboard(\n self,\n screenshot_path: str,\n logs: list[str],\n prompt_template: str = DEFAULT_LLM_PROMPTS[\"dashboard_validation_prompt\"],\n ) -> dict[str, Any]:\n with belief_scope(\"analyze_dashboard\"):\n # Optimize image to reduce token count (US1 / T023)\n # Gemini/Gemma models have limits on input tokens, and large images contribute significantly.\n try:\n with Image.open(screenshot_path) as img:\n # Convert to RGB if necessary\n if img.mode in (\"RGBA\", \"P\"):\n img = img.convert(\"RGB\")\n\n # Resize if too large (max 1024px width while maintaining aspect ratio)\n # We reduce width further to 1024px to stay within token limits for long dashboards\n max_width = 1024\n if img.width > max_width or img.height > 2048:\n # Calculate scaling factor to fit within 1024x2048\n scale = min(max_width / img.width, 2048 / img.height)\n if scale < 1.0:\n new_width = int(img.width * scale)\n new_height = int(img.height * scale)\n img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)\n logger.info(f\"[analyze_dashboard] Resized image from {img.width}x{img.height} to {new_width}x{new_height}\")\n\n # Compress and convert to base64\n buffer = io.BytesIO()\n # Lower quality to 60% to further reduce payload size\n img.save(buffer, format=\"JPEG\", quality=60, optimize=True)\n base_64_image = base64.b64encode(buffer.getvalue()).decode('utf-8')\n logger.info(f\"[analyze_dashboard] Optimized image size: {len(buffer.getvalue()) / 1024:.2f} KB\")\n except Exception as img_e:\n logger.warning(f\"[analyze_dashboard] Image optimization failed: {img_e}. Using raw image.\")\n with open(screenshot_path, \"rb\") as image_file:\n base_64_image = base64.b64encode(image_file.read()).decode('utf-8')\n\n log_text = \"\\n\".join(logs)\n prompt = render_prompt(prompt_template, {\"logs\": log_text})\n\n messages = [\n {\n \"role\": \"user\",\n \"content\": [\n {\"type\": \"text\", \"text\": prompt},\n {\n \"type\": \"image_url\",\n \"image_url\": {\n \"url\": f\"data:image/jpeg;base64,{base_64_image}\"\n }\n }\n ]\n }\n ]\n\n try:\n return await self.get_json_completion(messages)\n except Exception as e:\n logger.error(f\"[analyze_dashboard] Failed to get analysis: {e!s}\")\n return {\n \"status\": \"UNKNOWN\",\n \"summary\": f\"Failed to get response from LLM: {e!s}\",\n \"issues\": [{\"severity\": \"UNKNOWN\", \"message\": \"LLM provider returned empty or invalid response\"}]\n }\n # endregion LLMClient.analyze_dashboard\n# #endregion LLMClient\n\n# #endregion LLMAnalysisService\n" + "body": "# #region LLMAnalysisService [C:3] [TYPE Module] [SEMANTICS llm, screenshot, playwright, openai, tenacity]\n# @BRIEF Services for LLM interaction and dashboard screenshots.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> playwright\n# @RELATION DEPENDS_ON -> openai\n# @RELATION DEPENDS_ON -> tenacity\n# @INVARIANT: Screenshots must be 1920px width and capture full page height.\n\nimport asyncio\nimport base64\nimport io\nimport json\nimport os\nfrom typing import Any\nfrom urllib.parse import urlsplit\n\nimport httpx\nfrom openai import AsyncOpenAI, RateLimitError\nfrom openai import AuthenticationError as OpenAIAuthenticationError\nfrom PIL import Image\nfrom playwright.async_api import async_playwright\nfrom tenacity import retry, retry_if_exception, stop_after_attempt, wait_exponential\n\nfrom ...core.config_models import Environment\nfrom ...core.logger import belief_scope, logger\nfrom ...services.llm_prompt_templates import DEFAULT_LLM_PROMPTS, render_prompt\nfrom .models import LLMProviderType\n\n\n# #region ScreenshotService [TYPE Class]\n# @BRIEF Handles capturing screenshots of Superset dashboards.\nclass ScreenshotService:\n # region ScreenshotService.__init__ [TYPE Function]\n # @PURPOSE: Initializes the ScreenshotService with environment configuration.\n # @PRE: env is a valid Environment object.\n def __init__(self, env: Environment):\n self.env = env\n # endregion ScreenshotService.__init__\n\n # region ScreenshotService._find_first_visible_locator [TYPE Function]\n # @PURPOSE: Resolve the first visible locator from multiple Playwright locator strategies.\n # @PRE: candidates is a non-empty list of locator-like objects.\n # @POST: Returns a locator ready for interaction or None when nothing matches.\n async def _find_first_visible_locator(self, candidates) -> Any:\n for locator in candidates:\n try:\n match_count = await locator.count()\n for index in range(match_count):\n candidate = locator.nth(index)\n if await candidate.is_visible():\n return candidate\n except Exception:\n continue\n return None\n # endregion ScreenshotService._find_first_visible_locator\n\n # region ScreenshotService._iter_login_roots [TYPE Function]\n # @PURPOSE: Enumerate page and child frames where login controls may be rendered.\n # @PRE: page is a Playwright page-like object.\n # @POST: Returns ordered roots starting with main page followed by frames.\n def _iter_login_roots(self, page) -> list[Any]:\n roots = [page]\n page_frames = getattr(page, \"frames\", [])\n try:\n for frame in page_frames:\n if frame not in roots:\n roots.append(frame)\n except Exception:\n pass\n return roots\n # endregion ScreenshotService._iter_login_roots\n\n # region ScreenshotService._extract_hidden_login_fields [TYPE Function]\n # @PURPOSE: Collect hidden form fields required for direct login POST fallback.\n # @PRE: Login page is loaded.\n # @POST: Returns hidden input name/value mapping aggregated from page and child frames.\n async def _extract_hidden_login_fields(self, page) -> dict[str, str]:\n hidden_fields: dict[str, str] = {}\n for root in self._iter_login_roots(page):\n try:\n locator = root.locator(\"input[type='hidden'][name]\")\n count = await locator.count()\n for index in range(count):\n candidate = locator.nth(index)\n field_name = str(await candidate.get_attribute(\"name\") or \"\").strip()\n if not field_name or field_name in hidden_fields:\n continue\n hidden_fields[field_name] = str(await candidate.input_value()).strip()\n except Exception:\n continue\n return hidden_fields\n # endregion ScreenshotService._extract_hidden_login_fields\n\n # region ScreenshotService._extract_csrf_token [TYPE Function]\n # @PURPOSE: Resolve CSRF token value from main page or embedded login frame.\n # @PRE: Login page is loaded.\n # @POST: Returns first non-empty csrf token or empty string.\n async def _extract_csrf_token(self, page) -> str:\n hidden_fields = await self._extract_hidden_login_fields(page)\n return str(hidden_fields.get(\"csrf_token\") or \"\").strip()\n # endregion ScreenshotService._extract_csrf_token\n\n # region ScreenshotService._response_looks_like_login_page [TYPE Function]\n # @PURPOSE: Detect when fallback login POST returned the login form again instead of an authenticated page.\n # @PRE: response_text is normalized HTML or text from login POST response.\n # @POST: Returns True when login-page markers dominate the response body.\n def _response_looks_like_login_page(self, response_text: str) -> bool:\n normalized = str(response_text or \"\").strip().lower()\n if not normalized:\n return False\n\n markers = [\n \"enter your login and password below\",\n \"username:\",\n \"password:\",\n \"sign in\",\n 'name=\"csrf_token\"',\n ]\n return sum(marker in normalized for marker in markers) >= 3\n # endregion ScreenshotService._response_looks_like_login_page\n\n # region ScreenshotService._redirect_looks_authenticated [TYPE Function]\n # @PURPOSE: Treat non-login redirects after form POST as successful authentication without waiting for redirect target.\n # @PRE: redirect_location may be empty or relative.\n # @POST: Returns True when redirect target does not point back to login flow.\n def _redirect_looks_authenticated(self, redirect_location: str) -> bool:\n normalized = str(redirect_location or \"\").strip().lower()\n if not normalized:\n return True\n return \"/login\" not in normalized\n # endregion ScreenshotService._redirect_looks_authenticated\n\n # region ScreenshotService._submit_login_via_form_post [TYPE Function]\n # @PURPOSE: Fallback login path that submits credentials directly with csrf token.\n # @PRE: login_url is same-origin and csrf token can be read from DOM.\n # @POST: Browser context receives authenticated cookies when login succeeds.\n async def _submit_login_via_form_post(self, page, login_url: str) -> bool:\n hidden_fields = await self._extract_hidden_login_fields(page)\n csrf_token = str(hidden_fields.get(\"csrf_token\") or \"\").strip()\n if not csrf_token:\n logger.warning(\"[DEBUG] Direct form login fallback skipped: csrf_token not found\")\n return False\n\n try:\n request_context = page.context.request\n except Exception as context_error:\n logger.warning(f\"[DEBUG] Direct form login fallback skipped: request context unavailable: {context_error}\")\n return False\n\n parsed_url = urlsplit(login_url)\n origin = f\"{parsed_url.scheme}://{parsed_url.netloc}\" if parsed_url.scheme and parsed_url.netloc else login_url\n payload = dict(hidden_fields)\n payload[\"username\"] = self.env.username\n payload[\"password\"] = self.env.password\n logger.info(\n f\"[DEBUG] Attempting direct form login fallback via browser context request with hidden fields: {sorted(hidden_fields.keys())}\"\n )\n\n response = await request_context.post(\n login_url,\n form=payload,\n headers={\n \"Origin\": origin,\n \"Referer\": login_url,\n },\n timeout=10000,\n fail_on_status_code=False,\n max_redirects=0,\n )\n response_url = str(getattr(response, \"url\", \"\") or \"\")\n response_status = int(getattr(response, \"status\", 0) or 0)\n response_headers = dict(getattr(response, \"headers\", {}) or {})\n redirect_location = str(\n response_headers.get(\"location\")\n or response_headers.get(\"Location\")\n or \"\"\n ).strip()\n redirect_statuses = {301, 302, 303, 307, 308}\n if response_status in redirect_statuses:\n redirect_authenticated = self._redirect_looks_authenticated(redirect_location)\n logger.info(\n f\"[DEBUG] Direct form login fallback redirect response: status={response_status} url={response_url} location={redirect_location!r} authenticated={redirect_authenticated}\"\n )\n return redirect_authenticated\n\n response_text = await response.text()\n text_snippet = \" \".join(response_text.split())[:200]\n looks_like_login_page = self._response_looks_like_login_page(response_text)\n logger.info(\n f\"[DEBUG] Direct form login fallback response: status={response_status} url={response_url} login_markup={looks_like_login_page} snippet={text_snippet!r}\"\n )\n return not looks_like_login_page\n # endregion ScreenshotService._submit_login_via_form_post\n\n # region ScreenshotService._find_login_field_locator [TYPE Function]\n # @PURPOSE: Resolve login form input using semantic label text plus generic visible-input fallbacks.\n # @PRE: field_name is `username` or `password`.\n # @POST: Returns a locator for the corresponding input or None.\n async def _find_login_field_locator(self, page, field_name: str) -> Any:\n normalized = str(field_name or \"\").strip().lower()\n for root in self._iter_login_roots(page):\n if normalized == \"username\":\n input_candidates = [\n root.get_by_label(\"Username\", exact=False),\n root.get_by_label(\"Login\", exact=False),\n root.locator(\"label:text-matches('Username|Login', 'i')\").locator(\"xpath=following::input[1]\"),\n root.locator(\"text=/Username|Login/i\").locator(\"xpath=following::input[1]\"),\n root.locator(\"input[name='username']\"),\n root.locator(\"input#username\"),\n root.locator(\"input[placeholder*='Username']\"),\n root.locator(\"input[type='text']\"),\n root.locator(\"input:not([type='password'])\"),\n ]\n locator = await self._find_first_visible_locator(input_candidates)\n if locator:\n return locator\n\n if normalized == \"password\":\n input_candidates = [\n root.get_by_label(\"Password\", exact=False),\n root.locator(\"label:text-matches('Password', 'i')\").locator(\"xpath=following::input[1]\"),\n root.locator(\"text=/Password/i\").locator(\"xpath=following::input[1]\"),\n root.locator(\"input[name='password']\"),\n root.locator(\"input#password\"),\n root.locator(\"input[placeholder*='Password']\"),\n root.locator(\"input[type='password']\"),\n ]\n locator = await self._find_first_visible_locator(input_candidates)\n if locator:\n return locator\n\n return None\n # endregion ScreenshotService._find_login_field_locator\n\n # region ScreenshotService._find_submit_locator [TYPE Function]\n # @PURPOSE: Resolve login submit button from main page or embedded auth frame.\n # @PRE: page is ready for login interaction.\n # @POST: Returns visible submit locator or None.\n async def _find_submit_locator(self, page) -> Any:\n selectors = [\n lambda root: root.get_by_role(\"button\", name=\"Sign in\", exact=False),\n lambda root: root.get_by_role(\"button\", name=\"Login\", exact=False),\n lambda root: root.locator(\"button[type='submit']\"),\n lambda root: root.locator(\"button#submit\"),\n lambda root: root.locator(\".btn-primary\"),\n lambda root: root.locator(\"input[type='submit']\"),\n ]\n for root in self._iter_login_roots(page):\n locator = await self._find_first_visible_locator([factory(root) for factory in selectors])\n if locator:\n return locator\n return None\n # endregion ScreenshotService._find_submit_locator\n\n # region ScreenshotService._goto_resilient [TYPE Function]\n # @PURPOSE: Navigate without relying on networkidle for pages with long-polling or persistent requests.\n # @PRE: page is a valid Playwright page and url is non-empty.\n # @POST: Returns last navigation response or raises when both primary and fallback waits fail.\n async def _goto_resilient(\n self,\n page,\n url: str,\n primary_wait_until: str = \"domcontentloaded\",\n fallback_wait_until: str = \"load\",\n timeout: int = 60000,\n ):\n try:\n return await page.goto(url, wait_until=primary_wait_until, timeout=timeout)\n except Exception as primary_error:\n logger.warning(\n f\"[ScreenshotService._goto_resilient] Primary navigation wait '{primary_wait_until}' failed for {url}: {primary_error}\"\n )\n return await page.goto(url, wait_until=fallback_wait_until, timeout=timeout)\n # endregion ScreenshotService._goto_resilient\n\n # region ScreenshotService.capture_dashboard [TYPE Function]\n # @PURPOSE: Captures a full-page screenshot of a dashboard using Playwright and CDP.\n # @PRE: dashboard_id is a valid string, output_path is a writable path.\n # @POST: Returns True if screenshot is saved successfully.\n # @SIDE_EFFECT: Launches a browser, performs UI login, switches tabs, and writes a PNG file.\n # @UX_STATE: [Navigating] -> Loading dashboard UI\n # @UX_STATE: [TabSwitching] -> Iterating through dashboard tabs to trigger lazy loading\n # @UX_STATE: [CalculatingHeight] -> Determining dashboard dimensions\n # @UX_STATE: [Capturing] -> Executing CDP screenshot\n async def capture_dashboard(self, dashboard_id: str, output_path: str) -> bool:\n with belief_scope(\"capture_dashboard\", f\"dashboard_id={dashboard_id}\"):\n logger.info(f\"Capturing screenshot for dashboard {dashboard_id}\")\n async with async_playwright() as p:\n browser = await p.chromium.launch(\n headless=True,\n args=[\n \"--disable-blink-features=AutomationControlled\",\n \"--disable-infobars\",\n \"--no-sandbox\"\n ]\n )\n # Set a realistic user agent to avoid 403 Forbidden from OpenResty/WAF\n user_agent = \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\"\n # Construct base UI URL from environment (strip /api/v1 suffix)\n base_ui_url = self.env.url.rstrip(\"/\")\n if base_ui_url.endswith(\"/api/v1\"):\n base_ui_url = base_ui_url[:-len(\"/api/v1\")]\n\n # Create browser context with realistic headers\n context = await browser.new_context(\n viewport={'width': 1280, 'height': 720},\n user_agent=user_agent,\n extra_http_headers={\n \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\",\n \"Accept-Language\": \"ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7\",\n \"Upgrade-Insecure-Requests\": \"1\",\n \"Sec-Fetch-Dest\": \"document\",\n \"Sec-Fetch-Mode\": \"navigate\",\n \"Sec-Fetch-Site\": \"none\",\n \"Sec-Fetch-User\": \"?1\"\n }\n )\n logger.info(\"Browser context created successfully\")\n\n page = await context.new_page()\n # Bypass navigator.webdriver detection\n await page.add_init_script(\"delete Object.getPrototypeOf(navigator).webdriver\")\n\n # 1. Navigate to login page and authenticate\n login_url = f\"{base_ui_url.rstrip('/')}/login/\"\n logger.info(f\"[DEBUG] Navigating to login page: {login_url}\")\n\n response = await self._goto_resilient(\n page,\n login_url,\n primary_wait_until=\"domcontentloaded\",\n fallback_wait_until=\"load\",\n timeout=60000,\n )\n if response:\n logger.info(f\"[DEBUG] Login page response status: {response.status}\")\n\n # Wait for login form to be ready\n await page.wait_for_load_state(\"domcontentloaded\")\n\n # More exhaustive list of selectors for various Superset versions/themes\n selectors = {\n \"username\": ['input[name=\"username\"]', 'input#username', 'input[placeholder*=\"Username\"]', 'input[type=\"text\"]'],\n \"password\": ['input[name=\"password\"]', 'input#password', 'input[placeholder*=\"Password\"]', 'input[type=\"password\"]'],\n \"submit\": ['button[type=\"submit\"]', 'button#submit', '.btn-primary', 'input[type=\"submit\"]']\n }\n logger.info(\"[DEBUG] Attempting to find login form elements...\")\n\n try:\n used_direct_form_login = False\n # Find and fill username\n username_locator = await self._find_login_field_locator(page, \"username\")\n\n if not username_locator:\n roots = self._iter_login_roots(page)\n logger.info(f\"[DEBUG] Found {len(roots)} login roots including child frames\")\n for root_index, root in enumerate(roots[:5]):\n all_inputs = await root.locator('input').all()\n logger.info(f\"[DEBUG] Root {root_index}: found {len(all_inputs)} input fields\")\n for i, inp in enumerate(all_inputs[:5]): # Log first 5\n inp_type = await inp.get_attribute('type')\n inp_name = await inp.get_attribute('name')\n inp_id = await inp.get_attribute('id')\n logger.info(f\"[DEBUG] Root {root_index} input {i}: type={inp_type}, name={inp_name}, id={inp_id}\")\n used_direct_form_login = await self._submit_login_via_form_post(page, login_url)\n if not used_direct_form_login:\n raise RuntimeError(\"Could not find username input field on login page\")\n username_locator = None\n\n if username_locator is not None:\n logger.info(\"[DEBUG] Filling username field\")\n await username_locator.fill(self.env.username)\n\n # Find and fill password\n password_locator = await self._find_login_field_locator(page, \"password\") if username_locator is not None else None\n\n if username_locator is not None and not password_locator:\n raise RuntimeError(\"Could not find password input field on login page\")\n\n if password_locator is not None:\n logger.info(\"[DEBUG] Filling password field\")\n await password_locator.fill(self.env.password)\n\n # Click submit\n submit_locator = await self._find_submit_locator(page) if username_locator is not None else None\n\n if username_locator is not None and not submit_locator:\n raise RuntimeError(\"Could not find submit button on login page\")\n\n if submit_locator is not None:\n logger.info(\"[DEBUG] Clicking submit button\")\n await submit_locator.click()\n\n # Wait for navigation after login\n if not used_direct_form_login:\n try:\n await page.wait_for_load_state(\"load\", timeout=30000)\n except Exception as load_wait_error:\n logger.warning(f\"[DEBUG] Login post-submit load wait timed out: {load_wait_error}\")\n\n # Check if login was successful\n if not used_direct_form_login and \"/login\" in page.url:\n # Check for error messages on page\n error_msg = await page.locator(\".alert-danger, .error-message\").text_content() if await page.locator(\".alert-danger, .error-message\").count() > 0 else \"Unknown error\"\n logger.error(f\"[DEBUG] Login failed. Still on login page. Error: {error_msg}\")\n debug_path = output_path.replace(\".png\", \"_debug_failed_login.png\")\n await page.screenshot(path=debug_path)\n raise RuntimeError(f\"Login failed: {error_msg}. Debug screenshot saved to {debug_path}\")\n\n logger.info(f\"[DEBUG] Login successful. Current URL: {page.url}\")\n\n # Check cookies after successful login\n page_cookies = await context.cookies()\n logger.info(f\"[DEBUG] Cookies after login: {len(page_cookies)}\")\n for c in page_cookies:\n logger.info(f\"[DEBUG] Cookie: name={c['name']}, domain={c['domain']}, value={c.get('value', '')[:20]}...\")\n\n except Exception as e:\n page_title = await page.title()\n logger.error(f\"UI Login failed. Page title: {page_title}, URL: {page.url}, Error: {e!s}\")\n debug_path = output_path.replace(\".png\", \"_debug_failed_login.png\")\n await page.screenshot(path=debug_path)\n raise RuntimeError(f\"Login failed: {e!s}. Debug screenshot saved to {debug_path}\")\n\n # 2. Navigate to dashboard\n # @UX_STATE: [Navigating] -> Loading dashboard UI\n dashboard_url = f\"{base_ui_url.rstrip('/')}/superset/dashboard/{dashboard_id}/?standalone=true\"\n\n if base_ui_url.startswith(\"https://\") and dashboard_url.startswith(\"http://\"):\n dashboard_url = dashboard_url.replace(\"http://\", \"https://\")\n\n logger.info(f\"[DEBUG] Navigating to dashboard: {dashboard_url}\")\n\n # Dashboard pages can keep polling/network activity open indefinitely.\n response = await self._goto_resilient(\n page,\n dashboard_url,\n primary_wait_until=\"domcontentloaded\",\n fallback_wait_until=\"load\",\n timeout=60000,\n )\n\n if response:\n logger.info(f\"[DEBUG] Dashboard navigation response status: {response.status}, URL: {response.url}\")\n\n if \"/login\" in page.url:\n debug_path = output_path.replace(\".png\", \"_debug_failed_dashboard_auth.png\")\n await page.screenshot(path=debug_path)\n raise RuntimeError(\n f\"Dashboard navigation redirected to login page after authentication. Debug screenshot saved to {debug_path}\"\n )\n\n try:\n # Wait for the dashboard grid to be present\n await page.wait_for_selector('.dashboard-component, .dashboard-header, [data-test=\"dashboard-grid\"]', timeout=30000)\n logger.info(\"[DEBUG] Dashboard container loaded\")\n\n # Wait for charts to finish loading (Superset uses loading spinners/skeletons)\n # We wait until loading indicators disappear or a timeout occurs\n try:\n # Wait for loading indicators to disappear\n await page.wait_for_selector('.loading, .ant-skeleton, .spinner', state=\"hidden\", timeout=60000)\n logger.info(\"[DEBUG] Loading indicators hidden\")\n except Exception:\n logger.warning(\"[DEBUG] Timeout waiting for loading indicators to hide\")\n\n # Wait for charts to actually render their content (e.g., ECharts, NVD3)\n # We look for common chart containers that should have content\n try:\n await page.wait_for_selector('.chart-container canvas, .slice_container svg, .superset-chart-canvas, .grid-content .chart-container', timeout=60000)\n logger.info(\"[DEBUG] Chart content detected\")\n except Exception:\n logger.warning(\"[DEBUG] Timeout waiting for chart content\")\n\n # Additional check: wait for all chart containers to have non-empty content\n logger.info(\"[DEBUG] Waiting for all charts to have rendered content...\")\n await page.wait_for_function(\"\"\"() => {\n const charts = document.querySelectorAll('.chart-container, .slice_container');\n if (charts.length === 0) return true; // No charts to wait for\n \n // Check if all charts have rendered content (canvas, svg, or non-empty div)\n return Array.from(charts).every(chart => {\n const hasCanvas = chart.querySelector('canvas') !== null;\n const hasSvg = chart.querySelector('svg') !== null;\n const hasContent = chart.innerText.trim().length > 0 || chart.children.length > 0;\n return hasCanvas || hasSvg || hasContent;\n });\n }\"\"\", timeout=60000)\n logger.info(\"[DEBUG] All charts have rendered content\")\n\n # Scroll to bottom and back to top to trigger lazy loading of all charts\n logger.info(\"[DEBUG] Scrolling to trigger lazy loading...\")\n await page.evaluate(\"\"\"async () => {\n const delay = ms => new Promise(resolve => setTimeout(resolve, ms));\n for (let i = 0; i < document.body.scrollHeight; i += 500) {\n window.scrollTo(0, i);\n await delay(100);\n }\n window.scrollTo(0, 0);\n await delay(500);\n }\"\"\")\n\n except Exception as e:\n logger.warning(f\"[DEBUG] Dashboard content wait failed: {e}, proceeding anyway after delay\")\n\n # Final stabilization delay - increased for complex dashboards\n logger.info(\"[DEBUG] Final stabilization delay...\")\n await asyncio.sleep(15)\n\n # Logic to handle tabs and full-page capture\n try:\n # 1. Handle Tabs (Recursive switching)\n # @UX_STATE: [TabSwitching] -> Iterating through dashboard tabs to trigger lazy loading\n processed_tabs = set()\n\n async def switch_tabs(depth=0):\n if depth > 3:\n return # Limit recursion depth\n\n tab_selectors = [\n '.ant-tabs-nav-list .ant-tabs-tab',\n '.dashboard-component-tabs .ant-tabs-tab',\n '[data-test=\"dashboard-component-tabs\"] .ant-tabs-tab'\n ]\n\n found_tabs = []\n for selector in tab_selectors:\n found_tabs = await page.locator(selector).all()\n if found_tabs:\n break\n\n if found_tabs:\n logger.info(f\"[DEBUG][TabSwitching] Found {len(found_tabs)} tabs at depth {depth}\")\n for i, tab in enumerate(found_tabs):\n try:\n tab_text = (await tab.inner_text()).strip()\n tab_id = f\"{depth}_{i}_{tab_text}\"\n\n if tab_id in processed_tabs:\n continue\n\n if await tab.is_visible():\n logger.info(f\"[DEBUG][TabSwitching] Switching to tab: {tab_text}\")\n processed_tabs.add(tab_id)\n\n is_active = \"ant-tabs-tab-active\" in (await tab.get_attribute(\"class\") or \"\")\n if not is_active:\n await tab.click()\n await asyncio.sleep(2) # Wait for content to render\n\n await switch_tabs(depth + 1)\n except Exception as tab_e:\n logger.warning(f\"[DEBUG][TabSwitching] Failed to process tab {i}: {tab_e}\")\n\n try:\n first_tab = found_tabs[0]\n if \"ant-tabs-tab-active\" not in (await first_tab.get_attribute(\"class\") or \"\"):\n await first_tab.click()\n await asyncio.sleep(1)\n except Exception:\n pass\n\n await switch_tabs()\n\n # 2. Calculate full height for screenshot\n # @UX_STATE: [CalculatingHeight] -> Determining dashboard dimensions\n full_height = await page.evaluate(\"\"\"() => {\n const body = document.body;\n const html = document.documentElement;\n const dashboardContent = document.querySelector('.dashboard-content');\n \n return Math.max(\n body.scrollHeight, body.offsetHeight,\n html.clientHeight, html.scrollHeight, html.offsetHeight,\n dashboardContent ? dashboardContent.scrollHeight + 100 : 0\n );\n }\"\"\")\n logger.info(f\"[DEBUG] Calculated full height: {full_height}\")\n\n # DIAGNOSTIC: Count chart elements before resize\n chart_count_before = await page.evaluate(\"\"\"() => {\n return {\n chartContainers: document.querySelectorAll('.chart-container, .slice_container').length,\n canvasElements: document.querySelectorAll('canvas').length,\n svgElements: document.querySelectorAll('.chart-container svg, .slice_container svg').length,\n visibleCharts: document.querySelectorAll('.chart-container:visible, .slice_container:visible').length\n };\n }\"\"\")\n logger.info(f\"[DIAGNOSTIC] Chart elements BEFORE viewport resize: {chart_count_before}\")\n\n # DIAGNOSTIC: Capture pre-resize screenshot for comparison\n pre_resize_path = output_path.replace(\".png\", \"_preresize.png\")\n try:\n await page.screenshot(path=pre_resize_path, full_page=False, timeout=10000)\n import os\n pre_resize_size = os.path.getsize(pre_resize_path) if os.path.exists(pre_resize_path) else 0\n logger.info(f\"[DIAGNOSTIC] Pre-resize screenshot saved: {pre_resize_path} ({pre_resize_size} bytes)\")\n except Exception as pre_e:\n logger.warning(f\"[DIAGNOSTIC] Failed to capture pre-resize screenshot: {pre_e}\")\n\n logger.info(f\"[DIAGNOSTIC] Resizing viewport from current to 1920x{int(full_height)}\")\n await page.set_viewport_size({\"width\": 1920, \"height\": int(full_height)})\n\n # DIAGNOSTIC: Increased wait time and log timing\n logger.info(\"[DIAGNOSTIC] Waiting 10 seconds after viewport resize for re-render...\")\n await asyncio.sleep(10)\n logger.info(\"[DIAGNOSTIC] Wait completed\")\n\n # DIAGNOSTIC: Count chart elements after resize and wait\n chart_count_after = await page.evaluate(\"\"\"() => {\n return {\n chartContainers: document.querySelectorAll('.chart-container, .slice_container').length,\n canvasElements: document.querySelectorAll('canvas').length,\n svgElements: document.querySelectorAll('.chart-container svg, .slice_container svg').length,\n visibleCharts: document.querySelectorAll('.chart-container:visible, .slice_container:visible').length\n };\n }\"\"\")\n logger.info(f\"[DIAGNOSTIC] Chart elements AFTER viewport resize + wait: {chart_count_after}\")\n\n # DIAGNOSTIC: Check if any charts have error states\n chart_errors = await page.evaluate(\"\"\"() => {\n const errors = [];\n document.querySelectorAll('.chart-container, .slice_container').forEach((chart, i) => {\n const errorEl = chart.querySelector('.error, .alert-danger, .ant-alert-error');\n if (errorEl) {\n errors.push({index: i, text: errorEl.innerText.substring(0, 100)});\n }\n });\n return errors;\n }\"\"\")\n if chart_errors:\n logger.warning(f\"[DIAGNOSTIC] Charts with error states detected: {chart_errors}\")\n else:\n logger.info(\"[DIAGNOSTIC] No chart error states detected\")\n\n # 3. Take screenshot using CDP to bypass Playwright's font loading wait\n # @UX_STATE: [Capturing] -> Executing CDP screenshot\n logger.info(\"[DEBUG] Attempting full-page screenshot via CDP...\")\n cdp = await page.context.new_cdp_session(page)\n\n screenshot_data = await cdp.send(\"Page.captureScreenshot\", {\n \"format\": \"png\",\n \"fromSurface\": True,\n \"captureBeyondViewport\": True\n })\n\n image_data = base64.b64decode(screenshot_data[\"data\"])\n\n with open(output_path, 'wb') as f:\n f.write(image_data)\n\n # DIAGNOSTIC: Verify screenshot file\n import os\n final_size = os.path.getsize(output_path) if os.path.exists(output_path) else 0\n logger.info(f\"[DIAGNOSTIC] Final screenshot saved: {output_path}\")\n logger.info(f\"[DIAGNOSTIC] Final screenshot size: {final_size} bytes ({final_size / 1024:.2f} KB)\")\n\n # DIAGNOSTIC: Get image dimensions\n try:\n with Image.open(output_path) as final_img:\n logger.info(f\"[DIAGNOSTIC] Final screenshot dimensions: {final_img.width}x{final_img.height}\")\n except Exception as img_err:\n logger.warning(f\"[DIAGNOSTIC] Could not read final image dimensions: {img_err}\")\n\n logger.info(f\"Full-page screenshot saved to {output_path} (via CDP)\")\n except Exception as e:\n logger.error(f\"[DEBUG] Full-page/Tab capture failed: {e}\")\n try:\n await page.screenshot(path=output_path, full_page=True, timeout=10000)\n except Exception as e2:\n logger.error(f\"[DEBUG] Fallback screenshot also failed: {e2}\")\n await page.screenshot(path=output_path, timeout=5000)\n\n await browser.close()\n return True\n # endregion ScreenshotService.capture_dashboard\n# #endregion ScreenshotService\n\n# #region LLMClient [TYPE Class]\n# @BRIEF Wrapper for LLM provider APIs.\nclass LLMClient:\n # region LLMClient.__init__ [TYPE Function]\n # @PURPOSE: Initializes the LLMClient with provider settings.\n # @PRE: api_key, base_url, and default_model are non-empty strings.\n def __init__(self, provider_type: LLMProviderType, api_key: str, base_url: str, default_model: str):\n self.provider_type = provider_type\n normalized_key = (api_key or \"\").strip()\n if normalized_key.lower().startswith(\"bearer \"):\n normalized_key = normalized_key[7:].strip()\n self.api_key = normalized_key\n self.base_url = base_url\n self.default_model = default_model\n\n # DEBUG: Log initialization parameters (without exposing full API key)\n logger.info(\"[LLMClient.__init__] Initializing LLM client:\")\n logger.info(f\"[LLMClient.__init__] Provider Type: {provider_type}\")\n logger.info(f\"[LLMClient.__init__] Base URL: {base_url}\")\n logger.info(f\"[LLMClient.__init__] Default Model: {default_model}\")\n logger.info(f\"[LLMClient.__init__] API Key (first 8 chars): {self.api_key[:8] if self.api_key and len(self.api_key) > 8 else 'EMPTY_OR_NONE'}...\")\n logger.info(f\"[LLMClient.__init__] API Key Length: {len(self.api_key) if self.api_key else 0}\")\n\n # Some OpenAI-compatible gateways are strict about auth header naming.\n default_headers = {\"Authorization\": f\"Bearer {self.api_key}\"}\n if self.provider_type == LLMProviderType.OPENROUTER:\n default_headers[\"HTTP-Referer\"] = (\n os.getenv(\"OPENROUTER_SITE_URL\", \"\").strip()\n or os.getenv(\"APP_BASE_URL\", \"\").strip()\n or \"http://localhost:8000\"\n )\n default_headers[\"X-Title\"] = os.getenv(\"OPENROUTER_APP_NAME\", \"\").strip() or \"ss-tools\"\n if self.provider_type == LLMProviderType.KILO:\n default_headers[\"Authentication\"] = f\"Bearer {self.api_key}\"\n default_headers[\"X-API-Key\"] = self.api_key\n # LiteLLM proxy uses standard OpenAI-compatible Bearer auth — no special headers needed.\n # It routes to upstream providers transparently, and the default Authorization header\n # is sufficient. No additional headers like HTTP-Referer or X-API-Key are required.\n\n http_client = httpx.AsyncClient(headers=default_headers, timeout=120.0)\n self.client = AsyncOpenAI(\n api_key=self.api_key,\n base_url=base_url,\n default_headers=default_headers,\n http_client=http_client,\n )\n # endregion LLMClient.__init__\n\n # region LLMClient._supports_json_response_format [TYPE Function]\n # @PURPOSE: Detect whether provider/model is likely compatible with response_format=json_object.\n # @PRE: Client initialized with base_url and default_model.\n # @POST: Returns False for known-incompatible combinations to avoid avoidable 400 errors.\n def _supports_json_response_format(self) -> bool:\n base = (self.base_url or \"\").lower()\n model = (self.default_model or \"\").lower()\n\n # OpenRouter routes to many upstream providers; some models reject json_object mode.\n if \"openrouter.ai\" in base:\n incompatible_tokens = (\n \"stepfun/\",\n \"step-\",\n \":free\",\n )\n if any(token in model for token in incompatible_tokens):\n return False\n return True\n # endregion LLMClient._supports_json_response_format\n\n # region LLMClient.get_json_completion [TYPE Function]\n # @PURPOSE: Helper to handle LLM calls with JSON mode and fallback parsing.\n # @PRE: messages is a list of valid message dictionaries.\n # @POST: Returns a parsed JSON dictionary.\n # @SIDE_EFFECT: Calls external LLM API.\n def _should_retry(exception: Exception) -> bool:\n \"\"\"Custom retry predicate that excludes authentication errors.\"\"\"\n # Don't retry on authentication errors\n if isinstance(exception, OpenAIAuthenticationError):\n return False\n # Retry on rate limit errors and other exceptions\n return isinstance(exception, (RateLimitError, Exception))\n\n @retry(\n stop=stop_after_attempt(5),\n wait=wait_exponential(multiplier=2, min=5, max=60),\n retry=retry_if_exception(_should_retry),\n reraise=True\n )\n async def get_json_completion(self, messages: list[dict[str, Any]]) -> dict[str, Any]:\n with belief_scope(\"get_json_completion\"):\n response = None\n try:\n use_json_mode = self._supports_json_response_format()\n try:\n logger.info(\n f\"[get_json_completion] Attempting LLM call for model: {self.default_model} \"\n f\"(json_mode={'on' if use_json_mode else 'off'})\"\n )\n logger.info(f\"[get_json_completion] Base URL being used: {self.base_url}\")\n logger.info(f\"[get_json_completion] Number of messages: {len(messages)}\")\n logger.info(f\"[get_json_completion] API Key present: {bool(self.api_key and len(self.api_key) > 0)}\")\n\n if use_json_mode:\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages,\n response_format={\"type\": \"json_object\"}\n )\n else:\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages\n )\n except Exception as e:\n if use_json_mode and (\n \"JSON mode is not enabled\" in str(e)\n or \"json_object is not supported\" in str(e).lower()\n or \"response_format\" in str(e).lower()\n or \"400\" in str(e)\n ):\n logger.warning(f\"[get_json_completion] JSON mode failed or not supported: {e!s}. Falling back to plain text response.\")\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages\n )\n else:\n raise e\n\n logger.debug(f\"[get_json_completion] LLM Response: {response}\")\n except OpenAIAuthenticationError as e:\n logger.error(f\"[get_json_completion] Authentication error: {e!s}\")\n # Do not retry on auth errors - re-raise to stop retry\n raise\n except RateLimitError as e:\n logger.warning(f\"[get_json_completion] Rate limit hit: {e!s}\")\n\n # Extract retry_delay from error metadata if available\n retry_delay = 5.0 # Default fallback\n try:\n # Based on logs, the raw response is in e.body or e.response.json()\n # The logs show 'metadata': {'raw': '...'} which suggests a proxy or specific client wrapper\n # Let's try to find the 'retryDelay' in the error message or response\n import re\n\n # Try to find \"retryDelay\": \"XXs\" in the string representation of the error\n error_str = str(e)\n match = re.search(r'\"retryDelay\":\\s*\"(\\d+)s\"', error_str)\n if match:\n retry_delay = float(match.group(1))\n else:\n # Try to parse from response if it's a standard OpenAI-like error with body\n if hasattr(e, 'body') and isinstance(e.body, dict):\n # Some providers put it in details\n details = e.body.get('error', {}).get('details', [])\n for detail in details:\n if detail.get('@type') == 'type.googleapis.com/google.rpc.RetryInfo':\n delay_str = detail.get('retryDelay', '5s')\n retry_delay = float(delay_str.rstrip('s'))\n break\n except Exception as parse_e:\n logger.debug(f\"[get_json_completion] Failed to parse retry delay: {parse_e}\")\n\n # Add a small safety margin (0.5s) as requested\n wait_time = retry_delay + 0.5\n logger.info(f\"[get_json_completion] Waiting for {wait_time}s before retry...\")\n await asyncio.sleep(wait_time)\n raise\n except Exception as e:\n logger.error(f\"[get_json_completion] LLM call failed: {e!s}\")\n raise\n\n if not response or not hasattr(response, 'choices') or not response.choices:\n raise RuntimeError(f\"Invalid LLM response: {response}\")\n\n content = response.choices[0].message.content\n logger.debug(f\"[get_json_completion] Raw content to parse: {content}\")\n\n try:\n return json.loads(content)\n except json.JSONDecodeError:\n logger.warning(\"[get_json_completion] Failed to parse JSON directly, attempting to extract from code blocks\")\n if \"```json\" in content:\n json_str = content.split(\"```json\")[1].split(\"```\")[0].strip()\n return json.loads(json_str)\n elif \"```\" in content:\n json_str = content.split(\"```\")[1].split(\"```\")[0].strip()\n return json.loads(json_str)\n else:\n raise\n # endregion LLMClient.get_json_completion\n\n # region LLMClient.test_runtime_connection [TYPE Function]\n # @PURPOSE: Validate provider credentials using the same chat completions transport as runtime analysis.\n # @PRE: Client is initialized with provider credentials and default_model.\n # @POST: Returns lightweight JSON payload when runtime auth/model path is valid.\n # @SIDE_EFFECT: Calls external LLM API.\n async def test_runtime_connection(self) -> dict[str, Any]:\n with belief_scope(\"test_runtime_connection\"):\n messages = [\n {\n \"role\": \"user\",\n \"content\": 'Return exactly this JSON object and nothing else: {\"ok\": true}',\n }\n ]\n return await self.get_json_completion(messages)\n # endregion LLMClient.test_runtime_connection\n\n # region LLMClient.fetch_models [TYPE Function]\n # @PURPOSE: Fetch available models from the provider's API.\n # @PRE: Client is initialized with provider credentials.\n # @POST: Returns a list of model ID strings.\n # @SIDE_EFFECT: Calls external LLM API /v1/models endpoint.\n async def fetch_models(self) -> list[str]:\n with belief_scope(\"LLMClient.fetch_models\"):\n try:\n response = await self.client.models.list()\n model_ids = [m.id for m in response.data]\n model_ids.sort()\n logger.reason(\n f\"[LLMClient.fetch_models] Fetched {len(model_ids)} models from {self.base_url}\",\n extra={\"src\": \"LLMClient.fetch_models\"},\n )\n return model_ids\n except Exception as e:\n logger.warning(\n f\"[LLMClient.fetch_models] Failed to fetch models: {e}\",\n )\n raise\n # endregion LLMClient.fetch_models\n\n # region LLMClient.analyze_dashboard [TYPE Function]\n # @PURPOSE: Sends dashboard data (screenshot + logs) to LLM for health analysis.\n # @PRE: screenshot_path exists, logs is a list of strings.\n # @POST: Returns a structured analysis dictionary (status, summary, issues).\n # @SIDE_EFFECT: Reads screenshot file and calls external LLM API.\n async def analyze_dashboard(\n self,\n screenshot_path: str,\n logs: list[str],\n prompt_template: str = DEFAULT_LLM_PROMPTS[\"dashboard_validation_prompt\"],\n ) -> dict[str, Any]:\n with belief_scope(\"analyze_dashboard\"):\n # Optimize image to reduce token count (US1 / T023)\n # Gemini/Gemma models have limits on input tokens, and large images contribute significantly.\n try:\n with Image.open(screenshot_path) as img:\n # Convert to RGB if necessary\n if img.mode in (\"RGBA\", \"P\"):\n img = img.convert(\"RGB\")\n\n # Resize if too large (max 1024px width while maintaining aspect ratio)\n # We reduce width further to 1024px to stay within token limits for long dashboards\n max_width = 1024\n if img.width > max_width or img.height > 2048:\n # Calculate scaling factor to fit within 1024x2048\n scale = min(max_width / img.width, 2048 / img.height)\n if scale < 1.0:\n new_width = int(img.width * scale)\n new_height = int(img.height * scale)\n img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)\n logger.info(f\"[analyze_dashboard] Resized image from {img.width}x{img.height} to {new_width}x{new_height}\")\n\n # Compress and convert to base64\n buffer = io.BytesIO()\n # Lower quality to 60% to further reduce payload size\n img.save(buffer, format=\"JPEG\", quality=60, optimize=True)\n base_64_image = base64.b64encode(buffer.getvalue()).decode('utf-8')\n logger.info(f\"[analyze_dashboard] Optimized image size: {len(buffer.getvalue()) / 1024:.2f} KB\")\n except Exception as img_e:\n logger.warning(f\"[analyze_dashboard] Image optimization failed: {img_e}. Using raw image.\")\n with open(screenshot_path, \"rb\") as image_file:\n base_64_image = base64.b64encode(image_file.read()).decode('utf-8')\n\n log_text = \"\\n\".join(logs)\n prompt = render_prompt(prompt_template, {\"logs\": log_text})\n\n messages = [\n {\n \"role\": \"user\",\n \"content\": [\n {\"type\": \"text\", \"text\": prompt},\n {\n \"type\": \"image_url\",\n \"image_url\": {\n \"url\": f\"data:image/jpeg;base64,{base_64_image}\"\n }\n }\n ]\n }\n ]\n\n try:\n return await self.get_json_completion(messages)\n except Exception as e:\n logger.error(f\"[analyze_dashboard] Failed to get analysis: {e!s}\")\n return {\n \"status\": \"UNKNOWN\",\n \"summary\": f\"Failed to get response from LLM: {e!s}\",\n \"issues\": [{\"severity\": \"UNKNOWN\", \"message\": \"LLM provider returned empty or invalid response\"}]\n }\n # endregion LLMClient.analyze_dashboard\n# #endregion LLMClient\n\n# #endregion LLMAnalysisService\n" }, { "contract_id": "ScreenshotService", @@ -48609,7 +48297,7 @@ "contract_type": "Class", "file_path": "backend/src/plugins/llm_analysis/service.py", "start_line": 679, - "end_line": 976, + "end_line": 979, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -48619,7 +48307,7 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region LLMClient [TYPE Class]\n# @BRIEF Wrapper for LLM provider APIs.\nclass LLMClient:\n # region LLMClient.__init__ [TYPE Function]\n # @PURPOSE: Initializes the LLMClient with provider settings.\n # @PRE: api_key, base_url, and default_model are non-empty strings.\n def __init__(self, provider_type: LLMProviderType, api_key: str, base_url: str, default_model: str):\n self.provider_type = provider_type\n normalized_key = (api_key or \"\").strip()\n if normalized_key.lower().startswith(\"bearer \"):\n normalized_key = normalized_key[7:].strip()\n self.api_key = normalized_key\n self.base_url = base_url\n self.default_model = default_model\n\n # DEBUG: Log initialization parameters (without exposing full API key)\n logger.info(\"[LLMClient.__init__] Initializing LLM client:\")\n logger.info(f\"[LLMClient.__init__] Provider Type: {provider_type}\")\n logger.info(f\"[LLMClient.__init__] Base URL: {base_url}\")\n logger.info(f\"[LLMClient.__init__] Default Model: {default_model}\")\n logger.info(f\"[LLMClient.__init__] API Key (first 8 chars): {self.api_key[:8] if self.api_key and len(self.api_key) > 8 else 'EMPTY_OR_NONE'}...\")\n logger.info(f\"[LLMClient.__init__] API Key Length: {len(self.api_key) if self.api_key else 0}\")\n\n # Some OpenAI-compatible gateways are strict about auth header naming.\n default_headers = {\"Authorization\": f\"Bearer {self.api_key}\"}\n if self.provider_type == LLMProviderType.OPENROUTER:\n default_headers[\"HTTP-Referer\"] = (\n os.getenv(\"OPENROUTER_SITE_URL\", \"\").strip()\n or os.getenv(\"APP_BASE_URL\", \"\").strip()\n or \"http://localhost:8000\"\n )\n default_headers[\"X-Title\"] = os.getenv(\"OPENROUTER_APP_NAME\", \"\").strip() or \"ss-tools\"\n if self.provider_type == LLMProviderType.KILO:\n default_headers[\"Authentication\"] = f\"Bearer {self.api_key}\"\n default_headers[\"X-API-Key\"] = self.api_key\n\n http_client = httpx.AsyncClient(headers=default_headers, timeout=120.0)\n self.client = AsyncOpenAI(\n api_key=self.api_key,\n base_url=base_url,\n default_headers=default_headers,\n http_client=http_client,\n )\n # endregion LLMClient.__init__\n\n # region LLMClient._supports_json_response_format [TYPE Function]\n # @PURPOSE: Detect whether provider/model is likely compatible with response_format=json_object.\n # @PRE: Client initialized with base_url and default_model.\n # @POST: Returns False for known-incompatible combinations to avoid avoidable 400 errors.\n def _supports_json_response_format(self) -> bool:\n base = (self.base_url or \"\").lower()\n model = (self.default_model or \"\").lower()\n\n # OpenRouter routes to many upstream providers; some models reject json_object mode.\n if \"openrouter.ai\" in base:\n incompatible_tokens = (\n \"stepfun/\",\n \"step-\",\n \":free\",\n )\n if any(token in model for token in incompatible_tokens):\n return False\n return True\n # endregion LLMClient._supports_json_response_format\n\n # region LLMClient.get_json_completion [TYPE Function]\n # @PURPOSE: Helper to handle LLM calls with JSON mode and fallback parsing.\n # @PRE: messages is a list of valid message dictionaries.\n # @POST: Returns a parsed JSON dictionary.\n # @SIDE_EFFECT: Calls external LLM API.\n def _should_retry(exception: Exception) -> bool:\n \"\"\"Custom retry predicate that excludes authentication errors.\"\"\"\n # Don't retry on authentication errors\n if isinstance(exception, OpenAIAuthenticationError):\n return False\n # Retry on rate limit errors and other exceptions\n return isinstance(exception, (RateLimitError, Exception))\n\n @retry(\n stop=stop_after_attempt(5),\n wait=wait_exponential(multiplier=2, min=5, max=60),\n retry=retry_if_exception(_should_retry),\n reraise=True\n )\n async def get_json_completion(self, messages: list[dict[str, Any]]) -> dict[str, Any]:\n with belief_scope(\"get_json_completion\"):\n response = None\n try:\n use_json_mode = self._supports_json_response_format()\n try:\n logger.info(\n f\"[get_json_completion] Attempting LLM call for model: {self.default_model} \"\n f\"(json_mode={'on' if use_json_mode else 'off'})\"\n )\n logger.info(f\"[get_json_completion] Base URL being used: {self.base_url}\")\n logger.info(f\"[get_json_completion] Number of messages: {len(messages)}\")\n logger.info(f\"[get_json_completion] API Key present: {bool(self.api_key and len(self.api_key) > 0)}\")\n\n if use_json_mode:\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages,\n response_format={\"type\": \"json_object\"}\n )\n else:\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages\n )\n except Exception as e:\n if use_json_mode and (\n \"JSON mode is not enabled\" in str(e)\n or \"json_object is not supported\" in str(e).lower()\n or \"response_format\" in str(e).lower()\n or \"400\" in str(e)\n ):\n logger.warning(f\"[get_json_completion] JSON mode failed or not supported: {e!s}. Falling back to plain text response.\")\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages\n )\n else:\n raise e\n\n logger.debug(f\"[get_json_completion] LLM Response: {response}\")\n except OpenAIAuthenticationError as e:\n logger.error(f\"[get_json_completion] Authentication error: {e!s}\")\n # Do not retry on auth errors - re-raise to stop retry\n raise\n except RateLimitError as e:\n logger.warning(f\"[get_json_completion] Rate limit hit: {e!s}\")\n\n # Extract retry_delay from error metadata if available\n retry_delay = 5.0 # Default fallback\n try:\n # Based on logs, the raw response is in e.body or e.response.json()\n # The logs show 'metadata': {'raw': '...'} which suggests a proxy or specific client wrapper\n # Let's try to find the 'retryDelay' in the error message or response\n import re\n\n # Try to find \"retryDelay\": \"XXs\" in the string representation of the error\n error_str = str(e)\n match = re.search(r'\"retryDelay\":\\s*\"(\\d+)s\"', error_str)\n if match:\n retry_delay = float(match.group(1))\n else:\n # Try to parse from response if it's a standard OpenAI-like error with body\n if hasattr(e, 'body') and isinstance(e.body, dict):\n # Some providers put it in details\n details = e.body.get('error', {}).get('details', [])\n for detail in details:\n if detail.get('@type') == 'type.googleapis.com/google.rpc.RetryInfo':\n delay_str = detail.get('retryDelay', '5s')\n retry_delay = float(delay_str.rstrip('s'))\n break\n except Exception as parse_e:\n logger.debug(f\"[get_json_completion] Failed to parse retry delay: {parse_e}\")\n\n # Add a small safety margin (0.5s) as requested\n wait_time = retry_delay + 0.5\n logger.info(f\"[get_json_completion] Waiting for {wait_time}s before retry...\")\n await asyncio.sleep(wait_time)\n raise\n except Exception as e:\n logger.error(f\"[get_json_completion] LLM call failed: {e!s}\")\n raise\n\n if not response or not hasattr(response, 'choices') or not response.choices:\n raise RuntimeError(f\"Invalid LLM response: {response}\")\n\n content = response.choices[0].message.content\n logger.debug(f\"[get_json_completion] Raw content to parse: {content}\")\n\n try:\n return json.loads(content)\n except json.JSONDecodeError:\n logger.warning(\"[get_json_completion] Failed to parse JSON directly, attempting to extract from code blocks\")\n if \"```json\" in content:\n json_str = content.split(\"```json\")[1].split(\"```\")[0].strip()\n return json.loads(json_str)\n elif \"```\" in content:\n json_str = content.split(\"```\")[1].split(\"```\")[0].strip()\n return json.loads(json_str)\n else:\n raise\n # endregion LLMClient.get_json_completion\n\n # region LLMClient.test_runtime_connection [TYPE Function]\n # @PURPOSE: Validate provider credentials using the same chat completions transport as runtime analysis.\n # @PRE: Client is initialized with provider credentials and default_model.\n # @POST: Returns lightweight JSON payload when runtime auth/model path is valid.\n # @SIDE_EFFECT: Calls external LLM API.\n async def test_runtime_connection(self) -> dict[str, Any]:\n with belief_scope(\"test_runtime_connection\"):\n messages = [\n {\n \"role\": \"user\",\n \"content\": 'Return exactly this JSON object and nothing else: {\"ok\": true}',\n }\n ]\n return await self.get_json_completion(messages)\n # endregion LLMClient.test_runtime_connection\n\n # region LLMClient.fetch_models [TYPE Function]\n # @PURPOSE: Fetch available models from the provider's API.\n # @PRE: Client is initialized with provider credentials.\n # @POST: Returns a list of model ID strings.\n # @SIDE_EFFECT: Calls external LLM API /v1/models endpoint.\n async def fetch_models(self) -> list[str]:\n with belief_scope(\"LLMClient.fetch_models\"):\n try:\n response = await self.client.models.list()\n model_ids = [m.id for m in response.data]\n model_ids.sort()\n logger.reason(\n f\"[LLMClient.fetch_models] Fetched {len(model_ids)} models from {self.base_url}\",\n extra={\"src\": \"LLMClient.fetch_models\"},\n )\n return model_ids\n except Exception as e:\n logger.warning(\n f\"[LLMClient.fetch_models] Failed to fetch models: {e}\",\n )\n raise\n # endregion LLMClient.fetch_models\n\n # region LLMClient.analyze_dashboard [TYPE Function]\n # @PURPOSE: Sends dashboard data (screenshot + logs) to LLM for health analysis.\n # @PRE: screenshot_path exists, logs is a list of strings.\n # @POST: Returns a structured analysis dictionary (status, summary, issues).\n # @SIDE_EFFECT: Reads screenshot file and calls external LLM API.\n async def analyze_dashboard(\n self,\n screenshot_path: str,\n logs: list[str],\n prompt_template: str = DEFAULT_LLM_PROMPTS[\"dashboard_validation_prompt\"],\n ) -> dict[str, Any]:\n with belief_scope(\"analyze_dashboard\"):\n # Optimize image to reduce token count (US1 / T023)\n # Gemini/Gemma models have limits on input tokens, and large images contribute significantly.\n try:\n with Image.open(screenshot_path) as img:\n # Convert to RGB if necessary\n if img.mode in (\"RGBA\", \"P\"):\n img = img.convert(\"RGB\")\n\n # Resize if too large (max 1024px width while maintaining aspect ratio)\n # We reduce width further to 1024px to stay within token limits for long dashboards\n max_width = 1024\n if img.width > max_width or img.height > 2048:\n # Calculate scaling factor to fit within 1024x2048\n scale = min(max_width / img.width, 2048 / img.height)\n if scale < 1.0:\n new_width = int(img.width * scale)\n new_height = int(img.height * scale)\n img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)\n logger.info(f\"[analyze_dashboard] Resized image from {img.width}x{img.height} to {new_width}x{new_height}\")\n\n # Compress and convert to base64\n buffer = io.BytesIO()\n # Lower quality to 60% to further reduce payload size\n img.save(buffer, format=\"JPEG\", quality=60, optimize=True)\n base_64_image = base64.b64encode(buffer.getvalue()).decode('utf-8')\n logger.info(f\"[analyze_dashboard] Optimized image size: {len(buffer.getvalue()) / 1024:.2f} KB\")\n except Exception as img_e:\n logger.warning(f\"[analyze_dashboard] Image optimization failed: {img_e}. Using raw image.\")\n with open(screenshot_path, \"rb\") as image_file:\n base_64_image = base64.b64encode(image_file.read()).decode('utf-8')\n\n log_text = \"\\n\".join(logs)\n prompt = render_prompt(prompt_template, {\"logs\": log_text})\n\n messages = [\n {\n \"role\": \"user\",\n \"content\": [\n {\"type\": \"text\", \"text\": prompt},\n {\n \"type\": \"image_url\",\n \"image_url\": {\n \"url\": f\"data:image/jpeg;base64,{base_64_image}\"\n }\n }\n ]\n }\n ]\n\n try:\n return await self.get_json_completion(messages)\n except Exception as e:\n logger.error(f\"[analyze_dashboard] Failed to get analysis: {e!s}\")\n return {\n \"status\": \"UNKNOWN\",\n \"summary\": f\"Failed to get response from LLM: {e!s}\",\n \"issues\": [{\"severity\": \"UNKNOWN\", \"message\": \"LLM provider returned empty or invalid response\"}]\n }\n # endregion LLMClient.analyze_dashboard\n# #endregion LLMClient\n" + "body": "# #region LLMClient [TYPE Class]\n# @BRIEF Wrapper for LLM provider APIs.\nclass LLMClient:\n # region LLMClient.__init__ [TYPE Function]\n # @PURPOSE: Initializes the LLMClient with provider settings.\n # @PRE: api_key, base_url, and default_model are non-empty strings.\n def __init__(self, provider_type: LLMProviderType, api_key: str, base_url: str, default_model: str):\n self.provider_type = provider_type\n normalized_key = (api_key or \"\").strip()\n if normalized_key.lower().startswith(\"bearer \"):\n normalized_key = normalized_key[7:].strip()\n self.api_key = normalized_key\n self.base_url = base_url\n self.default_model = default_model\n\n # DEBUG: Log initialization parameters (without exposing full API key)\n logger.info(\"[LLMClient.__init__] Initializing LLM client:\")\n logger.info(f\"[LLMClient.__init__] Provider Type: {provider_type}\")\n logger.info(f\"[LLMClient.__init__] Base URL: {base_url}\")\n logger.info(f\"[LLMClient.__init__] Default Model: {default_model}\")\n logger.info(f\"[LLMClient.__init__] API Key (first 8 chars): {self.api_key[:8] if self.api_key and len(self.api_key) > 8 else 'EMPTY_OR_NONE'}...\")\n logger.info(f\"[LLMClient.__init__] API Key Length: {len(self.api_key) if self.api_key else 0}\")\n\n # Some OpenAI-compatible gateways are strict about auth header naming.\n default_headers = {\"Authorization\": f\"Bearer {self.api_key}\"}\n if self.provider_type == LLMProviderType.OPENROUTER:\n default_headers[\"HTTP-Referer\"] = (\n os.getenv(\"OPENROUTER_SITE_URL\", \"\").strip()\n or os.getenv(\"APP_BASE_URL\", \"\").strip()\n or \"http://localhost:8000\"\n )\n default_headers[\"X-Title\"] = os.getenv(\"OPENROUTER_APP_NAME\", \"\").strip() or \"ss-tools\"\n if self.provider_type == LLMProviderType.KILO:\n default_headers[\"Authentication\"] = f\"Bearer {self.api_key}\"\n default_headers[\"X-API-Key\"] = self.api_key\n # LiteLLM proxy uses standard OpenAI-compatible Bearer auth — no special headers needed.\n # It routes to upstream providers transparently, and the default Authorization header\n # is sufficient. No additional headers like HTTP-Referer or X-API-Key are required.\n\n http_client = httpx.AsyncClient(headers=default_headers, timeout=120.0)\n self.client = AsyncOpenAI(\n api_key=self.api_key,\n base_url=base_url,\n default_headers=default_headers,\n http_client=http_client,\n )\n # endregion LLMClient.__init__\n\n # region LLMClient._supports_json_response_format [TYPE Function]\n # @PURPOSE: Detect whether provider/model is likely compatible with response_format=json_object.\n # @PRE: Client initialized with base_url and default_model.\n # @POST: Returns False for known-incompatible combinations to avoid avoidable 400 errors.\n def _supports_json_response_format(self) -> bool:\n base = (self.base_url or \"\").lower()\n model = (self.default_model or \"\").lower()\n\n # OpenRouter routes to many upstream providers; some models reject json_object mode.\n if \"openrouter.ai\" in base:\n incompatible_tokens = (\n \"stepfun/\",\n \"step-\",\n \":free\",\n )\n if any(token in model for token in incompatible_tokens):\n return False\n return True\n # endregion LLMClient._supports_json_response_format\n\n # region LLMClient.get_json_completion [TYPE Function]\n # @PURPOSE: Helper to handle LLM calls with JSON mode and fallback parsing.\n # @PRE: messages is a list of valid message dictionaries.\n # @POST: Returns a parsed JSON dictionary.\n # @SIDE_EFFECT: Calls external LLM API.\n def _should_retry(exception: Exception) -> bool:\n \"\"\"Custom retry predicate that excludes authentication errors.\"\"\"\n # Don't retry on authentication errors\n if isinstance(exception, OpenAIAuthenticationError):\n return False\n # Retry on rate limit errors and other exceptions\n return isinstance(exception, (RateLimitError, Exception))\n\n @retry(\n stop=stop_after_attempt(5),\n wait=wait_exponential(multiplier=2, min=5, max=60),\n retry=retry_if_exception(_should_retry),\n reraise=True\n )\n async def get_json_completion(self, messages: list[dict[str, Any]]) -> dict[str, Any]:\n with belief_scope(\"get_json_completion\"):\n response = None\n try:\n use_json_mode = self._supports_json_response_format()\n try:\n logger.info(\n f\"[get_json_completion] Attempting LLM call for model: {self.default_model} \"\n f\"(json_mode={'on' if use_json_mode else 'off'})\"\n )\n logger.info(f\"[get_json_completion] Base URL being used: {self.base_url}\")\n logger.info(f\"[get_json_completion] Number of messages: {len(messages)}\")\n logger.info(f\"[get_json_completion] API Key present: {bool(self.api_key and len(self.api_key) > 0)}\")\n\n if use_json_mode:\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages,\n response_format={\"type\": \"json_object\"}\n )\n else:\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages\n )\n except Exception as e:\n if use_json_mode and (\n \"JSON mode is not enabled\" in str(e)\n or \"json_object is not supported\" in str(e).lower()\n or \"response_format\" in str(e).lower()\n or \"400\" in str(e)\n ):\n logger.warning(f\"[get_json_completion] JSON mode failed or not supported: {e!s}. Falling back to plain text response.\")\n response = await self.client.chat.completions.create(\n model=self.default_model,\n messages=messages\n )\n else:\n raise e\n\n logger.debug(f\"[get_json_completion] LLM Response: {response}\")\n except OpenAIAuthenticationError as e:\n logger.error(f\"[get_json_completion] Authentication error: {e!s}\")\n # Do not retry on auth errors - re-raise to stop retry\n raise\n except RateLimitError as e:\n logger.warning(f\"[get_json_completion] Rate limit hit: {e!s}\")\n\n # Extract retry_delay from error metadata if available\n retry_delay = 5.0 # Default fallback\n try:\n # Based on logs, the raw response is in e.body or e.response.json()\n # The logs show 'metadata': {'raw': '...'} which suggests a proxy or specific client wrapper\n # Let's try to find the 'retryDelay' in the error message or response\n import re\n\n # Try to find \"retryDelay\": \"XXs\" in the string representation of the error\n error_str = str(e)\n match = re.search(r'\"retryDelay\":\\s*\"(\\d+)s\"', error_str)\n if match:\n retry_delay = float(match.group(1))\n else:\n # Try to parse from response if it's a standard OpenAI-like error with body\n if hasattr(e, 'body') and isinstance(e.body, dict):\n # Some providers put it in details\n details = e.body.get('error', {}).get('details', [])\n for detail in details:\n if detail.get('@type') == 'type.googleapis.com/google.rpc.RetryInfo':\n delay_str = detail.get('retryDelay', '5s')\n retry_delay = float(delay_str.rstrip('s'))\n break\n except Exception as parse_e:\n logger.debug(f\"[get_json_completion] Failed to parse retry delay: {parse_e}\")\n\n # Add a small safety margin (0.5s) as requested\n wait_time = retry_delay + 0.5\n logger.info(f\"[get_json_completion] Waiting for {wait_time}s before retry...\")\n await asyncio.sleep(wait_time)\n raise\n except Exception as e:\n logger.error(f\"[get_json_completion] LLM call failed: {e!s}\")\n raise\n\n if not response or not hasattr(response, 'choices') or not response.choices:\n raise RuntimeError(f\"Invalid LLM response: {response}\")\n\n content = response.choices[0].message.content\n logger.debug(f\"[get_json_completion] Raw content to parse: {content}\")\n\n try:\n return json.loads(content)\n except json.JSONDecodeError:\n logger.warning(\"[get_json_completion] Failed to parse JSON directly, attempting to extract from code blocks\")\n if \"```json\" in content:\n json_str = content.split(\"```json\")[1].split(\"```\")[0].strip()\n return json.loads(json_str)\n elif \"```\" in content:\n json_str = content.split(\"```\")[1].split(\"```\")[0].strip()\n return json.loads(json_str)\n else:\n raise\n # endregion LLMClient.get_json_completion\n\n # region LLMClient.test_runtime_connection [TYPE Function]\n # @PURPOSE: Validate provider credentials using the same chat completions transport as runtime analysis.\n # @PRE: Client is initialized with provider credentials and default_model.\n # @POST: Returns lightweight JSON payload when runtime auth/model path is valid.\n # @SIDE_EFFECT: Calls external LLM API.\n async def test_runtime_connection(self) -> dict[str, Any]:\n with belief_scope(\"test_runtime_connection\"):\n messages = [\n {\n \"role\": \"user\",\n \"content\": 'Return exactly this JSON object and nothing else: {\"ok\": true}',\n }\n ]\n return await self.get_json_completion(messages)\n # endregion LLMClient.test_runtime_connection\n\n # region LLMClient.fetch_models [TYPE Function]\n # @PURPOSE: Fetch available models from the provider's API.\n # @PRE: Client is initialized with provider credentials.\n # @POST: Returns a list of model ID strings.\n # @SIDE_EFFECT: Calls external LLM API /v1/models endpoint.\n async def fetch_models(self) -> list[str]:\n with belief_scope(\"LLMClient.fetch_models\"):\n try:\n response = await self.client.models.list()\n model_ids = [m.id for m in response.data]\n model_ids.sort()\n logger.reason(\n f\"[LLMClient.fetch_models] Fetched {len(model_ids)} models from {self.base_url}\",\n extra={\"src\": \"LLMClient.fetch_models\"},\n )\n return model_ids\n except Exception as e:\n logger.warning(\n f\"[LLMClient.fetch_models] Failed to fetch models: {e}\",\n )\n raise\n # endregion LLMClient.fetch_models\n\n # region LLMClient.analyze_dashboard [TYPE Function]\n # @PURPOSE: Sends dashboard data (screenshot + logs) to LLM for health analysis.\n # @PRE: screenshot_path exists, logs is a list of strings.\n # @POST: Returns a structured analysis dictionary (status, summary, issues).\n # @SIDE_EFFECT: Reads screenshot file and calls external LLM API.\n async def analyze_dashboard(\n self,\n screenshot_path: str,\n logs: list[str],\n prompt_template: str = DEFAULT_LLM_PROMPTS[\"dashboard_validation_prompt\"],\n ) -> dict[str, Any]:\n with belief_scope(\"analyze_dashboard\"):\n # Optimize image to reduce token count (US1 / T023)\n # Gemini/Gemma models have limits on input tokens, and large images contribute significantly.\n try:\n with Image.open(screenshot_path) as img:\n # Convert to RGB if necessary\n if img.mode in (\"RGBA\", \"P\"):\n img = img.convert(\"RGB\")\n\n # Resize if too large (max 1024px width while maintaining aspect ratio)\n # We reduce width further to 1024px to stay within token limits for long dashboards\n max_width = 1024\n if img.width > max_width or img.height > 2048:\n # Calculate scaling factor to fit within 1024x2048\n scale = min(max_width / img.width, 2048 / img.height)\n if scale < 1.0:\n new_width = int(img.width * scale)\n new_height = int(img.height * scale)\n img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)\n logger.info(f\"[analyze_dashboard] Resized image from {img.width}x{img.height} to {new_width}x{new_height}\")\n\n # Compress and convert to base64\n buffer = io.BytesIO()\n # Lower quality to 60% to further reduce payload size\n img.save(buffer, format=\"JPEG\", quality=60, optimize=True)\n base_64_image = base64.b64encode(buffer.getvalue()).decode('utf-8')\n logger.info(f\"[analyze_dashboard] Optimized image size: {len(buffer.getvalue()) / 1024:.2f} KB\")\n except Exception as img_e:\n logger.warning(f\"[analyze_dashboard] Image optimization failed: {img_e}. Using raw image.\")\n with open(screenshot_path, \"rb\") as image_file:\n base_64_image = base64.b64encode(image_file.read()).decode('utf-8')\n\n log_text = \"\\n\".join(logs)\n prompt = render_prompt(prompt_template, {\"logs\": log_text})\n\n messages = [\n {\n \"role\": \"user\",\n \"content\": [\n {\"type\": \"text\", \"text\": prompt},\n {\n \"type\": \"image_url\",\n \"image_url\": {\n \"url\": f\"data:image/jpeg;base64,{base_64_image}\"\n }\n }\n ]\n }\n ]\n\n try:\n return await self.get_json_completion(messages)\n except Exception as e:\n logger.error(f\"[analyze_dashboard] Failed to get analysis: {e!s}\")\n return {\n \"status\": \"UNKNOWN\",\n \"summary\": f\"Failed to get response from LLM: {e!s}\",\n \"issues\": [{\"severity\": \"UNKNOWN\", \"message\": \"LLM provider returned empty or invalid response\"}]\n }\n # endregion LLMClient.analyze_dashboard\n# #endregion LLMClient\n" }, { "contract_id": "MapperPluginModule", @@ -49010,52 +48698,22 @@ "contract_type": "Package", "file_path": "backend/src/plugins/translate/__init__.py", "start_line": 1, - "end_line": 3, + "end_line": 2, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Translation plugin package for LLM-based cross-Superset SQL/dashboard translation." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Package'", - "detail": { - "actual_type": "Package", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Package' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Package" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslatePluginPackage [TYPE Package] [SEMANTICS translate, plugin, package, sql, dashboard]\n# @BRIEF Translation plugin package for LLM-based cross-Superset SQL/dashboard translation.\n# #endregion TranslatePluginPackage\n" + "body": "# #region TranslatePluginPackage [TYPE Package] [SEMANTICS translate, plugin, package, sql, dashboard]\n# #endregion TranslatePluginPackage\n" }, { "contract_id": "TestClickHouseTimestampNormalization", "contract_type": "Class", "file_path": "backend/src/plugins/translate/__tests__/test_clickhouse_insert_integration.py", - "start_line": 76, - "end_line": 136, + "start_line": 81, + "end_line": 141, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -49071,8 +48729,8 @@ "contract_id": "TestClickHouseInsertSqlGeneration", "contract_type": "Class", "file_path": "backend/src/plugins/translate/__tests__/test_clickhouse_insert_integration.py", - "start_line": 139, - "end_line": 248, + "start_line": 144, + "end_line": 253, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -49088,8 +48746,8 @@ "contract_id": "TestClickHouseJoinVerification", "contract_type": "Class", "file_path": "backend/src/plugins/translate/__tests__/test_clickhouse_insert_integration.py", - "start_line": 251, - "end_line": 319, + "start_line": 256, + "end_line": 324, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -49105,8 +48763,8 @@ "contract_id": "TestOrchestratorInsertFlow", "contract_type": "Class", "file_path": "backend/src/plugins/translate/__tests__/test_clickhouse_insert_integration.py", - "start_line": 322, - "end_line": 410, + "start_line": 327, + "end_line": 416, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -49116,14 +48774,14 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TestOrchestratorInsertFlow [TYPE Class]\n# @BRIEF Verify the orchestrator's _generate_and_insert_sql produces correct SQL for ClickHouse.\nclass TestOrchestratorInsertFlow:\n \"\"\"Verify TranslationOrchestrator._generate_and_insert_sql with mocked DB.\"\"\"\n\n # region test_generate_and_insert_sql_with_timestamps [TYPE Function]\n # @BRIEF Orchestrator builds correct rows_for_sql from source_data with timestamps.\n def test_generate_and_insert_sql_with_timestamps(self) -> None:\n \"\"\"Verify the orchestrator correctly passes source_data through to SQLGenerator.\"\"\"\n from src.plugins.translate.orchestrator import TranslationOrchestrator\n\n # Mock DB session\n db = MagicMock()\n config_manager = MagicMock()\n\n # Create mock records with timestamp source_data\n rec1 = _make_mock_record(\n target_sql=\"Translated comment 1\",\n report_date=\"1726358400000.0\",\n document_number=\"DOC-001\",\n )\n rec2 = _make_mock_record(\n target_sql=\"Translated comment 2\",\n report_date=\"1726444800000.0\",\n document_number=\"DOC-002\",\n )\n\n # Mock the DB query to return our records\n mock_query = MagicMock()\n mock_query.filter.return_value.all.return_value = [rec1, rec2]\n db.query.return_value = mock_query\n\n # Create mock job\n job = _make_mock_job()\n\n # Create mock run\n run = MagicMock()\n run.id = \"run-test-001\"\n run.job_id = job.id\n\n # Create orchestrator\n orch = TranslationOrchestrator(db, config_manager, \"test-user\")\n\n # Mock the event_log and SupersetSqlLabExecutor\n with patch.object(orch, \"event_log\") as mock_event_log, patch(\n \"src.plugins.translate.orchestrator.SupersetSqlLabExecutor\"\n ) as MockExecutor:\n mock_executor = MagicMock()\n MockExecutor.return_value = mock_executor\n mock_executor.resolve_database_id.return_value = None\n mock_executor.get_database_backend.return_value = None\n mock_executor.execute_and_poll.return_value = {\n \"status\": \"success\",\n \"query_id\": \"q-123\",\n \"rows_affected\": 2,\n }\n\n # Call _generate_and_insert_sql\n result = orch._generate_and_insert_sql(job, run)\n\n # Verify executor was called\n mock_executor.execute_and_poll.assert_called_once()\n\n # Get the SQL that was submitted\n call_args = mock_executor.execute_and_poll.call_args\n submitted_sql = call_args.kwargs.get(\"sql\") or call_args[1].get(\"sql\")\n\n # Verify the SQL contains normalized dates\n assert \"'2024-09-15'\" in submitted_sql, f\"Expected '2024-09-15' in SQL:\\n{submitted_sql}\"\n assert \"'2024-09-16'\" in submitted_sql, f\"Expected '2024-09-16' in SQL:\\n{submitted_sql}\"\n\n # Verify document numbers are preserved\n assert \"'DOC-001'\" in submitted_sql\n assert \"'DOC-002'\" in submitted_sql\n\n # Verify translated text is present\n assert \"'Translated comment 1'\" in submitted_sql\n assert \"'Translated comment 2'\" in submitted_sql\n\n # Verify ClickHouse-specific quoting (backticks)\n assert \"`report_date`\" in submitted_sql\n assert \"`document_number`\" in submitted_sql\n assert \"`comment_text_en`\" in submitted_sql\n\n # Verify result\n assert result[\"status\"] == \"success\"\n\n # endregion test_generate_and_insert_sql_with_timestamps\n# #endregion TestOrchestratorInsertFlow\n" + "body": "# #region TestOrchestratorInsertFlow [TYPE Class]\n# @BRIEF Verify the orchestrator's _generate_and_insert_sql produces correct SQL for ClickHouse.\nclass TestOrchestratorInsertFlow:\n \"\"\"Verify TranslationOrchestrator._generate_and_insert_sql with mocked DB.\"\"\"\n\n # region test_generate_and_insert_sql_with_timestamps [TYPE Function]\n # @BRIEF Orchestrator builds correct rows_for_sql from source_data with timestamps.\n def test_generate_and_insert_sql_with_timestamps(self) -> None:\n \"\"\"Verify the orchestrator correctly passes source_data through to SQLGenerator.\"\"\"\n from src.plugins.translate.orchestrator import TranslationOrchestrator\n\n # Mock DB session\n db = MagicMock()\n config_manager = MagicMock()\n\n # Create mock records with timestamp source_data\n rec1 = _make_mock_record(\n target_sql=\"Translated comment 1\",\n report_date=\"1726358400000.0\",\n document_number=\"DOC-001\",\n )\n rec2 = _make_mock_record(\n target_sql=\"Translated comment 2\",\n report_date=\"1726444800000.0\",\n document_number=\"DOC-002\",\n )\n\n # Mock the DB query to return our records\n # Note: orchestrator uses .options(joinedload()) before .filter()\n mock_query = MagicMock()\n mock_query.options.return_value.filter.return_value.all.return_value = [rec1, rec2]\n db.query.return_value = mock_query\n\n # Create mock job\n job = _make_mock_job()\n\n # Create mock run\n run = MagicMock()\n run.id = \"run-test-001\"\n run.job_id = job.id\n\n # Create orchestrator\n orch = TranslationOrchestrator(db, config_manager, \"test-user\")\n\n # Mock the event_log and SupersetSqlLabExecutor\n with patch.object(orch, \"event_log\") as mock_event_log, patch(\n \"src.plugins.translate.orchestrator.SupersetSqlLabExecutor\"\n ) as MockExecutor:\n mock_executor = MagicMock()\n MockExecutor.return_value = mock_executor\n mock_executor.resolve_database_id.return_value = None\n mock_executor.get_database_backend.return_value = None\n mock_executor.execute_and_poll.return_value = {\n \"status\": \"success\",\n \"query_id\": \"q-123\",\n \"rows_affected\": 2,\n }\n\n # Call _generate_and_insert_sql\n result = orch._generate_and_insert_sql(job, run)\n\n # Verify executor was called\n mock_executor.execute_and_poll.assert_called_once()\n\n # Get the SQL that was submitted\n call_args = mock_executor.execute_and_poll.call_args\n submitted_sql = call_args.kwargs.get(\"sql\") or call_args[1].get(\"sql\")\n\n # Verify the SQL contains normalized dates\n assert \"'2024-09-15'\" in submitted_sql, f\"Expected '2024-09-15' in SQL:\\n{submitted_sql}\"\n assert \"'2024-09-16'\" in submitted_sql, f\"Expected '2024-09-16' in SQL:\\n{submitted_sql}\"\n\n # Verify document numbers are preserved\n assert \"'DOC-001'\" in submitted_sql\n assert \"'DOC-002'\" in submitted_sql\n\n # Verify translated text is present\n assert \"'Translated comment 1'\" in submitted_sql\n assert \"'Translated comment 2'\" in submitted_sql\n\n # Verify ClickHouse-specific quoting (backticks)\n assert \"`report_date`\" in submitted_sql\n assert \"`document_number`\" in submitted_sql\n assert \"`comment_text_en`\" in submitted_sql\n\n # Verify result\n assert result[\"status\"] == \"success\"\n\n # endregion test_generate_and_insert_sql_with_timestamps\n# #endregion TestOrchestratorInsertFlow\n" }, { "contract_id": "TestClickHouseEndToEnd", "contract_type": "Class", "file_path": "backend/src/plugins/translate/__tests__/test_clickhouse_insert_integration.py", - "start_line": 413, - "end_line": 539, + "start_line": 419, + "end_line": 545, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -49135,39 +48793,6 @@ "tier_source": "AutoCalculated", "body": "# #region TestClickHouseEndToEnd [TYPE Class]\n# @BRIEF End-to-end test: generate SQL, verify structure, simulate execution.\nclass TestClickHouseEndToEnd:\n \"\"\"End-to-end verification of ClickHouse INSERT flow.\"\"\"\n\n # region test_full_insert_sql_valid_for_clickhouse [TYPE Function]\n # @BRIEF Generated SQL is valid ClickHouse INSERT with proper date formatting.\n def test_full_insert_sql_valid_for_clickhouse(self) -> None:\n \"\"\"Simulate the full flow: records -> SQL generation -> verification.\"\"\"\n # Simulate what the orchestrator does\n records = [\n _make_mock_record(\n target_sql=\"Comment about supply chain\",\n report_date=\"1726358400000.0\",\n document_number=\"INV-2024-001\",\n ),\n _make_mock_record(\n target_sql=\"Payment terms description\",\n report_date=\"1726444800000.0\",\n document_number=\"INV-2024-002\",\n ),\n _make_mock_record(\n target_sql=\"Delivery schedule notes\",\n report_date=\"1726531200000.0\",\n document_number=\"INV-2024-003\",\n ),\n ]\n\n # Build rows_for_sql (same logic as orchestrator._generate_and_insert_sql)\n job = _make_mock_job()\n columns = []\n if job.translation_column:\n columns.append(job.translation_column)\n if job.target_key_cols:\n for k in job.target_key_cols:\n if k not in columns:\n columns.append(k)\n\n rows_for_sql = []\n for rec in records:\n row_data = {}\n if job.translation_column:\n row_data[job.translation_column] = rec.target_sql or \"\"\n if job.target_key_cols:\n source_data = rec.source_data or {}\n for k in job.target_key_cols:\n row_data[k] = source_data.get(k, \"\")\n rows_for_sql.append(row_data)\n\n # Generate SQL\n sql, count = SQLGenerator.generate(\n dialect=job.database_dialect or job.target_dialect or \"clickhouse\",\n target_schema=job.target_schema,\n target_table=job.target_table,\n columns=columns,\n rows=rows_for_sql,\n key_columns=job.target_key_cols,\n upsert_strategy=job.upsert_strategy or \"INSERT\",\n )\n\n # Verify SQL structure\n assert count == 3\n assert sql.startswith(\"INSERT INTO\")\n assert \"dm_view\" in sql\n assert \"financial_comments_translated\" in sql\n\n # Verify all dates are normalized\n assert \"'2024-09-15'\" in sql # 1726358400000\n assert \"'2024-09-16'\" in sql # 1726444800000\n assert \"'2024-09-17'\" in sql # 1726531200000\n\n # Verify NO raw timestamps leaked through\n assert \"1726358400000\" not in sql\n assert \"1726444800000\" not in sql\n assert \"1726531200000\" not in sql\n\n # Verify document numbers\n assert \"'INV-2024-001'\" in sql\n assert \"'INV-2024-002'\" in sql\n assert \"'INV-2024-003'\" in sql\n\n # Verify translated texts\n assert \"'Comment about supply chain'\" in sql\n assert \"'Payment terms description'\" in sql\n assert \"'Delivery schedule notes'\" in sql\n\n # Print SQL for manual verification\n print(f\"\\n{'='*60}\")\n print(\"Generated ClickHouse INSERT SQL:\")\n print(f\"{'='*60}\")\n print(sql)\n print(f\"{'='*60}\")\n\n # region test_verification_join_query [TYPE Function]\n # @BRIEF The JOIN verification query that can be run against ClickHouse.\n def test_verification_join_query(self) -> None:\n \"\"\"Generate the verification JOIN query for manual execution.\"\"\"\n join_sql = \"\"\"\nSELECT\n f_a.report_date,\n f_a.document_number,\n comment_text AS comment_text_ru,\n counterparty,\n supply_country,\n f_c.comment_text_en\nFROM dm_view.financial_arrears f_a\nLEFT JOIN dm_view.financial_comments_translated f_c\n ON f_a.report_date::date = f_c.report_date::date\n AND f_a.document_number = f_c.document_number\nWHERE f_c.comment_text_en IS NOT NULL\nLIMIT 10\n\"\"\"\n print(f\"\\n{'='*60}\")\n print(\"Verification JOIN query (run in ClickHouse):\")\n print(f\"{'='*60}\")\n print(join_sql)\n print(f\"{'='*60}\")\n\n # Verify structure\n assert \"f_a.report_date::date = f_c.report_date::date\" in join_sql\n assert \"f_a.document_number = f_c.document_number\" in join_sql\n assert \"dm_view.financial_arrears\" in join_sql\n assert \"dm_view.financial_comments_translated\" in join_sql\n\n # endregion test_full_insert_sql_valid_for_clickhouse\n # endregion test_verification_join_query\n# #endregion TestClickHouseEndToEnd\n" }, - { - "contract_id": "estimate_token_budget", - "contract_type": "Module", - "file_path": "backend/src/plugins/translate/_token_budget.py", - "start_line": 1, - "end_line": 201, - "tier": "TIER_2", - "complexity": 3, - "metadata": { - "COMPLEXITY": 3, - "LAYER": "Domain", - "PURPOSE": "Calculate safe batch_size and max_tokens for LLM translation calls based on actual content length and model context window limits.", - "RATIONALE": "Prevents LLM truncation (finish_reason=length) by sizing batches within context limits." - }, - "relations": [ - { - "source_id": "estimate_token_budget", - "relation_type": "DEPENDS_ON", - "target_id": "TranslationPreview:Module", - "target_ref": "[TranslationPreview:Module]" - }, - { - "source_id": "estimate_token_budget", - "relation_type": "DEPENDS_ON", - "target_id": "TranslationExecutor:Module", - "target_ref": "[TranslationExecutor:Module]" - } - ], - "schema_warnings": [], - "anchor_syntax": "region", - "tier_source": "AutoCalculated", - "body": "# #region estimate_token_budget [C:3] [TYPE Module] [SEMANTICS translate, token, budget, estimation, llm]\n# @BRIEF Calculate safe batch_size and max_tokens for LLM translation calls based on actual content length and model context window limits.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [TranslationPreview:Module]\n# @RELATION DEPENDS_ON -> [TranslationExecutor:Module]\n# @RATIONALE: Prevents LLM truncation (finish_reason=length) by sizing batches within context limits.\n# DeepSeek v4 Flash supports up to 64K context window; output is limited by max_tokens.\n# @REJECTED: External tokenizer library — would introduce heavy dependency for estimation only.\n# Fixed batch_size of 50 — causes truncation on long-content rows.\n\n# #region DEFAULT_CONTEXT_WINDOW [TYPE Constant]\n# @BRIEF Default context window for DeepSeek v4 Flash: up to 64K tokens.\nDEFAULT_CONTEXT_WINDOW = 64000\n# #endregion DEFAULT_CONTEXT_WINDOW\n\n# #region DEFAULT_MAX_OUTPUT_TOKENS [TYPE Constant]\n# @BRIEF Default max_tokens setting for LLM output (8192 tokens).\nDEFAULT_MAX_OUTPUT_TOKENS = 8192\n# #endregion DEFAULT_MAX_OUTPUT_TOKENS\n\n# #region REASONING_OVERHEAD [TYPE Constant]\n# @BRIEF CoT reasoning overhead tokens for DeepSeek models (~2000 tokens for chain-of-thought).\nREASONING_OVERHEAD = 2000\n# #endregion REASONING_OVERHEAD\n\n# #region OUTPUT_PER_ROW_PER_LANG [TYPE Constant]\n# @BRIEF Estimated output tokens per row per language in JSON response format.\nOUTPUT_PER_ROW_PER_LANG = 60\n# #endregion OUTPUT_PER_ROW_PER_LANG\n\n# #region PROMPT_BASE_TOKENS [TYPE Constant]\n# @BRIEF Base tokens for system prompt + instructions + JSON format specification.\nPROMPT_BASE_TOKENS = 300\n# #endregion PROMPT_BASE_TOKENS\n\n# #region DICT_TOKENS_PER_ENTRY [TYPE Constant]\n# @BRIEF Estimated tokens per dictionary entry in the glossary section.\nDICT_TOKENS_PER_ENTRY = 20\n# #endregion DICT_TOKENS_PER_ENTRY\n\n# #region DICT_TOKENS_MAX [TYPE Constant]\n# @BRIEF Cap for dictionary tokens in estimation to avoid overestimating.\nDICT_TOKENS_MAX = 5000\n# #endregion DICT_TOKENS_MAX\n\n# #region CHARS_PER_TOKEN_MIXED [TYPE Constant]\n# @BRIEF Characters per token for mixed Russian/English text (empirical ~2.2 for mixed, ~2 for Russian, ~4 for English).\nCHARS_PER_TOKEN_MIXED = 2.2\n# #endregion CHARS_PER_TOKEN_MIXED\n\n# #region MIN_MAX_TOKENS [TYPE Constant]\n# @BRIEF Minimum max_tokens to allow (4096 ensures reasonable output even for small batches).\nMIN_MAX_TOKENS = 4096\n# #endregion MIN_MAX_TOKENS\n\n# #region MAX_OUTPUT_HEADROOM [TYPE Constant]\n# @BRIEF Extra headroom added to max_output_needed beyond the estimate (1000 tokens buffer).\nMAX_OUTPUT_HEADROOM = 1000\n# #endregion MAX_OUTPUT_HEADROOM\n\n\n# region _count_rows_that_fit [TYPE Function]\n# @BRIEF Count how many rows fit within the available input budget.\n# @PRE: input_per_row is non-empty; available_budget > 0.\n# @POST: Returns (safe_count, total_input_tokens).\ndef _count_rows_that_fit(\n input_per_row: list[int],\n available_budget: int,\n) -> tuple[int, int]:\n \"\"\"Count consecutive rows that fit within available_budget.\n\n Returns:\n (safe_count, total_input_tokens): Number of rows and their total tokens.\n \"\"\"\n running_total = 0\n safe_size = 0\n for tokens in input_per_row:\n if running_total + tokens + REASONING_OVERHEAD < available_budget:\n running_total += tokens\n safe_size += 1\n else:\n break\n safe_size = max(safe_size, 1)\n return safe_size, running_total\n# endregion _count_rows_that_fit\n\n\n# region estimate_token_budget [C:3] [TYPE Function]\n# @BRIEF Estimate token budget for a batch of source rows and return safe batch parameters.\n# @PRE: source_rows is a list of dicts (can be empty). target_languages is a non-empty list.\n# @POST: Returns dict with batch_size_adjusted, estimated_input_tokens, estimated_output_tokens, max_output_needed, warning.\n# @RATIONALE: Uses character-count heuristics (chars/2.2 for mixed text) since exact tokenization\n# depends on the LLM model. Estimates are intentionally conservative to prevent truncation.\n# @REJECTED: Using tiktoken or similar tokenizer — would introduce a heavy dependency and still\n# not match DeepSeek's tokenizer exactly.\ndef estimate_token_budget(\n source_rows: list[dict],\n target_languages: list[str],\n source_column: str = \"source_text\",\n context_columns: list[str] | None = None,\n dictionary_entries: list | None = None,\n batch_size: int | None = None,\n context_window: int = DEFAULT_CONTEXT_WINDOW,\n max_output_tokens: int = DEFAULT_MAX_OUTPUT_TOKENS,\n) -> dict:\n \"\"\"Estimate token budget and return safe batch parameters.\n\n Args:\n source_rows: List of row dicts with source text.\n target_languages: List of target language codes.\n source_column: Key for the source text in each row dict.\n context_columns: Optional list of keys for context columns.\n dictionary_entries: Optional list of dictionary entries for glossary.\n batch_size: Desired batch size. If None, auto-calculate max safe size.\n context_window: Model context window (default 64000 for DeepSeek v4 Flash).\n max_output_tokens: Hard max output tokens limit (default 8192).\n\n Returns:\n dict with:\n batch_size_adjusted: Safe batch size (may be less than requested).\n estimated_input_tokens: Total estimated input tokens for the batch.\n estimated_output_tokens: Total estimated output tokens.\n max_output_needed: Recommended max_tokens for this batch.\n warning: str | None if batch was reduced.\n \"\"\"\n if not target_languages:\n target_languages = [\"en\"]\n\n num_languages = len(target_languages)\n\n # 1. Estimate tokens per row\n input_per_row = []\n limit = batch_size if batch_size else len(source_rows)\n for i, row in enumerate(source_rows):\n if i >= limit:\n break\n text = str(row.get(source_column, \"\") or \"\")\n # Use ~2.2 chars per token for mixed Russian/English text\n estimated_tokens = max(1, int(len(text) / CHARS_PER_TOKEN_MIXED))\n # Add context columns\n if context_columns:\n for col in context_columns:\n val = str(row.get(col, \"\") or \"\")\n estimated_tokens += max(1, int(len(val) / CHARS_PER_TOKEN_MIXED))\n input_per_row.append(estimated_tokens)\n\n # 2. Calculate dictionary tokens\n dict_tokens = 0\n if dictionary_entries:\n dict_tokens = min(\n len(dictionary_entries) * DICT_TOKENS_PER_ENTRY,\n DICT_TOKENS_MAX,\n )\n\n prompt_tokens = PROMPT_BASE_TOKENS + dict_tokens\n available_input_budget = context_window - max_output_tokens\n\n # 3. Calculate safe batch size using shared helper\n safe_size, input_total = _count_rows_that_fit(input_per_row, available_input_budget)\n estimated_input = prompt_tokens + input_total\n\n # If batch_size was specified and we reduced it, recalculate\n if batch_size and safe_size < batch_size:\n # Ensure at minimum one row even for huge content\n safe_size = max(safe_size, 1)\n _, truncated_total = _count_rows_that_fit(\n input_per_row[:batch_size], available_input_budget,\n )\n estimated_input = prompt_tokens + truncated_total\n\n # 4. Estimate output tokens\n estimated_output = (\n safe_size * num_languages * OUTPUT_PER_ROW_PER_LANG + REASONING_OVERHEAD\n )\n\n # 5. Calculate recommended max_tokens\n max_output_needed = min(\n estimated_output + MAX_OUTPUT_HEADROOM,\n context_window - estimated_input,\n )\n max_output_needed = max(max_output_needed, MIN_MAX_TOKENS)\n max_output_needed = min(max_output_needed, max_output_tokens)\n\n # 6. Generate warning if batch was reduced\n warning = None\n if batch_size and safe_size < batch_size:\n total_estimated = estimated_input + max_output_needed\n warning = (\n f\"Reduced batch size from {batch_size} to {safe_size} \"\n f\"(estimated {total_estimated} tokens vs {context_window} window)\"\n )\n\n return {\n \"batch_size_adjusted\": safe_size,\n \"estimated_input_tokens\": estimated_input,\n \"estimated_output_tokens\": estimated_output,\n \"max_output_needed\": max_output_needed,\n \"warning\": warning,\n }\n# endregion estimate_token_budget\n# #endregion estimate_token_budget\n" - }, { "contract_id": "DEFAULT_CONTEXT_WINDOW", "contract_type": "Constant", @@ -49176,42 +48801,12 @@ "end_line": 14, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Default context window for DeepSeek v4 Flash: up to 64K tokens." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region DEFAULT_CONTEXT_WINDOW [TYPE Constant]\n# @BRIEF Default context window for DeepSeek v4 Flash: up to 64K tokens.\nDEFAULT_CONTEXT_WINDOW = 64000\n# #endregion DEFAULT_CONTEXT_WINDOW\n" + "body": "# #region DEFAULT_CONTEXT_WINDOW [TYPE Constant]\n\nDEFAULT_CONTEXT_WINDOW = 64000\n# #endregion DEFAULT_CONTEXT_WINDOW\n" }, { "contract_id": "DEFAULT_MAX_OUTPUT_TOKENS", @@ -49221,42 +48816,12 @@ "end_line": 19, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Default max_tokens setting for LLM output (8192 tokens)." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region DEFAULT_MAX_OUTPUT_TOKENS [TYPE Constant]\n# @BRIEF Default max_tokens setting for LLM output (8192 tokens).\nDEFAULT_MAX_OUTPUT_TOKENS = 8192\n# #endregion DEFAULT_MAX_OUTPUT_TOKENS\n" + "body": "# #region DEFAULT_MAX_OUTPUT_TOKENS [TYPE Constant]\n\nDEFAULT_MAX_OUTPUT_TOKENS = 16384\n# #endregion DEFAULT_MAX_OUTPUT_TOKENS\n" }, { "contract_id": "REASONING_OVERHEAD", @@ -49266,357 +48831,147 @@ "end_line": 24, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "CoT reasoning overhead tokens for DeepSeek models (~2000 tokens for chain-of-thought)." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region REASONING_OVERHEAD [TYPE Constant]\n# @BRIEF CoT reasoning overhead tokens for DeepSeek models (~2000 tokens for chain-of-thought).\nREASONING_OVERHEAD = 2000\n# #endregion REASONING_OVERHEAD\n" + "body": "# #region REASONING_OVERHEAD [TYPE Constant]\n\nREASONING_OVERHEAD = 2000\n# #endregion REASONING_OVERHEAD\n" + }, + { + "contract_id": "PROVIDER_DEFAULTS", + "contract_type": "Constant", + "file_path": "backend/src/plugins/translate/_token_budget.py", + "start_line": 26, + "end_line": 40, + "tier": "TIER_1", + "complexity": 1, + "metadata": {}, + "relations": [], + "schema_warnings": [], + "anchor_syntax": "region", + "tier_source": "AutoCalculated", + "body": "# #region PROVIDER_DEFAULTS [TYPE Constant]\n\n# Maps model name (or \"default\" fallback) to capacity limits.\n# @RATIONALE Different providers have drastically different context windows and\n# output limits. Using a single default for all causes either wasted\n# capacity (underestimation) or truncation (overestimation).\nPROVIDER_DEFAULTS: dict[str, dict[str, int]] = {\n \"gpt-4o-mini\": {\"context_window\": 128000, \"max_output_tokens\": 16384},\n \"gpt-4o\": {\"context_window\": 128000, \"max_output_tokens\": 16384},\n \"o1-mini\": {\"context_window\": 128000, \"max_output_tokens\": 65536},\n \"claude-3-5-sonnet\": {\"context_window\": 200000, \"max_output_tokens\": 8192},\n \"deepseek-v4-flash\": {\"context_window\": 64000, \"max_output_tokens\": 8192},\n \"default\": {\"context_window\": 64000, \"max_output_tokens\": 16384},\n}\n# #endregion PROVIDER_DEFAULTS\n" }, { "contract_id": "OUTPUT_PER_ROW_PER_LANG", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/_token_budget.py", - "start_line": 26, - "end_line": 29, + "start_line": 42, + "end_line": 46, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Estimated output tokens per row per language in JSON response format." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region OUTPUT_PER_ROW_PER_LANG [TYPE Constant]\n# @BRIEF Estimated output tokens per row per language in JSON response format.\nOUTPUT_PER_ROW_PER_LANG = 60\n# #endregion OUTPUT_PER_ROW_PER_LANG\n" + "body": "# #region OUTPUT_PER_ROW_PER_LANG [TYPE Constant]\n\n# Increased from 60 to 120 because SQL/dashboard text and JSON structure need more.\nOUTPUT_PER_ROW_PER_LANG = 120\n# #endregion OUTPUT_PER_ROW_PER_LANG\n" + }, + { + "contract_id": "JSON_OVERHEAD_PER_ROW", + "contract_type": "Constant", + "file_path": "backend/src/plugins/translate/_token_budget.py", + "start_line": 48, + "end_line": 51, + "tier": "TIER_1", + "complexity": 1, + "metadata": {}, + "relations": [], + "schema_warnings": [], + "anchor_syntax": "region", + "tier_source": "AutoCalculated", + "body": "# #region JSON_OVERHEAD_PER_ROW [TYPE Constant]\n\nJSON_OVERHEAD_PER_ROW = 50\n# #endregion JSON_OVERHEAD_PER_ROW\n" }, { "contract_id": "PROMPT_BASE_TOKENS", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/_token_budget.py", - "start_line": 31, - "end_line": 34, + "start_line": 53, + "end_line": 57, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Base tokens for system prompt + instructions + JSON format specification." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region PROMPT_BASE_TOKENS [TYPE Constant]\n# @BRIEF Base tokens for system prompt + instructions + JSON format specification.\nPROMPT_BASE_TOKENS = 300\n# #endregion PROMPT_BASE_TOKENS\n" + "body": "# #region PROMPT_BASE_TOKENS [TYPE Constant]\n\n# Increased from 300 to 600 to account for longer template, system msg, and dict preamble.\nPROMPT_BASE_TOKENS = 600\n# #endregion PROMPT_BASE_TOKENS\n" }, { "contract_id": "DICT_TOKENS_PER_ENTRY", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/_token_budget.py", - "start_line": 36, - "end_line": 39, + "start_line": 59, + "end_line": 62, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Estimated tokens per dictionary entry in the glossary section." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region DICT_TOKENS_PER_ENTRY [TYPE Constant]\n# @BRIEF Estimated tokens per dictionary entry in the glossary section.\nDICT_TOKENS_PER_ENTRY = 20\n# #endregion DICT_TOKENS_PER_ENTRY\n" + "body": "# #region DICT_TOKENS_PER_ENTRY [TYPE Constant]\n\nDICT_TOKENS_PER_ENTRY = 20\n# #endregion DICT_TOKENS_PER_ENTRY\n" }, { "contract_id": "DICT_TOKENS_MAX", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/_token_budget.py", - "start_line": 41, - "end_line": 44, + "start_line": 64, + "end_line": 67, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Cap for dictionary tokens in estimation to avoid overestimating." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region DICT_TOKENS_MAX [TYPE Constant]\n# @BRIEF Cap for dictionary tokens in estimation to avoid overestimating.\nDICT_TOKENS_MAX = 5000\n# #endregion DICT_TOKENS_MAX\n" + "body": "# #region DICT_TOKENS_MAX [TYPE Constant]\n\nDICT_TOKENS_MAX = 5000\n# #endregion DICT_TOKENS_MAX\n" }, { "contract_id": "CHARS_PER_TOKEN_MIXED", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/_token_budget.py", - "start_line": 46, - "end_line": 49, + "start_line": 69, + "end_line": 72, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Characters per token for mixed Russian/English text (empirical ~2.2 for mixed, ~2 for Russian, ~4 for English)." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region CHARS_PER_TOKEN_MIXED [TYPE Constant]\n# @BRIEF Characters per token for mixed Russian/English text (empirical ~2.2 for mixed, ~2 for Russian, ~4 for English).\nCHARS_PER_TOKEN_MIXED = 2.2\n# #endregion CHARS_PER_TOKEN_MIXED\n" + "body": "# #region CHARS_PER_TOKEN_MIXED [TYPE Constant]\n\nCHARS_PER_TOKEN_MIXED = 2.2\n# #endregion CHARS_PER_TOKEN_MIXED\n" }, { "contract_id": "MIN_MAX_TOKENS", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/_token_budget.py", - "start_line": 51, - "end_line": 54, + "start_line": 74, + "end_line": 77, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Minimum max_tokens to allow (4096 ensures reasonable output even for small batches)." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region MIN_MAX_TOKENS [TYPE Constant]\n# @BRIEF Minimum max_tokens to allow (4096 ensures reasonable output even for small batches).\nMIN_MAX_TOKENS = 4096\n# #endregion MIN_MAX_TOKENS\n" + "body": "# #region MIN_MAX_TOKENS [TYPE Constant]\n\nMIN_MAX_TOKENS = 4096\n# #endregion MIN_MAX_TOKENS\n" }, { "contract_id": "MAX_OUTPUT_HEADROOM", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/_token_budget.py", - "start_line": 56, - "end_line": 59, + "start_line": 79, + "end_line": 83, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Extra headroom added to max_output_needed beyond the estimate (1000 tokens buffer)." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region MAX_OUTPUT_HEADROOM [TYPE Constant]\n# @BRIEF Extra headroom added to max_output_needed beyond the estimate (1000 tokens buffer).\nMAX_OUTPUT_HEADROOM = 1000\n# #endregion MAX_OUTPUT_HEADROOM\n" + "body": "# #region MAX_OUTPUT_HEADROOM [TYPE Constant]\n\n# Increased from 1000 to 3000 because SQL/dashboard text output varies significantly.\nMAX_OUTPUT_HEADROOM = 3000\n# #endregion MAX_OUTPUT_HEADROOM\n" }, { "contract_id": "DictionaryUtils", @@ -49672,104 +49027,12 @@ "tier_source": "AutoCalculated", "body": "# #region _detect_delimiter [TYPE Function]\n# @BRIEF Detect the delimiter used in a CSV/TSV header line.\ndef _detect_delimiter(header_line: str) -> str:\n \"\"\"Detect delimiter by counting tabs vs commas in the first line.\"\"\"\n if not header_line:\n return \",\"\n tab_count = header_line.count(\"\\t\")\n comma_count = header_line.count(\",\")\n return \"\\t\" if tab_count > comma_count else \",\"\n# #endregion _detect_delimiter\n" }, - { - "contract_id": "DictionaryManagerModule", - "contract_type": "Module", - "file_path": "backend/src/plugins/translate/dictionary.py", - "start_line": 1, - "end_line": 1002, - "tier": "TIER_2", - "complexity": 4, - "metadata": { - "COMPLEXITY": 4, - "LAYER": "Domain", - "PURPOSE": "Business logic for terminology dictionary management, entry CRUD, CSV/TSV import with conflict detection, and per-batch filtering.", - "RATIONALE": "C4 complexity because dictionary CRUD is stateful with referential integrity enforcement on deletion and unique constraint on normalized source term.", - "REJECTED": "\"Keep both\" as conflict option — UniqueConstraint(dictionary_id, source_term_normalized) prohibits variants; only overwrite/keep existing." - }, - "relations": [ - { - "source_id": "DictionaryManagerModule", - "relation_type": "DEPENDS_ON", - "target_id": "TerminologyDictionary:Class", - "target_ref": "[TerminologyDictionary:Class]" - }, - { - "source_id": "DictionaryManagerModule", - "relation_type": "DEPENDS_ON", - "target_id": "DictionaryEntry:Class", - "target_ref": "[DictionaryEntry:Class]" - }, - { - "source_id": "DictionaryManagerModule", - "relation_type": "DEPENDS_ON", - "target_id": "TranslationJobDictionary:Class", - "target_ref": "[TranslationJobDictionary:Class]" - }, - { - "source_id": "DictionaryManagerModule", - "relation_type": "DEPENDS_ON", - "target_id": "TranslationJob:Class", - "target_ref": "[TranslationJob:Class]" - } - ], - "schema_warnings": [ - { - "code": "missing_required_tag", - "tag": "POST", - "message": "@POST is required for contract type 'Module' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Module" - } - }, - { - "code": "missing_required_tag", - "tag": "PRE", - "message": "@PRE is required for contract type 'Module' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Module" - } - }, - { - "code": "missing_required_tag", - "tag": "SIDE_EFFECT", - "message": "@SIDE_EFFECT is required for contract type 'Module' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Module" - } - } - ], - "anchor_syntax": "region", - "tier_source": "AutoCalculated", - "body": "# #region DictionaryManagerModule [C:4] [TYPE Module] [SEMANTICS sqlalchemy, translate, dictionary, batch, term]\n# @BRIEF Business logic for terminology dictionary management, entry CRUD, CSV/TSV import with conflict detection, and per-batch filtering.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [TerminologyDictionary:Class]\n# @RELATION DEPENDS_ON -> [DictionaryEntry:Class]\n# @RELATION DEPENDS_ON -> [TranslationJobDictionary:Class]\n# @RELATION DEPENDS_ON -> [TranslationJob:Class]\n# @RATIONALE: C4 complexity because dictionary CRUD is stateful with referential integrity enforcement on deletion and unique constraint on normalized source term.\n# @REJECTED: Pure C3 CRUD without state guards would allow orphaned job-dictionary links.\n# @REJECTED: \"Keep both\" as conflict option — UniqueConstraint(dictionary_id, source_term_normalized) prohibits variants; only overwrite/keep existing.\n\nimport csv\nimport io\nimport re\nfrom typing import Any\n\nfrom sqlalchemy import func, or_\nfrom sqlalchemy.orm import Session\n\nfrom ...core.logger import belief_scope, logger\nfrom ...models.translate import (\n DictionaryEntry,\n TerminologyDictionary,\n TranslationJob,\n TranslationJobDictionary,\n)\nfrom ._utils import _detect_delimiter, _normalize_term\nfrom .prompt_builder import ContextAwarePromptBuilder\n\n\n# #region _validate_bcp47 [C:2] [TYPE Function] [SEMANTICS validation, bcp47, language]\n# @BRIEF Validate that a language tag is a non-empty BCP-47 string.\ndef _validate_bcp47(tag: str, field_name: str) -> None:\n \"\"\"Validate that tag is a non-empty BCP-47 string (basic check).\"\"\"\n if not tag or not tag.strip():\n raise ValueError(f\"{field_name} must be a non-empty BCP-47 language tag\")\n tag = tag.strip()\n # Basic BCP-47 validation: must match lang[-script][-region][-variant]* pattern\n import re as _re\n if not _re.match(r'^[a-zA-Z]{2,8}(-[a-zA-Z0-9]{1,8})*$', tag):\n raise ValueError(\n f\"{field_name} is not a valid BCP-47 tag: '{tag}'. \"\n \"Expected format like 'en', 'ru', 'zh-CN', 'pt-BR'.\"\n )\n# #endregion _validate_bcp47\n\n\n# #region DictionaryManager [C:4] [TYPE Class]\n# @BRIEF Manages terminology dictionaries and their entries with referential integrity.\n# @PRE: Database session is open and valid.\n# @POST: Dictionary and entry mutations are persisted with conflict detection.\n# @SIDE_EFFECT: Creates, updates, deletes TerminologyDictionary and DictionaryEntry rows; enforces deletion guards.\nclass DictionaryManager:\n # region DictionaryManager.create_dictionary [TYPE Function]\n # @PURPOSE: Create a new terminology dictionary (language independent at dictionary level; language pairs are per-entry).\n # @PRE: name is provided.\n # @POST: New TerminologyDictionary row is created and returned.\n @staticmethod\n def create_dictionary(\n db: Session, name: str,\n source_dialect: str = \"\",\n target_dialect: str = \"\",\n created_by: str | None = None, description: str | None = None,\n is_active: bool = True,\n ) -> TerminologyDictionary:\n with belief_scope(\"DictionaryManager.create_dictionary\"):\n logger.reason(\"Creating dictionary\", {\"name\": name, \"source\": source_dialect, \"target\": target_dialect})\n dictionary = TerminologyDictionary(\n name=name,\n description=description,\n source_dialect=source_dialect or \"\",\n target_dialect=target_dialect or \"\",\n is_active=is_active,\n created_by=created_by,\n )\n db.add(dictionary)\n db.commit()\n db.refresh(dictionary)\n logger.reflect(\"Dictionary created\", {\"id\": dictionary.id})\n return dictionary\n # endregion DictionaryManager.create_dictionary\n\n # region DictionaryManager.update_dictionary [TYPE Function]\n # @PURPOSE: Update an existing terminology dictionary.\n # @PRE: dict_id exists in terminology_dictionaries table.\n # @POST: Dictionary metadata is updated and returned.\n @staticmethod\n def update_dictionary(\n db: Session, dict_id: str, name: str | None = None,\n description: str | None = None, source_dialect: str | None = None,\n target_dialect: str | None = None, is_active: bool | None = None,\n ) -> TerminologyDictionary:\n with belief_scope(\"DictionaryManager.update_dictionary\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n logger.reason(\"Updating dictionary\", {\"id\": dict_id})\n if name is not None:\n dictionary.name = name\n if description is not None:\n dictionary.description = description\n if source_dialect is not None:\n dictionary.source_dialect = source_dialect\n if target_dialect is not None:\n dictionary.target_dialect = target_dialect\n if is_active is not None:\n dictionary.is_active = is_active\n db.commit()\n db.refresh(dictionary)\n logger.reflect(\"Dictionary updated\", {\"id\": dictionary.id})\n return dictionary\n # endregion DictionaryManager.update_dictionary\n\n # region DictionaryManager.delete_dictionary [TYPE Function]\n # @PURPOSE: Delete a dictionary, blocked if attached to active/scheduled jobs.\n # @PRE: dict_id exists.\n # @POST: Dictionary and its entries are deleted, unless attached to ACTIVE/READY/RUNNING/SCHEDULED jobs.\n # @SIDE_EFFECT: Deletes TerminologyDictionary row and all DictionaryEntry rows.\n @staticmethod\n def delete_dictionary(db: Session, dict_id: str) -> None:\n with belief_scope(\"DictionaryManager.delete_dictionary\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n\n # Check for attached active/scheduled jobs\n blocked_statuses = [\"ACTIVE\", \"READY\", \"RUNNING\", \"SCHEDULED\"]\n attached_jobs = (\n db.query(TranslationJobDictionary)\n .filter(\n TranslationJobDictionary.dictionary_id == dict_id,\n TranslationJobDictionary.job_id.in_(\n db.query(TranslationJob.id).filter(\n TranslationJob.status.in_(blocked_statuses)\n )\n ),\n )\n .count()\n )\n\n if attached_jobs > 0:\n logger.explore(\"Delete blocked: dictionary attached to active jobs\", {\"dict_id\": dict_id, \"jobs\": attached_jobs})\n raise ValueError(\n f\"Cannot delete dictionary attached to {attached_jobs} active/scheduled job(s). \"\n \"Remove dictionary associations from jobs first.\"\n )\n\n logger.reason(\"Deleting dictionary\", {\"id\": dict_id})\n # Delete entries and job-dictionary links first\n db.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).delete()\n db.query(TranslationJobDictionary).filter(TranslationJobDictionary.dictionary_id == dict_id).delete()\n db.delete(dictionary)\n db.commit()\n logger.reflect(\"Dictionary deleted\", {\"id\": dict_id})\n # endregion DictionaryManager.delete_dictionary\n\n # region DictionaryManager.get_dictionary [TYPE Function]\n # @PURPOSE: Get a single dictionary by ID with entry count.\n # @PRE: dict_id exists.\n # @POST: Returns dict with dictionary + entry_count or raises ValueError.\n @staticmethod\n def get_dictionary(db: Session, dict_id: str) -> TerminologyDictionary:\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n return dictionary\n # endregion DictionaryManager.get_dictionary\n\n # region DictionaryManager.list_dictionaries [TYPE Function]\n # @PURPOSE: List dictionaries with pagination and entry counts.\n # @PRE: page >= 1, page_size between 1 and 100.\n # @POST: Returns (list of dicts, total_count).\n @staticmethod\n def list_dictionaries(\n db: Session, page: int = 1, page_size: int = 20,\n ) -> tuple[list[TerminologyDictionary], int]:\n total = db.query(func.count(TerminologyDictionary.id)).scalar() or 0\n dictionaries = (\n db.query(TerminologyDictionary)\n .order_by(TerminologyDictionary.created_at.desc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n return dictionaries, total\n # endregion DictionaryManager.list_dictionaries\n\n # region DictionaryManager.add_entry [TYPE Function]\n # @PURPOSE: Add an entry to a dictionary with language-pair-aware uniqueness validation.\n # @PRE: dict_id exists. source_term, target_term are non-empty. source_language, target_language are valid BCP-47.\n # @POST: New DictionaryEntry row is created or raises on duplicate.\n @staticmethod\n def add_entry(\n db: Session, dict_id: str, source_term: str, target_term: str,\n context_notes: str | None = None,\n source_language: str = \"und\",\n target_language: str = \"und\",\n ) -> DictionaryEntry:\n with belief_scope(\"DictionaryManager.add_entry\"):\n _validate_bcp47(source_language, \"source_language\")\n _validate_bcp47(target_language, \"target_language\")\n\n normalized = _normalize_term(source_term)\n # Uniqueness is now (dictionary_id, source_term_normalized, source_language, target_language)\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == source_language,\n DictionaryEntry.target_language == target_language,\n )\n .first()\n )\n if existing:\n raise ValueError(\n f\"Duplicate entry: '{source_term}' (lang: {source_language}→{target_language}) \"\n f\"already exists in this dictionary (id={existing.id}). \"\n \"Use overwrite or keep_existing conflict mode.\"\n )\n\n logger.reason(\"Adding dictionary entry\", {\"dict_id\": dict_id, \"term\": source_term, \"src_lang\": source_language, \"tgt_lang\": target_language})\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term.strip(),\n source_term_normalized=normalized,\n target_term=target_term.strip(),\n context_notes=context_notes.strip() if context_notes else None,\n source_language=source_language.strip(),\n target_language=target_language.strip(),\n )\n db.add(entry)\n db.commit()\n db.refresh(entry)\n logger.reflect(\"Entry added\", {\"entry_id\": entry.id, \"src_lang\": source_language, \"tgt_lang\": target_language})\n return entry\n # endregion DictionaryManager.add_entry\n\n # region DictionaryManager.edit_entry [TYPE Function]\n # @PURPOSE: Edit an existing dictionary entry with language-pair-aware duplicate check.\n # @PRE: entry_id exists.\n # @POST: Entry fields are updated.\n @staticmethod\n def edit_entry(\n db: Session, entry_id: str, source_term: str | None = None,\n target_term: str | None = None, context_notes: str | None = None,\n source_language: str | None = None,\n target_language: str | None = None,\n ) -> DictionaryEntry:\n with belief_scope(\"DictionaryManager.edit_entry\"):\n entry = db.query(DictionaryEntry).filter(DictionaryEntry.id == entry_id).first()\n if not entry:\n raise ValueError(f\"Entry not found: {entry_id}\")\n\n logger.reason(\"Editing dictionary entry\", {\"entry_id\": entry_id})\n\n if source_language is not None:\n _validate_bcp47(source_language, \"source_language\")\n entry.source_language = source_language.strip()\n if target_language is not None:\n _validate_bcp47(target_language, \"target_language\")\n entry.target_language = target_language.strip()\n\n if source_term is not None:\n normalized = _normalize_term(source_term)\n # Check uniqueness within the same dictionary + language pair\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == entry.dictionary_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == entry.source_language,\n DictionaryEntry.target_language == entry.target_language,\n DictionaryEntry.id != entry_id,\n )\n .first()\n )\n if existing:\n raise ValueError(\n f\"Duplicate entry: '{source_term}' already exists in this dictionary \"\n f\"(id={existing.id}).\"\n )\n entry.source_term = source_term.strip()\n entry.source_term_normalized = normalized\n if target_term is not None:\n entry.target_term = target_term.strip()\n if context_notes is not None:\n entry.context_notes = context_notes.strip() if context_notes else None\n db.commit()\n db.refresh(entry)\n logger.reflect(\"Entry updated\", {\"entry_id\": entry.id})\n return entry\n # endregion DictionaryManager.edit_entry\n\n # region DictionaryManager.delete_entry [TYPE Function]\n # @PURPOSE: Delete a single dictionary entry.\n # @PRE: entry_id exists.\n # @POST: Entry is deleted.\n @staticmethod\n def delete_entry(db: Session, entry_id: str) -> None:\n with belief_scope(\"DictionaryManager.delete_entry\"):\n entry = db.query(DictionaryEntry).filter(DictionaryEntry.id == entry_id).first()\n if not entry:\n raise ValueError(f\"Entry not found: {entry_id}\")\n logger.reason(\"Deleting dictionary entry\", {\"entry_id\": entry_id})\n db.delete(entry)\n db.commit()\n logger.reflect(\"Entry deleted\", {\"entry_id\": entry_id})\n # endregion DictionaryManager.delete_entry\n\n # region DictionaryManager.clear_entries [TYPE Function]\n # @PURPOSE: Delete all entries for a dictionary.\n # @PRE: dict_id exists.\n # @POST: All entries for the dictionary are deleted.\n @staticmethod\n def clear_entries(db: Session, dict_id: str) -> int:\n with belief_scope(\"DictionaryManager.clear_entries\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n logger.reason(\"Clearing all entries\", {\"dict_id\": dict_id})\n deleted = db.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).delete()\n db.commit()\n logger.reflect(\"Entries cleared\", {\"dict_id\": dict_id, \"count\": deleted})\n return deleted\n # endregion DictionaryManager.clear_entries\n\n # region DictionaryManager.list_entries [TYPE Function]\n # @PURPOSE: List entries for a dictionary with pagination.\n # @PRE: dict_id exists.\n # @POST: Returns (list of entries, total_count).\n @staticmethod\n def list_entries(\n db: Session, dict_id: str, page: int = 1, page_size: int = 50,\n ) -> tuple[list[DictionaryEntry], int]:\n total = (\n db.query(func.count(DictionaryEntry.id))\n .filter(DictionaryEntry.dictionary_id == dict_id)\n .scalar()\n or 0\n )\n entries = (\n db.query(DictionaryEntry)\n .filter(DictionaryEntry.dictionary_id == dict_id)\n .order_by(DictionaryEntry.source_term.asc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n return entries, total\n # endregion DictionaryManager.list_entries\n\n # region DictionaryManager.import_entries [TYPE Function]\n # @PURPOSE: Import entries from CSV/TSV content with duplicate detection and conflict resolution.\n # @PRE: content is valid CSV or TSV. dict_id exists.\n # @POST: Entries are created/updated/skipped per conflict mode. Returns result summary.\n # @SIDE_EFFECT: Batch-inserts or updates DictionaryEntry rows.\n @staticmethod\n def import_entries(\n db: Session, dict_id: str, content: str,\n delimiter: str | None = None,\n on_conflict: str = \"overwrite\",\n preview_only: bool = False,\n default_source_language: str | None = None,\n default_target_language: str | None = None,\n ) -> dict[str, Any]:\n with belief_scope(\"DictionaryManager.import_entries\"):\n # Validate dictionary\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n\n # Detect delimiter if not specified\n if not delimiter:\n delimiter = _detect_delimiter(content)\n logger.reason(\"Detected delimiter\", {\"delimiter\": repr(delimiter)})\n\n if delimiter not in (\",\", \"\\t\"):\n raise ValueError(f\"Unsupported delimiter: {delimiter!r}. Use ',' or '\\\\t'.\")\n\n # Parse content\n reader = csv.DictReader(io.StringIO(content), delimiter=delimiter)\n required_fields = {\"source_term\", \"target_term\"}\n if not reader.fieldnames or not required_fields.issubset(reader.fieldnames):\n raise ValueError(\n f\"CSV/TSV must have at least 'source_term' and 'target_term' columns. \"\n f\"Got: {reader.fieldnames}\"\n )\n\n result: dict[str, Any] = {\n \"total\": 0,\n \"created\": 0,\n \"updated\": 0,\n \"skipped\": 0,\n \"errors\": [],\n \"preview\": [],\n }\n\n rows = list(reader)\n result[\"total\"] = len(rows)\n\n for row_idx, row in enumerate(rows):\n try:\n source_term = row.get(\"source_term\", \"\").strip()\n target_term = row.get(\"target_term\", \"\").strip()\n context_notes = row.get(\"context_notes\", \"\").strip() or None\n source_language = row.get(\"source_language\", \"\").strip() or default_source_language or \"und\"\n target_language = row.get(\"target_language\", \"\").strip() or default_target_language or \"und\"\n\n if not source_term or not target_term:\n result[\"errors\"].append({\n \"line\": row_idx + 2, # +2 for header and 1-indexed\n \"error\": \"Both source_term and target_term are required\",\n \"row\": row,\n })\n continue\n\n normalized = _normalize_term(source_term)\n\n if preview_only:\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == source_language,\n DictionaryEntry.target_language == target_language,\n )\n .first()\n )\n preview_row = {\n \"line\": row_idx + 2,\n \"source_term\": source_term,\n \"target_term\": target_term,\n \"context_notes\": context_notes,\n \"source_language\": source_language,\n \"target_language\": target_language,\n \"is_conflict\": existing is not None,\n \"existing_target_term\": existing.target_term if existing else None,\n }\n result[\"preview\"].append(preview_row)\n continue\n\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == source_language,\n DictionaryEntry.target_language == target_language,\n )\n .first()\n )\n\n if existing:\n if on_conflict == \"overwrite\":\n existing.source_term = source_term\n existing.target_term = target_term\n existing.context_notes = context_notes\n existing.source_language = source_language\n existing.target_language = target_language\n result[\"updated\"] += 1\n elif on_conflict == \"keep_existing\":\n result[\"skipped\"] += 1\n else: # cancel\n result[\"errors\"].append({\n \"line\": row_idx + 2,\n \"error\": f\"Conflict on '{source_term}' and on_conflict='cancel'\",\n \"row\": row,\n })\n continue\n else:\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term,\n source_term_normalized=normalized,\n target_term=target_term,\n context_notes=context_notes,\n source_language=source_language,\n target_language=target_language,\n )\n db.add(entry)\n result[\"created\"] += 1\n\n except Exception as e:\n result[\"errors\"].append({\n \"line\": row_idx + 2,\n \"error\": str(e),\n \"row\": row,\n })\n\n if not preview_only:\n db.commit()\n\n logger.reflect(\"Import complete\", {\n \"dict_id\": dict_id,\n \"total\": result[\"total\"],\n \"created\": result[\"created\"],\n \"updated\": result[\"updated\"],\n \"skipped\": result[\"skipped\"],\n \"errors\": len(result[\"errors\"]),\n })\n return result\n # endregion DictionaryManager.import_entries\n\n # region DictionaryManager.export_entries [TYPE Function]\n # @PURPOSE: Export all entries as CSV string with language columns.\n # @PRE: dict_id exists.\n # @POST: Returns CSV string with header: source_term, target_term, source_language, target_language, context_notes, context_data, usage_notes.\n @staticmethod\n def export_entries(\n db: Session, dict_id: str, delimiter: str = \",\",\n ) -> str:\n with belief_scope(\"DictionaryManager.export_entries\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n\n entries = (\n db.query(DictionaryEntry)\n .filter(DictionaryEntry.dictionary_id == dict_id)\n .order_by(DictionaryEntry.source_term.asc())\n .all()\n )\n\n output = io.StringIO()\n fieldnames = [\n \"source_term\", \"target_term\",\n \"source_language\", \"target_language\",\n \"context_notes\", \"context_data\", \"usage_notes\",\n ]\n writer = csv.DictWriter(output, fieldnames=fieldnames, delimiter=delimiter)\n writer.writeheader()\n\n for entry in entries:\n writer.writerow({\n \"source_term\": entry.source_term,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes or \"\",\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes or \"\",\n })\n\n result = output.getvalue()\n logger.reflect(\"Entries exported\", {\"dict_id\": dict_id, \"count\": len(entries), \"delimiter\": repr(delimiter)})\n return result\n # endregion DictionaryManager.export_entries\n\n # region DictionaryManager.migrate_old_entries [TYPE Function]\n # @PURPOSE: Migrate existing single-language dictionary entries to populate source_language and target_language.\n # @PRE: db session is open.\n # @POST: Entries with \"und\" source_language or target_language are updated with inferred values.\n # @SIDE_EFFECT: Updates DictionaryEntry rows in bulk.\n @staticmethod\n def migrate_old_entries(db: Session) -> dict[str, int]:\n with belief_scope(\"DictionaryManager.migrate_old_entries\"):\n logger.reason(\"Starting migration of old dictionary entries\")\n migrated_source = 0\n migrated_target = 0\n skipped = 0\n\n # Find all entries that may need migration (source_language is \"und\" or target_language is \"und\")\n entries = (\n db.query(DictionaryEntry)\n .filter(\n (DictionaryEntry.source_language == \"und\") |\n (DictionaryEntry.target_language == \"und\")\n )\n .all()\n )\n\n for entry in entries:\n # Try to infer source_language from origin_source_language\n if entry.source_language == \"und\":\n if entry.origin_source_language:\n entry.source_language = entry.origin_source_language\n migrated_source += 1\n\n # Note: target_language inference from dictionary-level column was removed\n # because TerminologyDictionary.target_language was deprecated and dropped.\n\n if entry.source_language == \"und\" and entry.target_language == \"und\":\n skipped += 1\n\n db.commit()\n\n logger.reflect(\"Migration complete\", {\n \"migrated_source\": migrated_source,\n \"migrated_target\": migrated_target,\n \"skipped\": skipped,\n \"total_processed\": len(entries),\n })\n return {\n \"migrated_source\": migrated_source,\n \"migrated_target\": migrated_target,\n \"skipped\": skipped,\n \"total_processed\": len(entries),\n }\n # endregion DictionaryManager.migrate_old_entries\n\n # region DictionaryManager.filter_for_batch [TYPE Function]\n # @PURPOSE: Scan batch texts for case-insensitive, word-boundary-aware matches against all dictionaries attached to a job,\n # optionally filtered by language pair. When row_context is provided, computes context-aware priority flags.\n # @PRE: job_id exists and source_texts is a list of strings.\n # @POST: Returns list of matched entries with match info, sorted by priority across attached dictionaries.\n # When row_context is given, each result includes a 'priority_match' boolean.\n # @SIDE_EFFECT: Queries TranslationJobDictionary, TerminologyDictionary, and DictionaryEntry tables.\n @staticmethod\n def filter_for_batch(\n db: Session, source_texts: list[str], job_id: str,\n source_language: str | None = None,\n target_language: str | None = None,\n row_context: dict | None = None,\n ) -> list[dict[str, Any]]:\n with belief_scope(\"DictionaryManager.filter_for_batch\"):\n # Get dictionaries attached to this job\n job_dict_links = (\n db.query(TranslationJobDictionary)\n .filter(TranslationJobDictionary.job_id == job_id)\n .all()\n )\n if not job_dict_links:\n logger.reason(\"No dictionaries attached to job\", {\"job_id\": job_id})\n return []\n\n dict_ids = [jd.dictionary_id for jd in job_dict_links]\n\n # Get all active dictionaries\n dictionaries = (\n db.query(TerminologyDictionary)\n .filter(\n TerminologyDictionary.id.in_(dict_ids),\n TerminologyDictionary.is_active == True,\n )\n .all()\n )\n if not dictionaries:\n return []\n\n # Build dict_id -> dict_name lookup\n dict_map = {d.id: d.name for d in dictionaries}\n\n # Fetch all entries for these dictionaries, ordered by dictionary (priority order from link order)\n all_entries = (\n db.query(DictionaryEntry)\n .filter(DictionaryEntry.dictionary_id.in_(dict_ids))\n .order_by(DictionaryEntry.source_term.asc())\n .all()\n )\n\n if not all_entries:\n return []\n\n # PERFORMANCE NOTE: O(rows × entries) regex matching per batch.\n # Acceptable for current scale (50-100 rows × <1000 entries).\n # For larger batches (>500 rows or >5000 entries), consider\n # replacing with an Aho-Corasick automaton (e.g., pyahocorasick).\n matched: list[dict[str, Any]] = []\n seen_source_match: set = set()\n\n for text_idx, source_text in enumerate(source_texts):\n if not source_text:\n continue\n\n text_lower = source_text.lower()\n\n for entry in all_entries:\n norm = entry.source_term_normalized\n if not norm:\n continue\n\n # Apply language pair filtering\n if source_language is not None:\n src = source_language.strip().lower()\n entry_src = entry.source_language.strip().lower()\n if entry_src != src and entry_src != \"und\":\n continue\n if target_language is not None:\n tgt = target_language.strip().lower()\n entry_tgt = entry.target_language.strip().lower()\n if entry_tgt != tgt:\n continue\n\n # Word-boundary-aware matching\n # Build pattern: \\bterm\\b (case-insensitive)\n # Escape regex special chars in the search term\n escaped = re.escape(norm)\n pattern = re.compile(r\"\\b\" + escaped + r\"\\b\", re.IGNORECASE)\n\n if pattern.search(text_lower):\n match_key = (text_idx, entry.id)\n if match_key not in seen_source_match:\n seen_source_match.add(match_key)\n\n # Compute context-aware priority if row_context is available\n priority_match = False\n if row_context and entry.has_context and entry.context_data:\n similarity = ContextAwarePromptBuilder.compute_context_similarity(\n entry.context_data, row_context\n )\n priority_match = similarity >= 0.5\n\n matched.append({\n \"text_index\": text_idx,\n \"source_text\": source_text,\n \"entry_id\": entry.id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"dictionary_id\": entry.dictionary_id,\n \"dictionary_name\": dict_map.get(entry.dictionary_id, \"\"),\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"usage_notes\": entry.usage_notes,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"priority_match\": priority_match,\n })\n\n # Sort by dictionary link priority (order of dict_ids from link order)\n dict_priority = {d_id: idx for idx, d_id in enumerate(dict_ids)}\n matched.sort(key=lambda m: (dict_priority.get(m[\"dictionary_id\"], 999), m[\"text_index\"]))\n\n logger.reflect(\"Batch filter match complete\", {\n \"job_id\": job_id,\n \"source_texts\": len(source_texts),\n \"matches\": len(matched),\n \"dictionaries_used\": len(dictionaries),\n \"source_language\": source_language,\n \"target_language\": target_language,\n })\n return matched\n # endregion DictionaryManager.filter_for_batch\n\n\n # region DictionaryManager.submit_correction [TYPE Function]\n # @PURPOSE: Submit a term correction from a run result. Validates language match, detects conflicts.\n # @PRE: source_term, incorrect_target_term, corrected_target_term are non-empty. dict_id exists.\n # @POST: Entry created or updated; origin tracking populated. Returns action + conflict info.\n # @SIDE_EFFECT: Creates/updates DictionaryEntry row.\n @staticmethod\n def submit_correction(\n db: Session,\n dict_id: str,\n source_term: str,\n incorrect_target_term: str,\n corrected_target_term: str,\n origin_run_id: str | None = None,\n origin_row_key: str | None = None,\n origin_user_id: str | None = None,\n on_conflict: str = \"overwrite\",\n context_data: dict[str, Any] | None = None,\n usage_notes: str | None = None,\n keep_context: bool = True,\n ) -> dict[str, Any]:\n with belief_scope(\"DictionaryManager.submit_correction\"):\n # Validate dictionary exists\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary '{dict_id}' not found\")\n\n # Handle keep_context=False (user explicitly removed context)\n effective_context = context_data\n effective_context_source = \"auto\"\n if not keep_context:\n effective_context = None\n effective_context_source = \"manual\"\n\n normalized = _normalize_term(source_term)\n entry_src_lang = dictionary.source_dialect or \"und\"\n entry_tgt_lang = dictionary.target_dialect or \"und\"\n # Find existing entry: match exact language pair OR old-style \"und\"/\"und\" entries\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n or_(\n # Exact language pair match\n (DictionaryEntry.source_language == entry_src_lang) &\n (DictionaryEntry.target_language == entry_tgt_lang),\n # Old-style entries without language pair\n (DictionaryEntry.source_language == \"und\") &\n (DictionaryEntry.target_language == \"und\"),\n ),\n )\n .first()\n )\n\n result: dict[str, Any] = {\n \"source_term\": source_term,\n \"target_term\": corrected_target_term,\n \"action\": \"created\",\n \"entry_id\": None,\n \"conflict\": None,\n \"message\": None,\n }\n\n if existing:\n # Conflict detected\n if on_conflict == \"keep_existing\":\n result[\"action\"] = \"conflict_detected\"\n result[\"conflict\"] = {\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target_term,\n \"action\": \"keep_existing\",\n }\n result[\"message\"] = f\"Existing entry kept: '{existing.target_term}'\"\n return result\n elif on_conflict == \"overwrite\":\n existing.target_term = corrected_target_term.strip()\n if origin_run_id:\n existing.origin_run_id = origin_run_id\n if origin_row_key:\n existing.origin_row_key = origin_row_key\n if origin_user_id:\n existing.origin_user_id = origin_user_id\n if context_data is not None or not keep_context:\n existing.context_data = effective_context\n existing.has_context = bool(effective_context)\n existing.context_source = effective_context_source\n if usage_notes is not None:\n existing.usage_notes = usage_notes\n db.flush()\n result[\"action\"] = \"updated\"\n result[\"entry_id\"] = existing.id\n result[\"message\"] = f\"Entry updated from '{existing.target_term}' to '{corrected_target_term}'\"\n else: # cancel\n result[\"action\"] = \"skipped\"\n result[\"conflict\"] = {\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target_term,\n \"action\": \"cancel\",\n }\n result[\"message\"] = \"Correction cancelled by conflict mode\"\n return result\n else:\n # Create new entry — derive language pair from dictionary\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term.strip(),\n source_term_normalized=normalized,\n target_term=corrected_target_term.strip(),\n source_language=entry_src_lang,\n target_language=entry_tgt_lang,\n context_data=effective_context,\n usage_notes=usage_notes,\n has_context=bool(effective_context),\n context_source=effective_context_source if effective_context else None,\n origin_run_id=origin_run_id,\n origin_row_key=origin_row_key,\n origin_user_id=origin_user_id,\n )\n db.add(entry)\n db.flush()\n result[\"entry_id\"] = entry.id\n result[\"message\"] = f\"Entry created for '{source_term}' -> '{corrected_target_term}'\"\n\n db.commit()\n logger.reflect(\"Correction processed\", result)\n return result\n # endregion DictionaryManager.submit_correction\n\n # region DictionaryManager.submit_bulk_corrections [TYPE Function]\n # @PURPOSE: Submit multiple term corrections atomically — all succeed or all fail.\n # @PRE: corrections list is non-empty. dict_id exists.\n # @POST: All corrections applied or none applied with conflict list.\n # @SIDE_EFFECT: Creates/updates DictionaryEntry rows; commits once.\n @staticmethod\n def submit_bulk_corrections(\n db: Session,\n dict_id: str,\n corrections: list[dict[str, Any]],\n origin_user_id: str | None = None,\n ) -> dict[str, Any]:\n with belief_scope(\"DictionaryManager.submit_bulk_corrections\"):\n # Validate dictionary first\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary '{dict_id}' not found\")\n\n results: list[dict[str, Any]] = []\n conflicts: list[dict[str, Any]] = []\n all_ok = True\n\n for corr in corrections:\n source_term = corr.get(\"source_term\", \"\").strip()\n incorrect_target = corr.get(\"incorrect_target_term\", \"\").strip()\n corrected_target = corr.get(\"corrected_target_term\", \"\").strip()\n\n if not source_term or not corrected_target:\n results.append({\n \"source_term\": source_term,\n \"action\": \"error\",\n \"message\": \"source_term and corrected_target_term are required\",\n })\n all_ok = False\n continue\n\n normalized = _normalize_term(source_term)\n bulk_src_lang = dictionary.source_dialect or \"und\"\n bulk_tgt_lang = dictionary.target_dialect or \"und\"\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n or_(\n (DictionaryEntry.source_language == bulk_src_lang) &\n (DictionaryEntry.target_language == bulk_tgt_lang),\n (DictionaryEntry.source_language == \"und\") &\n (DictionaryEntry.target_language == \"und\"),\n ),\n )\n .first()\n )\n\n if existing:\n on_conflict = corr.get(\"on_conflict\", \"overwrite\")\n if on_conflict == \"keep_existing\":\n conflicts.append({\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target,\n \"action\": \"keep_existing\",\n })\n results.append({\n \"source_term\": source_term,\n \"action\": \"conflict_detected\",\n \"message\": f\"Existing entry kept: '{existing.target_term}'\",\n })\n continue\n elif on_conflict == \"overwrite\":\n existing.target_term = corrected_target\n existing.origin_user_id = origin_user_id\n results.append({\n \"source_term\": source_term,\n \"action\": \"updated\",\n \"entry_id\": existing.id,\n \"message\": f\"Updated to '{corrected_target}'\",\n })\n else:\n conflicts.append({\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target,\n \"action\": \"cancel\",\n })\n results.append({\n \"source_term\": source_term,\n \"action\": \"skipped\",\n \"message\": \"Cancelled by conflict mode\",\n })\n else:\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term,\n source_term_normalized=normalized,\n target_term=corrected_target,\n source_language=bulk_src_lang,\n target_language=bulk_tgt_lang,\n origin_run_id=corr.get(\"origin_run_id\"),\n origin_row_key=corr.get(\"origin_row_key\"),\n origin_user_id=origin_user_id,\n )\n db.add(entry)\n results.append({\n \"source_term\": source_term,\n \"action\": \"created\",\n \"message\": f\"Entry created for '{source_term}' -> '{corrected_target}'\",\n })\n\n if conflicts and not all_ok:\n # Rollback — all failed\n db.rollback()\n return {\n \"status\": \"conflicts\",\n \"results\": results,\n \"conflicts\": conflicts,\n \"message\": \"Conflicts detected. Resolve before retrying.\",\n }\n\n db.commit()\n\n return {\n \"status\": \"completed\",\n \"results\": results,\n \"conflicts\": conflicts,\n \"total\": len(corrections),\n \"applied\": sum(1 for r in results if r[\"action\"] in (\"created\", \"updated\")),\n }\n # endregion DictionaryManager.submit_bulk_corrections\n\n\n# #endregion DictionaryManager\n# #endregion DictionaryManagerModule\n" - }, - { - "contract_id": "_validate_bcp47", - "contract_type": "Function", - "file_path": "backend/src/plugins/translate/dictionary.py", - "start_line": 31, - "end_line": 45, - "tier": "TIER_1", - "complexity": 2, - "metadata": { - "COMPLEXITY": 2, - "PURPOSE": "Validate that a language tag is a non-empty BCP-47 string." - }, - "relations": [], - "schema_warnings": [], - "anchor_syntax": "region", - "tier_source": "AutoCalculated", - "body": "# #region _validate_bcp47 [C:2] [TYPE Function] [SEMANTICS validation, bcp47, language]\n# @BRIEF Validate that a language tag is a non-empty BCP-47 string.\ndef _validate_bcp47(tag: str, field_name: str) -> None:\n \"\"\"Validate that tag is a non-empty BCP-47 string (basic check).\"\"\"\n if not tag or not tag.strip():\n raise ValueError(f\"{field_name} must be a non-empty BCP-47 language tag\")\n tag = tag.strip()\n # Basic BCP-47 validation: must match lang[-script][-region][-variant]* pattern\n import re as _re\n if not _re.match(r'^[a-zA-Z]{2,8}(-[a-zA-Z0-9]{1,8})*$', tag):\n raise ValueError(\n f\"{field_name} is not a valid BCP-47 tag: '{tag}'. \"\n \"Expected format like 'en', 'ru', 'zh-CN', 'pt-BR'.\"\n )\n# #endregion _validate_bcp47\n" - }, { "contract_id": "DictionaryManager", "contract_type": "Class", "file_path": "backend/src/plugins/translate/dictionary.py", "start_line": 48, - "end_line": 1001, + "end_line": 1003, "tier": "TIER_2", "complexity": 4, "metadata": { @@ -49779,21 +49042,18 @@ "PURPOSE": "Manages terminology dictionaries and their entries with referential integrity.", "SIDE_EFFECT": "Creates, updates, deletes TerminologyDictionary and DictionaryEntry rows; enforces deletion guards." }, - "relations": [], - "schema_warnings": [ + "relations": [ { - "code": "missing_required_tag", - "tag": "RELATION", - "message": "@RELATION is required for contract type 'Class' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Class" - } + "source_id": "DictionaryManager", + "relation_type": "DEPENDS_ON", + "target_id": "TerminologyDictionary:Class], DEPENDS_ON -> [DictionaryEntry:Class], DEPENDS_ON -> [TranslationJobDictionary:Class], DEPENDS_ON -> [TranslationJob:Class", + "target_ref": "[TerminologyDictionary:Class], DEPENDS_ON -> [DictionaryEntry:Class], DEPENDS_ON -> [TranslationJobDictionary:Class], DEPENDS_ON -> [TranslationJob:Class]" } ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region DictionaryManager [C:4] [TYPE Class]\n# @BRIEF Manages terminology dictionaries and their entries with referential integrity.\n# @PRE: Database session is open and valid.\n# @POST: Dictionary and entry mutations are persisted with conflict detection.\n# @SIDE_EFFECT: Creates, updates, deletes TerminologyDictionary and DictionaryEntry rows; enforces deletion guards.\nclass DictionaryManager:\n # region DictionaryManager.create_dictionary [TYPE Function]\n # @PURPOSE: Create a new terminology dictionary (language independent at dictionary level; language pairs are per-entry).\n # @PRE: name is provided.\n # @POST: New TerminologyDictionary row is created and returned.\n @staticmethod\n def create_dictionary(\n db: Session, name: str,\n source_dialect: str = \"\",\n target_dialect: str = \"\",\n created_by: str | None = None, description: str | None = None,\n is_active: bool = True,\n ) -> TerminologyDictionary:\n with belief_scope(\"DictionaryManager.create_dictionary\"):\n logger.reason(\"Creating dictionary\", {\"name\": name, \"source\": source_dialect, \"target\": target_dialect})\n dictionary = TerminologyDictionary(\n name=name,\n description=description,\n source_dialect=source_dialect or \"\",\n target_dialect=target_dialect or \"\",\n is_active=is_active,\n created_by=created_by,\n )\n db.add(dictionary)\n db.commit()\n db.refresh(dictionary)\n logger.reflect(\"Dictionary created\", {\"id\": dictionary.id})\n return dictionary\n # endregion DictionaryManager.create_dictionary\n\n # region DictionaryManager.update_dictionary [TYPE Function]\n # @PURPOSE: Update an existing terminology dictionary.\n # @PRE: dict_id exists in terminology_dictionaries table.\n # @POST: Dictionary metadata is updated and returned.\n @staticmethod\n def update_dictionary(\n db: Session, dict_id: str, name: str | None = None,\n description: str | None = None, source_dialect: str | None = None,\n target_dialect: str | None = None, is_active: bool | None = None,\n ) -> TerminologyDictionary:\n with belief_scope(\"DictionaryManager.update_dictionary\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n logger.reason(\"Updating dictionary\", {\"id\": dict_id})\n if name is not None:\n dictionary.name = name\n if description is not None:\n dictionary.description = description\n if source_dialect is not None:\n dictionary.source_dialect = source_dialect\n if target_dialect is not None:\n dictionary.target_dialect = target_dialect\n if is_active is not None:\n dictionary.is_active = is_active\n db.commit()\n db.refresh(dictionary)\n logger.reflect(\"Dictionary updated\", {\"id\": dictionary.id})\n return dictionary\n # endregion DictionaryManager.update_dictionary\n\n # region DictionaryManager.delete_dictionary [TYPE Function]\n # @PURPOSE: Delete a dictionary, blocked if attached to active/scheduled jobs.\n # @PRE: dict_id exists.\n # @POST: Dictionary and its entries are deleted, unless attached to ACTIVE/READY/RUNNING/SCHEDULED jobs.\n # @SIDE_EFFECT: Deletes TerminologyDictionary row and all DictionaryEntry rows.\n @staticmethod\n def delete_dictionary(db: Session, dict_id: str) -> None:\n with belief_scope(\"DictionaryManager.delete_dictionary\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n\n # Check for attached active/scheduled jobs\n blocked_statuses = [\"ACTIVE\", \"READY\", \"RUNNING\", \"SCHEDULED\"]\n attached_jobs = (\n db.query(TranslationJobDictionary)\n .filter(\n TranslationJobDictionary.dictionary_id == dict_id,\n TranslationJobDictionary.job_id.in_(\n db.query(TranslationJob.id).filter(\n TranslationJob.status.in_(blocked_statuses)\n )\n ),\n )\n .count()\n )\n\n if attached_jobs > 0:\n logger.explore(\"Delete blocked: dictionary attached to active jobs\", {\"dict_id\": dict_id, \"jobs\": attached_jobs})\n raise ValueError(\n f\"Cannot delete dictionary attached to {attached_jobs} active/scheduled job(s). \"\n \"Remove dictionary associations from jobs first.\"\n )\n\n logger.reason(\"Deleting dictionary\", {\"id\": dict_id})\n # Delete entries and job-dictionary links first\n db.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).delete()\n db.query(TranslationJobDictionary).filter(TranslationJobDictionary.dictionary_id == dict_id).delete()\n db.delete(dictionary)\n db.commit()\n logger.reflect(\"Dictionary deleted\", {\"id\": dict_id})\n # endregion DictionaryManager.delete_dictionary\n\n # region DictionaryManager.get_dictionary [TYPE Function]\n # @PURPOSE: Get a single dictionary by ID with entry count.\n # @PRE: dict_id exists.\n # @POST: Returns dict with dictionary + entry_count or raises ValueError.\n @staticmethod\n def get_dictionary(db: Session, dict_id: str) -> TerminologyDictionary:\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n return dictionary\n # endregion DictionaryManager.get_dictionary\n\n # region DictionaryManager.list_dictionaries [TYPE Function]\n # @PURPOSE: List dictionaries with pagination and entry counts.\n # @PRE: page >= 1, page_size between 1 and 100.\n # @POST: Returns (list of dicts, total_count).\n @staticmethod\n def list_dictionaries(\n db: Session, page: int = 1, page_size: int = 20,\n ) -> tuple[list[TerminologyDictionary], int]:\n total = db.query(func.count(TerminologyDictionary.id)).scalar() or 0\n dictionaries = (\n db.query(TerminologyDictionary)\n .order_by(TerminologyDictionary.created_at.desc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n return dictionaries, total\n # endregion DictionaryManager.list_dictionaries\n\n # region DictionaryManager.add_entry [TYPE Function]\n # @PURPOSE: Add an entry to a dictionary with language-pair-aware uniqueness validation.\n # @PRE: dict_id exists. source_term, target_term are non-empty. source_language, target_language are valid BCP-47.\n # @POST: New DictionaryEntry row is created or raises on duplicate.\n @staticmethod\n def add_entry(\n db: Session, dict_id: str, source_term: str, target_term: str,\n context_notes: str | None = None,\n source_language: str = \"und\",\n target_language: str = \"und\",\n ) -> DictionaryEntry:\n with belief_scope(\"DictionaryManager.add_entry\"):\n _validate_bcp47(source_language, \"source_language\")\n _validate_bcp47(target_language, \"target_language\")\n\n normalized = _normalize_term(source_term)\n # Uniqueness is now (dictionary_id, source_term_normalized, source_language, target_language)\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == source_language,\n DictionaryEntry.target_language == target_language,\n )\n .first()\n )\n if existing:\n raise ValueError(\n f\"Duplicate entry: '{source_term}' (lang: {source_language}→{target_language}) \"\n f\"already exists in this dictionary (id={existing.id}). \"\n \"Use overwrite or keep_existing conflict mode.\"\n )\n\n logger.reason(\"Adding dictionary entry\", {\"dict_id\": dict_id, \"term\": source_term, \"src_lang\": source_language, \"tgt_lang\": target_language})\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term.strip(),\n source_term_normalized=normalized,\n target_term=target_term.strip(),\n context_notes=context_notes.strip() if context_notes else None,\n source_language=source_language.strip(),\n target_language=target_language.strip(),\n )\n db.add(entry)\n db.commit()\n db.refresh(entry)\n logger.reflect(\"Entry added\", {\"entry_id\": entry.id, \"src_lang\": source_language, \"tgt_lang\": target_language})\n return entry\n # endregion DictionaryManager.add_entry\n\n # region DictionaryManager.edit_entry [TYPE Function]\n # @PURPOSE: Edit an existing dictionary entry with language-pair-aware duplicate check.\n # @PRE: entry_id exists.\n # @POST: Entry fields are updated.\n @staticmethod\n def edit_entry(\n db: Session, entry_id: str, source_term: str | None = None,\n target_term: str | None = None, context_notes: str | None = None,\n source_language: str | None = None,\n target_language: str | None = None,\n ) -> DictionaryEntry:\n with belief_scope(\"DictionaryManager.edit_entry\"):\n entry = db.query(DictionaryEntry).filter(DictionaryEntry.id == entry_id).first()\n if not entry:\n raise ValueError(f\"Entry not found: {entry_id}\")\n\n logger.reason(\"Editing dictionary entry\", {\"entry_id\": entry_id})\n\n if source_language is not None:\n _validate_bcp47(source_language, \"source_language\")\n entry.source_language = source_language.strip()\n if target_language is not None:\n _validate_bcp47(target_language, \"target_language\")\n entry.target_language = target_language.strip()\n\n if source_term is not None:\n normalized = _normalize_term(source_term)\n # Check uniqueness within the same dictionary + language pair\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == entry.dictionary_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == entry.source_language,\n DictionaryEntry.target_language == entry.target_language,\n DictionaryEntry.id != entry_id,\n )\n .first()\n )\n if existing:\n raise ValueError(\n f\"Duplicate entry: '{source_term}' already exists in this dictionary \"\n f\"(id={existing.id}).\"\n )\n entry.source_term = source_term.strip()\n entry.source_term_normalized = normalized\n if target_term is not None:\n entry.target_term = target_term.strip()\n if context_notes is not None:\n entry.context_notes = context_notes.strip() if context_notes else None\n db.commit()\n db.refresh(entry)\n logger.reflect(\"Entry updated\", {\"entry_id\": entry.id})\n return entry\n # endregion DictionaryManager.edit_entry\n\n # region DictionaryManager.delete_entry [TYPE Function]\n # @PURPOSE: Delete a single dictionary entry.\n # @PRE: entry_id exists.\n # @POST: Entry is deleted.\n @staticmethod\n def delete_entry(db: Session, entry_id: str) -> None:\n with belief_scope(\"DictionaryManager.delete_entry\"):\n entry = db.query(DictionaryEntry).filter(DictionaryEntry.id == entry_id).first()\n if not entry:\n raise ValueError(f\"Entry not found: {entry_id}\")\n logger.reason(\"Deleting dictionary entry\", {\"entry_id\": entry_id})\n db.delete(entry)\n db.commit()\n logger.reflect(\"Entry deleted\", {\"entry_id\": entry_id})\n # endregion DictionaryManager.delete_entry\n\n # region DictionaryManager.clear_entries [TYPE Function]\n # @PURPOSE: Delete all entries for a dictionary.\n # @PRE: dict_id exists.\n # @POST: All entries for the dictionary are deleted.\n @staticmethod\n def clear_entries(db: Session, dict_id: str) -> int:\n with belief_scope(\"DictionaryManager.clear_entries\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n logger.reason(\"Clearing all entries\", {\"dict_id\": dict_id})\n deleted = db.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).delete()\n db.commit()\n logger.reflect(\"Entries cleared\", {\"dict_id\": dict_id, \"count\": deleted})\n return deleted\n # endregion DictionaryManager.clear_entries\n\n # region DictionaryManager.list_entries [TYPE Function]\n # @PURPOSE: List entries for a dictionary with pagination.\n # @PRE: dict_id exists.\n # @POST: Returns (list of entries, total_count).\n @staticmethod\n def list_entries(\n db: Session, dict_id: str, page: int = 1, page_size: int = 50,\n ) -> tuple[list[DictionaryEntry], int]:\n total = (\n db.query(func.count(DictionaryEntry.id))\n .filter(DictionaryEntry.dictionary_id == dict_id)\n .scalar()\n or 0\n )\n entries = (\n db.query(DictionaryEntry)\n .filter(DictionaryEntry.dictionary_id == dict_id)\n .order_by(DictionaryEntry.source_term.asc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n return entries, total\n # endregion DictionaryManager.list_entries\n\n # region DictionaryManager.import_entries [TYPE Function]\n # @PURPOSE: Import entries from CSV/TSV content with duplicate detection and conflict resolution.\n # @PRE: content is valid CSV or TSV. dict_id exists.\n # @POST: Entries are created/updated/skipped per conflict mode. Returns result summary.\n # @SIDE_EFFECT: Batch-inserts or updates DictionaryEntry rows.\n @staticmethod\n def import_entries(\n db: Session, dict_id: str, content: str,\n delimiter: str | None = None,\n on_conflict: str = \"overwrite\",\n preview_only: bool = False,\n default_source_language: str | None = None,\n default_target_language: str | None = None,\n ) -> dict[str, Any]:\n with belief_scope(\"DictionaryManager.import_entries\"):\n # Validate dictionary\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n\n # Detect delimiter if not specified\n if not delimiter:\n delimiter = _detect_delimiter(content)\n logger.reason(\"Detected delimiter\", {\"delimiter\": repr(delimiter)})\n\n if delimiter not in (\",\", \"\\t\"):\n raise ValueError(f\"Unsupported delimiter: {delimiter!r}. Use ',' or '\\\\t'.\")\n\n # Parse content\n reader = csv.DictReader(io.StringIO(content), delimiter=delimiter)\n required_fields = {\"source_term\", \"target_term\"}\n if not reader.fieldnames or not required_fields.issubset(reader.fieldnames):\n raise ValueError(\n f\"CSV/TSV must have at least 'source_term' and 'target_term' columns. \"\n f\"Got: {reader.fieldnames}\"\n )\n\n result: dict[str, Any] = {\n \"total\": 0,\n \"created\": 0,\n \"updated\": 0,\n \"skipped\": 0,\n \"errors\": [],\n \"preview\": [],\n }\n\n rows = list(reader)\n result[\"total\"] = len(rows)\n\n for row_idx, row in enumerate(rows):\n try:\n source_term = row.get(\"source_term\", \"\").strip()\n target_term = row.get(\"target_term\", \"\").strip()\n context_notes = row.get(\"context_notes\", \"\").strip() or None\n source_language = row.get(\"source_language\", \"\").strip() or default_source_language or \"und\"\n target_language = row.get(\"target_language\", \"\").strip() or default_target_language or \"und\"\n\n if not source_term or not target_term:\n result[\"errors\"].append({\n \"line\": row_idx + 2, # +2 for header and 1-indexed\n \"error\": \"Both source_term and target_term are required\",\n \"row\": row,\n })\n continue\n\n normalized = _normalize_term(source_term)\n\n if preview_only:\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == source_language,\n DictionaryEntry.target_language == target_language,\n )\n .first()\n )\n preview_row = {\n \"line\": row_idx + 2,\n \"source_term\": source_term,\n \"target_term\": target_term,\n \"context_notes\": context_notes,\n \"source_language\": source_language,\n \"target_language\": target_language,\n \"is_conflict\": existing is not None,\n \"existing_target_term\": existing.target_term if existing else None,\n }\n result[\"preview\"].append(preview_row)\n continue\n\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == source_language,\n DictionaryEntry.target_language == target_language,\n )\n .first()\n )\n\n if existing:\n if on_conflict == \"overwrite\":\n existing.source_term = source_term\n existing.target_term = target_term\n existing.context_notes = context_notes\n existing.source_language = source_language\n existing.target_language = target_language\n result[\"updated\"] += 1\n elif on_conflict == \"keep_existing\":\n result[\"skipped\"] += 1\n else: # cancel\n result[\"errors\"].append({\n \"line\": row_idx + 2,\n \"error\": f\"Conflict on '{source_term}' and on_conflict='cancel'\",\n \"row\": row,\n })\n continue\n else:\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term,\n source_term_normalized=normalized,\n target_term=target_term,\n context_notes=context_notes,\n source_language=source_language,\n target_language=target_language,\n )\n db.add(entry)\n result[\"created\"] += 1\n\n except Exception as e:\n result[\"errors\"].append({\n \"line\": row_idx + 2,\n \"error\": str(e),\n \"row\": row,\n })\n\n if not preview_only:\n db.commit()\n\n logger.reflect(\"Import complete\", {\n \"dict_id\": dict_id,\n \"total\": result[\"total\"],\n \"created\": result[\"created\"],\n \"updated\": result[\"updated\"],\n \"skipped\": result[\"skipped\"],\n \"errors\": len(result[\"errors\"]),\n })\n return result\n # endregion DictionaryManager.import_entries\n\n # region DictionaryManager.export_entries [TYPE Function]\n # @PURPOSE: Export all entries as CSV string with language columns.\n # @PRE: dict_id exists.\n # @POST: Returns CSV string with header: source_term, target_term, source_language, target_language, context_notes, context_data, usage_notes.\n @staticmethod\n def export_entries(\n db: Session, dict_id: str, delimiter: str = \",\",\n ) -> str:\n with belief_scope(\"DictionaryManager.export_entries\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n\n entries = (\n db.query(DictionaryEntry)\n .filter(DictionaryEntry.dictionary_id == dict_id)\n .order_by(DictionaryEntry.source_term.asc())\n .all()\n )\n\n output = io.StringIO()\n fieldnames = [\n \"source_term\", \"target_term\",\n \"source_language\", \"target_language\",\n \"context_notes\", \"context_data\", \"usage_notes\",\n ]\n writer = csv.DictWriter(output, fieldnames=fieldnames, delimiter=delimiter)\n writer.writeheader()\n\n for entry in entries:\n writer.writerow({\n \"source_term\": entry.source_term,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes or \"\",\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes or \"\",\n })\n\n result = output.getvalue()\n logger.reflect(\"Entries exported\", {\"dict_id\": dict_id, \"count\": len(entries), \"delimiter\": repr(delimiter)})\n return result\n # endregion DictionaryManager.export_entries\n\n # region DictionaryManager.migrate_old_entries [TYPE Function]\n # @PURPOSE: Migrate existing single-language dictionary entries to populate source_language and target_language.\n # @PRE: db session is open.\n # @POST: Entries with \"und\" source_language or target_language are updated with inferred values.\n # @SIDE_EFFECT: Updates DictionaryEntry rows in bulk.\n @staticmethod\n def migrate_old_entries(db: Session) -> dict[str, int]:\n with belief_scope(\"DictionaryManager.migrate_old_entries\"):\n logger.reason(\"Starting migration of old dictionary entries\")\n migrated_source = 0\n migrated_target = 0\n skipped = 0\n\n # Find all entries that may need migration (source_language is \"und\" or target_language is \"und\")\n entries = (\n db.query(DictionaryEntry)\n .filter(\n (DictionaryEntry.source_language == \"und\") |\n (DictionaryEntry.target_language == \"und\")\n )\n .all()\n )\n\n for entry in entries:\n # Try to infer source_language from origin_source_language\n if entry.source_language == \"und\":\n if entry.origin_source_language:\n entry.source_language = entry.origin_source_language\n migrated_source += 1\n\n # Note: target_language inference from dictionary-level column was removed\n # because TerminologyDictionary.target_language was deprecated and dropped.\n\n if entry.source_language == \"und\" and entry.target_language == \"und\":\n skipped += 1\n\n db.commit()\n\n logger.reflect(\"Migration complete\", {\n \"migrated_source\": migrated_source,\n \"migrated_target\": migrated_target,\n \"skipped\": skipped,\n \"total_processed\": len(entries),\n })\n return {\n \"migrated_source\": migrated_source,\n \"migrated_target\": migrated_target,\n \"skipped\": skipped,\n \"total_processed\": len(entries),\n }\n # endregion DictionaryManager.migrate_old_entries\n\n # region DictionaryManager.filter_for_batch [TYPE Function]\n # @PURPOSE: Scan batch texts for case-insensitive, word-boundary-aware matches against all dictionaries attached to a job,\n # optionally filtered by language pair. When row_context is provided, computes context-aware priority flags.\n # @PRE: job_id exists and source_texts is a list of strings.\n # @POST: Returns list of matched entries with match info, sorted by priority across attached dictionaries.\n # When row_context is given, each result includes a 'priority_match' boolean.\n # @SIDE_EFFECT: Queries TranslationJobDictionary, TerminologyDictionary, and DictionaryEntry tables.\n @staticmethod\n def filter_for_batch(\n db: Session, source_texts: list[str], job_id: str,\n source_language: str | None = None,\n target_language: str | None = None,\n row_context: dict | None = None,\n ) -> list[dict[str, Any]]:\n with belief_scope(\"DictionaryManager.filter_for_batch\"):\n # Get dictionaries attached to this job\n job_dict_links = (\n db.query(TranslationJobDictionary)\n .filter(TranslationJobDictionary.job_id == job_id)\n .all()\n )\n if not job_dict_links:\n logger.reason(\"No dictionaries attached to job\", {\"job_id\": job_id})\n return []\n\n dict_ids = [jd.dictionary_id for jd in job_dict_links]\n\n # Get all active dictionaries\n dictionaries = (\n db.query(TerminologyDictionary)\n .filter(\n TerminologyDictionary.id.in_(dict_ids),\n TerminologyDictionary.is_active == True,\n )\n .all()\n )\n if not dictionaries:\n return []\n\n # Build dict_id -> dict_name lookup\n dict_map = {d.id: d.name for d in dictionaries}\n\n # Fetch all entries for these dictionaries, ordered by dictionary (priority order from link order)\n all_entries = (\n db.query(DictionaryEntry)\n .filter(DictionaryEntry.dictionary_id.in_(dict_ids))\n .order_by(DictionaryEntry.source_term.asc())\n .all()\n )\n\n if not all_entries:\n return []\n\n # PERFORMANCE NOTE: O(rows × entries) regex matching per batch.\n # Acceptable for current scale (50-100 rows × <1000 entries).\n # For larger batches (>500 rows or >5000 entries), consider\n # replacing with an Aho-Corasick automaton (e.g., pyahocorasick).\n matched: list[dict[str, Any]] = []\n seen_source_match: set = set()\n\n for text_idx, source_text in enumerate(source_texts):\n if not source_text:\n continue\n\n text_lower = source_text.lower()\n\n for entry in all_entries:\n norm = entry.source_term_normalized\n if not norm:\n continue\n\n # Apply language pair filtering\n if source_language is not None:\n src = source_language.strip().lower()\n entry_src = entry.source_language.strip().lower()\n if entry_src != src and entry_src != \"und\":\n continue\n if target_language is not None:\n tgt = target_language.strip().lower()\n entry_tgt = entry.target_language.strip().lower()\n if entry_tgt != tgt:\n continue\n\n # Word-boundary-aware matching\n # Build pattern: \\bterm\\b (case-insensitive)\n # Escape regex special chars in the search term\n escaped = re.escape(norm)\n pattern = re.compile(r\"\\b\" + escaped + r\"\\b\", re.IGNORECASE)\n\n if pattern.search(text_lower):\n match_key = (text_idx, entry.id)\n if match_key not in seen_source_match:\n seen_source_match.add(match_key)\n\n # Compute context-aware priority if row_context is available\n priority_match = False\n if row_context and entry.has_context and entry.context_data:\n similarity = ContextAwarePromptBuilder.compute_context_similarity(\n entry.context_data, row_context\n )\n priority_match = similarity >= 0.5\n\n matched.append({\n \"text_index\": text_idx,\n \"source_text\": source_text,\n \"entry_id\": entry.id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"dictionary_id\": entry.dictionary_id,\n \"dictionary_name\": dict_map.get(entry.dictionary_id, \"\"),\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"usage_notes\": entry.usage_notes,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"priority_match\": priority_match,\n })\n\n # Sort by dictionary link priority (order of dict_ids from link order)\n dict_priority = {d_id: idx for idx, d_id in enumerate(dict_ids)}\n matched.sort(key=lambda m: (dict_priority.get(m[\"dictionary_id\"], 999), m[\"text_index\"]))\n\n logger.reflect(\"Batch filter match complete\", {\n \"job_id\": job_id,\n \"source_texts\": len(source_texts),\n \"matches\": len(matched),\n \"dictionaries_used\": len(dictionaries),\n \"source_language\": source_language,\n \"target_language\": target_language,\n })\n return matched\n # endregion DictionaryManager.filter_for_batch\n\n\n # region DictionaryManager.submit_correction [TYPE Function]\n # @PURPOSE: Submit a term correction from a run result. Validates language match, detects conflicts.\n # @PRE: source_term, incorrect_target_term, corrected_target_term are non-empty. dict_id exists.\n # @POST: Entry created or updated; origin tracking populated. Returns action + conflict info.\n # @SIDE_EFFECT: Creates/updates DictionaryEntry row.\n @staticmethod\n def submit_correction(\n db: Session,\n dict_id: str,\n source_term: str,\n incorrect_target_term: str,\n corrected_target_term: str,\n origin_run_id: str | None = None,\n origin_row_key: str | None = None,\n origin_user_id: str | None = None,\n on_conflict: str = \"overwrite\",\n context_data: dict[str, Any] | None = None,\n usage_notes: str | None = None,\n keep_context: bool = True,\n ) -> dict[str, Any]:\n with belief_scope(\"DictionaryManager.submit_correction\"):\n # Validate dictionary exists\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary '{dict_id}' not found\")\n\n # Handle keep_context=False (user explicitly removed context)\n effective_context = context_data\n effective_context_source = \"auto\"\n if not keep_context:\n effective_context = None\n effective_context_source = \"manual\"\n\n normalized = _normalize_term(source_term)\n entry_src_lang = dictionary.source_dialect or \"und\"\n entry_tgt_lang = dictionary.target_dialect or \"und\"\n # Find existing entry: match exact language pair OR old-style \"und\"/\"und\" entries\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n or_(\n # Exact language pair match\n (DictionaryEntry.source_language == entry_src_lang) &\n (DictionaryEntry.target_language == entry_tgt_lang),\n # Old-style entries without language pair\n (DictionaryEntry.source_language == \"und\") &\n (DictionaryEntry.target_language == \"und\"),\n ),\n )\n .first()\n )\n\n result: dict[str, Any] = {\n \"source_term\": source_term,\n \"target_term\": corrected_target_term,\n \"action\": \"created\",\n \"entry_id\": None,\n \"conflict\": None,\n \"message\": None,\n }\n\n if existing:\n # Conflict detected\n if on_conflict == \"keep_existing\":\n result[\"action\"] = \"conflict_detected\"\n result[\"conflict\"] = {\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target_term,\n \"action\": \"keep_existing\",\n }\n result[\"message\"] = f\"Existing entry kept: '{existing.target_term}'\"\n return result\n elif on_conflict == \"overwrite\":\n existing.target_term = corrected_target_term.strip()\n if origin_run_id:\n existing.origin_run_id = origin_run_id\n if origin_row_key:\n existing.origin_row_key = origin_row_key\n if origin_user_id:\n existing.origin_user_id = origin_user_id\n if context_data is not None or not keep_context:\n existing.context_data = effective_context\n existing.has_context = bool(effective_context)\n existing.context_source = effective_context_source\n if usage_notes is not None:\n existing.usage_notes = usage_notes\n db.flush()\n result[\"action\"] = \"updated\"\n result[\"entry_id\"] = existing.id\n result[\"message\"] = f\"Entry updated from '{existing.target_term}' to '{corrected_target_term}'\"\n else: # cancel\n result[\"action\"] = \"skipped\"\n result[\"conflict\"] = {\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target_term,\n \"action\": \"cancel\",\n }\n result[\"message\"] = \"Correction cancelled by conflict mode\"\n return result\n else:\n # Create new entry — derive language pair from dictionary\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term.strip(),\n source_term_normalized=normalized,\n target_term=corrected_target_term.strip(),\n source_language=entry_src_lang,\n target_language=entry_tgt_lang,\n context_data=effective_context,\n usage_notes=usage_notes,\n has_context=bool(effective_context),\n context_source=effective_context_source if effective_context else None,\n origin_run_id=origin_run_id,\n origin_row_key=origin_row_key,\n origin_user_id=origin_user_id,\n )\n db.add(entry)\n db.flush()\n result[\"entry_id\"] = entry.id\n result[\"message\"] = f\"Entry created for '{source_term}' -> '{corrected_target_term}'\"\n\n db.commit()\n logger.reflect(\"Correction processed\", result)\n return result\n # endregion DictionaryManager.submit_correction\n\n # region DictionaryManager.submit_bulk_corrections [TYPE Function]\n # @PURPOSE: Submit multiple term corrections atomically — all succeed or all fail.\n # @PRE: corrections list is non-empty. dict_id exists.\n # @POST: All corrections applied or none applied with conflict list.\n # @SIDE_EFFECT: Creates/updates DictionaryEntry rows; commits once.\n @staticmethod\n def submit_bulk_corrections(\n db: Session,\n dict_id: str,\n corrections: list[dict[str, Any]],\n origin_user_id: str | None = None,\n ) -> dict[str, Any]:\n with belief_scope(\"DictionaryManager.submit_bulk_corrections\"):\n # Validate dictionary first\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary '{dict_id}' not found\")\n\n results: list[dict[str, Any]] = []\n conflicts: list[dict[str, Any]] = []\n all_ok = True\n\n for corr in corrections:\n source_term = corr.get(\"source_term\", \"\").strip()\n incorrect_target = corr.get(\"incorrect_target_term\", \"\").strip()\n corrected_target = corr.get(\"corrected_target_term\", \"\").strip()\n\n if not source_term or not corrected_target:\n results.append({\n \"source_term\": source_term,\n \"action\": \"error\",\n \"message\": \"source_term and corrected_target_term are required\",\n })\n all_ok = False\n continue\n\n normalized = _normalize_term(source_term)\n bulk_src_lang = dictionary.source_dialect or \"und\"\n bulk_tgt_lang = dictionary.target_dialect or \"und\"\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n or_(\n (DictionaryEntry.source_language == bulk_src_lang) &\n (DictionaryEntry.target_language == bulk_tgt_lang),\n (DictionaryEntry.source_language == \"und\") &\n (DictionaryEntry.target_language == \"und\"),\n ),\n )\n .first()\n )\n\n if existing:\n on_conflict = corr.get(\"on_conflict\", \"overwrite\")\n if on_conflict == \"keep_existing\":\n conflicts.append({\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target,\n \"action\": \"keep_existing\",\n })\n results.append({\n \"source_term\": source_term,\n \"action\": \"conflict_detected\",\n \"message\": f\"Existing entry kept: '{existing.target_term}'\",\n })\n continue\n elif on_conflict == \"overwrite\":\n existing.target_term = corrected_target\n existing.origin_user_id = origin_user_id\n results.append({\n \"source_term\": source_term,\n \"action\": \"updated\",\n \"entry_id\": existing.id,\n \"message\": f\"Updated to '{corrected_target}'\",\n })\n else:\n conflicts.append({\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target,\n \"action\": \"cancel\",\n })\n results.append({\n \"source_term\": source_term,\n \"action\": \"skipped\",\n \"message\": \"Cancelled by conflict mode\",\n })\n else:\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term,\n source_term_normalized=normalized,\n target_term=corrected_target,\n source_language=bulk_src_lang,\n target_language=bulk_tgt_lang,\n origin_run_id=corr.get(\"origin_run_id\"),\n origin_row_key=corr.get(\"origin_row_key\"),\n origin_user_id=origin_user_id,\n )\n db.add(entry)\n results.append({\n \"source_term\": source_term,\n \"action\": \"created\",\n \"message\": f\"Entry created for '{source_term}' -> '{corrected_target}'\",\n })\n\n if conflicts and not all_ok:\n # Rollback — all failed\n db.rollback()\n return {\n \"status\": \"conflicts\",\n \"results\": results,\n \"conflicts\": conflicts,\n \"message\": \"Conflicts detected. Resolve before retrying.\",\n }\n\n db.commit()\n\n return {\n \"status\": \"completed\",\n \"results\": results,\n \"conflicts\": conflicts,\n \"total\": len(corrections),\n \"applied\": sum(1 for r in results if r[\"action\"] in (\"created\", \"updated\")),\n }\n # endregion DictionaryManager.submit_bulk_corrections\n\n\n# #endregion DictionaryManager\n" + "body": "# #region DictionaryManager [C:4] [TYPE Class]\n# @BRIEF Manages terminology dictionaries and their entries with referential integrity.\n# @PRE Database session is open and valid.\n# @POST Dictionary and entry mutations are persisted with conflict detection.\n# @SIDE_EFFECT Creates, updates, deletes TerminologyDictionary and DictionaryEntry rows; enforces deletion guards.\n# @RELATION DEPENDS_ON -> [TerminologyDictionary:Class], DEPENDS_ON -> [DictionaryEntry:Class], DEPENDS_ON -> [TranslationJobDictionary:Class], DEPENDS_ON -> [TranslationJob:Class]\n\nclass DictionaryManager:\n # region DictionaryManager.create_dictionary [TYPE Function]\n # @PURPOSE Create a new terminology dictionary (language independent at dictionary level; language pairs are per-entry).\n # @PRE name is provided.\n # @POST New TerminologyDictionary row is created and returned.\n @staticmethod\n def create_dictionary(\n db: Session, name: str,\n source_dialect: str = \"\",\n target_dialect: str = \"\",\n created_by: str | None = None, description: str | None = None,\n is_active: bool = True,\n ) -> TerminologyDictionary:\n with belief_scope(\"DictionaryManager.create_dictionary\"):\n logger.reason(\"Creating dictionary\", {\"name\": name, \"source\": source_dialect, \"target\": target_dialect})\n dictionary = TerminologyDictionary(\n name=name,\n description=description,\n source_dialect=source_dialect or \"\",\n target_dialect=target_dialect or \"\",\n is_active=is_active,\n created_by=created_by,\n )\n db.add(dictionary)\n db.commit()\n db.refresh(dictionary)\n logger.reflect(\"Dictionary created\", {\"id\": dictionary.id})\n return dictionary\n # endregion DictionaryManager.create_dictionary\n\n # region DictionaryManager.update_dictionary [TYPE Function]\n # @PURPOSE Update an existing terminology dictionary.\n # @PRE dict_id exists in terminology_dictionaries table.\n # @POST Dictionary metadata is updated and returned.\n @staticmethod\n def update_dictionary(\n db: Session, dict_id: str, name: str | None = None,\n description: str | None = None, source_dialect: str | None = None,\n target_dialect: str | None = None, is_active: bool | None = None,\n ) -> TerminologyDictionary:\n with belief_scope(\"DictionaryManager.update_dictionary\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n logger.reason(\"Updating dictionary\", {\"id\": dict_id})\n if name is not None:\n dictionary.name = name\n if description is not None:\n dictionary.description = description\n if source_dialect is not None:\n dictionary.source_dialect = source_dialect\n if target_dialect is not None:\n dictionary.target_dialect = target_dialect\n if is_active is not None:\n dictionary.is_active = is_active\n db.commit()\n db.refresh(dictionary)\n logger.reflect(\"Dictionary updated\", {\"id\": dictionary.id})\n return dictionary\n # endregion DictionaryManager.update_dictionary\n\n # region DictionaryManager.delete_dictionary [TYPE Function]\n # @PURPOSE Delete a dictionary, blocked if attached to active/scheduled jobs.\n # @PRE dict_id exists.\n # @POST Dictionary and its entries are deleted, unless attached to ACTIVE/READY/RUNNING/SCHEDULED jobs.\n # @SIDE_EFFECT Deletes TerminologyDictionary row and all DictionaryEntry rows.\n @staticmethod\n def delete_dictionary(db: Session, dict_id: str) -> None:\n with belief_scope(\"DictionaryManager.delete_dictionary\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n\n # Check for attached active/scheduled jobs\n blocked_statuses = [\"ACTIVE\", \"READY\", \"RUNNING\", \"SCHEDULED\"]\n attached_jobs = (\n db.query(TranslationJobDictionary)\n .filter(\n TranslationJobDictionary.dictionary_id == dict_id,\n TranslationJobDictionary.job_id.in_(\n db.query(TranslationJob.id).filter(\n TranslationJob.status.in_(blocked_statuses)\n )\n ),\n )\n .count()\n )\n\n if attached_jobs > 0:\n logger.explore(\"Delete blocked: dictionary attached to active jobs\", {\"dict_id\": dict_id, \"jobs\": attached_jobs})\n raise ValueError(\n f\"Cannot delete dictionary attached to {attached_jobs} active/scheduled job(s). \"\n \"Remove dictionary associations from jobs first.\"\n )\n\n logger.reason(\"Deleting dictionary\", {\"id\": dict_id})\n # Delete entries and job-dictionary links first\n db.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).delete()\n db.query(TranslationJobDictionary).filter(TranslationJobDictionary.dictionary_id == dict_id).delete()\n db.delete(dictionary)\n db.commit()\n logger.reflect(\"Dictionary deleted\", {\"id\": dict_id})\n # endregion DictionaryManager.delete_dictionary\n\n # region DictionaryManager.get_dictionary [TYPE Function]\n # @PURPOSE Get a single dictionary by ID with entry count.\n # @PRE dict_id exists.\n # @POST Returns dict with dictionary + entry_count or raises ValueError.\n @staticmethod\n def get_dictionary(db: Session, dict_id: str) -> TerminologyDictionary:\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n return dictionary\n # endregion DictionaryManager.get_dictionary\n\n # region DictionaryManager.list_dictionaries [TYPE Function]\n # @PURPOSE List dictionaries with pagination and entry counts.\n # @PRE page >= 1, page_size between 1 and 100.\n # @POST Returns (list of dicts, total_count).\n @staticmethod\n def list_dictionaries(\n db: Session, page: int = 1, page_size: int = 20,\n ) -> tuple[list[TerminologyDictionary], int]:\n total = db.query(func.count(TerminologyDictionary.id)).scalar() or 0\n dictionaries = (\n db.query(TerminologyDictionary)\n .order_by(TerminologyDictionary.created_at.desc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n return dictionaries, total\n # endregion DictionaryManager.list_dictionaries\n\n # region DictionaryManager.add_entry [TYPE Function]\n # @PURPOSE Add an entry to a dictionary with language-pair-aware uniqueness validation.\n # @PRE dict_id exists. source_term, target_term are non-empty. source_language, target_language are valid BCP-47.\n # @POST New DictionaryEntry row is created or raises on duplicate.\n @staticmethod\n def add_entry(\n db: Session, dict_id: str, source_term: str, target_term: str,\n context_notes: str | None = None,\n source_language: str = \"und\",\n target_language: str = \"und\",\n ) -> DictionaryEntry:\n with belief_scope(\"DictionaryManager.add_entry\"):\n _validate_bcp47(source_language, \"source_language\")\n _validate_bcp47(target_language, \"target_language\")\n\n normalized = _normalize_term(source_term)\n # Uniqueness is now (dictionary_id, source_term_normalized, source_language, target_language)\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == source_language,\n DictionaryEntry.target_language == target_language,\n )\n .first()\n )\n if existing:\n raise ValueError(\n f\"Duplicate entry: '{source_term}' (lang: {source_language}→{target_language}) \"\n f\"already exists in this dictionary (id={existing.id}). \"\n \"Use overwrite or keep_existing conflict mode.\"\n )\n\n logger.reason(\"Adding dictionary entry\", {\"dict_id\": dict_id, \"term\": source_term, \"src_lang\": source_language, \"tgt_lang\": target_language})\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term.strip(),\n source_term_normalized=normalized,\n target_term=target_term.strip(),\n context_notes=context_notes.strip() if context_notes else None,\n source_language=source_language.strip(),\n target_language=target_language.strip(),\n )\n db.add(entry)\n db.commit()\n db.refresh(entry)\n logger.reflect(\"Entry added\", {\"entry_id\": entry.id, \"src_lang\": source_language, \"tgt_lang\": target_language})\n return entry\n # endregion DictionaryManager.add_entry\n\n # region DictionaryManager.edit_entry [TYPE Function]\n # @PURPOSE Edit an existing dictionary entry with language-pair-aware duplicate check.\n # @PRE entry_id exists.\n # @POST Entry fields are updated.\n @staticmethod\n def edit_entry(\n db: Session, entry_id: str, source_term: str | None = None,\n target_term: str | None = None, context_notes: str | None = None,\n source_language: str | None = None,\n target_language: str | None = None,\n ) -> DictionaryEntry:\n with belief_scope(\"DictionaryManager.edit_entry\"):\n entry = db.query(DictionaryEntry).filter(DictionaryEntry.id == entry_id).first()\n if not entry:\n raise ValueError(f\"Entry not found: {entry_id}\")\n\n logger.reason(\"Editing dictionary entry\", {\"entry_id\": entry_id})\n\n if source_language is not None:\n _validate_bcp47(source_language, \"source_language\")\n entry.source_language = source_language.strip()\n if target_language is not None:\n _validate_bcp47(target_language, \"target_language\")\n entry.target_language = target_language.strip()\n\n if source_term is not None:\n normalized = _normalize_term(source_term)\n # Check uniqueness within the same dictionary + language pair\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == entry.dictionary_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == entry.source_language,\n DictionaryEntry.target_language == entry.target_language,\n DictionaryEntry.id != entry_id,\n )\n .first()\n )\n if existing:\n raise ValueError(\n f\"Duplicate entry: '{source_term}' already exists in this dictionary \"\n f\"(id={existing.id}).\"\n )\n entry.source_term = source_term.strip()\n entry.source_term_normalized = normalized\n if target_term is not None:\n entry.target_term = target_term.strip()\n if context_notes is not None:\n entry.context_notes = context_notes.strip() if context_notes else None\n db.commit()\n db.refresh(entry)\n logger.reflect(\"Entry updated\", {\"entry_id\": entry.id})\n return entry\n # endregion DictionaryManager.edit_entry\n\n # region DictionaryManager.delete_entry [TYPE Function]\n # @PURPOSE Delete a single dictionary entry.\n # @PRE entry_id exists.\n # @POST Entry is deleted.\n @staticmethod\n def delete_entry(db: Session, entry_id: str) -> None:\n with belief_scope(\"DictionaryManager.delete_entry\"):\n entry = db.query(DictionaryEntry).filter(DictionaryEntry.id == entry_id).first()\n if not entry:\n raise ValueError(f\"Entry not found: {entry_id}\")\n logger.reason(\"Deleting dictionary entry\", {\"entry_id\": entry_id})\n db.delete(entry)\n db.commit()\n logger.reflect(\"Entry deleted\", {\"entry_id\": entry_id})\n # endregion DictionaryManager.delete_entry\n\n # region DictionaryManager.clear_entries [TYPE Function]\n # @PURPOSE Delete all entries for a dictionary.\n # @PRE dict_id exists.\n # @POST All entries for the dictionary are deleted.\n @staticmethod\n def clear_entries(db: Session, dict_id: str) -> int:\n with belief_scope(\"DictionaryManager.clear_entries\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n logger.reason(\"Clearing all entries\", {\"dict_id\": dict_id})\n deleted = db.query(DictionaryEntry).filter(DictionaryEntry.dictionary_id == dict_id).delete()\n db.commit()\n logger.reflect(\"Entries cleared\", {\"dict_id\": dict_id, \"count\": deleted})\n return deleted\n # endregion DictionaryManager.clear_entries\n\n # region DictionaryManager.list_entries [TYPE Function]\n # @PURPOSE List entries for a dictionary with pagination.\n # @PRE dict_id exists.\n # @POST Returns (list of entries, total_count).\n @staticmethod\n def list_entries(\n db: Session, dict_id: str, page: int = 1, page_size: int = 50,\n ) -> tuple[list[DictionaryEntry], int]:\n total = (\n db.query(func.count(DictionaryEntry.id))\n .filter(DictionaryEntry.dictionary_id == dict_id)\n .scalar()\n or 0\n )\n entries = (\n db.query(DictionaryEntry)\n .filter(DictionaryEntry.dictionary_id == dict_id)\n .order_by(DictionaryEntry.source_term.asc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n return entries, total\n # endregion DictionaryManager.list_entries\n\n # region DictionaryManager.import_entries [TYPE Function]\n # @PURPOSE Import entries from CSV/TSV content with duplicate detection and conflict resolution.\n # @PRE content is valid CSV or TSV. dict_id exists.\n # @POST Entries are created/updated/skipped per conflict mode. Returns result summary.\n # @SIDE_EFFECT Batch-inserts or updates DictionaryEntry rows.\n @staticmethod\n def import_entries(\n db: Session, dict_id: str, content: str,\n delimiter: str | None = None,\n on_conflict: str = \"overwrite\",\n preview_only: bool = False,\n default_source_language: str | None = None,\n default_target_language: str | None = None,\n ) -> dict[str, Any]:\n with belief_scope(\"DictionaryManager.import_entries\"):\n # Validate dictionary\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n\n # Detect delimiter if not specified\n if not delimiter:\n delimiter = _detect_delimiter(content)\n logger.reason(\"Detected delimiter\", {\"delimiter\": repr(delimiter)})\n\n if delimiter not in (\",\", \"\\t\"):\n raise ValueError(f\"Unsupported delimiter: {delimiter!r}. Use ',' or '\\\\t'.\")\n\n # Parse content\n reader = csv.DictReader(io.StringIO(content), delimiter=delimiter)\n required_fields = {\"source_term\", \"target_term\"}\n if not reader.fieldnames or not required_fields.issubset(reader.fieldnames):\n raise ValueError(\n f\"CSV/TSV must have at least 'source_term' and 'target_term' columns. \"\n f\"Got: {reader.fieldnames}\"\n )\n\n result: dict[str, Any] = {\n \"total\": 0,\n \"created\": 0,\n \"updated\": 0,\n \"skipped\": 0,\n \"errors\": [],\n \"preview\": [],\n }\n\n rows = list(reader)\n result[\"total\"] = len(rows)\n\n for row_idx, row in enumerate(rows):\n try:\n source_term = row.get(\"source_term\", \"\").strip()\n target_term = row.get(\"target_term\", \"\").strip()\n context_notes = row.get(\"context_notes\", \"\").strip() or None\n source_language = row.get(\"source_language\", \"\").strip() or default_source_language or \"und\"\n target_language = row.get(\"target_language\", \"\").strip() or default_target_language or \"und\"\n\n if not source_term or not target_term:\n result[\"errors\"].append({\n \"line\": row_idx + 2, # +2 for header and 1-indexed\n \"error\": \"Both source_term and target_term are required\",\n \"row\": row,\n })\n continue\n\n normalized = _normalize_term(source_term)\n\n if preview_only:\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == source_language,\n DictionaryEntry.target_language == target_language,\n )\n .first()\n )\n preview_row = {\n \"line\": row_idx + 2,\n \"source_term\": source_term,\n \"target_term\": target_term,\n \"context_notes\": context_notes,\n \"source_language\": source_language,\n \"target_language\": target_language,\n \"is_conflict\": existing is not None,\n \"existing_target_term\": existing.target_term if existing else None,\n }\n result[\"preview\"].append(preview_row)\n continue\n\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n DictionaryEntry.source_language == source_language,\n DictionaryEntry.target_language == target_language,\n )\n .first()\n )\n\n if existing:\n if on_conflict == \"overwrite\":\n existing.source_term = source_term\n existing.target_term = target_term\n existing.context_notes = context_notes\n existing.source_language = source_language\n existing.target_language = target_language\n result[\"updated\"] += 1\n elif on_conflict == \"keep_existing\":\n result[\"skipped\"] += 1\n else: # cancel\n result[\"errors\"].append({\n \"line\": row_idx + 2,\n \"error\": f\"Conflict on '{source_term}' and on_conflict='cancel'\",\n \"row\": row,\n })\n continue\n else:\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term,\n source_term_normalized=normalized,\n target_term=target_term,\n context_notes=context_notes,\n source_language=source_language,\n target_language=target_language,\n )\n db.add(entry)\n result[\"created\"] += 1\n\n except Exception as e:\n result[\"errors\"].append({\n \"line\": row_idx + 2,\n \"error\": str(e),\n \"row\": row,\n })\n\n if not preview_only:\n db.commit()\n\n logger.reflect(\"Import complete\", {\n \"dict_id\": dict_id,\n \"total\": result[\"total\"],\n \"created\": result[\"created\"],\n \"updated\": result[\"updated\"],\n \"skipped\": result[\"skipped\"],\n \"errors\": len(result[\"errors\"]),\n })\n return result\n # endregion DictionaryManager.import_entries\n\n # region DictionaryManager.export_entries [TYPE Function]\n # @PURPOSE Export all entries as CSV string with language columns.\n # @PRE dict_id exists.\n # @POST Returns CSV string with header: source_term, target_term, source_language, target_language, context_notes, context_data, usage_notes.\n @staticmethod\n def export_entries(\n db: Session, dict_id: str, delimiter: str = \",\",\n ) -> str:\n with belief_scope(\"DictionaryManager.export_entries\"):\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary not found: {dict_id}\")\n\n entries = (\n db.query(DictionaryEntry)\n .filter(DictionaryEntry.dictionary_id == dict_id)\n .order_by(DictionaryEntry.source_term.asc())\n .all()\n )\n\n output = io.StringIO()\n fieldnames = [\n \"source_term\", \"target_term\",\n \"source_language\", \"target_language\",\n \"context_notes\", \"context_data\", \"usage_notes\",\n ]\n writer = csv.DictWriter(output, fieldnames=fieldnames, delimiter=delimiter)\n writer.writeheader()\n\n for entry in entries:\n writer.writerow({\n \"source_term\": entry.source_term,\n \"target_term\": entry.target_term,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"context_notes\": entry.context_notes or \"\",\n \"context_data\": entry.context_data,\n \"usage_notes\": entry.usage_notes or \"\",\n })\n\n result = output.getvalue()\n logger.reflect(\"Entries exported\", {\"dict_id\": dict_id, \"count\": len(entries), \"delimiter\": repr(delimiter)})\n return result\n # endregion DictionaryManager.export_entries\n\n # region DictionaryManager.migrate_old_entries [TYPE Function]\n # @PURPOSE Migrate existing single-language dictionary entries to populate source_language and target_language.\n # @PRE db session is open.\n # @POST Entries with \"und\" source_language or target_language are updated with inferred values.\n # @SIDE_EFFECT Updates DictionaryEntry rows in bulk.\n @staticmethod\n def migrate_old_entries(db: Session) -> dict[str, int]:\n with belief_scope(\"DictionaryManager.migrate_old_entries\"):\n logger.reason(\"Starting migration of old dictionary entries\")\n migrated_source = 0\n migrated_target = 0\n skipped = 0\n\n # Find all entries that may need migration (source_language is \"und\" or target_language is \"und\")\n entries = (\n db.query(DictionaryEntry)\n .filter(\n (DictionaryEntry.source_language == \"und\") |\n (DictionaryEntry.target_language == \"und\")\n )\n .all()\n )\n\n for entry in entries:\n # Try to infer source_language from origin_source_language\n if entry.source_language == \"und\":\n if entry.origin_source_language:\n entry.source_language = entry.origin_source_language\n migrated_source += 1\n\n # Note: target_language inference from dictionary-level column was removed\n # because TerminologyDictionary.target_language was deprecated and dropped.\n\n if entry.source_language == \"und\" and entry.target_language == \"und\":\n skipped += 1\n\n db.commit()\n\n logger.reflect(\"Migration complete\", {\n \"migrated_source\": migrated_source,\n \"migrated_target\": migrated_target,\n \"skipped\": skipped,\n \"total_processed\": len(entries),\n })\n return {\n \"migrated_source\": migrated_source,\n \"migrated_target\": migrated_target,\n \"skipped\": skipped,\n \"total_processed\": len(entries),\n }\n # endregion DictionaryManager.migrate_old_entries\n\n # region DictionaryManager.filter_for_batch [TYPE Function]\n # @PURPOSE Scan batch texts for case-insensitive, word-boundary-aware matches against all dictionaries attached to a job,\n # optionally filtered by language pair. When row_context is provided, computes context-aware priority flags.\n # @PRE job_id exists and source_texts is a list of strings.\n # @POST Returns list of matched entries with match info, sorted by priority across attached dictionaries.\n # When row_context is given, each result includes a 'priority_match' boolean.\n # @SIDE_EFFECT Queries TranslationJobDictionary, TerminologyDictionary, and DictionaryEntry tables.\n @staticmethod\n def filter_for_batch(\n db: Session, source_texts: list[str], job_id: str,\n source_language: str | None = None,\n target_language: str | None = None,\n row_context: dict | None = None,\n ) -> list[dict[str, Any]]:\n with belief_scope(\"DictionaryManager.filter_for_batch\"):\n # Get dictionaries attached to this job\n job_dict_links = (\n db.query(TranslationJobDictionary)\n .filter(TranslationJobDictionary.job_id == job_id)\n .all()\n )\n if not job_dict_links:\n logger.reason(\"No dictionaries attached to job\", {\"job_id\": job_id})\n return []\n\n dict_ids = [jd.dictionary_id for jd in job_dict_links]\n\n # Get all active dictionaries\n dictionaries = (\n db.query(TerminologyDictionary)\n .filter(\n TerminologyDictionary.id.in_(dict_ids),\n TerminologyDictionary.is_active == True,\n )\n .all()\n )\n if not dictionaries:\n return []\n\n # Build dict_id -> dict_name lookup\n dict_map = {d.id: d.name for d in dictionaries}\n\n # Fetch all entries for these dictionaries, ordered by dictionary (priority order from link order)\n all_entries = (\n db.query(DictionaryEntry)\n .filter(DictionaryEntry.dictionary_id.in_(dict_ids))\n .order_by(DictionaryEntry.source_term.asc())\n .all()\n )\n\n if not all_entries:\n return []\n\n # PERFORMANCE NOTE: O(rows × entries) regex matching per batch.\n # Acceptable for current scale (50-100 rows × <1000 entries).\n # For larger batches (>500 rows or >5000 entries), consider\n # replacing with an Aho-Corasick automaton (e.g., pyahocorasick).\n matched: list[dict[str, Any]] = []\n seen_source_match: set = set()\n\n for text_idx, source_text in enumerate(source_texts):\n if not source_text:\n continue\n\n text_lower = source_text.lower()\n\n for entry in all_entries:\n norm = entry.source_term_normalized\n if not norm:\n continue\n\n # Apply language pair filtering\n if source_language is not None:\n src = source_language.strip().lower()\n entry_src = entry.source_language.strip().lower()\n if entry_src != src and entry_src != \"und\":\n continue\n if target_language is not None:\n tgt = target_language.strip().lower()\n entry_tgt = entry.target_language.strip().lower()\n if entry_tgt != tgt:\n continue\n\n # Word-boundary-aware matching\n # Build pattern: \\bterm\\b (case-insensitive)\n # Escape regex special chars in the search term\n escaped = re.escape(norm)\n pattern = re.compile(r\"\\b\" + escaped + r\"\\b\", re.IGNORECASE)\n\n if pattern.search(text_lower):\n match_key = (text_idx, entry.id)\n if match_key not in seen_source_match:\n seen_source_match.add(match_key)\n\n # Compute context-aware priority if row_context is available\n priority_match = False\n if row_context and entry.has_context and entry.context_data:\n similarity = ContextAwarePromptBuilder.compute_context_similarity(\n entry.context_data, row_context\n )\n priority_match = similarity >= 0.5\n\n matched.append({\n \"text_index\": text_idx,\n \"source_text\": source_text,\n \"entry_id\": entry.id,\n \"source_term\": entry.source_term,\n \"source_term_normalized\": entry.source_term_normalized,\n \"target_term\": entry.target_term,\n \"dictionary_id\": entry.dictionary_id,\n \"dictionary_name\": dict_map.get(entry.dictionary_id, \"\"),\n \"context_notes\": entry.context_notes,\n \"context_data\": entry.context_data,\n \"has_context\": entry.has_context,\n \"context_source\": entry.context_source,\n \"usage_notes\": entry.usage_notes,\n \"source_language\": entry.source_language,\n \"target_language\": entry.target_language,\n \"priority_match\": priority_match,\n })\n\n # Sort by dictionary link priority (order of dict_ids from link order)\n dict_priority = {d_id: idx for idx, d_id in enumerate(dict_ids)}\n matched.sort(key=lambda m: (dict_priority.get(m[\"dictionary_id\"], 999), m[\"text_index\"]))\n\n logger.reflect(\"Batch filter match complete\", {\n \"job_id\": job_id,\n \"source_texts\": len(source_texts),\n \"matches\": len(matched),\n \"dictionaries_used\": len(dictionaries),\n \"source_language\": source_language,\n \"target_language\": target_language,\n })\n return matched\n # endregion DictionaryManager.filter_for_batch\n\n\n # region DictionaryManager.submit_correction [TYPE Function]\n # @PURPOSE Submit a term correction from a run result. Validates language match, detects conflicts.\n # @PRE source_term, incorrect_target_term, corrected_target_term are non-empty. dict_id exists.\n # @POST Entry created or updated; origin tracking populated. Returns action + conflict info.\n # @SIDE_EFFECT Creates/updates DictionaryEntry row.\n @staticmethod\n def submit_correction(\n db: Session,\n dict_id: str,\n source_term: str,\n incorrect_target_term: str,\n corrected_target_term: str,\n origin_run_id: str | None = None,\n origin_row_key: str | None = None,\n origin_user_id: str | None = None,\n on_conflict: str = \"overwrite\",\n context_data: dict[str, Any] | None = None,\n usage_notes: str | None = None,\n keep_context: bool = True,\n ) -> dict[str, Any]:\n with belief_scope(\"DictionaryManager.submit_correction\"):\n # Validate dictionary exists\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary '{dict_id}' not found\")\n\n # Handle keep_context=False (user explicitly removed context)\n effective_context = context_data\n effective_context_source = \"auto\"\n if not keep_context:\n effective_context = None\n effective_context_source = \"manual\"\n\n normalized = _normalize_term(source_term)\n entry_src_lang = dictionary.source_dialect or \"und\"\n entry_tgt_lang = dictionary.target_dialect or \"und\"\n # Find existing entry: match exact language pair OR old-style \"und\"/\"und\" entries\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n or_(\n # Exact language pair match\n (DictionaryEntry.source_language == entry_src_lang) &\n (DictionaryEntry.target_language == entry_tgt_lang),\n # Old-style entries without language pair\n (DictionaryEntry.source_language == \"und\") &\n (DictionaryEntry.target_language == \"und\"),\n ),\n )\n .first()\n )\n\n result: dict[str, Any] = {\n \"source_term\": source_term,\n \"target_term\": corrected_target_term,\n \"action\": \"created\",\n \"entry_id\": None,\n \"conflict\": None,\n \"message\": None,\n }\n\n if existing:\n # Conflict detected\n if on_conflict == \"keep_existing\":\n result[\"action\"] = \"conflict_detected\"\n result[\"conflict\"] = {\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target_term,\n \"action\": \"keep_existing\",\n }\n result[\"message\"] = f\"Existing entry kept: '{existing.target_term}'\"\n return result\n elif on_conflict == \"overwrite\":\n existing.target_term = corrected_target_term.strip()\n if origin_run_id:\n existing.origin_run_id = origin_run_id\n if origin_row_key:\n existing.origin_row_key = origin_row_key\n if origin_user_id:\n existing.origin_user_id = origin_user_id\n if context_data is not None or not keep_context:\n existing.context_data = effective_context\n existing.has_context = bool(effective_context)\n existing.context_source = effective_context_source\n if usage_notes is not None:\n existing.usage_notes = usage_notes\n db.flush()\n result[\"action\"] = \"updated\"\n result[\"entry_id\"] = existing.id\n result[\"message\"] = f\"Entry updated from '{existing.target_term}' to '{corrected_target_term}'\"\n else: # cancel\n result[\"action\"] = \"skipped\"\n result[\"conflict\"] = {\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target_term,\n \"action\": \"cancel\",\n }\n result[\"message\"] = \"Correction cancelled by conflict mode\"\n return result\n else:\n # Create new entry — derive language pair from dictionary\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term.strip(),\n source_term_normalized=normalized,\n target_term=corrected_target_term.strip(),\n source_language=entry_src_lang,\n target_language=entry_tgt_lang,\n context_data=effective_context,\n usage_notes=usage_notes,\n has_context=bool(effective_context),\n context_source=effective_context_source if effective_context else None,\n origin_run_id=origin_run_id,\n origin_row_key=origin_row_key,\n origin_user_id=origin_user_id,\n )\n db.add(entry)\n db.flush()\n result[\"entry_id\"] = entry.id\n result[\"message\"] = f\"Entry created for '{source_term}' -> '{corrected_target_term}'\"\n\n db.commit()\n logger.reflect(\"Correction processed\", result)\n return result\n # endregion DictionaryManager.submit_correction\n\n # region DictionaryManager.submit_bulk_corrections [TYPE Function]\n # @PURPOSE Submit multiple term corrections atomically — all succeed or all fail.\n # @PRE corrections list is non-empty. dict_id exists.\n # @POST All corrections applied or none applied with conflict list.\n # @SIDE_EFFECT Creates/updates DictionaryEntry rows; commits once.\n @staticmethod\n def submit_bulk_corrections(\n db: Session,\n dict_id: str,\n corrections: list[dict[str, Any]],\n origin_user_id: str | None = None,\n ) -> dict[str, Any]:\n with belief_scope(\"DictionaryManager.submit_bulk_corrections\"):\n # Validate dictionary first\n dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first()\n if not dictionary:\n raise ValueError(f\"Dictionary '{dict_id}' not found\")\n\n results: list[dict[str, Any]] = []\n conflicts: list[dict[str, Any]] = []\n all_ok = True\n\n for corr in corrections:\n source_term = corr.get(\"source_term\", \"\").strip()\n incorrect_target = corr.get(\"incorrect_target_term\", \"\").strip()\n corrected_target = corr.get(\"corrected_target_term\", \"\").strip()\n\n if not source_term or not corrected_target:\n results.append({\n \"source_term\": source_term,\n \"action\": \"error\",\n \"message\": \"source_term and corrected_target_term are required\",\n })\n all_ok = False\n continue\n\n normalized = _normalize_term(source_term)\n bulk_src_lang = dictionary.source_dialect or \"und\"\n bulk_tgt_lang = dictionary.target_dialect or \"und\"\n existing = (\n db.query(DictionaryEntry)\n .filter(\n DictionaryEntry.dictionary_id == dict_id,\n DictionaryEntry.source_term_normalized == normalized,\n or_(\n (DictionaryEntry.source_language == bulk_src_lang) &\n (DictionaryEntry.target_language == bulk_tgt_lang),\n (DictionaryEntry.source_language == \"und\") &\n (DictionaryEntry.target_language == \"und\"),\n ),\n )\n .first()\n )\n\n if existing:\n on_conflict = corr.get(\"on_conflict\", \"overwrite\")\n if on_conflict == \"keep_existing\":\n conflicts.append({\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target,\n \"action\": \"keep_existing\",\n })\n results.append({\n \"source_term\": source_term,\n \"action\": \"conflict_detected\",\n \"message\": f\"Existing entry kept: '{existing.target_term}'\",\n })\n continue\n elif on_conflict == \"overwrite\":\n existing.target_term = corrected_target\n existing.origin_user_id = origin_user_id\n results.append({\n \"source_term\": source_term,\n \"action\": \"updated\",\n \"entry_id\": existing.id,\n \"message\": f\"Updated to '{corrected_target}'\",\n })\n else:\n conflicts.append({\n \"source_term\": source_term,\n \"existing_target_term\": existing.target_term,\n \"submitted_target_term\": corrected_target,\n \"action\": \"cancel\",\n })\n results.append({\n \"source_term\": source_term,\n \"action\": \"skipped\",\n \"message\": \"Cancelled by conflict mode\",\n })\n else:\n entry = DictionaryEntry(\n dictionary_id=dict_id,\n source_term=source_term,\n source_term_normalized=normalized,\n target_term=corrected_target,\n source_language=bulk_src_lang,\n target_language=bulk_tgt_lang,\n origin_run_id=corr.get(\"origin_run_id\"),\n origin_row_key=corr.get(\"origin_row_key\"),\n origin_user_id=origin_user_id,\n )\n db.add(entry)\n results.append({\n \"source_term\": source_term,\n \"action\": \"created\",\n \"message\": f\"Entry created for '{source_term}' -> '{corrected_target}'\",\n })\n\n if conflicts and not all_ok:\n # Rollback — all failed\n db.rollback()\n return {\n \"status\": \"conflicts\",\n \"results\": results,\n \"conflicts\": conflicts,\n \"message\": \"Conflicts detected. Resolve before retrying.\",\n }\n\n db.commit()\n\n return {\n \"status\": \"completed\",\n \"results\": results,\n \"conflicts\": conflicts,\n \"total\": len(corrections),\n \"applied\": sum(1 for r in results if r[\"action\"] in (\"created\", \"updated\")),\n }\n # endregion DictionaryManager.submit_bulk_corrections\n\n\n# #endregion DictionaryManager\n" }, { "contract_id": "TranslationEventLog", @@ -49839,7 +49099,7 @@ "contract_type": "Module", "file_path": "backend/src/plugins/translate/executor.py", "start_line": 1, - "end_line": 1429, + "end_line": 1974, "tier": "TIER_3", "complexity": 5, "metadata": { @@ -49895,14 +49155,14 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslationExecutor [C:5] [TYPE Module] [SEMANTICS sqlalchemy, tenacity, translate, insert, llm-retry]\n# @BRIEF Process translation in batches: fetch source rows, call LLM, persist TranslationBatch and TranslationRecord rows.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [TranslationBatch]\n# @RELATION DEPENDS_ON -> [TranslationRecord]\n# @RELATION DEPENDS_ON -> [TranslationRun]\n# @RELATION DEPENDS_ON -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [DictionaryManager]\n# @RELATION DEPENDS_ON -> [TranslationPreview]\n# @PRE: Valid TranslationRun with job configuration. DB session is available.\n# @POST: TranslationBatch and TranslationRecord rows are created. Run status is updated.\n# @SIDE_EFFECT: Calls LLM provider; creates DB rows; updates run statistics.\n# @DATA_CONTRACT Input: TranslationRun + Job -> Output: updated Run with batch/record rows\n# @INVARIANT Batch processing is independent — one batch failure does not affect others.\n# @RATIONALE: Batch processing with retry — independent batches allow partial recovery.\n# @REJECTED: Single monolithic LLM call — would lose all progress on any failure.\n\nimport json\nimport time\nimport uuid\nfrom collections.abc import Callable\nfrom datetime import UTC, datetime\nfrom typing import Any\n\nfrom sqlalchemy.orm import Session, joinedload\n\nfrom ...core.config_manager import ConfigManager\nfrom ...core.logger import belief_scope, logger\nfrom ...models.translate import (\n TranslationBatch,\n TranslationJob,\n TranslationLanguage,\n TranslationPreviewRecord,\n TranslationPreviewSession,\n TranslationRecord,\n TranslationRun,\n TranslationRunLanguageStats,\n)\nfrom ...services.llm_prompt_templates import render_prompt\nfrom ...services.llm_provider import LLMProviderService\nfrom ._token_budget import DEFAULT_CONTEXT_WINDOW, DEFAULT_MAX_OUTPUT_TOKENS, estimate_token_budget\nfrom .dictionary import DictionaryManager\nfrom .preview import DEFAULT_EXECUTION_PROMPT_TEMPLATE\nfrom .prompt_builder import ContextAwarePromptBuilder\nfrom .sql_generator import SQLGenerator\nfrom .superset_executor import SupersetSqlLabExecutor\n\n# #region MAX_RETRIES_PER_BATCH [TYPE Constant]\nMAX_RETRIES_PER_BATCH = 3\n# #endregion MAX_RETRIES_PER_BATCH\n\n# #region MAX_ROWS_PER_RUN [TYPE Constant]\n# Safety cap: without it, a datasource with thousands of rows blocks the single\n# uvicorn worker for minutes/hours. Preview uses sample_size (5-10). Full run\n# should stay within reasonable bounds.\nMAX_ROWS_PER_RUN = 10000\n# #endregion MAX_ROWS_PER_RUN\n\n# #region TranslationExecutor [C:4] [TYPE Class]\n# @BRIEF Process translation batches: fetch source rows, filter dict, call LLM, persist results.\n# @PRE: DB session and config manager available.\n# @POST: Batches and records created with status tracking.\n# @SIDE_EFFECT: LLM API calls; DB writes.\nclass TranslationExecutor:\n\n def __init__(\n self,\n db: Session,\n config_manager: ConfigManager,\n current_user: str | None = None,\n on_batch_progress: Callable[[str, int, int, int, int], None] | None = None,\n ):\n self.db = db\n self.config_manager = config_manager\n self.current_user = current_user\n self.on_batch_progress = on_batch_progress\n self._current_run_id: str | None = None\n self._preview_edits_cache: dict[str, dict[str, str]] | None = None # key_hash -> {lang_code: edited_value}\n\n # region execute_run [TYPE Function]\n # @PURPOSE: Run full translation execution for a TranslationRun.\n # @PRE: run is in PENDING or RUNNING status with valid job config.\n # @POST: Run is populated with batches and records.\n # @SIDE_EFFECT: LLM API calls; DB batch writes.\n def execute_run(\n self,\n run: TranslationRun,\n llm_progress_callback: Callable[[str, int, int, int], None] | None = None,\n language_stats_map: dict[str, TranslationRunLanguageStats] | None = None,\n ) -> TranslationRun:\n with belief_scope(\"TranslationExecutor.execute_run\"):\n job = self.db.query(TranslationJob).filter(TranslationJob.id == run.job_id).first()\n if not job:\n raise ValueError(f\"Job '{run.job_id}' not found for run '{run.id}'\")\n\n logger.reason(\"Starting translation execution\", {\n \"run_id\": run.id,\n \"job_id\": job.id,\n \"batch_size\": job.batch_size,\n })\n\n # Load preview edits for carry-forward\n self._load_preview_edits(job.id)\n\n # Mark run as RUNNING\n run.status = \"RUNNING\"\n run.started_at = datetime.now(UTC)\n self.db.flush()\n\n # Determine whether this is a full translation (all rows) or incremental (new/changed only)\n full_translation = False\n if run.config_snapshot and isinstance(run.config_snapshot, dict):\n full_translation = run.config_snapshot.get(\"full_translation\", False)\n\n # Fetch source rows from the datasource\n source_rows = self._fetch_source_rows(job.id, run.id)\n if not source_rows:\n logger.explore(\"No source rows to translate\", {\"run_id\": run.id})\n run.status = \"COMPLETED\"\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n return run\n\n # Apply new-key-only filtering for scheduled runs OR manual incremental runs\n if run.trigger_type == \"new_key_only\" or (not full_translation and run.trigger_type == \"manual\"):\n source_rows = self._filter_new_keys(job, run.id, source_rows)\n if not source_rows:\n logger.reason(\"run_noop — no new rows to translate\", {\n \"job_id\": job.id, \"run_id\": run.id,\n })\n run.status = \"COMPLETED\"\n run.insert_status = \"skipped\"\n run.total_records = 0\n run.completed_at = datetime.now(UTC)\n self.db.commit()\n return\n\n total_rows = len(source_rows)\n run.total_records = total_rows\n\n # Split into batches\n batch_size = job.batch_size or 50\n batches = [\n source_rows[i:i + batch_size]\n for i in range(0, total_rows, batch_size)\n ]\n\n logger.reason(f\"Processing {len(batches)} batches\", {\n \"run_id\": run.id,\n \"total_rows\": total_rows,\n \"batch_size\": batch_size,\n })\n\n successful_records = 0\n failed_records = 0\n skipped_records = 0\n\n for batch_idx, batch_rows in enumerate(batches):\n batch_result = self._process_batch(\n job=job,\n run_id=run.id,\n batch_index=batch_idx,\n batch_rows=batch_rows,\n )\n successful_records += batch_result[\"successful\"]\n failed_records += batch_result[\"failed\"]\n skipped_records += batch_result[\"skipped\"]\n\n # Update run stats incrementally\n run.successful_records = successful_records\n run.failed_records = failed_records\n run.skipped_records = skipped_records\n\n # Commit after each batch to release row locks and make RUNNING\n # status + batch progress visible to other DB sessions (frontend polling).\n self.db.commit()\n\n # Incremental INSERT into target table after each batch,\n # so data appears incrementally in the target view/table\n # without waiting for the entire run to complete.\n batch_id = batch_result.get(\"batch_id\")\n if batch_id and batch_result[\"successful\"] > 0:\n try:\n self._insert_batch_to_target(job, batch_id, run.id)\n except Exception as e:\n logger.explore(\"Batch INSERT failed (non-fatal, continuing)\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n\n # Re-fetch run after commit to check for cancellation flag set by\n # cancel_run() fallback (direct SQL UPDATE of error_message).\n self.db.refresh(run)\n if run.error_message == \"CANCEL_REQUESTED\":\n logger.reason(\"Cancellation requested — stopping batch processing\", {\n \"run_id\": run.id,\n \"batch_index\": batch_idx,\n })\n run.status = \"CANCELLED\"\n run.completed_at = datetime.now(UTC)\n run.error_message = None # Clear the orchestration flag\n self.db.commit()\n return run\n\n # Update per-language statistics incrementally after each batch\n # so the frontend shows real-time per-language counts for RUNNING runs.\n if language_stats_map and batch_result[\"successful\"] > 0:\n try:\n self._update_language_stats_incremental(run.id, language_stats_map)\n except Exception as e:\n logger.explore(\"Language stats update failed (non-fatal)\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n\n if self.on_batch_progress:\n self.on_batch_progress(\n run.id, batch_idx + 1, len(batches),\n successful_records, total_rows,\n )\n\n # Update final run status\n if failed_records == 0 and skipped_records == 0:\n run.status = \"COMPLETED\"\n elif successful_records == 0:\n run.status = \"FAILED\"\n else:\n run.status = \"COMPLETED\" # Partial success\n\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n\n logger.reflect(\"Translation execution complete\", {\n \"run_id\": run.id,\n \"status\": run.status,\n \"total\": total_rows,\n \"successful\": successful_records,\n \"failed\": failed_records,\n \"skipped\": skipped_records,\n })\n\n return run\n # endregion execute_run\n\n # region _fetch_source_rows [TYPE Function]\n # @PURPOSE: Fetch full source dataset from Superset (via datasource) for full translation.\n # @PRE: job_id exists. Job may have source_datasource_id for full fetch.\n # @POST: Returns list of dicts with source data (all rows from the source datasource).\n # @SIDE_EFFECT: Makes HTTP call to Superset chart data API when datasource is configured.\n def _fetch_source_rows(self, job_id: str, run_id: str) -> list[dict[str, Any]]:\n with belief_scope(\"TranslationExecutor._fetch_source_rows\"):\n job = self.db.query(TranslationJob).filter(TranslationJob.id == job_id).first()\n\n # If source_datasource_id is configured, fetch ALL rows from the Superset chart data API\n if job and job.source_datasource_id:\n try:\n logger.reason(\"Fetching full dataset from Superset datasource\", {\n \"run_id\": run_id,\n \"datasource_id\": job.source_datasource_id,\n \"environment_id\": job.environment_id,\n })\n\n # Determine environment\n environments = self.config_manager.get_environments()\n target_env_id = job.environment_id or job.source_dialect or \"\"\n env_config = next(\n (e for e in environments if e.id == target_env_id),\n None,\n )\n if not env_config and environments:\n env_config = environments[0]\n\n if env_config:\n from ...core.superset_client import SupersetClient\n client = SupersetClient(env_config)\n\n # Fetch dataset detail to build proper query context\n dataset_detail = client.get_dataset_detail(int(job.source_datasource_id))\n\n # Build query context (same approach as preview but without row_limit)\n query_context = client.build_dataset_preview_query_context(\n dataset_id=int(job.source_datasource_id),\n dataset_record=dataset_detail,\n template_params={},\n effective_filters=[],\n )\n\n # Use a safety cap instead of removing row_limit entirely.\n # Preview uses sample_size (5-10). Full runs should not fetch unlimited rows\n # because each batch of 20 rows × 4 languages takes ~40s of LLM time.\n queries = query_context.get(\"queries\", [])\n if queries:\n queries[0][\"row_limit\"] = MAX_ROWS_PER_RUN\n queries[0].pop(\"result_type\", None)\n queries[0][\"metrics\"] = []\n query_context[\"result_type\"] = \"samples\"\n form_data = query_context.get(\"form_data\", {})\n form_data.pop(\"query_mode\", None)\n\n response = client.network.request(\n method=\"POST\",\n endpoint=\"/api/v1/chart/data\",\n data=json.dumps(query_context),\n headers={\"Content-Type\": \"application/json\"},\n )\n\n # Extract rows\n rows = self._extract_chart_data_rows(response)\n\n if rows:\n logger.reason(f\"Fetched {len(rows)} rows from Superset datasource\", {\n \"run_id\": run_id,\n })\n # Map rows to source_rows format\n source_rows = []\n for idx, row in enumerate(rows):\n source_data_dict = dict(row) if row else None\n source_text = str(row.get(job.translation_column, \"\")) if job.translation_column else json.dumps(row)\n source_rows.append({\n \"row_index\": str(idx),\n \"source_text\": source_text,\n \"approved_translation\": None,\n \"source_object_name\": f\"Row {idx}\",\n \"source_data\": source_data_dict,\n })\n return source_rows\n else:\n logger.explore(\"Superset datasource returned no rows\", {\n \"run_id\": run_id,\n \"datasource_id\": job.source_datasource_id,\n })\n else:\n logger.explore(\"No environment config found for datasource fetch\", {\n \"env_id\": target_env_id,\n })\n except Exception as e:\n logger.explore(\"Failed to fetch full dataset from Superset, falling back to preview\", {\n \"run_id\": run_id,\n \"error\": str(e),\n })\n # Fall through to preview-based fetch\n\n # Fallback: get the latest APPLIED preview session\n session = (\n self.db.query(TranslationPreviewSession)\n .filter(\n TranslationPreviewSession.job_id == job_id,\n TranslationPreviewSession.status == \"APPLIED\",\n )\n .order_by(TranslationPreviewSession.created_at.desc())\n .first()\n )\n if not session:\n logger.explore(\"No accepted preview session found\", {\"job_id\": job_id})\n return []\n\n # Fetch APPROVED or all records from the session\n records = (\n self.db.query(TranslationPreviewRecord)\n .filter(\n TranslationPreviewRecord.session_id == session.id,\n TranslationPreviewRecord.status.in_([\"APPROVED\", \"PENDING\"]),\n )\n .all()\n )\n\n source_rows = []\n for rec in records:\n source_data_dict = None\n if hasattr(rec, \"source_data\") and rec.source_data:\n source_data_dict = dict(rec.source_data)\n source_rows.append({\n \"row_index\": rec.source_object_id or \"0\",\n \"source_text\": rec.source_sql or \"\",\n \"approved_translation\": rec.target_sql if rec.status == \"APPROVED\" else None,\n \"source_object_name\": rec.source_object_name or \"\",\n \"source_data\": source_data_dict,\n })\n\n logger.reason(f\"Fetched {len(source_rows)} source rows from preview fallback\", {\n \"run_id\": run_id,\n \"session_id\": session.id,\n })\n return source_rows\n # endregion _fetch_source_rows\n\n # region _filter_new_keys [TYPE Function]\n # @PURPOSE: Filter source rows to only include those with keys absent from the last successful run.\n # @PRE: job and run are persisted; source_rows is a list of dicts with source_data containing key columns.\n # @POST: Returns filtered list of source rows with only new keys. Logs skip count.\n # @SIDE_EFFECT: Queries TranslationRecord from previous runs; no writes.\n def _filter_new_keys(self, job, run_id: str, source_rows: list) -> list:\n with belief_scope(\"TranslationExecutor._filter_new_keys\"):\n # Find most recent COMPLETED run with successful insert for this job\n prev_run = (\n self.db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job.id,\n TranslationRun.status == \"COMPLETED\",\n TranslationRun.insert_status == \"succeeded\",\n TranslationRun.id != run_id,\n )\n .order_by(TranslationRun.created_at.desc())\n .first()\n )\n if not prev_run:\n logger.reason(\"No prior successful run — all keys treated as new\", {\n \"job_id\": job.id,\n })\n return source_rows\n\n # Get successful records from that run\n prev_records = (\n self.db.query(TranslationRecord)\n .filter(\n TranslationRecord.run_id == prev_run.id,\n TranslationRecord.status == \"SUCCESS\",\n )\n .all()\n )\n if not prev_records:\n return source_rows\n\n # Build set of already-translated composite keys\n key_cols = job.target_key_cols or job.source_key_cols or []\n if not key_cols:\n logger.explore(\"No key columns configured — skipping new-key-only filter\",\n {\"job_id\": job.id})\n return source_rows\n existing_keys = set()\n for rec in prev_records:\n sd = rec.source_data or {}\n key_tuple = tuple(str(sd.get(k, \"\")) for k in key_cols)\n existing_keys.add(key_tuple)\n\n # Filter: keep only rows whose keys are NOT in the existing set\n filtered = []\n skipped = 0\n for row in source_rows:\n sd = row.get(\"source_data\", {}) or {}\n key_tuple = tuple(str(sd.get(k, \"\")) for k in key_cols)\n if key_tuple not in existing_keys:\n filtered.append(row)\n else:\n skipped += 1\n\n logger.reason(f\"New-key-only filter: {len(source_rows)} total → {len(filtered)} new, {skipped} skipped\", {\n \"job_id\": job.id,\n \"prev_run_id\": prev_run.id,\n \"key_cols\": key_cols,\n })\n return filtered\n # endregion _filter_new_keys\n\n # region _extract_chart_data_rows [TYPE Function]\n # @PURPOSE: Extract data rows from Superset chart data API response.\n # @POST: Returns list of dicts with column-value pairs.\n @staticmethod\n def _extract_chart_data_rows(response: dict[str, Any]) -> list[dict[str, Any]]:\n result = response.get(\"result\")\n if isinstance(result, list):\n for item in result:\n if isinstance(item, dict):\n data = item.get(\"data\")\n if isinstance(data, list) and data:\n return data\n if isinstance(result, dict):\n data = result.get(\"data\")\n if isinstance(data, list) and data:\n return data\n data = response.get(\"data\")\n if isinstance(data, list) and data:\n return data\n if isinstance(result, list):\n return result\n return []\n # endregion _extract_chart_data_rows\n\n # region _load_preview_edits [TYPE Function]\n # @PURPOSE: Load user edits from accepted preview session for carry-forward.\n # @PRE: job_id exists.\n # @POST: Populates _preview_edits_cache with key_hash -> {lang_code: edited_value}.\n # @SIDE_EFFECT: Queries TranslationPreviewLanguage and TranslationPreviewRecord.\n def _load_preview_edits(self, job_id: str) -> None:\n \"\"\"Load preview edits for carry-forward during execution.\"\"\"\n from ...models.translate import TranslationPreviewLanguage, TranslationPreviewRecord, TranslationPreviewSession\n\n with belief_scope(\"TranslationExecutor._load_preview_edits\"):\n session = (\n self.db.query(TranslationPreviewSession)\n .filter(\n TranslationPreviewSession.job_id == job_id,\n TranslationPreviewSession.status == \"APPLIED\",\n )\n .order_by(TranslationPreviewSession.created_at.desc())\n .first()\n )\n if not session:\n logger.reason(\"No applied preview session found — no edits to carry forward\", {\n \"job_id\": job_id,\n })\n self._preview_edits_cache = {}\n return\n\n records = (\n self.db.query(TranslationPreviewRecord)\n .filter(TranslationPreviewRecord.session_id == session.id)\n .all()\n )\n\n edits: dict[str, dict[str, str]] = {}\n for rec in records:\n if not rec.source_data:\n continue\n # Compute key hash from source_data\n key_hash = self._compute_key_hash(rec.source_data)\n edited_langs: dict[str, str] = {}\n for lang_entry in (rec.languages or []):\n if lang_entry.status in (\"edited\", \"approved\") and lang_entry.user_edit:\n edited_langs[lang_entry.language_code] = lang_entry.final_value or lang_entry.user_edit\n logger.reason(\"Carrying forward preview edit\", {\n \"key_hash\": key_hash,\n \"language_code\": lang_entry.language_code,\n })\n if edited_langs:\n edits[key_hash] = edited_langs\n\n self._preview_edits_cache = edits\n logger.reason(f\"Loaded {len(edits)} preview edits for carry-forward\", {\n \"job_id\": job_id,\n })\n # endregion _load_preview_edits\n\n # region _compute_key_hash [TYPE Function]\n # @PURPOSE: Compute a stable hash from source_data dict for matching preview edits.\n @staticmethod\n def _compute_key_hash(source_data: dict) -> str:\n import hashlib\n stable = json.dumps(source_data, sort_keys=True)\n return hashlib.sha256(stable.encode()).hexdigest()[:16]\n # endregion _compute_key_hash\n\n # region _process_batch [TYPE Function]\n # @PURPOSE: Process a single batch: filter dict, build prompt, call LLM, persist records.\n # @PRE: job and batch_rows are valid.\n # @POST: TranslationBatch and TranslationRecord rows are created.\n # @SIDE_EFFECT: LLM API call.\n def _process_batch(\n self,\n job: TranslationJob,\n run_id: str,\n batch_index: int,\n batch_rows: list[dict[str, Any]],\n ) -> dict[str, int]:\n with belief_scope(\"TranslationExecutor._process_batch\"):\n batch_start = time.monotonic()\n\n # Create batch record\n batch = TranslationBatch(\n id=str(uuid.uuid4()),\n run_id=run_id,\n batch_index=batch_index,\n status=\"RUNNING\",\n total_records=len(batch_rows),\n started_at=datetime.now(UTC),\n )\n self.db.add(batch)\n self.db.flush()\n batch_id = batch.id\n\n result = {\"successful\": 0, \"failed\": 0, \"skipped\": 0, \"retries\": 0}\n\n # Extract source texts for dict filtering\n source_texts = [\n row.get(\"source_text\", \"\")\n for row in batch_rows\n if row.get(\"source_text\")\n ]\n\n # Filter dictionary entries with row context for priority matching\n row_context = batch_rows[0].get(\"source_data\") if batch_rows else None\n dict_matches = DictionaryManager.filter_for_batch(\n self.db, source_texts, job.id,\n row_context=row_context,\n )\n\n # For each row, determine if we need LLM translation or can use approved/preview edit\n rows_for_llm = []\n pre_translated = []\n\n for row in batch_rows:\n if row.get(\"approved_translation\"):\n pre_translated.append(row)\n continue\n\n # Check for preview edits carry-forward\n source_data = row.get(\"source_data\") or {}\n if source_data and self._preview_edits_cache:\n key_hash = self._compute_key_hash(source_data)\n preview_edit = self._preview_edits_cache.get(key_hash)\n if preview_edit:\n # Use the first edited language's value as the approved translation\n first_edit = next(iter(preview_edit.values()), None)\n if first_edit:\n row[\"approved_translation\"] = first_edit\n logger.reason(\"Using preview edit carry-forward\", {\n \"key_hash\": key_hash,\n \"langs\": list(preview_edit.keys()),\n })\n pre_translated.append(row)\n continue\n\n rows_for_llm.append(row)\n\n # Handle pre-translated (approved) rows\n target_languages = job.target_languages or [job.target_dialect or \"en\"]\n if not isinstance(target_languages, list):\n target_languages = [str(target_languages)]\n\n for row in pre_translated:\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=row.get(\"source_text\", \"\"),\n target_sql=row.get(\"approved_translation\"),\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"SUCCESS\",\n )\n self.db.add(record)\n\n # Create per-language entry for each target language (pre-approved)\n for lang_code in target_languages:\n lang_entry = TranslationLanguage(\n id=str(uuid.uuid4()),\n record_id=record.id,\n language_code=lang_code,\n source_language_detected=\"und\",\n translated_value=row.get(\"approved_translation\"),\n final_value=row.get(\"approved_translation\"),\n status=\"translated\",\n needs_review=False,\n )\n self.db.add(lang_entry)\n result[\"successful\"] += 1\n\n # Process rows needing LLM translation\n if rows_for_llm:\n # Check token budget for this batch to determine safe max_tokens\n token_budget = estimate_token_budget(\n source_rows=rows_for_llm,\n target_languages=target_languages,\n source_column=\"source_text\",\n context_columns=None, # Context is embedded in dict, not separate column\n dictionary_entries=dict_matches,\n batch_size=len(rows_for_llm),\n context_window=DEFAULT_CONTEXT_WINDOW,\n max_output_tokens=DEFAULT_MAX_OUTPUT_TOKENS,\n )\n if token_budget[\"warning\"]:\n logger.explore(\"Token budget warning for batch\", {\n \"batch_id\": batch_id,\n \"batch_index\": batch_index,\n \"warning\": token_budget[\"warning\"],\n })\n\n llm_result = self._call_llm_for_batch(\n job=job,\n run_id=run_id,\n batch_rows=rows_for_llm,\n dict_matches=dict_matches,\n batch_id=batch_id,\n max_tokens=token_budget[\"max_output_needed\"],\n )\n result[\"successful\"] += llm_result[\"successful\"]\n result[\"failed\"] += llm_result[\"failed\"]\n result[\"skipped\"] += llm_result[\"skipped\"]\n result[\"retries\"] += llm_result.get(\"retries\", 0)\n\n # Update batch status\n batch.successful_records = result[\"successful\"]\n batch.failed_records = result[\"failed\"]\n batch.completed_at = datetime.now(UTC)\n batch.status = \"COMPLETED\" if result[\"failed\"] == 0 else \"COMPLETED_WITH_ERRORS\"\n self.db.flush()\n\n batch_latency = int((time.monotonic() - batch_start) * 1000)\n logger.reason(f\"Batch {batch_index} complete\", {\n \"batch_id\": batch_id,\n \"latency_ms\": batch_latency,\n **result,\n })\n\n return {**result, \"batch_id\": batch_id}\n # endregion _process_batch\n\n # region _insert_batch_to_target [TYPE Function]\n # @PURPOSE: Insert successful records from a single batch into the target table via Superset SQL Lab.\n # @PRE: batch has committed TranslationRecords with status SUCCESS. job has target_table configured.\n # @POST: Per record, N+1 rows are INSERTED: 1 original + N translations.\n # Context columns are bundled into JSON in the `context` column.\n # `is_original=1` marks the source-language row.\n # @SIDE_EFFECT: HTTP call to Superset SQL Lab API. Writes to target database.\n def _insert_batch_to_target(\n self,\n job: TranslationJob,\n batch_id: str,\n run_id: str,\n ) -> None:\n with belief_scope(\"TranslationExecutor._insert_batch_to_target\"):\n records = (\n self.db.query(TranslationRecord)\n .options(joinedload(TranslationRecord.languages))\n .filter(\n TranslationRecord.batch_id == batch_id,\n TranslationRecord.status == \"SUCCESS\",\n TranslationRecord.target_sql.isnot(None),\n )\n .all()\n )\n\n if not records:\n return\n\n effective_target = job.target_column or job.translation_column\n primary_language = (job.target_languages or [\"en\"])[0]\n\n # Columns that exist in the target ClickHouse table\n columns = []\n if job.target_key_cols:\n columns.extend(job.target_key_cols)\n if effective_target:\n columns.append(effective_target)\n if job.target_language_column:\n columns.append(job.target_language_column)\n if job.target_source_column:\n columns.append(job.target_source_column)\n if job.target_source_language_column:\n columns.append(job.target_source_language_column)\n columns.append(\"context\")\n columns.append(\"is_original\")\n # Deduplicate while preserving order\n seen: set[str] = set()\n deduped: list[str] = []\n for c in columns:\n if c and c not in seen:\n deduped.append(c)\n seen.add(c)\n columns = deduped\n\n # Keys for the context JSON: context_columns + original translation_column\n context_keys = list(job.context_columns or [])\n if job.translation_column and job.translation_column != effective_target and job.translation_column not in context_keys:\n context_keys.append(job.translation_column)\n\n rows_for_sql: list[dict[str, object]] = []\n for rec in records:\n source_data = rec.source_data or {}\n\n # Detect source language from first TranslationLanguage entry\n detected_src_lang = \"und\"\n if rec.languages and len(rec.languages) > 0:\n detected_src_lang = rec.languages[0].source_language_detected or \"und\"\n\n # Build context JSON: all extra columns the user configured\n context_data: dict[str, str] = {}\n for key in context_keys:\n val = source_data.get(key)\n context_data[key] = str(val) if val is not None else \"\"\n\n # ── Shared base row (columns common to original and all translations) ──\n base_row: dict[str, object] = {}\n\n # Key columns (report_date, document_number)\n if job.target_key_cols:\n for k in job.target_key_cols:\n base_row[k] = source_data.get(k, \"\")\n\n # Source text: original\n if job.target_source_column:\n base_row[job.target_source_column] = rec.source_sql or \"\"\n\n # Source language (same for all rows of this record)\n if job.target_source_language_column:\n base_row[job.target_source_language_column] = detected_src_lang\n\n # Context JSON string\n base_row[\"context\"] = json.dumps(context_data, ensure_ascii=False)\n\n # ── 1. ORIGINAL row (is_original = 1) ──\n original_row = dict(base_row)\n if effective_target:\n original_row[effective_target] = rec.source_sql or \"\"\n if job.target_language_column:\n original_row[job.target_language_column] = detected_src_lang\n original_row[\"is_original\"] = 1\n rows_for_sql.append(original_row)\n\n # ── 2. TRANSLATION rows (is_original = 0) ──\n # Skip language that matches the source — the original row already covers it\n if rec.languages and len(rec.languages) > 0:\n for lang in rec.languages:\n if lang.language_code == detected_src_lang:\n continue\n trans_row = dict(base_row)\n trans_value = lang.final_value or lang.translated_value or \"\"\n if effective_target:\n trans_row[effective_target] = trans_value\n if job.target_language_column:\n trans_row[job.target_language_column] = lang.language_code\n trans_row[\"is_original\"] = 0\n rows_for_sql.append(trans_row)\n else:\n # Fallback: no per-language data → single translation row with primary language\n fallback_row = dict(base_row)\n if effective_target:\n fallback_row[effective_target] = rec.target_sql or \"\"\n if job.target_language_column:\n fallback_row[job.target_language_column] = primary_language\n fallback_row[\"is_original\"] = 0\n rows_for_sql.append(fallback_row)\n\n if not columns:\n columns = [effective_target or \"translated_text\"]\n rows_for_sql = [{columns[0]: rec.target_sql or \"\"} for rec in records]\n\n try:\n env_id = job.environment_id or job.source_dialect or \"\"\n executor = SupersetSqlLabExecutor(self.config_manager, env_id)\n executor.resolve_database_id(target_database_id=job.target_database_id)\n real_backend = executor.get_database_backend()\n except Exception as e:\n logger.explore(\"Failed to resolve database backend for batch insert\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n real_backend = None\n\n dialect = real_backend or job.database_dialect or job.target_dialect or \"postgresql\"\n\n try:\n sql, row_count = SQLGenerator.generate(\n dialect=dialect,\n target_schema=job.target_schema,\n target_table=job.target_table or \"translated_data\",\n columns=columns,\n rows=rows_for_sql,\n key_columns=job.target_key_cols,\n upsert_strategy=job.upsert_strategy or \"MERGE\",\n )\n except ValueError as e:\n logger.explore(\"SQL generation failed for batch\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n return\n\n try:\n result = executor.execute_and_poll(\n sql=sql,\n max_polls=30,\n poll_interval_seconds=2.0,\n )\n except Exception as e:\n logger.explore(\"Superset SQL submission failed for batch\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n return\n\n logger.reason(f\"Batch {batch_id[:12]} inserted {row_count} rows\", {\n \"batch_id\": batch_id,\n \"rows\": row_count,\n \"status\": result.get(\"status\"),\n })\n # endregion _insert_batch_to_target\n\n # region _update_language_stats_incremental [TYPE Function]\n # @PURPOSE: Update per-language TranslationRunLanguageStats incrementally after each batch.\n # @PRE: language_stats_map has entries for all target languages.\n # @POST: Language stat objects updated with counts from committed TranslationLanguage rows.\n # @SIDE_EFFECT: Mutates ORM objects; caller must commit.\n def _update_language_stats_incremental(\n self,\n run_id: str,\n language_stats_map: dict[str, TranslationRunLanguageStats],\n ) -> None:\n with belief_scope(\"TranslationExecutor._update_language_stats_incremental\"):\n records = (\n self.db.query(TranslationRecord)\n .filter(TranslationRecord.run_id == run_id)\n .all()\n )\n record_ids = [r.id for r in records]\n if not record_ids:\n return\n\n lang_entries = (\n self.db.query(TranslationLanguage)\n .filter(TranslationLanguage.record_id.in_(record_ids))\n .all()\n )\n\n from collections import defaultdict\n agg: dict[str, dict[str, int]] = defaultdict(\n lambda: {\"total\": 0, \"translated\": 0, \"failed\": 0, \"skipped\": 0}\n )\n for le in lang_entries:\n code = le.language_code\n agg[code][\"total\"] += 1\n if le.status in (\"translated\", \"approved\", \"edited\"):\n agg[code][\"translated\"] += 1\n elif le.status == \"failed\":\n agg[code][\"failed\"] += 1\n elif le.status == \"skipped\":\n agg[code][\"skipped\"] += 1\n\n total_tokens_est = max(1, sum(len(le.translated_value or \"\") for le in lang_entries if le.translated_value) // 4)\n num_langs = len(language_stats_map) or 1\n cost_per_token = 0.002 / 1000\n\n for lang_code, lang_stat in language_stats_map.items():\n data = agg.get(lang_code, {\"total\": 0, \"translated\": 0, \"failed\": 0, \"skipped\": 0})\n lang_stat.total_rows = data[\"total\"]\n lang_stat.translated_rows = data[\"translated\"]\n lang_stat.failed_rows = data[\"failed\"]\n lang_stat.skipped_rows = data[\"skipped\"]\n lang_stat.token_count = total_tokens_est // num_langs\n lang_stat.estimated_cost = round((lang_stat.token_count / 1000) * cost_per_token, 6)\n\n self.db.flush()\n # endregion _update_language_stats_incremental\n\n # region _call_llm_for_batch [TYPE Function]\n # @PURPOSE: Call LLM for a batch of rows requiring translation. Parse structured JSON response.\n # @PRE: job has valid provider_id. batch_rows is non-empty.\n # @POST: Returns dict with successful/failed/skipped counts. Creates TranslationRecord rows.\n # @SIDE_EFFECT: HTTP call to LLM provider.\n def _call_llm_for_batch(\n self,\n job: TranslationJob,\n run_id: str,\n batch_rows: list[dict[str, Any]],\n dict_matches: list[dict[str, Any]],\n batch_id: str,\n max_tokens: int = 8192,\n ) -> dict[str, int]:\n with belief_scope(\"TranslationExecutor._call_llm_for_batch\"):\n # Build dictionary section using ContextAwarePromptBuilder\n dictionary_section = \"\"\n if dict_matches:\n # Get row_context from first batch row if available\n row_context = batch_rows[0].get(\"source_data\") if batch_rows else None\n\n # Use ContextAwarePromptBuilder for context-aware annotations\n annotated_entries = ContextAwarePromptBuilder.build_context_entries(\n dict_matches, row_context\n )\n glossary_lines = []\n for annotated_line in annotated_entries:\n glossary_lines.append(f\"- {annotated_line}\")\n dictionary_section = (\n \"Terminology dictionary (use these translations when applicable):\\n\"\n + \"\\n\".join(glossary_lines)\n + \"\\n\\n\"\n )\n\n # Resolve target languages\n target_languages = job.target_languages or [job.target_dialect or \"en\"]\n if not isinstance(target_languages, list):\n target_languages = [str(target_languages)]\n target_languages_str = \", \".join(target_languages)\n\n # Build rows JSON for LLM\n rows_json = json.dumps([\n {\n \"row_id\": str(row.get(\"row_index\", idx)),\n \"text\": row.get(\"source_text\", \"\"),\n }\n for idx, row in enumerate(batch_rows)\n ], indent=2)\n\n # Build prompt (use multi-language format)\n prompt = render_prompt(\n DEFAULT_EXECUTION_PROMPT_TEMPLATE,\n {\n \"source_language\": job.source_dialect or \"SQL\",\n \"target_language\": target_languages_str,\n \"target_languages\": target_languages_str,\n \"source_dialect\": job.source_dialect or \"\",\n \"target_dialect\": job.target_dialect or \"\",\n \"translation_column\": job.translation_column or \"\",\n \"dictionary_section\": dictionary_section,\n \"rows_json\": rows_json,\n \"row_count\": str(len(batch_rows)),\n },\n )\n\n # Call LLM with retry\n llm_response = None\n last_error = None\n retries = 0\n\n for attempt in range(1, MAX_RETRIES_PER_BATCH + 1):\n try:\n llm_response = self._call_llm(job, prompt, max_tokens=max_tokens)\n break\n except Exception as e:\n last_error = str(e)\n retries += 1\n logger.explore(f\"LLM call failed (attempt {attempt})\", {\n \"batch_id\": batch_id,\n \"error\": last_error,\n \"attempt\": attempt,\n })\n if attempt < MAX_RETRIES_PER_BATCH:\n time.sleep(2 ** attempt) # Exponential backoff\n else:\n logger.explore(\"LLM call exhausted retries\", {\n \"batch_id\": batch_id,\n \"last_error\": last_error,\n })\n\n if llm_response is None:\n # All retries failed — mark all rows as failed\n for row in batch_rows:\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=row.get(\"source_text\", \"\"),\n target_sql=None,\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"FAILED\",\n error_message=f\"LLM call failed after {retries} retries: {last_error}\",\n )\n self.db.add(record)\n return {\"successful\": 0, \"failed\": len(batch_rows), \"skipped\": 0, \"retries\": retries}\n\n # Parse LLM response (multi-language aware)\n try:\n translations = self._parse_llm_response(llm_response, len(batch_rows), target_languages=target_languages)\n except ValueError as e:\n # Parse failure — mark all rows as SKIPPED\n logger.explore(\"LLM response parse failed\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n \"response_preview\": llm_response[:500] if llm_response else \"\",\n })\n skipped = len(batch_rows)\n for row in batch_rows:\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=row.get(\"source_text\", \"\"),\n target_sql=None,\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"SKIPPED\",\n error_message=f\"LLM parse failure: {e}\",\n )\n self.db.add(record)\n return {\n \"successful\": 0,\n \"failed\": 0,\n \"skipped\": skipped,\n \"retries\": retries,\n }\n\n successful = 0\n failed = 0\n skipped = 0\n\n for row in batch_rows:\n row_id = str(row.get(\"row_index\", \"\"))\n translation_data = translations.get(row_id)\n source_text = row.get(\"source_text\", \"\")\n detected_lang = \"und\"\n if translation_data:\n detected_lang = translation_data.get(\"detected_source_language\", \"und\")\n\n if translation_data is None:\n # NULL translation — skip\n skipped += 1\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=source_text,\n target_sql=\"\",\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"SKIPPED\",\n error_message=\"NULL translation returned by LLM\",\n )\n self.db.add(record)\n continue\n\n # Collect per-language translated values\n per_lang_values: dict[str, str] = {}\n has_any_translation = False\n\n # First try multi-language format (per-language keys)\n for lang_code in target_languages:\n lang_val = translation_data.get(lang_code)\n if lang_val is not None and str(lang_val).strip():\n per_lang_values[lang_code] = str(lang_val)\n has_any_translation = True\n\n # Fallback to legacy single \"translation\" key format\n if not has_any_translation:\n translation_text = translation_data.get(\"translation\", \"\")\n if translation_text.strip():\n per_lang_values[target_languages[0]] = translation_text\n has_any_translation = True\n\n if not has_any_translation:\n # Empty/all-empty translations — skip\n skipped += 1\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=source_text,\n target_sql=\"\",\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"SKIPPED\",\n error_message=\"Empty translation returned by LLM\",\n )\n self.db.add(record)\n continue\n\n # Use the first language's value as the primary target_sql for backward compat\n primary_translation = next(iter(per_lang_values.values()), \"\")\n\n successful += 1\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=source_text,\n target_sql=primary_translation,\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"SUCCESS\",\n )\n self.db.add(record)\n\n # Create per-language entries — one TranslationLanguage per language_code\n for lang_code in target_languages:\n lang_translation = per_lang_values.get(lang_code, \"\")\n\n # Source-as-reference: if detected source language matches this target,\n # store the original text verbatim (no translation needed)\n if detected_lang != \"und\" and str(lang_code).lower() == str(detected_lang).lower():\n lang_translation = source_text\n\n # Check for undetermined source language\n lang_needs_review = (detected_lang == \"und\")\n\n if lang_needs_review:\n logger.explore(\"undetected language\", {\n \"record_id\": row_id,\n \"language_code\": lang_code,\n \"source_text\": source_text[:100],\n })\n\n lang_entry = TranslationLanguage(\n id=str(uuid.uuid4()),\n record_id=record.id,\n language_code=lang_code,\n source_language_detected=detected_lang,\n translated_value=lang_translation or \"\",\n final_value=lang_translation or \"\",\n status=\"translated\",\n needs_review=lang_needs_review,\n )\n self.db.add(lang_entry)\n\n return {\n \"successful\": successful,\n \"failed\": failed,\n \"skipped\": skipped,\n \"retries\": retries,\n }\n # endregion _call_llm_for_batch\n\n # region _call_llm [TYPE Function]\n # @PURPOSE: Call the configured LLM provider with the batch prompt.\n # @PRE: job has valid provider_id.\n # @POST: Returns raw LLM response string.\n # @SIDE_EFFECT: HTTP call to LLM provider.\n def _call_llm(self, job: TranslationJob, prompt: str, max_tokens: int = 8192) -> str:\n with belief_scope(\"TranslationExecutor._call_llm\"):\n if not job.provider_id:\n raise ValueError(\"Job has no LLM provider configured\")\n\n provider_svc = LLMProviderService(self.db)\n provider = provider_svc.get_provider(job.provider_id)\n if not provider:\n raise ValueError(f\"LLM provider '{job.provider_id}' not found\")\n\n api_key = provider_svc.get_decrypted_api_key(job.provider_id)\n if not api_key:\n raise ValueError(f\"Could not decrypt API key for provider '{job.provider_id}'\")\n\n model = provider.default_model or \"gpt-4o-mini\"\n provider_type = provider.provider_type.lower() if provider.provider_type else \"openai\"\n\n disable_reasoning = getattr(job, 'disable_reasoning', False)\n\n if provider_type in (\"openai\", \"openai_compatible\", \"openrouter\", \"kilo\"):\n response_text = self._call_openai_compatible(\n base_url=provider.base_url,\n api_key=api_key,\n model=model,\n prompt=prompt,\n provider_type=provider_type,\n max_tokens=max_tokens,\n disable_reasoning=disable_reasoning,\n )\n return response_text\n else:\n raise ValueError(f\"Unsupported provider type '{provider_type}'\")\n # endregion _call_llm\n\n # region _call_openai_compatible [TYPE Function]\n # @PURPOSE: Call OpenAI-compatible API for batch translation.\n # @PRE: Valid API endpoint, key, model, and prompt.\n # @POST: Returns response text.\n # @SIDE_EFFECT: HTTP POST to LLM API.\n @staticmethod\n def _call_openai_compatible(\n base_url: str,\n api_key: str,\n model: str,\n prompt: str,\n provider_type: str = \"openai\",\n max_tokens: int = 8192,\n disable_reasoning: bool = False,\n ) -> str:\n with belief_scope(\"TranslationExecutor._call_openai_compatible\"):\n import requests as http_requests\n\n url = f\"{base_url.rstrip('/')}/chat/completions\"\n headers = {\n \"Authorization\": f\"Bearer {api_key}\",\n \"Content-Type\": \"application/json\",\n }\n payload = {\n \"model\": model,\n \"messages\": [\n {\"role\": \"system\", \"content\": \"You are a database content translation assistant. Translate the provided text accurately, preserving data semantics.\"},\n {\"role\": \"user\", \"content\": prompt},\n ],\n \"temperature\": 0.1,\n \"max_tokens\": max_tokens,\n }\n # Structured output — OpenRouter and Kilo also support response_format, but some\n # upstream providers (e.g. StepFun) reject it. We try with response_format and\n # fall back on 400 \"structured_outputs is not supported\".\n if provider_type in (\"openai\", \"openai_compatible\", \"kilo\", \"openrouter\"):\n payload[\"response_format\"] = {\"type\": \"json_object\"}\n\n # Suppress Chain of Thought reasoning to save output tokens\n # NOTE: Kilo/OpenRouter do NOT support disabling reasoning (returns 400)\n if disable_reasoning:\n if provider_type not in (\"kilo\", \"openrouter\"):\n payload[\"reasoning_effort\"] = \"none\"\n payload[\"extra_body\"] = {\"reasoning_effort\": \"none\"}\n payload.pop(\"response_format\", None)\n payload[\"messages\"][0] = {\"role\": \"system\", \"content\": \"You are a database content translation assistant. Translate the provided text accurately, preserving data semantics. Respond directly with ONLY the JSON result. Do NOT include any reasoning, thinking, chain-of-thought, analysis, or explanation. Output ONLY valid JSON.\"}\n\n logger.reason(\n f\"LLM request url={base_url} model={payload.get('model')} \"\n f\"provider_type={provider_type} \"\n f\"response_format={'yes' if 'response_format' in payload else 'no'} \"\n f\"prompt_len={len(prompt)}\"\n )\n\n # ── Try request; retry once without response_format if upstream rejects it ──\n response = http_requests.post(url, headers=headers, json=payload, timeout=180)\n if not response.ok and response.status_code == 400 and \"structured_outputs is not supported\" in (response.text or \"\"):\n logger.explore(\"Structured outputs not supported by upstream, retrying without response_format\", extra={\"src\": \"executor\"})\n payload.pop(\"response_format\", None)\n response = http_requests.post(url, headers=headers, json=payload, timeout=180)\n if not response.ok:\n logger.explore(\n f\"LLM API error status={response.status_code} \"\n f\"model={payload.get('model')} \"\n f\"body={response.text[:2000]}\"\n )\n response.raise_for_status()\n data = response.json()\n\n choices = data.get(\"choices\", [])\n if not choices:\n logger.explore(\"LLM returned no choices\", extra={\"src\": \"executor\", \"response_keys\": list(data.keys()), \"response_preview\": str(data)[:2000]})\n raise ValueError(\"LLM returned no choices\")\n\n finish_reason = choices[0].get(\"finish_reason\") or \"none\"\n msg = choices[0].get(\"message\") or {}\n # Handle model refusal (content is empty/null, refusal field has reason)\n refusal = msg.get(\"refusal\")\n if refusal:\n logger.explore(\"LLM refused to respond\", extra={\n \"src\": \"executor\",\n \"refusal\": str(refusal)[:500],\n \"finish_reason\": finish_reason,\n })\n raise ValueError(f\"LLM refused to respond: {refusal}\")\n content = msg.get(\"content\") or \"\"\n logger.reason(f\"LLM response finish_reason={finish_reason} content_len={len(content)} msg_keys={list(msg.keys())}\")\n if not content:\n logger.explore(\"LLM returned empty content\", extra={\"src\": \"executor\", \"finish_reason\": finish_reason, \"msg_keys\": list(msg.keys()), \"response_preview\": str(data)[:2000]})\n raise ValueError(\"LLM returned empty content\")\n\n return content\n # endregion _call_openai_compatible\n\n # region _parse_llm_response [TYPE Function]\n # @PURPOSE: Parse LLM JSON response into dict of row_id -> per-language translations.\n # Supports multi-language format: {\"row_id\": 0, \"detected_source_language\": \"fr\", \"ru\": \"текст\", \"en\": \"text\"}\n # Backward-compat: old format {\"row_id\": 0, \"translation\": \"text\", \"detected_source_language\": \"fr\"}\n # @PRE: response_text is valid JSON with {\"rows\": [...]} structure.\n # @POST: Returns dict mapping row_id to dict with 'detected_source_language' and per-language codes.\n @staticmethod\n def _parse_llm_response(response_text: str, expected_count: int, target_languages: list[str] | None = None, finish_reason: str | None = None) -> dict[str, dict[str, str]]:\n with belief_scope(\"TranslationExecutor._parse_llm_response\"):\n try:\n data = json.loads(response_text)\n except json.JSONDecodeError:\n # Try to extract from markdown code block\n import re\n match = re.search(r'```(?:json)?\\s*\\n?(.*?)\\n?```', response_text, re.DOTALL)\n if match:\n try:\n data = json.loads(match.group(1))\n rows = data.get(\"rows\", [])\n if isinstance(rows, list) and rows:\n logger.reason(\"Parsed JSON from markdown code block\", {\"rows\": len(rows)})\n except json.JSONDecodeError:\n pass # fall through to partial recovery\n else:\n pass # fall through to partial recovery\n \n # If finish_reason=length, try to recover complete rows from truncated JSON\n logger.explore(\"LLM truncated, trying partial row recovery\", extra={\"src\": \"executor\", \"finish_reason\": finish_reason, \"response_length\": len(response_text)})\n rows_match = re.findall(r'\\{\\s*\"row_id\"\\s*:\\s*\"\\d+\".*?\\}\\s*', response_text, re.DOTALL)\n if rows_match:\n partial_rows = []\n for row_text in rows_match:\n try:\n row_data = json.loads(row_text)\n partial_rows.append(row_data)\n except json.JSONDecodeError:\n continue\n if partial_rows:\n logger.explore(f\"Recovered {len(partial_rows)}/{expected_count} complete rows from truncated response\", extra={\"src\": \"executor\"})\n data = {\"rows\": partial_rows}\n else:\n logger.explore(\"Could not recover any complete rows\", extra={\"src\": \"executor\", \"response_preview\": response_text[:1000]})\n raise ValueError(\"LLM response was not valid JSON (could not recover any rows)\")\n else:\n logger.explore(\"No complete rows found in truncated response\", extra={\"src\": \"executor\", \"response_preview\": response_text[:1000]})\n raise ValueError(\"LLM response was not valid JSON\")\n\n rows = data.get(\"rows\", [])\n if not isinstance(rows, list):\n raise ValueError(\"LLM response missing 'rows' array\")\n\n translations: dict[str, dict[str, str]] = {}\n for item in rows:\n row_id = str(item.get(\"row_id\", \"\"))\n if not row_id:\n continue\n\n detected_lang = str(item.get(\"detected_source_language\", \"und\")) if item.get(\"detected_source_language\") else \"und\"\n result: dict[str, str] = {\n \"detected_source_language\": detected_lang,\n }\n\n # Multi-language format: extract per-language keys\n has_language_data = False\n if target_languages:\n for lang_code in target_languages:\n lang_val = item.get(lang_code)\n if lang_val is not None and str(lang_val).strip():\n result[lang_code] = str(lang_val)\n has_language_data = True\n\n # Fallback to legacy single \"translation\" key format\n if not has_language_data:\n translation = item.get(\"translation\")\n if translation is not None:\n result[\"translation\"] = str(translation)\n has_language_data = True\n\n if has_language_data:\n translations[row_id] = result\n\n if len(translations) < expected_count:\n logger.explore(\n f\"LLM returned fewer translations expected={expected_count} \"\n f\"got={len(translations)}\"\n )\n\n return translations\n # endregion _parse_llm_response\n\n\n# #endregion TranslationExecutor\n# #endregion TranslationExecutor\n" + "body": "# #region TranslationExecutor [C:5] [TYPE Module] [SEMANTICS sqlalchemy, tenacity, translate, insert, llm-retry]\n# @BRIEF Process translation in batches: fetch source rows, call LLM, persist TranslationBatch and TranslationRecord rows.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [TranslationBatch]\n# @RELATION DEPENDS_ON -> [TranslationRecord]\n# @RELATION DEPENDS_ON -> [TranslationRun]\n# @RELATION DEPENDS_ON -> [LLMProviderService]\n# @RELATION DEPENDS_ON -> [DictionaryManager]\n# @RELATION DEPENDS_ON -> [TranslationPreview]\n# @PRE: Valid TranslationRun with job configuration. DB session is available.\n# @POST: TranslationBatch and TranslationRecord rows are created. Run status is updated.\n# @SIDE_EFFECT: Calls LLM provider; creates DB rows; updates run statistics.\n# @DATA_CONTRACT Input: TranslationRun + Job -> Output: updated Run with batch/record rows\n# @INVARIANT Batch processing is independent — one batch failure does not affect others.\n# @RATIONALE: Batch processing with retry — independent batches allow partial recovery.\n# @REJECTED: Single monolithic LLM call — would lose all progress on any failure.\n\nimport hashlib\nimport json\nimport re\nimport time\nimport uuid\nfrom collections.abc import Callable\nfrom datetime import UTC, datetime\nfrom typing import Any\n\nfrom sqlalchemy.orm import Session, joinedload\n\nfrom ...core.config_manager import ConfigManager\nfrom ...core.logger import belief_scope, logger\nfrom ...models.translate import (\n TranslationBatch,\n TranslationJob,\n TranslationLanguage,\n TranslationPreviewRecord,\n TranslationPreviewSession,\n TranslationRecord,\n TranslationRun,\n TranslationRunLanguageStats,\n)\nfrom ...services.llm_prompt_templates import render_prompt\nfrom ...services.llm_provider import LLMProviderService\nfrom ._token_budget import estimate_token_budget\nfrom .dictionary import DictionaryManager\nfrom .preview import DEFAULT_EXECUTION_PROMPT_TEMPLATE\nfrom .prompt_builder import ContextAwarePromptBuilder\nfrom .sql_generator import SQLGenerator, _normalize_timestamp_value\nfrom .superset_executor import SupersetSqlLabExecutor\n\n# #region MAX_RETRIES_PER_BATCH [TYPE Constant]\nMAX_RETRIES_PER_BATCH = 3\n# #endregion MAX_RETRIES_PER_BATCH\n\n# #region MAX_ROWS_PER_RUN [TYPE Constant]\n# Safety cap: without it, a datasource with thousands of rows blocks the single\n# uvicorn worker for minutes/hours. Preview uses sample_size (5-10). Full run\n# should stay within reasonable bounds.\nMAX_ROWS_PER_RUN = 10000\n# #endregion MAX_ROWS_PER_RUN\n\n# #region _enforce_dictionary [TYPE Function]\n# @BRIEF Post-processing: enforce dictionary entries in LLM output.\n# If a source term from the dictionary appears in the original text,\n# but the target term is missing from the translation, replace occurrences\n# of the source term in the translated output with the target term.\n# @PRE: dict_matches is a list of dict entries with source_term/target_term.\n# @POST: per_lang_values may be mutated to include forced dictionary replacements.\n# @SIDE_EFFECT: Logs when enforcement is applied.\ndef _enforce_dictionary(\n source_text: str,\n per_lang_values: dict[str, str],\n dict_matches: list[dict[str, Any]],\n batch_id: str,\n row_id: str,\n) -> None:\n \"\"\"Post-processing: enforce dictionary terms in LLM translation output.\n\n For each dictionary entry whose source_term appears in the source_text,\n verify that the target_term is present in each language's translation.\n If missing, replace any occurrence of the source term (which the LLM\n may have left untranslated) with the dictionary target term.\n \"\"\"\n if not dict_matches or not source_text:\n return\n\n text_lower = source_text.lower()\n\n for dm in dict_matches:\n src_term = dm.get(\"source_term\", \"\")\n tgt_term = dm.get(\"target_term\", \"\")\n if not src_term or not tgt_term:\n continue\n\n # Only process if source term actually appears in the original text\n if src_term.lower() not in text_lower:\n continue\n\n # Try to replace in each language\n for lang_code in list(per_lang_values.keys()):\n val = per_lang_values[lang_code]\n if not val:\n continue\n\n # Check if target term is already present (case-insensitive)\n if tgt_term.lower() in val.lower():\n continue\n\n # Try to find the source term (or a close variant) in the output\n # This catches cases where the LLM left the source term untranslated\n # Use lambda to prevent regex back-reference injection from tgt_term\n src_pattern = re.compile(re.escape(src_term), re.IGNORECASE)\n if src_pattern.search(val):\n new_val = src_pattern.sub(lambda _: tgt_term, val)\n if new_val != val:\n logger.reason(\"Dictionary enforcement applied\", {\n \"batch_id\": batch_id,\n \"row_id\": row_id,\n \"language_code\": lang_code,\n \"source_term\": src_term,\n \"target_term\": tgt_term,\n \"before\": val[:200],\n \"after\": new_val[:200],\n })\n per_lang_values[lang_code] = new_val\n# #endregion _enforce_dictionary\n\n\n# #region _compute_source_hash [TYPE Function]\n# @BRIEF Compute deterministic cache key for a source row.\n# SHA256 of (source_text + context_fields + dict_snapshot_hash + config_hash).\n# Key columns (row_id, primary keys) are EXCLUDED — only the text content\n# and meaningful context affect the hash. This ensures identical text with\n# different row identifiers still produces a cache hit.\ndef _compute_source_hash(\n source_text: str,\n source_data: dict | None,\n dict_snapshot_hash: str | None,\n config_hash: str | None,\n context_keys: list[str] | None = None,\n) -> str:\n \"\"\"Deterministic cache key for a translation source row.\n\n Only includes source_text and context-relevant fields from source_data.\n Key/identifier columns are excluded so identical text in different rows hits cache.\n \"\"\"\n # Extract only context-relevant fields from source_data (not keys/identifiers)\n context_data: dict[str, str] = {}\n if source_data and context_keys:\n for key in context_keys:\n val = source_data.get(key)\n if val is not None:\n context_data[key] = str(val)\n\n payload = json.dumps({\n \"text\": source_text,\n \"ctx\": context_data, # only context columns, no key/row_id\n \"dict_hash\": dict_snapshot_hash or \"\",\n \"config_hash\": config_hash or \"\",\n }, sort_keys=True, ensure_ascii=False)\n return hashlib.sha256(payload.encode(\"utf-8\")).hexdigest()\n# #endregion _compute_source_hash\n\n\n# #region _check_translation_cache [TYPE Function]\n# @BRIEF Look up a previously successful translation by source_hash.\n# Returns per-language dict {lang_code: final_value} or None.\ndef _check_translation_cache(\n db: Session,\n source_hash: str,\n) -> dict[str, str] | None:\n \"\"\"Check if this source_hash was already translated successfully.\"\"\"\n from sqlalchemy.orm import joinedload\n from ...models.translate import TranslationRecord, TranslationLanguage\n\n cached = (\n db.query(TranslationRecord)\n .options(joinedload(TranslationRecord.languages))\n .filter(\n TranslationRecord.source_hash == source_hash,\n TranslationRecord.status == \"SUCCESS\",\n )\n .order_by(TranslationRecord.created_at.desc())\n .first()\n )\n if not cached:\n return None\n\n lang_values: dict[str, str] = {}\n for lang in cached.languages:\n if lang.status == \"translated\" and lang.final_value:\n lang_values[lang.language_code] = lang.final_value\n\n return lang_values if lang_values else None\n# #endregion _check_translation_cache\n\n\n# #region estimate_row_tokens [C:4] [TYPE Function] [SEMANTICS translate, token, estimation]\n# @BRIEF Estimate token count for a single source row including context fields.\n# @PRE source_text is a string.\n# @POST Returns estimated token count >= 1.\n# @RELATION DEPENDS_ON -> [_estimate_tokens_for_text]\n\ndef estimate_row_tokens(\n source_text: str,\n source_data: dict | None,\n job,\n) -> int:\n \"\"\"Estimate token count for a single source row including context fields.\n\n Uses CJK-aware heuristics via _token_budget._estimate_tokens_for_text.\n Context fields from job.context_columns are included in the estimate.\n \"\"\"\n from ._token_budget import _estimate_tokens_for_text\n\n text_tokens = _estimate_tokens_for_text(source_text or \"\")\n\n context_keys = job.context_columns or []\n ctx_text = \"\"\n if source_data and context_keys:\n ctx_text = \" \".join(str(source_data.get(k, \"\")) for k in context_keys)\n ctx_tokens = _estimate_tokens_for_text(ctx_text)\n\n return text_tokens + ctx_tokens\n# #endregion estimate_row_tokens\n\n\n# #region TranslationExecutor [C:4] [TYPE Class]\n# @BRIEF Process translation batches: fetch source rows, filter dict, call LLM, persist results.\n# @PRE: DB session and config manager available.\n# @POST: Batches and records created with status tracking.\n# @SIDE_EFFECT: LLM API calls; DB writes.\nclass TranslationExecutor:\n\n def __init__(\n self,\n db: Session,\n config_manager: ConfigManager,\n current_user: str | None = None,\n on_batch_progress: Callable[[str, int, int, int, int], None] | None = None,\n ):\n self.db = db\n self.config_manager = config_manager\n self.current_user = current_user\n self.on_batch_progress = on_batch_progress\n self._current_run_id: str | None = None\n self._preview_edits_cache: dict[str, dict[str, str]] | None = None # key_hash -> {lang_code: edited_value}\n\n # region _resolve_provider_model [TYPE Function]\n # @BRIEF Resolve the LLM provider model name for token budget estimation.\n # @POST: Returns model name string or None if resolution fails.\n # @SIDE_EFFECT: DB query to LLM provider table.\n def _resolve_provider_model(self, job) -> str | None:\n \"\"\"Resolve the provider model name for token budget estimation.\"\"\"\n if not job.provider_id:\n return None\n try:\n p_svc = LLMProviderService(self.db)\n p = p_svc.get_provider(job.provider_id)\n if p:\n return p.default_model or \"gpt-4o-mini\"\n except Exception:\n pass\n return None\n # endregion _resolve_provider_model\n\n # region _auto_size_batches [TYPE Function]\n # @PURPOSE: Split source rows into variable-sized batches based on actual content length.\n # Uses estimate_token_budget to determine safe per-batch budgets and\n # _count_rows_that_fit logic to greedily fill each batch.\n # @PRE: source_rows is non-empty. job has valid config.\n # @POST: Returns list of batches, each batch is a list of row dicts.\n # Each batch fits within the estimated token budget for its rows.\n # @SIDE_EFFECT: DB query to resolve provider model. Logs batch statistics.\n # @RATIONALE: Fixed batch_size of 50 wastes LLM context for short rows and\n # overflows for long rows. Variable sizing adapts to row content length,\n # maximizing throughput while preventing truncation.\n # @REJECTED: Fixed batch_size of 50 — causes truncation on long-content rows.\n # Single monolithic batch — would lose all progress on any failure.\n def _auto_size_batches(\n self,\n job,\n source_rows: list[dict],\n target_languages: list[str],\n provider_info: str | None = None,\n ) -> list[list[dict]]:\n \"\"\"Split source rows into auto-sized batches based on content length.\n\n Each batch is sized so that its total estimated tokens fit within the\n available context window (input budget), accounting for prompt overhead,\n dictionary entries, and output tokens.\n\n Returns:\n List of batches, each batch is a list of row dicts.\n \"\"\"\n with belief_scope(\"TranslationExecutor._auto_size_batches\"):\n if not source_rows:\n return []\n\n # Resolve provider info if not provided\n if provider_info is None:\n provider_info = self._resolve_provider_model(job)\n\n # 1. Estimate per-row token counts\n row_tokens: list[int] = []\n for row in source_rows:\n source_text = row.get(\"source_text\", \"\")\n source_data = row.get(\"source_data\")\n tokens = estimate_row_tokens(source_text, source_data, job)\n row_tokens.append(tokens)\n\n # 2. Get budget recommendation from estimate_token_budget\n # Pass all rows to get a global safe batch size estimate.\n budget = estimate_token_budget(\n source_rows=source_rows,\n target_languages=target_languages,\n source_column=job.translation_column or \"source_text\",\n context_columns=job.context_columns,\n batch_size=len(source_rows),\n provider_info=provider_info,\n )\n\n recommended = budget.get(\"batch_size_adjusted\", 0)\n\n # Fallback: if budget calculation fails or returns zero, use fixed size\n if recommended <= 0:\n fallback_size = job.batch_size or 50\n logger.explore(\"Token budget returned zero — falling back to fixed batch size\", {\n \"fallback_size\": fallback_size,\n \"total_rows\": len(source_rows),\n })\n return [\n source_rows[i:i + fallback_size]\n for i in range(0, len(source_rows), fallback_size)\n ]\n\n # 3. Compute per-batch row-content budget\n # estimated_input_tokens includes PROMPT_BASE_TOKENS + dict_tokens +\n # sum of row tokens for the first `recommended` rows.\n # We subtract the prompt overhead to get the per-batch row budget.\n from ._token_budget import PROMPT_BASE_TOKENS\n\n estimated_input = budget.get(\"estimated_input_tokens\", 50000)\n per_batch_budget = estimated_input - PROMPT_BASE_TOKENS\n\n # Safety: if budget computation collapsed, fall back\n if per_batch_budget <= 0:\n fallback_size = job.batch_size or 50\n logger.explore(\"Per-batch budget collapsed — falling back to fixed batch size\", {\n \"estimated_input\": estimated_input,\n \"prompt_base\": PROMPT_BASE_TOKENS,\n \"fallback_size\": fallback_size,\n })\n return [\n source_rows[i:i + fallback_size]\n for i in range(0, len(source_rows), fallback_size)\n ]\n\n # 4. Greedy batch splitting: accumulate rows until the next row would\n # exceed the per-batch token budget. Use output-aware hard cap too:\n # a single batch never exceeds 2x the recommended batch size as\n # a safety net against wildly oversized estimates.\n max_rows_hard_cap = max(recommended * 2, 20)\n batches: list[list[dict]] = []\n current_batch: list[dict] = []\n current_tokens = 0\n\n for i, row in enumerate(source_rows):\n rt = row_tokens[i]\n\n # ── Edge case: single row exceeds entire per-batch budget ──\n if rt > per_batch_budget:\n # Flush current batch first\n if current_batch:\n batches.append(current_batch)\n current_batch = []\n current_tokens = 0\n logger.reason(\"Single row exceeds per-batch token budget — placing in own batch\", {\n \"row_index\": i,\n \"row_tokens\": rt,\n \"per_batch_budget\": per_batch_budget,\n })\n batches.append([row])\n continue\n\n # ── Start a new batch if: ──\n # a) Batch is non-empty AND adding this row would exceed budget\n # b) Hard row-count cap reached\n should_split = bool(\n current_batch\n and (current_tokens + rt > per_batch_budget\n or len(current_batch) >= max_rows_hard_cap)\n )\n\n if should_split:\n batches.append(current_batch)\n current_batch = [row]\n current_tokens = rt\n else:\n current_batch.append(row)\n current_tokens += rt\n\n if current_batch:\n batches.append(current_batch)\n\n # 5. Log adaptive batch statistics\n if batches:\n avg_size = sum(len(b) for b in batches) / max(1, len(batches))\n max_size = max(len(b) for b in batches)\n logger.reason(\"Auto-sized batches\", {\n \"total_rows\": len(source_rows),\n \"num_batches\": len(batches),\n \"avg_batch_size\": round(avg_size, 1),\n \"max_batch_size\": max_size,\n \"recommended_batch_size\": recommended,\n \"per_batch_budget\": per_batch_budget,\n })\n\n return batches\n # endregion _auto_size_batches\n\n # region execute_run [TYPE Function]\n # @PURPOSE: Run full translation execution for a TranslationRun.\n # @PRE: run is in PENDING or RUNNING status with valid job config.\n # @POST: Run is populated with batches and records.\n # @SIDE_EFFECT: LLM API calls; DB batch writes.\n def execute_run(\n self,\n run: TranslationRun,\n llm_progress_callback: Callable[[str, int, int, int], None] | None = None,\n language_stats_map: dict[str, TranslationRunLanguageStats] | None = None,\n ) -> TranslationRun:\n with belief_scope(\"TranslationExecutor.execute_run\"):\n job = self.db.query(TranslationJob).filter(TranslationJob.id == run.job_id).first()\n if not job:\n raise ValueError(f\"Job '{run.job_id}' not found for run '{run.id}'\")\n\n logger.reason(\"Starting translation execution\", {\n \"run_id\": run.id,\n \"job_id\": job.id,\n \"configured_batch_size\": job.batch_size,\n })\n\n # Load preview edits for carry-forward\n self._load_preview_edits(job.id)\n\n # Mark run as RUNNING\n run.status = \"RUNNING\"\n run.started_at = datetime.now(UTC)\n self.db.flush()\n\n # Determine whether this is a full translation (all rows) or incremental (new/changed only)\n full_translation = False\n if run.config_snapshot and isinstance(run.config_snapshot, dict):\n full_translation = run.config_snapshot.get(\"full_translation\", False)\n\n # Fetch source rows from the datasource\n source_rows = self._fetch_source_rows(job.id, run.id)\n if not source_rows:\n logger.explore(\"No source rows to translate\", {\"run_id\": run.id})\n run.status = \"COMPLETED\"\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n return run\n\n # Apply new-key-only filtering for scheduled runs OR manual incremental runs\n if run.trigger_type == \"new_key_only\" or (not full_translation and run.trigger_type == \"manual\"):\n source_rows = self._filter_new_keys(job, run.id, source_rows)\n if not source_rows:\n logger.reason(\"run_noop — no new rows to translate\", {\n \"job_id\": job.id, \"run_id\": run.id,\n })\n run.status = \"COMPLETED\"\n run.insert_status = \"skipped\"\n run.total_records = 0\n run.completed_at = datetime.now(UTC)\n self.db.commit()\n return\n\n total_rows = len(source_rows)\n run.total_records = total_rows\n\n # Resolve target languages for batch sizing\n target_languages = job.target_languages or [job.target_dialect or \"en\"]\n if not isinstance(target_languages, list):\n target_languages = [str(target_languages)]\n\n # Split into auto-sized batches based on content length\n provider_model = self._resolve_provider_model(job)\n batches = self._auto_size_batches(\n job=job,\n source_rows=source_rows,\n target_languages=target_languages,\n provider_info=provider_model,\n )\n\n logger.reason(f\"Processing {len(batches)} batches\", {\n \"run_id\": run.id,\n \"total_rows\": total_rows,\n \"num_batches\": len(batches),\n \"avg_batch_size\": round(sum(len(b) for b in batches) / max(1, len(batches)), 1),\n \"max_batch_size\": max(len(b) for b in batches) if batches else 0,\n })\n\n successful_records = 0\n failed_records = 0\n skipped_records = 0\n\n for batch_idx, batch_rows in enumerate(batches):\n batch_result = self._process_batch(\n job=job,\n run_id=run.id,\n batch_index=batch_idx,\n batch_rows=batch_rows,\n dict_snapshot_hash=run.dict_snapshot_hash,\n config_hash=run.config_hash,\n )\n successful_records += batch_result[\"successful\"]\n failed_records += batch_result[\"failed\"]\n skipped_records += batch_result[\"skipped\"]\n\n # Update run stats incrementally\n run.successful_records = successful_records\n run.failed_records = failed_records\n run.skipped_records = skipped_records\n\n # Commit after each batch to release row locks and make RUNNING\n # status + batch progress visible to other DB sessions (frontend polling).\n self.db.commit()\n\n # Incremental INSERT into target table after each batch,\n # so data appears incrementally in the target view/table\n # without waiting for the entire run to complete.\n batch_id = batch_result.get(\"batch_id\")\n if batch_id and batch_result[\"successful\"] > 0:\n try:\n self._insert_batch_to_target(job, batch_id, run.id)\n except Exception as e:\n logger.explore(\"Batch INSERT failed (non-fatal, continuing)\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n\n # Re-fetch run after commit to check for cancellation flag set by\n # cancel_run() fallback (direct SQL UPDATE of error_message).\n self.db.refresh(run)\n if run.error_message == \"CANCEL_REQUESTED\":\n logger.reason(\"Cancellation requested — stopping batch processing\", {\n \"run_id\": run.id,\n \"batch_index\": batch_idx,\n })\n run.status = \"CANCELLED\"\n run.completed_at = datetime.now(UTC)\n run.error_message = None # Clear the orchestration flag\n self.db.commit()\n return run\n\n # Update per-language statistics incrementally after each batch\n # so the frontend shows real-time per-language counts for RUNNING runs.\n if language_stats_map and batch_result[\"successful\"] > 0:\n try:\n self._update_language_stats_incremental(run.id, language_stats_map)\n except Exception as e:\n logger.explore(\"Language stats update failed (non-fatal)\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n\n if self.on_batch_progress:\n self.on_batch_progress(\n run.id, batch_idx + 1, len(batches),\n successful_records, total_rows,\n )\n\n # Update final run status\n if failed_records == 0 and skipped_records == 0:\n run.status = \"COMPLETED\"\n elif successful_records == 0:\n run.status = \"FAILED\"\n else:\n run.status = \"COMPLETED\" # Partial success\n\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n\n logger.reflect(\"Translation execution complete\", {\n \"run_id\": run.id,\n \"status\": run.status,\n \"total\": total_rows,\n \"successful\": successful_records,\n \"failed\": failed_records,\n \"skipped\": skipped_records,\n })\n\n return run\n # endregion execute_run\n\n # region _fetch_source_rows [TYPE Function]\n # @PURPOSE: Fetch full source dataset from Superset (via datasource) for full translation.\n # @PRE: job_id exists. Job may have source_datasource_id for full fetch.\n # @POST: Returns list of dicts with source data (all rows from the source datasource).\n # @SIDE_EFFECT: Makes HTTP call to Superset chart data API when datasource is configured.\n def _fetch_source_rows(self, job_id: str, run_id: str) -> list[dict[str, Any]]:\n with belief_scope(\"TranslationExecutor._fetch_source_rows\"):\n job = self.db.query(TranslationJob).filter(TranslationJob.id == job_id).first()\n\n # If source_datasource_id is configured, fetch ALL rows from the Superset chart data API\n if job and job.source_datasource_id:\n try:\n logger.reason(\"Fetching full dataset from Superset datasource\", {\n \"run_id\": run_id,\n \"datasource_id\": job.source_datasource_id,\n \"environment_id\": job.environment_id,\n })\n\n # Determine environment\n environments = self.config_manager.get_environments()\n target_env_id = job.environment_id or job.source_dialect or \"\"\n env_config = next(\n (e for e in environments if e.id == target_env_id),\n None,\n )\n if not env_config and environments:\n env_config = environments[0]\n\n if env_config:\n from ...core.superset_client import SupersetClient\n client = SupersetClient(env_config)\n\n # Fetch dataset detail to build proper query context\n dataset_detail = client.get_dataset_detail(int(job.source_datasource_id))\n\n # Build query context (same approach as preview but without row_limit)\n query_context = client.build_dataset_preview_query_context(\n dataset_id=int(job.source_datasource_id),\n dataset_record=dataset_detail,\n template_params={},\n effective_filters=[],\n )\n\n # Use a safety cap instead of removing row_limit entirely.\n # Preview uses sample_size (5-10). Full runs should not fetch unlimited rows\n # because each batch of 20 rows × 4 languages takes ~40s of LLM time.\n queries = query_context.get(\"queries\", [])\n if queries:\n queries[0][\"row_limit\"] = MAX_ROWS_PER_RUN\n queries[0].pop(\"result_type\", None)\n queries[0][\"metrics\"] = []\n query_context[\"result_type\"] = \"samples\"\n form_data = query_context.get(\"form_data\", {})\n form_data.pop(\"query_mode\", None)\n\n response = client.network.request(\n method=\"POST\",\n endpoint=\"/api/v1/chart/data\",\n data=json.dumps(query_context),\n headers={\"Content-Type\": \"application/json\"},\n )\n\n # Extract rows\n rows = self._extract_chart_data_rows(response)\n\n if rows:\n logger.reason(f\"Fetched {len(rows)} rows from Superset datasource\", {\n \"run_id\": run_id,\n })\n # Map rows to source_rows format\n source_rows = []\n for idx, row in enumerate(rows):\n source_data_dict = dict(row) if row else None\n source_text = str(row.get(job.translation_column, \"\")) if job.translation_column else json.dumps(row)\n source_rows.append({\n \"row_index\": str(idx),\n \"source_text\": source_text,\n \"approved_translation\": None,\n \"source_object_name\": f\"Row {idx}\",\n \"source_data\": source_data_dict,\n })\n return source_rows\n else:\n logger.explore(\"Superset datasource returned no rows\", {\n \"run_id\": run_id,\n \"datasource_id\": job.source_datasource_id,\n })\n else:\n logger.explore(\"No environment config found for datasource fetch\", {\n \"env_id\": target_env_id,\n })\n except Exception as e:\n logger.explore(\"Failed to fetch full dataset from Superset, falling back to preview\", {\n \"run_id\": run_id,\n \"error\": str(e),\n })\n # Fall through to preview-based fetch\n\n # Fallback: get the latest APPLIED preview session\n session = (\n self.db.query(TranslationPreviewSession)\n .filter(\n TranslationPreviewSession.job_id == job_id,\n TranslationPreviewSession.status == \"APPLIED\",\n )\n .order_by(TranslationPreviewSession.created_at.desc())\n .first()\n )\n if not session:\n logger.explore(\"No accepted preview session found\", {\"job_id\": job_id})\n return []\n\n # Fetch APPROVED or all records from the session\n records = (\n self.db.query(TranslationPreviewRecord)\n .filter(\n TranslationPreviewRecord.session_id == session.id,\n TranslationPreviewRecord.status.in_([\"APPROVED\", \"PENDING\"]),\n )\n .all()\n )\n\n source_rows = []\n for rec in records:\n source_data_dict = None\n if hasattr(rec, \"source_data\") and rec.source_data:\n source_data_dict = dict(rec.source_data)\n source_rows.append({\n \"row_index\": rec.source_object_id or \"0\",\n \"source_text\": rec.source_sql or \"\",\n \"approved_translation\": rec.target_sql if rec.status == \"APPROVED\" else None,\n \"source_object_name\": rec.source_object_name or \"\",\n \"source_data\": source_data_dict,\n })\n\n logger.reason(f\"Fetched {len(source_rows)} source rows from preview fallback\", {\n \"run_id\": run_id,\n \"session_id\": session.id,\n })\n return source_rows\n # endregion _fetch_source_rows\n\n # region _filter_new_keys [TYPE Function]\n # @PURPOSE: Filter source rows to only include those with keys absent from the last successful run.\n # @PRE: job and run are persisted; source_rows is a list of dicts with source_data containing key columns.\n # @POST: Returns filtered list of source rows with only new keys. Logs skip count.\n # @SIDE_EFFECT: Queries TranslationRecord from previous runs; no writes.\n def _filter_new_keys(self, job, run_id: str, source_rows: list) -> list:\n with belief_scope(\"TranslationExecutor._filter_new_keys\"):\n # Find most recent COMPLETED run with successful insert for this job\n prev_run = (\n self.db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job.id,\n TranslationRun.status == \"COMPLETED\",\n TranslationRun.insert_status == \"succeeded\",\n TranslationRun.id != run_id,\n )\n .order_by(TranslationRun.created_at.desc())\n .first()\n )\n if not prev_run:\n logger.reason(\"No prior successful run — all keys treated as new\", {\n \"job_id\": job.id,\n })\n return source_rows\n\n # Get successful records from that run\n prev_records = (\n self.db.query(TranslationRecord)\n .filter(\n TranslationRecord.run_id == prev_run.id,\n TranslationRecord.status == \"SUCCESS\",\n )\n .all()\n )\n if not prev_records:\n return source_rows\n\n # Build set of already-translated composite keys\n key_cols = job.target_key_cols or job.source_key_cols or []\n if not key_cols:\n logger.explore(\"No key columns configured — skipping new-key-only filter\",\n {\"job_id\": job.id})\n return source_rows\n existing_keys = set()\n for rec in prev_records:\n sd = rec.source_data or {}\n key_tuple = tuple(str(sd.get(k, \"\")) for k in key_cols)\n existing_keys.add(key_tuple)\n\n # Filter: keep only rows whose keys are NOT in the existing set\n filtered = []\n skipped = 0\n for row in source_rows:\n sd = row.get(\"source_data\", {}) or {}\n key_tuple = tuple(str(sd.get(k, \"\")) for k in key_cols)\n if key_tuple not in existing_keys:\n filtered.append(row)\n else:\n skipped += 1\n\n logger.reason(f\"New-key-only filter: {len(source_rows)} total → {len(filtered)} new, {skipped} skipped\", {\n \"job_id\": job.id,\n \"prev_run_id\": prev_run.id,\n \"key_cols\": key_cols,\n })\n return filtered\n # endregion _filter_new_keys\n\n # region _extract_chart_data_rows [TYPE Function]\n # @PURPOSE: Extract data rows from Superset chart data API response.\n # @POST: Returns list of dicts with column-value pairs.\n @staticmethod\n def _extract_chart_data_rows(response: dict[str, Any]) -> list[dict[str, Any]]:\n result = response.get(\"result\")\n if isinstance(result, list):\n for item in result:\n if isinstance(item, dict):\n data = item.get(\"data\")\n if isinstance(data, list) and data:\n return data\n if isinstance(result, dict):\n data = result.get(\"data\")\n if isinstance(data, list) and data:\n return data\n data = response.get(\"data\")\n if isinstance(data, list) and data:\n return data\n if isinstance(result, list):\n return result\n return []\n # endregion _extract_chart_data_rows\n\n # region _load_preview_edits [TYPE Function]\n # @PURPOSE: Load user edits from accepted preview session for carry-forward.\n # @PRE: job_id exists.\n # @POST: Populates _preview_edits_cache with key_hash -> {lang_code: edited_value}.\n # @SIDE_EFFECT: Queries TranslationPreviewLanguage and TranslationPreviewRecord.\n def _load_preview_edits(self, job_id: str) -> None:\n \"\"\"Load preview edits for carry-forward during execution.\"\"\"\n from ...models.translate import TranslationPreviewLanguage, TranslationPreviewRecord, TranslationPreviewSession\n\n with belief_scope(\"TranslationExecutor._load_preview_edits\"):\n session = (\n self.db.query(TranslationPreviewSession)\n .filter(\n TranslationPreviewSession.job_id == job_id,\n TranslationPreviewSession.status == \"APPLIED\",\n )\n .order_by(TranslationPreviewSession.created_at.desc())\n .first()\n )\n if not session:\n logger.reason(\"No applied preview session found — no edits to carry forward\", {\n \"job_id\": job_id,\n })\n self._preview_edits_cache = {}\n return\n\n records = (\n self.db.query(TranslationPreviewRecord)\n .filter(TranslationPreviewRecord.session_id == session.id)\n .all()\n )\n\n edits: dict[str, dict[str, str]] = {}\n for rec in records:\n if not rec.source_data:\n continue\n # Compute key hash from source_data\n key_hash = self._compute_key_hash(rec.source_data)\n edited_langs: dict[str, str] = {}\n for lang_entry in (rec.languages or []):\n if lang_entry.status in (\"edited\", \"approved\") and lang_entry.user_edit:\n edited_langs[lang_entry.language_code] = lang_entry.final_value or lang_entry.user_edit\n logger.reason(\"Carrying forward preview edit\", {\n \"key_hash\": key_hash,\n \"language_code\": lang_entry.language_code,\n })\n if edited_langs:\n edits[key_hash] = edited_langs\n\n self._preview_edits_cache = edits\n logger.reason(f\"Loaded {len(edits)} preview edits for carry-forward\", {\n \"job_id\": job_id,\n })\n # endregion _load_preview_edits\n\n # region _compute_key_hash [TYPE Function]\n # @PURPOSE: Compute a stable hash from source_data dict for matching preview edits.\n @staticmethod\n def _compute_key_hash(source_data: dict) -> str:\n import hashlib\n stable = json.dumps(source_data, sort_keys=True)\n return hashlib.sha256(stable.encode()).hexdigest()[:16]\n # endregion _compute_key_hash\n\n # region _process_batch [TYPE Function]\n # @PURPOSE: Process a single batch: filter dict, build prompt, call LLM, persist records.\n # @PRE: job and batch_rows are valid.\n # @POST: TranslationBatch and TranslationRecord rows are created.\n # @SIDE_EFFECT: LLM API call.\n def _process_batch(\n self,\n job: TranslationJob,\n run_id: str,\n batch_index: int,\n batch_rows: list[dict[str, Any]],\n dict_snapshot_hash: str | None = None,\n config_hash: str | None = None,\n ) -> dict[str, int]:\n with belief_scope(\"TranslationExecutor._process_batch\"):\n batch_start = time.monotonic()\n\n # Create batch record\n batch = TranslationBatch(\n id=str(uuid.uuid4()),\n run_id=run_id,\n batch_index=batch_index,\n status=\"RUNNING\",\n total_records=len(batch_rows),\n started_at=datetime.now(UTC),\n )\n self.db.add(batch)\n self.db.flush()\n batch_id = batch.id\n\n result = {\"successful\": 0, \"failed\": 0, \"skipped\": 0, \"retries\": 0}\n\n # Extract source texts for dict filtering\n source_texts = [\n row.get(\"source_text\", \"\")\n for row in batch_rows\n if row.get(\"source_text\")\n ]\n\n # Filter dictionary entries with row context for priority matching\n row_context = batch_rows[0].get(\"source_data\") if batch_rows else None\n dict_matches = DictionaryManager.filter_for_batch(\n self.db, source_texts, job.id,\n row_context=row_context,\n )\n\n # ── Translation cache check: skip LLM for rows translated before ──\n for row in batch_rows:\n if row.get(\"approved_translation\"):\n continue\n source_text = row.get(\"source_text\", \"\")\n if not source_text:\n continue\n source_data = row.get(\"source_data\")\n # Build context_keys from job config (exclude key columns from hash)\n ctx_keys = list(job.context_columns or [])\n source_hash = _compute_source_hash(\n source_text, source_data,\n dict_snapshot_hash, config_hash,\n context_keys=ctx_keys,\n )\n row[\"_source_hash\"] = source_hash\n cached = _check_translation_cache(self.db, source_hash)\n if cached:\n row[\"_cached_lang_values\"] = cached\n logger.reason(\"Translation cache hit\", {\n \"source_hash\": source_hash[:12],\n \"langs\": list(cached.keys()),\n })\n\n # For each row, determine if we need LLM translation or can use approved/preview edit/cache\n rows_for_llm = []\n pre_translated = []\n\n for row in batch_rows:\n if row.get(\"approved_translation\"):\n pre_translated.append(row)\n continue\n\n # Translation cache hit → treat as pre-translated\n # BUT only if cached langs cover ALL target languages\n cached_langs = row.get(\"_cached_lang_values\")\n if cached_langs:\n target_langs_for_check = job.target_languages or [job.target_dialect or \"en\"]\n if not isinstance(target_langs_for_check, list):\n target_langs_for_check = [str(target_langs_for_check)]\n cached_covers_all = all(\n lang_code in cached_langs\n for lang_code in target_langs_for_check\n )\n if cached_covers_all:\n pre_translated.append(row)\n continue\n else:\n logger.reason(\"Partial cache hit — missing languages, routing to LLM\", {\n \"cached_langs\": list(cached_langs.keys()),\n \"target_langs\": target_langs_for_check,\n })\n\n # Check for preview edits carry-forward\n source_data = row.get(\"source_data\") or {}\n if source_data and self._preview_edits_cache:\n key_hash = self._compute_key_hash(source_data)\n preview_edit = self._preview_edits_cache.get(key_hash)\n if preview_edit:\n # Use the first edited language's value as the approved translation\n first_edit = next(iter(preview_edit.values()), None)\n if first_edit:\n row[\"approved_translation\"] = first_edit\n logger.reason(\"Using preview edit carry-forward\", {\n \"key_hash\": key_hash,\n \"langs\": list(preview_edit.keys()),\n })\n pre_translated.append(row)\n continue\n\n rows_for_llm.append(row)\n\n # Handle pre-translated (approved) rows\n target_languages = job.target_languages or [job.target_dialect or \"en\"]\n if not isinstance(target_languages, list):\n target_languages = [str(target_languages)]\n\n for row in pre_translated:\n cached_langs = row.get(\"_cached_lang_values\") # None for approved, dict for cache hits\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=row.get(\"source_text\", \"\"),\n target_sql=row.get(\"approved_translation\", \"\"),\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n source_hash=row.get(\"_source_hash\"),\n status=\"SUCCESS\",\n )\n self.db.add(record)\n\n # Create per-language entry for each target language\n for lang_code in target_languages:\n if cached_langs and lang_code in cached_langs:\n final_val = cached_langs[lang_code]\n else:\n final_val = row.get(\"approved_translation\", \"\")\n lang_entry = TranslationLanguage(\n id=str(uuid.uuid4()),\n record_id=record.id,\n language_code=lang_code,\n source_language_detected=\"und\",\n translated_value=final_val,\n final_value=final_val,\n status=\"translated\",\n needs_review=False,\n )\n self.db.add(lang_entry)\n result[\"successful\"] += 1\n\n # Process rows needing LLM translation\n if rows_for_llm:\n # Resolve provider model for provider-aware token budget\n provider_model = None\n if job.provider_id:\n try:\n p_svc = LLMProviderService(self.db)\n p = p_svc.get_provider(job.provider_id)\n if p:\n provider_model = p.default_model or \"gpt-4o-mini\"\n except Exception:\n provider_model = None\n\n # Check token budget for this batch to determine safe max_tokens\n token_budget = estimate_token_budget(\n source_rows=rows_for_llm,\n target_languages=target_languages,\n source_column=\"source_text\",\n context_columns=None, # Context is embedded in dict, not separate column\n dictionary_entries=dict_matches,\n batch_size=len(rows_for_llm),\n provider_info=provider_model,\n )\n if token_budget[\"warning\"]:\n logger.explore(\"Token budget warning for batch\", {\n \"batch_id\": batch_id,\n \"batch_index\": batch_index,\n \"warning\": token_budget[\"warning\"],\n })\n\n llm_result = self._call_llm_for_batch(\n job=job,\n run_id=run_id,\n batch_rows=rows_for_llm,\n dict_matches=dict_matches,\n batch_id=batch_id,\n max_tokens=token_budget[\"max_output_needed\"],\n )\n result[\"successful\"] += llm_result[\"successful\"]\n result[\"failed\"] += llm_result[\"failed\"]\n result[\"skipped\"] += llm_result[\"skipped\"]\n result[\"retries\"] += llm_result.get(\"retries\", 0)\n\n # Update batch status\n batch.successful_records = result[\"successful\"]\n batch.failed_records = result[\"failed\"]\n batch.completed_at = datetime.now(UTC)\n batch.status = \"COMPLETED\" if result[\"failed\"] == 0 else \"COMPLETED_WITH_ERRORS\"\n self.db.flush()\n\n batch_latency = int((time.monotonic() - batch_start) * 1000)\n logger.reason(f\"Batch {batch_index} complete\", {\n \"batch_id\": batch_id,\n \"latency_ms\": batch_latency,\n **result,\n })\n\n return {**result, \"batch_id\": batch_id}\n # endregion _process_batch\n\n # region _insert_batch_to_target [TYPE Function]\n # @PURPOSE: Insert successful records from a single batch into the target table via Superset SQL Lab.\n # @PRE: batch has committed TranslationRecords with status SUCCESS. job has target_table configured.\n # @POST: Per record, N+1 rows are INSERTED: 1 original + N translations.\n # Context columns are bundled into JSON in the `context` column.\n # `is_original=1` marks the source-language row.\n # @SIDE_EFFECT: HTTP call to Superset SQL Lab API. Writes to target database.\n def _insert_batch_to_target(\n self,\n job: TranslationJob,\n batch_id: str,\n run_id: str,\n ) -> None:\n with belief_scope(\"TranslationExecutor._insert_batch_to_target\"):\n records = (\n self.db.query(TranslationRecord)\n .options(joinedload(TranslationRecord.languages))\n .filter(\n TranslationRecord.batch_id == batch_id,\n TranslationRecord.status == \"SUCCESS\",\n TranslationRecord.target_sql.isnot(None),\n )\n .all()\n )\n\n if not records:\n return\n\n effective_target = job.target_column or job.translation_column\n primary_language = (job.target_languages or [\"en\"])[0]\n\n # Columns that exist in the target ClickHouse table\n columns = []\n if job.target_key_cols:\n columns.extend(job.target_key_cols)\n if effective_target:\n columns.append(effective_target)\n if job.target_language_column:\n columns.append(job.target_language_column)\n if job.target_source_column:\n columns.append(job.target_source_column)\n if job.target_source_language_column:\n columns.append(job.target_source_language_column)\n columns.append(\"context\")\n columns.append(\"is_original\")\n # Deduplicate while preserving order\n seen: set[str] = set()\n deduped: list[str] = []\n for c in columns:\n if c and c not in seen:\n deduped.append(c)\n seen.add(c)\n columns = deduped\n\n # Keys for the context JSON: context_columns + original translation_column\n context_keys = list(job.context_columns or [])\n if job.translation_column and job.translation_column != effective_target and job.translation_column not in context_keys:\n context_keys.append(job.translation_column)\n\n rows_for_sql: list[dict[str, object]] = []\n for rec in records:\n source_data = rec.source_data or {}\n\n # Detect source language from first TranslationLanguage entry\n detected_src_lang = \"und\"\n if rec.languages and len(rec.languages) > 0:\n detected_src_lang = rec.languages[0].source_language_detected or \"und\"\n\n # Build context JSON: all extra columns the user configured\n context_data: dict[str, str] = {}\n for key in context_keys:\n val = source_data.get(key)\n context_data[key] = str(val) if val is not None else \"\"\n\n # ── Shared base row (columns common to original and all translations) ──\n base_row: dict[str, object] = {}\n\n # Key columns (report_date, document_number) — normalize timestamps to YYYY-MM-DD\n if job.target_key_cols:\n for k in job.target_key_cols:\n raw = source_data.get(k)\n if raw is not None:\n normalized = _normalize_timestamp_value(raw)\n base_row[k] = normalized if normalized else raw\n else:\n base_row[k] = None\n\n # Source text: original\n if job.target_source_column:\n base_row[job.target_source_column] = rec.source_sql or \"\"\n\n # Source language (same for all rows of this record)\n if job.target_source_language_column:\n base_row[job.target_source_language_column] = detected_src_lang\n\n # Context JSON string\n base_row[\"context\"] = json.dumps(context_data, ensure_ascii=False)\n\n # ── 1. ORIGINAL row (is_original = 1) ──\n original_row = dict(base_row)\n if effective_target:\n original_row[effective_target] = rec.source_sql or \"\"\n if job.target_language_column:\n original_row[job.target_language_column] = detected_src_lang\n original_row[\"is_original\"] = 1\n rows_for_sql.append(original_row)\n\n # ── 2. TRANSLATION rows (is_original = 0) ──\n # Skip language that matches the source — the original row already covers it\n if rec.languages and len(rec.languages) > 0:\n for lang in rec.languages:\n if lang.language_code == detected_src_lang:\n continue\n trans_row = dict(base_row)\n trans_value = lang.final_value or lang.translated_value or \"\"\n if effective_target:\n trans_row[effective_target] = trans_value\n if job.target_language_column:\n trans_row[job.target_language_column] = lang.language_code\n trans_row[\"is_original\"] = 0\n rows_for_sql.append(trans_row)\n else:\n # Fallback: no per-language data → single translation row with primary language\n fallback_row = dict(base_row)\n if effective_target:\n fallback_row[effective_target] = rec.target_sql or \"\"\n if job.target_language_column:\n fallback_row[job.target_language_column] = primary_language\n fallback_row[\"is_original\"] = 0\n rows_for_sql.append(fallback_row)\n\n if not columns:\n columns = [effective_target or \"translated_text\"]\n rows_for_sql = [{columns[0]: rec.target_sql or \"\"} for rec in records]\n\n try:\n env_id = job.environment_id or job.source_dialect or \"\"\n executor = SupersetSqlLabExecutor(self.config_manager, env_id)\n executor.resolve_database_id(target_database_id=job.target_database_id)\n real_backend = executor.get_database_backend()\n except Exception as e:\n logger.explore(\"Failed to resolve database backend for batch insert\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n real_backend = None\n\n dialect = real_backend or job.database_dialect or job.target_dialect or \"postgresql\"\n\n try:\n sql, row_count = SQLGenerator.generate(\n dialect=dialect,\n target_schema=job.target_schema,\n target_table=job.target_table or \"translated_data\",\n columns=columns,\n rows=rows_for_sql,\n key_columns=job.target_key_cols,\n upsert_strategy=job.upsert_strategy or \"MERGE\",\n )\n except ValueError as e:\n logger.explore(\"SQL generation failed for batch\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n return\n\n try:\n result = executor.execute_and_poll(\n sql=sql,\n max_polls=30,\n poll_interval_seconds=2.0,\n )\n except Exception as e:\n logger.explore(\"Superset SQL submission failed for batch\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n })\n return\n\n logger.reason(f\"Batch {batch_id[:12]} inserted {row_count} rows\", {\n \"batch_id\": batch_id,\n \"rows\": row_count,\n \"status\": result.get(\"status\"),\n })\n # endregion _insert_batch_to_target\n\n # region _update_language_stats_incremental [TYPE Function]\n # @PURPOSE: Update per-language TranslationRunLanguageStats incrementally after each batch.\n # @PRE: language_stats_map has entries for all target languages.\n # @POST: Language stat objects updated with counts from committed TranslationLanguage rows.\n # @SIDE_EFFECT: Mutates ORM objects; caller must commit.\n def _update_language_stats_incremental(\n self,\n run_id: str,\n language_stats_map: dict[str, TranslationRunLanguageStats],\n ) -> None:\n with belief_scope(\"TranslationExecutor._update_language_stats_incremental\"):\n records = (\n self.db.query(TranslationRecord)\n .filter(TranslationRecord.run_id == run_id)\n .all()\n )\n record_ids = [r.id for r in records]\n if not record_ids:\n return\n\n lang_entries = (\n self.db.query(TranslationLanguage)\n .filter(TranslationLanguage.record_id.in_(record_ids))\n .all()\n )\n\n from collections import defaultdict\n agg: dict[str, dict[str, int]] = defaultdict(\n lambda: {\"total\": 0, \"translated\": 0, \"failed\": 0, \"skipped\": 0}\n )\n for le in lang_entries:\n code = le.language_code\n agg[code][\"total\"] += 1\n if le.status in (\"translated\", \"approved\", \"edited\"):\n agg[code][\"translated\"] += 1\n elif le.status == \"failed\":\n agg[code][\"failed\"] += 1\n elif le.status == \"skipped\":\n agg[code][\"skipped\"] += 1\n\n total_tokens_est = max(1, sum(len(le.translated_value or \"\") for le in lang_entries if le.translated_value) // 4)\n num_langs = len(language_stats_map) or 1\n cost_per_token = 0.002 / 1000\n\n for lang_code, lang_stat in language_stats_map.items():\n data = agg.get(lang_code, {\"total\": 0, \"translated\": 0, \"failed\": 0, \"skipped\": 0})\n lang_stat.total_rows = data[\"total\"]\n lang_stat.translated_rows = data[\"translated\"]\n lang_stat.failed_rows = data[\"failed\"]\n lang_stat.skipped_rows = data[\"skipped\"]\n lang_stat.token_count = total_tokens_est // num_langs\n lang_stat.estimated_cost = round((lang_stat.token_count / 1000) * cost_per_token, 6)\n\n self.db.flush()\n # endregion _update_language_stats_incremental\n\n # region _call_llm_for_batch [TYPE Function]\n # @PURPOSE: Call LLM for a batch of rows requiring translation. Parse structured JSON response.\n # @PRE: job has valid provider_id. batch_rows is non-empty.\n # @POST: Returns dict with successful/failed/skipped counts. Creates TranslationRecord rows.\n # @SIDE_EFFECT: HTTP call to LLM provider.\n def _call_llm_for_batch(\n self,\n job: TranslationJob,\n run_id: str,\n batch_rows: list[dict[str, Any]],\n dict_matches: list[dict[str, Any]],\n batch_id: str,\n max_tokens: int = 8192,\n _recursion_depth: int = 0,\n ) -> dict[str, int]:\n with belief_scope(\"TranslationExecutor._call_llm_for_batch\"):\n # Build dictionary section using ContextAwarePromptBuilder\n dictionary_section = \"\"\n if dict_matches:\n # Get row_context from first batch row if available\n row_context = batch_rows[0].get(\"source_data\") if batch_rows else None\n\n # Use ContextAwarePromptBuilder for context-aware annotations\n annotated_entries = ContextAwarePromptBuilder.build_context_entries(\n dict_matches, row_context\n )\n glossary_lines = []\n for annotated_line in annotated_entries:\n glossary_lines.append(f\"- {annotated_line}\")\n dictionary_section = (\n \"Terminology dictionary (use these translations when applicable):\\n\"\n + \"\\n\".join(glossary_lines)\n + \"\\n\\n\"\n )\n\n # Resolve target languages\n target_languages = job.target_languages or [job.target_dialect or \"en\"]\n if not isinstance(target_languages, list):\n target_languages = [str(target_languages)]\n target_languages_str = \", \".join(target_languages)\n\n # Build rows JSON for LLM\n rows_json = json.dumps([\n {\n \"row_id\": str(row.get(\"row_index\", idx)),\n \"text\": row.get(\"source_text\", \"\"),\n }\n for idx, row in enumerate(batch_rows)\n ], indent=2)\n\n # Build prompt (use multi-language format)\n prompt = render_prompt(\n DEFAULT_EXECUTION_PROMPT_TEMPLATE,\n {\n \"source_language\": job.source_dialect or \"SQL\",\n \"target_language\": target_languages_str,\n \"target_languages\": target_languages_str,\n \"source_dialect\": job.source_dialect or \"\",\n \"target_dialect\": job.target_dialect or \"\",\n \"translation_column\": job.translation_column or \"\",\n \"dictionary_section\": dictionary_section,\n \"rows_json\": rows_json,\n \"row_count\": str(len(batch_rows)),\n },\n )\n\n # Call LLM with retry\n llm_response = None\n last_error = None\n retries = 0\n\n finish_reason: str | None = None\n for attempt in range(1, MAX_RETRIES_PER_BATCH + 1):\n try:\n llm_response, finish_reason = self._call_llm(job, prompt, max_tokens=max_tokens)\n break\n except Exception as e:\n last_error = str(e)\n retries += 1\n logger.explore(f\"LLM call failed (attempt {attempt})\", {\n \"batch_id\": batch_id,\n \"error\": last_error,\n \"attempt\": attempt,\n })\n if attempt < MAX_RETRIES_PER_BATCH:\n time.sleep(2 ** attempt) # Exponential backoff\n else:\n logger.explore(\"LLM call exhausted retries\", {\n \"batch_id\": batch_id,\n \"last_error\": last_error,\n })\n\n if llm_response is None:\n # All retries failed — mark all rows as failed\n for row in batch_rows:\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=row.get(\"source_text\", \"\"),\n target_sql=None,\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"FAILED\",\n error_message=f\"LLM call failed after {retries} retries: {last_error}\",\n )\n self.db.add(record)\n return {\"successful\": 0, \"failed\": len(batch_rows), \"skipped\": 0, \"retries\": retries}\n\n # ── Truncation detection: finish_reason=\"length\" → split batch ──\n if finish_reason == \"length\" and len(batch_rows) >= 2 and run_id:\n if _recursion_depth < MAX_RETRIES_PER_BATCH:\n mid = len(batch_rows) // 2\n logger.explore(\"LLM output truncated — splitting batch\", {\n \"batch_id\": batch_id,\n \"batch_size\": len(batch_rows),\n \"split_at\": mid,\n \"recursion_depth\": _recursion_depth,\n \"finish_reason\": finish_reason,\n })\n left_result = self._call_llm_for_batch(\n job=job,\n run_id=run_id,\n batch_rows=batch_rows[:mid],\n dict_matches=dict_matches,\n batch_id=batch_id + \"_L\",\n max_tokens=max_tokens,\n _recursion_depth=_recursion_depth + 1,\n )\n right_result = self._call_llm_for_batch(\n job=job,\n run_id=run_id,\n batch_rows=batch_rows[mid:],\n dict_matches=dict_matches,\n batch_id=batch_id + \"_R\",\n max_tokens=max_tokens,\n _recursion_depth=_recursion_depth + 1,\n )\n merged = {\n \"successful\": left_result[\"successful\"] + right_result[\"successful\"],\n \"failed\": left_result[\"failed\"] + right_result[\"failed\"],\n \"skipped\": left_result[\"skipped\"] + right_result[\"skipped\"],\n \"retries\": retries + left_result.get(\"retries\", 0) + right_result.get(\"retries\", 0),\n }\n logger.reason(\"Truncation resolved by batch splitting\", {\n \"batch_id\": batch_id,\n \"left_successful\": left_result[\"successful\"],\n \"right_successful\": right_result[\"successful\"],\n \"left_size\": len(batch_rows[:mid]),\n \"right_size\": len(batch_rows[mid:]),\n })\n return merged\n else:\n logger.explore(\"Truncation recursion depth exceeded — accepting truncated output\", {\n \"batch_id\": batch_id,\n \"recursion_depth\": _recursion_depth,\n \"max_depth\": MAX_RETRIES_PER_BATCH,\n })\n # Fall through to parse truncated response\n\n # Parse LLM response (multi-language aware)\n try:\n translations = self._parse_llm_response(llm_response, len(batch_rows), target_languages=target_languages)\n except ValueError as e:\n # Parse failure — mark all rows as SKIPPED\n logger.explore(\"LLM response parse failed\", {\n \"batch_id\": batch_id,\n \"error\": str(e),\n \"response_preview\": llm_response[:500] if llm_response else \"\",\n })\n skipped = len(batch_rows)\n for row in batch_rows:\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=row.get(\"source_text\", \"\"),\n target_sql=None,\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"SKIPPED\",\n error_message=f\"LLM parse failure: {e}\",\n )\n self.db.add(record)\n return {\n \"successful\": 0,\n \"failed\": 0,\n \"skipped\": skipped,\n \"retries\": retries,\n }\n\n successful = 0\n failed = 0\n skipped = 0\n\n for row in batch_rows:\n row_id = str(row.get(\"row_index\", \"\"))\n translation_data = translations.get(row_id)\n source_text = row.get(\"source_text\", \"\")\n detected_lang = \"und\"\n if translation_data:\n detected_lang = translation_data.get(\"detected_source_language\", \"und\")\n\n if translation_data is None:\n # NULL translation — skip\n skipped += 1\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=source_text,\n target_sql=\"\",\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"SKIPPED\",\n error_message=\"NULL translation returned by LLM\",\n )\n self.db.add(record)\n continue\n\n # Collect per-language translated values\n per_lang_values: dict[str, str] = {}\n has_any_translation = False\n\n # First try multi-language format (per-language keys)\n for lang_code in target_languages:\n lang_val = translation_data.get(lang_code)\n if lang_val is not None and str(lang_val).strip():\n per_lang_values[lang_code] = str(lang_val)\n has_any_translation = True\n\n # Fallback to legacy single \"translation\" key format\n if not has_any_translation:\n translation_text = translation_data.get(\"translation\", \"\")\n if translation_text.strip():\n per_lang_values[target_languages[0]] = translation_text\n has_any_translation = True\n\n # ── Dictionary post-processing enforcement ──\n # If a dictionary entry matches the source text but the target term\n # is missing from the LLM output, apply forced replacement.\n if dict_matches and source_text:\n _enforce_dictionary(source_text, per_lang_values, dict_matches,\n batch_id, row_id)\n # ── End dictionary enforcement ──\n\n if not has_any_translation:\n # Empty/all-empty translations — skip\n skipped += 1\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=source_text,\n target_sql=\"\",\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n status=\"SKIPPED\",\n error_message=\"Empty translation returned by LLM\",\n )\n self.db.add(record)\n continue\n\n # Use the first language's value as the primary target_sql for backward compat\n primary_translation = next(iter(per_lang_values.values()), \"\")\n\n successful += 1\n record = TranslationRecord(\n id=str(uuid.uuid4()),\n batch_id=batch_id,\n run_id=run_id,\n source_sql=source_text,\n target_sql=primary_translation,\n source_object_type=\"table_row\",\n source_object_id=row.get(\"row_index\"),\n source_object_name=row.get(\"source_object_name\", \"\"),\n source_data=row.get(\"source_data\"),\n source_hash=row.get(\"_source_hash\"),\n status=\"SUCCESS\",\n )\n self.db.add(record)\n\n # Create per-language entries — one TranslationLanguage per language_code\n for lang_code in target_languages:\n # Skip if this language matches the detected source language —\n # no translation needed, and it shouldn't appear in stats\n if detected_lang != \"und\" and str(lang_code).lower() == str(detected_lang).lower():\n continue\n\n lang_translation = per_lang_values.get(lang_code, \"\")\n\n # Check for undetermined source language\n lang_needs_review = (detected_lang == \"und\")\n\n if lang_needs_review:\n logger.explore(\"undetected language\", {\n \"record_id\": row_id,\n \"language_code\": lang_code,\n \"source_text\": source_text[:100],\n })\n\n lang_entry = TranslationLanguage(\n id=str(uuid.uuid4()),\n record_id=record.id,\n language_code=lang_code,\n source_language_detected=detected_lang,\n translated_value=lang_translation or \"\",\n final_value=lang_translation or \"\",\n status=\"translated\",\n needs_review=lang_needs_review,\n )\n self.db.add(lang_entry)\n\n return {\n \"successful\": successful,\n \"failed\": failed,\n \"skipped\": skipped,\n \"retries\": retries,\n }\n # endregion _call_llm_for_batch\n\n # region _call_llm [TYPE Function]\n # @PURPOSE: Call the configured LLM provider with the batch prompt.\n # @PRE: job has valid provider_id.\n # @POST: Returns raw LLM response string.\n # @SIDE_EFFECT: HTTP call to LLM provider.\n def _call_llm(self, job: TranslationJob, prompt: str, max_tokens: int = 8192) -> tuple[str, str | None]:\n with belief_scope(\"TranslationExecutor._call_llm\"):\n if not job.provider_id:\n raise ValueError(\"Job has no LLM provider configured\")\n\n provider_svc = LLMProviderService(self.db)\n provider = provider_svc.get_provider(job.provider_id)\n if not provider:\n raise ValueError(f\"LLM provider '{job.provider_id}' not found\")\n\n api_key = provider_svc.get_decrypted_api_key(job.provider_id)\n if not api_key:\n raise ValueError(f\"Could not decrypt API key for provider '{job.provider_id}'\")\n\n model = provider.default_model or \"gpt-4o-mini\"\n provider_type = provider.provider_type.lower() if provider.provider_type else \"openai\"\n\n disable_reasoning = getattr(job, 'disable_reasoning', False)\n\n if provider_type in (\"openai\", \"openai_compatible\", \"openrouter\", \"kilo\", \"litellm\"):\n response_text, finish_reason = self._call_openai_compatible(\n base_url=provider.base_url,\n api_key=api_key,\n model=model,\n prompt=prompt,\n provider_type=provider_type,\n max_tokens=max_tokens,\n disable_reasoning=disable_reasoning,\n )\n return response_text, finish_reason\n else:\n raise ValueError(f\"Unsupported provider type '{provider_type}'\")\n # endregion _call_llm\n\n # region _call_openai_compatible [TYPE Function]\n # @PURPOSE: Call OpenAI-compatible API for batch translation.\n # @PRE: Valid API endpoint, key, model, and prompt.\n # @POST: Returns response text.\n # @SIDE_EFFECT: HTTP POST to LLM API.\n @staticmethod\n def _call_openai_compatible(\n base_url: str,\n api_key: str,\n model: str,\n prompt: str,\n provider_type: str = \"openai\",\n max_tokens: int = 8192,\n disable_reasoning: bool = False,\n ) -> tuple[str, str | None]:\n with belief_scope(\"TranslationExecutor._call_openai_compatible\"):\n if not base_url:\n raise ValueError(\"LLM provider has no base_url configured\")\n import requests as http_requests\n\n url = f\"{base_url.rstrip('/')}/chat/completions\"\n headers = {\n \"Authorization\": f\"Bearer {api_key}\",\n \"Content-Type\": \"application/json\",\n }\n # Reasoning-safe system prompt: always forbid chain-of-thought in output.\n # Reasoning models (DeepSeek, QwQ, etc.) may leak reasoning into content\n # when response_format=json_object is set. Explicit suppression in the\n # system message prevents this regardless of the disable_reasoning flag.\n system_content = (\n \"You are a database content translation assistant. \"\n \"Translate the provided text accurately, preserving data semantics. \"\n \"Respond directly with ONLY the JSON result. \"\n \"Do NOT include any reasoning, thinking, chain-of-thought, analysis, \"\n \"or explanation. Output ONLY valid JSON.\"\n )\n\n payload = {\n \"model\": model,\n \"messages\": [\n {\"role\": \"system\", \"content\": system_content},\n {\"role\": \"user\", \"content\": prompt},\n ],\n \"temperature\": 0.1,\n \"max_tokens\": max_tokens,\n }\n\n # Structured output — OpenRouter and Kilo also support response_format, but some\n # upstream providers (e.g. StepFun) reject it. We try with response_format and\n # fall back on 400 \"structured_outputs is not supported\".\n # NOTE: Reasoning models (deepseek, qwen with reasoning, etc.) often break with\n # response_format=json_object, producing reasoning text instead of JSON.\n # When disable_reasoning is set, we remove response_format and rely on the\n # system prompt to enforce JSON-only output.\n if provider_type in (\"openai\", \"openai_compatible\", \"kilo\", \"openrouter\", \"litellm\"):\n if not disable_reasoning:\n payload[\"response_format\"] = {\"type\": \"json_object\"}\n\n # Suppress Chain of Thought reasoning to save output tokens\n # NOTE: Kilo/OpenRouter/LiteLLM do NOT support disabling reasoning (returns 400)\n if disable_reasoning:\n if provider_type not in (\"kilo\", \"openrouter\", \"litellm\"):\n payload[\"reasoning_effort\"] = \"none\"\n # response_format already skipped above when disable_reasoning is True\n # Use caller-provided max_tokens instead of hardcoded 8192\n payload[\"max_tokens\"] = max_tokens\n\n logger.reason(\n f\"LLM request url={base_url} model={payload.get('model')} \"\n f\"provider_type={provider_type} \"\n f\"response_format={'yes' if 'response_format' in payload else 'no'} \"\n f\"prompt_len={len(prompt)}\"\n )\n\n # ── Handle rate limiting with Retry-After header ──\n import time as _time\n _max_retry_429 = 3\n _retry_count_429 = 0\n while _retry_count_429 < _max_retry_429:\n response = http_requests.post(url, headers=headers, json=payload, timeout=180)\n if response.status_code == 429:\n _retry_count_429 += 1\n retry_after = response.headers.get(\"Retry-After\")\n if retry_after:\n try:\n wait = int(retry_after)\n except (ValueError, TypeError):\n wait = 2 ** _retry_count_429\n else:\n wait = 2 ** _retry_count_429\n logger.explore(\n f\"Rate limited (429), retry {_retry_count_429}/{_max_retry_429} \"\n f\"after {wait}s\",\n extra={\"src\": \"executor\", \"retry_after\": retry_after, \"wait\": wait},\n )\n _time.sleep(wait)\n if _retry_count_429 >= _max_retry_429:\n break\n else:\n break\n\n _response_format_error_patterns = (\"response_format\", \"structured_outputs\", \"structured\", \"json_object\")\n if (\n not response.ok\n and response.status_code == 400\n and any(p in (response.text or \"\").lower() for p in _response_format_error_patterns)\n ):\n logger.explore(\"Structured outputs not supported by upstream, retrying without response_format\", extra={\"src\": \"executor\"})\n payload.pop(\"response_format\", None)\n response = http_requests.post(url, headers=headers, json=payload, timeout=180)\n if not response.ok:\n logger.explore(\n f\"LLM API error status={response.status_code} \"\n f\"model={payload.get('model')} \"\n f\"body={response.text[:2000]}\"\n )\n response.raise_for_status()\n data = response.json()\n\n choices = data.get(\"choices\", [])\n if not choices:\n logger.explore(\"LLM returned no choices\", extra={\"src\": \"executor\", \"response_keys\": list(data.keys()), \"response_preview\": str(data)[:2000]})\n raise ValueError(\"LLM returned no choices\")\n\n try:\n finish_reason = choices[0].get(\"finish_reason\") or \"none\"\n msg = choices[0].get(\"message\") or {}\n except (TypeError, AttributeError) as e:\n logger.explore(\"TypeError processing LLM response choices\", extra={\n \"src\": \"executor_diag\",\n \"error\": str(e),\n \"choices_0_type\": type(choices[0]).__name__ if choices else \"N/A\",\n \"choices_0_repr\": repr(choices[0])[:2000] if choices else \"N/A\",\n \"data_type\": type(data).__name__,\n \"data_preview\": str(data)[:2000],\n })\n raise ValueError(f\"LLM response processing failed: {e}\")\n\n # Handle model refusal (content is empty/null, refusal field has reason)\n refusal = msg.get(\"refusal\") if isinstance(msg, dict) else None\n if refusal:\n logger.explore(\"LLM refused to respond\", extra={\n \"src\": \"executor\",\n \"refusal\": str(refusal)[:500],\n \"finish_reason\": finish_reason,\n })\n raise ValueError(f\"LLM refused to respond: {refusal}\")\n content = msg.get(\"content\") if isinstance(msg, dict) else \"\"\n if not content and isinstance(msg, dict):\n content = msg.get(\"content\") or \"\"\n logger.reason(f\"LLM response finish_reason={finish_reason} content_len={len(content)} msg_keys={list(msg.keys()) if isinstance(msg, dict) else []}\")\n if not content:\n logger.explore(\"LLM returned empty content\", extra={\"src\": \"executor\", \"finish_reason\": finish_reason, \"msg_keys\": list(msg.keys()) if isinstance(msg, dict) else [], \"response_preview\": str(data)[:2000]})\n raise ValueError(\"LLM returned empty content\")\n\n return content, finish_reason\n # endregion _call_openai_compatible\n\n # region _parse_llm_response [TYPE Function]\n # @PURPOSE: Parse LLM JSON response into dict of row_id -> per-language translations.\n # Supports multi-language format: {\"row_id\": 0, \"detected_source_language\": \"fr\", \"ru\": \"текст\", \"en\": \"text\"}\n # Backward-compat: old format {\"row_id\": 0, \"translation\": \"text\", \"detected_source_language\": \"fr\"}\n # @PRE: response_text is valid JSON with {\"rows\": [...]} structure.\n # @POST: Returns dict mapping row_id to dict with 'detected_source_language' and per-language codes.\n @staticmethod\n def _parse_llm_response(response_text: str, expected_count: int, target_languages: list[str] | None = None, finish_reason: str | None = None) -> dict[str, dict[str, str]]:\n with belief_scope(\"TranslationExecutor._parse_llm_response\"):\n try:\n data = json.loads(response_text)\n except json.JSONDecodeError:\n # Try to extract from markdown code block\n import re\n match = re.search(r'```(?:json)?\\s*\\n?(.*?)\\n?```', response_text, re.DOTALL)\n if match:\n try:\n data = json.loads(match.group(1))\n rows = data.get(\"rows\", [])\n if isinstance(rows, list) and rows:\n logger.reason(\"Parsed JSON from markdown code block\", {\"rows\": len(rows)})\n except json.JSONDecodeError:\n pass # fall through to partial recovery\n else:\n pass # fall through to partial recovery\n \n # If finish_reason=length, try to recover complete rows from truncated JSON\n logger.explore(\"LLM truncated, trying partial row recovery\", extra={\"src\": \"executor\", \"finish_reason\": finish_reason, \"response_length\": len(response_text)})\n rows_match = re.findall(r'\\{\\s*\"row_id\"\\s*:\\s*(?:\\d+|\"\\d+\").*?\\}\\s*', response_text, re.DOTALL)\n if rows_match:\n partial_rows = []\n for row_text in rows_match:\n try:\n row_data = json.loads(row_text)\n partial_rows.append(row_data)\n except json.JSONDecodeError:\n continue\n if partial_rows:\n logger.explore(f\"Recovered {len(partial_rows)}/{expected_count} complete rows from truncated response\", extra={\"src\": \"executor\"})\n data = {\"rows\": partial_rows}\n else:\n logger.explore(\"Could not recover any complete rows\", extra={\"src\": \"executor\", \"response_preview\": response_text[:1000]})\n raise ValueError(\"LLM response was not valid JSON (could not recover any rows)\")\n else:\n logger.explore(\"No complete rows found in truncated response\", extra={\"src\": \"executor\", \"response_preview\": response_text[:1000]})\n raise ValueError(\"LLM response was not valid JSON\")\n\n rows = data.get(\"rows\", [])\n if not isinstance(rows, list):\n raise ValueError(\"LLM response missing 'rows' array\")\n\n translations: dict[str, dict[str, str]] = {}\n for item in rows:\n row_id = str(item.get(\"row_id\", \"\"))\n if not row_id:\n continue\n\n detected_lang = str(item.get(\"detected_source_language\", \"und\")) if item.get(\"detected_source_language\") else \"und\"\n result: dict[str, str] = {\n \"detected_source_language\": detected_lang,\n }\n\n # Multi-language format: extract per-language keys\n has_language_data = False\n if target_languages:\n for lang_code in target_languages:\n lang_val = item.get(lang_code)\n if lang_val is not None and str(lang_val).strip():\n result[lang_code] = str(lang_val)\n has_language_data = True\n\n # Fallback to legacy single \"translation\" key format\n if not has_language_data:\n translation = item.get(\"translation\")\n if translation is not None:\n result[\"translation\"] = str(translation)\n has_language_data = True\n\n if has_language_data:\n translations[row_id] = result\n\n if len(translations) < expected_count:\n logger.explore(\n f\"LLM returned fewer translations expected={expected_count} \"\n f\"got={len(translations)}\"\n )\n\n return translations\n # endregion _parse_llm_response\n\n\n# #endregion TranslationExecutor\n# #endregion TranslationExecutor\n" }, { "contract_id": "MAX_RETRIES_PER_BATCH", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/executor.py", - "start_line": 48, - "end_line": 50, + "start_line": 50, + "end_line": 52, "tier": "TIER_1", "complexity": 1, "metadata": {}, @@ -49916,8 +49176,8 @@ "contract_id": "MAX_ROWS_PER_RUN", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/executor.py", - "start_line": 52, - "end_line": 57, + "start_line": 54, + "end_line": 59, "tier": "TIER_1", "complexity": 1, "metadata": {}, @@ -49927,16 +49187,104 @@ "tier_source": "AutoCalculated", "body": "# #region MAX_ROWS_PER_RUN [TYPE Constant]\n# Safety cap: without it, a datasource with thousands of rows blocks the single\n# uvicorn worker for minutes/hours. Preview uses sample_size (5-10). Full run\n# should stay within reasonable bounds.\nMAX_ROWS_PER_RUN = 10000\n# #endregion MAX_ROWS_PER_RUN\n" }, + { + "contract_id": "_enforce_dictionary", + "contract_type": "Function", + "file_path": "backend/src/plugins/translate/executor.py", + "start_line": 61, + "end_line": 125, + "tier": "TIER_1", + "complexity": 1, + "metadata": { + "PURPOSE": "Post-processing: enforce dictionary entries in LLM output." + }, + "relations": [], + "schema_warnings": [], + "anchor_syntax": "region", + "tier_source": "AutoCalculated", + "body": "# #region _enforce_dictionary [TYPE Function]\n# @BRIEF Post-processing: enforce dictionary entries in LLM output.\n# If a source term from the dictionary appears in the original text,\n# but the target term is missing from the translation, replace occurrences\n# of the source term in the translated output with the target term.\n# @PRE: dict_matches is a list of dict entries with source_term/target_term.\n# @POST: per_lang_values may be mutated to include forced dictionary replacements.\n# @SIDE_EFFECT: Logs when enforcement is applied.\ndef _enforce_dictionary(\n source_text: str,\n per_lang_values: dict[str, str],\n dict_matches: list[dict[str, Any]],\n batch_id: str,\n row_id: str,\n) -> None:\n \"\"\"Post-processing: enforce dictionary terms in LLM translation output.\n\n For each dictionary entry whose source_term appears in the source_text,\n verify that the target_term is present in each language's translation.\n If missing, replace any occurrence of the source term (which the LLM\n may have left untranslated) with the dictionary target term.\n \"\"\"\n if not dict_matches or not source_text:\n return\n\n text_lower = source_text.lower()\n\n for dm in dict_matches:\n src_term = dm.get(\"source_term\", \"\")\n tgt_term = dm.get(\"target_term\", \"\")\n if not src_term or not tgt_term:\n continue\n\n # Only process if source term actually appears in the original text\n if src_term.lower() not in text_lower:\n continue\n\n # Try to replace in each language\n for lang_code in list(per_lang_values.keys()):\n val = per_lang_values[lang_code]\n if not val:\n continue\n\n # Check if target term is already present (case-insensitive)\n if tgt_term.lower() in val.lower():\n continue\n\n # Try to find the source term (or a close variant) in the output\n # This catches cases where the LLM left the source term untranslated\n # Use lambda to prevent regex back-reference injection from tgt_term\n src_pattern = re.compile(re.escape(src_term), re.IGNORECASE)\n if src_pattern.search(val):\n new_val = src_pattern.sub(lambda _: tgt_term, val)\n if new_val != val:\n logger.reason(\"Dictionary enforcement applied\", {\n \"batch_id\": batch_id,\n \"row_id\": row_id,\n \"language_code\": lang_code,\n \"source_term\": src_term,\n \"target_term\": tgt_term,\n \"before\": val[:200],\n \"after\": new_val[:200],\n })\n per_lang_values[lang_code] = new_val\n# #endregion _enforce_dictionary\n" + }, + { + "contract_id": "_compute_source_hash", + "contract_type": "Function", + "file_path": "backend/src/plugins/translate/executor.py", + "start_line": 128, + "end_line": 161, + "tier": "TIER_1", + "complexity": 1, + "metadata": { + "PURPOSE": "Compute deterministic cache key for a source row." + }, + "relations": [], + "schema_warnings": [], + "anchor_syntax": "region", + "tier_source": "AutoCalculated", + "body": "# #region _compute_source_hash [TYPE Function]\n# @BRIEF Compute deterministic cache key for a source row.\n# SHA256 of (source_text + context_fields + dict_snapshot_hash + config_hash).\n# Key columns (row_id, primary keys) are EXCLUDED — only the text content\n# and meaningful context affect the hash. This ensures identical text with\n# different row identifiers still produces a cache hit.\ndef _compute_source_hash(\n source_text: str,\n source_data: dict | None,\n dict_snapshot_hash: str | None,\n config_hash: str | None,\n context_keys: list[str] | None = None,\n) -> str:\n \"\"\"Deterministic cache key for a translation source row.\n\n Only includes source_text and context-relevant fields from source_data.\n Key/identifier columns are excluded so identical text in different rows hits cache.\n \"\"\"\n # Extract only context-relevant fields from source_data (not keys/identifiers)\n context_data: dict[str, str] = {}\n if source_data and context_keys:\n for key in context_keys:\n val = source_data.get(key)\n if val is not None:\n context_data[key] = str(val)\n\n payload = json.dumps({\n \"text\": source_text,\n \"ctx\": context_data, # only context columns, no key/row_id\n \"dict_hash\": dict_snapshot_hash or \"\",\n \"config_hash\": config_hash or \"\",\n }, sort_keys=True, ensure_ascii=False)\n return hashlib.sha256(payload.encode(\"utf-8\")).hexdigest()\n# #endregion _compute_source_hash\n" + }, + { + "contract_id": "_check_translation_cache", + "contract_type": "Function", + "file_path": "backend/src/plugins/translate/executor.py", + "start_line": 164, + "end_line": 194, + "tier": "TIER_1", + "complexity": 1, + "metadata": { + "PURPOSE": "Look up a previously successful translation by source_hash." + }, + "relations": [], + "schema_warnings": [], + "anchor_syntax": "region", + "tier_source": "AutoCalculated", + "body": "# #region _check_translation_cache [TYPE Function]\n# @BRIEF Look up a previously successful translation by source_hash.\n# Returns per-language dict {lang_code: final_value} or None.\ndef _check_translation_cache(\n db: Session,\n source_hash: str,\n) -> dict[str, str] | None:\n \"\"\"Check if this source_hash was already translated successfully.\"\"\"\n from sqlalchemy.orm import joinedload\n from ...models.translate import TranslationRecord, TranslationLanguage\n\n cached = (\n db.query(TranslationRecord)\n .options(joinedload(TranslationRecord.languages))\n .filter(\n TranslationRecord.source_hash == source_hash,\n TranslationRecord.status == \"SUCCESS\",\n )\n .order_by(TranslationRecord.created_at.desc())\n .first()\n )\n if not cached:\n return None\n\n lang_values: dict[str, str] = {}\n for lang in cached.languages:\n if lang.status == \"translated\" and lang.final_value:\n lang_values[lang.language_code] = lang.final_value\n\n return lang_values if lang_values else None\n# #endregion _check_translation_cache\n" + }, + { + "contract_id": "estimate_row_tokens", + "contract_type": "Function", + "file_path": "backend/src/plugins/translate/executor.py", + "start_line": 197, + "end_line": 224, + "tier": "TIER_2", + "complexity": 4, + "metadata": { + "COMPLEXITY": 4, + "POST": "Returns estimated token count >= 1.", + "PRE": "source_text is a string.", + "PURPOSE": "Estimate token count for a single source row including context fields." + }, + "relations": [ + { + "source_id": "estimate_row_tokens", + "relation_type": "DEPENDS_ON", + "target_id": "_estimate_tokens_for_text", + "target_ref": "[_estimate_tokens_for_text]" + } + ], + "schema_warnings": [ + { + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", + "detail": { + "actual_complexity": 4, + "contract_type": "Function" + } + } + ], + "anchor_syntax": "region", + "tier_source": "AutoCalculated", + "body": "# #region estimate_row_tokens [C:4] [TYPE Function] [SEMANTICS translate, token, estimation]\n# @BRIEF Estimate token count for a single source row including context fields.\n# @PRE source_text is a string.\n# @POST Returns estimated token count >= 1.\n# @RELATION DEPENDS_ON -> [_estimate_tokens_for_text]\n\ndef estimate_row_tokens(\n source_text: str,\n source_data: dict | None,\n job,\n) -> int:\n \"\"\"Estimate token count for a single source row including context fields.\n\n Uses CJK-aware heuristics via _token_budget._estimate_tokens_for_text.\n Context fields from job.context_columns are included in the estimate.\n \"\"\"\n from ._token_budget import _estimate_tokens_for_text\n\n text_tokens = _estimate_tokens_for_text(source_text or \"\")\n\n context_keys = job.context_columns or []\n ctx_text = \"\"\n if source_data and context_keys:\n ctx_text = \" \".join(str(source_data.get(k, \"\")) for k in context_keys)\n ctx_tokens = _estimate_tokens_for_text(ctx_text)\n\n return text_tokens + ctx_tokens\n# #endregion estimate_row_tokens\n" + }, { "contract_id": "TranslationMetrics", "contract_type": "Module", "file_path": "backend/src/plugins/translate/metrics.py", "start_line": 1, - "end_line": 191, + "end_line": 192, "tier": "TIER_2", - "complexity": 3, + "complexity": 4, "metadata": { - "COMPLEXITY": 3, + "COMPLEXITY": "4", "LAYER": "Domain", "POST": "Metrics are aggregated and returned; no side effects.", "PRE": "Database session is open.", @@ -49948,20 +49296,20 @@ { "source_id": "TranslationMetrics", "relation_type": "DEPENDS_ON", - "target_id": "TranslationEvent:Class", - "target_ref": "[TranslationEvent:Class]" + "target_id": "TranslationSchedule:Class", + "target_ref": "[TranslationSchedule:Class]" }, { "source_id": "TranslationMetrics", "relation_type": "DEPENDS_ON", - "target_id": "MetricSnapshot:Class", - "target_ref": "[MetricSnapshot:Class]" + "target_id": "TranslationSchedule:Class", + "target_ref": "[TranslationSchedule:Class]" }, { "source_id": "TranslationMetrics", "relation_type": "DEPENDS_ON", - "target_id": "TranslationRun:Class", - "target_ref": "[TranslationRun:Class]" + "target_id": "TranslationSchedule:Class", + "target_ref": "[TranslationSchedule:Class]" }, { "source_id": "TranslationMetrics", @@ -49972,34 +49320,25 @@ ], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Module' at C3", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Module' at C4", "detail": { - "actual_complexity": 3, - "contract_type": "Module" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Module' at C3", - "detail": { - "actual_complexity": 3, + "actual_complexity": 4, "contract_type": "Module" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslationMetrics [C:3] [TYPE Module] [SEMANTICS sqlalchemy, translate, metrics, job, statistics]\n# @BRIEF Aggregate translation metrics from live TranslationEvent + MetricSnapshot for per-job reporting.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [TranslationEvent:Class]\n# @RELATION DEPENDS_ON -> [MetricSnapshot:Class]\n# @RELATION DEPENDS_ON -> [TranslationRun:Class]\n# @RELATION DEPENDS_ON -> [TranslationSchedule:Class]\n# @PRE: Database session is open.\n# @POST: Metrics are aggregated and returned; no side effects.\n# @RATIONALE: Live events (<90 days) + MetricSnapshot (>=90 days) fusion for complete picture.\n# @REJECTED: Querying all events for all time — would be slow; MetricSnapshot provides historical summary.\n\nfrom datetime import UTC, datetime\nfrom typing import Any\n\nfrom sqlalchemy import func\nfrom sqlalchemy.orm import Session\n\nfrom ...core.logger import belief_scope\nfrom ...models.translate import (\n MetricSnapshot,\n TranslationEvent,\n TranslationRun,\n TranslationRunLanguageStats,\n TranslationSchedule,\n)\n\n\n# #region TranslationMetrics [C:3] [TYPE Class]\n# @BRIEF Aggregate translation metrics from live events and MetricSnapshot.\nclass TranslationMetrics:\n\n def __init__(self, db: Session):\n self.db = db\n\n # region get_job_metrics [TYPE Function]\n # @PURPOSE: Get aggregated metrics for a specific job.\n # @PRE: job_id exists.\n # @POST: Returns dict with metrics from events + latest snapshot.\n def get_job_metrics(self, job_id: str) -> dict[str, Any]:\n with belief_scope(\"TranslationMetrics.get_job_metrics\"):\n # Run counts from TranslationRun\n run_counts = (\n self.db.query(\n TranslationRun.status,\n func.count(TranslationRun.id),\n )\n .filter(TranslationRun.job_id == job_id)\n .group_by(TranslationRun.status)\n .all()\n )\n\n total_runs = 0\n status_counts: dict[str, int] = {}\n for status_val, count in run_counts:\n status_counts[status_val] = count\n total_runs += count\n\n # Aggregate record stats from runs\n record_stats = (\n self.db.query(\n func.coalesce(func.sum(TranslationRun.total_records), 0),\n func.coalesce(func.sum(TranslationRun.successful_records), 0),\n func.coalesce(func.sum(TranslationRun.failed_records), 0),\n func.coalesce(func.sum(TranslationRun.skipped_records), 0),\n )\n .filter(TranslationRun.job_id == job_id)\n .first()\n )\n total_records = record_stats[0] if record_stats else 0\n successful_records = record_stats[1] if record_stats else 0\n failed_records = record_stats[2] if record_stats else 0\n skipped_records = record_stats[3] if record_stats else 0\n\n # Average duration from runs\n avg_duration = (\n self.db.query(\n func.avg(\n func.extract('epoch', TranslationRun.completed_at - TranslationRun.started_at) * 1000\n )\n )\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.started_at.isnot(None),\n TranslationRun.completed_at.isnot(None),\n )\n .scalar()\n )\n\n # Last run\n last_run = (\n self.db.query(TranslationRun)\n .filter(TranslationRun.job_id == job_id)\n .order_by(TranslationRun.created_at.desc())\n .first()\n )\n\n # Next scheduled run\n next_schedule = (\n self.db.query(TranslationSchedule)\n .filter(\n TranslationSchedule.job_id == job_id,\n TranslationSchedule.is_active == True,\n )\n .first()\n )\n\n # Cumulative tokens/cost from MetricSnapshot + events\n cumulative_tokens = 0\n cumulative_cost = 0.0\n\n # Latest MetricSnapshot\n latest_snapshot = (\n self.db.query(MetricSnapshot)\n .filter(MetricSnapshot.job_id == job_id)\n .order_by(MetricSnapshot.snapshot_date.desc())\n .first()\n )\n if latest_snapshot:\n # MetricSnapshot stores per-snapshot aggregated tokens/cost\n # Here we sum what we stored — in practice MetricSnapshot.covers_events_before\n # indicates cutoff\n pass\n\n # Per-language metrics from TranslationRunLanguageStats (live runs)\n per_language: dict[str, dict[str, int | float]] = {}\n lang_stats_rows = (\n self.db.query(TranslationRunLanguageStats)\n .join(TranslationRun, TranslationRunLanguageStats.run_id == TranslationRun.id)\n .filter(TranslationRun.job_id == job_id)\n .all()\n )\n for ls in lang_stats_rows:\n lang = ls.language_code\n if lang not in per_language:\n per_language[lang] = {\"tokens\": 0, \"cost\": 0.0, \"runs\": 0, \"translated_rows\": 0}\n per_language[lang][\"tokens\"] += ls.token_count or 0\n per_language[lang][\"cost\"] += ls.estimated_cost or 0.0\n per_language[lang][\"runs\"] += 1\n per_language[lang][\"translated_rows\"] += ls.translated_rows or 0\n\n # Merge in per-language data from MetricSnapshot (pruned period)\n latest_snapshot = (\n self.db.query(MetricSnapshot)\n .filter(MetricSnapshot.job_id == job_id)\n .order_by(MetricSnapshot.snapshot_date.desc())\n .first()\n )\n if latest_snapshot and latest_snapshot.per_language_metrics:\n for lang, snap_data in latest_snapshot.per_language_metrics.items():\n if lang not in per_language:\n per_language[lang] = {\"tokens\": 0, \"cost\": 0.0, \"runs\": 0, \"translated_rows\": 0}\n per_language[lang][\"tokens\"] += snap_data.get(\"cumulative_tokens\", 0)\n per_language[lang][\"cost\"] += snap_data.get(\"cumulative_cost\", 0.0)\n per_language[lang][\"runs\"] += snap_data.get(\"runs\", 0)\n\n return {\n \"job_id\": job_id,\n \"total_runs\": total_runs,\n \"successful_runs\": status_counts.get(\"COMPLETED\", 0),\n \"failed_runs\": status_counts.get(\"FAILED\", 0),\n \"cancelled_runs\": status_counts.get(\"CANCELLED\", 0),\n \"total_records\": int(total_records),\n \"successful_records\": int(successful_records),\n \"failed_records\": int(failed_records),\n \"skipped_records\": int(skipped_records),\n \"cumulative_tokens\": cumulative_tokens,\n \"cumulative_cost\": cumulative_cost,\n \"avg_duration_ms\": int(avg_duration) if avg_duration else None,\n \"last_run_at\": last_run.created_at.isoformat() if last_run else None,\n \"next_scheduled_run\": next_schedule.last_run_at.isoformat() if next_schedule and next_schedule.last_run_at else None,\n \"per_language_metrics\": per_language,\n }\n # endregion get_job_metrics\n\n # region get_all_metrics [TYPE Function]\n # @PURPOSE: Get aggregated metrics for all jobs.\n # @POST: Returns list of per-job metrics.\n def get_all_metrics(self) -> list[dict[str, Any]]:\n with belief_scope(\"TranslationMetrics.get_all_metrics\"):\n job_ids = (\n self.db.query(TranslationRun.job_id)\n .distinct()\n .all()\n )\n return [self.get_job_metrics(jid[0]) for jid in job_ids if jid[0]]\n # endregion get_all_metrics\n\n\n# #endregion TranslationMetrics\n# #endregion TranslationMetrics\n" + "body": "# #region TranslationMetrics [C:3] [TYPE Module] [SEMANTICS sqlalchemy, translate, metrics, job, statistics]\n# @BRIEF Aggregate translation metrics from live TranslationEvent + MetricSnapshot for per-job reporting.\n# @LAYER Domain\n# @RELATION DEPENDS_ON -> [TranslationSchedule:Class]\n# @RELATION DEPENDS_ON -> [TranslationSchedule:Class]\n# @RELATION DEPENDS_ON -> [TranslationSchedule:Class]\n# @RELATION DEPENDS_ON -> [TranslationSchedule:Class]\n# @PRE Database session is open.\n# @POST Metrics are aggregated and returned; no side effects.\n# @RATIONALE Live events (<90 days) + MetricSnapshot (>=90 days) fusion for complete picture.\n# @REJECTED Querying all events for all time — would be slow; MetricSnapshot provides historical summary.\n# @COMPLEXITY 4\n\nfrom datetime import UTC, datetime\nfrom typing import Any\n\nfrom sqlalchemy import func\nfrom sqlalchemy.orm import Session\n\nfrom ...core.logger import belief_scope\nfrom ...models.translate import (\n MetricSnapshot,\n TranslationEvent,\n TranslationRun,\n TranslationRunLanguageStats,\n TranslationSchedule,\n)\n\n\n# #region TranslationMetrics [C:3] [TYPE Class]\n# @BRIEF Aggregate translation metrics from live events and MetricSnapshot.\nclass TranslationMetrics:\n\n def __init__(self, db: Session):\n self.db = db\n\n # region get_job_metrics [TYPE Function]\n # @PURPOSE Get aggregated metrics for a specific job.\n # @PRE job_id exists.\n # @POST Returns dict with metrics from events + latest snapshot.\n def get_job_metrics(self, job_id: str) -> dict[str, Any]:\n with belief_scope(\"TranslationMetrics.get_job_metrics\"):\n # Run counts from TranslationRun\n run_counts = (\n self.db.query(\n TranslationRun.status,\n func.count(TranslationRun.id),\n )\n .filter(TranslationRun.job_id == job_id)\n .group_by(TranslationRun.status)\n .all()\n )\n\n total_runs = 0\n status_counts: dict[str, int] = {}\n for status_val, count in run_counts:\n status_counts[status_val] = count\n total_runs += count\n\n # Aggregate record stats from runs\n record_stats = (\n self.db.query(\n func.coalesce(func.sum(TranslationRun.total_records), 0),\n func.coalesce(func.sum(TranslationRun.successful_records), 0),\n func.coalesce(func.sum(TranslationRun.failed_records), 0),\n func.coalesce(func.sum(TranslationRun.skipped_records), 0),\n )\n .filter(TranslationRun.job_id == job_id)\n .first()\n )\n total_records = record_stats[0] if record_stats else 0\n successful_records = record_stats[1] if record_stats else 0\n failed_records = record_stats[2] if record_stats else 0\n skipped_records = record_stats[3] if record_stats else 0\n\n # Average duration from runs\n avg_duration = (\n self.db.query(\n func.avg(\n func.extract('epoch', TranslationRun.completed_at - TranslationRun.started_at) * 1000\n )\n )\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.started_at.isnot(None),\n TranslationRun.completed_at.isnot(None),\n )\n .scalar()\n )\n\n # Last run\n last_run = (\n self.db.query(TranslationRun)\n .filter(TranslationRun.job_id == job_id)\n .order_by(TranslationRun.created_at.desc())\n .first()\n )\n\n # Next scheduled run\n next_schedule = (\n self.db.query(TranslationSchedule)\n .filter(\n TranslationSchedule.job_id == job_id,\n TranslationSchedule.is_active == True,\n )\n .first()\n )\n\n # Cumulative tokens/cost from MetricSnapshot + events\n cumulative_tokens = 0\n cumulative_cost = 0.0\n\n # Latest MetricSnapshot\n latest_snapshot = (\n self.db.query(MetricSnapshot)\n .filter(MetricSnapshot.job_id == job_id)\n .order_by(MetricSnapshot.snapshot_date.desc())\n .first()\n )\n if latest_snapshot:\n # MetricSnapshot stores per-snapshot aggregated tokens/cost\n # Here we sum what we stored — in practice MetricSnapshot.covers_events_before\n # indicates cutoff\n pass\n\n # Per-language metrics from TranslationRunLanguageStats (live runs)\n per_language: dict[str, dict[str, int | float]] = {}\n lang_stats_rows = (\n self.db.query(TranslationRunLanguageStats)\n .join(TranslationRun, TranslationRunLanguageStats.run_id == TranslationRun.id)\n .filter(TranslationRun.job_id == job_id)\n .all()\n )\n for ls in lang_stats_rows:\n lang = ls.language_code\n if lang not in per_language:\n per_language[lang] = {\"tokens\": 0, \"cost\": 0.0, \"runs\": 0, \"translated_rows\": 0}\n per_language[lang][\"tokens\"] += ls.token_count or 0\n per_language[lang][\"cost\"] += ls.estimated_cost or 0.0\n per_language[lang][\"runs\"] += 1\n per_language[lang][\"translated_rows\"] += ls.translated_rows or 0\n\n # Merge in per-language data from MetricSnapshot (pruned period)\n latest_snapshot = (\n self.db.query(MetricSnapshot)\n .filter(MetricSnapshot.job_id == job_id)\n .order_by(MetricSnapshot.snapshot_date.desc())\n .first()\n )\n if latest_snapshot and latest_snapshot.per_language_metrics:\n for lang, snap_data in latest_snapshot.per_language_metrics.items():\n if lang not in per_language:\n per_language[lang] = {\"tokens\": 0, \"cost\": 0.0, \"runs\": 0, \"translated_rows\": 0}\n per_language[lang][\"tokens\"] += snap_data.get(\"cumulative_tokens\", 0)\n per_language[lang][\"cost\"] += snap_data.get(\"cumulative_cost\", 0.0)\n per_language[lang][\"runs\"] += snap_data.get(\"runs\", 0)\n\n return {\n \"job_id\": job_id,\n \"total_runs\": total_runs,\n \"successful_runs\": status_counts.get(\"COMPLETED\", 0),\n \"failed_runs\": status_counts.get(\"FAILED\", 0),\n \"cancelled_runs\": status_counts.get(\"CANCELLED\", 0),\n \"total_records\": int(total_records),\n \"successful_records\": int(successful_records),\n \"failed_records\": int(failed_records),\n \"skipped_records\": int(skipped_records),\n \"cumulative_tokens\": cumulative_tokens,\n \"cumulative_cost\": cumulative_cost,\n \"avg_duration_ms\": int(avg_duration) if avg_duration else None,\n \"last_run_at\": last_run.created_at.isoformat() if last_run else None,\n \"next_scheduled_run\": next_schedule.last_run_at.isoformat() if next_schedule and next_schedule.last_run_at else None,\n \"per_language_metrics\": per_language,\n }\n # endregion get_job_metrics\n\n # region get_all_metrics [TYPE Function]\n # @PURPOSE Get aggregated metrics for all jobs.\n # @POST Returns list of per-job metrics.\n def get_all_metrics(self) -> list[dict[str, Any]]:\n with belief_scope(\"TranslationMetrics.get_all_metrics\"):\n job_ids = (\n self.db.query(TranslationRun.job_id)\n .distinct()\n .all()\n )\n return [self.get_job_metrics(jid[0]) for jid in job_ids if jid[0]]\n # endregion get_all_metrics\n\n\n# #endregion TranslationMetrics\n# #endregion TranslationMetrics\n" }, { "contract_id": "TranslationOrchestrator", "contract_type": "Class", "file_path": "backend/src/plugins/translate/orchestrator.py", "start_line": 46, - "end_line": 1115, + "end_line": 1137, "tier": "TIER_3", "complexity": 5, "metadata": { @@ -50033,7 +49372,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslationOrchestrator [C:5] [TYPE Class]\n# @BRIEF Coordinates full translation run lifecycle: validation, execution, SQL generation, Superset submission, event logging.\n# @PRE: DB session and config manager are available.\n# @POST: Runs are created, executed, and finalized with event records.\n# @SIDE_EFFECT: Creates/modifies TranslationRun, TranslationBatch, TranslationRecord, TranslationEvent rows.\n# @INVARIANT: State transitions: PENDING -> RUNNING -> COMPLETED|FAILED|CANCELLED.\nclass TranslationOrchestrator:\n\n def __init__(\n self,\n db: Session,\n config_manager: ConfigManager,\n current_user: str | None = None,\n ):\n self.db = db\n self.config_manager = config_manager\n self.current_user = current_user\n self.event_log = TranslationEventLog(db)\n self._job: TranslationJob | None = None\n\n # region start_run [TYPE Function]\n # @PURPOSE: Start a new translation run for a job.\n # @PRE: job_id exists. For manual runs, there must be an accepted preview session.\n # @POST: TranslationRun is created in PENDING status with hash fields and config snapshot. Events are recorded.\n # @SIDE_EFFECT: DB writes.\n def start_run(\n self,\n job_id: str,\n is_scheduled: bool = False,\n trigger_type: str | None = None,\n full_translation: bool = False,\n ) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.start_run\"):\n # Load and validate job\n job = self.db.query(TranslationJob).filter(TranslationJob.id == job_id).first()\n if not job:\n raise ValueError(f\"Translation job '{job_id}' not found\")\n self._job = job\n\n logger.reason(\"Starting translation run\", {\n \"job_id\": job_id,\n \"is_scheduled\": is_scheduled,\n })\n\n # Validate preconditions\n self._validate_preconditions(job, is_scheduled=is_scheduled)\n\n # Compute hashes\n config_hash = self._compute_config_hash(job)\n dict_snapshot_hash = self._compute_dict_snapshot_hash(job_id)\n\n # Build config snapshot\n config_snapshot = {\n \"source_dialect\": job.source_dialect,\n \"target_dialect\": job.target_dialect,\n \"database_dialect\": job.database_dialect,\n \"source_datasource_id\": job.source_datasource_id,\n \"source_table\": job.source_table,\n \"target_schema\": job.target_schema,\n \"target_table\": job.target_table,\n \"source_key_cols\": job.source_key_cols,\n \"target_key_cols\": job.target_key_cols,\n \"translation_column\": job.translation_column,\n \"target_column\": job.target_column,\n \"context_columns\": job.context_columns,\n \"provider_id\": job.provider_id,\n \"batch_size\": job.batch_size,\n \"upsert_strategy\": job.upsert_strategy,\n \"dictionary_ids\": self._compute_dict_snapshot_hash(job_id),\n \"full_translation\": full_translation,\n }\n\n # Compute key_hash from source_key_cols\n import hashlib\n key_hash_input = json.dumps({\n \"source_key_cols\": job.source_key_cols,\n \"source_datasource_id\": job.source_datasource_id,\n \"source_table\": job.source_table,\n }, sort_keys=True)\n key_hash = hashlib.sha256(key_hash_input.encode()).hexdigest()[:16]\n\n # Create run record with all hash/snapshot fields\n run = TranslationRun(\n id=str(uuid.uuid4()),\n job_id=job_id,\n status=\"PENDING\",\n trigger_type=trigger_type or (\"scheduled\" if is_scheduled else \"manual\"),\n config_snapshot=config_snapshot,\n key_hash=key_hash,\n config_hash=config_hash,\n dict_snapshot_hash=dict_snapshot_hash,\n created_by=self.current_user,\n created_at=datetime.now(UTC),\n )\n self.db.add(run)\n self.db.flush()\n\n # Record event\n self.event_log.log_event(\n job_id=job_id,\n run_id=run.id,\n event_type=\"RUN_STARTED\",\n payload={\n \"is_scheduled\": is_scheduled,\n \"trigger_type\": run.trigger_type,\n \"config_hash\": config_hash,\n \"dict_snapshot_hash\": dict_snapshot_hash,\n \"key_hash\": key_hash,\n },\n created_by=self.current_user,\n )\n\n self.db.commit()\n self.db.refresh(run)\n\n logger.reflect(\"Run created\", {\n \"run_id\": run.id,\n \"job_id\": job_id,\n \"status\": run.status,\n \"trigger_type\": run.trigger_type,\n })\n return run\n # endregion start_run\n\n # region execute_run [TYPE Function]\n # @PURPOSE: Execute a translation run: dispatch executor, generate SQL, submit to Superset.\n # @PRE: run is in PENDING status.\n # @POST: Run is executed, SQL generated, Superset submission attempted.\n # @SIDE_EFFECT: LLM calls, DB writes, Superset API calls.\n def execute_run(\n self,\n run: TranslationRun,\n on_batch_progress: Callable[[str, int, int, int, int], None] | None = None,\n skip_insert: bool = False,\n ) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.execute_run\"):\n job = self.db.query(TranslationJob).filter(TranslationJob.id == run.job_id).first()\n if not job:\n raise ValueError(f\"Job '{run.job_id}' not found\")\n self._job = job\n\n if run.status != \"PENDING\":\n raise ValueError(\n f\"Cannot execute run in status '{run.status}'. \"\n f\"Run must be in PENDING status.\"\n )\n\n logger.reason(\"Executing run\", {\n \"run_id\": run.id,\n \"job_id\": job.id,\n \"skip_insert\": skip_insert,\n })\n\n # Record translation phase start\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"TRANSLATION_PHASE_STARTED\",\n payload={},\n created_by=self.current_user,\n )\n\n # Initialize per-language stats\n target_languages = job.target_languages or [job.target_dialect or \"en\"]\n if not isinstance(target_languages, list):\n target_languages = [str(target_languages)]\n language_stats_map: dict[str, TranslationRunLanguageStats] = {}\n for lang_code in target_languages:\n lang_stat = TranslationRunLanguageStats(\n id=str(uuid.uuid4()),\n run_id=run.id,\n language_code=lang_code,\n total_rows=0,\n translated_rows=0,\n failed_rows=0,\n skipped_rows=0,\n token_count=0,\n estimated_cost=0.0,\n )\n self.db.add(lang_stat)\n language_stats_map[lang_code] = lang_stat\n self.db.flush()\n\n # Dispatch executor\n executor = TranslationExecutor(\n self.db, self.config_manager, self.current_user,\n on_batch_progress=on_batch_progress,\n )\n try:\n run = executor.execute_run(run, llm_progress_callback=None, language_stats_map=language_stats_map)\n except Exception as e:\n logger.explore(\"Translation execution failed\", {\n \"run_id\": run.id,\n \"error\": str(e),\n })\n run.status = \"FAILED\"\n run.error_message = f\"Translation execution failed: {e}\"\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_FAILED\",\n payload={\"error\": str(e), \"phase\": \"translation\"},\n created_by=self.current_user,\n )\n self.db.commit()\n return run\n\n # Check if executor cancelled itself due to cancellation flag\n if run.status == \"CANCELLED\":\n self._update_language_stats(run.id, language_stats_map)\n self.db.commit()\n logger.reflect(\"Run cancelled via cancellation flag\", {\n \"run_id\": run.id,\n })\n return run\n\n # Aggregate per-language statistics after executor completes\n self._update_language_stats(run.id, language_stats_map)\n\n # Record translation phase complete\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"TRANSLATION_PHASE_COMPLETED\",\n payload={\n \"total\": run.total_records,\n \"successful\": run.successful_records,\n \"failed\": run.failed_records,\n \"skipped\": run.skipped_records,\n },\n created_by=self.current_user,\n )\n\n # Skip insert phase if requested (e.g., for preview-only execution)\n if skip_insert:\n run.status = \"COMPLETED\"\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_COMPLETED\",\n payload={\"skip_insert\": True},\n created_by=self.current_user,\n )\n self.db.commit()\n return run\n\n # Generate SQL and submit to Superset\n insert_result = self._generate_and_insert_sql(job, run)\n run.insert_status = insert_result.get(\"status\")\n run.superset_execution_id = str(insert_result.get(\"query_id\") or \"\")\n run.superset_execution_log = insert_result\n # Preserve the translation-phase status. If the executor already\n # marked the run FAILED (all LLM calls failed) we do NOT upgrade it\n # to COMPLETED just because the insert phase ran.\n if run.status != \"FAILED\":\n run.status = \"COMPLETED\"\n if insert_result.get(\"error_message\"):\n run.error_message = insert_result[\"error_message\"]\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n\n # Record terminal event\n terminal_event = \"RUN_COMPLETED\"\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=terminal_event,\n payload={\n \"insert_status\": insert_result.get(\"status\"),\n \"query_id\": insert_result.get(\"query_id\"),\n \"rows_affected\": insert_result.get(\"rows_affected\"),\n \"total_records\": run.total_records,\n \"successful\": run.successful_records,\n \"failed\": run.failed_records,\n \"skipped\": run.skipped_records,\n },\n created_by=self.current_user,\n )\n\n self.db.commit()\n # Re-query run after commit — refresh may fail if the object was\n # created in a different session or became detached during commit.\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run.id).first()\n\n logger.reflect(\"Run execution complete\", {\n \"run_id\": run.id,\n \"status\": run.status,\n \"insert_status\": run.insert_status,\n })\n return run\n # endregion execute_run\n\n # region _generate_and_insert_sql [TYPE Function]\n # @PURPOSE: Generate INSERT SQL from successful records and submit to Superset SQL Lab.\n # @PRE: job has target table configured. run has successful records.\n # @POST: SQL is generated and submitted. Returns execution result.\n # @SIDE_EFFECT: Superset API call.\n def _generate_and_insert_sql(\n self,\n job: TranslationJob,\n run: TranslationRun,\n ) -> dict[str, Any]:\n with belief_scope(\"TranslationOrchestrator._generate_and_insert_sql\"):\n # Fetch successful records with eager-loaded language data\n records = (\n self.db.query(TranslationRecord)\n .options(joinedload(TranslationRecord.languages))\n .filter(\n TranslationRecord.run_id == run.id,\n TranslationRecord.status == \"SUCCESS\",\n TranslationRecord.target_sql.isnot(None),\n )\n .all()\n )\n\n if not records:\n logger.reason(\"No successful records to insert\", {\"run_id\": run.id})\n return {\"status\": \"skipped\", \"reason\": \"no_records\", \"query_id\": None}\n\n logger.reason(f\"Generating SQL for {len(records)} records\", {\n \"run_id\": run.id,\n \"dialect\": job.database_dialect or job.target_dialect,\n })\n\n effective_target = job.target_column or job.translation_column\n primary_language = (job.target_languages or [\"en\"])[0]\n\n # Columns that exist in the target ClickHouse table\n columns = []\n if job.target_key_cols:\n columns.extend(job.target_key_cols)\n if effective_target:\n columns.append(effective_target)\n if job.target_language_column:\n columns.append(job.target_language_column)\n if job.target_source_column:\n columns.append(job.target_source_column)\n if job.target_source_language_column:\n columns.append(job.target_source_language_column)\n columns.append(\"context\")\n columns.append(\"is_original\")\n # Deduplicate while preserving order\n seen: set[str] = set()\n deduped: list[str] = []\n for c in columns:\n if c and c not in seen:\n deduped.append(c)\n seen.add(c)\n columns = deduped\n\n # Keys for the context JSON: context_columns + original translation_column\n context_keys = list(job.context_columns or [])\n if job.translation_column and job.translation_column != effective_target and job.translation_column not in context_keys:\n context_keys.append(job.translation_column)\n\n rows_for_sql: list[dict[str, object]] = []\n for rec in records:\n source_data = rec.source_data or {}\n\n # Detect source language from first TranslationLanguage entry\n detected_src_lang = \"und\"\n if rec.languages and len(rec.languages) > 0:\n detected_src_lang = rec.languages[0].source_language_detected or \"und\"\n\n # Build context JSON: all extra columns the user configured\n context_data: dict[str, str] = {}\n for key in context_keys:\n val = source_data.get(key)\n context_data[key] = str(val) if val is not None else \"\"\n\n # ── Shared base row ──\n base_row: dict[str, object] = {}\n\n if job.target_key_cols:\n for k in job.target_key_cols:\n base_row[k] = source_data.get(k, \"\")\n\n if job.target_source_column:\n base_row[job.target_source_column] = rec.source_sql or \"\"\n\n if job.target_source_language_column:\n base_row[job.target_source_language_column] = detected_src_lang\n\n base_row[\"context\"] = json.dumps(context_data, ensure_ascii=False)\n\n # ── 1. ORIGINAL row (is_original = 1) ──\n original_row = dict(base_row)\n if effective_target:\n original_row[effective_target] = rec.source_sql or \"\"\n if job.target_language_column:\n original_row[job.target_language_column] = detected_src_lang\n original_row[\"is_original\"] = 1\n rows_for_sql.append(original_row)\n\n # ── 2. TRANSLATION rows (is_original = 0) ──\n # Skip language that matches the source — the original row already covers it\n if rec.languages and len(rec.languages) > 0:\n for lang in rec.languages:\n if lang.language_code == detected_src_lang:\n continue\n trans_row = dict(base_row)\n trans_value = lang.final_value or lang.translated_value or \"\"\n if effective_target:\n trans_row[effective_target] = trans_value\n if job.target_language_column:\n trans_row[job.target_language_column] = lang.language_code\n trans_row[\"is_original\"] = 0\n rows_for_sql.append(trans_row)\n else:\n # Fallback: no per-language data\n fallback_row = dict(base_row)\n if effective_target:\n fallback_row[effective_target] = rec.target_sql or \"\"\n if job.target_language_column:\n fallback_row[job.target_language_column] = primary_language\n fallback_row[\"is_original\"] = 0\n rows_for_sql.append(fallback_row)\n\n if not columns:\n columns = [effective_target or \"translated_text\"]\n rows_for_sql = [{columns[0]: rec.target_sql or \"\"} for rec in records]\n\n # Resolve the real database backend engine from Superset\n try:\n env_id = job.environment_id or job.source_dialect or \"\"\n executor = SupersetSqlLabExecutor(self.config_manager, env_id)\n executor.resolve_database_id(\n target_database_id=job.target_database_id,\n )\n real_backend = executor.get_database_backend()\n except Exception as e:\n logger.explore(\"Failed to resolve database backend, falling back to job dialet\", {\n \"error\": str(e),\n })\n real_backend = None\n\n dialect = real_backend or job.database_dialect or job.target_dialect or \"postgresql\"\n\n # Generate SQL\n try:\n sql, row_count = SQLGenerator.generate(\n dialect=dialect,\n target_schema=job.target_schema,\n target_table=job.target_table or \"translated_data\",\n columns=columns,\n rows=rows_for_sql,\n key_columns=job.target_key_cols,\n upsert_strategy=job.upsert_strategy or \"MERGE\",\n )\n except ValueError as e:\n logger.explore(\"SQL generation failed\", {\"error\": str(e)})\n return {\"status\": \"failed\", \"error_message\": str(e), \"query_id\": None}\n\n logger.reason(\"SQL generated with dialect\", {\n \"dialect\": dialect,\n \"real_backend\": real_backend,\n \"job_database_dialect\": job.database_dialect,\n \"job_target_dialect\": job.target_dialect,\n })\n\n # Log insert phase start\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"INSERT_PHASE_STARTED\",\n payload={\"sql_length\": len(sql), \"row_count\": row_count, \"dialect\": dialect},\n created_by=self.current_user,\n )\n\n # Submit to Superset\n try:\n result = executor.execute_and_poll(\n sql=sql,\n max_polls=30,\n poll_interval_seconds=2.0,\n )\n except Exception as e:\n logger.explore(\"Superset SQL submission failed\", {\n \"run_id\": run.id,\n \"error\": str(e),\n })\n result = {\"status\": \"failed\", \"error_message\": str(e), \"query_id\": None}\n\n # Log insert phase complete\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"INSERT_PHASE_COMPLETED\",\n payload=result,\n created_by=self.current_user,\n )\n\n return result\n # endregion _generate_and_insert_sql\n\n # region _validate_preconditions [TYPE Function]\n # @PURPOSE: Validate preconditions before starting a run.\n # @PRE: None.\n # @POST: Raises ValueError if preconditions are not met.\n # @SIDE_EFFECT: None.\n def _validate_preconditions(\n self,\n job: TranslationJob,\n is_scheduled: bool = False,\n ) -> None:\n with belief_scope(\"TranslationOrchestrator._validate_preconditions\"):\n # Job must be in valid status\n if job.status in (\"DRAFT\",):\n raise ValueError(\n f\"Cannot run job '{job.id}' in status '{job.status}'. \"\n f\"Job must be READY, ACTIVE, or COMPLETED.\"\n )\n\n # Must have target table configured\n if not job.target_table:\n raise ValueError(\n f\"Job '{job.id}' has no target table configured. \"\n \"Configure a target table before running.\"\n )\n\n # Must have LLM provider configured\n if not job.provider_id:\n raise ValueError(\n f\"Job '{job.id}' has no LLM provider configured. \"\n \"Select an LLM provider before running.\"\n )\n\n # Must have a translation column\n if not job.translation_column:\n raise ValueError(\n f\"Job '{job.id}' has no translation column configured. \"\n \"Select a translation column before running.\"\n )\n\n # For manual runs, must have accepted preview\n if not is_scheduled:\n accepted_session = (\n self.db.query(TranslationPreviewSession)\n .filter(\n TranslationPreviewSession.job_id == job.id,\n TranslationPreviewSession.status == \"APPLIED\",\n )\n .order_by(TranslationPreviewSession.created_at.desc())\n .first()\n )\n if not accepted_session:\n raise ValueError(\n f\"Job '{job.id}' has no accepted preview session. \"\n \"Run and accept a preview before executing a manual translation run.\"\n )\n\n logger.reason(\"Preconditions validated\", {\n \"job_id\": job.id,\n \"is_scheduled\": is_scheduled,\n })\n # endregion _validate_preconditions\n\n # region retry_failed_batches [TYPE Function]\n # @PURPOSE: Retry failed batches in a run.\n # @PRE: run exists and has failed batches.\n # @POST: Failed batches are re-processed.\n # @SIDE_EFFECT: LLM calls, DB writes.\n def retry_failed_batches(\n self,\n run_id: str,\n ) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.retry_failed_batches\"):\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n\n job = self.db.query(TranslationJob).filter(TranslationJob.id == run.job_id).first()\n if not job:\n raise ValueError(f\"Job '{run.job_id}' not found\")\n self._job = job\n\n # Find failed batches\n failed_batches = (\n self.db.query(TranslationBatch)\n .filter(\n TranslationBatch.run_id == run_id,\n TranslationBatch.status.in_([\"FAILED\", \"COMPLETED_WITH_ERRORS\"]),\n )\n .all()\n )\n\n if not failed_batches:\n raise ValueError(f\"No failed batches found for run '{run_id}'\")\n\n logger.reason(\"Retrying failed batches\", {\n \"run_id\": run_id,\n \"batch_count\": len(failed_batches),\n })\n\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_RETRYING\",\n payload={\"batch_count\": len(failed_batches)},\n created_by=self.current_user,\n )\n\n # Re-process each failed batch\n executor = TranslationExecutor(self.db, self.config_manager, self.current_user)\n run.status = \"RUNNING\"\n self.db.flush()\n\n for batch in failed_batches:\n # Fetch failed records for this batch\n failed_records = (\n self.db.query(TranslationRecord)\n .filter(\n TranslationRecord.batch_id == batch.id,\n TranslationRecord.status == \"FAILED\",\n )\n .all()\n )\n\n if not failed_records:\n continue\n\n # Build rows from failed records\n retry_rows = []\n for rec in failed_records:\n retry_rows.append({\n \"row_index\": rec.source_object_id or \"0\",\n \"source_text\": rec.source_sql or \"\",\n \"approved_translation\": None,\n \"source_object_name\": rec.source_object_name or \"\",\n })\n\n # Process retry batch\n result = executor._process_batch(\n job=job,\n run_id=run_id,\n batch_index=batch.batch_index,\n batch_rows=retry_rows,\n )\n\n # Update run stats\n run.successful_records = (run.successful_records or 0) + result[\"successful\"]\n run.failed_records = (run.failed_records or 0) + result[\"failed\"]\n run.skipped_records = (run.skipped_records or 0) + result[\"skipped\"]\n self.db.flush()\n\n # Update run status\n if run.failed_records == 0:\n run.status = \"COMPLETED\"\n elif run.successful_records == 0:\n run.status = \"FAILED\"\n else:\n run.status = \"COMPLETED\"\n\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_COMPLETED\",\n payload={\"retry\": True},\n created_by=self.current_user,\n )\n\n self.db.commit()\n logger.reflect(\"Retry complete\", {\n \"run_id\": run_id,\n \"status\": run.status,\n })\n return run\n # endregion retry_failed_batches\n\n # region retry_insert [TYPE Function]\n # @PURPOSE: Retry the SQL insert phase for a completed run.\n # @PRE: run exists and has successful records.\n # @POST: SQL is regenerated and re-submitted to Superset.\n # @SIDE_EFFECT: Superset API call.\n def retry_insert(\n self,\n run_id: str,\n ) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.retry_insert\"):\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n\n job = self.db.query(TranslationJob).filter(TranslationJob.id == run.job_id).first()\n if not job:\n raise ValueError(f\"Job '{run.job_id}' not found\")\n self._job = job\n\n logger.reason(\"Retrying insert phase\", {\n \"run_id\": run_id,\n })\n\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_RETRY_INSERT\",\n payload={},\n created_by=self.current_user,\n )\n\n # Regenerate SQL and submit\n insert_result = self._generate_and_insert_sql(job, run)\n\n # Update run\n run.insert_status = insert_result.get(\"status\")\n run.superset_execution_id = str(insert_result.get(\"query_id\") or \"\")\n if insert_result.get(\"error_message\"):\n run.error_message = insert_result[\"error_message\"]\n self.db.flush()\n self.db.commit()\n self.db.refresh(run)\n\n logger.reflect(\"Insert retry complete\", {\n \"run_id\": run_id,\n \"insert_status\": run.insert_status,\n })\n return run\n # endregion retry_insert\n\n # region cancel_run [TYPE Function]\n # @PURPOSE: Cancel a running translation.\n # @PRE: run is in PENDING or RUNNING status.\n # @POST: Run status is set to CANCELLED.\n # @SIDE_EFFECT: DB write; records event.\n def cancel_run(self, run_id: str) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.cancel_run\"):\n # Set short lock timeout to avoid blocking on row lock held by\n # background executor thread (which holds RowExclusiveLock during\n # batch processing). If the row is locked, we fall back to setting\n # a cancellation flag via direct SQL UPDATE, which the executor\n # checks after each batch commit.\n try:\n self.db.execute(text(\"SET LOCAL lock_timeout = '3s'\"))\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n except Exception:\n # Row is locked — set cancellation flag via direct SQL\n logger.explore(\"Row lock timeout — setting cancellation flag via direct SQL\", {\n \"run_id\": run_id,\n })\n self.db.execute(\n text(\"UPDATE translation_runs SET error_message = 'CANCEL_REQUESTED' WHERE id = :run_id\"),\n {\"run_id\": run_id},\n )\n self.db.commit()\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n logger.reflect(\"Cancellation flag set for locked run\", {\n \"run_id\": run_id,\n })\n return run\n\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n\n if run.status not in (\"PENDING\", \"RUNNING\"):\n raise ValueError(\n f\"Cannot cancel run in status '{run.status}'. \"\n f\"Only PENDING or RUNNING runs can be cancelled.\"\n )\n\n run.status = \"CANCELLED\"\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n\n self.event_log.log_event(\n job_id=run.job_id,\n run_id=run.id,\n event_type=\"RUN_CANCELLED\",\n payload={},\n created_by=self.current_user,\n )\n\n self.db.commit()\n self.db.refresh(run)\n\n logger.reflect(\"Run cancelled\", {\n \"run_id\": run_id,\n })\n return run\n # endregion cancel_run\n\n # region get_run_status [TYPE Function]\n # @PURPOSE: Get run status with statistics.\n # @PRE: run_id exists.\n # @POST: Returns dict with run details.\n def get_run_status(self, run_id: str) -> dict[str, Any]:\n with belief_scope(\"TranslationOrchestrator.get_run_status\"):\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n\n # Count batches\n batch_count = (\n self.db.query(TranslationBatch)\n .filter(TranslationBatch.run_id == run_id)\n .count()\n )\n\n # Get event summary\n event_summary = self.event_log.get_run_event_summary(run_id)\n\n # Get language stats\n language_stats_entries = (\n self.db.query(TranslationRunLanguageStats)\n .filter(TranslationRunLanguageStats.run_id == run_id)\n .all()\n )\n language_stats = [\n {\n \"language_code\": ls.language_code,\n \"total_rows\": ls.total_rows or 0,\n \"translated_rows\": ls.translated_rows or 0,\n \"failed_rows\": ls.failed_rows or 0,\n \"skipped_rows\": ls.skipped_rows or 0,\n \"token_count\": ls.token_count or 0,\n \"estimated_cost\": ls.estimated_cost or 0.0,\n }\n for ls in language_stats_entries\n ]\n\n return {\n \"id\": run.id,\n \"job_id\": run.job_id,\n \"status\": run.status,\n \"started_at\": run.started_at.isoformat() if run.started_at else None,\n \"completed_at\": run.completed_at.isoformat() if run.completed_at else None,\n \"error_message\": run.error_message,\n \"total_records\": run.total_records or 0,\n \"successful_records\": run.successful_records or 0,\n \"failed_records\": run.failed_records or 0,\n \"skipped_records\": run.skipped_records or 0,\n \"insert_status\": run.insert_status,\n \"superset_execution_id\": run.superset_execution_id,\n \"batch_count\": batch_count,\n \"language_stats\": language_stats,\n \"event_invariants\": {\n \"has_run_started\": event_summary[\"has_run_started\"],\n \"terminal_event_count\": event_summary[\"terminal_event_count\"],\n \"invariant_valid\": event_summary[\"invariant_valid\"],\n },\n \"created_by\": run.created_by,\n \"created_at\": run.created_at.isoformat() if run.created_at else None,\n }\n # endregion get_run_status\n\n # region get_run_records [TYPE Function]\n # @PURPOSE: Get paginated records for a run.\n # @PRE: run_id exists.\n # @POST: Returns dict with records and pagination info.\n def get_run_records(\n self,\n run_id: str,\n page: int = 1,\n page_size: int = 50,\n status_filter: str | None = None,\n ) -> dict[str, Any]:\n with belief_scope(\"TranslationOrchestrator.get_run_records\"):\n query = (\n self.db.query(TranslationRecord)\n .options(selectinload(TranslationRecord.languages))\n .filter(TranslationRecord.run_id == run_id)\n )\n\n if status_filter:\n query = query.filter(TranslationRecord.status == status_filter)\n\n total = query.count()\n offset = (page - 1) * page_size\n records = (\n query.order_by(TranslationRecord.created_at.desc())\n .offset(offset)\n .limit(page_size)\n .all()\n )\n\n return {\n \"items\": [\n {\n \"id\": r.id,\n \"batch_id\": r.batch_id,\n \"source_sql\": r.source_sql,\n \"target_sql\": r.target_sql,\n \"source_object_type\": r.source_object_type,\n \"source_object_id\": r.source_object_id,\n \"source_object_name\": r.source_object_name,\n \"status\": r.status,\n \"error_message\": r.error_message,\n \"created_at\": r.created_at.isoformat() if r.created_at else None,\n \"languages\": [\n {\n \"language_code\": tl.language_code,\n \"translated_value\": tl.translated_value,\n \"final_value\": tl.final_value or tl.translated_value,\n \"source_language_detected\": tl.source_language_detected,\n \"status\": tl.status,\n \"needs_review\": tl.needs_review or False,\n }\n for tl in (r.languages or [])\n ],\n }\n for r in records\n ],\n \"total\": total,\n \"page\": page,\n \"page_size\": page_size,\n \"status_filter\": status_filter,\n }\n # endregion get_run_records\n\n # region get_run_history [TYPE Function]\n # @PURPOSE: Get run history for a job.\n # @PRE: job_id exists.\n # @POST: Returns list of runs.\n def get_run_history(\n self,\n job_id: str,\n page: int = 1,\n page_size: int = 20,\n ) -> tuple[int, list[dict[str, Any]]]:\n with belief_scope(\"TranslationOrchestrator.get_run_history\"):\n query = self.db.query(TranslationRun).filter(\n TranslationRun.job_id == job_id\n )\n total = query.count()\n runs = (\n query.order_by(TranslationRun.created_at.desc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n\n return total, [\n {\n \"id\": r.id,\n \"job_id\": r.job_id,\n \"status\": r.status,\n \"started_at\": r.started_at.isoformat() if r.started_at else None,\n \"completed_at\": r.completed_at.isoformat() if r.completed_at else None,\n \"error_message\": r.error_message,\n \"total_records\": r.total_records or 0,\n \"successful_records\": r.successful_records or 0,\n \"failed_records\": r.failed_records or 0,\n \"skipped_records\": r.skipped_records or 0,\n \"insert_status\": r.insert_status,\n \"superset_execution_id\": r.superset_execution_id,\n \"created_by\": r.created_by,\n \"created_at\": r.created_at.isoformat() if r.created_at else None,\n }\n for r in runs\n ]\n # endregion get_run_history\n\n # region _update_language_stats [TYPE Function]\n # @PURPOSE: Aggregate TranslationLanguage entries by language_code and update TranslationRunLanguageStats.\n # @PRE: run_id and language_stats_map are valid. DB session is available.\n # @POST: Language stats are updated with row counts and estimated tokens/cost.\n # @SIDE_EFFECT: DB writes on language_stats objects.\n def _update_language_stats(\n self,\n run_id: str,\n language_stats_map: dict[str, TranslationRunLanguageStats],\n ) -> None:\n with belief_scope(\"TranslationOrchestrator._update_language_stats\"):\n # Get all records for this run to join with TranslationLanguage\n records = (\n self.db.query(TranslationRecord)\n .filter(TranslationRecord.run_id == run_id)\n .all()\n )\n record_ids = [r.id for r in records]\n\n if not record_ids:\n logger.reason(\"No records for language stats aggregation\", {\"run_id\": run_id})\n return\n\n # Get all language entries for this run's records\n lang_entries = (\n self.db.query(TranslationLanguage)\n .filter(TranslationLanguage.record_id.in_(record_ids))\n .all()\n )\n\n # Aggregate by language_code\n from collections import defaultdict\n agg: dict[str, dict[str, int]] = defaultdict(lambda: {\"total\": 0, \"translated\": 0, \"failed\": 0, \"skipped\": 0})\n\n for le in lang_entries:\n code = le.language_code\n agg[code][\"total\"] += 1\n if le.status in (\"translated\", \"approved\", \"edited\"):\n agg[code][\"translated\"] += 1\n elif le.status == \"failed\":\n agg[code][\"failed\"] += 1\n elif le.status == \"skipped\":\n agg[code][\"skipped\"] += 1\n\n # Estimate tokens: heuristic based on character count of translated values\n total_chars = sum(\n len(le.translated_value or \"\") for le in lang_entries if le.translated_value\n )\n total_tokens = max(1, total_chars // 4) # ~4 chars per token\n cost_per_token = 0.002 / 1000 # $0.002 per 1K tokens\n\n # Update each language stat entry\n for lang_code, lang_stat in language_stats_map.items():\n data = agg.get(lang_code, {\"total\": 0, \"translated\": 0, \"failed\": 0, \"skipped\": 0})\n lang_stat.total_rows = data[\"total\"]\n lang_stat.translated_rows = data[\"translated\"]\n lang_stat.failed_rows = data[\"failed\"]\n lang_stat.skipped_rows = data[\"skipped\"]\n\n # Proportional token split: share tokens across languages\n num_langs = len(language_stats_map)\n if num_langs > 0:\n lang_stat.token_count = total_tokens // num_langs\n lang_stat.estimated_cost = round((lang_stat.token_count / 1000) * cost_per_token, 6)\n\n self.db.flush()\n\n logger.reason(\"Language stats updated\", {\n \"run_id\": run_id,\n \"languages\": list(language_stats_map.keys()),\n \"total_tokens_est\": total_tokens,\n })\n # endregion _update_language_stats\n\n # region _compute_config_hash [TYPE Function]\n # @PURPOSE: Compute a hash of the job's current configuration for snapshot comparison.\n @staticmethod\n def _compute_config_hash(job: TranslationJob) -> str:\n import hashlib\n config_str = json.dumps({\n \"source_dialect\": job.source_dialect,\n \"target_dialect\": job.target_dialect,\n \"source_datasource_id\": job.source_datasource_id,\n \"translation_column\": job.translation_column,\n \"context_columns\": job.context_columns,\n \"target_languages\": sorted(job.target_languages) if job.target_languages else [],\n \"provider_id\": job.provider_id,\n \"batch_size\": job.batch_size,\n \"upsert_strategy\": job.upsert_strategy,\n }, sort_keys=True)\n return hashlib.sha256(config_str.encode()).hexdigest()[:16]\n # endregion _compute_config_hash\n\n # region _compute_dict_snapshot_hash [TYPE Function]\n # @PURPOSE: Compute a hash of dictionary state for snapshot comparison.\n def _compute_dict_snapshot_hash(self, job_id: str) -> str:\n import hashlib\n\n from ...models.translate import TranslationJobDictionary\n dict_links = (\n self.db.query(TranslationJobDictionary)\n .filter(TranslationJobDictionary.job_id == job_id)\n .all()\n )\n dict_ids = sorted([dl.dictionary_id for dl in dict_links])\n hash_input = \",\".join(dict_ids)\n return hashlib.sha256(hash_input.encode()).hexdigest()[:16]\n # endregion _compute_dict_snapshot_hash\n\n# #endregion TranslationOrchestrator\n" + "body": "# #region TranslationOrchestrator [C:5] [TYPE Class]\n# @BRIEF Coordinates full translation run lifecycle: validation, execution, SQL generation, Superset submission, event logging.\n# @PRE: DB session and config manager are available.\n# @POST: Runs are created, executed, and finalized with event records.\n# @SIDE_EFFECT: Creates/modifies TranslationRun, TranslationBatch, TranslationRecord, TranslationEvent rows.\n# @INVARIANT: State transitions: PENDING -> RUNNING -> COMPLETED|FAILED|CANCELLED.\nclass TranslationOrchestrator:\n\n def __init__(\n self,\n db: Session,\n config_manager: ConfigManager,\n current_user: str | None = None,\n ):\n self.db = db\n self.config_manager = config_manager\n self.current_user = current_user\n self.event_log = TranslationEventLog(db)\n self._job: TranslationJob | None = None\n\n # region start_run [TYPE Function]\n # @PURPOSE: Start a new translation run for a job.\n # @PRE: job_id exists. For manual runs, there must be an accepted preview session.\n # @POST: TranslationRun is created in PENDING status with hash fields and config snapshot. Events are recorded.\n # @SIDE_EFFECT: DB writes.\n def start_run(\n self,\n job_id: str,\n is_scheduled: bool = False,\n trigger_type: str | None = None,\n full_translation: bool = False,\n ) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.start_run\"):\n # Load and validate job\n job = self.db.query(TranslationJob).filter(TranslationJob.id == job_id).first()\n if not job:\n raise ValueError(f\"Translation job '{job_id}' not found\")\n self._job = job\n\n logger.reason(\"Starting translation run\", {\n \"job_id\": job_id,\n \"is_scheduled\": is_scheduled,\n })\n\n # Validate preconditions\n self._validate_preconditions(job, is_scheduled=is_scheduled)\n\n # Compute hashes\n config_hash = self._compute_config_hash(job)\n dict_snapshot_hash = self._compute_dict_snapshot_hash(job_id)\n\n # Build config snapshot\n config_snapshot = {\n \"source_dialect\": job.source_dialect,\n \"target_dialect\": job.target_dialect,\n \"database_dialect\": job.database_dialect,\n \"source_datasource_id\": job.source_datasource_id,\n \"source_table\": job.source_table,\n \"target_schema\": job.target_schema,\n \"target_table\": job.target_table,\n \"source_key_cols\": job.source_key_cols,\n \"target_key_cols\": job.target_key_cols,\n \"translation_column\": job.translation_column,\n \"target_column\": job.target_column,\n \"context_columns\": job.context_columns,\n \"provider_id\": job.provider_id,\n \"batch_size\": job.batch_size,\n \"upsert_strategy\": job.upsert_strategy,\n \"dictionary_ids\": self._compute_dict_snapshot_hash(job_id),\n \"full_translation\": full_translation,\n }\n\n # Compute key_hash from source_key_cols\n import hashlib\n key_hash_input = json.dumps({\n \"source_key_cols\": job.source_key_cols,\n \"source_datasource_id\": job.source_datasource_id,\n \"source_table\": job.source_table,\n }, sort_keys=True)\n key_hash = hashlib.sha256(key_hash_input.encode()).hexdigest()[:16]\n\n # Create run record with all hash/snapshot fields\n run = TranslationRun(\n id=str(uuid.uuid4()),\n job_id=job_id,\n status=\"PENDING\",\n trigger_type=trigger_type or (\"scheduled\" if is_scheduled else \"manual\"),\n config_snapshot=config_snapshot,\n key_hash=key_hash,\n config_hash=config_hash,\n dict_snapshot_hash=dict_snapshot_hash,\n created_by=self.current_user,\n created_at=datetime.now(UTC),\n )\n self.db.add(run)\n self.db.flush()\n\n # Record event\n self.event_log.log_event(\n job_id=job_id,\n run_id=run.id,\n event_type=\"RUN_STARTED\",\n payload={\n \"is_scheduled\": is_scheduled,\n \"trigger_type\": run.trigger_type,\n \"config_hash\": config_hash,\n \"dict_snapshot_hash\": dict_snapshot_hash,\n \"key_hash\": key_hash,\n },\n created_by=self.current_user,\n )\n\n self.db.commit()\n self.db.refresh(run)\n\n logger.reflect(\"Run created\", {\n \"run_id\": run.id,\n \"job_id\": job_id,\n \"status\": run.status,\n \"trigger_type\": run.trigger_type,\n })\n return run\n # endregion start_run\n\n # region execute_run [TYPE Function]\n # @PURPOSE: Execute a translation run: dispatch executor, generate SQL, submit to Superset.\n # @PRE: run is in PENDING status.\n # @POST: Run is executed, SQL generated, Superset submission attempted.\n # @SIDE_EFFECT: LLM calls, DB writes, Superset API calls.\n def execute_run(\n self,\n run: TranslationRun,\n on_batch_progress: Callable[[str, int, int, int, int], None] | None = None,\n skip_insert: bool = False,\n ) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.execute_run\"):\n job = self.db.query(TranslationJob).filter(TranslationJob.id == run.job_id).first()\n if not job:\n raise ValueError(f\"Job '{run.job_id}' not found\")\n self._job = job\n\n if run.status != \"PENDING\":\n raise ValueError(\n f\"Cannot execute run in status '{run.status}'. \"\n f\"Run must be in PENDING status.\"\n )\n\n logger.reason(\"Executing run\", {\n \"run_id\": run.id,\n \"job_id\": job.id,\n \"skip_insert\": skip_insert,\n })\n\n # Record translation phase start\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"TRANSLATION_PHASE_STARTED\",\n payload={},\n created_by=self.current_user,\n )\n\n # Initialize per-language stats\n target_languages = job.target_languages or [job.target_dialect or \"en\"]\n if not isinstance(target_languages, list):\n target_languages = [str(target_languages)]\n language_stats_map: dict[str, TranslationRunLanguageStats] = {}\n for lang_code in target_languages:\n lang_stat = TranslationRunLanguageStats(\n id=str(uuid.uuid4()),\n run_id=run.id,\n language_code=lang_code,\n total_rows=0,\n translated_rows=0,\n failed_rows=0,\n skipped_rows=0,\n token_count=0,\n estimated_cost=0.0,\n )\n self.db.add(lang_stat)\n language_stats_map[lang_code] = lang_stat\n self.db.flush()\n\n # Dispatch executor\n executor = TranslationExecutor(\n self.db, self.config_manager, self.current_user,\n on_batch_progress=on_batch_progress,\n )\n try:\n run = executor.execute_run(run, llm_progress_callback=None, language_stats_map=language_stats_map)\n except Exception as e:\n logger.explore(\"Translation execution failed\", {\n \"run_id\": run.id,\n \"error\": str(e),\n })\n # Rollback the session if it's in a broken state (e.g. after a\n # failed flush from a previous exception in executor.execute_run).\n # Without this, subsequent flush()/commit() calls raise\n # \"This Session's transaction has been rolled back\".\n try:\n self.db.rollback()\n except Exception:\n pass\n run = self.db.merge(run)\n run.status = \"FAILED\"\n run.error_message = f\"Translation execution failed: {e}\"\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_FAILED\",\n payload={\"error\": str(e), \"phase\": \"translation\"},\n created_by=self.current_user,\n )\n self.db.commit()\n return run\n\n # Check if executor cancelled itself due to cancellation flag\n if run.status == \"CANCELLED\":\n self._update_language_stats(run.id, language_stats_map)\n self.event_log.log_event(\n job_id=job.id if job else (self._job.id if self._job else run.job_id),\n run_id=run.id,\n event_type=\"RUN_CANCELLED\",\n payload={\"reason\": \"cancellation_flag\"},\n created_by=self.current_user,\n )\n self.db.commit()\n logger.reflect(\"Run cancelled via cancellation flag\", {\n \"run_id\": run.id,\n })\n return run\n\n # Aggregate per-language statistics after executor completes\n self._update_language_stats(run.id, language_stats_map)\n\n # Record translation phase complete\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"TRANSLATION_PHASE_COMPLETED\",\n payload={\n \"total\": run.total_records,\n \"successful\": run.successful_records,\n \"failed\": run.failed_records,\n \"skipped\": run.skipped_records,\n },\n created_by=self.current_user,\n )\n\n # Skip insert phase if requested (e.g., for preview-only execution)\n if skip_insert:\n run.status = \"COMPLETED\"\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_COMPLETED\",\n payload={\"skip_insert\": True},\n created_by=self.current_user,\n )\n self.db.commit()\n return run\n\n # Generate SQL and submit to Superset\n insert_result = self._generate_and_insert_sql(job, run)\n run.insert_status = insert_result.get(\"status\")\n run.superset_execution_id = str(insert_result.get(\"query_id\") or \"\")\n run.superset_execution_log = insert_result\n # Preserve the translation-phase status. If the executor already\n # marked the run FAILED (all LLM calls failed) we do NOT upgrade it\n # to COMPLETED just because the insert phase ran.\n if run.status != \"FAILED\":\n run.status = \"COMPLETED\"\n if insert_result.get(\"error_message\"):\n run.error_message = insert_result[\"error_message\"]\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n\n # Record terminal event\n terminal_event = \"RUN_COMPLETED\"\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=terminal_event,\n payload={\n \"insert_status\": insert_result.get(\"status\"),\n \"query_id\": insert_result.get(\"query_id\"),\n \"rows_affected\": insert_result.get(\"rows_affected\"),\n \"total_records\": run.total_records,\n \"successful\": run.successful_records,\n \"failed\": run.failed_records,\n \"skipped\": run.skipped_records,\n },\n created_by=self.current_user,\n )\n\n self.db.commit()\n # Re-query run after commit — refresh may fail if the object was\n # created in a different session or became detached during commit.\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run.id).first()\n\n logger.reflect(\"Run execution complete\", {\n \"run_id\": run.id,\n \"status\": run.status,\n \"insert_status\": run.insert_status,\n })\n return run\n # endregion execute_run\n\n # region _generate_and_insert_sql [TYPE Function]\n # @PURPOSE: Generate INSERT SQL from successful records and submit to Superset SQL Lab.\n # @PRE: job has target table configured. run has successful records.\n # @POST: SQL is generated and submitted. Returns execution result.\n # @SIDE_EFFECT: Superset API call.\n def _generate_and_insert_sql(\n self,\n job: TranslationJob,\n run: TranslationRun,\n ) -> dict[str, Any]:\n with belief_scope(\"TranslationOrchestrator._generate_and_insert_sql\"):\n # Fetch successful records with eager-loaded language data\n records = (\n self.db.query(TranslationRecord)\n .options(joinedload(TranslationRecord.languages))\n .filter(\n TranslationRecord.run_id == run.id,\n TranslationRecord.status == \"SUCCESS\",\n TranslationRecord.target_sql.isnot(None),\n )\n .all()\n )\n\n if not records:\n logger.reason(\"No successful records to insert\", {\"run_id\": run.id})\n return {\"status\": \"skipped\", \"reason\": \"no_records\", \"query_id\": None}\n\n logger.reason(f\"Generating SQL for {len(records)} records\", {\n \"run_id\": run.id,\n \"dialect\": job.database_dialect or job.target_dialect,\n })\n\n effective_target = job.target_column or job.translation_column\n primary_language = (job.target_languages or [\"en\"])[0]\n\n # Columns that exist in the target ClickHouse table\n columns = []\n if job.target_key_cols:\n columns.extend(job.target_key_cols)\n if effective_target:\n columns.append(effective_target)\n if job.target_language_column:\n columns.append(job.target_language_column)\n if job.target_source_column:\n columns.append(job.target_source_column)\n if job.target_source_language_column:\n columns.append(job.target_source_language_column)\n columns.append(\"context\")\n columns.append(\"is_original\")\n # Deduplicate while preserving order\n seen: set[str] = set()\n deduped: list[str] = []\n for c in columns:\n if c and c not in seen:\n deduped.append(c)\n seen.add(c)\n columns = deduped\n\n # Keys for the context JSON: context_columns + original translation_column\n context_keys = list(job.context_columns or [])\n if job.translation_column and job.translation_column != effective_target and job.translation_column not in context_keys:\n context_keys.append(job.translation_column)\n\n rows_for_sql: list[dict[str, object]] = []\n for rec in records:\n source_data = rec.source_data or {}\n\n # Detect source language from first TranslationLanguage entry\n detected_src_lang = \"und\"\n if rec.languages and len(rec.languages) > 0:\n detected_src_lang = rec.languages[0].source_language_detected or \"und\"\n\n # Build context JSON: all extra columns the user configured\n context_data: dict[str, str] = {}\n for key in context_keys:\n val = source_data.get(key)\n context_data[key] = str(val) if val is not None else \"\"\n\n # ── Shared base row ──\n base_row: dict[str, object] = {}\n\n if job.target_key_cols:\n for k in job.target_key_cols:\n raw = source_data.get(k)\n if raw is not None:\n normalized = _normalize_timestamp_value(raw)\n base_row[k] = normalized if normalized else raw\n else:\n base_row[k] = None\n\n if job.target_source_column:\n base_row[job.target_source_column] = rec.source_sql or \"\"\n\n if job.target_source_language_column:\n base_row[job.target_source_language_column] = detected_src_lang\n\n base_row[\"context\"] = json.dumps(context_data, ensure_ascii=False)\n\n # ── 1. ORIGINAL row (is_original = 1) ──\n original_row = dict(base_row)\n if effective_target:\n original_row[effective_target] = rec.source_sql or \"\"\n if job.target_language_column:\n original_row[job.target_language_column] = detected_src_lang\n original_row[\"is_original\"] = 1\n rows_for_sql.append(original_row)\n\n # ── 2. TRANSLATION rows (is_original = 0) ──\n # Skip language that matches the source — the original row already covers it\n if rec.languages and len(rec.languages) > 0:\n for lang in rec.languages:\n if lang.language_code == detected_src_lang:\n continue\n trans_row = dict(base_row)\n trans_value = lang.final_value or lang.translated_value or \"\"\n if effective_target:\n trans_row[effective_target] = trans_value\n if job.target_language_column:\n trans_row[job.target_language_column] = lang.language_code\n trans_row[\"is_original\"] = 0\n rows_for_sql.append(trans_row)\n else:\n # Fallback: no per-language data\n fallback_row = dict(base_row)\n if effective_target:\n fallback_row[effective_target] = rec.target_sql or \"\"\n if job.target_language_column:\n fallback_row[job.target_language_column] = primary_language\n fallback_row[\"is_original\"] = 0\n rows_for_sql.append(fallback_row)\n\n if not columns:\n columns = [effective_target or \"translated_text\"]\n rows_for_sql = [{columns[0]: rec.target_sql or \"\"} for rec in records]\n\n # Resolve the real database backend engine from Superset\n try:\n env_id = job.environment_id or job.source_dialect or \"\"\n executor = SupersetSqlLabExecutor(self.config_manager, env_id)\n executor.resolve_database_id(\n target_database_id=job.target_database_id,\n )\n real_backend = executor.get_database_backend()\n except Exception as e:\n logger.explore(\"Failed to resolve database backend, falling back to job dialet\", {\n \"error\": str(e),\n })\n real_backend = None\n\n dialect = real_backend or job.database_dialect or job.target_dialect or \"postgresql\"\n\n # Generate SQL\n try:\n sql, row_count = SQLGenerator.generate(\n dialect=dialect,\n target_schema=job.target_schema,\n target_table=job.target_table or \"translated_data\",\n columns=columns,\n rows=rows_for_sql,\n key_columns=job.target_key_cols,\n upsert_strategy=job.upsert_strategy or \"MERGE\",\n )\n except ValueError as e:\n logger.explore(\"SQL generation failed\", {\"error\": str(e)})\n return {\"status\": \"failed\", \"error_message\": str(e), \"query_id\": None}\n\n logger.reason(\"SQL generated with dialect\", {\n \"dialect\": dialect,\n \"real_backend\": real_backend,\n \"job_database_dialect\": job.database_dialect,\n \"job_target_dialect\": job.target_dialect,\n })\n\n # Log insert phase start\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"INSERT_PHASE_STARTED\",\n payload={\"sql_length\": len(sql), \"row_count\": row_count, \"dialect\": dialect},\n created_by=self.current_user,\n )\n\n # Submit to Superset\n try:\n result = executor.execute_and_poll(\n sql=sql,\n max_polls=30,\n poll_interval_seconds=2.0,\n )\n except Exception as e:\n logger.explore(\"Superset SQL submission failed\", {\n \"run_id\": run.id,\n \"error\": str(e),\n })\n result = {\"status\": \"failed\", \"error_message\": str(e), \"query_id\": None}\n\n # Log insert phase complete\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"INSERT_PHASE_COMPLETED\",\n payload=result,\n created_by=self.current_user,\n )\n\n return result\n # endregion _generate_and_insert_sql\n\n # region _validate_preconditions [TYPE Function]\n # @PURPOSE: Validate preconditions before starting a run.\n # @PRE: None.\n # @POST: Raises ValueError if preconditions are not met.\n # @SIDE_EFFECT: None.\n def _validate_preconditions(\n self,\n job: TranslationJob,\n is_scheduled: bool = False,\n ) -> None:\n with belief_scope(\"TranslationOrchestrator._validate_preconditions\"):\n # Job must be in valid status\n if job.status in (\"DRAFT\",):\n raise ValueError(\n f\"Cannot run job '{job.id}' in status '{job.status}'. \"\n f\"Job must be READY, ACTIVE, or COMPLETED.\"\n )\n\n # Must have target table configured\n if not job.target_table:\n raise ValueError(\n f\"Job '{job.id}' has no target table configured. \"\n \"Configure a target table before running.\"\n )\n\n # Must have LLM provider configured\n if not job.provider_id:\n raise ValueError(\n f\"Job '{job.id}' has no LLM provider configured. \"\n \"Select an LLM provider before running.\"\n )\n\n # Must have a translation column\n if not job.translation_column:\n raise ValueError(\n f\"Job '{job.id}' has no translation column configured. \"\n \"Select a translation column before running.\"\n )\n\n # For manual runs, must have accepted preview\n if not is_scheduled:\n accepted_session = (\n self.db.query(TranslationPreviewSession)\n .filter(\n TranslationPreviewSession.job_id == job.id,\n TranslationPreviewSession.status == \"APPLIED\",\n )\n .order_by(TranslationPreviewSession.created_at.desc())\n .first()\n )\n if not accepted_session:\n raise ValueError(\n f\"Job '{job.id}' has no accepted preview session. \"\n \"Run and accept a preview before executing a manual translation run.\"\n )\n\n logger.reason(\"Preconditions validated\", {\n \"job_id\": job.id,\n \"is_scheduled\": is_scheduled,\n })\n # endregion _validate_preconditions\n\n # region retry_failed_batches [TYPE Function]\n # @PURPOSE: Retry failed batches in a run.\n # @PRE: run exists and has failed batches.\n # @POST: Failed batches are re-processed.\n # @SIDE_EFFECT: LLM calls, DB writes.\n def retry_failed_batches(\n self,\n run_id: str,\n ) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.retry_failed_batches\"):\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n\n job = self.db.query(TranslationJob).filter(TranslationJob.id == run.job_id).first()\n if not job:\n raise ValueError(f\"Job '{run.job_id}' not found\")\n self._job = job\n\n # Find failed batches\n failed_batches = (\n self.db.query(TranslationBatch)\n .filter(\n TranslationBatch.run_id == run_id,\n TranslationBatch.status.in_([\"FAILED\", \"COMPLETED_WITH_ERRORS\"]),\n )\n .all()\n )\n\n if not failed_batches:\n raise ValueError(f\"No failed batches found for run '{run_id}'\")\n\n logger.reason(\"Retrying failed batches\", {\n \"run_id\": run_id,\n \"batch_count\": len(failed_batches),\n })\n\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_RETRYING\",\n payload={\"batch_count\": len(failed_batches)},\n created_by=self.current_user,\n )\n\n # Re-process each failed batch\n executor = TranslationExecutor(self.db, self.config_manager, self.current_user)\n run.status = \"RUNNING\"\n self.db.flush()\n\n for batch in failed_batches:\n # Fetch failed records for this batch\n failed_records = (\n self.db.query(TranslationRecord)\n .filter(\n TranslationRecord.batch_id == batch.id,\n TranslationRecord.status == \"FAILED\",\n )\n .all()\n )\n\n if not failed_records:\n continue\n\n # Build rows from failed records\n retry_rows = []\n for rec in failed_records:\n retry_rows.append({\n \"row_index\": rec.source_object_id or \"0\",\n \"source_text\": rec.source_sql or \"\",\n \"approved_translation\": None,\n \"source_object_name\": rec.source_object_name or \"\",\n })\n\n # Process retry batch\n result = executor._process_batch(\n job=job,\n run_id=run_id,\n batch_index=batch.batch_index,\n batch_rows=retry_rows,\n )\n\n # Update run stats\n run.successful_records = (run.successful_records or 0) + result[\"successful\"]\n run.failed_records = (run.failed_records or 0) + result[\"failed\"]\n run.skipped_records = (run.skipped_records or 0) + result[\"skipped\"]\n self.db.flush()\n\n # Update run status\n if run.failed_records == 0:\n run.status = \"COMPLETED\"\n elif run.successful_records == 0:\n run.status = \"FAILED\"\n else:\n run.status = \"COMPLETED\"\n\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_COMPLETED\",\n payload={\"retry\": True},\n created_by=self.current_user,\n )\n\n self.db.commit()\n logger.reflect(\"Retry complete\", {\n \"run_id\": run_id,\n \"status\": run.status,\n })\n return run\n # endregion retry_failed_batches\n\n # region retry_insert [TYPE Function]\n # @PURPOSE: Retry the SQL insert phase for a completed run.\n # @PRE: run exists and has successful records.\n # @POST: SQL is regenerated and re-submitted to Superset.\n # @SIDE_EFFECT: Superset API call.\n def retry_insert(\n self,\n run_id: str,\n ) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.retry_insert\"):\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n\n job = self.db.query(TranslationJob).filter(TranslationJob.id == run.job_id).first()\n if not job:\n raise ValueError(f\"Job '{run.job_id}' not found\")\n self._job = job\n\n logger.reason(\"Retrying insert phase\", {\n \"run_id\": run_id,\n })\n\n self.event_log.log_event(\n job_id=job.id,\n run_id=run.id,\n event_type=\"RUN_RETRY_INSERT\",\n payload={},\n created_by=self.current_user,\n )\n\n # Regenerate SQL and submit\n insert_result = self._generate_and_insert_sql(job, run)\n\n # Update run\n run.insert_status = insert_result.get(\"status\")\n run.superset_execution_id = str(insert_result.get(\"query_id\") or \"\")\n if insert_result.get(\"error_message\"):\n run.error_message = insert_result[\"error_message\"]\n self.db.flush()\n self.db.commit()\n self.db.refresh(run)\n\n logger.reflect(\"Insert retry complete\", {\n \"run_id\": run_id,\n \"insert_status\": run.insert_status,\n })\n return run\n # endregion retry_insert\n\n # region cancel_run [TYPE Function]\n # @PURPOSE: Cancel a running translation.\n # @PRE: run is in PENDING or RUNNING status.\n # @POST: Run status is set to CANCELLED.\n # @SIDE_EFFECT: DB write; records event.\n def cancel_run(self, run_id: str) -> TranslationRun:\n with belief_scope(\"TranslationOrchestrator.cancel_run\"):\n # Set short lock timeout to avoid blocking on row lock held by\n # background executor thread (which holds RowExclusiveLock during\n # batch processing). If the row is locked, we fall back to setting\n # a cancellation flag via direct SQL UPDATE, which the executor\n # checks after each batch commit.\n try:\n self.db.execute(text(\"SET LOCAL lock_timeout = '3s'\"))\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n except Exception:\n # Row is locked — set cancellation flag via direct SQL\n logger.explore(\"Row lock timeout — setting cancellation flag via direct SQL\", {\n \"run_id\": run_id,\n })\n self.db.execute(\n text(\"UPDATE translation_runs SET error_message = 'CANCEL_REQUESTED' WHERE id = :run_id\"),\n {\"run_id\": run_id},\n )\n self.db.commit()\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n logger.reflect(\"Cancellation flag set for locked run\", {\n \"run_id\": run_id,\n })\n return run\n\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n\n if run.status not in (\"PENDING\", \"RUNNING\"):\n raise ValueError(\n f\"Cannot cancel run in status '{run.status}'. \"\n f\"Only PENDING or RUNNING runs can be cancelled.\"\n )\n\n run.status = \"CANCELLED\"\n run.completed_at = datetime.now(UTC)\n self.db.flush()\n\n self.event_log.log_event(\n job_id=run.job_id,\n run_id=run.id,\n event_type=\"RUN_CANCELLED\",\n payload={},\n created_by=self.current_user,\n )\n\n self.db.commit()\n self.db.refresh(run)\n\n logger.reflect(\"Run cancelled\", {\n \"run_id\": run_id,\n })\n return run\n # endregion cancel_run\n\n # region get_run_status [TYPE Function]\n # @PURPOSE: Get run status with statistics.\n # @PRE: run_id exists.\n # @POST: Returns dict with run details.\n def get_run_status(self, run_id: str) -> dict[str, Any]:\n with belief_scope(\"TranslationOrchestrator.get_run_status\"):\n run = self.db.query(TranslationRun).filter(TranslationRun.id == run_id).first()\n if not run:\n raise ValueError(f\"Run '{run_id}' not found\")\n\n # Count batches\n batch_count = (\n self.db.query(TranslationBatch)\n .filter(TranslationBatch.run_id == run_id)\n .count()\n )\n\n # Get event summary\n event_summary = self.event_log.get_run_event_summary(run_id)\n\n # Get language stats\n language_stats_entries = (\n self.db.query(TranslationRunLanguageStats)\n .filter(TranslationRunLanguageStats.run_id == run_id)\n .all()\n )\n language_stats = [\n {\n \"language_code\": ls.language_code,\n \"total_rows\": ls.total_rows or 0,\n \"translated_rows\": ls.translated_rows or 0,\n \"failed_rows\": ls.failed_rows or 0,\n \"skipped_rows\": ls.skipped_rows or 0,\n \"token_count\": ls.token_count or 0,\n \"estimated_cost\": ls.estimated_cost or 0.0,\n }\n for ls in language_stats_entries\n ]\n\n return {\n \"id\": run.id,\n \"job_id\": run.job_id,\n \"status\": run.status,\n \"trigger_type\": run.trigger_type,\n \"started_at\": run.started_at.isoformat() if run.started_at else None,\n \"completed_at\": run.completed_at.isoformat() if run.completed_at else None,\n \"error_message\": run.error_message,\n \"total_records\": run.total_records or 0,\n \"successful_records\": run.successful_records or 0,\n \"failed_records\": run.failed_records or 0,\n \"skipped_records\": run.skipped_records or 0,\n \"insert_status\": run.insert_status,\n \"superset_execution_id\": run.superset_execution_id,\n \"batch_count\": batch_count,\n \"language_stats\": language_stats,\n \"event_invariants\": {\n \"has_run_started\": event_summary[\"has_run_started\"],\n \"terminal_event_count\": event_summary[\"terminal_event_count\"],\n \"invariant_valid\": event_summary[\"invariant_valid\"],\n },\n \"created_by\": run.created_by,\n \"created_at\": run.created_at.isoformat() if run.created_at else None,\n }\n # endregion get_run_status\n\n # region get_run_records [TYPE Function]\n # @PURPOSE: Get paginated records for a run.\n # @PRE: run_id exists.\n # @POST: Returns dict with records and pagination info.\n def get_run_records(\n self,\n run_id: str,\n page: int = 1,\n page_size: int = 50,\n status_filter: str | None = None,\n ) -> dict[str, Any]:\n with belief_scope(\"TranslationOrchestrator.get_run_records\"):\n query = (\n self.db.query(TranslationRecord)\n .options(selectinload(TranslationRecord.languages))\n .filter(TranslationRecord.run_id == run_id)\n )\n\n if status_filter:\n query = query.filter(TranslationRecord.status == status_filter)\n\n total = query.count()\n offset = (page - 1) * page_size\n records = (\n query.order_by(TranslationRecord.created_at.desc())\n .offset(offset)\n .limit(page_size)\n .all()\n )\n\n return {\n \"items\": [\n {\n \"id\": r.id,\n \"batch_id\": r.batch_id,\n \"source_sql\": r.source_sql,\n \"target_sql\": r.target_sql,\n \"source_object_type\": r.source_object_type,\n \"source_object_id\": r.source_object_id,\n \"source_object_name\": r.source_object_name,\n \"status\": r.status,\n \"error_message\": r.error_message,\n \"created_at\": r.created_at.isoformat() if r.created_at else None,\n \"languages\": [\n {\n \"language_code\": tl.language_code,\n \"translated_value\": tl.translated_value,\n \"final_value\": tl.final_value or tl.translated_value,\n \"source_language_detected\": tl.source_language_detected,\n \"status\": tl.status,\n \"needs_review\": tl.needs_review or False,\n }\n for tl in (r.languages or [])\n ],\n }\n for r in records\n ],\n \"total\": total,\n \"page\": page,\n \"page_size\": page_size,\n \"status_filter\": status_filter,\n }\n # endregion get_run_records\n\n # region get_run_history [TYPE Function]\n # @PURPOSE: Get run history for a job.\n # @PRE: job_id exists.\n # @POST: Returns list of runs.\n def get_run_history(\n self,\n job_id: str,\n page: int = 1,\n page_size: int = 20,\n ) -> tuple[int, list[dict[str, Any]]]:\n with belief_scope(\"TranslationOrchestrator.get_run_history\"):\n query = self.db.query(TranslationRun).filter(\n TranslationRun.job_id == job_id\n )\n total = query.count()\n runs = (\n query.order_by(TranslationRun.created_at.desc())\n .offset((page - 1) * page_size)\n .limit(page_size)\n .all()\n )\n\n return total, [\n {\n \"id\": r.id,\n \"job_id\": r.job_id,\n \"status\": r.status,\n \"started_at\": r.started_at.isoformat() if r.started_at else None,\n \"completed_at\": r.completed_at.isoformat() if r.completed_at else None,\n \"error_message\": r.error_message,\n \"total_records\": r.total_records or 0,\n \"successful_records\": r.successful_records or 0,\n \"failed_records\": r.failed_records or 0,\n \"skipped_records\": r.skipped_records or 0,\n \"insert_status\": r.insert_status,\n \"superset_execution_id\": r.superset_execution_id,\n \"created_by\": r.created_by,\n \"created_at\": r.created_at.isoformat() if r.created_at else None,\n }\n for r in runs\n ]\n # endregion get_run_history\n\n # region _update_language_stats [TYPE Function]\n # @PURPOSE: Aggregate TranslationLanguage entries by language_code and update TranslationRunLanguageStats.\n # @PRE: run_id and language_stats_map are valid. DB session is available.\n # @POST: Language stats are updated with row counts and estimated tokens/cost.\n # @SIDE_EFFECT: DB writes on language_stats objects.\n def _update_language_stats(\n self,\n run_id: str,\n language_stats_map: dict[str, TranslationRunLanguageStats],\n ) -> None:\n with belief_scope(\"TranslationOrchestrator._update_language_stats\"):\n # Get all records for this run to join with TranslationLanguage\n records = (\n self.db.query(TranslationRecord)\n .filter(TranslationRecord.run_id == run_id)\n .all()\n )\n record_ids = [r.id for r in records]\n\n if not record_ids:\n logger.reason(\"No records for language stats aggregation\", {\"run_id\": run_id})\n return\n\n # Get all language entries for this run's records\n lang_entries = (\n self.db.query(TranslationLanguage)\n .filter(TranslationLanguage.record_id.in_(record_ids))\n .all()\n )\n\n # Aggregate by language_code\n from collections import defaultdict\n agg: dict[str, dict[str, int]] = defaultdict(lambda: {\"total\": 0, \"translated\": 0, \"failed\": 0, \"skipped\": 0})\n\n for le in lang_entries:\n code = le.language_code\n agg[code][\"total\"] += 1\n if le.status in (\"translated\", \"approved\", \"edited\"):\n agg[code][\"translated\"] += 1\n elif le.status == \"failed\":\n agg[code][\"failed\"] += 1\n elif le.status == \"skipped\":\n agg[code][\"skipped\"] += 1\n\n # Estimate tokens: heuristic based on character count of translated values\n total_chars = sum(\n len(le.translated_value or \"\") for le in lang_entries if le.translated_value\n )\n total_tokens = max(1, total_chars // 4) # ~4 chars per token\n cost_per_token = 0.002 / 1000 # $0.002 per 1K tokens\n\n # Update each language stat entry\n for lang_code, lang_stat in language_stats_map.items():\n data = agg.get(lang_code, {\"total\": 0, \"translated\": 0, \"failed\": 0, \"skipped\": 0})\n lang_stat.total_rows = data[\"total\"]\n lang_stat.translated_rows = data[\"translated\"]\n lang_stat.failed_rows = data[\"failed\"]\n lang_stat.skipped_rows = data[\"skipped\"]\n\n # Proportional token split: share tokens across languages\n num_langs = len(language_stats_map)\n if num_langs > 0:\n lang_stat.token_count = total_tokens // num_langs\n lang_stat.estimated_cost = round((lang_stat.token_count / 1000) * cost_per_token, 6)\n\n self.db.flush()\n\n logger.reason(\"Language stats updated\", {\n \"run_id\": run_id,\n \"languages\": list(language_stats_map.keys()),\n \"total_tokens_est\": total_tokens,\n })\n # endregion _update_language_stats\n\n # region _compute_config_hash [TYPE Function]\n # @PURPOSE: Compute a hash of the job's current configuration for snapshot comparison.\n @staticmethod\n def _compute_config_hash(job: TranslationJob) -> str:\n import hashlib\n config_str = json.dumps({\n \"source_dialect\": job.source_dialect,\n \"target_dialect\": job.target_dialect,\n \"source_datasource_id\": job.source_datasource_id,\n \"translation_column\": job.translation_column,\n \"context_columns\": job.context_columns,\n \"target_languages\": sorted(job.target_languages) if job.target_languages else [],\n \"provider_id\": job.provider_id,\n \"batch_size\": job.batch_size,\n \"upsert_strategy\": job.upsert_strategy,\n }, sort_keys=True)\n return hashlib.sha256(config_str.encode()).hexdigest()[:16]\n # endregion _compute_config_hash\n\n # region _compute_dict_snapshot_hash [TYPE Function]\n # @PURPOSE: Compute a hash of dictionary state for snapshot comparison.\n def _compute_dict_snapshot_hash(self, job_id: str) -> str:\n import hashlib\n\n from ...models.translate import TranslationJobDictionary\n dict_links = (\n self.db.query(TranslationJobDictionary)\n .filter(TranslationJobDictionary.job_id == job_id)\n .all()\n )\n dict_ids = sorted([dl.dictionary_id for dl in dict_links])\n hash_input = \",\".join(dict_ids)\n return hashlib.sha256(hash_input.encode()).hexdigest()[:16]\n # endregion _compute_dict_snapshot_hash\n\n# #endregion TranslationOrchestrator\n" }, { "contract_id": "TranslatePlugin", @@ -50071,104 +49410,44 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslatePlugin [C:2] [TYPE Module] [SEMANTICS clickhouse, translate, sql, dashboard, dialect]\n# @BRIEF TranslatePlugin skeleton for cross-dialect SQL and dashboard translation.\n# @LAYER: Domain\n# @RELATION INHERITS -> [PluginBase]\n# @RATIONALE: Separate plugin avoids bloating LLMAnalysisPlugin beyond fractal limit (<400 lines).\n# @REJECTED: Extending LLMAnalysisPlugin would conflate two distinct feature domains.\n\nfrom typing import Any\n\nfrom ...core.plugin_base import PluginBase\n\n\n# #region TranslatePlugin [TYPE Class]\n# @BRIEF Plugin for translating SQL queries and dashboard definitions across database dialects.\n# @RELATION IMPLEMENTS -> backend.src.core.plugin_base.PluginBase\nclass TranslatePlugin(PluginBase):\n @property\n def id(self) -> str:\n return \"translate\"\n\n @property\n def name(self) -> str:\n return \"LLM Table Translation\"\n\n @property\n def description(self) -> str:\n return \"Cross-dialect SQL and dashboard translation using LLMs.\"\n\n @property\n def version(self) -> str:\n return \"1.0.0\"\n\n def get_schema(self) -> dict[str, Any]:\n return {\n \"type\": \"object\",\n \"properties\": {\n \"source_dialect\": {\n \"type\": \"string\",\n \"title\": \"Source Dialect\",\n \"description\": \"Source database dialect (e.g. PostgreSQL, ClickHouse)\",\n },\n \"target_dialect\": {\n \"type\": \"string\",\n \"title\": \"Target Dialect\",\n \"description\": \"Target database dialect (e.g. PostgreSQL, ClickHouse)\",\n },\n \"job_id\": {\n \"type\": \"string\",\n \"title\": \"Translation Job ID\",\n \"description\": \"Existing translation job to execute\",\n },\n },\n \"required\": [\"job_id\"],\n }\n\n async def execute(self, params: dict[str, Any], context: Any | None = None):\n \"\"\"Execute a translation job — implementation deferred to later phases.\"\"\"\n raise NotImplementedError(\"TranslatePlugin.execute not yet implemented\")\n# #endregion TranslatePlugin\n\n# #endregion TranslatePlugin\n" + "body": "# #region TranslatePlugin [C:2] [TYPE Module] [SEMANTICS clickhouse, translate, sql, dashboard, dialect]\n# @BRIEF TranslatePlugin skeleton for cross-dialect SQL and dashboard translation.\n# @LAYER Domain\n# @RELATION INHERITS -> [PluginBase]\n# @RATIONALE Separate plugin avoids bloating LLMAnalysisPlugin beyond fractal limit (<400 lines).\n# @REJECTED Extending LLMAnalysisPlugin would conflate two distinct feature domains.\n\nfrom typing import Any\n\nfrom ...core.plugin_base import PluginBase\n\n\n# #region TranslatePlugin [TYPE Class]\n# @BRIEF Plugin for translating SQL queries and dashboard definitions across database dialects.\n# @RELATION IMPLEMENTS -> backend.src.core.plugin_base.PluginBase\nclass TranslatePlugin(PluginBase):\n @property\n def id(self) -> str:\n return \"translate\"\n\n @property\n def name(self) -> str:\n return \"LLM Table Translation\"\n\n @property\n def description(self) -> str:\n return \"Cross-dialect SQL and dashboard translation using LLMs.\"\n\n @property\n def version(self) -> str:\n return \"1.0.0\"\n\n def get_schema(self) -> dict[str, Any]:\n return {\n \"type\": \"object\",\n \"properties\": {\n \"source_dialect\": {\n \"type\": \"string\",\n \"title\": \"Source Dialect\",\n \"description\": \"Source database dialect (e.g. PostgreSQL, ClickHouse)\",\n },\n \"target_dialect\": {\n \"type\": \"string\",\n \"title\": \"Target Dialect\",\n \"description\": \"Target database dialect (e.g. PostgreSQL, ClickHouse)\",\n },\n \"job_id\": {\n \"type\": \"string\",\n \"title\": \"Translation Job ID\",\n \"description\": \"Existing translation job to execute\",\n },\n },\n \"required\": [\"job_id\"],\n }\n\n async def execute(self, params: dict[str, Any], context: Any | None = None):\n \"\"\"Execute a translation job — implementation deferred to later phases.\"\"\"\n raise NotImplementedError(\"TranslatePlugin.execute not yet implemented\")\n# #endregion TranslatePlugin\n\n# #endregion TranslatePlugin\n" }, { "contract_id": "DEFAULT_EXECUTION_PROMPT_TEMPLATE", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/preview.py", "start_line": 43, - "end_line": 60, + "end_line": 64, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Default prompt template for batch LLM translation execution (no context columns — faster)." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region DEFAULT_EXECUTION_PROMPT_TEMPLATE [TYPE Constant]\n# @BRIEF Default prompt template for batch LLM translation execution (no context columns — faster).\n# Supports both single-language and multi-language modes via {target_languages} placeholder.\nDEFAULT_EXECUTION_PROMPT_TEMPLATE: str = (\n \"Translate the following database content from {source_language} to the following language(s): {target_languages}.\\n\\n\"\n \"Source dialect: {source_dialect}\\n\"\n \"Target dialect(s): {target_dialect}\\n\"\n \"Column to translate: {translation_column}\\n\\n\"\n \"{dictionary_section}\"\n \"For each row, provide an accurate translation of the text into each target language.\\n\\n\"\n \"Rows to translate:\\n{rows_json}\\n\\n\"\n \"Respond with a JSON object in this exact format:\\n\"\n '{{\"rows\": [{{\"row_id\": \"\", \"detected_source_language\": \"\", \"\": \"\", \"\": \"\"}}]}}\\n'\n \"For each row, return the detected source language as a BCP-47 tag (e.g. 'en', 'ru', 'fr'), or 'und' if uncertain.\\n\"\n \"Include a separate key for EACH target language code with the translated text in that language.\\n\"\n \"Each row_id must match the index provided. Return exactly {row_count} entries.\"\n)\n# #endregion DEFAULT_EXECUTION_PROMPT_TEMPLATE\n" + "body": "# #region DEFAULT_EXECUTION_PROMPT_TEMPLATE [TYPE Constant]\n\n# Supports both single-language and multi-language modes via {target_languages} placeholder.\nDEFAULT_EXECUTION_PROMPT_TEMPLATE: str = (\n \"Translate the following database content from {source_language} to the following language(s): {target_languages}.\\n\\n\"\n \"Source dialect: {source_dialect}\\n\"\n \"Target dialect(s): {target_dialect}\\n\"\n \"Column to translate: {translation_column}\\n\\n\"\n \"{dictionary_section}\"\n \"IMPORTANT — You MUST use the terminology dictionary above. \"\n \"For any source term listed in the dictionary, you MUST use its exact target translation. \"\n \"Do not translate dictionary terms differently. \"\n \"This is mandatory, not optional.\\n\\n\"\n \"For each row, provide an accurate translation of the text into each target language.\\n\\n\"\n \"Rows to translate:\\n{rows_json}\\n\\n\"\n \"Respond with a JSON object in this exact format:\\n\"\n '{{\"rows\": [{{\"row_id\": \"\", \"detected_source_language\": \"\", \"\": \"\", \"\": \"\"}}]}}\\n'\n \"For each row, return the detected source language as a BCP-47 tag (e.g. 'en', 'ru', 'fr'), or 'und' if uncertain.\\n\"\n \"Include a separate key for EACH target language code with the translated text in that language.\\n\"\n \"Each row_id must match the index provided. Return exactly {row_count} entries.\"\n)\n# #endregion DEFAULT_EXECUTION_PROMPT_TEMPLATE\n" }, { "contract_id": "DEFAULT_PREVIEW_PROMPT_TEMPLATE", "contract_type": "Constant", "file_path": "backend/src/plugins/translate/preview.py", - "start_line": 63, - "end_line": 80, + "start_line": 67, + "end_line": 88, "tier": "TIER_1", "complexity": 1, - "metadata": { - "PURPOSE": "Default prompt template for LLM translation preview." - }, + "metadata": {}, "relations": [], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "PURPOSE", - "message": "@PURPOSE is not allowed for contract type 'Constant'", - "detail": { - "actual_type": "Constant", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "ADR", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PURPOSE", - "message": "@PURPOSE is forbidden for contract type 'Constant' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Constant" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region DEFAULT_PREVIEW_PROMPT_TEMPLATE [TYPE Constant]\n# @BRIEF Default prompt template for LLM translation preview.\nDEFAULT_PREVIEW_PROMPT_TEMPLATE: str = (\n \"Translate the following database content.\\n\\n\"\n \"Source dialect: {source_dialect}\\n\"\n \"Column to translate: {translation_column}\\n\\n\"\n \"{dictionary_section}\"\n \"Translate to the following languages: {target_languages}\\n\\n\"\n \"For each row, provide an accurate translation of the '{translation_column}' value into each language.\\n\"\n \"Consider the context columns when determining the meaning of the text.\\n\\n\"\n \"Rows to translate:\\n{rows_json}\\n\\n\"\n \"Respond with a JSON object in this exact format:\\n\"\n '{{\"rows\": [{{\"row_id\": \"\", \"detected_source_language\": \"\", \"language_code_1\": \"\", \"language_code_2\": \"\"}}]}}\\n'\n \"For each row, return the detected source language as a BCP-47 tag (e.g. 'en', 'ru', 'fr'), or 'und' if uncertain.\\n\"\n \"Include a separate key for EACH target language code with the translated text in that language.\\n\"\n \"Each row_id must match the index provided. Return exactly {row_count} entries.\"\n)\n# #endregion DEFAULT_PREVIEW_PROMPT_TEMPLATE\n" + "body": "# #region DEFAULT_PREVIEW_PROMPT_TEMPLATE [TYPE Constant]\n\nDEFAULT_PREVIEW_PROMPT_TEMPLATE: str = (\n \"Translate the following database content.\\n\\n\"\n \"Source dialect: {source_dialect}\\n\"\n \"Column to translate: {translation_column}\\n\\n\"\n \"{dictionary_section}\"\n \"IMPORTANT — You MUST use the terminology dictionary above. \"\n \"For any source term listed in the dictionary, you MUST use its exact target translation. \"\n \"Do not translate dictionary terms differently. \"\n \"This is mandatory, not optional.\\n\\n\"\n \"Translate to the following languages: {target_languages}\\n\\n\"\n \"For each row, provide an accurate translation of the '{translation_column}' value into each language.\\n\"\n \"Consider the context columns when determining the meaning of the text.\\n\\n\"\n \"Rows to translate:\\n{rows_json}\\n\\n\"\n \"Respond with a JSON object in this exact format:\\n\"\n '{{\"rows\": [{{\"row_id\": \"\", \"detected_source_language\": \"\", \"language_code_1\": \"\", \"language_code_2\": \"\"}}]}}\\n'\n \"For each row, return the detected source language as a BCP-47 tag (e.g. 'en', 'ru', 'fr'), or 'und' if uncertain.\\n\"\n \"Include a separate key for EACH target language code with the translated text in that language.\\n\"\n \"Each row_id must match the index provided. Return exactly {row_count} entries.\"\n)\n# #endregion DEFAULT_PREVIEW_PROMPT_TEMPLATE\n" }, { "contract_id": "TokenEstimator", "contract_type": "Class", "file_path": "backend/src/plugins/translate/preview.py", - "start_line": 83, - "end_line": 141, + "start_line": 91, + "end_line": 149, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -50180,14 +49459,14 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TokenEstimator [TYPE Class]\n# @BRIEF Estimate token counts and costs for LLM translation operations.\n# @RATIONALE: Token estimation uses a heuristic (chars/token ratio) since exact tokenization depends on the LLM model.\n# @REJECTED: Using an external tokenizer library would introduce a heavy dependency for estimation only.\nclass TokenEstimator:\n \"\"\"Estimate token counts and costs for LLM operations.\"\"\"\n\n CHARS_PER_TOKEN_ESTIMATE: float = 4.0\n OUTPUT_TOKENS_PER_ROW_ESTIMATE: int = 50\n MULTI_LANG_FACTOR: float = 1.2 # Overhead for multi-language in one call\n TOKEN_COST_PER_1K: float = 0.002 # Default cost per 1K tokens\n COST_WARNING_THRESHOLD: int = 30 # Show warning above this sample size\n\n # region estimate_prompt_tokens [TYPE Function]\n # @PURPOSE: Estimate token count for a prompt string.\n # @PRE: prompt is a non-empty string.\n # @POST: Returns estimated token count (integer).\n @staticmethod\n def estimate_prompt_tokens(prompt: str) -> int:\n if not prompt:\n return 0\n return max(1, int(len(prompt) / TokenEstimator.CHARS_PER_TOKEN_ESTIMATE))\n # endregion estimate_prompt_tokens\n\n # region estimate_output_tokens [TYPE Function]\n # @PURPOSE: Estimate output token count for translating N rows across N languages.\n # @PRE: row_count >= 0, num_languages >= 1.\n # @POST: Returns estimated output token count.\n @staticmethod\n def estimate_output_tokens(row_count: int, num_languages: int = 1) -> int:\n return int(row_count * num_languages * TokenEstimator.OUTPUT_TOKENS_PER_ROW_ESTIMATE * TokenEstimator.MULTI_LANG_FACTOR)\n # endregion estimate_output_tokens\n\n # region estimate_cost [TYPE Function]\n # @PURPOSE: Estimate cost for a given number of tokens.\n # @PRE: total_tokens >= 0.\n # @POST: Returns estimated cost in USD.\n @staticmethod\n def estimate_cost(total_tokens: int, cost_per_1k: float | None = None) -> float:\n rate = cost_per_1k if cost_per_1k is not None else TokenEstimator.TOKEN_COST_PER_1K\n return round((total_tokens / 1000) * rate, 6)\n # endregion estimate_cost\n\n # region check_cost_warning [TYPE Function]\n # @PURPOSE: Generate cost warning for large previews.\n # @PRE: sample_size > 0, num_languages >= 1.\n # @POST: Returns warning string or None.\n @staticmethod\n def check_cost_warning(sample_size: int, num_languages: int, estimated_tokens: int, estimated_cost: float) -> str | None:\n if sample_size > TokenEstimator.COST_WARNING_THRESHOLD:\n return (\n f\"Large preview — estimated {estimated_tokens} tokens, ~${estimated_cost:.4f} cost \"\n f\"(across {num_languages} languages)\"\n )\n return None\n # endregion check_cost_warning\n\n\n# #endregion TokenEstimator\n" + "body": "# #region TokenEstimator [TYPE Class]\n# @BRIEF Estimate token counts and costs for LLM translation operations.\n# @RATIONALE: Token estimation uses a heuristic (chars/token ratio) since exact tokenization depends on the LLM model.\n# @REJECTED: Using an external tokenizer library would introduce a heavy dependency for estimation only.\nclass TokenEstimator:\n \"\"\"Estimate token counts and costs for LLM operations.\"\"\"\n\n CHARS_PER_TOKEN_ESTIMATE: float = 2.2\n OUTPUT_TOKENS_PER_ROW_ESTIMATE: int = 120\n MULTI_LANG_FACTOR: float = 1.2 # Overhead for multi-language in one call\n TOKEN_COST_PER_1K: float = 0.002 # Default cost per 1K tokens\n COST_WARNING_THRESHOLD: int = 30 # Show warning above this sample size\n\n # region estimate_prompt_tokens [TYPE Function]\n # @PURPOSE: Estimate token count for a prompt string.\n # @PRE: prompt is a non-empty string.\n # @POST: Returns estimated token count (integer).\n @staticmethod\n def estimate_prompt_tokens(prompt: str) -> int:\n if not prompt:\n return 0\n return max(1, int(len(prompt) / TokenEstimator.CHARS_PER_TOKEN_ESTIMATE))\n # endregion estimate_prompt_tokens\n\n # region estimate_output_tokens [TYPE Function]\n # @PURPOSE: Estimate output token count for translating N rows across N languages.\n # @PRE: row_count >= 0, num_languages >= 1.\n # @POST: Returns estimated output token count.\n @staticmethod\n def estimate_output_tokens(row_count: int, num_languages: int = 1) -> int:\n return int(row_count * num_languages * TokenEstimator.OUTPUT_TOKENS_PER_ROW_ESTIMATE * TokenEstimator.MULTI_LANG_FACTOR)\n # endregion estimate_output_tokens\n\n # region estimate_cost [TYPE Function]\n # @PURPOSE: Estimate cost for a given number of tokens.\n # @PRE: total_tokens >= 0.\n # @POST: Returns estimated cost in USD.\n @staticmethod\n def estimate_cost(total_tokens: int, cost_per_1k: float | None = None) -> float:\n rate = cost_per_1k if cost_per_1k is not None else TokenEstimator.TOKEN_COST_PER_1K\n return round((total_tokens / 1000) * rate, 6)\n # endregion estimate_cost\n\n # region check_cost_warning [TYPE Function]\n # @PURPOSE: Generate cost warning for large previews.\n # @PRE: sample_size > 0, num_languages >= 1.\n # @POST: Returns warning string or None.\n @staticmethod\n def check_cost_warning(sample_size: int, num_languages: int, estimated_tokens: int, estimated_cost: float) -> str | None:\n if sample_size > TokenEstimator.COST_WARNING_THRESHOLD:\n return (\n f\"Large preview — estimated {estimated_tokens} tokens, ~${estimated_cost:.4f} cost \"\n f\"(across {num_languages} languages)\"\n )\n return None\n # endregion check_cost_warning\n\n\n# #endregion TokenEstimator\n" }, { "contract_id": "ContextAwarePromptBuilder", "contract_type": "Module", "file_path": "backend/src/plugins/translate/prompt_builder.py", "start_line": 1, - "end_line": 152, + "end_line": 153, "tier": "TIER_1", "complexity": 2, "metadata": { @@ -50218,14 +49497,14 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region ContextAwarePromptBuilder [C:2] [TYPE Module] [SEMANTICS translate, prompt, context, dictionary]\n# @BRIEF Pure-function prompt builder that enhances dictionary entries with context annotations.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [DictionaryEntry:Class]\n# @RATIONALE: Pure functions only — no I/O, no DB access. Separated from executor for testability.\n# @REJECTED: Embedding context inline in the executor would make it untestable without mocking DB.\n#\n# Typical workflow:\n# 1. Call build_context_entries(dictionary_entries, row_context) to get annotated, prioritized entries\n# 2. Each entry is rendered via render_entry() with optional priority flag\n# 3. Jaccard similarity >= 0.5 triggers priority flagging\n\nimport json\nfrom typing import Any\n\n# #region ContextAwarePromptBuilder [C:2] [TYPE Class]\n# @BRIEF Build LLM prompts with context-aware dictionary entries and similarity-based priority.\n\nclass ContextAwarePromptBuilder:\n \"\"\"Build LLM prompts with context-aware dictionary entries.\n\n Pure function — no I/O, no DB access.\n \"\"\"\n\n @staticmethod\n def render_entry(entry: Any, priority: bool = False, row_context: dict | None = None) -> str:\n \"\"\"Render a dictionary entry for the LLM prompt.\n\n Args:\n entry: DictionaryEntry-like object (must have source_term, target_term, has_context,\n context_data, usage_notes attrs or dict keys).\n priority: Whether this entry should be flagged as high priority.\n row_context: Optional row context dict (unused in rendering, kept for API symmetry).\n\n Returns:\n Rendered prompt line string.\n \"\"\"\n # Support both object attrs and dict access\n if isinstance(entry, dict):\n source_term = entry.get(\"source_term\", \"\")\n target_term = entry.get(\"target_term\", \"\")\n has_context = entry.get(\"has_context\", False)\n context_data = entry.get(\"context_data\")\n usage_notes = entry.get(\"usage_notes\")\n else:\n source_term = getattr(entry, \"source_term\", \"\")\n target_term = getattr(entry, \"target_term\", \"\")\n has_context = getattr(entry, \"has_context\", False)\n context_data = getattr(entry, \"context_data\", None)\n usage_notes = getattr(entry, \"usage_notes\", None)\n\n # Build base line\n line = f'\"{source_term}\" -> \"{target_term}\"'\n\n # Add context annotation if present\n if has_context and context_data:\n context_items = []\n if isinstance(context_data, dict):\n context_items = [f\"{k}={v}\" for k, v in context_data.items()]\n elif isinstance(context_data, str):\n try:\n parsed = json.loads(context_data)\n if isinstance(parsed, dict):\n context_items = [f\"{k}={v}\" for k, v in parsed.items()]\n else:\n context_items = [str(context_data)]\n except (json.JSONDecodeError, TypeError):\n context_items = [str(context_data)]\n\n context_str = \", \".join(context_items)\n\n # Truncate if too long (500 tokens ≈ 2000 chars)\n if len(context_str) > 2000:\n context_str = context_str[:1997] + \"...[truncated]\"\n\n line = f'\"{source_term}\" (context: {context_str}) -> \"{target_term}\"'\n\n # Add usage notes\n if usage_notes:\n notes = str(usage_notes)[:200] # cap at 200 chars\n line += f\" # Usage: {notes}\"\n\n # Add priority prefix\n if priority:\n line = f\"# PRIORITY (context match) — {line}\"\n\n return line\n\n @staticmethod\n def compute_context_similarity(entry_context: dict | None, row_context: dict | None) -> float:\n \"\"\"Jaccard similarity between entry context and row context. Returns 0.0-1.0.\n\n Compares the sets of lowercased string values from both contexts.\n Returns 1.0 for identical contexts, 0.0 for disjoint or missing.\n \"\"\"\n if not entry_context or not row_context:\n return 0.0\n\n entry_vals = set(str(v).lower() for v in entry_context.values() if v is not None)\n row_vals = set(str(v).lower() for v in row_context.values() if v is not None)\n\n if not entry_vals or not row_vals:\n return 0.0\n\n intersection = entry_vals & row_vals\n union = entry_vals | row_vals\n return len(intersection) / len(union)\n\n @staticmethod\n def build_context_entries(\n dictionary_entries: list[Any],\n row_context: dict | None = None,\n ) -> list[str]:\n \"\"\"Build prioritized dictionary entry list with context annotations.\n\n Args:\n dictionary_entries: List of DictionaryEntry-like objects or dicts.\n row_context: Optional dict of current row's context columns.\n\n Returns:\n List of rendered prompt strings, sorted with priority entries first.\n \"\"\"\n results: list[tuple[Any, bool]] = []\n\n for entry in dictionary_entries:\n priority = False\n if row_context:\n # Extract entry context_data\n if isinstance(entry, dict):\n entry_context = entry.get(\"context_data\")\n else:\n entry_context = getattr(entry, \"context_data\", None)\n\n if entry_context:\n similarity = ContextAwarePromptBuilder.compute_context_similarity(\n entry_context, row_context\n )\n priority = similarity >= 0.5\n\n results.append((entry, priority))\n\n # Sort: priority first, then non-priority\n results.sort(key=lambda x: (not x[1]))\n\n return [\n ContextAwarePromptBuilder.render_entry(entry, priority, row_context)\n for entry, priority in results\n ]\n# #endregion ContextAwarePromptBuilder\n\n\n# #endregion ContextAwarePromptBuilder\n" + "body": "# #region ContextAwarePromptBuilder [C:2] [TYPE Module] [SEMANTICS translate, prompt, context, dictionary]\n# @BRIEF Pure-function prompt builder that enhances dictionary entries with context annotations.\n# @LAYER Domain\n# @RELATION DEPENDS_ON -> [DictionaryEntry:Class]\n# @RATIONALE Pure functions only — no I/O, no DB access. Separated from executor for testability.\n# @REJECTED Embedding context inline in the executor would make it untestable without mocking DB.\n\n#\n# Typical workflow:\n# 1. Call build_context_entries(dictionary_entries, row_context) to get annotated, prioritized entries\n# 2. Each entry is rendered via render_entry() with optional priority flag\n# 3. Jaccard similarity >= 0.5 triggers priority flagging\n\nimport json\nfrom typing import Any\n\n# #region ContextAwarePromptBuilder [C:2] [TYPE Class]\n# @BRIEF Build LLM prompts with context-aware dictionary entries and similarity-based priority.\n\nclass ContextAwarePromptBuilder:\n \"\"\"Build LLM prompts with context-aware dictionary entries.\n\n Pure function — no I/O, no DB access.\n \"\"\"\n\n @staticmethod\n def render_entry(entry: Any, priority: bool = False, row_context: dict | None = None) -> str:\n \"\"\"Render a dictionary entry for the LLM prompt.\n\n Args:\n entry: DictionaryEntry-like object (must have source_term, target_term, has_context,\n context_data, usage_notes attrs or dict keys).\n priority: Whether this entry should be flagged as high priority.\n row_context: Optional row context dict (unused in rendering, kept for API symmetry).\n\n Returns:\n Rendered prompt line string.\n \"\"\"\n # Support both object attrs and dict access\n if isinstance(entry, dict):\n source_term = entry.get(\"source_term\", \"\")\n target_term = entry.get(\"target_term\", \"\")\n has_context = entry.get(\"has_context\", False)\n context_data = entry.get(\"context_data\")\n usage_notes = entry.get(\"usage_notes\")\n else:\n source_term = getattr(entry, \"source_term\", \"\")\n target_term = getattr(entry, \"target_term\", \"\")\n has_context = getattr(entry, \"has_context\", False)\n context_data = getattr(entry, \"context_data\", None)\n usage_notes = getattr(entry, \"usage_notes\", None)\n\n # Build base line\n line = f'\"{source_term}\" -> \"{target_term}\"'\n\n # Add context annotation if present\n if has_context and context_data:\n context_items = []\n if isinstance(context_data, dict):\n context_items = [f\"{k}={v}\" for k, v in context_data.items()]\n elif isinstance(context_data, str):\n try:\n parsed = json.loads(context_data)\n if isinstance(parsed, dict):\n context_items = [f\"{k}={v}\" for k, v in parsed.items()]\n else:\n context_items = [str(context_data)]\n except (json.JSONDecodeError, TypeError):\n context_items = [str(context_data)]\n\n context_str = \", \".join(context_items)\n\n # Truncate if too long (500 tokens ≈ 2000 chars)\n if len(context_str) > 2000:\n context_str = context_str[:1997] + \"...[truncated]\"\n\n line = f'\"{source_term}\" (context: {context_str}) -> \"{target_term}\"'\n\n # Add usage notes\n if usage_notes:\n notes = str(usage_notes)[:200] # cap at 200 chars\n line += f\" # Usage: {notes}\"\n\n # Add priority prefix\n if priority:\n line = f\"# PRIORITY (context match) — {line}\"\n\n return line\n\n @staticmethod\n def compute_context_similarity(entry_context: dict | None, row_context: dict | None) -> float:\n \"\"\"Jaccard similarity between entry context and row context. Returns 0.0-1.0.\n\n Compares the sets of lowercased string values from both contexts.\n Returns 1.0 for identical contexts, 0.0 for disjoint or missing.\n \"\"\"\n if not entry_context or not row_context:\n return 0.0\n\n entry_vals = set(str(v).lower() for v in entry_context.values() if v is not None)\n row_vals = set(str(v).lower() for v in row_context.values() if v is not None)\n\n if not entry_vals or not row_vals:\n return 0.0\n\n intersection = entry_vals & row_vals\n union = entry_vals | row_vals\n return len(intersection) / len(union)\n\n @staticmethod\n def build_context_entries(\n dictionary_entries: list[Any],\n row_context: dict | None = None,\n ) -> list[str]:\n \"\"\"Build prioritized dictionary entry list with context annotations.\n\n Args:\n dictionary_entries: List of DictionaryEntry-like objects or dicts.\n row_context: Optional dict of current row's context columns.\n\n Returns:\n List of rendered prompt strings, sorted with priority entries first.\n \"\"\"\n results: list[tuple[Any, bool]] = []\n\n for entry in dictionary_entries:\n priority = False\n if row_context:\n # Extract entry context_data\n if isinstance(entry, dict):\n entry_context = entry.get(\"context_data\")\n else:\n entry_context = getattr(entry, \"context_data\", None)\n\n if entry_context:\n similarity = ContextAwarePromptBuilder.compute_context_similarity(\n entry_context, row_context\n )\n priority = similarity >= 0.5\n\n results.append((entry, priority))\n\n # Sort: priority first, then non-priority\n results.sort(key=lambda x: (not x[1]))\n\n return [\n ContextAwarePromptBuilder.render_entry(entry, priority, row_context)\n for entry, priority in results\n ]\n# #endregion ContextAwarePromptBuilder\n\n\n# #endregion ContextAwarePromptBuilder\n" }, { "contract_id": "TranslationScheduler", "contract_type": "Module", "file_path": "backend/src/plugins/translate/scheduler.py", "start_line": 1, - "end_line": 434, + "end_line": 436, "tier": "TIER_2", "complexity": 4, "metadata": { @@ -50267,17 +49546,18 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslationScheduler [C:4] [TYPE Module] [SEMANTICS sqlalchemy, translate, schedule, cron, job]\n# @BRIEF Manage TranslationSchedule rows and register them with core SchedulerService.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [TranslationSchedule:Class]\n# @RELATION DEPENDS_ON -> [SchedulerService:Class]\n# @RELATION DEPENDS_ON -> [TranslationOrchestrator:Class]\n# @RELATION DEPENDS_ON -> [TranslationEventLog:Class]\n# @PRE: Database session and SchedulerService are available.\n# @POST: TranslationSchedule CRUD persisted; APScheduler jobs registered/updated/removed.\n# @SIDE_EFFECT: Registers APScheduler jobs; runs translations on trigger; creates events.\n# @RATIONALE: Uses existing SchedulerService (APScheduler) to avoid creating a second scheduler instance.\n# @REJECTED: Separate scheduler instance would create resource contention.\n# @REJECTED: Polling-based approach — event-driven APScheduler is more precise.\n\nimport uuid\nfrom datetime import UTC, datetime, timedelta\n\nfrom sqlalchemy.orm import Session\n\nfrom ...core.config_manager import ConfigManager\nfrom ...core.cot_logger import seed_trace_id\nfrom ...core.logger import belief_scope, logger\nfrom ...models.translate import TranslationJob, TranslationRun, TranslationSchedule\nfrom ...services.notifications.service import NotificationService\nfrom .events import TranslationEventLog\n\n\n# #region TranslationScheduler [C:4] [TYPE Class]\n# @BRIEF CRUD for TranslationSchedule rows + APScheduler registration wrappers.\nclass TranslationScheduler:\n\n def __init__(self, db: Session, config_manager: ConfigManager, current_user: str | None = None):\n self.db = db\n self.config_manager = config_manager\n self.current_user = current_user\n self.event_log = TranslationEventLog(db)\n\n # region create_schedule [TYPE Function]\n # @PURPOSE: Create a new schedule for a job.\n # @PRE: job_id exists. cron_expression is valid.\n # @POST: TranslationSchedule row created.\n def create_schedule(\n self,\n job_id: str,\n cron_expression: str,\n timezone: str = \"UTC\",\n is_active: bool = True,\n execution_mode: str = \"full\",\n ) -> TranslationSchedule:\n with belief_scope(\"TranslationScheduler.create_schedule\"):\n # Verify job exists\n job = self.db.query(TranslationJob).filter(TranslationJob.id == job_id).first()\n if not job:\n raise ValueError(f\"Translation job '{job_id}' not found\")\n\n schedule = TranslationSchedule(\n id=str(uuid.uuid4()),\n job_id=job_id,\n cron_expression=cron_expression,\n timezone=timezone,\n is_active=is_active,\n execution_mode=execution_mode,\n created_by=self.current_user,\n )\n self.db.add(schedule)\n self.db.commit()\n self.db.refresh(schedule)\n\n self.event_log.log_event(\n job_id=job_id,\n event_type=\"SCHEDULE_CREATED\",\n payload={\n \"schedule_id\": schedule.id,\n \"cron_expression\": cron_expression,\n \"timezone\": timezone,\n },\n created_by=self.current_user,\n )\n\n logger.reflect(\"Schedule created\", {\"schedule_id\": schedule.id, \"job_id\": job_id})\n return schedule\n # endregion create_schedule\n\n # region update_schedule [TYPE Function]\n # @PURPOSE: Update an existing schedule.\n # @PRE: job_id has an existing schedule.\n # @POST: Schedule updated.\n def update_schedule(\n self,\n job_id: str,\n cron_expression: str | None = None,\n timezone_str: str | None = None,\n is_active: bool | None = None,\n execution_mode: str | None = None,\n ) -> TranslationSchedule:\n with belief_scope(\"TranslationScheduler.update_schedule\"):\n schedule = self.db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n if not schedule:\n raise ValueError(f\"No schedule found for job '{job_id}'\")\n\n if cron_expression is not None:\n schedule.cron_expression = cron_expression\n if timezone_str is not None:\n schedule.timezone = timezone_str\n if is_active is not None:\n schedule.is_active = is_active\n if execution_mode is not None:\n schedule.execution_mode = execution_mode\n schedule.updated_at = datetime.now(UTC)\n self.db.commit()\n self.db.refresh(schedule)\n\n self.event_log.log_event(\n job_id=job_id,\n event_type=\"SCHEDULE_UPDATED\",\n payload={\n \"schedule_id\": schedule.id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n },\n created_by=self.current_user,\n )\n\n logger.reflect(\"Schedule updated\", {\"schedule_id\": schedule.id, \"job_id\": job_id})\n return schedule\n # endregion update_schedule\n\n # region delete_schedule [TYPE Function]\n # @PURPOSE: Delete a schedule for a job.\n # @PRE: job_id has an existing schedule.\n # @POST: Schedule deleted.\n def delete_schedule(self, job_id: str) -> None:\n with belief_scope(\"TranslationScheduler.delete_schedule\"):\n schedule = self.db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n if not schedule:\n raise ValueError(f\"No schedule found for job '{job_id}'\")\n\n schedule_id = schedule.id\n self.db.delete(schedule)\n self.db.commit()\n\n self.event_log.log_event(\n job_id=job_id,\n event_type=\"SCHEDULE_DELETED\",\n payload={\"schedule_id\": schedule_id},\n created_by=self.current_user,\n )\n\n logger.reflect(\"Schedule deleted\", {\"schedule_id\": schedule_id, \"job_id\": job_id})\n # endregion delete_schedule\n\n # region enable_disable_schedule [TYPE Function]\n # @PURPOSE: Enable or disable a schedule.\n # @PRE: job_id has an existing schedule.\n # @POST: Schedule is_active updated.\n def set_schedule_active(self, job_id: str, is_active: bool) -> TranslationSchedule:\n with belief_scope(\"TranslationScheduler.set_schedule_active\"):\n schedule = self.db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n if not schedule:\n raise ValueError(f\"No schedule found for job '{job_id}'\")\n\n schedule.is_active = is_active\n schedule.updated_at = datetime.now(UTC)\n self.db.commit()\n self.db.refresh(schedule)\n\n logger.reflect(\"Schedule active state set\", {\n \"schedule_id\": schedule.id,\n \"job_id\": job_id,\n \"is_active\": is_active,\n })\n return schedule\n # endregion enable_disable_schedule\n\n # region get_schedule [TYPE Function]\n # @PURPOSE: Get schedule for a job.\n # @PRE: job_id exists.\n # @POST: Returns TranslationSchedule or raises ValueError.\n def get_schedule(self, job_id: str) -> TranslationSchedule:\n with belief_scope(\"TranslationScheduler.get_schedule\"):\n schedule = self.db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n if not schedule:\n raise ValueError(f\"No schedule found for job '{job_id}'\")\n return schedule\n # endregion get_schedule\n\n # region list_active_schedules [TYPE Function]\n # @PURPOSE: List all active schedules.\n # @POST: Returns list of active TranslationSchedule rows.\n @staticmethod\n def list_active_schedules(db: Session) -> list[TranslationSchedule]:\n return (\n db.query(TranslationSchedule)\n .filter(TranslationSchedule.is_active)\n .all()\n )\n # endregion list_active_schedules\n\n # region get_next_executions [TYPE Function]\n # @PURPOSE: Compute next N execution times from cron expression.\n # @PRE: cron_expression is valid.\n # @POST: Returns list of ISO datetime strings.\n @staticmethod\n def get_next_executions(cron_expression: str, timezone_str: str = \"UTC\", n: int = 3) -> list[str]:\n from zoneinfo import ZoneInfo\n\n from apscheduler.triggers.cron import CronTrigger\n\n try:\n tz = ZoneInfo(timezone_str)\n trigger = CronTrigger.from_crontab(cron_expression, timezone=tz)\n except (ValueError, KeyError) as e:\n logger.warning(f\"[get_next_executions] Invalid cron: {e}\")\n return []\n\n now = datetime.now(tz)\n results = []\n next_time = now\n prev = None\n for _ in range(n):\n ft = trigger.get_next_fire_time(prev, next_time)\n if ft is None:\n break\n results.append(ft.isoformat())\n prev = ft\n next_time = ft\n return results\n # endregion get_next_executions\n\n\n# #endregion TranslationScheduler\n\n\n# #region execute_scheduled_translation [TYPE Function]\n# @BRIEF APScheduler job handler — runs scheduled translation with concurrency check and new-key-only fallback.\n# @PRE: schedule_id is valid.\n# @POST: Translation run created and executed if no concurrent run exists.\n# @SIDE_EFFECT: DB writes; LLM calls; Superset API calls.\n# @RATIONALE: New-key-only mode compares keys against most recent succeeded run; baseline_expired fallback does full.\ndef execute_scheduled_translation(\n schedule_id: str,\n job_id: str,\n db_session_maker,\n config_manager: ConfigManager,\n execution_mode: str = \"full\",\n) -> None:\n \"\"\"APScheduler job callback for scheduled translations.\"\"\"\n seed_trace_id()\n db: Session = db_session_maker()\n try:\n with belief_scope(\"execute_scheduled_translation\"):\n # Load schedule\n schedule = db.query(TranslationSchedule).filter(\n TranslationSchedule.id == schedule_id,\n TranslationSchedule.job_id == job_id,\n ).first()\n if not schedule or not schedule.is_active:\n logger.info(f\"[scheduled_translation] Schedule inactive/missing: {schedule_id}\")\n return\n\n logger.reason(\"Scheduled translation triggered\", {\n \"schedule_id\": schedule_id,\n \"job_id\": job_id,\n })\n\n # Concurrency check: max 1 pending/running run per job\n stale_threshold = timedelta(hours=1)\n now = datetime.now(UTC)\n\n active_run = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.status.in_([\"PENDING\", \"RUNNING\"]),\n )\n .order_by(TranslationRun.created_at.asc()) # oldest first\n .first()\n )\n if active_run:\n is_stale = (\n active_run.status == \"PENDING\"\n and active_run.created_at\n and (now - active_run.created_at) > stale_threshold\n )\n if is_stale:\n # Mark ALL stale PENDING runs as FAILED\n stale_runs = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.status == \"PENDING\",\n TranslationRun.created_at < (now - stale_threshold),\n )\n .all()\n )\n for sr in stale_runs:\n sr.status = \"FAILED\"\n sr.error_message = \"Stale: concurrency deadlock — marked by scheduled trigger\"\n sr.completed_at = now\n db.commit()\n logger.explore(f\"Cleared {len(stale_runs)} stale PENDING run(s), proceeding with scheduled trigger\", {\n \"job_id\": job_id,\n \"stale_run_ids\": [sr.id for sr in stale_runs],\n })\n # Proceed to execute the scheduled run (do NOT return)\n else:\n logger.explore(\"Skipping scheduled run — concurrent run in progress\", {\n \"job_id\": job_id,\n \"existing_run_id\": active_run.id,\n })\n event_log = TranslationEventLog(db)\n event_log.log_event(\n job_id=job_id,\n event_type=\"RUN_STARTED\",\n payload={\"reason\": \"skipped_concurrent\", \"existing_run_id\": active_run.id},\n )\n return\n\n # Check baseline expiry\n baseline_expired = False\n most_recent = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.insert_status == \"succeeded\",\n )\n .order_by(TranslationRun.created_at.desc())\n .first()\n )\n if most_recent:\n age = datetime.now(UTC) - most_recent.created_at\n if age > timedelta(days=90):\n baseline_expired = True\n logger.reason(\"Baseline expired — full translation\", {\n \"job_id\": job_id,\n \"last_run\": most_recent.created_at.isoformat(),\n \"age_days\": age.days,\n })\n\n # Import and execute\n from .orchestrator import TranslationOrchestrator\n\n orch = TranslationOrchestrator(db, config_manager, current_user=\"scheduler\")\n run = orch.start_run(\n job_id=job_id,\n is_scheduled=True,\n )\n\n # Determine trigger type based on execution_mode and baseline expiry\n if execution_mode == \"new_key_only\" and not baseline_expired:\n run.trigger_type = \"new_key_only\"\n elif execution_mode == \"new_key_only\" and baseline_expired:\n logger.reason(\"Baseline expired — falling back to full translation\", {\n \"job_id\": job_id, \"schedule_id\": schedule_id, \"age_days\": age.days,\n })\n run.trigger_type = \"scheduled\"\n else:\n run.trigger_type = \"scheduled\"\n db.flush()\n\n if baseline_expired:\n event_log = TranslationEventLog(db)\n event_log.log_event(\n job_id=job_id,\n run_id=run.id,\n event_type=\"RUN_STARTED\",\n payload={\"reason\": \"baseline_expired\", \"age_days\": age.days},\n )\n\n # Execute in same thread (APScheduler runs in background thread pool)\n try:\n orch.execute_run(run)\n run.insert_status = run.insert_status or \"succeeded\"\n except Exception as exec_err:\n logger.explore(\"Scheduled translation execution failed\", {\n \"run_id\": run.id,\n \"error\": str(exec_err),\n })\n # Send notification on scheduled-run failure (FR-041, FR-048)\n try:\n notify_service = NotificationService(db, config_manager)\n notify_service._initialize_providers()\n subject = f\"Scheduled translation failed: job={job_id}\"\n body = (\n f\"Scheduled translation run failed for job '{job_id}'.\\n\"\n f\"Schedule: {schedule_id}\\n\"\n f\"Error: {exec_err}\\n\"\n f\"Time: {datetime.now(UTC).isoformat()}\"\n )\n import asyncio\n for provider in notify_service._providers.values():\n asyncio.run(provider.send(recipient=\"admin\", subject=subject, body=body))\n except Exception as notify_err:\n logger.warning(f\"Failed to send failure notification: {notify_err}\")\n\n # Leave schedule enabled — schedule continues on failure\n run.status = \"FAILED\"\n run.error_message = str(exec_err)\n run.completed_at = datetime.now(UTC)\n\n # Update schedule tracking\n schedule.last_run_at = datetime.now(UTC)\n db.commit()\n\n logger.reflect(\"Scheduled translation complete\", {\n \"schedule_id\": schedule_id,\n \"run_id\": run.id,\n \"status\": run.status,\n })\n except Exception as e:\n logger.error(f\"[scheduled_translation] Unexpected error: {e}\")\n try:\n notify_service = NotificationService(db, config_manager)\n notify_service._initialize_providers()\n subject = f\"Scheduled translation CRASHED: job={job_id}\"\n body = f\"Scheduled translation for job '{job_id}' crashed unexpectedly.\\nError: {e}\\nTime: {datetime.now(UTC).isoformat()}\"\n import asyncio\n for provider in notify_service._providers.values():\n asyncio.run(provider.send(recipient=\"admin\", subject=subject, body=body))\n except Exception:\n pass\n finally:\n db.close()\n# #endregion execute_scheduled_translation\n# #endregion TranslationScheduler\n" + "body": "# #region TranslationScheduler [C:4] [TYPE Module] [SEMANTICS sqlalchemy, translate, schedule, cron, job]\n# @BRIEF Manage TranslationSchedule rows and register them with core SchedulerService.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [TranslationSchedule:Class]\n# @RELATION DEPENDS_ON -> [SchedulerService:Class]\n# @RELATION DEPENDS_ON -> [TranslationOrchestrator:Class]\n# @RELATION DEPENDS_ON -> [TranslationEventLog:Class]\n# @PRE: Database session and SchedulerService are available.\n# @POST: TranslationSchedule CRUD persisted; APScheduler jobs registered/updated/removed.\n# @SIDE_EFFECT: Registers APScheduler jobs; runs translations on trigger; creates events.\n# @RATIONALE: Uses existing SchedulerService (APScheduler) to avoid creating a second scheduler instance.\n# @REJECTED: Separate scheduler instance would create resource contention.\n# @REJECTED: Polling-based approach — event-driven APScheduler is more precise.\n\nimport uuid\nfrom datetime import UTC, datetime, timedelta\n\nfrom sqlalchemy.orm import Session\n\nfrom ...core.config_manager import ConfigManager\nfrom ...core.cot_logger import seed_trace_id\nfrom ...core.logger import belief_scope, logger\nfrom ...models.translate import TranslationJob, TranslationRun, TranslationSchedule\nfrom ...services.notifications.service import NotificationService\nfrom .events import TranslationEventLog\n\n\n# #region TranslationScheduler [C:4] [TYPE Class]\n# @BRIEF CRUD for TranslationSchedule rows + APScheduler registration wrappers.\nclass TranslationScheduler:\n\n def __init__(self, db: Session, config_manager: ConfigManager, current_user: str | None = None):\n self.db = db\n self.config_manager = config_manager\n self.current_user = current_user\n self.event_log = TranslationEventLog(db)\n\n # region create_schedule [TYPE Function]\n # @PURPOSE: Create a new schedule for a job.\n # @PRE: job_id exists. cron_expression is valid.\n # @POST: TranslationSchedule row created.\n def create_schedule(\n self,\n job_id: str,\n cron_expression: str,\n timezone: str = \"UTC\",\n is_active: bool = True,\n execution_mode: str = \"full\",\n ) -> TranslationSchedule:\n with belief_scope(\"TranslationScheduler.create_schedule\"):\n # Verify job exists\n job = self.db.query(TranslationJob).filter(TranslationJob.id == job_id).first()\n if not job:\n raise ValueError(f\"Translation job '{job_id}' not found\")\n\n schedule = TranslationSchedule(\n id=str(uuid.uuid4()),\n job_id=job_id,\n cron_expression=cron_expression,\n timezone=timezone,\n is_active=is_active,\n execution_mode=execution_mode,\n created_by=self.current_user,\n )\n self.db.add(schedule)\n self.db.commit()\n self.db.refresh(schedule)\n\n self.event_log.log_event(\n job_id=job_id,\n event_type=\"SCHEDULE_CREATED\",\n payload={\n \"schedule_id\": schedule.id,\n \"cron_expression\": cron_expression,\n \"timezone\": timezone,\n },\n created_by=self.current_user,\n )\n\n logger.reflect(\"Schedule created\", {\"schedule_id\": schedule.id, \"job_id\": job_id})\n return schedule\n # endregion create_schedule\n\n # region update_schedule [TYPE Function]\n # @PURPOSE: Update an existing schedule.\n # @PRE: job_id has an existing schedule.\n # @POST: Schedule updated.\n def update_schedule(\n self,\n job_id: str,\n cron_expression: str | None = None,\n timezone_str: str | None = None,\n is_active: bool | None = None,\n execution_mode: str | None = None,\n ) -> TranslationSchedule:\n with belief_scope(\"TranslationScheduler.update_schedule\"):\n schedule = self.db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n if not schedule:\n raise ValueError(f\"No schedule found for job '{job_id}'\")\n\n if cron_expression is not None:\n schedule.cron_expression = cron_expression\n if timezone_str is not None:\n schedule.timezone = timezone_str\n if is_active is not None:\n schedule.is_active = is_active\n if execution_mode is not None:\n schedule.execution_mode = execution_mode\n schedule.updated_at = datetime.now(UTC)\n self.db.commit()\n self.db.refresh(schedule)\n\n self.event_log.log_event(\n job_id=job_id,\n event_type=\"SCHEDULE_UPDATED\",\n payload={\n \"schedule_id\": schedule.id,\n \"cron_expression\": schedule.cron_expression,\n \"timezone\": schedule.timezone,\n \"is_active\": schedule.is_active,\n },\n created_by=self.current_user,\n )\n\n logger.reflect(\"Schedule updated\", {\"schedule_id\": schedule.id, \"job_id\": job_id})\n return schedule\n # endregion update_schedule\n\n # region delete_schedule [TYPE Function]\n # @PURPOSE: Delete a schedule for a job.\n # @PRE: job_id has an existing schedule.\n # @POST: Schedule deleted.\n def delete_schedule(self, job_id: str) -> None:\n with belief_scope(\"TranslationScheduler.delete_schedule\"):\n schedule = self.db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n if not schedule:\n raise ValueError(f\"No schedule found for job '{job_id}'\")\n\n schedule_id = schedule.id\n self.db.delete(schedule)\n self.db.commit()\n\n self.event_log.log_event(\n job_id=job_id,\n event_type=\"SCHEDULE_DELETED\",\n payload={\"schedule_id\": schedule_id},\n created_by=self.current_user,\n )\n\n logger.reflect(\"Schedule deleted\", {\"schedule_id\": schedule_id, \"job_id\": job_id})\n # endregion delete_schedule\n\n # region enable_disable_schedule [TYPE Function]\n # @PURPOSE: Enable or disable a schedule.\n # @PRE: job_id has an existing schedule.\n # @POST: Schedule is_active updated.\n def set_schedule_active(self, job_id: str, is_active: bool) -> TranslationSchedule:\n with belief_scope(\"TranslationScheduler.set_schedule_active\"):\n schedule = self.db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n if not schedule:\n raise ValueError(f\"No schedule found for job '{job_id}'\")\n\n schedule.is_active = is_active\n schedule.updated_at = datetime.now(UTC)\n self.db.commit()\n self.db.refresh(schedule)\n\n logger.reflect(\"Schedule active state set\", {\n \"schedule_id\": schedule.id,\n \"job_id\": job_id,\n \"is_active\": is_active,\n })\n return schedule\n # endregion enable_disable_schedule\n\n # region get_schedule [TYPE Function]\n # @PURPOSE: Get schedule for a job.\n # @PRE: job_id exists.\n # @POST: Returns TranslationSchedule or raises ValueError.\n def get_schedule(self, job_id: str) -> TranslationSchedule:\n with belief_scope(\"TranslationScheduler.get_schedule\"):\n schedule = self.db.query(TranslationSchedule).filter(\n TranslationSchedule.job_id == job_id\n ).first()\n if not schedule:\n raise ValueError(f\"No schedule found for job '{job_id}'\")\n return schedule\n # endregion get_schedule\n\n # region list_active_schedules [TYPE Function]\n # @PURPOSE: List all active schedules.\n # @POST: Returns list of active TranslationSchedule rows.\n @staticmethod\n def list_active_schedules(db: Session) -> list[TranslationSchedule]:\n return (\n db.query(TranslationSchedule)\n .filter(TranslationSchedule.is_active)\n .all()\n )\n # endregion list_active_schedules\n\n # region get_next_executions [TYPE Function]\n # @PURPOSE: Compute next N execution times from cron expression.\n # @PRE: cron_expression is valid.\n # @POST: Returns list of ISO datetime strings.\n @staticmethod\n def get_next_executions(cron_expression: str, timezone_str: str = \"UTC\", n: int = 3) -> list[str]:\n from zoneinfo import ZoneInfo\n\n from apscheduler.triggers.cron import CronTrigger\n\n try:\n tz = ZoneInfo(timezone_str)\n trigger = CronTrigger.from_crontab(cron_expression, timezone=tz)\n except (ValueError, KeyError) as e:\n logger.warning(f\"[get_next_executions] Invalid cron: {e}\")\n return []\n\n now = datetime.now(tz)\n results = []\n next_time = now\n prev = None\n for _ in range(n):\n ft = trigger.get_next_fire_time(prev, next_time)\n if ft is None:\n break\n results.append(ft.isoformat())\n prev = ft\n next_time = ft\n return results\n # endregion get_next_executions\n\n\n# #endregion TranslationScheduler\n\n\n# #region execute_scheduled_translation [C:4] [TYPE Function]\n# @BRIEF APScheduler job handler — runs scheduled translation with concurrency check and new-key-only fallback.\n# @PRE schedule_id is valid.\n# @POST Translation run created and executed if no concurrent run exists.\n# @SIDE_EFFECT DB writes; LLM calls; Superset API calls.\n# @RATIONALE New-key-only mode compares keys against most recent succeeded run; baseline_expired fallback does full.\n# @COMPLEXITY 4\n\ndef execute_scheduled_translation(\n schedule_id: str,\n job_id: str,\n db_session_maker,\n config_manager: ConfigManager,\n execution_mode: str = \"full\",\n) -> None:\n \"\"\"APScheduler job callback for scheduled translations.\"\"\"\n seed_trace_id()\n db: Session = db_session_maker()\n try:\n with belief_scope(\"execute_scheduled_translation\"):\n # Load schedule\n schedule = db.query(TranslationSchedule).filter(\n TranslationSchedule.id == schedule_id,\n TranslationSchedule.job_id == job_id,\n ).first()\n if not schedule or not schedule.is_active:\n logger.info(f\"[scheduled_translation] Schedule inactive/missing: {schedule_id}\")\n return\n\n logger.reason(\"Scheduled translation triggered\", {\n \"schedule_id\": schedule_id,\n \"job_id\": job_id,\n })\n\n # Concurrency check: max 1 pending/running run per job\n stale_threshold = timedelta(hours=1)\n now = datetime.now(UTC)\n\n active_run = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.status.in_([\"PENDING\", \"RUNNING\"]),\n )\n .order_by(TranslationRun.created_at.asc()) # oldest first\n .first()\n )\n if active_run:\n is_stale = (\n active_run.status == \"PENDING\"\n and active_run.created_at\n and (now - active_run.created_at) > stale_threshold\n )\n if is_stale:\n # Mark ALL stale PENDING runs as FAILED\n stale_runs = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.status == \"PENDING\",\n TranslationRun.created_at < (now - stale_threshold),\n )\n .all()\n )\n for sr in stale_runs:\n sr.status = \"FAILED\"\n sr.error_message = \"Stale: concurrency deadlock — marked by scheduled trigger\"\n sr.completed_at = now\n db.commit()\n logger.explore(f\"Cleared {len(stale_runs)} stale PENDING run(s), proceeding with scheduled trigger\", {\n \"job_id\": job_id,\n \"stale_run_ids\": [sr.id for sr in stale_runs],\n })\n # Proceed to execute the scheduled run (do NOT return)\n else:\n logger.explore(\"Skipping scheduled run — concurrent run in progress\", {\n \"job_id\": job_id,\n \"existing_run_id\": active_run.id,\n })\n event_log = TranslationEventLog(db)\n event_log.log_event(\n job_id=job_id,\n event_type=\"RUN_STARTED\",\n payload={\"reason\": \"skipped_concurrent\", \"existing_run_id\": active_run.id},\n )\n return\n\n # Check baseline expiry\n baseline_expired = False\n most_recent = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.insert_status == \"succeeded\",\n )\n .order_by(TranslationRun.created_at.desc())\n .first()\n )\n if most_recent:\n age = datetime.now(UTC) - most_recent.created_at\n if age > timedelta(days=90):\n baseline_expired = True\n logger.reason(\"Baseline expired — full translation\", {\n \"job_id\": job_id,\n \"last_run\": most_recent.created_at.isoformat(),\n \"age_days\": age.days,\n })\n\n # Import and execute\n from .orchestrator import TranslationOrchestrator\n\n orch = TranslationOrchestrator(db, config_manager, current_user=\"scheduler\")\n run = orch.start_run(\n job_id=job_id,\n is_scheduled=True,\n )\n\n # Determine trigger type based on execution_mode and baseline expiry\n if execution_mode == \"new_key_only\" and not baseline_expired:\n run.trigger_type = \"new_key_only\"\n elif execution_mode == \"new_key_only\" and baseline_expired:\n logger.reason(\"Baseline expired — falling back to full translation\", {\n \"job_id\": job_id, \"schedule_id\": schedule_id, \"age_days\": age.days,\n })\n run.trigger_type = \"scheduled\"\n else:\n run.trigger_type = \"scheduled\"\n db.flush()\n\n if baseline_expired:\n event_log = TranslationEventLog(db)\n event_log.log_event(\n job_id=job_id,\n run_id=run.id,\n event_type=\"RUN_STARTED\",\n payload={\"reason\": \"baseline_expired\", \"age_days\": age.days},\n )\n\n # Execute in same thread (APScheduler runs in background thread pool)\n try:\n orch.execute_run(run)\n run.insert_status = run.insert_status or \"succeeded\"\n except Exception as exec_err:\n logger.explore(\"Scheduled translation execution failed\", {\n \"run_id\": run.id,\n \"error\": str(exec_err),\n })\n # Send notification on scheduled-run failure (FR-041, FR-048)\n try:\n notify_service = NotificationService(db, config_manager)\n notify_service._initialize_providers()\n subject = f\"Scheduled translation failed: job={job_id}\"\n body = (\n f\"Scheduled translation run failed for job '{job_id}'.\\n\"\n f\"Schedule: {schedule_id}\\n\"\n f\"Error: {exec_err}\\n\"\n f\"Time: {datetime.now(UTC).isoformat()}\"\n )\n import asyncio\n for provider in notify_service._providers.values():\n asyncio.run(provider.send(recipient=\"admin\", subject=subject, body=body))\n except Exception as notify_err:\n logger.warning(f\"Failed to send failure notification: {notify_err}\")\n\n # Leave schedule enabled — schedule continues on failure\n run.status = \"FAILED\"\n run.error_message = str(exec_err)\n run.completed_at = datetime.now(UTC)\n\n # Update schedule tracking\n schedule.last_run_at = datetime.now(UTC)\n db.commit()\n\n logger.reflect(\"Scheduled translation complete\", {\n \"schedule_id\": schedule_id,\n \"run_id\": run.id,\n \"status\": run.status,\n })\n except Exception as e:\n logger.error(f\"[scheduled_translation] Unexpected error: {e}\")\n try:\n notify_service = NotificationService(db, config_manager)\n notify_service._initialize_providers()\n subject = f\"Scheduled translation CRASHED: job={job_id}\"\n body = f\"Scheduled translation for job '{job_id}' crashed unexpectedly.\\nError: {e}\\nTime: {datetime.now(UTC).isoformat()}\"\n import asyncio\n for provider in notify_service._providers.values():\n asyncio.run(provider.send(recipient=\"admin\", subject=subject, body=body))\n except Exception:\n pass\n finally:\n db.close()\n# #endregion execute_scheduled_translation\n# #endregion TranslationScheduler\n" }, { "contract_id": "execute_scheduled_translation", "contract_type": "Function", "file_path": "backend/src/plugins/translate/scheduler.py", "start_line": 243, - "end_line": 433, - "tier": "TIER_1", - "complexity": 1, + "end_line": 435, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "Translation run created and executed if no concurrent run exists.", "PRE": "schedule_id is valid.", "PURPOSE": "APScheduler job handler — runs scheduled translation with concurrency check and new-key-only fallback.", @@ -50287,36 +49567,18 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "SIDE_EFFECT", - "message": "@SIDE_EFFECT is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region execute_scheduled_translation [TYPE Function]\n# @BRIEF APScheduler job handler — runs scheduled translation with concurrency check and new-key-only fallback.\n# @PRE: schedule_id is valid.\n# @POST: Translation run created and executed if no concurrent run exists.\n# @SIDE_EFFECT: DB writes; LLM calls; Superset API calls.\n# @RATIONALE: New-key-only mode compares keys against most recent succeeded run; baseline_expired fallback does full.\ndef execute_scheduled_translation(\n schedule_id: str,\n job_id: str,\n db_session_maker,\n config_manager: ConfigManager,\n execution_mode: str = \"full\",\n) -> None:\n \"\"\"APScheduler job callback for scheduled translations.\"\"\"\n seed_trace_id()\n db: Session = db_session_maker()\n try:\n with belief_scope(\"execute_scheduled_translation\"):\n # Load schedule\n schedule = db.query(TranslationSchedule).filter(\n TranslationSchedule.id == schedule_id,\n TranslationSchedule.job_id == job_id,\n ).first()\n if not schedule or not schedule.is_active:\n logger.info(f\"[scheduled_translation] Schedule inactive/missing: {schedule_id}\")\n return\n\n logger.reason(\"Scheduled translation triggered\", {\n \"schedule_id\": schedule_id,\n \"job_id\": job_id,\n })\n\n # Concurrency check: max 1 pending/running run per job\n stale_threshold = timedelta(hours=1)\n now = datetime.now(UTC)\n\n active_run = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.status.in_([\"PENDING\", \"RUNNING\"]),\n )\n .order_by(TranslationRun.created_at.asc()) # oldest first\n .first()\n )\n if active_run:\n is_stale = (\n active_run.status == \"PENDING\"\n and active_run.created_at\n and (now - active_run.created_at) > stale_threshold\n )\n if is_stale:\n # Mark ALL stale PENDING runs as FAILED\n stale_runs = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.status == \"PENDING\",\n TranslationRun.created_at < (now - stale_threshold),\n )\n .all()\n )\n for sr in stale_runs:\n sr.status = \"FAILED\"\n sr.error_message = \"Stale: concurrency deadlock — marked by scheduled trigger\"\n sr.completed_at = now\n db.commit()\n logger.explore(f\"Cleared {len(stale_runs)} stale PENDING run(s), proceeding with scheduled trigger\", {\n \"job_id\": job_id,\n \"stale_run_ids\": [sr.id for sr in stale_runs],\n })\n # Proceed to execute the scheduled run (do NOT return)\n else:\n logger.explore(\"Skipping scheduled run — concurrent run in progress\", {\n \"job_id\": job_id,\n \"existing_run_id\": active_run.id,\n })\n event_log = TranslationEventLog(db)\n event_log.log_event(\n job_id=job_id,\n event_type=\"RUN_STARTED\",\n payload={\"reason\": \"skipped_concurrent\", \"existing_run_id\": active_run.id},\n )\n return\n\n # Check baseline expiry\n baseline_expired = False\n most_recent = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.insert_status == \"succeeded\",\n )\n .order_by(TranslationRun.created_at.desc())\n .first()\n )\n if most_recent:\n age = datetime.now(UTC) - most_recent.created_at\n if age > timedelta(days=90):\n baseline_expired = True\n logger.reason(\"Baseline expired — full translation\", {\n \"job_id\": job_id,\n \"last_run\": most_recent.created_at.isoformat(),\n \"age_days\": age.days,\n })\n\n # Import and execute\n from .orchestrator import TranslationOrchestrator\n\n orch = TranslationOrchestrator(db, config_manager, current_user=\"scheduler\")\n run = orch.start_run(\n job_id=job_id,\n is_scheduled=True,\n )\n\n # Determine trigger type based on execution_mode and baseline expiry\n if execution_mode == \"new_key_only\" and not baseline_expired:\n run.trigger_type = \"new_key_only\"\n elif execution_mode == \"new_key_only\" and baseline_expired:\n logger.reason(\"Baseline expired — falling back to full translation\", {\n \"job_id\": job_id, \"schedule_id\": schedule_id, \"age_days\": age.days,\n })\n run.trigger_type = \"scheduled\"\n else:\n run.trigger_type = \"scheduled\"\n db.flush()\n\n if baseline_expired:\n event_log = TranslationEventLog(db)\n event_log.log_event(\n job_id=job_id,\n run_id=run.id,\n event_type=\"RUN_STARTED\",\n payload={\"reason\": \"baseline_expired\", \"age_days\": age.days},\n )\n\n # Execute in same thread (APScheduler runs in background thread pool)\n try:\n orch.execute_run(run)\n run.insert_status = run.insert_status or \"succeeded\"\n except Exception as exec_err:\n logger.explore(\"Scheduled translation execution failed\", {\n \"run_id\": run.id,\n \"error\": str(exec_err),\n })\n # Send notification on scheduled-run failure (FR-041, FR-048)\n try:\n notify_service = NotificationService(db, config_manager)\n notify_service._initialize_providers()\n subject = f\"Scheduled translation failed: job={job_id}\"\n body = (\n f\"Scheduled translation run failed for job '{job_id}'.\\n\"\n f\"Schedule: {schedule_id}\\n\"\n f\"Error: {exec_err}\\n\"\n f\"Time: {datetime.now(UTC).isoformat()}\"\n )\n import asyncio\n for provider in notify_service._providers.values():\n asyncio.run(provider.send(recipient=\"admin\", subject=subject, body=body))\n except Exception as notify_err:\n logger.warning(f\"Failed to send failure notification: {notify_err}\")\n\n # Leave schedule enabled — schedule continues on failure\n run.status = \"FAILED\"\n run.error_message = str(exec_err)\n run.completed_at = datetime.now(UTC)\n\n # Update schedule tracking\n schedule.last_run_at = datetime.now(UTC)\n db.commit()\n\n logger.reflect(\"Scheduled translation complete\", {\n \"schedule_id\": schedule_id,\n \"run_id\": run.id,\n \"status\": run.status,\n })\n except Exception as e:\n logger.error(f\"[scheduled_translation] Unexpected error: {e}\")\n try:\n notify_service = NotificationService(db, config_manager)\n notify_service._initialize_providers()\n subject = f\"Scheduled translation CRASHED: job={job_id}\"\n body = f\"Scheduled translation for job '{job_id}' crashed unexpectedly.\\nError: {e}\\nTime: {datetime.now(UTC).isoformat()}\"\n import asyncio\n for provider in notify_service._providers.values():\n asyncio.run(provider.send(recipient=\"admin\", subject=subject, body=body))\n except Exception:\n pass\n finally:\n db.close()\n# #endregion execute_scheduled_translation\n" + "body": "# #region execute_scheduled_translation [C:4] [TYPE Function]\n# @BRIEF APScheduler job handler — runs scheduled translation with concurrency check and new-key-only fallback.\n# @PRE schedule_id is valid.\n# @POST Translation run created and executed if no concurrent run exists.\n# @SIDE_EFFECT DB writes; LLM calls; Superset API calls.\n# @RATIONALE New-key-only mode compares keys against most recent succeeded run; baseline_expired fallback does full.\n# @COMPLEXITY 4\n\ndef execute_scheduled_translation(\n schedule_id: str,\n job_id: str,\n db_session_maker,\n config_manager: ConfigManager,\n execution_mode: str = \"full\",\n) -> None:\n \"\"\"APScheduler job callback for scheduled translations.\"\"\"\n seed_trace_id()\n db: Session = db_session_maker()\n try:\n with belief_scope(\"execute_scheduled_translation\"):\n # Load schedule\n schedule = db.query(TranslationSchedule).filter(\n TranslationSchedule.id == schedule_id,\n TranslationSchedule.job_id == job_id,\n ).first()\n if not schedule or not schedule.is_active:\n logger.info(f\"[scheduled_translation] Schedule inactive/missing: {schedule_id}\")\n return\n\n logger.reason(\"Scheduled translation triggered\", {\n \"schedule_id\": schedule_id,\n \"job_id\": job_id,\n })\n\n # Concurrency check: max 1 pending/running run per job\n stale_threshold = timedelta(hours=1)\n now = datetime.now(UTC)\n\n active_run = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.status.in_([\"PENDING\", \"RUNNING\"]),\n )\n .order_by(TranslationRun.created_at.asc()) # oldest first\n .first()\n )\n if active_run:\n is_stale = (\n active_run.status == \"PENDING\"\n and active_run.created_at\n and (now - active_run.created_at) > stale_threshold\n )\n if is_stale:\n # Mark ALL stale PENDING runs as FAILED\n stale_runs = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.status == \"PENDING\",\n TranslationRun.created_at < (now - stale_threshold),\n )\n .all()\n )\n for sr in stale_runs:\n sr.status = \"FAILED\"\n sr.error_message = \"Stale: concurrency deadlock — marked by scheduled trigger\"\n sr.completed_at = now\n db.commit()\n logger.explore(f\"Cleared {len(stale_runs)} stale PENDING run(s), proceeding with scheduled trigger\", {\n \"job_id\": job_id,\n \"stale_run_ids\": [sr.id for sr in stale_runs],\n })\n # Proceed to execute the scheduled run (do NOT return)\n else:\n logger.explore(\"Skipping scheduled run — concurrent run in progress\", {\n \"job_id\": job_id,\n \"existing_run_id\": active_run.id,\n })\n event_log = TranslationEventLog(db)\n event_log.log_event(\n job_id=job_id,\n event_type=\"RUN_STARTED\",\n payload={\"reason\": \"skipped_concurrent\", \"existing_run_id\": active_run.id},\n )\n return\n\n # Check baseline expiry\n baseline_expired = False\n most_recent = (\n db.query(TranslationRun)\n .filter(\n TranslationRun.job_id == job_id,\n TranslationRun.insert_status == \"succeeded\",\n )\n .order_by(TranslationRun.created_at.desc())\n .first()\n )\n if most_recent:\n age = datetime.now(UTC) - most_recent.created_at\n if age > timedelta(days=90):\n baseline_expired = True\n logger.reason(\"Baseline expired — full translation\", {\n \"job_id\": job_id,\n \"last_run\": most_recent.created_at.isoformat(),\n \"age_days\": age.days,\n })\n\n # Import and execute\n from .orchestrator import TranslationOrchestrator\n\n orch = TranslationOrchestrator(db, config_manager, current_user=\"scheduler\")\n run = orch.start_run(\n job_id=job_id,\n is_scheduled=True,\n )\n\n # Determine trigger type based on execution_mode and baseline expiry\n if execution_mode == \"new_key_only\" and not baseline_expired:\n run.trigger_type = \"new_key_only\"\n elif execution_mode == \"new_key_only\" and baseline_expired:\n logger.reason(\"Baseline expired — falling back to full translation\", {\n \"job_id\": job_id, \"schedule_id\": schedule_id, \"age_days\": age.days,\n })\n run.trigger_type = \"scheduled\"\n else:\n run.trigger_type = \"scheduled\"\n db.flush()\n\n if baseline_expired:\n event_log = TranslationEventLog(db)\n event_log.log_event(\n job_id=job_id,\n run_id=run.id,\n event_type=\"RUN_STARTED\",\n payload={\"reason\": \"baseline_expired\", \"age_days\": age.days},\n )\n\n # Execute in same thread (APScheduler runs in background thread pool)\n try:\n orch.execute_run(run)\n run.insert_status = run.insert_status or \"succeeded\"\n except Exception as exec_err:\n logger.explore(\"Scheduled translation execution failed\", {\n \"run_id\": run.id,\n \"error\": str(exec_err),\n })\n # Send notification on scheduled-run failure (FR-041, FR-048)\n try:\n notify_service = NotificationService(db, config_manager)\n notify_service._initialize_providers()\n subject = f\"Scheduled translation failed: job={job_id}\"\n body = (\n f\"Scheduled translation run failed for job '{job_id}'.\\n\"\n f\"Schedule: {schedule_id}\\n\"\n f\"Error: {exec_err}\\n\"\n f\"Time: {datetime.now(UTC).isoformat()}\"\n )\n import asyncio\n for provider in notify_service._providers.values():\n asyncio.run(provider.send(recipient=\"admin\", subject=subject, body=body))\n except Exception as notify_err:\n logger.warning(f\"Failed to send failure notification: {notify_err}\")\n\n # Leave schedule enabled — schedule continues on failure\n run.status = \"FAILED\"\n run.error_message = str(exec_err)\n run.completed_at = datetime.now(UTC)\n\n # Update schedule tracking\n schedule.last_run_at = datetime.now(UTC)\n db.commit()\n\n logger.reflect(\"Scheduled translation complete\", {\n \"schedule_id\": schedule_id,\n \"run_id\": run.id,\n \"status\": run.status,\n })\n except Exception as e:\n logger.error(f\"[scheduled_translation] Unexpected error: {e}\")\n try:\n notify_service = NotificationService(db, config_manager)\n notify_service._initialize_providers()\n subject = f\"Scheduled translation CRASHED: job={job_id}\"\n body = f\"Scheduled translation for job '{job_id}' crashed unexpectedly.\\nError: {e}\\nTime: {datetime.now(UTC).isoformat()}\"\n import asyncio\n for provider in notify_service._providers.values():\n asyncio.run(provider.send(recipient=\"admin\", subject=subject, body=body))\n except Exception:\n pass\n finally:\n db.close()\n# #endregion execute_scheduled_translation\n" }, { "contract_id": "get_dialect_from_database", @@ -50324,9 +49586,10 @@ "file_path": "backend/src/plugins/translate/service.py", "start_line": 41, "end_line": 84, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns normalized dialect string or raises ValueError if unsupported.", "PRE": "database_record is a dict from Superset API.", "PURPOSE": "Extract normalized dialect string from a Superset database record." @@ -50334,27 +49597,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region get_dialect_from_database [TYPE Function]\n# @BRIEF Extract normalized dialect string from a Superset database record.\n# @PRE: database_record is a dict from Superset API.\n# @POST: Returns normalized dialect string or raises ValueError if unsupported.\ndef get_dialect_from_database(database_record: dict[str, Any]) -> str:\n \"\"\"Extract and validate dialect from Superset database record.\"\"\"\n backend = (\n database_record.get(\"backend\")\n or database_record.get(\"engine\")\n or \"\"\n ).lower().strip()\n\n if not backend:\n raise ValueError(\"Could not determine database dialect from connection\")\n\n # Map Superset backend names to normalized dialect\n dialect_map = {\n \"postgresql\": \"postgresql\",\n \"greenplum\": \"postgresql\",\n \"mysql\": \"mysql\",\n \"clickhouse\": \"clickhouse\",\n \"clickhousedb\": \"clickhouse\",\n \"sqlite\": \"sqlite\",\n \"mssql\": \"mssql\",\n \"oracle\": \"oracle\",\n \"snowflake\": \"snowflake\",\n \"bigquery\": \"bigquery\",\n \"redshift\": \"redshift\",\n \"presto\": \"presto\",\n \"trino\": \"trino\",\n \"druid\": \"druid\",\n \"hive\": \"hive\",\n \"spark\": \"spark\",\n \"databricks\": \"databricks\",\n }\n\n normalized = dialect_map.get(backend, backend)\n if normalized not in SUPPORTED_DIALECTS:\n raise ValueError(\n f\"Unsupported database dialect: '{backend}'. \"\n f\"Supported dialects: {', '.join(sorted(SUPPORTED_DIALECTS))}\"\n )\n return normalized\n# #endregion get_dialect_from_database\n" + "body": "# #region get_dialect_from_database [C:4] [TYPE Function]\n# @BRIEF Extract normalized dialect string from a Superset database record.\n# @PRE: database_record is a dict from Superset API.\n# @POST: Returns normalized dialect string or raises ValueError if unsupported.\ndef get_dialect_from_database(database_record: dict[str, Any]) -> str:\n \"\"\"Extract and validate dialect from Superset database record.\"\"\"\n backend = (\n database_record.get(\"backend\")\n or database_record.get(\"engine\")\n or \"\"\n ).lower().strip()\n\n if not backend:\n raise ValueError(\"Could not determine database dialect from connection\")\n\n # Map Superset backend names to normalized dialect\n dialect_map = {\n \"postgresql\": \"postgresql\",\n \"greenplum\": \"postgresql\",\n \"mysql\": \"mysql\",\n \"clickhouse\": \"clickhouse\",\n \"clickhousedb\": \"clickhouse\",\n \"sqlite\": \"sqlite\",\n \"mssql\": \"mssql\",\n \"oracle\": \"oracle\",\n \"snowflake\": \"snowflake\",\n \"bigquery\": \"bigquery\",\n \"redshift\": \"redshift\",\n \"presto\": \"presto\",\n \"trino\": \"trino\",\n \"druid\": \"druid\",\n \"hive\": \"hive\",\n \"spark\": \"spark\",\n \"databricks\": \"databricks\",\n }\n\n normalized = dialect_map.get(backend, backend)\n if normalized not in SUPPORTED_DIALECTS:\n raise ValueError(\n f\"Unsupported database dialect: '{backend}'. \"\n f\"Supported dialects: {', '.join(sorted(SUPPORTED_DIALECTS))}\"\n )\n return normalized\n# #endregion get_dialect_from_database\n" }, { "contract_id": "fetch_datasource_metadata", @@ -50362,9 +49625,10 @@ "file_path": "backend/src/plugins/translate/service.py", "start_line": 87, "end_line": 141, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns (columns_list, dialect_string) or raises on failure.", "PRE": "dataset_id is a valid Superset dataset ID and environment has valid credentials.", "PURPOSE": "Fetch datasource columns and database dialect from Superset." @@ -50372,27 +49636,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region fetch_datasource_metadata [TYPE Function]\n# @BRIEF Fetch datasource columns and database dialect from Superset.\n# @PRE: dataset_id is a valid Superset dataset ID and environment has valid credentials.\n# @POST: Returns (columns_list, dialect_string) or raises on failure.\ndef fetch_datasource_metadata(\n dataset_id: int,\n env_id: str,\n config_manager: ConfigManager,\n) -> tuple[list[dict[str, Any]], str]:\n \"\"\"Fetch column metadata and database dialect for a datasource from Superset.\"\"\"\n # Find environment config\n environments = config_manager.get_environments()\n env_config = next((e for e in environments if e.id == env_id), None)\n if not env_config:\n raise ValueError(f\"Superset environment '{env_id}' not found in configuration\")\n\n # Create Superset client and fetch dataset detail\n client = SupersetClient(env_config)\n dataset_detail = client.get_dataset_detail(dataset_id)\n\n # Extract columns\n raw_columns = dataset_detail.get(\"columns\", [])\n columns = []\n for col in raw_columns:\n col_name = col.get(\"name\") or col.get(\"column_name\")\n if not col_name:\n continue\n columns.append({\n \"name\": str(col_name),\n \"type\": col.get(\"type\"),\n \"is_physical\": col.get(\"is_physical\", True),\n \"is_dttm\": col.get(\"is_dttm\", False),\n \"description\": col.get(\"description\", \"\"),\n })\n\n # Extract database dialect from datasource\n database_info = dataset_detail.get(\"database\", {})\n if isinstance(database_info, dict):\n dialect = get_dialect_from_database(database_info)\n else:\n # Fallback: try to fetch database directly\n try:\n db_id = dataset_detail.get(\"database_id\")\n if db_id:\n db_record = client.get_database(int(db_id))\n db_result = db_record.get(\"result\", db_record) if isinstance(db_record, dict) else db_record\n dialect = get_dialect_from_database(db_result)\n else:\n raise ValueError(\"No database information available for this datasource\")\n except Exception as e:\n logger.warning(f\"[translate_service] Could not fetch database dialect: {e}\")\n raise ValueError(f\"Could not determine database dialect: {e}\")\n\n return columns, dialect\n# #endregion fetch_datasource_metadata\n" + "body": "# #region fetch_datasource_metadata [C:4] [TYPE Function]\n# @BRIEF Fetch datasource columns and database dialect from Superset.\n# @PRE: dataset_id is a valid Superset dataset ID and environment has valid credentials.\n# @POST: Returns (columns_list, dialect_string) or raises on failure.\ndef fetch_datasource_metadata(\n dataset_id: int,\n env_id: str,\n config_manager: ConfigManager,\n) -> tuple[list[dict[str, Any]], str]:\n \"\"\"Fetch column metadata and database dialect for a datasource from Superset.\"\"\"\n # Find environment config\n environments = config_manager.get_environments()\n env_config = next((e for e in environments if e.id == env_id), None)\n if not env_config:\n raise ValueError(f\"Superset environment '{env_id}' not found in configuration\")\n\n # Create Superset client and fetch dataset detail\n client = SupersetClient(env_config)\n dataset_detail = client.get_dataset_detail(dataset_id)\n\n # Extract columns\n raw_columns = dataset_detail.get(\"columns\", [])\n columns = []\n for col in raw_columns:\n col_name = col.get(\"name\") or col.get(\"column_name\")\n if not col_name:\n continue\n columns.append({\n \"name\": str(col_name),\n \"type\": col.get(\"type\"),\n \"is_physical\": col.get(\"is_physical\", True),\n \"is_dttm\": col.get(\"is_dttm\", False),\n \"description\": col.get(\"description\", \"\"),\n })\n\n # Extract database dialect from datasource\n database_info = dataset_detail.get(\"database\", {})\n if isinstance(database_info, dict):\n dialect = get_dialect_from_database(database_info)\n else:\n # Fallback: try to fetch database directly\n try:\n db_id = dataset_detail.get(\"database_id\")\n if db_id:\n db_record = client.get_database(int(db_id))\n db_result = db_record.get(\"result\", db_record) if isinstance(db_record, dict) else db_record\n dialect = get_dialect_from_database(db_result)\n else:\n raise ValueError(\"No database information available for this datasource\")\n except Exception as e:\n logger.warning(f\"[translate_service] Could not fetch database dialect: {e}\")\n raise ValueError(f\"Could not determine database dialect: {e}\")\n\n return columns, dialect\n# #endregion fetch_datasource_metadata\n" }, { "contract_id": "detect_virtual_columns", @@ -50400,9 +49664,10 @@ "file_path": "backend/src/plugins/translate/service.py", "start_line": 144, "end_line": 151, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns list of virtual column names.", "PRE": "columns is a list of dicts with 'is_physical' key.", "PURPOSE": "Identify virtual (calculated) columns from column metadata." @@ -50410,27 +49675,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region detect_virtual_columns [TYPE Function]\n# @BRIEF Identify virtual (calculated) columns from column metadata.\n# @PRE: columns is a list of dicts with 'is_physical' key.\n# @POST: Returns list of virtual column names.\ndef detect_virtual_columns(columns: list[dict[str, Any]]) -> list[str]:\n \"\"\"Return names of columns that are virtual (not physical).\"\"\"\n return [col[\"name\"] for col in columns if not col.get(\"is_physical\", True)]\n# #endregion detect_virtual_columns\n" + "body": "# #region detect_virtual_columns [C:4] [TYPE Function]\n# @BRIEF Identify virtual (calculated) columns from column metadata.\n# @PRE: columns is a list of dicts with 'is_physical' key.\n# @POST: Returns list of virtual column names.\ndef detect_virtual_columns(columns: list[dict[str, Any]]) -> list[str]:\n \"\"\"Return names of columns that are virtual (not physical).\"\"\"\n return [col[\"name\"] for col in columns if not col.get(\"is_physical\", True)]\n# #endregion detect_virtual_columns\n" }, { "contract_id": "TranslateJobService", @@ -50471,10 +49736,11 @@ "contract_type": "Function", "file_path": "backend/src/plugins/translate/service.py", "start_line": 522, - "end_line": 584, - "tier": "TIER_1", - "complexity": 1, + "end_line": 586, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "Returns DatasourceColumnsResponse with column metadata and database dialect.", "PRE": "datasource_id is a valid Superset dataset ID.", "PURPOSE": "Fetch datasource column metadata from Superset and return structured response.", @@ -50483,43 +49749,25 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "SIDE_EFFECT", - "message": "@SIDE_EFFECT is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region DatasourceColumnsService [TYPE Function]\n# @BRIEF Fetch datasource column metadata from Superset and return structured response.\n# @PRE: datasource_id is a valid Superset dataset ID.\n# @POST: Returns DatasourceColumnsResponse with column metadata and database dialect.\n# @SIDE_EFFECT: Queries Superset API for dataset detail and database info.\ndef get_datasource_columns(\n datasource_id: int,\n env_id: str,\n config_manager: ConfigManager,\n) -> DatasourceColumnsResponse:\n \"\"\"Fetch and return column metadata for a given Superset datasource.\"\"\"\n logger.info(f\"[get_datasource_columns] Fetching columns for datasource {datasource_id}\")\n\n # Find environment config\n environments = config_manager.get_environments()\n env_config = next((e for e in environments if e.id == env_id), None)\n if not env_config:\n raise ValueError(f\"Superset environment '{env_id}' not found\")\n\n # Create Superset client\n client = SupersetClient(env_config)\n dataset_detail = client.get_dataset_detail(datasource_id)\n\n # Extract database dialect\n database_info = dataset_detail.get(\"database\", {})\n dialect = None\n if isinstance(database_info, dict):\n try:\n dialect = get_dialect_from_database(database_info)\n except (ValueError, KeyError):\n dialect = None\n if dialect is None:\n database_id = dataset_detail.get(\"database_id\")\n if database_id:\n db_record = client.get_database(int(database_id))\n db_result = db_record.get(\"result\", db_record) if isinstance(db_record, dict) else db_record\n dialect = get_dialect_from_database(db_result)\n else:\n raise ValueError(\"Could not determine database dialect for this datasource\")\n\n # Extract columns\n raw_columns = dataset_detail.get(\"columns\", [])\n columns = []\n for col in raw_columns:\n col_name = col.get(\"name\") or col.get(\"column_name\")\n if not col_name:\n continue\n columns.append(DatasourceColumnResponse(\n name=str(col_name),\n type=col.get(\"type\"),\n is_physical=col.get(\"is_physical\", True),\n is_dttm=col.get(\"is_dttm\", False),\n description=col.get(\"description\", \"\"),\n ))\n\n return DatasourceColumnsResponse(\n datasource_id=datasource_id,\n datasource_name=dataset_detail.get(\"table_name\"),\n schema_name=dataset_detail.get(\"schema\"),\n database_dialect=dialect,\n columns=columns,\n )\n# #endregion DatasourceColumnsService\n" + "body": "# #region DatasourceColumnsService [C:4] [TYPE Function]\n# @BRIEF Fetch datasource column metadata from Superset and return structured response.\n# @PRE datasource_id is a valid Superset dataset ID.\n# @POST Returns DatasourceColumnsResponse with column metadata and database dialect.\n# @SIDE_EFFECT Queries Superset API for dataset detail and database info.\n# @COMPLEXITY 4\n\ndef get_datasource_columns(\n datasource_id: int,\n env_id: str,\n config_manager: ConfigManager,\n) -> DatasourceColumnsResponse:\n \"\"\"Fetch and return column metadata for a given Superset datasource.\"\"\"\n logger.info(f\"[get_datasource_columns] Fetching columns for datasource {datasource_id}\")\n\n # Find environment config\n environments = config_manager.get_environments()\n env_config = next((e for e in environments if e.id == env_id), None)\n if not env_config:\n raise ValueError(f\"Superset environment '{env_id}' not found\")\n\n # Create Superset client\n client = SupersetClient(env_config)\n dataset_detail = client.get_dataset_detail(datasource_id)\n\n # Extract database dialect\n database_info = dataset_detail.get(\"database\", {})\n dialect = None\n if isinstance(database_info, dict):\n try:\n dialect = get_dialect_from_database(database_info)\n except (ValueError, KeyError):\n dialect = None\n if dialect is None:\n database_id = dataset_detail.get(\"database_id\")\n if database_id:\n db_record = client.get_database(int(database_id))\n db_result = db_record.get(\"result\", db_record) if isinstance(db_record, dict) else db_record\n dialect = get_dialect_from_database(db_result)\n else:\n raise ValueError(\"Could not determine database dialect for this datasource\")\n\n # Extract columns\n raw_columns = dataset_detail.get(\"columns\", [])\n columns = []\n for col in raw_columns:\n col_name = col.get(\"name\") or col.get(\"column_name\")\n if not col_name:\n continue\n columns.append(DatasourceColumnResponse(\n name=str(col_name),\n type=col.get(\"type\"),\n is_physical=col.get(\"is_physical\", True),\n is_dttm=col.get(\"is_dttm\", False),\n description=col.get(\"description\", \"\"),\n ))\n\n return DatasourceColumnsResponse(\n datasource_id=datasource_id,\n datasource_name=dataset_detail.get(\"table_name\"),\n schema_name=dataset_detail.get(\"schema\"),\n database_dialect=dialect,\n columns=columns,\n )\n# #endregion DatasourceColumnsService\n" }, { "contract_id": "InlineCorrectionService", "contract_type": "Class", "file_path": "backend/src/plugins/translate/service.py", - "start_line": 587, - "end_line": 826, + "start_line": 589, + "end_line": 828, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -50546,8 +49794,8 @@ "contract_id": "BulkFindReplaceService", "contract_type": "Class", "file_path": "backend/src/plugins/translate/service.py", - "start_line": 829, - "end_line": 1046, + "start_line": 831, + "end_line": 1048, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -50575,7 +49823,7 @@ "contract_type": "Module", "file_path": "backend/src/plugins/translate/sql_generator.py", "start_line": 1, - "end_line": 376, + "end_line": 378, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -50633,7 +49881,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region SQLGenerator [C:3] [TYPE Module] [SEMANTICS clickhouse, translate, sql, insert, generate]\n# @BRIEF Dialect-aware safe SQL generation for INSERT/UPSERT operations.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [TranslationJob]\n# @RELATION DEPENDS_ON -> [TranslationRun]\n# @PRE: Job has target_schema and target_table configured. Dialect is one of supported SUPPORTED_DIALECTS.\n# @POST: Returns safe SQL strings for the target dialect.\n# @SIDE_EFFECT: None — pure code generation.\n# @RATIONALE: Dialect-aware SQL uses ON CONFLICT for PostgreSQL; plain INSERT for ClickHouse with documented limitations.\n# @REJECTED: UPDATE statements — source is append-only; UPSERT covers overwrite case.\n# @REJECTED: ORM-based insert bypasses Superset's SQL Lab audit trail.\n\nfrom datetime import UTC, datetime\nfrom typing import Any\n\nfrom ...core.logger import belief_scope, logger\n\n# PostgreSQL/Greenplum dialects that support ON CONFLICT\nPOSTGRESQL_DIALECTS = {\"postgresql\", \"redshift\", \"greenplum\"}\n# Dialects that use backtick or no quoting\nCLICKHOUSE_DIALECTS = {\"clickhouse\"}\n\n\n# #region _normalize_timestamp_value [C:2] [TYPE Function] [SEMANTICS translate,sql,timestamp]\n# @BRIEF Detect Unix timestamp strings (seconds or millis) and convert to 'YYYY-MM-DD' for Date columns.\ndef _normalize_timestamp_value(value: Any) -> str | None:\n \"\"\"Detect Unix timestamp values and convert to date string.\n\n Handles:\n - Integer/float Unix timestamps (seconds or milliseconds)\n - String representations of Unix timestamps (e.g. '1726358400000.0')\n\n Returns 'YYYY-MM-DD' if conversion succeeds, None if value is not a timestamp.\n \"\"\"\n # Try numeric conversion first\n try:\n ts = float(value)\n except (ValueError, TypeError):\n return None\n\n # Heuristic: Unix timestamps in seconds are ~10 digits (1e9 range for 2001-2033)\n # Unix timestamps in milliseconds are ~13 digits (1e12 range for 2001-2033)\n if 1e9 <= ts < 1e12:\n # Already in seconds\n pass\n elif 1e12 <= ts < 1e15:\n # Milliseconds — convert to seconds\n ts = ts / 1000.0\n else:\n # Not a plausible Unix timestamp\n return None\n\n try:\n dt = datetime.fromtimestamp(ts, tz=UTC)\n return dt.strftime(\"%Y-%m-%d\")\n except (OSError, OverflowError, ValueError):\n return None\n# #endregion _normalize_timestamp_value\n\n\n# #region _quote_identifier [TYPE Function]\n# @BRIEF Quote an identifier per dialect rules. PostgreSQL uses double quotes; ClickHouse uses backticks.\n# @PRE: identifier is a non-empty string.\n# @POST: Returns safely quoted identifier.\ndef _quote_identifier(identifier: str, dialect: str) -> str:\n \"\"\"Quote a SQL identifier per dialect rules.\"\"\"\n if not identifier:\n return identifier\n # Remove any existing quotes to avoid double-quoting\n cleaned = identifier.strip().strip('\"').strip('`').strip('[]')\n if dialect in POSTGRESQL_DIALECTS:\n return f'\"{cleaned}\"'\n elif dialect in CLICKHOUSE_DIALECTS:\n return f\"`{cleaned}`\"\n else:\n # Generic ANSI double-quote\n return f'\"{cleaned}\"'\n# #endregion _quote_identifier\n\n\n# #region _encode_sql_value [C:2] [TYPE Function] [SEMANTICS translate,sql,encoding]\n# @BRIEF Encode a Python value into a SQL-safe literal for INSERT VALUES, with ClickHouse timestamp normalization.\ndef _encode_sql_value(value: Any, dialect: str | None = None) -> str:\n \"\"\"Encode a Python value into a SQL-safe literal.\n\n For ClickHouse dialect, attempts to detect Unix timestamp strings\n and convert them to 'YYYY-MM-DD' format for Date column compatibility.\n \"\"\"\n if value is None:\n return \"NULL\"\n if isinstance(value, bool):\n return \"TRUE\" if value else \"FALSE\"\n\n # For ClickHouse: try to normalize timestamp-like values (both string and numeric)\n if dialect in CLICKHOUSE_DIALECTS:\n if (isinstance(value, str) and value) or isinstance(value, (int, float)):\n normalized = _normalize_timestamp_value(value)\n if normalized:\n return f\"'{normalized}'\"\n\n if isinstance(value, (int, float)):\n return str(value)\n # String — escape single quotes by doubling them\n escaped = str(value).replace(\"'\", \"''\")\n return f\"'{escaped}'\"\n# #endregion _encode_sql_value\n\n\n# #region _build_values_clause [C:2] [TYPE Function] [SEMANTICS translate,sql,values]\n# @BRIEF Build a VALUES clause for multiple rows with per-column value encoding and dialect-aware quoting.\ndef _build_values_clause(columns: list[str], rows: list[dict[str, Any]], dialect: str | None = None) -> str:\n \"\"\"Build VALUES (...) clause for multiple rows.\n\n NOTE: columns may be quoted (e.g. '\"col\"' or '`col`') for the SQL column list,\n but row dicts have UNQUOTED keys. Strip quotes before value lookup.\n \"\"\"\n value_groups = []\n for row in rows:\n values = []\n for col in columns:\n # Strip quoting for value lookup — row keys are unquoted\n lookup_key = col.strip('\"').strip('`').strip('[]')\n val = row.get(lookup_key)\n values.append(_encode_sql_value(val, dialect=dialect))\n value_groups.append(f\"({', '.join(values)})\")\n return \",\\n\".join(value_groups)\n# #endregion _build_values_clause\n\n\n# #region generate_insert_sql [C:3] [TYPE Function] [SEMANTICS translate,sql,insert]\n# @BRIEF Generate a dialect-aware plain INSERT SQL statement for the given table, columns, and rows.\n# @RELATION DEPENDS_ON -> [_quote_identifier]\n# @RELATION DEPENDS_ON -> [_build_values_clause]\ndef generate_insert_sql(\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n rows: list[dict[str, Any]],\n dialect: str | None = None,\n) -> str:\n \"\"\"Generate a plain INSERT SQL.\"\"\"\n with belief_scope(\"generate_insert_sql\"):\n if not target_table:\n raise ValueError(\"target_table is required for INSERT SQL generation\")\n if not columns:\n raise ValueError(\"At least one column is required for INSERT SQL generation\")\n if not rows:\n raise ValueError(\"At least one row is required for INSERT SQL generation\")\n\n col_list = \", \".join(columns)\n values = _build_values_clause(columns, rows, dialect=dialect)\n\n table_ref = target_table\n if target_schema:\n table_ref = f\"{target_schema}.{target_table}\"\n\n sql = f\"INSERT INTO {table_ref} ({col_list})\\nVALUES\\n{values};\"\n return sql\n# #endregion generate_insert_sql\n\n\n# #region generate_upsert_sql [TYPE Function]\n# @BRIEF Generate PostgreSQL dialect UPSERT SQL with ON CONFLICT DO UPDATE.\n# @PRE: dialect is postgresql-compatible. target_table, columns, key_columns are non-empty.\n# @POST: Returns UPSERT SQL string or raises ValueError.\ndef generate_upsert_sql(\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n key_columns: list[str],\n rows: list[dict[str, Any]],\n) -> str:\n \"\"\"Generate INSERT ... ON CONFLICT (key_cols) DO UPDATE SET ... SQL.\"\"\"\n with belief_scope(\"generate_upsert_sql\"):\n if not target_table:\n raise ValueError(\"target_table is required for UPSERT SQL generation\")\n if not columns:\n raise ValueError(\"At least one column is required for UPSERT SQL generation\")\n if not key_columns:\n raise ValueError(\"key_columns are required for UPSERT SQL generation\")\n if not rows:\n raise ValueError(\"At least one row is required for UPSERT SQL generation\")\n\n col_list = \", \".join(columns)\n key_list = \", \".join(key_columns)\n values = _build_values_clause(columns, rows)\n\n table_ref = target_table\n if target_schema:\n table_ref = f\"{target_schema}.{target_table}\"\n\n # Build SET clause: exclude key columns from update\n update_cols = [c for c in columns if c not in key_columns]\n if not update_cols:\n # If only key columns, use DO NOTHING\n conflict_action = \"DO NOTHING\"\n else:\n set_parts = [f\"{col} = EXCLUDED.{col}\" for col in update_cols]\n conflict_action = \"DO UPDATE SET\\n\" + \",\\n\".join(set_parts)\n\n sql = (\n f\"INSERT INTO {table_ref} ({col_list})\\n\"\n f\"VALUES\\n\"\n f\"{values}\\n\"\n f\"ON CONFLICT ({key_list}) {conflict_action};\"\n )\n return sql\n# #endregion generate_upsert_sql\n\n\n# #region SQLGenerator [C:3] [TYPE Class]\n# @BRIEF Generate safe, dialect-appropriate SQL INSERT/UPSERT statements.\n# @PRE: Job has target_schema, target_table, key columns configured.\n# @POST: Returns generated SQL string for the target dialect.\nclass SQLGenerator:\n\n # region SQLGenerator.generate [TYPE Function]\n # @PURPOSE: Generate SQL for a set of rows, detecting dialect from the job configuration.\n # @PRE: dialect is a supported database dialect. columns list is non-empty. rows is non-empty.\n # @POST: Returns tuple of (sql_string, statement_count).\n # @SIDE_EFFECT: None — pure SQL generation.\n @staticmethod\n def generate(\n dialect: str,\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n rows: list[dict[str, Any]],\n key_columns: list[str] | None = None,\n upsert_strategy: str = \"MERGE\",\n ) -> tuple[str, int]:\n \"\"\"Generate dialect-appropriate INSERT/UPSERT SQL.\n\n Args:\n dialect: Target database dialect (e.g. 'postgresql', 'clickhouse').\n target_schema: Optional schema name.\n target_table: Target table name.\n columns: List of column names to insert.\n rows: List of row dicts with column values.\n key_columns: Key columns for conflict resolution (UPSERT).\n upsert_strategy: 'MERGE' (UPSERT), 'INSERT' (plain INSERT).\n\n Returns:\n Tuple of (sql_string, row_count).\n \"\"\"\n with belief_scope(\"SQLGenerator.generate\"):\n logger.reason(\"Generating SQL\", {\n \"dialect\": dialect,\n \"schema\": target_schema,\n \"table\": target_table,\n \"columns\": len(columns),\n \"rows\": len(rows),\n \"strategy\": upsert_strategy,\n })\n\n # Validate inputs\n if not target_table:\n raise ValueError(\"target_table is required\")\n if not columns:\n raise ValueError(\"At least one column is required\")\n if not rows:\n raise ValueError(\"At least one row is required\")\n\n # Build fully qualified table reference\n table_ref = target_table\n if target_schema:\n quoted_schema = _quote_identifier(target_schema, dialect)\n quoted_table = _quote_identifier(target_table, dialect)\n table_ref = f\"{quoted_schema}.{quoted_table}\"\n else:\n table_ref = _quote_identifier(target_table, dialect)\n\n # Quote columns per dialect\n quoted_columns = [_quote_identifier(c, dialect) for c in columns]\n quoted_key_columns = (\n [_quote_identifier(k, dialect) for k in key_columns]\n if key_columns\n else []\n )\n\n # Generate SQL per dialect and strategy\n use_upsert = upsert_strategy.upper() == \"MERGE\" and key_columns\n\n if dialect in POSTGRESQL_DIALECTS or dialect not in CLICKHOUSE_DIALECTS:\n # PostgreSQL and other ANSI dialects: support UPSERT via ON CONFLICT\n if use_upsert:\n sql = generate_upsert_sql(\n target_schema=None,\n target_table=table_ref,\n columns=quoted_columns,\n key_columns=quoted_key_columns,\n rows=rows,\n )\n else:\n sql = generate_insert_sql(\n target_schema=None,\n target_table=table_ref,\n columns=quoted_columns,\n rows=rows,\n dialect=dialect,\n )\n elif dialect in CLICKHOUSE_DIALECTS:\n # ClickHouse: plain INSERT, no ON CONFLICT support\n sql = generate_insert_sql(\n target_schema=None,\n target_table=table_ref,\n columns=quoted_columns,\n rows=rows,\n dialect=dialect,\n )\n if use_upsert:\n logger.reason(\"ClickHouse UPSERT not supported; using plain INSERT\", {\n \"note\": \"ClickHouse does not support ON CONFLICT. Use ReplacingMergeTree for dedup.\",\n })\n else:\n # Fallback: plain INSERT\n sql = generate_insert_sql(\n target_schema=None,\n target_table=table_ref,\n columns=quoted_columns,\n rows=rows,\n dialect=dialect,\n )\n\n logger.reflect(\"SQL generated\", {\n \"dialect\": dialect,\n \"row_count\": len(rows),\n \"sql_length\": len(sql),\n })\n return sql, len(rows)\n # endregion SQLGenerator.generate\n\n # region SQLGenerator.generate_batch [TYPE Function]\n # @PURPOSE: Generate separate INSERT statements for each row (batch-safe version).\n # @PRE: Same as generate().\n # @POST: Returns list of (sql_string, row_index) tuples.\n @staticmethod\n def generate_batch(\n dialect: str,\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n rows: list[dict[str, Any]],\n key_columns: list[str] | None = None,\n upsert_strategy: str = \"MERGE\",\n max_rows_per_statement: int = 500,\n ) -> list[tuple[str, int]]:\n \"\"\"Generate SQL in batches, splitting large row sets into multiple statements.\n\n Returns:\n List of (sql_string, row_count) tuples.\n \"\"\"\n with belief_scope(\"SQLGenerator.generate_batch\"):\n if not rows:\n return []\n\n statements = []\n for i in range(0, len(rows), max_rows_per_statement):\n chunk = rows[i:i + max_rows_per_statement]\n sql, count = SQLGenerator.generate(\n dialect=dialect,\n target_schema=target_schema,\n target_table=target_table,\n columns=columns,\n rows=chunk,\n key_columns=key_columns,\n upsert_strategy=upsert_strategy,\n )\n statements.append((sql, count))\n\n return statements\n # endregion SQLGenerator.generate_batch\n\n\n# #endregion SQLGenerator\n# #endregion SQLGenerator\n" + "body": "# #region SQLGenerator [C:3] [TYPE Module] [SEMANTICS clickhouse, translate, sql, insert, generate]\n# @BRIEF Dialect-aware safe SQL generation for INSERT/UPSERT operations.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [TranslationJob]\n# @RELATION DEPENDS_ON -> [TranslationRun]\n# @PRE: Job has target_schema and target_table configured. Dialect is one of supported SUPPORTED_DIALECTS.\n# @POST: Returns safe SQL strings for the target dialect.\n# @SIDE_EFFECT: None — pure code generation.\n# @RATIONALE: Dialect-aware SQL uses ON CONFLICT for PostgreSQL; plain INSERT for ClickHouse with documented limitations.\n# @REJECTED: UPDATE statements — source is append-only; UPSERT covers overwrite case.\n# @REJECTED: ORM-based insert bypasses Superset's SQL Lab audit trail.\n\nfrom datetime import UTC, datetime\nfrom typing import Any\n\nfrom ...core.logger import belief_scope, logger\n\n# PostgreSQL/Greenplum dialects that support ON CONFLICT\nPOSTGRESQL_DIALECTS = {\"postgresql\", \"redshift\", \"greenplum\"}\n# Dialects that use backtick or no quoting\nCLICKHOUSE_DIALECTS = {\"clickhouse\"}\n\n\n# #region _normalize_timestamp_value [C:2] [TYPE Function] [SEMANTICS translate,sql,timestamp]\n# @BRIEF Detect Unix timestamp strings (seconds or millis) and convert to 'YYYY-MM-DD' for Date columns.\ndef _normalize_timestamp_value(value: Any) -> str | None:\n \"\"\"Detect Unix timestamp values and convert to date string.\n\n Handles:\n - Integer/float Unix timestamps (seconds or milliseconds)\n - String representations of Unix timestamps (e.g. '1726358400000.0')\n\n Returns 'YYYY-MM-DD' if conversion succeeds, None if value is not a timestamp.\n \"\"\"\n # Try numeric conversion first\n try:\n ts = float(value)\n except (ValueError, TypeError):\n return None\n\n # Heuristic: Unix timestamps in seconds are ~10 digits (1e9 range for 2001-2033)\n # Unix timestamps in milliseconds are ~13 digits (1e12 range for 2001-2033)\n if 1e9 <= ts < 1e12:\n # Already in seconds\n pass\n elif 1e12 <= ts < 1e15:\n # Milliseconds — convert to seconds\n ts = ts / 1000.0\n else:\n # Not a plausible Unix timestamp\n return None\n\n try:\n dt = datetime.fromtimestamp(ts, tz=UTC)\n return dt.strftime(\"%Y-%m-%d\")\n except (OSError, OverflowError, ValueError):\n return None\n# #endregion _normalize_timestamp_value\n\n\n# #region _quote_identifier [C:4] [TYPE Function]\n# @BRIEF Quote an identifier per dialect rules. PostgreSQL uses double quotes; ClickHouse uses backticks.\n# @PRE: identifier is a non-empty string.\n# @POST: Returns safely quoted identifier.\ndef _quote_identifier(identifier: str, dialect: str) -> str:\n \"\"\"Quote a SQL identifier per dialect rules.\"\"\"\n if not identifier:\n return identifier\n # Remove any existing quotes to avoid double-quoting\n cleaned = identifier.strip().strip('\"').strip('`').strip('[]')\n if dialect in POSTGRESQL_DIALECTS:\n return f'\"{cleaned}\"'\n elif dialect in CLICKHOUSE_DIALECTS:\n return f\"`{cleaned}`\"\n else:\n # Generic ANSI double-quote\n return f'\"{cleaned}\"'\n# #endregion _quote_identifier\n\n\n# #region _encode_sql_value [C:2] [TYPE Function] [SEMANTICS translate,sql,encoding]\n# @BRIEF Encode a Python value into a SQL-safe literal for INSERT VALUES, with ClickHouse timestamp normalization.\ndef _encode_sql_value(value: Any, dialect: str | None = None) -> str:\n \"\"\"Encode a Python value into a SQL-safe literal.\n\n For ClickHouse dialect, attempts to detect Unix timestamp strings\n and convert them to 'YYYY-MM-DD' format for Date column compatibility.\n \"\"\"\n if value is None:\n return \"NULL\"\n if isinstance(value, bool):\n return \"TRUE\" if value else \"FALSE\"\n\n # For ClickHouse: try to normalize timestamp-like values (both string and numeric)\n if dialect in CLICKHOUSE_DIALECTS:\n if (isinstance(value, str) and value) or isinstance(value, (int, float)):\n normalized = _normalize_timestamp_value(value)\n if normalized:\n return f\"'{normalized}'\"\n\n if isinstance(value, (int, float)):\n return str(value)\n # String — escape single quotes by doubling them\n escaped = str(value).replace(\"'\", \"''\")\n return f\"'{escaped}'\"\n# #endregion _encode_sql_value\n\n\n# #region _build_values_clause [C:2] [TYPE Function] [SEMANTICS translate,sql,values]\n# @BRIEF Build a VALUES clause for multiple rows with per-column value encoding and dialect-aware quoting.\ndef _build_values_clause(columns: list[str], rows: list[dict[str, Any]], dialect: str | None = None) -> str:\n \"\"\"Build VALUES (...) clause for multiple rows.\n\n NOTE: columns may be quoted (e.g. '\"col\"' or '`col`') for the SQL column list,\n but row dicts have UNQUOTED keys. Strip quotes before value lookup.\n \"\"\"\n value_groups = []\n for row in rows:\n values = []\n for col in columns:\n # Strip quoting for value lookup — row keys are unquoted\n lookup_key = col.strip('\"').strip('`').strip('[]')\n val = row.get(lookup_key)\n values.append(_encode_sql_value(val, dialect=dialect))\n value_groups.append(f\"({', '.join(values)})\")\n return \",\\n\".join(value_groups)\n# #endregion _build_values_clause\n\n\n# #region generate_insert_sql [C:3] [TYPE Function] [SEMANTICS translate,sql,insert]\n# @BRIEF Generate a dialect-aware plain INSERT SQL statement for the given table, columns, and rows.\n# @RELATION DEPENDS_ON -> [_quote_identifier]\n# @RELATION DEPENDS_ON -> [_build_values_clause]\ndef generate_insert_sql(\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n rows: list[dict[str, Any]],\n dialect: str | None = None,\n) -> str:\n \"\"\"Generate a plain INSERT SQL.\"\"\"\n with belief_scope(\"generate_insert_sql\"):\n if not target_table:\n raise ValueError(\"target_table is required for INSERT SQL generation\")\n if not columns:\n raise ValueError(\"At least one column is required for INSERT SQL generation\")\n if not rows:\n raise ValueError(\"At least one row is required for INSERT SQL generation\")\n\n col_list = \", \".join(columns)\n values = _build_values_clause(columns, rows, dialect=dialect)\n\n table_ref = target_table\n if target_schema:\n table_ref = f\"{target_schema}.{target_table}\"\n\n sql = f\"INSERT INTO {table_ref} ({col_list})\\nVALUES\\n{values};\"\n return sql\n# #endregion generate_insert_sql\n\n\n# #region generate_upsert_sql [C:4] [TYPE Function]\n# @BRIEF Generate PostgreSQL dialect UPSERT SQL with ON CONFLICT DO UPDATE.\n# @PRE dialect is postgresql-compatible. target_table, columns, key_columns are non-empty.\n# @POST Returns UPSERT SQL string or raises ValueError.\n# @COMPLEXITY 4\n\ndef generate_upsert_sql(\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n key_columns: list[str],\n rows: list[dict[str, Any]],\n) -> str:\n \"\"\"Generate INSERT ... ON CONFLICT (key_cols) DO UPDATE SET ... SQL.\"\"\"\n with belief_scope(\"generate_upsert_sql\"):\n if not target_table:\n raise ValueError(\"target_table is required for UPSERT SQL generation\")\n if not columns:\n raise ValueError(\"At least one column is required for UPSERT SQL generation\")\n if not key_columns:\n raise ValueError(\"key_columns are required for UPSERT SQL generation\")\n if not rows:\n raise ValueError(\"At least one row is required for UPSERT SQL generation\")\n\n col_list = \", \".join(columns)\n key_list = \", \".join(key_columns)\n values = _build_values_clause(columns, rows)\n\n table_ref = target_table\n if target_schema:\n table_ref = f\"{target_schema}.{target_table}\"\n\n # Build SET clause: exclude key columns from update\n update_cols = [c for c in columns if c not in key_columns]\n if not update_cols:\n # If only key columns, use DO NOTHING\n conflict_action = \"DO NOTHING\"\n else:\n set_parts = [f\"{col} = EXCLUDED.{col}\" for col in update_cols]\n conflict_action = \"DO UPDATE SET\\n\" + \",\\n\".join(set_parts)\n\n sql = (\n f\"INSERT INTO {table_ref} ({col_list})\\n\"\n f\"VALUES\\n\"\n f\"{values}\\n\"\n f\"ON CONFLICT ({key_list}) {conflict_action};\"\n )\n return sql\n# #endregion generate_upsert_sql\n\n\n# #region SQLGenerator [C:3] [TYPE Class]\n# @BRIEF Generate safe, dialect-appropriate SQL INSERT/UPSERT statements.\n# @PRE: Job has target_schema, target_table, key columns configured.\n# @POST: Returns generated SQL string for the target dialect.\nclass SQLGenerator:\n\n # region SQLGenerator.generate [TYPE Function]\n # @PURPOSE: Generate SQL for a set of rows, detecting dialect from the job configuration.\n # @PRE: dialect is a supported database dialect. columns list is non-empty. rows is non-empty.\n # @POST: Returns tuple of (sql_string, statement_count).\n # @SIDE_EFFECT: None — pure SQL generation.\n @staticmethod\n def generate(\n dialect: str,\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n rows: list[dict[str, Any]],\n key_columns: list[str] | None = None,\n upsert_strategy: str = \"MERGE\",\n ) -> tuple[str, int]:\n \"\"\"Generate dialect-appropriate INSERT/UPSERT SQL.\n\n Args:\n dialect: Target database dialect (e.g. 'postgresql', 'clickhouse').\n target_schema: Optional schema name.\n target_table: Target table name.\n columns: List of column names to insert.\n rows: List of row dicts with column values.\n key_columns: Key columns for conflict resolution (UPSERT).\n upsert_strategy: 'MERGE' (UPSERT), 'INSERT' (plain INSERT).\n\n Returns:\n Tuple of (sql_string, row_count).\n \"\"\"\n with belief_scope(\"SQLGenerator.generate\"):\n logger.reason(\"Generating SQL\", {\n \"dialect\": dialect,\n \"schema\": target_schema,\n \"table\": target_table,\n \"columns\": len(columns),\n \"rows\": len(rows),\n \"strategy\": upsert_strategy,\n })\n\n # Validate inputs\n if not target_table:\n raise ValueError(\"target_table is required\")\n if not columns:\n raise ValueError(\"At least one column is required\")\n if not rows:\n raise ValueError(\"At least one row is required\")\n\n # Build fully qualified table reference\n table_ref = target_table\n if target_schema:\n quoted_schema = _quote_identifier(target_schema, dialect)\n quoted_table = _quote_identifier(target_table, dialect)\n table_ref = f\"{quoted_schema}.{quoted_table}\"\n else:\n table_ref = _quote_identifier(target_table, dialect)\n\n # Quote columns per dialect\n quoted_columns = [_quote_identifier(c, dialect) for c in columns]\n quoted_key_columns = (\n [_quote_identifier(k, dialect) for k in key_columns]\n if key_columns\n else []\n )\n\n # Generate SQL per dialect and strategy\n use_upsert = upsert_strategy.upper() == \"MERGE\" and key_columns\n\n if dialect in POSTGRESQL_DIALECTS or dialect not in CLICKHOUSE_DIALECTS:\n # PostgreSQL and other ANSI dialects: support UPSERT via ON CONFLICT\n if use_upsert:\n sql = generate_upsert_sql(\n target_schema=None,\n target_table=table_ref,\n columns=quoted_columns,\n key_columns=quoted_key_columns,\n rows=rows,\n )\n else:\n sql = generate_insert_sql(\n target_schema=None,\n target_table=table_ref,\n columns=quoted_columns,\n rows=rows,\n dialect=dialect,\n )\n elif dialect in CLICKHOUSE_DIALECTS:\n # ClickHouse: plain INSERT, no ON CONFLICT support\n sql = generate_insert_sql(\n target_schema=None,\n target_table=table_ref,\n columns=quoted_columns,\n rows=rows,\n dialect=dialect,\n )\n if use_upsert:\n logger.reason(\"ClickHouse UPSERT not supported; using plain INSERT\", {\n \"note\": \"ClickHouse does not support ON CONFLICT. Use ReplacingMergeTree for dedup.\",\n })\n else:\n # Fallback: plain INSERT\n sql = generate_insert_sql(\n target_schema=None,\n target_table=table_ref,\n columns=quoted_columns,\n rows=rows,\n dialect=dialect,\n )\n\n logger.reflect(\"SQL generated\", {\n \"dialect\": dialect,\n \"row_count\": len(rows),\n \"sql_length\": len(sql),\n })\n return sql, len(rows)\n # endregion SQLGenerator.generate\n\n # region SQLGenerator.generate_batch [TYPE Function]\n # @PURPOSE: Generate separate INSERT statements for each row (batch-safe version).\n # @PRE: Same as generate().\n # @POST: Returns list of (sql_string, row_index) tuples.\n @staticmethod\n def generate_batch(\n dialect: str,\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n rows: list[dict[str, Any]],\n key_columns: list[str] | None = None,\n upsert_strategy: str = \"MERGE\",\n max_rows_per_statement: int = 500,\n ) -> list[tuple[str, int]]:\n \"\"\"Generate SQL in batches, splitting large row sets into multiple statements.\n\n Returns:\n List of (sql_string, row_count) tuples.\n \"\"\"\n with belief_scope(\"SQLGenerator.generate_batch\"):\n if not rows:\n return []\n\n statements = []\n for i in range(0, len(rows), max_rows_per_statement):\n chunk = rows[i:i + max_rows_per_statement]\n sql, count = SQLGenerator.generate(\n dialect=dialect,\n target_schema=target_schema,\n target_table=target_table,\n columns=columns,\n rows=chunk,\n key_columns=key_columns,\n upsert_strategy=upsert_strategy,\n )\n statements.append((sql, count))\n\n return statements\n # endregion SQLGenerator.generate_batch\n\n\n# #endregion SQLGenerator\n# #endregion SQLGenerator\n" }, { "contract_id": "_normalize_timestamp_value", @@ -50659,9 +49907,10 @@ "file_path": "backend/src/plugins/translate/sql_generator.py", "start_line": 61, "end_line": 78, - "tier": "TIER_1", - "complexity": 1, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": 4, "POST": "Returns safely quoted identifier.", "PRE": "identifier is a non-empty string.", "PURPOSE": "Quote an identifier per dialect rules. PostgreSQL uses double quotes; ClickHouse uses backticks." @@ -50669,27 +49918,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region _quote_identifier [TYPE Function]\n# @BRIEF Quote an identifier per dialect rules. PostgreSQL uses double quotes; ClickHouse uses backticks.\n# @PRE: identifier is a non-empty string.\n# @POST: Returns safely quoted identifier.\ndef _quote_identifier(identifier: str, dialect: str) -> str:\n \"\"\"Quote a SQL identifier per dialect rules.\"\"\"\n if not identifier:\n return identifier\n # Remove any existing quotes to avoid double-quoting\n cleaned = identifier.strip().strip('\"').strip('`').strip('[]')\n if dialect in POSTGRESQL_DIALECTS:\n return f'\"{cleaned}\"'\n elif dialect in CLICKHOUSE_DIALECTS:\n return f\"`{cleaned}`\"\n else:\n # Generic ANSI double-quote\n return f'\"{cleaned}\"'\n# #endregion _quote_identifier\n" + "body": "# #region _quote_identifier [C:4] [TYPE Function]\n# @BRIEF Quote an identifier per dialect rules. PostgreSQL uses double quotes; ClickHouse uses backticks.\n# @PRE: identifier is a non-empty string.\n# @POST: Returns safely quoted identifier.\ndef _quote_identifier(identifier: str, dialect: str) -> str:\n \"\"\"Quote a SQL identifier per dialect rules.\"\"\"\n if not identifier:\n return identifier\n # Remove any existing quotes to avoid double-quoting\n cleaned = identifier.strip().strip('\"').strip('`').strip('[]')\n if dialect in POSTGRESQL_DIALECTS:\n return f'\"{cleaned}\"'\n elif dialect in CLICKHOUSE_DIALECTS:\n return f\"`{cleaned}`\"\n else:\n # Generic ANSI double-quote\n return f'\"{cleaned}\"'\n# #endregion _quote_identifier\n" }, { "contract_id": "_encode_sql_value", @@ -50763,10 +50012,11 @@ "contract_type": "Function", "file_path": "backend/src/plugins/translate/sql_generator.py", "start_line": 162, - "end_line": 208, - "tier": "TIER_1", - "complexity": 1, + "end_line": 210, + "tier": "TIER_2", + "complexity": 4, "metadata": { + "COMPLEXITY": "4", "POST": "Returns UPSERT SQL string or raises ValueError.", "PRE": "dialect is postgresql-compatible. target_table, columns, key_columns are non-empty.", "PURPOSE": "Generate PostgreSQL dialect UPSERT SQL with ON CONFLICT DO UPDATE." @@ -50774,27 +50024,27 @@ "relations": [], "schema_warnings": [ { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "RELATION", + "message": "@RELATION is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } }, { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Function' at C1", + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Function' at C4", "detail": { - "actual_complexity": 1, + "actual_complexity": 4, "contract_type": "Function" } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region generate_upsert_sql [TYPE Function]\n# @BRIEF Generate PostgreSQL dialect UPSERT SQL with ON CONFLICT DO UPDATE.\n# @PRE: dialect is postgresql-compatible. target_table, columns, key_columns are non-empty.\n# @POST: Returns UPSERT SQL string or raises ValueError.\ndef generate_upsert_sql(\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n key_columns: list[str],\n rows: list[dict[str, Any]],\n) -> str:\n \"\"\"Generate INSERT ... ON CONFLICT (key_cols) DO UPDATE SET ... SQL.\"\"\"\n with belief_scope(\"generate_upsert_sql\"):\n if not target_table:\n raise ValueError(\"target_table is required for UPSERT SQL generation\")\n if not columns:\n raise ValueError(\"At least one column is required for UPSERT SQL generation\")\n if not key_columns:\n raise ValueError(\"key_columns are required for UPSERT SQL generation\")\n if not rows:\n raise ValueError(\"At least one row is required for UPSERT SQL generation\")\n\n col_list = \", \".join(columns)\n key_list = \", \".join(key_columns)\n values = _build_values_clause(columns, rows)\n\n table_ref = target_table\n if target_schema:\n table_ref = f\"{target_schema}.{target_table}\"\n\n # Build SET clause: exclude key columns from update\n update_cols = [c for c in columns if c not in key_columns]\n if not update_cols:\n # If only key columns, use DO NOTHING\n conflict_action = \"DO NOTHING\"\n else:\n set_parts = [f\"{col} = EXCLUDED.{col}\" for col in update_cols]\n conflict_action = \"DO UPDATE SET\\n\" + \",\\n\".join(set_parts)\n\n sql = (\n f\"INSERT INTO {table_ref} ({col_list})\\n\"\n f\"VALUES\\n\"\n f\"{values}\\n\"\n f\"ON CONFLICT ({key_list}) {conflict_action};\"\n )\n return sql\n# #endregion generate_upsert_sql\n" + "body": "# #region generate_upsert_sql [C:4] [TYPE Function]\n# @BRIEF Generate PostgreSQL dialect UPSERT SQL with ON CONFLICT DO UPDATE.\n# @PRE dialect is postgresql-compatible. target_table, columns, key_columns are non-empty.\n# @POST Returns UPSERT SQL string or raises ValueError.\n# @COMPLEXITY 4\n\ndef generate_upsert_sql(\n target_schema: str | None,\n target_table: str,\n columns: list[str],\n key_columns: list[str],\n rows: list[dict[str, Any]],\n) -> str:\n \"\"\"Generate INSERT ... ON CONFLICT (key_cols) DO UPDATE SET ... SQL.\"\"\"\n with belief_scope(\"generate_upsert_sql\"):\n if not target_table:\n raise ValueError(\"target_table is required for UPSERT SQL generation\")\n if not columns:\n raise ValueError(\"At least one column is required for UPSERT SQL generation\")\n if not key_columns:\n raise ValueError(\"key_columns are required for UPSERT SQL generation\")\n if not rows:\n raise ValueError(\"At least one row is required for UPSERT SQL generation\")\n\n col_list = \", \".join(columns)\n key_list = \", \".join(key_columns)\n values = _build_values_clause(columns, rows)\n\n table_ref = target_table\n if target_schema:\n table_ref = f\"{target_schema}.{target_table}\"\n\n # Build SET clause: exclude key columns from update\n update_cols = [c for c in columns if c not in key_columns]\n if not update_cols:\n # If only key columns, use DO NOTHING\n conflict_action = \"DO NOTHING\"\n else:\n set_parts = [f\"{col} = EXCLUDED.{col}\" for col in update_cols]\n conflict_action = \"DO UPDATE SET\\n\" + \",\\n\".join(set_parts)\n\n sql = (\n f\"INSERT INTO {table_ref} ({col_list})\\n\"\n f\"VALUES\\n\"\n f\"{values}\\n\"\n f\"ON CONFLICT ({key_list}) {conflict_action};\"\n )\n return sql\n# #endregion generate_upsert_sql\n" }, { "contract_id": "SupersetSqlLabExecutor", @@ -50806,7 +50056,7 @@ "complexity": 4, "metadata": { "COMPLEXITY": 4, - "LAYER": "Infra", + "LAYER": "Infrastructure", "POST": "SQL is submitted to Superset SQL Lab; execution reference is returned.", "PRE": "Valid Superset environment configuration and authenticated client.", "PURPOSE": "Submit SQL to Superset SQL Lab API and poll execution status.", @@ -50818,8 +50068,8 @@ { "source_id": "SupersetSqlLabExecutor", "relation_type": "DEPENDS_ON", - "target_id": "SupersetClient", - "target_ref": "[SupersetClient]" + "target_id": "ConfigManager", + "target_ref": "[ConfigManager]" }, { "source_id": "SupersetSqlLabExecutor", @@ -50828,28 +50078,10 @@ "target_ref": "[ConfigManager]" } ], - "schema_warnings": [ - { - "code": "invalid_enum_value", - "tag": "LAYER", - "message": "@LAYER value 'Infra' is not in allowed enum", - "detail": { - "allowed": [ - "Core", - "Domain", - "API", - "UI", - "Service", - "Infrastructure", - "Plugin" - ], - "value": "Infra" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region SupersetSqlLabExecutor [C:4] [TYPE Module] [SEMANTICS translate, superset, sqllab, execute, query]\n# @BRIEF Submit SQL to Superset SQL Lab API and poll execution status.\n# @LAYER: Infra\n# @RELATION DEPENDS_ON -> [SupersetClient]\n# @RELATION DEPENDS_ON -> [ConfigManager]\n# @PRE: Valid Superset environment configuration and authenticated client.\n# @POST: SQL is submitted to Superset SQL Lab; execution reference is returned.\n# @SIDE_EFFECT: Makes HTTP calls to Superset /api/v1/sqllab/execute/; polls status.\n# @RATIONALE: Direct SQL Lab API submission provides audit trail and execution monitoring within Superset.\n# @REJECTED: Direct database connection bypass — would skip Superset's SQL Lab audit and RBAC.\n\nimport json\nimport time\nimport uuid\nfrom typing import Any\n\nfrom ...core.config_manager import ConfigManager\nfrom ...core.logger import belief_scope, logger\nfrom ...core.superset_client import SupersetClient\n\n\n# #region SupersetSqlLabExecutor [C:4] [TYPE Class]\n# @BRIEF Submit SQL to Superset SQL Lab API with polling and status tracking.\n# @PRE: Valid environment ID and ConfigManager.\n# @POST: SQL submitted to Superset; execution reference recorded.\n# @SIDE_EFFECT: Makes HTTP POST to /api/v1/sqllab/execute/; polls GET for status.\nclass SupersetSqlLabExecutor:\n\n def __init__(self, config_manager: ConfigManager, env_id: str):\n self.config_manager = config_manager\n self.env_id = env_id\n self._client: SupersetClient | None = None\n self._database_id: int | None = None\n self._database_backend: str | None = None\n\n # region _get_client [TYPE Function]\n # @PURPOSE: Lazy-initialize SupersetClient for the configured environment.\n # @PRE: env_id must correspond to a valid environment config.\n # @POST: Returns authenticated SupersetClient.\n # @SIDE_EFFECT: Authenticates against Superset API.\n def _get_client(self) -> SupersetClient:\n with belief_scope(\"SupersetSqlLabExecutor._get_client\"):\n if self._client is not None:\n return self._client\n\n environments = self.config_manager.get_environments()\n env_config = next((e for e in environments if e.id == self.env_id), None)\n if not env_config:\n raise ValueError(f\"Superset environment '{self.env_id}' not found\")\n\n self._client = SupersetClient(env_config)\n return self._client\n # endregion _get_client\n\n # region resolve_database_id [TYPE Function]\n # @PURPOSE: Resolve the target database ID from the environment and fetch its backend engine.\n # @PRE: database_name or database_id should be known.\n # @POST: Returns database_id integer or raises ValueError. Also sets self._database_backend.\n # @SIDE_EFFECT: Fetches databases list from Superset; optionally fetches single DB for backend.\n def resolve_database_id(\n self,\n database_name: str | None = None,\n target_database_id: str | None = None,\n ) -> int:\n with belief_scope(\"SupersetSqlLabExecutor.resolve_database_id\"):\n client = self._get_client()\n\n # If a specific target_database_id is provided, use it directly\n if target_database_id:\n try:\n db_id = int(target_database_id)\n # Fetch full DB info to get backend\n db_info = client.get_database(db_id)\n result = db_info.get(\"result\", db_info)\n self._database_id = result.get(\"id\", db_id)\n self._database_backend = result.get(\"backend\") or result.get(\"engine\")\n logger.reason(\"Resolved database ID by target_database_id\", {\n \"target_database_id\": target_database_id,\n \"database_id\": self._database_id,\n \"backend\": self._database_backend,\n })\n return self._database_id\n except Exception as e:\n logger.explore(\"Failed to resolve by target_database_id, falling back\", {\n \"target_database_id\": target_database_id,\n \"error\": str(e),\n })\n # Fall through to default resolution\n\n # Fetch full database list with backend info\n _, databases = client.get_databases(\n query={\"columns\": [\"id\", \"database_name\", \"backend\"]}\n )\n if not databases:\n raise ValueError(\"No databases found in Superset environment\")\n\n if database_name:\n for db in databases:\n if db.get(\"database_name\", \"\").lower() == database_name.lower():\n self._database_id = db[\"id\"]\n self._database_backend = db.get(\"backend\")\n logger.reason(\"Resolved database ID by name\", {\n \"database_name\": database_name,\n \"database_id\": self._database_id,\n \"backend\": self._database_backend,\n })\n return self._database_id\n raise ValueError(\n f\"Database '{database_name}' not found in Superset environment\"\n )\n\n # Default: use first database\n self._database_id = databases[0][\"id\"]\n self._database_backend = databases[0].get(\"backend\")\n logger.reason(\"Using default database\", {\n \"database_name\": databases[0].get(\"database_name\"),\n \"database_id\": self._database_id,\n \"backend\": self._database_backend,\n })\n return self._database_id\n # endregion resolve_database_id\n\n # region get_database_backend [TYPE Function]\n # @PURPOSE: Return the cached database backend/engine string.\n # @POST: Returns backend string or None if not yet resolved.\n def get_database_backend(self) -> str | None:\n return self._database_backend\n # endregion get_database_backend\n\n # region execute_sql [TYPE Function]\n # @PURPOSE: Submit SQL to Superset SQL Lab and return execution tracking info.\n # @PRE: sql is valid SQL string. database_id is a valid Superset DB ID.\n # @POST: Returns execution result dict with query_id and status.\n # @SIDE_EFFECT: Makes HTTP POST to /api/v1/sqllab/execute/.\n def execute_sql(\n self,\n sql: str,\n database_id: int | None = None,\n run_async: bool = True,\n ) -> dict[str, Any]:\n with belief_scope(\"SupersetSqlLabExecutor.execute_sql\"):\n client = self._get_client()\n db_id = database_id or self._database_id\n if not db_id:\n db_id = self.resolve_database_id()\n\n logger.reason(\"Submitting SQL to Superset SQL Lab\", {\n \"database_id\": db_id,\n \"sql_length\": len(sql),\n \"run_async\": run_async,\n })\n\n payload = {\n \"database_id\": db_id,\n \"sql\": sql,\n \"runAsync\": run_async,\n \"schema\": None,\n \"tab\": \"translation-insert\",\n \"client_id\": f\"trl-{uuid.uuid4().hex[:7]}\", # max 11 chars for Superset varchar(11)\n }\n\n # Try multiple SQL Lab endpoints — some Superset versions use /sqllab/execute/,\n # others use /sql_lab/execute/ (with underscore)\n candidate_endpoints = [\"/api/v1/sqllab/execute/\", \"/api/v1/sql_lab/execute/\"]\n response = None\n last_error = None\n for endpoint in candidate_endpoints:\n try:\n response = client.network.request(\n method=\"POST\",\n endpoint=endpoint,\n data=json.dumps(payload),\n headers={\"Content-Type\": \"application/json\"},\n )\n if isinstance(response, dict) and response:\n logger.reason(\"SQL Lab endpoint succeeded\", extra={\n \"endpoint\": endpoint,\n })\n break\n except Exception as ep_err:\n last_error = ep_err\n logger.explore(\"SQL Lab endpoint failed, trying next\", extra={\n \"error\": str(ep_err),\n \"endpoint\": endpoint,\n })\n response = None\n\n if response is None:\n error_msg = f\"All SQL Lab endpoints failed. Last error: {last_error}\"\n logger.explore(\"SQL Lab execute failed\", extra={\"error\": error_msg})\n raise ValueError(f\"Superset SQL Lab execute failed: {error_msg}\")\n\n # Parse response — try multiple known Superset response formats\n result = response if isinstance(response, dict) else {}\n\n # Superset may return query_id at top level, nested in \"result\", as \"id\", or in \"query\" block\n query_id = (\n result.get(\"query_id\")\n or result.get(\"id\")\n or (result.get(\"result\") or {}).get(\"query_id\")\n or (result.get(\"result\") or {}).get(\"id\")\n or (result.get(\"query\") or {}).get(\"query_id\")\n or (result.get(\"query\") or {}).get(\"id\")\n )\n status = result.get(\"status\", \"unknown\")\n\n # Log full response for debugging if query_id is missing\n if not query_id:\n logger.explore(\"No query_id from SQL Lab execute\", extra={\n \"database_id\": db_id,\n \"status\": status,\n \"raw_response_keys\": list(result.keys()),\n \"raw_response_preview\": json.dumps(result)[:2000],\n })\n logger.reason(\"SQL Lab execute response (no query_id)\", extra={\n \"database_id\": db_id,\n \"status\": status,\n \"response_keys\": list(result.keys()),\n \"has_result\": \"result\" in result,\n \"has_query\": \"query\" in result,\n \"raw_preview\": json.dumps(result)[:2000],\n })\n\n logger.reason(\"SQL Lab execute response\", {\n \"query_id\": query_id,\n \"status\": status,\n \"response_keys\": list(result.keys()),\n })\n\n return {\n \"query_id\": query_id,\n \"status\": status,\n \"raw_response\": result,\n \"database_id\": db_id,\n }\n # endregion execute_sql\n\n # region poll_execution_status [TYPE Function]\n # @PURPOSE: Poll Superset for SQL execution status until completion or timeout.\n # @PRE: query_id is a valid Superset query ID.\n # @POST: Returns final execution status dict.\n # @SIDE_EFFECT: Makes HTTP GET requests to Superset.\n def poll_execution_status(\n self,\n query_id: str,\n max_polls: int = 60,\n poll_interval_seconds: float = 2.0,\n ) -> dict[str, Any]:\n with belief_scope(\"SupersetSqlLabExecutor.poll_execution_status\"):\n client = self._get_client()\n\n logger.reason(\"Polling SQL execution status\", {\n \"query_id\": query_id,\n \"max_polls\": max_polls,\n \"interval\": poll_interval_seconds,\n })\n\n for attempt in range(max_polls):\n try:\n response = client.network.request(\n method=\"GET\",\n endpoint=f\"/api/v1/query/{query_id}\",\n )\n raw = response if isinstance(response, dict) else {}\n # Superset wraps query data in a nested \"result\" field\n result = raw.get(\"result\", raw)\n status = result.get(\"status\", \"unknown\")\n state = result.get(\"state\", result.get(\"status\", \"\"))\n\n # Terminal states\n if state in (\"success\", \"finished\", \"completed\"):\n logger.reason(\"SQL execution completed\", {\n \"query_id\": query_id,\n \"attempt\": attempt + 1,\n \"rows_affected\": result.get(\"rows\"),\n })\n return {\n \"query_id\": query_id,\n \"status\": \"success\",\n \"state\": state,\n \"rows_affected\": result.get(\"rows\"),\n \"error_message\": result.get(\"error_message\"),\n \"results\": result.get(\"results\"),\n \"completed_on\": result.get(\"completed_on\"),\n }\n\n if state in (\"failed\", \"error\", \"stopped\"):\n error_msg = result.get(\"error_message\", \"Unknown error\")\n logger.explore(\"SQL execution failed\", {\n \"query_id\": query_id,\n \"state\": state,\n \"error\": error_msg,\n })\n return {\n \"query_id\": query_id,\n \"status\": \"failed\",\n \"state\": state,\n \"error_message\": error_msg,\n \"results\": None,\n }\n\n if state in (\"pending\", \"running\", \"started\"):\n time.sleep(poll_interval_seconds)\n continue\n\n # Unknown state — treat as still running\n time.sleep(poll_interval_seconds)\n\n except Exception as e:\n logger.explore(\"Polling error, retrying\", {\n \"query_id\": query_id,\n \"attempt\": attempt + 1,\n \"error\": str(e),\n })\n time.sleep(poll_interval_seconds)\n continue\n\n # Timeout\n logger.explore(\"SQL execution polling timed out\", {\n \"query_id\": query_id,\n \"max_polls\": max_polls,\n })\n return {\n \"query_id\": query_id,\n \"status\": \"timeout\",\n \"error_message\": f\"Polling timed out after {max_polls} attempts\",\n \"results\": None,\n }\n # endregion poll_execution_status\n\n # region execute_and_poll [TYPE Function]\n # @PURPOSE: Execute SQL and wait for completion. One-shot convenience method.\n # @PRE: sql is valid SQL.\n # @POST: Returns final execution result.\n # @SIDE_EFFECT: Makes HTTP calls to Superset API.\n def execute_and_poll(\n self,\n sql: str,\n database_id: int | None = None,\n max_polls: int = 60,\n poll_interval_seconds: float = 2.0,\n ) -> dict[str, Any]:\n with belief_scope(\"SupersetSqlLabExecutor.execute_and_poll\"):\n exec_result = self.execute_sql(\n sql=sql,\n database_id=database_id,\n run_async=False, # Sync mode — Superset returns result directly\n )\n\n status = exec_result.get(\"status\", \"unknown\")\n\n # Sync mode: response already has the final status and data\n if status == \"success\":\n logger.reason(\"SQL execution completed (sync)\", {\n \"query_id\": exec_result.get(\"query_id\"),\n })\n return {\n \"query_id\": exec_result.get(\"query_id\"),\n \"status\": \"success\",\n \"state\": \"success\",\n \"rows_affected\": exec_result.get(\"raw_response\", {}).get(\"query\", {}).get(\"rows\"),\n \"error_message\": None,\n \"results\": exec_result.get(\"raw_response\", {}).get(\"data\"),\n \"completed_on\": exec_result.get(\"raw_response\", {}).get(\"query\", {}).get(\"endDttm\"),\n }\n\n # For async mode (fallback): poll for completion\n query_id = exec_result.get(\"query_id\")\n if not query_id:\n logger.explore(\"No query_id from SQL Lab execute\", {\n \"raw_response\": exec_result.get(\"raw_response\"),\n })\n return {\n \"status\": \"failed\",\n \"error_message\": \"No query_id returned from SQL Lab\",\n \"query_id\": None,\n }\n\n return self.poll_execution_status(\n query_id=str(query_id),\n max_polls=max_polls,\n poll_interval_seconds=poll_interval_seconds,\n )\n # endregion execute_and_poll\n\n # region get_query_results [TYPE Function]\n # @PURPOSE: Fetch the results of a completed query.\n # @PRE: query_id is a valid Superset query ID.\n # @POST: Returns query results if available.\n # @SIDE_EFFECT: Makes HTTP GET to Superset.\n def get_query_results(self, query_id: str) -> dict[str, Any]:\n with belief_scope(\"SupersetSqlLabExecutor.get_query_results\"):\n client = self._get_client()\n try:\n response = client.network.request(\n method=\"GET\",\n endpoint=f\"/api/v1/query/{query_id}/results\",\n )\n result = response if isinstance(response, dict) else {}\n return {\n \"query_id\": query_id,\n \"status\": \"success\" if result.get(\"results\") else \"no_results\",\n \"results\": result.get(\"results\"),\n }\n except Exception as e:\n logger.explore(\"Failed to fetch query results\", {\n \"query_id\": query_id,\n \"error\": str(e),\n })\n return {\n \"query_id\": query_id,\n \"status\": \"failed\",\n \"error_message\": str(e),\n \"results\": None,\n }\n # endregion get_query_results\n\n\n# #endregion SupersetSqlLabExecutor\n# #endregion SupersetSqlLabExecutor\n" + "body": "# #region SupersetSqlLabExecutor [C:4] [TYPE Module] [SEMANTICS translate, superset, sqllab, execute, query]\n# @BRIEF Submit SQL to Superset SQL Lab API and poll execution status.\n# @LAYER Infrastructure\n# @RELATION DEPENDS_ON -> [ConfigManager]\n# @RELATION DEPENDS_ON -> [ConfigManager]\n# @PRE Valid Superset environment configuration and authenticated client.\n# @POST SQL is submitted to Superset SQL Lab; execution reference is returned.\n# @SIDE_EFFECT Makes HTTP calls to Superset /api/v1/sqllab/execute/; polls status.\n# @RATIONALE Direct SQL Lab API submission provides audit trail and execution monitoring within Superset.\n# @REJECTED Direct database connection bypass — would skip Superset's SQL Lab audit and RBAC.\n\nimport json\nimport time\nimport uuid\nfrom typing import Any\n\nfrom ...core.config_manager import ConfigManager\nfrom ...core.logger import belief_scope, logger\nfrom ...core.superset_client import SupersetClient\n\n\n# #region SupersetSqlLabExecutor [C:4] [TYPE Class]\n# @BRIEF Submit SQL to Superset SQL Lab API with polling and status tracking.\n# @PRE Valid environment ID and ConfigManager.\n# @POST SQL submitted to Superset; execution reference recorded.\n# @SIDE_EFFECT Makes HTTP POST to /api/v1/sqllab/execute/; polls GET for status.\nclass SupersetSqlLabExecutor:\n\n def __init__(self, config_manager: ConfigManager, env_id: str):\n self.config_manager = config_manager\n self.env_id = env_id\n self._client: SupersetClient | None = None\n self._database_id: int | None = None\n self._database_backend: str | None = None\n\n # region _get_client [TYPE Function]\n # @PURPOSE Lazy-initialize SupersetClient for the configured environment.\n # @PRE env_id must correspond to a valid environment config.\n # @POST Returns authenticated SupersetClient.\n # @SIDE_EFFECT Authenticates against Superset API.\n def _get_client(self) -> SupersetClient:\n with belief_scope(\"SupersetSqlLabExecutor._get_client\"):\n if self._client is not None:\n return self._client\n\n environments = self.config_manager.get_environments()\n env_config = next((e for e in environments if e.id == self.env_id), None)\n if not env_config:\n raise ValueError(f\"Superset environment '{self.env_id}' not found\")\n\n self._client = SupersetClient(env_config)\n return self._client\n # endregion _get_client\n\n # region resolve_database_id [TYPE Function]\n # @PURPOSE Resolve the target database ID from the environment and fetch its backend engine.\n # @PRE database_name or database_id should be known.\n # @POST Returns database_id integer or raises ValueError. Also sets self._database_backend.\n # @SIDE_EFFECT Fetches databases list from Superset; optionally fetches single DB for backend.\n def resolve_database_id(\n self,\n database_name: str | None = None,\n target_database_id: str | None = None,\n ) -> int:\n with belief_scope(\"SupersetSqlLabExecutor.resolve_database_id\"):\n client = self._get_client()\n\n # If a specific target_database_id is provided, use it directly\n if target_database_id:\n try:\n db_id = int(target_database_id)\n # Fetch full DB info to get backend\n db_info = client.get_database(db_id)\n result = db_info.get(\"result\", db_info)\n self._database_id = result.get(\"id\", db_id)\n self._database_backend = result.get(\"backend\") or result.get(\"engine\")\n logger.reason(\"Resolved database ID by target_database_id\", {\n \"target_database_id\": target_database_id,\n \"database_id\": self._database_id,\n \"backend\": self._database_backend,\n })\n return self._database_id\n except Exception as e:\n logger.explore(\"Failed to resolve by target_database_id, falling back\", {\n \"target_database_id\": target_database_id,\n \"error\": str(e),\n })\n # Fall through to default resolution\n\n # Fetch full database list with backend info\n _, databases = client.get_databases(\n query={\"columns\": [\"id\", \"database_name\", \"backend\"]}\n )\n if not databases:\n raise ValueError(\"No databases found in Superset environment\")\n\n if database_name:\n for db in databases:\n if db.get(\"database_name\", \"\").lower() == database_name.lower():\n self._database_id = db[\"id\"]\n self._database_backend = db.get(\"backend\")\n logger.reason(\"Resolved database ID by name\", {\n \"database_name\": database_name,\n \"database_id\": self._database_id,\n \"backend\": self._database_backend,\n })\n return self._database_id\n raise ValueError(\n f\"Database '{database_name}' not found in Superset environment\"\n )\n\n # Default: use first database\n self._database_id = databases[0][\"id\"]\n self._database_backend = databases[0].get(\"backend\")\n logger.reason(\"Using default database\", {\n \"database_name\": databases[0].get(\"database_name\"),\n \"database_id\": self._database_id,\n \"backend\": self._database_backend,\n })\n return self._database_id\n # endregion resolve_database_id\n\n # region get_database_backend [TYPE Function]\n # @PURPOSE Return the cached database backend/engine string.\n # @POST Returns backend string or None if not yet resolved.\n def get_database_backend(self) -> str | None:\n return self._database_backend\n # endregion get_database_backend\n\n # region execute_sql [TYPE Function]\n # @PURPOSE Submit SQL to Superset SQL Lab and return execution tracking info.\n # @PRE sql is valid SQL string. database_id is a valid Superset DB ID.\n # @POST Returns execution result dict with query_id and status.\n # @SIDE_EFFECT Makes HTTP POST to /api/v1/sqllab/execute/.\n def execute_sql(\n self,\n sql: str,\n database_id: int | None = None,\n run_async: bool = True,\n ) -> dict[str, Any]:\n with belief_scope(\"SupersetSqlLabExecutor.execute_sql\"):\n client = self._get_client()\n db_id = database_id or self._database_id\n if not db_id:\n db_id = self.resolve_database_id()\n\n logger.reason(\"Submitting SQL to Superset SQL Lab\", {\n \"database_id\": db_id,\n \"sql_length\": len(sql),\n \"run_async\": run_async,\n })\n\n payload = {\n \"database_id\": db_id,\n \"sql\": sql,\n \"runAsync\": run_async,\n \"schema\": None,\n \"tab\": \"translation-insert\",\n \"client_id\": f\"trl-{uuid.uuid4().hex[:7]}\", # max 11 chars for Superset varchar(11)\n }\n\n # Try multiple SQL Lab endpoints — some Superset versions use /sqllab/execute/,\n # others use /sql_lab/execute/ (with underscore)\n candidate_endpoints = [\"/api/v1/sqllab/execute/\", \"/api/v1/sql_lab/execute/\"]\n response = None\n last_error = None\n for endpoint in candidate_endpoints:\n try:\n response = client.network.request(\n method=\"POST\",\n endpoint=endpoint,\n data=json.dumps(payload),\n headers={\"Content-Type\": \"application/json\"},\n )\n if isinstance(response, dict) and response:\n logger.reason(\"SQL Lab endpoint succeeded\", extra={\n \"endpoint\": endpoint,\n })\n break\n except Exception as ep_err:\n last_error = ep_err\n logger.explore(\"SQL Lab endpoint failed, trying next\", extra={\n \"error\": str(ep_err),\n \"endpoint\": endpoint,\n })\n response = None\n\n if response is None:\n error_msg = f\"All SQL Lab endpoints failed. Last error: {last_error}\"\n logger.explore(\"SQL Lab execute failed\", extra={\"error\": error_msg})\n raise ValueError(f\"Superset SQL Lab execute failed: {error_msg}\")\n\n # Parse response — try multiple known Superset response formats\n result = response if isinstance(response, dict) else {}\n\n # Superset may return query_id at top level, nested in \"result\", as \"id\", or in \"query\" block\n query_id = (\n result.get(\"query_id\")\n or result.get(\"id\")\n or (result.get(\"result\") or {}).get(\"query_id\")\n or (result.get(\"result\") or {}).get(\"id\")\n or (result.get(\"query\") or {}).get(\"query_id\")\n or (result.get(\"query\") or {}).get(\"id\")\n )\n status = result.get(\"status\", \"unknown\")\n\n # Log full response for debugging if query_id is missing\n if not query_id:\n logger.explore(\"No query_id from SQL Lab execute\", extra={\n \"database_id\": db_id,\n \"status\": status,\n \"raw_response_keys\": list(result.keys()),\n \"raw_response_preview\": json.dumps(result)[:2000],\n })\n logger.reason(\"SQL Lab execute response (no query_id)\", extra={\n \"database_id\": db_id,\n \"status\": status,\n \"response_keys\": list(result.keys()),\n \"has_result\": \"result\" in result,\n \"has_query\": \"query\" in result,\n \"raw_preview\": json.dumps(result)[:2000],\n })\n\n logger.reason(\"SQL Lab execute response\", {\n \"query_id\": query_id,\n \"status\": status,\n \"response_keys\": list(result.keys()),\n })\n\n return {\n \"query_id\": query_id,\n \"status\": status,\n \"raw_response\": result,\n \"database_id\": db_id,\n }\n # endregion execute_sql\n\n # region poll_execution_status [TYPE Function]\n # @PURPOSE Poll Superset for SQL execution status until completion or timeout.\n # @PRE query_id is a valid Superset query ID.\n # @POST Returns final execution status dict.\n # @SIDE_EFFECT Makes HTTP GET requests to Superset.\n def poll_execution_status(\n self,\n query_id: str,\n max_polls: int = 60,\n poll_interval_seconds: float = 2.0,\n ) -> dict[str, Any]:\n with belief_scope(\"SupersetSqlLabExecutor.poll_execution_status\"):\n client = self._get_client()\n\n logger.reason(\"Polling SQL execution status\", {\n \"query_id\": query_id,\n \"max_polls\": max_polls,\n \"interval\": poll_interval_seconds,\n })\n\n for attempt in range(max_polls):\n try:\n response = client.network.request(\n method=\"GET\",\n endpoint=f\"/api/v1/query/{query_id}\",\n )\n raw = response if isinstance(response, dict) else {}\n # Superset wraps query data in a nested \"result\" field\n result = raw.get(\"result\", raw)\n status = result.get(\"status\", \"unknown\")\n state = result.get(\"state\", result.get(\"status\", \"\"))\n\n # Terminal states\n if state in (\"success\", \"finished\", \"completed\"):\n logger.reason(\"SQL execution completed\", {\n \"query_id\": query_id,\n \"attempt\": attempt + 1,\n \"rows_affected\": result.get(\"rows\"),\n })\n return {\n \"query_id\": query_id,\n \"status\": \"success\",\n \"state\": state,\n \"rows_affected\": result.get(\"rows\"),\n \"error_message\": result.get(\"error_message\"),\n \"results\": result.get(\"results\"),\n \"completed_on\": result.get(\"completed_on\"),\n }\n\n if state in (\"failed\", \"error\", \"stopped\"):\n error_msg = result.get(\"error_message\", \"Unknown error\")\n logger.explore(\"SQL execution failed\", {\n \"query_id\": query_id,\n \"state\": state,\n \"error\": error_msg,\n })\n return {\n \"query_id\": query_id,\n \"status\": \"failed\",\n \"state\": state,\n \"error_message\": error_msg,\n \"results\": None,\n }\n\n if state in (\"pending\", \"running\", \"started\"):\n time.sleep(poll_interval_seconds)\n continue\n\n # Unknown state — treat as still running\n time.sleep(poll_interval_seconds)\n\n except Exception as e:\n logger.explore(\"Polling error, retrying\", {\n \"query_id\": query_id,\n \"attempt\": attempt + 1,\n \"error\": str(e),\n })\n time.sleep(poll_interval_seconds)\n continue\n\n # Timeout\n logger.explore(\"SQL execution polling timed out\", {\n \"query_id\": query_id,\n \"max_polls\": max_polls,\n })\n return {\n \"query_id\": query_id,\n \"status\": \"timeout\",\n \"error_message\": f\"Polling timed out after {max_polls} attempts\",\n \"results\": None,\n }\n # endregion poll_execution_status\n\n # region execute_and_poll [TYPE Function]\n # @PURPOSE Execute SQL and wait for completion. One-shot convenience method.\n # @PRE sql is valid SQL.\n # @POST Returns final execution result.\n # @SIDE_EFFECT Makes HTTP calls to Superset API.\n def execute_and_poll(\n self,\n sql: str,\n database_id: int | None = None,\n max_polls: int = 60,\n poll_interval_seconds: float = 2.0,\n ) -> dict[str, Any]:\n with belief_scope(\"SupersetSqlLabExecutor.execute_and_poll\"):\n exec_result = self.execute_sql(\n sql=sql,\n database_id=database_id,\n run_async=False, # Sync mode — Superset returns result directly\n )\n\n status = exec_result.get(\"status\", \"unknown\")\n\n # Sync mode: response already has the final status and data\n if status == \"success\":\n logger.reason(\"SQL execution completed (sync)\", {\n \"query_id\": exec_result.get(\"query_id\"),\n })\n return {\n \"query_id\": exec_result.get(\"query_id\"),\n \"status\": \"success\",\n \"state\": \"success\",\n \"rows_affected\": exec_result.get(\"raw_response\", {}).get(\"query\", {}).get(\"rows\"),\n \"error_message\": None,\n \"results\": exec_result.get(\"raw_response\", {}).get(\"data\"),\n \"completed_on\": exec_result.get(\"raw_response\", {}).get(\"query\", {}).get(\"endDttm\"),\n }\n\n # For async mode (fallback): poll for completion\n query_id = exec_result.get(\"query_id\")\n if not query_id:\n logger.explore(\"No query_id from SQL Lab execute\", {\n \"raw_response\": exec_result.get(\"raw_response\"),\n })\n return {\n \"status\": \"failed\",\n \"error_message\": \"No query_id returned from SQL Lab\",\n \"query_id\": None,\n }\n\n return self.poll_execution_status(\n query_id=str(query_id),\n max_polls=max_polls,\n poll_interval_seconds=poll_interval_seconds,\n )\n # endregion execute_and_poll\n\n # region get_query_results [TYPE Function]\n # @PURPOSE Fetch the results of a completed query.\n # @PRE query_id is a valid Superset query ID.\n # @POST Returns query results if available.\n # @SIDE_EFFECT Makes HTTP GET to Superset.\n def get_query_results(self, query_id: str) -> dict[str, Any]:\n with belief_scope(\"SupersetSqlLabExecutor.get_query_results\"):\n client = self._get_client()\n try:\n response = client.network.request(\n method=\"GET\",\n endpoint=f\"/api/v1/query/{query_id}/results\",\n )\n result = response if isinstance(response, dict) else {}\n return {\n \"query_id\": query_id,\n \"status\": \"success\" if result.get(\"results\") else \"no_results\",\n \"results\": result.get(\"results\"),\n }\n except Exception as e:\n logger.explore(\"Failed to fetch query results\", {\n \"query_id\": query_id,\n \"error\": str(e),\n })\n return {\n \"query_id\": query_id,\n \"status\": \"failed\",\n \"error_message\": str(e),\n \"results\": None,\n }\n # endregion get_query_results\n\n\n# #endregion SupersetSqlLabExecutor\n# #endregion SupersetSqlLabExecutor\n" }, { "contract_id": "SchemasPackage", @@ -52090,10 +51322,10 @@ "file_path": "backend/src/schemas/translate.py", "start_line": 1, "end_line": 688, - "tier": "TIER_1", - "complexity": 2, + "tier": "TIER_2", + "complexity": 3, "metadata": { - "COMPLEXITY": 2, + "COMPLEXITY": 3, "LAYER": "API", "PURPOSE": "Pydantic v2 schemas for translation API request/response serialization." }, @@ -52105,20 +51337,10 @@ "target_ref": "pydantic" } ], - "schema_warnings": [ - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Module' at C2", - "detail": { - "actual_complexity": 2, - "contract_type": "Module" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region TranslateSchemas [C:2] [TYPE Module] [SEMANTICS pydantic, translate, schema, translate-job-create]\n# @BRIEF Pydantic v2 schemas for translation API request/response serialization.\n# @LAYER: API\n# @RELATION DEPENDS_ON -> pydantic\n\nfrom datetime import datetime\nfrom typing import Any\n\nimport re\n\nfrom pydantic import BaseModel, Field, field_validator\n\n\ndef _validate_bcp47_list(v: list[str] | None) -> list[str] | None:\n \"\"\"Validate that each item in target_languages is a valid BCP-47 tag.\"\"\"\n if not v:\n return v\n for tag in v:\n if not tag or not tag.strip():\n raise ValueError(f\"Invalid BCP-47 tag: '{tag}' — must be a non-empty string\")\n if not re.match(r'^[a-zA-Z]{2,8}(-[a-zA-Z0-9]{1,8})*$', tag.strip()):\n raise ValueError(\n f\"Invalid BCP-47 tag: '{tag}'. \"\n \"Expected format like 'en', 'ru', 'zh-CN', 'pt-BR'.\"\n )\n return v\n\n\n# #region TranslateJobCreate [TYPE Class]\n# @BRIEF Schema for creating a new translation job.\nclass TranslateJobCreate(BaseModel):\n name: str\n description: str | None = None\n source_dialect: str = Field(..., description=\"Source database dialect (e.g. postgresql, clickhouse)\")\n target_dialect: str = Field(..., description=\"Target database dialect (e.g. postgresql, clickhouse)\")\n database_dialect: str | None = Field(None, description=\"Detected dialect from Superset connection at save time\")\n source_datasource_id: str | None = Field(None, description=\"Superset datasource ID\")\n source_table: str | None = Field(None, description=\"Source table name\")\n target_schema: str | None = Field(None, description=\"Target table schema\")\n target_table: str | None = Field(None, description=\"Target table name\")\n source_key_cols: list[str] | None = Field(default_factory=list, description=\"Source key column names\")\n target_key_cols: list[str] | None = Field(default_factory=list, description=\"Target key column names\")\n translation_column: str | None = Field(None, description=\"Source column to translate\")\n target_column: str | None = Field(None, description=\"Target column for translated output (defaults to translation_column)\")\n target_language_column: str | None = Field(None, description=\"Target column for language code (e.g. 'ru', 'en')\")\n target_source_column: str | None = Field(None, description=\"Target column for source/original text\")\n target_source_language_column: str | None = Field(None, description=\"Target column for detected source language (BCP-47)\")\n context_columns: list[str] | None = Field(default_factory=list, description=\"Context column names\")\n target_language: str | None = Field(None, description=\"Target language code [DEPRECATED: use target_languages]\")\n target_languages: list[str] | None = Field(default_factory=list, description=\"List of BCP-47 target language codes\")\n provider_id: str | None = Field(None, description=\"LLM provider ID\")\n\n _validate_target_languages = field_validator(\"target_languages\")(_validate_bcp47_list)\n batch_size: int = Field(50, description=\"Records per batch\")\n disable_reasoning: bool = Field(False, description=\"If true, pass reasoning_effort:none to LLM to save output tokens\")\n upsert_strategy: str = Field(\"MERGE\", description=\"UPSERT strategy: MERGE, INSERT, UPDATE\")\n dictionary_ids: list[str] | None = Field(default_factory=list, description=\"Associated terminology dictionary IDs\")\n environment_id: str | None = Field(None, description=\"Superset environment ID\")\n target_database_id: str | None = Field(None, description=\"Superset database ID for SQL Lab insert target\")\n# #endregion TranslateJobCreate\n\n\n# #region TranslateJobUpdate [TYPE Class]\n# @BRIEF Schema for updating an existing translation job.\nclass TranslateJobUpdate(BaseModel):\n name: str | None = None\n description: str | None = None\n source_dialect: str | None = None\n target_dialect: str | None = None\n database_dialect: str | None = None\n source_datasource_id: str | None = None\n source_table: str | None = None\n target_schema: str | None = None\n target_table: str | None = None\n source_key_cols: list[str] | None = None\n target_key_cols: list[str] | None = None\n translation_column: str | None = None\n target_column: str | None = None\n target_language_column: str | None = None\n target_source_column: str | None = None\n target_source_language_column: str | None = None\n context_columns: list[str] | None = None\n target_language: str | None = None\n target_languages: list[str] | None = None\n provider_id: str | None = None\n\n _validate_target_languages = field_validator(\"target_languages\")(_validate_bcp47_list)\n batch_size: int | None = None\n disable_reasoning: bool | None = None\n upsert_strategy: str | None = None\n status: str | None = None\n dictionary_ids: list[str] | None = None\n environment_id: str | None = None\n target_database_id: str | None = None\n# #endregion TranslateJobUpdate\n\n\n# #region TranslateJobResponse [TYPE Class]\n# @BRIEF Schema for translation job API responses.\nclass TranslateJobResponse(BaseModel):\n id: str\n name: str\n description: str | None = None\n source_dialect: str\n target_dialect: str\n database_dialect: str | None = None\n source_datasource_id: str | None = None\n source_table: str | None = None\n target_schema: str | None = None\n target_table: str | None = None\n source_key_cols: list[str] | None = None\n target_key_cols: list[str] | None = None\n translation_column: str | None = None\n target_column: str | None = None\n target_language_column: str | None = None\n target_source_column: str | None = None\n target_source_language_column: str | None = None\n context_columns: list[str] | None = None\n # source_language removed — deprecated, per-row auto-detected\n target_languages: list[str] | None = None\n provider_id: str | None = None\n batch_size: int = 50\n disable_reasoning: bool = False\n upsert_strategy: str = \"MERGE\"\n status: str\n created_by: str | None = None\n created_at: datetime\n updated_at: datetime | None = None\n dictionary_ids: list[str] | None = None\n environment_id: str | None = None\n target_database_id: str | None = None\n\n class Config:\n from_attributes = True\n# #endregion TranslateJobResponse\n\n\n# #region DatasourceColumnResponse [TYPE Class]\n# @BRIEF Schema for datasource column metadata response.\nclass DatasourceColumnResponse(BaseModel):\n name: str\n type: str | None = None\n is_physical: bool = True\n is_dttm: bool = False\n description: str | None = None\n\n# #endregion DatasourceColumnResponse\n\n# #region DatasourceColumnsResponse [TYPE Class]\n# @BRIEF Schema for datasource columns endpoint response.\nclass DatasourceColumnsResponse(BaseModel):\n datasource_id: int\n datasource_name: str | None = None\n schema_name: str | None = None\n database_dialect: str\n columns: list[DatasourceColumnResponse] = []\n\n class Config:\n from_attributes = True\n# #endregion DatasourceColumnsResponse\n\n\n# #region DuplicateJobResponse [TYPE Class]\n# @BRIEF Schema for duplicate job response.\nclass DuplicateJobResponse(BaseModel):\n id: str\n name: str\n message: str = \"Job duplicated successfully\"\n\n class Config:\n from_attributes = True\n# #endregion DuplicateJobResponse\n\n\n# #region DictionaryCreate [TYPE Class]\n# @BRIEF Schema for creating a new terminology dictionary.\nclass DictionaryCreate(BaseModel):\n name: str\n description: str | None = None\n source_dialect: str\n target_dialect: str\n is_active: bool = True\n# #endregion DictionaryCreate\n\n\n# #region DictionaryImport [TYPE Class]\n# @BRIEF Schema for importing entries into a terminology dictionary.\nclass DictionaryImport(BaseModel):\n content: str = Field(..., description=\"CSV or TSV content as raw string\")\n delimiter: str | None = Field(None, description=\"Detected or forced delimiter: ',' or '\\\\t'. Auto-detect if omitted.\")\n on_conflict: str = Field(\"overwrite\", description=\"'overwrite' or 'keep_existing' or 'cancel'\")\n preview_only: bool = Field(False, description=\"If true, return preview without applying\")\n default_source_language: str | None = Field(None, description=\"Default BCP-47 source language when column missing in data\")\n default_target_language: str | None = Field(None, description=\"Default BCP-47 target language when column missing in data\")\n# #endregion DictionaryImport\n\n\n# #region DictionaryResponse [TYPE Class]\n# @BRIEF Schema for terminology dictionary API responses.\nclass DictionaryResponse(BaseModel):\n id: str\n name: str\n description: str | None = None\n source_dialect: str\n target_dialect: str\n is_active: bool\n created_by: str | None = None\n created_at: datetime\n updated_at: datetime | None = None\n entry_count: int | None = None\n\n class Config:\n from_attributes = True\n# #endregion DictionaryResponse\n\n\n# #region DictionaryEntryCreate [TYPE Class]\n# @BRIEF Schema for adding/editing a dictionary entry with language pair support.\nclass DictionaryEntryCreate(BaseModel):\n source_term: str = Field(..., description=\"Source term to translate\")\n target_term: str = Field(..., description=\"Target/translated term\")\n context_notes: str | None = Field(None, description=\"Optional context notes\")\n source_language: str = Field(\"und\", description=\"BCP-47 source language code (default: und)\")\n target_language: str = Field(\"und\", description=\"BCP-47 target language code (default: und)\")\n\n class Config:\n from_attributes = True\n# #endregion DictionaryEntryCreate\n\n\n# #region DictionaryEntryResponse [TYPE Class]\n# @BRIEF Schema for dictionary entry API responses.\nclass DictionaryEntryResponse(BaseModel):\n id: str\n dictionary_id: str\n source_term: str\n source_term_normalized: str\n target_term: str\n source_language: str | None = None\n target_language: str | None = None\n context_notes: str | None = None\n context_data: dict[str, Any] | None = None\n usage_notes: str | None = None\n has_context: bool = False\n context_source: str | None = None\n origin_source_language: str | None = None\n created_at: datetime\n updated_at: datetime | None = None\n\n class Config:\n from_attributes = True\n# #endregion DictionaryEntryResponse\n\n\n# #region DictionaryImportResult [TYPE Class]\n# @BRIEF Schema for dictionary import result.\nclass DictionaryImportResult(BaseModel):\n total: int = 0\n created: int = 0\n updated: int = 0\n skipped: int = 0\n errors: list[dict[str, Any]] = Field(default_factory=list)\n preview: list[dict[str, Any]] = Field(default_factory=list, description=\"Preview rows with conflict flags\")\n# #endregion DictionaryImportResult\n\n\n# #region PreviewRequest [TYPE Class]\n# @BRIEF Schema for triggering a translation preview.\nclass PreviewRequest(BaseModel):\n sample_size: int = Field(10, ge=1, le=100, description=\"Number of sample rows to preview\")\n prompt_template: str | None = Field(None, description=\"Optional custom prompt template\")\n env_id: str | None = Field(None, description=\"Superset environment ID for preview data fetch\")\n# #endregion PreviewRequest\n\n\n# #region PreviewRowUpdate [TYPE Class]\n# @BRIEF Schema for approving/editing/rejecting a preview row.\nclass PreviewRowUpdate(BaseModel):\n action: str = Field(..., description=\"'approve', 'reject', or 'edit'\")\n translation: str | None = Field(None, description=\"Edited translation (required for 'edit' action)\")\n feedback: str | None = Field(None, description=\"Optional feedback/comment\")\n language_code: str | None = Field(None, description=\"BCP-47 language code for per-language actions (optional, applies to all if omitted)\")\n# #endregion PreviewRowUpdate\n\n\n# #region PreviewAcceptResponse [TYPE Class]\n# @BRIEF Schema for preview accept response.\nclass PreviewAcceptResponse(BaseModel):\n id: str\n job_id: str\n status: str\n created_by: str | None = None\n created_at: datetime\n expires_at: datetime | None = None\n records: list['PreviewRow'] = []\n target_languages: list[str] = Field(default_factory=list, description=\"Target languages for this preview\")\n\n class Config:\n from_attributes = True\n# #endregion PreviewAcceptResponse\n\n\n# #region CostEstimate [TYPE Class]\n# @BRIEF Schema for cost estimation in preview response.\nclass CostEstimate(BaseModel):\n sample_size: int = 0\n num_languages: int = 1\n sample_prompt_tokens: int = 0\n sample_output_tokens: int = 0\n sample_total_tokens: int = 0\n sample_cost: float = 0.0\n estimated_total_rows: int = 0\n estimated_tokens: int = 0\n estimated_cost: float = 0.0\n warning: str | None = None\n# #endregion CostEstimate\n\n\n# #region TermCorrectionSubmit [TYPE Class]\n# @BRIEF Schema for submitting a term correction in a translation preview.\nclass TermCorrectionSubmit(BaseModel):\n source_term: str\n incorrect_target_term: str\n corrected_target_term: str\n dictionary_id: str | None = Field(None, description=\"Target dictionary ID (language-filtered)\")\n origin_run_id: str | None = Field(None, description=\"Run ID from which this correction originated\")\n origin_row_key: str | None = Field(None, description=\"Row key within the run\")\n context_data: dict[str, Any] | None = Field(None, description=\"Context data for the dictionary entry\")\n usage_notes: str | None = Field(None, description=\"Usage notes for the dictionary entry\")\n keep_context: bool = Field(True, description=\"If false, clear context_data and set has_context=False on dictionary entry\")\n# #endregion TermCorrectionSubmit\n\n\n# #region TermCorrectionBulkSubmit [TYPE Class]\n# @BRIEF Schema for submitting multiple term corrections atomically.\nclass TermCorrectionBulkSubmit(BaseModel):\n corrections: list[TermCorrectionSubmit]\n dictionary_id: str = Field(..., description=\"Target dictionary ID for all corrections\")\n# #endregion TermCorrectionBulkSubmit\n\n\n# #region CorrectionConflictResult [TYPE Class]\n# @BRIEF Schema for reporting conflicts in correction submission.\nclass CorrectionConflictResult(BaseModel):\n source_term: str\n existing_target_term: str\n submitted_target_term: str\n action: str = \"keep_existing\"\n# #endregion CorrectionConflictResult\n\n\n# #region CorrectionSubmitResponse [TYPE Class]\n# @BRIEF Schema for correction submission response.\nclass CorrectionSubmitResponse(BaseModel):\n entry_id: str | None = None\n action: str # \"created\", \"updated\", \"conflict_detected\", \"skipped\"\n source_term: str\n target_term: str\n conflict: CorrectionConflictResult | None = None\n message: str | None = None\n# #endregion CorrectionSubmitResponse\n\n\n# #region ScheduleConfig [TYPE Class]\n# @BRIEF Schema for configuring a recurring translation schedule.\nclass ScheduleConfig(BaseModel):\n cron_expression: str = Field(..., description=\"Cron expression for scheduling (e.g. '0 2 * * *')\")\n timezone: str = Field(\"UTC\", description=\"Timezone for the cron schedule (e.g. 'UTC', 'Europe/Moscow')\")\n is_active: bool = True\n execution_mode: str = Field(\"full\", description=\"Translation execution mode: 'full' or 'new_key_only'\")\n# #endregion ScheduleConfig\n\n\n# #region ScheduleResponse [TYPE Class]\n# @BRIEF Schema for schedule API responses.\nclass ScheduleResponse(BaseModel):\n id: str\n job_id: str\n cron_expression: str\n timezone: str\n is_active: bool\n execution_mode: str = \"full\"\n last_run_at: datetime | None = None\n next_run_at: datetime | None = None\n created_by: str | None = None\n created_at: datetime\n updated_at: datetime | None = None\n\n class Config:\n from_attributes = True\n# #endregion ScheduleResponse\n\n\n# #region NextExecutionResponse [TYPE Class]\n# @BRIEF Schema for next execution preview.\nclass NextExecutionResponse(BaseModel):\n job_id: str\n cron_expression: str\n timezone: str\n next_executions: list[str] = []\n# #endregion NextExecutionResponse\n\n\n# #region TranslationRunResponse [TYPE Class]\n# @BRIEF Schema for translation run API responses.\nclass TranslationRunResponse(BaseModel):\n id: str\n job_id: str\n status: str\n trigger_type: str | None = None\n started_at: datetime | None = None\n completed_at: datetime | None = None\n error_message: str | None = None\n total_records: int = 0\n successful_records: int = 0\n failed_records: int = 0\n skipped_records: int = 0\n insert_status: str | None = None\n superset_execution_id: str | None = None\n config_snapshot: dict[str, Any] | None = None\n key_hash: str | None = None\n config_hash: str | None = None\n dict_snapshot_hash: str | None = None\n created_by: str | None = None\n created_at: datetime\n language_stats: list['TranslationRunLanguageStatsResponse'] | None = None\n\n class Config:\n from_attributes = True\n# #endregion TranslationRunResponse\n\n\n# #region RunDetailResponse [TYPE Class]\n# @BRIEF Schema for detailed run response including records, events, and config snapshot.\nclass RunDetailResponse(BaseModel):\n run: TranslationRunResponse\n records: list[dict[str, Any]] = Field(default_factory=list, description=\"Paginated translation records\")\n events: list[dict[str, Any]] = Field(default_factory=list, description=\"Run events\")\n event_invariants: dict[str, Any] | None = None\n batch_count: int = 0\n# #endregion RunDetailResponse\n\n\n# #region RunHistoryFilter [TYPE Class]\n# @BRIEF Schema for filtering run history.\nclass RunHistoryFilter(BaseModel):\n job_id: str | None = None\n status: str | None = None\n trigger_type: str | None = None\n created_by: str | None = None\n date_from: datetime | None = None\n date_to: datetime | None = None\n page: int = 1\n page_size: int = 20\n# #endregion RunHistoryFilter\n\n\n# #region RunListResponse [TYPE Class]\n# @BRIEF Schema for paginated run list response.\nclass RunListResponse(BaseModel):\n items: list[TranslationRunResponse] = []\n total: int = 0\n page: int = 1\n page_size: int = 20\n# #endregion RunListResponse\n\n\n# #region AggregatedMetricsResponse [TYPE Class]\n# @BRIEF Schema for aggregated per-job metrics.\nclass AggregatedMetricsResponse(BaseModel):\n job_id: str\n total_runs: int = 0\n successful_runs: int = 0\n failed_runs: int = 0\n cancelled_runs: int = 0\n total_records: int = 0\n successful_records: int = 0\n failed_records: int = 0\n skipped_records: int = 0\n cumulative_tokens: int | None = None\n cumulative_cost: float | None = None\n avg_duration_ms: float | None = None\n last_run_at: datetime | None = None\n next_scheduled_run: datetime | None = None\n# #endregion AggregatedMetricsResponse\n\n\n# #region TranslationPreviewLanguageResponse [C:1] [TYPE Class]\n# @BRIEF Schema for per-language preview data in API responses.\nclass TranslationPreviewLanguageResponse(BaseModel):\n language_code: str\n source_language_detected: str | None = None\n translated_value: str | None = None\n user_edit: str | None = None\n final_value: str | None = None\n status: str = \"pending\"\n needs_review: bool = False\n\n class Config:\n from_attributes = True\n# #endregion TranslationPreviewLanguageResponse\n\n\n# #region PreviewRow [TYPE Class]\n# @BRIEF A single row in a translation preview showing original and translated content side by side.\nclass PreviewRow(BaseModel):\n id: str\n source_sql: str | None = None\n target_sql: str | None = None\n source_object_type: str | None = None\n source_object_id: str | None = None\n source_object_name: str | None = None\n status: str = \"PENDING\"\n feedback: str | None = None\n source_language_detected: str | None = None\n needs_review: bool = False\n languages: list[TranslationPreviewLanguageResponse] = Field(default_factory=list, description=\"Per-language translation data\")\n# #endregion PreviewRow\n\n\n# #region TranslationPreviewResponse [TYPE Class]\n# @BRIEF Schema for translation preview session API responses.\nclass TranslationPreviewResponse(BaseModel):\n id: str\n job_id: str\n run_id: str | None = None\n status: str\n created_by: str | None = None\n created_at: datetime\n expires_at: datetime | None = None\n records: list[PreviewRow] = []\n target_languages: list[str] = Field(default_factory=list, description=\"Target languages for this preview\")\n cost_estimate: CostEstimate | None = None\n\n class Config:\n from_attributes = True\n# #endregion TranslationPreviewResponse\n\n\n# #region TranslationBatchResponse [TYPE Class]\n# @BRIEF Schema for translation batch API responses.\nclass TranslationBatchResponse(BaseModel):\n id: str\n run_id: str\n batch_index: int\n status: str\n total_records: int = 0\n successful_records: int = 0\n failed_records: int = 0\n started_at: datetime | None = None\n completed_at: datetime | None = None\n created_at: datetime\n\n class Config:\n from_attributes = True\n# #endregion TranslationBatchResponse\n\n\n# #region MetricsResponse [TYPE Class]\n# @BRIEF Schema for translation metrics API responses.\nclass MetricsResponse(BaseModel):\n job_id: str\n snapshot_date: datetime\n total_jobs: int = 0\n total_runs: int = 0\n total_records: int = 0\n successful_records: int = 0\n failed_records: int = 0\n skipped_records: int = 0\n avg_duration_ms: int | None = None\n p50_duration_ms: int | None = None\n p95_duration_ms: int | None = None\n p99_duration_ms: int | None = None\n\n class Config:\n from_attributes = True\n# #endregion MetricsResponse\n\n\n# #region TranslationLanguageResponse [C:1] [TYPE Class]\n# @BRIEF Schema for per-language translation data in API responses.\nclass TranslationLanguageResponse(BaseModel):\n language_code: str\n source_language_detected: str | None = None\n translated_value: str | None = None\n user_edit: str | None = None\n final_value: str | None = None\n status: str = \"pending\"\n needs_review: bool = False\n language_overridden: bool = False\n\n class Config:\n from_attributes = True\n# #endregion TranslationLanguageResponse\n\n\n# #region TranslationRunLanguageStatsResponse [C:1] [TYPE Class]\n# @BRIEF Schema for per-language statistics in run API responses.\nclass TranslationRunLanguageStatsResponse(BaseModel):\n language_code: str\n total_rows: int = 0\n translated_rows: int = 0\n failed_rows: int = 0\n skipped_rows: int = 0\n token_count: int = 0\n estimated_cost: float = 0.0\n\n class Config:\n from_attributes = True\n# #endregion TranslationRunLanguageStatsResponse\n\n\n# #region OverrideLanguageRequest [C:1] [TYPE Class]\n# @BRIEF Schema for manually overriding the detected source language of a translation.\nclass OverrideLanguageRequest(BaseModel):\n source_language: str = Field(..., description=\"BCP-47 language code to override with\")\n# #endregion OverrideLanguageRequest\n\n\n# #region BulkFindReplaceRequest [C:1] [TYPE Class]\n# @BRIEF Schema for bulk find-and-replace within translations for a target language.\nclass BulkFindReplaceRequest(BaseModel):\n find_pattern: str = Field(..., description=\"Text or regex pattern to find\", max_length=500)\n is_regex: bool = Field(False, description=\"Whether find_pattern is a regular expression\")\n replacement_text: str = Field(..., description=\"Replacement text\")\n target_language: str = Field(..., description=\"BCP-47 language code to target\")\n preview: bool = Field(True, description=\"If true, return matches without applying replacements\")\n submit_to_dictionary: bool = Field(False, description=\"If true, also submit corrections to dictionary\")\n dictionary_id: str | None = Field(None, description=\"Target dictionary ID for submitting corrections\")\n usage_notes: str | None = Field(None, description=\"Usage notes for dictionary entries\")\n submit_to_dictionary_with_context: bool = Field(False, description=\"If true, auto-capture source row context when submitting to dictionary\")\n\n @field_validator(\"find_pattern\")\n @classmethod\n def validate_pattern_length(cls, v: str) -> str:\n \"\"\"Reject patterns longer than 500 characters to prevent ReDoS.\"\"\"\n if len(v) > 500:\n raise ValueError(\"Pattern too long (max 500 characters)\")\n return v\n# #endregion BulkFindReplaceRequest\n\n\n# #region InlineEditRequest [C:1] [TYPE Class]\n# @BRIEF Schema for inline editing a translated value on a completed run result.\nclass InlineEditRequest(BaseModel):\n final_value: str = Field(..., description=\"Corrected/edited translation text\")\n submit_to_dictionary: bool = Field(False, description=\"If true, also save correction to dictionary\")\n dictionary_id: str | None = Field(None, description=\"Target dictionary ID for saving correction\")\n context_data_override: dict[str, Any] | None = Field(None, description=\"Override auto-captured context data for dictionary entry\")\n usage_notes: str | None = Field(None, description=\"Usage notes for the dictionary entry\")\n keep_context: bool = Field(True, description=\"If false, clear context_data and set has_context=False on dictionary entry\")\n# #endregion InlineEditRequest\n\n\n# #region BulkReplacePreviewItem [C:1] [TYPE Class]\n# @BRIEF A single item in a bulk replace preview list.\nclass BulkReplacePreviewItem(BaseModel):\n record_id: str\n language_code: str\n source_term: str\n current_value: str\n new_value: str\n source_language_detected: str | None = None\n# #endregion BulkReplacePreviewItem\n\n\n# #region BulkReplaceResponse [C:1] [TYPE Class]\n# @BRIEF Response for bulk find-and-replace operation.\nclass BulkReplaceResponse(BaseModel):\n rows_affected: int = 0\n corrections_submitted: int = 0\n preview: list[BulkReplacePreviewItem] = Field(default_factory=list)\n# #endregion BulkReplaceResponse\n\n\n# #region InlineCorrectionSubmit [C:1] [TYPE Class]\n# @BRIEF Schema for submitting an inline correction for a specific translated record.\nclass InlineCorrectionSubmit(BaseModel):\n record_id: str = Field(..., description=\"Translation record ID\")\n language_code: str = Field(..., description=\"BCP-47 language code of the translated value\")\n source_term: str = Field(..., description=\"Original source term that was incorrectly translated\")\n corrected_term: str = Field(..., description=\"Corrected translation\")\n dictionary_id: str = Field(..., description=\"Target dictionary ID to also save the correction\")\n source_language: str = Field(..., description=\"BCP-47 source language code\")\n# #endregion InlineCorrectionSubmit\n\n\n# #endregion TranslateSchemas\n" + "body": "# #region TranslateSchemas [C:3] [TYPE Module] [SEMANTICS pydantic, translate, schema, translate-job-create]\n# @BRIEF Pydantic v2 schemas for translation API request/response serialization.\n# @LAYER API\n# @RELATION DEPENDS_ON -> pydantic\n\nfrom datetime import datetime\nfrom typing import Any\n\nimport re\n\nfrom pydantic import BaseModel, Field, field_validator\n\n\ndef _validate_bcp47_list(v: list[str] | None) -> list[str] | None:\n \"\"\"Validate that each item in target_languages is a valid BCP-47 tag.\"\"\"\n if not v:\n return v\n for tag in v:\n if not tag or not tag.strip():\n raise ValueError(f\"Invalid BCP-47 tag: '{tag}' — must be a non-empty string\")\n if not re.match(r'^[a-zA-Z]{2,8}(-[a-zA-Z0-9]{1,8})*$', tag.strip()):\n raise ValueError(\n f\"Invalid BCP-47 tag: '{tag}'. \"\n \"Expected format like 'en', 'ru', 'zh-CN', 'pt-BR'.\"\n )\n return v\n\n\n# #region TranslateJobCreate [TYPE Class]\n# @BRIEF Schema for creating a new translation job.\nclass TranslateJobCreate(BaseModel):\n name: str\n description: str | None = None\n source_dialect: str = Field(..., description=\"Source database dialect (e.g. postgresql, clickhouse)\")\n target_dialect: str = Field(..., description=\"Target database dialect (e.g. postgresql, clickhouse)\")\n database_dialect: str | None = Field(None, description=\"Detected dialect from Superset connection at save time\")\n source_datasource_id: str | None = Field(None, description=\"Superset datasource ID\")\n source_table: str | None = Field(None, description=\"Source table name\")\n target_schema: str | None = Field(None, description=\"Target table schema\")\n target_table: str | None = Field(None, description=\"Target table name\")\n source_key_cols: list[str] | None = Field(default_factory=list, description=\"Source key column names\")\n target_key_cols: list[str] | None = Field(default_factory=list, description=\"Target key column names\")\n translation_column: str | None = Field(None, description=\"Source column to translate\")\n target_column: str | None = Field(None, description=\"Target column for translated output (defaults to translation_column)\")\n target_language_column: str | None = Field(None, description=\"Target column for language code (e.g. 'ru', 'en')\")\n target_source_column: str | None = Field(None, description=\"Target column for source/original text\")\n target_source_language_column: str | None = Field(None, description=\"Target column for detected source language (BCP-47)\")\n context_columns: list[str] | None = Field(default_factory=list, description=\"Context column names\")\n target_language: str | None = Field(None, description=\"Target language code [DEPRECATED: use target_languages]\")\n target_languages: list[str] | None = Field(default_factory=list, description=\"List of BCP-47 target language codes\")\n provider_id: str | None = Field(None, description=\"LLM provider ID\")\n\n _validate_target_languages = field_validator(\"target_languages\")(_validate_bcp47_list)\n batch_size: int = Field(50, description=\"Records per batch\")\n disable_reasoning: bool = Field(False, description=\"If true, pass reasoning_effort:none to LLM to save output tokens\")\n upsert_strategy: str = Field(\"MERGE\", description=\"UPSERT strategy: MERGE, INSERT, UPDATE\")\n dictionary_ids: list[str] | None = Field(default_factory=list, description=\"Associated terminology dictionary IDs\")\n environment_id: str | None = Field(None, description=\"Superset environment ID\")\n target_database_id: str | None = Field(None, description=\"Superset database ID for SQL Lab insert target\")\n# #endregion TranslateJobCreate\n\n\n# #region TranslateJobUpdate [TYPE Class]\n# @BRIEF Schema for updating an existing translation job.\nclass TranslateJobUpdate(BaseModel):\n name: str | None = None\n description: str | None = None\n source_dialect: str | None = None\n target_dialect: str | None = None\n database_dialect: str | None = None\n source_datasource_id: str | None = None\n source_table: str | None = None\n target_schema: str | None = None\n target_table: str | None = None\n source_key_cols: list[str] | None = None\n target_key_cols: list[str] | None = None\n translation_column: str | None = None\n target_column: str | None = None\n target_language_column: str | None = None\n target_source_column: str | None = None\n target_source_language_column: str | None = None\n context_columns: list[str] | None = None\n target_language: str | None = None\n target_languages: list[str] | None = None\n provider_id: str | None = None\n\n _validate_target_languages = field_validator(\"target_languages\")(_validate_bcp47_list)\n batch_size: int | None = None\n disable_reasoning: bool | None = None\n upsert_strategy: str | None = None\n status: str | None = None\n dictionary_ids: list[str] | None = None\n environment_id: str | None = None\n target_database_id: str | None = None\n# #endregion TranslateJobUpdate\n\n\n# #region TranslateJobResponse [TYPE Class]\n# @BRIEF Schema for translation job API responses.\nclass TranslateJobResponse(BaseModel):\n id: str\n name: str\n description: str | None = None\n source_dialect: str\n target_dialect: str\n database_dialect: str | None = None\n source_datasource_id: str | None = None\n source_table: str | None = None\n target_schema: str | None = None\n target_table: str | None = None\n source_key_cols: list[str] | None = None\n target_key_cols: list[str] | None = None\n translation_column: str | None = None\n target_column: str | None = None\n target_language_column: str | None = None\n target_source_column: str | None = None\n target_source_language_column: str | None = None\n context_columns: list[str] | None = None\n # source_language removed — deprecated, per-row auto-detected\n target_languages: list[str] | None = None\n provider_id: str | None = None\n batch_size: int = 50\n disable_reasoning: bool = False\n upsert_strategy: str = \"MERGE\"\n status: str\n created_by: str | None = None\n created_at: datetime\n updated_at: datetime | None = None\n dictionary_ids: list[str] | None = None\n environment_id: str | None = None\n target_database_id: str | None = None\n\n class Config:\n from_attributes = True\n# #endregion TranslateJobResponse\n\n\n# #region DatasourceColumnResponse [TYPE Class]\n# @BRIEF Schema for datasource column metadata response.\nclass DatasourceColumnResponse(BaseModel):\n name: str\n type: str | None = None\n is_physical: bool = True\n is_dttm: bool = False\n description: str | None = None\n\n# #endregion DatasourceColumnResponse\n\n# #region DatasourceColumnsResponse [TYPE Class]\n# @BRIEF Schema for datasource columns endpoint response.\nclass DatasourceColumnsResponse(BaseModel):\n datasource_id: int\n datasource_name: str | None = None\n schema_name: str | None = None\n database_dialect: str\n columns: list[DatasourceColumnResponse] = []\n\n class Config:\n from_attributes = True\n# #endregion DatasourceColumnsResponse\n\n\n# #region DuplicateJobResponse [TYPE Class]\n# @BRIEF Schema for duplicate job response.\nclass DuplicateJobResponse(BaseModel):\n id: str\n name: str\n message: str = \"Job duplicated successfully\"\n\n class Config:\n from_attributes = True\n# #endregion DuplicateJobResponse\n\n\n# #region DictionaryCreate [TYPE Class]\n# @BRIEF Schema for creating a new terminology dictionary.\nclass DictionaryCreate(BaseModel):\n name: str\n description: str | None = None\n source_dialect: str\n target_dialect: str\n is_active: bool = True\n# #endregion DictionaryCreate\n\n\n# #region DictionaryImport [TYPE Class]\n# @BRIEF Schema for importing entries into a terminology dictionary.\nclass DictionaryImport(BaseModel):\n content: str = Field(..., description=\"CSV or TSV content as raw string\")\n delimiter: str | None = Field(None, description=\"Detected or forced delimiter: ',' or '\\\\t'. Auto-detect if omitted.\")\n on_conflict: str = Field(\"overwrite\", description=\"'overwrite' or 'keep_existing' or 'cancel'\")\n preview_only: bool = Field(False, description=\"If true, return preview without applying\")\n default_source_language: str | None = Field(None, description=\"Default BCP-47 source language when column missing in data\")\n default_target_language: str | None = Field(None, description=\"Default BCP-47 target language when column missing in data\")\n# #endregion DictionaryImport\n\n\n# #region DictionaryResponse [TYPE Class]\n# @BRIEF Schema for terminology dictionary API responses.\nclass DictionaryResponse(BaseModel):\n id: str\n name: str\n description: str | None = None\n source_dialect: str\n target_dialect: str\n is_active: bool\n created_by: str | None = None\n created_at: datetime\n updated_at: datetime | None = None\n entry_count: int | None = None\n\n class Config:\n from_attributes = True\n# #endregion DictionaryResponse\n\n\n# #region DictionaryEntryCreate [TYPE Class]\n# @BRIEF Schema for adding/editing a dictionary entry with language pair support.\nclass DictionaryEntryCreate(BaseModel):\n source_term: str = Field(..., description=\"Source term to translate\")\n target_term: str = Field(..., description=\"Target/translated term\")\n context_notes: str | None = Field(None, description=\"Optional context notes\")\n source_language: str = Field(\"und\", description=\"BCP-47 source language code (default: und)\")\n target_language: str = Field(\"und\", description=\"BCP-47 target language code (default: und)\")\n\n class Config:\n from_attributes = True\n# #endregion DictionaryEntryCreate\n\n\n# #region DictionaryEntryResponse [TYPE Class]\n# @BRIEF Schema for dictionary entry API responses.\nclass DictionaryEntryResponse(BaseModel):\n id: str\n dictionary_id: str\n source_term: str\n source_term_normalized: str\n target_term: str\n source_language: str | None = None\n target_language: str | None = None\n context_notes: str | None = None\n context_data: dict[str, Any] | None = None\n usage_notes: str | None = None\n has_context: bool = False\n context_source: str | None = None\n origin_source_language: str | None = None\n created_at: datetime\n updated_at: datetime | None = None\n\n class Config:\n from_attributes = True\n# #endregion DictionaryEntryResponse\n\n\n# #region DictionaryImportResult [TYPE Class]\n# @BRIEF Schema for dictionary import result.\nclass DictionaryImportResult(BaseModel):\n total: int = 0\n created: int = 0\n updated: int = 0\n skipped: int = 0\n errors: list[dict[str, Any]] = Field(default_factory=list)\n preview: list[dict[str, Any]] = Field(default_factory=list, description=\"Preview rows with conflict flags\")\n# #endregion DictionaryImportResult\n\n\n# #region PreviewRequest [TYPE Class]\n# @BRIEF Schema for triggering a translation preview.\nclass PreviewRequest(BaseModel):\n sample_size: int = Field(10, ge=1, le=100, description=\"Number of sample rows to preview\")\n prompt_template: str | None = Field(None, description=\"Optional custom prompt template\")\n env_id: str | None = Field(None, description=\"Superset environment ID for preview data fetch\")\n# #endregion PreviewRequest\n\n\n# #region PreviewRowUpdate [TYPE Class]\n# @BRIEF Schema for approving/editing/rejecting a preview row.\nclass PreviewRowUpdate(BaseModel):\n action: str = Field(..., description=\"'approve', 'reject', or 'edit'\")\n translation: str | None = Field(None, description=\"Edited translation (required for 'edit' action)\")\n feedback: str | None = Field(None, description=\"Optional feedback/comment\")\n language_code: str | None = Field(None, description=\"BCP-47 language code for per-language actions (optional, applies to all if omitted)\")\n# #endregion PreviewRowUpdate\n\n\n# #region PreviewAcceptResponse [TYPE Class]\n# @BRIEF Schema for preview accept response.\nclass PreviewAcceptResponse(BaseModel):\n id: str\n job_id: str\n status: str\n created_by: str | None = None\n created_at: datetime\n expires_at: datetime | None = None\n records: list['PreviewRow'] = []\n target_languages: list[str] = Field(default_factory=list, description=\"Target languages for this preview\")\n\n class Config:\n from_attributes = True\n# #endregion PreviewAcceptResponse\n\n\n# #region CostEstimate [TYPE Class]\n# @BRIEF Schema for cost estimation in preview response.\nclass CostEstimate(BaseModel):\n sample_size: int = 0\n num_languages: int = 1\n sample_prompt_tokens: int = 0\n sample_output_tokens: int = 0\n sample_total_tokens: int = 0\n sample_cost: float = 0.0\n estimated_total_rows: int = 0\n estimated_tokens: int = 0\n estimated_cost: float = 0.0\n warning: str | None = None\n# #endregion CostEstimate\n\n\n# #region TermCorrectionSubmit [TYPE Class]\n# @BRIEF Schema for submitting a term correction in a translation preview.\nclass TermCorrectionSubmit(BaseModel):\n source_term: str\n incorrect_target_term: str\n corrected_target_term: str\n dictionary_id: str | None = Field(None, description=\"Target dictionary ID (language-filtered)\")\n origin_run_id: str | None = Field(None, description=\"Run ID from which this correction originated\")\n origin_row_key: str | None = Field(None, description=\"Row key within the run\")\n context_data: dict[str, Any] | None = Field(None, description=\"Context data for the dictionary entry\")\n usage_notes: str | None = Field(None, description=\"Usage notes for the dictionary entry\")\n keep_context: bool = Field(True, description=\"If false, clear context_data and set has_context=False on dictionary entry\")\n# #endregion TermCorrectionSubmit\n\n\n# #region TermCorrectionBulkSubmit [TYPE Class]\n# @BRIEF Schema for submitting multiple term corrections atomically.\nclass TermCorrectionBulkSubmit(BaseModel):\n corrections: list[TermCorrectionSubmit]\n dictionary_id: str = Field(..., description=\"Target dictionary ID for all corrections\")\n# #endregion TermCorrectionBulkSubmit\n\n\n# #region CorrectionConflictResult [TYPE Class]\n# @BRIEF Schema for reporting conflicts in correction submission.\nclass CorrectionConflictResult(BaseModel):\n source_term: str\n existing_target_term: str\n submitted_target_term: str\n action: str = \"keep_existing\"\n# #endregion CorrectionConflictResult\n\n\n# #region CorrectionSubmitResponse [TYPE Class]\n# @BRIEF Schema for correction submission response.\nclass CorrectionSubmitResponse(BaseModel):\n entry_id: str | None = None\n action: str # \"created\", \"updated\", \"conflict_detected\", \"skipped\"\n source_term: str\n target_term: str\n conflict: CorrectionConflictResult | None = None\n message: str | None = None\n# #endregion CorrectionSubmitResponse\n\n\n# #region ScheduleConfig [TYPE Class]\n# @BRIEF Schema for configuring a recurring translation schedule.\nclass ScheduleConfig(BaseModel):\n cron_expression: str = Field(..., description=\"Cron expression for scheduling (e.g. '0 2 * * *')\")\n timezone: str = Field(\"UTC\", description=\"Timezone for the cron schedule (e.g. 'UTC', 'Europe/Moscow')\")\n is_active: bool = True\n execution_mode: str = Field(\"full\", description=\"Translation execution mode: 'full' or 'new_key_only'\")\n# #endregion ScheduleConfig\n\n\n# #region ScheduleResponse [TYPE Class]\n# @BRIEF Schema for schedule API responses.\nclass ScheduleResponse(BaseModel):\n id: str\n job_id: str\n cron_expression: str\n timezone: str\n is_active: bool\n execution_mode: str = \"full\"\n last_run_at: datetime | None = None\n next_run_at: datetime | None = None\n created_by: str | None = None\n created_at: datetime\n updated_at: datetime | None = None\n\n class Config:\n from_attributes = True\n# #endregion ScheduleResponse\n\n\n# #region NextExecutionResponse [TYPE Class]\n# @BRIEF Schema for next execution preview.\nclass NextExecutionResponse(BaseModel):\n job_id: str\n cron_expression: str\n timezone: str\n next_executions: list[str] = []\n# #endregion NextExecutionResponse\n\n\n# #region TranslationRunResponse [TYPE Class]\n# @BRIEF Schema for translation run API responses.\nclass TranslationRunResponse(BaseModel):\n id: str\n job_id: str\n status: str\n trigger_type: str | None = None\n started_at: datetime | None = None\n completed_at: datetime | None = None\n error_message: str | None = None\n total_records: int = 0\n successful_records: int = 0\n failed_records: int = 0\n skipped_records: int = 0\n insert_status: str | None = None\n superset_execution_id: str | None = None\n config_snapshot: dict[str, Any] | None = None\n key_hash: str | None = None\n config_hash: str | None = None\n dict_snapshot_hash: str | None = None\n created_by: str | None = None\n created_at: datetime\n language_stats: list['TranslationRunLanguageStatsResponse'] | None = None\n\n class Config:\n from_attributes = True\n# #endregion TranslationRunResponse\n\n\n# #region RunDetailResponse [TYPE Class]\n# @BRIEF Schema for detailed run response including records, events, and config snapshot.\nclass RunDetailResponse(BaseModel):\n run: TranslationRunResponse\n records: list[dict[str, Any]] = Field(default_factory=list, description=\"Paginated translation records\")\n events: list[dict[str, Any]] = Field(default_factory=list, description=\"Run events\")\n event_invariants: dict[str, Any] | None = None\n batch_count: int = 0\n# #endregion RunDetailResponse\n\n\n# #region RunHistoryFilter [TYPE Class]\n# @BRIEF Schema for filtering run history.\nclass RunHistoryFilter(BaseModel):\n job_id: str | None = None\n status: str | None = None\n trigger_type: str | None = None\n created_by: str | None = None\n date_from: datetime | None = None\n date_to: datetime | None = None\n page: int = 1\n page_size: int = 20\n# #endregion RunHistoryFilter\n\n\n# #region RunListResponse [TYPE Class]\n# @BRIEF Schema for paginated run list response.\nclass RunListResponse(BaseModel):\n items: list[TranslationRunResponse] = []\n total: int = 0\n page: int = 1\n page_size: int = 20\n# #endregion RunListResponse\n\n\n# #region AggregatedMetricsResponse [TYPE Class]\n# @BRIEF Schema for aggregated per-job metrics.\nclass AggregatedMetricsResponse(BaseModel):\n job_id: str\n total_runs: int = 0\n successful_runs: int = 0\n failed_runs: int = 0\n cancelled_runs: int = 0\n total_records: int = 0\n successful_records: int = 0\n failed_records: int = 0\n skipped_records: int = 0\n cumulative_tokens: int | None = None\n cumulative_cost: float | None = None\n avg_duration_ms: float | None = None\n last_run_at: datetime | None = None\n next_scheduled_run: datetime | None = None\n# #endregion AggregatedMetricsResponse\n\n\n# #region TranslationPreviewLanguageResponse [C:1] [TYPE Class]\n# @BRIEF Schema for per-language preview data in API responses.\nclass TranslationPreviewLanguageResponse(BaseModel):\n language_code: str\n source_language_detected: str | None = None\n translated_value: str | None = None\n user_edit: str | None = None\n final_value: str | None = None\n status: str = \"pending\"\n needs_review: bool = False\n\n class Config:\n from_attributes = True\n# #endregion TranslationPreviewLanguageResponse\n\n\n# #region PreviewRow [TYPE Class]\n# @BRIEF A single row in a translation preview showing original and translated content side by side.\nclass PreviewRow(BaseModel):\n id: str\n source_sql: str | None = None\n target_sql: str | None = None\n source_object_type: str | None = None\n source_object_id: str | None = None\n source_object_name: str | None = None\n status: str = \"PENDING\"\n feedback: str | None = None\n source_language_detected: str | None = None\n needs_review: bool = False\n languages: list[TranslationPreviewLanguageResponse] = Field(default_factory=list, description=\"Per-language translation data\")\n# #endregion PreviewRow\n\n\n# #region TranslationPreviewResponse [TYPE Class]\n# @BRIEF Schema for translation preview session API responses.\nclass TranslationPreviewResponse(BaseModel):\n id: str\n job_id: str\n run_id: str | None = None\n status: str\n created_by: str | None = None\n created_at: datetime\n expires_at: datetime | None = None\n records: list[PreviewRow] = []\n target_languages: list[str] = Field(default_factory=list, description=\"Target languages for this preview\")\n cost_estimate: CostEstimate | None = None\n\n class Config:\n from_attributes = True\n# #endregion TranslationPreviewResponse\n\n\n# #region TranslationBatchResponse [TYPE Class]\n# @BRIEF Schema for translation batch API responses.\nclass TranslationBatchResponse(BaseModel):\n id: str\n run_id: str\n batch_index: int\n status: str\n total_records: int = 0\n successful_records: int = 0\n failed_records: int = 0\n started_at: datetime | None = None\n completed_at: datetime | None = None\n created_at: datetime\n\n class Config:\n from_attributes = True\n# #endregion TranslationBatchResponse\n\n\n# #region MetricsResponse [TYPE Class]\n# @BRIEF Schema for translation metrics API responses.\nclass MetricsResponse(BaseModel):\n job_id: str\n snapshot_date: datetime\n total_jobs: int = 0\n total_runs: int = 0\n total_records: int = 0\n successful_records: int = 0\n failed_records: int = 0\n skipped_records: int = 0\n avg_duration_ms: int | None = None\n p50_duration_ms: int | None = None\n p95_duration_ms: int | None = None\n p99_duration_ms: int | None = None\n\n class Config:\n from_attributes = True\n# #endregion MetricsResponse\n\n\n# #region TranslationLanguageResponse [C:1] [TYPE Class]\n# @BRIEF Schema for per-language translation data in API responses.\nclass TranslationLanguageResponse(BaseModel):\n language_code: str\n source_language_detected: str | None = None\n translated_value: str | None = None\n user_edit: str | None = None\n final_value: str | None = None\n status: str = \"pending\"\n needs_review: bool = False\n language_overridden: bool = False\n\n class Config:\n from_attributes = True\n# #endregion TranslationLanguageResponse\n\n\n# #region TranslationRunLanguageStatsResponse [C:1] [TYPE Class]\n# @BRIEF Schema for per-language statistics in run API responses.\nclass TranslationRunLanguageStatsResponse(BaseModel):\n language_code: str\n total_rows: int = 0\n translated_rows: int = 0\n failed_rows: int = 0\n skipped_rows: int = 0\n token_count: int = 0\n estimated_cost: float = 0.0\n\n class Config:\n from_attributes = True\n# #endregion TranslationRunLanguageStatsResponse\n\n\n# #region OverrideLanguageRequest [C:1] [TYPE Class]\n# @BRIEF Schema for manually overriding the detected source language of a translation.\nclass OverrideLanguageRequest(BaseModel):\n source_language: str = Field(..., description=\"BCP-47 language code to override with\")\n# #endregion OverrideLanguageRequest\n\n\n# #region BulkFindReplaceRequest [C:1] [TYPE Class]\n# @BRIEF Schema for bulk find-and-replace within translations for a target language.\nclass BulkFindReplaceRequest(BaseModel):\n find_pattern: str = Field(..., description=\"Text or regex pattern to find\", max_length=500)\n is_regex: bool = Field(False, description=\"Whether find_pattern is a regular expression\")\n replacement_text: str = Field(..., description=\"Replacement text\")\n target_language: str = Field(..., description=\"BCP-47 language code to target\")\n preview: bool = Field(True, description=\"If true, return matches without applying replacements\")\n submit_to_dictionary: bool = Field(False, description=\"If true, also submit corrections to dictionary\")\n dictionary_id: str | None = Field(None, description=\"Target dictionary ID for submitting corrections\")\n usage_notes: str | None = Field(None, description=\"Usage notes for dictionary entries\")\n submit_to_dictionary_with_context: bool = Field(False, description=\"If true, auto-capture source row context when submitting to dictionary\")\n\n @field_validator(\"find_pattern\")\n @classmethod\n def validate_pattern_length(cls, v: str) -> str:\n \"\"\"Reject patterns longer than 500 characters to prevent ReDoS.\"\"\"\n if len(v) > 500:\n raise ValueError(\"Pattern too long (max 500 characters)\")\n return v\n# #endregion BulkFindReplaceRequest\n\n\n# #region InlineEditRequest [C:1] [TYPE Class]\n# @BRIEF Schema for inline editing a translated value on a completed run result.\nclass InlineEditRequest(BaseModel):\n final_value: str = Field(..., description=\"Corrected/edited translation text\")\n submit_to_dictionary: bool = Field(False, description=\"If true, also save correction to dictionary\")\n dictionary_id: str | None = Field(None, description=\"Target dictionary ID for saving correction\")\n context_data_override: dict[str, Any] | None = Field(None, description=\"Override auto-captured context data for dictionary entry\")\n usage_notes: str | None = Field(None, description=\"Usage notes for the dictionary entry\")\n keep_context: bool = Field(True, description=\"If false, clear context_data and set has_context=False on dictionary entry\")\n# #endregion InlineEditRequest\n\n\n# #region BulkReplacePreviewItem [C:1] [TYPE Class]\n# @BRIEF A single item in a bulk replace preview list.\nclass BulkReplacePreviewItem(BaseModel):\n record_id: str\n language_code: str\n source_term: str\n current_value: str\n new_value: str\n source_language_detected: str | None = None\n# #endregion BulkReplacePreviewItem\n\n\n# #region BulkReplaceResponse [C:1] [TYPE Class]\n# @BRIEF Response for bulk find-and-replace operation.\nclass BulkReplaceResponse(BaseModel):\n rows_affected: int = 0\n corrections_submitted: int = 0\n preview: list[BulkReplacePreviewItem] = Field(default_factory=list)\n# #endregion BulkReplaceResponse\n\n\n# #region InlineCorrectionSubmit [C:1] [TYPE Class]\n# @BRIEF Schema for submitting an inline correction for a specific translated record.\nclass InlineCorrectionSubmit(BaseModel):\n record_id: str = Field(..., description=\"Translation record ID\")\n language_code: str = Field(..., description=\"BCP-47 language code of the translated value\")\n source_term: str = Field(..., description=\"Original source term that was incorrectly translated\")\n corrected_term: str = Field(..., description=\"Corrected translation\")\n dictionary_id: str = Field(..., description=\"Target dictionary ID to also save the correction\")\n source_language: str = Field(..., description=\"BCP-47 source language code\")\n# #endregion InlineCorrectionSubmit\n\n\n# #endregion TranslateSchemas\n" }, { "contract_id": "TranslateJobCreate", @@ -59084,13 +58306,13 @@ "contract_type": "Module", "file_path": "backend/src/services/git/_base.py", "start_line": 1, - "end_line": 317, + "end_line": 383, "tier": "TIER_2", - "complexity": 3, + "complexity": 4, "metadata": { - "COMPLEXITY": 3, + "COMPLEXITY": 4, "LAYER": "Infra", - "PURPOSE": "Core GitService base class — initialization, path resolution, repo lifecycle (init/delete/get), and identity configuration." + "PURPOSE": "Core GitService base class — initialization, path resolution, repo lifecycle (init/delete/get), identity configuration, concurrent locking, and shared HTTP client pool." }, "relations": [ { @@ -59136,6 +58358,33 @@ "value": "Infra" } }, + { + "code": "missing_required_tag", + "tag": "POST", + "message": "@POST is required for contract type 'Module' at C4", + "detail": { + "actual_complexity": 4, + "contract_type": "Module" + } + }, + { + "code": "missing_required_tag", + "tag": "PRE", + "message": "@PRE is required for contract type 'Module' at C4", + "detail": { + "actual_complexity": 4, + "contract_type": "Module" + } + }, + { + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Module' at C4", + "detail": { + "actual_complexity": 4, + "contract_type": "Module" + } + }, { "code": "invalid_relation_predicate", "tag": "RELATION", @@ -59157,7 +58406,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region GitServiceBase [C:3] [TYPE Module] [SEMANTICS git, repository, clone, base, mixin]\n# @LAYER: Infra\n# @BRIEF Core GitService base class — initialization, path resolution, repo lifecycle (init/delete/get), and identity configuration.\n# @RELATION INHERITED_BY -> [GitService]\n# @RELATION DEPENDS_ON -> [SessionLocal]\n# @RELATION DEPENDS_ON -> [AppConfigRecord]\n# @RELATION DEPENDS_ON -> [GitRepository]\n\nimport os\nimport re\nimport shutil\nfrom pathlib import Path\n\nfrom fastapi import HTTPException\nfrom git import Repo\nfrom git.exc import InvalidGitRepositoryError, NoSuchPathError\n\nfrom src.core.database import SessionLocal\nfrom src.core.logger import belief_scope, logger\nfrom src.models.config import AppConfigRecord\nfrom src.models.git import GitRepository\n\n\n# #region GitServiceBase [C:3] [TYPE Class]\n# @BRIEF Base class for GitService providing initialization, path resolution, repository lifecycle, and identity.\nclass GitServiceBase:\n # region GitService_init [TYPE Function]\n # @PURPOSE: Initializes the GitService with a base path for repositories.\n # @PARAM: base_path (str) - Root directory for all Git clones.\n # @PRE: base_path is a valid string path.\n # @POST: GitService is initialized; base_path directory exists.\n def __init__(self, base_path: str = \"git_repos\"):\n with belief_scope(\"GitService.__init__\"):\n backend_root = Path(__file__).parents[3]\n self.legacy_base_path = str((backend_root / \"git_repos\").resolve())\n self._uses_default_base_path = base_path == \"git_repos\"\n self.base_path = self._resolve_base_path(base_path)\n self._ensure_base_path_exists()\n # endregion GitService_init\n\n # region _ensure_base_path_exists [TYPE Function]\n # @PURPOSE: Ensure the repositories root directory exists and is a directory.\n # @PRE: self.base_path is resolved to filesystem path.\n # @POST: self.base_path exists as directory or raises ValueError.\n def _ensure_base_path_exists(self) -> None:\n base = Path(self.base_path)\n if base.exists() and not base.is_dir():\n raise ValueError(f\"Git repositories base path is not a directory: {self.base_path}\")\n try:\n base.mkdir(parents=True, exist_ok=True)\n except (PermissionError, OSError) as e:\n logger.warning(\n f\"[_ensure_base_path_exists][Coherence:Failed] Cannot create Git repositories base path: {self.base_path}. Error: {e}\"\n )\n raise ValueError(f\"Cannot create Git repositories base path: {self.base_path}. {e}\")\n # endregion _ensure_base_path_exists\n\n # region _resolve_base_path [TYPE Function]\n # @PURPOSE: Resolve base repository directory from explicit argument or global storage settings.\n # @PRE: base_path is a string path.\n # @POST: Returns absolute path for Git repositories root.\n # @RETURN: str\n def _resolve_base_path(self, base_path: str) -> str:\n backend_root = Path(__file__).parents[3]\n fallback_path = str((backend_root / base_path).resolve())\n if base_path != \"git_repos\":\n return fallback_path\n try:\n session = SessionLocal()\n try:\n config_row = session.query(AppConfigRecord).filter(AppConfigRecord.id == \"global\").first()\n finally:\n session.close()\n payload = (config_row.payload if config_row and config_row.payload else {}) if config_row else {}\n storage_cfg = payload.get(\"settings\", {}).get(\"storage\", {}) if isinstance(payload, dict) else {}\n root_path = str(storage_cfg.get(\"root_path\", \"\")).strip()\n repo_path = str(storage_cfg.get(\"repo_path\", \"\")).strip()\n if not root_path:\n return fallback_path\n project_root = Path(__file__).parents[4]\n root = Path(root_path)\n if not root.is_absolute():\n root = (project_root / root).resolve()\n repo_root = Path(repo_path) if repo_path else Path(\"repositorys\")\n if repo_root.is_absolute():\n return str(repo_root.resolve())\n return str((root / repo_root).resolve())\n except Exception as e:\n logger.warning(f\"[_resolve_base_path][Coherence:Failed] Falling back to default path: {e}\")\n return fallback_path\n # endregion _resolve_base_path\n\n # region _normalize_repo_key [TYPE Function]\n # @PURPOSE: Convert user/dashboard-provided key to safe filesystem directory name.\n # @PRE: repo_key can be None/empty.\n # @POST: Returns normalized non-empty key.\n # @RETURN: str\n def _normalize_repo_key(self, repo_key: str | None) -> str:\n raw_key = str(repo_key or \"\").strip().lower()\n normalized = re.sub(r\"[^a-z0-9._-]+\", \"-\", raw_key).strip(\"._-\")\n return normalized or \"dashboard\"\n # endregion _normalize_repo_key\n\n # region _update_repo_local_path [TYPE Function]\n # @PURPOSE: Persist repository local_path in GitRepository table when record exists.\n # @PRE: dashboard_id is valid integer.\n # @POST: local_path is updated for existing record.\n def _update_repo_local_path(self, dashboard_id: int, local_path: str) -> None:\n try:\n session = SessionLocal()\n try:\n db_repo = (\n session.query(GitRepository)\n .filter(GitRepository.dashboard_id == int(dashboard_id))\n .first()\n )\n if db_repo:\n db_repo.local_path = local_path\n session.commit()\n finally:\n session.close()\n except Exception as e:\n logger.warning(f\"[_update_repo_local_path][Coherence:Failed] {e}\")\n # endregion _update_repo_local_path\n\n # region _migrate_repo_directory [TYPE Function]\n # @PURPOSE: Move legacy repository directory to target path and sync DB metadata.\n # @PRE: source_path exists.\n # @POST: Repository content available at target_path.\n # @RETURN: str\n def _migrate_repo_directory(self, dashboard_id: int, source_path: str, target_path: str) -> str:\n source_abs = os.path.abspath(source_path)\n target_abs = os.path.abspath(target_path)\n if source_abs == target_abs:\n return source_abs\n if os.path.exists(target_abs):\n logger.reason(f\"Target already exists, keeping source path: {target_abs}\", extra={\"src\": \"_migrate_repo_directory\"})\n return source_abs\n Path(target_abs).parent.mkdir(parents=True, exist_ok=True)\n try:\n os.replace(source_abs, target_abs)\n except OSError:\n shutil.move(source_abs, target_abs)\n self._update_repo_local_path(dashboard_id, target_abs)\n logger.info(\n f\"[_migrate_repo_directory][Coherence:OK] Repository migrated for dashboard {dashboard_id}: {source_abs} -> {target_abs}\"\n )\n return target_abs\n # endregion _migrate_repo_directory\n\n # region _get_repo_path [TYPE Function]\n # @PURPOSE: Resolves the local filesystem path for a dashboard's repository.\n # @PARAM: dashboard_id (int)\n # @PARAM: repo_key (Optional[str]) - Slug-like key used when DB local_path is absent.\n # @PRE: dashboard_id is an integer.\n # @POST: Returns DB-local_path when present, otherwise base_path/.\n # @RETURN: str\n def _get_repo_path(self, dashboard_id: int, repo_key: str | None = None) -> str:\n with belief_scope(\"GitService._get_repo_path\"):\n if dashboard_id is None:\n raise ValueError(\"dashboard_id cannot be None\")\n self._ensure_base_path_exists()\n fallback_key = repo_key if repo_key is not None else str(dashboard_id)\n normalized_key = self._normalize_repo_key(fallback_key)\n target_path = os.path.join(self.base_path, normalized_key)\n if not self._uses_default_base_path:\n return target_path\n try:\n session = SessionLocal()\n try:\n db_repo = (\n session.query(GitRepository)\n .filter(GitRepository.dashboard_id == int(dashboard_id))\n .first()\n )\n finally:\n session.close()\n if db_repo and db_repo.local_path:\n db_path = os.path.abspath(db_repo.local_path)\n if os.path.exists(db_path):\n if (\n os.path.abspath(self.base_path) != os.path.abspath(self.legacy_base_path)\n and db_path.startswith(os.path.abspath(self.legacy_base_path) + os.sep)\n ):\n return self._migrate_repo_directory(dashboard_id, db_path, target_path)\n return db_path\n except Exception as e:\n logger.warning(f\"[_get_repo_path][Coherence:Failed] Could not resolve local_path from DB: {e}\")\n legacy_id_path = os.path.join(self.legacy_base_path, str(dashboard_id))\n if os.path.exists(legacy_id_path) and not os.path.exists(target_path):\n return self._migrate_repo_directory(dashboard_id, legacy_id_path, target_path)\n if os.path.exists(target_path):\n self._update_repo_local_path(dashboard_id, target_path)\n return target_path\n # endregion _get_repo_path\n\n # region init_repo [TYPE Function]\n # @PURPOSE: Initialize or clone a repository for a dashboard.\n # @PARAM: dashboard_id (int), remote_url (str), pat (str), repo_key (Optional[str]).\n # @PRE: dashboard_id is int, remote_url is valid Git URL, pat is provided.\n # @POST: Repository is cloned or opened at the local path.\n # @RETURN: Repo\n def init_repo(self, dashboard_id: int, remote_url: str, pat: str, repo_key: str | None = None) -> Repo:\n with belief_scope(\"GitService.init_repo\"):\n self._ensure_base_path_exists()\n repo_path = self._get_repo_path(dashboard_id, repo_key=repo_key or str(dashboard_id))\n Path(repo_path).parent.mkdir(parents=True, exist_ok=True)\n if pat and \"://\" in remote_url:\n proto, rest = remote_url.split(\"://\", 1)\n auth_url = f\"{proto}://oauth2:{pat}@{rest}\"\n else:\n auth_url = remote_url\n if os.path.exists(repo_path):\n logger.reason(f\"Opening existing repo at {repo_path}\", extra={\"src\": \"init_repo\"})\n try:\n repo = Repo(repo_path)\n except (InvalidGitRepositoryError, NoSuchPathError):\n logger.reason(f\"Existing path is not a Git repository, recreating: {repo_path}\", extra={\"src\": \"init_repo\"})\n stale_path = Path(repo_path)\n if stale_path.exists():\n shutil.rmtree(stale_path, ignore_errors=True)\n if stale_path.exists():\n try:\n stale_path.unlink()\n except Exception:\n pass\n repo = Repo.clone_from(auth_url, repo_path)\n self._ensure_gitflow_branches(repo, dashboard_id)\n return repo\n logger.reason(f\"Cloning {remote_url} to {repo_path}\", extra={\"src\": \"init_repo\"})\n repo = Repo.clone_from(auth_url, repo_path)\n self._ensure_gitflow_branches(repo, dashboard_id)\n return repo\n # endregion init_repo\n\n # region delete_repo [TYPE Function]\n # @PURPOSE: Remove local repository and DB binding for a dashboard.\n # @PRE: dashboard_id is a valid integer.\n # @POST: Local path is deleted when present and GitRepository row is removed.\n def delete_repo(self, dashboard_id: int) -> None:\n with belief_scope(\"GitService.delete_repo\"):\n repo_path = self._get_repo_path(dashboard_id)\n removed_files = False\n if os.path.exists(repo_path):\n if os.path.isdir(repo_path):\n shutil.rmtree(repo_path)\n else:\n os.remove(repo_path)\n removed_files = True\n session = SessionLocal()\n try:\n db_repo = (\n session.query(GitRepository)\n .filter(GitRepository.dashboard_id == int(dashboard_id))\n .first()\n )\n if db_repo:\n session.delete(db_repo)\n session.commit()\n return\n if removed_files:\n return\n raise HTTPException(\n status_code=404,\n detail=f\"Repository for dashboard {dashboard_id} not found\",\n )\n except HTTPException:\n session.rollback()\n raise\n except Exception as e:\n session.rollback()\n logger.error(f\"[delete_repo][Coherence:Failed] Failed to delete repository for dashboard {dashboard_id}: {e}\")\n raise HTTPException(status_code=500, detail=f\"Failed to delete repository: {e!s}\")\n finally:\n session.close()\n # endregion delete_repo\n\n # region get_repo [TYPE Function]\n # @PURPOSE: Get Repo object for a dashboard.\n # @PRE: Repository must exist on disk for the given dashboard_id.\n # @POST: Returns a GitPython Repo instance for the dashboard.\n # @RETURN: Repo\n def get_repo(self, dashboard_id: int) -> Repo:\n with belief_scope(\"GitService.get_repo\"):\n repo_path = self._get_repo_path(dashboard_id)\n if not os.path.exists(repo_path):\n logger.error(f\"[get_repo][Coherence:Failed] Repository for dashboard {dashboard_id} does not exist\")\n raise HTTPException(status_code=404, detail=f\"Repository for dashboard {dashboard_id} not found\")\n try:\n return Repo(repo_path)\n except Exception as e:\n logger.error(f\"[get_repo][Coherence:Failed] Failed to open repository at {repo_path}: {e}\")\n raise HTTPException(status_code=500, detail=\"Failed to open local Git repository\")\n # endregion get_repo\n\n # region configure_identity [TYPE Function]\n # @PURPOSE: Configure repository-local Git committer identity for user-scoped operations.\n # @PRE: dashboard_id repository exists; git_username/git_email may be empty.\n # @POST: Repository config has user.name and user.email when both identity values are provided.\n def configure_identity(self, dashboard_id: int, git_username: str | None, git_email: str | None) -> None:\n with belief_scope(\"GitService.configure_identity\"):\n normalized_username = str(git_username or \"\").strip()\n normalized_email = str(git_email or \"\").strip()\n if not normalized_username or not normalized_email:\n return\n repo = self.get_repo(dashboard_id)\n try:\n with repo.config_writer(config_level=\"repository\") as config_writer:\n config_writer.set_value(\"user\", \"name\", normalized_username)\n config_writer.set_value(\"user\", \"email\", normalized_email)\n logger.reason(f\"Applied repository-local git identity for dashboard {dashboard_id}\", extra={\"src\": \"configure_identity\"})\n except Exception as e:\n logger.error(f\"[configure_identity][Coherence:Failed] Failed to configure git identity: {e}\")\n raise HTTPException(status_code=500, detail=f\"Failed to configure git identity: {e!s}\")\n # endregion configure_identity\n# #endregion GitServiceBase\n# #endregion GitServiceBase\n" + "body": "# #region GitServiceBase [C:4] [TYPE Module] [SEMANTICS git, repository, clone, base, mixin, lock, http]\n# @LAYER: Infra\n# @BRIEF Core GitService base class — initialization, path resolution, repo lifecycle (init/delete/get), identity configuration, concurrent locking, and shared HTTP client pool.\n# @RELATION INHERITED_BY -> [GitService]\n# @RELATION DEPENDS_ON -> [SessionLocal]\n# @RELATION DEPENDS_ON -> [AppConfigRecord]\n# @RELATION DEPENDS_ON -> [GitRepository]\n\nimport os\nimport re\nimport shutil\nimport threading\nfrom contextlib import contextmanager\nfrom pathlib import Path\n\nimport httpx\nfrom fastapi import HTTPException\nfrom git import Repo\nfrom git.exc import InvalidGitRepositoryError, NoSuchPathError\n\nfrom src.core.database import SessionLocal\nfrom src.core.logger import belief_scope, logger\nfrom src.models.config import AppConfigRecord\nfrom src.models.git import GitRepository\n\n\n# #region GitServiceBase [C:4] [TYPE Class]\n# @BRIEF Base class for GitService providing initialization, path resolution, repository lifecycle, identity, concurrent locking, and shared HTTP client.\n# @PRE base_path is a valid string path.\n# @POST GitService is initialized; base_path directory exists; shared AsyncClient ready; lock registry empty.\n# @SIDE_EFFECT Creates HTTP connection pool (max_keepalive=20, max_connections=100).\nclass GitServiceBase:\n # region GitService_init [C:4] [TYPE Function]\n # @PURPOSE: Initializes the GitService with a base path, concurrent access locks, and shared HTTP client.\n # @PRE: base_path is a valid string path.\n # @POST: GitService is initialized; base_path directory exists; _lock_registry, _http_client ready.\n def __init__(self, base_path: str = \"git_repos\"):\n with belief_scope(\"GitService.__init__\"):\n backend_root = Path(__file__).parents[3]\n self.legacy_base_path = str((backend_root / \"git_repos\").resolve())\n self._uses_default_base_path = base_path == \"git_repos\"\n self.base_path = self._resolve_base_path(base_path)\n self._ensure_base_path_exists()\n\n # Fix 3: Per-dashboard reentrant lock registry for concurrent access protection\n self._lock_registry: dict[int, threading.RLock] = {}\n self._reg_lock = threading.Lock()\n\n # Fix 5: Shared httpx.AsyncClient with connection pooling\n self._http_client = httpx.AsyncClient(\n timeout=30.0,\n limits=httpx.Limits(max_keepalive_connections=20, max_connections=100),\n )\n # endregion GitService_init\n\n # region _get_lock [C:2] [TYPE Function] [SEMANTICS lock,concurrency]\n # @BRIEF Get or create a per-dashboard reentrant lock for thread-safe GitPython access.\n # @PRE: dashboard_id is a valid integer.\n # @POST: Returns a threading.RLock unique to the given dashboard_id.\n def _get_lock(self, dashboard_id: int) -> threading.RLock:\n with self._reg_lock:\n if dashboard_id not in self._lock_registry:\n self._lock_registry[dashboard_id] = threading.RLock()\n return self._lock_registry[dashboard_id]\n # endregion _get_lock\n\n # region _locked [C:2] [TYPE Function] [SEMANTICS lock,context,concurrency]\n # @BRIEF Context manager that acquires the per-dashboard reentrant lock for the duration of the block.\n # @PRE: dashboard_id is a valid integer.\n # @POST: Lock is acquired on enter, released on exit.\n @contextmanager\n def _locked(self, dashboard_id: int):\n lock = self._get_lock(dashboard_id)\n lock.acquire()\n try:\n yield\n finally:\n lock.release()\n # endregion _locked\n\n # region _clone_with_auth [C:2] [TYPE Function] [SEMANTICS git,clone,auth,security]\n # @BRIEF Clone repository with PAT and immediately strip credentials from origin remote URL.\n # @PRE: remote_url is a valid Git URL; pat is provided when required; repo_path is writable.\n # @POST: Repo cloned at repo_path; origin remote URL has no embedded PAT.\n # @SIDE_EFFECT Clones remote repository to local filesystem.\n def _clone_with_auth(self, remote_url: str, repo_path: str, pat: str) -> Repo:\n auth_url = remote_url\n if pat and \"://\" in remote_url:\n proto, rest = remote_url.split(\"://\", 1)\n auth_url = f\"{proto}://oauth2:{pat}@{rest}\"\n repo = Repo.clone_from(auth_url, repo_path)\n # Strip PAT from origin URL to prevent credential leakage in .git/config, logs, ps aux\n repo.git.remote(\"set-url\", \"origin\", remote_url)\n return repo\n # endregion _clone_with_auth\n\n # region _ensure_base_path_exists [TYPE Function]\n # @PURPOSE: Ensure the repositories root directory exists and is a directory.\n # @PRE: self.base_path is resolved to filesystem path.\n # @POST: self.base_path exists as directory or raises ValueError.\n def _ensure_base_path_exists(self) -> None:\n base = Path(self.base_path)\n if base.exists() and not base.is_dir():\n raise ValueError(f\"Git repositories base path is not a directory: {self.base_path}\")\n try:\n base.mkdir(parents=True, exist_ok=True)\n except (PermissionError, OSError) as e:\n logger.warning(\n f\"[_ensure_base_path_exists][Coherence:Failed] Cannot create Git repositories base path: {self.base_path}. Error: {e}\"\n )\n raise ValueError(f\"Cannot create Git repositories base path: {self.base_path}. {e}\")\n # endregion _ensure_base_path_exists\n\n # region _resolve_base_path [TYPE Function]\n # @PURPOSE: Resolve base repository directory from explicit argument or global storage settings.\n # @PRE: base_path is a string path.\n # @POST: Returns absolute path for Git repositories root.\n # @RETURN: str\n def _resolve_base_path(self, base_path: str) -> str:\n backend_root = Path(__file__).parents[3]\n fallback_path = str((backend_root / base_path).resolve())\n if base_path != \"git_repos\":\n return fallback_path\n try:\n session = SessionLocal()\n try:\n config_row = session.query(AppConfigRecord).filter(AppConfigRecord.id == \"global\").first()\n finally:\n session.close()\n payload = (config_row.payload if config_row and config_row.payload else {}) if config_row else {}\n storage_cfg = payload.get(\"settings\", {}).get(\"storage\", {}) if isinstance(payload, dict) else {}\n root_path = str(storage_cfg.get(\"root_path\", \"\")).strip()\n repo_path = str(storage_cfg.get(\"repo_path\", \"\")).strip()\n if not root_path:\n return fallback_path\n project_root = Path(__file__).parents[4]\n root = Path(root_path)\n if not root.is_absolute():\n root = (project_root / root).resolve()\n repo_root = Path(repo_path) if repo_path else Path(\"repositorys\")\n if repo_root.is_absolute():\n return str(repo_root.resolve())\n return str((root / repo_root).resolve())\n except Exception as e:\n logger.warning(f\"[_resolve_base_path][Coherence:Failed] Falling back to default path: {e}\")\n return fallback_path\n # endregion _resolve_base_path\n\n # region _normalize_repo_key [TYPE Function]\n # @PURPOSE: Convert user/dashboard-provided key to safe filesystem directory name.\n # @PRE: repo_key can be None/empty.\n # @POST: Returns normalized non-empty key.\n # @RETURN: str\n def _normalize_repo_key(self, repo_key: str | None) -> str:\n raw_key = str(repo_key or \"\").strip().lower()\n normalized = re.sub(r\"[^a-z0-9._-]+\", \"-\", raw_key).strip(\"._-\")\n return normalized or \"dashboard\"\n # endregion _normalize_repo_key\n\n # region _update_repo_local_path [TYPE Function]\n # @PURPOSE: Persist repository local_path in GitRepository table when record exists.\n # @PRE: dashboard_id is valid integer.\n # @POST: local_path is updated for existing record.\n def _update_repo_local_path(self, dashboard_id: int, local_path: str) -> None:\n try:\n session = SessionLocal()\n try:\n db_repo = (\n session.query(GitRepository)\n .filter(GitRepository.dashboard_id == int(dashboard_id))\n .first()\n )\n if db_repo:\n db_repo.local_path = local_path\n session.commit()\n finally:\n session.close()\n except Exception as e:\n logger.warning(f\"[_update_repo_local_path][Coherence:Failed] {e}\")\n # endregion _update_repo_local_path\n\n # region _migrate_repo_directory [TYPE Function]\n # @PURPOSE: Move legacy repository directory to target path and sync DB metadata.\n # @PRE: source_path exists.\n # @POST: Repository content available at target_path.\n # @RETURN: str\n def _migrate_repo_directory(self, dashboard_id: int, source_path: str, target_path: str) -> str:\n source_abs = os.path.abspath(source_path)\n target_abs = os.path.abspath(target_path)\n if source_abs == target_abs:\n return source_abs\n if os.path.exists(target_abs):\n logger.reason(f\"Target already exists, keeping source path: {target_abs}\", extra={\"src\": \"_migrate_repo_directory\"})\n return source_abs\n Path(target_abs).parent.mkdir(parents=True, exist_ok=True)\n try:\n os.replace(source_abs, target_abs)\n except OSError:\n shutil.move(source_abs, target_abs)\n self._update_repo_local_path(dashboard_id, target_abs)\n logger.info(\n f\"[_migrate_repo_directory][Coherence:OK] Repository migrated for dashboard {dashboard_id}: {source_abs} -> {target_abs}\"\n )\n return target_abs\n # endregion _migrate_repo_directory\n\n # region _get_repo_path [TYPE Function]\n # @PURPOSE: Resolves the local filesystem path for a dashboard's repository.\n # @PARAM: dashboard_id (int)\n # @PARAM: repo_key (Optional[str]) - Slug-like key used when DB local_path is absent.\n # @PRE: dashboard_id is an integer.\n # @POST: Returns DB-local_path when present, otherwise base_path/.\n # @RETURN: str\n def _get_repo_path(self, dashboard_id: int, repo_key: str | None = None) -> str:\n with belief_scope(\"GitService._get_repo_path\"):\n if dashboard_id is None:\n raise ValueError(\"dashboard_id cannot be None\")\n self._ensure_base_path_exists()\n fallback_key = repo_key if repo_key is not None else str(dashboard_id)\n normalized_key = self._normalize_repo_key(fallback_key)\n target_path = os.path.join(self.base_path, normalized_key)\n if not self._uses_default_base_path:\n return target_path\n try:\n session = SessionLocal()\n try:\n db_repo = (\n session.query(GitRepository)\n .filter(GitRepository.dashboard_id == int(dashboard_id))\n .first()\n )\n finally:\n session.close()\n if db_repo and db_repo.local_path:\n db_path = os.path.abspath(db_repo.local_path)\n if os.path.exists(db_path):\n if (\n os.path.abspath(self.base_path) != os.path.abspath(self.legacy_base_path)\n and db_path.startswith(os.path.abspath(self.legacy_base_path) + os.sep)\n ):\n return self._migrate_repo_directory(dashboard_id, db_path, target_path)\n return db_path\n except Exception as e:\n logger.warning(f\"[_get_repo_path][Coherence:Failed] Could not resolve local_path from DB: {e}\")\n legacy_id_path = os.path.join(self.legacy_base_path, str(dashboard_id))\n if os.path.exists(legacy_id_path) and not os.path.exists(target_path):\n return self._migrate_repo_directory(dashboard_id, legacy_id_path, target_path)\n if os.path.exists(target_path):\n self._update_repo_local_path(dashboard_id, target_path)\n return target_path\n # endregion _get_repo_path\n\n # region init_repo [C:4] [TYPE Function] [SEMANTICS git,clone,init,lock]\n # @PURPOSE: Initialize or clone a repository for a dashboard with concurrent access protection.\n # @PARAM: dashboard_id (int), remote_url (str), pat (str), repo_key (Optional[str]).\n # @PRE: dashboard_id is int, remote_url is valid Git URL, pat is provided.\n # @POST: Repository is cloned or opened at the local path. Origin remote URL has no embedded PAT.\n # @SIDE_EFFECT Clones remote repository; modifies local filesystem; protected by per-dashboard lock.\n # @RETURN: Repo\n def init_repo(self, dashboard_id: int, remote_url: str, pat: str, repo_key: str | None = None) -> Repo:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.init_repo\"):\n self._ensure_base_path_exists()\n repo_path = self._get_repo_path(dashboard_id, repo_key=repo_key or str(dashboard_id))\n Path(repo_path).parent.mkdir(parents=True, exist_ok=True)\n if os.path.exists(repo_path):\n logger.reason(f\"Opening existing repo at {repo_path}\", extra={\"src\": \"init_repo\"})\n try:\n repo = Repo(repo_path)\n except (InvalidGitRepositoryError, NoSuchPathError):\n logger.reason(f\"Existing path is not a Git repository, recreating: {repo_path}\", extra={\"src\": \"init_repo\"})\n stale_path = Path(repo_path)\n if stale_path.exists():\n shutil.rmtree(stale_path, ignore_errors=True)\n if stale_path.exists():\n try:\n stale_path.unlink()\n except Exception:\n pass\n repo = self._clone_with_auth(remote_url, repo_path, pat)\n self._ensure_gitflow_branches(repo, dashboard_id)\n return repo\n logger.reason(f\"Cloning {remote_url} to {repo_path}\", extra={\"src\": \"init_repo\"})\n repo = self._clone_with_auth(remote_url, repo_path, pat)\n self._ensure_gitflow_branches(repo, dashboard_id)\n return repo\n # endregion init_repo\n\n # region delete_repo [C:4] [TYPE Function] [SEMANTICS git,delete,lock]\n # @PURPOSE: Remove local repository and DB binding for a dashboard with concurrent access protection.\n # @PRE: dashboard_id is a valid integer.\n # @POST: Local path is deleted when present and GitRepository row is removed.\n # @SIDE_EFFECT Deletes filesystem directory and DB record; protected by per-dashboard lock.\n def delete_repo(self, dashboard_id: int) -> None:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.delete_repo\"):\n repo_path = self._get_repo_path(dashboard_id)\n removed_files = False\n if os.path.exists(repo_path):\n if os.path.isdir(repo_path):\n shutil.rmtree(repo_path)\n else:\n os.remove(repo_path)\n removed_files = True\n session = SessionLocal()\n try:\n db_repo = (\n session.query(GitRepository)\n .filter(GitRepository.dashboard_id == int(dashboard_id))\n .first()\n )\n if db_repo:\n session.delete(db_repo)\n session.commit()\n return\n if removed_files:\n return\n raise HTTPException(\n status_code=404,\n detail=f\"Repository for dashboard {dashboard_id} not found\",\n )\n except HTTPException:\n session.rollback()\n raise\n except Exception as e:\n session.rollback()\n logger.error(f\"[delete_repo][Coherence:Failed] Failed to delete repository for dashboard {dashboard_id}: {e}\")\n raise HTTPException(status_code=500, detail=f\"Failed to delete repository: {e!s}\")\n finally:\n session.close()\n # endregion delete_repo\n\n # region get_repo [C:4] [TYPE Function] [SEMANTICS git,open,lock]\n # @PURPOSE: Get Repo object for a dashboard with concurrent access protection.\n # @PRE: Repository must exist on disk for the given dashboard_id.\n # @POST: Returns a GitPython Repo instance for the dashboard.\n # @RETURN: Repo\n def get_repo(self, dashboard_id: int) -> Repo:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.get_repo\"):\n repo_path = self._get_repo_path(dashboard_id)\n if not os.path.exists(repo_path):\n logger.error(f\"[get_repo][Coherence:Failed] Repository for dashboard {dashboard_id} does not exist\")\n raise HTTPException(status_code=404, detail=f\"Repository for dashboard {dashboard_id} not found\")\n try:\n return Repo(repo_path)\n except Exception as e:\n logger.error(f\"[get_repo][Coherence:Failed] Failed to open repository at {repo_path}: {e}\")\n raise HTTPException(status_code=500, detail=\"Failed to open local Git repository\")\n # endregion get_repo\n\n # region configure_identity [C:4] [TYPE Function] [SEMANTICS git,identity,lock]\n # @PURPOSE: Configure repository-local Git committer identity with concurrent access protection.\n # @PRE: dashboard_id repository exists; git_username/git_email may be empty.\n # @POST: Repository config has user.name and user.email when both identity values are provided.\n # @SIDE_EFFECT Writes to repository git config; protected by per-dashboard lock.\n def configure_identity(self, dashboard_id: int, git_username: str | None, git_email: str | None) -> None:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.configure_identity\"):\n normalized_username = str(git_username or \"\").strip()\n normalized_email = str(git_email or \"\").strip()\n if not normalized_username or not normalized_email:\n return\n repo = self.get_repo(dashboard_id)\n try:\n with repo.config_writer(config_level=\"repository\") as config_writer:\n config_writer.set_value(\"user\", \"name\", normalized_username)\n config_writer.set_value(\"user\", \"email\", normalized_email)\n logger.reason(f\"Applied repository-local git identity for dashboard {dashboard_id}\", extra={\"src\": \"configure_identity\"})\n except Exception as e:\n logger.error(f\"[configure_identity][Coherence:Failed] Failed to configure git identity: {e}\")\n raise HTTPException(status_code=500, detail=f\"Failed to configure git identity: {e!s}\")\n # endregion configure_identity\n\n # region close [C:2] [TYPE Function] [SEMANTICS http,cleanup,shutdown]\n # @BRIEF Gracefully close the shared HTTP client (connection pool).\n # @PRE: _http_client is initialized.\n # @POST: HTTP connections closed.\n async def close(self):\n await self._http_client.aclose()\n # endregion close\n# #endregion GitServiceBase\n# #endregion GitServiceBase\n" }, { "contract_id": "GitServiceBranchMixin", @@ -59288,13 +58537,13 @@ "contract_type": "Module", "file_path": "backend/src/services/git/_merge.py", "start_line": 1, - "end_line": 277, + "end_line": 300, "tier": "TIER_2", - "complexity": 3, + "complexity": 4, "metadata": { - "COMPLEXITY": 3, + "COMPLEXITY": 4, "LAYER": "Infra", - "PURPOSE": "Merge operations for GitService — conflict detection, resolution, abort, continue, and direct promote." + "PURPOSE": "Merge operations for GitService — conflict detection, resolution, abort, continue, and direct promote (all concurrent-safe via per-dashboard locks)." }, "relations": [ { @@ -59322,6 +58571,33 @@ "value": "Infra" } }, + { + "code": "missing_required_tag", + "tag": "POST", + "message": "@POST is required for contract type 'Module' at C4", + "detail": { + "actual_complexity": 4, + "contract_type": "Module" + } + }, + { + "code": "missing_required_tag", + "tag": "PRE", + "message": "@PRE is required for contract type 'Module' at C4", + "detail": { + "actual_complexity": 4, + "contract_type": "Module" + } + }, + { + "code": "missing_required_tag", + "tag": "SIDE_EFFECT", + "message": "@SIDE_EFFECT is required for contract type 'Module' at C4", + "detail": { + "actual_complexity": 4, + "contract_type": "Module" + } + }, { "code": "invalid_relation_predicate", "tag": "RELATION", @@ -59343,7 +58619,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region GitServiceMergeMixin [C:3] [TYPE Module] [SEMANTICS git, merge, branch, conflict, resolution]\n# @LAYER: Infra\n# @BRIEF Merge operations for GitService — conflict detection, resolution, abort, continue, and direct promote.\n# @RELATION USED_BY -> [GitService]\n\nimport os\nfrom pathlib import Path\nfrom typing import Any\n\nfrom fastapi import HTTPException\nfrom git import Repo\nfrom git.exc import GitCommandError\nfrom git.objects.blob import Blob\n\nfrom src.core.logger import belief_scope\n\n\n# #region GitServiceMergeMixin [C:3] [TYPE Class]\n# @BRIEF Mixin providing merge operations for GitService.\nclass GitServiceMergeMixin:\n # region _read_blob_text [TYPE Function]\n # @PURPOSE: Read text from a Git blob.\n def _read_blob_text(self, blob: Blob) -> str:\n with belief_scope(\"GitService._read_blob_text\"):\n if blob is None:\n return \"\"\n try:\n return blob.data_stream.read().decode(\"utf-8\", errors=\"replace\")\n except Exception:\n return \"\"\n # endregion _read_blob_text\n\n # region _get_unmerged_file_paths [TYPE Function]\n # @PURPOSE: List files with merge conflicts.\n def _get_unmerged_file_paths(self, repo: Repo) -> list[str]:\n with belief_scope(\"GitService._get_unmerged_file_paths\"):\n try:\n return sorted(list(repo.index.unmerged_blobs().keys()))\n except Exception:\n return []\n # endregion _get_unmerged_file_paths\n\n # region _build_unfinished_merge_payload [TYPE Function]\n # @PURPOSE: Build payload for unfinished merge state.\n def _build_unfinished_merge_payload(self, repo: Repo) -> dict[str, Any]:\n with belief_scope(\"GitService._build_unfinished_merge_payload\"):\n merge_head_path = os.path.join(repo.git_dir, \"MERGE_HEAD\")\n merge_head_value = \"\"\n merge_msg_preview = \"\"\n current_branch = \"unknown\"\n try:\n merge_head_value = Path(merge_head_path).read_text(encoding=\"utf-8\").strip()\n except Exception:\n merge_head_value = \"\"\n try:\n merge_msg_path = os.path.join(repo.git_dir, \"MERGE_MSG\")\n if os.path.exists(merge_msg_path):\n merge_msg_preview = (\n Path(merge_msg_path).read_text(encoding=\"utf-8\").strip().splitlines()[:1] or [\"\"]\n )[0]\n except Exception:\n merge_msg_preview = \"\"\n try:\n current_branch = repo.active_branch.name\n except Exception:\n current_branch = \"detached_or_unknown\"\n conflicts_count = len(self._get_unmerged_file_paths(repo))\n return {\n \"error_code\": \"GIT_UNFINISHED_MERGE\",\n \"message\": (\n \"\\u0412 \\u0440\\u0435\\u043f\\u043e\\u0437\\u0438\\u0442\\u043e\\u0440\\u0438\\u0438 \\u0435\\u0441\\u0442\\u044c \\u043d\\u0435\\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0451\\u043d\\u043d\\u043e\\u0435 \\u0441\\u043b\\u0438\\u044f\\u043d\\u0438\\u0435. \"\n \"\\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u0435 \\u0438\\u043b\\u0438 \\u043e\\u0442\\u043c\\u0435\\u043d\\u0438\\u0442\\u0435 \\u0441\\u043b\\u0438\\u044f\\u043d\\u0438\\u0435 \\u0432\\u0440\\u0443\\u0447\\u043d\\u0443\\u044e.\"\n ),\n \"repository_path\": repo.working_tree_dir,\n \"git_dir\": repo.git_dir,\n \"current_branch\": current_branch,\n \"merge_head\": merge_head_value,\n \"merge_message_preview\": merge_msg_preview,\n \"conflicts_count\": conflicts_count,\n \"next_steps\": [\n \"\\u041e\\u0442\\u043a\\u0440\\u043e\\u0439\\u0442\\u0435 \\u043b\\u043e\\u043a\\u0430\\u043b\\u044c\\u043d\\u044b\\u0439 \\u0440\\u0435\\u043f\\u043e\\u0437\\u0438\\u0442\\u043e\\u0440\\u0438\\u0439 \\u043f\\u043e \\u043f\\u0443\\u0442\\u0438 repository_path\",\n \"\\u041f\\u0440\\u043e\\u0432\\u0435\\u0440\\u044c\\u0442\\u0435 \\u0441\\u043e\\u0441\\u0442\\u043e\\u044f\\u043d\\u0438\\u0435: git status\",\n \"\\u0420\\u0430\\u0437\\u0440\\u0435\\u0448\\u0438\\u0442\\u0435 \\u043a\\u043e\\u043d\\u0444\\u043b\\u0438\\u043a\\u0442\\u044b \\u0438 \\u0432\\u044b\\u043f\\u043e\\u043b\\u043d\\u0438\\u0442\\u0435 commit, \\u043b\\u0438\\u0431\\u043e \\u043e\\u0442\\u043c\\u0435\\u043d\\u0438\\u0442\\u0435: git merge --abort\",\n \"\\u041f\\u043e\\u0441\\u043b\\u0435 \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043d\\u0438\\u044f/\\u043e\\u0442\\u043c\\u0435\\u043d\\u044b \\u0441\\u043b\\u0438\\u044f\\u043d\\u0438\\u044f \\u043f\\u043e\\u0432\\u0442\\u043e\\u0440\\u0438\\u0442\\u0435 Pull \\u0438\\u0437 \\u0438\\u043d\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u0430\",\n ],\n \"manual_commands\": [\"git status\", \"git add \", 'git commit -m \"resolve merge conflicts\"', \"git merge --abort\"],\n }\n # endregion _build_unfinished_merge_payload\n\n # region get_merge_status [TYPE Function]\n # @PURPOSE: Get current merge status for a dashboard repository.\n def get_merge_status(self, dashboard_id: int) -> dict[str, Any]:\n with belief_scope(\"GitService.get_merge_status\"):\n repo = self.get_repo(dashboard_id)\n merge_head_path = os.path.join(repo.git_dir, \"MERGE_HEAD\")\n if not os.path.exists(merge_head_path):\n current_branch = \"unknown\"\n try:\n current_branch = repo.active_branch.name\n except Exception:\n current_branch = \"detached_or_unknown\"\n return {\n \"has_unfinished_merge\": False,\n \"repository_path\": repo.working_tree_dir,\n \"git_dir\": repo.git_dir,\n \"current_branch\": current_branch,\n \"merge_head\": None,\n \"merge_message_preview\": None,\n \"conflicts_count\": 0,\n }\n payload = self._build_unfinished_merge_payload(repo)\n return {\n \"has_unfinished_merge\": True,\n \"repository_path\": payload[\"repository_path\"],\n \"git_dir\": payload[\"git_dir\"],\n \"current_branch\": payload[\"current_branch\"],\n \"merge_head\": payload[\"merge_head\"],\n \"merge_message_preview\": payload[\"merge_message_preview\"],\n \"conflicts_count\": int(payload.get(\"conflicts_count\") or 0),\n }\n # endregion get_merge_status\n\n # region get_merge_conflicts [TYPE Function]\n # @PURPOSE: List all files with conflicts and their contents.\n def get_merge_conflicts(self, dashboard_id: int) -> list[dict[str, Any]]:\n with belief_scope(\"GitService.get_merge_conflicts\"):\n repo = self.get_repo(dashboard_id)\n conflicts = []\n unmerged = repo.index.unmerged_blobs()\n for file_path, stages in unmerged.items():\n mine_blob = None\n theirs_blob = None\n for stage, blob in stages:\n if stage == 2:\n mine_blob = blob\n elif stage == 3:\n theirs_blob = blob\n conflicts.append({\n \"file_path\": file_path,\n \"mine\": self._read_blob_text(mine_blob) if mine_blob else \"\",\n \"theirs\": self._read_blob_text(theirs_blob) if theirs_blob else \"\",\n })\n return sorted(conflicts, key=lambda item: item[\"file_path\"])\n # endregion get_merge_conflicts\n\n # region resolve_merge_conflicts [TYPE Function]\n # @PURPOSE: Resolve conflicts using specified strategy.\n def resolve_merge_conflicts(self, dashboard_id: int, resolutions: list[dict[str, Any]]) -> list[str]:\n with belief_scope(\"GitService.resolve_merge_conflicts\"):\n repo = self.get_repo(dashboard_id)\n resolved_files: list[str] = []\n repo_root = os.path.abspath(str(repo.working_tree_dir or \"\"))\n if not repo_root:\n raise HTTPException(status_code=500, detail=\"Repository working tree directory is unavailable\")\n for item in resolutions or []:\n file_path = str(item.get(\"file_path\") or \"\").strip()\n strategy = str(item.get(\"resolution\") or \"\").strip().lower()\n content = item.get(\"content\")\n if not file_path:\n raise HTTPException(status_code=400, detail=\"resolution.file_path is required\")\n if strategy not in {\"mine\", \"theirs\", \"manual\"}:\n raise HTTPException(status_code=400, detail=f\"Unsupported resolution strategy: {strategy}\")\n if strategy == \"mine\":\n repo.git.checkout(\"--ours\", \"--\", file_path)\n elif strategy == \"theirs\":\n repo.git.checkout(\"--theirs\", \"--\", file_path)\n else:\n abs_target = os.path.abspath(os.path.join(repo_root, file_path))\n if abs_target != repo_root and not abs_target.startswith(repo_root + os.sep):\n raise HTTPException(status_code=400, detail=f\"Invalid conflict file path: {file_path}\")\n os.makedirs(os.path.dirname(abs_target), exist_ok=True)\n with open(abs_target, \"w\", encoding=\"utf-8\") as file_obj:\n file_obj.write(str(content or \"\"))\n repo.git.add(file_path)\n resolved_files.append(file_path)\n return resolved_files\n # endregion resolve_merge_conflicts\n\n # region abort_merge [TYPE Function]\n # @PURPOSE: Abort ongoing merge.\n def abort_merge(self, dashboard_id: int) -> dict[str, Any]:\n with belief_scope(\"GitService.abort_merge\"):\n repo = self.get_repo(dashboard_id)\n try:\n repo.git.merge(\"--abort\")\n except GitCommandError as e:\n details = str(e)\n lowered = details.lower()\n if \"there is no merge to abort\" in lowered or \"no merge to abort\" in lowered:\n return {\"status\": \"no_merge_in_progress\"}\n raise HTTPException(status_code=409, detail=f\"Cannot abort merge: {details}\")\n return {\"status\": \"aborted\"}\n # endregion abort_merge\n\n # region continue_merge [TYPE Function]\n # @PURPOSE: Finalize merge after conflict resolution.\n def continue_merge(self, dashboard_id: int, message: str | None = None) -> dict[str, Any]:\n with belief_scope(\"GitService.continue_merge\"):\n repo = self.get_repo(dashboard_id)\n unmerged_files = self._get_unmerged_file_paths(repo)\n if unmerged_files:\n raise HTTPException(\n status_code=409,\n detail={\n \"error_code\": \"GIT_MERGE_CONFLICTS_REMAIN\",\n \"message\": \"\\u041d\\u0435\\u0432\\u043e\\u0437\\u043c\\u043e\\u0436\\u043d\\u043e \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u044c merge: \\u043e\\u0441\\u0442\\u0430\\u043b\\u0438\\u0441\\u044c \\u043d\\u0435\\u0440\\u0430\\u0437\\u0440\\u0435\\u0448\\u0451\\u043d\\u043d\\u044b\\u0435 \\u043a\\u043e\\u043d\\u0444\\u043b\\u0438\\u043a\\u0442\\u044b.\",\n \"unresolved_files\": unmerged_files,\n },\n )\n try:\n normalized_message = str(message or \"\").strip()\n if normalized_message:\n repo.git.commit(\"-m\", normalized_message)\n else:\n repo.git.commit(\"--no-edit\")\n except GitCommandError as e:\n details = str(e)\n lowered = details.lower()\n if \"nothing to commit\" in lowered:\n return {\"status\": \"already_clean\"}\n raise HTTPException(status_code=409, detail=f\"Cannot continue merge: {details}\")\n commit_hash = \"\"\n try:\n commit_hash = repo.head.commit.hexsha\n except Exception:\n commit_hash = \"\"\n return {\"status\": \"committed\", \"commit_hash\": commit_hash}\n # endregion continue_merge\n\n # region promote_direct_merge [TYPE Function]\n # @PURPOSE: Perform direct merge between branches in local repo and push target branch.\n # @PRE: Repository exists and both branches are valid.\n # @POST: Target branch contains merged changes from source branch.\n # @RETURN: Dict[str, Any]\n def promote_direct_merge(self, dashboard_id: int, from_branch: str, to_branch: str) -> dict[str, Any]:\n with belief_scope(\"GitService.promote_direct_merge\"):\n if not from_branch or not to_branch:\n raise HTTPException(status_code=400, detail=\"from_branch and to_branch are required\")\n repo = self.get_repo(dashboard_id)\n source = from_branch.strip()\n target = to_branch.strip()\n if source == target:\n raise HTTPException(status_code=400, detail=\"from_branch and to_branch must be different\")\n try:\n origin = repo.remote(name=\"origin\")\n except ValueError:\n raise HTTPException(status_code=400, detail=\"Remote 'origin' not configured\")\n try:\n origin.fetch()\n if source not in [head.name for head in repo.heads]:\n if f\"origin/{source}\" in [ref.name for ref in repo.refs]:\n repo.git.checkout(\"-b\", source, f\"origin/{source}\")\n else:\n raise HTTPException(status_code=404, detail=f\"Source branch '{source}' not found\")\n if target in [head.name for head in repo.heads]:\n repo.git.checkout(target)\n elif f\"origin/{target}\" in [ref.name for ref in repo.refs]:\n repo.git.checkout(\"-b\", target, f\"origin/{target}\")\n else:\n raise HTTPException(status_code=404, detail=f\"Target branch '{target}' not found\")\n try:\n origin.pull(target)\n except Exception:\n pass\n repo.git.merge(source, \"--no-ff\", \"-m\", f\"chore(flow): promote {source} -> {target}\")\n origin.push(refspec=f\"{target}:{target}\")\n except HTTPException:\n raise\n except Exception as e:\n message = str(e)\n if \"CONFLICT\" in message.upper():\n raise HTTPException(status_code=409, detail=f\"Merge conflict during direct promote: {message}\")\n raise HTTPException(status_code=500, detail=f\"Direct promote failed: {message}\")\n return {\"mode\": \"direct\", \"from_branch\": source, \"to_branch\": target, \"status\": \"merged\"}\n # endregion promote_direct_merge\n# #endregion GitServiceMergeMixin\n# #endregion GitServiceMergeMixin\n" + "body": "# #region GitServiceMergeMixin [C:4] [TYPE Module] [SEMANTICS git, merge, branch, conflict, resolution, lock]\n# @LAYER: Infra\n# @BRIEF Merge operations for GitService — conflict detection, resolution, abort, continue, and direct promote (all concurrent-safe via per-dashboard locks).\n# @RELATION USED_BY -> [GitService]\n\nimport os\nfrom pathlib import Path\nfrom typing import Any\n\nfrom fastapi import HTTPException\nfrom git import Repo\nfrom git.exc import GitCommandError\nfrom git.objects.blob import Blob\n\nfrom src.core.logger import belief_scope\n\n\n# #region GitServiceMergeMixin [C:3] [TYPE Class]\n# @BRIEF Mixin providing merge operations for GitService.\nclass GitServiceMergeMixin:\n # region _read_blob_text [TYPE Function]\n # @PURPOSE: Read text from a Git blob.\n def _read_blob_text(self, blob: Blob) -> str:\n with belief_scope(\"GitService._read_blob_text\"):\n if blob is None:\n return \"\"\n try:\n return blob.data_stream.read().decode(\"utf-8\", errors=\"replace\")\n except Exception:\n return \"\"\n # endregion _read_blob_text\n\n # region _get_unmerged_file_paths [TYPE Function]\n # @PURPOSE: List files with merge conflicts.\n def _get_unmerged_file_paths(self, repo: Repo) -> list[str]:\n with belief_scope(\"GitService._get_unmerged_file_paths\"):\n try:\n return sorted(list(repo.index.unmerged_blobs().keys()))\n except Exception:\n return []\n # endregion _get_unmerged_file_paths\n\n # region _build_unfinished_merge_payload [TYPE Function]\n # @PURPOSE: Build payload for unfinished merge state.\n def _build_unfinished_merge_payload(self, repo: Repo) -> dict[str, Any]:\n with belief_scope(\"GitService._build_unfinished_merge_payload\"):\n merge_head_path = os.path.join(repo.git_dir, \"MERGE_HEAD\")\n merge_head_value = \"\"\n merge_msg_preview = \"\"\n current_branch = \"unknown\"\n try:\n merge_head_value = Path(merge_head_path).read_text(encoding=\"utf-8\").strip()\n except Exception:\n merge_head_value = \"\"\n try:\n merge_msg_path = os.path.join(repo.git_dir, \"MERGE_MSG\")\n if os.path.exists(merge_msg_path):\n merge_msg_preview = (\n Path(merge_msg_path).read_text(encoding=\"utf-8\").strip().splitlines()[:1] or [\"\"]\n )[0]\n except Exception:\n merge_msg_preview = \"\"\n try:\n current_branch = repo.active_branch.name\n except Exception:\n current_branch = \"detached_or_unknown\"\n conflicts_count = len(self._get_unmerged_file_paths(repo))\n return {\n \"error_code\": \"GIT_UNFINISHED_MERGE\",\n \"message\": (\n \"\\u0412 \\u0440\\u0435\\u043f\\u043e\\u0437\\u0438\\u0442\\u043e\\u0440\\u0438\\u0438 \\u0435\\u0441\\u0442\\u044c \\u043d\\u0435\\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0451\\u043d\\u043d\\u043e\\u0435 \\u0441\\u043b\\u0438\\u044f\\u043d\\u0438\\u0435. \"\n \"\\u0417\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u0435 \\u0438\\u043b\\u0438 \\u043e\\u0442\\u043c\\u0435\\u043d\\u0438\\u0442\\u0435 \\u0441\\u043b\\u0438\\u044f\\u043d\\u0438\\u0435 \\u0432\\u0440\\u0443\\u0447\\u043d\\u0443\\u044e.\"\n ),\n \"repository_path\": repo.working_tree_dir,\n \"git_dir\": repo.git_dir,\n \"current_branch\": current_branch,\n \"merge_head\": merge_head_value,\n \"merge_message_preview\": merge_msg_preview,\n \"conflicts_count\": conflicts_count,\n \"next_steps\": [\n \"\\u041e\\u0442\\u043a\\u0440\\u043e\\u0439\\u0442\\u0435 \\u043b\\u043e\\u043a\\u0430\\u043b\\u044c\\u043d\\u044b\\u0439 \\u0440\\u0435\\u043f\\u043e\\u0437\\u0438\\u0442\\u043e\\u0440\\u0438\\u0439 \\u043f\\u043e \\u043f\\u0443\\u0442\\u0438 repository_path\",\n \"\\u041f\\u0440\\u043e\\u0432\\u0435\\u0440\\u044c\\u0442\\u0435 \\u0441\\u043e\\u0441\\u0442\\u043e\\u044f\\u043d\\u0438\\u0435: git status\",\n \"\\u0420\\u0430\\u0437\\u0440\\u0435\\u0448\\u0438\\u0442\\u0435 \\u043a\\u043e\\u043d\\u0444\\u043b\\u0438\\u043a\\u0442\\u044b \\u0438 \\u0432\\u044b\\u043f\\u043e\\u043b\\u043d\\u0438\\u0442\\u0435 commit, \\u043b\\u0438\\u0431\\u043e \\u043e\\u0442\\u043c\\u0435\\u043d\\u0438\\u0442\\u0435: git merge --abort\",\n \"\\u041f\\u043e\\u0441\\u043b\\u0435 \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0435\\u043d\\u0438\\u044f/\\u043e\\u0442\\u043c\\u0435\\u043d\\u044b \\u0441\\u043b\\u0438\\u044f\\u043d\\u0438\\u044f \\u043f\\u043e\\u0432\\u0442\\u043e\\u0440\\u0438\\u0442\\u0435 Pull \\u0438\\u0437 \\u0438\\u043d\\u0442\\u0435\\u0440\\u0444\\u0435\\u0439\\u0441\\u0430\",\n ],\n \"manual_commands\": [\"git status\", \"git add \", 'git commit -m \"resolve merge conflicts\"', \"git merge --abort\"],\n }\n # endregion _build_unfinished_merge_payload\n\n # region get_merge_status [C:4] [TYPE Function] [SEMANTICS git,merge,status,lock]\n # @PURPOSE: Get current merge status for a dashboard repository (concurrent-safe).\n def get_merge_status(self, dashboard_id: int) -> dict[str, Any]:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.get_merge_status\"):\n repo = self.get_repo(dashboard_id)\n merge_head_path = os.path.join(repo.git_dir, \"MERGE_HEAD\")\n if not os.path.exists(merge_head_path):\n current_branch = \"unknown\"\n try:\n current_branch = repo.active_branch.name\n except Exception:\n current_branch = \"detached_or_unknown\"\n return {\n \"has_unfinished_merge\": False,\n \"repository_path\": repo.working_tree_dir,\n \"git_dir\": repo.git_dir,\n \"current_branch\": current_branch,\n \"merge_head\": None,\n \"merge_message_preview\": None,\n \"conflicts_count\": 0,\n }\n payload = self._build_unfinished_merge_payload(repo)\n return {\n \"has_unfinished_merge\": True,\n \"repository_path\": payload[\"repository_path\"],\n \"git_dir\": payload[\"git_dir\"],\n \"current_branch\": payload[\"current_branch\"],\n \"merge_head\": payload[\"merge_head\"],\n \"merge_message_preview\": payload[\"merge_message_preview\"],\n \"conflicts_count\": int(payload.get(\"conflicts_count\") or 0),\n }\n # endregion get_merge_status\n\n # region get_merge_conflicts [C:4] [TYPE Function] [SEMANTICS git,conflict,list,lock]\n # @PURPOSE: List all files with conflicts and their contents (concurrent-safe).\n def get_merge_conflicts(self, dashboard_id: int) -> list[dict[str, Any]]:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.get_merge_conflicts\"):\n repo = self.get_repo(dashboard_id)\n conflicts = []\n unmerged = repo.index.unmerged_blobs()\n for file_path, stages in unmerged.items():\n mine_blob = None\n theirs_blob = None\n for stage, blob in stages:\n if stage == 2:\n mine_blob = blob\n elif stage == 3:\n theirs_blob = blob\n conflicts.append({\n \"file_path\": file_path,\n \"mine\": self._read_blob_text(mine_blob) if mine_blob else \"\",\n \"theirs\": self._read_blob_text(theirs_blob) if theirs_blob else \"\",\n })\n return sorted(conflicts, key=lambda item: item[\"file_path\"])\n # endregion get_merge_conflicts\n\n # region resolve_merge_conflicts [C:4] [TYPE Function] [SEMANTICS git,conflict,resolve,lock]\n # @PURPOSE: Resolve conflicts using specified strategy (concurrent-safe).\n def resolve_merge_conflicts(self, dashboard_id: int, resolutions: list[dict[str, Any]]) -> list[str]:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.resolve_merge_conflicts\"):\n repo = self.get_repo(dashboard_id)\n resolved_files: list[str] = []\n repo_root = os.path.abspath(str(repo.working_tree_dir or \"\"))\n if not repo_root:\n raise HTTPException(status_code=500, detail=\"Repository working tree directory is unavailable\")\n for item in resolutions or []:\n file_path = str(item.get(\"file_path\") or \"\").strip()\n strategy = str(item.get(\"resolution\") or \"\").strip().lower()\n content = item.get(\"content\")\n if not file_path:\n raise HTTPException(status_code=400, detail=\"resolution.file_path is required\")\n if strategy not in {\"mine\", \"theirs\", \"manual\"}:\n raise HTTPException(status_code=400, detail=f\"Unsupported resolution strategy: {strategy}\")\n if strategy == \"mine\":\n repo.git.checkout(\"--ours\", \"--\", file_path)\n elif strategy == \"theirs\":\n repo.git.checkout(\"--theirs\", \"--\", file_path)\n else:\n abs_target = os.path.abspath(os.path.join(repo_root, file_path))\n if abs_target != repo_root and not abs_target.startswith(repo_root + os.sep):\n raise HTTPException(status_code=400, detail=f\"Invalid conflict file path: {file_path}\")\n os.makedirs(os.path.dirname(abs_target), exist_ok=True)\n with open(abs_target, \"w\", encoding=\"utf-8\") as file_obj:\n file_obj.write(str(content or \"\"))\n repo.git.add(file_path)\n resolved_files.append(file_path)\n return resolved_files\n # endregion resolve_merge_conflicts\n\n # region abort_merge [C:4] [TYPE Function] [SEMANTICS git,merge,abort,lock]\n # @PURPOSE: Abort ongoing merge (concurrent-safe).\n def abort_merge(self, dashboard_id: int) -> dict[str, Any]:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.abort_merge\"):\n repo = self.get_repo(dashboard_id)\n try:\n repo.git.merge(\"--abort\")\n except GitCommandError as e:\n details = str(e)\n lowered = details.lower()\n if \"there is no merge to abort\" in lowered or \"no merge to abort\" in lowered:\n return {\"status\": \"no_merge_in_progress\"}\n raise HTTPException(status_code=409, detail=f\"Cannot abort merge: {details}\")\n return {\"status\": \"aborted\"}\n # endregion abort_merge\n\n # region continue_merge [C:4] [TYPE Function] [SEMANTICS git,merge,continue,lock]\n # @PURPOSE: Finalize merge after conflict resolution (concurrent-safe).\n def continue_merge(self, dashboard_id: int, message: str | None = None) -> dict[str, Any]:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.continue_merge\"):\n repo = self.get_repo(dashboard_id)\n unmerged_files = self._get_unmerged_file_paths(repo)\n if unmerged_files:\n raise HTTPException(\n status_code=409,\n detail={\n \"error_code\": \"GIT_MERGE_CONFLICTS_REMAIN\",\n \"message\": \"\\u041d\\u0435\\u0432\\u043e\\u0437\\u043c\\u043e\\u0436\\u043d\\u043e \\u0437\\u0430\\u0432\\u0435\\u0440\\u0448\\u0438\\u0442\\u044c merge: \\u043e\\u0441\\u0442\\u0430\\u043b\\u0438\\u0441\\u044c \\u043d\\u0435\\u0440\\u0430\\u0437\\u0440\\u0435\\u0448\\u0451\\u043d\\u043d\\u044b\\u0435 \\u043a\\u043e\\u043d\\u0444\\u043b\\u0438\\u043a\\u0442\\u044b.\",\n \"unresolved_files\": unmerged_files,\n },\n )\n try:\n normalized_message = str(message or \"\").strip()\n if normalized_message:\n repo.git.commit(\"-m\", normalized_message)\n else:\n repo.git.commit(\"--no-edit\")\n except GitCommandError as e:\n details = str(e)\n lowered = details.lower()\n if \"nothing to commit\" in lowered:\n return {\"status\": \"already_clean\"}\n raise HTTPException(status_code=409, detail=f\"Cannot continue merge: {details}\")\n commit_hash = \"\"\n try:\n commit_hash = repo.head.commit.hexsha\n except Exception:\n commit_hash = \"\"\n return {\"status\": \"committed\", \"commit_hash\": commit_hash}\n # endregion continue_merge\n\n # region promote_direct_merge [C:4] [TYPE Function] [SEMANTICS git,merge,promote,branch,isolation]\n # @PURPOSE: Perform direct merge between branches with branch isolation — original branch restored on error.\n # @PRE: Repository exists and both branches are valid.\n # @POST: Target branch contains merged changes from source branch. Active branch restored to original.\n # Merge survives locally even if push fails (partial success).\n # @SIDE_EFFECT: Changes local branch state during merge; restores original branch in finally block.\n # @RETURN: Dict[str, Any]\n def promote_direct_merge(self, dashboard_id: int, from_branch: str, to_branch: str) -> dict[str, Any]:\n with self._locked(dashboard_id):\n with belief_scope(\"GitService.promote_direct_merge\"):\n if not from_branch or not to_branch:\n raise HTTPException(status_code=400, detail=\"from_branch and to_branch are required\")\n repo = self.get_repo(dashboard_id)\n source = from_branch.strip()\n target = to_branch.strip()\n if source == target:\n raise HTTPException(status_code=400, detail=\"from_branch and to_branch must be different\")\n try:\n origin = repo.remote(name=\"origin\")\n except ValueError:\n raise HTTPException(status_code=400, detail=\"Remote 'origin' not configured\")\n\n # Remember original branch for restoration in finally\n original_branch = None\n try:\n original_branch = repo.active_branch.name\n except Exception:\n original_branch = None\n\n try:\n origin.fetch()\n if source not in [head.name for head in repo.heads]:\n if f\"origin/{source}\" in [ref.name for ref in repo.refs]:\n repo.git.checkout(\"-b\", source, f\"origin/{source}\")\n else:\n raise HTTPException(status_code=404, detail=f\"Source branch '{source}' not found\")\n if target in [head.name for head in repo.heads]:\n repo.git.checkout(target)\n elif f\"origin/{target}\" in [ref.name for ref in repo.refs]:\n repo.git.checkout(\"-b\", target, f\"origin/{target}\")\n else:\n raise HTTPException(status_code=404, detail=f\"Target branch '{target}' not found\")\n try:\n origin.pull(target)\n except Exception:\n pass\n repo.git.merge(source, \"--no-ff\", \"-m\", f\"chore(flow): promote {source} -> {target}\")\n origin.push(refspec=f\"{target}:{target}\")\n except HTTPException:\n raise\n except Exception as e:\n message = str(e)\n if \"CONFLICT\" in message.upper():\n raise HTTPException(status_code=409, detail=f\"Merge conflict during direct promote: {message}\")\n raise HTTPException(status_code=500, detail=f\"Direct promote failed: {message}\")\n finally:\n # Restore original branch even if merge/push partially failed\n if original_branch:\n try:\n repo.git.checkout(original_branch)\n except Exception:\n pass\n return {\"mode\": \"direct\", \"from_branch\": source, \"to_branch\": target, \"status\": \"merged\"}\n # endregion promote_direct_merge\n# #endregion GitServiceMergeMixin\n# #endregion GitServiceMergeMixin\n" }, { "contract_id": "GitServiceGitlabMixin", @@ -59749,7 +59025,7 @@ "contract_type": "Module", "file_path": "backend/src/services/llm_prompt_templates.py", "start_line": 1, - "end_line": 194, + "end_line": 205, "tier": "TIER_1", "complexity": 2, "metadata": { @@ -59788,14 +59064,14 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region llm_prompt_templates [C:2] [TYPE Module] [SEMANTICS llm, prompt, template, normalization]\n# @BRIEF Provide default LLM prompt templates and normalization helpers for runtime usage.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [backend.src.core.config_manager:Function]\n# @INVARIANT: All required prompt template keys are always present after normalization.\n\nfrom __future__ import annotations\n\nfrom copy import deepcopy\nfrom typing import Any\n\n# #region DEFAULT_LLM_PROMPTS [C:2] [TYPE Constant]\n# @BRIEF Default prompt templates used by documentation, dashboard validation, and git commit generation.\nDEFAULT_LLM_PROMPTS: dict[str, str] = {\n \"dashboard_validation_prompt\": (\n \"Analyze the attached dashboard screenshot and the following execution logs for health and visual issues.\\n\\n\"\n \"Logs:\\n\"\n \"{logs}\\n\\n\"\n \"Provide the analysis in JSON format with the following structure:\\n\"\n \"{\\n\"\n ' \"status\": \"PASS\" | \"WARN\" | \"FAIL\",\\n'\n ' \"summary\": \"Short summary of findings\",\\n'\n ' \"issues\": [\\n'\n \" {\\n\"\n ' \"severity\": \"WARN\" | \"FAIL\",\\n'\n ' \"message\": \"Description of the issue\",\\n'\n ' \"location\": \"Optional location info (e.g. chart name)\"\\n'\n \" }\\n\"\n \" ]\\n\"\n \"}\"\n ),\n \"documentation_prompt\": (\n \"Generate professional documentation for the following dataset and its columns.\\n\"\n \"Dataset: {dataset_name}\\n\"\n \"Columns: {columns_json}\\n\\n\"\n \"Provide the documentation in JSON format:\\n\"\n \"{\\n\"\n ' \"dataset_description\": \"General description of the dataset\",\\n'\n ' \"column_descriptions\": [\\n'\n \" {\\n\"\n ' \"name\": \"column_name\",\\n'\n ' \"description\": \"Generated description\"\\n'\n \" }\\n\"\n \" ]\\n\"\n \"}\"\n ),\n \"git_commit_prompt\": (\n \"Generate a concise and professional git commit message based on the following diff and recent history.\\n\"\n \"Use Conventional Commits format (e.g., feat: ..., fix: ..., docs: ...).\\n\\n\"\n \"Recent History:\\n\"\n \"{history}\\n\\n\"\n \"Diff:\\n\"\n \"{diff}\\n\\n\"\n \"Commit Message:\"\n ),\n}\n# #endregion DEFAULT_LLM_PROMPTS\n\n\n# #region DEFAULT_LLM_PROVIDER_BINDINGS [C:2] [TYPE Constant]\n# @BRIEF Default provider binding per task domain.\nDEFAULT_LLM_PROVIDER_BINDINGS: dict[str, str] = {\n \"dashboard_validation\": \"\",\n \"documentation\": \"\",\n \"git_commit\": \"\",\n}\n# #endregion DEFAULT_LLM_PROVIDER_BINDINGS\n\n\n# #region DEFAULT_LLM_ASSISTANT_SETTINGS [C:2] [TYPE Constant]\n# @BRIEF Default planner settings for assistant chat intent model/provider resolution.\nDEFAULT_LLM_ASSISTANT_SETTINGS: dict[str, str] = {\n \"assistant_planner_provider\": \"\",\n \"assistant_planner_model\": \"\",\n}\n# #endregion DEFAULT_LLM_ASSISTANT_SETTINGS\n\n\n# #region normalize_llm_settings [C:3] [TYPE Function]\n# @BRIEF Ensure llm settings contain stable schema with prompts section and default templates.\n# @PRE: llm_settings is dictionary-like value or None.\n# @POST: Returned dict contains prompts with all required template keys.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef normalize_llm_settings(llm_settings: Any) -> dict[str, Any]:\n normalized: dict[str, Any] = {\n \"providers\": [],\n \"default_provider\": \"\",\n \"prompts\": {},\n \"provider_bindings\": {},\n **DEFAULT_LLM_ASSISTANT_SETTINGS,\n }\n if isinstance(llm_settings, dict):\n normalized.update(\n {\n k: v\n for k, v in llm_settings.items()\n if k\n in (\n \"providers\",\n \"default_provider\",\n \"prompts\",\n \"provider_bindings\",\n \"assistant_planner_provider\",\n \"assistant_planner_model\",\n )\n }\n )\n prompts = normalized.get(\"prompts\") if isinstance(normalized.get(\"prompts\"), dict) else {}\n merged_prompts = deepcopy(DEFAULT_LLM_PROMPTS)\n merged_prompts.update({k: v for k, v in prompts.items() if isinstance(v, str) and v.strip()})\n normalized[\"prompts\"] = merged_prompts\n bindings = normalized.get(\"provider_bindings\") if isinstance(normalized.get(\"provider_bindings\"), dict) else {}\n merged_bindings = deepcopy(DEFAULT_LLM_PROVIDER_BINDINGS)\n merged_bindings.update({k: v for k, v in bindings.items() if isinstance(v, str)})\n normalized[\"provider_bindings\"] = merged_bindings\n for key, default_value in DEFAULT_LLM_ASSISTANT_SETTINGS.items():\n value = normalized.get(key, default_value)\n normalized[key] = value.strip() if isinstance(value, str) else default_value\n return normalized\n# #endregion normalize_llm_settings\n\n\n# #region is_multimodal_model [C:3] [TYPE Function]\n# @BRIEF Heuristically determine whether model supports image input required for dashboard validation.\n# @PRE: model_name may be empty or mixed-case.\n# @POST: Returns True when model likely supports multimodal input.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef is_multimodal_model(model_name: str, provider_type: str | None = None) -> bool:\n token = (model_name or \"\").strip().lower()\n if not token:\n return False\n provider = (provider_type or \"\").strip().lower()\n text_only_markers = (\n \"text-only\",\n \"embedding\",\n \"rerank\",\n \"whisper\",\n \"tts\",\n \"transcribe\",\n )\n if any(marker in token for marker in text_only_markers):\n return False\n multimodal_markers = (\n \"gpt-4o\",\n \"gpt-4.1\",\n \"vision\",\n \"vl\",\n \"gemini\",\n \"claude-3\",\n \"claude-sonnet-4\",\n \"omni\",\n \"multimodal\",\n \"pixtral\",\n \"llava\",\n \"internvl\",\n \"qwen-vl\",\n \"qwen2-vl\",\n )\n if any(marker in token for marker in multimodal_markers):\n return True\n return False\n# #endregion is_multimodal_model\n\n\n# #region resolve_bound_provider_id [C:3] [TYPE Function]\n# @BRIEF Resolve provider id configured for a task binding with fallback to default provider.\n# @PRE: llm_settings is normalized or raw dict from config.\n# @POST: Returns configured provider id or fallback id/empty string when not defined.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef resolve_bound_provider_id(llm_settings: Any, task_key: str) -> str:\n normalized = normalize_llm_settings(llm_settings)\n bindings = normalized.get(\"provider_bindings\", {})\n bound = bindings.get(task_key)\n if isinstance(bound, str) and bound.strip():\n return bound.strip()\n default_provider = normalized.get(\"default_provider\", \"\")\n return default_provider.strip() if isinstance(default_provider, str) else \"\"\n# #endregion resolve_bound_provider_id\n\n\n# #region render_prompt [C:3] [TYPE Function]\n# @BRIEF Render prompt template using deterministic placeholder replacement with graceful fallback.\n# @PRE: template is a string and variables values are already stringifiable.\n# @POST: Returns rendered prompt text with known placeholders substituted.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef render_prompt(template: str, variables: dict[str, Any]) -> str:\n rendered = template\n for key, value in variables.items():\n rendered = rendered.replace(\"{\" + key + \"}\", str(value))\n return rendered\n# #endregion render_prompt\n\n\n# #endregion llm_prompt_templates\n" + "body": "# #region llm_prompt_templates [C:2] [TYPE Module] [SEMANTICS llm, prompt, template, normalization]\n# @BRIEF Provide default LLM prompt templates and normalization helpers for runtime usage.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [backend.src.core.config_manager:Function]\n# @INVARIANT: All required prompt template keys are always present after normalization.\n\nfrom __future__ import annotations\n\nfrom copy import deepcopy\nfrom typing import Any\n\nfrom ..core.logger import logger\n\n# #region DEFAULT_LLM_PROMPTS [C:2] [TYPE Constant]\n# @BRIEF Default prompt templates used by documentation, dashboard validation, and git commit generation.\nDEFAULT_LLM_PROMPTS: dict[str, str] = {\n \"dashboard_validation_prompt\": (\n \"Analyze the attached dashboard screenshot and the following execution logs for health and visual issues.\\n\\n\"\n \"Logs:\\n\"\n \"{logs}\\n\\n\"\n \"Provide the analysis in JSON format with the following structure:\\n\"\n \"{\\n\"\n ' \"status\": \"PASS\" | \"WARN\" | \"FAIL\",\\n'\n ' \"summary\": \"Short summary of findings\",\\n'\n ' \"issues\": [\\n'\n \" {\\n\"\n ' \"severity\": \"WARN\" | \"FAIL\",\\n'\n ' \"message\": \"Description of the issue\",\\n'\n ' \"location\": \"Optional location info (e.g. chart name)\"\\n'\n \" }\\n\"\n \" ]\\n\"\n \"}\"\n ),\n \"documentation_prompt\": (\n \"Generate professional documentation for the following dataset and its columns.\\n\"\n \"Dataset: {dataset_name}\\n\"\n \"Columns: {columns_json}\\n\\n\"\n \"Provide the documentation in JSON format:\\n\"\n \"{\\n\"\n ' \"dataset_description\": \"General description of the dataset\",\\n'\n ' \"column_descriptions\": [\\n'\n \" {\\n\"\n ' \"name\": \"column_name\",\\n'\n ' \"description\": \"Generated description\"\\n'\n \" }\\n\"\n \" ]\\n\"\n \"}\"\n ),\n \"git_commit_prompt\": (\n \"Generate a concise and professional git commit message based on the following diff and recent history.\\n\"\n \"Use Conventional Commits format (e.g., feat: ..., fix: ..., docs: ...).\\n\\n\"\n \"Recent History:\\n\"\n \"{history}\\n\\n\"\n \"Diff:\\n\"\n \"{diff}\\n\\n\"\n \"Commit Message:\"\n ),\n}\n# #endregion DEFAULT_LLM_PROMPTS\n\n\n# #region DEFAULT_LLM_PROVIDER_BINDINGS [C:2] [TYPE Constant]\n# @BRIEF Default provider binding per task domain.\nDEFAULT_LLM_PROVIDER_BINDINGS: dict[str, str] = {\n \"dashboard_validation\": \"\",\n \"documentation\": \"\",\n \"git_commit\": \"\",\n}\n# #endregion DEFAULT_LLM_PROVIDER_BINDINGS\n\n\n# #region DEFAULT_LLM_ASSISTANT_SETTINGS [C:2] [TYPE Constant]\n# @BRIEF Default planner settings for assistant chat intent model/provider resolution.\nDEFAULT_LLM_ASSISTANT_SETTINGS: dict[str, str] = {\n \"assistant_planner_provider\": \"\",\n \"assistant_planner_model\": \"\",\n}\n# #endregion DEFAULT_LLM_ASSISTANT_SETTINGS\n\n\n# #region normalize_llm_settings [C:3] [TYPE Function]\n# @BRIEF Ensure llm settings contain stable schema with prompts section and default templates.\n# @PRE: llm_settings is dictionary-like value or None.\n# @POST: Returned dict contains prompts with all required template keys.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef normalize_llm_settings(llm_settings: Any) -> dict[str, Any]:\n normalized: dict[str, Any] = {\n \"providers\": [],\n \"default_provider\": \"\",\n \"prompts\": {},\n \"provider_bindings\": {},\n **DEFAULT_LLM_ASSISTANT_SETTINGS,\n }\n if isinstance(llm_settings, dict):\n normalized.update(\n {\n k: v\n for k, v in llm_settings.items()\n if k\n in (\n \"providers\",\n \"default_provider\",\n \"prompts\",\n \"provider_bindings\",\n \"assistant_planner_provider\",\n \"assistant_planner_model\",\n )\n }\n )\n prompts = normalized.get(\"prompts\") if isinstance(normalized.get(\"prompts\"), dict) else {}\n merged_prompts = deepcopy(DEFAULT_LLM_PROMPTS)\n merged_prompts.update({k: v for k, v in prompts.items() if isinstance(v, str) and v.strip()})\n normalized[\"prompts\"] = merged_prompts\n bindings = normalized.get(\"provider_bindings\") if isinstance(normalized.get(\"provider_bindings\"), dict) else {}\n merged_bindings = deepcopy(DEFAULT_LLM_PROVIDER_BINDINGS)\n merged_bindings.update({k: v for k, v in bindings.items() if isinstance(v, str)})\n normalized[\"provider_bindings\"] = merged_bindings\n for key, default_value in DEFAULT_LLM_ASSISTANT_SETTINGS.items():\n value = normalized.get(key, default_value)\n normalized[key] = value.strip() if isinstance(value, str) else default_value\n return normalized\n# #endregion normalize_llm_settings\n\n\n# #region is_multimodal_model [C:3] [TYPE Function]\n# @BRIEF Heuristically determine whether model supports image input required for dashboard validation.\n# @PRE: model_name may be empty or mixed-case.\n# @POST: Returns True when model likely supports multimodal input.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef is_multimodal_model(model_name: str, provider_type: str | None = None) -> bool:\n token = (model_name or \"\").strip().lower()\n if not token:\n return False\n provider = (provider_type or \"\").strip().lower()\n text_only_markers = (\n \"text-only\",\n \"embedding\",\n \"rerank\",\n \"whisper\",\n \"tts\",\n \"transcribe\",\n )\n if any(marker in token for marker in text_only_markers):\n return False\n multimodal_markers = (\n \"gpt-4o\",\n \"gpt-4.1\",\n \"vision\",\n \"vl\",\n \"gemini\",\n \"claude-3\",\n \"claude-sonnet-4\",\n \"omni\",\n \"multimodal\",\n \"pixtral\",\n \"llava\",\n \"internvl\",\n \"qwen-vl\",\n \"qwen2-vl\",\n )\n if any(marker in token for marker in multimodal_markers):\n return True\n return False\n# #endregion is_multimodal_model\n\n\n# #region resolve_bound_provider_id [C:3] [TYPE Function]\n# @BRIEF Resolve provider id configured for a task binding with fallback to default provider.\n# @PRE: llm_settings is normalized or raw dict from config.\n# @POST: Returns configured provider id or fallback id/empty string when not defined.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef resolve_bound_provider_id(llm_settings: Any, task_key: str) -> str:\n normalized = normalize_llm_settings(llm_settings)\n bindings = normalized.get(\"provider_bindings\", {})\n bound = bindings.get(task_key)\n if isinstance(bound, str) and bound.strip():\n return bound.strip()\n default_provider = normalized.get(\"default_provider\", \"\")\n return default_provider.strip() if isinstance(default_provider, str) else \"\"\n# #endregion resolve_bound_provider_id\n\n\n# #region render_prompt [C:3] [TYPE Function]\n# @BRIEF Render prompt template using deterministic placeholder replacement with graceful fallback.\n# @PRE: template is a string and variables values are already stringifiable.\n# @POST: Returns rendered prompt text with known placeholders substituted. Warns about unfilled placeholders.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef render_prompt(template: str, variables: dict[str, Any]) -> str:\n rendered = template\n for key, value in variables.items():\n rendered = rendered.replace(\"{\" + key + \"}\", str(value))\n\n # Warn about unfilled placeholders that would be sent to LLM\n import re\n unfilled = re.findall(r'\\{(\\w+)\\}', rendered)\n if unfilled:\n logger.warning(\n f\"[render_prompt] Unfilled placeholders in rendered prompt: {unfilled}\"\n )\n\n return rendered\n# #endregion render_prompt\n\n\n# #endregion llm_prompt_templates\n" }, { "contract_id": "DEFAULT_LLM_PROMPTS", "contract_type": "Constant", "file_path": "backend/src/services/llm_prompt_templates.py", - "start_line": 12, - "end_line": 57, + "start_line": 14, + "end_line": 59, "tier": "TIER_1", "complexity": 2, "metadata": { @@ -59866,8 +59142,8 @@ "contract_id": "DEFAULT_LLM_PROVIDER_BINDINGS", "contract_type": "Constant", "file_path": "backend/src/services/llm_prompt_templates.py", - "start_line": 60, - "end_line": 67, + "start_line": 62, + "end_line": 69, "tier": "TIER_1", "complexity": 2, "metadata": { @@ -59938,8 +59214,8 @@ "contract_id": "DEFAULT_LLM_ASSISTANT_SETTINGS", "contract_type": "Constant", "file_path": "backend/src/services/llm_prompt_templates.py", - "start_line": 70, - "end_line": 76, + "start_line": 72, + "end_line": 78, "tier": "TIER_1", "complexity": 2, "metadata": { @@ -60010,8 +59286,8 @@ "contract_id": "normalize_llm_settings", "contract_type": "Function", "file_path": "backend/src/services/llm_prompt_templates.py", - "start_line": 79, - "end_line": 120, + "start_line": 81, + "end_line": 122, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -60056,8 +59332,8 @@ "contract_id": "is_multimodal_model", "contract_type": "Function", "file_path": "backend/src/services/llm_prompt_templates.py", - "start_line": 123, - "end_line": 162, + "start_line": 125, + "end_line": 164, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -60102,8 +59378,8 @@ "contract_id": "resolve_bound_provider_id", "contract_type": "Function", "file_path": "backend/src/services/llm_prompt_templates.py", - "start_line": 165, - "end_line": 178, + "start_line": 167, + "end_line": 180, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -60148,13 +59424,13 @@ "contract_id": "render_prompt", "contract_type": "Function", "file_path": "backend/src/services/llm_prompt_templates.py", - "start_line": 181, - "end_line": 191, + "start_line": 183, + "end_line": 202, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "POST": "Returns rendered prompt text with known placeholders substituted.", + "POST": "Returns rendered prompt text with known placeholders substituted. Warns about unfilled placeholders.", "PRE": "template is a string and variables values are already stringifiable.", "PURPOSE": "Render prompt template using deterministic placeholder replacement with graceful fallback." }, @@ -60188,14 +59464,14 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region render_prompt [C:3] [TYPE Function]\n# @BRIEF Render prompt template using deterministic placeholder replacement with graceful fallback.\n# @PRE: template is a string and variables values are already stringifiable.\n# @POST: Returns rendered prompt text with known placeholders substituted.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef render_prompt(template: str, variables: dict[str, Any]) -> str:\n rendered = template\n for key, value in variables.items():\n rendered = rendered.replace(\"{\" + key + \"}\", str(value))\n return rendered\n# #endregion render_prompt\n" + "body": "# #region render_prompt [C:3] [TYPE Function]\n# @BRIEF Render prompt template using deterministic placeholder replacement with graceful fallback.\n# @PRE: template is a string and variables values are already stringifiable.\n# @POST: Returns rendered prompt text with known placeholders substituted. Warns about unfilled placeholders.\n# @RELATION DEPENDS_ON -> LLMProviderService\ndef render_prompt(template: str, variables: dict[str, Any]) -> str:\n rendered = template\n for key, value in variables.items():\n rendered = rendered.replace(\"{\" + key + \"}\", str(value))\n\n # Warn about unfilled placeholders that would be sent to LLM\n import re\n unfilled = re.findall(r'\\{(\\w+)\\}', rendered)\n if unfilled:\n logger.warning(\n f\"[render_prompt] Unfilled placeholders in rendered prompt: {unfilled}\"\n )\n\n return rendered\n# #endregion render_prompt\n" }, { "contract_id": "llm_provider", "contract_type": "Module", "file_path": "backend/src/services/llm_provider.py", "start_line": 1, - "end_line": 261, + "end_line": 304, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -60226,14 +59502,92 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region llm_provider [C:3] [TYPE Module] [SEMANTICS sqlalchemy, llm, provider, encryption, config]\n# @BRIEF Service for managing LLM provider configurations with encrypted API keys.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [LLMProvider]\n# @RELATION DEPENDS_ON -> [EncryptionManager]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\nimport os\nfrom typing import TYPE_CHECKING\n\nfrom cryptography.fernet import Fernet\nfrom sqlalchemy.orm import Session\n\nfrom ..core.logger import belief_scope, logger\nfrom ..models.llm import LLMProvider\n\nif TYPE_CHECKING:\n from ..plugins.llm_analysis.models import LLMProviderConfig\n\nMASKED_API_KEY_PLACEHOLDER = \"********\"\n\n\n# #region _require_fernet_key [C:5] [TYPE Function]\n# @BRIEF Load and validate the Fernet key used for secret encryption.\n# @PRE: ENCRYPTION_KEY environment variable must be set to a valid Fernet key.\n# @POST: Returns validated key bytes ready for Fernet initialization.\n# @RELATION DEPENDS_ON -> [backend.src.core.logger:Function]\n# @SIDE_EFFECT: Emits belief-state logs for missing or invalid encryption configuration.\n# @DATA_CONTRACT: Input[ENCRYPTION_KEY:str] -> Output[bytes]\n# @INVARIANT: Encryption initialization never falls back to a hardcoded secret.\ndef _require_fernet_key() -> bytes:\n with belief_scope(\"_require_fernet_key\"):\n raw_key = os.getenv(\"ENCRYPTION_KEY\", \"\").strip()\n if not raw_key:\n logger.explore(\n \"Missing ENCRYPTION_KEY blocks EncryptionManager initialization\"\n )\n raise RuntimeError(\"ENCRYPTION_KEY must be set\")\n\n key = raw_key.encode()\n try:\n Fernet(key)\n except Exception as exc:\n logger.explore(\n \"Invalid ENCRYPTION_KEY blocks EncryptionManager initialization\"\n )\n raise RuntimeError(\"ENCRYPTION_KEY must be a valid Fernet key\") from exc\n\n logger.reflect(\"Validated ENCRYPTION_KEY for EncryptionManager initialization\")\n return key\n\n\n# #endregion _require_fernet_key\n\n\n# #region EncryptionManager [C:5] [TYPE Class]\n# @BRIEF Handles encryption and decryption of sensitive data like API keys.\n# @RELATION CALLS -> [_require_fernet_key]\n# @PRE: ENCRYPTION_KEY is configured with a valid Fernet key before instantiation.\n# @POST: Manager exposes reversible encrypt/decrypt operations for persisted secrets.\n# @SIDE_EFFECT: Initializes Fernet cryptography state from process environment.\n# @DATA_CONTRACT: Input[str] -> Output[str]\n# @INVARIANT: Uses only a validated secret key from environment.\n#\n# @TEST_CONTRACT: EncryptionManagerModel ->\n# {\n# required_fields: {},\n# invariants: [\n# \"encrypted data can be decrypted back to the original string\"\n# ]\n# }\n# @TEST_FIXTURE: basic_encryption_cycle -> {\"data\": \"my_secret_key\"}\n# @TEST_EDGE: decrypt_invalid_data -> raises Exception\n# @TEST_EDGE: empty_string_encryption -> {\"data\": \"\"}\n# @TEST_INVARIANT: symmetric_encryption -> verifies: [basic_encryption_cycle, empty_string_encryption]\nclass EncryptionManager:\n # region EncryptionManager_init [TYPE Function]\n # @PURPOSE: Initialize the encryption manager with a Fernet key.\n # @PRE: ENCRYPTION_KEY env var must be set to a valid Fernet key.\n # @POST: Fernet instance ready for encryption/decryption.\n def __init__(self):\n self.key = _require_fernet_key()\n self.fernet = Fernet(self.key)\n\n # endregion EncryptionManager_init\n\n # region encrypt [TYPE Function]\n # @PURPOSE: Encrypt a plaintext string.\n # @PRE: data must be a non-empty string.\n # @POST: Returns encrypted string.\n def encrypt(self, data: str) -> str:\n with belief_scope(\"encrypt\"):\n return self.fernet.encrypt(data.encode()).decode()\n\n # endregion encrypt\n\n # region decrypt [TYPE Function]\n # @PURPOSE: Decrypt an encrypted string.\n # @PRE: encrypted_data must be a valid Fernet-encrypted string.\n # @POST: Returns original plaintext string.\n def decrypt(self, encrypted_data: str) -> str:\n with belief_scope(\"decrypt\"):\n return self.fernet.decrypt(encrypted_data.encode()).decode()\n\n # endregion decrypt\n\n\n# #endregion EncryptionManager\n\n\n# #region LLMProviderService [C:3] [TYPE Class]\n# @BRIEF Service to manage LLM provider lifecycle.\n# @RELATION DEPENDS_ON -> [LLMProvider]\n# @RELATION DEPENDS_ON -> [EncryptionManager]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\nclass LLMProviderService:\n # region LLMProviderService_init [TYPE Function]\n # @PURPOSE: Initialize the service with database session.\n # @PRE: db must be a valid SQLAlchemy Session.\n # @POST: Service ready for provider operations.\n # @RELATION: [DEPENDS_ON] ->[EncryptionManager]\n def __init__(self, db: Session):\n self.db = db\n self.encryption = EncryptionManager()\n\n # endregion LLMProviderService_init\n\n # region get_all_providers [TYPE Function]\n # @PURPOSE: Returns all configured LLM providers.\n # @PRE: Database connection must be active.\n # @POST: Returns list of all LLMProvider records.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def get_all_providers(self) -> list[LLMProvider]:\n with belief_scope(\"get_all_providers\"):\n return self.db.query(LLMProvider).all()\n\n # endregion get_all_providers\n\n # region get_provider [TYPE Function]\n # @PURPOSE: Returns a single LLM provider by ID.\n # @PRE: provider_id must be a valid string.\n # @POST: Returns LLMProvider or None if not found.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def get_provider(self, provider_id: str) -> LLMProvider | None:\n with belief_scope(\"get_provider\"):\n return (\n self.db.query(LLMProvider).filter(LLMProvider.id == provider_id).first()\n )\n\n # endregion get_provider\n\n # region create_provider [TYPE Function]\n # @PURPOSE: Creates a new LLM provider with encrypted API key.\n # @PRE: config must contain valid provider configuration.\n # @POST: New provider created and persisted to database.\n # @RELATION: [DEPENDS_ON] ->[LLMProviderConfig]\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[encrypt]\n def create_provider(self, config: \"LLMProviderConfig\") -> LLMProvider:\n with belief_scope(\"create_provider\"):\n encrypted_key = self.encryption.encrypt(config.api_key)\n db_provider = LLMProvider(\n provider_type=config.provider_type.value,\n name=config.name,\n base_url=config.base_url,\n api_key=encrypted_key,\n default_model=config.default_model,\n is_active=config.is_active,\n )\n self.db.add(db_provider)\n self.db.commit()\n self.db.refresh(db_provider)\n return db_provider\n\n # endregion create_provider\n\n # region update_provider [TYPE Function]\n # @PURPOSE: Updates an existing LLM provider.\n # @PRE: provider_id must exist, config must be valid.\n # @POST: Provider updated and persisted to database.\n # @RELATION: [DEPENDS_ON] ->[LLMProviderConfig]\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[encrypt]\n def update_provider(\n self, provider_id: str, config: \"LLMProviderConfig\"\n ) -> LLMProvider | None:\n with belief_scope(\"update_provider\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n return None\n\n db_provider.provider_type = config.provider_type.value\n db_provider.name = config.name\n db_provider.base_url = config.base_url\n # Ignore masked placeholder values; they are display-only and must not overwrite secrets.\n if (\n config.api_key is not None\n and config.api_key != \"\"\n and config.api_key != MASKED_API_KEY_PLACEHOLDER\n ):\n db_provider.api_key = self.encryption.encrypt(config.api_key)\n db_provider.default_model = config.default_model\n db_provider.is_active = config.is_active\n\n self.db.commit()\n self.db.refresh(db_provider)\n return db_provider\n\n # endregion update_provider\n\n # region delete_provider [TYPE Function]\n # @PURPOSE: Deletes an LLM provider.\n # @PRE: provider_id must exist.\n # @POST: Provider removed from database.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def delete_provider(self, provider_id: str) -> bool:\n with belief_scope(\"delete_provider\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n return False\n self.db.delete(db_provider)\n self.db.commit()\n return True\n\n # endregion delete_provider\n\n # region get_decrypted_api_key [TYPE Function]\n # @PURPOSE: Returns the decrypted API key for a provider.\n # @PRE: provider_id must exist with valid encrypted key.\n # @POST: Returns decrypted API key or None on failure.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[decrypt]\n def get_decrypted_api_key(self, provider_id: str) -> str | None:\n with belief_scope(\"get_decrypted_api_key\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n logger.warning(\n f\"[get_decrypted_api_key] Provider {provider_id} not found in database\"\n )\n return None\n\n logger.info(f\"[get_decrypted_api_key] Provider found: {db_provider.id}\")\n logger.info(\n f\"[get_decrypted_api_key] Encrypted API key length: {len(db_provider.api_key) if db_provider.api_key else 0}\"\n )\n\n try:\n decrypted_key = self.encryption.decrypt(db_provider.api_key)\n logger.info(\n f\"[get_decrypted_api_key] Decryption successful, key length: {len(decrypted_key) if decrypted_key else 0}\"\n )\n return decrypted_key\n except Exception as e:\n logger.error(f\"[get_decrypted_api_key] Decryption failed: {e!s}\")\n return None\n\n # endregion get_decrypted_api_key\n\n\n# #endregion LLMProviderService\n\n# #endregion llm_provider\n" + "body": "# #region llm_provider [C:3] [TYPE Module] [SEMANTICS sqlalchemy, llm, provider, encryption, config]\n# @BRIEF Service for managing LLM provider configurations with encrypted API keys.\n# @LAYER: Domain\n# @RELATION DEPENDS_ON -> [LLMProvider]\n# @RELATION DEPENDS_ON -> [EncryptionManager]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\nimport os\nfrom typing import TYPE_CHECKING\n\nfrom cryptography.fernet import Fernet\nfrom cryptography.exceptions import InvalidTag\nfrom sqlalchemy.orm import Session\n\nfrom ..core.logger import belief_scope, logger\nfrom ..models.llm import LLMProvider\n\nif TYPE_CHECKING:\n from ..plugins.llm_analysis.models import LLMProviderConfig\n\nMASKED_API_KEY_PLACEHOLDER = \"********\"\n\n\n# #region mask_api_key [C:2] [TYPE Function]\n# @BRIEF Mask an API key for safe display, showing first 4 and last 4 characters.\n# @PRE: api_key is a plaintext string or None.\n# @POST: Returns \"****\" for very short keys; \"{first 2}...{last 2}\" for <=8 chars;\n# \"{first 4}...{last 4}\" for longer keys; \"\" for None/empty.\ndef mask_api_key(api_key: str | None) -> str:\n if not api_key:\n return \"\"\n if len(api_key) <= 4:\n return \"****\"\n if len(api_key) <= 8:\n return f\"{api_key[:2]}...{api_key[-2:]}\"\n return f\"{api_key[:4]}...{api_key[-4:]}\"\n\n\n# #endregion mask_api_key\n\n\n# #region is_masked_or_placeholder [C:2] [TYPE Function]\n# @BRIEF Predicate: True when api_key is None, empty, \"********\", or contains \"...\".\n# @PRE: api_key can be None.\n# @POST: Returns True only for non-real-key values.\ndef is_masked_or_placeholder(api_key: str | None) -> bool:\n if not api_key:\n return True\n return api_key == MASKED_API_KEY_PLACEHOLDER or \"...\" in api_key\n\n\n# #endregion is_masked_or_placeholder\n\n\n# #region _require_fernet_key [C:5] [TYPE Function]\n# @BRIEF Load and validate the Fernet key used for secret encryption.\n# @PRE: ENCRYPTION_KEY environment variable must be set to a valid Fernet key.\n# @POST: Returns validated key bytes ready for Fernet initialization.\n# @RELATION DEPENDS_ON -> [backend.src.core.logger:Function]\n# @SIDE_EFFECT: Emits belief-state logs for missing or invalid encryption configuration.\n# @DATA_CONTRACT: Input[ENCRYPTION_KEY:str] -> Output[bytes]\n# @INVARIANT: Encryption initialization never falls back to a hardcoded secret.\ndef _require_fernet_key() -> bytes:\n with belief_scope(\"_require_fernet_key\"):\n raw_key = os.getenv(\"ENCRYPTION_KEY\", \"\").strip()\n if not raw_key:\n logger.explore(\n \"Missing ENCRYPTION_KEY blocks EncryptionManager initialization\"\n )\n raise RuntimeError(\"ENCRYPTION_KEY must be set\")\n\n key = raw_key.encode()\n try:\n Fernet(key)\n except Exception as exc:\n logger.explore(\n \"Invalid ENCRYPTION_KEY blocks EncryptionManager initialization\"\n )\n raise RuntimeError(\"ENCRYPTION_KEY must be a valid Fernet key\") from exc\n\n logger.reflect(\"Validated ENCRYPTION_KEY for EncryptionManager initialization\")\n return key\n\n\n# #endregion _require_fernet_key\n\n\n# #region EncryptionManager [C:5] [TYPE Class]\n# @BRIEF Handles encryption and decryption of sensitive data like API keys.\n# @RELATION CALLS -> [_require_fernet_key]\n# @PRE: ENCRYPTION_KEY is configured with a valid Fernet key before instantiation.\n# @POST: Manager exposes reversible encrypt/decrypt operations for persisted secrets.\n# @SIDE_EFFECT: Initializes Fernet cryptography state from process environment.\n# @DATA_CONTRACT: Input[str] -> Output[str]\n# @INVARIANT: Uses only a validated secret key from environment.\n#\n# @TEST_CONTRACT: EncryptionManagerModel ->\n# {\n# required_fields: {},\n# invariants: [\n# \"encrypted data can be decrypted back to the original string\"\n# ]\n# }\n# @TEST_FIXTURE: basic_encryption_cycle -> {\"data\": \"my_secret_key\"}\n# @TEST_EDGE: decrypt_invalid_data -> raises Exception\n# @TEST_EDGE: empty_string_encryption -> {\"data\": \"\"}\n# @TEST_INVARIANT: symmetric_encryption -> verifies: [basic_encryption_cycle, empty_string_encryption]\nclass EncryptionManager:\n # region EncryptionManager_init [TYPE Function]\n # @PURPOSE: Initialize the encryption manager with a Fernet key.\n # @PRE: ENCRYPTION_KEY env var must be set to a valid Fernet key.\n # @POST: Fernet instance ready for encryption/decryption.\n def __init__(self):\n self.key = _require_fernet_key()\n self.fernet = Fernet(self.key)\n\n # endregion EncryptionManager_init\n\n # region encrypt [TYPE Function]\n # @PURPOSE: Encrypt a plaintext string.\n # @PRE: data must be a non-empty string.\n # @POST: Returns encrypted string.\n def encrypt(self, data: str) -> str:\n with belief_scope(\"encrypt\"):\n return self.fernet.encrypt(data.encode()).decode()\n\n # endregion encrypt\n\n # region decrypt [TYPE Function]\n # @PURPOSE: Decrypt an encrypted string.\n # @PRE: encrypted_data must be a valid Fernet-encrypted string.\n # @POST: Returns original plaintext string.\n def decrypt(self, encrypted_data: str) -> str:\n with belief_scope(\"decrypt\"):\n return self.fernet.decrypt(encrypted_data.encode()).decode()\n\n # endregion decrypt\n\n\n# #endregion EncryptionManager\n\n\n# #region LLMProviderService [C:3] [TYPE Class]\n# @BRIEF Service to manage LLM provider lifecycle.\n# @RELATION DEPENDS_ON -> [LLMProvider]\n# @RELATION DEPENDS_ON -> [EncryptionManager]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\nclass LLMProviderService:\n # region LLMProviderService_init [TYPE Function]\n # @PURPOSE: Initialize the service with database session.\n # @PRE: db must be a valid SQLAlchemy Session.\n # @POST: Service ready for provider operations.\n # @RELATION: [DEPENDS_ON] ->[EncryptionManager]\n def __init__(self, db: Session):\n self.db = db\n self.encryption = EncryptionManager()\n\n # endregion LLMProviderService_init\n\n # region get_all_providers [TYPE Function]\n # @PURPOSE: Returns all configured LLM providers.\n # @PRE: Database connection must be active.\n # @POST: Returns list of all LLMProvider records.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def get_all_providers(self) -> list[LLMProvider]:\n with belief_scope(\"get_all_providers\"):\n return self.db.query(LLMProvider).all()\n\n # endregion get_all_providers\n\n # region get_provider [TYPE Function]\n # @PURPOSE: Returns a single LLM provider by ID.\n # @PRE: provider_id must be a valid string.\n # @POST: Returns LLMProvider or None if not found.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def get_provider(self, provider_id: str) -> LLMProvider | None:\n with belief_scope(\"get_provider\"):\n return (\n self.db.query(LLMProvider).filter(LLMProvider.id == provider_id).first()\n )\n\n # endregion get_provider\n\n # region create_provider [TYPE Function]\n # @PURPOSE: Creates a new LLM provider with encrypted API key.\n # @PRE: config must contain valid provider configuration.\n # @POST: New provider created and persisted to database.\n # @RELATION: [DEPENDS_ON] ->[LLMProviderConfig]\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[encrypt]\n def create_provider(self, config: \"LLMProviderConfig\") -> LLMProvider:\n with belief_scope(\"create_provider\"):\n encrypted_key = self.encryption.encrypt(config.api_key)\n db_provider = LLMProvider(\n provider_type=config.provider_type.value,\n name=config.name,\n base_url=config.base_url,\n api_key=encrypted_key,\n default_model=config.default_model,\n is_active=config.is_active,\n )\n self.db.add(db_provider)\n self.db.commit()\n self.db.refresh(db_provider)\n return db_provider\n\n # endregion create_provider\n\n # region update_provider [TYPE Function]\n # @PURPOSE: Updates an existing LLM provider.\n # @PRE: provider_id must exist, config must be valid.\n # @POST: Provider updated and persisted to database.\n # @RELATION: [DEPENDS_ON] ->[LLMProviderConfig]\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[encrypt]\n def update_provider(\n self, provider_id: str, config: \"LLMProviderConfig\"\n ) -> LLMProvider | None:\n with belief_scope(\"update_provider\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n return None\n\n db_provider.provider_type = config.provider_type.value\n db_provider.name = config.name\n db_provider.base_url = config.base_url\n # Ignore masked/placeholder values; they are display-only and must not overwrite secrets.\n if not is_masked_or_placeholder(config.api_key):\n db_provider.api_key = self.encryption.encrypt(config.api_key)\n db_provider.default_model = config.default_model\n db_provider.is_active = config.is_active\n\n self.db.commit()\n self.db.refresh(db_provider)\n return db_provider\n\n # endregion update_provider\n\n # region delete_provider [TYPE Function]\n # @PURPOSE: Deletes an LLM provider.\n # @PRE: provider_id must exist.\n # @POST: Provider removed from database.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def delete_provider(self, provider_id: str) -> bool:\n with belief_scope(\"delete_provider\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n return False\n self.db.delete(db_provider)\n self.db.commit()\n return True\n\n # endregion delete_provider\n\n # region get_decrypted_api_key [TYPE Function]\n # @PURPOSE: Returns the decrypted API key for a provider.\n # @PRE: provider_id must exist with valid encrypted key.\n # @POST: Returns decrypted API key or None on failure.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[decrypt]\n def get_decrypted_api_key(self, provider_id: str) -> str | None:\n with belief_scope(\"get_decrypted_api_key\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n logger.warning(\n f\"[get_decrypted_api_key] Provider {provider_id} not found in database\"\n )\n return None\n\n logger.info(f\"[get_decrypted_api_key] Provider found: {db_provider.id}\")\n logger.info(\n f\"[get_decrypted_api_key] Encrypted API key length: {len(db_provider.api_key) if db_provider.api_key else 0}\"\n )\n\n try:\n decrypted_key = self.encryption.decrypt(db_provider.api_key)\n logger.info(\n f\"[get_decrypted_api_key] Decryption successful, key length: {len(decrypted_key) if decrypted_key else 0}\"\n )\n return decrypted_key\n except InvalidTag as e:\n logger.error(\n f\"[get_decrypted_api_key] Integrity check failed (InvalidTag): {e!s}. \"\n \"The encrypted API key may be corrupted or the ENCRYPTION_KEY has changed.\"\n )\n return None\n except ValueError as e:\n logger.error(\n f\"[get_decrypted_api_key] Decryption format error (ValueError): {e!s}. \"\n \"The encrypted data may not be valid Fernet ciphertext.\"\n )\n return None\n except Exception as e:\n logger.error(\n f\"[get_decrypted_api_key] Decryption failed with unexpected error \"\n f\"({type(e).__name__}): {e!s}\"\n )\n return None\n\n # endregion get_decrypted_api_key\n\n\n# #endregion LLMProviderService\n\n# #endregion llm_provider\n" + }, + { + "contract_id": "mask_api_key", + "contract_type": "Function", + "file_path": "backend/src/services/llm_provider.py", + "start_line": 23, + "end_line": 38, + "tier": "TIER_1", + "complexity": 2, + "metadata": { + "COMPLEXITY": 2, + "POST": "Returns \"****\" for very short keys; \"{first 2}...{last 2}\" for <=8 chars;", + "PRE": "api_key is a plaintext string or None.", + "PURPOSE": "Mask an API key for safe display, showing first 4 and last 4 characters." + }, + "relations": [], + "schema_warnings": [ + { + "code": "tag_forbidden_by_complexity", + "tag": "POST", + "message": "@POST is forbidden for contract type 'Function' at C2", + "detail": { + "actual_complexity": 2, + "contract_type": "Function" + } + }, + { + "code": "tag_forbidden_by_complexity", + "tag": "PRE", + "message": "@PRE is forbidden for contract type 'Function' at C2", + "detail": { + "actual_complexity": 2, + "contract_type": "Function" + } + } + ], + "anchor_syntax": "region", + "tier_source": "AutoCalculated", + "body": "# #region mask_api_key [C:2] [TYPE Function]\n# @BRIEF Mask an API key for safe display, showing first 4 and last 4 characters.\n# @PRE: api_key is a plaintext string or None.\n# @POST: Returns \"****\" for very short keys; \"{first 2}...{last 2}\" for <=8 chars;\n# \"{first 4}...{last 4}\" for longer keys; \"\" for None/empty.\ndef mask_api_key(api_key: str | None) -> str:\n if not api_key:\n return \"\"\n if len(api_key) <= 4:\n return \"****\"\n if len(api_key) <= 8:\n return f\"{api_key[:2]}...{api_key[-2:]}\"\n return f\"{api_key[:4]}...{api_key[-4:]}\"\n\n\n# #endregion mask_api_key\n" + }, + { + "contract_id": "is_masked_or_placeholder", + "contract_type": "Function", + "file_path": "backend/src/services/llm_provider.py", + "start_line": 41, + "end_line": 51, + "tier": "TIER_1", + "complexity": 2, + "metadata": { + "COMPLEXITY": 2, + "POST": "Returns True only for non-real-key values.", + "PRE": "api_key can be None.", + "PURPOSE": "Predicate: True when api_key is None, empty, \"********\", or contains \"...\"." + }, + "relations": [], + "schema_warnings": [ + { + "code": "tag_forbidden_by_complexity", + "tag": "POST", + "message": "@POST is forbidden for contract type 'Function' at C2", + "detail": { + "actual_complexity": 2, + "contract_type": "Function" + } + }, + { + "code": "tag_forbidden_by_complexity", + "tag": "PRE", + "message": "@PRE is forbidden for contract type 'Function' at C2", + "detail": { + "actual_complexity": 2, + "contract_type": "Function" + } + } + ], + "anchor_syntax": "region", + "tier_source": "AutoCalculated", + "body": "# #region is_masked_or_placeholder [C:2] [TYPE Function]\n# @BRIEF Predicate: True when api_key is None, empty, \"********\", or contains \"...\".\n# @PRE: api_key can be None.\n# @POST: Returns True only for non-real-key values.\ndef is_masked_or_placeholder(api_key: str | None) -> bool:\n if not api_key:\n return True\n return api_key == MASKED_API_KEY_PLACEHOLDER or \"...\" in api_key\n\n\n# #endregion is_masked_or_placeholder\n" }, { "contract_id": "_require_fernet_key", "contract_type": "Function", "file_path": "backend/src/services/llm_provider.py", - "start_line": 22, - "end_line": 52, + "start_line": 54, + "end_line": 84, "tier": "TIER_3", "complexity": 5, "metadata": { @@ -60262,8 +59616,8 @@ "contract_id": "EncryptionManager", "contract_type": "Class", "file_path": "backend/src/services/llm_provider.py", - "start_line": 55, - "end_line": 107, + "start_line": 87, + "end_line": 139, "tier": "TIER_3", "complexity": 5, "metadata": { @@ -60315,8 +59669,8 @@ "contract_id": "LLMProviderService", "contract_type": "Class", "file_path": "backend/src/services/llm_provider.py", - "start_line": 110, - "end_line": 259, + "start_line": 142, + "end_line": 302, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -60346,7 +59700,7 @@ "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "# #region LLMProviderService [C:3] [TYPE Class]\n# @BRIEF Service to manage LLM provider lifecycle.\n# @RELATION DEPENDS_ON -> [LLMProvider]\n# @RELATION DEPENDS_ON -> [EncryptionManager]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\nclass LLMProviderService:\n # region LLMProviderService_init [TYPE Function]\n # @PURPOSE: Initialize the service with database session.\n # @PRE: db must be a valid SQLAlchemy Session.\n # @POST: Service ready for provider operations.\n # @RELATION: [DEPENDS_ON] ->[EncryptionManager]\n def __init__(self, db: Session):\n self.db = db\n self.encryption = EncryptionManager()\n\n # endregion LLMProviderService_init\n\n # region get_all_providers [TYPE Function]\n # @PURPOSE: Returns all configured LLM providers.\n # @PRE: Database connection must be active.\n # @POST: Returns list of all LLMProvider records.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def get_all_providers(self) -> list[LLMProvider]:\n with belief_scope(\"get_all_providers\"):\n return self.db.query(LLMProvider).all()\n\n # endregion get_all_providers\n\n # region get_provider [TYPE Function]\n # @PURPOSE: Returns a single LLM provider by ID.\n # @PRE: provider_id must be a valid string.\n # @POST: Returns LLMProvider or None if not found.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def get_provider(self, provider_id: str) -> LLMProvider | None:\n with belief_scope(\"get_provider\"):\n return (\n self.db.query(LLMProvider).filter(LLMProvider.id == provider_id).first()\n )\n\n # endregion get_provider\n\n # region create_provider [TYPE Function]\n # @PURPOSE: Creates a new LLM provider with encrypted API key.\n # @PRE: config must contain valid provider configuration.\n # @POST: New provider created and persisted to database.\n # @RELATION: [DEPENDS_ON] ->[LLMProviderConfig]\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[encrypt]\n def create_provider(self, config: \"LLMProviderConfig\") -> LLMProvider:\n with belief_scope(\"create_provider\"):\n encrypted_key = self.encryption.encrypt(config.api_key)\n db_provider = LLMProvider(\n provider_type=config.provider_type.value,\n name=config.name,\n base_url=config.base_url,\n api_key=encrypted_key,\n default_model=config.default_model,\n is_active=config.is_active,\n )\n self.db.add(db_provider)\n self.db.commit()\n self.db.refresh(db_provider)\n return db_provider\n\n # endregion create_provider\n\n # region update_provider [TYPE Function]\n # @PURPOSE: Updates an existing LLM provider.\n # @PRE: provider_id must exist, config must be valid.\n # @POST: Provider updated and persisted to database.\n # @RELATION: [DEPENDS_ON] ->[LLMProviderConfig]\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[encrypt]\n def update_provider(\n self, provider_id: str, config: \"LLMProviderConfig\"\n ) -> LLMProvider | None:\n with belief_scope(\"update_provider\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n return None\n\n db_provider.provider_type = config.provider_type.value\n db_provider.name = config.name\n db_provider.base_url = config.base_url\n # Ignore masked placeholder values; they are display-only and must not overwrite secrets.\n if (\n config.api_key is not None\n and config.api_key != \"\"\n and config.api_key != MASKED_API_KEY_PLACEHOLDER\n ):\n db_provider.api_key = self.encryption.encrypt(config.api_key)\n db_provider.default_model = config.default_model\n db_provider.is_active = config.is_active\n\n self.db.commit()\n self.db.refresh(db_provider)\n return db_provider\n\n # endregion update_provider\n\n # region delete_provider [TYPE Function]\n # @PURPOSE: Deletes an LLM provider.\n # @PRE: provider_id must exist.\n # @POST: Provider removed from database.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def delete_provider(self, provider_id: str) -> bool:\n with belief_scope(\"delete_provider\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n return False\n self.db.delete(db_provider)\n self.db.commit()\n return True\n\n # endregion delete_provider\n\n # region get_decrypted_api_key [TYPE Function]\n # @PURPOSE: Returns the decrypted API key for a provider.\n # @PRE: provider_id must exist with valid encrypted key.\n # @POST: Returns decrypted API key or None on failure.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[decrypt]\n def get_decrypted_api_key(self, provider_id: str) -> str | None:\n with belief_scope(\"get_decrypted_api_key\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n logger.warning(\n f\"[get_decrypted_api_key] Provider {provider_id} not found in database\"\n )\n return None\n\n logger.info(f\"[get_decrypted_api_key] Provider found: {db_provider.id}\")\n logger.info(\n f\"[get_decrypted_api_key] Encrypted API key length: {len(db_provider.api_key) if db_provider.api_key else 0}\"\n )\n\n try:\n decrypted_key = self.encryption.decrypt(db_provider.api_key)\n logger.info(\n f\"[get_decrypted_api_key] Decryption successful, key length: {len(decrypted_key) if decrypted_key else 0}\"\n )\n return decrypted_key\n except Exception as e:\n logger.error(f\"[get_decrypted_api_key] Decryption failed: {e!s}\")\n return None\n\n # endregion get_decrypted_api_key\n\n\n# #endregion LLMProviderService\n" + "body": "# #region LLMProviderService [C:3] [TYPE Class]\n# @BRIEF Service to manage LLM provider lifecycle.\n# @RELATION DEPENDS_ON -> [LLMProvider]\n# @RELATION DEPENDS_ON -> [EncryptionManager]\n# @RELATION DEPENDS_ON -> [LLMProviderConfig]\nclass LLMProviderService:\n # region LLMProviderService_init [TYPE Function]\n # @PURPOSE: Initialize the service with database session.\n # @PRE: db must be a valid SQLAlchemy Session.\n # @POST: Service ready for provider operations.\n # @RELATION: [DEPENDS_ON] ->[EncryptionManager]\n def __init__(self, db: Session):\n self.db = db\n self.encryption = EncryptionManager()\n\n # endregion LLMProviderService_init\n\n # region get_all_providers [TYPE Function]\n # @PURPOSE: Returns all configured LLM providers.\n # @PRE: Database connection must be active.\n # @POST: Returns list of all LLMProvider records.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def get_all_providers(self) -> list[LLMProvider]:\n with belief_scope(\"get_all_providers\"):\n return self.db.query(LLMProvider).all()\n\n # endregion get_all_providers\n\n # region get_provider [TYPE Function]\n # @PURPOSE: Returns a single LLM provider by ID.\n # @PRE: provider_id must be a valid string.\n # @POST: Returns LLMProvider or None if not found.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def get_provider(self, provider_id: str) -> LLMProvider | None:\n with belief_scope(\"get_provider\"):\n return (\n self.db.query(LLMProvider).filter(LLMProvider.id == provider_id).first()\n )\n\n # endregion get_provider\n\n # region create_provider [TYPE Function]\n # @PURPOSE: Creates a new LLM provider with encrypted API key.\n # @PRE: config must contain valid provider configuration.\n # @POST: New provider created and persisted to database.\n # @RELATION: [DEPENDS_ON] ->[LLMProviderConfig]\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[encrypt]\n def create_provider(self, config: \"LLMProviderConfig\") -> LLMProvider:\n with belief_scope(\"create_provider\"):\n encrypted_key = self.encryption.encrypt(config.api_key)\n db_provider = LLMProvider(\n provider_type=config.provider_type.value,\n name=config.name,\n base_url=config.base_url,\n api_key=encrypted_key,\n default_model=config.default_model,\n is_active=config.is_active,\n )\n self.db.add(db_provider)\n self.db.commit()\n self.db.refresh(db_provider)\n return db_provider\n\n # endregion create_provider\n\n # region update_provider [TYPE Function]\n # @PURPOSE: Updates an existing LLM provider.\n # @PRE: provider_id must exist, config must be valid.\n # @POST: Provider updated and persisted to database.\n # @RELATION: [DEPENDS_ON] ->[LLMProviderConfig]\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[encrypt]\n def update_provider(\n self, provider_id: str, config: \"LLMProviderConfig\"\n ) -> LLMProvider | None:\n with belief_scope(\"update_provider\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n return None\n\n db_provider.provider_type = config.provider_type.value\n db_provider.name = config.name\n db_provider.base_url = config.base_url\n # Ignore masked/placeholder values; they are display-only and must not overwrite secrets.\n if not is_masked_or_placeholder(config.api_key):\n db_provider.api_key = self.encryption.encrypt(config.api_key)\n db_provider.default_model = config.default_model\n db_provider.is_active = config.is_active\n\n self.db.commit()\n self.db.refresh(db_provider)\n return db_provider\n\n # endregion update_provider\n\n # region delete_provider [TYPE Function]\n # @PURPOSE: Deletes an LLM provider.\n # @PRE: provider_id must exist.\n # @POST: Provider removed from database.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n def delete_provider(self, provider_id: str) -> bool:\n with belief_scope(\"delete_provider\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n return False\n self.db.delete(db_provider)\n self.db.commit()\n return True\n\n # endregion delete_provider\n\n # region get_decrypted_api_key [TYPE Function]\n # @PURPOSE: Returns the decrypted API key for a provider.\n # @PRE: provider_id must exist with valid encrypted key.\n # @POST: Returns decrypted API key or None on failure.\n # @RELATION: [DEPENDS_ON] ->[LLMProvider]\n # @RELATION: [CALLS] ->[decrypt]\n def get_decrypted_api_key(self, provider_id: str) -> str | None:\n with belief_scope(\"get_decrypted_api_key\"):\n db_provider = self.get_provider(provider_id)\n if not db_provider:\n logger.warning(\n f\"[get_decrypted_api_key] Provider {provider_id} not found in database\"\n )\n return None\n\n logger.info(f\"[get_decrypted_api_key] Provider found: {db_provider.id}\")\n logger.info(\n f\"[get_decrypted_api_key] Encrypted API key length: {len(db_provider.api_key) if db_provider.api_key else 0}\"\n )\n\n try:\n decrypted_key = self.encryption.decrypt(db_provider.api_key)\n logger.info(\n f\"[get_decrypted_api_key] Decryption successful, key length: {len(decrypted_key) if decrypted_key else 0}\"\n )\n return decrypted_key\n except InvalidTag as e:\n logger.error(\n f\"[get_decrypted_api_key] Integrity check failed (InvalidTag): {e!s}. \"\n \"The encrypted API key may be corrupted or the ENCRYPTION_KEY has changed.\"\n )\n return None\n except ValueError as e:\n logger.error(\n f\"[get_decrypted_api_key] Decryption format error (ValueError): {e!s}. \"\n \"The encrypted data may not be valid Fernet ciphertext.\"\n )\n return None\n except Exception as e:\n logger.error(\n f\"[get_decrypted_api_key] Decryption failed with unexpected error \"\n f\"({type(e).__name__}): {e!s}\"\n )\n return None\n\n # endregion get_decrypted_api_key\n\n\n# #endregion LLMProviderService\n" }, { "contract_id": "mapping_service", @@ -78986,7 +78340,7 @@ "contract_type": "Component", "file_path": "frontend/src/components/llm/ProviderConfig.svelte", "start_line": 1, - "end_line": 536, + "end_line": 588, "tier": "TIER_2", "complexity": 3, "metadata": { @@ -79029,7 +78383,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n\n\n
\n
\n

{$t.llm.providers_title}

\n {\n resetForm();\n showForm = true;\n }}\n >\n {$t.llm.add_provider}\n \n
\n\n {#if showForm}\n \n
\n

\n {editingProvider ? $t.llm.edit_provider : $t.llm.new_provider}\n

\n\n
\n
\n {$t.llm.name}\n \n
\n\n
\n {$t.llm.type}\n \n \n \n \n \n
\n\n
\n {$t.llm.base_url}\n
\n \n \n {isLoadingModels ? \"...\" : \"↻\"}\n \n
\n
\n\n
\n {$t.llm.api_key}\n \n
\n\n
\n {$t.llm.default_model}\n\n {#if isLoadingModels}\n
\n \n {$t.llm.loading_models}\n
\n {:else if modelsLoadError}\n
{modelsLoadError}
\n {/if}\n\n {#if availableModels.length > 0 && !isLoadingModels}\n \n {#each availableModels as model}\n \n {/each}\n \n
\n {($t.llm.models_loaded || \"{count} models loaded\").replace(\"{count}\", availableModels.length)}\n
\n {:else}\n \n {/if}\n
\n\n
\n \n {$t.llm.active}\n
\n
\n\n {#if testStatus.message}\n \n {testStatus.message}\n
\n {/if}\n\n
\n {\n showForm = false;\n }}\n >\n {$t.llm.cancel}\n \n \n {isTesting ? $t.llm.testing : $t.llm.test}\n \n \n {$t.llm.save}\n \n
\n
\n \n {/if}\n\n
\n {#each providers as provider}\n \n
\n
\n {provider.name}\n \n {provider.is_active ? $t.llm.active : \"Inactive\"}\n \n \n {isMultimodalModel(provider.default_model)\n ? ($t.llm?.multimodal )\n : ($t.llm?.text_only )}\n \n
\n
\n {provider.provider_type} • {provider.default_model}\n
\n
\n
\n handleEdit(provider)}\n >\n {$t.common.edit}\n \n handleDelete(provider)}\n disabled={deletingProviderIds.has(provider.id)}\n >\n {#if deletingProviderIds.has(provider.id)}\n ...\n {:else}\n {$t.common.delete}\n {/if}\n \n toggleActive(provider)}\n disabled={togglingProviderIds.has(provider.id) || deletingProviderIds.has(provider.id)}\n >\n {#if togglingProviderIds.has(provider.id)}\n ...\n {:else}\n {provider.is_active ? \"Deactivate\" : \"Activate\"}\n {/if}\n \n
\n
\n {:else}\n \n {$t.llm.no_providers}\n \n {/each}\n \n\n\n\n" + "body": "\n\n\n\n\n\n\n
\n
\n

{$t.llm.providers_title}

\n {\n resetForm();\n showForm = true;\n }}\n >\n {$t.llm.add_provider}\n \n
\n\n {#if showForm}\n \n
\n

\n {editingProvider ? $t.llm.edit_provider : $t.llm.new_provider}\n

\n\n
\n
\n {$t.llm.name}\n \n
\n\n
\n {$t.llm.type}\n updateBaseUrlForType(formData.provider_type)}\n class=\"mt-1 block w-full border rounded-md p-2\"\n >\n \n \n \n \n \n
\n\n
\n {$t.llm.base_url}\n
\n \n \n {isLoadingModels ? \"...\" : \"↻\"}\n \n
\n
\n\n
\n {$t.llm.api_key}\n {#if editingProvider && formData.api_key && isMaskedKey(formData.api_key)}\n
\n \n {formData.api_key}\n \n { formData.api_key = \"\"; }}\n >\n {$t.llm?.change_key || \"Change\"}\n \n
\n

\n {$t.llm?.key_masked_hint || \"Masked for security. Click \\\"Change\\\" to enter a new key.\"}\n

\n {:else}\n \n {/if}\n
\n\n
\n {$t.llm.default_model}\n\n {#if isLoadingModels}\n
\n \n {$t.llm.loading_models}\n
\n {:else if modelsLoadError}\n
{modelsLoadError}
\n {/if}\n\n {#if availableModels.length > 0 && !isLoadingModels}\n \n {#each availableModels as model}\n \n {/each}\n \n
\n {($t.llm.models_loaded || \"{count} models loaded\").replace(\"{count}\", availableModels.length)}\n
\n {:else}\n \n {/if}\n
\n\n
\n \n {$t.llm.active}\n
\n
\n\n {#if testStatus.message}\n \n {testStatus.message}\n
\n {/if}\n\n
\n {\n showForm = false;\n }}\n >\n {$t.llm.cancel}\n \n \n {isTesting ? $t.llm.testing : $t.llm.test}\n \n \n {$t.llm.save}\n \n
\n
\n \n {/if}\n\n
\n {#each providers as provider}\n \n
\n
\n {provider.name}\n \n {provider.is_active ? $t.llm.active : \"Inactive\"}\n \n \n {isMultimodalModel(provider.default_model)\n ? ($t.llm?.multimodal )\n : ($t.llm?.text_only )}\n \n
\n
\n {provider.provider_type} • {provider.default_model}\n
\n
\n
\n handleEdit(provider)}\n >\n {$t.common.edit}\n \n handleDelete(provider)}\n disabled={deletingProviderIds.has(provider.id)}\n >\n {#if deletingProviderIds.has(provider.id)}\n ...\n {:else}\n {$t.common.delete}\n {/if}\n \n toggleActive(provider)}\n disabled={togglingProviderIds.has(provider.id) || deletingProviderIds.has(provider.id)}\n >\n {#if togglingProviderIds.has(provider.id)}\n ...\n {:else}\n {provider.is_active ? \"Deactivate\" : \"Activate\"}\n {/if}\n \n
\n
\n {:else}\n \n {$t.llm.no_providers}\n \n {/each}\n \n\n\n\n" }, { "contract_id": "ValidationReport", @@ -79095,126 +78449,12 @@ "tier_source": "AutoCalculated", "body": "\n\n\n\n\n\n\n\n\n{#if result}\n
\n
\n

{$t.llm?.validation_report_title}

\n \n {result.status}\n \n
\n\n
\n

{$t.reports?.summary}

\n

{result.summary}

\n
\n\n {#if result.issues && result.issues.length > 0}\n
\n

{$t.tasks?.issues}

\n
\n \n \n \n \n \n \n \n \n \n {#each result.issues as issue}\n \n \n \n \n \n {/each}\n \n
{$t.reports?.severity}{$t.reports?.message}{$t.tasks?.location}
\n \n {issue.severity}\n \n {issue.message}{issue.location || $t.common?.not_available}
\n
\n
\n {/if}\n\n {#if result.screenshot_path}\n
\n

{$t.dashboard?.screenshot_strategy}

\n {$t.dashboard?.dashboard_validation}\n
\n {/if}\n
\n{:else}\n
\n

{$t.llm?.no_validation_result}

\n
\n{/if}\n\n\n" }, - { - "contract_id": "ProviderConfigIntegrationTest:Module", - "contract_type": "Function", - "file_path": "frontend/src/components/llm/__tests__/provider_config.integration.test.js", - "start_line": 1, - "end_line": 59, - "tier": "TIER_1", - "complexity": 1, - "metadata": { - "INVARIANT": "Edit action keeps explicit click handler and opens normalized edit form.", - "LAYER": "UI Tests", - "PURPOSE": "Protect edit and delete interaction contracts in LLM provider settings UI.", - "SEMANTICS": [ - "llm", - "provider-config", - "integration-test", - "edit-flow", - "delete-flow" - ] - }, - "relations": [ - { - "source_id": "ProviderConfigIntegrationTest:Module", - "relation_type": "DEPENDS_ON", - "target_id": "ProviderConfig", - "target_ref": "[ProviderConfig]" - } - ], - "schema_warnings": [ - { - "code": "tag_forbidden_by_complexity", - "tag": "INVARIANT", - "message": "@INVARIANT is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Function'", - "detail": { - "actual_type": "Function", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "invalid_enum_value", - "tag": "LAYER", - "message": "@LAYER value 'UI Tests' is not in allowed enum", - "detail": { - "allowed": [ - "Core", - "Domain", - "API", - "UI", - "Service", - "Infrastructure", - "Plugin" - ], - "value": "UI Tests" - } - }, - { - "code": "tag_not_for_contract_type", - "tag": "SEMANTICS", - "message": "@SEMANTICS is not allowed for contract type 'Function'", - "detail": { - "actual_type": "Function", - "allowed_types": [ - "Module", - "Block", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "SEMANTICS", - "message": "@SEMANTICS is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Function' at C1", - "detail": { - "actual_complexity": 1, - "contract_type": "Function" - } - } - ], - "anchor_syntax": "region", - "tier_source": "AutoCalculated", - "body": "// #region ProviderConfigIntegrationTest:Module [TYPE Function]\n// @SEMANTICS: llm, provider-config, integration-test, edit-flow, delete-flow\n// @PURPOSE: Protect edit and delete interaction contracts in LLM provider settings UI.\n// @LAYER: UI Tests\n// @RELATION: DEPENDS_ON -> [ProviderConfig]\n// @INVARIANT: Edit action keeps explicit click handler and opens normalized edit form.\n\nimport { describe, it, expect } from 'vitest';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst COMPONENT_PATH = path.resolve(\n process.cwd(),\n 'src/components/llm/ProviderConfig.svelte',\n);\n\n// #region provider_config_edit_contract_tests:Function [TYPE Function]\n// @RELATION: BINDS_TO -> [ProviderConfigIntegrationTest]\n// @PURPOSE: Validate edit and delete handler wiring plus normalized edit form state mapping.\n// @PRE: ProviderConfig component source exists in expected path.\n// @POST: Contract checks ensure edit click cannot degrade into no-op flow.\ndescribe('ProviderConfig edit interaction contract', () => {\n it('keeps explicit edit click handler with guarded button semantics', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('type=\"button\"');\n expect(source).toContain('onclick={() => handleEdit(provider)}');\n });\n\n it('normalizes provider payload into editable form shape', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('formData = {');\n expect(source).toContain('name: provider?.name ?? \"\"');\n expect(source).toContain('provider_type: provider?.provider_type ?? \"openai\"');\n expect(source).toContain('default_model: provider?.default_model ?? \"gpt-4o\"');\n expect(source).toContain('showForm = true;');\n });\n\n it('keeps explicit delete flow with confirmation and delete request', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('async function handleDelete(provider)');\n expect(source).toContain('$t.llm.delete_confirm.replace(\"{name}\", provider.name || provider.id)');\n expect(source).toContain('await requestApi(`/llm/providers/${provider.id}`, \"DELETE\")');\n expect(source).toContain('onclick={() => handleDelete(provider)}');\n });\n\n it('does not forward masked api_key when toggling provider activation', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('const updatePayload = {');\n expect(source).toContain('provider_type: provider.provider_type');\n expect(source).toContain('default_model: provider.default_model');\n expect(source).not.toContain('await requestApi(`/llm/providers/${provider.id}`, \"PUT\", {\\n ...provider,');\n });\n});\n// #endregion provider_config_edit_contract_tests:Function\n// #endregion ProviderConfigIntegrationTest:Module\n" - }, { "contract_id": "provider_config_edit_contract_tests:Function", "contract_type": "Function", "file_path": "frontend/src/components/llm/__tests__/provider_config.integration.test.js", "start_line": 17, - "end_line": 58, + "end_line": 92, "tier": "TIER_1", "complexity": 1, "metadata": { @@ -79261,7 +78501,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "// #region provider_config_edit_contract_tests:Function [TYPE Function]\n// @RELATION: BINDS_TO -> [ProviderConfigIntegrationTest]\n// @PURPOSE: Validate edit and delete handler wiring plus normalized edit form state mapping.\n// @PRE: ProviderConfig component source exists in expected path.\n// @POST: Contract checks ensure edit click cannot degrade into no-op flow.\ndescribe('ProviderConfig edit interaction contract', () => {\n it('keeps explicit edit click handler with guarded button semantics', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('type=\"button\"');\n expect(source).toContain('onclick={() => handleEdit(provider)}');\n });\n\n it('normalizes provider payload into editable form shape', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('formData = {');\n expect(source).toContain('name: provider?.name ?? \"\"');\n expect(source).toContain('provider_type: provider?.provider_type ?? \"openai\"');\n expect(source).toContain('default_model: provider?.default_model ?? \"gpt-4o\"');\n expect(source).toContain('showForm = true;');\n });\n\n it('keeps explicit delete flow with confirmation and delete request', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('async function handleDelete(provider)');\n expect(source).toContain('$t.llm.delete_confirm.replace(\"{name}\", provider.name || provider.id)');\n expect(source).toContain('await requestApi(`/llm/providers/${provider.id}`, \"DELETE\")');\n expect(source).toContain('onclick={() => handleDelete(provider)}');\n });\n\n it('does not forward masked api_key when toggling provider activation', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('const updatePayload = {');\n expect(source).toContain('provider_type: provider.provider_type');\n expect(source).toContain('default_model: provider.default_model');\n expect(source).not.toContain('await requestApi(`/llm/providers/${provider.id}`, \"PUT\", {\\n ...provider,');\n });\n});\n// #endregion provider_config_edit_contract_tests:Function\n" + "body": "// #region provider_config_edit_contract_tests:Function [TYPE Function]\n// @RELATION: BINDS_TO -> [ProviderConfigIntegrationTest]\n// @PURPOSE: Validate edit and delete handler wiring plus normalized edit form state mapping.\n// @PRE: ProviderConfig component source exists in expected path.\n// @POST: Contract checks ensure edit click cannot degrade into no-op flow.\ndescribe('ProviderConfig edit interaction contract', () => {\n it('keeps explicit edit click handler with guarded button semantics', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('type=\"button\"');\n expect(source).toContain('onclick={() => handleEdit(provider)}');\n });\n\n it('normalizes provider payload into editable form shape', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('formData = {');\n expect(source).toContain('name: provider?.name ?? \"\"');\n expect(source).toContain('provider_type: provider?.provider_type ?? \"openai\"');\n expect(source).toContain('default_model: provider?.default_model ?? \"gpt-4o\"');\n expect(source).toContain('showForm = true;');\n });\n\n it('preserves masked api_key from provider in edit form', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('api_key: provider?.api_key ?? \"\"');\n });\n\n it('includes isMaskedKey helper to detect masked placeholders', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('function isMaskedKey(key)');\n expect(source).toContain('key === \"********\" || key.includes(\"...\")');\n });\n\n it('excludes masked api_key from fetchModels payload, uses provider_id fallback', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain(\"if (formData.api_key && !isMaskedKey(formData.api_key))\");\n expect(source).toContain('payload.provider_id = editingProvider.id;');\n });\n\n it('keeps explicit delete flow with confirmation and delete request', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('async function handleDelete(provider)');\n expect(source).toContain('$t.llm.delete_confirm.replace(\"{name}\", provider.name || provider.id)');\n expect(source).toContain('await requestApi(`/llm/providers/${provider.id}`, \"DELETE\")');\n expect(source).toContain('onclick={() => handleDelete(provider)}');\n });\n\n it('excludes masked or empty api_key from submit data when editing', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain(\"if (editingProvider && (!submitData.api_key || isMaskedKey(submitData.api_key)))\");\n expect(source).toContain(\"delete submitData.api_key;\");\n });\n\n it('excludes masked api_key from test payload when testing connection', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain(\"if (isMaskedKey(testData.api_key))\");\n expect(source).toContain(\"delete testData.api_key;\");\n });\n\n it('does not forward api_key (masked or otherwise) when toggling provider activation', () => {\n const source = fs.readFileSync(COMPONENT_PATH, 'utf-8');\n\n expect(source).toContain('const updatePayload = {');\n expect(source).toContain('provider_type: provider.provider_type');\n expect(source).toContain('default_model: provider.default_model');\n expect(source).not.toContain('await requestApi(`/llm/providers/${provider.id}`, \"PUT\", {\\n ...provider,');\n });\n});\n// #endregion provider_config_edit_contract_tests:Function\n" }, { "contract_id": "isDirectory:Function", @@ -88130,74 +87370,46 @@ "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/BulkCorrectionSidebar.svelte", "start_line": 1, - "end_line": 293, + "end_line": 301, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", - "PURPOSE": "Sidebar for collecting multiple incorrect terms across different rows," + "PURPOSE": "Sidebar for collecting multiple incorrect terms across different rows,", + "UX_FEEDBACK": "Toast on success/error; count badge on toggle button", + "UX_RECOVERY": "Remove individual items; retry submission", + "UX_STATE": "submitted -> Success feedback" }, - "relations": [], - "schema_warnings": [ + "relations": [ { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } + "source_id": "BulkCorrectionSidebar", + "relation_type": "DEPENDS_ON", + "target_id": "TranslateApi", + "target_ref": "[TranslateApi]" }, { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - }, - { - "code": "missing_required_tag", - "tag": "RELATION", - "message": "@RELATION is required for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - }, - { - "code": "missing_required_tag", - "tag": "UX_STATE", - "message": "@UX_STATE is required for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } + "source_id": "BulkCorrectionSidebar", + "relation_type": "DEPENDS_ON", + "target_id": "dictionaryApi", + "target_ref": "[dictionaryApi]" } ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n\n\n \n \n \n {$t.translate?.corrections?.title}\n {#if items.length > 0}\n \n {items.length}\n \n {/if}\n\n\n\n{#if isOpen}\n
\n\n
\n \n
\n
\n

{$t.translate?.corrections?.title}

\n

{$t.translate?.corrections?.subtitle}

\n
\n \n
\n\n \n
\n {#if uxState === 'collecting'}\n {#if items.length === 0}\n
\n \n \n \n

{$t.translate?.corrections?.no_items}

\n

{$t.translate?.corrections?.add_hint}

\n
\n {:else}\n {#each items as item, idx}\n
\n
\n
\n

{$t.translate?.corrections?.source_term}

\n

{item.sourceTerm}

\n
\n removeItem(idx)}\n class=\"ml-2 p-0.5 text-gray-400 hover:text-red-500 rounded\"\n title={$t.translate?.corrections?.remove}\n >\n \n \n \n \n
\n
\n

{$t.translate?.corrections?.incorrect_target}

\n

{item.incorrectTarget}

\n
\n
\n \n updateCorrection(idx, e.target.value)}\n placeholder={$t.translate?.corrections?.corrected_placeholder}\n class=\"w-full mt-1 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-green-500 focus:border-green-500\"\n />\n
\n
\n {/each}\n {/if}\n\n {:else if uxState === 'submitting'}\n
\n
\n

{$t.translate?.corrections?.submitting}

\n
\n\n {:else if uxState === 'submitted'}\n
\n \n \n \n

{$t.translate?.corrections?.submit_success}

\n {#if submitResult}\n

\n {$t.translate?.corrections?.submit_partial\n ?.replace('{success}', submitResult.created_count || submitResult.updated_count || items.length)\n ?.replace('{total}', items.length)}\n

\n {/if}\n
\n {/if}\n
\n\n \n
\n {#if uxState === 'collecting' && items.length > 0}\n \n
\n \n \n \n {#each dictionaries as dict}\n \n {/each}\n \n
\n\n \n {$t.translate?.corrections?.submit_all?.replace('{count}', validItemCount)}\n \n {:else if uxState === 'submitted'}\n \n {$t.translate?.common?.close}\n \n {/if}\n
\n
\n{/if}\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n \n \n \n {$t.translate?.corrections?.title}\n {#if items.length > 0}\n \n {items.length}\n \n {/if}\n\n\n\n{#if isOpen}\n
\n\n
\n \n
\n
\n

{$t.translate?.corrections?.title}

\n

{$t.translate?.corrections?.subtitle}

\n
\n \n
\n\n \n
\n {#if uxState === 'collecting'}\n {#if items.length === 0}\n
\n \n \n \n

{$t.translate?.corrections?.no_items}

\n

{$t.translate?.corrections?.add_hint}

\n
\n {:else}\n {#each items as item, idx}\n
\n
\n
\n

{$t.translate?.corrections?.source_term}

\n

{item.sourceTerm}

\n
\n removeItem(idx)}\n class=\"ml-2 p-0.5 text-gray-400 hover:text-red-500 rounded\"\n title={$t.translate?.corrections?.remove}\n >\n \n \n \n \n
\n
\n

{$t.translate?.corrections?.incorrect_target}

\n

{item.incorrectTarget}

\n
\n
\n \n updateCorrection(idx, e.target.value)}\n placeholder={$t.translate?.corrections?.corrected_placeholder}\n class=\"w-full mt-1 px-2 py-1 text-sm border border-gray-300 rounded focus:ring-2 focus:ring-green-500 focus:border-green-500\"\n />\n
\n
\n {/each}\n {/if}\n\n {:else if uxState === 'submitting'}\n
\n
\n

{$t.translate?.corrections?.submitting}

\n
\n\n {:else if uxState === 'submitted'}\n
\n \n \n \n

{$t.translate?.corrections?.submit_success}

\n {#if submitResult}\n

\n {$t.translate?.corrections?.submit_partial\n ?.replace('{success}', submitResult.created_count || submitResult.updated_count || items.length)\n ?.replace('{total}', items.length)}\n

\n {/if}\n
\n {/if}\n
\n\n \n
\n {#if uxState === 'collecting' && items.length > 0}\n \n
\n \n \n \n {#each dictionaries as dict}\n \n {/each}\n \n
\n\n \n {$t.translate?.corrections?.submit_all?.replace('{count}', validItemCount)}\n \n {:else if uxState === 'submitted'}\n \n {$t.translate?.common?.close}\n \n {/if}\n
\n
\n{/if}\n\n" }, { "contract_id": "BulkReplaceModal", "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/BulkReplaceModal.svelte", "start_line": 1, - "end_line": 396, + "end_line": 379, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", "PURPOSE": "Modal dialog for bulk find-and-replace on translated values within a completed run.", - "TYPE": "{'closed'|'configuring'|'previewing'|'preview_error'|'confirming'|'applying'|'applied'|'apply_error'}", "UX_STATE": "apply_error -> Apply failed with error message" }, "relations": [ @@ -88208,61 +87420,30 @@ "target_ref": "[TranslateApi]" } ], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - }, - { - "code": "unknown_tag", - "tag": "TYPE", - "message": "@TYPE is not defined in axiom_config.yaml tags", - "detail": null - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n\n\n{#if show}\n\t\n\t\t e.stopPropagation()}\n\t\t\trole=\"dialog\"\n\t\t\taria-modal=\"true\"\n\t\t\taria-label={$t.translate?.bulk_replace?.title}\n\t\t>\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t.translate?.bulk_replace?.title}

\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'configuring' || uxState === 'previewing' || uxState === 'preview_error'}\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t e.key === 'Enter' && handlePreview()}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t e.key === 'Enter' && handlePreview()}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{$t.translate?.bulk_replace?.preview_affected}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'preview_error'}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{errorMessage}

\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'previewing' && affectedRecords.length > 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.preview_count.replace('{count}', affectedRecords.length)}\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.apply_changes}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#each affectedRecords.slice(0, 100) as row, i}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
{$t.translate?.bulk_replace?.table_number}{$t.translate?.bulk_replace?.table_before}{$t.translate?.bulk_replace?.table_after}
{i + 1}{row.final_value || row.translated_value || ''}{previewAfter(row)}
\n\t\t\t\t\t\t\t{#if affectedRecords.length > 100}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.showing_first.replace('{shown}', 100).replace('{total}', affectedRecords.length)}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t{#if uxState === 'previewing' && affectedRecords.length === 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.bulk_replace?.no_matching}

\n\t\t\t\t\t\t

{$t.translate?.bulk_replace?.no_matching_hint}

\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'confirming'}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.confirm_title.replace('{count}', applyCount)}\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.confirm_body.replace('{language}', targetLanguage)}\n\t\t\t\t\t\t\t\t

\n\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#if submitToDict}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.cancel}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.apply_count.replace('{count}', applyCount)}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'applying'}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.bulk_replace?.applying}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'applied'}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t

{$t.translate?.bulk_replace?.applied_title}

\n\t\t\t\t\t\t

{$t.translate?.bulk_replace?.applied_body.replace('{count}', applyCount)}

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.done}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'apply_error'}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{errorMessage}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.retry}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.cancel}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.bulk_replace?.close}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t\n{/if}\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n\n\tlet uxState = $state('closed');\n\n\tlet findPattern = $state('');\n\tlet replacementText = $state('');\n\tlet isRegex = $state(false);\n\tlet targetLanguage = $state('');\n\tlet affectedRecords = $state([]);\n\tlet applyCount = $state(0);\n\tlet errorMessage = $state('');\n\tlet submitToDict = $state(false);\n\tlet usageNotes = $state('');\n\n\t$effect(() => {\n\t\tif (show) {\n\t\t\tuxState = 'configuring';\n\t\t\tfindPattern = '';\n\t\t\treplacementText = '';\n\t\t\tisRegex = false;\n\t\t\ttargetLanguage = '';\n\t\t\taffectedRecords = [];\n\t\t\tapplyCount = 0;\n\t\t\terrorMessage = '';\n\t\t\tsubmitToDict = false;\n\t\t\tusageNotes = '';\n\t\t} else {\n\t\t\tuxState = 'closed';\n\t\t}\n\t});\n\n\t/** Preview affected records */\n\tasync function handlePreview() {\n\t\tif (!findPattern.trim()) {\n\t\t\taddToast(_('translate.bulk_replace.find_pattern_required'), 'warning');\n\t\t\treturn;\n\t\t}\n\t\tif (!targetLanguage) {\n\t\t\taddToast(_('translate.bulk_replace.target_language_required'), 'warning');\n\t\t\treturn;\n\t\t}\n\t\tuxState = 'previewing';\n\t\terrorMessage = '';\n\t\ttry {\n\t\t\tconst result = await bulkReplacePreview(runId, {\n\t\t\t\tpattern: findPattern,\n\t\t\t\tis_regex: isRegex,\n\t\t\t\treplacement_text: replacementText,\n\t\t\t\ttarget_language: targetLanguage\n\t\t\t});\n\t\t\taffectedRecords = result?.items || result?.records || [];\n\t\t\tapplyCount = affectedRecords.length;\n\t\t\tuxState = 'previewing';\n\t\t} catch (err) {\n\t\t\terrorMessage = err?.message || _('translate.bulk_replace.preview_failed');\n\t\t\tuxState = 'preview_error';\n\t\t\taddToast(errorMessage, 'error');\n\t\t}\n\t}\n\n\t/** Open confirmation */\n\tfunction handleShowConfirm() {\n\t\tuxState = 'confirming';\n\t}\n\n\t/** Cancel confirmation, back to preview */\n\tfunction handleCancelConfirm() {\n\t\tuxState = 'previewing';\n\t}\n\n\t/** Apply the bulk replacement */\n\tasync function handleApply() {\n\t\tuxState = 'applying';\n\t\ttry {\n\t\t\tconst result = await bulkFindReplace(runId, {\n\t\t\t\tpattern: findPattern,\n\t\t\t\tis_regex: isRegex,\n\t\t\t\treplacement_text: replacementText,\n\t\t\t\ttarget_language: targetLanguage,\n\t\t\t\tsubmit_to_dictionary: submitToDict,\n\t\t\t\tusage_notes: usageNotes || undefined,\n\t\t\t\tsubmit_to_dictionary_with_context: submitToDict\n\t\t\t});\n\t\t\tconst changed = result?.changed || result?.affected || applyCount;\n\t\t\tuxState = 'applied';\n\t\t\tonApplied(changed);\n\t\t\taddToast(_('translate.bulk_replace.applied_toast').replace('{count}', changed), 'success');\n\t\t} catch (err) {\n\t\t\terrorMessage = err?.message || _('translate.bulk_replace.apply_failed');\n\t\t\tuxState = 'apply_error';\n\t\t\taddToast(errorMessage, 'error');\n\t\t}\n\t}\n\n\tfunction handleClose() {\n\t\tonClose();\n\t}\n\n\tfunction previewAfter(row) {\n\t\tif (!replacementText && !isRegex) return '';\n\t\tif (isRegex) {\n\t\t\ttry {\n\t\t\t\tconst re = new RegExp(findPattern, 'g');\n\t\t\t\treturn (row.final_value || row.translated_value || '').replace(re, replacementText);\n\t\t\t} catch {\n\t\t\t\treturn row.final_value || row.translated_value || '';\n\t\t\t}\n\t\t}\n\t\treturn (row.final_value || row.translated_value || '').split(findPattern).join(replacementText);\n\t}\n\n\n{#if show}\n\t\n\t\t e.stopPropagation()}\n\t\t\trole=\"dialog\"\n\t\t\taria-modal=\"true\"\n\t\t\taria-label={$t.translate?.bulk_replace?.title}\n\t\t>\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t.translate?.bulk_replace?.title}

\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'configuring' || uxState === 'previewing' || uxState === 'preview_error'}\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t e.key === 'Enter' && handlePreview()}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t e.key === 'Enter' && handlePreview()}\n\t\t\t\t\t\t/>\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{$t.translate?.bulk_replace?.preview_affected}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'preview_error'}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{errorMessage}

\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'previewing' && affectedRecords.length > 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.preview_count.replace('{count}', affectedRecords.length)}\n\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.apply_changes}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#each affectedRecords.slice(0, 100) as row, i}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
{$t.translate?.bulk_replace?.table_number}{$t.translate?.bulk_replace?.table_before}{$t.translate?.bulk_replace?.table_after}
{i + 1}{row.final_value || row.translated_value || ''}{previewAfter(row)}
\n\t\t\t\t\t\t\t{#if affectedRecords.length > 100}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.showing_first.replace('{shown}', 100).replace('{total}', affectedRecords.length)}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t{#if uxState === 'previewing' && affectedRecords.length === 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.bulk_replace?.no_matching}

\n\t\t\t\t\t\t

{$t.translate?.bulk_replace?.no_matching_hint}

\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'confirming'}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.confirm_title.replace('{count}', applyCount)}\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t

\n\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.confirm_body.replace('{language}', targetLanguage)}\n\t\t\t\t\t\t\t\t

\n\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#if submitToDict}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.cancel}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.apply_count.replace('{count}', applyCount)}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'applying'}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.bulk_replace?.applying}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'applied'}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t

{$t.translate?.bulk_replace?.applied_title}

\n\t\t\t\t\t\t

{$t.translate?.bulk_replace?.applied_body.replace('{count}', applyCount)}

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.done}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if uxState === 'apply_error'}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{errorMessage}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.retry}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t.translate?.bulk_replace?.cancel}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.bulk_replace?.close}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t\n{/if}\n\n" }, { "contract_id": "CorrectionCell", "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/CorrectionCell.svelte", "start_line": 1, - "end_line": 326, + "end_line": 307, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", "PURPOSE": "Inline-editable cell for translation run results with edit-save-cancel UX and submit-to-dictionary flow.", - "TYPE": "{'view'|'editing'|'saving'|'saved'|'submitting_to_dict'|'dict_submitted'|'error'}", "UX_STATE": "error -> Error state with retry option" }, "relations": [ { "source_id": "CorrectionCell", "relation_type": "DEPENDS_ON", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" + "target_id": "dictionaryApi", + "target_ref": "[dictionaryApi]" }, { "source_id": "CorrectionCell", @@ -88271,51 +87452,21 @@ "target_ref": "[dictionaryApi]" } ], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - }, - { - "code": "unknown_tag", - "tag": "TYPE", - "message": "@TYPE is not defined in axiom_config.yaml tags", - "detail": null - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n\n\n\n\n{#if uxState === 'view'}\n\t e.key === 'Enter' && startEditing()}\n\t>\n\t\t\t{#if displayValue}\n\t\t\t{displayValue}\n\t\t{:else}\n\t\t\t\n\t\t{/if}\n\t\n\n\n{:else if uxState === 'editing'}\n\t
\n\t\t {\n\t\t\t\tif (e.key === 'Enter') saveEdit();\n\t\t\t\tif (e.key === 'Escape') cancelEditing();\n\t\t\t}}\n\t\t/>\n\t\t
\n\t\t\t\n\t\t\t\t✓\n\t\t\t\n\t\t\t\n\t\t\t\t✕\n\t\t\t\n\t\t
\n\t
\n\n\n{:else if uxState === 'saving'}\n\t
\n\t\t
\n\t\t{$t.translate?.corrections?.cell_saving}\n\t
\n\n\n{:else if uxState === 'saved'}\n\t
\n\t\t{displayValue}\n\t\t📝\n\t\t\n\t\t\t📕 {$t.translate?.corrections?.cell_submit_to_dict}\n\t\t\n\t
\n\n\n{:else if uxState === 'submitting_to_dict'}\n\t
\n\t\t
\n\t\t{$t.translate?.corrections?.cell_submitting}\n\t
\n\n\n{:else if uxState === 'dict_submitted'}\n\t
\n\t\t{displayValue}\n\t\t✅ {$t.translate?.corrections?.cell_submitted_badge}\n\t
\n\n\n{:else if uxState === 'error'}\n\t
\n\t\t{displayValue}\n\t\t{errorMessage}\n\t\t\n\t\t\t{$t.translate?.corrections?.cell_retry}\n\t\t\n\t
\n{/if}\n\n\n{#if dictPopupOpen}\n\t\n\t\t
e.stopPropagation()}>\n\t\t\t

{$t.translate?.corrections?.cell_submit_to_dict}

\n\t\t\t
\n\t\t\t\t{#if sourceLanguage}\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.corrections?.cell_language_pair} {sourceLanguage} → {languageCode}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
{editValue}
\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{#each dictionaries as dict}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t{#if editableContextData || contextEditMode}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t.translate?.corrections?.cell_context_data}\n\t\t\t\t\t\t\t { contextEditMode = !contextEditMode; if (!contextEditMode) editableContextData = contextData ? JSON.parse(JSON.stringify(contextData)) : null; }}\n\t\t\t\t\t\t\t\tclass=\"text-xs text-blue-600 hover:text-blue-700\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{contextEditMode ? $t.translate?.corrections?.cell_cancel_edit : $t.translate?.corrections?.cell_edit}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t{#if contextEditMode}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{:else if typeof editableContextData === 'object' && editableContextData !== null}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#each Object.entries(editableContextData) as [key, val]}\n\t\t\t\t\t\t\t\t\t
{key}: {String(val).substring(0, 100)}
\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t

{$t.translate?.corrections?.cell_no_context_data}

\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t{$t.translate?.corrections?.cell_cancel}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{$t.translate?.corrections?.cell_submit}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t
\n{/if}\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n\n\n\tlet uxState = $state('view');\n\tlet editValue = $state(value);\n\tlet displayValue = $state(value);\n\tlet errorMessage = $state('');\n\tlet dictPopupOpen = $state(false);\n\tlet dictionaries = $state([]);\n\tlet selectedDictId = $state('');\n\tlet editableContextData = $state(null);\n\tlet editableUsageNotes = $state('');\n\tlet contextEditMode = $state(false);\n\n\t$effect(() => {\n\t\t// Reset if value prop changes externally\n\t\tif (value !== displayValue) {\n\t\t\teditValue = value;\n\t\t\tdisplayValue = value;\n\t\t\tuxState = 'view';\n\t\t}\n\t});\n\n\tfunction startEditing() {\n\t\teditValue = value;\n\t\tuxState = 'editing';\n\t}\n\n\tfunction cancelEditing() {\n\t\teditValue = value;\n\t\tuxState = 'view';\n\t}\n\n\t/** @returns {Promise} */\n\tasync function saveEdit() {\n\t\tconst newValue = editValue.trim();\n\t\tif (!newValue || newValue === displayValue) {\n\t\t\tuxState = 'view';\n\t\t\treturn;\n\t\t}\n\t\tuxState = 'saving';\n\t\terrorMessage = '';\n\t\ttry {\n\t\t\tawait inlineEditCorrection(runId, recordId, languageCode, {\n\t\t\t\tfinal_value: newValue,\n\t\t\t\tsubmit_to_dictionary: false\n\t\t\t});\n\t\t\tdisplayValue = newValue;\n\t\t\tuxState = 'saved';\n\t\t\tonSave(newValue);\n\t\t\taddToast(_('translate.corrections.cell_save_success'), 'success');\n\t\t} catch (err) {\n\t\t\terrorMessage = err?.message || _('translate.corrections.cell_save_failed');\n\t\t\tuxState = 'error';\n\t\t\taddToast(errorMessage, 'error');\n\t\t}\n\t}\n\n\t/** @returns {Promise} */\n\tasync function openDictPopup() {\n\t\tdictPopupOpen = true;\n\t\teditableContextData = contextData ? JSON.parse(JSON.stringify(contextData)) : null;\n\t\teditableUsageNotes = usageNotes || '';\n\t\tcontextEditMode = false;\n\t\ttry {\n\t\t\tconst result = await dictionaryApi.fetchDictionaries({ page_size: 100 });\n\t\t\tdictionaries = Array.isArray(result) ? result : (result?.items || []);\n\t\t\tif (dictionaries.length === 1) {\n\t\t\t\tselectedDictId = dictionaries[0].id;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\taddToast(_('translate.corrections.cell_dict_load_failed'), 'error');\n\t\t}\n\t}\n\n\t/** @returns {Promise} */\n\tasync function handleDictSubmit() {\n\t\tif (!selectedDictId) {\n\t\t\taddToast(_('translate.corrections.cell_select_dict_warning'), 'warning');\n\t\t\treturn;\n\t\t}\n\t\tuxState = 'submitting_to_dict';\n\t\ttry {\n\t\t\t// First save the edit to dictionary\n\t\t\tawait inlineEditCorrection(runId, recordId, languageCode, {\n\t\t\t\tfinal_value: editValue,\n\t\t\t\tsubmit_to_dictionary: true,\n\t\t\t\tdictionary_id: selectedDictId\n\t\t\t});\n\t\t\tuxState = 'dict_submitted';\n\t\t\taddToast(_('translate.corrections.cell_dict_submit_success'), 'success');\n\t\t\tdictPopupOpen = false;\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || _('translate.corrections.cell_dict_submit_failed'), 'error');\n\t\t\tuxState = 'saved';\n\t\t}\n\t}\n\n\tfunction cancelDictSubmit() {\n\t\tdictPopupOpen = false;\n\t\tuxState = 'saved';\n\t}\n\n\tfunction handleCloseDictPopup() {\n\t\tdictPopupOpen = false;\n\t}\n\n\n\n{#if uxState === 'view'}\n\t e.key === 'Enter' && startEditing()}\n\t>\n\t\t\t{#if displayValue}\n\t\t\t{displayValue}\n\t\t{:else}\n\t\t\t\n\t\t{/if}\n\t\n\n\n{:else if uxState === 'editing'}\n\t
\n\t\t {\n\t\t\t\tif (e.key === 'Enter') saveEdit();\n\t\t\t\tif (e.key === 'Escape') cancelEditing();\n\t\t\t}}\n\t\t/>\n\t\t
\n\t\t\t\n\t\t\t\t✓\n\t\t\t\n\t\t\t\n\t\t\t\t✕\n\t\t\t\n\t\t
\n\t
\n\n\n{:else if uxState === 'saving'}\n\t
\n\t\t
\n\t\t{$t.translate?.corrections?.cell_saving}\n\t
\n\n\n{:else if uxState === 'saved'}\n\t
\n\t\t{displayValue}\n\t\t📝\n\t\t\n\t\t\t📕 {$t.translate?.corrections?.cell_submit_to_dict}\n\t\t\n\t
\n\n\n{:else if uxState === 'submitting_to_dict'}\n\t
\n\t\t
\n\t\t{$t.translate?.corrections?.cell_submitting}\n\t
\n\n\n{:else if uxState === 'dict_submitted'}\n\t
\n\t\t{displayValue}\n\t\t✅ {$t.translate?.corrections?.cell_submitted_badge}\n\t
\n\n\n{:else if uxState === 'error'}\n\t
\n\t\t{displayValue}\n\t\t{errorMessage}\n\t\t\n\t\t\t{$t.translate?.corrections?.cell_retry}\n\t\t\n\t
\n{/if}\n\n\n{#if dictPopupOpen}\n\t\n\t\t
e.stopPropagation()}>\n\t\t\t

{$t.translate?.corrections?.cell_submit_to_dict}

\n\t\t\t
\n\t\t\t\t{#if sourceLanguage}\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.corrections?.cell_language_pair} {sourceLanguage} → {languageCode}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
{editValue}
\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{#each dictionaries as dict}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t{#if editableContextData || contextEditMode}\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t.translate?.corrections?.cell_context_data}\n\t\t\t\t\t\t\t { contextEditMode = !contextEditMode; if (!contextEditMode) editableContextData = contextData ? JSON.parse(JSON.stringify(contextData)) : null; }}\n\t\t\t\t\t\t\t\tclass=\"text-xs text-blue-600 hover:text-blue-700\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{contextEditMode ? $t.translate?.corrections?.cell_cancel_edit : $t.translate?.corrections?.cell_edit}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t{#if contextEditMode}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{:else if typeof editableContextData === 'object' && editableContextData !== null}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#each Object.entries(editableContextData) as [key, val]}\n\t\t\t\t\t\t\t\t\t
{key}: {String(val).substring(0, 100)}
\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t

{$t.translate?.corrections?.cell_no_context_data}

\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t{$t.translate?.corrections?.cell_cancel}\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t{$t.translate?.corrections?.cell_submit}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t
\n{/if}\n\n" }, { "contract_id": "ScheduleConfig", "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/ScheduleConfig.svelte", "start_line": 1, - "end_line": 289, + "end_line": 273, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", "PURPOSE": "Schedule configuration for a translation job — cron/interval/once triggers, timezone, enable/disable.", "UX_FEEDBACK": "Next-3-executions preview; toast on save/error", "UX_RECOVERY": "Enable/disable toggle; delete schedule", @@ -88329,45 +87480,21 @@ "target_ref": "[TranslateApi]" } ], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n
\n\t
\n\t\t

{$t.translate?.schedule?.title || 'Schedule Configuration'}

\n\t\t{#if !isLoading && scheduleExists}\n\t\t\t\n\t\t\t\t\n\t\t\t\t{isEnabled ? ($t.translate?.schedule?.enabled || 'Enabled') : ($t.translate?.schedule?.disabled || 'Disabled')}\n\t\t\t\n\t\t{/if}\n\t
\n\n\t{#if isLoading}\n\t\t
\n\n\t{:else if uxState === 'idle'}\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t

{$t.translate?.schedule?.no_schedule || 'No schedule configured'}

\n\t\t\t

{$t.translate?.schedule?.no_schedule_hint || 'Set up a recurring schedule for automatic translations'}

\n\t\t\t\n\t\t
\n\n\t{:else if uxState === 'editing' || uxState === 'enabled' || uxState === 'disabled'}\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t

\n\t\t\t\t\t{getCronDescription(cronExpression)}\n\t\t\t\t

\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t{#each commonTimezones as tz}\n\t\t\t\t\t\t\n\t\t\t\t\t{/each}\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if nextExecutions.length > 0}\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.schedule?.next_executions || 'Next Executions'}

\n\t\t\t\t\t{#each nextExecutions as dt, i}\n\t\t\t\t\t\t

{i + 1}. {new Date(dt).toLocaleString()}

\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t\n\t\t\t{#if uxState === 'no_prior_run_warning'}\n\t\t\t\t
\n\t\t\t\t\t

\n\t\t\t\t\t\t⚠ {$t.translate?.schedule?.no_prior_run_warning || 'No prior successful manual run. Schedule may run with default settings. Consider running manually first.'}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t\n\t\t\t
\n\t\t\t\t{#if scheduleExists}\n\t\t\t\t\t\n\t\t\t\t{:else}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t\t\t{#if scheduleExists && (uxState === 'enabled' || uxState === 'disabled')}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t\t{#if uxState === 'editing'}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t{/if}\n
\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n\n\tlet uxState = $state('idle');\n\tlet cronExpression = $state('0 2 * * *');\n\tlet timezone = $state('UTC');\n\tlet executionMode = $state('full');\n\tlet isEnabled = $state(false);\n\tlet isLoading = $state(true);\n\tlet nextExecutions = $state([]);\n\tlet hasPriorRun = $state(true);\n\tlet scheduleExists = $state(false);\n\tlet isEditing = $derived(uxState === 'editing');\n\n\t$effect(() => {\n\t\tif (jobId) loadSchedule();\n\t});\n\n\tasync function loadSchedule() {\n\t\tisLoading = true;\n\t\ttry {\n\t\t\tconst sch = await fetchSchedule(jobId);\n\t\t\tif (sch && sch.cron_expression) {\n\t\t\t\tcronExpression = sch.cron_expression;\n\t\t\t\ttimezone = sch.timezone || 'UTC';\n\t\t\t\texecutionMode = sch.execution_mode || 'full';\n\t\t\t\tisEnabled = sch.is_active !== false;\n\t\t\t\tscheduleExists = true;\n\t\t\t\tuxState = isEnabled ? 'enabled' : 'disabled';\n\t\t\t\tloadNextExecutions();\n\t\t\t} else {\n\t\t\t\tuxState = 'idle';\n\t\t\t}\n\t\t} catch (err) {\n\t\t\t// 404 = no schedule\n\t\t\tuxState = 'idle';\n\t\t} finally {\n\t\t\tisLoading = false;\n\t\t}\n\t}\n\n\tasync function loadNextExecutions() {\n\t\ttry {\n\t\t\tconst result = await fetchNextExecutions(jobId, 3);\n\t\t\tnextExecutions = result?.next_executions || [];\n\t\t} catch (err) {\n\t\t\tnextExecutions = [];\n\t\t}\n\t}\n\n\tasync function handleSave() {\n\t\ttry {\n\t\t\tconst payload = {\n\t\t\t\tcron_expression: cronExpression,\n\t\t\t\ttimezone: timezone,\n\t\t\t\tis_active: isEnabled,\n\t\t\t\texecution_mode: executionMode,\n\t\t\t};\n\t\t\tawait setSchedule(jobId, payload);\n\t\t\taddToast($t.translate?.schedule?.saved || 'Schedule saved', 'success');\n\t\t\tscheduleExists = true;\n\t\t\tuxState = isEnabled ? 'enabled' : 'disabled';\n\t\t\tloadNextExecutions();\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || ($t.translate?.schedule?.save_failed || 'Failed to save schedule'), 'error');\n\t\t}\n\t}\n\n\tasync function handleDelete() {\n\t\ttry {\n\t\t\tawait deleteSchedule(jobId);\n\t\t\taddToast($t.translate?.schedule?.deleted || 'Schedule deleted', 'success');\n\t\t\tscheduleExists = false;\n\t\t\tuxState = 'idle';\n\t\t\tcronExpression = '0 2 * * *';\n\t\t\tnextExecutions = [];\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || ($t.translate?.schedule?.delete_failed || 'Failed to delete schedule'), 'error');\n\t\t}\n\t}\n\n\tasync function handleToggle() {\n\t\ttry {\n\t\t\tif (isEnabled) {\n\t\t\t\tawait disableSchedule(jobId);\n\t\t\t\tisEnabled = false;\n\t\t\t\tuxState = 'disabled';\n\t\t\t\taddToast($t.translate?.schedule?.disabled_toast || 'Schedule disabled', 'info');\n\t\t\t} else {\n\t\t\t\tif (!scheduleExists) {\n\t\t\t\t\tawait handleSave();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tawait enableSchedule(jobId);\n\t\t\t\tisEnabled = true;\n\t\t\t\tuxState = 'enabled';\n\t\t\t\taddToast($t.translate?.schedule?.enabled_toast || 'Schedule enabled', 'success');\n\t\t\t\tloadNextExecutions();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || ($t.translate?.schedule?.toggle_failed || 'Failed to toggle schedule'), 'error');\n\t\t}\n\t}\n\n\tfunction getCronDescription(cron) {\n\t\tconst parts = cron.split(' ');\n\t\tif (parts.length !== 5) return cron;\n\t\tif (parts[1] === '*' && parts[2] === '*' && parts[4] === '*') {\n\t\t\tconst hour = parts[0] === '*' ? '' : parts[0];\n\t\t\tconst min = parts[1];\n\t\t\tif (parts[0] === '*') {\n\t\t\t\treturn ($t.translate?.schedule?.description_every_hour || 'at :{minute} past the hour').replace('{minute}', min);\n\t\t\t}\n\t\t\treturn ($t.translate?.schedule?.description_daily || 'Daily at {hour}:{minute}').replace('{hour}', hour).replace('{minute}', min);\n\t\t}\n\t\treturn ($t.translate?.schedule?.description_cron || 'Cron: {cron}').replace('{cron}', cron);\n\t}\n\n\tconst commonTimezones = [\n\t\t'UTC', 'US/Eastern', 'US/Central', 'US/Mountain', 'US/Pacific',\n\t\t'Europe/London', 'Europe/Berlin', 'Europe/Moscow', 'Europe/Paris',\n\t\t'Asia/Tokyo', 'Asia/Shanghai', 'Asia/Kolkata', 'Australia/Sydney',\n\t];\n\n\n
\n\t
\n\t\t

{$t.translate?.schedule?.title || 'Schedule Configuration'}

\n\t\t{#if !isLoading && scheduleExists}\n\t\t\t\n\t\t\t\t\n\t\t\t\t{isEnabled ? ($t.translate?.schedule?.enabled || 'Enabled') : ($t.translate?.schedule?.disabled || 'Disabled')}\n\t\t\t\n\t\t{/if}\n\t
\n\n\t{#if isLoading}\n\t\t
\n\n\t{:else if uxState === 'idle'}\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t

{$t.translate?.schedule?.no_schedule || 'No schedule configured'}

\n\t\t\t

{$t.translate?.schedule?.no_schedule_hint || 'Set up a recurring schedule for automatic translations'}

\n\t\t\t\n\t\t
\n\n\t{:else if uxState === 'editing' || uxState === 'enabled' || uxState === 'disabled'}\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t

\n\t\t\t\t\t{getCronDescription(cronExpression)}\n\t\t\t\t

\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t{#each commonTimezones as tz}\n\t\t\t\t\t\t\n\t\t\t\t\t{/each}\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if nextExecutions.length > 0}\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.schedule?.next_executions || 'Next Executions'}

\n\t\t\t\t\t{#each nextExecutions as dt, i}\n\t\t\t\t\t\t

{i + 1}. {new Date(dt).toLocaleString()}

\n\t\t\t\t\t{/each}\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t\n\t\t\t{#if uxState === 'no_prior_run_warning'}\n\t\t\t\t
\n\t\t\t\t\t

\n\t\t\t\t\t\t⚠ {$t.translate?.schedule?.no_prior_run_warning || 'No prior successful manual run. Schedule may run with default settings. Consider running manually first.'}\n\t\t\t\t\t

\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t\n\t\t\t
\n\t\t\t\t{#if scheduleExists}\n\t\t\t\t\t\n\t\t\t\t{:else}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t\t\t{#if scheduleExists && (uxState === 'enabled' || uxState === 'disabled')}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t\t{#if uxState === 'editing'}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t{/if}\n
\n\n" }, { "contract_id": "TermCorrectionPopup", "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/TermCorrectionPopup.svelte", "start_line": 1, - "end_line": 349, + "end_line": 323, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", "PURPOSE": "Popup for submitting a term correction from a run result row.", "UX_FEEDBACK": "Toast on success/error; conflict dialog with action buttons", "UX_RECOVERY": "Cancel button returns to previous state", @@ -88376,9 +87503,9 @@ "relations": [ { "source_id": "TermCorrectionPopup", - "relation_type": "DEPENDS_ON", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" + "relation_type": "BINDS_TO", + "target_id": "dictionaryApi", + "target_ref": "[dictionaryApi]" }, { "source_id": "TermCorrectionPopup", @@ -88387,105 +87514,49 @@ "target_ref": "[dictionaryApi]" } ], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n{#if uxState !== 'closed'}\n\t
\n\t\t
e.stopPropagation()}>\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t.translate?.term_correction?.title}

\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if uxState === 'selecting'}\n\t\t\t\t

{$t.translate?.term_correction?.loading_dictionaries}

\n\n\t\t\t\n\t\t\t{:else if uxState === 'editing'}\n\t\t\t\t
\n\t\t\t\t\t{#if sourceLanguage || targetLanguage}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t.translate?.term_correction?.language_pair} {sourceLanguage || $t.translate?.term_correction?.detected} → {targetLanguage || languageCode}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t { showContextEditor = !showContextEditor; }}\n\t\t\t\t\t\t\tclass=\"w-full flex items-center justify-between px-3 py-2 text-xs font-medium text-gray-600 bg-gray-50 hover:bg-gray-100 transition-colors\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t📋\n\t\t\t\t\t\t\t\t{$t.translate?.term_correction?.context_from_source}\n\t\t\t\t\t\t\t\t{#if contextData}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{hasEditedContext ? '✏️ ' + $t.translate?.term_correction?.edited_badge : '🤖 ' + $t.translate?.term_correction?.auto_badge}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{#if showContextEditor}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#if contextData}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{JSON.stringify(contextData, null, 2)}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\tcontextEditorText = JSON.stringify(contextData, null, 2);\n\t\t\t\t\t\t\t\t\t\t\t\tshowContextEditor = true;\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"text-[11px] px-2 py-1 rounded border border-gray-300 text-gray-600 hover:bg-gray-50\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.term_correction?.edit_context}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\tcontextData = null;\n\t\t\t\t\t\t\t\t\t\t\t\thasEditedContext = true;\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"text-[11px] px-2 py-1 rounded border border-red-200 text-red-600 hover:bg-red-50\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.term_correction?.remove_context}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t

{$t.translate?.term_correction?.no_context}

\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tcontextData = JSON.parse(JSON.stringify(initialContextData));\n\t\t\t\t\t\t\t\t\t\t\tcontextEditorText = JSON.stringify(initialContextData, null, 2);\n\t\t\t\t\t\t\t\t\t\t\thasEditedContext = false;\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\tclass=\"text-[11px] px-2 py-1 rounded border border-gray-300 text-gray-600 hover:bg-gray-50\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{$t.translate?.term_correction?.restore_context}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each dictionaries as dict}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t.translate?.term_correction?.submit_correction}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\n\t\t\t{:else if uxState === 'submitting'}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.term_correction?.submitting}

\n\t\t\t\t
\n\n\t\t\t\n\t\t\t{:else if uxState === 'conflict_detected'}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.term_correction?.conflict_detected}

\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{$t.translate?.term_correction?.conflict_description.replace('{source}', conflictInfo?.source_term || '').replace('{existing}', conflictInfo?.existing_target_term || '')}\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\n\t\t\t{:else if uxState === 'submitted'}\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t

{$t.translate?.term_correction?.submitted}

\n\t\t\t\t\t

{submitResult?.message || ''}

\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t{/if}\n\t\t
\n\t
\n{/if}\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\tlet uxState = $state('closed');\n\tlet correctedTarget = $state('');\n\tlet dictionaries = $state([]);\n\tlet selectedDictId = $state('');\n\tlet conflictInfo = $state(null);\n\tlet submitResult = $state(null);\n\n\t// Context state\n\tlet contextData = $state(null);\n\tlet contextUsageNotes = $state('');\n\tlet hasEditedContext = $state(false);\n\tlet showContextEditor = $state(false);\n\tlet contextEditorText = $state('');\n\n\t$effect(() => {\n\t\tif (initialContextData) {\n\t\t\tcontextData = JSON.parse(JSON.stringify(initialContextData));\n\t\t\tcontextEditorText = JSON.stringify(initialContextData, null, 2);\n\t\t}\n\t\tif (initialUsageNotes) {\n\t\t\tcontextUsageNotes = initialUsageNotes;\n\t\t}\n\t});\n\n\t$effect(() => {\n\t\tif (sourceTerm && incorrectTarget) {\n\t\t\tuxState = 'selecting';\n\t\t\tloadDictionaries();\n\t\t}\n\t});\n\n\tasync function loadDictionaries() {\n\t\ttry {\n\t\t\tconst result = await dictionaryApi.fetchDictionaries({ page_size: 100 });\n\t\t\tdictionaries = Array.isArray(result) ? result : (result?.items || []);\n\t\t\tif (dictionaries.length === 1) {\n\t\t\t\tselectedDictId = dictionaries[0].id;\n\t\t\t}\n\t\t\tuxState = dictionaries.length > 0 ? 'editing' : 'selecting';\n\t\t} catch (err) {\n\t\t\taddToast(_('translate.term_correction.dict_load_failed'), 'error');\n\t\t\tuxState = 'editing';\n\t\t}\n\t}\n\n\tasync function handleSubmit() {\n\t\tif (!selectedDictId || !correctedTarget.trim()) return;\n\t\tuxState = 'submitting';\n\t\ttry {\n\t\t\tconst payload = {\n\t\t\t\tsource_term: sourceTerm,\n\t\t\t\tincorrect_target_term: incorrectTarget,\n\t\t\t\tcorrected_target_term: correctedTarget.trim(),\n\t\t\t\tdictionary_id: selectedDictId,\n\t\t\t\torigin_run_id: runId || undefined,\n\t\t\t\torigin_row_key: rowKey || undefined,\n\t\t\t};\n\t\t\tif (contextData) {\n\t\t\t\tpayload.context_data = contextData;\n\t\t\t}\n\t\t\tif (contextUsageNotes) {\n\t\t\t\tpayload.usage_notes = contextUsageNotes;\n\t\t\t}\n\t\t\tconst result = await submitCorrection(payload);\n\t\t\tif (result.action === 'conflict_detected') {\n\t\t\t\tconflictInfo = result.conflict;\n\t\t\t\tuxState = 'conflict_detected';\n\t\t\t} else if (result.action === 'created' || result.action === 'updated') {\n\t\t\t\tsubmitResult = result;\n\t\t\t\tuxState = 'submitted';\n\t\t\t\taddToast(result.message || _('translate.term_correction.submit_success'), 'success');\n\t\t\t\tonSubmitted(result);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || _('translate.term_correction.submit_failed'), 'error');\n\t\t\tuxState = 'editing';\n\t\t}\n\t}\n\n\tasync function handleConflictOverwrite() {\n\t\ttry {\n\t\t\tconst result = await submitCorrection({\n\t\t\t\tsource_term: sourceTerm,\n\t\t\t\tincorrect_target_term: incorrectTarget,\n\t\t\t\tcorrected_target_term: correctedTarget.trim(),\n\t\t\t\tdictionary_id: selectedDictId,\n\t\t\t\torigin_run_id: runId || undefined,\n\t\t\t\torigin_row_key: rowKey || undefined,\n\t\t\t});\n\t\t\tsubmitResult = result;\n\t\t\tuxState = 'submitted';\n\t\t\taddToast(result.message || _('translate.term_correction.overwrite_success'), 'success');\n\t\t\tonSubmitted(result);\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || _('translate.term_correction.overwrite_failed'), 'error');\n\t\t\tuxState = 'conflict_detected';\n\t\t}\n\t}\n\n\tfunction handleConflictKeep() {\n\t\tuxState = 'submitted';\n\t\taddToast(_('translate.term_correction.keep_success'), 'info');\n\t\tonSubmitted({ action: 'kept' });\n\t}\n\n\tfunction handleClose() {\n\t\tuxState = 'closed';\n\t\tonClose();\n\t}\n\n\n{#if uxState !== 'closed'}\n\t
\n\t\t
e.stopPropagation()}>\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t.translate?.term_correction?.title}

\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if uxState === 'selecting'}\n\t\t\t\t

{$t.translate?.term_correction?.loading_dictionaries}

\n\n\t\t\t\n\t\t\t{:else if uxState === 'editing'}\n\t\t\t\t
\n\t\t\t\t\t{#if sourceLanguage || targetLanguage}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t.translate?.term_correction?.language_pair} {sourceLanguage || $t.translate?.term_correction?.detected} → {targetLanguage || languageCode}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t { showContextEditor = !showContextEditor; }}\n\t\t\t\t\t\t\tclass=\"w-full flex items-center justify-between px-3 py-2 text-xs font-medium text-gray-600 bg-gray-50 hover:bg-gray-100 transition-colors\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t📋\n\t\t\t\t\t\t\t\t{$t.translate?.term_correction?.context_from_source}\n\t\t\t\t\t\t\t\t{#if contextData}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{hasEditedContext ? '✏️ ' + $t.translate?.term_correction?.edited_badge : '🤖 ' + $t.translate?.term_correction?.auto_badge}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{#if showContextEditor}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#if contextData}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{JSON.stringify(contextData, null, 2)}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\tcontextEditorText = JSON.stringify(contextData, null, 2);\n\t\t\t\t\t\t\t\t\t\t\t\tshowContextEditor = true;\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"text-[11px] px-2 py-1 rounded border border-gray-300 text-gray-600 hover:bg-gray-50\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.term_correction?.edit_context}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\tcontextData = null;\n\t\t\t\t\t\t\t\t\t\t\t\thasEditedContext = true;\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"text-[11px] px-2 py-1 rounded border border-red-200 text-red-600 hover:bg-red-50\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.term_correction?.remove_context}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t

{$t.translate?.term_correction?.no_context}

\n\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\tcontextData = JSON.parse(JSON.stringify(initialContextData));\n\t\t\t\t\t\t\t\t\t\t\tcontextEditorText = JSON.stringify(initialContextData, null, 2);\n\t\t\t\t\t\t\t\t\t\t\thasEditedContext = false;\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\tclass=\"text-[11px] px-2 py-1 rounded border border-gray-300 text-gray-600 hover:bg-gray-50\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{$t.translate?.term_correction?.restore_context}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each dictionaries as dict}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t.translate?.term_correction?.submit_correction}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\n\t\t\t{:else if uxState === 'submitting'}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.term_correction?.submitting}

\n\t\t\t\t
\n\n\t\t\t\n\t\t\t{:else if uxState === 'conflict_detected'}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.term_correction?.conflict_detected}

\n\t\t\t\t\t\t

\n\t\t\t\t\t\t\t{$t.translate?.term_correction?.conflict_description.replace('{source}', conflictInfo?.source_term || '').replace('{existing}', conflictInfo?.existing_target_term || '')}\n\t\t\t\t\t\t

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\n\t\t\t{:else if uxState === 'submitted'}\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t

{$t.translate?.term_correction?.submitted}

\n\t\t\t\t\t

{submitResult?.message || ''}

\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t{/if}\n\t\t
\n\t
\n{/if}\n\n" }, { "contract_id": "TranslationMetricsDashboard", "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/TranslationMetricsDashboard.svelte", "start_line": 1, - "end_line": 192, + "end_line": 198, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", - "PURPOSE": "Admin metrics dashboard for translation — per-job run counts, success/failure ratio," + "PURPOSE": "Admin metrics dashboard for translation — per-job run counts, success/failure ratio,", + "UX_FEEDBACK": "Spinner during load; card grid for totals; table for per-job breakdown", + "UX_RECOVERY": "Retry button on error", + "UX_STATE": "empty -> No metrics data available" }, - "relations": [], - "schema_warnings": [ + "relations": [ { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - }, - { - "code": "missing_required_tag", - "tag": "RELATION", - "message": "@RELATION is required for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - }, - { - "code": "missing_required_tag", - "tag": "UX_STATE", - "message": "@UX_STATE is required for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } + "source_id": "TranslationMetricsDashboard", + "relation_type": "DEPENDS_ON", + "target_id": "TranslateApi", + "target_ref": "[TranslateApi]" } ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n
\n \n
\n
\n

{$t.translate?.metrics?.title}

\n

{$t.translate?.metrics?.subtitle}

\n
\n \n \n \n \n {$t.translate?.metrics?.refresh}\n \n
\n\n \n {#if uxState === 'loading'}\n
\n {#each Array(3) as _}\n
\n {/each}\n
\n\n \n {:else if uxState === 'error'}\n
\n

{errorMessage}

\n \n {$t.translate?.common?.retry}\n \n
\n\n \n {:else if uxState === 'empty'}\n
\n \n \n \n

{$t.translate?.metrics?.no_data}

\n
\n\n \n {:else if uxState === 'loaded'}\n \n
\n
\n

{$t.translate?.metrics?.total_runs}

\n

{totalRuns}

\n
\n
\n

{$t.translate?.metrics?.success_rate}

\n

{successRate}%

\n
\n
\n

{$t.translate?.metrics?.failed_runs}

\n

{failedRuns}

\n
\n
\n

{$t.translate?.metrics?.total_records}

\n

{totalRecords.toLocaleString()}

\n
\n
\n

{$t.translate?.metrics?.total_tokens}

\n

{totalTokens.toLocaleString()}

\n
\n
\n

{$t.translate?.metrics?.total_cost}

\n

${totalCost.toFixed(2)}

\n
\n
\n\n \n
\n
\n

{$t.translate?.metrics?.runs_per_job}

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n {#each metrics as m}\n \n \n \n \n \n \n \n \n {/each}\n \n
{$t.translate?.metrics?.job}{$t.translate?.metrics?.runs}{$t.translate?.metrics?.successful}{$t.translate?.metrics?.failed}{$t.translate?.metrics?.tokens}{$t.translate?.metrics?.cost}
{m.job_name || m.job_id}{m.total_runs || 0}{m.successful_runs || 0}{m.failed_runs || 0}{(m.total_tokens || 0).toLocaleString()}${(m.total_cost || 0).toFixed(2)}
\n
\n
\n\n \n {#if avgLatency !== null}\n
\n

{$t.translate?.metrics?.avg_latency}

\n

{avgLatency}s

\n
\n {/if}\n {/if}\n
\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n
\n \n
\n
\n

{$t.translate?.metrics?.title}

\n

{$t.translate?.metrics?.subtitle}

\n
\n \n \n \n \n {$t.translate?.metrics?.refresh}\n \n
\n\n \n {#if uxState === 'loading'}\n
\n {#each Array(3) as _}\n
\n {/each}\n
\n\n \n {:else if uxState === 'error'}\n
\n

{errorMessage}

\n \n {$t.translate?.common?.retry}\n \n
\n\n \n {:else if uxState === 'empty'}\n
\n \n \n \n

{$t.translate?.metrics?.no_data}

\n
\n\n \n {:else if uxState === 'loaded'}\n \n
\n
\n

{$t.translate?.metrics?.total_runs}

\n

{totalRuns}

\n
\n
\n

{$t.translate?.metrics?.success_rate}

\n

{successRate}%

\n
\n
\n

{$t.translate?.metrics?.failed_runs}

\n

{failedRuns}

\n
\n
\n

{$t.translate?.metrics?.total_records}

\n

{totalRecords.toLocaleString()}

\n
\n
\n

{$t.translate?.metrics?.total_tokens}

\n

{totalTokens.toLocaleString()}

\n
\n
\n

{$t.translate?.metrics?.total_cost}

\n

${totalCost.toFixed(2)}

\n
\n
\n\n \n
\n
\n

{$t.translate?.metrics?.runs_per_job}

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n {#each metrics as m}\n \n \n \n \n \n \n \n \n {/each}\n \n
{$t.translate?.metrics?.job}{$t.translate?.metrics?.runs}{$t.translate?.metrics?.successful}{$t.translate?.metrics?.failed}{$t.translate?.metrics?.tokens}{$t.translate?.metrics?.cost}
{m.job_name || m.job_id}{m.total_runs || 0}{m.successful_runs || 0}{m.failed_runs || 0}{(m.total_tokens || 0).toLocaleString()}${(m.total_cost || 0).toFixed(2)}
\n
\n
\n\n \n {#if avgLatency !== null}\n
\n

{$t.translate?.metrics?.avg_latency}

\n

{avgLatency}s

\n
\n {/if}\n {/if}\n
\n\n" }, { "contract_id": "TranslationPreview", "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/TranslationPreview.svelte", "start_line": 1, - "end_line": 531, + "end_line": 549, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", "POST": "User can preview, approve/edit/reject per-language rows, and accept the session to gate full execution.", "PRE": "jobId is a valid translation job ID.", "PURPOSE": "Multi-language translation preview with dynamic per-language columns, approve/edit/reject, cost estimate, and accept gate.", @@ -88497,8 +87568,8 @@ { "source_id": "TranslationPreview", "relation_type": "DEPENDS_ON", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" + "target_id": "api_module", + "target_ref": "[api_module]" }, { "source_id": "TranslationPreview", @@ -88508,28 +87579,6 @@ } ], "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - }, { "code": "tag_forbidden_by_complexity", "tag": "POST", @@ -88551,22 +87600,21 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n
\n\t\n\t
\n\t\t

{$t.translate?.preview?.title}

\n\t\t{#if uxState === 'preview_loaded'}\n\t\t\t\n\t\t\t\t{session?.status === 'APPLIED' ? $t.translate?.preview?.accepted_badge : $t.translate?.preview?.active_badge}\n\t\t\t\n\t\t{/if}\n\t
\n\n\t\n\t{#if uxState === 'idle'}\n\t\t
\n\t\t\t

\n\t\t\t\t{$t.translate?.preview?.title} — {$t.translate?.preview?.sample_size} {sampleSize}\n\t\t\t

\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t{sampleSize}\n\t\t\t
\n\t\t\t{#if sampleSize > 30}\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{($t.translate?.preview?.cost_warning) || 'Large sample size may incur higher costs'}\n\t\t\t\t
\n\t\t\t{/if}\n\t\t\t\n\t\t\t\t{$t.translate?.preview?.run_preview}\n\t\t\t\n\t\t
\n\n\t\n\t{:else if uxState === 'loading'}\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t{$t.translate?.preview?.running_preview}\n\t\t\t
\n\t\t\t
\n\t\t\t\t{#each Array(3) as _}\n\t\t\t\t\t
\n\t\t\t\t{/each}\n\t\t\t
\n\t\t
\n\n\t\n\t{:else if uxState === 'preview_error'}\n\t\t
\n\t\t\t

{errorMessage}

\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.preview?.retry_preview}\n\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\n\t\n\t{:else if uxState === 'preview_loaded'}\n\t\t\n\t\t{#if costEstimate}\n\t\t\t
\n\t\t\t\t

{$t.translate?.preview?.cost_estimate}

\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.preview?.sample_rows}\n\t\t\t\t\t\t{costEstimate.sample_size}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.preview?.sample_tokens}\n\t\t\t\t\t\t{costEstimate.sample_total_tokens?.toLocaleString()}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.preview?.sample_cost}\n\t\t\t\t\t\t${costEstimate.sample_cost?.toFixed(6)}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.preview?.est_total_cost}\n\t\t\t\t\t\t${costEstimate.estimated_cost?.toFixed(6)}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t

\n\t\t\t\t\t{$t.translate?.preview?.across_languages?.replace('{count}', costEstimate.num_languages || targetLanguages.length || 1)}\n\t\t\t\t

\n\t\t\t\t\n\t\t\t\t{#if costEstimate.warning}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{costEstimate.warning}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\t\t{/if}\n\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t{$t.translate?.preview?.rows_count?.replace('{count}', records.length)}\n\t\t\t\t{$t.translate?.preview?.approved_count?.replace('{count}', approvedCount)}\n\t\t\t\t{$t.translate?.preview?.rejected_count?.replace('{count}', rejectedCount)}\n\t\t\t\t{#if pendingCount > 0}\n\t\t\t\t\t{$t.translate?.preview?.pending_count?.replace('{count}', pendingCount)}\n\t\t\t\t{/if}\n\t\t\t\t{#if targetLanguages.length > 0}\n\t\t\t\t\t{$t.translate?.preview?.languages_count?.replace('{count}', targetLanguages.length)}\n\t\t\t\t{/if}\n\t\t\t
\n\t\t\t
\n\t\t\t\t{#if pendingCount > 0}\n\t\t\t\t\t\n\t\t\t\t\t\t{$t.translate?.preview?.approve_all}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.preview?.re_run}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t{#each records as row, idx}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\t{@const langKey = `${row.id}_${lang}`}\n\t\t\t\t\t\t\t\t{@const langVal = getLangValue(row, lang)}\n\t\t\t\t\t\t\t\t{@const langStatus = getLangStatus(row, lang)}\n\t\t\t\t\t\t\t\t{@const isEditing = editingCells[langKey]}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t{/each}\n\t\t\t\t\n\t\t\t
#{$t.translate?.preview?.table_source}{$t.translate?.preview?.detected_language}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{lang}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
{$t.translate?.preview?.table_status}
{idx + 1}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{row.source_sql || $t.translate?.preview?.empty_placeholder}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{getDetectedLang(row)}\n\t\t\t\t\t\t\t\t{#if getDetectedLang(row) === 'und'}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#if isEditing}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t saveLangEdit(row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-0.5 text-xs bg-blue-600 text-white rounded hover:bg-blue-700\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.save_edit}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t cancelLangEdit(row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-0.5 text-xs border border-gray-300 text-gray-600 rounded hover:bg-gray-50\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.cancel_edit}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{langVal || $t.translate?.preview?.pending_placeholder}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{#if langStatus !== 'approved' && langStatus !== 'edited'}\n\t\t\t\t\t\t\t\t\t\t\t\t approveRow(jobId, row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-green-100 text-green-700 rounded hover:bg-green-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.approve}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.approve}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t startLangEdit(row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-blue-100 text-blue-700 rounded hover:bg-blue-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.edit}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.edit}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{#if langStatus !== 'rejected'}\n\t\t\t\t\t\t\t\t\t\t\t\t rejectRow(jobId, row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-red-100 text-red-700 rounded hover:bg-red-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.reject}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.reject}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{row.status}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{#if row.status !== 'APPROVED'}\n\t\t\t\t\t\t\t\t\t\t\t handleApprove(row.id)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-green-100 text-green-700 rounded hover:bg-green-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.approve_all}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.approve}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{#if row.status !== 'REJECTED'}\n\t\t\t\t\t\t\t\t\t\t\t handleReject(row.id)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-red-100 text-red-700 rounded hover:bg-red-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.reject}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.reject}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.preview?.quality_gate}

\n\t\t\t\t

\n\t\t\t\t\t{isAllApproved ? $t.translate?.preview?.quality_gate_ready : $t.translate?.preview?.quality_gate_pending}\n\t\t\t\t

\n\t\t\t
\n\t\t\t\n\t\t\t\t{isAccepting ? $t.translate?.preview?.accepting : $t.translate?.preview?.accept_preview}\n\t\t\t\n\t\t
\n\n\t\n\t{:else if uxState === 'accepted'}\n\t\t
\n\t\t\t
\n\t\t\t

{$t.translate?.preview?.accepted_title}

\n\t\t\t

\n\t\t\t\t{$t.translate?.preview?.accepted_body}\n\t\t\t

\n\t\t\t\n\t\t\t\t{$t.translate?.preview?.run_preview_again}\n\t\t\t\n\t\t
\n\t{/if}\n
\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\tlet uxState = $state('idle');\n\tlet session = $state(null);\n\tlet records = $state([]);\n\tlet targetLanguages = $state([]);\n\tlet costEstimate = $state(null);\n\tlet errorMessage = $state('');\n\tlet sampleSize = $state(10);\n\tlet isAccepting = $state(false);\n\n\t// Per-language editing state: { `${rowId}_${langCode}`: true }\n\tlet editingCells = $state({});\n\tlet editValues = $state({});\n\n\t// Derived\n\tlet approvedCount = $derived(records.filter(r => r.status === 'APPROVED').length);\n\tlet rejectedCount = $derived(records.filter(r => r.status === 'REJECTED').length);\n\tlet pendingCount = $derived(records.filter(r => r.status === 'PENDING').length);\n\tlet isAllApproved = $derived(pendingCount === 0 && records.length > 0);\n\n\tfunction getRowStatusLabel(status) {\n\t\tconst labels = {\n\t\t\tAPPROVED: $t.translate?.preview?.status_approved || 'Approved',\n\t\t\tREJECTED: $t.translate?.preview?.status_rejected || 'Rejected',\n\t\t\tPENDING: $t.translate?.preview?.status_pending || 'Pending',\n\t\t};\n\t\treturn labels[status] || status;\n\t}\n\n\t/** Get language status for a record and language code */\n\tfunction getLangStatus(record, langCode) {\n\t\tconst lang = (record.languages || []).find(l => l.language_code === langCode);\n\t\treturn lang ? lang.status : 'pending';\n\t}\n\n\t/** Get translated value for a record and language code */\n\tfunction getLangValue(record, langCode) {\n\t\tconst lang = (record.languages || []).find(l => l.language_code === langCode);\n\t\treturn lang ? (lang.user_edit || lang.final_value || lang.translated_value || '') : '';\n\t}\n\n\t/** Get detected language for a record */\n\tfunction getDetectedLang(record) {\n\t\tif (record.source_language_detected && record.source_language_detected !== 'und') {\n\t\t\treturn record.source_language_detected;\n\t\t}\n\t\tconst langs = record.languages || [];\n\t\tfor (const l of langs) {\n\t\t\tif (l.source_language_detected && l.source_language_detected !== 'und') {\n\t\t\t\treturn l.source_language_detected;\n\t\t\t}\n\t\t}\n\t\treturn 'und';\n\t}\n\n\t/** @returns {Promise} */\n\tasync function handlePreview() {\n\t\tuxState = 'loading';\n\t\terrorMessage = '';\n\t\ttry {\n\t\t\tconst result = await fetchPreview(jobId, sampleSize, environmentId);\n\t\t\tsession = result;\n\t\t\trecords = (result.records || []).map(r => ({ ...r, _isEdited: false }));\n\t\t\ttargetLanguages = result.target_languages || [];\n\t\t\tcostEstimate = result.cost_estimate || null;\n\t\t\tuxState = 'preview_loaded';\n\t\t\taddToast($t.translate?.preview?.preview_loaded?.replace('{count}', records.length), 'success');\n\t\t} catch (err) {\n\t\t\terrorMessage = err?.message || $t.translate?.preview?.preview_failed;\n\t\t\tuxState = 'preview_error';\n\t\t\taddToast(errorMessage, 'error');\n\t\t}\n\t}\n\n\t/** @param {string} rowId */\n\tasync function handleApprove(rowId) {\n\t\ttry {\n\t\t\tconst updated = await approveRow(jobId, rowId);\n\t\t\trecords = records.map(r => r.id === rowId ? {\n\t\t\t\t...r,\n\t\t\t\tstatus: updated.status || 'APPROVED',\n\t\t\t\ttarget_sql: updated.target_sql || r.target_sql,\n\t\t\t\tlanguages: updated.languages || r.languages,\n\t\t\t\t_isEdited: false\n\t\t\t} : r);\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.preview?.approve_failed, 'error');\n\t\t}\n\t}\n\n\t/** @param {string} rowId */\n\tasync function handleReject(rowId) {\n\t\ttry {\n\t\t\tconst updated = await rejectRow(jobId, rowId);\n\t\t\trecords = records.map(r => r.id === rowId ? {\n\t\t\t\t...r,\n\t\t\t\tstatus: updated.status || 'REJECTED',\n\t\t\t\tlanguages: updated.languages || r.languages,\n\t\t\t\t_isEdited: false\n\t\t\t} : r);\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.preview?.reject_failed, 'error');\n\t\t}\n\t}\n\n\t/** @param {string} rowId @param {string} langCode */\n\tasync function handleApproveLanguage(rowId, langCode) {\n\t\ttry {\n\t\t\tconst updated = await approveRow(jobId, rowId, langCode);\n\t\t\trecords = records.map(r => r.id === rowId ? {\n\t\t\t\t...r,\n\t\t\t\tstatus: updated.status || r.status,\n\t\t\t\ttarget_sql: updated.target_sql || r.target_sql,\n\t\t\t\tlanguages: updated.languages || r.languages,\n\t\t\t\t_isEdited: false\n\t\t\t} : r);\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.preview?.approve_failed, 'error');\n\t\t}\n\t}\n\n\t/** @param {string} rowId @param {string} langCode */\n\tasync function handleRejectLanguage(rowId, langCode) {\n\t\ttry {\n\t\t\tconst updated = await rejectRow(jobId, rowId, langCode);\n\t\t\trecords = records.map(r => r.id === rowId ? {\n\t\t\t\t...r,\n\t\t\t\tstatus: updated.status || r.status,\n\t\t\t\tlanguages: updated.languages || r.languages,\n\t\t\t\t_isEdited: false\n\t\t\t} : r);\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.preview?.reject_failed, 'error');\n\t\t}\n\t}\n\n\t/** @param {string} rowId @param {string} langCode */\n\tfunction startLangEdit(rowId, langCode) {\n\t\tconst key = `${rowId}_${langCode}`;\n\t\teditingCells = { ...editingCells, [key]: true };\n\t\teditValues = { ...editValues, [key]: getLangValue(records.find(r => r.id === rowId), langCode) };\n\t}\n\n\t/** @param {string} rowId @param {string} langCode */\n\tasync function saveLangEdit(rowId, langCode) {\n\t\tconst key = `${rowId}_${langCode}`;\n\t\tconst value = (editValues[key] || '').trim();\n\t\tif (!value) return;\n\t\ttry {\n\t\t\tconst updated = await editRow(jobId, rowId, value, langCode);\n\t\t\trecords = records.map(r =>\n\t\t\t\tr.id === rowId\n\t\t\t\t\t? {\n\t\t\t\t\t\t...r,\n\t\t\t\t\t\tstatus: updated.status || r.status,\n\t\t\t\t\t\ttarget_sql: updated.target_sql || r.target_sql,\n\t\t\t\t\t\tlanguages: updated.languages || r.languages,\n\t\t\t\t\t\t_isEdited: true\n\t\t\t\t\t}\n\t\t\t\t\t: r\n\t\t\t);\n\t\t\tconst newEditing = { ...editingCells };\n\t\t\tdelete newEditing[key];\n\t\t\teditingCells = newEditing;\n\t\t\tconst newValues = { ...editValues };\n\t\t\tdelete newValues[key];\n\t\t\teditValues = newValues;\n\t\t\taddToast($t.translate?.preview?.row_updated, 'success');\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.preview?.edit_failed, 'error');\n\t\t}\n\t}\n\n\t/** @param {string} rowId @param {string} langCode */\n\tfunction cancelLangEdit(rowId, langCode) {\n\t\tconst key = `${rowId}_${langCode}`;\n\t\tconst newEditing = { ...editingCells };\n\t\tdelete newEditing[key];\n\t\teditingCells = newEditing;\n\t\tconst newValues = { ...editValues };\n\t\tdelete newValues[key];\n\t\teditValues = newValues;\n\t}\n\n\tasync function handleBulkApprove() {\n\t\tfor (const row of records) {\n\t\t\tif (row.status === 'PENDING') {\n\t\t\t\tawait handleApprove(row.id);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync function handleAccept() {\n\t\tisAccepting = true;\n\t\ttry {\n\t\t\tconst result = await acceptPreview(jobId);\n\t\t\tuxState = 'accepted';\n\t\t\tonAccept();\n\t\t\taddToast($t.translate?.preview?.accepted_body, 'success');\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.preview?.accept_failed, 'error');\n\t\t} finally {\n\t\t\tisAccepting = false;\n\t\t}\n\t}\n\n\n
\n\t\n\t
\n\t\t

{$t.translate?.preview?.title}

\n\t\t{#if uxState === 'preview_loaded'}\n\t\t\t\n\t\t\t\t{session?.status === 'APPLIED' ? $t.translate?.preview?.accepted_badge : $t.translate?.preview?.active_badge}\n\t\t\t\n\t\t{/if}\n\t
\n\n\t\n\t{#if uxState === 'idle'}\n\t\t
\n\t\t\t

\n\t\t\t\t{$t.translate?.preview?.title} — {$t.translate?.preview?.sample_size} {sampleSize}\n\t\t\t

\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t{sampleSize}\n\t\t\t
\n\t\t\t{#if sampleSize > 30}\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{($t.translate?.preview?.cost_warning) || 'Large sample size may incur higher costs'}\n\t\t\t\t
\n\t\t\t{/if}\n\t\t\t\n\t\t\t\t{$t.translate?.preview?.run_preview}\n\t\t\t\n\t\t
\n\n\t\n\t{:else if uxState === 'loading'}\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t{$t.translate?.preview?.running_preview}\n\t\t\t
\n\t\t\t
\n\t\t\t\t{#each Array(3) as _}\n\t\t\t\t\t
\n\t\t\t\t{/each}\n\t\t\t
\n\t\t
\n\n\t\n\t{:else if uxState === 'preview_error'}\n\t\t
\n\t\t\t

{errorMessage}

\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.preview?.retry_preview}\n\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t
\n\n\t\n\t{:else if uxState === 'preview_loaded'}\n\t\t\n\t\t{#if costEstimate}\n\t\t\t
\n\t\t\t\t

{$t.translate?.preview?.cost_estimate}

\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.preview?.sample_rows}\n\t\t\t\t\t\t{costEstimate.sample_size}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.preview?.sample_tokens}\n\t\t\t\t\t\t{costEstimate.sample_total_tokens?.toLocaleString()}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.preview?.sample_cost}\n\t\t\t\t\t\t${costEstimate.sample_cost?.toFixed(6)}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.preview?.est_total_cost}\n\t\t\t\t\t\t${costEstimate.estimated_cost?.toFixed(6)}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t

\n\t\t\t\t\t{$t.translate?.preview?.across_languages?.replace('{count}', costEstimate.num_languages || targetLanguages.length || 1)}\n\t\t\t\t

\n\t\t\t\t\n\t\t\t\t{#if costEstimate.warning}\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{costEstimate.warning}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\t\t{/if}\n\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t{$t.translate?.preview?.rows_count?.replace('{count}', records.length)}\n\t\t\t\t{$t.translate?.preview?.approved_count?.replace('{count}', approvedCount)}\n\t\t\t\t{$t.translate?.preview?.rejected_count?.replace('{count}', rejectedCount)}\n\t\t\t\t{#if pendingCount > 0}\n\t\t\t\t\t{$t.translate?.preview?.pending_count?.replace('{count}', pendingCount)}\n\t\t\t\t{/if}\n\t\t\t\t{#if targetLanguages.length > 0}\n\t\t\t\t\t{$t.translate?.preview?.languages_count?.replace('{count}', targetLanguages.length)}\n\t\t\t\t{/if}\n\t\t\t
\n\t\t\t
\n\t\t\t\t{#if pendingCount > 0}\n\t\t\t\t\t\n\t\t\t\t\t\t{$t.translate?.preview?.approve_all}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.preview?.re_run}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t{#each records as row, idx}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\t{@const langKey = `${row.id}_${lang}`}\n\t\t\t\t\t\t\t\t{@const langVal = getLangValue(row, lang)}\n\t\t\t\t\t\t\t\t{@const langStatus = getLangStatus(row, lang)}\n\t\t\t\t\t\t\t\t{@const isEditing = editingCells[langKey]}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t{/each}\n\t\t\t\t\n\t\t\t
#{$t.translate?.preview?.table_source}{$t.translate?.preview?.detected_language}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{lang}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
{$t.translate?.preview?.table_status}
{idx + 1}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{row.source_sql || $t.translate?.preview?.empty_placeholder}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{getDetectedLang(row)}\n\t\t\t\t\t\t\t\t{#if getDetectedLang(row) === 'und'}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#if isEditing}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t saveLangEdit(row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-0.5 text-xs bg-blue-600 text-white rounded hover:bg-blue-700\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.save_edit}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t cancelLangEdit(row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-0.5 text-xs border border-gray-300 text-gray-600 rounded hover:bg-gray-50\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.cancel_edit}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{langVal || $t.translate?.preview?.pending_placeholder}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t{#if langStatus !== 'approved' && langStatus !== 'edited'}\n\t\t\t\t\t\t\t\t\t\t\t\t handleApproveLanguage(row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-green-100 text-green-700 rounded hover:bg-green-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.approve}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.approve}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t startLangEdit(row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-blue-100 text-blue-700 rounded hover:bg-blue-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.edit}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.edit}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{#if langStatus !== 'rejected'}\n\t\t\t\t\t\t\t\t\t\t\t\t handleRejectLanguage(row.id, lang)}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-red-100 text-red-700 rounded hover:bg-red-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.reject}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.reject}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{getRowStatusLabel(row.status)}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{#if row.status !== 'APPROVED'}\n\t\t\t\t\t\t\t\t\t\t\t handleApprove(row.id)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-green-100 text-green-700 rounded hover:bg-green-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.approve_all}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.approve}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t{#if row.status !== 'REJECTED'}\n\t\t\t\t\t\t\t\t\t\t\t handleReject(row.id)}\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-1.5 py-0.5 text-xs bg-red-100 text-red-700 rounded hover:bg-red-200 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={$t.translate?.preview?.reject}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.preview?.reject}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.preview?.quality_gate}

\n\t\t\t\t

\n\t\t\t\t\t{isAllApproved ? $t.translate?.preview?.quality_gate_ready : $t.translate?.preview?.quality_gate_pending}\n\t\t\t\t

\n\t\t\t
\n\t\t\t\n\t\t\t\t{isAccepting ? $t.translate?.preview?.accepting : $t.translate?.preview?.accept_preview}\n\t\t\t\n\t\t
\n\n\t\n\t{:else if uxState === 'accepted'}\n\t\t
\n\t\t\t
\n\t\t\t

{$t.translate?.preview?.accepted_title}

\n\t\t\t

\n\t\t\t\t{$t.translate?.preview?.accepted_body}\n\t\t\t

\n\t\t\t\n\t\t\t\t{$t.translate?.preview?.run_preview_again}\n\t\t\t\n\t\t
\n\t{/if}\n
\n\n" }, { "contract_id": "TranslationRunGlobalIndicator", "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/TranslationRunGlobalIndicator.svelte", "start_line": 1, - "end_line": 139, + "end_line": 203, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", - "PURPOSE": "Persistent mini progress indicator for translation runs, rendered in root layout.", - "UX_FEEDBACK": "Click banner navigates to the translation run page", - "UX_STATE": "failed -> Red bar + \"Failed\" banner (auto-hides after 8s)" + "PURPOSE": "Persistent rich progress panel for active translation runs, rendered in root layout.", + "UX_FEEDBACK": "Rich stats during active run; compact auto-dismiss for terminal states", + "UX_STATE": "cancelled -> Compact gray banner with auto-dismiss after 8s" }, "relations": [ { @@ -88576,45 +87624,21 @@ "target_ref": "[translationRunStore]" } ], - "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - } - ], + "schema_warnings": [], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n\n\n\n\n\n\n{#if show}\n\t\n\t\t\n\t\t
\n\t\t\t\n\t\t
\n\t\t\n\t\t
\n\t\t\t{label}\n\n\t\t\t{#if uxState === 'running' || uxState === 'inserting'}\n\t\t\t\t\n\t\t\t\t\t{runState.current?.successfulRecords || 0}/{runState.current?.totalRecords || 0}\n\t\t\t\t\n\t\t\t\t{#if (runState.current?.failedRecords || 0) > 0}\n\t\t\t\t\t\n\t\t\t\t\t\t{$t.translate?.run?.failed || 'Failed'}: {runState.current?.failedRecords}\n\t\t\t\t\t\n\t\t\t\t{/if}\n\t\t\t\t\n\t\t\t\t\t▶ {$t.common?.open || 'Open'}\n\t\t\t\t\n\t\t\t{:else if uxState === 'partial'}\n\t\t\t\t\n\t\t\t\t\t{runState.current?.successfulRecords || 0}/{runState.current?.totalRecords || 0}\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t▶ {$t.common?.open || 'Open'}\n\t\t\t\t\n\t\t\t{:else}\n\t\t\t\t\n\t\t\t\t\t▶ {$t.common?.open || 'Open'}\n\t\t\t\t\n\t\t\t{/if}\n\t\t
\n\t
\n{/if}\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n\n{#if show}\n\t\n\t\t{#if isActive}\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{$t.common?.open || 'Open'}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{isCancelling\n\t\t\t\t\t\t\t\t\t? ($t.translate?.run?.cancelling || 'Cancelling...')\n\t\t\t\t\t\t\t\t\t: ($t.translate?.run?.cancel || 'Cancel')}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{($t.translate?.run?.total || 'Total')}\n\t\t\t\t\t\t\t{totalRecords}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{($t.translate?.run?.success || 'OK')}\n\t\t\t\t\t\t\t{successfulRecords}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{($t.translate?.run?.failed || 'Fail')}\n\t\t\t\t\t\t\t{failedRecords}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{($t.translate?.run?.skipped || 'Skip')}\n\t\t\t\t\t\t\t{skippedRecords}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t{:else}\n\t\t\t\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{#if uxState === 'completed'}\n\t\t\t\t\t\t\n\t\t\t\t\t{:else if uxState === 'partial' || uxState === 'failed'}\n\t\t\t\t\t\t\n\t\t\t\t\t{:else}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t\t{label}\n\t\t\t\t\t{#if uxState === 'partial'}\n\t\t\t\t\t\t{successfulRecords}/{totalRecords}\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t▶ {$t.common?.open || 'Open'}\n\t\t\t\t\n\t\t\t
\n\t\t{/if}\n\t
\n{/if}\n\n" }, { "contract_id": "TranslationRunProgress", "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/TranslationRunProgress.svelte", "start_line": 1, - "end_line": 320, + "end_line": 309, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", "POST": "Real-time progress display with two-phase (translation + insert) and state transitions.", "PRE": "runId is a valid translation run ID.", "PURPOSE": "WebSocket-driven progress bar with batch counter, success/failure/skip counts, two-phase display, and cancel button.", @@ -88626,8 +87650,8 @@ { "source_id": "TranslationRunProgress", "relation_type": "DEPENDS_ON", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" + "target_id": "api_module", + "target_ref": "[api_module]" }, { "source_id": "TranslationRunProgress", @@ -88637,28 +87661,6 @@ } ], "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Component" - } - }, { "code": "tag_forbidden_by_complexity", "tag": "POST", @@ -88680,23 +87682,23 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n
\n\t{#if uxState === 'idle'}\n\t\t
\n\t\t\t

{$t.translate?.run?.loading}

\n\t\t
\n\n\t{:else if isRunning}\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t{uxState === 'inserting' ? $t.translate?.run?.insert_phase : $t.translate?.run?.translate_phase}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{isCancelling ? $t.translate?.run?.cancelling : $t.translate?.run?.cancel}\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.total}\n\t\t\t\t\t

{totalRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.success}\n\t\t\t\t\t

{successfulRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.failed}\n\t\t\t\t\t

{failedRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.skipped}\n\t\t\t\t\t

{skippedRecords}

\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t

\n\t\t\t\t{$t.translate?.run?.batches_progress?.replace('{count}', batchCount).replace('{pct}', progressPct)}\n\t\t\t

\n\t\t
\n\n\t\n\t{:else if uxState === 'completed'}\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t{$t.translate?.run?.completed}\n\t\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.total}\n\t\t\t\t\t

{successfulRecords} / {totalRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.skipped}\n\t\t\t\t\t

{skippedRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.insert_status?.replace(':', '')}\n\t\t\t\t\t

{insertStatus || $t.translate?.run?.insert_completed}

\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\n\t{:else if uxState === 'partial'}\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.completed_with_errors}\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.retry_failed}\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.success}\n\t\t\t\t\t

{successfulRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.failed}\n\t\t\t\t\t

{failedRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.skipped}\n\t\t\t\t\t

{skippedRecords}

\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\n\t{:else if uxState === 'failed'}\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.translation_failed}\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.common?.retry}\n\t\t\t\t\n\t\t\t
\n\t\t\t

{status?.error_message || $t.translate?.common?.unknown}

\n\t\t
\n\n\t\n\t{:else if uxState === 'insert_failed'}\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.insert_failed}\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.retry_insert}\n\t\t\t\t\n\t\t\t
\n\t\t\t

\n\t\t\t\t{$t.translate?.run?.completed} ({successfulRecords}) — {$t.translate?.run?.insert_failed}\n\t\t\t

\n\t\t
\n\n\t\n\t{:else if uxState === 'cancelled'}\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t{$t.translate?.run?.cancelled}\n\t\t\t
\n\t\t
\n\t{/if}\n
\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t */\n\tlet uxState = $state('idle');\n\tlet status = $state(null);\n\tlet pollingInterval = $state(null);\n\tlet isCancelling = $state(false);\n\tlet pollingStarted = $state(false);\n\t// Safety net: stop polling after 300 attempts (10 min at 2s interval)\n\t// to prevent infinite spinner if the run gets stuck.\n\tlet pollCount = $state(0);\n\tconst MAX_POLLS = 300;\n\n\t// Derived\n\tlet totalRecords = $derived(status?.total_records || 0);\n\tlet successfulRecords = $derived(status?.successful_records || 0);\n\tlet failedRecords = $derived(status?.failed_records || 0);\n\tlet skippedRecords = $derived(status?.skipped_records || 0);\n\tlet progressPct = $derived.by(() => {\n\t\tif (totalRecords === 0) return 0;\n\t\treturn Math.round(((successfulRecords + failedRecords + skippedRecords) / totalRecords) * 100);\n\t});\n\tlet batchCount = $derived(status?.batch_count || 0);\n\tlet insertStatus = $derived(status?.insert_status || null);\n\n\tlet isRunning = $derived(uxState === 'running' || uxState === 'inserting');\n\n\t// Use onMount instead of $effect because $effect cleanup can fire\n\t// unexpectedly during re-renders in Svelte 5, stopping the polling\n\t// interval while leaving uxState as 'running' — the spinner keeps\n\t// spinning forever with no further polls. onMount/onDestroy gives\n\t// deterministic lifecycle: start when the component appears, stop\n\t// only when it is destroyed (currentRunId → null).\n\tonMount(() => {\n\t\tif (runId) {\n\t\t\tpollingStarted = true;\n\t\t\tstartPolling();\n\t\t}\n\t\treturn () => {\n\t\t\tstopPolling();\n\t\t};\n\t});\n\n\tfunction startPolling() {\n\t\tstopPolling();\n\t\tpollingInterval = setInterval(pollStatus, 2000);\n\t\tpollStatus();\n\t}\n\n\tfunction stopPolling() {\n\t\tif (pollingInterval) {\n\t\t\tclearInterval(pollingInterval);\n\t\t\tpollingInterval = null;\n\t\t}\n\t}\n\n\t/** @returns {Promise} */\n\tasync function pollStatus() {\n\t\t// Safety net: stop polling after MAX_POLLS attempts\n\t\tpollCount++;\n\t\tif (pollCount > MAX_POLLS) {\n\t\t\tconsole.warn('[TranslationRunProgress] Max polls reached, stopping');\n\t\t\tstopPolling();\n\t\t\tuxState = 'failed';\n\t\t\tonComplete({ status: 'TIMEOUT', error_message: 'Translation run timed out' });\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst data = await fetchRunStatus(runId);\n\t\t\tstatus = data;\n\n\t\t\t// Determine state from status\n\t\t\tconst s = data?.status;\n\t\t\tconst insertS = data?.insert_status;\n\n\t\t\tif (s === 'PENDING' || s === 'RUNNING') {\n\t\t\t\tif (insertS === 'started' || insertS === 'pending' || insertS === 'running') {\n\t\t\t\t\tuxState = 'inserting';\n\t\t\t\t} else {\n\t\t\t\t\tuxState = 'running';\n\t\t\t\t}\n\t\t\t} else if (s === 'COMPLETED') {\n\t\t\t\tif (insertS === 'failed' || insertS === 'timeout') {\n\t\t\t\t\tuxState = 'insert_failed';\n\t\t\t\t} else if (data.failed_records > 0) {\n\t\t\t\t\tuxState = 'partial';\n\t\t\t\t} else {\n\t\t\t\t\tuxState = 'completed';\n\t\t\t\t}\n\t\t\t\tstopPolling();\n\t\t\t\tonComplete(data);\n\t\t\t} else if (s === 'FAILED') {\n\t\t\t\tuxState = 'failed';\n\t\t\t\tstopPolling();\n\t\t\t\tonComplete(data);\n\t\t\t} else if (s === 'CANCELLED') {\n\t\t\t\tuxState = 'cancelled';\n\t\t\t\tstopPolling();\n\t\t\t\tonComplete(data);\n\t\t\t} else {\n\t\t\t\t// Unknown status — log and keep polling\n\t\t\t\tconsole.warn('[TranslationRunProgress] Unknown status:', s);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tconsole.warn('[TranslationRunProgress] Poll error:', err);\n\t\t}\n\t}\n\n\t/** @returns {Promise} */\n\tasync function handleCancel() {\n\t\tisCancelling = true;\n\t\ttry {\n\t\t\tawait cancelRun(runId);\n\t\t\tuxState = 'cancelled';\n\t\t\tstopPolling();\n\t\t\t// Notify the parent page that the run is complete so it resets\n\t\t\t// isRunning and hides the emit-processing UI. Without this, the\n\t\t\t// page-level isRunning stays `true` and the store's independent\n\t\t\t// polling (translationRunStore) triggers a SECOND onComplete\n\t\t\t// callback, causing duplicate toasts and state thrashing.\n\t\t\tonComplete({ status: 'CANCELLED', error_message: null });\n\t\t\taddToast($t.translate?.run?.run_cancelled, 'info');\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.run?.cancel_failed, 'error');\n\t\t} finally {\n\t\t\tisCancelling = false;\n\t\t}\n\t}\n\n\n
\n\t{#if uxState === 'idle'}\n\t\t
\n\t\t\t

{$t.translate?.run?.loading}

\n\t\t
\n\n\t{:else if isRunning}\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t{uxState === 'inserting' ? $t.translate?.run?.insert_phase : $t.translate?.run?.translate_phase}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{isCancelling ? $t.translate?.run?.cancelling : $t.translate?.run?.cancel}\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.total}\n\t\t\t\t\t

{totalRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.success}\n\t\t\t\t\t

{successfulRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.failed}\n\t\t\t\t\t

{failedRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.skipped}\n\t\t\t\t\t

{skippedRecords}

\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t

\n\t\t\t\t{$t.translate?.run?.batches_progress?.replace('{count}', batchCount).replace('{pct}', progressPct)}\n\t\t\t

\n\t\t
\n\n\t\n\t{:else if uxState === 'completed'}\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t{$t.translate?.run?.completed}\n\t\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.total}\n\t\t\t\t\t

{successfulRecords} / {totalRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.skipped}\n\t\t\t\t\t

{skippedRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.insert_status?.replace(':', '')}\n\t\t\t\t\t

{insertStatus || $t.translate?.run?.insert_completed}

\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\n\t{:else if uxState === 'partial'}\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.completed_with_errors}\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.retry_failed}\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.success}\n\t\t\t\t\t

{successfulRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.failed}\n\t\t\t\t\t

{failedRecords}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t{$t.translate?.run?.skipped}\n\t\t\t\t\t

{skippedRecords}

\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\n\t\n\t{:else if uxState === 'failed'}\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.translation_failed}\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.common?.retry}\n\t\t\t\t\n\t\t\t
\n\t\t\t

{status?.error_message || $t.translate?.common?.unknown}

\n\t\t
\n\n\t\n\t{:else if uxState === 'insert_failed'}\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.insert_failed}\n\t\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.retry_insert}\n\t\t\t\t\n\t\t\t
\n\t\t\t

\n\t\t\t\t{$t.translate?.run?.completed} ({successfulRecords}) — {$t.translate?.run?.insert_failed}\n\t\t\t

\n\t\t
\n\n\t\n\t{:else if uxState === 'cancelled'}\n\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t{$t.translate?.run?.cancelled}\n\t\t\t
\n\t\t
\n\t{/if}\n
\n\n" }, { "contract_id": "TranslationRunResult", "contract_type": "Component", "file_path": "frontend/src/lib/components/translate/TranslationRunResult.svelte", "start_line": 1, - "end_line": 510, + "end_line": 603, "tier": "TIER_2", "complexity": 4, "metadata": { "COMPLEXITY": "4", - "LAYER": "UI", "POST": "Results displayed with actions for retry, SQL audit, and Superset reference.", "PRE": "runId is a valid completed/failed/pending run.", "PURPOSE": "Run result display with statistics, insert status badge, Superset query reference, SQL audit block, and retry buttons.", "TASK": "connect_orphaned_components", + "TYPE": "{'completed'|'partial'|'failed'|'insert_failed'}", "UX_FEEDBACK": "Tabular statistics; click to copy Superset query ID; collapsible SQL block.", "UX_RECOVERY": "Retry failed batches; retry insert.", "UX_STATE": "insert_failed -> Insert phase failed, retry-insert available" @@ -88717,8 +87719,8 @@ { "source_id": "TranslationRunResult", "relation_type": "DEPENDS_ON", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" + "target_id": "api_module", + "target_ref": "[api_module]" }, { "source_id": "TranslationRunResult", @@ -88728,34 +87730,18 @@ } ], "schema_warnings": [ - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Component'", - "detail": { - "actual_type": "Component", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Component' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Component" - } - }, { "code": "unknown_tag", "tag": "TASK", "message": "@TASK is not defined in axiom_config.yaml tags", "detail": null }, + { + "code": "unknown_tag", + "tag": "TYPE", + "message": "@TYPE is not defined in axiom_config.yaml tags", + "detail": null + }, { "code": "missing_required_tag", "tag": "SIDE_EFFECT", @@ -88768,7 +87754,7 @@ ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
\n\t{#if !status}\n\t\t
\n\t\t\t

{$t.translate?.run?.loading_result}

\n\t\t
\n\t{:else}\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t.translate?.run?.result_title}

\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t{#if uxState === 'failed' || uxState === 'partial'}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{isRetrying ? $t.translate?.run?.retrying : $t.translate?.run?.retry_failed}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t\t{#if uxState === 'insert_failed'}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{isRetryingInsert ? $t.translate?.run?.retrying_insert : $t.translate?.run?.retry_insert}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t\t showBulkReplace = true}\n\t\t\t\t\t\tclass=\"px-3 py-1.5 text-xs bg-indigo-600 text-white rounded hover:bg-indigo-700 transition-colors\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{$t.translate?.run?.bulk_replace || 'Bulk Replace'}\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.total_records}

\n\t\t\t\t\t

{status.total_records || 0}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.success}

\n\t\t\t\t\t

{status.successful_records || 0}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.failed}

\n\t\t\t\t\t

{status.failed_records || 0}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.skipped}

\n\t\t\t\t\t

{status.skipped_records || 0}

\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if status.language_stats && status.language_stats.length > 0}\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.per_language ?? 'Per-Language Statistics'}

\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each status.language_stats as lang}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t

{lang.language_code}

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{lang.translated_rows}\n\t\t\t\t\t\t\t\t\t{#if lang.failed_rows > 0}\n\t\t\t\t\t\t\t\t\t\t{lang.failed_rows}\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t{#if lang.skipped_rows > 0}\n\t\t\t\t\t\t\t\t\t\t{lang.skipped_rows}\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t{lang.total_rows}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.run?.run_id}\n\t\t\t\t\t\t{status.id}\n\t\t\t\t\t\t copyToClipboard(status.id)}\n\t\t\t\t\t\t\tclass=\"ml-1 text-blue-500 hover:text-blue-700 text-xs\"\n\t\t\t\t\t\t\ttitle={$t.translate?.run?.copy_id}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{$t.translate?.run?.copy_id}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.run?.insert_status}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{insertStatusBadge.label}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t{#if status.superset_execution_id}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t.translate?.run?.superset_query}\n\t\t\t\t\t\t\t{status.superset_execution_id}\n\t\t\t\t\t\t\t copyToClipboard(status.superset_execution_id)}\n\t\t\t\t\t\t\t\tclass=\"ml-1 text-blue-500 hover:text-blue-700 text-xs\"\n\t\t\t\t\t\t\t\ttitle={$t.translate?.run?.copy_id}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{$t.translate?.run?.copy_id}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t\t{#if status.error_message}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t.translate?.run?.error_label?.replace('{error}', status.error_message)}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.sql_audit?.replace('{count}', sqlAuditLines.length)}\n\t\t\t\t\n\n\t\t\t\t{#if showSqlAudit}\n\t\t\t\t\t
\n\t\t\t\t\t\t{#if sqlAuditLines.length === 0}\n\t\t\t\t\t\t\t

{$t.translate?.run?.no_sql_audit}

\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t{#each sqlAuditLines as line, idx}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{idx + 1}. {line}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if status.event_invariants}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.run?.event_invariants}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{status.event_invariants.invariant_valid ? $t.translate?.run?.valid : $t.translate?.run?.violated}\n\t\t\t\t\t\t\n\t\t\t\t\t\t|\n\t\t\t\t\t\t{$t.translate?.run?.superset_query_id_label} {status.event_invariants.has_run_started ? $t.translate?.run?.started_yes : $t.translate?.run?.started_no}\n\t\t\t\t\t\t|\n\t\t\t\t\t\t{$t.translate?.run?.terminal_events?.replace('{count}', status.event_invariants.terminal_event_count)}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t\n\t\t\t{#if records.length > 0}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t showRecords = !showRecords}\n\t\t\t\t\t\t\tclass=\"flex items-center gap-2 text-sm text-gray-700 hover:text-gray-900\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t.translate?.run?.records_title || 'Translation Records'} ({records.length})\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\n\t\t\t\t\t{#if showRecords}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t{#if targetLanguages.length === 0}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#each records as row, idx}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{#if targetLanguages.length > 0}\n\t\t\t\t\t\t\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const langData = (row.languages || []).find(l => l.language_code === lang)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const cellValue = langData?.final_value || langData?.translated_value || ''}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{@const detectedSource = langData?.source_language_detected || row.source_language_detected || ''}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
#{$t.translate?.preview?.table_source || 'Source'}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t{lang}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
{$t.translate?.preview?.translation || 'Translation'}{$t.translate?.preview?.table_status || 'Status'}{$t.translate?.preview?.table_actions || 'Actions'}
{idx + 1}\n\t\t\t\t\t\t\t\t\t\t\t\t{row.source_sql || row.source_object_name || '—'}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{row.status}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst firstLang = row.languages?.[0] || {};\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcorrectionPopupData = {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsourceTerm: row.source_sql || row.source_object_name || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tincorrectTarget: firstLang?.final_value || firstLang?.translated_value || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsourceLanguage: firstLang?.source_language_detected || row.source_language_detected || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetLanguage: targetLanguages[0] || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trunId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trowKey: row.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-0.5 text-[10px] bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Submit Correction\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.run?.correct || 'Correct'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst firstLang = row.languages?.[0] || {};\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst item = {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsourceTerm: row.source_sql || row.source_object_name || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tincorrectTarget: firstLang?.final_value || firstLang?.translated_value || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trowKey: row.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst exists = bulkCorrectionItems.some(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ti => i.sourceTerm === item.sourceTerm && i.incorrectTarget === item.incorrectTarget\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tif (!exists) {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tbulkCorrectionItems = [...bulkCorrectionItems, item];\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-0.5 text-[10px] bg-purple-600 text-white rounded hover:bg-purple-700 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Collect for bulk correction\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.run?.collect || 'Collect'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t{/if}\n\t\t
\n\t{/if}\n
\n\n\n correctionPopupData = null}\n\tonSubmitted={() => { correctionPopupData = null; loadData(); onRefresh(); }}\n/>\n\n showBulkReplace = false}\n\tonApplied={(count) => { showBulkReplace = false; loadData(); onRefresh(); }}\n/>\n\n bulkCorrectionItems = []}\n\tonSubmitted={() => { bulkCorrectionItems = []; loadData(); onRefresh(); }}\n/>\n\n" + "body": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\t */\n\n\timport { t } from '$lib/i18n';\n\timport { addToast } from '$lib/toasts.js';\n\timport {\n\t\tfetchRunStatus,\n\t\tfetchRunRecords,\n\t\tretryFailedBatches,\n\t\tretryInsert,\n\t\tfetchRunBatches\n\t} from '$lib/api/translate.js';\n\timport CorrectionCell from './CorrectionCell.svelte';\n\timport TermCorrectionPopup from './TermCorrectionPopup.svelte';\n\timport BulkReplaceModal from './BulkReplaceModal.svelte';\n\n\t/** @type {{ runId: string, onRefresh: Function }} */\n\tlet { runId, onRefresh = () => {} } = $props();\n\n\t/**\n\t * @type {'completed'|'partial'|'failed'|'insert_failed'}\n\t */\n\tlet uxState = $state('completed');\n\tlet status = $state(null);\n\tlet records = $state([]);\n\tlet batches = $state([]);\n\tlet showRecords = $state(false);\n\tlet showSqlAudit = $state(false);\n\tlet isRetrying = $state(false);\n\tlet isRetryingInsert = $state(false);\n\tlet sqlAuditLines = $state([]);\n\tlet targetLanguages = $state([]);\n\tlet correctionPopupData = $state(null);\n\tlet showBulkReplace = $state(false);\n\n\t// Pagination & filter state\n\tlet currentPage = $state(1);\n\tlet pageSize = $state(25);\n\tlet totalRecords = $state(0);\n\tlet statusFilter = $state('');\n\n\tlet totalPages = $derived(totalRecords > 0 ? Math.ceil(totalRecords / pageSize) : 1);\n\tlet recordStart = $derived(totalRecords > 0 ? (currentPage - 1) * pageSize + 1 : 0);\n\tlet recordEnd = $derived(Math.min(currentPage * pageSize, totalRecords));\n\n\tlet visiblePages = $derived.by(() => {\n\t\tif (totalPages <= 7) {\n\t\t\treturn Array.from({ length: totalPages }, (_, i) => i + 1);\n\t\t}\n\t\tconst half = 3;\n\t\tlet start = Math.max(1, currentPage - half);\n\t\tlet end = Math.min(totalPages, currentPage + half);\n\t\tif (start === 1) end = Math.min(totalPages, start + 6);\n\t\tif (end === totalPages) start = Math.max(1, end - 6);\n\t\tconst pages = [];\n\t\tfor (let p = start; p <= end; p++) pages.push(p);\n\t\treturn pages;\n\t});\n\n\tconst STATUS_FILTERS = [\n\t\t{ value: '', labelKey: 'translate.run.filter_all', label: 'All' },\n\t\t{ value: 'FAILED', labelKey: 'translate.run.translation_failed', label: 'Failed' },\n\t\t{ value: 'SUCCESS', labelKey: 'translate.run.success', label: 'Success' },\n\t\t{ value: 'SKIPPED', labelKey: 'translate.run.skipped', label: 'Skipped' },\n\t];\n\n\tfunction getRunStatusLabel(value) {\n\t\tconst labels = {\n\t\t\tCOMPLETED: $t.translate?.run?.completed || 'Completed',\n\t\t\tFAILED: $t.translate?.run?.translation_failed || 'Failed',\n\t\t\tRUNNING: $t.translate?.history?.status_running || 'Running',\n\t\t\tPENDING: $t.translate?.history?.status_pending || 'Pending',\n\t\t\tCANCELLED: $t.translate?.run?.cancelled || 'Cancelled',\n\t\t};\n\t\treturn labels[value] || value;\n\t}\n\n\t// Derived\n\tlet insertStatusBadge = $derived.by(() => {\n\t\tconst s = status?.insert_status;\n\t\tif (!s || s === 'success') return { label: $t.translate?.run?.success, class: 'bg-green-100 text-green-700' };\n\t\tif (s === 'failed' || s === 'timeout') return { label: $t.translate?.run?.failed, class: 'bg-red-100 text-red-700' };\n\t\treturn { label: s, class: 'bg-yellow-100 text-yellow-700' };\n\t});\n\n\t$effect(() => {\n\t\tif (runId) {\n\t\t\tloadData();\n\t\t}\n\t});\n\n\t// Reload records when page, filter, or table visibility changes\n\tlet recordsLoadKey = $derived(showRecords ? `${currentPage}-${statusFilter}-${pageSize}-${runId}` : null);\n\t$effect(() => {\n\t\tif (recordsLoadKey) {\n\t\t\tloadRecords();\n\t\t}\n\t});\n\n\t/** @returns {Promise} */\n\tasync function loadData() {\n\t\ttry {\n\t\t\tconst [statusData, batchesData] = await Promise.all([\n\t\t\t\tfetchRunStatus(runId),\n\t\t\t\tfetchRunBatches(runId),\n\t\t\t]);\n\t\t\tstatus = statusData;\n\t\t\tbatches = Array.isArray(batchesData) ? batchesData : [];\n\n\t\t\t// Extract target languages from language_stats\n\t\t\tif (statusData.language_stats && statusData.language_stats.length > 0) {\n\t\t\t\ttargetLanguages = statusData.language_stats.map(l => l.language_code);\n\t\t\t}\n\n\t\t\t// Determine state\n\t\t\tconst s = statusData.status;\n\t\t\tconst insertS = statusData.insert_status;\n\t\t\tif (s === 'COMPLETED' && insertS === 'failed') {\n\t\t\t\tuxState = 'insert_failed';\n\t\t\t} else if (s === 'COMPLETED' && (statusData.failed_records || 0) > 0) {\n\t\t\t\tuxState = 'partial';\n\t\t\t} else if (s === 'FAILED') {\n\t\t\t\tuxState = 'failed';\n\t\t\t} else {\n\t\t\t\tuxState = 'completed';\n\t\t\t}\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.run?.load_failed, 'error');\n\t\t}\n\t}\n\n\t/** @returns {Promise} */\n\tasync function loadRecords() {\n\t\ttry {\n\t\t\tconst recordsData = await fetchRunRecords(runId, {\n\t\t\t\tpage: currentPage,\n\t\t\t\tpage_size: pageSize,\n\t\t\t\tstatus: statusFilter || undefined\n\t\t\t});\n\t\t\trecords = recordsData?.items || [];\n\t\t\ttotalRecords = recordsData?.total || 0;\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.run?.load_failed, 'error');\n\t\t}\n\t}\n\n\t/** @param {number} page */\n\tfunction goToPage(page) {\n\t\tif (page < 1 || page > totalPages) return;\n\t\tcurrentPage = page;\n\t}\n\n\t/** @param {string} filterValue */\n\tfunction setFilter(filterValue) {\n\t\tstatusFilter = filterValue;\n\t\tcurrentPage = 1;\n\t}\n\n\t/** @returns {Promise} */\n\tasync function handleRetry() {\n\t\tisRetrying = true;\n\t\ttry {\n\t\t\tawait retryFailedBatches(runId);\n\t\t\taddToast($t.translate?.run?.retry_success, 'success');\n\t\t\tawait loadData();\n\t\t\tonRefresh();\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.run?.retry_failed_msg, 'error');\n\t\t} finally {\n\t\t\tisRetrying = false;\n\t\t}\n\t}\n\n\t/** @returns {Promise} */\n\tasync function handleRetryInsert() {\n\t\tisRetryingInsert = true;\n\t\ttry {\n\t\t\tawait retryInsert(runId);\n\t\t\taddToast($t.translate?.run?.insert_retry_success, 'success');\n\t\t\tawait loadData();\n\t\t\tonRefresh();\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.run?.insert_retry_failed_msg, 'error');\n\t\t} finally {\n\t\t\tisRetryingInsert = false;\n\t\t}\n\t}\n\n\t/** @param {string} text */\n\tfunction copyToClipboard(text) {\n\t\tnavigator.clipboard.writeText(text).then(() => {\n\t\t\taddToast($t.translate?.run?.copy_success, 'success');\n\t\t}).catch(() => {\n\t\t\taddToast($t.translate?.run?.copy_failed, 'error');\n\t\t});\n\t}\n\n\t/** @returns {Promise} */\n\tasync function toggleSqlAudit() {\n\t\tshowSqlAudit = !showSqlAudit;\n\t\tif (showSqlAudit && sqlAuditLines.length === 0) {\n\t\t\t// Load all records with successful translation for SQL audit\n\t\t\ttry {\n\t\t\t\tconst allRecords = await fetchRunRecords(runId, { page_size: 500, status: 'SUCCESS' });\n\t\t\t\tconst items = allRecords?.items || [];\n\t\t\t\tsqlAuditLines = items.slice(0, 20).map(r => r.target_sql).filter(Boolean);\n\t\t\t} catch (err) {\n\t\t\t\tsqlAuditLines = [$t.translate?.run?.no_sql_audit];\n\t\t\t}\n\t\t}\n\t}\n\n\n
\n\t{#if !status}\n\t\t
\n\t\t\t

{$t.translate?.run?.loading_result}

\n\t\t
\n\t{:else}\n\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t\t

{$t.translate?.run?.result_title}

\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t{#if uxState === 'failed' || uxState === 'partial'}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{isRetrying ? $t.translate?.run?.retrying : $t.translate?.run?.retry_failed}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t\t{#if uxState === 'insert_failed'}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{isRetryingInsert ? $t.translate?.run?.retrying_insert : $t.translate?.run?.retry_insert}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.total_records}

\n\t\t\t\t\t

{status.total_records || 0}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.success}

\n\t\t\t\t\t

{status.successful_records || 0}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.failed}

\n\t\t\t\t\t

{status.failed_records || 0}

\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.skipped}

\n\t\t\t\t\t

{status.skipped_records || 0}

\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if status.language_stats && status.language_stats.length > 0}\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.run?.per_language ?? 'Per-Language Statistics'}

\n\t\t\t\t\t
\n\t\t\t\t\t\t{#each status.language_stats as lang}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t

{lang.language_code}

\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{lang.translated_rows}\n\t\t\t\t\t\t\t\t\t{#if lang.failed_rows > 0}\n\t\t\t\t\t\t\t\t\t\t{lang.failed_rows}\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t{#if lang.skipped_rows > 0}\n\t\t\t\t\t\t\t\t\t\t{lang.skipped_rows}\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t{lang.total_rows}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.run?.run_id}\n\t\t\t\t\t\t{status.id}\n\t\t\t\t\t\t copyToClipboard(status.id)}\n\t\t\t\t\t\t\tclass=\"ml-1 text-blue-500 hover:text-blue-700 text-xs\"\n\t\t\t\t\t\t\ttitle={$t.translate?.run?.copy_id}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{$t.translate?.run?.copy_id}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.run?.status_label || 'Статус'}\n\t\t\t\t\t\t{getRunStatusLabel(status.status)}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.run?.insert_status}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{insertStatusBadge.label}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t{#if status.superset_execution_id}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t.translate?.run?.superset_query}\n\t\t\t\t\t\t\t{status.superset_execution_id}\n\t\t\t\t\t\t\t copyToClipboard(status.superset_execution_id)}\n\t\t\t\t\t\t\t\tclass=\"ml-1 text-blue-500 hover:text-blue-700 text-xs\"\n\t\t\t\t\t\t\t\ttitle={$t.translate?.run?.copy_id}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{$t.translate?.run?.copy_id}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t\t{#if status.error_message}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{$t.translate?.run?.error_label?.replace('{error}', status.error_message)}\n\t\t\t\t\t\t
\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t
\n\n\t\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t{$t.translate?.run?.sql_audit?.replace('{count}', sqlAuditLines.length)}\n\t\t\t\t\n\n\t\t\t\t{#if showSqlAudit}\n\t\t\t\t\t
\n\t\t\t\t\t\t{#if sqlAuditLines.length === 0}\n\t\t\t\t\t\t\t

{$t.translate?.run?.no_sql_audit}

\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t{#each sqlAuditLines as line, idx}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{idx + 1}. {line}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\n\t\t\t\n\t\t\t{#if status.event_invariants}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t{$t.translate?.run?.event_invariants}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{status.event_invariants.invariant_valid ? $t.translate?.run?.valid : $t.translate?.run?.violated}\n\t\t\t\t\t\t\n\t\t\t\t\t\t|\n\t\t\t\t\t\t{$t.translate?.run?.superset_query_id_label} {status.event_invariants.has_run_started ? $t.translate?.run?.started_yes : $t.translate?.run?.started_no}\n\t\t\t\t\t\t|\n\t\t\t\t\t\t{$t.translate?.run?.terminal_events?.replace('{count}', status.event_invariants.terminal_event_count)}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/if}\n\n\t\t\t\n\t\t\t{#if status && status.total_records > 0}\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\tshowRecords = !showRecords;\n\t\t\t\t\t\t\t\tif (showRecords) { currentPage = 1; statusFilter = ''; }\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tclass=\"flex items-center gap-2 text-sm text-gray-700 hover:text-gray-900\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$t.translate?.run?.records_title || 'Translation Records'}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t({totalRecords > 0 ? `${recordStart}–${recordEnd} / ${totalRecords}` : status.total_records})\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\n\t\t\t\t\t\t{#if showRecords}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{#each STATUS_FILTERS as f}\n\t\t\t\t\t\t\t\t\t setFilter(f.value)}\n\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-0.5 text-[11px] rounded-full border transition-colors\n\t\t\t\t\t\t\t\t\t\t\t{statusFilter === f.value\n\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-blue-600 text-white border-blue-600'\n\t\t\t\t\t\t\t\t\t\t\t\t: 'text-gray-500 border-gray-300 hover:bg-gray-100'}\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{$t.translate?.run?.[f.labelKey] || f.label}\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t
\n\n\t\t\t\t\t{#if showRecords}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t{#if targetLanguages.length === 0}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#each records as row, idx}\n\t\t\t\t\t\t\t\t\t\t{@const globalIdx = (currentPage - 1) * pageSize + idx + 1}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{#if targetLanguages.length > 0}\n\t\t\t\t\t\t\t\t\t\t\t\t{#if row.languages && row.languages.length > 0}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{#each targetLanguages as lang}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@const langData = (row.languages || []).find(l => l.language_code === lang)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@const cellValue = langData?.final_value || langData?.translated_value || ''}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{@const detectedSource = langData?.source_language_detected || row.source_language_detected || ''}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
#{$t.translate?.preview?.table_source || 'Source'}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t\t\t{lang}\n\t\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t\t
{$t.translate?.preview?.translation || 'Translation'}{$t.translate?.preview?.table_status || 'Status'}{$t.translate?.preview?.table_actions || 'Actions'}
{globalIdx}\n\t\t\t\t\t\t\t\t\t\t\t\t{row.source_sql || row.source_object_name || '—'}\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.run?.translation_failed || 'Translation failed'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t{row.status}\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{#if row.languages && row.languages.length > 0}\n\t\t\t\t\t\t\t\t\t\t\t\t\t {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tconst firstLang = row.languages?.[0] || {};\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcorrectionPopupData = {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsourceTerm: row.source_sql || row.source_object_name || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tincorrectTarget: firstLang?.final_value || firstLang?.translated_value || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsourceLanguage: firstLang?.source_language_detected || row.source_language_detected || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttargetLanguage: targetLanguages[0] || '',\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trunId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trowKey: row.id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-0.5 text-[10px] bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttitle=\"Submit Correction\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{$t.translate?.run?.correct || 'Correct'}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t{#if totalPages > 1}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{$t.translate?.run?.records_title || 'Records'}: {recordStart}–{recordEnd} / {totalRecords}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t goToPage(currentPage - 1)}\n\t\t\t\t\t\t\t\t\t\tdisabled={currentPage <= 1}\n\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-1 text-xs border border-gray-300 rounded hover:bg-gray-100 disabled:opacity-30 disabled:cursor-default transition-colors\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t‹\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{#each visiblePages as page}\n\t\t\t\t\t\t\t\t\t\t goToPage(page)}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-1 text-xs border rounded transition-colors\n\t\t\t\t\t\t\t\t\t\t\t\t{page === currentPage\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'bg-blue-600 text-white border-blue-600'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: 'border-gray-300 text-gray-600 hover:bg-gray-100'}\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{page}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t\t goToPage(currentPage + 1)}\n\t\t\t\t\t\t\t\t\t\tdisabled={currentPage >= totalPages}\n\t\t\t\t\t\t\t\t\t\tclass=\"px-2 py-1 text-xs border border-gray-300 rounded hover:bg-gray-100 disabled:opacity-30 disabled:cursor-default transition-colors\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t›\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\t\t\t{/if}\n\t\t
\n\t{/if}\n
\n\n\n correctionPopupData = null}\n\tonSubmitted={() => { correctionPopupData = null; loadData(); onRefresh(); }}\n/>\n\n showBulkReplace = false}\n\tonApplied={(count) => { showBulkReplace = false; loadData(); onRefresh(); }}\n/>\n\n" }, { "contract_id": "BulkReplaceModalTests", @@ -93239,24 +92225,16 @@ "contract_type": "Store", "file_path": "frontend/src/lib/stores/translationRun.js", "start_line": 1, - "end_line": 209, + "end_line": 195, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "INVARIANT": "Store is writable so page components can also set initial state.", + "PROPERTY": "{boolean} isActive - Derived: true when polling is active", "PURPOSE": "Global store for active translation run progress — survives page navigation.", - "TYPEDEF": "{'idle'|'running'|'inserting'|'completed'|'partial'|'failed'|'cancelled'} UxState", - "UX_STATE": "cancelled -> Run cancelled" + "TYPEDEF": "{Object} TranslationRunState" }, - "relations": [ - { - "source_id": "TranslationRunStore", - "relation_type": "DEPENDS_ON", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" - } - ], + "relations": [], "schema_warnings": [ { "code": "tag_not_for_contract_type", @@ -93285,30 +92263,10 @@ } }, { - "code": "tag_not_for_contract_type", - "tag": "INVARIANT", - "message": "@INVARIANT is not allowed for contract type 'Store'", - "detail": { - "actual_type": "Store", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Block", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "INVARIANT", - "message": "@INVARIANT is forbidden for contract type 'Store' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Store" - } + "code": "unknown_tag", + "tag": "PROPERTY", + "message": "@PROPERTY is not defined in axiom_config.yaml tags", + "detail": null }, { "code": "tag_not_for_contract_type", @@ -93342,47 +92300,18 @@ "tag": "TYPEDEF", "message": "@TYPEDEF is not defined in axiom_config.yaml tags", "detail": null - }, - { - "code": "tag_not_for_contract_type", - "tag": "UX_STATE", - "message": "@UX_STATE is not allowed for contract type 'Store'", - "detail": { - "actual_type": "Store", - "allowed_types": [ - "Component" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "UX_STATE", - "message": "@UX_STATE is forbidden for contract type 'Store' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Store" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Store' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Store" - } } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "// #region TranslationRunStore [C:3] [TYPE Store] [SEMANTICS translate, run, progress, polling, store]\n// @BRIEF Global store for active translation run progress — survives page navigation.\n// @RELATION DEPENDS_ON -> [TranslateApi]\n// @UX_STATE idle -> No active run\n// @UX_STATE running -> Translation phase in progress\n// @UX_STATE inserting -> Insert phase in progress\n// @UX_STATE completed -> Run completed\n// @UX_STATE partial -> Completed with errors\n// @UX_STATE failed -> Run failed\n// @UX_STATE cancelled -> Run cancelled\n// @INVARIANT Polling stops when run reaches a terminal state (completed/failed/cancelled).\n// @INVARIANT Store is writable so page components can also set initial state.\n\nimport { writable, derived } from 'svelte/store';\nimport { fetchRunStatus } from '$lib/api/translate.js';\n\n/**\n * @typedef {'idle'|'running'|'inserting'|'completed'|'partial'|'failed'|'cancelled'} UxState\n */\n\n/**\n * @typedef {Object} TranslationRunState\n * @property {string|null} runId - Active translation run ID\n * @property {UxState} uxState - Current UX state\n * @property {Object|null} status - Raw status data from API\n * @property {number} totalRecords\n * @property {number} successfulRecords\n * @property {number} failedRecords\n * @property {number} skippedRecords\n * @property {number} progressPct - 0-100\n * @property {string|null} insertStatus\n * @property {number} batchCount\n * @property {string|null} jobId - The job this run belongs to (for navigation)\n * @property {boolean} isFullRun\n * @property {boolean} isActive - Derived: true when polling is active\n */\n\nconst initialState = {\n\trunId: null,\n\tuxState: 'idle',\n\tstatus: null,\n\ttotalRecords: 0,\n\tsuccessfulRecords: 0,\n\tfailedRecords: 0,\n\tskippedRecords: 0,\n\tprogressPct: 0,\n\tinsertStatus: null,\n\tbatchCount: 0,\n\tjobId: null,\n\tisFullRun: false,\n};\n\n/** @type {import('svelte/store').Writable} */\nexport const translationRunStore = writable(initialState);\n\n/** Derived: true when a run is actively being polled */\nexport const isTranslationActive = derived(\n\ttranslationRunStore,\n\t($store) => $store.uxState === 'running' || $store.uxState === 'inserting'\n);\n\n/** Derived: true when run has finished */\nexport const isTranslationFinished = derived(\n\ttranslationRunStore,\n\t($store) =>\n\t\t$store.uxState === 'completed' ||\n\t\t$store.uxState === 'partial' ||\n\t\t$store.uxState === 'failed' ||\n\t\t$store.uxState === 'cancelled'\n);\n\n// Internal polling state (not reactive)\nlet _pollingInterval = null;\nlet _pollCount = 0;\nconst MAX_POLLS = 300;\nlet _onCompleteCallback = null;\n\n/**\n * Clear the onComplete callback without stopping polling.\n * Used by page components when they unmount — the store keeps polling\n * so the global indicator still works, but the page callback is detached.\n */\nexport function clearOnCompleteCallback() {\n\t_onCompleteCallback = null;\n}\n\n/**\n * Start polling for a translation run.\n * @param {string} runId\n * @param {Object} [options]\n * @param {string} [options.jobId] - Job ID for navigation\n * @param {boolean} [options.isFullRun]\n * @param {Function} [options.onComplete] - Called when run reaches terminal state\n */\nexport function startTranslationRun(runId, options = {}) {\n\tif (!runId) return;\n\n\tstopTranslationRun();\n\n\ttranslationRunStore.set({\n\t\t...initialState,\n\t\trunId,\n\t\tuxState: 'running',\n\t\tjobId: options.jobId || null,\n\t\tisFullRun: options.isFullRun || false,\n\t});\n\n\t_onCompleteCallback = options.onComplete || null;\n\t_pollCount = 0;\n\t_pollingInterval = setInterval(pollStatus, 2000);\n\tpollStatus();\n}\n\n/**\n * Stop polling without clearing state (run may persist).\n */\nexport function stopTranslationRun() {\n\tif (_pollingInterval) {\n\t\tclearInterval(_pollingInterval);\n\t\t_pollingInterval = null;\n\t}\n}\n\n/**\n * Reset store to idle.\n */\nexport function resetTranslationRun() {\n\tstopTranslationRun();\n\ttranslationRunStore.set(initialState);\n\t_onCompleteCallback = null;\n}\n\n/**\n * Manually set the run state (used by page component for lifecycle hooks).\n * @param {Partial} state\n */\nexport function updateTranslationRunState(state) {\n\ttranslationRunStore.update(prev => ({ ...prev, ...state }));\n}\n\n/** @returns {Promise} */\nasync function pollStatus() {\n\t_pollCount++;\n\tif (_pollCount > MAX_POLLS) {\n\t\tconsole.warn('[translationRunStore] Max polls reached, stopping');\n\t\tstopTranslationRun();\n\t\ttranslationRunStore.update(s => ({ ...s, uxState: 'failed' }));\n\t\tif (_onCompleteCallback) _onCompleteCallback({ status: 'TIMEOUT', error_message: 'Translation run timed out' });\n\t\treturn;\n\t}\n\n\tlet currentState;\n\ttranslationRunStore.subscribe(s => { currentState = s; })();\n\tif (!currentState || !currentState.runId) {\n\t\tstopTranslationRun();\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst data = await fetchRunStatus(currentState.runId);\n\t\tif (!data) return;\n\n\t\tconst s = data?.status;\n\t\tconst insertS = data?.insert_status;\n\n\t\tlet newUxState = currentState.uxState;\n\n\t\tif (s === 'PENDING' || s === 'RUNNING') {\n\t\t\tnewUxState = (insertS === 'started' || insertS === 'pending' || insertS === 'running')\n\t\t\t\t? 'inserting' : 'running';\n\t\t} else if (s === 'COMPLETED') {\n\t\t\tnewUxState = (insertS === 'failed' || insertS === 'timeout') ? 'insert_failed'\n\t\t\t\t: (data.failed_records > 0) ? 'partial' : 'completed';\n\t\t\tstopTranslationRun();\n\t\t} else if (s === 'FAILED') {\n\t\t\tnewUxState = 'failed';\n\t\t\tstopTranslationRun();\n\t\t} else if (s === 'CANCELLED') {\n\t\t\tnewUxState = 'cancelled';\n\t\t\tstopTranslationRun();\n\t\t}\n\n\t\tconst total = data?.total_records || 0;\n\t\tconst pct = total > 0\n\t\t\t? Math.round(((data.successful_records + data.failed_records + data.skipped_records) / total) * 100)\n\t\t\t: 0;\n\n\t\ttranslationRunStore.update(prev => ({\n\t\t\t...prev,\n\t\t\tuxState: newUxState,\n\t\t\tstatus: data,\n\t\t\ttotalRecords: total,\n\t\t\tsuccessfulRecords: data?.successful_records || 0,\n\t\t\tfailedRecords: data?.failed_records || 0,\n\t\t\tskippedRecords: data?.skipped_records || 0,\n\t\t\tprogressPct: pct,\n\t\t\tinsertStatus: data?.insert_status || null,\n\t\t\tbatchCount: data?.batch_count || 0,\n\t\t}));\n\n\t\t// Fire completion callback if terminal\n\t\tif (newUxState !== 'running' && newUxState !== 'inserting') {\n\t\t\tif (_onCompleteCallback) _onCompleteCallback(data);\n\t\t}\n\t} catch (err) {\n\t\tconsole.warn('[translationRunStore] Poll error:', err);\n\t}\n}\n// #endregion TranslationRunStore\n" + "body": "// #region TranslationRunStore [C:3] [TYPE Store] [SEMANTICS translate, run, progress, polling, store]\n// @BRIEF Global store for active translation run progress — survives page navigation.\n// @TYPEDEF {'idle'|'running'|'inserting'|'completed'|'partial'|'failed'|'cancelled'} UxState\n\n */\n\n/**\n * @typedef {Object} TranslationRunState\n * @property {string|null} runId - Active translation run ID\n * @property {UxState} uxState - Current UX state\n * @property {Object|null} status - Raw status data from API\n * @property {number} totalRecords\n * @property {number} successfulRecords\n * @property {number} failedRecords\n * @property {number} skippedRecords\n * @property {number} progressPct - 0-100\n * @property {string|null} insertStatus\n * @property {number} batchCount\n * @property {string|null} jobId - The job this run belongs to (for navigation)\n * @property {boolean} isFullRun\n * @property {boolean} isActive - Derived: true when polling is active\n */\n\nconst initialState = {\n\trunId: null,\n\tuxState: 'idle',\n\tstatus: null,\n\ttotalRecords: 0,\n\tsuccessfulRecords: 0,\n\tfailedRecords: 0,\n\tskippedRecords: 0,\n\tprogressPct: 0,\n\tinsertStatus: null,\n\tbatchCount: 0,\n\tjobId: null,\n\tisFullRun: false,\n};\n\n/** @type {import('svelte/store').Writable} */\nexport const translationRunStore = writable(initialState);\n\n/** Derived: true when a run is actively being polled */\nexport const isTranslationActive = derived(\n\ttranslationRunStore,\n\t($store) => $store.uxState === 'running' || $store.uxState === 'inserting'\n);\n\n/** Derived: true when run has finished */\nexport const isTranslationFinished = derived(\n\ttranslationRunStore,\n\t($store) =>\n\t\t$store.uxState === 'completed' ||\n\t\t$store.uxState === 'partial' ||\n\t\t$store.uxState === 'failed' ||\n\t\t$store.uxState === 'cancelled'\n);\n\n// Internal polling state (not reactive)\nlet _pollingInterval = null;\nlet _pollCount = 0;\nconst MAX_POLLS = 300;\nlet _onCompleteCallback = null;\n\n/**\n * Clear the onComplete callback without stopping polling.\n * Used by page components when they unmount — the store keeps polling\n * so the global indicator still works, but the page callback is detached.\n */\nexport function clearOnCompleteCallback() {\n\t_onCompleteCallback = null;\n}\n\n/**\n * Start polling for a translation run.\n * @param {string} runId\n * @param {Object} [options]\n * @param {string} [options.jobId] - Job ID for navigation\n * @param {boolean} [options.isFullRun]\n * @param {Function} [options.onComplete] - Called when run reaches terminal state\n */\nexport function startTranslationRun(runId, options = {}) {\n\tif (!runId) return;\n\n\tstopTranslationRun();\n\n\ttranslationRunStore.set({\n\t\t...initialState,\n\t\trunId,\n\t\tuxState: 'running',\n\t\tjobId: options.jobId || null,\n\t\tisFullRun: options.isFullRun || false,\n\t});\n\n\t_onCompleteCallback = options.onComplete || null;\n\t_pollCount = 0;\n\t_pollingInterval = setInterval(pollStatus, 2000);\n\tpollStatus();\n}\n\n/**\n * Stop polling without clearing state (run may persist).\n */\nexport function stopTranslationRun() {\n\tif (_pollingInterval) {\n\t\tclearInterval(_pollingInterval);\n\t\t_pollingInterval = null;\n\t}\n}\n\n/**\n * Reset store to idle.\n */\nexport function resetTranslationRun() {\n\tstopTranslationRun();\n\ttranslationRunStore.set(initialState);\n\t_onCompleteCallback = null;\n}\n\n/**\n * Manually set the run state (used by page component for lifecycle hooks).\n * @param {Partial} state\n */\nexport function updateTranslationRunState(state) {\n\ttranslationRunStore.update(prev => ({ ...prev, ...state }));\n}\n\n/** @returns {Promise} */\nasync function pollStatus() {\n\t_pollCount++;\n\tif (_pollCount > MAX_POLLS) {\n\t\tconsole.warn('[translationRunStore] Max polls reached, stopping');\n\t\tstopTranslationRun();\n\t\ttranslationRunStore.update(s => ({ ...s, uxState: 'failed' }));\n\t\tif (_onCompleteCallback) _onCompleteCallback({ status: 'TIMEOUT', error_message: 'Translation run timed out' });\n\t\treturn;\n\t}\n\n\tlet currentState;\n\ttranslationRunStore.subscribe(s => { currentState = s; })();\n\tif (!currentState || !currentState.runId) {\n\t\tstopTranslationRun();\n\t\treturn;\n\t}\n\n\ttry {\n\t\tconst data = await fetchRunStatus(currentState.runId);\n\t\tif (!data) return;\n\n\t\tconst s = data?.status;\n\t\tconst insertS = data?.insert_status;\n\n\t\tlet newUxState = currentState.uxState;\n\n\t\tif (s === 'PENDING' || s === 'RUNNING') {\n\t\t\tnewUxState = (insertS === 'started' || insertS === 'pending' || insertS === 'running')\n\t\t\t\t? 'inserting' : 'running';\n\t\t} else if (s === 'COMPLETED') {\n\t\t\tnewUxState = (insertS === 'failed' || insertS === 'timeout') ? 'insert_failed'\n\t\t\t\t: (data.failed_records > 0) ? 'partial' : 'completed';\n\t\t\tstopTranslationRun();\n\t\t} else if (s === 'FAILED') {\n\t\t\tnewUxState = 'failed';\n\t\t\tstopTranslationRun();\n\t\t} else if (s === 'CANCELLED') {\n\t\t\tnewUxState = 'cancelled';\n\t\t\tstopTranslationRun();\n\t\t}\n\n\t\tconst total = data?.total_records || 0;\n\t\tconst pct = total > 0\n\t\t\t? Math.round(((data.successful_records + data.failed_records + data.skipped_records) / total) * 100)\n\t\t\t: 0;\n\n\t\ttranslationRunStore.update(prev => ({\n\t\t\t...prev,\n\t\t\tuxState: newUxState,\n\t\t\tstatus: data,\n\t\t\ttotalRecords: total,\n\t\t\tsuccessfulRecords: data?.successful_records || 0,\n\t\t\tfailedRecords: data?.failed_records || 0,\n\t\t\tskippedRecords: data?.skipped_records || 0,\n\t\t\tprogressPct: pct,\n\t\t\tinsertStatus: data?.insert_status || null,\n\t\t\tbatchCount: data?.batch_count || 0,\n\t\t}));\n\n\t\t// Fire completion callback if terminal\n\t\tif (newUxState !== 'running' && newUxState !== 'inserting') {\n\t\t\tif (_onCompleteCallback) _onCompleteCallback(data);\n\t\t}\n\t} catch (err) {\n\t\tconsole.warn('[translationRunStore] Poll error:', err);\n\t}\n}\n// #endregion TranslationRunStore\n" }, { "contract_id": "frontend/src/lib/stores/translationRun.js::clearOnCompleteCallback", "contract_type": "Function", "file_path": "frontend/src/lib/stores/translationRun.js", - "start_line": 78, - "end_line": 83, + "start_line": 64, + "end_line": 69, "tier": "TIER_1", "complexity": 1, "metadata": {}, @@ -93396,8 +92325,8 @@ "contract_id": "frontend/src/lib/stores/translationRun.js::startTranslationRun", "contract_type": "Function", "file_path": "frontend/src/lib/stores/translationRun.js", - "start_line": 87, - "end_line": 95, + "start_line": 73, + "end_line": 81, "tier": "TIER_1", "complexity": 1, "metadata": {}, @@ -93411,8 +92340,8 @@ "contract_id": "frontend/src/lib/stores/translationRun.js::stopTranslationRun", "contract_type": "Function", "file_path": "frontend/src/lib/stores/translationRun.js", - "start_line": 114, - "end_line": 117, + "start_line": 100, + "end_line": 103, "tier": "TIER_1", "complexity": 1, "metadata": {}, @@ -93426,8 +92355,8 @@ "contract_id": "frontend/src/lib/stores/translationRun.js::resetTranslationRun", "contract_type": "Function", "file_path": "frontend/src/lib/stores/translationRun.js", - "start_line": 124, - "end_line": 127, + "start_line": 110, + "end_line": 113, "tier": "TIER_1", "complexity": 1, "metadata": {}, @@ -93441,8 +92370,8 @@ "contract_id": "frontend/src/lib/stores/translationRun.js::updateTranslationRunState", "contract_type": "Function", "file_path": "frontend/src/lib/stores/translationRun.js", - "start_line": 133, - "end_line": 137, + "start_line": 119, + "end_line": 123, "tier": "TIER_1", "complexity": 1, "metadata": {}, @@ -93456,8 +92385,8 @@ "contract_id": "frontend/src/lib/stores/translationRun.js::pollStatus", "contract_type": "Function", "file_path": "frontend/src/lib/stores/translationRun.js", - "start_line": 141, - "end_line": 142, + "start_line": 127, + "end_line": 128, "tier": "TIER_1", "complexity": 1, "metadata": {}, @@ -103107,31 +102036,15 @@ "contract_type": "Page", "file_path": "frontend/src/routes/translate/+page.svelte", "start_line": 1, - "end_line": 301, + "end_line": 282, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", - "POST": "Renders job list with status badges and navigation to config.", - "PRE": "Valid auth context before fetching job list.", - "PURPOSE": "Translation job list page showing all jobs with status and schedule indicators.", - "UX_STATE": "error -> Shows error message with retry button" + "PURPOSE": "Translation job list page showing all jobs with status badges and schedule indicators.", + "TYPE": "{string} idle | loading | empty | populated | error" }, - "relations": [ - { - "source_id": "TranslateJobList", - "relation_type": "CALLS", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" - }, - { - "source_id": "TranslateJobList", - "relation_type": "DEPENDS_ON", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" - } - ], + "relations": [], "schema_warnings": [ { "code": "tag_not_for_contract_type", @@ -103159,76 +102072,6 @@ "contract_type": "Page" } }, - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Page'", - "detail": { - "actual_type": "Page", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Page' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Page" - } - }, - { - "code": "tag_not_for_contract_type", - "tag": "POST", - "message": "@POST is not allowed for contract type 'Page'", - "detail": { - "actual_type": "Page", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "POST", - "message": "@POST is forbidden for contract type 'Page' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Page" - } - }, - { - "code": "tag_not_for_contract_type", - "tag": "PRE", - "message": "@PRE is not allowed for contract type 'Page'", - "detail": { - "actual_type": "Page", - "allowed_types": [ - "Module", - "Function", - "Class", - "Component", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "PRE", - "message": "@PRE is forbidden for contract type 'Page' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Page" - } - }, { "code": "tag_not_for_contract_type", "tag": "PURPOSE", @@ -103257,73 +102100,30 @@ } }, { - "code": "tag_not_for_contract_type", - "tag": "UX_STATE", - "message": "@UX_STATE is not allowed for contract type 'Page'", - "detail": { - "actual_type": "Page", - "allowed_types": [ - "Component" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "UX_STATE", - "message": "@UX_STATE is forbidden for contract type 'Page' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Page" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Page' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Page" - } + "code": "unknown_tag", + "tag": "TYPE", + "message": "@TYPE is not defined in axiom_config.yaml tags", + "detail": null } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n\n\n\n\n\n\n
\n
\n
\n

{$t.translate?.jobs?.title}

\n

\n {$t.translate?.jobs?.subtitle}\n

\n
\n \n \n \n \n {$t.translate?.jobs?.new_job}\n \n
\n\n \n
\n {#each statusPills as pill}\n filterByStatus(pill.value)}\n class=\"px-3 py-1.5 text-sm rounded-full border transition-colors\n {statusFilter === pill.value\n ? 'bg-blue-50 border-blue-300 text-blue-700'\n : 'bg-white border-gray-200 text-gray-600 hover:border-gray-300'}\"\n >\n {pill.label}\n ({pill.count})\n \n {/each}\n
\n\n \n {#if isLoading}\n
\n {#each Array(5) as _}\n
\n {/each}\n
\n\n \n {:else if uxState === 'error'}\n
\n

{error || $t.translate?.jobs?.an_error_occurred}

\n \n {$t.translate?.common?.retry}\n \n
\n\n \n {:else if uxState === 'empty'}\n
\n \n \n \n {#if statusFilter}\n

{$t.translate?.jobs?.no_jobs_filtered.replace('{status}', statusFilter)}

\n filterByStatus('')}\n class=\"text-blue-600 hover:text-blue-700 text-sm\"\n >\n {$t.translate?.jobs?.clear_filter}\n \n {:else}\n

{$t.translate?.jobs?.no_jobs}

\n \n {$t.translate?.jobs?.create_job}\n \n {/if}\n
\n\n \n {:else if uxState === 'populated'}\n
\n {#each jobs as job}\n navigateToConfig(job.id)}\n class=\"bg-white border border-gray-200 rounded-lg p-4 hover:shadow-md hover:border-gray-300 transition-all cursor-pointer\"\n >\n
\n
\n
\n

{job.name}

\n \n {job.status}\n \n
\n {#if job.description}\n

{job.description}

\n {/if}\n
\n {#if job.source_dialect && job.target_dialect}\n {job.source_dialect} → {job.target_dialect}\n {/if}\n\t\t\t\t{#if job.target_languages?.length > 0}\n\t\t\t\t
\n\t\t\t\t {#each job.target_languages as lang}\n\t\t\t\t {lang}\n\t\t\t\t {/each}\n\t\t\t\t
\n\t\t\t\t{:else if job.target_language}\n\t\t\t\t {job.target_language}\n\t\t\t\t{/if}\n {#if job.translation_column}\n {$t.translate?.jobs?.column_label.replace('{column}', job.translation_column)}\n {/if}\n {#if job.created_by}\n {$t.translate?.jobs?.by_label.replace('{by}', job.created_by)}\n {/if}\n {#if job.updated_at}\n {$t.translate?.jobs?.updated_label.replace('{date}', new Date(job.updated_at).toLocaleDateString())}\n {/if}\n
\n
\n
e.stopPropagation()}>\n handleDuplicate(job.id)}\n class=\"p-2 text-gray-400 hover:text-blue-600 rounded-lg hover:bg-blue-50 transition-colors\"\n title={$t.translate?.jobs?.duplicate}\n >\n \n \n \n \n {#if showDeleteConfirm === job.id}\n
\n handleDelete(job.id)}\n class=\"px-2 py-1 text-xs bg-red-600 text-white rounded hover:bg-red-700\"\n >\n {$t.translate?.jobs?.confirm_delete}\n \n (showDeleteConfirm = null)}\n class=\"px-2 py-1 text-xs bg-gray-200 text-gray-700 rounded hover:bg-gray-300\"\n >\n {$t.translate?.jobs?.cancel_delete}\n \n
\n {:else}\n (showDeleteConfirm = job.id)}\n class=\"p-2 text-gray-400 hover:text-red-600 rounded-lg hover:bg-red-50 transition-colors\"\n title={$t.translate?.common?.delete}\n >\n \n \n \n \n {/if}\n
\n
\n
\n {/each}\n
\n {/if}\n
\n\n\n" + "body": "\n\n\n */\n\n import { onMount } from 'svelte';\n import { goto } from '$app/navigation';\n import { t, _ } from '$lib/i18n';\n import { addToast } from '$lib/toasts.js';\n import { fetchJobs, deleteJob, duplicateJob } from '$lib/api/translate.js';\n\n /** @type {string} idle | loading | empty | populated | error */\n let uxState = $state('idle');\n let jobs = $state([]);\n let error = $state(null);\n let isLoading = $state(true);\n let showDeleteConfirm = $state(null);\n let statusFilter = $state('');\n let currentPage = $state(1);\n let pageSize = $state(20);\n\n // Count jobs by status for filter pills\n let statusCounts = $derived.by(() => {\n const counts = { DRAFT: 0, READY: 0, RUNNING: 0, COMPLETED: 0, FAILED: 0, CANCELLED: 0 };\n for (const job of jobs) {\n if (counts[job.status] !== undefined) counts[job.status]++;\n }\n return counts;\n });\n\n let statusPills = $derived([\n { label: 'All', value: '', count: jobs.length },\n { label: $t.translate?.jobs?.status_draft, value: 'DRAFT', count: statusCounts.DRAFT },\n { label: $t.translate?.jobs?.status_ready, value: 'READY', count: statusCounts.READY },\n { label: $t.translate?.jobs?.status_running, value: 'RUNNING', count: statusCounts.RUNNING },\n { label: $t.translate?.jobs?.status_completed, value: 'COMPLETED', count: statusCounts.COMPLETED },\n { label: $t.translate?.jobs?.status_failed, value: 'FAILED', count: statusCounts.FAILED },\n ]);\n\n onMount(() => {\n loadJobs();\n });\n\n /** @returns {Promise} */\n async function loadJobs() {\n isLoading = true;\n uxState = 'loading';\n error = null;\n try {\n const result = await fetchJobs({ page: currentPage, page_size: pageSize, status: statusFilter || undefined });\n jobs = Array.isArray(result) ? result : (result?.results || result?.items || []);\n uxState = jobs.length === 0 ? 'empty' : 'populated';\n } catch (err) {\n error = err?.message || $t.translate?.jobs?.load_failed;\n uxState = 'error';\n } finally {\n isLoading = false;\n }\n }\n\n /** @param {string} status */\n function filterByStatus(status) {\n statusFilter = status;\n currentPage = 1;\n loadJobs();\n }\n\n /** @param {string} jobId */\n function navigateToConfig(jobId) {\n goto(`/translate/${jobId}`);\n }\n\n function navigateToCreate() {\n goto('/translate/new');\n }\n\n /** @param {string} jobId */\n async function handleDuplicate(jobId) {\n try {\n const result = await duplicateJob(jobId);\n addToast(`${_('translate.jobs.job_duplicated').replace('{name}', result.name)}`, 'success');\n loadJobs();\n } catch (err) {\n addToast(err?.message || _('translate.jobs.duplicate_failed'), 'error');\n }\n }\n\n /** @param {string} jobId */\n async function handleDelete(jobId) {\n try {\n await deleteJob(jobId);\n addToast(_('translate.jobs.job_deleted'), 'success');\n showDeleteConfirm = null;\n loadJobs();\n } catch (err) {\n addToast(err?.message || _('translate.jobs.delete_failed'), 'error');\n showDeleteConfirm = null;\n }\n }\n\n /** @param {string} status */\n function getStatusBadgeClass(status) {\n const map = {\n DRAFT: 'bg-gray-100 text-gray-700',\n READY: 'bg-blue-100 text-blue-700',\n RUNNING: 'bg-yellow-100 text-yellow-700',\n COMPLETED: 'bg-green-100 text-green-700',\n FAILED: 'bg-red-100 text-red-700',\n CANCELLED: 'bg-gray-100 text-gray-500',\n };\n return map[status] || 'bg-gray-100 text-gray-700';\n }\n\n\n
\n
\n
\n

{$t.translate?.jobs?.title}

\n

\n {$t.translate?.jobs?.subtitle}\n

\n
\n \n \n \n \n {$t.translate?.jobs?.new_job}\n \n
\n\n \n
\n {#each statusPills as pill}\n filterByStatus(pill.value)}\n class=\"px-3 py-1.5 text-sm rounded-full border transition-colors\n {statusFilter === pill.value\n ? 'bg-blue-50 border-blue-300 text-blue-700'\n : 'bg-white border-gray-200 text-gray-600 hover:border-gray-300'}\"\n >\n {pill.label}\n ({pill.count})\n \n {/each}\n
\n\n \n {#if isLoading}\n
\n {#each Array(5) as _}\n
\n {/each}\n
\n\n \n {:else if uxState === 'error'}\n
\n

{error || $t.translate?.jobs?.an_error_occurred}

\n \n {$t.translate?.common?.retry}\n \n
\n\n \n {:else if uxState === 'empty'}\n
\n \n \n \n {#if statusFilter}\n

{$t.translate?.jobs?.no_jobs_filtered.replace('{status}', statusFilter)}

\n filterByStatus('')}\n class=\"text-blue-600 hover:text-blue-700 text-sm\"\n >\n {$t.translate?.jobs?.clear_filter}\n \n {:else}\n

{$t.translate?.jobs?.no_jobs}

\n \n {$t.translate?.jobs?.create_job}\n \n {/if}\n
\n\n \n {:else if uxState === 'populated'}\n
\n {#each jobs as job}\n navigateToConfig(job.id)}\n class=\"bg-white border border-gray-200 rounded-lg p-4 hover:shadow-md hover:border-gray-300 transition-all cursor-pointer\"\n >\n
\n
\n
\n

{job.name}

\n \n {job.status}\n \n
\n {#if job.description}\n

{job.description}

\n {/if}\n
\n {#if job.source_dialect && job.target_dialect}\n {job.source_dialect} → {job.target_dialect}\n {/if}\n\t\t\t\t{#if job.target_languages?.length > 0}\n\t\t\t\t
\n\t\t\t\t {#each job.target_languages as lang}\n\t\t\t\t {lang}\n\t\t\t\t {/each}\n\t\t\t\t
\n\t\t\t\t{:else if job.target_language}\n\t\t\t\t {job.target_language}\n\t\t\t\t{/if}\n {#if job.translation_column}\n {$t.translate?.jobs?.column_label.replace('{column}', job.translation_column)}\n {/if}\n {#if job.created_by}\n {$t.translate?.jobs?.by_label.replace('{by}', job.created_by)}\n {/if}\n {#if job.updated_at}\n {$t.translate?.jobs?.updated_label.replace('{date}', new Date(job.updated_at).toLocaleDateString())}\n {/if}\n
\n
\n
e.stopPropagation()}>\n handleDuplicate(job.id)}\n class=\"p-2 text-gray-400 hover:text-blue-600 rounded-lg hover:bg-blue-50 transition-colors\"\n title={$t.translate?.jobs?.duplicate}\n >\n \n \n \n \n {#if showDeleteConfirm === job.id}\n
\n handleDelete(job.id)}\n class=\"px-2 py-1 text-xs bg-red-600 text-white rounded hover:bg-red-700\"\n >\n {$t.translate?.jobs?.confirm_delete}\n \n (showDeleteConfirm = null)}\n class=\"px-2 py-1 text-xs bg-gray-200 text-gray-700 rounded hover:bg-gray-300\"\n >\n {$t.translate?.jobs?.cancel_delete}\n \n
\n {:else}\n (showDeleteConfirm = job.id)}\n class=\"p-2 text-gray-400 hover:text-red-600 rounded-lg hover:bg-red-50 transition-colors\"\n title={$t.translate?.common?.delete}\n >\n \n \n \n \n {/if}\n
\n
\n
\n {/each}\n
\n {/if}\n
\n\n\n" }, { "contract_id": "TranslationJobConfig", "contract_type": "Page", "file_path": "frontend/src/routes/translate/[id]/+page.svelte", "start_line": 1, - "end_line": 1364, + "end_line": 1381, "tier": "TIER_2", "complexity": 4, "metadata": { "COMPLEXITY": 4, - "LAYER": "Page", - "PURPOSE": "Translation job configuration page with datasource selector, column mapping, target table config,", - "UX_STATE": "datasource_unavailable -> Selected datasource cannot be reached" + "PURPOSE": "Translation job configuration page with datasource selector, column mapping, target table config, LLM provider, and dictionary attachment.", + "TYPE": "{string} idle | loading | configured | saving | validation_error | datasource_unavailable" }, - "relations": [ - { - "source_id": "TranslationJobConfig", - "relation_type": "DEPENDS_ON", - "target_id": "ScheduleConfig", - "target_ref": "[ScheduleConfig]" - }, - { - "source_id": "TranslationJobConfig", - "relation_type": "CALLS", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" - }, - { - "source_id": "TranslationJobConfig", - "relation_type": "CALLS", - "target_id": "api_module", - "target_ref": "[api_module]" - } - ], + "relations": [], "schema_warnings": [ { "code": "tag_not_for_contract_type", @@ -103351,45 +102151,6 @@ "contract_type": "Page" } }, - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Page'", - "detail": { - "actual_type": "Page", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Page' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Page" - } - }, - { - "code": "invalid_enum_value", - "tag": "LAYER", - "message": "@LAYER value 'Page' is not in allowed enum", - "detail": { - "allowed": [ - "Core", - "Domain", - "API", - "UI", - "Service", - "Infrastructure", - "Plugin" - ], - "value": "Page" - } - }, { "code": "tag_not_for_contract_type", "tag": "PURPOSE", @@ -103418,61 +102179,30 @@ } }, { - "code": "tag_not_for_contract_type", - "tag": "UX_STATE", - "message": "@UX_STATE is not allowed for contract type 'Page'", - "detail": { - "actual_type": "Page", - "allowed_types": [ - "Component" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "UX_STATE", - "message": "@UX_STATE is forbidden for contract type 'Page' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Page" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Page' at C4", - "detail": { - "actual_complexity": 4, - "contract_type": "Page" - } + "code": "unknown_tag", + "tag": "TYPE", + "message": "@TYPE is not defined in axiom_config.yaml tags", + "detail": null } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n
\n \n
\n
\n

\n {isNewJob ? $t.translate?.config?.new_title : $t.translate?.config?.edit_title}\n

\n {#if !isNewJob && existingJob}\n

{$t.translate?.config?.id_label.replace('{id}', existingJob.id)}

\n {/if}\n
\n
\n goto('/translate')}\n class=\"px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors\"\n >\n {$t.translate?.config?.cancel}\n \n \n {isSaving ? $t.translate?.config?.saving : $t.translate?.config?.save}\n \n
\n
\n\n \n {#if warnings.length > 0}\n
\n

{$t.translate?.config?.warnings_title}

\n
    \n {#each warnings as w}\n
  • {w}
  • \n {/each}\n
\n
\n {/if}\n\n \n {#if uxState === 'loading'}\n
\n {#each Array(6) as _}\n
\n {/each}\n
\n\n \n {:else if uxState === 'error'}\n
\n

{error || $t.translate?.common?.unknown_error}

\n goto('/translate')}\n class=\"px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors\"\n >\n {$t.translate?.config?.back_to_jobs}\n \n
\n\n \n {:else if uxState === 'datasource_unavailable'}\n
\n

\n {$t.translate?.config?.datasource_unavailable}\n

\n
\n\n \n {:else if uxState === 'configured' || uxState === 'saving' || uxState === 'validation_error' || uxState === 'idle'}\n \n
\n \n
\n\n \n
\n
\n \n
\n

{$t.translate?.config?.basic_info}

\n
\n
\n \n \n {#if validationErrors.name}\n

{validationErrors.name}

\n {/if}\n
\n
\n \n \n
\n
\n
\n\n \n
\n

{$t.translate?.config?.datasource}

\n
\n
\n \n \n \n {#each environments as env}\n \n {/each}\n \n
\n
\n \n searchDatasources('')}\n onblur={() => setTimeout(() => (showDatasourceDropdown = false), 150)}\n class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n placeholder={$t.translate?.config?.datasource_search_placeholder || 'Search datasets...'}\n />\n {#if datasourceLoading}\n
\n {/if}\n {#if showDatasourceDropdown}\n
\n {#each datasourceList as ds}\n selectDatasource(ds)}\n class=\"w-full text-left px-3 py-2 text-sm hover:bg-blue-50 border-b border-gray-100 last:border-b-0\"\n >\n {ds.table_name}\n {ds.schema}\n {ds.database_name} · {ds.database_dialect}\n \n {/each}\n
\n {/if}\n
\n {#if databaseDialect}\n
\n \n {$t.translate?.config?.detected_dialect.replace('{dialect}', databaseDialect)}\n \n
\n {/if}\n
\n\n {#if isColumnsLoading}\n
\n {/if}\n\n {#if columnList.length > 0}\n
\n \n
\n {#each columnList as col}\n
\n {col.name}\n {#if col.type}\n ({col.type})\n {/if}\n {#if isVirtual(col.name)}\n {$t.translate?.config?.virtual}\n {/if}\n
\n {/each}\n
\n
\n {/if}\n
\n\n \n
\n

{$t.translate?.config?.column_mapping}

\n\n \n
\n \n \n \n {#each columnList as col}\n \n {/each}\n \n {#if validationErrors.translationColumn}\n

{validationErrors.translationColumn}

\n {/if}\n
\n\n \n
\n
\n \n \n
\n
\n \n \n
\n
\n\n \n
\n
\n \n {#if sourceKeyCols.length > 0}\n { sourceKeyCols = []; targetKeyCols = []; }}\n class=\"text-xs text-red-600 hover:text-red-700\"\n >\n {$t.translate?.config?.clear_all}\n \n {/if}\n
\n\n {#if columnList.length === 0}\n
\n {\n sourceKeyCols = [...sourceKeyCols, ''];\n targetKeyCols = [...targetKeyCols, ''];\n }}\n class=\"px-3 py-1 text-xs border border-dashed border-gray-300 rounded text-gray-500 hover:border-blue-300 hover:text-blue-600\"\n >\n {$t.translate?.config?.add_key_column}\n \n
\n {:else}\n
\n {#each columnList as col}\n toggleSourceKeyCol(col.name)}\n class=\"px-2.5 py-1 text-xs rounded-full border transition-colors\n {sourceKeyCols.includes(col.name)\n ? 'bg-blue-100 border-blue-300 text-blue-700'\n : 'bg-white border-gray-200 text-gray-600 hover:border-gray-300'}\"\n title={isVirtual(col.name) ? $t.translate?.config?.virtual_column_warning.replace('{col}', col.name) : ''}\n >\n {col.name}\n {#if isVirtual(col.name)}\n *\n {/if}\n \n {/each}\n
\n {/if}\n\n {#if sourceKeyCols.length > 0}\n
\n {#each sourceKeyCols as srcCol, idx}\n
\n \n \n updateTargetKeyCol(idx, e.target.value)}\n class=\"flex-1 px-3 py-1.5 border border-gray-300 rounded text-sm font-mono focus:ring-2 focus:ring-blue-500\"\n />\n {\n sourceKeyCols = sourceKeyCols.filter((_, i) => i !== idx);\n targetKeyCols = targetKeyCols.filter((_, i) => i !== idx);\n }}\n class=\"p-1 text-gray-400 hover:text-red-500\"\n >\n \n \n \n \n
\n {/each}\n
\n {/if}\n {#if validationErrors.targetKeyCols}\n

{validationErrors.targetKeyCols}

\n {/if}\n
\n\n \n
\n \n {#if columnList.length === 0}\n

{$t.translate?.config?.select_datasource_hint}

\n {:else}\n
\n {#each columnList as col}\n toggleContextColumn(col.name)}\n class=\"px-2.5 py-1 text-xs rounded-full border transition-colors\n {contextColumns.includes(col.name)\n ? 'bg-purple-100 border-purple-300 text-purple-700'\n : 'bg-white border-gray-200 text-gray-600 hover:border-gray-300'}\"\n >\n {col.name}\n {#if isVirtual(col.name)}\n *\n {/if}\n \n {/each}\n
\n {/if}\n
\n
\n\n \n
\n

{$t.translate?.config?.target_table_title}

\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n \n {#each databases as db}\n \n {/each}\n \n {#if databasesLoading}\n

{$t.translate?.config?.loading_databases}

\n {:else if !environmentId}\n

{$t.translate?.config?.select_environment}

\n {:else}\n

{$t.translate?.config?.target_database_hint}

\n {/if}\n
\n
\n\n \n
\n

Target Column Mapping

\n

Configure which columns receive translated data, language codes, source text, and detected source language during INSERT.

\n
\n
\n \n \n

{$t.translate?.config?.target_column_hint}

\n
\n
\n \n \n

{$t.translate?.config?.target_language_column_hint}

\n
\n
\n \n \n

{$t.translate?.config?.target_source_column_hint}

\n
\n
\n \n \n

{$t.translate?.config?.target_source_language_column_hint}

\n
\n
\n
\n
\n\n \n
\n

{$t.translate?.config?.llm_settings}

\n
\n
\n \n \n
\n
\n \n {#if targetLanguages.length === 0}\n

Select at least one target language

\n {/if}\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n
\n \n
\n\n \n
\n \n
\n
\n\n \n
\n

{$t.translate?.config?.terminology_dictionaries}

\n {#if availableDictionaries.length === 0}\n

\n {$t.translate?.config?.no_dictionaries}\n

\n {:else}\n
\n {#each availableDictionaries as dict}\n \n {/each}\n
\n {/if}\n
\n
\n\n
\n\n \n
\n
\n \n
\n
\n\n \n
\n {#if !isNewJob && existingJob}\n
\n

{$t.translate?.preview?.title}

\n \n
\n {:else}\n
\n

{$t.translate?.config?.save} {$t.translate?.config?.basic_info?.toLowerCase?.() || 'Save the job first'}

\n
\n {/if}\n
\n\n \n
\n {#if !isNewJob && existingJob}\n
\n

{$t.translate?.config?.run_translation}

\n\n \n
\n {$t.translate?.config?.status}:\n \n {status || 'DRAFT'}\n \n {#if status === 'DRAFT'}\n {\n try {\n await updateJob(jobId, { status: 'READY' });\n status = 'READY';\n addToast($t.translate?.config?.job_updated, 'success');\n } catch (e) {\n addToast(e?.message || 'Failed to update status', 'error');\n }\n }}\n class=\"ml-auto px-3 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors\"\n >\n Mark as READY\n \n {/if}\n
\n\n
\n \n
\n \n
\n
\n
\n \n \n \n
\n
\n

{$t.translate?.config?.run_incremental}

\n

{$t.translate?.config?.run_incremental_desc}

\n handleTriggerRun(false)}\n disabled={isRunning || status === 'DRAFT'}\n class=\"mt-3 px-5 py-1.5 text-sm font-medium bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isRunning && !isFullRun ? $t.translate?.config?.running : $t.translate?.config?.run_translation}\n \n
\n
\n
\n\n \n
\n
\n
\n \n \n \n
\n
\n

{$t.translate?.config?.run_full}

\n

{$t.translate?.config?.run_full_desc}

\n handleTriggerRun(true)}\n disabled={isRunning || status === 'DRAFT'}\n class=\"mt-3 px-5 py-1.5 text-sm font-medium bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isRunning && isFullRun ? $t.translate?.config?.running : $t.translate?.config?.full_translate}\n \n
\n
\n
\n
\n\n {#if runError}\n
\n

{runError}

\n
\n {/if}\n\n {#if currentRunId}\n \n {/if}\n\n {#if completedRuns.length > 0}\n
\n
\n

{$t.translate?.config?.recent_runs}

\n showPageBulkReplace = true}\n class=\"px-3 py-1.5 text-xs bg-indigo-600 text-white rounded hover:bg-indigo-700 transition-colors\"\n >\n {$t.translate?.run?.bulk_replace || 'Bulk Replace'}\n \n
\n
\n {#each completedRuns as run}\n \n {/each}\n
\n
\n {/if}\n
\n
\n {:else}\n
\n

{$t.translate?.config?.save} {$t.translate?.config?.basic_info?.toLowerCase?.() || 'Save the job first'}

\n
\n {/if}\n
\n {/if}\n
\n\n showPageBulkReplace = false}\n\tonApplied={(count) => { showPageBulkReplace = false; loadRunHistory(); }}\n/>\n\n" + "body": "\n\n\n * LLM provider selection, target language, prompt settings, and dictionary attachment.\n * @LAYER UI\n * @RELATION DEPENDS_ON -> [TranslateApi]\n * @RELATION DEPENDS_ON -> [api_module]\n * @PRE Route param id may be \"new\" for create mode or a valid job UUID for edit mode.\n * @POST Job is created or updated with all configuration persisted.\n *\n * @UX_STATE idle -> Initial state before loading\n * @UX_STATE loading -> Fetching job data (edit mode) or datasource metadata\n * @UX_STATE configured -> Form is ready with loaded data\n * @UX_STATE saving -> Save operation in progress\n * @UX_STATE validation_error -> Form validation failure\n * @UX_STATE datasource_unavailable -> Selected datasource cannot be reached\n *\n * @UX_REACTIVITY columnList is $derived from selected datasource\n */\n\n import { onMount, onDestroy } from 'svelte';\n import { goto } from '$app/navigation';\n import { page } from '$app/state';\n import { t, _ } from '$lib/i18n';\n import { addToast } from '$lib/toasts.js';\n import { api } from '$lib/api.js';\n import {\n fetchJobs,\n createJob,\n updateJob,\n fetchDatasourceColumns,\n fetchDatasources,\n } from '$lib/api/translate.js';\n import MultiSelect from '$lib/components/ui/MultiSelect.svelte';\n import TranslationPreview from '$lib/components/translate/TranslationPreview.svelte';\n import TranslationRunProgress from '$lib/components/translate/TranslationRunProgress.svelte';\n import TranslationRunResult from '$lib/components/translate/TranslationRunResult.svelte';\n import ScheduleConfig from '$lib/components/translate/ScheduleConfig.svelte';\n\timport { triggerRun, fetchRunHistory, cancelRun } from '$lib/api/translate.js';\n\timport BulkReplaceModal from '$lib/components/translate/BulkReplaceModal.svelte';\n\timport { fromStore } from 'svelte/store';\n\timport {\n\t\ttranslationRunStore,\n\t\tstartTranslationRun,\n\t\tresetTranslationRun,\n\t\tclearOnCompleteCallback,\n\t} from '$lib/stores/translationRun.js';\n\n const LANGUAGE_LABELS = {\n ru: 'Русский',\n en: 'English',\n de: 'Deutsch',\n fr: 'Français',\n es: 'Español',\n it: 'Italiano',\n pt: 'Português',\n zh: '中文',\n ja: '日本語',\n ko: '한국어',\n ar: 'العربية',\n tr: 'Türkçe',\n nl: 'Nederlands',\n pl: 'Polski',\n sv: 'Svenska',\n da: 'Dansk',\n fi: 'Suomi',\n cs: 'Čeština',\n hu: 'Magyar',\n ro: 'Română',\n vi: 'Tiếng Việt',\n th: 'ไทย',\n he: 'עברית',\n id: 'Bahasa Indonesia',\n ms: 'Bahasa Melayu',\n };\n\n const LANGUAGES = Object.entries(LANGUAGE_LABELS).map(([code, name]) => ({ code, name }));\n\n /** @type {string} idle | loading | configured | saving | validation_error | datasource_unavailable */\n let uxState = $state('idle');\n let isNewJob = $derived(page.params.id === 'new');\n let jobId = $derived(page.params.id);\n let existingJob = $state(null);\n let error = $state(null);\n\n // Form state\n let name = $state('');\n let description = $state('');\n let sourceDialect = $state('postgresql');\n let targetDialect = $state('postgresql');\n let sourceDatasourceId = $state('');\n let sourceTable = $state('');\n let targetSchema = $state('');\n let targetTable = $state('');\n let sourceKeyCols = $state([]);\n let targetKeyCols = $state([]);\n let translationColumn = $state('');\n let targetColumn = $state('');\n let targetLanguageColumn = $state('');\n let targetSourceColumn = $state('');\n let targetSourceLanguageColumn = $state('');\n let contextColumns = $state([]);\n let targetLanguages = $state(['en']);\n let includeSourceReference = $state(true);\n let providerId = $state('');\n let batchSize = $state(50);\n let disableReasoning = $state(false);\n let upsertStrategy = $state('MERGE');\n let dictionaryIds = $state([]);\n let status = $state('DRAFT');\n\n // Datasource & env state\n let environmentId = $state('');\n let environments = $state([]);\n let datasourceId = $state('');\n let datasourceSearch = $state('');\n let datasourceList = $state([]);\n let datasourceLoading = $state(false);\n let showDatasourceDropdown = $state(false);\n let availableColumns = $state([]);\n let virtualColumns = $state([]);\n let databaseDialect = $state('');\n let isColumnsLoading = $state(false);\n let llmProviders = $state([]);\n let availableDictionaries = $state([]);\n let targetDatabaseId = $state('');\n let databases = $state([]);\n let databasesLoading = $state(false);\n\n // Tab state\n /** @type {'config'|'preview'|'run'|'schedule'} */\n let activeTab = $state('config');\n let tabs = $derived.by(() => {\n const base = [\n { id: 'config', label: $t.translate?.config?.basic_info || 'Configuration' },\n { id: 'preview', label: $t.translate?.preview?.title || 'Preview' },\n { id: 'run', label: $t.translate?.config?.run_translation || 'Run' },\n ];\n if (!isNewJob && existingJob) {\n base.push({ id: 'schedule', label: $t.translate?.schedule?.tab_label || 'Schedule' });\n }\n return base;\n });\n\n // Derived\n let columnList = $derived(availableColumns);\n let logicalColumns = $derived(availableColumns.filter(c => c.is_physical !== false));\n let virtualColumnNames = $derived(virtualColumns);\n\n let isSaving = $state(false);\n let validationErrors = $state({});\n let warnings = $state([]);\n let pageTitle = $derived.by(() => {\n if (isNewJob) return $t.translate?.config?.new_title || 'New Translation Job';\n const jobName = existingJob?.name || name;\n const base = $t.translate?.config?.edit_title || 'Edit Translation Job';\n return jobName ? `${jobName} | ${base}` : base;\n });\n\n // Run state — using global store to survive page navigation\n let runState = fromStore(translationRunStore);\n let currentRunId = $derived(runState.current?.runId || null);\n let completedRuns = $state([]);\n let isRunning = $state(false);\n let isFullRun = $state(false);\n\tlet runError = $state('');\n\tlet showPageBulkReplace = $state(false);\n\n async function handleTriggerRun(full = false) {\n isRunning = true;\n isFullRun = full;\n runError = '';\n try {\n const run = await triggerRun(jobId, full);\n startTranslationRun(run.id, { jobId, isFullRun: full, onComplete: handleRunComplete });\n addToast(full ? 'Полный перевод запущен (все строки)' : _('translate.config.run_started'), 'success');\n } catch (err) {\n runError = err?.message || _('translate.config.run_failed');\n isRunning = false;\n }\n }\n\n function handleRunComplete(statusData) {\n if (!isRunning) return; // Idempotent: already handled\n isRunning = false;\n // Don't resetTranslationRun() here — the store's own polling detects the\n // terminal state and stops automatically, keeping the terminal UX visible\n // in the global indicator (TranslationRunGlobalIndicator) until auto-dismiss.\n loadRunHistory();\n const statusLabel = statusData?.status || _('translate.run.completed');\n addToast(`${_('translate.run.run_id')} ${statusLabel.toLowerCase()}`, 'info');\n }\n\n async function loadRunHistory() {\n try {\n const data = await fetchRunHistory(jobId, { page_size: 5 });\n completedRuns = data?.items || [];\n } catch (_err) {\n completedRuns = [];\n }\n }\n\n async function handleRetryRun() {\n if (currentRunId) {\n try {\n await cancelRun(currentRunId);\n } catch (_e) { /* ignore */ }\n }\n resetTranslationRun();\n await handleTriggerRun();\n }\n\n async function handleRetryInsert() {\n if (currentRunId) {\n try {\n const result = await api.postApi(`/translate/runs/${currentRunId}/retry-insert`, {});\n addToast(_('translate.config.insert_retry_started'), 'success');\n } catch (err) {\n addToast(err?.message || _('translate.config.insert_retry_failed'), 'error');\n }\n }\n }\n\n onMount(async () => {\n // Load environments first\n try {\n const envs = await api.getEnvironments?.() || [];\n environments = Array.isArray(envs) ? envs : [];\n if (environments.length > 0 && !environmentId) {\n environmentId = environments[0]?.id || '';\n }\n } catch (_e) {}\n\n await loadInitialData();\n if (!isNewJob) {\n await loadJob();\n } else {\n if (environmentId) {\n await loadDatabases();\n }\n uxState = 'configured';\n }\n });\n\n // Clean up store callback when page unmounts — the store keeps\n // polling so the global indicator in the layout still works.\n onDestroy(() => {\n clearOnCompleteCallback();\n });\n\n /** @returns {Promise} */\n async function loadInitialData() {\n try {\n // Load LLM providers\n try {\n const consolidated = await api.getConsolidatedSettings?.() || {};\n llmProviders = consolidated.llm_providers || [];\n // Also try settings endpoint\n if (llmProviders.length === 0 && api.getLlmStatus) {\n const llmStatus = await api.getLlmStatus().catch(() => ({}));\n if (llmStatus?.providers) {\n llmProviders = llmStatus.providers;\n }\n }\n } catch (_e) {\n llmProviders = [];\n }\n\n // Load dictionaries (from available translate dictionaries)\n try {\n const result = await api.fetchApi('/translate/dictionaries').catch(() => ({}));\n availableDictionaries = Array.isArray(result) ? result : (result?.items || []);\n } catch (_e) {\n availableDictionaries = [];\n }\n } catch (err) {\n console.warn('[translate/config] Failed to load some initial data:', err);\n }\n }\n\n /** @returns {Promise} */\n async function loadJob() {\n uxState = 'loading';\n try {\n const jobs = await fetchJobs({ page_size: 100 });\n const jobsArr = Array.isArray(jobs) ? jobs : (jobs?.results || []);\n const job = jobsArr.find(j => j.id === jobId);\n if (!job) {\n // Try direct fetch\n try {\n const directJob = await api.fetchApi(`/translate/jobs/${jobId}`);\n existingJob = directJob;\n } catch (_e) {\n error = _('translate.config.job_not_found').replace('{id}', jobId);\n uxState = 'error';\n return;\n }\n } else {\n existingJob = job;\n }\n\n // Populate form\n const j = existingJob;\n name = j.name || '';\n description = j.description || '';\n sourceDialect = j.source_dialect || 'postgresql';\n targetDialect = j.target_dialect || 'postgresql';\n sourceDatasourceId = j.source_datasource_id || '';\n sourceTable = j.source_table || '';\n targetSchema = j.target_schema || '';\n targetTable = j.target_table || '';\n sourceKeyCols = j.source_key_cols || [];\n targetKeyCols = j.target_key_cols || [];\n translationColumn = j.translation_column || '';\n targetColumn = j.target_column || '';\n targetLanguageColumn = j.target_language_column || '';\n targetSourceColumn = j.target_source_column || '';\n targetSourceLanguageColumn = j.target_source_language_column || '';\n contextColumns = j.context_columns || [];\n targetLanguages = j.target_languages?.length > 0 ? j.target_languages : (j.target_language ? [j.target_language] : ['en']);\n providerId = j.provider_id || '';\n batchSize = j.batch_size || 50;\n disableReasoning = j.disable_reasoning || false;\n upsertStrategy = j.upsert_strategy || 'MERGE';\n dictionaryIds = j.dictionary_ids || [];\n status = j.status || 'DRAFT';\n\n // Restore target database\n if (j.target_database_id) {\n targetDatabaseId = j.target_database_id;\n }\n\n // Load databases for the environment\n if (j.environment_id) {\n await loadDatabases();\n }\n\n // If there's a datasource, load its columns\n if (j.source_datasource_id && environments.length > 0) {\n datasourceId = j.source_datasource_id;\n // Restore the search display from saved table info\n if (j.source_table) {\n const dialect = j.database_dialect || '';\n datasourceSearch = dialect\n ? `${j.source_table} (${dialect})`\n : j.source_table;\n } else {\n datasourceSearch = `Dataset #${j.source_datasource_id}`;\n }\n // Restore environment from stored job, fall back to first available\n if (j.environment_id && environments.some(e => e.id === j.environment_id)) {\n environmentId = j.environment_id;\n } else {\n environmentId = environments[0]?.id || '';\n }\n await loadColumnsForDatasource();\n }\n\n uxState = 'configured';\n // Load run history\n await loadRunHistory();\n } catch (err) {\n error = err?.message || 'Failed to load job';\n uxState = 'error';\n }\n }\n\n /** @returns {Promise} */\n async function loadColumnsForDatasource() {\n if (!datasourceId || !environmentId) {\n availableColumns = [];\n virtualColumns = [];\n return;\n }\n\n isColumnsLoading = true;\n try {\n const result = await fetchDatasourceColumns(parseInt(datasourceId), environmentId);\n if (result) {\n availableColumns = result.columns || [];\n databaseDialect = result.database_dialect || '';\n virtualColumns = availableColumns.filter(c => c.is_physical === false).map(c => c.name);\n }\n } catch (err) {\n uxState = 'datasource_unavailable';\n addToast(`${_('common.error')}: ${err?.message}`, 'error');\n } finally {\n isColumnsLoading = false;\n }\n }\n\n /** Load all datasets (or search) */\n async function searchDatasources(searchTerm = '') {\n if (!environmentId) return;\n datasourceLoading = true;\n try {\n datasourceList = await fetchDatasources(environmentId, searchTerm);\n showDatasourceDropdown = true;\n } catch (err) {\n datasourceList = [];\n } finally {\n datasourceLoading = false;\n }\n }\n\n async function handleDatasourceSearch() {\n await searchDatasources(datasourceSearch);\n }\n\n /** Select a dataset from the dropdown */\n function selectDatasource(ds) {\n datasourceId = String(ds.id);\n datasourceSearch = `${ds.table_name} (${ds.database_name} · ${ds.database_dialect})`;\n showDatasourceDropdown = false;\n availableColumns = [];\n virtualColumns = [];\n sourceKeyCols = [];\n targetKeyCols = [];\n translationColumn = '';\n targetColumn = '';\n targetLanguageColumn = '';\n targetSourceColumn = '';\n targetSourceLanguageColumn = '';\n contextColumns = [];\n databaseDialect = ds.database_dialect || '';\n if (datasourceId && environmentId) {\n loadColumnsForDatasource();\n }\n }\n\n /** @param {Event} event */\n async function handleDatasourceChange(event) {\n const val = event.target.value;\n datasourceId = val;\n availableColumns = [];\n virtualColumns = [];\n // Reset column selections when datasource changes\n sourceKeyCols = [];\n targetKeyCols = [];\n translationColumn = '';\n contextColumns = [];\n\n if (val && environmentId) {\n await loadColumnsForDatasource();\n }\n }\n\n async function loadDatabases() {\n if (!environmentId) {\n databases = [];\n return;\n }\n databasesLoading = true;\n try {\n databases = await api.getEnvironmentDatabases(environmentId) || [];\n } catch (_e) {\n databases = [];\n } finally {\n databasesLoading = false;\n }\n }\n\n /** @param {Event} event */\n async function handleEnvChange(event) {\n const newEnv = event.target.value;\n environmentId = newEnv;\n // Clear search state, but KEEP datasourceId if already loaded from job\n // — just reload columns with the new environment\n datasourceSearch = '';\n datasourceList = [];\n databaseDialect = '';\n targetDatabaseId = '';\n await loadDatabases();\n if (datasourceId) {\n await loadColumnsForDatasource();\n }\n }\n\n /** @param {string} col */\n function toggleContextColumn(col) {\n if (contextColumns.includes(col)) {\n contextColumns = contextColumns.filter(c => c !== col);\n } else {\n contextColumns = [...contextColumns, col];\n }\n }\n\n /** @param {string} col */\n function toggleSourceKeyCol(col) {\n const idx = sourceKeyCols.indexOf(col);\n if (idx >= 0) {\n sourceKeyCols = sourceKeyCols.filter((_, i) => i !== idx);\n // Also remove corresponding target key\n if (targetKeyCols[idx]) {\n targetKeyCols = targetKeyCols.filter((_, i) => i !== idx);\n }\n } else {\n sourceKeyCols = [...sourceKeyCols, col];\n targetKeyCols = [...targetKeyCols, ''];\n }\n }\n\n /** @param {number} idx @param {string} val */\n function updateTargetKeyCol(idx, val) {\n const updated = [...targetKeyCols];\n updated[idx] = val;\n targetKeyCols = updated;\n }\n\n /** @returns {string[]} */\n function validate() {\n const errs = {};\n const warns = [];\n\n if (!name.trim()) {\n errs.name = _('translate.config.name_required');\n }\n\n if (targetLanguages.length === 0) {\n errs.targetLanguages = 'At least one target language is required';\n }\n\n if (!translationColumn) {\n errs.translationColumn = _('translate.config.translation_column_required');\n }\n\n if (sourceKeyCols.length > 0 && sourceKeyCols.some((_, i) => !targetKeyCols[i])) {\n errs.targetKeyCols = _('translate.config.key_mapping_required');\n }\n\n // Warn about virtual key columns\n for (const col of sourceKeyCols) {\n if (virtualColumnNames.includes(col)) {\n warns.push(_('translate.config.virtual_column_warning').replace('{col}', col));\n }\n }\n\n // Check duplicate column mapping\n if (contextColumns.includes(translationColumn)) {\n warns.push(_('translate.config.redundant_column_warning'));\n }\n\n validationErrors = errs;\n warnings = warns;\n return Object.keys(errs);\n }\n\n /** @returns {Promise} */\n async function handleSave() {\n const errs = validate();\n if (errs.length > 0) {\n uxState = 'validation_error';\n return;\n }\n\n isSaving = true;\n uxState = 'saving';\n const payload = {\n name: name.trim(),\n description: description.trim() || null,\n source_dialect: sourceDialect,\n target_dialect: targetDialect,\n source_datasource_id: datasourceId || null,\n source_table: sourceTable || null,\n target_schema: targetSchema || null,\n target_table: targetTable || null,\n source_key_cols: sourceKeyCols,\n target_key_cols: targetKeyCols,\n translation_column: translationColumn || null,\n target_column: targetColumn || null,\n target_language_column: targetLanguageColumn || null,\n target_source_column: targetSourceColumn || null,\n target_source_language_column: targetSourceLanguageColumn || null,\n context_columns: contextColumns,\n target_languages: targetLanguages?.length > 0 ? targetLanguages : null,\n provider_id: providerId || null,\n batch_size: parseInt(batchSize) || 50,\n disable_reasoning: disableReasoning,\n upsert_strategy: upsertStrategy,\n dictionary_ids: dictionaryIds,\n database_dialect: databaseDialect || sourceDialect,\n environment_id: environmentId || null,\n target_database_id: targetDatabaseId || null,\n status: status,\n };\n\n try {\n if (isNewJob) {\n const created = await createJob(payload);\n addToast(_('translate.config.job_created'), 'success');\n goto(`/translate/${created.id}`);\n } else {\n await updateJob(jobId, payload);\n addToast(_('translate.config.job_updated'), 'success');\n uxState = 'configured';\n }\n } catch (err) {\n addToast(err?.message || _('translate.config.save_failed'), 'error');\n uxState = 'validation_error';\n } finally {\n isSaving = false;\n }\n }\n\n /** @param {string} col */\n function isVirtual(col) {\n return virtualColumnNames.includes(col);\n }\n\n function getJobStatusLabel(value) {\n const labels = {\n DRAFT: $t.translate?.config?.status_draft || 'Draft',\n READY: $t.translate?.config?.status_ready || 'Ready',\n ACTIVE: $t.translate?.config?.status_active || 'Active',\n RUNNING: $t.translate?.jobs?.status_running || 'Running',\n COMPLETED: $t.translate?.jobs?.status_completed || 'Completed',\n FAILED: $t.translate?.jobs?.status_failed || 'Failed',\n CANCELLED: $t.translate?.history?.status_cancelled || 'Cancelled',\n };\n return labels[value] || value || ($t.translate?.config?.status_draft || 'Draft');\n }\n\n\n\n {pageTitle}\n\n\n
\n \n
\n
\n

\n {isNewJob ? $t.translate?.config?.new_title : $t.translate?.config?.edit_title}\n

\n {#if !isNewJob && existingJob}\n

{$t.translate?.config?.id_label.replace('{id}', existingJob.id)}

\n {/if}\n
\n
\n goto('/translate')}\n class=\"px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors\"\n >\n {$t.translate?.config?.cancel}\n \n \n {isSaving ? $t.translate?.config?.saving : $t.translate?.config?.save}\n \n
\n
\n\n \n {#if warnings.length > 0}\n
\n

{$t.translate?.config?.warnings_title}

\n
    \n {#each warnings as w}\n
  • {w}
  • \n {/each}\n
\n
\n {/if}\n\n \n {#if uxState === 'loading'}\n
\n {#each Array(6) as _}\n
\n {/each}\n
\n\n \n {:else if uxState === 'error'}\n
\n

{error || $t.translate?.common?.unknown_error}

\n goto('/translate')}\n class=\"px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors\"\n >\n {$t.translate?.config?.back_to_jobs}\n \n
\n\n \n {:else if uxState === 'datasource_unavailable'}\n
\n

\n {$t.translate?.config?.datasource_unavailable}\n

\n
\n\n \n {:else if uxState === 'configured' || uxState === 'saving' || uxState === 'validation_error' || uxState === 'idle'}\n \n
\n \n
\n\n \n
\n
\n \n
\n

{$t.translate?.config?.basic_info}

\n
\n
\n \n \n {#if validationErrors.name}\n

{validationErrors.name}

\n {/if}\n
\n
\n \n \n
\n
\n
\n\n \n
\n

{$t.translate?.config?.datasource}

\n
\n
\n \n \n \n {#each environments as env}\n \n {/each}\n \n
\n
\n \n searchDatasources('')}\n onblur={() => setTimeout(() => (showDatasourceDropdown = false), 150)}\n class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"\n placeholder={$t.translate?.config?.datasource_search_placeholder || 'Search datasets...'}\n />\n {#if datasourceLoading}\n
\n {/if}\n {#if showDatasourceDropdown}\n
\n {#each datasourceList as ds}\n selectDatasource(ds)}\n class=\"w-full text-left px-3 py-2 text-sm hover:bg-blue-50 border-b border-gray-100 last:border-b-0\"\n >\n {ds.table_name}\n {ds.schema}\n {ds.database_name} · {ds.database_dialect}\n \n {/each}\n
\n {/if}\n
\n {#if databaseDialect}\n
\n \n {$t.translate?.config?.detected_dialect.replace('{dialect}', databaseDialect)}\n \n
\n {/if}\n
\n\n {#if isColumnsLoading}\n
\n {/if}\n\n {#if columnList.length > 0}\n
\n \n
\n {#each columnList as col}\n
\n {col.name}\n {#if col.type}\n ({col.type})\n {/if}\n {#if isVirtual(col.name)}\n {$t.translate?.config?.virtual}\n {/if}\n
\n {/each}\n
\n
\n {/if}\n
\n\n \n
\n

{$t.translate?.config?.column_mapping}

\n\n \n
\n \n \n \n {#each columnList as col}\n \n {/each}\n \n {#if validationErrors.translationColumn}\n

{validationErrors.translationColumn}

\n {/if}\n
\n\n \n
\n
\n \n \n
\n
\n \n \n
\n
\n\n \n
\n
\n \n {#if sourceKeyCols.length > 0}\n { sourceKeyCols = []; targetKeyCols = []; }}\n class=\"text-xs text-red-600 hover:text-red-700\"\n >\n {$t.translate?.config?.clear_all}\n \n {/if}\n
\n\n {#if columnList.length === 0}\n
\n {\n sourceKeyCols = [...sourceKeyCols, ''];\n targetKeyCols = [...targetKeyCols, ''];\n }}\n class=\"px-3 py-1 text-xs border border-dashed border-gray-300 rounded text-gray-500 hover:border-blue-300 hover:text-blue-600\"\n >\n {$t.translate?.config?.add_key_column}\n \n
\n {:else}\n
\n {#each columnList as col}\n toggleSourceKeyCol(col.name)}\n class=\"px-2.5 py-1 text-xs rounded-full border transition-colors\n {sourceKeyCols.includes(col.name)\n ? 'bg-blue-100 border-blue-300 text-blue-700'\n : 'bg-white border-gray-200 text-gray-600 hover:border-gray-300'}\"\n title={isVirtual(col.name) ? $t.translate?.config?.virtual_column_warning.replace('{col}', col.name) : ''}\n >\n {col.name}\n {#if isVirtual(col.name)}\n *\n {/if}\n \n {/each}\n
\n {/if}\n\n {#if sourceKeyCols.length > 0}\n
\n {#each sourceKeyCols as srcCol, idx}\n
\n \n \n updateTargetKeyCol(idx, e.target.value)}\n class=\"flex-1 px-3 py-1.5 border border-gray-300 rounded text-sm font-mono focus:ring-2 focus:ring-blue-500\"\n />\n {\n sourceKeyCols = sourceKeyCols.filter((_, i) => i !== idx);\n targetKeyCols = targetKeyCols.filter((_, i) => i !== idx);\n }}\n class=\"p-1 text-gray-400 hover:text-red-500\"\n >\n \n \n \n \n
\n {/each}\n
\n {/if}\n {#if validationErrors.targetKeyCols}\n

{validationErrors.targetKeyCols}

\n {/if}\n
\n\n \n
\n \n {#if columnList.length === 0}\n

{$t.translate?.config?.select_datasource_hint}

\n {:else}\n
\n {#each columnList as col}\n toggleContextColumn(col.name)}\n class=\"px-2.5 py-1 text-xs rounded-full border transition-colors\n {contextColumns.includes(col.name)\n ? 'bg-purple-100 border-purple-300 text-purple-700'\n : 'bg-white border-gray-200 text-gray-600 hover:border-gray-300'}\"\n >\n {col.name}\n {#if isVirtual(col.name)}\n *\n {/if}\n \n {/each}\n
\n {/if}\n
\n
\n\n \n
\n

{$t.translate?.config?.target_table_title}

\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n \n {#each databases as db}\n \n {/each}\n \n {#if databasesLoading}\n

{$t.translate?.config?.loading_databases}

\n {:else if !environmentId}\n

{$t.translate?.config?.select_environment}

\n {:else}\n

{$t.translate?.config?.target_database_hint}

\n {/if}\n
\n
\n\n \n
\n

{$t.translate?.config?.target_column_mapping_title || 'Target Column Mapping'}

\n

{$t.translate?.config?.target_column_mapping_description || 'Configure which columns receive translated data, language codes, source text, and detected source language during INSERT.'}

\n
\n
\n \n \n

{$t.translate?.config?.target_column_hint}

\n
\n
\n \n \n

{$t.translate?.config?.target_language_column_hint}

\n
\n
\n \n \n

{$t.translate?.config?.target_source_column_hint}

\n
\n
\n \n \n

{$t.translate?.config?.target_source_language_column_hint}

\n
\n
\n
\n
\n\n \n
\n

{$t.translate?.config?.llm_settings}

\n
\n
\n \n \n
\n
\n \n {#if targetLanguages.length === 0}\n

{$t.translate?.config?.target_language_required || 'Select at least one target language'}

\n {/if}\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n
\n \n
\n\n \n
\n \n
\n
\n\n \n
\n

{$t.translate?.config?.terminology_dictionaries}

\n {#if availableDictionaries.length === 0}\n

\n {$t.translate?.config?.no_dictionaries}\n

\n {:else}\n
\n {#each availableDictionaries as dict}\n \n {/each}\n
\n {/if}\n
\n
\n\n
\n\n \n
\n
\n \n
\n
\n\n \n
\n {#if !isNewJob && existingJob}\n
\n

{$t.translate?.preview?.title}

\n \n
\n {:else}\n
\n

{$t.translate?.config?.save_job_first || 'Save the job first'}

\n
\n {/if}\n
\n\n \n
\n {#if !isNewJob && existingJob}\n
\n

{$t.translate?.config?.run_translation}

\n\n \n
\n {$t.translate?.config?.status}:\n \n {getJobStatusLabel(status)}\n \n {#if status === 'DRAFT'}\n {\n try {\n await updateJob(jobId, { status: 'READY' });\n status = 'READY';\n addToast($t.translate?.config?.job_updated, 'success');\n } catch (e) {\n addToast(e?.message || 'Failed to update status', 'error');\n }\n }}\n class=\"ml-auto px-3 py-1 text-xs bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors\"\n >\n {$t.translate?.config?.mark_ready || 'Mark as READY'}\n \n {/if}\n
\n\n
\n \n
\n \n
\n
\n
\n \n \n \n
\n
\n

{$t.translate?.config?.run_incremental}

\n

{$t.translate?.config?.run_incremental_desc}

\n handleTriggerRun(false)}\n disabled={isRunning || status === 'DRAFT'}\n class=\"mt-3 px-5 py-1.5 text-sm font-medium bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isRunning && !isFullRun ? $t.translate?.config?.running : $t.translate?.config?.run_translation}\n \n
\n
\n
\n\n \n
\n
\n
\n \n \n \n
\n
\n

{$t.translate?.config?.run_full}

\n

{$t.translate?.config?.run_full_desc}

\n handleTriggerRun(true)}\n disabled={isRunning || status === 'DRAFT'}\n class=\"mt-3 px-5 py-1.5 text-sm font-medium bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isRunning && isFullRun ? $t.translate?.config?.running : $t.translate?.config?.full_translate}\n \n
\n
\n
\n
\n\n {#if runError}\n
\n

{runError}

\n
\n {/if}\n\n {#if currentRunId}\n \n {/if}\n\n {#if completedRuns.length > 0}\n
\n
\n

{$t.translate?.config?.recent_runs}

\n showPageBulkReplace = true}\n class=\"px-3 py-1.5 text-xs bg-indigo-600 text-white rounded hover:bg-indigo-700 transition-colors\"\n >\n {$t.translate?.run?.bulk_replace || 'Bulk Replace'}\n \n
\n
\n {#each completedRuns as run}\n \n {/each}\n
\n
\n {/if}\n
\n
\n {:else}\n
\n

{$t.translate?.config?.save_job_first || 'Save the job first'}

\n
\n {/if}\n
\n {/if}\n
\n\n showPageBulkReplace = false}\n\tonApplied={(count) => { showPageBulkReplace = false; loadRunHistory(); }}\n/>\n\n" }, { "contract_id": "TranslateHistoryPage", "contract_type": "Page", "file_path": "frontend/src/routes/translate/history/+page.svelte", "start_line": 1, - "end_line": 537, + "end_line": 560, "tier": "TIER_2", "complexity": 3, "metadata": { "COMPLEXITY": 3, - "LAYER": "UI", - "PURPOSE": "Filterable run history page with click-to-expand detail view, metrics summary.", - "UX_STATE": "pruned -> Data older than 90 days; MetricSnapshot referenced" + "PURPOSE": "Page component: routes/translate/history/+page.svelte", + "TYPE": "{'idle'|'loading'|'empty'|'populated'|'detail_open'|'pruned'}" }, - "relations": [ - { - "source_id": "TranslateHistoryPage", - "relation_type": "DEPENDS_ON", - "target_id": "TranslateApi", - "target_ref": "[TranslateApi]" - } - ], + "relations": [], "schema_warnings": [ { "code": "tag_not_for_contract_type", @@ -103500,28 +102230,6 @@ "contract_type": "Page" } }, - { - "code": "tag_not_for_contract_type", - "tag": "LAYER", - "message": "@LAYER is not allowed for contract type 'Page'", - "detail": { - "actual_type": "Page", - "allowed_types": [ - "Module", - "Skill", - "Agent" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "LAYER", - "message": "@LAYER is forbidden for contract type 'Page' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Page" - } - }, { "code": "tag_not_for_contract_type", "tag": "PURPOSE", @@ -103550,38 +102258,15 @@ } }, { - "code": "tag_not_for_contract_type", - "tag": "UX_STATE", - "message": "@UX_STATE is not allowed for contract type 'Page'", - "detail": { - "actual_type": "Page", - "allowed_types": [ - "Component" - ] - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "UX_STATE", - "message": "@UX_STATE is forbidden for contract type 'Page' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Page" - } - }, - { - "code": "tag_forbidden_by_complexity", - "tag": "RELATION", - "message": "@RELATION is forbidden for contract type 'Page' at C3", - "detail": { - "actual_complexity": 3, - "contract_type": "Page" - } + "code": "unknown_tag", + "tag": "TYPE", + "message": "@TYPE is not defined in axiom_config.yaml tags", + "detail": null } ], "anchor_syntax": "region", "tier_source": "AutoCalculated", - "body": "\n\n\n\n\n
\n\t
\n\t\t
\n\t\t\t

{$t.translate?.history?.title}

\n\t\t\t

{$t.translate?.history?.subtitle}

\n\t\t
\n\t\t\n\t
\n\n\t\n\t{#if totalMetric()}\n\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.history?.total_runs}

\n\t\t\t\t

{totalMetric().total_runs}

\n\t\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.history?.successful}

\n\t\t\t\t

{totalMetric().successful_runs}

\n\t\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.history?.failed}

\n\t\t\t\t

{totalMetric().failed_runs}

\n\t\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.history?.records}

\n\t\t\t\t

{totalMetric().total_records}

\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t\n\t
\n\t\t
\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t
\n\t
\n\n\t\n\t{#if isLoading}\n\t\t
\n\t\t\t{#each Array(5) as _}\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\n\t\n\t{:else if uxState === 'empty'}\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t

{$t.translate?.history?.no_runs}

\n\t\t\t

{$t.translate?.history?.no_runs_hint}

\n\t\t
\n\n\t\n\t{:else if uxState === 'populated' || uxState === 'detail_open'}\n\t\t
\n\t\t\t{#each runs as run}\n\t\t\t\t openDetail(run)}\n\t\t\t\t\tclass=\"bg-white border border-gray-200 rounded-lg p-3 hover:shadow-sm hover:border-gray-300 transition-all cursor-pointer\"\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{getJobName(run.job_id)}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{run.status}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{#if run.trigger_type}\n\t\t\t\t\t\t\t\t\t{run.trigger_type}\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{run.created_at ? new Date(run.created_at).toLocaleString() : ''}\n\t\t\t\t\t\t\t\t{_('translate.history.records_label').replace('{count}', run.total_records || 0)}\n\t\t\t\t\t\t\t\t{_('translate.history.ok_label').replace('{count}', run.successful_records || 0)}\n\t\t\t\t\t\t\t\t{_('translate.history.fail_label').replace('{count}', run.failed_records || 0)}\n\t\t\t\t\t\t\t\t{#if run.created_by}\n\t\t\t\t\t\t\t\t\t{_('translate.history.by_label').replace('{user}', run.created_by)}\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#if run.target_languages && run.target_languages.length > 0}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{#each run.target_languages as lang}\n\t\t\t\t\t\t\t\t\t\t{lang}\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
e.stopPropagation()}>\n\t\t\t\t\t\t\t{#if run.status === 'PENDING' || run.status === 'RUNNING'}\n\t\t\t\t\t\t\t\t handleCancelRun(run.id)}\n\t\t\t\t\t\t\t\t\tdisabled={cancellingRunId === run.id}\n\t\t\t\t\t\t\t\t\tclass=\"px-2 py-1 text-xs bg-red-100 text-red-700 rounded hover:bg-red-200 disabled:opacity-50 transition-colors\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{cancellingRunId === run.id\n\t\t\t\t\t\t\t\t\t\t? ($t.translate?.run?.cancelling || '...')\n\t\t\t\t\t\t\t\t\t\t: ($t.translate?.run?.cancel || 'Cancel')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if run.status === 'FAILED'}\n\t\t\t\t\t\t\t\t handleRetryRun(run.id)}\n\t\t\t\t\t\t\t\t\tdisabled={retryingRunId === run.id}\n\t\t\t\t\t\t\t\t\tclass=\"px-2 py-1 text-xs bg-amber-100 text-amber-700 rounded hover:bg-amber-200 disabled:opacity-50 transition-colors\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{retryingRunId === run.id\n\t\t\t\t\t\t\t\t\t\t? ($t.translate?.run?.retrying || '...')\n\t\t\t\t\t\t\t\t\t\t: ($t.translate?.run?.retry_failed || 'Retry')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if run.status === 'COMPLETED' && (run.skipped_records || 0) > 0}\n\t\t\t\t\t\t\t\t handleDownloadSkipped(run.id)}\n\t\t\t\t\t\t\t\t\tclass=\"text-xs text-blue-600 hover:text-blue-700\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{$t.translate?.history?.skipped_csv}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t

{_('translate.history.showing').replace('{count}', runs.length).replace('{total}', total)}

\n\t\t\t
\n\t\t\t\t { currentPage--; loadRuns(); }}\n\t\t\t\t\tdisabled={currentPage <= 1}\n\t\t\t\t\tclass=\"px-3 py-1.5 text-sm border border-gray-200 rounded-lg disabled:opacity-50\"\n\t\t\t\t>\n\t\t\t\t\t{$t.translate?.history?.previous}\n\t\t\t\t\n\t\t\t\t { currentPage++; loadRuns(); }}\n\t\t\t\t\tdisabled={currentPage * pageSize >= total}\n\t\t\t\t\tclass=\"px-3 py-1.5 text-sm border border-gray-200 rounded-lg disabled:opacity-50\"\n\t\t\t\t>\n\t\t\t\t\t{$t.translate?.history?.next}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t\n\t{#if uxState === 'detail_open' && selectedRunDetail}\n\t\t
\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.history?.run_detail}

\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.status}

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{selectedRunDetail.status}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.trigger}

\n\t\t\t\t\t\t

{selectedRunDetail.trigger_type || 'manual'}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.started}

\n\t\t\t\t\t\t

{selectedRunDetail.started_at ? new Date(selectedRunDetail.started_at).toLocaleString() : '-'}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.completed}

\n\t\t\t\t\t\t

{selectedRunDetail.completed_at ? new Date(selectedRunDetail.completed_at).toLocaleString() : '-'}

\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{selectedRunDetail.total_records || 0}

\n\t\t\t\t\t\t

{$t.translate?.history?.total}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{selectedRunDetail.successful_records || 0}

\n\t\t\t\t\t\t

{$t.translate?.history?.success}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{selectedRunDetail.failed_records || 0}

\n\t\t\t\t\t\t

{$t.translate?.history?.failed}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{selectedRunDetail.skipped_records || 0}

\n\t\t\t\t\t\t

{$t.translate?.history?.skipped}

\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.language_stats && Object.keys(selectedRunDetail.language_stats).length > 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.run?.per_language}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each Object.entries(selectedRunDetail.language_stats) as [lang, stats]}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{lang}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{_('translate.run.translated_label').replace('{count}', stats.translated_rows)}\n\t\t\t\t\t\t\t\t\t\t{_('translate.run.failed_label').replace('{count}', stats.failed_rows)}\n\t\t\t\t\t\t\t\t\t\t{_('translate.run.skipped_label').replace('{count}', stats.skipped_rows)}\n\t\t\t\t\t\t\t\t\t\t{_('translate.run.tokens_label').replace('{count}', stats.token_count)}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{#if selectedRunDetail.status === 'PENDING' || selectedRunDetail.status === 'RUNNING'}\n\t\t\t\t\t\t handleCancelRun(selectedRunDetail.id)}\n\t\t\t\t\t\t\tdisabled={cancellingRunId === selectedRunDetail.id}\n\t\t\t\t\t\t\tclass=\"px-4 py-2 text-sm bg-red-600 text-white rounded-lg hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{cancellingRunId === selectedRunDetail.id\n\t\t\t\t\t\t\t\t? ($t.translate?.run?.cancelling || 'Cancelling...')\n\t\t\t\t\t\t\t\t: ($t.translate?.run?.cancel || 'Cancel')}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t\t{#if selectedRunDetail.status === 'FAILED'}\n\t\t\t\t\t\t handleRetryRun(selectedRunDetail.id)}\n\t\t\t\t\t\t\tdisabled={retryingRunId === selectedRunDetail.id}\n\t\t\t\t\t\t\tclass=\"px-4 py-2 text-sm bg-amber-600 text-white rounded-lg hover:bg-amber-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{retryingRunId === selectedRunDetail.id\n\t\t\t\t\t\t\t\t? ($t.translate?.run?.retrying || 'Retrying...')\n\t\t\t\t\t\t\t\t: ($t.translate?.run?.retry_failed || 'Retry Failed')}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.config_snapshot}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.config_snapshot}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
{JSON.stringify(selectedRunDetail.config_snapshot, null, 2)}
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.config_hash || selectedRunDetail.dict_snapshot_hash}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.snapshots}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#if selectedRunDetail.config_hash}\n\t\t\t\t\t\t\t\t
Config: {selectedRunDetail.config_hash}
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if selectedRunDetail.dict_snapshot_hash}\n\t\t\t\t\t\t\t\t
Dict: {selectedRunDetail.dict_snapshot_hash}
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if selectedRunDetail.key_hash}\n\t\t\t\t\t\t\t\t
Key: {selectedRunDetail.key_hash}
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.events && selectedRunDetail.events.length > 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.events.replace('{count}', selectedRunDetail.events.length)}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each selectedRunDetail.events as evt}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{evt.event_type}\n\t\t\t\t\t\t\t\t\t|\n\t\t\t\t\t\t\t\t\t{evt.created_at ? new Date(evt.created_at).toLocaleString() : ''}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.error_message}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.error}

\n\t\t\t\t\t\t

{selectedRunDetail.error_message}

\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.superset_execution_id}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.superset_query_id}

\n\t\t\t\t\t\t

{selectedRunDetail.superset_execution_id}

\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\t\t
\n\t{/if}\n
\n\n" + "body": "\n\n\n\t */\n\timport { onMount } from 'svelte';\n\timport { addToast } from '$lib/toasts.js';\n\timport { t, _ } from '$lib/i18n';\n\timport {\n\t\tfetchAllRuns,\n\t\tfetchRunDetail,\n\t\tfetchAllMetrics,\n\t\tfetchJobs,\n\t\tdownloadSkippedCsv,\n\t\tcancelRun,\n\t\tretryFailedBatches\n\t} from '$lib/api/translate.js';\n\n\t/** @type {'idle'|'loading'|'empty'|'populated'|'detail_open'|'pruned'} */\n\tlet uxState = $state('idle');\n\n\tlet cancellingRunId = $state(null);\n\tlet retryingRunId = $state(null);\n\n\tlet runs = $state([]);\n\tlet total = $state(0);\n\tlet currentPage = $state(1);\n\tlet pageSize = $state(20);\n\tlet selectedRun = $state(null);\n\tlet selectedRunDetail = $state(null);\n\tlet isLoading = $state(false);\n\tlet metrics = $state([]);\n\tlet jobs = $state([]);\n\n\t/** Display-friendly language names keyed by BCP-47 code */\n\tconst LANGUAGE_NAMES = {\n\t\tru: 'Русский',\n\t\ten: 'English',\n\t\tde: 'Deutsch',\n\t\tfr: 'Français',\n\t\tes: 'Español',\n\t\tit: 'Italiano',\n\t\tpt: 'Português',\n\t\tnl: 'Nederlands',\n\t\tpl: 'Polski',\n\t\tcs: 'Čeština',\n\t\tda: 'Dansk',\n\t\tfi: 'Suomi',\n\t\tsv: 'Svenska',\n\t\tnb: 'Norsk Bokmål',\n\t\tuk: 'Українська',\n\t\tja: '日本語',\n\t\tko: '한국어',\n\t\tzh: '中文',\n\t\tar: 'العربية',\n\t\ttr: 'Türkçe',\n\t\tel: 'Ελληνικά',\n\t\tro: 'Română',\n\t\thu: 'Magyar',\n\t\tvi: 'Tiếng Việt',\n\t\tth: 'ไทย',\n\t\the: 'עברית',\n\t};\n\n\tfunction getLanguageName(code) {\n\t\tconst c = String(code);\n\t\treturn LANGUAGE_NAMES[c] || (c.match(/^[a-zA-Z]{2,8}/) ? c : c);\n\t}\n\n\t// Filters\n\tlet filterJobId = $state('');\n\tlet filterStatus = $state('');\n\tlet filterTrigger = $state('');\n\tlet filterDateFrom = $state('');\n\tlet filterDateTo = $state('');\n\n\tonMount(() => {\n\t\tloadRuns();\n\t\tloadMetrics();\n\t\tloadJobs();\n\t});\n\n\tasync function loadRuns() {\n\t\tisLoading = true;\n\t\tuxState = 'loading';\n\t\ttry {\n\t\t\tconst result = await fetchAllRuns({\n\t\t\t\tpage: currentPage,\n\t\t\t\tpage_size: pageSize,\n\t\t\t\tjob_id: filterJobId || undefined,\n\t\t\t\tstatus: filterStatus || undefined,\n\t\t\t\ttrigger_type: filterTrigger || undefined,\n\t\t\t});\n\t\t\truns = result?.items || [];\n\t\t\ttotal = result?.total || 0;\n\t\t\tuxState = runs.length === 0 ? 'empty' : 'populated';\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || _('translate.history.load_failed'), 'error');\n\t\t\tuxState = 'empty';\n\t\t} finally {\n\t\t\tisLoading = false;\n\t\t}\n\t}\n\n\tasync function loadMetrics() {\n\t\ttry {\n\t\t\tmetrics = await fetchAllMetrics();\n\t\t} catch (err) {\n\t\t\t// Non-critical\n\t\t}\n\t}\n\n\tasync function loadJobs() {\n\t\ttry {\n\t\t\tconst result = await fetchJobs({ page_size: 100 });\n\t\t\tjobs = Array.isArray(result) ? result : (result?.items || result?.results || []);\n\t\t} catch (err) {\n\t\t\t// Non-critical\n\t\t}\n\t}\n\n\tasync function openDetail(run) {\n\t\tselectedRun = run;\n\t\tselectedRunDetail = null;\n\t\ttry {\n\t\t\tselectedRunDetail = await fetchRunDetail(run.id);\n\t\t\tuxState = 'detail_open';\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || _('translate.history.load_detail_failed'), 'error');\n\t\t}\n\t}\n\n\tfunction closeDetail() {\n\t\tselectedRun = null;\n\t\tselectedRunDetail = null;\n\t\tuxState = runs.length === 0 ? 'empty' : 'populated';\n\t}\n\n\tfunction applyFilters() {\n\t\tcurrentPage = 1;\n\t\tloadRuns();\n\t}\n\n\tfunction getStatusClass(status) {\n\t\tconst map = {\n\t\t\tPENDING: 'bg-yellow-100 text-yellow-700',\n\t\t\tRUNNING: 'bg-blue-100 text-blue-700',\n\t\t\tCOMPLETED: 'bg-green-100 text-green-700',\n\t\t\tFAILED: 'bg-red-100 text-red-700',\n\t\t\tCANCELLED: 'bg-gray-100 text-gray-500',\n\t\t};\n\t\treturn map[status] || 'bg-gray-100 text-gray-700';\n\t}\n\n\tfunction getJobName(jobId) {\n\t\tconst job = jobs.find(j => j.id === jobId);\n\t\treturn job ? job.name : jobId?.substring(0, 8) + '...';\n\t}\n\n\tasync function handleCancelRun(runId) {\n\t\tcancellingRunId = runId;\n\t\ttry {\n\t\t\tawait cancelRun(runId);\n\t\t\taddToast($t.translate?.run?.run_cancelled || 'Run cancelled', 'success');\n\t\t\t// Refresh the detail panel and list\n\t\t\tif (selectedRunDetail?.id === runId) {\n\t\t\t\tselectedRunDetail = await fetchRunDetail(runId);\n\t\t\t}\n\t\t\tloadRuns();\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.run?.cancel_failed || 'Failed to cancel run', 'error');\n\t\t} finally {\n\t\t\tcancellingRunId = null;\n\t\t}\n\t}\n\n\tasync function handleRetryRun(runId) {\n\t\tretryingRunId = runId;\n\t\ttry {\n\t\t\tawait retryFailedBatches(runId);\n\t\t\taddToast($t.translate?.run?.retry_success || 'Retry started', 'success');\n\t\t\t// Refresh the detail panel and list\n\t\t\tif (selectedRunDetail?.id === runId) {\n\t\t\t\tselectedRunDetail = await fetchRunDetail(runId);\n\t\t\t}\n\t\t\tloadRuns();\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || $t.translate?.run?.retry_failed_msg || 'Retry failed', 'error');\n\t\t} finally {\n\t\t\tretryingRunId = null;\n\t\t}\n\t}\n\n\tasync function handleDownloadSkipped(runId) {\n\t\ttry {\n\t\t\tawait downloadSkippedCsv(runId);\n\t\t} catch (err) {\n\t\t\taddToast(err?.message || _('translate.history.download_failed'), 'error');\n\t\t}\n\t}\n\n\tfunction totalMetric() {\n\t\tif (!metrics.length) return null;\n\t\tconst agg = {\n\t\t\ttotal_runs: metrics.reduce((s, m) => s + (m.total_runs || 0), 0),\n\t\t\tsuccessful_runs: metrics.reduce((s, m) => s + (m.successful_runs || 0), 0),\n\t\t\tfailed_runs: metrics.reduce((s, m) => s + (m.failed_runs || 0), 0),\n\t\t\ttotal_records: metrics.reduce((s, m) => s + (m.total_records || 0), 0),\n\t\t};\n\t\treturn agg;\n\t}\n\n\n
\n\t
\n\t\t
\n\t\t\t

{$t.translate?.history?.title}

\n\t\t\t

{$t.translate?.history?.subtitle}

\n\t\t
\n\t\t\n\t
\n\n\t\n\t{#if totalMetric()}\n\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.history?.total_runs}

\n\t\t\t\t

{totalMetric().total_runs}

\n\t\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.history?.successful}

\n\t\t\t\t

{totalMetric().successful_runs}

\n\t\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.history?.failed}

\n\t\t\t\t

{totalMetric().failed_runs}

\n\t\t\t
\n\t\t\t
\n\t\t\t\t

{$t.translate?.history?.records}

\n\t\t\t\t

{totalMetric().total_records}

\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t\n\t
\n\t\t
\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t
\n\t
\n\n\t\n\t{#if isLoading}\n\t\t
\n\t\t\t{#each Array(5) as _}\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\n\t\n\t{:else if uxState === 'empty'}\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\t

{$t.translate?.history?.no_runs}

\n\t\t\t

{$t.translate?.history?.no_runs_hint}

\n\t\t
\n\n\t\n\t{:else if uxState === 'populated' || uxState === 'detail_open'}\n\t\t
\n\t\t\t{#each runs as run}\n\t\t\t\t openDetail(run)}\n\t\t\t\t\tclass=\"bg-white border border-gray-200 rounded-lg p-3 hover:shadow-sm hover:border-gray-300 transition-all cursor-pointer\"\n\t\t\t\t>\n\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{getJobName(run.job_id)}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{run.status}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{#if run.trigger_type}\n\t\t\t\t\t\t\t\t\t{run.trigger_type}\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t{run.created_at ? new Date(run.created_at).toLocaleString() : ''}\n\t\t\t\t\t\t\t\t{_('translate.history.records_label').replace('{count}', run.total_records || 0)}\n\t\t\t\t\t\t\t\t{_('translate.history.ok_label').replace('{count}', run.successful_records || 0)}\n\t\t\t\t\t\t\t\t{_('translate.history.fail_label').replace('{count}', run.failed_records || 0)}\n\t\t\t\t\t\t\t\t{#if run.created_by}\n\t\t\t\t\t\t\t\t\t{_('translate.history.by_label').replace('{user}', run.created_by)}\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{#if run.target_languages && run.target_languages.length > 0}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{#each run.target_languages as lang}\n\t\t\t\t\t\t\t\t\t\t{lang}\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
e.stopPropagation()}>\n\t\t\t\t\t\t\t{#if run.status === 'PENDING' || run.status === 'RUNNING'}\n\t\t\t\t\t\t\t\t handleCancelRun(run.id)}\n\t\t\t\t\t\t\t\t\tdisabled={cancellingRunId === run.id}\n\t\t\t\t\t\t\t\t\tclass=\"px-2 py-1 text-xs bg-red-100 text-red-700 rounded hover:bg-red-200 disabled:opacity-50 transition-colors\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{cancellingRunId === run.id\n\t\t\t\t\t\t\t\t\t\t? ($t.translate?.run?.cancelling || '...')\n\t\t\t\t\t\t\t\t\t\t: ($t.translate?.run?.cancel || 'Cancel')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if run.status === 'FAILED'}\n\t\t\t\t\t\t\t\t handleRetryRun(run.id)}\n\t\t\t\t\t\t\t\t\tdisabled={retryingRunId === run.id}\n\t\t\t\t\t\t\t\t\tclass=\"px-2 py-1 text-xs bg-amber-100 text-amber-700 rounded hover:bg-amber-200 disabled:opacity-50 transition-colors\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{retryingRunId === run.id\n\t\t\t\t\t\t\t\t\t\t? ($t.translate?.run?.retrying || '...')\n\t\t\t\t\t\t\t\t\t\t: ($t.translate?.run?.retry_failed || 'Retry')}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if run.status === 'COMPLETED' && (run.skipped_records || 0) > 0}\n\t\t\t\t\t\t\t\t handleDownloadSkipped(run.id)}\n\t\t\t\t\t\t\t\t\tclass=\"text-xs text-blue-600 hover:text-blue-700\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{$t.translate?.history?.skipped_csv}\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t{/each}\n\t\t
\n\n\t\t\n\t\t
\n\t\t\t

{_('translate.history.showing').replace('{count}', runs.length).replace('{total}', total)}

\n\t\t\t
\n\t\t\t\t { currentPage--; loadRuns(); }}\n\t\t\t\t\tdisabled={currentPage <= 1}\n\t\t\t\t\tclass=\"px-3 py-1.5 text-sm border border-gray-200 rounded-lg disabled:opacity-50\"\n\t\t\t\t>\n\t\t\t\t\t{$t.translate?.history?.previous}\n\t\t\t\t\n\t\t\t\t { currentPage++; loadRuns(); }}\n\t\t\t\t\tdisabled={currentPage * pageSize >= total}\n\t\t\t\t\tclass=\"px-3 py-1.5 text-sm border border-gray-200 rounded-lg disabled:opacity-50\"\n\t\t\t\t>\n\t\t\t\t\t{$t.translate?.history?.next}\n\t\t\t\t\n\t\t\t
\n\t\t
\n\t{/if}\n\n\t\n\t{#if uxState === 'detail_open' && selectedRunDetail}\n\t\t
\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{$t.translate?.history?.run_detail}

\n\t\t\t\t\t\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.status}

\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{selectedRunDetail.status}\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.trigger}

\n\t\t\t\t\t\t

{selectedRunDetail.trigger_type || 'manual'}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.started}

\n\t\t\t\t\t\t

{selectedRunDetail.started_at ? new Date(selectedRunDetail.started_at).toLocaleString() : '-'}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.completed}

\n\t\t\t\t\t\t

{selectedRunDetail.completed_at ? new Date(selectedRunDetail.completed_at).toLocaleString() : '-'}

\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{selectedRunDetail.total_records || 0}

\n\t\t\t\t\t\t

{$t.translate?.history?.total}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{selectedRunDetail.successful_records || 0}

\n\t\t\t\t\t\t

{$t.translate?.history?.success}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{selectedRunDetail.failed_records || 0}

\n\t\t\t\t\t\t

{$t.translate?.history?.failed}

\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t

{selectedRunDetail.skipped_records || 0}

\n\t\t\t\t\t\t

{$t.translate?.history?.skipped}

\n\t\t\t\t\t
\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.language_stats && (Array.isArray(selectedRunDetail.language_stats) ? selectedRunDetail.language_stats.length : Object.keys(selectedRunDetail.language_stats).length) > 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.run?.per_language}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each (Array.isArray(selectedRunDetail.language_stats) ? selectedRunDetail.language_stats : Object.entries(selectedRunDetail.language_stats).map(([code, s]) => ({ ...s, language_code: code }))) as stat}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{getLanguageName(stat.language_code)}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t\t{_('translate.run.translated_label').replace('{count}', stat.translated_rows)}\n\t\t\t\t\t\t\t\t\t\t{_('translate.run.failed_label').replace('{count}', stat.failed_rows)}\n\t\t\t\t\t\t\t\t\t\t{_('translate.run.skipped_label').replace('{count}', stat.skipped_rows)}\n\t\t\t\t\t\t\t\t\t\t{_('translate.run.tokens_label').replace('{count}', stat.token_count)}\n\t\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t
\n\t\t\t\t\t{#if selectedRunDetail.status === 'PENDING' || selectedRunDetail.status === 'RUNNING'}\n\t\t\t\t\t\t handleCancelRun(selectedRunDetail.id)}\n\t\t\t\t\t\t\tdisabled={cancellingRunId === selectedRunDetail.id}\n\t\t\t\t\t\t\tclass=\"px-4 py-2 text-sm bg-red-600 text-white rounded-lg hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{cancellingRunId === selectedRunDetail.id\n\t\t\t\t\t\t\t\t? ($t.translate?.run?.cancelling || 'Cancelling...')\n\t\t\t\t\t\t\t\t: ($t.translate?.run?.cancel || 'Cancel')}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t\t{#if selectedRunDetail.status === 'FAILED'}\n\t\t\t\t\t\t handleRetryRun(selectedRunDetail.id)}\n\t\t\t\t\t\t\tdisabled={retryingRunId === selectedRunDetail.id}\n\t\t\t\t\t\t\tclass=\"px-4 py-2 text-sm bg-amber-600 text-white rounded-lg hover:bg-amber-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{retryingRunId === selectedRunDetail.id\n\t\t\t\t\t\t\t\t? ($t.translate?.run?.retrying || 'Retrying...')\n\t\t\t\t\t\t\t\t: ($t.translate?.run?.retry_failed || 'Retry Failed')}\n\t\t\t\t\t\t\n\t\t\t\t\t{/if}\n\t\t\t\t
\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.config_snapshot}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.config_snapshot}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
{JSON.stringify(selectedRunDetail.config_snapshot, null, 2)}
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.config_hash || selectedRunDetail.dict_snapshot_hash}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.snapshots}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#if selectedRunDetail.config_hash}\n\t\t\t\t\t\t\t\t
Config: {selectedRunDetail.config_hash}
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if selectedRunDetail.dict_snapshot_hash}\n\t\t\t\t\t\t\t\t
Dict: {selectedRunDetail.dict_snapshot_hash}
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t{#if selectedRunDetail.key_hash}\n\t\t\t\t\t\t\t\t
Key: {selectedRunDetail.key_hash}
\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.events && selectedRunDetail.events.length > 0}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.events.replace('{count}', selectedRunDetail.events.length)}

\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{#each selectedRunDetail.events as evt}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\t{evt.event_type}\n\t\t\t\t\t\t\t\t\t|\n\t\t\t\t\t\t\t\t\t{evt.created_at ? new Date(evt.created_at).toLocaleString() : ''}\n\t\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.error_message}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.error}

\n\t\t\t\t\t\t

{selectedRunDetail.error_message}

\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\n\t\t\t\t\n\t\t\t\t{#if selectedRunDetail.superset_execution_id}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{$t.translate?.history?.superset_query_id}

\n\t\t\t\t\t\t

{selectedRunDetail.superset_execution_id}

\n\t\t\t\t\t
\n\t\t\t\t{/if}\n\t\t\t
\n\t\t
\n\t{/if}\n
\n\n" }, { "contract_id": "GitServiceContractTests", @@ -106675,6455 +105360,6456 @@ } ], "file_hashes": { - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_searchsorted.py": 11161416745923343741, - "venv/lib/python3.13/site-packages/pygments/lexers/javascript.py": 16869490851285457857, - "venv/lib/python3.13/site-packages/numpy/f2py/_backends/__init__.py": 4265012785873472600, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_mem_policy.py": 13856626567322091253, - "backend/src/api/routes/__tests__/test_assistant_api.py": 16438212076178324033, - "venv/lib/python3.13/site-packages/pandas/io/formats/info.py": 215303194287834907, - "venv/lib/python3.13/site-packages/keyring/util/__init__.py": 9635016046220094473, - "venv/lib/python3.13/site-packages/_pytest/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/__init__.py": 5933622200264572885, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_style.py": 14810883299259169406, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_promote.py": 16601872688404846307, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ndarray.pyi": 14369009813591437391, - "venv/lib/python3.13/site-packages/pip/_internal/utils/compatibility_tags.py": 15862649917149158291, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/getitem.py": 556339939822508169, - "venv/lib/python3.13/site-packages/anyio/streams/buffered.py": 8916188172110578587, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_slice.py": 9936085512921529417, - "backend/src/services/clean_release/report_builder.py": 6953311294119437848, - "venv/lib/python3.13/site-packages/pydantic/v1/typing.py": 13467331592583800299, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/__init__.py": 9884041726796476720, - "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/windows.py": 16452996247375488034, - "frontend/build/_app/immutable/chunks/CKlo2EnU.js": 1125213830095559337, - "frontend/static/favicon.svg": 6451919037497541980, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/caches/__init__.py": 11808051681178127650, - "venv/lib/python3.13/site-packages/_pytest/scope.py": 7603155109542903644, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/__init__.py": 2345997696115514124, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/__init__.py": 15130871412783076140, - "frontend/src/lib/ui/Button.svelte": 921807193049478213, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_formats.py": 13500155167624433622, - "venv/lib/python3.13/site-packages/pygments/formatters/html.py": 1918541918263449443, - "venv/lib/python3.13/site-packages/authlib/integrations/django_client/__init__.py": 14186136918454802843, - "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_xlsxwriter.py": 8445547874832015203, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_freq_attr.py": 17290447066681837965, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arraysetops.pyi": 1782641795267171928, - "venv/lib/python3.13/site-packages/numpy/core/records.py": 12604652690721760437, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_astype.py": 7542068949396266883, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/asyncmy.py": 11808138763443525443, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/util.py": 3591114320020964752, - "venv/lib/python3.13/site-packages/numpy/random/_examples/numba/extending.py": 16515485226251040454, - "venv/lib/python3.13/site-packages/pygments/lexers/cplint.py": 13990118342267278047, - "frontend/src/app.css": 3020720639702954139, - "venv/lib/python3.13/site-packages/psycopg2/errors.py": 10441360078612452560, - "backend/src/core/utils/matching.py": 12567367681677699487, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame.py": 16757570551941104401, - "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/LICENSE": 16510279186416777136, - "venv/lib/python3.13/site-packages/pycparser/c_ast.py": 5942107079119742, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_dict_compat.py": 18196543090455290455, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_indexing.py": 16623126293364224089, - "venv/lib/python3.13/site-packages/pandas/tests/api/test_api.py": 13385690673368400635, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_index_col.py": 17604191644943383567, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/context.py": 1041624619197012753, - "venv/lib/python3.13/site-packages/tzlocal-5.3.1.dist-info/RECORD": 15574202654585730126, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/__init__.py": 3673322722277252961, - "venv/lib/python3.13/site-packages/numpy/_typing/_nested_sequence.py": 7827259347937485408, - "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/RECORD": 16874983218652404911, - "venv/lib/python3.13/site-packages/pygments/styles/monokai.py": 12381683616720952636, - "frontend/src/lib/components/dataset-review/__tests__/source_intake_panel.ux.test.js": 13445861056598482363, - "venv/lib/python3.13/site-packages/pygments/lexers/amdgpu.py": 17249439604111838979, - "frontend/src/lib/components/layout/TaskDrawer.svelte": 6654829127438406248, - "backend/src/api/routes/__tests__/test_dataset_review_api.py": 17381354465400611694, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_period.py": 6255181868181182780, - "backend/src/services/clean_release/manifest_builder.py": 17216832960644499305, - "venv/lib/python3.13/site-packages/idna/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/smmap/buf.py": 18231775424238352734, - "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/httpx/_transports/default.py": 8391302827983907910, - "venv/lib/python3.13/site-packages/greenlet/TThreadStateCreator.hpp": 9887867766190459665, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8414/models.py": 3567900447243180991, - "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/utils.py": 2959395751217827103, - "venv/lib/python3.13/site-packages/pip/_internal/operations/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_internal/index/sources.py": 11609272011565024668, - "venv/lib/python3.13/site-packages/cryptography/hazmat/asn1/asn1.py": 8801155055979135092, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/base.py": 10200916670889920659, - "venv/lib/python3.13/site-packages/apscheduler/triggers/interval.py": 3964550092873794670, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/stride_tricks.pyi": 8257977532799807847, - "venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/WHEEL": 2357997949040430835, - "venv/lib/python3.13/site-packages/numpy/random/_pickle.pyi": 16081120364372391381, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/mod_derived_types.f90": 2556756676983488922, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_block_docstring.py": 18146459246837890489, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_offsets_properties.py": 2340780879864207500, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_console.py": 11657469008721907972, - "venv/lib/python3.13/site-packages/pygments/lexers/kuin.py": 2818054539831699366, - "frontend/src/lib/components/dataset-review/SourceIntakePanel.svelte": 10994864579319870671, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/LCSseq_py.py": 16054175754674285311, - "venv/lib/python3.13/site-packages/pygments/lexers/configs.py": 7712797939104760185, - "venv/lib/python3.13/site-packages/rsa/prime.py": 10820898885044917095, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/scope.py": 4096190764836489091, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_category.py": 3944898528119364040, - "frontend/src/routes/profile/__tests__/profile-preferences.integration.test.js": 6561454515118560091, - "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/licenses/LICENSE.BSD": 3858254215169937333, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_copy.py": 3114918520437707410, - "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_quantile.py": 11966156706220948315, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ma.py": 15912582628112164509, - "frontend/src/routes/translate/+page.svelte": 3906731431561226801, - "backend/src/core/utils/superset_context_extractor/_templates.py": 6790136732701012567, - "backend/src/core/task_manager/context.py": 17543992255093146942, - "frontend/src/lib/components/translate/TranslationRunProgress.svelte": 10281443795034845589, - "frontend/src/lib/components/layout/__tests__/test_sidebar.svelte.js": 1424894894434906249, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/_serialization.py": 2011519400081029270, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/assertions.py": 5556667947376168765, - "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/RECORD": 1164466882742493155, - "venv/lib/python3.13/site-packages/numpy/f2py/func2subr.pyi": 16573082220135508510, - "venv/lib/python3.13/site-packages/greenlet/tests/__init__.py": 1181423074160870921, - "venv/lib/python3.13/site-packages/annotated_types/__init__.py": 7367790549779882749, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/introspection.py": 5447179017910128771, - "venv/lib/python3.13/site-packages/starlette/status.py": 16192064443996706569, - "backend/src/services/clean_release/__tests__/test_report_builder.py": 17265459530244848426, - "venv/lib/python3.13/site-packages/pydantic/warnings.py": 5142352784799639092, - "venv/lib/python3.13/site-packages/pygments/styles/lovelace.py": 15082819162722685616, - "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/WHEEL": 5160964596297665692, - "venv/lib/python3.13/site-packages/httpx/_auth.py": 1283189552342958900, - "venv/lib/python3.13/site-packages/pygments/lexers/_luau_builtins.py": 13690910306072616818, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/constant_time.py": 2499026008403328245, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_shift.py": 14681514853406253657, - "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/licenses/LICENSE": 3868190977070717994, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/arrayprint.pyi": 2424868797160722318, - "frontend/eslint.config.js": 14296985543214702398, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_constructors.py": 17392287676196409733, - "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py": 9627328185578406121, - "venv/lib/python3.13/site-packages/pip/_vendor/tomli/_parser.py": 3540203683732744499, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/emoji.py": 9166774203918731896, - "venv/lib/python3.13/site-packages/apscheduler/jobstores/zookeeper.py": 4318643109309626098, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/test_calendar.py": 12313641252472031631, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/roles.py": 5448455900674232810, - "venv/lib/python3.13/site-packages/PIL/_webp.cpython-313-x86_64-linux-gnu.so": 11070012119632935103, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_clip.py": 11111183698178681331, - "venv/lib/python3.13/site-packages/sqlalchemy/util/__init__.py": 15821192733097665649, - "venv/lib/python3.13/site-packages/jsonschema/__init__.py": 6925762200139836824, - "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/RECORD": 17594742868855466393, - "venv/lib/python3.13/site-packages/pandas/_libs/window/aggregations.pyi": 6072607880689125113, - "frontend/src/lib/components/assistant/AssistantChatPanel.svelte": 15111082609123137207, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_printing.py": 1901396869745313303, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_format.py": 9331389900588353667, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_drop.py": 15751236355035730554, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_windows.py": 13341168721223961872, - "venv/lib/python3.13/site-packages/dateutil/tz/_common.py": 9009072600471223066, - "backend/tests/core/migration/test_archive_parser.py": 1909788158234142574, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_find_common_type.py": 9581516171321396333, - "venv/lib/python3.13/site-packages/pandas/io/json/__init__.py": 12333505155070172626, - "venv/lib/python3.13/site-packages/pip/_internal/operations/build/metadata_editable.py": 12494407279914732338, - "backend/src/plugins/translate/scheduler.py": 5240440208871506497, - "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/limited_api_latest.c": 5472633890472031814, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_set_name.py": 9094608008538024609, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_datetime64.py": 16272266806084058743, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Jaro.pyi": 3145980717589050713, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py": 15294662527620994511, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/starlette/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_infer_dtype.py": 1647214604678171565, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/period.cpython-313-x86_64-linux-gnu.so": 14468234464367691795, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_pct_change.py": 10313980265441433173, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_concatenate_chunks.py": 9693420220003544490, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Prefix_py.py": 17041764258638228993, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_cumulative.py": 16283954014170928273, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/screen.py": 6933591906297023845, - "venv/lib/python3.13/site-packages/PIL/XpmImagePlugin.py": 6644626557482464312, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_util.py": 16024147572807865219, - "frontend/src/components/git/CommitModal.svelte": 2248630732087966961, - "venv/lib/python3.13/site-packages/anyio/_core/_subprocesses.py": 7271614043537119143, - "venv/lib/python3.13/site-packages/pandas/tests/apply/test_str.py": 149688376805129532, - "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/METADATA": 2810323639884879396, - "venv/lib/python3.13/site-packages/pandas/util/_exceptions.py": 14426860079366867683, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_format.py": 16150524965028369071, - "venv/lib/python3.13/site-packages/rsa/asn1.py": 3861968161559322784, - "frontend/src/pages/Settings.svelte": 5110524159352803966, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_nunique.py": 8450574751331186943, - "frontend/build/_app/immutable/nodes/32.CA42qfpo.js": 6728008632143803945, - "venv/lib/python3.13/site-packages/pip/_internal/utils/entrypoints.py": 11268059714209889385, - "venv/lib/python3.13/site-packages/pandas/core/groupby/categorical.py": 16588862198627920420, - "backend/conftest.py": 16521922883434363388, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_alpha_unix.h": 2759424570606117942, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/claims.py": 14794441479740527265, - "venv/lib/python3.13/site-packages/typing_inspection/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc_unix.h": 9608771997528633681, - "venv/lib/python3.13/site-packages/jaraco/classes/ancestry.py": 11742194518360056470, - "venv/lib/python3.13/site-packages/websockets/asyncio/async_timeout.py": 7267209982032642028, - "venv/lib/python3.13/site-packages/attrs/setters.py": 5775359015245600592, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Postfix_py.py": 6653895521961110080, - "venv/lib/python3.13/site-packages/jose/backends/__init__.py": 16581763658239070112, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi": 1626154870044880426, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/array_constructors.py": 6727209833819502644, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_to_frame.py": 4303613319747945709, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_math.h": 16701045088211015518, - "venv/lib/python3.13/site-packages/numpy/_core/_methods.pyi": 9155045948490225716, - "backend/src/plugins/translate/__tests__/test_dictionary.py": 17973099740734727535, - "venv/lib/python3.13/site-packages/dateutil/rrule.py": 398469817784230512, - "backend/src/services/git/_merge.py": 5311748722639845465, - "venv/lib/python3.13/site-packages/jsonschema/tests/test_exceptions.py": 15290185665769490169, - "venv/lib/python3.13/site-packages/urllib3-2.6.2.dist-info/RECORD": 15194367076254806710, - "venv/lib/python3.13/site-packages/pandas/core/window/ewm.py": 12143982379243011357, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_win_type.py": 18105114803686556748, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/entities.py": 8001287806688585081, - "venv/lib/python3.13/site-packages/pydantic/_internal/_config.py": 12760390602912369447, - "venv/lib/python3.13/site-packages/referencing/tests/test_referencing_suite.py": 8875120876162738440, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/util.py": 955993052529739164, - "venv/lib/python3.13/site-packages/requests/sessions.py": 6936640856092320928, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/__init__.py": 8558541715982163200, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_year.py": 1478857009124807836, - "venv/lib/python3.13/site-packages/sqlalchemy/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/appengine.py": 16194997685865850655, - "venv/lib/python3.13/site-packages/pygments/lexers/monte.py": 15158829991357994573, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/_dml_constructors.py": 4497038002189188311, - "venv/lib/python3.13/site-packages/pandas/tests/tools/test_to_numeric.py": 18037815278545274402, - "venv/lib/python3.13/site-packages/fastapi/testclient.py": 2606236077497278662, - "venv/lib/python3.13/site-packages/pip/_internal/models/wheel.py": 10473287225032326206, - "backend/src/services/clean_release/exceptions.py": 14631509303697939633, - "backend/src/plugins/debug.py": 2035734953696955393, - "venv/lib/python3.13/site-packages/pydantic/__init__.py": 11536052293866174672, - "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/METADATA": 14608873187265321271, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_engines.py": 2790877023773269276, - "venv/lib/python3.13/site-packages/pip/_internal/cli/autocompletion.py": 7651919329635563739, - "venv/lib/python3.13/site-packages/numpy/_core/_exceptions.py": 5570375156851166818, - "venv/lib/python3.13/site-packages/numpy/doc/ufuncs.py": 11706048944901534664, - "venv/lib/python3.13/site-packages/numpy/_distributor_init.py": 1101690714183863290, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc_aix.h": 13316272821279308986, - "venv/lib/python3.13/site-packages/pandas/io/parsers/__init__.py": 11752833703934347122, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/plugin/plugin_base.py": 2880434680251108786, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/util.py": 2176177435997659667, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/cymysql.py": 5570881524647789741, - "venv/lib/python3.13/site-packages/pandas/core/indexing.py": 15507869313676722071, - "venv/lib/python3.13/site-packages/pyasn1/type/useful.py": 10313387611927801339, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_periodindex.py": 13430003945574795792, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_insert.py": 9195027887301180927, - "venv/lib/python3.13/site-packages/numpy/_array_api_info.py": 9709101823494959741, - "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/WHEEL": 16784970174376303810, - "venv/lib/python3.13/site-packages/numpy/_distributor_init.pyi": 1308368231471324581, - "venv/lib/python3.13/site-packages/passlib/tests/test_registry.py": 15377817946543064293, - "venv/lib/python3.13/site-packages/passlib/tests/sample1b.cfg": 2872366861156549016, - "venv/lib/python3.13/site-packages/pandas/tests/resample/test_resampler_grouper.py": 8388490036390951341, - "venv/lib/python3.13/site-packages/pygments/lexers/rego.py": 16022844238903541738, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/conftest.py": 9673372914396625461, - "venv/lib/python3.13/site-packages/pygments/lexers/_cl_builtins.py": 16540712925368297957, - "venv/lib/python3.13/site-packages/pygments/lexers/agile.py": 5686162551416231457, - "venv/lib/python3.13/site-packages/pygments/lexers/solidity.py": 5071425960622305629, - "venv/lib/python3.13/site-packages/packaging/specifiers.py": 2800449842990194437, - "backend/src/core/utils/superset_context_extractor/_filters.py": 281666313889333545, - "backend/src/plugins/search.py": 888817404897740233, - "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/WHEEL": 2357997949040430835, - "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/licenses/COPYING": 14459782230785388765, - "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/METADATA": 15970460108484113417, - "venv/lib/python3.13/site-packages/numpy/ma/LICENSE": 6723325434476453127, - "venv/lib/python3.13/site-packages/pip/_internal/cache.py": 210214424535803946, - "venv/lib/python3.13/site-packages/pydantic/experimental/pipeline.py": 9493054841465227788, - "venv/lib/python3.13/site-packages/numpy/tests/test_public_api.py": 11741922758622705449, - "venv/lib/python3.13/site-packages/pandas/tests/window/moments/test_moments_consistency_ewm.py": 12153690217678546798, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_range.py": 3544406211185340973, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_quoted_character.py": 2316632434571797367, - "venv/lib/python3.13/site-packages/referencing/exceptions.py": 2535322334430110414, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_join.py": 1697622393195784944, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_reduction.py": 2671872764983553906, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_concat.py": 1351478313334379336, - "venv/lib/python3.13/site-packages/starlette/types.py": 6372850128669191435, - "venv/lib/python3.13/site-packages/PIL/ImageMorph.py": 13861961228125789866, - "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js": 10530710876604536879, - "venv/lib/python3.13/site-packages/pandas/io/excel/_base.py": 1251545894005373734, - "venv/lib/python3.13/site-packages/uvicorn/lifespan/on.py": 2866195356090164296, - "backend/src/models/mapping.py": 9358348320550260098, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/events.py": 15099787454570172797, - "backend/tests/test_translate_scheduler_execution.py": 16687335504320865378, - "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_constructors.py": 979131987837682435, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_halfyear.py": 587199217232946404, - "venv/lib/python3.13/site-packages/jeepney/tests/test_bindgen.py": 8637878018232418583, - "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/WHEEL": 9788895026336324100, - "venv/lib/python3.13/site-packages/keyring/compat/properties.py": 1562771115138037410, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arcsinh.csv": 13695545125848146013, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_sorting.py": 8944017570338511677, - "venv/lib/python3.13/site-packages/iniconfig/_parse.py": 11418441008427032090, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_index.py": 4908727359687606870, - "venv/lib/python3.13/site-packages/h11/_receivebuffer.py": 4631433251046338381, - "venv/lib/python3.13/site-packages/pandas/tests/resample/test_resample_api.py": 3278209206670699265, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_halfyear.py": 9376272342364384767, - "venv/lib/python3.13/site-packages/_pytest/skipping.py": 10561585274103209115, - "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/METADATA": 6438256145634479451, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_align.py": 10284128457194204574, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/default_comparator.py": 47118577003622736, - "venv/lib/python3.13/site-packages/uvicorn/loops/asyncio.py": 16496916527760969173, - "venv/bin/pyrsa-sign": 15564653706771700451, - "venv/lib/python3.13/site-packages/numpy/_core/tests/_locales.py": 18178500267053825979, - "venv/lib/python3.13/site-packages/websockets/imports.py": 1995356703044158461, - "venv/lib/python3.13/site-packages/cryptography/x509/extensions.py": 10197248868682803019, - "venv/lib/python3.13/site-packages/anyio/_core/_resources.py": 8463932838064724564, - "venv/lib/python3.13/site-packages/pip/_vendor/distro/distro.py": 12494639376332486204, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_rename.py": 4890779493250245903, - "venv/lib/python3.13/site-packages/pandas/_config/localization.py": 11197430792343731762, - "venv/lib/python3.13/site-packages/apscheduler/executors/debug.py": 14072221218785494847, - "venv/lib/python3.13/site-packages/pycparser/c_lexer.py": 10964496587169353564, - "venv/lib/python3.13/site-packages/numpy/core/_dtype_ctypes.pyi": 15130871412783076140, - "venv/lib/python3.13/site-packages/charset_normalizer/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/RECORD": 12733654377842895588, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/_initialize.pyi": 6616985736007157309, - "venv/lib/python3.13/site-packages/pygments/lexers/esoteric.py": 16839499623020685146, - "backend/src/models/dataset_review.py": 5814500849969423218, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_day.py": 6371851881186970754, - "venv/lib/python3.13/site-packages/sqlalchemy/pool/base.py": 16951268065271931235, - "frontend/vitest.config.js": 142965001777137074, - "venv/lib/python3.13/site-packages/rpds/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/libs/test_join.py": 5982692266872978444, - "backend/src/api/routes/__tests__/test_reports_detail_api.py": 6960149894653710507, - "venv/lib/python3.13/site-packages/numpy/_core/numerictypes.pyi": 12144531601504755948, - "venv/lib/python3.13/site-packages/pandas/tests/io/sas/test_sas7bdat.py": 8963983384208121526, - "venv/lib/python3.13/site-packages/numpy/linalg/tests/test_deprecations.py": 14624516389510242394, - "backend/src/models/dataset_review_pkg/_finding_models.py": 17608383421420046491, - "venv/lib/python3.13/site-packages/numpy/lib/_iotools.pyi": 12873243737872788349, - "venv/lib/python3.13/site-packages/pandas/tests/reductions/__init__.py": 16150188643076000414, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_join.py": 11910166566689889389, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_pickle.py": 8705060420848529055, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_array_to_datetime.py": 3467480435568672840, - "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER": 17282701611721059870, - "frontend/src/lib/components/reports/__tests__/report_detail.integration.test.js": 4193615240297330385, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_pipe.py": 7400355977900310233, - "venv/lib/python3.13/site-packages/itsdangerous/timed.py": 12571102606719555007, - "venv/lib/python3.13/site-packages/pygments/lexers/markup.py": 10311057264960923433, - "venv/lib/python3.13/site-packages/pip/_internal/cli/req_command.py": 2887890799766730546, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/authorization_server.py": 2913800420669417152, - "venv/lib/python3.13/site-packages/pandas/core/array_algos/datetimelike_accumulations.py": 14719894187829729175, - "frontend/build/_app/immutable/nodes/14.DafKLV_n.js": 10724793506995782696, - "venv/lib/python3.13/site-packages/click-8.3.1.dist-info/RECORD": 3649834117451110628, - "venv/lib/python3.13/site-packages/websockets/uri.py": 264376168984862773, - "venv/lib/python3.13/site-packages/passlib/utils/handlers.py": 10182859005870689026, - "venv/lib/python3.13/site-packages/numpy/_core/fromnumeric.pyi": 13945478633722616250, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/tools.py": 6437315459200419827, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_rowcount.py": 6682800584070485197, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_numpy.py": 222383250675686477, - "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/LICENSE": 6774760218010069963, - "venv/lib/python3.13/site-packages/rsa/pkcs1.py": 2576732594642491910, - "venv/lib/python3.13/site-packages/yaml/constructor.py": 8731384179016992045, - "venv/lib/python3.13/site-packages/pycparser/c_parser.py": 3954138286651284327, - "venv/lib/python3.13/site-packages/pandas/tests/extension/array_with_attr/test_array_with_attr.py": 6568496539994758985, - "backend/src/plugins/translate/__tests__/test_orthogonal_fixes.py": 17679703384686060315, - "backend/alembic/env.py": 17839770100030276595, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_comparison.py": 4611207880323503098, - "venv/lib/python3.13/site-packages/pygments/lexers/igor.py": 6506221273085723501, - "frontend/src/lib/components/dataset-review/__tests__/validation_findings_panel.ux.test.js": 8994492423600483794, - "venv/lib/python3.13/site-packages/_pytest/fixtures.py": 1442767529726551116, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_indexing.py": 14552860427542050771, - "backend/src/api/routes/mappings.py": 7071183920129492341, - "venv/lib/python3.13/site-packages/jsonschema_specifications/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/provision.py": 9472538482404392519, - "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/WHEEL": 16097436423493754389, - "frontend/build/_app/immutable/entry/app.Cen278DP.js": 13502215328893449420, - "venv/lib/python3.13/site-packages/passlib/handlers/mssql.py": 2800599582708170195, - "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/METADATA": 5045041742469375466, - "venv/lib/python3.13/site-packages/PIL/DdsImagePlugin.py": 10646832373516499548, - "backend/src/core/superset_client/_dashboards_filters.py": 11002883972618719697, - "venv/lib/python3.13/site-packages/pygments/lexers/crystal.py": 3579547906957125755, - "frontend/build/_app/immutable/chunks/Vi81AIny.js": 9283030674196335690, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi": 13061638961405460334, - "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/backports/weakref_finalize.py": 7019253290755243568, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/gh26920/two_mods_with_no_public_entities.f90": 12296500845667555672, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libkrb5-fcafa220.so.3.3": 5620628698712971642, - "venv/lib/python3.13/site-packages/numpy/_typing/_shape.py": 12667553664122376205, - "backend/src/services/git/_remote_providers.py": 12661659133898682960, - "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/click/decorators.py": 2920877667855377423, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/dtype.py": 5553691086633215767, - "venv/lib/python3.13/site-packages/numpy/f2py/f90mod_rules.pyi": 18290764713161282980, - "venv/lib/python3.13/site-packages/_pytest/subtests.py": 14573790885130724739, - "venv/lib/python3.13/site-packages/pydantic/utils.py": 1533455264689378477, - "venv/lib/python3.13/site-packages/greenlet/__init__.py": 3447613556981242475, - "venv/lib/python3.13/site-packages/pip/_internal/models/pylock.py": 16697828625021519459, - "venv/lib/python3.13/site-packages/attr/converters.py": 5179615793507170642, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/memmap.pyi": 2834143910417841603, - "venv/lib/python3.13/site-packages/pandas/errors/cow.py": 14381282473643980776, - "venv/lib/python3.13/site-packages/numpy/conftest.py": 8971879716683736515, - "venv/lib/python3.13/site-packages/numpy/random/_sfc64.cpython-313-x86_64-linux-gnu.so": 17186695607612591922, - "venv/lib/python3.13/site-packages/pandas/tests/apply/test_numba.py": 203614377324678234, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7519/claims.py": 12645788445743898136, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_missing.py": 7034102519042351965, - "venv/lib/python3.13/site-packages/numpy/f2py/cb_rules.pyi": 12275242654430570028, - "venv/lib/python3.13/site-packages/urllib3/contrib/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/styles/inkpot.py": 10516710471319090846, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/wait.py": 6196757964348666082, - "venv/lib/python3.13/site-packages/annotated_doc/__init__.py": 6834198874252778425, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/dynamic.py": 15220116196072524330, - "frontend/src/lib/components/reports/__tests__/report_detail.ux.test.js": 11878832350632571462, - "frontend/build/_app/immutable/nodes/3.BXhpfKSB.js": 17728812616595273401, - "backend/src/core/__tests__/test_native_filters.py": 823253235973274872, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libcom_err-2abe824b.so.2.1": 12870528025628871295, - "venv/lib/python3.13/site-packages/numpy/linalg/tests/test_regression.py": 5652583095949741238, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pydantic/v1/networks.py": 13262073155118055511, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_memmap.py": 8709963808115492688, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ndarray_misc.pyi": 16219573290116396099, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/metrics_cpp.cpython-313-x86_64-linux-gnu.so": 10263790384087091349, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi": 3254633032236269842, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_to_dict.py": 16252265723192124609, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth1/resource_protector.py": 14354891358060085360, - "venv/lib/python3.13/site-packages/keyring/backends/fail.py": 5792496235026678021, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_client/integration.py": 12670848015915724967, - "venv/lib/python3.13/site-packages/starlette/middleware/wsgi.py": 12506262030845301606, - "venv/lib/python3.13/site-packages/apscheduler/jobstores/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/passlib/tests/test_utils_handlers.py": 556092537518149789, - "venv/lib/python3.13/site-packages/pandas/_libs/missing.pyi": 13543139288840944411, - "venv/lib/python3.13/site-packages/pandas/tests/io/xml/test_xml_dtypes.py": 18293138335799308439, - "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/_lint_dependency_groups.py": 2609365889369107154, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_apply.py": 7511915990531229598, - "venv/lib/python3.13/site-packages/httpx/_decoders.py": 14478643473006178492, - "venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py": 7301817934902711510, - "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/auto.py": 12818217385551437286, - "venv/lib/python3.13/site-packages/starlette/staticfiles.py": 11256974306202558529, - "venv/lib/python3.13/site-packages/keyring/devpi_client.py": 3445890176686211982, - "venv/lib/python3.13/site-packages/pandas/tests/apply/test_series_apply.py": 12349190935180702377, - "venv/lib/python3.13/site-packages/pandas/_libs/ops_dispatch.cpython-313-x86_64-linux-gnu.so": 5284769758475394095, - "venv/lib/python3.13/site-packages/numpy/ma/tests/test_deprecations.py": 8310244516175542839, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_ccalendar.py": 8819357623337533489, - "venv/lib/python3.13/site-packages/pip/py.typed": 10243553515949422671, - "venv/lib/python3.13/site-packages/websockets/protocol.py": 6401227903827084901, - "venv/lib/python3.13/site-packages/pyasn1/codec/der/decoder.py": 11876648884087997745, - "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/licenses/numpy/_core/src/highway/LICENSE": 15636145745140922094, - "frontend/build/_app/immutable/chunks/DoFYTHzk.js": 8080725323375789598, - "backend/src/core/superset_client/_charts.py": 8101226087578568224, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/numpyconfig.h": 13339221870252345205, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_read_fwf.py": 13684564756628524738, - "backend/src/api/routes/__tests__/test_assistant_authz.py": 8404416194210647280, - "backend/src/plugins/git/__init__.py": 2144831474780401625, - "backend/src/services/clean_release/repositories/policy_repository.py": 10990781278715170404, - "venv/lib/python3.13/site-packages/pandas/_testing/_hypothesis.py": 12298650110193262367, - "venv/lib/python3.13/site-packages/passlib/ext/django/utils.py": 8118896525895185626, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/pymssql.py": 6650008663791598065, - "venv/lib/python3.13/site-packages/pandas/_testing/asserters.py": 272063856832951430, - "backend/src/plugins/translate/service.py": 16088501346836746367, - "backend/src/core/mapping_service.py": 15428037653286485748, - "venv/lib/python3.13/site-packages/attr/exceptions.pyi": 12323493441583681402, - "backend/src/__init__.py": 11250920420388310655, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_tz_localize.py": 4937713043686953098, - "venv/lib/python3.13/site-packages/urllib3/fields.py": 9503202008440141421, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/__version__.py": 14138134075118474134, - "venv/lib/python3.13/site-packages/numpy/lib/tests/data/win64python2.npy": 11005857803794805939, - "venv/lib/python3.13/site-packages/numpy/f2py/rules.pyi": 1342380029484127983, - "venv/lib/python3.13/site-packages/numpy/polynomial/laguerre.pyi": 11310761736182213265, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_box_unbox.py": 7579587620530953711, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/characteristics.py": 2916810597149066609, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_indexing_slow.py": 12558235638488427077, - "venv/lib/python3.13/site-packages/pygments/lexers/web.py": 11104721221615804497, - "venv/lib/python3.13/site-packages/_pytest/compat.py": 10046464101087327473, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_both.f90": 13943066317159236257, - "venv/lib/python3.13/site-packages/pip/_internal/metadata/importlib/_envs.py": 5657151394155576509, - "venv/lib/python3.13/site-packages/pandas/core/nanops.py": 18291534943985310005, - "venv/lib/python3.13/site-packages/pandas/_libs/writers.cpython-313-x86_64-linux-gnu.so": 6878855127769203372, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timedeltas.pyi": 3783371827926657291, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/errors.py": 12777931960088171310, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_to_julian_date.py": 6868080396693831272, - "venv/bin/uvicorn": 3776670267111625708, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_as_unit.py": 8492383355919959393, - "venv/lib/python3.13/site-packages/pip/_vendor/tomli/_types.py": 7175518113263603341, - "backend/src/services/dataset_review/clarification_pkg/_helpers.py": 4822867945130807759, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/random/bitgen.h": 12421210767114721499, - "venv/lib/python3.13/site-packages/pandas/core/window/numba_.py": 1444123360986509769, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_join.py": 17874591841487372992, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_join.py": 1364886077926604427, - "frontend/src/lib/components/assistant/AssistantClarificationCard.svelte": 15872391111837089374, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/refresh_token.py": 17927903035152872556, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/align.py": 6078666505647454830, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_nep50_promotions.py": 6318578947214302182, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_equivalence.py": 17843623343419137458, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_formats.py": 1265319125235346745, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_compression.py": 8577756801858495168, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_attr_equal.py": 5612259834202470413, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_pickle.py": 6232650893735715362, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_eng_formatting.py": 14768815444701392013, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/style.py": 12654125988995362593, - "venv/lib/python3.13/site-packages/pygments/lexers/templates.py": 16267123844830647955, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/common.py": 16314033415206173235, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_integer.f90": 8308927438994838783, - "venv/lib/python3.13/site-packages/websockets/typing.py": 13268801935274185287, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/test_other.py": 783701524491896337, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/collections.cpython-313-x86_64-linux-gnu.so": 6157288238855118017, - "backend/src/core/migration/dry_run_orchestrator.py": 10939333902806918334, - "venv/lib/python3.13/site-packages/authlib/consts.py": 14226604841596736870, - "venv/lib/python3.13/site-packages/greenlet/tests/test_version.py": 6317733744899804292, - "venv/lib/python3.13/site-packages/numpy/ma/tests/test_mrecords.py": 15952147778503828707, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_hermite.py": 3628609433179387988, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_datetime.py": 7456744745424297814, - "venv/lib/python3.13/site-packages/requests/__init__.py": 2604491098990438229, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_tokenizer.py": 7994281991455916540, - "venv/lib/python3.13/site-packages/numpy/lib/_npyio_impl.py": 2215312866861772848, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/pyodbc.py": 11271266527234292020, - "venv/lib/python3.13/site-packages/pygments/lexers/_sourcemod_builtins.py": 11809503654078535014, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/types.py": 356973910046950951, - "venv/lib/python3.13/site-packages/pygments/lexers/forth.py": 17623561228839319663, - "venv/lib/python3.13/site-packages/httpcore/_models.py": 2501866419898326733, - "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/lexers/maxima.py": 11824576541091274641, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh17859.f": 13562390882286135024, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/loading.py": 3460142472461437916, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_array_ops.py": 6709502157317299163, - "venv/lib/python3.13/site-packages/anyio/_core/_sockets.py": 10525237827959090532, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_elffile.py": 5728213398707817049, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/useless_applicator_schemas.py": 8395157526634674143, - "venv/lib/python3.13/site-packages/pydantic/config.py": 1450073446492183677, - "venv/lib/python3.13/site-packages/numpy/_core/umath.py": 6132761780113682667, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_fiscal.py": 14652343698825214870, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/emath.pyi": 3718540478840842664, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_repeat.py": 18394463352314692642, - "venv/lib/python3.13/site-packages/pygments/lexers/clean.py": 17772169928835025327, - "scripts/generate_financial_arrears_data.py": 15989529185799374517, - "venv/lib/python3.13/site-packages/pip/_internal/utils/direct_url_helpers.py": 6075890513530296650, - "venv/lib/python3.13/site-packages/numpy/_pyinstaller/hook-numpy.py": 5221550291012182443, - "venv/lib/python3.13/site-packages/authlib/oauth2/client.py": 7237359250450222680, - "venv/lib/python3.13/site-packages/pygments/lexers/graphviz.py": 15677908982985025500, - "venv/lib/python3.13/site-packages/websockets/sync/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_odf.py": 15484940480924122966, - "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/METADATA": 13428590545328703138, - "venv/lib/python3.13/site-packages/bcrypt/_bcrypt.pyi": 15067467792538416274, - "venv/lib/python3.13/site-packages/numpy/_core/_add_newdocs_scalars.py": 652332294655046106, - "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/parsing.cpython-313-x86_64-linux-gnu.so": 13104135217735363438, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth1/authorization_server.py": 724388765673082720, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/conversion.pyi": 16976386782787920450, - "backend/src/plugins/translate/executor.py": 15432132330664319797, - "venv/lib/python3.13/site-packages/pandas/tests/io/generate_legacy_storage_files.py": 11015954144633661960, - "venv/lib/python3.13/site-packages/urllib3/contrib/socks.py": 645086578806871685, - "backend/src/plugins/translate/__tests__/test_inline_correction.py": 3246602656771855554, - "venv/lib/python3.13/site-packages/pygments/lexers/gsql.py": 15294109558028161389, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated": 11193150813314469394, - "venv/lib/python3.13/site-packages/pandas/tseries/frequencies.py": 2262381949755250516, - "venv/lib/python3.13/site-packages/keyring/cli.py": 10996746519742056101, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data": 11957232619107868023, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/highlighter.py": 10615901258492570740, - "venv/lib/python3.13/site-packages/pip/_internal/vcs/subversion.py": 7423946096970870727, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ufuncs.pyi": 14995898537484305307, - "venv/lib/python3.13/site-packages/starlette/middleware/cors.py": 11384893784314197864, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Postfix.py": 4440659352851541547, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/connection.py": 8190661510572400096, - "venv/lib/python3.13/site-packages/numpy/random/_generator.cpython-313-x86_64-linux-gnu.so": 15625019745021657670, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7515/jws.py": 7505219131537941130, - "venv/lib/python3.13/site-packages/fastapi/datastructures.py": 727178955746650271, - "venv/lib/python3.13/site-packages/starlette/_utils.py": 8384087194580626069, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/pager.py": 18072372651585577904, - "venv/lib/python3.13/site-packages/pandas/core/arrays/timedeltas.py": 7318851108932357510, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/revocation.py": 5718403706971163960, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_timedeltaindex.py": 2359681317403710563, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/__init__.py": 11709551233929312136, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_numba.py": 6409810497251251179, - "venv/lib/python3.13/site-packages/charset_normalizer/utils.py": 11724147997933584095, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/columns.py": 8488246354818721406, - "venv/lib/python3.13/site-packages/pygments/lexers/modeling.py": 15714077241620129689, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_snap.py": 4515844385819802784, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_os.h": 7883047506449405400, - "frontend/src/lib/components/dataset-review/ExecutionMappingReview.svelte": 11412197655386056417, - "backend/src/api/routes/translate/_router.py": 12128313085703966868, - "venv/lib/python3.13/site-packages/PIL/GbrImagePlugin.py": 7681313333602257802, - "venv/lib/python3.13/site-packages/numpy/_typing/_nbit_base.pyi": 6546751036506752550, - "venv/lib/python3.13/site-packages/numpy/random/_mt19937.pyi": 15904463104068500379, - "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi": 3893434234020223151, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_counting.py": 5628494712451120258, - "venv/lib/python3.13/site-packages/uvicorn/middleware/proxy_headers.py": 13206297306768491360, - "venv/lib/python3.13/site-packages/smmap/test/test_util.py": 5634593621291240094, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py": 6109201994963211857, - "venv/lib/python3.13/site-packages/apscheduler/schedulers/blocking.py": 3483388533957772681, - "venv/lib/python3.13/site-packages/numpy/lib/user_array.py": 14003456655595459585, - "venv/lib/python3.13/site-packages/click/parser.py": 13988544851350599869, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/secrets.py": 17816662030600756342, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/string.f": 17328443345813567449, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh24662.f90": 4241310200087270990, - "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/RECORD": 15865206372525596795, - "venv/lib/python3.13/site-packages/pygments/lexers/prql.py": 1159462562014166646, - "frontend/src/components/llm/ProviderConfig.svelte": 3397430691323614679, - "venv/lib/python3.13/site-packages/pydantic/_internal/_decorators.py": 9928398334052202841, - "venv/lib/python3.13/site-packages/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py": 14824388798676578409, - "venv/lib/python3.13/site-packages/iniconfig/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/httpx/_api.py": 6697370469329778523, - "venv/lib/python3.13/site-packages/gitdb/const.py": 14122142034887286559, - "venv/lib/python3.13/site-packages/numpy/random/_examples/cython/extending_distributions.pyx": 15302775787158991386, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-expm1.csv": 8710594949902856122, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/pg8000.py": 6580512354643849828, - "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/websockets_impl.py": 15673691977157706075, - "venv/lib/python3.13/site-packages/annotated_types/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/core": 10616484054735611337, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/padding.py": 11244071766694094554, - "venv/lib/python3.13/site-packages/pandas/core/flags.py": 4690844374637430013, - "venv/lib/python3.13/site-packages/pandas/tests/base/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/fixtures/orm.py": 7554417559838785150, - "venv/lib/python3.13/site-packages/pygments/lexers/qlik.py": 3881663935630350152, - "venv/lib/python3.13/site-packages/pygments/lexers/asc.py": 10744437946030875496, - "frontend/postcss.config.js": 5714274776976071678, - "venv/lib/python3.13/site-packages/h11/py.typed": 12358286306858197118, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_accessor.py": 10074496835594855786, - "backend/src/plugins/__init__.py": 17194112070692432889, - "venv/lib/python3.13/site-packages/uvicorn/protocols/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/useless_keywords.py": 11487954623214475915, - "venv/lib/python3.13/site-packages/passlib/ext/__init__.py": 15240312484046875203, - "venv/lib/python3.13/site-packages/pygments/lexers/vip.py": 11287963054551122004, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/contains.py": 3098087827310439993, - "venv/lib/python3.13/site-packages/dateutil/tz/_factories.py": 7538654189286887518, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_take.py": 9847808489301498487, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/jwt_bearer.py": 6973859422068490274, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/__main__.py": 487896753130729554, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_regression.py": 9375525053479844400, - "venv/lib/python3.13/site-packages/pandas/util/_doctools.py": 3766479022075611396, - "frontend/src/routes/dashboards/[id]/components/DashboardLinkedResources.svelte": 8279080994517688482, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_symbolic.py": 8093576026032854125, - "venv/lib/python3.13/site-packages/pygments/lexers/wowtoc.py": 17527844575999013592, - "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/licenses/LICENSE": 3868190977070717994, - "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/WHEEL": 16223367184831500348, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_pytables_missing.py": 12154582133989896069, - "venv/lib/python3.13/site-packages/git/compat.py": 16843345645628515492, - "frontend/build/_app/immutable/chunks/CMf6_6c5.js": 8239303945597047871, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/__init__.py": 5118993772178969104, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi": 4376871621615688626, - "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/licenses/LICENSE": 6653202620765194772, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_astype.py": 6578539240745332772, - "backend/tests/test_resource_hubs.py": 3990299394875930767, - "frontend/build/_app/immutable/chunks/Bvsi1riv.js": 1050921296731081209, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_complex/foo77.f": 7353666717928699883, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/segment.py": 2555584120510872966, - "venv/lib/python3.13/site-packages/sqlalchemy/connectors/aioodbc.py": 2018840826888846321, - "venv/lib/python3.13/site-packages/gitdb/test/test_base.py": 16305779366785148795, - "venv/lib/python3.13/site-packages/click/_winconsole.py": 8133229457213704188, - "venv/lib/python3.13/site-packages/_pytest/cacheprovider.py": 5114545353355728201, - "venv/lib/python3.13/site-packages/urllib3/response.py": 1987354357306756790, - "venv/lib/python3.13/site-packages/pydantic/deprecated/json.py": 17692639396786690144, - "venv/lib/python3.13/site-packages/numpy/lib/recfunctions.pyi": 10487441680587530350, - "venv/lib/python3.13/site-packages/pygments/lexers/promql.py": 8929925488296216854, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/exc.py": 3242780937124999391, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/types.py": 1343652083039949282, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/nested_sequence.pyi": 11245460325970663922, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_indexing.py": 14461606056665485421, - "venv/lib/python3.13/site-packages/ecdsa/der.py": 9374566189581725372, - "venv/lib/python3.13/site-packages/uvicorn/supervisors/basereload.py": 14479857245734482609, - "venv/lib/python3.13/site-packages/pandas/_libs/sas.cpython-313-x86_64-linux-gnu.so": 16309838137741124637, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_api.py": 14120915875574640262, - "venv/lib/python3.13/site-packages/starlette/middleware/sessions.py": 11719848399661632707, - "venv/lib/python3.13/site-packages/_pytest/_io/saferepr.py": 8025234471485188730, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/mod.pyi": 10591120373577657317, - "venv/lib/python3.13/site-packages/pandas/core/arrays/categorical.py": 15993211255162930859, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_string.py": 9224234539232195189, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/indexable.py": 4131796922597075758, - "backend/src/api/routes/dashboards/_projection.py": 1559143255780155174, - "backend/src/services/git/_status.py": 8680190871452171795, - "venv/lib/python3.13/site-packages/pygments/styles/_mapping.py": 11752660241391232497, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/test_as_unit.py": 4283520188709399244, - "frontend/build/_app/immutable/chunks/EgbRkp7y.js": 17762282902360655390, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/_initialize.py": 15511055314565873928, - "venv/lib/python3.13/site-packages/git/index/__init__.py": 7874728461616959441, - "venv/lib/python3.13/site-packages/numpy/_core/_simd.cpython-313-x86_64-linux-gnu.so": 12609666221355091416, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_deprecate_kwarg.py": 10417724544218303733, - "venv/lib/python3.13/site-packages/passlib/tests/test_hosts.py": 8284535274820610802, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/npyio.pyi": 15811156298637131744, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_umath.py": 13010939919217862164, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/__init__.py": 293004106962245584, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/AB.inc": 10441778083831997423, - "venv/lib/python3.13/site-packages/pycparser/__init__.py": 16499940957411260458, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/styled.py": 8726407298858811635, - "venv/lib/python3.13/site-packages/pandas/core/internals/ops.py": 4740509262379451243, - "venv/lib/python3.13/site-packages/smmap/test/test_tutorial.py": 16309370344201290582, - "venv/lib/python3.13/site-packages/uvicorn/logging.py": 12858188863836778374, - "backend/test_pat_api.py": 4206656943734821967, - "venv/lib/python3.13/site-packages/numpy/char/__init__.pyi": 12214207343146244743, - "venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/METADATA": 14158024963562115116, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/operators.f90": 5949265961908386974, - "frontend/build/_app/immutable/chunks/BD7asREZ.js": 16800371498949052968, - "frontend/build/_app/immutable/chunks/DezVU0o_.js": 8360472471203146195, - "venv/lib/python3.13/site-packages/itsdangerous/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/secretstorage/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_libsparse.py": 1859997684694949029, - "venv/lib/python3.13/site-packages/referencing/tests/test_jsonschema.py": 9815500901482570619, - "venv/lib/python3.13/site-packages/pygments/lexers/maple.py": 7402467573829618534, - "venv/lib/python3.13/site-packages/rapidfuzz/__init__.pyi": 13131938031654453415, - "venv/lib/python3.13/site-packages/rapidfuzz/fuzz_cpp_avx2.cpython-313-x86_64-linux-gnu.so": 12574929384355254457, - "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/oauth2_client.py": 6742098857947604729, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/style.py": 10665998898253460114, - "venv/lib/python3.13/site-packages/jeepney/io/blocking.py": 4699555965372200059, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/__init__.py": 13841777369501476946, - "venv/lib/python3.13/site-packages/pip/_internal/cli/status_codes.py": 9143625681947330756, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_non_compound.f90": 8048650253889213460, - "backend/src/core/utils/superset_context_extractor/_base.py": 15730081123519745401, - "venv/lib/python3.13/site-packages/apscheduler/triggers/cron/__init__.py": 14268033439436797364, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_arithmetic.py": 11620920548943067949, - "backend/src/services/__tests__/test_health_service.py": 5498183976797488518, - "venv/lib/python3.13/site-packages/uvicorn/supervisors/watchfilesreload.py": 13062440477475586751, - "venv/lib/python3.13/site-packages/sqlalchemy/events.py": 9329355486544822483, - "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py2-objarr.npy": 9694707264403109829, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/provision.py": 13156893203444936309, - "backend/src/core/task_manager/__tests__/test_context.py": 4211690952139950648, - "venv/lib/python3.13/site-packages/PIL/ImageMath.py": 7395449734007598797, - "venv/lib/python3.13/site-packages/pandas/io/formats/xml.py": 12235904458551067063, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_arraymethod.py": 14421188237635557797, - "venv/lib/python3.13/site-packages/numpy/_core/arrayprint.py": 1262543237476191714, - "venv/include/site/python3.13/greenlet/greenlet.h": 16216120538647882082, - "venv/lib/python3.13/site-packages/jose/constants.py": 10880650415673457194, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_finfo.py": 17174674209412768422, - "frontend/build/_app/immutable/chunks/CITo06Mq.js": 12229096590932140328, - "venv/lib/python3.13/site-packages/anyio/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_internal/utils/unpacking.py": 106471890743211548, - "venv/lib/python3.13/site-packages/jose/utils.py": 1926138853970725083, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/__init__.py": 15130871412783076140, - "backend/tests/test_models.py": 3998442321184404421, - "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/RECORD": 6187512110158566509, - "venv/lib/python3.13/site-packages/pygments/lexers/j.py": 9766594937623918053, - "frontend/src/lib/stores.js": 8062876368729352764, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/constants.pyi": 15633065380510755171, - "venv/lib/python3.13/site-packages/pip/_internal/operations/install/wheel.py": 3531093454660385247, - "venv/lib/python3.13/site-packages/pygments/scanner.py": 5640381644804445932, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_pivot_multilevel.py": 9830258607305392103, - "backend/src/api/routes/translate/__init__.py": 12084177602375001417, - "venv/lib/python3.13/site-packages/numpy/ma/testutils.pyi": 3899348061346041413, - "venv/lib/python3.13/site-packages/fastapi/_compat/may_v1.py": 9880083742927480960, - "venv/lib/python3.13/site-packages/pygments/styles/vim.py": 2621036947954523349, - "venv/lib/python3.13/site-packages/numpy/lib/format.py": 14971167445246135440, - "venv/lib/python3.13/site-packages/pygments/lexers/_stan_builtins.py": 1961875544235948177, - "frontend/src/lib/components/assistant/__tests__/assistant_confirmation.integration.test.js": 12582020574293031526, - "venv/lib/python3.13/site-packages/httpcore/_backends/sync.py": 576247220834179928, - "venv/lib/python3.13/site-packages/pygments/styles/native.py": 4559566444883341527, - "venv/lib/python3.13/site-packages/yaml/nodes.py": 3463622922914760653, - "venv/lib/python3.13/site-packages/h11/_state.py": 11398755254809751139, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/metrics_cpp_avx2.cpython-313-x86_64-linux-gnu.so": 2991745295485163924, - "venv/lib/python3.13/site-packages/psycopg2/_psycopg.cpython-313-x86_64-linux-gnu.so": 13471342289207537479, - "venv/lib/python3.13/site-packages/_pytest/raises.py": 8188402179490363767, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/lib_version.py": 10384171076976071398, - "venv/lib/python3.13/site-packages/pandas/core/sparse/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/nditer.py": 10038360623032425884, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/validator.py": 11486125578281617670, - "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/AUTHORS": 10073225185964928425, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_equals.py": 18261810467289276844, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_setops.py": 5208912445718807372, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/_neighborhood_iterator_imp.h": 10634781863364525895, - "venv/lib/python3.13/site-packages/authlib/integrations/starlette_client/integration.py": 3085827906490477329, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_partial.py": 3151286293440416036, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_arithmetics.py": 16527169124402715210, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_setops.py": 17350958906336246626, - "frontend/src/components/DashboardGrid.svelte": 5332611872674866671, - "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/INSTALLER": 17282701611721059870, - "frontend/build/_app/immutable/chunks/CiqmpDGb.js": 15624069632350610927, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_head_tail.py": 13047338070883375426, - "venv/lib/python3.13/site-packages/pip/_vendor/truststore/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/datetimes/test_reductions.py": 1713831062142840146, - "venv/lib/python3.13/site-packages/PIL/FliImagePlugin.py": 13364963053197422923, - "venv/lib/python3.13/site-packages/charset_normalizer/legacy.py": 1706599349563920562, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_map.py": 32086858815359344, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ma.pyi": 8186102816080813799, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_pop.py": 14885271225856187795, - "backend/tests/core/migration/test_dry_run_orchestrator.py": 5158126098685923841, - "venv/lib/python3.13/site-packages/anyio/_core/_typedattr.py": 17584691045493791349, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/string_/test_concat.py": 3788461606817482165, - "venv/lib/python3.13/site-packages/annotated_types/test_cases.py": 5123395674810472016, - "venv/lib/python3.13/site-packages/greenlet/greenlet_allocator.hpp": 11041111583319491309, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_array_api_info.py": 16491629653122827368, - "venv/lib/python3.13/site-packages/secretstorage/__init__.py": 4535964275649588672, - "venv/lib/python3.13/site-packages/keyring/__main__.py": 14602212239605295451, - "venv/lib/python3.13/site-packages/pip/_internal/operations/build/wheel_editable.py": 14352954849821931255, - "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/LICENSE": 16343563559291870811, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/providers.py": 7267821734584612032, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_setops.py": 8622608042929141013, - "venv/lib/python3.13/site-packages/sqlalchemy/event/registry.py": 2857989070743633709, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_constructors.py": 11065058462752831997, - "venv/lib/python3.13/site-packages/PIL/ImageShow.py": 11437096951764124817, - "venv/lib/python3.13/site-packages/yaml/serializer.py": 1783761365703703558, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/traversals.py": 1188980668212022868, - "venv/lib/python3.13/site-packages/pip/_internal/utils/wheel.py": 450504439085405418, - "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/METADATA": 7788873460115135563, - "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/WHEEL": 7145482834744213753, - "frontend/src/lib/components/translate/__tests__/test_translation_preview.svelte.js": 15091004230843271151, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/nbit_base_example.pyi": 8571452382806614924, - "venv/lib/python3.13/site-packages/pandas/core/ops/missing.py": 9493665944764255854, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_iceberg.py": 1726421917529457420, - "venv/lib/python3.13/site-packages/pygments/lexers/idl.py": 11454152060351801696, - "venv/lib/python3.13/site-packages/PIL/ImageColor.py": 17058618058554970738, - "frontend/src/routes/admin/settings/llm/+page.svelte": 8015678318492837753, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_symbol.py": 10792387279408528886, - "venv/lib/python3.13/site-packages/pandas/core/computation/expr.py": 13085709365673019494, - "venv/lib/python3.13/site-packages/cffi/model.py": 13544803366359102016, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_api.py": 5813060676255093777, - "venv/lib/python3.13/site-packages/numpy/lib/_index_tricks_impl.pyi": 5892973170789971535, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_sql.py": 3901697517087276687, - "venv/lib/python3.13/site-packages/websockets/legacy/framing.py": 12759022769700230409, - "venv/lib/python3.13/site-packages/typing_inspection/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pydantic/_internal/_validators.py": 2939255306814626438, - "frontend/svelte.config.js": 8473636210766821560, - "backend/src/plugins/translate/__tests__/test_orchestrator.py": 17131259366407024684, - "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/RECORD": 7914594107698271488, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/argon2.py": 15389474901714096890, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/cryptography/hazmat/__init__.py": 6534110674636458866, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/missing.py": 11241703105777668183, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_combine.py": 4446011286411407708, - "venv/lib/python3.13/site-packages/pandas/io/formats/templates/html_style.tpl": 3352086677417301397, - "venv/lib/python3.13/site-packages/pillow.libs/liblcms2-cc10e42f.so.2.0.17": 4221600618602131476, - "backend/tests/services/dataset_review/test_superset_matrix.py": 5114797134613078913, - "venv/lib/python3.13/site-packages/pydantic/fields.py": 4142211199816898047, - "backend/alembic/README": 17625003422504608299, - "venv/lib/python3.13/site-packages/pip/_internal/operations/build/build_tracker.py": 9389444024234384408, - "venv/lib/python3.13/site-packages/pip/_internal/network/__init__.py": 1586148254475575526, - "venv/lib/python3.13/site-packages/httpx/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_seed_sequence.py": 7687465232954593311, - "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/zip-safe": 15240312484046875203, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi": 1065808244870331598, - "venv/lib/python3.13/site-packages/numpy/matrixlib/__init__.pyi": 1942236362568669510, - "backend/src/api/routes/__init__.py": 9441391336360835840, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libkrb5support-d0bcff84.so.0.1": 16819924515291719993, - "venv/lib/python3.13/site-packages/gitdb/test/lib.py": 13294941660283059872, - "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/WHEEL": 6514509858522675228, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_indexing.py": 9988098331562906950, - "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/WHEEL": 5990830714395966792, - "venv/lib/python3.13/site-packages/pygments/lexers/modula2.py": 6575895980250662093, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_union_categoricals.py": 6843679254735807, - "venv/lib/python3.13/site-packages/numpy/ma/tests/test_subclassing.py": 17318983445227658412, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_repr.py": 5577699046802107144, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/ndarrayobject.h": 7320510589635636690, - "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/licenses/LICENSE": 11986475601091748012, - "venv/lib/python3.13/site-packages/httpcore/_sync/interfaces.py": 1532245423878428608, - "venv/lib/python3.13/site-packages/pandas/tests/util/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_alter_axes.py": 11338712836362534969, - "backend/src/api/routes/assistant/_llm_planner_intent.py": 10603216032586398176, - "backend/src/services/mapping_service.py": 5976063352141712188, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7519/jwt.py": 9642615960197748105, - "venv/lib/python3.13/site-packages/packaging/_manylinux.py": 8268418900652790584, - "backend/alembic/versions/aa1b2c3d4e5f_drop_deprecated_translate_columns.py": 15433357059529111004, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/authorization_server.py": 17967809814827547139, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Hamming.py": 3762934316884129514, - "venv/lib/python3.13/site-packages/pydantic/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_csv.py": 6678500392137188521, - "venv/lib/python3.13/site-packages/jaraco/functools/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/authlib/integrations/django_client/integration.py": 13610682193839598457, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_take.py": 3281943380342853080, - "backend/src/api/routes/__tests__/conftest.py": 8465980330386544642, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/models.py": 16636119441941357281, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/util.py": 5252158959761945519, - "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/METADATA": 2053484635654179443, - "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/RECORD": 16740224557818847141, - "venv/lib/python3.13/site-packages/passlib/tests/test_pwd.py": 10089138951415591061, - "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/RECORD": 10036839994791510159, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/__init__.py": 16123561007855424016, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_append.py": 9644955836191900614, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/compat.py": 17520043776344667309, - "venv/lib/python3.13/site-packages/numpy/testing/_private/__init__.pyi": 15130871412783076140, - "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_django.py": 8455054295577203929, - "venv/lib/python3.13/site-packages/pandas/compat/pickle_compat.py": 11073483984394766474, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/__init__.py": 14522970260621605208, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_character/foo90.f90": 2435648607844815947, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/legacy/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_internal/utils/urls.py": 8698341051522816167, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/provider.py": 16463243654123731993, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hashes.py": 8545380307201732345, - "venv/lib/python3.13/site-packages/gitdb/utils/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/fastapi/requests.py": 14587947416691824903, - "venv/lib/python3.13/site-packages/pip/_internal/self_outdated_check.py": 991029278889407558, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi": 13850254283540522444, - "venv/lib/python3.13/site-packages/numpy/_utils/_pep440.py": 15854257948148516490, - "venv/lib/python3.13/site-packages/urllib3/util/ssltransport.py": 1175688460659745788, - "frontend/src/services/__tests__/gitService.test.js": 12845562082447336909, - "backend/src/api/routes/translate/_schedule_routes.py": 9081639461481759877, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_size.py": 14032390806503440750, - "venv/lib/python3.13/site-packages/pip/_internal/distributions/wheel.py": 13979536782410081712, - "venv/lib/python3.13/site-packages/numpy/tests/test_warnings.py": 3154249157134620649, - "venv/lib/python3.13/site-packages/pygments/lexers/fift.py": 7537570342576781911, - "venv/lib/python3.13/site-packages/jsonschema/tests/_suite.py": 6307666366417438489, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked_shared.py": 8453174300922339015, - "venv/lib/python3.13/site-packages/pandas/_testing/__init__.py": 10127496080404419481, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_expanding.py": 9047466276603454007, - "venv/lib/python3.13/site-packages/pip/_internal/req/req_uninstall.py": 3515239440812977025, - "venv/lib/python3.13/site-packages/pygments/formatters/other.py": 13124903290103898118, - "venv/lib/python3.13/site-packages/numpy/_core/lib/npy-pkg-config/mlib.ini": 16151597765994697599, - "venv/lib/python3.13/site-packages/numpy/_expired_attrs_2_0.py": 8786558894094307341, - "venv/lib/python3.13/site-packages/pip/_internal/req/constructors.py": 17485271035479378662, - "backend/tests/test_translate_scheduler.py": 14274816721941617887, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_reduction.py": 1049551473665255648, - "venv/lib/python3.13/site-packages/cryptography/x509/oid.py": 13909847799728532602, - "backend/tests/services/clean_release/test_candidate_manifest_services.py": 16861589402854148894, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_asof.py": 15792528637923603240, - "backend/tests/test_translate_scheduler_guard.py": 13396608843530344156, - "frontend/build/_app/immutable/chunks/omsjLJ6z.js": 45189729016770272, - "backend/src/plugins/translate/prompt_builder.py": 5148937139906904004, - "venv/lib/python3.13/site-packages/pandas/tests/test_expressions.py": 16378925536309828288, - "venv/lib/python3.13/site-packages/ecdsa/test_curves.py": 6577762025900435885, - "venv/lib/python3.13/site-packages/numpy/f2py/crackfortran.py": 12796890117561096360, - "venv/lib/python3.13/site-packages/pandas/tests/extension/uuid/test_uuid.py": 13955678427426850807, - "venv/lib/python3.13/site-packages/pygments/lexers/tlb.py": 1169242051301155885, - "frontend/src/components/StartupEnvironmentWizard.svelte": 6947772062866928838, - "backend/src/api/routes/assistant/_command_parser.py": 2745692387994130159, - "venv/lib/python3.13/site-packages/pygments/lexers/jsonnet.py": 1314044008440065525, - "venv/lib/python3.13/site-packages/pydantic/v1/errors.py": 11245135449608452720, - "frontend/src/routes/+page.svelte": 5766160745549070101, - "frontend/src/components/git/ConflictResolver.svelte": 12431368407090802392, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_sort_values.py": 15072305514178699398, - "venv/lib/python3.13/site-packages/pygments/util.py": 4815334059917765852, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7592/endpoint.py": 18260428408051728802, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/util.py": 6088868370298560146, - "venv/lib/python3.13/site-packages/jeepney/io/__init__.py": 2568357146184259175, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_setitem.py": 14920902525192671548, - "venv/lib/python3.13/site-packages/pandas/core/reshape/encoding.py": 12702053687535473061, - "venv/lib/python3.13/site-packages/pandas/tests/test_nanops.py": 8709105600366913011, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/descriptor_props.py": 7044852927393650707, - "venv/lib/python3.13/site-packages/pyasn1/compat/__init__.py": 5902203086150636670, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/__init__.py": 5863110456909134678, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/hooks.py": 8580161128344266243, - "venv/lib/python3.13/site-packages/numpy/testing/__init__.pyi": 8126257788928282891, - "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/licenses/LICENSE.APACHE": 8359973023624924624, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py": 16768579082462294745, - "venv/lib/python3.13/site-packages/yaml/error.py": 5626374410579058436, - "venv/lib/python3.13/site-packages/pyasn1/codec/native/decoder.py": 15336592439379775543, - "venv/lib/python3.13/site-packages/typing_inspection/typing_objects.pyi": 18193727720492050602, - "venv/lib/python3.13/site-packages/fastapi/openapi/models.py": 1680354178147364813, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_sh_gcc.h": 3574353221233465215, - "venv/lib/python3.13/site-packages/passlib/tests/test_handlers.py": 4940305787631493030, - "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/zip-safe": 15240312484046875203, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/wheel.py": 828222664427570058, - "venv/lib/python3.13/site-packages/passlib/totp.py": 14590474309565233318, - "venv/lib/python3.13/site-packages/starlette/concurrency.py": 9835859079437525910, - "venv/lib/python3.13/site-packages/pygments/lexers/_vbscript_builtins.py": 9882134558469491349, - "venv/lib/python3.13/site-packages/ecdsa/ssh.py": 17010933326090778158, - "venv/lib/python3.13/site-packages/numpy/_pytesttester.pyi": 5750506937526308405, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_describe.py": 11923715539897559626, - "frontend/build/_app/immutable/chunks/BOaVavs8.js": 7966656432589868496, - "backend/src/services/rbac_permission_catalog.py": 4811877724682885443, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_csky_gcc.h": 12929785267195988209, - "venv/lib/python3.13/site-packages/pip/_internal/utils/glibc.py": 12335359492756627313, - "venv/lib/python3.13/site-packages/pygments/lexers/ul4.py": 9580182474451220952, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/test_federal.py": 16758212916399857971, - "venv/lib/python3.13/site-packages/ecdsa/test_eddsa.py": 1785854649532849946, - "venv/lib/python3.13/site-packages/attr/_make.py": 2348458829123990579, - "backend/src/core/auth/repository.py": 7314530014494164270, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/_initialize_cpp.cpython-313-x86_64-linux-gnu.so": 7490281502544276964, - "venv/lib/python3.13/site-packages/pygments/lexers/_mql_builtins.py": 2376212048709413586, - "venv/lib/python3.13/site-packages/numpy/f2py/common_rules.py": 7352635707860427145, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_equals.py": 9903250467264848187, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_iter.py": 9232021002336641851, - "backend/src/models/dataset_review_pkg/_profile_models.py": 3121807680272913669, - "venv/lib/python3.13/site-packages/referencing/_attrs.pyi": 10030665705880994025, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_subclass.py": 13490541871827928531, - "venv/lib/python3.13/site-packages/pandas/io/formats/html.py": 11849941990375030601, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_pct_change.py": 18056235438452125034, - "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_searchsorted.py": 12074107109575676897, - "venv/lib/python3.13/site-packages/pydantic/_internal/_repr.py": 1758478941817868344, - "frontend/build/_app/immutable/nodes/34.B1G9Ceba.js": 15227780133014315507, - "backend/src/services/clean_release/repositories/candidate_repository.py": 14784344453820731941, - "venv/lib/python3.13/site-packages/greenlet/tests/fail_clearing_run_switches.py": 3020294789560975101, - "venv/lib/python3.13/site-packages/numpy/f2py/_isocbind.py": 15031197044862908440, - "backend/src/plugins/translate/__tests__/test_executor.py": 12854951718356374947, - "venv/lib/python3.13/site-packages/numpy/f2py/_isocbind.pyi": 5786150108623563787, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/authorization_server.py": 4427151727224472650, - "backend/src/schemas/dataset_review_pkg/_composites.py": 1898056130177244572, - "backend/src/core/config_manager.py": 18161236261317111528, - "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/RECORD": 11788532857617688351, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_astype.py": 8505605634958454768, - "backend/src/api/routes/assistant/_llm_planner.py": 17830859532452456684, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/_typing.py": 10524021526716767489, - "venv/lib/python3.13/site-packages/pycparser/ast_transforms.py": 9032453625977464506, - "backend/src/core/superset_client/_datasets_preview.py": 16476837380317508068, - "venv/lib/python3.13/site-packages/rapidfuzz/utils.py": 5682627611354455144, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/token.py": 6987328478153445584, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_comment.py": 5603075924832933306, - "venv/lib/python3.13/site-packages/pandas/core/window/expanding.py": 14141093310397856286, - "venv/lib/python3.13/site-packages/greenlet/CObjects.cpp": 3454797518210223926, - "backend/src/core/auth/oauth.py": 9925908783612235042, - "venv/lib/python3.13/site-packages/pandas/core/arrays/interval.py": 16281221279220043867, - "backend/src/api/routes/llm.py": 5549663650842379152, - "venv/lib/python3.13/site-packages/pandas/_libs/tslib.cpython-313-x86_64-linux-gnu.so": 10742615805788605480, - "venv/lib/python3.13/site-packages/pygments/styles/autumn.py": 8750937312198920971, - "venv/lib/python3.13/site-packages/numpy/matrixlib/__init__.py": 18402126171936308228, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_factorize.py": 5585211699185937865, - "venv/lib/python3.13/site-packages/pygments/lexers/eiffel.py": 8964563077187051946, - "frontend/src/routes/translate/history/+page.svelte": 8993421379788635747, - "frontend/build/_app/immutable/nodes/16.ClShNgv8.js": 11360248111062186711, - "venv/lib/python3.13/site-packages/apscheduler/executors/twisted.py": 2695602672085615762, - "venv/lib/python3.13/site-packages/packaging/_tokenizer.py": 7442392076059866359, - "venv/lib/python3.13/site-packages/pandas/tests/strings/test_extract.py": 14547819021277949280, - "venv/lib/python3.13/site-packages/pandas/tests/io/conftest.py": 5864037534050136837, - "venv/lib/python3.13/site-packages/pluggy/_manager.py": 18202550145040079612, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_ufunc.py": 5025605639221470838, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/missing.py": 14589973874359353799, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/test_arrow_compat.py": 12986187285988432338, - "venv/lib/python3.13/site-packages/PIL/ImagePath.py": 18323293175183057101, - "venv/lib/python3.13/site-packages/pygments/lexers/savi.py": 95400050302239274, - "venv/lib/python3.13/site-packages/pip/_vendor/tomli/__init__.py": 12906995259233516197, - "venv/lib/python3.13/site-packages/numpy/ma/README.rst": 13203411621071845216, - "frontend/src/routes/dashboards/+page.svelte": 7340914202224737248, - "venv/lib/python3.13/site-packages/gitdb/test/test_pack.py": 7518388297247535189, - "backend/alembic/versions/2a7b8c9d0e1f_add_target_languages_to_translation_jobs.py": 7201017059554918882, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test__exceptions.py": 12099001631178696189, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/__main__.py": 111505188798650285, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/transform/test_transform.py": 5674175311567817854, - "venv/lib/python3.13/site-packages/pygments/lexers/blueprint.py": 6370363226580199085, - "venv/lib/python3.13/site-packages/pandas/tests/resample/conftest.py": 1643522609234951699, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/_typing.py": 17598638528781632158, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_kind.py": 18178830861475480607, - "venv/lib/python3.13/site-packages/pandas/io/sas/__init__.py": 8095743504390517992, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/resource_protector.py": 3563910821279222695, - "venv/lib/python3.13/site-packages/git/refs/reference.py": 2038789918710608481, - "frontend/build/_app/immutable/nodes/12.C45g1odY.js": 3364177908218078138, - "venv/lib/python3.13/site-packages/pygments/lexers/macaulay2.py": 8022891778577314212, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/device_code.py": 6712610002227849281, - "backend/src/api/routes/assistant/_resolvers.py": 1139669723103531814, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/token.py": 4161760290367932793, - "venv/lib/python3.13/site-packages/h11/_readers.py": 832030916375670301, - "venv/lib/python3.13/site-packages/starlette/templating.py": 9242638124487797988, - "venv/lib/python3.13/site-packages/numpy/lib/_iotools.py": 10522826373041592861, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/applicator": 6616723520993329538, - "frontend/build/_app/immutable/nodes/7.D1ZRe8Pd.js": 10261432846649332940, - "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/_impl.py": 16610770935842519289, - "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/min_max_.py": 13457611930368041821, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_interval_range.py": 9900908262700749861, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_shift.py": 14026124784027171225, - "venv/lib/python3.13/site-packages/websockets/__init__.py": 4916307256304054040, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_inspect.py": 10573558345297891848, - "venv/lib/python3.13/site-packages/passlib/tests/sample_config_1s.cfg": 1809713544666311828, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_reindex_like.py": 1407890639206728014, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_common.py": 12064185052519470358, - "venv/lib/python3.13/site-packages/pandas/tests/frame/constructors/test_from_dict.py": 4937173601709369119, - "venv/lib/python3.13/site-packages/pygments/styles/algol.py": 3040373291664740876, - "frontend/build/_app/immutable/nodes/21.elO5x4W8.js": 7224240725322450585, - "venv/lib/python3.13/site-packages/pygments/lexers/go.py": 11118104397271513417, - "venv/lib/python3.13/site-packages/keyring/backends/macOS/api.py": 4502902558534812719, - "backend/src/api/routes/dashboards/__init__.py": 15214026957520044598, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_rolling_quantile.py": 4225791949463893856, - "venv/lib/python3.13/site-packages/numpy/_pyinstaller/tests/pyinstaller-smoke.py": 13570990531070630950, - "venv/lib/python3.13/site-packages/pandas/util/version/__init__.py": 3844072588696074141, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_backend.py": 17793139799410518847, - "venv/lib/python3.13/site-packages/pygments/lexers/_julia_builtins.py": 8846111188795315103, - "venv/lib/python3.13/site-packages/greenlet/greenlet.cpp": 3281107194043468270, - "venv/lib/python3.13/site-packages/pydantic/plugin/__init__.py": 16803973597469216174, - "venv/lib/python3.13/site-packages/pandas/core/methods/selectn.py": 13552176539021625776, - "venv/lib/python3.13/site-packages/pandas/io/api.py": 8275376846208437741, - "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/_libs/sas.pyi": 873957906443703977, - "frontend/build/_app/immutable/nodes/10.CN_3DgUX.js": 2682713571147054657, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libsasl2-883649fd.so.3.0.0": 13306917506408577991, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_repr.py": 16609300578827398344, - "backend/_convert_defs.py": 208535643309836247, - "frontend/src/routes/login/+page.svelte": 4542360917085342323, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/protocol.py": 932296179250981622, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm64_masm.asm": 16630507336149184509, - "venv/lib/python3.13/site-packages/numpy/typing/tests/test_typing.py": 10311989961588249147, - "venv/lib/python3.13/site-packages/rapidfuzz/process_cpp.py": 16797760903996739079, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_time_series.py": 5205199092955498507, - "frontend/src/lib/components/health/PolicyForm.svelte": 10697252862978287099, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_constructors.py": 5627819885594450614, - "venv/lib/python3.13/site-packages/numpy/linalg/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi": 11455144434558323236, - "venv/lib/python3.13/site-packages/numpy/_core/_type_aliases.pyi": 15848834171197104301, - "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/LICENSE": 16174281248426886206, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_cov_corr.py": 13652339565475304679, - "venv/lib/python3.13/site-packages/rapidfuzz/_feature_detector.py": 15458248567257759500, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_validate.py": 12789481642730855139, - "venv/lib/python3.13/site-packages/_pytest/tmpdir.py": 10834220332037364219, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/reflection.py": 5951113078754792806, - "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/util.py": 2866856507760047219, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/block_docstring/foo.f": 2489756567993745076, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_datetime.py": 1678885152993000592, - "venv/lib/python3.13/site-packages/numpy/tests/test_numpy_config.py": 3636377988901907424, - "venv/lib/python3.13/site-packages/PIL/__main__.py": 827665918723937817, - "venv/lib/python3.13/site-packages/numpy/_core/getlimits.pyi": 15746478550640231553, - "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_scrypt.py": 8106466515670064013, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_melt.py": 15663807247682146566, - "venv/lib/python3.13/site-packages/keyring/core.py": 2764269590123131634, - "venv/lib/python3.13/site-packages/pandas/_libs/index.pyi": 17679349633640606985, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_grouping.py": 5994909693214299971, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_reset_index.py": 5946666595195952693, - "venv/lib/python3.13/site-packages/git/objects/submodule/root.py": 5322248117564025370, - "venv/lib/python3.13/site-packages/pygments/lexers/_tsql_builtins.py": 829193091321611630, - "venv/lib/python3.13/site-packages/pygments/lexers/rnc.py": 9747352274729854620, - "backend/src/core/utils/superset_compilation_adapter.py": 4974836712746468095, - "backend/src/api/routes/__tests__/test_reports_api.py": 17714266401048048508, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/array_like.pyi": 1968570015765106620, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_custom_business_month.py": 1613806538807726074, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_rolling_skew_kurt.py": 15225601437354712351, - "venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.pyi": 16799580742094025110, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/json.py": 14755215397454910426, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_equals.py": 9405821443172167066, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion": 2607512361823362698, - "venv/lib/python3.13/site-packages/numpy/_utils/_convertions.py": 12886275429547303345, - "venv/lib/python3.13/site-packages/numpy/core/__init__.py": 10397294636564169377, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_interval.py": 2911379732293827726, - "venv/lib/python3.13/site-packages/cffi/pkgconfig.py": 16057517851163939571, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_pickle.py": 2910383574326146698, - "frontend/src/routes/dashboards/health/__tests__/health_page.integration.test.js": 11653729504599199718, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_period.py": 15671388508498441412, - "backend/src/api/routes/git/_config_routes.py": 6093940098740660772, - "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/REQUESTED": 15130871412783076140, - "frontend/src/components/llm/__tests__/provider_config.integration.test.js": 2061085383592666930, - "venv/lib/python3.13/site-packages/pip/_internal/cli/command_context.py": 9426036980437931533, - "venv/lib/python3.13/site-packages/numpy/core/einsumfunc.py": 3484247123731380377, - "venv/lib/python3.13/site-packages/pandas/_libs/properties.pyi": 9268470215750269699, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py": 2569618488804767890, - "venv/lib/python3.13/site-packages/PIL/PdfParser.py": 13710342183037972042, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_index_equal.py": 10617870815989304510, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/numeric.pyi": 17005748821769179511, - "venv/lib/python3.13/site-packages/numpy/_core/function_base.py": 5676611841071163854, - "venv/lib/python3.13/site-packages/numpy/polynomial/polynomial.pyi": 9935587108964614958, - "venv/lib/python3.13/site-packages/pygments/styles/algol_nu.py": 6229131016121450375, - "backend/src/plugins/llm_analysis/__tests__/test_service.py": 9850768293526486431, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/jwe_algs.py": 2479791018067637690, - "venv/lib/python3.13/site-packages/requests/exceptions.py": 6474239743209354989, - "venv/lib/python3.13/site-packages/numpy/linalg/__init__.pyi": 18409133021422984774, - "venv/lib/python3.13/site-packages/pygments/lexers/bare.py": 18266858932333619451, - "backend/tests/test_logger.py": 11506419828617508917, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_arrayobject.py": 13337664596833150958, - "backend/src/core/auth/logger.py": 14199514105394646252, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/base.py": 16052592982063844259, - "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/anyio/abc/_resources.py": 8330687082933028623, - "venv/lib/python3.13/site-packages/pandas/core/reshape/merge.py": 300205528811693975, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_extension_array_equal.py": 16119106336497630796, - "venv/lib/python3.13/site-packages/numpy/polynomial/legendre.pyi": 2029639875417260268, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/usecols/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS": 17022185907188244883, - "venv/lib/python3.13/site-packages/git/repo/base.py": 13975465345838683533, - "venv/lib/python3.13/site-packages/pandas/core/algorithms.py": 487939626203097713, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/strings.pyi": 15361498560917514616, - "venv/lib/python3.13/site-packages/fastapi/exceptions.py": 12665775976182239091, - "venv/bin/keyring": 1488968652276547659, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_frozen.py": 3484963987053884566, - "venv/lib/python3.13/site-packages/numpy/_typing/_char_codes.py": 14162745886150625150, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_api.py": 13739775047493489785, - "frontend/src/components/MappingTable.svelte": 3101497452073042168, - "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/lib/_arraypad_impl.py": 6348811074249398741, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/util.py": 4815334059917765852, - "venv/lib/python3.13/site-packages/numpy/_core/_dtype_ctypes.pyi": 14543653817631274394, - "venv/lib/python3.13/site-packages/jeepney/__init__.py": 1063800617763464773, - "venv/lib/python3.13/site-packages/numpy/fft/_pocketfft_umath.cpython-313-x86_64-linux-gnu.so": 16443444849116065961, - "venv/lib/python3.13/site-packages/pandas/tests/apply/test_frame_apply.py": 8103417303073393969, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/io.py": 11072022722163954973, - "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_loongarch64_linux.h": 6566098590441297326, - "venv/lib/python3.13/site-packages/numpy/f2py/_src_pyf.pyi": 8916922961301555974, - "venv/lib/python3.13/site-packages/urllib3/connection.py": 12761371748350595804, - "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/METADATA": 3044554568188160018, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/named_types.py": 6099185243355915896, - "frontend/src/components/git/DeploymentModal.svelte": 6232413208557068848, - "frontend/src/routes/settings/EnvironmentsTab.svelte": 16246319452479236452, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/auth.py": 912521029864094489, - "venv/lib/python3.13/site-packages/attrs/converters.py": 13109003765349201809, - "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/METADATA": 8985010079096958084, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/usecols/test_usecols_basic.py": 16382444980424010514, - "venv/lib/python3.13/site-packages/git/refs/tag.py": 9979537867501757110, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_skew.py": 5276102682005383261, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_limited_api.py": 15788485008764560845, - "venv/lib/python3.13/site-packages/numpy/_utils/_pep440.pyi": 6796322258258182775, - "venv/lib/python3.13/site-packages/pandas/_libs/window/indexers.pyi": 9911899303567570646, - "venv/lib/python3.13/site-packages/numpy/lib/_polynomial_impl.pyi": 2042169688753984918, - "venv/lib/python3.13/site-packages/urllib3/http2/connection.py": 1661620860881397554, - "backend/logs/app.log.bak": 12967408684782809237, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7636/challenge.py": 6388445104761482746, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timestamps.cpython-313-x86_64-linux-gnu.so": 13209491675968970365, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_resolution.py": 6076761538303762297, - "venv/lib/python3.13/site-packages/pygments/lexers/nix.py": 15050086330545600356, - "venv/lib/python3.13/site-packages/pandas/tests/test_take.py": 17628004130770326677, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_sequence.py": 3721630758772772345, - "venv/lib/python3.13/site-packages/jaraco/context/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi": 10548370574281981402, - "venv/lib/python3.13/site-packages/pygments/styles/material.py": 14325514369696961874, - "venv/lib/python3.13/site-packages/authlib/oidc/core/errors.py": 18154419146257747681, - "backend/alembic.ini": 8897071018956157084, - "venv/lib/python3.13/site-packages/pydantic/v1/class_validators.py": 6471688474853407718, - "venv/lib/python3.13/site-packages/requests/api.py": 12339139862411341381, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc_linux.h": 15772978805500070727, - "venv/lib/python3.13/site-packages/pygments/lexers/csound.py": 2175454840368374515, - "frontend/src/components/auth/ProtectedRoute.svelte": 7705373237339863677, - "venv/bin/pip": 13861749540792881808, - "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/licenses/LICENSE": 1424673738228762254, - "venv/lib/python3.13/site-packages/pip/_internal/metadata/importlib/__init__.py": 10834852742917144496, - "venv/lib/python3.13/site-packages/passlib/handlers/postgres.py": 14162382430051693313, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_reductions.py": 3575617237516305456, - "venv/lib/python3.13/site-packages/pygments/lexers/lisp.py": 11872331622183590758, - "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE": 2345070715845618109, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_sort_values.py": 12064896795288302730, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_formats.py": 5259100336852479395, - "frontend/src/services/adminService.js": 6375990585512014277, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_html.py": 12040484325988635032, - "venv/lib/python3.13/site-packages/pygments/lexers/pddl.py": 9256112535059995841, - "venv/lib/python3.13/site-packages/pandas/tests/resample/test_period_index.py": 6634414375581820058, - "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/anyio/from_thread.py": 7973678341601453474, - "venv/lib/python3.13/site-packages/greenlet/TMainGreenlet.cpp": 9489520001674089404, - "venv/lib/python3.13/site-packages/websockets/__main__.py": 11948055520555854616, - "backend/src/api/routes/__tests__/test_tasks_logs.py": 15585487936320145746, - "venv/lib/python3.13/site-packages/urllib3/util/retry.py": 17988032431204662756, - "venv/lib/python3.13/site-packages/greenlet/TExceptionState.cpp": 17617354576371846109, - "venv/lib/python3.13/site-packages/certifi/core.py": 7472627735517449185, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/index.py": 2763962019423787504, - "venv/lib/python3.13/site-packages/numpy/core/shape_base.py": 17925759824894973734, - "venv/lib/python3.13/site-packages/click/_compat.py": 14451519072991457993, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_convert.py": 13349243029437881949, - "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/LICENSE": 8177430849046795595, - "venv/lib/python3.13/site-packages/greenlet/platform/setup_switch_x64_masm.cmd": 8473866644286785815, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/base.py": 7110726987481713080, - "venv/lib/python3.13/site-packages/cryptography/x509/base.py": 9124948989225782039, - "venv/lib/python3.13/site-packages/pip/_internal/distributions/__init__.py": 1343779312297539596, - "venv/lib/python3.13/site-packages/numpy/random/__init__.py": 11732100564412485633, - "venv/lib/python3.13/site-packages/PIL/_version.py": 3025206805667405876, - "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/RECORD": 18314788057673509075, - "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pluggy/_callers.py": 1042673447718196223, - "venv/lib/python3.13/site-packages/pandas/core/ops/__init__.py": 4049432353810047365, - "venv/lib/python3.13/site-packages/sqlalchemy/util/preloaded.py": 3719702436309355562, - "venv/lib/python3.13/site-packages/charset_normalizer/api.py": 9172704759115759638, - "venv/lib/python3.13/site-packages/pygments/lexers/resource.py": 8057351545636349791, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_mixins.py": 12351373879926460719, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_infer_objects.py": 16868563755487479303, - "venv/lib/python3.13/site-packages/pip/_internal/commands/help.py": 4175797322922912326, - "venv/lib/python3.13/site-packages/passlib/handlers/pbkdf2.py": 16686685453577026643, - "venv/lib/python3.13/site-packages/charset_normalizer/md.cpython-313-x86_64-linux-gnu.so": 1835299664481804968, - "frontend/src/components/git/CommitHistory.svelte": 15861316872235672161, - "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/theme.py": 14225878259183479398, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/filepost.py": 16698380375728063923, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/f77comments.f": 4801926170672147659, - "backend/src/api/routes/assistant/_routes.py": 6252816315418755346, - "venv/lib/python3.13/site-packages/numpy/ma/__init__.pyi": 1855615156913010107, - "backend/src/services/clean_release/manifest_service.py": 10873340336255220063, - "venv/lib/python3.13/site-packages/pygments/styles/paraiso_light.py": 15941591873059208999, - "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/licenses/LICENSE": 14230156892574098522, - "venv/lib/python3.13/site-packages/jsonschema/_format.py": 8987698648383779883, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_network.py": 13159427333040109523, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/quoted_character/foo.f": 15318083678609435813, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/processors.cpython-313-x86_64-linux-gnu.so": 18139875206371157315, - "venv/lib/python3.13/site-packages/starlette/middleware/trustedhost.py": 8302185291610019926, - "venv/lib/python3.13/site-packages/pip/_internal/models/scheme.py": 4101322315594430874, - "venv/lib/python3.13/site-packages/greenlet/tests/_test_extension_cpp.cpython-313-x86_64-linux-gnu.so": 7659849621107414188, - "venv/lib/python3.13/site-packages/cryptography/hazmat/_oid.py": 755042927629281577, - "venv/lib/python3.13/site-packages/numpy/_core/shape_base.py": 3996519099530770308, - "venv/lib/python3.13/site-packages/pydantic/v1/error_wrappers.py": 5701679786078971939, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/char.f90": 12203871998988759364, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_setops.py": 15196593522537569500, - "venv/lib/python3.13/site-packages/pandas/tests/strings/test_string_array.py": 8260883866404398980, - "venv/lib/python3.13/site-packages/anyio/_core/_tempfile.py": 230288423480899091, - "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/RECORD": 10052481303799123207, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ufunclike.pyi": 12215374902556647369, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/events.py": 7576713358952064464, - "venv/lib/python3.13/site-packages/pydantic_settings/main.py": 17272314000918984790, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/dml.py": 6519732236745017861, - "venv/lib/python3.13/site-packages/numpy/f2py/src/fortranobject.h": 5980733638838895780, - "venv/lib/python3.13/site-packages/pandas/tests/apply/test_frame_transform.py": 6759417099641034692, - "venv/lib/python3.13/site-packages/PIL/McIdasImagePlugin.py": 14137545069906812251, - "venv/lib/python3.13/site-packages/PIL/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/io/formats/string.py": 14718123345899686696, - "venv/lib/python3.13/site-packages/httpcore/_async/connection.py": 2155744160496556069, - "venv/lib/python3.13/site-packages/fastapi/middleware/httpsredirect.py": 5775982317459922443, - "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/RECORD": 9964194961227523130, - "venv/lib/python3.13/site-packages/passlib/ext/django/models.py": 12386223748379294996, - "venv/lib/python3.13/site-packages/requests/_internal_utils.py": 4091305333167213279, - "venv/lib/python3.13/site-packages/click/types.py": 6491651131669287534, - "venv/lib/python3.13/site-packages/numpy/testing/tests/test_utils.py": 5291132432383518704, - "venv/lib/python3.13/site-packages/idna-3.11.dist-info/METADATA": 14036738037171597049, - "venv/lib/python3.13/site-packages/git/objects/fun.py": 15014209679156260865, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_coercion.py": 7261787308019145312, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/arrayterator.pyi": 15722265241660279277, - "venv/lib/python3.13/site-packages/git/index/fun.py": 18338379518768926372, - "venv/lib/python3.13/site-packages/PIL/SunImagePlugin.py": 7661622930404688367, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_delitem.py": 16538190861492829338, - "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/METADATA": 5628172948035858892, - "venv/lib/python3.13/site-packages/pygments/lexers/capnproto.py": 15713281507099411738, - "frontend/public/vite.svg": 13568221685541582385, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_normalize.py": 3203921571999101793, - "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/INSTALLER": 17282701611721059870, - "frontend/src/lib/stores/__tests__/setupTests.js": 13316015450930652502, - "frontend/src/routes/dashboards/[id]/+page.svelte": 1225758611896418118, - "venv/lib/python3.13/site-packages/pandas/compat/_constants.py": 3909166275649444824, - "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/licenses/LICENSE.BSD": 16833702494864671773, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/_version.py": 15336513445621042105, - "venv/lib/python3.13/site-packages/numpy/f2py/symbolic.pyi": 14558091964387571257, - "frontend/src/components/git/BranchSelector.svelte": 6225347983501401973, - "venv/lib/python3.13/site-packages/passlib/handlers/sha2_crypt.py": 8171587560325812664, - "venv/lib/python3.13/site-packages/dotenv/__init__.py": 15464030923609356017, - "venv/lib/python3.13/site-packages/pycparser/_ast_gen.py": 5464347629908906775, - "venv/lib/python3.13/site-packages/pygments/lexers/_qlik_builtins.py": 1728058904137960444, - "venv/lib/python3.13/site-packages/PIL/XVThumbImagePlugin.py": 7472134175570205454, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_nat.py": 18428782698385974380, - "venv/lib/python3.13/site-packages/jsonschema/_keywords.py": 949562792260528762, - "backend/src/plugins/llm_analysis/scheduler.py": 14673660738877638091, - "venv/lib/python3.13/site-packages/websockets/asyncio/server.py": 885415484558949506, - "venv/lib/python3.13/site-packages/uvicorn/middleware/message_logger.py": 14825516604555033580, - "venv/lib/python3.13/site-packages/pygments/lexers/_lilypond_builtins.py": 1766946089314631488, - "venv/lib/python3.13/site-packages/numpy/f2py/__init__.py": 7470157159992827389, - "venv/lib/python3.13/site-packages/more_itertools/__init__.py": 9858612105173082060, - "venv/lib/python3.13/site-packages/httpcore/_exceptions.py": 6716590711697088732, - "merge_spec.py": 5285945727297582371, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data": 9599129816090399315, - "venv/lib/python3.13/site-packages/pip/_internal/operations/freeze.py": 3469845570614985559, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/reporters.py": 10760889726586439724, - "venv/lib/python3.13/site-packages/pydantic/v1/generics.py": 1150594223414883339, - "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_categorical.py": 9923236395692943732, - "venv/lib/python3.13/site-packages/pandas/core/indexers/utils.py": 5861072573716897447, - "backend/src/services/git_service.py": 1930048176535450912, - "venv/lib/python3.13/site-packages/apscheduler/job.py": 1858376671986108667, - "venv/lib/python3.13/site-packages/numpy/f2py/__version__.pyi": 9759534743796867731, - "backend/src/plugins/translate/__tests__/test_sql_generator.py": 4467071942040481147, - "venv/lib/python3.13/site-packages/numpy/_core/printoptions.pyi": 16758392990920041462, - "venv/lib/python3.13/site-packages/pygments/lexers/ecl.py": 12886695201994337568, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/themes.py": 6451091097727599550, - "venv/lib/python3.13/site-packages/anyio/streams/memory.py": 11762722143503428823, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_nlargest.py": 1423106021399469694, - "venv/lib/python3.13/site-packages/numpy/_core/numerictypes.py": 6205675182996213576, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/assumed_shape/foo_free.f90": 10939657740734987454, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_timedelta_range.py": 15081095442418142820, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py": 7707707044867513457, - "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_str_accessor.py": 4066094997616994612, - "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/RECORD": 7810411361439269754, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/abc.py": 6227782083136374888, - "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/websockets_sansio_impl.py": 17221368219485351394, - "frontend/build/_app/immutable/chunks/Bq9hCVSi.js": 1342535258328212659, - "venv/lib/python3.13/site-packages/pip/_internal/index/__init__.py": 14829074315307489628, - "venv/lib/python3.13/site-packages/pydantic/v1/types.py": 10761779263335288559, - "venv/lib/python3.13/site-packages/jeepney/tests/secrets_introspect.xml": 10717038359151125278, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_numba.py": 1668304729617150075, - "venv/lib/python3.13/site-packages/jsonschema/_typing.py": 13152346021901861822, - "venv/lib/python3.13/site-packages/ecdsa/rfc6979.py": 11564966030899162026, - "venv/lib/python3.13/site-packages/numpy/_utils/_inspect.pyi": 17825266810219495672, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_textreader.py": 388955620768371652, - "venv/lib/python3.13/site-packages/pygments/styles/sas.py": 12363791134941577382, - "venv/lib/python3.13/site-packages/pygments/lexers/c_cpp.py": 1211842877043321095, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/plugin/__init__.py": 10470644151952252331, - "backend/src/core/superset_client/__init__.py": 6869599313455380203, - "venv/lib/python3.13/site-packages/authlib/integrations/base_client/errors.py": 6538052228921426956, - "venv/lib/python3.13/site-packages/pygments/lexers/qvt.py": 1895735583818227972, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/_collections.py": 14307204206312829238, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/models.py": 15928924478695638423, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_old_base.py": 191359377446257900, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_from_dummies.py": 5670811108950300761, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_coercion.py": 3339075591248270762, - "venv/lib/python3.13/site-packages/click/globals.py": 18353671282454331523, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/vectorized.pyi": 15498895254516573071, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/einsumfunc.py": 9944042006439149561, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/plugin/pytestplugin.py": 1591034895268415944, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/util.pyx": 649821942746469002, - "venv/lib/python3.13/site-packages/pygments/filter.py": 16846063190466267618, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/unitofwork.py": 3177255974394221645, - "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py": 15644105878811738516, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/scalars.py": 3468820466230647963, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/np_datetime.cpython-313-x86_64-linux-gnu.so": 7840846965295537844, - "venv/lib/python3.13/site-packages/pygments/lexers/factor.py": 15788613154609197002, - "frontend/src/lib/components/reports/__tests__/fixtures/reports.fixtures.js": 10647302793388244094, - "backend/tests/core/test_migration_engine.py": 7419155921858935534, - "venv/lib/python3.13/site-packages/PIL/ImagePalette.py": 9227837918026991107, - "backend/src/services/clean_release/repositories/artifact_repository.py": 9040122866024534865, - "venv/lib/python3.13/site-packages/pip/_internal/operations/build/wheel_legacy.py": 12735901522735836481, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_insert.py": 15536994417338770457, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_where.py": 14167991754625133668, - "venv/lib/python3.13/site-packages/apscheduler/jobstores/sqlalchemy.py": 811968833735951956, - "venv/lib/python3.13/site-packages/numpy/fft/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/common.py": 2075178842766962782, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_reindex_like.py": 13803156447991282315, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_astype.py": 15384141282611151560, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_wrap.py": 17083394123203662880, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_integrity.py": 12010564145080323987, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_round.py": 14996007456708990295, - "venv/lib/python3.13/site-packages/pandas/io/spss.py": 864838144958947906, - "venv/lib/python3.13/site-packages/pandas/io/parquet.py": 14415622950469421518, - "frontend/src/routes/datasets/review/__tests__/dataset_review_entry.test.js": 7689381541193660594, - "venv/lib/python3.13/site-packages/pandas/tests/computation/test_eval.py": 2921337784087808487, - "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/METADATA": 10220680536734745103, - "venv/lib/python3.13/site-packages/pygments/styles/stata_dark.py": 324072437809944751, - "venv/lib/python3.13/site-packages/pip/_internal/commands/wheel.py": 16397273591245583356, - "venv/lib/python3.13/site-packages/numpy/lib/_datasource.pyi": 5894423389796456573, - "venv/lib/python3.13/site-packages/fastapi/security/base.py": 15674297568104917749, - "venv/lib/python3.13/site-packages/pydantic/main.py": 14086938579850319884, - "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_bcrypt.py": 11146468670439284889, - "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/WHEEL": 8954358347596196608, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/lexers/python.py": 17886036670342116501, - "venv/lib/python3.13/site-packages/pandas/core/indexes/timedeltas.py": 13653393430363556843, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_astype.py": 4854809596041987380, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/clsregistry.py": 1589692649837935355, - "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pygments/lexers/mips.py": 2078929831513352031, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_interval.py": 14749388246573356959, - "venv/lib/python3.13/site-packages/_pytest/timing.py": 14080119171718125986, - "venv/lib/python3.13/site-packages/pydantic/v1/validators.py": 1060954771151908883, - "venv/lib/python3.13/site-packages/pandas/core/arrays/period.py": 11560984428402318984, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/test_function.py": 644452398244782315, - "frontend/src/services/taskService.js": 11368052191249479533, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/implicit.py": 7379110378465568776, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py": 3369246520501764730, - "venv/lib/python3.13/site-packages/numpy/_core/records.pyi": 14194354215405544730, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_compare.py": 3079569478684646491, - "venv/lib/python3.13/site-packages/pygments/modeline.py": 6194365452131561478, - "venv/lib/python3.13/site-packages/click/_termui_impl.py": 3296085065310341855, - "venv/lib/python3.13/site-packages/pip/_internal/vcs/mercurial.py": 335201628064585213, - "venv/lib/python3.13/site-packages/_pytest/_code/__init__.py": 6211607282116942091, - "venv/lib/python3.13/site-packages/pip/_internal/models/format_control.py": 12916176880659590456, - "venv/lib/python3.13/site-packages/tzlocal-5.3.1.dist-info/METADATA": 11439969143853665311, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh26681.f90": 1515497186927052106, - "venv/lib/python3.13/site-packages/pandas/io/formats/__init__.py": 18140261896107421679, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c": 17333399794446198412, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_tools.py": 13994164937603424236, - "backend/src/services/reports/report_service.py": 17586405613867657190, - "backend/src/api/routes/migration.py": 6030566471113208264, - "venv/lib/python3.13/site-packages/pandas/_libs/json.cpython-313-x86_64-linux-gnu.so": 2436285663630821963, - "backend/src/core/migration/__init__.py": 13075189211840127611, - "venv/lib/python3.13/site-packages/numpy/random/bit_generator.pyi": 15790738442952696034, - "venv/lib/python3.13/site-packages/PIL/WalImageFile.py": 12442826195371137238, - "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/_pip_wrapper.py": 2516476609539242190, - "venv/lib/python3.13/site-packages/requests/compat.py": 5836590172733661320, - "venv/lib/python3.13/site-packages/pygments/lexers/tcl.py": 10343667975436015855, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_join.py": 13115782559805895662, - "venv/lib/python3.13/site-packages/pydantic/_internal/_docs_extraction.py": 5210114991139191634, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/f2cmap/isoFortranEnvMap.f90": 15768643271492041853, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/index.py": 15694995431926521471, - "venv/lib/python3.13/site-packages/pandas/io/_util.py": 13608922148814398500, - "backend/src/core/superset_client/_dashboards_list.py": 16574697057051064609, - "venv/lib/python3.13/site-packages/psycopg2/tz.py": 9966680685796585198, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_map.py": 6556132946198785761, - "venv/lib/python3.13/site-packages/_pytest/python_api.py": 12953851690889336092, - "venv/lib/python3.13/site-packages/secretstorage/util.py": 1004887530903069622, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_duplicated.py": 6659959750254523427, - "venv/lib/python3.13/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py": 2375452080667815941, - "venv/lib/python3.13/site-packages/pygments/styles/dracula.py": 12379322008538174078, - "venv/lib/python3.13/site-packages/pygments/lexers/_asy_builtins.py": 1665617945818958781, - "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/extension_types.py": 1856757457657310231, - "venv/lib/python3.13/site-packages/pygments/lexers/ptx.py": 4855144232064607995, - "venv/lib/python3.13/site-packages/cffi/_embedding.h": 9441285871823018603, - "venv/lib/python3.13/site-packages/pandas/_libs/_cyutility.cpython-313-x86_64-linux-gnu.so": 3372660143512868174, - "frontend/src/routes/migration/__tests__/migration_dashboard.ux.test.js": 17118608261683412409, - "venv/lib/python3.13/site-packages/pygments/styles/paraiso_dark.py": 3976779833379189837, - "venv/lib/python3.13/site-packages/itsdangerous/signer.py": 3694661094753475840, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_validate_args_and_kwargs.py": 3381939712123764154, - "backend/src/models/assistant.py": 6168086159312177112, - "frontend/src/lib/components/layout/sidebarNavigation.js": 12067620255981492739, - "venv/lib/python3.13/site-packages/passlib/utils/__init__.py": 11704122205941631503, - "frontend/src/lib/components/translate/__tests__/test_correction_cell.svelte.js": 2314051239468628554, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame_color.py": 3700828402084646308, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_fillna.py": 7244491410004453166, - "venv/lib/python3.13/site-packages/numpy/f2py/f2py2e.pyi": 14181518821845356636, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/base.cpython-313-x86_64-linux-gnu.so": 8335798324308957127, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/parameters.py": 5104015814292812973, - "venv/lib/python3.13/site-packages/numpy/_core/_dtype.pyi": 17662618580381221543, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py": 17106903014205241774, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_is_monotonic.py": 4626065848596214108, - "venv/lib/python3.13/site-packages/yaml/reader.py": 9873075843010693205, - "venv/lib/python3.13/site-packages/pygments/lexers/sgf.py": 6802546617450210439, - "venv/lib/python3.13/site-packages/pandas/_libs/interval.pyi": 15698173755300703836, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/lower_f2py_fortran.f90": 13315882216322269383, - "backend/src/api/routes/clean_release.py": 1984604735131504761, - "venv/lib/python3.13/site-packages/python_multipart/exceptions.py": 5971058243218049183, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/object/test_astype.py": 563689238486630866, - "venv/lib/python3.13/site-packages/sqlalchemy/__init__.py": 9751988289698484530, - "venv/lib/python3.13/site-packages/httpcore/_async/http_proxy.py": 13364803556177997526, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/string/test_astype.py": 10171766562093740708, - "venv/lib/python3.13/site-packages/pydantic/class_validators.py": 4643597082531287188, - "venv/lib/python3.13/site-packages/git/index/typ.py": 11661540784850363024, - "backend/src/scripts/clean_release_cli.py": 14467814161661747234, - "venv/lib/python3.13/site-packages/pandas/tests/apply/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/dotenv/version.py": 13508433229287636267, - "backend/src/services/clean_release/policy_resolution_service.py": 5075709738950902327, - "backend/tests/test_task_manager.py": 1706217361689506763, - "venv/lib/python3.13/site-packages/cffi/_shimmed_dist_utils.py": 4824806157907607487, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust.abi3.so": 2537152913308578487, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/dtypes.py": 9839461501970023861, - "venv/lib/python3.13/site-packages/numpy/core/numeric.py": 17025007229779320230, - "frontend/src/routes/reports/llm/[taskId]/+page.svelte": 14394593511269407852, - "venv/lib/python3.13/site-packages/numpy/_core/_string_helpers.py": 17192211156810267818, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_extending.py": 10202091354750153497, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/base.py": 13269563625127294513, - "venv/lib/python3.13/site-packages/pluggy/_hooks.py": 6941854999025808196, - "venv/lib/python3.13/site-packages/pygments/lexers/robotframework.py": 11507713622179102496, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi": 1846673463183660140, - "venv/lib/python3.13/site-packages/pandas/core/resample.py": 13814298871925341617, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/requirements.py": 11478648568700804705, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/__init__.pyi": 17469792319379909312, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/sfc64-testset-2.csv": 4052796393463357888, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/conftest.py": 3691712173202219606, - "backend/src/api/routes/translate/_helpers.py": 15633442720544972283, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py": 14946223589457772924, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi": 7801211423226951223, - "backend/src/core/auth/config.py": 8065625913451826937, - "venv/lib/python3.13/site-packages/passlib/handlers/bcrypt.py": 8125664010268654055, - "venv/lib/python3.13/site-packages/passlib/crypto/_blowfish/base.py": 15872835330892203433, - "venv/lib/python3.13/site-packages/pandas/io/excel/_util.py": 6664989864423807444, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/exceptions.py": 7518613450390473990, - "venv/lib/python3.13/site-packages/pip/_internal/req/req_file.py": 5180401483285462901, - "venv/lib/python3.13/site-packages/anyio/to_interpreter.py": 8369454842687342401, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/azure.py": 13529763490186254462, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_insert.py": 11635164392966270621, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_formats.py": 9738552308554950419, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/constructors.py": 7587476800569414769, - "venv/lib/python3.13/site-packages/rapidfuzz/__pyinstaller/test_rapidfuzz_packaging.py": 5189055608193366093, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_constructors.py": 6440526981746306640, - "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE": 16499431172938528491, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/_py_row.py": 918740441973901148, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_unique.py": 16285418015531891530, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/__multiarray_api.c": 9134176670140958459, - "venv/lib/python3.13/site-packages/sqlalchemy/event/attr.py": 5971663429221923939, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_sorting.py": 10523740688498427929, - "backend/src/models/dataset_review_pkg/_mapping_models.py": 6232602161916984422, - "dist/docker/backend.v1.0.0-rc2-docker.tar": 9693793465773386616, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_to_latex.py": 2628895559914914693, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_algos.py": 5356470832688354593, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_integer.py": 16102640589300356639, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py": 2042509334195547606, - "venv/lib/python3.13/site-packages/smmap/util.py": 13276170835044840023, - "venv/lib/python3.13/site-packages/authlib/oidc/core/userinfo.py": 17201158389976619872, - "venv/lib/python3.13/site-packages/pandas/core/reshape/pivot.py": 4736553530021653868, - "backend/src/core/superset_client/_datasets.py": 4813403545392307482, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/asyncio.py": 2517623574187179319, - "venv/lib/python3.13/site-packages/pandas/tests/extension/decimal/test_decimal.py": 17811784255174057561, - "venv/lib/python3.13/site-packages/pandas/io/formats/printing.py": 3054136648863371285, - "venv/lib/python3.13/site-packages/numpy/_typing/_add_docstring.py": 14657346268890680694, - "venv/lib/python3.13/site-packages/sqlalchemy/future/__init__.py": 17688444679736059779, - "venv/lib/python3.13/site-packages/pygments/lexers/boa.py": 14409319610243925864, - "venv/lib/python3.13/site-packages/pygments/lexers/lilypond.py": 1088755646568511197, - "venv/lib/python3.13/site-packages/numpy/_array_api_info.pyi": 13283968924732680159, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_dtypes.py": 13065857166166716628, - "venv/lib/python3.13/site-packages/dateutil/tz/win.py": 8427821225251397364, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation": 4826455560768665215, - "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/licenses/AUTHORS": 3558407083075395848, - "venv/lib/python3.13/site-packages/yaml/tokens.py": 2279161238859232338, - "venv/lib/python3.13/site-packages/pandas/tests/reductions/test_stat_reductions.py": 11569525436540769152, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_delete.py": 3485374671877063217, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_complex.py": 12963449957363121481, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/__init__.py": 6327819603201859020, - "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/oauth1_client.py": 3129955464287331242, - "venv/lib/python3.13/site-packages/PIL/QoiImagePlugin.py": 17663704273100331654, - "venv/lib/python3.13/site-packages/httpcore/_sync/http_proxy.py": 14037211594707072958, - "venv/lib/python3.13/site-packages/uvicorn/_types.py": 3339389597716220585, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_to_pydatetime.py": 1129954411080617001, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/json_schema_test_suite.py": 6152158310190822762, - "venv/lib/python3.13/site-packages/keyring/testing/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/cryptography/utils.py": 18088508841978595788, - "venv/lib/python3.13/site-packages/pandas/core/common.py": 14578909424716153057, - "backend/src/services/clean_release/approval_service.py": 12912719831239691496, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_sample.py": 4641567828238191927, - "venv/lib/python3.13/site-packages/pydantic/v1/_hypothesis_plugin.py": 6383299780629629080, - "venv/lib/python3.13/site-packages/pydantic/annotated_handlers.py": 10186120300163345360, - "venv/lib/python3.13/site-packages/numpy/matlib.py": 112839038090033740, - "venv/lib/python3.13/site-packages/pandas/core/arrays/numeric.py": 17834058423729918687, - "venv/lib/python3.13/site-packages/requests/cookies.py": 4997226307650350876, - "venv/lib/python3.13/site-packages/numpy/_configtool.pyi": 9670892208858157218, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh18335.f90": 1126438609189294255, - "venv/lib/python3.13/site-packages/authlib/jose/jwk.py": 13560749074922587626, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8414/well_known.py": 7183795183137931709, - "venv/lib/python3.13/site-packages/gitdb/db/__init__.py": 2615504918450276693, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/chararray.pyi": 9683985931409244216, - "venv/lib/python3.13/site-packages/numpy/f2py/_src_pyf.py": 546976622482819095, - "venv/lib/python3.13/site-packages/numpy/_core/shape_base.pyi": 10225243791643474943, - "venv/lib/python3.13/site-packages/pandas/tests/generic/test_generic.py": 1297407268239966429, - "venv/lib/python3.13/site-packages/fastapi/middleware/trustedhost.py": 4285622102799027823, - "venv/lib/python3.13/site-packages/jose/jwe.py": 3325660353225624025, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_index_tricks.py": 4676293127859087991, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_interpolate.py": 6154072863387383113, - "venv/lib/python3.13/site-packages/pandas/tests/strings/test_split_partition.py": 2715491071550545667, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_array.py": 11271807719833350332, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/coercions.py": 5112358449376598662, - "venv/lib/python3.13/site-packages/urllib3/util/ssl_.py": 11720964010284185952, - "venv/lib/python3.13/site-packages/pygments/lexers/wgsl.py": 9718384325049172855, - "frontend/src/lib/stores/activity.js": 14023344642294459631, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/orderinglist.py": 10162099108656794338, - "venv/lib/python3.13/site-packages/numpy/ma/tests/test_regression.py": 12593643817685777170, - "venv/lib/python3.13/site-packages/pandas/core/ops/common.py": 17314999825118928100, - "frontend/src/lib/components/translate/TranslationRunResult.svelte": 17300907945810807567, - "venv/lib/python3.13/site-packages/websockets/sync/server.py": 7966978001578584901, - "frontend/src/routes/storage/repos/+page.svelte": 3849368960648953479, - "venv/lib/python3.13/site-packages/pandas/tests/frame/common.py": 7426635866622531875, - "backend/src/core/migration/archive_parser.py": 8828677257278703059, - "venv/lib/python3.13/site-packages/httpcore/_synchronization.py": 10997080307461682786, - "backend/src/api/routes/__tests__/test_migration_routes.py": 4357515184436771537, - "backend/alembic/versions/c4a3a2f74bfe_add_missing_columns_needs_review_.py": 4320606555167902916, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/lexers/_mapping.py": 17519715342700184302, - "venv/lib/python3.13/site-packages/anyio/_backends/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_protocols.py": 5019018645804313814, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_internals.py": 10472508540875131797, - "venv/lib/python3.13/site-packages/pydantic/color.py": 1007422551673129827, - "venv/lib/python3.13/site-packages/passlib/ifc.py": 14479133654556657428, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_timedelta64.py": 16821719402662529276, - "venv/lib/python3.13/site-packages/pip/_internal/cli/progress_bars.py": 7584699216537478969, - "venv/lib/python3.13/site-packages/apscheduler/jobstores/base.py": 11386927749822539057, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_extint128.py": 11074657869101074458, - "venv/lib/python3.13/site-packages/pandas/core/sparse/api.py": 9094651265692218821, - "venv/lib/python3.13/site-packages/more_itertools/more.pyi": 4844596605492825806, - "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/METADATA": 10810011740984652381, - "venv/lib/python3.13/site-packages/numpy/core/overrides.pyi": 1968648615494204783, - "venv/lib/python3.13/site-packages/numpy/linalg/_linalg.pyi": 9629457630163613604, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/lib_function_base.pyi": 2792940598392356268, - "venv/lib/python3.13/site-packages/pandas/core/groupby/indexing.py": 18009437216172136227, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_period.py": 6047041810867211174, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_dict_of_blocks.py": 11646835537993064324, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_autocorr.py": 1092316868398391792, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_arithmetic.py": 15071337112968886738, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_replace.py": 18039668944453454689, - "venv/lib/python3.13/site-packages/pandas/io/excel/_xlrd.py": 14430937200583666658, - "venv/lib/python3.13/site-packages/pillow.libs/libwebpmux-7f11e5ce.so.3.1.2": 13105940093474848953, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_musllinux.py": 2423350951094915747, - "venv/lib/python3.13/site-packages/pandas/io/excel/_xlsxwriter.py": 16873939324418752965, - "venv/lib/python3.13/site-packages/PIL/ImageGrab.py": 11114379915762058868, - "venv/lib/python3.13/site-packages/PIL/ImageEnhance.py": 3218405223202808764, - "venv/lib/python3.13/site-packages/httpx/_transports/base.py": 6498990531443644896, - "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py2-np0-objarr.npy": 1615546603475261360, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_monotonic.py": 11817523305776951162, - "backend/src/core/auth/jwt.py": 2300190538212143465, - "venv/lib/python3.13/site-packages/fastapi/cli.py": 13972351579061154035, - "venv/lib/python3.13/site-packages/python_multipart/__init__.py": 10290895261363712562, - "venv/lib/python3.13/site-packages/pyasn1/codec/ber/encoder.py": 8912119133548489544, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/generate_umath_validation_data.cpp": 10880681943327843928, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/interfaces.py": 4890958419958257958, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Prefix.pyi": 2700568594438441246, - "backend/src/services/clean_release/stages/no_external_endpoints.py": 11041035366569218866, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/controller.py": 5450931608244830335, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/retry.py": 17371295199208325375, - "venv/lib/python3.13/site-packages/cryptography/hazmat/decrepit/__init__.py": 63497920264089252, - "backend/src/services/git/__init__.py": 14011752637167919253, - "venv/lib/python3.13/site-packages/numpy/tests/test_matlib.py": 14395481530627870882, - "venv/lib/python3.13/site-packages/PIL/BufrStubImagePlugin.py": 5520941821175895889, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/filesize.py": 13234856188049714485, - "venv/lib/python3.13/site-packages/pandas/core/indexers/__init__.py": 1756397319068017890, - "venv/lib/python3.13/site-packages/pandas/core/indexes/category.py": 18192569199634406001, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_delitem.py": 14336697139511118920, - "frontend/src/services/storageService.js": 14408290095426695791, - "venv/lib/python3.13/site-packages/pandas/core/accessor.py": 1188716996113179524, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_indexing.py": 6039808271042864401, - "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/WHEEL": 3749476255729626245, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/interface.py": 3134071530627935964, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/syntax.py": 18070449217682341495, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/resultproxy.pyx": 5760108632332714287, - "venv/lib/python3.13/site-packages/pycparser/yacctab.py": 18018314512916353707, - "venv/lib/python3.13/site-packages/pip/__init__.py": 9449893869469432503, - "venv/lib/python3.13/site-packages/numpy/polynomial/_polybase.pyi": 15864712173815045309, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_shape_base.py": 4117141349022217163, - "venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/RECORD": 12015288851076448205, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/automap.py": 4672057253309608464, - "venv/lib/python3.13/site-packages/pygments/styles/__init__.py": 18383248976818885498, - "venv/lib/python3.13/site-packages/pandas/core/indexes/datetimelike.py": 4356700619632833878, - "venv/lib/python3.13/site-packages/PIL/_typing.py": 17407980360703360848, - "backend/src/core/auth/__tests__/test_auth.py": 14999763239648705195, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/__init__.py": 71159121705871510, - "venv/lib/python3.13/site-packages/authlib/integrations/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/parsing.pyi": 11967691665766452312, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_unsupported.py": 9115440166074553860, - "venv/lib/python3.13/site-packages/pygments/lexers/theorem.py": 15666523910367201285, - "frontend/src/lib/components/reports/__tests__/report_type_profiles.test.js": 498330292141991614, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/metadata.py": 1362166613223529108, - "venv/lib/python3.13/site-packages/_pytest/outcomes.py": 12782895027812738142, - "venv/lib/python3.13/site-packages/numpy/random/_common.pxd": 3402284125943848851, - "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/apply/test_series_apply_relabeling.py": 3930092097678339743, - "venv/lib/python3.13/site-packages/requests/certs.py": 16052880877156864882, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/padding.py": 7470483648602195268, - "venv/lib/python3.13/site-packages/starlette/middleware/base.py": 16850428285382256618, - "venv/lib/python3.13/site-packages/ecdsa/test_pyecdsa.py": 14542286382742044648, - "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/oauth1_session.py": 1911069558956893789, - "venv/lib/python3.13/site-packages/uvicorn/protocols/http/auto.py": 15947958974558543657, - "venv/lib/python3.13/site-packages/pip/_vendor/idna/intranges.py": 12536174834761591006, - "venv/lib/python3.13/site-packages/smmap/test/lib.py": 2924931247676024085, - "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/WHEEL": 7127684561765977531, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_formats.py": 12299393760307251036, - "frontend/src/lib/stores/__tests__/test_taskDrawer.js": 13582869875796637016, - "venv/lib/python3.13/site-packages/anyio/_backends/_asyncio.py": 10265081145401348863, - "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/accessors.py": 12743680296510533030, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_datetime.py": 13781712980921881470, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_values.py": 8372572954663628844, - "venv/lib/python3.13/site-packages/numpy/_core/numeric.pyi": 17785360553418386488, - "venv/lib/python3.13/site-packages/pandas/io/clipboard/__init__.py": 3656451154829206939, - "frontend/build/_app/immutable/nodes/23.DidEHbej.js": 11332551805513625112, - "venv/lib/python3.13/site-packages/pillow.libs/libbrotlidec-b57ddf63.so.1.2.0": 10032590058372320813, - "venv/lib/python3.13/site-packages/pandas/tests/series/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/ma/core.pyi": 4829891173366130328, - "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/licenses/numpy/_core/src/umath/svml/LICENSE": 9584102918819171104, - "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_matrix_linalg.py": 2858987911347541177, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_python_parser_only.py": 12231449578582108346, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/fromnumeric.pyi": 6708743411405679061, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_indexing.py": 16264430536704066847, - "venv/lib/python3.13/site-packages/pandas/tests/test_register_accessor.py": 16661086720613729974, - "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/frequencies/__init__.py": 15130871412783076140, - "backend/src/core/scheduler.py.bak": 15045927989195339728, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_interval.py": 8549680643865283913, - "backend/src/models/dataset_review_pkg/_session_models.py": 14346374229033337012, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/authorization_server.py": 13696038495666024258, - "venv/lib/python3.13/site-packages/greenlet/TPythonState.cpp": 12828594265749606443, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/containers.py": 5969831151685016772, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_reindex.py": 8772423071604000084, - "venv/lib/python3.13/site-packages/gitdb/typ.py": 16374739906244805047, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/liblber-1c9097e2.so.2.0.200": 2651879839738900594, - "venv/lib/python3.13/site-packages/attrs/exceptions.py": 2871247302586505373, - "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/WHEEL": 17410883677306885019, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_indexing.py": 7181087459615188551, - "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/licenses/LICENSE": 4123064337800029995, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/test_indexing.py": 9862204223281834360, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_tz_convert.py": 17957207974799052653, - "backend/src/services/notifications/__tests__/test_notification_service.py": 2377464079178202565, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/compat.py": 5871636909854554063, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_nlargest.py": 14518132768753249360, - "venv/lib/python3.13/site-packages/passlib/tests/sample1.cfg": 9597283806285213340, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/base.py": 3628510830969664111, - "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/stride_tricks.pyi": 5844806555773292985, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/scoping.py": 17216492046645992674, - "venv/lib/python3.13/site-packages/git/repo/fun.py": 9019364012746153312, - "venv/lib/python3.13/site-packages/pygments/formatters/svg.py": 11686316844190214147, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/lexers/asn1.py": 15030988816882821134, - "venv/lib/python3.13/site-packages/pydantic/networks.py": 15767095796055034509, - "venv/lib/python3.13/site-packages/numpy/random/__init__.pyi": 13833571330152615376, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/errors.py": 16711622442922413520, - "venv/lib/python3.13/site-packages/_pytest/mark/structures.py": 12237146603608917650, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_concat.py": 16549010224568175202, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_fillna.py": 4610666845291315333, - "venv/lib/python3.13/site-packages/pandas/core/tools/datetimes.py": 13313374952742129866, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_ops.py": 3875181889032080505, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libpq-9b38f5e3.so.5.17": 9680474575710585826, - "venv/lib/python3.13/site-packages/pygments/lexers/praat.py": 54226477748516386, - "backend/src/api/routes/git/_gitea_routes.py": 14644218433873522624, - "venv/bin/pyrsa-keygen": 8222542582170363672, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_modules.py": 16835987029123241300, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_bar.py": 7791068963246005463, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_indexing.py": 5063545640498065297, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_amd64_unix.h": 16679226724722970205, - "venv/lib/python3.13/site-packages/pandas/core/array_algos/masked_reductions.py": 633644255158859565, - "frontend/src/components/tasks/LogFilterBar.svelte": 11464828454938370522, - "venv/lib/python3.13/site-packages/pandas/tests/util/conftest.py": 11143177766735481660, - "venv/lib/python3.13/site-packages/numpy/core/overrides.py": 11228841530565061867, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/datetimes/__init__.py": 15130871412783076140, - "venv/bin/pyrsa-priv2pub": 16766314338551408621, - "venv/lib/python3.13/site-packages/pandas/io/orc.py": 12754290195886557668, - "backend/src/core/superset_client/_base.py": 17832258524611728315, - "backend/src/services/clean_release/publication_service.py": 5215271639026811180, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/memmap.pyi": 15629663272474054909, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_upcast.py": 6496794053681247105, - "backend/src/services/clean_release/compliance_orchestrator.py": 10585043190405940115, - "backend/src/schemas/profile.py": 7072251247808762582, - "venv/lib/python3.13/site-packages/jeepney/tests/test_auth.py": 15125055415702968414, - "venv/lib/python3.13/site-packages/passlib/handlers/django.py": 66638338316838351, - "venv/lib/python3.13/site-packages/pandas/core/computation/eval.py": 3176226283265954028, - "venv/lib/python3.13/site-packages/git/objects/blob.py": 8972374723153211283, - "backend/tests/test_task_persistence.py": 1764267414712263025, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/token.py": 4747545058753492487, - "venv/lib/python3.13/site-packages/pandas/tests/config/test_config.py": 4630144191929135452, - "backend/src/core/utils/superset_context_extractor/_pii.py": 14826864290194304173, - "venv/lib/python3.13/site-packages/passlib/utils/compat/__init__.py": 17369882236606423129, - "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/RECORD": 6349071557579241461, - "venv/lib/python3.13/site-packages/anyio/_core/_asyncio_selector_thread.py": 1760597191437520000, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_excel.py": 5467043641314706635, - "venv/lib/python3.13/site-packages/pygments/lexers/console.py": 18266795648198475028, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_get.py": 10255752806860493777, - "backend/src/api/routes/assistant/_admin_routes.py": 3380446401429936332, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/__init__.py": 13331622784664204066, - "venv/lib/python3.13/site-packages/PIL/ImageStat.py": 18442709639217312689, - "venv/lib/python3.13/site-packages/pygments/lexers/_lua_builtins.py": 12653747099659257203, - "venv/lib/python3.13/site-packages/referencing/tests/test_exceptions.py": 10846225989746274213, - "venv/lib/python3.13/site-packages/pandas/tests/base/test_constructors.py": 2439700578107653984, - "frontend/src/lib/Counter.svelte": 4002153662176313030, - "venv/lib/python3.13/site-packages/anyio/abc/_eventloop.py": 2023190981288767991, - "venv/lib/python3.13/site-packages/attr/__init__.py": 14436902698244718082, - "venv/lib/python3.13/site-packages/httpx/_content.py": 1301854837767395687, - "venv/lib/python3.13/site-packages/pip/_internal/models/target_python.py": 422323125651848065, - "venv/lib/python3.13/site-packages/pandas/tests/resample/test_timedelta.py": 17052461094717926538, - "venv/lib/python3.13/site-packages/pygments/lexers/rdf.py": 14488319292387226505, - "backend/src/plugins/translate/preview.py": 14641288388025627288, - "venv/lib/python3.13/site-packages/ecdsa/errors.py": 11755424742691057760, - "venv/lib/python3.13/site-packages/pyasn1/codec/cer/__init__.py": 15728752901274520502, - "venv/lib/python3.13/site-packages/pip/_internal/commands/search.py": 8292218231992728142, - "venv/lib/python3.13/site-packages/keyring/backends/null.py": 12740996416911604870, - "venv/lib/python3.13/site-packages/passlib/handlers/windows.py": 8969739861295277439, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_rank.py": 14115215895836854973, - "venv/lib/python3.13/site-packages/numpy/linalg/_umath_linalg.pyi": 7674087576653964854, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/apply.py": 3906017176873949738, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_arraypad.py": 4907193085512342162, - "venv/lib/python3.13/site-packages/numpy/lib/_arraypad_impl.pyi": 2186875333484796630, - "venv/lib/python3.13/site-packages/pygments/styles/rrt.py": 9490973359165080360, - "venv/lib/python3.13/site-packages/pygments/lexers/webmisc.py": 9530962803076253849, - "venv/lib/python3.13/site-packages/jeepney/io/tests/test_blocking.py": 6312017389529916004, - "venv/lib/python3.13/site-packages/certifi/__init__.py": 9045580998764948967, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_parquet.py": 11050150015772440923, - "venv/lib/python3.13/site-packages/pydantic/errors.py": 3506829204186508192, - "venv/lib/python3.13/site-packages/numpy/polynomial/laguerre.py": 151202984060101867, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_produces_warning.py": 750781117570375777, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_store.py": 1500300322083708794, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_compat.py": 6032546839876195115, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/_selectable_constructors.py": 84919308502240880, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/compiler.py": 13924519162400330589, - "venv/lib/python3.13/site-packages/pygments/styles/nord.py": 8469538903422182155, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_diff.py": 2216748778018882333, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_asfreq.py": 2701081989348180239, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_set_value.py": 4166411740676564820, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7591/endpoint.py": 13389763148720370546, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/_py_processors.py": 16557117595879208702, - "venv/lib/python3.13/site-packages/pygments/styles/stata_light.py": 16319330015324455277, - "venv/lib/python3.13/site-packages/pygments/lexers/tact.py": 1164971357219795420, - "venv/lib/python3.13/site-packages/ecdsa/curves.py": 812969717084691115, - "frontend/src/lib/stores/__tests__/mocks/environment.js": 16653269108049932073, - "venv/lib/python3.13/site-packages/websockets/asyncio/__init__.py": 15130871412783076140, - "frontend/src/components/tasks/TaskLogPanel.svelte": 7807716346006847037, - "backend/src/api/routes/tasks.py": 4058255810929412793, - "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/METADATA": 16498441272591167134, - "venv/lib/python3.13/site-packages/pandas/compat/numpy/__init__.py": 1037113402100731082, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/state.py": 10500170103357297551, - "venv/lib/python3.13/site-packages/pandas/core/arrays/masked.py": 13977195666947626206, - "venv/lib/python3.13/site-packages/_pytest/_io/pprint.py": 11202117031437223527, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_converter.py": 7709581879446228856, - "backend/src/services/clean_release/repositories/compliance_repository.py": 7402628017251283691, - "venv/lib/python3.13/site-packages/cryptography/hazmat/backends/openssl/backend.py": 9072112660082423691, - "venv/lib/python3.13/site-packages/numpy.libs/libscipy_openblas64_-096271d3.so": 14338208504131470036, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/rec.pyi": 14615751247243515028, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_put.py": 8547660958963625817, - "venv/lib/python3.13/site-packages/urllib3/http2/probe.py": 4069929463671970243, - "backend/src/schemas/__tests__/test_settings_and_health_schemas.py": 13526616847756581689, - "venv/lib/python3.13/site-packages/PIL/CurImagePlugin.py": 17697303205012181529, - "venv/lib/python3.13/site-packages/urllib3/util/timeout.py": 17922362393077965047, - "backend/src/services/reports/__tests__/test_report_service.py": 6045116534626984913, - "venv/lib/python3.13/site-packages/pandas/tests/test_errors.py": 10116716310156374885, - "venv/lib/python3.13/site-packages/numpy/random/_pickle.py": 7774620933480975313, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_inf.py": 8659210689723088321, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/psycopg.py": 8023549900603235601, - "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/licenses/LICENSE": 6965457853328313914, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_npfuncs.py": 13987318550267921389, - "venv/lib/python3.13/site-packages/pandas/_libs/window/indexers.cpython-313-x86_64-linux-gnu.so": 9380059465932560412, - "venv/lib/python3.13/site-packages/charset_normalizer/__main__.py": 8887772289570078278, - "venv/lib/python3.13/site-packages/charset_normalizer/md.py": 2029760700380476138, - "venv/lib/python3.13/site-packages/tzlocal/__init__.py": 11017072814026497364, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/arrayprint.py": 5912583751693302370, - "venv/lib/python3.13/site-packages/uvicorn/_subprocess.py": 13965754246493254447, - "frontend/src/app.html": 10207534912546549080, - "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/utils.py": 8633356372647820801, - "venv/lib/python3.13/site-packages/pydantic/v1/config.py": 12050640083719340958, - "frontend/src/routes/storage/+page.svelte": 5003283673039328108, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_converters.py": 8869440774594127419, - "venv/lib/python3.13/site-packages/pandas/core/_numba/extensions.py": 4132402557064663751, - "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pydantic/experimental/__init__.py": 492399139963056461, - "venv/lib/python3.13/site-packages/pip/_internal/commands/completion.py": 4435231315212798769, - "venv/lib/python3.13/site-packages/pip/_internal/configuration.py": 2563100675379605810, - "venv/lib/python3.13/site-packages/keyring/backends/macOS/__init__.py": 1010937986753403630, - "venv/lib/python3.13/site-packages/httpx/_client.py": 1063593889704982038, - "venv/lib/python3.13/site-packages/numpy/_core/strings.py": 3623885046208831106, - "frontend/build/_app/immutable/chunks/Bq_f12oM.js": 16951726516394160940, - "venv/lib/python3.13/site-packages/jeepney/io/common.py": 15160647397460834508, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/conftest.py": 4124267277224879990, - "venv/lib/python3.13/site-packages/pandas/core/arrays/_mixins.py": 14293848457775268010, - "venv/lib/python3.13/site-packages/PIL/ImageFile.py": 15765130658900167517, - "venv/lib/python3.13/site-packages/authlib/jose/errors.py": 15231759002942436632, - "venv/lib/python3.13/site-packages/numpy/random/bit_generator.cpython-313-x86_64-linux-gnu.so": 2574035311375507054, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_conversion.py": 9159276697339089788, - "venv/lib/python3.13/site-packages/httpcore/_sync/connection.py": 1539337414493486168, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/bitwise_ops.py": 14350609278188982939, - "venv/bin/pip3.13": 13861749540792881808, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc64_linux.h": 4071314837793906998, - "venv/lib/python3.13/site-packages/numpy/testing/_private/extbuild.py": 4984721349618127021, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/numerictypes.py": 5719374287454872555, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_asof.py": 8656532774394927388, - "venv/lib/python3.13/site-packages/_pytest/nodes.py": 515939386237558331, - "venv/lib/python3.13/site-packages/pip/_internal/req/__init__.py": 12266077985828139928, - "backend/logs/app.log.5": 4675888931719915527, - "venv/lib/python3.13/site-packages/pip/_internal/models/installation_report.py": 14534544543677907640, - "venv/lib/python3.13/site-packages/pandas/tests/strings/test_case_justify.py": 8204537837649550062, - "frontend/src/components/tools/ConnectionForm.svelte": 4060811448046342875, - "backend/ss_tools_backend.egg-info/PKG-INFO": 15134841326777953914, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_timezones.py": 15157687716502574038, - "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_openssl.py": 10446441599800965961, - "venv/lib/python3.13/site-packages/pip/_vendor/idna/idnadata.py": 1169745920682458213, - "venv/lib/python3.13/site-packages/numpy/random/_common.cpython-313-x86_64-linux-gnu.so": 12834724130890991478, - "venv/lib/python3.13/site-packages/pip/_internal/network/download.py": 5256731341219268693, - "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/WHEEL": 14929202952940710322, - "venv/lib/python3.13/site-packages/authlib/oauth2/base.py": 12948318231066662045, - "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/authlib/oidc/discovery/models.py": 6924198551176217533, - "venv/lib/python3.13/site-packages/PIL/Jpeg2KImagePlugin.py": 16905402423248975204, - "venv/lib/python3.13/site-packages/pandas/_libs/hashtable.pyi": 15034917167424979097, - "venv/lib/python3.13/site-packages/pip/_internal/operations/prepare.py": 13514839972066861359, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_astype.py": 15768655715952923925, - "venv/lib/python3.13/site-packages/pydantic/dataclasses.py": 12384956801113860889, - "venv/lib/python3.13/site-packages/pandas/io/clipboards.py": 5183212433608887727, - "venv/lib/python3.13/site-packages/yaml/composer.py": 13619070314468518566, - "venv/lib/python3.13/site-packages/pygments/lexers/graphics.py": 13783143864729682444, - "backend/src/api/routes/assistant/_dispatch.py": 3811422882986895565, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Levenshtein.py": 15426828792217499138, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/simple.py": 11050636051019777317, - "venv/lib/python3.13/site-packages/pandas/_libs/properties.cpython-313-x86_64-linux-gnu.so": 7092136701092358552, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ufunclike.pyi": 14623308681600207918, - "frontend/src/routes/profile/__tests__/fixtures/profile.fixtures.js": 2726190101012864209, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/numpy_/test_numpy.py": 9825804096508014886, - "venv/lib/python3.13/site-packages/_pytest/__init__.py": 3769333500910148571, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/incfile.f90": 4348588746493647387, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/token_endpoint.py": 6118770923176099889, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/base_server.py": 9643197259080504542, - "venv/lib/python3.13/site-packages/secretstorage/exceptions.py": 16701299261122689924, - "venv/lib/python3.13/site-packages/pip/_vendor/idna/package_data.py": 3078502124870585867, - "venv/lib/python3.13/site-packages/urllib3/util/url.py": 15992369651965016538, - "frontend/src/lib/stores/__tests__/mocks/state.js": 3616783395254238777, - "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/INSTALLER": 17282701611721059870, - "backend/src/dependencies.py": 9481683691730170837, - "frontend/src/lib/stores/datasetReviewSession.js": 3915919349382526215, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/privatemod.f90": 8999550841368314739, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/abstract_interface/gh18403_mod.f90": 6219951399575860362, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_reorder_levels.py": 10996225584998355824, - "backend/alembic/versions/543d43d752b8_migrate_old_dictionary_entries_and_.py": 10988663686444687907, - "venv/lib/python3.13/site-packages/numpy/lib/_utils_impl.py": 13012775703413938576, - "venv/lib/python3.13/site-packages/pandas/_libs/lib.pyi": 3049004444338566197, - "venv/lib/python3.13/site-packages/pandas/io/excel/_calamine.py": 13354427767660487637, - "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/types.py": 13384984564922916167, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libkeyutils-dfe70bd6.so.1.5": 9414227432578104677, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/format": 12697928260201325593, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/DamerauLevenshtein.py": 17993169044119596860, - "venv/lib/python3.13/site-packages/numpy/f2py/diagnose.pyi": 8607205990821121708, - "venv/lib/python3.13/site-packages/pydantic/_internal/_typing_extra.py": 14843908493613991959, - "venv/lib/python3.13/site-packages/numpy/testing/print_coercion_tables.py": 11758491058846790938, - "venv/lib/python3.13/site-packages/pip/_internal/metadata/importlib/_compat.py": 4677414058216516380, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalarprint.py": 9482100952562512933, - "venv/lib/python3.13/site-packages/pandas/tests/frame/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/casting.py": 12226803454582253097, - "venv/lib/python3.13/site-packages/PIL/_imagingtk.pyi": 18222325750818585549, - "venv/lib/python3.13/site-packages/pygments/lexers/procfile.py": 3860651022061090738, - "backend/src/api/routes/git_schemas.py": 9243114525622127446, - "venv/lib/python3.13/site-packages/pandas/core/methods/describe.py": 17006836623161521092, - "venv/lib/python3.13/site-packages/numpy/random/_bounded_integers.pyi": 14150710436003340771, - "backend/src/services/git/_gitea.py": 3585029718367932699, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_constructors.py": 9767900500541600486, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_arrayprint.py": 13335063050941743734, - "venv/lib/python3.13/site-packages/pygments/styles/friendly.py": 13799024964803741891, - "backend/src/services/git/_sync.py": 16596597562042405179, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_groupby.py": 2738671407653333726, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_cython.py": 14695721638531081573, - "frontend/src/lib/api/translate.js": 18119277537390809809, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/dtype.py": 9342837412280377086, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/wrappers.py": 315409799569602977, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalarmath.py": 9762951701352556447, - "venv/lib/python3.13/site-packages/rsa/py.typed": 11041940105507257053, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/progress_bar.py": 15595912766961891283, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/toml.py": 2225382735669153136, - "backend/src/services/clean_release/__tests__/test_stages.py": 15353751625742444422, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_validate_args.py": 5821040955806257450, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_sort_index.py": 13517883160584591966, - "venv/lib/python3.13/site-packages/pip/_internal/cli/index_command.py": 15798187258841839292, - "venv/lib/python3.13/site-packages/pandas/_libs/algos.pyi": 22405377601029952, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_dtypes.py": 481191723198819568, - "venv/lib/python3.13/site-packages/numpy/core/function_base.py": 12696640832638144604, - "venv/lib/python3.13/site-packages/numpy/_core/_multiarray_umath.cpython-313-x86_64-linux-gnu.so": 2888939866336522308, - "venv/lib/python3.13/site-packages/numpy/random/_bounded_integers.cpython-313-x86_64-linux-gnu.so": 16604252871044637047, - "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/RECORD": 9461427699726982537, - "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/METADATA": 4975169119201255384, - "venv/lib/python3.13/site-packages/pygments/lexers/gcodelexer.py": 13712494327007684318, - "venv/lib/python3.13/site-packages/authlib/integrations/base_client/sync_app.py": 14788764260762890368, - "venv/lib/python3.13/site-packages/authlib/deprecate.py": 3563885283090710443, - "venv/lib/python3.13/site-packages/_pytest/config/exceptions.py": 3199245296954654462, - "venv/lib/python3.13/site-packages/numpy/lib/_user_array_impl.py": 13115032674230220791, - "venv/lib/python3.13/site-packages/pandas/core/arrays/datetimelike.py": 394614958716301173, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_value_attrspec.py": 7741590618654448240, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_cumulative.py": 11752712818547022872, - "venv/lib/python3.13/site-packages/pygments/lexers/jslt.py": 11805394130669644345, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_duplicated.py": 14676058112867601208, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_logical/foo77.f": 16250175448122328648, - "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/RECORD": 2963644632913976754, - "venv/lib/python3.13/site-packages/pygments/lexers/basic.py": 12723021007042054402, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_m68k_gcc.h": 6580173800099450404, - "venv/lib/python3.13/site-packages/pygments/lexers/parsers.py": 12372594608244797844, - "venv/lib/python3.13/site-packages/pandas/tests/apply/test_frame_apply_relabeling.py": 5600949862632014700, - "venv/bin/f2py": 11006035631504708368, - "backend/src/services/clean_release/policy_engine.py": 3180817867716931725, - "venv/lib/python3.13/site-packages/greenlet/tests/_test_extension.cpython-313-x86_64-linux-gnu.so": 1211491156495529145, - "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/__init__.py": 10920760520224795596, - "venv/lib/python3.13/site-packages/smmap/test/test_mman.py": 8977119225749253844, - "venv/lib/python3.13/site-packages/keyring/backends/chainer.py": 12526770797516553385, - "frontend/build/_app/immutable/chunks/EsxH6zEp.js": 876411456806196282, - "venv/lib/python3.13/site-packages/pygments/lexers/scripting.py": 4167168074165473790, - "venv/lib/python3.13/site-packages/_pytest/assertion/rewrite.py": 2425990712758915831, - "venv/lib/python3.13/site-packages/packaging/_parser.py": 13394093358675095309, - "venv/bin/jsonschema": 16516374846693491442, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_timedeltas.py": 4118671843063545221, - "frontend/build/_app/immutable/chunks/B2w4gPsQ.js": 2675139182904252836, - "venv/lib/python3.13/site-packages/numpy/core/__init__.pyi": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalarbuffer.py": 16859789411010045795, - "venv/lib/python3.13/site-packages/numpy/_core/umath.pyi": 11775804030935134661, - "venv/lib/python3.13/site-packages/uvicorn/middleware/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_index.py": 12015738970771703312, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/util.cpython-313-x86_64-linux-gnu.so": 12473903676666680134, - "venv/lib/python3.13/site-packages/greenlet/tests/test_gc.py": 6383305344377882524, - "venv/lib/python3.13/site-packages/pandas/core/indexes/range.py": 3838277376774246164, - "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/misc/extended_precision.pyi": 6541258584648209609, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_period_range.py": 9598872514187524706, - "venv/lib/python3.13/site-packages/packaging/_structures.py": 13086687542872305890, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/licenses/_spdx.py": 17182188548503720124, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/lib_function_base.pyi": 10972502518588022706, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_nlargest_nsmallest.py": 11049656379959696695, - "frontend/src/routes/tools/debug/+page.svelte": 8699044541980877686, - "frontend/build/_app/immutable/nodes/11.DDAAtpef.js": 1940689094142880081, - "backend/src/services/dataset_review/repositories/session_repository.py": 4058785536575367912, - "venv/lib/python3.13/site-packages/rpds/__init__.py": 5862197328247012275, - "venv/lib/python3.13/site-packages/httpcore/_async/__init__.py": 491192949420297153, - "backend/tests/test_translate_jobs.py": 16585154497956900995, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/type_check.pyi": 1599910480066368464, - "venv/lib/python3.13/site-packages/gitdb/test/test_example.py": 3039934065817753241, - "venv/lib/python3.13/site-packages/pip/_vendor/msgpack/__init__.py": 5896924546560667519, - "venv/lib/python3.13/site-packages/jsonschema/tests/typing/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/random/_examples/cffi/extending.py": 9859383893070793914, - "venv/lib/python3.13/site-packages/numpy/_typing/_scalars.py": 17701938912160369190, - "frontend/src/components/Navbar.svelte": 12353588862181566761, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_stride_tricks.py": 1064774375151677743, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_string.py": 15542851215119662814, - "venv/lib/python3.13/site-packages/jaraco/context/__init__.py": 10975097417056278427, - "backend/src/api/__init__.py": 5287505122015708040, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-log1p.csv": 16333455295913164408, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_setops.py": 13440916133402971190, - "frontend/build/_app/immutable/nodes/35.CJsJv8X6.js": 10348890531182330576, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/__init__.py": 4752355175318979033, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_win32_console.py": 18092547253031555768, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_sort_index.py": 14187428934500390688, - "venv/lib/python3.13/site-packages/numpy/_core/overrides.pyi": 13121243505132110607, - "frontend/build/_app/immutable/nodes/13.Cj83yPnA.js": 2832245715688571342, - "venv/lib/python3.13/site-packages/numpy/__config__.pyi": 1422114139050700402, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/rec.pyi": 9140655366667421698, - "docker/frontend.Dockerfile": 9514032629617942525, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/assertion.py": 5287436128235488464, - "venv/lib/python3.13/site-packages/passlib/utils/des.py": 2650106381178222229, - "frontend/src/lib/stores/__tests__/test_datasetReviewSession.js": 12620073136419991103, - "backend/tests/services/clean_release/test_publication_service.py": 17277940125978730159, - "venv/lib/python3.13/site-packages/numpy/_pyinstaller/__init__.pyi": 15130871412783076140, - "venv/lib/python3.13/site-packages/passlib/handlers/misc.py": 18255658377109333797, - "venv/lib/python3.13/site-packages/_pytest/main.py": 931018496411984909, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/data_stmts.f90": 6123192245816905092, - "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/WHEEL": 2357997949040430835, - "venv/lib/python3.13/site-packages/numpy/_core/_rational_tests.cpython-313-x86_64-linux-gnu.so": 13475521165331256711, - "venv/lib/python3.13/site-packages/pandas/core/interchange/column.py": 9498044216416177656, - "venv/lib/python3.13/site-packages/rapidfuzz/fuzz_cpp.cpython-313-x86_64-linux-gnu.so": 14187094676876471848, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/table.py": 16856253349556498073, - "venv/lib/python3.13/site-packages/attr/exceptions.py": 11532465891810816417, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/test_arrow_compat.py": 17818294260710640443, - "venv/lib/python3.13/site-packages/pandas/io/formats/templates/string.tpl": 15796203549130590784, - "venv/lib/python3.13/site-packages/rsa/common.py": 10921682819204792805, - "venv/lib/python3.13/site-packages/PIL/MpegImagePlugin.py": 7726254767528154514, - "venv/lib/python3.13/site-packages/pygments/formatters/pangomarkup.py": 17135620955205945255, - "frontend/src/routes/profile/__tests__/profile-settings-state.integration.test.js": 3674649277514557450, - "venv/lib/python3.13/site-packages/six.py": 948867418687428421, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_to_numpy.py": 9663734591667845940, - "venv/lib/python3.13/site-packages/pandas/_testing/contexts.py": 11714205921752896137, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/base.py": 13634605726041717070, - "backend/src/api/routes/assistant/_dataset_review_dispatch.py": 10108558091463171824, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_contains.py": 17630310725250553247, - "venv/lib/python3.13/site-packages/PIL/GimpGradientFile.py": 9777026720028891154, - "backend/logs/app.log.1": 5033969853318871181, - "venv/bin/fastapi": 12400009726251862770, - "venv/lib/python3.13/site-packages/pandas/core/arrays/sparse/__init__.py": 6758781485387665020, - "venv/lib/python3.13/site-packages/sqlalchemy/util/queue.py": 1289234691048379395, - "venv/lib/python3.13/site-packages/numpy/_core/lib/pkgconfig/numpy.pc": 14713682399742251335, - "venv/lib/python3.13/site-packages/numpy/_core/overrides.py": 252940520997718714, - "venv/lib/python3.13/site-packages/passlib/utils/binary.py": 13123975436674239306, - "backend/src/models/report.py": 9185954411073383239, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_indexing.py": 4063662953863959087, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/__init__.py": 14571251344510763303, - "backend/src/services/clean_release/stages/internal_sources_only.py": 3635582838654262092, - "venv/lib/python3.13/site-packages/pandas/core/construction.py": 10646956189518908985, - "venv/lib/python3.13/site-packages/pyasn1/type/univ.py": 7163437624197683039, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/file_proxy.py": 17695135735211897444, - "venv/lib/python3.13/site-packages/pandas/io/json/_normalize.py": 15938855324762935595, - "venv/lib/python3.13/site-packages/numpy/_pyinstaller/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_concat.py": 4472503149811617440, - "backend/src/services/clean_release/artifact_catalog_loader.py": 16969668877930596278, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/halffloat.h": 16913381775994652343, - "backend/logs/app.log.3": 366149754645105088, - "venv/lib/python3.13/site-packages/numpy/_core/einsumfunc.py": 11393954913234866059, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/jsonschema/tests/test_cli.py": 3173259257428855556, - "venv/lib/python3.13/site-packages/greenlet/platform/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/_pytest/legacypath.py": 7512065138259788426, - "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/licenses/LICENSE.PSF": 13208428090829817329, - "venv/lib/python3.13/site-packages/passlib/tests/test_context.py": 15195609282700801214, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_loc.py": 2127584756555503873, "venv/lib/python3.13/site-packages/pandas/tests/test_sorting.py": 10751098438703044961, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_function_base.py": 15241999259666702619, - "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/numpy/lib/stride_tricks.pyi": 5879290708972838025, - "venv/lib/python3.13/site-packages/greenlet/tests/test_cpp.py": 10982107081267457286, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_filters.py": 17271344092069736999, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_casting_floatingpoint_errors.py": 4301355720042319835, - "venv/lib/python3.13/site-packages/jsonschema/tests/test_jsonschema_test_suite.py": 12733761462141495972, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_scalar_compat.py": 10068989979144965125, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_iat.py": 3570464615146398178, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/dml.py": 5340326603935367534, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arccosh.csv": 5882025767947638305, - "venv/lib/python3.13/site-packages/pandas/tests/strings/test_strings.py": 17398806130402098912, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py": 10093932131797570986, - "venv/lib/python3.13/site-packages/cffi/lock.py": 13507686939203117974, - "backend/src/services/notifications/providers.py": 5844970860921555598, - "venv/lib/python3.13/site-packages/pydantic/parse.py": 13750341403702794392, - "backend/src/services/clean_release/repository.py": 3077298775936340703, - "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/METADATA": 6656194161182818439, - "venv/lib/python3.13/site-packages/pygments/lexers/mosel.py": 1877592344012288438, - "venv/lib/python3.13/site-packages/pygments/lexers/perl.py": 17014528337020572681, - "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/anyio/pytest_plugin.py": 4165280709813718099, - "venv/lib/python3.13/site-packages/numpy/random/_bounded_integers.pxd": 9126772093305708921, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_generator_mt19937.py": 7299586490442539723, - "venv/lib/python3.13/site-packages/pydantic/v1/utils.py": 902648247561165429, - "venv/lib/python3.13/site-packages/attr/converters.pyi": 17675091788614033166, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/test_round.py": 7912161077874611432, - "venv/lib/python3.13/site-packages/apscheduler/schedulers/asyncio.py": 11442130378027628520, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_deprecated_kwargs.py": 10379929935087904974, - "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/METADATA": 11557342107649194148, - "venv/lib/python3.13/site-packages/pygments/lexers/_ada_builtins.py": 16331776886501464264, - "backend/src/models/clean_release.py": 6080208142954589647, - "venv/lib/python3.13/site-packages/authlib/jose/rfc8037/jws_eddsa.py": 833927568269372333, - "venv/lib/python3.13/site-packages/pandas/_libs/hashing.pyi": 5241441378073929968, - "venv/lib/python3.13/site-packages/pandas/core/window/rolling.py": 6316477981414191778, - "venv/lib/python3.13/site-packages/dateutil/zoneinfo/rebuild.py": 1813298167468704155, - "venv/lib/python3.13/site-packages/numpy/linalg/lapack_lite.pyi": 14069586330277925998, - "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/LICENSE": 8423899413955829149, - "venv/lib/python3.13/site-packages/_pytest/config/argparsing.py": 11156052029258891503, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_generic.py": 1098361087956787007, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/lambdas.py": 13377298868598623228, - "venv/lib/python3.13/site-packages/pygments/lexers/haskell.py": 8011341851439354724, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/module_data_docstring.f90": 18245403117623015312, - "venv/lib/python3.13/site-packages/starlette/exceptions.py": 15978811612997200737, - "venv/lib/python3.13/site-packages/ecdsa/eddsa.py": 15966858338456571506, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/signals.py": 10390443713463805090, - "venv/lib/python3.13/site-packages/pandas/core/computation/expressions.py": 10684284531040203747, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_conversion.py": 12184280074522274780, - "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/einsumfunc.pyi": 200771351328522379, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_style.py": 6849613206971398029, - "venv/lib/python3.13/site-packages/pip/_internal/utils/deprecation.py": 7435269308988385470, - "venv/lib/python3.13/site-packages/pygments/formatter.py": 11156733836937309465, - "venv/bin/Activate.ps1": 1196103427939049710, - "frontend/build/_app/immutable/chunks/DbEeTmbI.js": 7478467633861776200, - "venv/lib/python3.13/site-packages/numpy/_pyinstaller/tests/test_pyinstaller.py": 9430762168079129025, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/decl_class.py": 6328411894571735935, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Prefix.py": 4672871351134074735, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libk5crypto-b1f99d5c.so.3.1": 10669523137596317954, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/base.py": 6348888156686452825, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_setops.py": 9991233160569430204, - "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/METADATA": 3618776395694990076, - "venv/lib/python3.13/site-packages/pydantic_settings/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/jsonschema/cli.py": 9813914882090789748, - "venv/lib/python3.13/site-packages/typing_inspection/introspection.py": 13916695705179237047, - "venv/lib/python3.13/site-packages/pandas/core/reshape/tile.py": 17137524194129840833, - "venv/lib/python3.13/site-packages/authlib/common/security.py": 2291133227104016684, - "venv/lib/python3.13/site-packages/jaraco/classes/meta.py": 15930255620248873425, - "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/licenses/LICENSE.rst": 5960743202700406649, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/asymmetric_key.py": 14948661854506285135, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_arithmetic.py": 13304014482504911016, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/mypy.ini": 4763385838822685881, - "venv/lib/python3.13/site-packages/pandas/io/sas/sasreader.py": 12766780210114366112, - "venv/lib/python3.13/site-packages/PIL/IcoImagePlugin.py": 14483964191652228364, - "venv/lib/python3.13/site-packages/httpcore/_backends/trio.py": 8394426709857317737, - "venv/lib/python3.13/site-packages/pyasn1/type/tag.py": 13912273274509186651, - "venv/lib/python3.13/site-packages/pygments/lexers/iolang.py": 10997512999728724130, - "venv/lib/python3.13/site-packages/greenlet/PyGreenlet.cpp": 7360124705400993707, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_explode.py": 16195468192763502449, - "backend/src/scripts/seed_superset_load_test.py": 14952302154142898180, - "backend/src/core/__tests__/test_throttled_scheduler.py": 10949494963069000037, - "venv/lib/python3.13/site-packages/pygments/lexers/openscad.py": 13045825115225232396, - "venv/lib/python3.13/site-packages/numpy/lib/scimath.pyi": 18165025261847276375, - "venv/lib/python3.13/site-packages/greenlet/TStackState.cpp": 16720079760074325527, - "frontend/build/_app/immutable/chunks/BZrlJ68A.js": 11613051606352698644, - "venv/lib/python3.13/site-packages/pillow.libs/libXau-154567c4.so.6.0.0": 4340972632724893052, - "venv/lib/python3.13/site-packages/pip/_vendor/__init__.py": 11734104142988812647, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_pivot.py": 334735766122314163, - "venv/lib/python3.13/site-packages/pydantic/v1/mypy.py": 17076910404409672245, - "venv/lib/python3.13/site-packages/numpy/ma/core.py": 338350463523585845, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_can_hold_element.py": 5406431247116557833, - "venv/lib/python3.13/site-packages/charset_normalizer/__init__.py": 12412600648990392633, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/__ufunc_api.h": 4516275133897904047, - "venv/lib/python3.13/site-packages/websockets/datastructures.py": 5180442040351387281, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_item.py": 17619314877770917080, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_index.py": 1551768441102117466, - "venv/lib/python3.13/site-packages/pygments/lexers/func.py": 12257919753150082714, - "venv/lib/python3.13/site-packages/anyio/to_thread.py": 12767326212338028587, - "venv/lib/python3.13/site-packages/pandas/core/roperator.py": 2587829042468916489, - "venv/lib/python3.13/site-packages/git/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-sin.csv": 1443087417808027268, - "backend/alembic/versions/ed310b33f02c_multi_language_translation_tables.py": 1424919631205944664, - "venv/lib/python3.13/site-packages/pip/_vendor/msgpack/fallback.py": 4260636031748548345, - "venv/lib/python3.13/site-packages/click/exceptions.py": 9734955259447933725, - "venv/lib/python3.13/site-packages/anyio/to_process.py": 16627578499791368365, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_s390_unix.h": 5161826643658505126, - "frontend/src/lib/stores/__tests__/taskDrawer.test.js": 12307450726550947042, - "venv/lib/python3.13/site-packages/pydantic/deprecated/class_validators.py": 2200511838255373813, - "venv/lib/python3.13/site-packages/pandas/tests/frame/conftest.py": 15090201792072045503, - "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/RECORD": 1051307407005554409, - "venv/lib/python3.13/site-packages/pygments/lexers/_openedge_builtins.py": 4979988747924637739, - "venv/lib/python3.13/site-packages/jsonschema/__main__.py": 18136546137047911521, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/__init__.py": 11009955319062089477, - "venv/lib/python3.13/site-packages/rapidfuzz/__init__.py": 12462153783886855740, - "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/__init__.py": 5895297076117159245, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py": 14995426099932276090, - "venv/lib/python3.13/site-packages/pandas/core/window/doc.py": 4204367435727844696, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_methods.py": 12694335778731418517, - "venv/lib/python3.13/site-packages/attr/filters.py": 14156141488477438777, - "venv/lib/python3.13/site-packages/attr/validators.pyi": 15579625896285277321, - "venv/lib/python3.13/site-packages/greenlet/TGreenlet.hpp": 6993553444981671786, - "venv/lib/python3.13/site-packages/numpy/f2py/auxfuncs.pyi": 10077265709692546934, - "venv/lib/python3.13/site-packages/numpy/f2py/cb_rules.py": 11397521081216402594, - "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/RECORD": 10264342250058307301, - "venv/lib/python3.13/site-packages/keyring/http.py": 1408024724330078009, - "venv/lib/python3.13/site-packages/idna/idnadata.py": 200840860629525970, - "venv/lib/python3.13/site-packages/httpx/__init__.py": 6383055693166097719, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_round.py": 8336918400205461173, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_factorize.py": 17995673140216929494, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_delete.py": 5958440386632506587, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/accumulate.py": 5780225603819849877, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_drop.py": 11341278355685583133, - "venv/lib/python3.13/site-packages/pandas/tests/io/xml/test_xml.py": 6906547816798138000, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_function_base.py": 16624804022675533740, - "venv/lib/python3.13/site-packages/pandas/io/formats/csvs.py": 17223598248259691292, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/dml.py": 17308908394336670576, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/declarative/__init__.py": 2436195108273216959, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_warnings.py": 13071210233876253749, - "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_ufunclike.py": 13752381070039804384, - "venv/lib/python3.13/site-packages/charset_normalizer/cli/__init__.py": 9702005609966176016, - "frontend/src/components/RepositoryDashboardGrid.svelte": 17100594220040865069, - "frontend/build/_app/immutable/entry/start.BFyEuKf2.js": 2516797770297376306, - "venv/lib/python3.13/site-packages/pandas/core/internals/blocks.py": 6448406236246476333, - "backend/src/models/translate.py": 15814361247636396225, - "venv/lib/python3.13/site-packages/cryptography/x509/name.py": 15338066443257445241, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/linalg.pyi": 269481015135399748, - "venv/lib/python3.13/site-packages/passlib/tests/test_utils_pbkdf2.py": 17016810698578473773, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/resolver.py": 9931394693284776391, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/astype.py": 9925183787411796679, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_partial_indexing.py": 4132091154215910029, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_update.py": 9098258267671647783, - "venv/lib/python3.13/site-packages/greenlet/greenlet_compiler_compat.hpp": 465436185632243991, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/scripts.py": 8759605303886441976, - "venv/lib/python3.13/site-packages/jose/backends/ecdsa_backend.py": 2684176977865283060, - "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/WHEEL": 2823347510103459191, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_indexing.py": 14717641281147152159, - "venv/lib/python3.13/site-packages/pycparser/_build_tables.py": 569728086093219621, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/fields.py": 10090313990429695520, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/requests.py": 14847842002253212098, - "venv/lib/python3.13/site-packages/pandas/core/ops/docstrings.py": 3721650738518391256, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_downcast.py": 10399746645741452200, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_polynomial.py": 777287409151500309, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/requests.py": 1102963893193914979, - "venv/lib/python3.13/site-packages/numpy/random/_examples/cffi/parse.py": 2427019397934437210, - "venv/lib/python3.13/site-packages/sqlalchemy/event/__init__.py": 16182540084654095394, - "venv/lib/python3.13/site-packages/numpy/polynomial/_polytypes.pyi": 7528777458251688517, - "venv/lib/python3.13/site-packages/passlib/handlers/ldap_digests.py": 8658529184965699766, - "venv/lib/python3.13/site-packages/_pytest/unittest.py": 10848803633530338204, - "venv/lib/python3.13/site-packages/pip/_internal/utils/setuptools_build.py": 13594639612360346306, - "venv/lib/python3.13/site-packages/pydantic/types.py": 785880062468926292, - "venv/lib/python3.13/site-packages/pydantic/v1/datetime_parse.py": 10467114625921924095, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/util.py": 10640907133050862176, - "venv/lib/python3.13/site-packages/PIL/ImageSequence.py": 16459886231802763032, - "frontend/src/lib/components/assistant/__tests__/assistant_chat.integration.test.js": 13047006106880121194, - "backend/src/api/routes/git/__init__.py": 9989336734947065016, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/shape_base.pyi": 10291383658131961346, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/util.py": 1520899420719605380, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/content": 12465123883296161860, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_common.h": 14732581790650403546, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_reset_index.py": 14490875921264161445, - "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/PIL/PcfFontFile.py": 8867692961463798279, - "backend/src/services/clean_release/enums.py": 7365670144442292671, - "venv/lib/python3.13/site-packages/click-8.3.1.dist-info/WHEEL": 8600534672961461758, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_readlines.py": 15928258358116399067, - "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/WHEEL": 12054782055241301457, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/__init__.py": 15130871412783076140, - "backend/src/services/reports/type_profiles.py": 8365014439236739390, - "venv/lib/python3.13/site-packages/pygments/lexers/tnt.py": 4657130609241407766, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/unicode_comment.f90": 11326797716215117266, - "backend/src/services/git/_url.py": 15729080796864187707, - "venv/lib/python3.13/site-packages/fastapi/security/__init__.py": 1345944646061334046, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/mixed/foo_free.f90": 8821492401453445757, - "frontend/build/_app/immutable/chunks/Mn1vnqBO.js": 5859696651271826291, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/__init__.py": 2246704695838251846, - "venv/lib/python3.13/site-packages/cffi/vengine_cpy.py": 10684192171370688079, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/data_with_comments.f": 3980077223827767077, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_formats.py": 10263085276270778330, - "backend/src/api/routes/git/_router.py": 8701683910668868827, - "venv/lib/python3.13/site-packages/idna/uts46data.py": 5698404064763638591, - "venv/lib/python3.13/site-packages/pandas/io/parsers/base_parser.py": 15148258424429418380, - "venv/lib/python3.13/site-packages/jsonschema/tests/test_deprecations.py": 16367195310156359251, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_json_table_schema.py": 14454690938977069049, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/npyio.pyi": 14147712281086095231, - "venv/lib/python3.13/site-packages/git/index/base.py": 641556304637379636, - "venv/lib/python3.13/site-packages/yaml/scanner.py": 8554164423030397366, - "venv/lib/python3.13/site-packages/pandas/tests/test_common.py": 8987965560487646705, - "backend/src/api/routes/__tests__/test_connections_routes.py": 18044815559820610056, - "venv/lib/python3.13/site-packages/pandas/tests/window/moments/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/unused_registry.py": 10153622516112471812, - "venv/lib/python3.13/site-packages/rapidfuzz/_feature_detector_cpp.cpython-313-x86_64-linux-gnu.so": 16337157355618216738, - "venv/lib/python3.13/site-packages/git/refs/head.py": 16271128287606894569, - "venv/lib/python3.13/site-packages/pygments/lexers/_scheme_builtins.py": 5722444920479775453, - "frontend/src/lib/components/health/HealthMatrix.svelte": 16085178486788467644, - "venv/lib/python3.13/site-packages/ecdsa/keys.py": 10281170640484982154, - "venv/lib/python3.13/site-packages/numpy/random/_pcg64.pyi": 14785496840737986355, - "venv/lib/python3.13/site-packages/pandas/core/methods/to_dict.py": 4653628436393663485, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_get_value.py": 389530025760539462, - "venv/lib/python3.13/site-packages/passlib/pwd.py": 14727089240749133060, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/comparisons.py": 7327629888601690759, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_categorical.py": 12897027063618569795, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_partial.py": 15129217814262096441, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_isetitem.py": 14895552405828068177, - "venv/lib/python3.13/site-packages/apscheduler/executors/tornado.py": 6635576580306890718, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/type_api.py": 10299750173867399533, - "backend/src/plugins/llm_analysis/__init__.py": 9254812131558249924, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_read_errors.py": 4559483361564472117, - "backend/src/plugins/git_plugin.py": 12799126783361308681, - "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/packaging/licenses/__init__.py": 15879493360260891293, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_swaplevel.py": 3584770792383216626, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_datetimelike.py": 5317270578347271174, - "venv/lib/python3.13/site-packages/authlib/oidc/registration/claims.py": 4161520263207458028, - "venv/lib/python3.13/site-packages/pip/_internal/models/index.py": 2839270935469791607, - "venv/lib/python3.13/site-packages/numpy/_core/_ufunc_config.pyi": 9291444594181042963, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_extension.py": 17245018204352082978, - "venv/lib/python3.13/site-packages/pandas/tests/test_multilevel.py": 10500316376521962061, - "venv/lib/python3.13/site-packages/PIL/AvifImagePlugin.py": 1872412416505003364, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/cookies.py": 4997226307650350876, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/lexers/__init__.py": 7316739139609490520, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_copy.py": 12902342280706372953, - "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/mean_.py": 3641008929910474457, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_isin.py": 8136794647591341784, - "venv/lib/python3.13/site-packages/jsonschema/tests/test_validators.py": 14178329676643700151, - "frontend/build/_app/env.js": 8815854342083926790, - "venv/lib/python3.13/site-packages/pygments/lexers/ml.py": 7510453371068494792, - "venv/lib/python3.13/site-packages/greenlet/tests/fail_switch_three_greenlets.py": 10923534235697247269, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_data.py": 2397937327057341, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/bitwise_ops.pyi": 15598239133239520456, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/pymysql.py": 12923876736367229454, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/layout.py": 11554376181083053156, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_reshape.py": 5572426834735088916, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/result.py": 12262298207886620908, - "venv/lib/python3.13/site-packages/numpy/rec/__init__.pyi": 1782063765067823837, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/random.pyi": 1808392097712315455, - "venv/lib/python3.13/site-packages/pygments/lexers/typst.py": 8883289726601667104, - "venv/lib/python3.13/site-packages/pydantic/deprecated/decorator.py": 8609500261161560793, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/schema.py": 5391655516870611151, - "venv/lib/python3.13/site-packages/keyring/backends/libsecret.py": 11621088556661567306, - "venv/lib/python3.13/site-packages/pyasn1/codec/native/__init__.py": 15728752901274520502, - "venv/lib/python3.13/site-packages/pandas/api/__init__.py": 1145864207387918302, - "venv/lib/python3.13/site-packages/pandas/tests/extension/list/test_list.py": 4538625980063375611, - "frontend/src/lib/stores/sidebar.js": 8409084311045055153, - "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/util.py": 18279366777506483176, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi": 7385072545198817771, - "venv/lib/python3.13/site-packages/gitdb/db/pack.py": 13460574575424209609, - "venv/lib/python3.13/site-packages/pandas/tests/apply/common.py": 1113935558495455492, - "venv/lib/python3.13/site-packages/pyasn1/codec/ber/decoder.py": 12653470212935324322, - "venv/lib/python3.13/site-packages/_pytest/_py/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_numpy.py": 4073641912755651877, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/reduce.py": 14976712507854054776, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/_py_util.py": 12836989066113400538, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/instrumentation.py": 14882045449667214075, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/request.py": 10492090878715126550, - "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_internal/distributions/base.py": 16714704323747942110, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_timezones.py": 8042029178538467146, - "frontend/src/routes/admin/roles/+page.svelte": 12213033002088655108, - "backend/src/app.py": 1030174853208129525, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/resource_protector.py": 11539718751174980004, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/OSA_py.py": 2494967094400350388, - "venv/lib/python3.13/site-packages/pygments/formatters/rtf.py": 5850574956253747036, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_formats.py": 3525283725314253763, - "frontend/src/lib/components/ui/MultiSelect.svelte": 13967450784969599105, - "backend/src/services/dataset_review/repositories/__tests__/test_session_repository.py": 18317443814450756666, - "venv/lib/python3.13/site-packages/pygments/lexers/unicon.py": 1155454077052833821, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/_public_dtype_api_table.h": 8933016749764021181, - "venv/lib/python3.13/site-packages/idna/__init__.py": 6489437464172324468, - "venv/lib/python3.13/site-packages/pillow.libs/libsharpyuv-95d8a097.so.0.1.2": 5320647379976541240, - "venv/lib/python3.13/site-packages/pandas/core/interchange/dataframe_protocol.py": 12857147443873820285, - "venv/lib/python3.13/site-packages/pandas/_libs/json.pyi": 10989301892466547548, - "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/WHEEL": 7795541085444298627, - "venv/lib/python3.13/site-packages/numpy/ctypeslib/_ctypeslib.pyi": 10360538024015064036, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/baked.py": 6911138208603240867, - "venv/lib/python3.13/site-packages/websockets/legacy/server.py": 5390090919193784186, - "venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/_pytest/_argcomplete.py": 3586514420674089892, - "venv/lib/python3.13/site-packages/cryptography/x509/verification.py": 1079694784860492682, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_nonunique_indexes.py": 15468078561381939405, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_repeat.py": 16971624301893342868, - "venv/lib/python3.13/site-packages/PIL/_imagingmorph.cpython-313-x86_64-linux-gnu.so": 11171810899664471137, - "backend/src/api/routes/dataset_review.py": 3689614039699023362, - "venv/lib/python3.13/site-packages/iniconfig/__init__.py": 12623265899068631092, - "venv/lib/python3.13/site-packages/apscheduler/jobstores/mongodb.py": 5310116401542377882, - "venv/lib/python3.13/site-packages/pygments/styles/xcode.py": 14124729477757765388, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_null_file.py": 6885565068723353414, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_dlpack.py": 7908102652282922952, - "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/code.py": 9700811903057180696, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/array_constructors.pyi": 4993066609272697419, - "venv/lib/python3.13/site-packages/pandas/io/pickle.py": 5418646169167774144, - "venv/lib/python3.13/site-packages/pygments/lexers/stata.py": 7249891970174867363, - "venv/lib/python3.13/site-packages/anyio/_core/_synchronization.py": 7050001375718876608, - "venv/lib/python3.13/site-packages/pygments/lexers/elpi.py": 5736689911777977822, - "backend/src/api/routes/profile.py": 16065196073183170065, - "backend/src/scripts/create_admin.py": 11258281301071059367, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/groupby.py": 12749730954888633240, - "venv/lib/python3.13/site-packages/pip/_internal/metadata/pkg_resources.py": 14271077832999370068, - "venv/lib/python3.13/site-packages/_pytest/threadexception.py": 10464996327276961035, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_ufunc.py": 669019702416198889, - "venv/lib/python3.13/site-packages/passlib/crypto/des.py": 14699826058580193499, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/test_reductions.py": 8258332365408303156, - "venv/lib/python3.13/site-packages/certifi/__main__.py": 13417658012431779061, - "venv/lib/python3.13/site-packages/pandas/io/sas/sas_constants.py": 14492427939536692313, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_construct_object_arr.py": 4725156416161661681, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/exc.py": 1448374203226983757, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/assumed_shape/foo_use.f90": 2657057181044959432, - "venv/lib/python3.13/site-packages/PIL/MicImagePlugin.py": 13540523615822507963, - "venv/lib/python3.13/site-packages/uvicorn/_compat.py": 2593000889619875759, - "venv/lib/python3.13/site-packages/packaging/markers.py": 11602308203217831543, - "venv/lib/python3.13/site-packages/pydantic/plugin/_schema_validator.py": 857640644840976862, - "venv/lib/python3.13/site-packages/requests/models.py": 9104898985870652256, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/ufuncobject.h": 5112456258078662871, - "venv/lib/python3.13/site-packages/pygments/lexers/rust.py": 8605690716125176329, - "backend/src/api/routes/environments.py": 6892252055515346170, - "frontend/src/components/TaskLogViewer.svelte": 14310893311791502213, - "backend/src/plugins/translate/_token_budget.py": 7640610492252771386, - "backend/src/api/routes/dashboards/_helpers.py": 16683849709430404975, - "venv/lib/python3.13/site-packages/numpy/_typing/__init__.py": 18242238390157333247, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/pickleable.py": 7712040860601343075, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_get_set.py": 14375107704596905983, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/usecols/test_strings.py": 7379299916213794543, - "venv/lib/python3.13/site-packages/gitdb/pack.py": 7828244200815444166, - "venv/lib/python3.13/site-packages/pyasn1/codec/ber/__init__.py": 15728752901274520502, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_x64_msvc.h": 17581467060937454503, - "venv/lib/python3.13/site-packages/httpcore/_async/interfaces.py": 18250473733460571816, - "venv/lib/python3.13/site-packages/pygments/lexers/apl.py": 16767474673619335683, - "backend/src/api/routes/assistant/_history.py": 16318881016647964852, - "venv/lib/python3.13/site-packages/PIL/features.py": 3481835375516797487, - "backend/src/models/llm.py": 14295626272101666880, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/panel.py": 17832701246248000383, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/pcg64dxsm-testset-1.csv": 9129730764673472086, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_arithmetic.py": 13320788210970132239, - "venv/lib/python3.13/site-packages/pygments/lexers/rita.py": 8105458520706587972, - "venv/lib/python3.13/site-packages/pandas/core/arrays/integer.py": 7775055011080964709, - "frontend/build/_app/immutable/chunks/DVeZEXwF.js": 16066942382203165258, - "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/METADATA": 3814104793342833872, - "venv/lib/python3.13/site-packages/pygments/lexers/futhark.py": 10102535873433759899, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_numpy_array_equal.py": 2089426719574307767, - "venv/lib/python3.13/site-packages/pip/_internal/network/auth.py": 6159841209618753296, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/datasource.pyi": 16752667877866430577, - "frontend/build/_app/immutable/nodes/29.C8yatGIj.js": 7788955111119786799, - "venv/lib/python3.13/site-packages/greenlet/tests/fail_cpp_exception.py": 11851283355840567373, - "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_julian_date.py": 850085757774959003, - "venv/lib/python3.13/site-packages/numpy/testing/overrides.py": 11819432487830192978, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/__init__.py": 16146480198665866916, - "venv/lib/python3.13/site-packages/PIL/XbmImagePlugin.py": 12256750478268246137, - "venv/lib/python3.13/site-packages/numpy/typing/__init__.pyi": 10058149460938883683, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/test_holiday.py": 8541197358003711845, - "frontend/src/lib/stores/__tests__/assistantChat.test.js": 11822882112255142537, - "venv/lib/python3.13/site-packages/pygments/lexers/vyper.py": 6477559246065994199, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py": 14571251344510763303, - "venv/lib/python3.13/site-packages/uvicorn/config.py": 17317823506849880916, - "venv/lib/python3.13/site-packages/urllib3-2.6.2.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/numpy/f2py/src/fortranobject.c": 2519219289444051739, - "venv/lib/python3.13/site-packages/pandas/_libs/window/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/__init__.py": 2397613800206300456, - "backend/logs/app.log.4": 11598841362861329809, - "venv/lib/python3.13/site-packages/websockets/http11.py": 11022534176398053082, - "venv/lib/python3.13/site-packages/dateutil/parser/isoparser.py": 18015430204844361067, - "venv/lib/python3.13/site-packages/jaraco/functools/__init__.py": 15334412919279565528, - "venv/lib/python3.13/site-packages/greenlet/greenlet_msvc_compat.hpp": 12504008513945919250, - "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/METADATA": 13633829318759752611, - "venv/lib/python3.13/site-packages/numpy/exceptions.py": 10981418339663118022, - "venv/lib/python3.13/site-packages/greenlet/tests/test_generator.py": 14156412320760444199, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-exp2.csv": 10950201682596097452, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/dtypes/test_categorical.py": 9615571808720893016, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_cpu_features.py": 955759654189150497, - "venv/lib/python3.13/site-packages/pygments/styles/fruity.py": 7925019839136894457, - "venv/lib/python3.13/site-packages/numpy/random/__init__.pxd": 7355772669172093814, - "venv/lib/python3.13/site-packages/keyring/testing/util.py": 11195868112805409554, - "venv/lib/python3.13/site-packages/pip/_vendor/idna/core.py": 2199607970842512780, - "venv/lib/python3.13/site-packages/pandas/io/formats/templates/typst.tpl": 5055419073126793137, - "venv/lib/python3.13/site-packages/pygments/styles/gruvbox.py": 9489017112545247388, - "venv/lib/python3.13/site-packages/numpy/linalg/__init__.py": 15647756307608142577, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_log_render.py": 18076931145915444473, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py": 7852235015155694076, - "venv/lib/python3.13/site-packages/numpy/_core/_operand_flag_tests.cpython-313-x86_64-linux-gnu.so": 12296711931541109624, - "venv/lib/python3.13/site-packages/yaml/cyaml.py": 9157893742065554693, - "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/WHEEL": 7454950858448014158, - "venv/lib/python3.13/site-packages/pydantic/_internal/_core_metadata.py": 5704137311404303574, - "venv/lib/python3.13/site-packages/pyasn1/codec/cer/encoder.py": 3261739474564948330, - "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/INSTALLER": 17282701611721059870, - "scripts/build_offline_docker_bundle.sh": 18334040481218597997, - "venv/lib/python3.13/site-packages/attr/_version_info.py": 3850449653182103457, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Levenshtein.pyi": 12925006572914814268, - "venv/lib/python3.13/site-packages/pip/_internal/utils/appdirs.py": 539688259643447487, - "venv/lib/python3.13/site-packages/pydantic/_internal/_validate_call.py": 4078236486720616395, - "venv/lib/python3.13/site-packages/pandas/_libs/writers.pyi": 10494773026133244183, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_sparse.py": 14343335524508450058, - "venv/lib/python3.13/site-packages/httpcore/_ssl.py": 6665087294869504820, - "venv/lib/python3.13/site-packages/pycparser/ply/cpp.py": 1665654017894222868, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc64_aix.h": 8706347224105630468, - "venv/lib/python3.13/site-packages/pip/_internal/locations/__init__.py": 8147012724727395488, - "venv/lib/python3.13/site-packages/urllib3/exceptions.py": 9687187566231423953, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_f2py2e.py": 8571347023940385644, - "venv/lib/python3.13/site-packages/ecdsa/test_jacobi.py": 1249543655136186433, - "venv/lib/python3.13/site-packages/tzlocal-5.3.1.dist-info/WHEEL": 5076688779572520166, - "venv/lib/python3.13/site-packages/pygments/styles/lightbulb.py": 12433916113773598760, - "backend/src/scripts/clean_release_tui.py": 7514485779948079886, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_map.py": 4761668276463940924, - "frontend/src/lib/components/dataset-review/ValidationFindingsPanel.svelte": 2403276296409515448, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7662/__init__.py": 17454177076687312424, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7009/parameters.py": 1647490964392525933, - "backend/src/services/__tests__/test_rbac_permission_catalog.py": 5190751380981933250, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/comparisons.pyi": 9338084426044982038, - "backend/src/core/plugin_base.py": 11649432288706102843, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/scalars.pyi": 3130685119595923378, - "venv/lib/python3.13/site-packages/pandas/tests/strings/test_api.py": 10514120091521589282, - "backend/src/services/__tests__/test_llm_plugin_persistence.py": 2413185961042174999, - "venv/lib/python3.13/site-packages/pandas/core/indexes/base.py": 496956059910401121, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arrayterator.pyi": 7439348655567232744, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalar_ctors.py": 631744056514965940, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/__ufunc_api.c": 11535375410441493281, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_quarter.py": 11099677280196239755, - "venv/lib/python3.13/site-packages/pygments/lexers/hdl.py": 6772823608124143123, - "venv/lib/python3.13/site-packages/numpy/matrixlib/defmatrix.pyi": 17903213294220335051, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_logical.py": 6242457515917145500, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh23598Warn.f90": 7589258200398878529, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_groupby_dropna.py": 3620959914439334511, - "venv/lib/python3.13/site-packages/pygments/token.py": 6987328478153445584, - "venv/lib/python3.13/site-packages/sqlalchemy/pool/__init__.py": 4337679908168515343, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_emoji_replace.py": 8166203537440976347, - "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/cffi/cffi_opcode.py": 8411344362428947268, - "venv/lib/python3.13/site-packages/click/shell_completion.py": 5858110451169927373, - "venv/lib/python3.13/site-packages/cryptography/hazmat/asn1/__init__.py": 4290669519959060720, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/resource_protector.py": 9147671710440653567, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/json.py": 15373167408891671190, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/datetimes/test_constructors.py": 3440572832185940666, - "venv/lib/python3.13/site-packages/pandas/core/_numba/executor.py": 2205130934861422769, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_describe.py": 362374422228593281, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/string/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/METADATA": 6557531107125553027, - "venv/lib/python3.13/site-packages/keyring/credentials.py": 14515496037842724455, - "venv/lib/python3.13/site-packages/idna-3.11.dist-info/RECORD": 11541505360973709787, - "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/pygments/lexers/sql.py": 15566262873151658034, - "backend/src/api/routes/dashboards/_listing_routes.py": 1750634467656242702, - "venv/lib/python3.13/site-packages/jsonschema/tests/test_format.py": 1947585389944838076, - "venv/lib/python3.13/site-packages/numpy/testing/__init__.py": 2831275254313750978, - "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/WHEEL": 2357997949040430835, - "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/METADATA": 1862765380818895200, - "venv/lib/python3.13/site-packages/numpy/f2py/_backends/meson.build.template": 9935732744553086652, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/filewrapper.py": 17786759398959482432, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_timedeltas.py": 1175883320586860493, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/attributes.py": 5068814626384132900, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py": 6152202474302791232, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/names.py": 705261158040926128, - "backend/src/api/auth.py": 16577622718621592661, - "venv/lib/python3.13/site-packages/pygments/lexers/snobol.py": 414946539602526513, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/client_credentials.py": 256154392957334831, - "venv/lib/python3.13/site-packages/httpx/_config.py": 8800538050293498928, - "venv/lib/python3.13/site-packages/numpy/_core/getlimits.py": 7842717073693731093, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_set_value.py": 17480833128128893268, - "venv/lib/python3.13/site-packages/dateutil/utils.py": 7067026915090881998, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ufunclike.py": 12077553320554633586, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_month.py": 14597470286884431085, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/dotenv.py": 5872848911499749500, - "venv/lib/python3.13/site-packages/pip/_internal/network/session.py": 293771075394156394, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/core": 6622984507059115517, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_value_counts.py": 15633881149834196174, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/tree.py": 4991806303236874829, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/pcg64-testset-1.csv": 16078879475693341404, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_comparisons.py": 2764694394636775220, - "venv/lib/python3.13/site-packages/git/types.py": 13960696584646279283, - "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/response.py": 9594165377270752433, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_custom_dtypes.py": 12322100282959605055, - "backend/tests/test_log_persistence.py": 11073909507089982937, - "backend/src/api/routes/storage.py": 15226523387798112491, - "dist/docker/frontend.v1.0.0-rc2-docker.tar": 1705027241971137937, - "frontend/build/_app/immutable/chunks/rZT47aGk.js": 16973416575478295852, - "venv/lib/python3.13/site-packages/pandas/core/array_algos/take.py": 3170415198452493008, - "venv/lib/python3.13/site-packages/passlib/tests/test_apache.py": 12515001998770565375, - "venv/lib/python3.13/site-packages/gitdb/db/git.py": 13552203494737962026, - "venv/lib/python3.13/site-packages/numpy/f2py/use_rules.pyi": 18395688193449688360, - "venv/lib/python3.13/site-packages/passlib/tests/test_utils_md4.py": 15270721044910698449, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_libfrequencies.py": 16302676625743291594, - "venv/lib/python3.13/site-packages/pandas/_testing/_io.py": 14264259716673715185, - "venv/lib/python3.13/site-packages/pydantic/_internal/_signature.py": 17075003201648027250, - "venv/lib/python3.13/site-packages/pip/_internal/operations/build/wheel.py": 7728646523045538484, - "frontend/src/components/MissingMappingModal.svelte": 11264881226066623003, - "backend/src/core/cot_logger.py": 3113579786931231764, - "venv/lib/python3.13/site-packages/uvicorn/workers.py": 7213290478118197047, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/heuristics.py": 1422768761337195135, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_formats.py": 13752366280743004410, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_size.py": 12449835577388069407, - "venv/lib/python3.13/site-packages/pandas/tests/config/test_localization.py": 9524481848419616725, - "venv/lib/python3.13/site-packages/pip/_vendor/certifi/cacert.pem": 15624463015628939254, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_indexing.py": 4334578480714520407, - "venv/lib/python3.13/site-packages/httpcore/_sync/http11.py": 5947339024588907032, - "venv/lib/python3.13/site-packages/python_multipart/multipart.py": 5677735019292537174, - "venv/lib/python3.13/site-packages/websockets/speedups.cpython-313-x86_64-linux-gnu.so": 10941658763526595989, - "venv/lib/python3.13/site-packages/greenlet/tests/fail_switch_two_greenlets.py": 11156275429806391390, - "venv/lib/python3.13/site-packages/click/_utils.py": 16491408631876616271, - "venv/lib/python3.13/site-packages/gitdb/db/loose.py": 17467413653016810702, - "venv/lib/python3.13/site-packages/numpy/__init__.pxd": 13527602429695748857, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/spinner.py": 5564789578696016201, - "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/METADATA": 3179724236887811680, - "venv/lib/python3.13/site-packages/numpy/f2py/common_rules.pyi": 2104100025130695295, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/ansi.py": 948249091929588017, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py": 3960540415600165213, - "venv/lib/python3.13/site-packages/fastapi/background.py": 16533963020573865222, - "venv/lib/python3.13/site-packages/numpy/lib/_index_tricks_impl.py": 13574106461973044500, - "venv/lib/python3.13/site-packages/rpds/__init__.pyi": 12037474523494903587, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/styles/__init__.py": 9482393721453335937, - "venv/lib/python3.13/site-packages/pandas/_libs/algos.cpython-313-x86_64-linux-gnu.so": 15706331654215762588, - "venv/lib/python3.13/site-packages/fastapi/security/utils.py": 2616633862993539558, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/flatiter.pyi": 6885824496927786205, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/routines/subrout.pyf": 4660465266594480405, - "venv/lib/python3.13/site-packages/itsdangerous/_json.py": 4872267972688724005, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_replace.py": 4953527309376861139, - "venv/lib/python3.13/site-packages/pygments/styles/borland.py": 17570558758932580659, - "venv/lib/python3.13/site-packages/PIL/ImageDraw2.py": 13610162107593278891, - "venv/lib/python3.13/site-packages/pandas/tests/io/xml/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/starlette/_exception_handler.py": 9892172656847200124, - "venv/lib/python3.13/site-packages/numpy/polynomial/chebyshev.pyi": 5269136485429780038, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/__init__.py": 371829636800613137, - "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/__main__.py": 11917862648584782420, - "venv/lib/python3.13/site-packages/PIL/FpxImagePlugin.py": 18245056799205041866, - "venv/lib/python3.13/site-packages/packaging/version.py": 13470993734108007779, - "frontend/build/_app/immutable/chunks/ChLJ80AE.js": 8890526053620586309, - "frontend/build/_app/immutable/chunks/CM6FoQ_S.js": 4999908718789214708, - "venv/lib/python3.13/site-packages/numpy/fft/tests/test_pocketfft.py": 18025182347224285050, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/gh25337/data.f90": 13833561828029901615, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_round.py": 1299086932398824299, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/formatters/__init__.py": 3247870839323520754, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_string.py": 10141066226714805009, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/dtype_api.h": 17040779421165903019, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/progress.py": 5951745251426856398, - "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/WHEEL": 9100692507274722091, - "venv/lib/python3.13/site-packages/numpy/ma/tests/test_arrayobject.py": 9578555381494297966, - "venv/lib/python3.13/site-packages/numpy/f2py/__init__.pyi": 3566887042414273892, - "venv/lib/python3.13/site-packages/pandas/core/arrays/sparse/array.py": 3517673819082574078, - "venv/lib/python3.13/site-packages/pygments/styles/coffee.py": 17584414468786822371, - "venv/lib/python3.13/site-packages/pygments/lexers/json5.py": 9736695186548899403, - "venv/lib/python3.13/site-packages/packaging/licenses/_spdx.py": 16440031845441362300, - "backend/src/api/routes/settings.py": 1899503837234642538, - "backend/src/services/dataset_review/clarification_engine.py": 13640221555439415988, - "venv/lib/python3.13/site-packages/fastapi/security/http.py": 8465565118555441408, - "venv/lib/python3.13/site-packages/apscheduler/schedulers/gevent.py": 4811840397266785551, - "venv/lib/python3.13/site-packages/fastapi/middleware/asyncexitstack.py": 7733356903092860495, - "frontend/src/routes/translate/dictionaries/[id]/+page.svelte": 14484918248250087235, - "venv/lib/python3.13/site-packages/secretstorage/defines.py": 9590959702075554048, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_kurt.py": 2898715045429898166, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/conftest.py": 12368472226536104037, - "backend/src/services/clean_release/__tests__/test_compliance_orchestrator.py": 6332087444349093252, - "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/WHEEL": 11630459739183915695, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh23533.f": 12558972205518833024, - "venv/lib/python3.13/site-packages/_pytest/reports.py": 13930709338396699351, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_series.py": 16418555817529711079, - "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/RECORD": 1689342214025776630, - "venv/lib/python3.13/site-packages/itsdangerous/url_safe.py": 13454753900702274266, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_real.f90": 9097998998863007705, - "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE": 3996324383113221529, - "venv/lib/python3.13/site-packages/pandas/core/indexes/extension.py": 7193879749426425951, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/path_registry.py": 8410829659513707067, - "venv/lib/python3.13/site-packages/pygments/unistring.py": 18037463865539157041, - "venv/lib/python3.13/site-packages/fastapi/param_functions.py": 14585101566697773072, - "venv/lib/python3.13/site-packages/numpy/_core/memmap.pyi": 14124415711399771715, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/markers.py": 5247208668583898949, - "venv/lib/python3.13/site-packages/pandas/tests/computation/test_compat.py": 11655000938111420543, - "venv/lib/python3.13/site-packages/pandas/io/excel/_odfreader.py": 2760916559416783928, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/box.py": 14303526978791498662, - "backend/src/core/encryption_key.py": 10697135200011385036, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/scalar_string.f90": 15018631706178918578, - "backend/src/api/routes/translate/_run_list_routes.py": 13032962834082893523, - "backend/src/services/clean_release/audit_service.py": 9534298777117548502, - "frontend/build/_app/immutable/nodes/8.CoZYS_r2.js": 15980650324428132567, - "frontend/build/_app/immutable/chunks/ClTV31tE.js": 9373217372896609602, - "venv/lib/python3.13/site-packages/pyasn1/type/opentype.py": 340146737126677655, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/errors.py": 12935154705443176852, - "venv/lib/python3.13/site-packages/pygments/lexers/carbon.py": 6753624244809529841, - "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/_implementation.py": 12245524932757110911, - "venv/lib/python3.13/site-packages/numpy/fft/_helper.py": 15906444100774706395, - "venv/lib/python3.13/site-packages/sqlalchemy/util/compat.py": 16148553252010089561, - "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/licenses/AUTHORS": 2987771679926243782, - "frontend/build/_app/immutable/nodes/27.COAKd8S0.js": 18157514999440870259, - "venv/lib/python3.13/site-packages/pip/_internal/utils/filesystem.py": 7716285033598020316, - "backend/src/core/superset_client/_datasets_preview_filters.py": 12835417730902013107, - "venv/lib/python3.13/site-packages/pip/_internal/commands/inspect.py": 1401259241736437656, - "venv/lib/python3.13/site-packages/passlib/crypto/scrypt/__init__.py": 4390908730288159455, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_cpu_dispatcher.py": 2063037275809439611, - "venv/lib/python3.13/site-packages/dotenv/variables.py": 5438726379749803751, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_highlight.py": 7573043358244876807, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/__init__.py": 96341051676048910, - "venv/lib/python3.13/site-packages/pandas/core/indexes/datetimes.py": 14331984919364185787, - "venv/lib/python3.13/site-packages/pygments/formatters/_mapping.py": 13335227913146542481, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi": 10784666243744187693, - "venv/lib/python3.13/site-packages/numpy/_core/records.py": 11414828908088050950, - "backend/src/models/storage.py": 1218824901001897736, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/request.py": 10484280068366411541, - "venv/lib/python3.13/site-packages/pydantic/v1/decorator.py": 6124748023704700724, - "venv/lib/python3.13/site-packages/numpy/lib/_utils_impl.pyi": 15767546107856827543, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/multiarray.pyi": 6368268452184570195, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_orc.py": 1248059610437937895, - "venv/lib/python3.13/site-packages/click/testing.py": 15973659312284678327, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/utils.py": 9959199071030996679, - "venv/lib/python3.13/site-packages/pydantic/decorator.py": 6984214881365537811, - "venv/lib/python3.13/site-packages/numpy/core/_utils.py": 14108063453670701289, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_dst.py": 12243642467695371088, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_array.py": 16867692337986161626, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi": 9294219058621261971, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_records.py": 6252761812228230408, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_logical_ops.py": 4438104524064299182, - "venv/lib/python3.13/site-packages/pycparser/plyparser.py": 5251740466238035035, - "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/METADATA": 15658158392301945427, - "frontend/src/services/toolsService.js": 1568142461283466080, - "venv/lib/python3.13/site-packages/pip/_internal/locations/_sysconfig.py": 4773855595767526182, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/test_cython.py": 2933400021016349431, - "venv/lib/python3.13/site-packages/numpy/_core/multiarray.pyi": 3494871767602648807, - "venv/lib/python3.13/site-packages/PIL/TarIO.py": 13651401276968618561, - "venv/lib/python3.13/site-packages/pygments/lexers/tablegen.py": 2917626373516779857, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/palette.py": 17383863392192572184, - "backend/src/scripts/seed_permissions.py": 12022710984424584961, - "backend/alembic/script.py.mako": 7041141123739810216, - "venv/lib/python3.13/site-packages/rsa/parallel.py": 4968566722541778371, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Indel.pyi": 16372699564794732463, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/dtypes.pyi": 15114028503984522176, - "venv/lib/python3.13/site-packages/pygments/lexers/ruby.py": 6898093948224302592, - "backend/src/plugins/translate/__tests__/__init__.py": 2490542523242946173, - "frontend/build/_app/immutable/chunks/B9Q3NV6S.js": 302719787108962938, - "venv/lib/python3.13/site-packages/rsa/cli.py": 1753949198467694190, - "venv/lib/python3.13/site-packages/passlib/handlers/mysql.py": 11165762635383041460, - "venv/lib/python3.13/site-packages/pandas/core/arrays/base.py": 664515424699746697, - "venv/lib/python3.13/site-packages/PIL/TgaImagePlugin.py": 9208942494595222259, - "venv/lib/python3.13/site-packages/pydantic/env_settings.py": 18250976255197243083, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_http_headers.py": 13187746870209705951, - "venv/lib/python3.13/site-packages/pydantic/plugin/_loader.py": 792663962985957671, - "venv/lib/python3.13/site-packages/numpy/random/_philox.pyi": 7503018432880404291, - "venv/lib/python3.13/site-packages/passlib/tests/test_utils.py": 10362977406431569031, - "venv/lib/python3.13/site-packages/cryptography/hazmat/backends/openssl/__init__.py": 4870269250323682576, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/styles/default.py": 13992125975366109635, - "venv/lib/python3.13/site-packages/pandas/io/formats/_color_data.py": 15614976586613738953, - "frontend/src/components/git/__tests__/git_manager.unfinished_merge.integration.test.js": 2437475749409257490, - "backend/src/core/superset_client/_user_projection.py": 16484323195475306883, - "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/RECORD": 5169907629260560698, - "backend/src/models/dashboard.py": 8479838979214366697, - "venv/lib/python3.13/site-packages/numpy/rec/__init__.py": 7467092880535460658, - "venv/lib/python3.13/site-packages/_pytest/recwarn.py": 15312759837838083596, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_mixed.py": 3476101699679108025, - "venv/lib/python3.13/site-packages/pandas/core/arrays/sparse/scipy_sparse.py": 2091608752719976909, - "venv/lib/python3.13/site-packages/pandas/tests/generic/test_label_or_level_utils.py": 3423644585913150841, - "venv/bin/dotenv": 953953349803507198, - "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/WHEEL": 2357997949040430835, - "venv/lib/python3.13/site-packages/keyring/completion.py": 1027436016070040568, - "venv/lib/python3.13/site-packages/dateutil/_common.py": 12492546559232079071, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/index_tricks.pyi": 6035507586990711471, - "venv/lib/python3.13/site-packages/pandas/core/util/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/base_key.py": 10402800587070352851, - "venv/lib/python3.13/site-packages/pydantic/error_wrappers.py": 11273827025986230629, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/f77fixedform.f95": 10898637805043867370, - "venv/lib/python3.13/site-packages/pandas/_libs/byteswap.cpython-313-x86_64-linux-gnu.so": 17856848989827220388, - "venv/lib/python3.13/site-packages/pandas/io/xml.py": 326466573926392764, - "venv/lib/python3.13/site-packages/fastapi/_compat/model_field.py": 392699021682253569, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_equals.py": 11041884432990762650, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ndarray_conversion.pyi": 11580106670226366710, - "venv/lib/python3.13/site-packages/pytest_asyncio/__init__.py": 8162213292669110211, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/packages.py": 11689856177725665974, - "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_ssl_constants.py": 14834514243239777933, - "venv/lib/python3.13/site-packages/jsonschema/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pluggy/_tracing.py": 7276210024011809655, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7591/errors.py": 17581857895562494222, - "venv/lib/python3.13/site-packages/fastapi/staticfiles.py": 4094330072433302614, - "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/RECORD": 7266315886702289726, - "venv/lib/python3.13/site-packages/pandas/core/internals/__init__.py": 1377108808392195313, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_to_offset.py": 3929599794655343911, - "venv/lib/python3.13/site-packages/httpcore/_utils.py": 3255581092836273826, - "venv/lib/python3.13/site-packages/pandas/_libs/pandas_parser.cpython-313-x86_64-linux-gnu.so": 18056339336364727359, - "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/WHEEL": 691462085217888236, - "venv/lib/python3.13/site-packages/pygments/lexers/ambient.py": 18106929781201758080, - "venv/lib/python3.13/site-packages/pygments/lexers/teraterm.py": 16132676290138105984, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/lib_utils.py": 4612923732868929770, - "venv/lib/python3.13/site-packages/pygments/lexers/sophia.py": 15971500155124949058, - "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/WHEEL": 18203500019759199992, - "venv/bin/pip3": 13861749540792881808, - "venv/lib/python3.13/site-packages/pygments/lexers/x10.py": 6167378515780115912, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7662/models.py": 11521700876708601977, - "frontend/src/routes/tools/backups/+page.svelte": 6796050751535238723, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/OSA.py": 17817123894903120316, - "venv/lib/python3.13/site-packages/pip/_vendor/tomli/_re.py": 1296579393600719224, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_partial_slicing.py": 16219611534574666182, - "venv/lib/python3.13/site-packages/pandas/tests/tools/test_to_datetime.py": 3499461736474530062, - "venv/lib/python3.13/site-packages/pandas/tests/strings/test_get_dummies.py": 14871264482622327003, - "venv/lib/python3.13/site-packages/pygments/lexers/_postgres_builtins.py": 2383906454995727700, - "venv/lib/python3.13/site-packages/jeepney/low_level.py": 6546206679858571608, - "venv/lib/python3.13/site-packages/typing_extensions.py": 8402965285733247527, - "venv/lib/python3.13/site-packages/_pytest/mark/expression.py": 1821437025841201912, - "venv/lib/python3.13/site-packages/pandas/core/apply.py": 17080038932032802984, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_matplotlib.py": 12775850858284584296, - "frontend/src/assets/svelte.svg": 13872699371039797074, - "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/WHEEL": 9131187629260560775, - "venv/lib/python3.13/site-packages/smmap/mman.py": 9091562788592406032, - "venv/lib/python3.13/site-packages/pandas/tests/extension/decimal/array.py": 1919242530821725197, - "venv/lib/python3.13/site-packages/pandas/io/formats/templates/latex_longtable.tpl": 12723621983797415444, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/dml.py": 6567627855667533645, - "venv/lib/python3.13/site-packages/PIL/ImtImagePlugin.py": 17599267755504817169, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/__init__.py": 8093984834068317399, - "venv/lib/python3.13/site-packages/passlib/handlers/fshp.py": 13298827601291507295, - "frontend/build/_app/immutable/nodes/15.pFIXscWy.js": 13476852226803725758, - "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/RECORD": 11604408378998955169, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_apply.py": 11248068903302454630, - "backend/src/models/filter_state.py": 6470311179852241297, - "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py3-objarr.npz": 12369168483236619421, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/proxy.py": 13008557041076030198, - "venv/lib/python3.13/site-packages/jsonschema/exceptions.py": 15651767069704734674, - "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/licenses/LICENSE": 17284252174641192866, - "venv/lib/python3.13/site-packages/dotenv/main.py": 7408889670575271484, - "backend/src/api/routes/__tests__/test_git_status_route.py": 6306531435114264696, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_size.py": 8527839563560550806, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_boxplot_method.py": 7635131059308651529, - "venv/lib/python3.13/site-packages/numpy/f2py/cfuncs.pyi": 8232620851756907417, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_integer/foo90.f90": 3492659529812762861, - "venv/lib/python3.13/site-packages/pandas/io/excel/__init__.py": 6662687963241471393, - "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/licenses/LICENSE": 14524289631770661180, - "venv/lib/python3.13/site-packages/itsdangerous/exc.py": 11181421963755661477, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/constrain.py": 6513876973118703677, - "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py2-objarr.npz": 9481735723557962988, - "venv/lib/python3.13/site-packages/rsa/pem.py": 3118524021576082054, - "frontend/src/routes/dashboards/[id]/components/DashboardTaskHistory.svelte": 8800115118935646769, - "venv/lib/python3.13/site-packages/pydantic/_internal/_serializers.py": 12077180322353709930, - "backend/src/core/superset_client/_databases.py": 14837101840847724937, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_repeat.py": 11360772447747837958, - "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/WHEEL": 18203500019759199992, - "venv/lib/python3.13/site-packages/numpy/tests/test_scripts.py": 5790352482583964348, - "venv/lib/python3.13/site-packages/jose/backends/rsa_backend.py": 13507186353881931758, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/JaroWinkler.pyi": 9003794318682878317, - "venv/lib/python3.13/site-packages/git/objects/submodule/base.py": 10140490301897269395, - "venv/lib/python3.13/site-packages/fastapi/exception_handlers.py": 9907950997607808699, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py": 2873060848310644552, - "venv/lib/python3.13/site-packages/pandas/tests/base/test_transpose.py": 1686867045869336370, - "venv/lib/python3.13/site-packages/click/formatting.py": 6694583529073729642, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/linalg.pyi": 7217817172953767893, - "backend/src/core/utils/dataset_mapper.py": 13413818977924738147, - "venv/lib/python3.13/site-packages/git/remote.py": 1694919255653049944, - "venv/lib/python3.13/site-packages/cffi/commontypes.py": 11198107299826138465, - "venv/lib/python3.13/site-packages/pydantic_settings/__init__.py": 4374954204580208673, - "venv/lib/python3.13/site-packages/numpy/ma/extras.py": 7559838309481824664, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_array_from_pyobj.py": 9596224861290975079, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_intervalindex.py": 5053113227747333759, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_strings.py": 7168544888340296373, - "venv/lib/python3.13/site-packages/PIL/report.py": 9428754440615681790, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/plugin/bootstrap.py": 3865947239497500815, - "frontend/build/_app/immutable/nodes/9.CpD5O4dB.js": 2507508435328749555, - "venv/lib/python3.13/site-packages/pandas/_libs/testing.cpython-313-x86_64-linux-gnu.so": 16758333404587340103, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/auth.py": 13542332344403032059, - "venv/lib/python3.13/site-packages/_pytest/warning_types.py": 10695097290648045427, - "venv/lib/python3.13/site-packages/h11/__init__.py": 222801976520197308, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7009/revocation.py": 16178499278312777474, - "venv/lib/python3.13/site-packages/authlib/oauth1/__init__.py": 3692257288065292607, - "venv/lib/python3.13/site-packages/pandas/_libs/index.cpython-313-x86_64-linux-gnu.so": 18409579812171549318, - "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/RECORD": 8971487317739420905, - "venv/lib/python3.13/site-packages/pandas/tests/test_downstream.py": 5635510753354256507, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_custom_business_day.py": 2200008998040663785, - "venv/lib/python3.13/site-packages/pygments/lexers/pointless.py": 17552020039609007881, - "frontend/src/lib/components/translate/TranslationPreview.svelte": 15144385993961969187, - "venv/lib/python3.13/site-packages/authlib/oidc/discovery/__init__.py": 308178910015974550, - "venv/lib/python3.13/site-packages/pyasn1/type/__init__.py": 15728752901274520502, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_online.py": 11850497659869773510, - "venv/lib/python3.13/site-packages/ecdsa/ecdsa.py": 9770689913380927623, - "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/zip-safe": 15240312484046875203, - "venv/lib/python3.13/site-packages/numpy/testing/_private/utils.pyi": 17639497083041129225, - "venv/lib/python3.13/site-packages/numpy/lib/__init__.pyi": 1537495515469182226, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/session.py": 15060063324909302240, - "venv/lib/python3.13/site-packages/pygments/lexers/fantom.py": 1986459352773446836, - "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/f2py/crackfortran.pyi": 9206355685192026844, - "venv/lib/python3.13/site-packages/pandas/_config/__init__.py": 3577026529387563519, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_sample.py": 16877779557071324115, - "venv/lib/python3.13/site-packages/pip/_internal/utils/datetime.py": 6816292030061301011, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/numeric.py": 14451517189908259173, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/console.py": 10217327186846649646, - "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/METADATA": 732041908491732594, - "venv/lib/python3.13/site-packages/pandas/io/stata.py": 14370692318835340323, - "venv/lib/python3.13/site-packages/gitdb/test/test_util.py": 541144638852210407, - "venv/lib/python3.13/site-packages/PIL/PalmImagePlugin.py": 13008868719182630239, - "venv/lib/python3.13/site-packages/pandas/api/executors/__init__.py": 16954693181142010028, - "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/licenses/LICENSE": 13479831069780783137, - "frontend/src/components/tools/DebugTool.svelte": 17267560034225794881, - "venv/lib/python3.13/site-packages/apscheduler/triggers/base.py": 1170652622104554892, - "venv/lib/python3.13/site-packages/websockets/asyncio/connection.py": 17044409627448400723, - "venv/lib/python3.13/site-packages/pyasn1/type/tagmap.py": 14075859042659927673, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/strptime.pyi": 5973671421648618563, - "venv/lib/python3.13/site-packages/git/objects/util.py": 691018806932355307, - "venv/lib/python3.13/site-packages/jeepney/auth.py": 10565040329507087892, - "venv/lib/python3.13/site-packages/pygments/lexers/pony.py": 2720635393743617141, - "venv/lib/python3.13/site-packages/pandas/core/internals/concat.py": 12151675567885981464, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/database.py": 7111764828004587758, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/poly1305.py": 10854598965202354516, - "venv/lib/python3.13/site-packages/numpy/lib/_nanfunctions_impl.pyi": 12566839271245057531, - "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_meson.py": 5735043585893007971, - "venv/lib/python3.13/site-packages/pandas/tests/extension/list/__init__.py": 10072943986583771248, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/__init__.py": 12290228927283601067, - "venv/lib/python3.13/site-packages/PIL/_imagingft.pyi": 1385456134120145584, - "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/fetch.py": 13937019222549208796, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/connectionpool.py": 10453724680327835821, - "venv/lib/python3.13/site-packages/numpy/_typing/_extended_precision.py": 4201820494689784136, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi": 13016654355866474644, - "venv/lib/python3.13/site-packages/numpy/_core/function_base.pyi": 8343632640078378312, - "frontend/src/routes/migration/mappings/+page.svelte": 4566398049366943407, - "venv/lib/python3.13/site-packages/dateutil/tz/tz.py": 5258008267965780817, - "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/licenses/LICENSE.APACHE": 11041304845352917971, - "frontend/src/routes/datasets/+page.svelte": 18132315883450880052, - "venv/lib/python3.13/site-packages/py.py": 16248211404003181569, - "frontend/build/_app/immutable/chunks/ByztTpgj.js": 4913577152951971044, - "venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/RECORD": 173225030102092319, - "venv/lib/python3.13/site-packages/pip/_internal/build_env.py": 3614358934066127900, - "venv/lib/python3.13/site-packages/pandas/core/shared_docs.py": 2173578869755325502, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_real/foo90.f90": 58572216018544419, - "venv/lib/python3.13/site-packages/_pytest/terminalprogress.py": 5490989904517343259, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_half.py": 3121836756584726126, - "backend/src/api/routes/dataset_review_pkg/_routes.py": 11040245149296528009, - "venv/lib/python3.13/site-packages/numpy/f2py/func2subr.py": 338595621218646492, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_timezones.py": 4771674179164304731, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/aiomysql.py": 12999962380333482189, - "frontend/src/lib/components/layout/Breadcrumbs.svelte": 15202032505744987943, - "venv/lib/python3.13/site-packages/numpy/core/_internal.py": 14816989375912210674, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_formats.py": 17821648708413143557, - "venv/lib/python3.13/site-packages/pandas/_libs/ops.pyi": 1192312467209548763, - "venv/lib/python3.13/site-packages/jose/jwk.py": 2781217726458801910, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/core/indexes/frozen.py": 1419685664276640120, - "venv/lib/python3.13/site-packages/httpcore/_sync/http2.py": 11167798787131917524, - "venv/lib/python3.13/site-packages/greenlet/greenlet_refs.hpp": 8525862687669040510, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_pyf_src.py": 12351476647369971852, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_select.py": 7083040959665006634, - "venv/lib/python3.13/site-packages/PIL/PdfImagePlugin.py": 16082854250442815862, - "venv/lib/python3.13/site-packages/pygments/lexers/xorg.py": 14591144191879110061, - "venv/lib/python3.13/site-packages/pygments/lexers/asm.py": 13861952972248758785, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7662/introspection.py": 14645217065508644154, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_shift.py": 8547819448301023303, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/api.py": 9829629144629841277, - "venv/lib/python3.13/site-packages/uvicorn/protocols/http/flow_control.py": 4822804840057360506, - "venv/lib/python3.13/site-packages/anyio/streams/tls.py": 4191898645554875562, - "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_distutils.pyi": 3595910367556664837, - "venv/lib/python3.13/site-packages/_pytest/freeze_support.py": 494952714451267295, - "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/_arrow_utils.py": 17808805047762566525, - "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/hybrid.py": 14287665090144592501, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/philox-testset-1.csv": 2202360898679162333, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_setops.py": 14246225281428284850, - "venv/lib/python3.13/site-packages/referencing/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_month.py": 8485374160446988786, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/twodim_base.pyi": 3773329770170248877, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_indexing.py": 6539365174681852769, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_info.py": 15151894356374015812, - "venv/lib/python3.13/site-packages/urllib3/util/__init__.py": 2848870790459558599, - "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/RECORD": 6488682447051657004, - "venv/lib/python3.13/site-packages/pandas/io/formats/templates/html_table.tpl": 17198355067645888735, - "venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/METADATA": 8128818962678047497, - "venv/lib/python3.13/site-packages/idna-3.11.dist-info/WHEEL": 8600534672961461758, - "venv/lib/python3.13/site-packages/gitdb/stream.py": 3035738824550231197, - "venv/lib/python3.13/site-packages/pandas/io/sql.py": 12592024569011796790, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_randomstate_regression.py": 17425202198134409689, - "venv/lib/python3.13/site-packages/pygments/lexers/sieve.py": 15757825442941567629, - "venv/lib/python3.13/site-packages/pygments/__init__.py": 14493583771211110135, - "venv/lib/python3.13/site-packages/numpy/dtypes.py": 14128421398870718692, - "venv/lib/python3.13/site-packages/_pytest/capture.py": 17850967053989495422, - "venv/lib/python3.13/site-packages/authlib/oidc/core/models.py": 1370998944213578392, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arithmetic.pyi": 467927803068643024, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_defchararray.py": 15135904718007443306, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_dtype.py": 5336980126536664360, - "frontend/build/_app/immutable/nodes/37.ztsr65__.js": 622371390299823461, - "venv/lib/python3.13/site-packages/pydantic/_internal/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/greenlet/tests/test_greenlet_trash.py": 6191655521667576604, - "venv/lib/python3.13/site-packages/pandas/core/internals/managers.py": 13547912653290063698, - "venv/lib/python3.13/site-packages/jeepney/bus_messages.py": 5871341372541525842, - "venv/lib/python3.13/site-packages/pydantic/v1/main.py": 213087273729911492, - "venv/lib/python3.13/site-packages/rapidfuzz/utils_py.py": 12341806772632276946, - "venv/lib/python3.13/site-packages/pip/_internal/metadata/__init__.py": 8924316361489544973, - "venv/lib/python3.13/site-packages/passlib/crypto/_blowfish/_gen_files.py": 6273314671315377131, - "frontend/src/routes/translate/dictionaries/+page.svelte": 11204836397236633754, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_random.py": 4179188367630060654, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/selectable.py": 14646717349365560549, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_period.py": 12593777226419608212, - "venv/lib/python3.13/site-packages/numpy/random/mtrand.pyi": 17144590106028661128, - "venv/lib/python3.13/site-packages/numpy/_core/_add_newdocs.py": 15657897994221955882, - "venv/lib/python3.13/site-packages/pandas/_libs/testing.pyi": 16691791330088523786, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_construct_from_scalar.py": 16520136036631155842, - "venv/bin/pytest": 2593802493707545818, - "venv/lib/python3.13/site-packages/keyring/backends/kwallet.py": 3071915635647280308, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hmac.py": 18132740541139891824, - "venv/lib/python3.13/site-packages/h11/_util.py": 9362339074207904329, - "venv/lib/python3.13/site-packages/websockets/frames.py": 4901850477414613953, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/authorization_server.py": 2491654903543600678, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/securetransport.py": 12104888897121018676, - "venv/lib/python3.13/site-packages/pandas/core/base.py": 10262717073058743378, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_period.py": 9715843209581883915, - "backend/src/api/routes/__tests__/test_git_api.py": 43259826036955162, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/enumerated.py": 16623643153933348755, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_multithreading.py": 12547048606098186681, - "backend/tests/test_core_scheduler.py": 6589618269087393661, - "venv/lib/python3.13/site-packages/git/objects/tag.py": 7954808535585493300, - "venv/lib/python3.13/site-packages/itsdangerous/encoding.py": 205863946173486958, - "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/dtypes/test_empty.py": 6845813091876023610, - "backend/src/services/clean_release/candidate_service.py": 14044982155441302890, - "backend/src/services/clean_release/repositories/report_repository.py": 17952038790457725175, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_groupby_shift_diff.py": 1210657479716368827, - "venv/lib/python3.13/site-packages/rapidfuzz/process_cpp_impl.cpython-313-x86_64-linux-gnu.so": 11767771662042404251, - "venv/lib/python3.13/site-packages/pydantic/functional_serializers.py": 15285359935059345032, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/mixed/foo_fixed.f90": 8079594129222364031, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_spss.py": 4865360315909532603, - "venv/lib/python3.13/site-packages/numpy/f2py/use_rules.py": 17277594393032382485, - "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/fastapi/params.py": 14263655860695381481, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_randomstate.py": 11205714006000849926, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh17797.f90": 17319074662077763768, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_timestamp_method.py": 11393911418463808195, - "venv/lib/python3.13/site-packages/pandas/core/window/common.py": 17956497339875932377, - "frontend/src/lib/api/datasetReview.js": 3023829599261929428, - "frontend/build/_app/immutable/chunks/Bu8doFLC.js": 13169888001190175706, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_quantile.py": 17110581927381599978, - "venv/lib/python3.13/site-packages/passlib/tests/test_ext_django_source.py": 15577951591316518967, - "venv/lib/python3.13/site-packages/anyio/_core/_testing.py": 14024940365851109155, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_c_parser_only.py": 3038797485572319804, - "venv/lib/python3.13/site-packages/uvicorn/protocols/http/httptools_impl.py": 10367325397209016951, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/styles/_mapping.py": 11752660241391232497, - "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/_toml_compat.py": 10196543528439925412, - "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/unix.py": 13808187640638677050, - "venv/lib/python3.13/site-packages/requests/hooks.py": 9888227370911151341, - "venv/lib/python3.13/site-packages/pygments/lexers/supercollider.py": 15306357945850389082, - "backend/src/api/routes/git/_helpers.py": 6888678759460652166, - "frontend/tailwind.config.js": 13139381085024634801, - "venv/lib/python3.13/site-packages/pygments/lexers/matlab.py": 1880915927722177654, - "venv/lib/python3.13/site-packages/pandas/tests/computation/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_core_functionalities.py": 10449747865888948579, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libldap-2974a1ba.so.2.0.200": 13590569080612602445, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_droplevel.py": 2071197388394658690, - "frontend/src/lib/ui/index.ts": 6883965364006817450, - "venv/lib/python3.13/site-packages/numpy/f2py/symbolic.py": 16854377502491291093, - "venv/lib/python3.13/site-packages/pycparser/ply/ctokens.py": 1599952198962458730, - "backend/tests/test_auth.py": 6167020718035755646, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_array.py": 12514884344165043136, - "venv/lib/python3.13/site-packages/numpy/strings/__init__.py": 6523674574341354095, - "venv/lib/python3.13/site-packages/pygments/styles/gh_dark.py": 7717523746712391809, - "venv/lib/python3.13/site-packages/pygments/lexers/lean.py": 14518351619544450286, - "venv/lib/python3.13/site-packages/gitdb/db/mem.py": 15823812455996199993, - "venv/lib/python3.13/site-packages/pandas/core/tools/times.py": 6560679305255812809, - "venv/lib/python3.13/site-packages/multipart/decoders.py": 11346752866822986212, - "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_chebyshev.py": 8054411443686791426, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth1/resource_protector.py": 17080006010679257864, - "venv/lib/python3.13/site-packages/pygments/formatters/img.py": 18230180171537565208, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arraypad.pyi": 12178766981280242620, - "venv/lib/python3.13/site-packages/greenlet/greenlet_cpython_compat.hpp": 8396413256279803400, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/cli.py": 6382193953727339271, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_join.py": 17508073052750406350, - "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/METADATA": 4954909612926293404, - "venv/lib/python3.13/site-packages/click/__init__.py": 9261629899056870564, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_laguerre.py": 7475592201750602042, - "venv/lib/python3.13/site-packages/fastapi/_compat/v2.py": 15078491685917047095, - "venv/lib/python3.13/site-packages/rsa/pkcs1_v2.py": 3533398238491175229, - "venv/lib/python3.13/site-packages/pip/_vendor/pkg_resources/__init__.py": 1677532855362260338, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_simd.py": 7577408467041941595, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_timeseries_window.py": 1973365560758236222, - "venv/lib/python3.13/site-packages/httpcore/_sync/connection_pool.py": 8159971229245441880, - "frontend/src/routes/datasets/[id]/+page.svelte": 3377235098319163133, - "venv/lib/python3.13/site-packages/numpy/fft/__init__.pyi": 10354990065626209946, - "scripts/scan_secrets.sh": 599173385111063848, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/util.py": 9373891872370603585, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_constructors.py": 12628643543371952431, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_data_list.py": 1131520422778420758, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_interval_tree.py": 15789443159423302291, - "venv/lib/python3.13/site-packages/psycopg2/extras.py": 7510107741729866600, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_unary.py": 2573825678342936734, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/jose/backends/native.py": 50023158618000658, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi": 16934250836612138541, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/timeseries.py": 17641722582848932332, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_constructors.py": 2786493777737472376, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_ops.py": 14420421526781215500, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/strategy_options.py": 16342048970907383117, - "venv/lib/python3.13/site-packages/pycparser/c_generator.py": 14922277346364091122, - "frontend/src/lib/stores/__tests__/test_sidebar.js": 113566022614194798, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh23598.f90": 1409455010615482779, - "backend/src/services/clean_release/compliance_execution_service.py": 3379474749353250488, - "venv/lib/python3.13/site-packages/pandas/tests/tools/test_to_time.py": 10463741033950978270, - "venv/lib/python3.13/site-packages/_pytest/assertion/truncate.py": 14000392306994773741, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/nested_sequence.pyi": 10985533017688918283, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_join.py": 1131792466825856990, - "backend/src/services/dataset_review/orchestrator_pkg/_commands.py": 15474996500928063708, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_arraysetops.py": 10588903945244480732, - "venv/lib/python3.13/site-packages/attrs/__init__.py": 9502968706258875910, - "venv/bin/pyrsa-verify": 6762739642629904442, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_item_selection.py": 1027989019709029615, - "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/INSTALLER": 17282701611721059870, - "frontend/src/lib/components/translate/BulkReplaceModal.svelte": 8039616128453714824, - "venv/lib/python3.13/site-packages/numpy/lib/_arraysetops_impl.py": 5583478355093966990, - "venv/lib/python3.13/site-packages/requests/utils.py": 17045260098091371478, - "venv/lib/python3.13/site-packages/passlib/handlers/md5_crypt.py": 7669328623914474137, - "venv/lib/python3.13/site-packages/pandas/core/reshape/melt.py": 14109947554124551986, - "venv/lib/python3.13/site-packages/pandas/core/array_algos/replace.py": 1708865864686705833, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_matmul.py": 9323220618335072725, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_interval_pyarrow.py": 3849382723890414899, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/oct_key.py": 11504245602045724, - "venv/lib/python3.13/site-packages/pygments/lexers/ncl.py": 14838368780033177943, - "venv/lib/python3.13/site-packages/pydantic_settings/version.py": 17263591404988984480, - "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/LICENSE": 12064892310002036996, - "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/cython/meson.build": 9642189398934130247, - "venv/lib/python3.13/site-packages/greenlet/tests/test_greenlet.py": 10727595500186011102, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_append.py": 13152738905565588661, - "venv/lib/python3.13/site-packages/pygments/lexers/c_like.py": 13004061780700827047, - "venv/lib/python3.13/site-packages/pydantic/v1/color.py": 14545509649623975912, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_diff.py": 7420674239386436885, - "venv/lib/python3.13/site-packages/starlette/middleware/gzip.py": 4321371046149637632, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/cli/gh_22819.pyf": 14717748358749934995, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/dtypes/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_comparison.py": 5348781442514871089, - "venv/lib/python3.13/site-packages/pandas/core/computation/engines.py": 1889823242704769952, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_float.py": 10455718110956856820, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/__init__.py": 11526998905763262175, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_asfreq.py": 12721047184483392773, - "venv/lib/python3.13/site-packages/authlib/integrations/sqla_oauth2/tokens_mixins.py": 9923721909178724437, - "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py": 5995058808539530576, - "venv/lib/python3.13/site-packages/pandas/util/_decorators.py": 14780166101442507665, - "venv/lib/python3.13/site-packages/keyring/__init__.py": 991260689223167957, - "venv/lib/python3.13/site-packages/numpy/_core/defchararray.py": 15550230459945996915, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_convert_dtypes.py": 13612212066710266457, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libgssapi_krb5-497db0c6.so.2.2": 6199424505696679197, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/lib_utils.pyi": 12312677132342982866, - "venv/lib/python3.13/site-packages/numpy/polynomial/legendre.py": 1731362089136926666, - "venv/lib/python3.13/site-packages/pygments/lexers/phix.py": 16775285097768256238, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/jwe_encs.py": 9703572751543253904, - "venv/lib/python3.13/site-packages/pip/_vendor/idna/codec.py": 14814999235499530522, - "venv/lib/python3.13/site-packages/pandas/_libs/join.pyi": 369262334342439364, - "venv/lib/python3.13/site-packages/authlib/common/encoding.py": 52296066492753953, - "venv/lib/python3.13/site-packages/numpy/random/c_distributions.pxd": 16574161095720077595, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_chaining_and_caching.py": 15814259766479305290, - "venv/lib/python3.13/site-packages/numpy/_core/_type_aliases.py": 1644743742545329282, - "venv/lib/python3.13/site-packages/pandas/core/window/online.py": 13551140636252302201, - "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/WHEEL": 14550174241288068152, - "venv/lib/python3.13/site-packages/ecdsa/_compat.py": 13034064768731879065, - "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/oauth2_session.py": 7076806577412682835, - "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/licenses/LICENSE": 1641332741515411734, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/jwe_zips.py": 5184894018177148648, - "venv/lib/python3.13/site-packages/numpy/random/_philox.cpython-313-x86_64-linux-gnu.so": 9916143171057774705, - "venv/lib/python3.13/site-packages/pandas/tests/extension/date/array.py": 18073740473413382768, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-exp.csv": 601782991752922510, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_almost_equal.py": 9333355453468475330, - "venv/lib/python3.13/site-packages/sqlalchemy/util/langhelpers.py": 1246582552828542841, - "venv/lib/python3.13/site-packages/apscheduler/executors/__init__.py": 15130871412783076140, - "venv/bin/activate.fish": 5275961582873298085, - "backend/src/core/plugin_loader.py": 245217431000027879, - "venv/lib/python3.13/site-packages/h11/_version.py": 9334144031400715479, - "venv/lib/python3.13/site-packages/_pytest/pytester.py": 5423287282155948827, - "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/routines/funcfortranname.pyf": 1361426175133186231, - "venv/lib/python3.13/site-packages/pandas/tests/extension/list/array.py": 8065856342131357421, - "backend/src/core/middleware/__init__.py": 13917394130915533209, - "venv/lib/python3.13/site-packages/PIL/ExifTags.py": 6869879826623916830, - "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/greenlet/greenlet_internal.hpp": 13173115501223013762, - "venv/lib/python3.13/site-packages/ecdsa/test_keys.py": 17117188373419689467, - "venv/lib/python3.13/site-packages/pandas/tseries/offsets.py": 13750652921710295115, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/base.py": 3250660921154829815, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/decl_api.py": 5287018911951723034, - "venv/lib/python3.13/site-packages/numpy/random/_pcg64.cpython-313-x86_64-linux-gnu.so": 5759445712745875888, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ndarray_shape_manipulation.py": 12598431412396627042, - "venv/lib/python3.13/site-packages/anyio/__init__.py": 12752386096259972231, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/warnings.py": 2552021607611112605, - "frontend/src/lib/components/reports/__tests__/reports_list.ux.test.js": 16252194679528846126, - "venv/lib/python3.13/site-packages/git/db.py": 6680123567509774609, - "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/setup.py": 5482352991573617756, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_antijoin.py": 14435396877404907826, - "venv/lib/python3.13/site-packages/pycparser/ply/yacc.py": 4218889705926129841, - "venv/lib/python3.13/site-packages/pandas/tests/base/common.py": 13335865314907577354, - "venv/lib/python3.13/site-packages/pandas/core/interchange/dataframe.py": 16525846817331842080, - "venv/lib/python3.13/site-packages/yaml/__init__.py": 11044333164208875121, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_compat.py": 11481259393140789881, - "venv/lib/python3.13/site-packages/tzlocal/windows_tz.py": 11696765495002954868, - "venv/lib/python3.13/site-packages/_pytest/assertion/__init__.py": 16392348866405910618, - "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/METADATA": 6960169108624303030, - "venv/lib/python3.13/site-packages/authlib/integrations/base_client/registry.py": 13306388361671753373, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_api.py": 1910541672533352625, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_dtypes.py": 7550022036887594386, - "venv/lib/python3.13/site-packages/pygments/style.py": 17393199235575070210, - "frontend/src/components/tools/ConnectionList.svelte": 15981533028345710336, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/validation": 13992741783834278677, - "venv/lib/python3.13/site-packages/numpy/ma/mrecords.pyi": 8945514863366384484, - "venv/lib/python3.13/site-packages/rapidfuzz/utils_cpp.cpython-313-x86_64-linux-gnu.so": 16145829319198889672, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/common/gh19161.f90": 7691871466158049079, - "venv/lib/python3.13/site-packages/pandas/_libs/indexing.pyi": 90995502095752726, - "venv/lib/python3.13/site-packages/pygments/lexers/wren.py": 6259586056260713984, - "venv/lib/python3.13/site-packages/attrs/__init__.pyi": 14353885498883901855, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/modeline.py": 6194365452131561478, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/evaluator.py": 6348022789899831255, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_comparison.py": 11333735187162513512, - "frontend/src/components/__tests__/task_log_viewer.test.js": 6512653335179659416, - "backend/src/core/auth/__init__.py": 16435384828055133073, - "venv/lib/python3.13/site-packages/rapidfuzz/fuzz.pyi": 7410507092040239447, - "venv/lib/python3.13/site-packages/sqlalchemy/util/_collections.py": 7772437080414835462, - "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/numpy/typing/__init__.py": 3608665862759745048, - "venv/lib/python3.13/site-packages/pandas/tseries/api.py": 14785127136180503704, - "backend/src/services/clean_release/__tests__/test_manifest_builder.py": 9320699981861675127, - "backend/alembic_test.db": 12608323163910954537, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_arithmetic.py": 17043958604607035425, - "venv/lib/python3.13/site-packages/idna/package_data.py": 6583909243002417298, - "venv/lib/python3.13/site-packages/pydantic/v1/json.py": 16596969681914729251, - "backend/tests/services/clean_release/test_compliance_task_integration.py": 682622548217611215, - "venv/lib/python3.13/site-packages/authlib/jose/__init__.py": 317573635113539563, - "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_dt_accessor.py": 2229616362327032253, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libssl-81ffa89e.so.3": 13913673119166658781, - "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/__init__.py": 1798074863773727377, - "venv/lib/python3.13/site-packages/pandas/tests/strings/test_find_replace.py": 262184394614748386, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/strategies.py": 14007486769065345085, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_ratio.py": 940213488194452623, - "venv/lib/python3.13/site-packages/jose/__init__.py": 15575670403380555945, - "venv/lib/python3.13/site-packages/pandas/tests/interchange/test_spec_conformance.py": 16448324304809345527, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_npfuncs.py": 6132674295193898715, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_latex.py": 15077617224435782028, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_pickle.py": 3419420815386925630, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/dtype.pyi": 12178008080078679769, - "venv/lib/python3.13/site-packages/pyasn1/error.py": 11036037300994516747, - "venv/lib/python3.13/site-packages/dotenv/parser.py": 17651523026132242005, - "venv/lib/python3.13/site-packages/numpy/lib/_nanfunctions_impl.py": 11621577915097515056, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/resource_protector.py": 4823504113176750140, - "venv/lib/python3.13/site-packages/psycopg2/_ipaddress.py": 12332672835751787869, - "venv/lib/python3.13/site-packages/numpy/_core/_internal.py": 13056946834347643109, - "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_value_counts.py": 12340872812120847127, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_indexing.py": 16291207621268530224, - "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__init__.py": 3619091571279924576, - "venv/lib/python3.13/site-packages/jeepney/io/tests/test_threading.py": 6656552495241857519, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_is_homogeneous_dtype.py": 15378147393056707851, - "venv/lib/python3.13/site-packages/_pytest/faulthandler.py": 13329320306353718649, - "venv/lib/python3.13/site-packages/pandas/tests/generic/test_duplicate_labels.py": 10663105616336167414, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_deprecations.py": 16124509063246256847, - "venv/lib/python3.13/site-packages/PIL/ImageQt.py": 6607270889359138659, - "venv/lib/python3.13/site-packages/pygments/lexers/ooc.py": 14540210212631538007, - "venv/lib/python3.13/site-packages/authlib/jose/drafts/_jwe_enc_cryptodome.py": 15753279711312689193, - "venv/lib/python3.13/site-packages/numpy/lib/_format_impl.py": 1809069379864985298, - "venv/lib/python3.13/site-packages/pandas/_libs/internals.pyi": 11662356720550765508, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_complex/foo90.f90": 15981076495193915107, - "venv/lib/python3.13/site-packages/PIL/PaletteFile.py": 4104323659569809769, - "venv/lib/python3.13/site-packages/cryptography/__about__.py": 10471312538528457144, - "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/__init__.py": 18036348770966744795, - "venv/lib/python3.13/site-packages/pygments/lexers/_lasso_builtins.py": 4006621424665751347, - "venv/lib/python3.13/site-packages/pygments/lexers/berry.py": 4540479522141160472, - "venv/lib/python3.13/site-packages/numpy/core/_dtype_ctypes.py": 17055498803745289016, - "venv/lib/python3.13/site-packages/httpcore/_backends/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/WHEEL": 16097436423493754389, - "frontend/src/lib/ui/Icon.svelte": 2201964532717139995, - "backend/src/api/routes/connections.py": 3871707738958709690, - "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/WHEEL": 7454950858448014158, - "venv/lib/python3.13/site-packages/numpy/tests/test_numpy_version.py": 849232015288488204, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_arithmetic.py": 4858019700859362660, - "venv/lib/python3.13/site-packages/authlib/__init__.py": 8957417237987377675, - "venv/lib/python3.13/site-packages/git/util.py": 7424113599506269603, - "backend/src/plugins/llm_analysis/service.py": 13348026269882094952, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/modes.py": 6832635056228445650, - "venv/lib/python3.13/site-packages/_pytest/_code/code.py": 5248836309618416369, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_numpy_compat.py": 9112925138440787555, - "frontend/src/components/Toast.svelte": 340258331315696736, - "venv/lib/python3.13/site-packages/pandas/_libs/indexing.cpython-313-x86_64-linux-gnu.so": 13188040757764159731, - "backend/src/core/utils/__init__.py": 12454773556575864508, - "venv/lib/python3.13/site-packages/pyasn1/type/error.py": 1479878696626082697, - "venv/lib/python3.13/site-packages/authlib/integrations/base_client/async_openid.py": 8708980645667847439, - "venv/lib/python3.13/site-packages/pip/_internal/models/direct_url.py": 4619204214785230341, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_x86_unix.h": 13709837360414450184, - "venv/lib/python3.13/site-packages/numpy/_core/__init__.py": 14951958843029076911, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7591/__init__.py": 348469143777130970, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_na_indexing.py": 8712547126522523899, - "venv/lib/python3.13/site-packages/pip/_vendor/typing_extensions.py": 10158933506062403688, - "venv/lib/python3.13/site-packages/pandas/api/types/__init__.py": 3995030354191071797, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_feather.py": 13613400750755530364, - "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/METADATA": 1266134223155266488, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Indel.py": 17112989332785349705, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/__init__.py": 11157967642611612522, - "venv/lib/python3.13/site-packages/numpy/random/bit_generator.pxd": 14261507718442469988, - "venv/lib/python3.13/site-packages/apscheduler/schedulers/__init__.py": 16211693040174992594, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_indexerrors.py": 4434868847540549873, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_indexing.py": 8943385096811168079, - "venv/lib/python3.13/site-packages/starlette/schemas.py": 697416731579555195, - "venv/lib/python3.13/site-packages/pydantic/_migration.py": 17982816821930887921, - "venv/lib/python3.13/site-packages/pandas/tests/frame/constructors/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_multi.py": 4612816468258071810, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/horizontal_shard.py": 17615732108308858890, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_clip.py": 13498704233745189154, - "venv/lib/python3.13/site-packages/httpcore/_backends/base.py": 5491727479625093183, - "venv/lib/python3.13/site-packages/pygments/lexers/_usd_builtins.py": 8671446144574174964, - "venv/bin/pyrsa-decrypt": 10929163076272054227, - "backend/src/core/async_superset_client.py": 17097960069181151584, - "venv/lib/python3.13/site-packages/PIL/ImImagePlugin.py": 15452956038794399492, - "venv/lib/python3.13/site-packages/pandas/tests/extension/json/array.py": 7437107031079833232, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/string_/test_string_arrow.py": 10008395772220468318, - "backend/src/services/clean_release/__tests__/test_audit_service.py": 1679130450221264383, - "backend/src/services/clean_release/stages/data_purity.py": 775548350590498835, - "backend/tests/scripts/test_clean_release_tui_v2.py": 16893299982170941753, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_index_new.py": 796607958391882182, - "venv/lib/python3.13/site-packages/gitdb/db/ref.py": 1176701624460337371, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/version.py": 5000350193835202949, - "venv/lib/python3.13/site-packages/pandas/_config/config.py": 1494288055210911451, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_ujson.py": 12184194085993665945, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_convert_dtypes.py": 5005259275646154894, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_character.py": 17168707712220699738, - "venv/lib/python3.13/site-packages/pygments/lexers/testing.py": 17148598391210353576, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/inference.py": 2786441797589123900, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/__init__.py": 260774374497653876, - "venv/lib/python3.13/site-packages/keyring/backend_complete.zsh": 14339032329370660432, - "venv/lib/python3.13/site-packages/pandas/core/api.py": 3390809109543982410, - "venv/lib/python3.13/site-packages/pandas/tests/internals/test_api.py": 11217428888457119601, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/_orm_types.py": 5508682532707462217, - "backend/src/core/database.py": 7064138501006682572, - "venv/lib/python3.13/site-packages/rapidfuzz/_common_py.py": 15410157970973169427, - "venv/lib/python3.13/site-packages/pip/_internal/utils/subprocess.py": 3095012838116670328, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_loop.py": 6502725909989655118, - "venv/lib/python3.13/site-packages/numpy/testing/_private/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/gitdb/exc.py": 13975852664048821278, - "venv/lib/python3.13/site-packages/git/exc.py": 14278238357119816365, - "venv/lib/python3.13/site-packages/sqlalchemy/connectors/__init__.py": 17194870661869873083, - "backend/src/api/routes/git/_repo_operations_routes.py": 17252968422647919723, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/reporter.py": 6510864297510700136, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_sort.py": 11941630642630249576, - "venv/lib/python3.13/site-packages/pandas/_libs/groupby.pyi": 6154952803757623626, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_packbits.py": 7592956764084658562, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py": 8864112842559211971, - "backend/src/models/config.py": 7631082065091961320, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/ssh.py": 16262079536832961001, - "venv/lib/python3.13/site-packages/numpy/_core/multiarray.py": 4484782354376574355, - "venv/lib/python3.13/site-packages/pandas/core/arrays/numpy_.py": 16641729409992576358, - "venv/lib/python3.13/site-packages/pygments/lexers/_vim_builtins.py": 6842381248727719900, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/unistring.py": 18037463865539157041, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/bulk_persistence.py": 2307818727114646866, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_chunksize.py": 7107619871245389835, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/__init__.py": 15487893533770969571, - "backend/src/services/clean_release/dto.py": 11031410734734770552, - "venv/lib/python3.13/site-packages/numpy.libs/libgfortran-040039e1-0352e75f.so.5.0.0": 13946067868274850557, - "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/RECORD": 3205266407680661753, - "venv/lib/python3.13/site-packages/pip/_internal/commands/download.py": 7533669665002975823, - "venv/lib/python3.13/site-packages/numpy/linalg/_linalg.py": 9654828595008925976, - "venv/lib/python3.13/site-packages/numpy/random/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_quarter.py": 6795026338946382805, - "backend/src/plugins/translate/__tests__/test_preview.py": 12246698485015379020, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/config.py": 10707678943298497676, - "backend/tests/core/test_defensive_guards.py": 1048600814279591148, - "venv/lib/python3.13/site-packages/pydantic/root_model.py": 20625057828723317, - "venv/lib/python3.13/site-packages/rsa/key.py": 4783269051050521475, - "venv/lib/python3.13/site-packages/websockets/auth.py": 499195617702591757, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/mariadb.py": 3109042476851791996, - "venv/lib/python3.13/site-packages/PIL/IptcImagePlugin.py": 14329453455296600824, - "frontend/src/routes/dashboards/__tests__/dashboard-profile-override.integration.test.js": 15198772446275187033, - "venv/lib/python3.13/site-packages/numpy/_core/_umath_tests.pyi": 4587511199710546030, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/connection.py": 5582413226288987808, - "venv/lib/python3.13/site-packages/pip/_internal/network/lazy_wheel.py": 12936988990566680659, - "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/RECORD": 11904769847385571600, - "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/RECORD": 7320381591791379627, - "venv/lib/python3.13/site-packages/pip/_internal/distributions/installed.py": 18153165899841229597, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/index_tricks.py": 10051261976498916957, - "venv/bin/activate.csh": 14513616043256836238, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_info.py": 10615285239043745712, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_datetime.py": 11417836378782978454, - "venv/lib/python3.13/site-packages/yaml/loader.py": 18284689412149643959, - "venv/lib/python3.13/site-packages/pandas/tests/base/test_misc.py": 9190561773853901527, - "venv/lib/python3.13/site-packages/pandas/tests/base/test_fillna.py": 9297637486401098611, - "venv/lib/python3.13/site-packages/rapidfuzz/__pyinstaller/__init__.py": 1229691061011958769, - "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/licenses/LICENSE": 6808958893921859106, - "venv/lib/python3.13/site-packages/fastapi/encoders.py": 1914192268055730299, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_common.py": 3559416690270529904, - "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/METADATA": 3474626408138844944, - "venv/lib/python3.13/site-packages/websockets/connection.py": 5168795926684067030, - "venv/lib/python3.13/site-packages/packaging/requirements.py": 2896326326372719999, - "venv/lib/python3.13/site-packages/dotenv/cli.py": 7952654621986067973, - "venv/lib/python3.13/site-packages/rapidfuzz/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/testing/overrides.pyi": 17408923209056015964, - "venv/lib/python3.13/site-packages/pandas/_libs/hashing.cpython-313-x86_64-linux-gnu.so": 16124435225170610034, - "frontend/src/lib/i18n/index.ts": 2428391315568750665, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/yaml.py": 17801117405950663611, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/result.py": 8306497889916212025, - "venv/lib/python3.13/site-packages/fastapi/dependencies/utils.py": 7687214523082385505, - "venv/lib/python3.13/site-packages/pandas/tests/extension/conftest.py": 16886862391161243448, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/diagnose.py": 17275977405061138181, - "backend/src/api/routes/admin.py": 16164005699911103765, - "venv/lib/python3.13/site-packages/pip/_internal/utils/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_hist_box_by.py": 16197297231683144299, - "venv/lib/python3.13/site-packages/pandas/_libs/sparse.cpython-313-x86_64-linux-gnu.so": 2051484129454294720, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_stack_unstack.py": 3737891757817696405, - "venv/lib/python3.13/site-packages/pandas/core/series.py": 11618272682977710696, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_reindex.py": 15794750188594767026, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/interval/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/lib/_ufunclike_impl.py": 12251264586530536622, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py": 6509914623926583629, - "venv/lib/python3.13/site-packages/pandas/tests/io/sas/test_sas.py": 9446226619493404132, - "venv/lib/python3.13/site-packages/numpy/lib/stride_tricks.py": 548928042543500365, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_copy_deprecation.py": 16045426957636349035, - "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/core/strings/__init__.py": 13025387151657797130, - "frontend/src/routes/admin/users/+page.svelte": 18167787254767604761, - "venv/lib/python3.13/site-packages/jeepney/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/cffi/ffiplatform.py": 6782148110863549472, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/fields.pyi": 15420142722808118826, - "venv/lib/python3.13/site-packages/pandas/core/computation/pytables.py": 16326122536715961388, - "venv/lib/python3.13/site-packages/pydantic/deprecated/parse.py": 819235411736647763, - "venv/lib/python3.13/site-packages/passlib/utils/compat/_ordered_dict.py": 14248790502300368024, - "venv/lib/python3.13/site-packages/anyio/_core/_eventloop.py": 6853955946028131785, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/dependency.py": 7300594038182867460, - "backend/src/services/notifications/service.py": 7032100258837946310, - "venv/lib/python3.13/site-packages/pandas/util/_test_decorators.py": 17930801441803090445, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/period/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/jaraco/functools/__init__.pyi": 2872039192528394677, - "venv/lib/python3.13/site-packages/passlib/registry.py": 5050675058011850564, - "venv/lib/python3.13/site-packages/numpy/fft/__init__.py": 9343449448737026658, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_indexing.py": 5910217172351020811, - "venv/lib/python3.13/site-packages/keyring/backend_complete.bash": 16589214602970214174, - "venv/lib/python3.13/site-packages/numpy/polynomial/__init__.py": 15558318749939680074, - "backend/src/models/dataset_review_pkg/_clarification_models.py": 1561486208194983219, - "venv/lib/python3.13/site-packages/numpy/polynomial/chebyshev.py": 1590647503660777156, - "frontend/build/_app/immutable/nodes/25.BTe1wdeo.js": 8998144152410680482, - "venv/lib/python3.13/site-packages/pydantic/_internal/_fields.py": 2692892815545218335, - "venv/lib/python3.13/site-packages/pygments/lexers/yang.py": 7409365668425173823, - "venv/lib/python3.13/site-packages/referencing/typing.py": 8296636464219681747, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/generator_pcg64_np126.pkl.gz": 9677994062701347206, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arctan.csv": 4581185622290535488, - "venv/lib/python3.13/site-packages/passlib/tests/__main__.py": 16477180140297957650, - "venv/lib/python3.13/site-packages/numpy/lib/mixins.pyi": 15597536290223628777, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_setops.py": 16240008859650640717, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/boxplot.py": 7090586111222952862, - "venv/lib/python3.13/site-packages/fastapi/routing.py": 466387159689567541, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_dialect.py": 6294522482440261756, - "frontend/src/lib/stores/__tests__/test_activity.js": 15599963775430388097, - "venv/lib/python3.13/site-packages/passlib/handlers/cisco.py": 15004707325761459775, - "venv/lib/python3.13/site-packages/pandas/core/array_algos/masked_accumulations.py": 11428556645065711062, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_retain_attributes.py": 16210262356844956764, - "venv/lib/python3.13/site-packages/numpy/_typing/_array_like.py": 2161873439361281488, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/test_arithmetic.py": 14858832264660410908, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/test_astype.py": 1961831758405941258, - "venv/lib/python3.13/site-packages/pygments/lexers/bibtex.py": 14652372822443154388, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_setitem.py": 10389770446834773837, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_constructors.py": 7466113171256856591, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_indexing.py": 11572911419313573163, - "frontend/src/lib/components/reports/reportTypeProfiles.js": 10752964062056890725, - "backend/src/services/clean_release/__tests__/test_policy_engine.py": 9953185933720550640, - "venv/lib/python3.13/site-packages/pyasn1/type/char.py": 16518487114777185541, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_missing.py": 1963705411068820866, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_casting_unittests.py": 18157503533859249155, - "venv/lib/python3.13/site-packages/git/objects/base.py": 11498407361286170399, - "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/zip-safe": 15240312484046875203, - "venv/lib/python3.13/site-packages/sqlalchemy/pool/impl.py": 4687705587963980337, - "backend/src/plugins/translate/__init__.py": 10614338387079190520, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/const_vs_enum.py": 12873459583943748931, - "venv/lib/python3.13/site-packages/websockets/http.py": 9406523480132204760, - "frontend/src/lib/components/dataset-review/__tests__/us3_execution_batch.ux.test.js": 13416996904332574966, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/ssl_.py": 644667973700652263, - "venv/lib/python3.13/site-packages/pandas/io/parsers/readers.py": 7899068668225389287, - "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/connection.py": 15405047955974108860, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_css.py": 17765129030290410902, - "venv/lib/python3.13/site-packages/pygments/lexers/tls.py": 3552078393009383412, - "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pydantic_settings/utils.py": 14396350455520958703, - "venv/lib/python3.13/site-packages/greenlet/greenlet_slp_switch.hpp": 1694714764745718414, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/authenticate_client.py": 16886645731942472285, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/__init__.py": 8632038387100622009, - "venv/lib/python3.13/site-packages/_pytest/runner.py": 5772610501857496741, - "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/METADATA": 18410956221497090898, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/__init__.py": 15130871412783076140, - "frontend/src/routes/settings/automation/+page.svelte": 15129032082642700205, - "backend/src/services/clean_release/stages/base.py": 7739111130895140155, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/conftest.py": 25473177671450048, - "frontend/src/lib/auth/__tests__/permissions.test.js": 13555349356008297952, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arrayprint.pyi": 2053388407376896377, - "venv/lib/python3.13/site-packages/pandas/tests/internals/test_internals.py": 2049484047553296860, - "venv/lib/python3.13/site-packages/pygments/lexers/parasail.py": 7758704230750989282, - "venv/lib/python3.13/site-packages/pandas/_libs/interval.cpython-313-x86_64-linux-gnu.so": 8964760352224019544, - "venv/lib/python3.13/site-packages/PIL/BmpImagePlugin.py": 16682567630621529903, - "frontend/build/_app/immutable/chunks/ikwS1dR6.js": 3267599179274813039, - "backend/src/models/git.py": 6898938895423263836, - "backend/src/plugins/translate/sql_generator.py": 13328440537803602154, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/base.py": 13454394827268856575, - "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/LICENSE": 2032205056284080825, - "venv/lib/python3.13/site-packages/starlette/applications.py": 13591448461234020757, - "venv/lib/python3.13/site-packages/pip/_internal/utils/filetypes.py": 4415340223268174913, - "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_interaction.py": 12216477658994324787, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh15035.f": 13126319439858352711, - "venv/lib/python3.13/site-packages/passlib/hash.py": 5238710192614935875, - "venv/lib/python3.13/site-packages/uvicorn/__init__.py": 11517239807926185665, - "venv/lib/python3.13/site-packages/pygments/lexers/soong.py": 10273453675034306814, - "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/METADATA": 11419135077900073883, - "venv/lib/python3.13/site-packages/yaml/representer.py": 10550136298978225013, - "venv/lib/python3.13/site-packages/pygments/lexers/hexdump.py": 10665271113239513484, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/__init__.py": 15130871412783076140, - "frontend/src/routes/settings/+page.svelte": 3103346197225933395, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/lib_utils.pyi": 3708905060001289515, - "venv/lib/python3.13/site-packages/pygments/lexers/dsls.py": 11696359794961833087, - "venv/lib/python3.13/site-packages/httpx/_transports/mock.py": 13564349939188292363, - "venv/lib/python3.13/site-packages/numpy/lib/_scimath_impl.py": 14696841109142419143, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame_legend.py": 13094583566764796297, - "venv/lib/python3.13/site-packages/httpcore/__init__.py": 5897259569277532401, - "venv/lib/python3.13/site-packages/anyio/_core/_exceptions.py": 11969894261631227715, - "venv/lib/python3.13/site-packages/PIL/ContainerIO.py": 7551929914241379005, - "venv/lib/python3.13/site-packages/gitdb/utils/encoding.py": 1668793658272579497, - "venv/lib/python3.13/site-packages/passlib/crypto/_blowfish/unrolled.py": 12651425412640834530, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/requirements.py": 2362917888844255805, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/immutabledict.pxd": 13413785818999584986, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/lib/_user_array_impl.pyi": 7171810177801103765, - "frontend/src/lib/components/reports/ReportsList.svelte": 15460090587871611258, - "frontend/src/components/backups/BackupList.svelte": 14706629542956875064, - "venv/lib/python3.13/site-packages/attrs/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/cffi/_cffi_include.h": 13094899814611950155, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/live_render.py": 14619617939683800204, - "venv/lib/python3.13/site-packages/_pytest/_io/__init__.py": 1510478951261938348, - "venv/lib/python3.13/site-packages/pygments/lexers/jmespath.py": 7426464251945393925, - "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_backend.pyi": 13083543811193933305, - "backend/src/core/task_manager/models.py": 749015551222331342, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_indexing.py": 1926721846569184112, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/factory.py": 3042005457138956499, - "venv/lib/python3.13/site-packages/websockets/legacy/exceptions.py": 16025779645869134942, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timestamps.pyi": 51728851886466199, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_dropna.py": 17272771246760486947, - "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/RECORD": 16342326945648353387, - "venv/lib/python3.13/site-packages/pandas/api/internals.py": 5188134630996087511, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/nested_secrets.py": 15691917061715429369, - "backend/src/models/__tests__/test_clean_release.py": 3448406855108346883, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9207/__init__.py": 8434616194114282088, - "venv/lib/python3.13/site-packages/pip/_internal/metadata/importlib/_dists.py": 16746363359320577809, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/gh25337/use_data.f90": 14624074967635860696, - "venv/lib/python3.13/site-packages/numpy/lib/introspect.pyi": 12239924839764265507, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_append_common.py": 2377351120083195530, - "backend/src/api/routes/dashboards/_action_routes.py": 11814954258886577640, - "backend/src/models/dataset_review_pkg/_filter_models.py": 8426742165406106645, - "backend/tests/test_smoke_plugins.py": 10704454611361370435, - "venv/lib/python3.13/site-packages/passlib/crypto/_md4.py": 379335134151814809, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_ticks.py": 11649965080303468085, - "venv/lib/python3.13/site-packages/jeepney/wrappers.py": 15234251727654692435, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_errors.py": 3301889847545579393, - "frontend/build/_app/immutable/chunks/D6Fz1J1p.js": 10270384133479184115, - "venv/lib/python3.13/site-packages/pandas/core/computation/scope.py": 16859504937881548797, - "venv/lib/python3.13/site-packages/pip/_internal/models/link.py": 7172346414535657925, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_common_basic.py": 1945891789924713537, - "venv/lib/python3.13/site-packages/cffi/_cffi_errors.h": 1479575326813300292, - "venv/lib/python3.13/site-packages/pygments/lexers/nit.py": 18262907536841158657, - "venv/lib/python3.13/site-packages/dateutil/__init__.py": 17666544837260663800, - "venv/lib/python3.13/site-packages/jeepney/io/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_internal/locations/base.py": 7966628150470073365, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_recfunctions.py": 16014503284544747867, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_bin_groupby.py": 14831211659124136806, - "venv/lib/python3.13/site-packages/secretstorage/item.py": 15486045386471912693, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_deprecate.py": 16723865897349422996, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py": 1359143503575245224, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_series_equal.py": 1223890028205011021, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/frequencies/test_freq_code.py": 13758353172962109140, - "venv/lib/python3.13/site-packages/pygments/styles/manni.py": 11853576691280294949, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_ddl.py": 3690189094298044728, - "venv/lib/python3.13/site-packages/websockets/sync/utils.py": 15135020989207191168, - "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/WHEEL": 8600534672961461758, - "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/RECORD": 11976403473353722969, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_iloc.py": 16160558158155038350, - "venv/lib/python3.13/site-packages/pandas/_libs/arrays.cpython-313-x86_64-linux-gnu.so": 8634234308863090562, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/numpy_/test_indexing.py": 13703573544185051910, - "venv/lib/python3.13/site-packages/greenlet/TGreenlet.cpp": 4121171004360760212, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/core.py": 14576109242055156193, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/sqltypes.py": 2952996080192295188, - "frontend/build/_app/immutable/chunks/BmPq9SJR.js": 15603957767748490749, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/numerictypes.pyi": 15443101937711720707, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_numeric_only.py": 13610631681447961840, - "venv/lib/python3.13/site-packages/numpy/_core/_umath_tests.cpython-313-x86_64-linux-gnu.so": 3478629877770507853, - "backend/src/services/llm_prompt_templates.py": 10835300145692648460, - "backend/src/services/clean_release/repositories/publication_repository.py": 11208425895213508165, - "venv/lib/python3.13/site-packages/numpy/lib/introspect.py": 5706729538356302335, - "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py3-objarr.npy": 17303060773267383537, - "frontend/src/routes/settings/git/+page.svelte": 9547035239167234477, - "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/shared.py": 1162754506656505196, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_compression.py": 8061288329598682752, - "venv/lib/python3.13/site-packages/pandas/core/indexes/api.py": 13750417165301467381, - "venv/pyvenv.cfg": 2513607356761047446, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/console.py": 3540660073442483692, - "venv/lib/python3.13/site-packages/httpx/_transports/wsgi.py": 11981127094916412794, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/__init__.py": 4014740126161973332, - "venv/lib/python3.13/site-packages/pandas/__init__.py": 793698514004377486, - "venv/lib/python3.13/site-packages/numpy/polynomial/polyutils.py": 12355826783958402233, - "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/certifi/__main__.py": 8412594868333244844, - "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_windows.py": 9171347078900057357, - "backend/src/api/routes/datasets.py": 10960880516483380521, - "backend/src/services/clean_release/mappers.py": 8684357784135236729, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_util.py": 6573034224727513309, - "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/__main__.py": 6480933938145689964, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-sinh.csv": 12358602501260667480, - "venv/lib/python3.13/site-packages/pandas/_libs/ops_dispatch.pyi": 16176428602589420913, - "venv/lib/python3.13/site-packages/uvicorn/protocols/http/h11_impl.py": 1419719055394402145, - "venv/lib/python3.13/site-packages/pygments/lexers/grammar_notation.py": 9009644754106884095, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_iterator.py": 13869355993837330406, - "venv/lib/python3.13/site-packages/pandas/core/ops/invalid.py": 10186546927135277579, - "frontend/build/_app/immutable/nodes/5.ASUv2wEk.js": 11537528209294787573, - "venv/lib/python3.13/site-packages/uvicorn/supervisors/multiprocess.py": 10047012560534876149, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/ndarraytypes.h": 6765088137325865789, - "venv/lib/python3.13/site-packages/pandas/core/internals/api.py": 14632030170441237968, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/period.pyi": 13110894861187422750, - "venv/lib/python3.13/site-packages/pandas/core/computation/common.py": 5369352684655954608, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_interp_fillna.py": 5530041283991655415, - "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/licenses/LICENSE": 3868190977070717994, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_series.py": 17126297747681689408, - "backend/src/schemas/dataset_review_pkg/_dtos.py": 13301885458265748263, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ndarray_misc.py": 8706265665356094801, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/tzconversion.pyi": 10947021928755741366, - "venv/lib/python3.13/site-packages/pandas/tests/window/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_hashing.py": 11119284926039008503, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/exceptions.py": 14982764273485726199, - "venv/lib/python3.13/site-packages/PIL/EpsImagePlugin.py": 1824020964179364491, - "venv/lib/python3.13/site-packages/pandas/core/sorting.py": 689057022713741051, - "venv/lib/python3.13/site-packages/numpy/_core/_add_newdocs_scalars.pyi": 1826323011169529229, - "venv/lib/python3.13/site-packages/pygments/lexers/diff.py": 5930189593946259160, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_groupby_subclass.py": 5540948578269213810, - "frontend/build/favicon.png": 16740551781088792605, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-log.csv": 221209423454319618, - "venv/lib/python3.13/site-packages/pygments/lexer.py": 3658450949169647228, - "backend/src/api/routes/__tests__/test_profile_api.py": 1724401885939803766, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/JaroWinkler.py": 1341849861184131135, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm32_ios.h": 6751259472945638998, - "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/licenses/LICENSE": 4672913476475760762, - "venv/lib/python3.13/site-packages/pydantic/_internal/_internal_dataclass.py": 13206778611516171760, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/numerictypes.pyi": 15565573472138344755, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/cli/hiworld.f90": 6321872653169140461, - "venv/lib/python3.13/site-packages/numpy/matlib.pyi": 8017902013455539981, - "venv/lib/python3.13/site-packages/h11/_writers.py": 8585823094975171480, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/fields.cpython-313-x86_64-linux-gnu.so": 16927263232848238698, - "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/__init__.py": 17630019785411546070, - "venv/lib/python3.13/site-packages/jsonschema/_utils.py": 14237077105384631097, - "venv/lib/python3.13/site-packages/pandas/plotting/_misc.py": 10202078355301731720, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_datetimelike.py": 10294379314868108649, - "venv/lib/python3.13/site-packages/websockets/sync/connection.py": 2791040669094103200, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/operators.py": 5584982192355398255, - "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/METADATA": 10707390829735099042, - "venv/lib/python3.13/site-packages/pygments/styles/bw.py": 869630132791421268, - "venv/lib/python3.13/site-packages/pygments/lexers/mime.py": 7768464764849718825, - "venv/lib/python3.13/site-packages/pandas/tests/resample/test_time_grouper.py": 3203729024111501071, - "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE": 8438117004702803716, - "venv/bin/httpx": 6651007446865716794, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_manylinux.py": 12272275001961360101, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/measure.py": 18327922742808984428, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/parameters.py": 247312908510013930, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_week.py": 5756727157937218128, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/lib_user_array.py": 8711697474193296376, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/string_/test_string.py": 3834067244989779682, - "frontend/src/lib/api.js": 12667793754050845906, - "backend/test.db": 15130871412783076140, - "venv/lib/python3.13/site-packages/jeepney/io/tests/conftest.py": 13381752264278264218, - "venv/lib/python3.13/site-packages/cffi/cparser.py": 5246372077864988755, - "venv/lib/python3.13/site-packages/uvicorn/middleware/wsgi.py": 9179913252056394958, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/jupyter.py": 4446535289156468239, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_nanfunctions.py": 2356468092336024365, - "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/METADATA": 9586902338916246692, - "venv/lib/python3.13/site-packages/pip/_internal/operations/install/editable_legacy.py": 8145740626773638218, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/ranges.py": 14549386056660231279, - "venv/lib/python3.13/site-packages/pip/_internal/utils/retry.py": 15244280495983216204, - "venv/lib/python3.13/site-packages/pip/_vendor/idna/__init__.py": 6489437464172324468, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_truncate.py": 9623613657871046837, - "venv/lib/python3.13/site-packages/pygments/lexers/verification.py": 16645885125604874183, - "venv/lib/python3.13/site-packages/pandas/io/json/_table_schema.py": 3103776773020058805, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh2848.f90": 5799106109999071412, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/groupby.py": 14661033005768429774, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_where.py": 3272402211717453881, - "frontend/src/lib/components/reports/ReportCard.svelte": 15186215132458187779, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ufunc_config.pyi": 11754104063443791746, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/dtype.pyi": 465999539608816848, - "venv/lib/python3.13/site-packages/numpy/f2py/rules.py": 9322914583460630877, - "backend/src/services/resource_service.py": 4718644821742397530, - "venv/lib/python3.13/site-packages/pip/_internal/utils/temp_dir.py": 8657533190026387896, - "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/__init__.py": 8880435562116660648, - "venv/lib/python3.13/site-packages/pygments/lexers/srcinfo.py": 16466766917840327610, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_is_monotonic.py": 12899848089975919515, - "venv/lib/python3.13/site-packages/pygments/lexers/varnish.py": 3608370156692616959, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/color_triplet.py": 17264298271771184, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/__init__.py": 8811477458865810434, - "venv/lib/python3.13/site-packages/numpy/lib/_scimath_impl.pyi": 8879196542984811747, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_2_compat.h": 6512969159139431485, - "venv/lib/python3.13/site-packages/pandas/core/internals/construction.py": 893951849732478990, - "venv/lib/python3.13/site-packages/pygments/formatters/__init__.py": 8009871891954370829, - "venv/lib/python3.13/site-packages/cffi/setuptools_ext.py": 30796527302561458, - "venv/lib/python3.13/site-packages/urllib3/http2/__init__.py": 62108352209996836, - "venv/lib/python3.13/site-packages/pillow.libs/libwebpdemux-747f2b49.so.2.0.17": 7615380692384566654, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_util.py": 16908393806993809474, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_unary.py": 8046543893978198672, - "venv/lib/python3.13/site-packages/numpy/lib/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/core/arrays/__init__.py": 10460719978124302175, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_interval.py": 1228451182688768814, - "venv/lib/python3.13/site-packages/pandas/tests/base/test_value_counts.py": 9352540710376306520, - "venv/lib/python3.13/site-packages/pygments/formatters/bbcode.py": 8614391937525677922, - "venv/lib/python3.13/site-packages/requests/help.py": 1885413874621672882, - "venv/lib/python3.13/site-packages/urllib3/util/util.py": 12490259045660543460, - "backend/src/core/ws_log_handler.py": 7507069843556560249, - "venv/lib/python3.13/site-packages/pip/_vendor/tomli/py.typed": 9796674040111366709, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_fsspec.py": 6936167344144998172, - "venv/lib/python3.13/site-packages/rapidfuzz/_utils.py": 14374663810542207121, - "venv/lib/python3.13/site-packages/pygments/console.py": 3540660073442483692, - "venv/lib/python3.13/site-packages/yaml/events.py": 745772917931702910, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi": 17458894097215345719, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_hashtable.py": 2620207240930665390, - "venv/lib/python3.13/site-packages/apscheduler/schedulers/twisted.py": 16059862619165885730, - "venv/lib/python3.13/site-packages/more_itertools/recipes.py": 5254043689078173976, - "venv/lib/python3.13/site-packages/numpy/_globals.pyi": 12839879732484756979, - "venv/lib/python3.13/site-packages/passlib/crypto/scrypt/_salsa.py": 1756536674254931838, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_iteration.py": 2118501258411211851, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame_subplots.py": 4596089143375088346, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_index_as_string.py": 292438690259156869, - "venv/lib/python3.13/site-packages/pip/_internal/network/xmlrpc.py": 5064964778750366059, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_constructors.py": 6515602441449955627, - "venv/lib/python3.13/site-packages/pandas/arrays/__init__.py": 10005095663840461954, - "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/licenses/LICENSE": 9235234428907927646, - "venv/lib/python3.13/site-packages/numpy/fft/tests/test_helper.py": 5362180645056413158, - "venv/lib/python3.13/site-packages/pip/_vendor/certifi/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/legacy/resolver.py": 2492962412506082234, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/isocintrin/isoCtests.f90": 6208802899442270399, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_smoke.py": 7476432112986643847, - "venv/lib/python3.13/site-packages/numpy/ctypeslib/_ctypeslib.py": 12636954896699095212, - "venv/lib/python3.13/site-packages/pip/_internal/req/req_install.py": 7181005402818830655, - "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/WHEEL": 18203500019759199992, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/literal.py": 5641382379370828704, - "venv/lib/python3.13/site-packages/fastapi/concurrency.py": 17937688838625807004, - "venv/lib/python3.13/site-packages/pip/_internal/commands/debug.py": 13528633965821062950, - "venv/lib/python3.13/site-packages/pydantic/v1/schema.py": 10008526732895436361, - "venv/lib/python3.13/site-packages/numpy/lib/_function_base_impl.pyi": 2628035888703031029, - "venv/lib/python3.13/site-packages/pandas/api/typing/aliases.py": 11194270368147942988, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_getitem.py": 11882213323878588707, - "venv/lib/python3.13/site-packages/git/refs/symbolic.py": 14996822770181935589, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/arrayobject.h": 4345432890284821918, - "venv/lib/python3.13/site-packages/dateutil/tzwin.py": 5149446703746204349, - "venv/lib/python3.13/site-packages/pygments/lexers/kusto.py": 16285533608924436308, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/__init__.py": 15130871412783076140, - "frontend/src/components/TaskRunner.svelte": 18014857434320509117, - "venv/lib/python3.13/site-packages/pandas/util/__init__.py": 4070574585004336241, - "frontend/build/_app/immutable/nodes/22.j448M3vH.js": 17913524864494885194, - "backend/src/api/routes/__tests__/test_clean_release_legacy_compat.py": 10703957986116846695, - "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/INSTALLER": 17282701611721059870, - "backend/src/api/routes/__tests__/test_clean_release_v2_api.py": 13464989331176891726, - "backend/src/api/routes/dashboards/_schemas.py": 10686942001147126057, - "backend/src/services/profile_service.py": 10016788771873430731, - "venv/lib/python3.13/site-packages/attr/_cmp.py": 3297594878548179300, - "venv/lib/python3.13/site-packages/pandas/tests/test_optional_dependency.py": 7930709646653025355, - "backend/tests/test_translate_history.py": 14504549443376034614, - "venv/lib/python3.13/site-packages/PIL/_imaging.pyi": 17119084207765761921, - "venv/lib/python3.13/site-packages/PIL/_deprecate.py": 15076725072094847812, - "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/__init__.py": 12975130533860125272, - "frontend/static/favicon.png": 16740551781088792605, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh25211.f": 1486878017876900890, - "venv/lib/python3.13/site-packages/httpcore/_sync/__init__.py": 7676937747094511752, - "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/licenses/numpy/ma/LICENSE": 6723325434476453127, - "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE": 17675192170670564526, - "venv/lib/python3.13/site-packages/PIL/ImageFilter.py": 10974179993355630141, - "venv/lib/python3.13/site-packages/httpcore/_sync/socks_proxy.py": 4418971149260853240, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/__init__.py": 9106887608906281686, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_endian.h": 2761502363895204652, - "venv/lib/python3.13/site-packages/numpy/_core/__init__.pyi": 2783897020308040960, - "venv/lib/python3.13/site-packages/pandas/_libs/reshape.pyi": 88721900806893992, - "venv/lib/python3.13/site-packages/itsdangerous/__init__.py": 6969210114978365656, - "venv/lib/python3.13/site-packages/PIL/ImageMode.py": 2361901066832921091, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_dtypes.py": 2313400030798822026, - "venv/lib/python3.13/site-packages/pandas/core/reshape/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/inout.f90": 1521999519583206538, - "backend/tests/services/clean_release/test_policy_resolution_service.py": 3471271299977247598, - "venv/lib/python3.13/site-packages/pandas/core/ops/dispatch.py": 7041711677358147534, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/__init__.py": 15340047930888693984, - "venv/lib/python3.13/site-packages/passlib/handlers/des_crypt.py": 15304795681178011265, - "venv/lib/python3.13/site-packages/pygments/lexers/_googlesql_builtins.py": 12251289884604745945, - "venv/lib/python3.13/site-packages/git/repo/__init__.py": 6173604284490891047, - "backend/src/services/clean_release/repositories/audit_repository.py": 9617813121624417458, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/pubprivmod.f90": 8680230823849248800, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/OSA.pyi": 2700568594438441246, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_shares_memory.py": 2911029123657970431, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/timedeltas/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/url.py": 1558176735588971829, - "venv/lib/python3.13/site-packages/pandas/_libs/byteswap.pyi": 6723628093359375823, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/authlib/jose/drafts/__init__.py": 4770885427721822507, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_routines.py": 4860173574365060471, - "venv/lib/python3.13/site-packages/numpy/_core/printoptions.py": 3897105663572772838, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_stack.py": 5316826578695144945, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh23879.f90": 13174951794239860144, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/common/block.f": 10727418304254548946, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/lib_polynomial.pyi": 4266795948450921502, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_dropna.py": 3168969662216766293, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_dialect.py": 4700488504252803159, - "venv/lib/python3.13/site-packages/pygments/lexers/usd.py": 934820244673741695, - "venv/lib/python3.13/site-packages/authlib/oidc/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/fixed_string.f90": 8654788371538544793, - "venv/lib/python3.13/site-packages/pydantic/_internal/_generate_schema.py": 7343380095251236503, - "venv/lib/python3.13/site-packages/PIL/PixarImagePlugin.py": 12061717157676819554, - "frontend/src/components/storage/FileUpload.svelte": 14805293086068620697, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/url.py": 9692934100602135106, - "venv/lib/python3.13/site-packages/apscheduler/triggers/cron/expressions.py": 8015786996064244656, - "venv/lib/python3.13/site-packages/apscheduler/triggers/calendarinterval.py": 12481650996882675949, - "venv/lib/python3.13/site-packages/jose/jwt.py": 3405491994268385178, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_combine_first.py": 11425059090050867171, - "venv/lib/python3.13/site-packages/apscheduler/schedulers/qt.py": 12828696264909048448, - "backend/src/services/__tests__/test_encryption_manager.py": 7097521778855634174, - "frontend/src/components/tools/MapperTool.svelte": 16528529568914906720, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7515/models.py": 17356033861792227817, - "venv/lib/python3.13/site-packages/pygments/regexopt.py": 16073437303824961930, - "frontend/build/_app/immutable/chunks/C2fyd9aw.js": 14162123587373788258, - "venv/lib/python3.13/site-packages/pip/_internal/locations/_distutils.py": 13559457590971821771, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/help.py": 3124654001287175629, - "venv/lib/python3.13/site-packages/git/objects/commit.py": 12317865358846331802, - "venv/lib/python3.13/site-packages/pygments/lexers/installers.py": 5083244110640899732, - "venv/lib/python3.13/site-packages/pip/_internal/commands/show.py": 11839987586497822300, - "frontend/src/lib/auth/permissions.js": 12787489292887903923, - "venv/lib/python3.13/site-packages/pandas/core/arrays/sparse/accessor.py": 8373575175447947401, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth1/cache.py": 15037055977924167732, - "venv/lib/python3.13/site-packages/pandas/core/missing.py": 13332302791063948992, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_setitem.py": 2059440410065814114, - "venv/lib/python3.13/site-packages/idna/core.py": 7725813731640753267, - "venv/lib/python3.13/site-packages/pydantic/_internal/_decorators_v1.py": 16723743964226526155, - "venv/lib/python3.13/site-packages/passlib/tests/utils.py": 15403198680231294263, - "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/REQUESTED": 15130871412783076140, - "backend/src/models/__init__.py": 13280325675752905975, - "frontend/src/components/tasks/LogEntryRow.svelte": 10992388311554587653, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/requests.py": 6703055041957748929, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_normalize.py": 11373923576259976755, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_aarch64_gcc.h": 8140470539757491899, - "venv/lib/python3.13/site-packages/gitdb/test/test_stream.py": 18159319442241857857, - "backend/src/plugins/llm_analysis/plugin.py": 13563674795958736723, - "backend/src/core/superset_profile_lookup.py": 7009220677319131100, - "venv/lib/python3.13/site-packages/passlib/utils/pbkdf2.py": 15802959653228956404, - "venv/lib/python3.13/site-packages/numpy/polynomial/hermite_e.py": 3654583356259850174, - "venv/lib/python3.13/site-packages/pandas/_libs/hashtable.cpython-313-x86_64-linux-gnu.so": 16963890771704942465, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_index.py": 10837492069089787028, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_custom_business_hour.py": 14128508606361694814, - "venv/lib/python3.13/site-packages/pandas/io/parsers/c_parser_wrapper.py": 5841064965672872661, - "venv/lib/python3.13/site-packages/tzlocal-5.3.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pygments/formatters/terminal256.py": 11473927781167132440, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/resource_owner_password_credentials.py": 9559295461619813890, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py": 13763154154862871596, - "venv/lib/python3.13/site-packages/passlib/handlers/sha1_crypt.py": 5752556581809938954, - "venv/bin/pygmentize": 2166491742035772877, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/methods.py": 8059372834301557565, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_rename_axis.py": 12432900682994622455, - "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/RECORD": 8104089156015954779, - "venv/lib/python3.13/site-packages/pandas/io/formats/style.py": 13235403689320479165, - "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/PIL/SgiImagePlugin.py": 6916200065790425497, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/token_validator.py": 2932866595454490527, - "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/assertion_session.py": 18109848200936213195, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/region.py": 12579403230062395563, - "venv/lib/python3.13/site-packages/pandas/core/indexers/objects.py": 409599993084996593, - "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/METADATA": 9458019064618635438, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/base.py": 5924592154431932923, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/markers.py": 8297667990263004097, - "venv/lib/python3.13/site-packages/pandas/core/arrays/_arrow_string_mixins.py": 16862663427780315543, - "venv/lib/python3.13/site-packages/pygments/styles/abap.py": 11543320440974964547, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_x32_unix.h": 9165722966153119887, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/validator.py": 14200019408839159476, - "venv/lib/python3.13/site-packages/numpy/testing/_private/utils.py": 3646474165295296669, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_type_check.py": 3935091606923106753, - "venv/lib/python3.13/site-packages/PIL/BlpImagePlugin.py": 7209218638257731375, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/errors.py": 4817746830102488448, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_generator_mt19937_regressions.py": 8981142329261965538, - "venv/lib/python3.13/site-packages/pygments/lexers/fortran.py": 6859638690245583968, - "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/limited_api1.c": 8494977275215578998, - "frontend/src/routes/settings/git/__tests__/git_settings_page.ux.test.js": 5334904955024018051, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_pickle.py": 388513670243918273, - "venv/lib/python3.13/site-packages/anyio/_core/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/testing.pyi": 16263777835572515739, - "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/RECORD": 2024211292009381421, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_date_range.py": 16128552513324017349, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_repr.py": 7552782621374439569, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-cbrt.csv": 8929553891827331780, - "venv/lib/python3.13/site-packages/pluggy/_result.py": 16790448391766505769, - "venv/lib/python3.13/site-packages/jose/backends/base.py": 12795468809472351388, - "venv/lib/python3.13/site-packages/pygments/lexers/q.py": 15426466074881683794, - "venv/lib/python3.13/site-packages/pycparser/ply/__init__.py": 5040371336745852622, - "venv/lib/python3.13/site-packages/numpy/_expired_attrs_2_0.pyi": 1424133074692438241, - "venv/lib/python3.13/site-packages/starlette/middleware/__init__.py": 9524277524025372856, - "venv/lib/python3.13/site-packages/pyasn1/codec/__init__.py": 15728752901274520502, - "venv/lib/python3.13/site-packages/cffi/api.py": 5325821560978502135, - "venv/lib/python3.13/site-packages/numpy/core/getlimits.py": 14257687540182552408, - "venv/lib/python3.13/site-packages/urllib3-2.6.2.dist-info/METADATA": 2624107638694809013, - "venv/lib/python3.13/site-packages/numpy/f2py/f90mod_rules.py": 2953876934840155920, - "venv/lib/python3.13/site-packages/pandas/core/computation/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/styles/lilypond.py": 12486578704132023691, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7515/__init__.py": 2791238481813240414, - "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE": 14316979437290727897, - "backend/src/services/git/_branch.py": 17338017626270161139, - "venv/lib/python3.13/site-packages/greenlet/slp_platformselect.h": 10428201768098464386, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_extension.py": 4831602527247293866, - "venv/lib/python3.13/site-packages/jeepney/tests/test_wrappers.py": 4813328996575597980, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/structures.py": 2668010839316715865, - "venv/lib/python3.13/site-packages/pygments/lexers/rebol.py": 10460732731157940232, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_reductions.py": 17810094294132819208, - "venv/lib/python3.13/site-packages/rsa/util.py": 13423880475585138638, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_types.py": 4083297168618981717, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_array_utils.py": 14682148205379122638, - "venv/lib/python3.13/site-packages/pandas/tests/test_algos.py": 11860014896449369497, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/DamerauLevenshtein.pyi": 2700568594438441246, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/api.py": 12339139862411341381, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/mapper.py": 16715864918034312591, - "venv/lib/python3.13/site-packages/pip/__pip-runner__.py": 1138979712722199296, - "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/RECORD": 10552906886624188222, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_chained_assignment_deprecation.py": 7694137945143129474, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/hybrid.py": 5793056975404534989, - "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/WHEEL": 12146818530001975731, - "frontend/src/lib/components/reports/__tests__/reports_filter_performance.test.js": 10583581505638859039, - "venv/lib/python3.13/site-packages/passlib/tests/test_totp.py": 9150389741890748343, - "backend/delete_running_tasks.py": 4453015771820664913, - "venv/lib/python3.13/site-packages/rapidfuzz/process_py.py": 17563673072076876125, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/multiarray.pyi": 17232959648651278910, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_unstack.py": 11834917665446437163, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/_cmd.py": 14954780419054588164, - "venv/lib/python3.13/site-packages/rsa/randnum.py": 13958355153634534526, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_first_valid_index.py": 15763400665851423183, - "frontend/src/routes/settings/connections/+page.svelte": 276661107137418305, - "frontend/build/_app/immutable/chunks/BqoVXGj7.js": 4487374658501570122, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_fillna.py": 13675584998644907265, - "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/METADATA": 14070889071949105584, - "venv/lib/python3.13/site-packages/pandas/tests/construction/test_extract_array.py": 3100523370314150013, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_datetimeindex.py": 14816894350435545024, - "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/licenses/LICENSE": 14610443802372107702, - "venv/lib/python3.13/site-packages/passlib/ext/django/__init__.py": 11713862539944289631, - "venv/lib/python3.13/site-packages/rapidfuzz/fuzz.py": 1094797446508902066, - "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/METADATA": 3786116428839780263, - "venv/lib/python3.13/site-packages/urllib3/util/request.py": 9432067988603015849, - "venv/lib/python3.13/site-packages/numpy/_core/tests/_natype.py": 12013743717306900860, - "venv/lib/python3.13/site-packages/pandas/tests/libs/test_libalgos.py": 13934861984871432400, - "venv/lib/python3.13/site-packages/pandas/testing.py": 18352975429944961593, - "venv/lib/python3.13/site-packages/pip/_vendor/idna/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/test_numba.py": 1548992314517942530, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_mangle_dupes.py": 15613858733226279352, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/numpy_/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_period.py": 3419297908709788013, - "venv/lib/python3.13/site-packages/pygments/lexers/dotnet.py": 17970884907837213099, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7516/jwe.py": 10478268710546063196, - "venv/lib/python3.13/site-packages/passlib/tests/__init__.py": 16543763165305017760, - "venv/lib/python3.13/site-packages/pandas/tests/io/sas/test_xport.py": 14080914637071212225, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/object/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/array.py": 13840510238256626632, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/_psycopg_common.py": 4111797647365870137, - "venv/lib/python3.13/site-packages/starlette/config.py": 16509394679506300256, - "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/assertion_client.py": 4261295109880943573, - "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/WHEEL": 13232065379159720345, - "venv/lib/python3.13/site-packages/pyasn1/codec/ber/eoo.py": 5126566925877730303, - "venv/lib/python3.13/site-packages/websockets/speedups.pyi": 10322382420215791091, - "venv/lib/python3.13/site-packages/websockets/legacy/handshake.py": 376095144657994920, - "venv/lib/python3.13/site-packages/pygments/formatters/terminal.py": 14380579279194052030, - "backend/src/schemas/__init__.py": 8700952869915187087, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/common.py": 9354442374897565498, - "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/METADATA": 12747626683405487434, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_mips_unix.h": 17373389962405490482, - "venv/lib/python3.13/site-packages/pydantic/_internal/_namespace_utils.py": 5237766366340181382, - "venv/lib/python3.13/site-packages/PIL/_avif.pyi": 18222325750818585549, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_join.py": 4213933887062089605, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/frequencies/test_frequencies.py": 14039315221824123200, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi": 16312114056447366970, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7521/__init__.py": 635200170992032966, - "venv/lib/python3.13/site-packages/pygments/lexers/pascal.py": 5992422120590352714, - "frontend/src/lib/utils.js": 5436516952434475239, - "backend/src/models/dataset_review_pkg/_enums.py": 2243691494729792539, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/char.pyi": 1639810799219814643, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/schema.py": 8358664670620586363, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py": 18325640697829098192, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_xs.py": 1897144171324167738, - "venv/lib/python3.13/site-packages/pygments/lexers/felix.py": 2334148799642808111, - "venv/lib/python3.13/site-packages/pydantic_core/__init__.py": 10563134104783445328, - "venv/lib/python3.13/site-packages/passlib/crypto/_blowfish/__init__.py": 3521833849634327520, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/base.py": 10514193453447954780, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/validation": 5398883285871046811, - "venv/lib/python3.13/site-packages/numpy/ma/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/test_na_scalar.py": 13950899907067372014, - "venv/lib/python3.13/site-packages/pip/_internal/models/candidate.py": 3099785363867247334, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_rolling_functions.py": 2255133375699086300, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_setops.py": 15365064654930699428, - "venv/lib/python3.13/site-packages/pandas/core/interchange/from_dataframe.py": 8278998381856028462, - "venv/lib/python3.13/site-packages/jeepney/fds.py": 16573439672758641560, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/interval/test_interval.py": 18040371224712437126, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/yaml/emitter.py": 6036657460416912844, - "venv/lib/python3.13/site-packages/pygments/lexers/dylan.py": 16246299896150112270, - "venv/lib/python3.13/site-packages/httpx/_urlparse.py": 8270294590945652412, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_rank.py": 5398520257852104723, - "venv/lib/python3.13/site-packages/pydantic/_internal/_git.py": 8760779092674434712, - "venv/lib/python3.13/site-packages/pygments/__main__.py": 13200500591994479045, - "backend/src/plugins/storage/__init__.py": 5046554941488079204, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/io/excel/_pyxlsb.py": 10035587889954731645, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/histograms.pyi": 9255620158831643201, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi": 9835891299056543360, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_resolution.py": 18198858926948468112, - "venv/lib/python3.13/site-packages/pygments/lexers/devicetree.py": 9820277168995231927, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_arithmetic.py": 17427754419626622853, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_at.py": 1029744344133153888, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_groupby.py": 16180421154734310873, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/_initialize_py.py": 6518425342376455821, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_quoting.py": 1147992698412929658, - "venv/lib/python3.13/site-packages/authlib/oidc/registration/__init__.py": 9448552677419908109, - "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/RECORD": 7844752443022514265, - "venv/lib/python3.13/site-packages/numpy/core/numerictypes.py": 5776941405348611027, - "venv/lib/python3.13/site-packages/keyring/backends/SecretService.py": 17482492072907438453, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_astype.py": 15275720655739710249, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_size.py": 16658280641516916790, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_ipython_compat.py": 12632271159842988561, - "frontend/build/_app/immutable/chunks/Ce0bZqFT.js": 15458334940864777489, - "venv/lib/python3.13/site-packages/ecdsa/test_sha3.py": 12459459298663975730, - "backend/src/core/__tests__/test_config_manager_compat.py": 656412918792001580, - "frontend/src/services/connectionService.js": 16613277497380191542, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi": 9405122752084210696, - "venv/lib/python3.13/site-packages/pygments/styles/colorful.py": 15708848112363953394, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_function.py": 14142346897145169685, - "venv/lib/python3.13/site-packages/passlib/hosts.py": 11171402820797380938, - "venv/lib/python3.13/site-packages/pandas/api/extensions/__init__.py": 882327406374020760, - "venv/lib/python3.13/site-packages/pip/_internal/__init__.py": 7060345458485901709, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/openssl/__init__.py": 14571251344510763303, - "venv/lib/python3.13/site-packages/pandas/tests/tools/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/PIL/PsdImagePlugin.py": 4347581352716313298, - "frontend/build/_app/immutable/nodes/6.LcHOtDGX.js": 6856038459830890051, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_parser.py": 8145921552236189367, - "backend/src/core/scheduler.py": 2472649939852521889, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/_numpyconfig.h": 18150055639397773687, - "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-tan.csv": 6233924374281534211, - "venv/lib/python3.13/site-packages/numpy/core/umath.py": 4476812261188108155, - "venv/lib/python3.13/site-packages/_pytest/_version.py": 4890545974654141536, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py": 14190849361275154667, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_pick.py": 15760492005471315814, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_asof.py": 18383318159051131669, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/polynomial_polyutils.pyi": 3841660741524427413, - "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/WHEEL": 2357997949040430835, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_iloc.py": 7586049606891014270, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_round_trip.py": 14684057343786032776, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/relationships.py": 8401569216905291119, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/chararray.pyi": 17340224970247832290, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/ext.py": 8361831054662096695, - "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/RECORD": 5612689057373270213, - "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/rule.py": 5318344538761246105, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_arrow.py": 10068866425352207113, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_string.py": 13234624995755549329, - "venv/lib/python3.13/site-packages/pygments/lexers/ampl.py": 4594260378653369985, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_tolist.py": 8343058000369672317, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_pandas.py": 10894229569442891394, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_stata.py": 3030675968330529905, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/timedeltas/test_cumulative.py": 8178093067175155877, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_docs.py": 17898499890274685198, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_timegrouper.py": 276805119790266454, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_normalize.py": 7378656958631172603, - "venv/lib/python3.13/site-packages/pandas/io/json/_json.py": 1393095374713367445, - "venv/lib/python3.13/site-packages/pandas/core/array_algos/quantile.py": 17341029201525362074, - "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/event/base.py": 18238453132776154057, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/tags.py": 254599446317461188, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_libgroupby.py": 9985769511561272200, - "venv/lib/python3.13/site-packages/pygments/lexers/comal.py": 769727871242464458, - "venv/lib/python3.13/site-packages/ecdsa/test_malformed_sigs.py": 2606295770491528880, - "venv/lib/python3.13/site-packages/fastapi/responses.py": 6092572095549573593, - "venv/lib/python3.13/site-packages/apscheduler/events.py": 10748346950729364899, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py": 10057637461647348368, - "venv/lib/python3.13/site-packages/_pytest/_io/wcwidth.py": 13515196388826152383, - "venv/lib/python3.13/site-packages/numpy/_core/_internal.pyi": 6840742333427164920, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-cosh.csv": 9287774220881887502, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7519/__init__.py": 16532856661318424631, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_npfuncs.py": 8238385897366878558, - "venv/lib/python3.13/site-packages/uvicorn/main.py": 5843333502356171120, - "backend/src/services/clean_release/source_isolation.py": 10193276492118254392, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/__init__.py": 15735839022111230173, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py": 16550134594771291234, - "venv/lib/python3.13/site-packages/PIL/BdfFontFile.py": 10811640174253687845, - "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/WHEEL": 2357997949040430835, - "venv/lib/python3.13/site-packages/PIL/GifImagePlugin.py": 16073125611077200693, - "venv/lib/python3.13/site-packages/pygments/lexers/gleam.py": 5161116162065925222, - "venv/lib/python3.13/site-packages/pygments/lexers/prolog.py": 599539463980699865, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/lib_version.pyi": 18093228021414910594, - "venv/lib/python3.13/site-packages/gitdb/db/base.py": 3375449584942578385, - "venv/lib/python3.13/site-packages/greenlet/tests/test_contextvars.py": 6077482318964702132, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_legendre.py": 18266595775500927868, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test__version.py": 9037916251087760255, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_simd_module.py": 7122050193390298178, - "venv/lib/python3.13/site-packages/pandas/core/window/__init__.py": 15180240812077877933, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_tz_convert.py": 77283525137564255, - "venv/lib/python3.13/site-packages/sqlalchemy/event/legacy.py": 13465911776953636949, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/routines/funcfortranname.f": 9330535808093475892, - "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_api.py": 7131036004750554905, - "venv/lib/python3.13/site-packages/git/objects/__init__.py": 2110615482320355676, - "venv/lib/python3.13/site-packages/click/termui.py": 7022374827798605437, - "frontend/build/_app/immutable/chunks/BxhFButn.js": 4448858164012016666, - "venv/lib/python3.13/site-packages/pydantic/tools.py": 8931184638883701008, - "frontend/build/_app/immutable/chunks/BBh5Ucdi.js": 9301800916108664548, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timezones.pyi": 17528421279543420867, - "venv/lib/python3.13/site-packages/gitdb/base.py": 7927279446353696534, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/random/distributions.h": 17162252287980242413, - "venv/lib/python3.13/site-packages/numpy/lib/_format_impl.pyi": 15745512451486888537, - "venv/lib/python3.13/site-packages/pandas/core/tools/timedeltas.py": 7221393731712061943, - "venv/lib/python3.13/site-packages/PIL/_imagingtk.cpython-313-x86_64-linux-gnu.so": 1708198947135304326, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_all_methods.py": 18227706645906325527, - "venv/lib/python3.13/site-packages/uvicorn/protocols/http/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/f2py/cfuncs.py": 10803011906582324302, - "venv/lib/python3.13/site-packages/numpy/_core/einsumfunc.pyi": 4729765666018003634, - "venv/lib/python3.13/site-packages/sqlalchemy/future/engine.py": 4639404437703898767, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py": 11225173272050906977, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_at_time.py": 15352346149584081864, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth1/__init__.py": 13749066369790839334, - "venv/lib/python3.13/site-packages/attr/validators.py": 4453080662115911998, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_nth.py": 17151595277666975085, - "venv/lib/python3.13/site-packages/pandas/core/indexes/multi.py": 9115751979327393200, - "venv/lib/python3.13/site-packages/PIL/_imagingmorph.pyi": 18222325750818585549, - "venv/lib/python3.13/site-packages/fastapi/openapi/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/ssltransport.py": 8148652555908329980, - "venv/lib/python3.13/site-packages/numpy/lib/_function_base_impl.py": 14034239842770171660, - "venv/lib/python3.13/site-packages/urllib3/_request_methods.py": 13671055114247516883, - "backend/src/core/logger/__tests__/test_logger.py": 4188397720199242180, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_datetime.py": 4179112383995346046, - "venv/lib/python3.13/site-packages/psycopg2/sql.py": 18041240927130590808, - "venv/lib/python3.13/site-packages/greenlet/TUserGreenlet.cpp": 16258622236210473101, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_infer_datetimelike.py": 17304541521108700317, - "venv/lib/python3.13/site-packages/tzlocal/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/io/excel/_openpyxl.py": 6676451564208867775, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/associationproxy.py": 7421910137064160936, - "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/RECORD": 2688648742701423549, - "venv/lib/python3.13/site-packages/pydantic/deprecated/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_repeat.py": 9155935990114743583, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_s3.py": 17425535047477778484, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/base.py": 7855217087916771270, - "venv/lib/python3.13/site-packages/pygments/lexers/yara.py": 17510792615476493957, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_logical.py": 6818185860296232476, - "venv/lib/python3.13/site-packages/starlette/endpoints.py": 3379755567024530053, - "venv/lib/python3.13/site-packages/numpy/polynomial/polyutils.pyi": 8195943561787777127, - "venv/lib/python3.13/site-packages/pygments/sphinxext.py": 2768161754523156130, - "frontend/src/lib/ui/Card.svelte": 3442526978752993023, - "venv/lib/python3.13/site-packages/attr/filters.pyi": 13942974755090135724, - "venv/lib/python3.13/site-packages/anyio/functools.py": 4300781896135050327, - "backend/src/core/task_manager/__tests__/test_task_logger.py": 8175976176924379623, - "venv/lib/python3.13/site-packages/jeepney/io/tests/test_asyncio.py": 2820168745710754136, - "venv/lib/python3.13/site-packages/numpy/_typing/_nbit_base.py": 7446626038222262489, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_function.py": 13492077564015386528, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_timer.py": 8196003526646080498, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/interfaces.py": 5371646629319169153, - "venv/lib/python3.13/site-packages/uvicorn/importer.py": 5449629250971153435, - "venv/lib/python3.13/site-packages/pygments/lexers/graphql.py": 2307854666015224677, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/astype_copy.pkl": 4470155972891419163, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_drop_duplicates.py": 12601598844977346213, - "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/RECORD": 12555714734722889695, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/warnings_and_errors.pyi": 9019577563960071498, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_interpolate.py": 7354685880215444946, - "venv/lib/python3.13/site-packages/pandas/io/common.py": 12088195203620154171, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_f2cmap.py": 5863341586534531594, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi": 11733449165276720901, - "frontend/build/_app/immutable/chunks/Cb8PHtdN.js": 5528961359985342557, - "venv/lib/python3.13/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py": 63497920264089252, - "venv/lib/python3.13/site-packages/numpy/lib/user_array.pyi": 1968484998382771986, - "venv/lib/python3.13/site-packages/PIL/ImageCms.py": 5610897002957128546, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/reserved_words.py": 8022121780993497971, - "venv/lib/python3.13/site-packages/numpy/lib/_datasource.py": 18136853038949826147, - "venv/lib/python3.13/site-packages/pandas/core/reshape/reshape.py": 5387892304726762127, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_character.py": 850554362222586199, - "venv/lib/python3.13/site-packages/pygments/lexers/nimrod.py": 8457135214131458774, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_construction.py": 10619501873883861745, - "backend/src/scripts/test_dataset_dashboard_relations.py": 6373309008834670516, - "venv/lib/python3.13/site-packages/cryptography/x509/__init__.py": 4066756344049307453, - "backend/src/core/task_manager/__init__.py": 15991802770215124472, - "backend/src/services/health_service.py": 7295235621662158359, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_setitem.py": 15731267129055947469, - "venv/lib/python3.13/site-packages/pluggy/_version.py": 7195758686717994475, - "venv/lib/python3.13/site-packages/pandas/_config/dates.py": 3863381688916127265, - "venv/lib/python3.13/site-packages/pillow.libs/libopenjp2-94e588ba.so.2.5.4": 2483101806045986252, - "venv/lib/python3.13/site-packages/pygments/lexers/archetype.py": 8995143180482517233, - "frontend/build/_app/immutable/chunks/BEid6Q9Q.js": 16254084752087873732, - "venv/lib/python3.13/site-packages/pandas/tests/apply/conftest.py": 13798335354961261346, - "venv/lib/python3.13/site-packages/websockets/exceptions.py": 12048063047468402740, - "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/WHEEL": 8600534672961461758, - "venv/lib/python3.13/site-packages/ecdsa/test_der.py": 7117444285097824495, - "venv/lib/python3.13/site-packages/pillow.libs/libfreetype-ee1c40c4.so.6.20.4": 12301510769231120312, - "venv/lib/python3.13/site-packages/pandas/core/groupby/__init__.py": 3015263524218708013, - "venv/lib/python3.13/site-packages/pandas/tests/io/sas/test_byteswap.py": 5090287710338893350, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/status_codes.py": 17005213506461710202, - "venv/lib/python3.13/site-packages/ecdsa/_sha3.py": 10353612776869489378, - "venv/lib/python3.13/site-packages/cryptography/__init__.py": 641702726372647770, - "venv/lib/python3.13/site-packages/numpy/lib/_arrayterator_impl.py": 4037074830103951000, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_array_interface.py": 13648006002292914247, - "venv/lib/python3.13/site-packages/sqlalchemy/util/_py_collections.py": 16825222625411635946, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_interval_array_equal.py": 15654625365223535344, - "venv/lib/python3.13/site-packages/pycparser/lextab.py": 16045770806700381661, - "venv/lib/python3.13/site-packages/pip/_internal/commands/check.py": 6937252393012787214, - "venv/lib/python3.13/site-packages/numpy/_typing/_nbit.py": 15498810174380798405, - "venv/lib/python3.13/site-packages/pygments/lexers/compiled.py": 8215290039991205531, - "venv/lib/python3.13/site-packages/charset_normalizer/models.py": 9917300217816275821, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/lib/mixins.py": 7404729635424818497, - "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/RECORD": 1556051687830913019, - "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/apscheduler/jobstores/etcd.py": 4078354986605069279, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/strategies.py": 15673926142454922581, - "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_matmul.py": 16169282225324014901, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_combine_first.py": 3100691245280812910, - "venv/lib/python3.13/site-packages/pygments/styles/onedark.py": 17580335614588531275, - "venv/lib/python3.13/site-packages/pygments/lexers/css.py": 517586374264113606, - "backend/src/core/middleware/trace.py": 13093835356377196397, - "venv/lib/python3.13/site-packages/passlib/tests/test_crypto_scrypt.py": 17378030264314664250, - "backend/src/services/clean_release/__init__.py": 18311663871116449547, - "venv/lib/python3.13/site-packages/pydantic/aliases.py": 14481980467204711659, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/random/libdivide.h": 5314232500579481313, - "run_clean_tui.sh": 14305120550349644601, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth1/nonce.py": 18219168479887197733, - "venv/lib/python3.13/site-packages/websockets/extensions/base.py": 2457233337788783873, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/locators.py": 7638945613579396718, - "venv/lib/python3.13/site-packages/numpy/linalg/lapack_lite.cpython-313-x86_64-linux-gnu.so": 11074560904305737689, - "venv/lib/python3.13/site-packages/pandas/_libs/groupby.cpython-313-x86_64-linux-gnu.so": 14931394220112658268, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_join.py": 2585969863606902743, - "venv/lib/python3.13/site-packages/anyio/_core/_fileio.py": 4507100568401692983, - "venv/lib/python3.13/site-packages/PIL/WebPImagePlugin.py": 5378349556563611996, - "frontend/src/services/gitService.js": 7044825476968825357, - "venv/lib/python3.13/site-packages/numpy/_core/_string_helpers.pyi": 12043292763005874815, - "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/android.py": 8607703347015852195, - "venv/lib/python3.13/site-packages/pandas/core/computation/ops.py": 18250656489649699560, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/generic.py": 17755579497690180620, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_astype.py": 8633811117672837999, - "venv/lib/python3.13/site-packages/starlette/websockets.py": 13834731343216764204, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_update_delete.py": 1317337950621091021, - "venv/lib/python3.13/site-packages/urllib3/util/ssl_match_hostname.py": 7411350247004265632, - "venv/lib/python3.13/site-packages/greenlet/tests/test_leaks.py": 1219835696230215605, - "venv/lib/python3.13/site-packages/pip/_vendor/distro/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/response.py": 353446217435156470, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/generator_pcg64_np121.pkl.gz": 4708588000144933291, - "venv/lib/python3.13/site-packages/numpy/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/_libs/ops.cpython-313-x86_64-linux-gnu.so": 10490009128553397250, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_reductions.py": 2816859231048302619, - "venv/lib/python3.13/site-packages/starlette/responses.py": 14081178833432955042, - "venv/lib/python3.13/site-packages/pip/_internal/wheel_builder.py": 3893922370396791826, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_infer_objects.py": 6769980076977692890, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/assumed_shape/precision.f90": 5518911932373565642, - "venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/authlib/oauth2/auth.py": 8455258275234626534, - "venv/lib/python3.13/site-packages/numpy/lib/_polynomial_impl.py": 12771179468704314203, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_check_indexer.py": 2929031395357057009, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_series.py": 10922287546912569276, - "venv/lib/python3.13/site-packages/pip/_internal/models/__init__.py": 13482731985065116750, - "venv/lib/python3.13/site-packages/h11/_abnf.py": 10456996134895429201, - "venv/lib/python3.13/site-packages/numpy/__init__.cython-30.pxd": 1476688171642957054, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py": 5997687240967250164, - "venv/lib/python3.13/site-packages/numpy/ma/tests/test_core.py": 12712110303898046818, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_astype.py": 11995587502880659624, - "venv/lib/python3.13/site-packages/pygments/lexers/email.py": 4355246279485460399, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_no_deprecated_api.h": 7896028550808252043, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_common.py": 9446944526144164255, - "backend/src/schemas/settings.py": 7182606947764184429, - "backend/src/api/routes/__tests__/test_reports_openapi_conformance.py": 14282751162289385516, - "backend/src/models/dataset_review_pkg/__init__.py": 17789694606496652579, - "venv/lib/python3.13/site-packages/pandas/plotting/_core.py": 6766059152026241747, - "backend/src/services/__tests__/test_llm_provider.py": 8882903833125900085, - "venv/lib/python3.13/site-packages/pandas/core/reshape/api.py": 16364136812252343076, - "venv/lib/python3.13/site-packages/pygments/lexers/actionscript.py": 8829560731160428621, - "venv/lib/python3.13/site-packages/pandas/tests/io/__init__.py": 15130871412783076140, - "frontend/build/_app/immutable/nodes/2.CPPJxXwI.js": 17556014813501798121, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ufunc_config.py": 14728417910466146460, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_deprecate_nonkeyword_arguments.py": 11676809974120007408, - "venv/lib/python3.13/site-packages/httpx/__version__.py": 9473642134297828811, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_count.py": 16947194157984708529, - "backend/src/core/utils/superset_context_extractor/_recovery.py": 13298493527065532076, - "backend/src/core/logger.py": 2069537382060633888, - "venv/lib/python3.13/site-packages/apscheduler/triggers/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_subclass.py": 11096337912872573314, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/markup.py": 5413800947725916538, - "venv/lib/python3.13/site-packages/referencing/py.typed": 15130871412783076140, - "merge_kilo.py": 4282227344165325430, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/util.py": 387985965709546512, - "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/LICENSE": 14599279238716790189, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_pydatetime.py": 16228315407662560992, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_searchsorted.py": 11208047646304753715, - "backend/src/api/routes/__tests__/test_clean_release_source_policy.py": 15242462571646635922, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_io.py": 4199750143323741326, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/gcp.py": 12328409672947966867, - "venv/lib/python3.13/site-packages/sqlalchemy/util/topological.py": 11586868018611359415, - "venv/lib/python3.13/site-packages/pip/_internal/models/selection_prefs.py": 14700113736620314923, - "venv/lib/python3.13/site-packages/httpx/_types.py": 12461152436147308761, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/assignOnlyModule.f90": 15967199601254912096, - "venv/lib/python3.13/site-packages/pygments/lexers/automation.py": 8021926838024836904, - "venv/lib/python3.13/site-packages/dateutil/parser/_parser.py": 9443336890192151397, - "docker/nginx.conf": 16884137323072604795, - "venv/lib/python3.13/site-packages/ecdsa/util.py": 10543788809724099209, - "venv/lib/python3.13/site-packages/httpx/_models.py": 6516278895118988231, - "venv/lib/python3.13/site-packages/gitdb/fun.py": 55226330810782184, - "venv/lib/python3.13/site-packages/pillow.libs/libtiff-295fd75c.so.6.2.0": 17866578820357536656, - "venv/lib/python3.13/site-packages/pandas/tests/apply/test_invalid_arg.py": 610891871484326055, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_missing.py": 6224750552277937853, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/hstore.py": 9700253004947691961, - "venv/lib/python3.13/site-packages/numpy/linalg/_umath_linalg.cpython-313-x86_64-linux-gnu.so": 17652852308919396403, - "backend/src/plugins/llm_analysis/__tests__/test_screenshot_service.py": 17792688368296989200, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/abstract_interface/foo.f90": 14491312943942432024, - "venv/lib/python3.13/site-packages/pandas/_libs/missing.cpython-313-x86_64-linux-gnu.so": 1990755879067132155, - "venv/lib/python3.13/site-packages/authlib/oidc/core/util.py": 5364455058429522633, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalar_methods.py": 14881910898871057761, - "frontend/src/lib/stores/__tests__/mocks/env_public.js": 2540122820969872185, - "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/METADATA": 11759004913807981650, - "backend/src/schemas/translate.py": 12829370350127265103, - "backend/src/core/migration_engine.py": 1303635539349402052, - "backend/src/api/routes/translate/_metrics_routes.py": 18396200266145781983, - "frontend/build/_app/immutable/assets/9.tn0RQdqM.css": 15130871412783076140, - "venv/lib/python3.13/site-packages/passlib/tests/_test_bad_register.py": 5446848310854472376, - "frontend/src/lib/api/reports.js": 2328857814720054000, - "backend/src/scripts/__init__.py": 5400372851656282343, - "venv/lib/python3.13/site-packages/jose/jws.py": 11592462993417182096, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/registration.py": 14639972512303032712, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_np_datetime.py": 18363183516409621205, - "venv/lib/python3.13/site-packages/ecdsa/test_ecdh.py": 3338834550392548171, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Indel_py.py": 9112851333217812996, - "venv/lib/python3.13/site-packages/pip/_internal/commands/configuration.py": 1608697090566485893, - "venv/lib/python3.13/site-packages/pandas/tests/window/moments/conftest.py": 6137462119729731945, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/offsets.pyi": 13173581775000480811, - "venv/lib/python3.13/site-packages/pandas/tests/libs/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/styles/rainbow_dash.py": 12236746494379891602, - "frontend/src/types/dashboard.ts": 9718666955722367566, - "venv/lib/python3.13/site-packages/numpy/_core/lib/libnpymath.a": 17555816778997028283, - "venv/lib/python3.13/site-packages/pygments/lexers/textfmts.py": 17438453283146671997, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/sync.py": 6189260178302531376, - "frontend/build/_app/immutable/nodes/17.DQejmqiq.js": 17001830891938974854, - "venv/lib/python3.13/site-packages/pandas/core/interchange/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/lib/_stride_tricks_impl.pyi": 9243829615414967057, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_append.py": 17016125916761365423, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/collections.pyx": 17138729833735744777, - "venv/lib/python3.13/site-packages/git/cmd.py": 8854786163152637148, - "venv/lib/python3.13/site-packages/numpy/ma/tests/test_extras.py": 16160090335191788104, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm64_msvc.h": 14580207065003376324, - "venv/lib/python3.13/site-packages/referencing/tests/test_retrieval.py": 10782087416909177703, - "venv/lib/python3.13/site-packages/pip/_internal/network/cache.py": 10606522397693824641, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_clipboard.py": 12674078499544938252, - "venv/lib/python3.13/site-packages/PIL/PpmImagePlugin.py": 7107324729714470853, - "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/METADATA": 16700674024421191168, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/errors.py": 2147619440137604907, - "venv/lib/python3.13/site-packages/_pytest/config/__init__.py": 12787579251203510616, - "venv/lib/python3.13/site-packages/numpy/__config__.py": 15794735729525094396, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_html.py": 7947998260347730581, - "venv/lib/python3.13/site-packages/PIL/FtexImagePlugin.py": 8196606399875185931, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/issue232.py": 6498198296113395367, - "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/WHEEL": 7414522633964952515, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7591/claims.py": 11853156183819186599, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py": 7624316342162916928, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/env.py": 1254612137164491329, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/lib_version.pyi": 3636944488233043883, - "venv/lib/python3.13/site-packages/numpy/random/_common.pyi": 5094115692952609118, - "venv/lib/python3.13/site-packages/pygments/lexers/jsx.py": 13855228003748707341, - "venv/bin/numpy-config": 17104619511899110252, - "venv/lib/python3.13/site-packages/numpy/lib/_histograms_impl.py": 13446184230476677032, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_masked.py": 5380551301916549925, - "frontend/src/lib/components/layout/__tests__/test_topNavbar.svelte.js": 11053755879172333925, - "venv/lib/python3.13/site-packages/httpcore/_backends/auto.py": 10488908050307887802, - "dist/docker/postgres.v1.0.0-rc2-docker.tar": 5933711120559789473, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/keywrap.py": 10424037418528425800, - "venv/lib/python3.13/site-packages/keyring/compat/__init__.py": 7578020229796085753, - "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/licenses/LICENSE": 1126906383149772681, - "frontend/src/routes/settings/__tests__/settings_page.ux.test.js": 8756428349518151160, - "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/WHEEL": 16097436423493754389, - "backend/src/plugins/translate/__tests__/test_scheduler.py": 4988970087981965515, - "frontend/src/lib/ui/PageHeader.svelte": 3800784212388586861, - "backend/src/core/__init__.py": 8673978627093182792, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_drop.py": 17940792593116540139, - "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/RECORD": 17341460504914308184, - "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_writers.py": 103545128256076790, - "venv/lib/python3.13/site-packages/fastapi/middleware/wsgi.py": 3706578020491442104, - "venv/lib/python3.13/site-packages/numpy/_typing/_ufunc.py": 16859854811012717088, - "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/numpy/lib/_ufunclike_impl.pyi": 9854069702366900663, - "venv/lib/python3.13/site-packages/greenlet/tests/test_weakref.py": 6780204527427742510, - "venv/lib/python3.13/site-packages/referencing/jsonschema.py": 14596871117484757502, - "venv/lib/python3.13/site-packages/pandas/tests/strings/__init__.py": 5060190599837877599, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_decimal.py": 3515468590005709183, - "venv/lib/python3.13/site-packages/pandas/_typing.py": 17795648994662434211, - "venv/lib/python3.13/site-packages/pydantic/v1/annotated_types.py": 12925825196690299531, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/cells.py": 8299309127027168194, - "venv/lib/python3.13/site-packages/passlib/handlers/__init__.py": 3424087230285514388, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_common.py": 2223069451949206319, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/__init__.py": 3577295959173028042, - "venv/lib/python3.13/site-packages/pandas/compat/_optional.py": 12714208019736797073, - "venv/lib/python3.13/site-packages/passlib/tests/test_apps.py": 10454136535890937237, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/transform/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/keyring/errors.py": 4587094338672775040, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_fillna.py": 16297834601116528166, - "venv/lib/python3.13/site-packages/requests/adapters.py": 17161109043243436200, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_head_tail.py": 1906121476322841736, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_values.py": 430620320423334461, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_read.py": 8825002468745406134, - "venv/lib/python3.13/site-packages/passlib/utils/decor.py": 21351391767102899, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py": 1331438198531942242, - "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/WHEEL": 8600534672961461758, - "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/licenses/LICENSE": 12684353321716745791, - "venv/lib/python3.13/site-packages/charset_normalizer/cd.py": 8687667152597941021, - "venv/lib/python3.13/site-packages/urllib3/_base_connection.py": 14495079097019980154, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_classes.py": 1440926036579216168, - "venv/lib/python3.13/site-packages/uvicorn/loops/uvloop.py": 5385425357504191161, - "venv/lib/python3.13/site-packages/pygments/styles/murphy.py": 8177992406082781615, - "frontend/build/_app/immutable/chunks/DsnmJJEf.js": 17416107759626312632, - "venv/lib/python3.13/site-packages/pygments/lexers/functional.py": 15896849233007178031, - "venv/lib/python3.13/site-packages/pandas/_libs/internals.cpython-313-x86_64-linux-gnu.so": 2008633361686956377, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_getitem.py": 8414677531950073373, - "frontend/src/routes/reports/llm/[taskId]/report_page.contract.test.js": 4825093389030464765, - "venv/lib/python3.13/site-packages/authlib/common/errors.py": 16082974228565515177, - "venv/lib/python3.13/site-packages/attr/_next_gen.py": 16353246941851015420, - "backend/src/services/notifications/__init__.py": 13930898146447654299, - "venv/lib/python3.13/site-packages/sqlalchemy/pool/events.py": 13473858896579666058, - "backend/src/services/clean_release/preparation_service.py": 7557861288058237966, - "venv/lib/python3.13/site-packages/authlib/oidc/core/__init__.py": 16029653645350087968, - "frontend/src/lib/components/reports/__tests__/report_card.ux.test.js": 14899927226278257748, - "venv/lib/python3.13/site-packages/keyring/backend.py": 14185808762437650951, - "venv/lib/python3.13/site-packages/websockets/extensions/permessage_deflate.py": 12957105912018691390, - "venv/lib/python3.13/site-packages/numpy/_pytesttester.py": 17737911110576747508, - "venv/lib/python3.13/site-packages/PIL/TiffImagePlugin.py": 6845043550424565106, - "venv/lib/python3.13/site-packages/pytest/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/lexers/make.py": 5345282370426217663, - "backend/src/services/dataset_review/repositories/repository_pkg/_mutations.py": 11718998093543631478, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_rolling.py": 18221459425751198218, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_fields.py": 16222559851336781264, - "venv/lib/python3.13/site-packages/numpy/lib/_shape_base_impl.pyi": 12052895927933479005, - "venv/lib/python3.13/site-packages/cffi/recompiler.py": 17809252277700094304, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_case_when.py": 4133174129660834805, - "venv/lib/python3.13/site-packages/passlib/exc.py": 15559705487889657091, - "venv/lib/python3.13/site-packages/_pytest/config/findpaths.py": 7867603756389940562, - "venv/lib/python3.13/site-packages/pandas/core/strings/object_array.py": 17631047188839635539, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_freq_attr.py": 7685756734926749093, - "venv/lib/python3.13/site-packages/passlib/utils/md4.py": 4521366665754877351, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/_orm_constructors.py": 4276726508722638048, - "venv/lib/python3.13/site-packages/pillow.libs/libjpeg-32d42e18.so.62.4.0": 11717944412021329207, - "venv/lib/python3.13/site-packages/numpy/f2py/__version__.py": 8588600299261398598, - "backend/src/plugins/git/llm_extension.py": 9347391649685791102, - "venv/lib/python3.13/site-packages/starlette/__init__.py": 3252794766897205870, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_categorical.py": 8844412630511751140, - "venv/bin/py.test": 2593802493707545818, - "venv/lib/python3.13/site-packages/passlib/crypto/digest.py": 15890463288512025688, - "venv/lib/python3.13/site-packages/PIL/_imagingcms.cpython-313-x86_64-linux-gnu.so": 15198265718775753589, - "venv/lib/python3.13/site-packages/pygments/lexers/trafficscript.py": 776604244051744646, - "venv/lib/python3.13/site-packages/pygments/lexers/arrow.py": 6699009974224818979, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/repr.py": 839417422880281122, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_insert.py": 17383986803728810587, - "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/core/generic.py": 10174952306641057447, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/__init__.py": 15130871412783076140, - "backend/src/api/routes/dashboards/_detail_routes.py": 12522696473630508473, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_fillna.py": 8802093795040069423, - "backend/src/api/routes/dashboards/_router.py": 8641007051243802224, - "venv/lib/python3.13/site-packages/pandas/_libs/sparse.pyi": 12526865400413362140, - "venv/lib/python3.13/site-packages/starlette/datastructures.py": 12097180838087229563, - "venv/lib/python3.13/site-packages/authlib/integrations/base_client/sync_openid.py": 5670082466989878756, - "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/WHEEL": 7314207500206292683, - "venv/lib/python3.13/site-packages/httpx/_transports/asgi.py": 12335675489870745729, - "frontend/build/_app/immutable/nodes/31.C2pdSWrJ.js": 8877169612979767666, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_scalar_compat.py": 10534925655070162459, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_records.py": 18167578114159312327, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/frequencies/test_inference.py": 5741637509343419942, - "venv/lib/python3.13/site-packages/authlib/jose/drafts/_jwe_algorithms.py": 15219408648894515493, - "frontend/build/favicon.svg": 6451919037497541980, - "venv/lib/python3.13/site-packages/pygments/lexers/business.py": 10410439210806697214, - "venv/lib/python3.13/site-packages/urllib3/_version.py": 839747992523118887, - "venv/lib/python3.13/site-packages/greenlet/TBrokenGreenlet.cpp": 10822436772777706192, - "venv/lib/python3.13/site-packages/websockets/utils.py": 17624459810483092475, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_map.py": 10769974470610467636, - "venv/lib/python3.13/site-packages/passlib/apps.py": 4633269405597895766, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_assumed_shape.py": 18361009188431483164, - "venv/lib/python3.13/site-packages/pycparser/_c_ast.cfg": 2649342580905806389, - "venv/lib/python3.13/site-packages/pygments/styles/zenburn.py": 1031869867933871309, - "backend/src/core/utils/superset_context_extractor/__init__.py": 1074191108198498747, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_replace.py": 15992095120302643273, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_unicode.py": 1103608932321322989, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_isna.py": 18394053826166267096, - "venv/lib/python3.13/site-packages/attrs/validators.py": 10346563361083429998, - "venv/lib/python3.13/site-packages/jeepney/tests/test_bus.py": 4260827599660763641, - "frontend/build/_app/immutable/chunks/CX0pjb_y.js": 1050355978995628415, - "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_xlrd.py": 9982457234958102003, - "venv/lib/python3.13/site-packages/pydantic/_internal/_core_utils.py": 13248004771466837645, - "venv/lib/python3.13/site-packages/websockets/client.py": 5229500079651465772, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_exceptions.py": 12145393002202744314, - "venv/lib/python3.13/site-packages/PIL/ImageTransform.py": 7825940907094338808, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_crackfortran.py": 10278720705326601943, - "frontend/src/routes/datasets/review/+page.svelte": 1567550142023887137, - "venv/lib/python3.13/site-packages/gitdb/util.py": 645937410168583692, - "venv/lib/python3.13/site-packages/numpy/ctypeslib/__init__.pyi": 13208953770241664384, - "backend/src/models/dataset_review_pkg/_execution_models.py": 9810012962062422666, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/testing.pyi": 9317922890783549326, - "venv/lib/python3.13/site-packages/pandas/core/interchange/utils.py": 11401792151107207834, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_iterrows.py": 3791886201566670087, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_categorical.py": 10863578845754547294, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/traceback.py": 2535227583726271746, - "venv/lib/python3.13/site-packages/tzlocal/utils.py": 7893867974548929154, - "venv/lib/python3.13/site-packages/numpy/_core/_asarray.py": 3468915833437229425, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/string/test_indexing.py": 2274965639578580723, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_equals.py": 6429101196875333574, - "venv/lib/python3.13/site-packages/numpy/_typing/_dtype_like.py": 1598511035344858524, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_to_typst.py": 14619529283550788275, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_astype.py": 7497268667246913350, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/crud.py": 805684028038896506, - "venv/lib/python3.13/site-packages/authlib/jose/drafts/_jwe_enc_cryptography.py": 4383854709657749855, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_period.py": 7727606662611927293, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_between_time.py": 14266333058913745751, - "venv/lib/python3.13/site-packages/sqlalchemy/schema.py": 16029542421991709659, - "venv/lib/python3.13/site-packages/pip/_vendor/tomli_w/__init__.py": 12849371269359608058, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/kind/foo.f90": 1367964042730902899, - "venv/lib/python3.13/site-packages/passlib/handlers/sun_md5_crypt.py": 1949993216707182407, - "venv/lib/python3.13/site-packages/starlette/convertors.py": 17839747154274354605, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/client.py": 18443944554871937480, - "venv/lib/python3.13/site-packages/_pytest/config/compat.py": 13175516717549204627, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_construction.py": 13953424340346625390, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9207/parameter.py": 2793853093222900337, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/flatiter.pyi": 9598576095384661517, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/parameters.py": 12219404446503940066, - "venv/lib/python3.13/site-packages/numpy/ctypeslib/__init__.py": 2401931196871484449, - "venv/lib/python3.13/site-packages/uvicorn/loops/auto.py": 13086005977087619545, - "venv/lib/python3.13/site-packages/pygments/lexers/python.py": 8995519134978505744, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/cursor.py": 8074527784886981710, - "backend/src/schemas/health.py": 13764741380188041840, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/utils.py": 3710235316419562274, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_hermite_e.py": 8912795372542950449, - "venv/lib/python3.13/site-packages/httpcore/_async/socks_proxy.py": 9026788710898138090, - "venv/lib/python3.13/site-packages/jeepney/io/tests/utils.py": 10346859893683522113, - "venv/lib/python3.13/site-packages/pygments/lexers/minecraft.py": 10133579583270214110, - "frontend/src/routes/reports/+page.svelte": 7370192460972346131, - "frontend/build/_app/immutable/nodes/18.BazzBSLx.js": 14618705072013016073, - "venv/lib/python3.13/site-packages/sqlalchemy/util/tool_support.py": 4926732415520779988, - "venv/lib/python3.13/site-packages/starlette/middleware/authentication.py": 15211131264936212938, - "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/metrics_py.py": 13809503317864539103, - "venv/lib/python3.13/site-packages/numpy/_core/_ufunc_config.py": 7365943844063702766, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arccos.csv": 3026156288992132911, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_cumulative.py": 15644124412739892167, - "venv/lib/python3.13/site-packages/pygments/plugin.py": 18006138132596504101, - "venv/lib/python3.13/site-packages/python_multipart/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/_pytest/tracemalloc.py": 5530234062530858720, - "venv/lib/python3.13/site-packages/click-8.3.1.dist-info/INSTALLER": 17282701611721059870, - "backend/src/services/clean_release/demo_data_service.py": 8795028177684641724, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_compound.f90": 15012692484070385863, - "venv/lib/python3.13/site-packages/pygments/lexers/_scilab_builtins.py": 17453812809700086888, - "venv/lib/python3.13/site-packages/rapidfuzz/process.py": 3870006735299537016, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/philox-testset-2.csv": 7063857529719738394, - "venv/lib/python3.13/site-packages/urllib3-2.6.2.dist-info/WHEEL": 7454950858448014158, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/status.py": 2214590120238144582, - "venv/lib/python3.13/site-packages/keyring/backends/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pydantic/v1/dataclasses.py": 7833468513057242440, - "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/cython/checks.pyx": 13187505236986855177, - "venv/lib/python3.13/site-packages/pandas/core/computation/parsing.py": 3084943521520476999, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/fromnumeric.py": 3792079757208879362, - "venv/lib/python3.13/site-packages/pandas/tests/libs/test_hashtable.py": 7762405492052106717, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/_py_util.py": 13997184496924925110, - "venv/lib/python3.13/site-packages/sqlalchemy/util/_has_cy.py": 11979732417254560424, - "backend/src/api/routes/translate/_preview_routes.py": 744850480449179988, - "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/RECORD": 17545477863793260029, - "backend/src/services/clean_release/stages/manifest_consistency.py": 1270636316397468243, - "venv/lib/python3.13/site-packages/jsonschema/_legacy_keywords.py": 16481666133229715822, - "venv/lib/python3.13/site-packages/numpy/lib/tests/data/python3.npy": 4957143805477405905, - "venv/lib/python3.13/site-packages/pydantic/_internal/_schema_gather.py": 3634138701088855517, - "venv/lib/python3.13/site-packages/pandas/_libs/parsers.cpython-313-x86_64-linux-gnu.so": 14134106903573638108, - "venv/lib/python3.13/site-packages/git/__init__.py": 15867730798565725003, - "frontend/build/_app/immutable/chunks/DySFV_q2.js": 9541708291004606021, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_missing.py": 2976310411820303374, - "frontend/src/lib/components/dataset-review/LaunchConfirmationPanel.svelte": 5965510319309867257, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/recfunctions.py": 18330899924171767482, - "venv/lib/python3.13/site-packages/pandas/errors/__init__.py": 13444371880185740908, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/session.py": 5126234270435358377, - "venv/lib/python3.13/site-packages/pandas/core/groupby/grouper.py": 18110568057754450953, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth1/__init__.py": 14481526616887213120, - "venv/lib/python3.13/site-packages/pydantic/_internal/_import_utils.py": 4029157986210295967, - "venv/lib/python3.13/site-packages/psycopg2/_json.py": 9729420372294281405, - "frontend/src/routes/settings/settings-utils.js": 9490767217322820262, - "venv/lib/python3.13/site-packages/numpy/ma/__init__.py": 214772578978182427, - "venv/lib/python3.13/site-packages/numpy/_core/_add_newdocs.pyi": 9282436354926617898, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/__init__.py": 15130871412783076140, - "backend/src/api/routes/translate/_run_routes.py": 8915481471453671422, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_get_level_values.py": 12490886203425393290, - "venv/lib/python3.13/site-packages/pandas/tests/strings/test_cat.py": 15988884721768567895, - "venv/lib/python3.13/site-packages/numpy/f2py/capi_maps.pyi": 10638891136586198429, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/collections.py": 16114931075157411913, - "venv/lib/python3.13/site-packages/anyio/_core/_tasks.py": 14104828341875599130, - "venv/lib/python3.13/site-packages/fastapi/utils.py": 15402403831700548944, - "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pip/_internal/commands/__init__.py": 3795218871212151351, - "venv/lib/python3.13/site-packages/httpcore/_async/http2.py": 16350817785499681224, - "frontend/build/_app/immutable/chunks/1IJdlE0w.js": 14600235715446716616, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/test_constructors.py": 9478848417896027917, - "venv/lib/python3.13/site-packages/jeepney/io/threading.py": 525625939662612746, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/pretty.py": 2448541115957861214, - "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz": 17626265752554406355, - "venv/lib/python3.13/site-packages/ecdsa/ecdh.py": 12596317261561755793, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_map.py": 1115471561925426082, - "venv/lib/python3.13/site-packages/numpy/_core/numeric.py": 12261531924304979741, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_print.py": 598821365691498702, - "venv/lib/python3.13/site-packages/attrs/filters.py": 6844006230924162452, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_api.py": 16743710460729009914, - "venv/lib/python3.13/site-packages/pandas/_libs/lib.cpython-313-x86_64-linux-gnu.so": 6452315003728497116, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_html.py": 7732330576029516854, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_markdown.py": 9964642152916165317, - "venv/lib/python3.13/site-packages/sqlalchemy/util/deprecations.py": 7020540470348910770, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/endpoints.py": 18334891787808122725, - "venv/lib/python3.13/site-packages/_pytest/python.py": 1829073958095716309, - "venv/lib/python3.13/site-packages/pandas/core/ops/array_ops.py": 10061020479033421596, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm32_gcc.h": 3561524604274567339, - "venv/lib/python3.13/site-packages/pip/_vendor/truststore/__init__.py": 1948986914452928735, - "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/WHEEL": 5838667082726674838, - "venv/lib/python3.13/site-packages/pyasn1/compat/integer.py": 7365270971777550573, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth1/authorization_server.py": 6858244712625056421, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi": 14119032813912827271, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/f90continuation.f90": 17056591497459381871, - "venv/lib/python3.13/site-packages/git/refs/remote.py": 4391983355222612205, - "frontend/src/components/Footer.svelte": 17748559656923032, - "frontend/build/_app/immutable/chunks/ZFFoSDus.js": 12701360151670566914, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/publicmod.f90": 2174121054736811903, - "venv/lib/python3.13/site-packages/jose/backends/_asn1.py": 14123487576864105793, - "venv/lib/python3.13/site-packages/packaging/__init__.py": 5479277484104486860, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/tzconversion.cpython-313-x86_64-linux-gnu.so": 15960606468596245469, - "frontend/src/lib/ui/LanguageSwitcher.svelte": 3899734183369041614, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/timeout.py": 1807228452024740476, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/__init__.py": 15130871412783076140, - "backend/src/services/dataset_review/__init__.py": 15781509495698823404, - "venv/lib/python3.13/site-packages/fastapi/security/open_id_connect_url.py": 3502444317759870079, - "venv/lib/python3.13/site-packages/pydantic/v1/__init__.py": 1642038155370734658, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/array_constructors.pyi": 164538008396001632, - "backend/src/plugins/migration.py": 16567640288127443175, - "venv/lib/python3.13/site-packages/starlette/middleware/errors.py": 1337979724602864440, - "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_cisco.py": 1786475632318043669, - "venv/lib/python3.13/site-packages/pandas/core/computation/api.py": 17919983681396223380, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_astype.py": 16700461036658228009, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_reflection.py": 10520778598547003532, - "backend/tests/conftest.py": 14115149798000023183, - "venv/lib/python3.13/site-packages/attr/_config.py": 1864824728504480204, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/models.py": 9513172366009392193, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/version.py": 6401479036177132374, - "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/RECORD": 3288669175908898843, - "venv/lib/python3.13/site-packages/pydantic/generics.py": 15019381413806459410, - "venv/lib/python3.13/site-packages/websockets/asyncio/client.py": 5694225754072131767, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/__init__.py": 3969805865048730851, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/base.py": 16891437410462671317, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/oracledb.py": 7158791242292470472, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/formatters/_mapping.py": 13335227913146542481, - "venv/lib/python3.13/site-packages/numpy/lib/_version.pyi": 10804755840730584157, - "venv/lib/python3.13/site-packages/jsonschema/_types.py": 9250091433305395923, - "venv/lib/python3.13/site-packages/PIL/DcxImagePlugin.py": 11767274218348117481, - "frontend/src/routes/admin/settings/+page.svelte": 6706258642200752989, - "frontend/build/_app/immutable/nodes/19.B67FSCEK.js": 10417078689207235455, - "venv/lib/python3.13/site-packages/cffi/verifier.py": 8473599757770867754, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/mapped_collection.py": 11377280960650304531, - "venv/lib/python3.13/site-packages/PIL/ImageChops.py": 16368812693381154921, - "frontend/src/lib/stores/taskDrawer.js": 6659505033581293370, - "frontend/build/_app/immutable/chunks/Cp3Pv6sc.js": 7350545894220053516, - "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/licenses/LICENSE": 7959949171752474553, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/writeonly.py": 14213960431308620600, - "backend/src/core/task_manager/task_logger.py": 12820629458199400734, - "venv/lib/python3.13/site-packages/gitdb/test/__init__.py": 18393574323937353933, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_inference.py": 16402427186697071465, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_keys.py": 10813898277355150779, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timezones.cpython-313-x86_64-linux-gnu.so": 1137453907225619959, - "venv/lib/python3.13/site-packages/pygments/lexers/text.py": 10572727755736597715, - "venv/lib/python3.13/site-packages/numpy/lib/_arrayterator_impl.pyi": 2744889206277418389, - "venv/lib/python3.13/site-packages/pandas/tests/resample/test_base.py": 15292769510056833117, - "venv/lib/python3.13/site-packages/multipart/multipart.py": 26386459953377432, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_export_format.py": 17862222951776199694, - "venv/lib/python3.13/site-packages/numpy/f2py/diagnose.py": 503331864022631297, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_numeric.py": 5627177940393829333, - "venv/lib/python3.13/site-packages/passlib/win32.py": 5311393450489898748, - "venv/lib/python3.13/site-packages/pandas/tests/io/xml/conftest.py": 17514079438846557943, - "venv/lib/python3.13/site-packages/PIL/WmfImagePlugin.py": 15685062825106444247, - "backend/src/api/routes/assistant/_dataset_review.py": 14508672919199309579, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/create.py": 16693261531832306870, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/socks.py": 17581293673028078134, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/nattype.pyi": 1830368009811266890, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/aioodbc.py": 12704729841432715246, - "venv/lib/python3.13/site-packages/pip/_vendor/certifi/__init__.py": 2833016678144975576, - "venv/lib/python3.13/site-packages/anyio/lowlevel.py": 11919467357746648592, - "venv/lib/python3.13/site-packages/pip/_vendor/distro/__main__.py": 2688850704829116818, - "venv/lib/python3.13/site-packages/pandas/_testing/compat.py": 10172779814048259850, - "venv/lib/python3.13/site-packages/pyasn1/type/namedval.py": 8273171503516887681, - "venv/lib/python3.13/site-packages/urllib3/_collections.py": 3440910792774742262, - "frontend/build/_app/immutable/chunks/2dOUpm6k.js": 11811637653151731489, - "backend/test_app.db": 2486636812009447051, - "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/WHEEL": 13347410390513723930, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/fixtures/mypy.py": 823265492261272631, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/cast.py": 342228968652072052, - "backend/src/api/routes/git/_deps.py": 12613530531412985767, - "venv/lib/python3.13/site-packages/pip/_internal/vcs/versioncontrol.py": 13742159245132241245, - "venv/lib/python3.13/site-packages/pip/_vendor/msgpack/exceptions.py": 11303331612406662162, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_missing.py": 3991199131191840903, - "backend/tests/services/clean_release/test_compliance_execution_service.py": 5924200441771414883, - "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/WHEEL": 16784970174376303810, - "venv/lib/python3.13/site-packages/pygments/lexers/_cocoa_builtins.py": 15468347167912293394, - "venv/lib/python3.13/site-packages/ecdsa/ellipticcurve.py": 15624510849346629163, - "venv/lib/python3.13/site-packages/pygments/lexers/algebra.py": 9651893228421050246, - "frontend/src/components/TaskHistory.svelte": 7819646903874958430, - "venv/lib/python3.13/site-packages/starlette/middleware/httpsredirect.py": 16837082808103815044, - "frontend/build/_app/immutable/chunks/CeAdK8Hz.js": 230807975890735821, - "venv/lib/python3.13/site-packages/pandas/compat/numpy/function.py": 18405870240333383757, - "venv/lib/python3.13/site-packages/pyasn1/type/constraint.py": 16776615503418773283, - "venv/lib/python3.13/site-packages/fastapi/middleware/gzip.py": 436886407566263171, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/greenlet/PyGreenletUnswitchable.cpp": 3160733517193315684, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_take.py": 1216902953519121176, - "backend/src/api/routes/git/_environment_routes.py": 1034235394475003092, - "venv/lib/python3.13/site-packages/pyasn1/codec/der/encoder.py": 4790267438222037867, - "backend/src/core/task_manager/persistence.py": 7896628499516364250, - "frontend/src/lib/components/reports/ReportDetailPanel.svelte": 16014663571033060148, - "backend/src/services/clean_release/__tests__/test_source_isolation.py": 17714198919084455758, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/requirements.py": 13215932786801020434, - "venv/lib/python3.13/site-packages/apscheduler/jobstores/memory.py": 9947113720821563254, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_cpu.h": 13799366908726531089, - "venv/lib/python3.13/site-packages/requests/status_codes.py": 17005213506461710202, - "venv/lib/python3.13/site-packages/pandas/tests/reductions/test_reductions.py": 11624188672428941494, - "venv/lib/python3.13/site-packages/authlib/oidc/core/claims.py": 2035724920423374302, - "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_cat_accessor.py": 4304062895283374927, - "frontend/build/_app/immutable/nodes/4.B0v4GDoW.js": 9805334845064330359, - "venv/lib/python3.13/site-packages/anyio/abc/_sockets.py": 15042495744291042062, - "venv/lib/python3.13/site-packages/pip/_internal/commands/cache.py": 1892313355296776523, - "venv/lib/python3.13/site-packages/yaml/_yaml.cpython-313-x86_64-linux-gnu.so": 16990140871649227895, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_client/__init__.py": 9997757256282720103, - "venv/lib/python3.13/site-packages/apscheduler/triggers/combining.py": 1264860868414723829, - "venv/lib/python3.13/site-packages/packaging/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/char/__init__.py": 4428218097234308768, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/regexopt.py": 16073437303824961930, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_clip.py": 8087139801274517318, - "venv/lib/python3.13/site-packages/pydantic/_internal/_known_annotated_metadata.py": 9308620143218174987, - "venv/lib/python3.13/site-packages/numpy/_core/arrayprint.pyi": 84606125837677197, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/_internal_utils.py": 4091305333167213279, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_get.py": 6509102681661636928, - "venv/lib/python3.13/site-packages/psycopg2/_range.py": 7184417109068014771, - "venv/lib/python3.13/site-packages/pandas/core/config_init.py": 16537122488227845090, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/provision.py": 17812733620485633975, - "venv/lib/python3.13/site-packages/fastapi/_compat/shared.py": 5249003014594464718, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/events.py": 10224214051422678828, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_repr.py": 13147587049887075335, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_is_full.py": 9577318758326279694, - "venv/lib/python3.13/site-packages/yaml/dumper.py": 11376022992370902766, - "backend/tests/test_translate_corrections.py": 1228642484417924184, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_parameter.py": 15173522343126004527, - "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/conversion.cpython-313-x86_64-linux-gnu.so": 9014806524727977333, - "venv/lib/python3.13/site-packages/apscheduler/schedulers/base.py": 17992335652981439676, - "venv/lib/python3.13/site-packages/numpy/f2py/capi_maps.py": 2490579175967301457, - "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/request.py": 17642741955458391050, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/index_tricks.pyi": 11463747060334597941, - "venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/WHEEL": 5179340427739743287, - "venv/lib/python3.13/site-packages/_pytest/_code/source.py": 9488112979192923739, - "venv/lib/python3.13/site-packages/websockets/legacy/__init__.py": 12332512329533591682, - "venv/lib/python3.13/site-packages/_pytest/setuponly.py": 10959102379908419762, - "venv/lib/python3.13/site-packages/jaraco/classes/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/bcrypt/__init__.py": 11415155211917181362, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_3kcompat.h": 5377305123299457337, - "frontend/src/routes/dashboards/[id]/components/DashboardGitManager.svelte": 4990524146294551357, - "frontend/src/routes/settings/notifications/+page.svelte": 14504217413720856827, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/test_nat.py": 7400568652984772972, - "venv/lib/python3.13/site-packages/pandas/tests/extension/json/test_json.py": 12422315190455428620, - "venv/lib/python3.13/site-packages/pygments/lexers/urbi.py": 1611762607688882372, - "venv/lib/python3.13/site-packages/authlib/integrations/django_client/apps.py": 14151733532064278336, - "frontend/src/routes/tools/storage/+page.svelte": 2651964385884112547, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/einsumfunc.pyi": 8902765738896588686, - "venv/lib/python3.13/site-packages/websockets/headers.py": 13587146967490088657, - "venv/lib/python3.13/site-packages/authlib/oauth1/errors.py": 1154126353335030046, - "venv/lib/python3.13/site-packages/numpy/f2py/_backends/__init__.pyi": 16164841755339571545, - "docker/backend.Dockerfile": 10320070102635663967, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py": 5946672289916692272, - "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/WHEEL": 16097436423493754389, - "venv/lib/python3.13/site-packages/numpy/testing/_private/extbuild.pyi": 18253950285509265097, - "venv/lib/python3.13/site-packages/certifi/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/psycopg2/errorcodes.py": 6711830404954314463, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/polynomial_polybase.pyi": 3404315099794022355, - "venv/lib/python3.13/site-packages/apscheduler/executors/gevent.py": 17122637996105344344, - "venv/lib/python3.13/site-packages/numpy/lib/_type_check_impl.pyi": 2667658052734929089, - "venv/lib/python3.13/site-packages/numpy/random/mtrand.cpython-313-x86_64-linux-gnu.so": 16146459667121787202, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_abstract_interface.py": 17625870734108222276, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_select_dtypes.py": 3530442956913622572, - "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/var_.py": 12699521091170347311, - "venv/lib/python3.13/site-packages/numpy/_core/_exceptions.pyi": 3661113433078225952, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/__init__.py": 6532094538918910232, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/conftest.py": 13388202260795998613, - "venv/lib/python3.13/site-packages/attr/_funcs.py": 5302917072419624193, - "venv/lib/python3.13/site-packages/websockets/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/cryptography/exceptions.py": 5173540025160237758, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/array_like.py": 2778905818573625118, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/key_set.py": 8485970749081951741, - "venv/lib/python3.13/site-packages/pandas/tests/window/moments/test_moments_consistency_rolling.py": 11118160360072677569, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/licenses/__init__.py": 2635182244050911910, - "venv/lib/python3.13/site-packages/referencing/tests/test_core.py": 8280983300775878144, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_to_string.py": 11422235699126032386, - "venv/lib/python3.13/site-packages/multipart/__init__.py": 6496114190873890851, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_hist_method.py": 2109257663592928, - "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/METADATA": 3643456577199395707, - "venv/lib/python3.13/site-packages/jsonschema_specifications/_core.py": 14812112544772987164, - "venv/lib/python3.13/site-packages/idna/intranges.py": 12536174834761591006, - "venv/lib/python3.13/site-packages/referencing/retrieval.py": 11921179805977833120, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_integer/foo77.f": 12000068001851270396, - "venv/lib/python3.13/site-packages/pandas/conftest.py": 747626550806435763, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_file_buffer_url.py": 15377043158761455858, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/filters/__init__.py": 835924536855006596, - "venv/lib/python3.13/site-packages/urllib3/util/wait.py": 14629837381061032546, - "venv/lib/python3.13/site-packages/pygments/lexers/_sql_builtins.py": 5906655440517434734, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/WHEEL": 2357997949040430835, - "venv/lib/python3.13/site-packages/pygments/lexers/other.py": 6893672181003034910, - "venv/lib/python3.13/site-packages/websockets/legacy/client.py": 4032878587330592478, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/char.pyi": 13143560686049627445, - "venv/lib/python3.13/site-packages/pandas/tests/frame/constructors/test_from_records.py": 12217508428903265797, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_sparc_sun_gcc.h": 2915128971664712180, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/twodim_base.pyi": 10156504423258357540, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_multiarray.py": 5928288171180750841, - "venv/lib/python3.13/site-packages/pandas/tests/extension/array_with_attr/array.py": 13040461386712513615, - "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_sparse_accessor.py": 6774374030220202353, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/array_pad.pyi": 14755435694146003896, - "venv/lib/python3.13/site-packages/pandas/tests/extension/json/__init__.py": 7877061921210840955, - "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/INSTALLER": 17282701611721059870, - "frontend/src/lib/ui/Input.svelte": 8632025378593263475, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/LCSseq.pyi": 16372699564794732463, - "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/zip-safe": 15240312484046875203, - "frontend/src/routes/tools/mapper/+page.svelte": 11954517841045690204, - "frontend/build/_app/immutable/nodes/0.SLFvtO6J.js": 14720565923096646553, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_insert.py": 6970975909565157982, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/annotation.py": 15855576199737688124, "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_sort_values.py": 834951665649981397, - "venv/lib/python3.13/site-packages/jaraco/classes/properties.py": 6976928770004942858, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py": 9623344347519683527, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/routines/subrout.f": 18215492757907465667, - "venv/lib/python3.13/site-packages/anyio/streams/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/lexers/freefem.py": 9851243865301700231, - "backend/tests/core/test_git_service_gitea_pr.py": 6098719224379185148, - "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/version.py": 14723293207188512766, - "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/PIL/Image.py": 13615925856053031482, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_umath_complex.py": 11732242536199439023, - "venv/lib/python3.13/site-packages/pyasn1/codec/der/__init__.py": 15728752901274520502, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_results.py": 18145930285449734975, - "backend/src/models/task.py": 10701108436197477339, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_longdouble.py": 9337218715436213122, - "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/METADATA": 317417773265681256, - "venv/lib/python3.13/site-packages/jeepney/tests/test_fds.py": 6257376331171761928, - "venv/lib/python3.13/site-packages/numpy/lib/_version.py": 15294708923334674060, - "venv/lib/python3.13/site-packages/pandas/core/indexes/accessors.py": 14394872772838047912, - "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/RECORD": 17985800801793506250, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/strptime.cpython-313-x86_64-linux-gnu.so": 12654188486128938170, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_truncate.py": 6478126371906156409, - "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/WHEEL": 9788895026336324100, - "venv/lib/python3.13/site-packages/numpy.libs/libquadmath-96973f99-934c22de.so.0.0.0": 5098713761946118139, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_categorical.py": 18254320571347851153, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_align.py": 6050248183327934780, - "venv/lib/python3.13/site-packages/pillow.libs/libwebp-d8b9687f.so.7.2.0": 116215608212382352, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_loc.py": 17839380711970027363, - "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/meson.build": 8784756284679859367, - "venv/lib/python3.13/site-packages/pip/_internal/vcs/git.py": 17060377178521307324, - "venv/lib/python3.13/site-packages/pandas/io/feather_format.py": 13242733585787888812, - "venv/lib/python3.13/site-packages/pandas/io/formats/style_render.py": 13586834368514801953, - "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_pbkdf2.py": 4977067068389993374, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_pairwise.py": 2673370322448160842, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-log2.csv": 13979868713429167140, - "venv/lib/python3.13/site-packages/pyasn1/debug.py": 6305633719847955310, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/bar.py": 12859947592168238832, - "venv/lib/python3.13/site-packages/pydantic_core/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/__init__.py": 697601512486720554, - "venv/lib/python3.13/site-packages/git/refs/log.py": 5175801243332619268, - "venv/lib/python3.13/site-packages/pygments/lexers/slash.py": 8320676560518108343, - "venv/lib/python3.13/site-packages/pygments/styles/pastie.py": 15784474766258831882, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalarinherit.py": 15240535501243623986, - "venv/lib/python3.13/site-packages/pygments/lexers/graph.py": 10445044822847205061, - "venv/lib/python3.13/site-packages/click/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/licenses/LICENSE": 3509878235770224233, - "frontend/vite.config.js": 6182424215365912367, - "backend/src/plugins/translate/metrics.py": 11569471420441638918, - "venv/lib/python3.13/site-packages/numpy/polynomial/hermite_e.pyi": 13068936038543812082, - "backend/src/services/clean_release/facade.py": 11231787492455424065, - "venv/lib/python3.13/site-packages/websockets/sync/messages.py": 15699091325918681038, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/sfc64_np126.pkl.gz": 1630203486813146863, - "venv/lib/python3.13/site-packages/numpy/lib/recfunctions.py": 3001896031623204147, - "frontend/src/lib/components/assistant/__tests__/assistant_clarification.integration.test.js": 12258270009984504212, - "venv/lib/python3.13/site-packages/ecdsa/_rwlock.py": 7971972922423146201, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/modules.pyi": 14176187662591164602, - "venv/lib/python3.13/site-packages/more_itertools/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/sessions.py": 5400174351499383072, - "venv/lib/python3.13/site-packages/pandas/tests/config/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_is_unique.py": 3989602816674881215, - "venv/lib/python3.13/site-packages/pandas/core/arrays/datetimes.py": 10585034798097337148, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_set_index.py": 10971093361190283675, - "venv/lib/python3.13/site-packages/git/index/util.py": 5022401275553420722, - "frontend/src/lib/components/layout/TopNavbar.svelte": 14489256050586877364, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libselinux-0922c95c.so.1": 16516617710645376769, - "venv/lib/python3.13/site-packages/pip/_internal/utils/virtualenv.py": 14416705841810221305, - "venv/lib/python3.13/site-packages/pydantic/json_schema.py": 15678278224696353978, - "venv/lib/python3.13/site-packages/pandas/tests/api/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/RECORD": 9502524172853437600, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/jws_algs.py": 11920621906391796226, - "frontend/src/routes/settings/__tests__/settings_page.integration.test.js": 13008989176397099996, - "venv/lib/python3.13/site-packages/pandas/core/indexes/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/uvicorn/lifespan/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_parsing.py": 2228959015420278458, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_setops.py": 10550603120090683350, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_csv.py": 571991535656576735, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/datetimes/test_cumulative.py": 6346947559103505068, - "venv/lib/python3.13/site-packages/numpy/_configtool.py": 2946953194004101078, - "venv/lib/python3.13/site-packages/pip/_internal/exceptions.py": 2252826732781519645, - "venv/lib/python3.13/site-packages/pyasn1/codec/cer/decoder.py": 17559787396729804535, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/cache.py": 3177663126928467950, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/aws.py": 1364297790170663941, - "venv/lib/python3.13/site-packages/pillow.libs/libbrotlicommon-c55a5f7a.so.1.2.0": 2635192149914499986, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/arrayscalars.h": 16142050006935459679, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_quantile.py": 11273403743157110261, - "venv/lib/python3.13/site-packages/pip/_internal/commands/freeze.py": 8644678595442275485, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/engine.py": 2652248727360250985, - "frontend/src/routes/+page.ts": 11160596731849880920, - "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/REQUESTED": 15130871412783076140, - "frontend/src/routes/migration/+page.svelte": 7821836225621176583, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_raises.py": 9678418010293693275, - "backend/src/api/routes/assistant/__init__.py": 14959302162073618175, - "venv/lib/python3.13/site-packages/pyasn1/type/namedtype.py": 1859990775875190116, - "backend/src/models/connection.py": 7467905739729526898, - "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/RECORD": 8956798822782402202, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7592/__init__.py": 1398617034174236358, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_xs.py": 11073752361562791165, - "venv/lib/python3.13/site-packages/pygments/lexers/teal.py": 10004159499175716139, - "backend/src/services/clean_release/__tests__/test_preparation_service.py": 4958420988131940633, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/lib_polynomial.pyi": 9134949577567498894, - "venv/lib/python3.13/site-packages/pydantic/v1/fields.py": 3948219773415054006, - "venv/lib/python3.13/site-packages/authlib/integrations/starlette_client/__init__.py": 9083396324667433335, - "venv/lib/python3.13/site-packages/pandas/tests/extension/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/io/formats/templates/latex.tpl": 8820045022551446255, - "venv/lib/python3.13/site-packages/requests/auth.py": 912521029864094489, - "venv/lib/python3.13/site-packages/pluggy/__init__.py": 8269188363563356631, - "venv/bin/websockets": 12099863324306500376, - "frontend/src/components/llm/DocPreview.svelte": 9286231407313632900, - "venv/lib/python3.13/site-packages/annotated_doc/main.py": 3384049281532298500, - "venv/lib/python3.13/site-packages/pillow.libs/libharfbuzz-0692f733.so.0.61230.0": 23110287346167671, - "venv/lib/python3.13/site-packages/fastapi/_compat/__init__.py": 12380131786677943349, - "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_shift.py": 9952689832827833247, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_unique.py": 6234921572794573905, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_multi_thread.py": 5660352470978621226, - "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/METADATA": 6502714564886871819, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_cython_aggregations.py": 10818895573637904764, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_add_prefix_suffix.py": 8367793476246001244, - "venv/lib/python3.13/site-packages/psycopg2/pool.py": 8855184580138330938, - "frontend/src/lib/stores/assistantChat.js": 13807808925025688939, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/authorization_code.py": 4958605540994687737, - "venv/lib/python3.13/site-packages/authlib/integrations/sqla_oauth2/client_mixin.py": 17980753662107479779, - "venv/lib/python3.13/site-packages/numpy/typing/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/random/_sfc64.pyi": 3536657185495782642, - "venv/lib/python3.13/site-packages/numpy/matrixlib/defmatrix.py": 4940170059946018356, - "venv/lib/python3.13/site-packages/numpy/typing/tests/test_isfile.py": 14544060093268573418, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/pcg64dxsm-testset-2.csv": 16611724551361692999, - "venv/lib/python3.13/site-packages/numpy/dtypes.pyi": 6713822658171717369, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/conftest.py": 17897502508251413988, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_to_csv.py": 10737444103906389448, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/fft.pyi": 14454280827038327842, - "venv/lib/python3.13/site-packages/fastapi/temp_pydantic_v1_params.py": 9436036880518047720, - "venv/lib/python3.13/site-packages/_pytest/_io/terminalwriter.py": 16852767249156780980, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/string_/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/log.py": 10101221585967121334, - "venv/lib/python3.13/site-packages/uvicorn/__main__.py": 6086938803696646644, - "venv/lib/python3.13/site-packages/pygments/lexers/codeql.py": 4254046699692048717, - "frontend/src/lib/utils/debounce.js": 16884470786640632815, - "venv/lib/python3.13/site-packages/sqlalchemy/types.py": 15819795572817819102, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh25286_bc.pyf": 10893958728966010450, - "venv/lib/python3.13/site-packages/PIL/SpiderImagePlugin.py": 12234762087257163606, - "docker/all-in-one.Dockerfile": 10896793266219904099, - "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/licenses/LICENSE": 4274653168153720331, - "venv/lib/python3.13/site-packages/pandas/core/array_algos/__init__.py": 11850945878149145990, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_isocalendar.py": 5704842983090753914, - "backend/test_auth.db": 18214842050920965999, - "venv/lib/python3.13/site-packages/pandas/core/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_internal/commands/hash.py": 11824898770873127699, - "venv/lib/python3.13/site-packages/numpy/core/_dtype.py": 2806449741844148191, - "backend/src/services/clean_release/stages/__init__.py": 11216464651705158032, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_numeric.py": 9383693732009547935, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_datetimes.py": 8863748366184419339, - "venv/lib/python3.13/site-packages/pip/_internal/vcs/bazaar.py": 5630511062945201327, - "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_style.py": 10405592363651914796, - "venv/lib/python3.13/site-packages/passlib/handlers/phpass.py": 16489430001449160264, - "venv/lib/python3.13/site-packages/httpx/_main.py": 1812303705973265093, - "venv/lib/python3.13/site-packages/pygments/lexers/spice.py": 1376126320935115350, - "venv/lib/python3.13/site-packages/greenlet/tests/test_stack_saved.py": 7767924647054186572, - "venv/lib/python3.13/site-packages/pandas/core/arrays/_utils.py": 11574890848475750509, - "venv/lib/python3.13/site-packages/pydantic/_internal/_forward_ref.py": 10623110912945011703, - "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_list_accessor.py": 8613662386779664039, - "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/METADATA": 13695483537390128881, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/mutable.py": 9323088483998280629, - "venv/lib/python3.13/site-packages/numpy/_typing/_ufunc.pyi": 6286629088867436848, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_cross.py": 17742712348672218852, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_formats.py": 18080627205081311773, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/base.py": 5294446834530589760, - "venv/lib/python3.13/site-packages/ecdsa/_version.py": 17707102226558781833, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_utils.py": 15197339054699289627, - "venv/lib/python3.13/site-packages/ecdsa/__init__.py": 6237633580090653788, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/token.py": 11655733747403312358, - "backend/src/core/__tests__/test_superset_preview_pipeline.py": 5338719285630543394, - "venv/lib/python3.13/site-packages/_pytest/assertion/util.py": 16957445183087315124, - "venv/lib/python3.13/site-packages/pygments/styles/perldoc.py": 4857683935715743356, - "venv/lib/python3.13/site-packages/PIL/PngImagePlugin.py": 5076219228736706283, - "venv/lib/python3.13/site-packages/pandas/tests/generic/__init__.py": 15130871412783076140, - "backend/src/core/task_manager/cleanup.py": 8316318318890394605, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_timestamp.py": 10308799534491190078, - "venv/lib/python3.13/site-packages/pygments/lexers/smithy.py": 10789533877863290582, - "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/licenses/LICENSE": 8364608327598414179, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_arrow_interface.py": 9757086292358759236, - "frontend/src/lib/components/layout/__tests__/test_taskDrawer.svelte.js": 15288009749873977442, - "venv/lib/python3.13/site-packages/passlib/crypto/__init__.py": 16409196868630049365, - "venv/lib/python3.13/site-packages/uvicorn/server.py": 15714008348292243019, - "backend/src/api/routes/git/_repo_lifecycle_routes.py": 6605622512801609889, - "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/METADATA": 15987199384245264478, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_empty.py": 13006241382741353569, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ufuncs.pyi": 1808147181379894521, - "backend/src/services/clean_release/repositories/manifest_repository.py": 12395698948312301073, - "backend/src/plugins/translate/plugin.py": 6629867705339506493, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm64_masm.obj": 11967637259540485652, - "venv/lib/python3.13/site-packages/ecdsa/numbertheory.py": 13538480862642502330, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/LCSseq.py": 13853220736499979380, - "venv/lib/python3.13/site-packages/PIL/_imagingmath.cpython-313-x86_64-linux-gnu.so": 77725273398858074, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/resultproxy.cpython-313-x86_64-linux-gnu.so": 6011605826065363427, - "venv/lib/python3.13/site-packages/pydantic_core/core_schema.py": 8106298745123589494, - "venv/lib/python3.13/site-packages/numpy/lib/_array_utils_impl.py": 14446508217220482719, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libpcre-9513aab5.so.1.2.0": 12298861477332583668, - "venv/lib/python3.13/site-packages/authlib/oidc/discovery/well_known.py": 8259335680633926377, - "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_backend.py": 1472021608501071055, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_easter.py": 3397863170068576196, - "venv/lib/python3.13/site-packages/numpy/core/defchararray.py": 11371045148957547088, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/__init__.py": 10357835306532281617, - "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/LICENSE": 6989336036499641077, - "backend/src/models/auth.py.bak": 7050478675360007060, - "backend/src/services/reports/__tests__/test_report_normalizer.py": 5030199452684805352, - "frontend/src/lib/components/reports/__tests__/reports_page.integration.test.js": 16463062768004950066, - "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/RECORD": 9974324769263969827, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/getlimits.pyi": 93747984191461691, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/wrapper.py": 14599195010683170123, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/utils.h": 3920393832993063886, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/object/test_indexing.py": 1947969642791009285, - "venv/lib/python3.13/site-packages/_pytest/pathlib.py": 9719734558816066453, - "venv/lib/python3.13/site-packages/numpy/ma/extras.pyi": 15292036493781556342, - "venv/lib/python3.13/site-packages/greenlet/tests/test_extension_interface.py": 11693583111513457974, - "venv/lib/python3.13/site-packages/pip/_internal/vcs/__init__.py": 1197579313959289096, - "venv/lib/python3.13/site-packages/numpy/lib/_stride_tricks_impl.py": 5260748170237305627, - "venv/lib/python3.13/site-packages/passlib/handlers/argon2.py": 835074952952017434, - "venv/lib/python3.13/site-packages/_pytest/pytester_assertions.py": 1899505062406979663, - "venv/lib/python3.13/site-packages/pandas/core/indexes/interval.py": 17965911911189276907, - "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/WHEEL": 1598805043198466373, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_value_counts.py": 13270420637302622174, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_cov_corr.py": 8117511855230249212, - "venv/lib/python3.13/site-packages/pygments/lexers/mojo.py": 8616283481592061285, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/provision.py": 2676876852726658063, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_mask.py": 9654897363729130542, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/__init__.py": 7526309124863950484, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_select.py": 17823338162428379468, - "backend/src/api/routes/git/_repo_routes.py": 7367823490321383813, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/provision.py": 7362363963464621570, - "venv/lib/python3.13/site-packages/numpy/lib/npyio.pyi": 3212440968296555560, - "venv/lib/python3.13/site-packages/PIL/_imagingft.cpython-313-x86_64-linux-gnu.so": 11054435406031360190, - "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/RECORD": 963865046004908868, - "venv/lib/python3.13/site-packages/pandas/tests/interchange/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/version.py": 1497063540123613501, - "venv/lib/python3.13/site-packages/pandas/_libs/window/aggregations.cpython-313-x86_64-linux-gnu.so": 16327106317503267067, - "venv/lib/python3.13/site-packages/pandas/tests/test_aggregation.py": 16863055892192070549, - "venv/lib/python3.13/site-packages/psycopg2/extensions.py": 14197445323992851566, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Hamming.pyi": 4432290176776357346, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_semicolon_split.py": 8153770165829451525, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_lexsort.py": 10394832796613968484, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/shape.pyi": 2188930129299348535, - "venv/lib/python3.13/site-packages/pygments/lexers/d.py": 18120801227801223376, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py": 18069619555394133945, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/mixed/foo.f": 5690596225005490325, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_getlimits.py": 118041882543413669, - "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/METADATA": 7727489762869155085, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/control.py": 2034518223411945438, - "venv/lib/python3.13/site-packages/numpy/_pyinstaller/tests/__init__.py": 13331622784664204066, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-cos.csv": 12896264205153363031, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/reshaping.py": 18085648077811502689, - "venv/lib/python3.13/site-packages/smmap/test/test_buf.py": 6910367739329016619, - "venv/lib/python3.13/site-packages/pydantic/deprecated/config.py": 12280996227531856230, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_constructors.py": 13890224577680389718, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_api.py": 13319208721442328400, - "venv/lib/python3.13/site-packages/pygments/lexers/pawn.py": 18127303119414402594, - "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/api.py": 13134394673408254097, - "venv/lib/python3.13/site-packages/apscheduler/jobstores/rethinkdb.py": 16413299263077970925, - "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libcrypto-2e26a48f.so.3": 3658133521245392852, - "venv/lib/python3.13/site-packages/pandas/core/tools/numeric.py": 8510988912136285022, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/formatter.py": 16499277113779414101, - "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/queue.py": 7820997545569991670, - "venv/lib/python3.13/site-packages/_pytest/_py/path.py": 8464061867075119556, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ndarray_conversion.py": 6205112277291935767, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/json.py": 4660603129768960095, - "frontend/build/_app/immutable/chunks/pZ1kmITO.js": 7579087758312850992, - "venv/lib/python3.13/site-packages/bcrypt/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/_pytest/_py/error.py": 2257336434271433543, - "venv/lib/python3.13/site-packages/bcrypt/_bcrypt.abi3.so": 8691731321358529264, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_umath_accuracy.py": 3904008607472505376, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/lexer.py": 14400766876373764268, - "venv/lib/python3.13/site-packages/pip/_vendor/idna/compat.py": 9758194415584776667, - "venv/lib/python3.13/site-packages/fastapi/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/ec_key.py": 5961540064444749833, - "venv/lib/python3.13/site-packages/pip/_internal/cli/main.py": 9304034369762028739, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py": 6712732248328458871, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/timedeltas/test_reductions.py": 14432782234851468060, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/aiosqlite.py": 9888795404090331319, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/base.py": 14734745827490220667, - "venv/lib/python3.13/site-packages/pytest/__init__.py": 6137272437722350673, - "backend/src/plugins/backup.py": 9697280948668596192, - "backend/src/plugins/translate/superset_executor.py": 13699494825970352543, - "venv/lib/python3.13/site-packages/jsonschema/tests/test_types.py": 651959450717569029, - "build.sh": 37085706669440656, - "venv/lib/python3.13/site-packages/pip/_internal/pyproject.py": 6155748929233245415, - "venv/lib/python3.13/site-packages/numpy/lib/npyio.py": 6782361985454523908, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/json.py": 15085143436056608404, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_tz_localize.py": 13699016868476153393, - "venv/lib/python3.13/site-packages/pandas/core/groupby/base.py": 16888506204910366302, - "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/METADATA": 3448868793744358515, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_datetime.py": 7862725028865965163, - "venv/lib/python3.13/site-packages/PIL/_tkinter_finder.py": 14045496501810535432, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_transpose.py": 17983160984979323393, - "venv/lib/python3.13/site-packages/httpcore/_async/http11.py": 7565741048880549265, - "venv/lib/python3.13/site-packages/uvicorn/supervisors/__init__.py": 15971002342788501945, - "venv/lib/python3.13/site-packages/pygments/lexers/bqn.py": 11823648528816858738, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_constructors.py": 9815164146739254076, - "backend/src/core/config_models.py": 4264626520931642357, - "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/METADATA": 4868544280955034757, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/fixtures/sql.py": 13315906751371033170, - "venv/lib/python3.13/site-packages/PIL/GdImageFile.py": 16875784236394545058, - "backend/src/plugins/storage/plugin.py": 13669428465597027289, - "venv/lib/python3.13/site-packages/attr/_version_info.pyi": 7141309436445210354, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/negative_bounds/issue_20853.f90": 3911644649277659333, - "venv/lib/python3.13/site-packages/pandas/tests/window/conftest.py": 10673264126684571493, - "venv/lib/python3.13/site-packages/anyio/abc/_testing.py": 16484863782452886547, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_windows_renderer.py": 1076842273781909680, - "venv/lib/python3.13/site-packages/pip/_vendor/idna/uts46data.py": 1867240043777383728, - "venv/lib/python3.13/site-packages/starlette/middleware/exceptions.py": 10408538054406276433, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_duplicates.py": 9830936017197354309, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Jaro.py": 183817263146423033, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_2_complexcompat.h": 5001035774408914877, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_index_as_string.py": 13421551616136437100, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py": 5728866271812501558, - "venv/lib/python3.13/site-packages/websockets/legacy/http.py": 16266924390281399720, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test__datasource.py": 1747755531265478106, - "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py": 14216912708889351810, - "venv/lib/python3.13/site-packages/pandas/core/tools/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_get_numeric_data.py": 11747933921770969706, - "venv/lib/python3.13/site-packages/pandas/io/html.py": 2727247063231114153, - "venv/lib/python3.13/site-packages/greenlet/PyGreenlet.hpp": 6366432579621298231, - "venv/lib/python3.13/site-packages/pycparser/ply/ygen.py": 5520340773074632724, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi": 16048852379621461316, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_get_numeric_data.py": 5896318696417090902, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_frame_equal.py": 12459816298158655274, - "venv/lib/python3.13/site-packages/more_itertools/__init__.pyi": 16479506520351014608, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ndarray_assignability.pyi": 1718610984396277997, - "venv/lib/python3.13/site-packages/pandas/tests/base/test_unique.py": 3148372351781648868, - "venv/lib/python3.13/site-packages/pandas/_testing/_warnings.py": 5640964566228306285, - "venv/lib/python3.13/site-packages/pygments/formatters/groff.py": 6480830447984073574, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/modules.py": 9023244176010175562, - "venv/lib/python3.13/site-packages/pygments/lexers/r.py": 14263133575134645005, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/__init__.py": 9820770319106921495, - "venv/lib/python3.13/site-packages/pygments/lexers/thingsdb.py": 6073447481627395832, - "frontend/src/components/llm/ValidationReport.svelte": 11810571826279890996, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_repr.py": 11737029058271003097, - "venv/lib/python3.13/site-packages/pydantic/v1/parse.py": 5875950480538559709, - "venv/lib/python3.13/site-packages/jeepney/io/trio.py": 11859775321483330469, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/operators.py": 2468046993343796128, - "backend/src/services/__init__.py": 15123879863527585264, - "venv/lib/python3.13/site-packages/pygments/lexers/special.py": 4972294202790455038, - "venv/lib/python3.13/site-packages/_pytest/stash.py": 14430252294763173610, - "backend/logs/app.log.2": 6692440785424103264, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/pcg64-testset-2.csv": 5423145402839576695, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/assumed_shape/foo_mod.f90": 5765181406654284736, - "backend/src/services/clean_release/repositories/approval_repository.py": 15069637305272665109, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_overrides.py": 92685611709898742, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_tooltip.py": 853808724844451770, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_spinners.py": 15873416546130487469, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/expression.py": 1248029135284283479, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/certs.py": 13366788049827102313, - "venv/lib/python3.13/site-packages/pydantic/typing.py": 6661252174518316760, - "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/METADATA": 6596896092754256988, - "venv/lib/python3.13/site-packages/PIL/PcdImagePlugin.py": 922809545463280224, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/assertsql.py": 4633484475514351889, - "venv/lib/python3.13/site-packages/ecdsa/test_ellipticcurve.py": 11125783981574901613, - "venv/lib/python3.13/site-packages/numpy/_core/strings.pyi": 1981676808027146409, - "backend/src/plugins/storage/__init__.py.bak": 7062955918227442388, - "frontend/src/lib/stores/__tests__/mocks/stores.js": 14098417748659067309, - "frontend/src/lib/components/layout/__tests__/sidebarNavigation.test.js": 9823973527644599938, - "backend/src/core/auth/security.py": 8289703096420999159, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_arrow_interface.py": 5231449831607036031, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_numerictypes.py": 8836108334624807647, - "backend/tests/scripts/test_clean_release_tui.py": 14700450036639944764, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/row.py": 9763935103055608874, - "backend/src/core/database.py.bak": 14909628735913201532, - "venv/lib/python3.13/site-packages/greenlet/tests/_test_extension_cpp.cpp": 4590574206187223887, - "venv/lib/python3.13/site-packages/rsa/core.py": 13203947234024924497, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ndarray_misc.pyi": 7544619194032232861, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_localize.py": 18409231586451494605, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_crosstab.py": 6631711416347033281, - "venv/lib/python3.13/site-packages/pandas/tests/tools/test_to_timedelta.py": 364160935264757405, - "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/LICENSE": 11167201188673981085, - "venv/lib/python3.13/site-packages/numpy/f2py/__main__.py": 12567751646090102167, - "frontend/src/lib/components/translate/TermCorrectionPopup.svelte": 563259816136630056, - "venv/lib/python3.13/site-packages/pydantic/_internal/_utils.py": 10936299012115637790, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_interval.py": 2613503970235591726, - "venv/lib/python3.13/site-packages/uvicorn/loops/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/serializer.py": 8708767945343850816, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/client_auth.py": 3301527187487914220, - "venv/lib/python3.13/site-packages/numpy/f2py/auxfuncs.py": 2234931733687557433, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/default_styles.py": 4469204219504675080, - "venv/lib/python3.13/site-packages/numpy/lib/_histograms_impl.pyi": 6534825471013879522, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/hooks.py": 9888227370911151341, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/interval/test_interval_new.py": 3893421355544517336, - "venv/lib/python3.13/site-packages/pandas/io/formats/templates/html.tpl": 12550218314797699304, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/fixtures/base.py": 10205051944814316388, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/util.py": 4890614159013873000, - "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/METADATA": 10241403930811743893, - "venv/lib/python3.13/site-packages/dateutil/relativedelta.py": 5903095963079475484, - "venv/lib/python3.13/site-packages/pydantic/experimental/arguments_schema.py": 8644805798814234457, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/functions.py": 16567226076808693246, - "venv/lib/python3.13/site-packages/passlib/handlers/scrypt.py": 10089372287597257402, - "venv/lib/python3.13/site-packages/numpy/random/_examples/numba/extending_distributions.py": 209769687436320665, - "venv/lib/python3.13/site-packages/pandas/core/computation/align.py": 13027916530558901817, - "frontend/src/lib/components/layout/__tests__/test_breadcrumbs.svelte.js": 1250794495161202602, - "venv/lib/python3.13/site-packages/requests/__version__.py": 6677532709758546816, - "frontend/build/_app/immutable/chunks/A8Q9-d-F.js": 1942778798008045249, - "venv/lib/python3.13/site-packages/anyio/_backends/_trio.py": 4764555436057754985, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh25286.f90": 1249061697041863692, - "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/licenses/LICENSE": 17020744174398828059, - "venv/lib/python3.13/site-packages/numpy/f2py/f2py2e.py": 738588649412338185, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_operators.py": 12686786829906210592, - "venv/lib/python3.13/site-packages/pluggy/_warnings.py": 2664977718908223842, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/specifiers.py": 12284901056355482991, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/adapter.py": 5908944827805063108, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_arithmetic.py": 16045758521544316139, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_doc.py": 7865575374100528149, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/scoping.py": 18223258186431020473, - "venv/lib/python3.13/site-packages/pyasn1/codec/native/encoder.py": 120921270972591880, - "venv/lib/python3.13/site-packages/starlette/formparsers.py": 9019779181100355515, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_function.py": 7048185308307653546, - "venv/lib/python3.13/site-packages/numpy/lib/format.pyi": 11752225430594536662, - "venv/lib/python3.13/site-packages/numpy/random/_examples/cython/extending.pyx": 756189645898997788, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/processors.pyx": 7085380878820747579, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/constants.pyi": 4415188581374004936, - "venv/lib/python3.13/site-packages/numpy/tests/test_ctypeslib.py": 9592016432737074765, - "venv/lib/python3.13/site-packages/attr/setters.pyi": 6670509359139243236, - "venv/lib/python3.13/site-packages/click-8.3.1.dist-info/METADATA": 3188324716322823755, - "venv/lib/python3.13/site-packages/pandas/core/_numba/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_multiarray.py": 3592197154340095431, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_is_monotonic.py": 12769599198035357753, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_contains.py": 5273759677032634868, - "venv/lib/python3.13/site-packages/charset_normalizer/constant.py": 3070316197495651997, - "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/RECORD": 8201759541483070364, - "venv/lib/python3.13/site-packages/PIL/ImageTk.py": 17063969714816564357, - "venv/lib/python3.13/site-packages/jsonschema/validators.py": 3832874295500213309, - "venv/lib/python3.13/site-packages/greenlet/_greenlet.cpython-313-x86_64-linux-gnu.so": 6036818030377883416, - "venv/lib/python3.13/site-packages/referencing/__init__.py": 10093824711214801917, - "venv/lib/python3.13/site-packages/pygments/lexers/apdlexer.py": 14222752852221009085, - "venv/lib/python3.13/site-packages/fastapi/middleware/cors.py": 7452860773712601508, - "venv/lib/python3.13/site-packages/pygments/lexers/cddl.py": 1303059539145221713, - "venv/lib/python3.13/site-packages/pillow.libs/libpng16-4a38ea05.so.16.53.0": 13136089038268964667, - "frontend/src/lib/components/dataset-review/__tests__/us2_semantic_workspace.ux.test.js": 3550956860258259058, - "venv/lib/python3.13/site-packages/pandas/_libs/parsers.pyi": 18402185996588014111, - "venv/lib/python3.13/site-packages/numpy/random/tests/test_direct.py": 12680113511641199070, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_cell_widths.py": 5724379364993806351, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_rename.py": 16148017223289654683, - "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/RECORD": 14375680579341422646, - "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/licenses/LICENSE": 5757922580822401219, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/logging.py": 2426447364230673649, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/bitwise_ops.pyi": 751490187478960891, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/warnings_and_errors.pyi": 3151453262733619734, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ctypeslib.pyi": 8623725566434725526, - "backend/src/api/routes/__tests__/test_clean_release_api.py": 11565478953155085157, - "backend/src/services/dataset_review/semantic_resolver.py": 8447496149691924829, - "venv/lib/python3.13/site-packages/pip/_internal/metadata/base.py": 13444129650880426353, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_analytics.py": 8155550493311083506, - "venv/lib/python3.13/site-packages/dotenv/__main__.py": 16883579879172186355, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/terminal_theme.py": 13727399800263579809, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_npy_units.py": 8966226962799105383, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_x86_msvc.h": 2465089365642299646, - "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/RECORD": 3617343778671172258, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/cmac.py": 12011944740750051829, - "venv/lib/python3.13/site-packages/fastapi/security/oauth2.py": 1925394177931547776, - "venv/lib/python3.13/site-packages/pydantic/mypy.py": 10034028441785588819, - "venv/lib/python3.13/site-packages/passlib/crypto/scrypt/_gen_files.py": 11149423183253618378, - "venv/lib/python3.13/site-packages/passlib/tests/test_win32.py": 13932956220195912963, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_explode.py": 18263694040596159591, - "venv/lib/python3.13/site-packages/charset_normalizer/md__mypyc.cpython-313-x86_64-linux-gnu.so": 12629680116730190225, - "venv/lib/python3.13/site-packages/pandas/util/_print_versions.py": 15326663505771588740, - "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_getitem.py": 18202683049446557047, - "venv/lib/python3.13/site-packages/pygments/lexers/bdd.py": 5389493859905397060, - "frontend/src/components/backups/BackupManager.svelte": 15722995371746548624, - "backend/src/services/reports/normalizer.py": 11408466232944555336, - "venv/lib/python3.13/site-packages/more_itertools/recipes.pyi": 16788810745659802820, - "venv/lib/python3.13/site-packages/urllib3/util/response.py": 6106698559175827207, - "venv/lib/python3.13/site-packages/greenlet/tests/fail_slp_switch.py": 16490710972475154509, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_rewrite_warning.py": 4895919872954538758, - "venv/lib/python3.13/site-packages/numpy/version.pyi": 5075382895829889457, - "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/RECORD": 6518517239543344179, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_x64_masm.obj": 2884737706935637437, - "venv/lib/python3.13/site-packages/pygments/lexers/arturo.py": 4565524340706235725, - "venv/lib/python3.13/site-packages/pygments/lexers/html.py": 17288963642862550116, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/data_multiplier.f": 13432803601367981068, - "venv/lib/python3.13/site-packages/pandas/core/arrays/floating.py": 1215586266958421991, - "venv/lib/python3.13/site-packages/pygments/lexers/dax.py": 10868881057512002309, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_array.f90": 9711940829820746513, - "venv/lib/python3.13/site-packages/greenlet/tests/test_generator_nested.py": 12822240347707346718, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/datonly.f90": 10495925225338237742, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/lexers/chapel.py": 7619603166448145621, - "venv/lib/python3.13/site-packages/dotenv/ipython.py": 15904966766060247908, - "backend/src/services/dataset_review/orchestrator_pkg/_helpers.py": 11613130235340753050, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_astype.py": 4975959187621745360, - "venv/lib/python3.13/site-packages/pygments/lexers/inferno.py": 9056274496469409594, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/conftest.py": 8406022190801970256, - "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_datetimelike.py": 15246580603900811749, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/pyodbc.py": 8175468565413744516, - "frontend/build/_app/immutable/chunks/ClIjFp8f.js": 4856629551122561739, - "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/WHEEL": 2357997949040430835, - "backend/src/core/utils/async_network.py": 2763204398131017281, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_numeric.py": 9417128508433693658, - "venv/lib/python3.13/site-packages/PIL/_imagingmath.pyi": 18222325750818585549, - "venv/lib/python3.13/site-packages/pip/_internal/operations/build/metadata_legacy.py": 9226692984248279094, - "venv/lib/python3.13/site-packages/pip/_internal/req/req_dependency_group.py": 10505346210114745037, - "venv/lib/python3.13/site-packages/rapidfuzz/process.pyi": 6959881293311340830, - "venv/lib/python3.13/site-packages/apscheduler/executors/base.py": 1980520974511822101, - "venv/lib/python3.13/site-packages/numpy/ma/testutils.py": 9355402483340137461, - "venv/lib/python3.13/site-packages/pandas/api/typing/__init__.py": 6438268275857060114, - "venv/lib/python3.13/site-packages/websockets/asyncio/router.py": 15039562376314233669, - "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/attr/_cmp.pyi": 14443711863388606665, - "venv/lib/python3.13/site-packages/pydantic/v1/env_settings.py": 6194387033830177219, - "venv/lib/python3.13/site-packages/fastapi/openapi/docs.py": 16735218221639179927, + "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/METADATA": 4975169119201255384, "venv/lib/python3.13/site-packages/jsonschema/benchmarks/subcomponents.py": 10365594934527289233, - "venv/lib/python3.13/site-packages/pandas/_version.py": 3480474785419232091, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_sorted.py": 1899608688401238388, - "venv/lib/python3.13/site-packages/authlib/common/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/rsa/transform.py": 12261738253972114335, - "venv/lib/python3.13/site-packages/numpy/random/_generator.pyi": 17167649524261942483, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_multiindex.py": 13723677252432338937, - "venv/lib/python3.13/site-packages/pygments/styles/arduino.py": 4578385147314541233, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_reindex.py": 12459492712890114705, - "venv/lib/python3.13/site-packages/PIL/ImageFont.py": 15621336334706847865, - "venv/lib/python3.13/site-packages/pyasn1/type/base.py": 6801504886392649787, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi": 16042272072158438261, - "venv/lib/python3.13/site-packages/pydantic/v1/tools.py": 1995883197897112278, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_assign.py": 2647902466315482406, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_value_counts.py": 5534713464063604301, - "frontend/src/routes/dashboards/health/+page.svelte": 5234355531444674267, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_mask.py": 5451866071307236899, - "venv/lib/python3.13/site-packages/pygments/styles/vs.py": 4188729978573652106, - "frontend/build/_app/immutable/nodes/33.JpEaV7re.js": 16783069657925281131, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/sphinxext.py": 2403717774564798878, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/vectorized.cpython-313-x86_64-linux-gnu.so": 5441755022198996487, - "venv/lib/python3.13/site-packages/numpy/_utils/_convertions.pyi": 362674842164060822, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/information_schema.py": 8551227132710897346, - "venv/lib/python3.13/site-packages/pygments/styles/staroffice.py": 11694998942433220413, - "frontend/src/lib/toasts.js": 199299975383904134, - "frontend/src/lib/components/dataset-review/SemanticLayerReview.svelte": 2844634204872241861, - "frontend/build/_app/immutable/chunks/Fxxb239K.js": 5574865482398189674, - "backend/src/models/profile.py": 18027673609437659833, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_pickle.py": 4216445955812104014, - "venv/lib/python3.13/site-packages/PIL/MspImagePlugin.py": 3034490726971829439, - "backend/src/services/reports/__init__.py": 11818812865847046400, - "venv/lib/python3.13/site-packages/authlib/jose/rfc8037/okp_key.py": 10312614562914096039, - "venv/lib/python3.13/site-packages/numpy/lib/_arraysetops_impl.pyi": 3111521453255595221, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/histograms.pyi": 2281259341486424755, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh27697.f90": 4990763135487995517, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_ufunc.py": 3326502989311831856, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/recarray_from_file.fits": 15116318600514274152, - "venv/lib/python3.13/site-packages/requests/packages.py": 7733041271118298220, - "venv/lib/python3.13/site-packages/passlib/handlers/scram.py": 12281469744083867450, - "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/METADATA": 15888766579203341778, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_tz_localize.py": 14495969595744172955, - "venv/lib/python3.13/site-packages/cffi/backend_ctypes.py": 2042566833861899211, - "venv/lib/python3.13/site-packages/pip/_internal/utils/misc.py": 17461379236189029253, - "venv/lib/python3.13/site-packages/annotated_doc/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/httpx/_utils.py": 4836536051177895209, - "venv/lib/python3.13/site-packages/numpy/testing/print_coercion_tables.pyi": 3814281717779245045, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/style.py": 15214295655795348821, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_isin.py": 11330491145602008796, - "venv/lib/python3.13/site-packages/sqlalchemy/util/typing.py": 17638587764345605306, - "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/licenses/LICENSE": 5669644120528099197, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/adapters.py": 2224938661429637508, - "venv/lib/python3.13/site-packages/uvicorn/supervisors/statreload.py": 5018382301336802445, - "venv/lib/python3.13/site-packages/pygments/lexers/_stata_builtins.py": 18053452765493556047, - "venv/lib/python3.13/site-packages/pygments/lexers/_php_builtins.py": 2344562378679281201, - "venv/lib/python3.13/site-packages/pandas/core/array_algos/transforms.py": 3773159138960141434, - "venv/lib/python3.13/site-packages/pygments/lexers/ride.py": 14096748896451437594, - "venv/lib/python3.13/site-packages/PIL/GimpPaletteFile.py": 2040459799887417392, - "backend/src/core/task_manager/manager.py": 5748204558787054193, - "backend/src/api/routes/assistant/_schemas.py": 6735060826070731731, - "venv/lib/python3.13/site-packages/pydantic/type_adapter.py": 10162988530225478692, - "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/licenses/LICENSE": 12688029424398428498, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7009/__init__.py": 1923528231486706442, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/strings.pyi": 402863478592154500, - "venv/lib/python3.13/site-packages/PIL/TiffTags.py": 8688883943071546469, - "venv/lib/python3.13/site-packages/git/config.py": 1724894144987553201, - "venv/lib/python3.13/site-packages/numpy/exceptions.pyi": 13942486291715303235, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/anyio/abc/_tasks.py": 16839170934610571514, - "venv/lib/python3.13/site-packages/attr/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_internal/commands/install.py": 17631777189475433503, - "venv/lib/python3.13/site-packages/fastapi/dependencies/models.py": 8626894872248212015, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/hist.py": 10928173425078493095, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_formats.py": 2601044064677727807, - "venv/lib/python3.13/site-packages/pydantic/schema.py": 83238111330132033, - "venv/lib/python3.13/site-packages/PIL/GribStubImagePlugin.py": 14954946414134984896, - "venv/lib/python3.13/site-packages/pandas/tests/apply/test_series_transform.py": 18159263102656491974, - "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/licenses/LICENSE": 5563356551734678258, - "venv/lib/python3.13/site-packages/greenlet/tests/test_tracing.py": 10227316489696807112, - "venv/lib/python3.13/site-packages/pip/_internal/commands/uninstall.py": 11003472265011262515, - "venv/lib/python3.13/site-packages/pandas/api/indexers/__init__.py": 17431573013142626187, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_add_prefix_suffix.py": 4815518357936092237, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py": 8530403239168529668, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py": 12264234876933578367, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/offsets.cpython-313-x86_64-linux-gnu.so": 6726636942028566415, - "venv/lib/python3.13/site-packages/attr/__init__.pyi": 13203080513025719049, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_get_dummies.py": 16112674981222844375, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/__init__.py": 15130871412783076140, - "frontend/src/routes/git/+page.svelte": 14169430923974356254, - "venv/lib/python3.13/site-packages/pygments/lexers/tal.py": 447505725492547461, - "backend/src/api/routes/__tests__/test_clean_release_v2_release_api.py": 14275839608126658275, - "venv/lib/python3.13/site-packages/psycopg2/__init__.py": 15798412693009721199, - "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE": 10253862887434903467, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/warnings_and_errors.py": 11172336980866895356, - "venv/lib/python3.13/site-packages/pandas/core/arrays/_ranges.py": 2572903460327860111, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/usecols/test_parse_dates.py": 5344929031479212951, - "venv/lib/python3.13/site-packages/pydantic/_internal/_schema_generation_shared.py": 23345689300073549, - "venv/lib/python3.13/site-packages/starlette/background.py": 10232175205397808417, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_describe.py": 14493300276133670380, - "venv/lib/python3.13/site-packages/pandas/io/iceberg.py": 13137128885014736233, - "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_freq_attr.py": 1720704043778272895, - "venv/lib/python3.13/site-packages/pyasn1/__init__.py": 8089201217593738032, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/jsonschema_specifications/__init__.py": 5732822354953774657, - "venv/lib/python3.13/site-packages/pip/_internal/utils/logging.py": 9639351759822608724, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge.py": 11366935134260232329, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_drop_duplicates.py": 5874427993953991216, - "venv/lib/python3.13/site-packages/jsonschema/tests/fuzz_validate.py": 2328585939822059352, - "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/WHEEL": 13232065379159720345, - "venv/lib/python3.13/site-packages/authlib/integrations/sqla_oauth2/__init__.py": 12911374670789466961, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_client/apps.py": 10758062668702044519, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7516/__init__.py": 11121941644106061453, - "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/RECORD": 9803861100978586236, - "venv/lib/python3.13/site-packages/websockets/legacy/protocol.py": 762692033188392424, - "venv/lib/python3.13/site-packages/httpx/_status_codes.py": 1758440600462491839, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/shape_base.pyi": 5046405609030418900, - "venv/lib/python3.13/site-packages/rapidfuzz/fuzz_py.py": 6428078990080243608, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/mt19937-testset-2.csv": 14641856424618845798, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/size/foo.f90": 18067977329435720684, + "venv/lib/python3.13/site-packages/numpy/_core/_type_aliases.pyi": 15848834171197104301, "venv/lib/python3.13/site-packages/pandas/util/_tester.py": 1033951890304257539, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_base_indexer.py": 3129123098288498633, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_compression.py": 3167245427092658522, - "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_macos.py": 7028037591427052891, - "venv/lib/python3.13/site-packages/urllib3/contrib/pyopenssl.py": 11839715589709210412, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_polynomial.py": 3049262771098624825, - "venv/lib/python3.13/site-packages/pygments/lexers/ada.py": 17032408976634308999, - "backend/src/services/llm_provider.py": 3024842945367976592, - "venv/lib/python3.13/site-packages/pydantic_settings/exceptions.py": 18300876747412267999, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_x64_masm.asm": 15349921092257597740, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_resolution.py": 9651416588266430520, - "venv/lib/python3.13/site-packages/pandas/io/pytables.py": 17482934215513855928, - "venv/lib/python3.13/site-packages/passlib/tests/test_crypto_des.py": 7198931807116651263, - "venv/lib/python3.13/site-packages/idna/compat.py": 9758194415584776667, - "venv/lib/python3.13/site-packages/pygments/lexers/_css_builtins.py": 17478340487186531268, - "venv/lib/python3.13/site-packages/numpy/lib/_type_check_impl.py": 15989335816452913951, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_partial_slicing.py": 15567512240469921599, - "venv/lib/python3.13/site-packages/_pytest/doctest.py": 6126919596776592278, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/random.pyi": 9425735458050148191, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/conftest.py": 16571782049241458769, - "venv/lib/python3.13/site-packages/jose/exceptions.py": 8562012933958941039, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_non_unique.py": 15286893322149413712, - "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/WHEEL": 7454950858448014158, - "venv/lib/python3.13/site-packages/jeepney/bindgen.py": 7094585345946943534, - "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/METADATA": 13776251749114562660, - "venv/lib/python3.13/site-packages/pip/_internal/network/utils.py": 13421242949994555055, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-tanh.csv": 6674536825156196036, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/data_common.f": 5913807803369559749, - "venv/lib/python3.13/site-packages/numpy/lib/_shape_base_impl.py": 16539612003028676816, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_frame.py": 2169381747938271444, - "venv/lib/python3.13/site-packages/pydantic/v1/version.py": 2758971830527526972, - "venv/lib/python3.13/site-packages/httpx/_exceptions.py": 18399489130669465891, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/serialize.py": 11139432164346742421, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ndarray_shape_manipulation.pyi": 3533739482029983817, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Jaro_py.py": 11991492232381887787, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_isoc.py": 4923640657296076239, - "venv/lib/python3.13/site-packages/pandas/io/formats/console.py": 10370334487585281127, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/engines.py": 10876954465293959720, - "venv/lib/python3.13/site-packages/numpy/_core/memmap.py": 4093510602708416858, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/reflection.py": 1799734694597531652, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/immutabledict.cpython-313-x86_64-linux-gnu.so": 12776951671822017500, - "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/RECORD": 9678861487447993169, - "venv/lib/python3.13/site-packages/cryptography/x509/general_name.py": 4481582226129488535, - "venv/lib/python3.13/site-packages/PIL/JpegPresets.py": 12342262642420923471, - "venv/lib/python3.13/site-packages/numpy/lib/array_utils.pyi": 14646405559193539455, - "venv/lib/python3.13/site-packages/pandas/_libs/join.cpython-313-x86_64-linux-gnu.so": 2947947484481830203, - "venv/lib/python3.13/site-packages/cffi/_imp_emulation.py": 17246725564704952212, - "venv/lib/python3.13/site-packages/pygments/lexers/typoscript.py": 6706632522710582762, - "frontend/src/lib/components/assistant/__tests__/assistant_first_message.integration.test.js": 16494645867501045726, - "frontend/src/lib/components/translate/CorrectionCell.svelte": 4880217280855576250, - "frontend/build/_app/immutable/assets/0.DpIDeCZ3.css": 7055983390808293488, - "backend/src/api/routes/dataset_review_pkg/_dependencies.py": 15939232213193276681, - "venv/lib/python3.13/site-packages/pip/_vendor/tomli_w/py.typed": 9796674040111366709, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/mt19937-testset-1.csv": 16349400027062875699, - "venv/lib/python3.13/site-packages/iniconfig/_version.py": 8497474654470220170, - "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/licenses/LICENSE": 16147999986886816451, - "venv/lib/python3.13/site-packages/fastapi/middleware/__init__.py": 5289703478893407762, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/gh26920/two_mods_with_one_public_routine.f90": 7957683453180939605, - "venv/lib/python3.13/site-packages/pandas/tests/window/moments/test_moments_consistency_expanding.py": 12504275097476819918, - "venv/lib/python3.13/site-packages/authlib/integrations/base_client/framework_integration.py": 3935346152156572025, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/openssl/binding.py": 17078280153372356796, - "venv/lib/python3.13/site-packages/greenlet/TThreadStateDestroy.cpp": 13749797532421836328, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/_pytest/hookspec.py": 387790426108810884, - "venv/lib/python3.13/site-packages/pandas/core/groupby/ops.py": 2036968385544457016, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_pop.py": 5700439923973346681, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/cache_key.py": 11118819065204404106, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_qcut.py": 8972418112914954758, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/types.py": 16795741817129898714, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/setitem.py": 9552868454186447925, - "venv/lib/python3.13/site-packages/more_itertools/more.py": 1631135582441184133, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py": 16386268386376675175, - "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/construction/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_loadtxt.py": 17562784940619942447, - "venv/lib/python3.13/site-packages/httpcore/_backends/anyio.py": 6167729964577005634, - "venv/lib/python3.13/site-packages/pandas/core/groupby/generic.py": 4857114071347950833, - "venv/lib/python3.13/site-packages/passlib/tests/test_context_deprecated.py": 7876919198015151018, - "frontend/src/lib/ui/Select.svelte": 11354509509920497811, - "frontend/build/_app/immutable/nodes/28.BeU7bdHC.js": 11715772981654521705, - "backend/src/api/routes/clean_release_v2.py": 2858037566596334612, - "backend/tests/test_translate_executor_filter.py": 11717560360659156703, - "frontend/build/_app/immutable/nodes/1.Bqvpw8Vl.js": 16310728508939338800, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/validator_creation.py": 2745563768861102999, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-log10.csv": 13441294827017400897, - "venv/lib/python3.13/site-packages/passlib/tests/test_crypto_builtin_md4.py": 16840174243259839994, - "venv/lib/python3.13/site-packages/pydantic/alias_generators.py": 5991580234179623842, - "backend/src/core/superset_client/_dashboards_crud.py": 4664728216214173491, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_deprecations.py": 6015734648984591027, - "backend/src/models/dataset_review_pkg/_semantic_models.py": 15376906129805121935, - "backend/src/plugins/llm_analysis/models.py": 11561165853127709481, - "venv/lib/python3.13/site-packages/numpy/linalg/tests/test_linalg.py": 2939739913218718132, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_iteration.py": 15247340610412364508, - "venv/lib/python3.13/site-packages/pygments/cmdline.py": 15628499088643246809, - "frontend/build/_app/immutable/chunks/BDmwnQQF.js": 1134144008713893143, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/multiarray.py": 3224049574922696945, - "venv/lib/python3.13/site-packages/pandas/tests/interchange/test_impl.py": 12056958946481531279, - "venv/lib/python3.13/site-packages/git/objects/tree.py": 10239027037919781330, - "backend/src/schemas/dataset_review.py": 15720223123457526104, - "venv/lib/python3.13/site-packages/pip/_internal/operations/build/metadata.py": 2021612995853910808, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/prompt.py": 9561640601516933380, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_copy.py": 11335108864615756235, - "venv/lib/python3.13/site-packages/httpcore/_api.py": 16499985295426060327, - "venv/lib/python3.13/site-packages/pygments/formatters/irc.py": 2133711525226123259, - "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_indexing.py": 81180636019259296, - "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/WHEEL": 15858869568085970864, - "venv/lib/python3.13/site-packages/attr/setters.py": 12467854389745942025, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_regression.py": 4611768764484593979, - "venv/lib/python3.13/site-packages/pandas/core/frame.py": 2630807744775247533, - "venv/lib/python3.13/site-packages/pandas/tests/extension/decimal/__init__.py": 4761936067832638585, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_concat.py": 10207975532842927134, - "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/METADATA": 2604612822968175379, - "venv/lib/python3.13/site-packages/_pytest/unraisableexception.py": 4597178803320514675, - "backend/src/models/__tests__/test_report_models.py": 14916807060334836854, - "venv/lib/python3.13/site-packages/keyring/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/scanner.py": 5640381644804445932, - "venv/lib/python3.13/site-packages/passlib/handlers/oracle.py": 12959175637015399815, - "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_argon2.py": 11345706437113628133, - "venv/lib/python3.13/site-packages/packaging/pylock.py": 15346942039643310253, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7636/__init__.py": 913610541217778747, - "venv/lib/python3.13/site-packages/passlib/tests/sample1c.cfg": 13088198405762342506, - "venv/lib/python3.13/site-packages/pygments/lexers/dns.py": 17149871769553794654, - "venv/lib/python3.13/site-packages/pip/_internal/models/search_scope.py": 9282393652860589543, - "venv/lib/python3.13/site-packages/fastapi/openapi/constants.py": 1111838716953068285, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py": 8539232219382152238, - "venv/lib/python3.13/site-packages/pip/_internal/utils/_jaraco_text.py": 9046755357011730699, - "venv/lib/python3.13/site-packages/pip/_internal/cli/__init__.py": 11914508471759255927, - "venv/lib/python3.13/site-packages/PIL/Hdf5StubImagePlugin.py": 15654153796225833950, - "venv/lib/python3.13/site-packages/_pytest/terminal.py": 885450969703476729, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/__init__.py": 13584649922093198724, - "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/signals.py": 13677199634497673845, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py": 17873236864291624476, - "venv/lib/python3.13/site-packages/pydantic/_internal/_mock_val_ser.py": 12265320534388472158, - "backend/src/core/utils/network.py": 2917454689057905472, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_dataframe.py": 14945138617962156057, - "venv/lib/python3.13/site-packages/h11/_events.py": 13800479928851716538, - "venv/lib/python3.13/site-packages/numpy/_utils/_inspect.py": 7029935879540617057, - "venv/lib/python3.13/site-packages/PIL/_util.py": 17116054813914373606, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_offsets.py": 4499538899492148499, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_header.py": 3564391095263545879, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/type_check.pyi": 9177256991985898824, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_datetimes.py": 7889573945417063183, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/JaroWinkler_py.py": 16189204441438239231, - "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_printing.py": 8859757563354666575, - "venv/lib/python3.13/site-packages/pandas/io/sas/sas7bdat.py": 17851617851457266799, - "venv/lib/python3.13/site-packages/websockets/sync/client.py": 10892557975919033983, - "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/licenses/COPYING": 14459782230785388765, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/arrayterator.py": 3076316311545674897, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/resources.py": 11533706401259147051, - "venv/lib/python3.13/site-packages/numpy/strings/__init__.pyi": 6213864891045042045, - "backend/src/services/clean_release/repositories/__init__.py": 9926779835387630811, - "venv/lib/python3.13/site-packages/smmap/test/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/test_aggregate.py": 15038915958422670700, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/jwk.py": 6458001384702613165, - "venv/lib/python3.13/site-packages/pip/_internal/cli/base_command.py": 10660882478126415399, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/matrix.pyi": 15667024840812783911, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_construction.py": 6564829381134214401, - "venv/lib/python3.13/site-packages/fastapi/dependencies/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/METADATA": 14543822270526146783, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/expression.py": 15511250785671695359, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_shape_base.py": 8837845103338426570, - "venv/lib/python3.13/site-packages/authlib/jose/rfc8037/__init__.py": 8705044848308260480, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/instrumentation.py": 13198832283145198959, - "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/__init__.py": 12793458753561114666, - "venv/lib/python3.13/site-packages/numpy/core/arrayprint.py": 9652368112174245943, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_year.py": 17441853503700184299, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/arithmetic.py": 3209727509456212551, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_where.py": 16584772339591698754, - "venv/lib/python3.13/site-packages/numpy/_utils/__init__.py": 7731065084036767848, - "venv/lib/python3.13/site-packages/git/objects/submodule/__init__.py": 12958523219496107878, - "frontend/src/pages/Dashboard.svelte": 3653104499351855122, - "frontend/src/lib/api/assistant.js": 15470667170083225911, - "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_defmatrix.py": 2982572083770917411, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8693/__init__.py": 11065844267208884531, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_analytics.py": 4510712334676848202, - "venv/lib/python3.13/site-packages/pip/_internal/cli/parser.py": 1064209424435927674, - "venv/lib/python3.13/site-packages/authlib/integrations/starlette_client/apps.py": 4219758952914498116, - "venv/lib/python3.13/site-packages/idna/codec.py": 4260241549062101779, - "venv/lib/python3.13/site-packages/fastapi/security/api_key.py": 6965937678104291169, - "venv/lib/python3.13/site-packages/pandas/core/util/hashing.py": 13106575357604888913, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_real/foo77.f": 16156304612888138777, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_query_eval.py": 17613800806748649144, - "venv/lib/python3.13/site-packages/rsa/__init__.py": 14623742580410014505, - "venv/lib/python3.13/site-packages/greenlet/greenlet.h": 16216120538647882082, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_asof.py": 17616601748321325187, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_pickle.py": 16196792378104396231, - "venv/lib/python3.13/site-packages/fastapi/logger.py": 11590581556317053213, - "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/METADATA": 12295263493152296681, - "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/limited_api2.pyx": 15936470837420356071, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_indexing.py": 3841182456456779012, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/base.py": 14908084548919567658, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_parse_iso8601.py": 9100947620267156161, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_multiprocessing.py": 5522490547285108448, - "venv/lib/python3.13/site-packages/pyasn1/codec/streaming.py": 13202773554072937140, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_hour.py": 13754646634123368797, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_subclass.py": 10949229764696473939, - "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/immutabledict.pyx": 11739218099400096619, - "venv/lib/python3.13/site-packages/PIL/_avif.cpython-313-x86_64-linux-gnu.so": 5243449968857258659, - "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/RECORD": 12598309574903741388, - "venv/lib/python3.13/site-packages/attr/_typing_compat.pyi": 2823472697859409877, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_take.py": 14969455660160053080, - "venv/lib/python3.13/site-packages/pandas/_version_meson.py": 4875297137037630519, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py": 5132658725516243311, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_ints.py": 5616705208267050659, - "venv/lib/python3.13/site-packages/pandas/_config/display.py": 433650013567734821, - "venv/lib/python3.13/site-packages/pip/_internal/commands/list.py": 13035925292530461722, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_common.py": 9097104320321411408, - "venv/lib/python3.13/site-packages/pydantic/json.py": 4109352930301629308, - "venv/lib/python3.13/site-packages/pygments/lexers/zig.py": 470431199354307668, - "venv/lib/python3.13/site-packages/_pytest/logging.py": 10972553724755511468, - "venv/lib/python3.13/site-packages/pydantic/deprecated/copy_internals.py": 1245428320513062166, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_stringdtype.py": 8381476847196129074, - "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/sum_.py": 7062070517544958546, - "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/RECORD": 7363618870628306370, - "venv/lib/python3.13/site-packages/pip/__main__.py": 574438877257676911, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/errors.py": 14634768861119075506, - "venv/lib/python3.13/site-packages/pandas/core/reshape/concat.py": 2218848863149192467, - "venv/lib/python3.13/site-packages/pip/_internal/operations/check.py": 16650663913976953800, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_argparse.py": 11111699418217357084, - "venv/lib/python3.13/site-packages/numpy/core/_dtype.pyi": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/transform/test_numba.py": 8250377774229977284, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_conversion_utils.py": 1567199588622767277, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_na_values.py": 13527837495439739279, - "venv/lib/python3.13/site-packages/pygments/lexers/sas.py": 6128008194712914874, - "frontend/src/components/git/GitManager.svelte": 10046788495793334635, - "venv/lib/python3.13/site-packages/pandas/tests/generic/test_finalize.py": 12539815426478689538, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_common.py": 10797243274520625867, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/profiling.py": 16030397164203895281, - "venv/lib/python3.13/site-packages/anyio/abc/_streams.py": 8089057973257658396, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/applicator": 6870991911243684132, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/use_modules.f90": 3913612933340746297, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_monotonic.py": 14648437502004398715, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_reductions.py": 3227705565578016389, - "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/RECORD": 16558021503346615951, - "venv/lib/python3.13/site-packages/smmap/__init__.py": 4557520096828426601, - "venv/lib/python3.13/site-packages/iniconfig/exceptions.py": 16501270224069836264, - "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/httpcore/_backends/mock.py": 6849131425727092784, - "venv/lib/python3.13/site-packages/dateutil/_version.py": 8279235793001245767, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py": 6067836157632573182, - "venv/lib/python3.13/site-packages/keyring/testing/backend.py": 14295829193445781542, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/flatiter.py": 3066469329738423021, - "venv/lib/python3.13/site-packages/pandas/tests/api/test_types.py": 8889829773923216769, - "venv/lib/python3.13/site-packages/pandas/_libs/reshape.cpython-313-x86_64-linux-gnu.so": 10639750501333255943, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/rsa_key.py": 7092106298733667895, - "venv/lib/python3.13/site-packages/requests/structures.py": 2668010839316715865, - "venv/lib/python3.13/site-packages/passlib/crypto/scrypt/_builtin.py": 74545289993348593, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/dim2.py": 5767600151459095125, - "venv/lib/python3.13/site-packages/pandas/tests/strings/conftest.py": 12065119814157197510, - "venv/lib/python3.13/site-packages/dateutil/parser/__init__.py": 11377431154251655738, - "venv/lib/python3.13/site-packages/pip/_vendor/tomli_w/_writer.py": 15053365792216907149, - "venv/lib/python3.13/site-packages/pandas/tests/io/test_gcs.py": 7733247456252965901, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/ccalendar.cpython-313-x86_64-linux-gnu.so": 11019871270094058038, - "venv/lib/python3.13/site-packages/pip/_internal/utils/compat.py": 1841193561752758336, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/foo_deps.f90": 14350239197565389508, - "venv/lib/python3.13/site-packages/uvicorn/lifespan/off.py": 16254524872582859561, - "venv/lib/python3.13/site-packages/fastapi/_compat/main.py": 13016492818231995760, - "venv/lib/python3.13/site-packages/numpy/tests/test_configtool.py": 8208415015478205502, - "backend/tests/services/clean_release/test_demo_mode_isolation.py": 1970652424726919519, - "venv/lib/python3.13/site-packages/ecdsa/test_rw_lock.py": 1572802971060634697, - "venv/lib/python3.13/site-packages/greenlet/tests/fail_initialstub_already_started.py": 5562030563802006923, - "venv/lib/python3.13/site-packages/websockets/speedups.c": 11800200877763464246, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_einsum.py": 17286384318722237180, - "venv/lib/python3.13/site-packages/PIL/FitsImagePlugin.py": 618556384932056120, - "venv/lib/python3.13/site-packages/pip/_vendor/msgpack/ext.py": 15699809032725838985, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_astype.py": 11386658862732393123, - "venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.cpython-313-x86_64-linux-gnu.so": 13917011878259810696, - "venv/lib/python3.13/site-packages/sqlalchemy/exc.py": 6372950288664776625, - "venv/lib/python3.13/site-packages/pygments/lexers/floscript.py": 944042220740663491, - "venv/lib/python3.13/site-packages/PIL/FontFile.py": 112053841436523305, - "frontend/src/lib/auth/store.ts": 14669655798912377598, - "venv/lib/python3.13/site-packages/numpy/_core/defchararray.pyi": 2277933368373498515, - "venv/lib/python3.13/site-packages/pandas/_libs/__init__.py": 17117112389651696580, - "venv/lib/python3.13/site-packages/numpy/random/_mt19937.cpython-313-x86_64-linux-gnu.so": 85866616759631810, - "venv/lib/python3.13/site-packages/pip/_internal/cli/main_parser.py": 7942770969280622910, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/cli/hi77.f": 10617763407280454334, - "venv/lib/python3.13/site-packages/pandas/_libs/arrays.pyi": 12629806207276576877, - "venv/lib/python3.13/site-packages/pandas/core/groupby/numba_.py": 15997665748332235683, - "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/authlib/integrations/sqla_oauth2/functions.py": 14107377669163193871, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/ccalendar.pyi": 15789956857032062129, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_floats.py": 3064615530495290792, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_dtypes.py": 6831042272921908068, - "venv/lib/python3.13/site-packages/urllib3/poolmanager.py": 15431313180245757657, - "venv/bin/activate": 14735074000123383125, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/poolmanager.py": 5071697837617938064, - "frontend/src/routes/+error.svelte": 16649211425103750277, - "frontend/build/_app/immutable/nodes/36.BfewuKxM.js": 16060258850338526744, - "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_numeric.py": 17391095502314929042, - "backend/src/core/migration/risk_assessor.py": 1061977005072298440, - "venv/lib/python3.13/site-packages/numpy/lib/_array_utils_impl.pyi": 11535882608355652318, - "venv/lib/python3.13/site-packages/pandas/compat/__init__.py": 6077099417711209303, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py": 5324490103277723813, - "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/RECORD": 4487477737768669516, - "frontend/build/_app/immutable/nodes/20.BNq1dr4D.js": 14340650458933574612, - "backend/tests/core/test_mapping_service.py": 6888870177029085220, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/conftest.py": 2643915042664055969, - "venv/lib/python3.13/site-packages/click/core.py": 16793133181718891193, - "venv/lib/python3.13/site-packages/pydantic/v1/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/plotting/__init__.py": 13230487326686617492, - "venv/lib/python3.13/site-packages/pygments/lexers/numbair.py": 17382646010399794697, - "venv/lib/python3.13/site-packages/fastapi/templating.py": 10760135144345238340, - "venv/lib/python3.13/site-packages/pygments/lexers/meson.py": 14659991367778746658, - "backend/src/api/routes/translate/_dictionary_routes.py": 13818233841800263162, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arcsin.csv": 1665171675551692225, - "venv/lib/python3.13/site-packages/pandas/tests/generic/test_series.py": 2980850649986213051, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_errstate.py": 10788459171326750780, - "venv/lib/python3.13/site-packages/starlette/authentication.py": 10905269573809129547, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/_cryptography_key.py": 388489172928969873, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/converter.py": 9041278869295701566, - "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_struct_accessor.py": 6567451952835390075, - "venv/lib/python3.13/site-packages/_pytest/junitxml.py": 5871715626272366062, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_encoding.py": 6622474614967398246, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_json_table_schema_ext_dtype.py": 13021437050542691118, - "venv/lib/python3.13/site-packages/sqlalchemy/event/api.py": 987321343916500337, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_ndarray_backed.py": 10097812802469682233, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_overlaps.py": 2328652590379935311, - "venv/lib/python3.13/site-packages/pygments/lexers/scdoc.py": 8481749542463754279, - "venv/lib/python3.13/site-packages/pygments/styles/tango.py": 12389250878249075539, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/_typing.py": 17589387690638097975, - "venv/lib/python3.13/site-packages/pygments/lexers/smv.py": 8253688483040957894, - "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_distutils.py": 1948707545068786, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_scalar_compat.py": 1306236983392218709, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/dtypes/test_dtypes_basic.py": 10917162988625838981, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/discovery.py": 12479996594187326798, - "venv/lib/python3.13/site-packages/pandas/core/interchange/buffer.py": 12961802206383700922, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/DamerauLevenshtein_py.py": 1933257612725836484, - "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/METADATA": 14180973198574731599, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/__init__.py": 14571251344510763303, - "venv/lib/python3.13/site-packages/numpy/f2py/setup.cfg": 17974180080760231128, - "venv/lib/python3.13/site-packages/cryptography/hazmat/backends/__init__.py": 18019562641348687547, - "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/misc.py": 6571241778569465264, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_skiprows.py": 8764515842253490735, - "venv/lib/python3.13/site-packages/keyring/util/platform_.py": 16978095707950463638, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/structs.py": 4479429145468072604, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/rsa.py": 5392922995791487437, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_logical_ops.py": 16166386974005224415, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_timezones.py": 1962830142478494009, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/mariadbconnector.py": 14865906347579451546, - "frontend/src/lib/api/__tests__/reports_api.test.js": 11820587361929895100, - "venv/lib/python3.13/site-packages/typing_inspection/typing_objects.py": 16856718240972376883, - "venv/lib/python3.13/site-packages/jeepney/io/asyncio.py": 13239992245695516007, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_nditer.py": 8341339612899904236, - "venv/lib/python3.13/site-packages/pandas/core/strings/accessor.py": 14021462534679430426, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh24008.f": 3872245077994936272, - "venv/lib/python3.13/site-packages/urllib3/util/proxy.py": 17667887856427739069, - "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/itsdangerous/serializer.py": 14271229091461164933, - "venv/lib/python3.13/site-packages/pip/_vendor/requests/models.py": 17870758731501836303, - "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/pluggy/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pillow.libs/libavif-01e67780.so.16.3.0": 13947441788826399574, - "venv/lib/python3.13/site-packages/httpx/_urls.py": 4219389176352582046, - "venv/lib/python3.13/site-packages/websockets/cli.py": 14049106025886056110, - "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/cython/setup.py": 17086801293303202021, - "venv/lib/python3.13/site-packages/pandas/core/arrays/boolean.py": 8821671562937866269, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_asfreq.py": 6162659555626011770, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/properties.py": 5799883762127872919, - "venv/lib/python3.13/site-packages/pygments/lexers/_mapping.py": 12987629396101138833, - "venv/lib/python3.13/site-packages/numpy/_core/_multiarray_tests.cpython-313-x86_64-linux-gnu.so": 442168371609469538, - "venv/lib/python3.13/site-packages/PIL/_imagingcms.pyi": 12476347428820346383, - "frontend/src/routes/datasets/review/__tests__/dataset_review_entry.ux.test.js": 5439236328736424365, - "venv/lib/python3.13/site-packages/keyring/backends/Windows.py": 4103830951042463184, - "venv/lib/python3.13/site-packages/anyio/abc/_subprocesses.py": 4297459042599291531, - "venv/lib/python3.13/site-packages/passlib/tests/tox_support.py": 9200774792427691602, - "venv/lib/python3.13/site-packages/fastapi/openapi/utils.py": 9058442333718187759, - "frontend/build/index.html": 6110200597222080248, - "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_openpyxl.py": 9542840527483618681, - "venv/lib/python3.13/site-packages/packaging/metadata.py": 9168255069163141470, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/common.py": 10996276696597806957, - "frontend/src/components/storage/FileList.svelte": 11661146151896000393, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_rank.py": 5817408252050193538, - "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/cffi/error.py": 8147505969912539538, - "frontend/build/_app/immutable/chunks/D5BsboTl.js": 12208458907620838691, - "backend/src/plugins/translate/__tests__/test_token_budget.py": 9060506557524997011, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/filter.py": 16846063190466267618, - "backend/src/api/routes/__tests__/test_dashboards.py": 14899529124453053148, - "venv/lib/python3.13/site-packages/apscheduler/util.py": 11440701924954413440, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh25286.pyf": 14358606965464506398, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_dtype.py": 12210524152630532747, - "venv/lib/python3.13/site-packages/pandas/core/array_algos/putmask.py": 10934101690788549526, - "venv/lib/python3.13/site-packages/pip/_vendor/pygments/plugin.py": 18006138132596504101, - "venv/lib/python3.13/site-packages/pydantic/validate_call_decorator.py": 11130572937843915026, - "venv/lib/python3.13/site-packages/pandas/_libs/pandas_datetime.cpython-313-x86_64-linux-gnu.so": 15874094681262240628, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_replace.py": 8165088653532047247, - "venv/lib/python3.13/site-packages/passlib/handlers/roundup.py": 3844280972451753019, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_subclass.py": 16342917051908786497, - "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arctanh.csv": 8620268345075425735, - "venv/lib/python3.13/site-packages/pandas/io/excel/_odswriter.py": 17220892094904423676, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py": 1437580234680265326, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_argsort.py": 7320661835159648143, - "venv/lib/python3.13/site-packages/anyio/_core/_signals.py": 1204322949639513552, - "venv/lib/python3.13/site-packages/pydantic/_internal/_discriminated_union.py": 1818179910730401509, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_constructors.py": 11786151805350138436, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/identity.py": 326059526167697655, - "venv/lib/python3.13/site-packages/secretstorage/dhcrypto.py": 8860116996905452758, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_dot.py": 16876560325283070437, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/json.py": 14738429697319670035, - "venv/lib/python3.13/site-packages/pygments/lexers/ezhil.py": 10294520768762308887, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/errors.py": 10750605030539582738, - "venv/lib/python3.13/site-packages/numpy/polynomial/polynomial.py": 7658234162211356265, - "frontend/build/_app/immutable/nodes/30.BHAvSo-A.js": 13327487733725668780, - "frontend/build/_app/immutable/chunks/D0X9GEx9.js": 7204815795777103850, - "venv/lib/python3.13/site-packages/numpy/_core/fromnumeric.py": 9409852116555866847, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_arrayterator.py": 1646530870049791291, - "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/macos.py": 17442393555211213147, - "venv/lib/python3.13/site-packages/apscheduler/schedulers/tornado.py": 6491136785917169991, - "venv/lib/python3.13/site-packages/pandas/tests/series/test_validate.py": 2133732492454460539, - "venv/lib/python3.13/site-packages/pandas/tests/io/json/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/PIL/IcnsImagePlugin.py": 1870235606309784691, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/common.py": 11384497819737275399, - "venv/lib/python3.13/site-packages/charset_normalizer/version.py": 9948878594498587774, - "venv/lib/python3.13/site-packages/dateutil/zoneinfo/__init__.py": 10261654903106416888, - "venv/lib/python3.13/site-packages/tzlocal/win32.py": 1949985224569608978, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_factorize.py": 861897342553280506, - "venv/lib/python3.13/site-packages/click/utils.py": 13475604420010022458, - "venv/lib/python3.13/site-packages/_pytest/pastebin.py": 13946273207624603063, - "backend/src/plugins/translate/_utils.py": 4311334421388625700, - "venv/lib/python3.13/site-packages/_pytest/setupplan.py": 7496801885995567141, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_drop_duplicates.py": 2397396876872403048, - "venv/lib/python3.13/site-packages/pydantic/_internal/_model_construction.py": 694471028676746110, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/accesstype.f90": 877982462186396830, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ufuncs.py": 12732599294816239111, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_categorical_equal.py": 7942323667866280872, - "venv/lib/python3.13/site-packages/pip/_vendor/distro/__init__.py": 11414161642984633362, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/visitors.py": 5190923415390076080, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/__init__.py": 16434403703599680307, - "venv/lib/python3.13/site-packages/jsonschema/tests/test_utils.py": 8668650648802195016, - "venv/lib/python3.13/site-packages/click/_textwrap.py": 14586282603215052310, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/common.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/PIL/PSDraw.py": 1927169421822446045, - "venv/lib/python3.13/site-packages/numpy/polynomial/_polybase.py": 893743087939984792, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/color.py": 15062855556511243610, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_fileno.py": 15541504502174047167, - "venv/lib/python3.13/site-packages/pandas/io/__init__.py": 9767690163638898709, - "venv/bin/pyrsa-encrypt": 5858278204727417482, - "backend/src/core/__tests__/test_superset_profile_lookup.py": 15590077829286810714, - "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/METADATA": 3390257212824996989, - "venv/lib/python3.13/site-packages/fastapi/types.py": 3867821246650502267, - "backend/src/api/routes/translate/_job_routes.py": 6716903871937439968, - "venv/lib/python3.13/site-packages/referencing/_attrs.py": 14434891919747863272, - "scripts/gen_semantics.py": 11397714264377363083, - "venv/lib/python3.13/site-packages/pandas/tests/test_flags.py": 411845322592830527, - "venv/lib/python3.13/site-packages/numpy/_core/_struct_ufunc_tests.cpython-313-x86_64-linux-gnu.so": 13269324887178340397, - "backend/src/api/routes/translate/_correction_routes.py": 17010744610493077227, - "venv/lib/python3.13/site-packages/numpy/typing/mypy_plugin.py": 1306129775649200113, - "venv/lib/python3.13/site-packages/httpx/_transports/__init__.py": 5931944625631942279, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7521/client.py": 17305914656270781911, - "venv/lib/python3.13/site-packages/pip/_internal/commands/lock.py": 14782882714168174465, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/metadata.py": 7204343250279221076, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/_elements_constructors.py": 9279447066034364952, - "venv/lib/python3.13/site-packages/urllib3/py.typed": 12220177918227495093, - "venv/lib/python3.13/site-packages/httpx/_multipart.py": 2181282475656908730, - "venv/lib/python3.13/site-packages/attr/_compat.py": 4016698632899969390, - "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/RECORD": 12575893084587070195, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/apscheduler/schedulers/background.py": 2105814165112049750, - "venv/lib/python3.13/site-packages/jeepney/io/tests/test_trio.py": 7459901627473962540, - "venv/lib/python3.13/site-packages/greenlet/tests/test_throw.py": 4119265751381210399, - "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/licenses/COPYING": 15270149301471737292, - "venv/lib/python3.13/site-packages/pydantic/deprecated/tools.py": 16466182352694741629, - "venv/lib/python3.13/site-packages/pip/_internal/index/collector.py": 7454652626329467900, - "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/numpy/lib/_npyio_impl.pyi": 6576645822227068922, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame_groupby.py": 12709199432838648700, - "venv/lib/python3.13/site-packages/numpy/polynomial/__init__.pyi": 8708830448398500675, - "venv/lib/python3.13/site-packages/numpy/lib/array_utils.py": 18135114588643176359, - "venv/lib/python3.13/site-packages/passlib/handlers/digests.py": 18158237109145814857, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_set_axis.py": 4097534573847151853, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_groupby.py": 15721254196883995634, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_fillna.py": 7349777469648334282, - "venv/lib/python3.13/site-packages/pandas/tests/generic/test_to_xarray.py": 15341013524814290960, - "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_file_handling.py": 732772718210067599, - "venv/lib/python3.13/site-packages/urllib3/connectionpool.py": 7493427857878469165, - "venv/lib/python3.13/site-packages/PIL/ImageWin.py": 13635870584232081323, - "venv/lib/python3.13/site-packages/pygments/filters/__init__.py": 2992007913496948339, - "venv/lib/python3.13/site-packages/pygments/lexers/ldap.py": 17455983150181177257, - "frontend/src/lib/stores/__tests__/mocks/navigation.js": 10782992736745205981, - "frontend/src/lib/components/translate/__tests__/test_bulk_replace_modal.svelte.js": 6802604718120565742, - "backend/src/scripts/init_auth_db.py": 7729644769215024318, - "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/json.py": 6429651405841289922, - "venv/lib/python3.13/site-packages/apscheduler/executors/asyncio.py": 2230610508870117058, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/shape.pyi": 17197248408326188931, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_validate_kwargs.py": 10173906126211473463, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_indexers.py": 6773751569163287047, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/np_datetime.pyi": 2884703536776830012, - "venv/lib/python3.13/site-packages/pandas/tests/extension/test_categorical.py": 1329115818264527103, - "venv/lib/python3.13/site-packages/pandas/core/computation/check.py": 16048939716341154196, - "venv/lib/python3.13/site-packages/sqlalchemy/connectors/asyncio.py": 4200928269459447593, - "venv/lib/python3.13/site-packages/yaml/parser.py": 18021832376874455801, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py": 2285778903282667561, - "venv/lib/python3.13/site-packages/numpy/ma/mrecords.py": 687170905107114309, - "venv/lib/python3.13/site-packages/pandas/core/indexes/period.py": 1181348384346239378, - "venv/lib/python3.13/site-packages/idna-3.11.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/urllib3/util/connection.py": 15511685694589961094, - "venv/lib/python3.13/site-packages/pygments/lexers/__init__.py": 4627540245914849670, - "venv/lib/python3.13/site-packages/packaging/utils.py": 3469957615920103670, - "frontend/src/types/backup.ts": 1741234034896689348, - "frontend/src/lib/stores/__tests__/sidebar.test.js": 5168138864675110669, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_validate_inclusive.py": 1389676378885557519, - "venv/lib/python3.13/site-packages/pip/_internal/main.py": 13711402795965346761, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_palettes.py": 13495896173068490516, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_functions.py": 16376319335326812580, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_overlaps.py": 8287716909793575347, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_indexing.py": 546024765756472196, - "venv/lib/python3.13/site-packages/pygments/formatters/latex.py": 7514533384468139434, - "venv/lib/python3.13/site-packages/keyring/compat/py312.py": 17581691618899404491, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/elements.py": 11185011314498348980, - "venv/lib/python3.13/site-packages/certifi/cacert.pem": 14727832973577663826, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_histograms.py": 47631283818191049, - "frontend/src/lib/components/translate/ScheduleConfig.svelte": 7232384043845640258, - "frontend/src/lib/components/translate/TranslationRunGlobalIndicator.svelte": 2809909710891941374, - "frontend/src/components/tasks/TaskResultPanel.svelte": 193489346212840974, - "venv/lib/python3.13/site-packages/jsonschema/benchmarks/nested_schemas.py": 4114645644496880787, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_regression.py": 6742646011852052645, - "venv/lib/python3.13/site-packages/pandas/core/groupby/groupby.py": 5536922766239875007, - "venv/lib/python3.13/site-packages/pandas/tests/frame/test_block_internals.py": 17935662848672401507, - "venv/lib/python3.13/site-packages/pandas/tests/io/xml/test_to_xml.py": 8477770807916990981, - "venv/lib/python3.13/site-packages/sqlalchemy/util/concurrency.py": 1325673503812009695, - "venv/lib/python3.13/site-packages/dateutil/tz/__init__.py": 9617254832690762236, - "backend/src/api/routes/reports.py": 13330739376983048757, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Postfix.pyi": 2700568594438441246, - "backend/src/plugins/translate/__tests__/test_clickhouse_insert_integration.py": 9290595122369575824, - "backend/src/plugins/translate/events.py": 14264965160929343917, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_abc.py": 10164280045317807365, - "venv/lib/python3.13/site-packages/pandas/core/arrays/string_arrow.py": 16997970607998635570, - "backend/tests/test_smoke_app.py": 7819901244710945164, - "venv/lib/python3.13/site-packages/ecdsa/test_numbertheory.py": 2117130219401053225, - "venv/lib/python3.13/site-packages/passlib/tests/test_ext_django.py": 3210697476292094384, - "venv/lib/python3.13/site-packages/pandas/core/ops/mask_ops.py": 16152832446926420035, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_real.py": 5283415082530228716, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_indexing.py": 17106010337164216722, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_character/foo77.f": 4046503346951676582, - "venv/lib/python3.13/site-packages/pandas/tests/extension/date/__init__.py": 16789767433439616593, - "venv/lib/python3.13/site-packages/pandas/io/parsers/arrow_parser_wrapper.py": 11745253171984606573, - "venv/lib/python3.13/site-packages/jeepney/tests/test_bus_messages.py": 4323093138307306385, - "frontend/src/lib/stores/health.js": 3437957055218721782, - "backend/src/plugins/mapper.py": 1970475023222782681, - "venv/lib/python3.13/site-packages/_pytest/deprecated.py": 5452408732344157294, - "venv/lib/python3.13/site-packages/numpy/lib/__init__.py": 12211229657513986410, - "venv/lib/python3.13/site-packages/yaml/resolver.py": 6669674521776449098, - "venv/lib/python3.13/site-packages/jeepney/bus.py": 5163075760432139100, - "venv/lib/python3.13/site-packages/apscheduler/__init__.py": 8652514118183227412, - "venv/lib/python3.13/site-packages/numpy/_utils/__init__.pyi": 13530522898409268883, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/common_with_division.f": 13517753924061311074, - "venv/lib/python3.13/site-packages/apscheduler/triggers/cron/fields.py": 8923326131649837922, - "venv/lib/python3.13/site-packages/passlib/__init__.py": 6987142392711441525, - "venv/lib/python3.13/site-packages/pandas/tests/internals/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/dictionary.py": 9599439750713575741, - "venv/lib/python3.13/site-packages/pygments/lexers/hare.py": 8693002231020751711, - "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_regression.py": 16266600037241221681, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_astype.py": 4178231007649608531, - "venv/lib/python3.13/site-packages/numpy/_pyinstaller/hook-numpy.pyi": 14233146512272397376, - "venv/lib/python3.13/site-packages/secretstorage/collection.py": 17903258621904912493, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/nditer.pyi": 2351219156711800159, - "venv/lib/python3.13/site-packages/pandas/io/parsers/python_parser.py": 7297059565749331767, - "venv/lib/python3.13/site-packages/cryptography/x509/certificate_transparency.py": 11337143186866439208, - "venv/lib/python3.13/site-packages/passlib/tests/backports.py": 17098448557554464521, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/query.py": 3867868366067025348, - "frontend/src/lib/components/layout/Sidebar.svelte": 15884718235284801177, - "venv/lib/python3.13/site-packages/pygments/lexers/foxpro.py": 14032656072384708293, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_liboffsets.py": 14705822637347212540, - "venv/lib/python3.13/site-packages/anyio/_core/_streams.py": 6436461519284884601, - "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/licenses/LICENSE": 11199044866758471950, - "venv/lib/python3.13/site-packages/bcrypt/__about__.py": 11196369962175692786, - "frontend/src/lib/components/translate/BulkCorrectionSidebar.svelte": 17772864954770752590, - "frontend/build/_app/immutable/nodes/26.Bv0TZ1Py.js": 12032769457852764082, - "venv/lib/python3.13/site-packages/authlib/oauth2/__init__.py": 13682931764896414722, - "backend/src/api/routes/plugins.py": 2062850550409490465, - "frontend/src/routes/storage/backups/+page.svelte": 3437582895363203695, - "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/METADATA": 9041650844081036822, - "venv/lib/python3.13/site-packages/cryptography/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/_libs/tslib.pyi": 16170606956605100142, - "venv/lib/python3.13/site-packages/_pytest/debugging.py": 16135285365306979060, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/infer.py": 12541904372617915365, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/declarative/extensions.py": 217122171232375592, - "backend/src/models/__tests__/test_models.py": 15975002719327784362, - "venv/lib/python3.13/site-packages/numpy/_core/_dtype_ctypes.py": 5564496700647900501, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timedeltas.cpython-313-x86_64-linux-gnu.so": 17089839649971421164, - "venv/lib/python3.13/site-packages/pandas/tests/resample/test_datetime_index.py": 15448494560143263150, - "venv/lib/python3.13/site-packages/pygments/lexers/elm.py": 14683437310498104916, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_any_index.py": 10800968419286329268, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_ops.py": 16346030307660048773, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/polynomial_series.pyi": 7112486730197067087, - "venv/lib/python3.13/site-packages/_pytest/stepwise.py": 3527630676691417317, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_scalar.py": 2434773821902319678, - "venv/lib/python3.13/site-packages/pycparser/ply/lex.py": 3393278208465358140, - "venv/lib/python3.13/site-packages/numpy/random/lib/libnpyrandom.a": 1802449692091040396, - "venv/lib/python3.13/site-packages/pandas/tests/interchange/test_utils.py": 2329717399120006435, - "venv/lib/python3.13/site-packages/starlette/testclient.py": 16088848330558215194, - "venv/lib/python3.13/site-packages/PIL/__init__.py": 10393984345353752724, - "venv/lib/python3.13/site-packages/pygments/lexers/erlang.py": 11757064262773184741, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/aead.py": 8740476942925346444, - "venv/lib/python3.13/site-packages/pydantic/_internal/_dataclasses.py": 15256193972947054890, - "venv/lib/python3.13/site-packages/pip/_internal/operations/install/__init__.py": 10456333337205488584, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/text.py": 6096215585499446070, - "venv/lib/python3.13/site-packages/PIL/ImageText.py": 15322496455608242001, - "venv/lib/python3.13/site-packages/numpy/tests/test_reloading.py": 17532116685329953400, - "backend/src/services/dataset_review/orchestrator.py": 8780497744077644822, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_logical/foo90.f90": 12508663906609241323, "venv/lib/python3.13/site-packages/starlette/requests.py": 4582424111350387010, - "venv/lib/python3.13/site-packages/pydantic/experimental/missing_sentinel.py": 1637241331832367709, - "venv/lib/python3.13/site-packages/numpy/lib/scimath.py": 7616377937400918724, - "venv/lib/python3.13/site-packages/websockets/legacy/auth.py": 16685128086396181286, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/response.py": 15004896908941411698, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_object.py": 9215781825358309147, - "venv/lib/python3.13/site-packages/pygments/styles/igor.py": 2291640843781128270, - "venv/lib/python3.13/site-packages/pandas/core/arrays/string_.py": 14865733321317939478, - "frontend/src/routes/profile/+page.svelte": 17979719303737794427, - "venv/lib/python3.13/site-packages/pip/_internal/utils/egg_link.py": 18211947508793805393, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ufunc_config.pyi": 2005481648919369399, - "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/licenses/numpy/_core/src/common/pythoncapi-compat/COPYING": 3474104874623144155, - "venv/lib/python3.13/site-packages/pygments/styles/solarized.py": 11231143047572616298, - "venv/lib/python3.13/site-packages/authlib/jose/rfc7516/models.py": 5656065513234782067, - "venv/lib/python3.13/site-packages/PIL/_webp.pyi": 18222325750818585549, - "venv/lib/python3.13/site-packages/pygments/lexers/_mysql_builtins.py": 16087227298679432006, - "venv/lib/python3.13/site-packages/pandas/tseries/__init__.py": 2648641118117738895, - "logs/app.log.1": 15453131390328548426, - "venv/lib/python3.13/site-packages/tzlocal/unix.py": 995019116408448073, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/decl_base.py": 8678387511873412922, - "venv/lib/python3.13/site-packages/greenlet/greenlet_exceptions.hpp": 8819301638282185623, - "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pandas/tests/io/sas/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_round.py": 9358309072340573675, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_ordered.py": 13827016781743929164, - "venv/lib/python3.13/site-packages/pip/_internal/distributions/sdist.py": 9490366272695301385, - "venv/lib/python3.13/site-packages/pip/_vendor/certifi/core.py": 2300277198409164253, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/random.py": 12722750410117529986, - "venv/lib/python3.13/site-packages/numpy/_core/_dtype.py": 7796703383405149774, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_dict.py": 808180749933361743, - "venv/lib/python3.13/site-packages/httpcore/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/pygments/lexers/verifpal.py": 9190813166686923928, - "venv/lib/python3.13/site-packages/websockets/streams.py": 15786581670248099415, - "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/test_observance.py": 18178414966262524534, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_twodim_base.py": 17623133613018058380, - "venv/lib/python3.13/site-packages/httpcore/_async/connection_pool.py": 932468678264370661, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/foo.f": 6349567809926050379, - "venv/lib/python3.13/site-packages/pygments/styles/trac.py": 13968346768164381833, - "venv/lib/python3.13/site-packages/pygments/lexers/whiley.py": 218468511185731869, - "frontend/src/lib/stores/environmentContext.js": 1762535408632658714, - "venv/lib/python3.13/site-packages/git/objects/submodule/util.py": 8621897576747443758, - "venv/lib/python3.13/site-packages/dateutil/easter.py": 14631233890122048084, - "frontend/src/routes/datasets/review/[id]/__tests__/dataset_review_workspace.ux.test.js": 4105775024258653726, - "backend/src/plugins/translate/dictionary.py": 4920392475082272360, - "backend/tests/services/clean_release/test_report_audit_immutability.py": 11385235469772417017, - "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/RECORD": 4857922668040624663, - "venv/lib/python3.13/site-packages/packaging/_musllinux.py": 2568338440457080365, - "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_bool.py": 433342705586552936, - "venv/lib/python3.13/site-packages/websockets/extensions/__init__.py": 13067201313921664210, - "backend/src/plugins/llm_analysis/__tests__/test_client_headers.py": 8746149613497088008, - "venv/lib/python3.13/site-packages/pip/_internal/index/package_finder.py": 12631116673181479779, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/totp.py": 11888568940387752607, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/vector.py": 9941185556118343263, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_api.py": 6439927798101672423, - "venv/lib/python3.13/site-packages/fastapi/_compat/v1.py": 9776181748061371513, - "run.sh": 4808871544409245302, - "venv/lib/python3.13/site-packages/pygments/lexers/smalltalk.py": 6113431347120223592, - "venv/lib/python3.13/site-packages/numpy/__init__.py": 2331213407497901634, - "venv/lib/python3.13/site-packages/pandas/tests/generic/test_frame.py": 9378623452601244234, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_dtypes.py": 4985057130807802395, - "backend/tests/test_dashboards_api.py": 2278794209397518464, - "venv/lib/python3.13/site-packages/authlib/jose/util.py": 14843923624043422219, - "venv/lib/python3.13/site-packages/websockets/asyncio/compatibility.py": 9716993942839407921, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_base.py": 16710163879060522196, - "venv/lib/python3.13/site-packages/PIL/_binary.py": 14039167720582222043, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_complex.py": 4290059580478881700, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_regression.py": 16293647824734937323, - "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/wrapper.py": 4633931060151989960, - "venv/lib/python3.13/site-packages/numpy/fft/_pocketfft.py": 6142062990691556076, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/comparisons.pyi": 4158883009780474220, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_strptime.py": 14328510310436552635, - "venv/lib/python3.13/site-packages/pandas/io/formats/excel.py": 797619981868084011, - "venv/lib/python3.13/site-packages/pygments/styles/emacs.py": 4540417536507245024, - "venv/lib/python3.13/site-packages/PIL/ImageOps.py": 3884310863548175682, - "venv/lib/python3.13/site-packages/pygments/lexers/dalvik.py": 5549983892200382076, - "venv/lib/python3.13/site-packages/apscheduler/jobstores/redis.py": 11313520438600819390, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/modules.pyi": 131293606732000587, - "venv/lib/python3.13/site-packages/apscheduler/executors/pool.py": 10344126569001032241, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Levenshtein_py.py": 11350239659973489646, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7662/token_validator.py": 13917237710752731397, - "venv/lib/python3.13/site-packages/pip/_internal/cli/cmdoptions.py": 12107733000019393631, - "venv/lib/python3.13/site-packages/cryptography/x509/ocsp.py": 17973800752927680404, - "venv/lib/python3.13/site-packages/pandas/tests/extension/array_with_attr/__init__.py": 15991623390785635773, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_combine_concat.py": 3201068805128232483, - "venv/lib/python3.13/site-packages/greenlet/PyModule.cpp": 3464405102256525511, - "venv/lib/python3.13/site-packages/numpy/core/multiarray.py": 18368443598659711836, - "venv/lib/python3.13/site-packages/sqlalchemy/inspection.py": 8563914460959280101, - "venv/lib/python3.13/site-packages/pygments/lexers/shell.py": 3842338968014057744, - "frontend/src/components/PasswordPrompt.svelte": 14457083234480062595, - "frontend/src/components/TaskList.svelte": 13727273029173605651, - "frontend/src/components/EnvSelector.svelte": 3937920133889630193, - "venv/lib/python3.13/site-packages/starlette/routing.py": 16063812008141874150, - "backend/src/services/__tests__/test_llm_prompt_templates.py": 17912427710321938757, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/_asymmetric.py": 6903778603779934572, - "backend/tests/services/clean_release/test_approval_service.py": 8359796700245201345, - "venv/lib/python3.13/site-packages/apscheduler/triggers/date.py": 9545774812177853725, - "venv/lib/python3.13/site-packages/greenlet/tests/fail_switch_three_greenlets2.py": 11100501798741918610, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_constructors.py": 16197656939536784142, - "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/RECORD": 3283324710045856319, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ma.pyi": 6467636381637286867, - "venv/lib/python3.13/site-packages/pandas/tests/extension/uuid/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/live.py": 8586058300967410643, - "venv/lib/python3.13/site-packages/pillow.libs/liblzma-61b1002e.so.5.8.2": 11405063484938458842, - "venv/lib/python3.13/site-packages/pydantic/version.py": 3699343596421001359, - "venv/lib/python3.13/site-packages/numpy/core/fromnumeric.py": 2136433423902202138, - "venv/lib/python3.13/site-packages/numpy/_core/_methods.py": 16805668769357938792, - "venv/lib/python3.13/site-packages/fastapi/__init__.py": 11054203163075417954, - "venv/lib/python3.13/site-packages/packaging/_elffile.py": 9546225990833172494, - "venv/lib/python3.13/site-packages/anyio/_core/_contextmanagers.py": 1258491775823080427, - "frontend/src/components/DynamicForm.svelte": 12658559315809907844, + "venv/lib/python3.13/site-packages/sqlalchemy/connectors/aioodbc.py": 2018840826888846321, + "venv/lib/python3.13/site-packages/pygments/styles/xcode.py": 14124729477757765388, + "backend/src/services/notifications/providers.py": 5844970860921555598, "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_common.py": 2265736743367879051, - "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_odswriter.py": 5134385709728943904, - "frontend/src/lib/components/dataset-review/CompiledSQLPreview.svelte": 14788599185006992431, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/__init__.py": 15130871412783076140, - "backend/src/plugins/translate/orchestrator.py": 6507658261488157100, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_category.py": 3944898528119364040, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_arithmetic.py": 17427754419626622853, + "backend/src/plugins/translate/sql_generator.py": 900251269659959067, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust.abi3.so": 2537152913308578487, + "venv/lib/python3.13/site-packages/numpy/random/_examples/numba/extending_distributions.py": 209769687436320665, + "venv/lib/python3.13/site-packages/pydantic/plugin/__init__.py": 16803973597469216174, + "venv/lib/python3.13/site-packages/passlib/ext/__init__.py": 15240312484046875203, + "venv/lib/python3.13/site-packages/passlib/crypto/_md4.py": 379335134151814809, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/__init__.py": 697601512486720554, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_concat.py": 10207975532842927134, + "venv/lib/python3.13/site-packages/_pytest/fixtures.py": 1442767529726551116, + "venv/lib/python3.13/site-packages/pip/_internal/commands/list.py": 13035925292530461722, + "venv/lib/python3.13/site-packages/cryptography/hazmat/backends/__init__.py": 18019562641348687547, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/descriptor_props.py": 7044852927393650707, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libkrb5-fcafa220.so.3.3": 5620628698712971642, + "frontend/src/components/backups/BackupList.svelte": 14706629542956875064, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_nonunique_indexes.py": 15468078561381939405, + "frontend/build/_app/immutable/chunks/Bn0PWGM8.js": 3309051336151057391, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/errors.py": 2147619440137604907, + "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/__init__.py": 2246704695838251846, + "venv/lib/python3.13/site-packages/numpy/_core/_simd.cpython-313-x86_64-linux-gnu.so": 12609666221355091416, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/halffloat.h": 16913381775994652343, + "venv/lib/python3.13/site-packages/pygments/styles/monokai.py": 12381683616720952636, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi": 17458894097215345719, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_constructors.py": 6440526981746306640, + "venv/lib/python3.13/site-packages/pandas/core/flags.py": 4690844374637430013, + "venv/lib/python3.13/site-packages/pygments/styles/rainbow_dash.py": 12236746494379891602, + "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/licenses/LICENSE": 14610443802372107702, + "venv/lib/python3.13/site-packages/urllib3/contrib/pyopenssl.py": 11839715589709210412, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc_aix.h": 13316272821279308986, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/test_arrow_compat.py": 17818294260710640443, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Prefix.pyi": 2700568594438441246, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_iteration.py": 15247340610412364508, + "venv/lib/python3.13/site-packages/pygments/lexers/d.py": 18120801227801223376, + "venv/lib/python3.13/site-packages/anyio/abc/_resources.py": 8330687082933028623, + "frontend/src/lib/components/dataset-review/__tests__/validation_findings_panel.ux.test.js": 8994492423600483794, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_take.py": 3281943380342853080, + "backend/src/scripts/create_admin.py": 11258281301071059367, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/parameters.py": 12219404446503940066, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_datetime.py": 7456744745424297814, + "venv/lib/python3.13/site-packages/pip/_vendor/msgpack/fallback.py": 4260636031748548345, + "venv/lib/python3.13/site-packages/authlib/oidc/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/_tsql_builtins.py": 829193091321611630, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_dtypes.py": 4985057130807802395, "venv/lib/python3.13/site-packages/jose/backends/cryptography_backend.py": 2875270748627611635, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/plugin.py": 16042078006552521613, - "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_structures.py": 16375837477294135345, - "venv/lib/python3.13/site-packages/cffi/vengine_gen.py": 6582900613549620556, - "venv/lib/python3.13/site-packages/greenlet/TGreenletGlobals.cpp": 17400526028982558937, - "venv/lib/python3.13/site-packages/_pytest/mark/__init__.py": 9343015549300275714, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh25211.pyf": 10179237442840865225, - "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_astype.py": 4023134352880775277, - "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/RECORD": 13494296690334916257, - "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/implicit.py": 5400555157488808948, - "venv/lib/python3.13/site-packages/pydantic/functional_validators.py": 5835339743072048940, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test__iotools.py": 6260912730310453037, - "venv/lib/python3.13/site-packages/numpy/fft/_pocketfft.pyi": 1521384724035973688, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/persistence.py": 17908437937265384125, - "venv/lib/python3.13/site-packages/urllib3/filepost.py": 3814725067598042920, - "venv/lib/python3.13/site-packages/urllib3/__init__.py": 1854778274753799815, - "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/RECORD": 18294169019244826301, - "frontend/src/lib/stores/translationRun.js": 4974041543031230789, - "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/py.typed": 15130871412783076140, - "backend/src/services/dataset_review/event_logger.py": 8083530196989488371, - "venv/lib/python3.13/site-packages/pygments/lexers/textedit.py": 14872291021083255414, - "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/wsproto_impl.py": 3072714633272661847, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_update.py": 12293424926920594944, - "venv/lib/python3.13/site-packages/PIL/PcxImagePlugin.py": 1691405132377495336, - "venv/lib/python3.13/site-packages/anyio/streams/stapled.py": 9733127303038397237, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/dtypes.cpython-313-x86_64-linux-gnu.so": 657138415665704488, - "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_construct_ndarray.py": 6863874753941288585, - "venv/lib/python3.13/site-packages/greenlet/tests/_test_extension.c": 9318655518998644962, - "venv/lib/python3.13/site-packages/numpy/__init__.pyi": 15820654525292895396, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_array_coercion.py": 3551994404797560483, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_constructors.py": 12118296571694122743, - "venv/lib/python3.13/site-packages/pandas/core/util/numba_.py": 3535857372998653863, - "venv/lib/python3.13/site-packages/PIL/_imaging.cpython-313-x86_64-linux-gnu.so": 1425926264271197601, - "venv/lib/python3.13/site-packages/jsonschema_specifications/tests/test_jsonschema_specifications.py": 12693210357718751894, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/naming.py": 14503932124671438340, - "frontend/src/routes/dashboards/[id]/components/DashboardHeader.svelte": 5572977872383619871, - "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_chaining_and_caching.py": 11784574265874608859, - "venv/lib/python3.13/site-packages/numpy/_globals.py": 8736093011628027941, - "venv/lib/python3.13/site-packages/pandas/compat/pyarrow.py": 13693379998785127538, - "venv/lib/python3.13/site-packages/uvicorn/middleware/asgi2.py": 5355343704425405009, - "backend/src/core/utils/fileio.py": 5783081738676692209, - "backend/tests/scripts/test_clean_release_cli.py": 13978106497618764226, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/mod.py": 2054876501774773382, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_reindex.py": 16348619731902304844, - "venv/lib/python3.13/site-packages/pandas/io/formats/css.py": 11288129826285529457, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/array_api_info.pyi": 7887489694871300469, - "venv/lib/python3.13/site-packages/numpy/lib/_twodim_base_impl.pyi": 15509415708991012734, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/six.py": 1161259802604920635, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/fromnumeric.pyi": 8671744104085911610, - "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/METADATA": 7214286447912789067, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py": 18229054440703471493, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/mock.py": 14010776046610111182, - "venv/lib/python3.13/site-packages/pydantic/validators.py": 8292819706488319845, - "venv/lib/python3.13/site-packages/pip/_internal/utils/hashes.py": 17310331665429541812, - "docker/backend.entrypoint.sh": 16079402358996730188, - "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/METADATA": 15876173086384975957, - "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/__init__.py": 5027355874704300832, - "venv/lib/python3.13/site-packages/pygments/lexers/data.py": 9365024327689738663, - "venv/lib/python3.13/site-packages/numpy/polynomial/hermite.py": 6617116180936986481, - "venv/lib/python3.13/site-packages/pygments/lexers/gdscript.py": 12775803139854052838, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/__init__.py": 7062045842767697645, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/processors.py": 4817637175145012571, - "backend/src/api/routes/__tests__/test_datasets.py": 14530822388357135497, + "venv/lib/python3.13/site-packages/pygments/lexers/_asy_builtins.py": 1665617945818958781, + "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/packaging/specifiers.py": 2800449842990194437, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/rsa.py": 5392922995791487437, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/pcg64-testset-1.csv": 16078879475693341404, + "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/WHEEL": 16223367184831500348, + "venv/lib/python3.13/site-packages/pandas/core/indexes/api.py": 13750417165301467381, + "venv/lib/python3.13/site-packages/jaraco/classes/properties.py": 6976928770004942858, + "venv/lib/python3.13/site-packages/attr/_compat.py": 4016698632899969390, + "venv/lib/python3.13/site-packages/passlib/tests/test_utils_handlers.py": 556092537518149789, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_factorize.py": 861897342553280506, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_groupby_dropna.py": 3620959914439334511, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_rename.py": 4890779493250245903, + "venv/lib/python3.13/site-packages/requests/__init__.py": 2604491098990438229, + "venv/lib/python3.13/site-packages/PIL/PixarImagePlugin.py": 12061717157676819554, + "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/fft/__init__.pyi": 10354990065626209946, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/measure.py": 18327922742808984428, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_raises.py": 9678418010293693275, + "backend/src/api/routes/translate/_dictionary_routes.py": 5480137091791704976, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_period.py": 7727606662611927293, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_cell_widths.py": 5724379364993806351, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/boxplot.py": 7090586111222952862, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth1/authorization_server.py": 724388765673082720, + "venv/lib/python3.13/site-packages/pip/_internal/vcs/__init__.py": 1197579313959289096, + "venv/lib/python3.13/site-packages/pandas/tests/resample/test_timedelta.py": 17052461094717926538, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_pct_change.py": 10313980265441433173, "venv/lib/python3.13/site-packages/sqlalchemy/testing/fixtures/__init__.py": 6654484356035994666, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/shape.py": 13410030168622484801, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh22648.pyf": 15094859289098837494, - "venv/lib/python3.13/site-packages/pandas/tests/test_col.py": 14781555956509956371, - "venv/lib/python3.13/site-packages/numpy/ma/tests/test_old_ma.py": 18116460138221458026, - "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_readers.py": 2136472634307645842, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_cte.py": 9274103293201050794, - "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/__init__.py": 16416360448593978041, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi": 18143251582901996072, - "venv/lib/python3.13/site-packages/numpy/_core/cversions.py": 9724467539503410469, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_to_timestamp.py": 17572983799929378095, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/__init__.py": 237070345717942041, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/__init__.py": 8807410829260170597, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/timedeltas/test_constructors.py": 17219764538851313252, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/pg_catalog.py": 15726448028057859831, - "venv/lib/python3.13/site-packages/uvicorn/protocols/utils.py": 6147480543268737542, - "venv/lib/python3.13/site-packages/pytest_asyncio/py.typed": 15130871412783076140, - "venv/bin/normalizer": 10972303485259566838, - "venv/lib/python3.13/site-packages/pandas/util/_validators.py": 10201915528715968165, - "backend/src/models/auth.py": 14495240302739907540, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc_macosx.h": 3653129798702716366, - "backend/alembic/versions/8dd0a93af539_drop_deprecated_source_language_column_.py": 3574830605384611294, - "venv/lib/python3.13/site-packages/multipart/exceptions.py": 8580522204565816132, - "venv/lib/python3.13/site-packages/pip/_vendor/distlib/manifest.py": 16998683210177680416, - "venv/lib/python3.13/site-packages/numpy/_core/_asarray.pyi": 3940937389249665490, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/types.py": 12298292195929126306, - "venv/lib/python3.13/site-packages/authlib/integrations/base_client/__init__.py": 1856412417337875350, - "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/content": 16257335642847993866, - "venv/lib/python3.13/site-packages/pandas/core/sample.py": 11913688303879867963, - "venv/lib/python3.13/site-packages/pandas/core/col.py": 4221271461723119525, - "venv/lib/python3.13/site-packages/pygments/lexers/_csound_builtins.py": 14530739818491398298, - "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/METADATA": 14126682758562488244, - "venv/lib/python3.13/site-packages/ecdsa/test_ecdsa.py": 17595025859105321562, - "venv/lib/python3.13/site-packages/pillow.libs/libzstd-761a17b6.so.1.5.7": 17875236748140157032, - "venv/lib/python3.13/site-packages/pip/_internal/utils/_log.py": 13011536737418204268, - "venv/lib/python3.13/site-packages/numpy/_core/lib/npy-pkg-config/npymath.ini": 1071371630695963467, - "venv/lib/python3.13/site-packages/pandas/io/formats/templates/latex_table.tpl": 8706980975714184465, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py": 10843527833285941674, - "venv/lib/python3.13/site-packages/numpy/tests/test__all__.py": 10689265249302359388, - "venv/lib/python3.13/site-packages/greenlet/greenlet_thread_support.hpp": 7773996382104849037, - "venv/lib/python3.13/site-packages/cffi/__init__.py": 13427266446746498548, - "venv/lib/python3.13/site-packages/passlib/apache.py": 2357244937629445006, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/printing.py": 8681519649669408795, - "venv/lib/python3.13/site-packages/pandas/api/interchange/__init__.py": 181911269762882069, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_reshape.py": 10576230966727810832, - "venv/lib/python3.13/site-packages/sqlalchemy/orm/state_changes.py": 11425629729736599320, - "venv/lib/python3.13/site-packages/passlib/tests/test_crypto_digest.py": 7988469818593853943, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/mysqldb.py": 2138006249556140141, - "venv/lib/python3.13/site-packages/numpy/lib/tests/test_format.py": 5231304674283854194, - "venv/lib/python3.13/site-packages/pygments/lexers/oberon.py": 15220143799603451830, - "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/signature.py": 3614545297259098566, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_period.py": 10309599277099605395, - "backend/src/api/routes/git/_merge_routes.py": 2019590524299108754, - "backend/src/services/reports/__tests__/test_type_profiles.py": 17262223295445285285, - "venv/lib/python3.13/site-packages/numpy/typing/tests/test_runtime.py": 12534621594420844770, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/arithmetic.pyi": 10265823318697442495, - "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/nattype.cpython-313-x86_64-linux-gnu.so": 2311012065007769341, - "backend/src/services/auth_service.py": 17642785219801916280, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/provision.py": 8707620698060818654, - "venv/lib/python3.13/site-packages/numpy/polynomial/hermite.pyi": 3573629536236108391, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_arithmetic.py": 17449383588949771785, - "backend/src/schemas/auth.py": 339608158310320944, - "venv/lib/python3.13/site-packages/pandas/tests/libs/test_lib.py": 6721066015604956469, - "venv/lib/python3.13/site-packages/anyio/streams/file.py": 15232345967545920863, - "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_polyutils.py": 675639791972685455, - "venv/lib/python3.13/site-packages/greenlet/tests/leakcheck.py": 10179165748538820557, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/value_attrspec/gh21665.f90": 11945588837236968550, - "venv/lib/python3.13/site-packages/passlib/context.py": 10362592407593745107, - "venv/lib/python3.13/site-packages/websockets/sync/router.py": 10335595955948355382, - "venv/lib/python3.13/site-packages/pandas/tests/window/test_ewm.py": 13789842684407148830, - "venv/lib/python3.13/site-packages/pip/_internal/cli/spinners.py": 7664056305019151182, - "venv/lib/python3.13/site-packages/sqlalchemy/testing/exclusions.py": 15852672500377116297, - "venv/lib/python3.13/site-packages/PIL/MpoImagePlugin.py": 644327650732339301, - "venv/lib/python3.13/site-packages/pygments/lexers/math.py": 1647133236144311250, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_replace.py": 8784361132920473760, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_cut.py": 7800833253841657380, - "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/__init__.py": 11283047029180207588, - "venv/lib/python3.13/site-packages/_pytest/monkeypatch.py": 2635774457434545573, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_to_numpy.py": 14845850776625255794, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_compare.py": 171535659467902751, - "venv/lib/python3.13/site-packages/sqlalchemy/sql/ddl.py": 5006132143435653867, - "frontend/src/lib/components/translate/TranslationMetricsDashboard.svelte": 7320072998752693628, - "backend/src/services/git/_base.py": 17717982195967253756, - "venv/lib/python3.13/site-packages/numpy/fft/_helper.pyi": 9546918341939102632, - "venv/lib/python3.13/site-packages/anyio/streams/text.py": 12507591237176920223, - "venv/lib/python3.13/site-packages/packaging/tags.py": 1999137344425005827, - "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/licenses/LICENSE": 13322127602069971876, - "venv/lib/python3.13/site-packages/fastapi/__main__.py": 10755252341326986079, - "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py": 8079219938116542284, - "venv/lib/python3.13/site-packages/pandas/core/methods/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/core/dtypes/concat.py": 14737724508458849795, - "venv/lib/python3.13/site-packages/pandas/io/sas/sas_xport.py": 16525568795475918874, - "venv/lib/python3.13/site-packages/_cffi_backend.cpython-313-x86_64-linux-gnu.so": 829323374625147265, - "venv/lib/python3.13/site-packages/pygments/lexers/roboconf.py": 1335271183806115470, - "venv/lib/python3.13/site-packages/pygments/lexers/webidl.py": 8097260111959278117, - "venv/lib/python3.13/site-packages/pydantic/_internal/_generics.py": 16438159042939059717, - "venv/lib/python3.13/site-packages/PIL/ImageDraw.py": 12919304671639673744, - "venv/lib/python3.13/site-packages/pandas/core/arraylike.py": 11439602957100017713, - "venv/lib/python3.13/site-packages/pytest_asyncio/plugin.py": 13414541723622354359, - "venv/lib/python3.13/site-packages/rpds/rpds.cpython-313-x86_64-linux-gnu.so": 561326734302492859, - "backend/src/services/__tests__/test_resource_service.py": 14435810729789665272, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_timedelta.py": 12822713918114715561, - "venv/lib/python3.13/site-packages/PIL/JpegImagePlugin.py": 10621639599916796689, - "venv/lib/python3.13/site-packages/greenlet/platform/switch_riscv_unix.h": 8607317131295864391, - "venv/lib/python3.13/site-packages/h11/_connection.py": 6971907312737339559, - "venv/lib/python3.13/site-packages/gitdb/__init__.py": 15381521749253695923, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_combine.py": 16193618152415684421, - "venv/lib/python3.13/site-packages/jaraco/classes/py.typed": 15130871412783076140, - "frontend/src/routes/translate/[id]/+page.svelte": 4980328348725787681, - "venv/lib/python3.13/site-packages/numpy/tests/test_lazyloading.py": 17465787495701737655, - "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/METADATA": 1684451390569121499, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_constructors.py": 1697610790604400105, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_numba.py": 18148155441554555618, - "backend/src/core/utils/superset_context_extractor/_parsing.py": 17093712504833649098, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_windows_renderer.py": 1076842273781909680, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_astype.py": 11995587502880659624, + "venv/lib/python3.13/site-packages/pandas/core/reshape/concat.py": 2218848863149192467, + "venv/lib/python3.13/site-packages/pygments/lexers/_mql_builtins.py": 2376212048709413586, + "venv/lib/python3.13/site-packages/pandas/core/internals/api.py": 14632030170441237968, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/py.typed": 15130871412783076140, "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_meson.pyi": 2101854548535776875, - "venv/lib/python3.13/site-packages/jsonschema/protocols.py": 847332300543820834, - "venv/lib/python3.13/site-packages/_yaml/__init__.py": 3395829783751571350, - "frontend/src/routes/+layout.svelte": 2748734957389681795, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_unary.py": 7128845965976836334, - "venv/lib/python3.13/site-packages/sqlalchemy/engine/default.py": 7906029092844029127, - "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/RECORD": 11111103193315065053, - "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi": 2777456956260371820, - "venv/lib/python3.13/site-packages/pydantic/datetime_parse.py": 196119761382305574, - "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/__multiarray_api.h": 16138871162713175599, - "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/candidates.py": 5951912505933673835, - "venv/lib/python3.13/site-packages/numpy/_core/_simd.pyi": 13553578040186939249, - "venv/lib/python3.13/site-packages/pillow.libs/libxcb-64009ff3.so.1.1.0": 7322566017497612912, - "venv/lib/python3.13/site-packages/jeepney/tests/test_low_level.py": 3579024079580951234, - "venv/lib/python3.13/site-packages/rapidfuzz/utils.pyi": 7400725037332728015, - "venv/lib/python3.13/site-packages/_pytest/warnings.py": 8633394774569709013, - "venv/lib/python3.13/site-packages/pandas/tseries/holiday.py": 1134358927599488799, - "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_tzconversion.py": 7205566353454584259, - "venv/lib/python3.13/site-packages/pygments/lexers/webassembly.py": 17811970490091406867, - "venv/lib/python3.13/site-packages/numpy/testing/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/rapidfuzz/distance/Hamming_py.py": 16133454512601432815, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_filter.py": 2388681477981649268, - "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/array.py": 12658302948819867972, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_formats.py": 2743326139283991615, - "venv/lib/python3.13/site-packages/pandas/tests/util/test_show_versions.py": 10577134135855180356, - "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_invalid.py": 2762344062554575805, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_reindex.py": 15680924058960901067, - "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/LICENSE": 6581019367004699816, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_names.py": 10037208029090713121, - "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/pyproject.py": 12684373765476932108, - "backend/src/api/routes/health.py": 4494840518139445701, - "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_arithmetic.py": 976970179757115407, - "venv/lib/python3.13/site-packages/pip/_vendor/rich/_emoji_codes.py": 13901854719435716195, - "venv/lib/python3.13/site-packages/anyio/abc/__init__.py": 11428476337116642278, - "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_pipe.py": 8255917221095334212, - "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_arithmetic.py": 16829312896275893002, - "venv/lib/python3.13/site-packages/authlib/oauth1/client.py": 7314311571224771, - "venv/lib/python3.13/site-packages/pygments/lexers/php.py": 17636450950688953288, - "venv/lib/python3.13/site-packages/authlib/common/urls.py": 13627272625165922482, - "venv/lib/python3.13/site-packages/pytest/__main__.py": 8747175113746141224, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/nditer.pyi": 33523064068376750, - "venv/lib/python3.13/site-packages/cffi/parse_c_type.h": 9889309594462820068, - "venv/lib/python3.13/site-packages/websockets/server.py": 16599051743322571790, - "venv/lib/python3.13/site-packages/cryptography/fernet.py": 10137792148038140630, - "venv/lib/python3.13/site-packages/charset_normalizer/cli/__main__.py": 2148876100501691980, - "venv/lib/python3.13/site-packages/referencing/_core.py": 14744795336121513230, - "frontend/build/_app/immutable/assets/AssistantChatPanel.D4L5jlt0.css": 10724144138366384732, - "venv/lib/python3.13/site-packages/fastapi/websockets.py": 9910052969532075719, - "venv/lib/python3.13/site-packages/pip/_internal/metadata/_json.py": 6718137926109998531, - "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/INSTALLER": 17282701611721059870, - "venv/lib/python3.13/site-packages/numpy/_core/tests/test_mem_overlap.py": 4202716587079637350, - "venv/lib/python3.13/site-packages/pandas/tests/base/test_conversion.py": 7507083194555424923, - "venv/lib/python3.13/site-packages/git/diff.py": 6691274015605451565, - "venv/lib/python3.13/site-packages/pygments/lexers/haxe.py": 1915270891525929751, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_subclass.py": 12453252499544981915, - "venv/lib/python3.13/site-packages/pandas/tests/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/sqlalchemy/ext/compiler.py": 11662494978292401568, - "venv/lib/python3.13/site-packages/pygments/lexers/int_fiction.py": 8806961041808606055, - "venv/lib/python3.13/site-packages/fastapi/applications.py": 4975013401500183420, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/endpoint.py": 8519226897299461194, - "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/REQUESTED": 15130871412783076140, - "venv/lib/python3.13/site-packages/httpcore/_trace.py": 9576253018891841832, - "venv/lib/python3.13/site-packages/dotenv/py.typed": 9796674040111366709, - "venv/lib/python3.13/site-packages/uvicorn/py.typed": 15240312484046875203, - "venv/lib/python3.13/site-packages/pygments/lexers/objective.py": 10966124906622044512, - "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/_pytest/helpconfig.py": 3033477806668470951, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/datasource.pyi": 80441145804744162, - "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_misc.py": 16393451200513733144, - "venv/lib/python3.13/site-packages/h11/_headers.py": 7592730288271984585, - "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/utils.py": 617314300548081590, - "venv/lib/python3.13/site-packages/pandas/tests/resample/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/extension/base/ops.py": 13451323660386712332, - "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_parse_dates.py": 7597935994945336408, - "frontend/src/routes/+layout.ts": 8392799000856778740, - "venv/lib/python3.13/site-packages/sqlalchemy/connectors/pyodbc.py": 4146960201984489604, - "venv/lib/python3.13/site-packages/numpy/random/_examples/cython/meson.build": 17468163147765825687, - "frontend/src/routes/settings/+page.ts": 3719008513710037840, - "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_masked_matrix.py": 1444179242134203344, - "venv/lib/python3.13/site-packages/pip/_internal/operations/build/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_between.py": 337429135216881486, - "venv/lib/python3.13/site-packages/pandas/tests/io/excel/__init__.py": 15130871412783076140, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_rename_axis.py": 13193478481758930128, - "venv/lib/python3.13/site-packages/numpy/random/tests/data/sfc64-testset-1.csv": 5708101386018266998, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_isin.py": 14387850595546682314, + "venv/lib/python3.13/site-packages/greenlet/tests/fail_switch_three_greenlets2.py": 11100501798741918610, + "venv/lib/python3.13/site-packages/attr/setters.pyi": 6670509359139243236, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_tz_convert.py": 17957207974799052653, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/base.py": 3250660921154829815, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/collections.py": 16114931075157411913, + "venv/lib/python3.13/site-packages/pygments/lexers/tcl.py": 10343667975436015855, + "venv/lib/python3.13/site-packages/cryptography/x509/base.py": 9124948989225782039, + "venv/lib/python3.13/site-packages/numpy/polynomial/laguerre.pyi": 11310761736182213265, + "frontend/src/components/__tests__/task_log_viewer.test.js": 6512653335179659416, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_memmap.py": 8709963808115492688, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_is_monotonic.py": 12769599198035357753, + "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/oauth2_session.py": 7076806577412682835, + "venv/lib/python3.13/site-packages/pandas/io/__init__.py": 9767690163638898709, + "venv/lib/python3.13/site-packages/pydantic/v1/env_settings.py": 6194387033830177219, + "venv/lib/python3.13/site-packages/pyasn1/codec/der/__init__.py": 15728752901274520502, + "venv/lib/python3.13/site-packages/pyasn1/codec/ber/__init__.py": 15728752901274520502, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/lexers/python.py": 17886036670342116501, + "venv/lib/python3.13/site-packages/numpy/rec/__init__.py": 7467092880535460658, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_categorical.py": 9923236395692943732, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/row.py": 9763935103055608874, + "frontend/build/_app/immutable/nodes/2.BTzX8n4L.js": 10193965644456469096, + "frontend/src/components/git/CommitModal.svelte": 2248630732087966961, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_freq_attr.py": 7685756734926749093, + "venv/lib/python3.13/site-packages/anyio/streams/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tseries/api.py": 14785127136180503704, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_duplicated.py": 6659959750254523427, + "venv/lib/python3.13/site-packages/pandas/_libs/byteswap.pyi": 6723628093359375823, + "venv/lib/python3.13/site-packages/PIL/PaletteFile.py": 4104323659569809769, + "venv/lib/python3.13/site-packages/pandas/core/arrays/numpy_.py": 16641729409992576358, + "venv/lib/python3.13/site-packages/keyring/backends/SecretService.py": 17482492072907438453, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/__init__.py": 14571251344510763303, + "venv/lib/python3.13/site-packages/pydantic/v1/config.py": 12050640083719340958, + "venv/lib/python3.13/site-packages/numpy/_core/records.pyi": 14194354215405544730, + "venv/lib/python3.13/site-packages/pytest_asyncio/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/f2py/f2py2e.py": 738588649412338185, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_integer/foo77.f": 12000068001851270396, + "venv/lib/python3.13/site-packages/pandas/core/ops/mask_ops.py": 16152832446926420035, + "venv/bin/normalizer": 10972303485259566838, + "venv/lib/python3.13/site-packages/numpy/ma/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/idna/core.py": 2199607970842512780, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_ufunclike.py": 13752381070039804384, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/parameters.py": 247312908510013930, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py": 16768579082462294745, + "venv/lib/python3.13/site-packages/pyasn1/type/namedtype.py": 1859990775875190116, + "backend/src/plugins/storage/plugin.py": 13669428465597027289, + "venv/lib/python3.13/site-packages/_cffi_backend.cpython-313-x86_64-linux-gnu.so": 829323374625147265, + "venv/lib/python3.13/site-packages/starlette/middleware/trustedhost.py": 8302185291610019926, + "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/licenses/LICENSE": 10253862887434903467, + "frontend/src/lib/api/translate.js": 18119277537390809809, + "venv/lib/python3.13/site-packages/pip/_internal/req/constructors.py": 17485271035479378662, + "venv/lib/python3.13/site-packages/numpy/_core/_multiarray_tests.cpython-313-x86_64-linux-gnu.so": 442168371609469538, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_libgroupby.py": 9985769511561272200, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/__init__.pyi": 17469792319379909312, "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_count.py": 9186538441380080087, - "venv/lib/python3.13/site-packages/websockets/version.py": 5853118844880112885, - "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8414/__init__.py": 4781700006554860887, - "venv/lib/python3.13/site-packages/numpy/py.typed": 15130871412783076140, - "venv/lib/python3.13/site-packages/authlib/integrations/base_client/async_app.py": 4329794049222609894, - "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_timestamp.py": 12253372102602993034, - "venv/lib/python3.13/site-packages/websockets/asyncio/messages.py": 6848925149930551457, - "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_timedelta.py": 7742506012012398830, - "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py": 17538029557373507617, - "venv/lib/python3.13/site-packages/pygments/lexers/julia.py": 13516407944275342328, - "venv/lib/python3.13/site-packages/pip/_internal/commands/index.py": 16715578868094112894, - "venv/lib/python3.13/site-packages/numpy/lib/_twodim_base_impl.py": 4154852745131464543, - "frontend/src/routes/datasets/review/[id]/+page.svelte": 2388350237937943327, - "venv/lib/python3.13/site-packages/pip/_internal/req/req_set.py": 534498057184394558, + "venv/lib/python3.13/site-packages/PIL/ImageText.py": 15322496455608242001, + "venv/lib/python3.13/site-packages/starlette/middleware/__init__.py": 9524277524025372856, + "venv/lib/python3.13/site-packages/jose/jwk.py": 2781217726458801910, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/strptime.pyi": 5973671421648618563, + "venv/lib/python3.13/site-packages/pip/_vendor/tomli/_re.py": 1296579393600719224, + "venv/lib/python3.13/site-packages/numpy/ma/tests/test_deprecations.py": 8310244516175542839, + "venv/lib/python3.13/site-packages/pandas/_libs/byteswap.cpython-313-x86_64-linux-gnu.so": 17856848989827220388, + "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/licenses/LICENSE": 14524289631770661180, + "venv/lib/python3.13/site-packages/urllib3/_collections.py": 3440910792774742262, + "frontend/src/routes/settings/settings-utils.js": 9490767217322820262, + "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/numpy/_typing/_shape.py": 12667553664122376205, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/chararray.pyi": 17340224970247832290, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_logical.py": 6818185860296232476, + "backend/src/schemas/auth.py": 339608158310320944, + "backend/src/core/task_manager/context.py": 17543992255093146942, + "backend/src/services/reports/__tests__/test_type_profiles.py": 17262223295445285285, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arctanh.csv": 8620268345075425735, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_factorize.py": 5585211699185937865, + "venv/lib/python3.13/site-packages/pandas/_config/dates.py": 3863381688916127265, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/util.py": 3591114320020964752, + "venv/lib/python3.13/site-packages/pydantic/_internal/_validators.py": 2939255306814626438, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7591/__init__.py": 348469143777130970, + "venv/lib/python3.13/site-packages/numpy/matrixlib/__init__.py": 18402126171936308228, + "frontend/src/lib/ui/Card.svelte": 3442526978752993023, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/modules.py": 9023244176010175562, + "backend/src/api/routes/settings.py": 1899503837234642538, + "venv/lib/python3.13/site-packages/cffi/_imp_emulation.py": 17246725564704952212, + "venv/lib/python3.13/site-packages/greenlet/tests/test_weakref.py": 6780204527427742510, + "venv/lib/python3.13/site-packages/pip/_vendor/distro/__init__.py": 11414161642984633362, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_year.py": 1478857009124807836, + "backend/src/api/routes/reports.py": 13330739376983048757, + "venv/lib/python3.13/site-packages/pandas/_testing/asserters.py": 272063856832951430, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_repr.py": 7552782621374439569, + "frontend/src/components/TaskLogViewer.svelte": 14310893311791502213, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_tooltip.py": 853808724844451770, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_set_index.py": 10971093361190283675, + "venv/lib/python3.13/site-packages/greenlet/tests/_test_extension_cpp.cpython-313-x86_64-linux-gnu.so": 7659849621107414188, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9207/__init__.py": 8434616194114282088, + "venv/lib/python3.13/site-packages/passlib/tests/test_win32.py": 13932956220195912963, + "venv/lib/python3.13/site-packages/pygments/styles/colorful.py": 15708848112363953394, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/dtypes/test_dtypes_basic.py": 10917162988625838981, + "frontend/src/lib/components/dataset-review/SemanticLayerReview.svelte": 2844634204872241861, + "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/LICENSE": 14599279238716790189, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_promote.py": 16601872688404846307, + "venv/lib/python3.13/site-packages/pandas/io/excel/_xlrd.py": 14430937200583666658, + "venv/lib/python3.13/site-packages/websockets/typing.py": 13268801935274185287, + "venv/lib/python3.13/site-packages/pydantic/v1/tools.py": 1995883197897112278, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/automap.py": 4672057253309608464, + "frontend/build/_app/immutable/chunks/4Mt7E5DY.js": 1876154373397748488, + "backend/src/core/task_manager/__tests__/test_task_logger.py": 8175976176924379623, + "venv/lib/python3.13/site-packages/pip/_internal/models/format_control.py": 12916176880659590456, + "backend/tests/services/clean_release/test_report_audit_immutability.py": 11385235469772417017, + "venv/lib/python3.13/site-packages/pandas/io/formats/xml.py": 12235904458551067063, + "venv/lib/python3.13/site-packages/numpy/linalg/tests/test_regression.py": 5652583095949741238, + "venv/bin/fastapi": 12400009726251862770, + "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/__init__.py": 10920760520224795596, + "venv/lib/python3.13/site-packages/pip/_vendor/distro/__main__.py": 2688850704829116818, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/__init__.py": 11709551233929312136, + "venv/lib/python3.13/site-packages/_pytest/pytester.py": 5423287282155948827, + "venv/lib/python3.13/site-packages/pandas/io/parquet.py": 14415622950469421518, + "venv/lib/python3.13/site-packages/authlib/jose/rfc8037/okp_key.py": 10312614562914096039, + "venv/lib/python3.13/site-packages/certifi/__init__.py": 9045580998764948967, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_shift.py": 8547819448301023303, + "venv/lib/python3.13/site-packages/numpy/_core/numeric.py": 12261531924304979741, + "venv/lib/python3.13/site-packages/uvicorn/protocols/http/httptools_impl.py": 10367325397209016951, + "backend/tests/services/clean_release/test_compliance_execution_service.py": 5924200441771414883, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_logical/foo90.f90": 12508663906609241323, + "venv/lib/python3.13/site-packages/pandas/core/util/numba_.py": 3535857372998653863, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_python_parser_only.py": 12231449578582108346, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/types.py": 356973910046950951, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_constructors.py": 13890224577680389718, + "venv/lib/python3.13/site-packages/uvicorn/loops/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/core/einsumfunc.py": 3484247123731380377, + "venv/lib/python3.13/site-packages/pip/_internal/distributions/installed.py": 18153165899841229597, + "venv/lib/python3.13/site-packages/pydantic/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_internal/utils/glibc.py": 12335359492756627313, + "venv/lib/python3.13/site-packages/ecdsa/test_keys.py": 17117188373419689467, + "venv/lib/python3.13/site-packages/jose/constants.py": 10880650415673457194, + "venv/lib/python3.13/site-packages/numpy/f2py/__main__.py": 12567751646090102167, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_errstate.py": 10788459171326750780, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/array.py": 13840510238256626632, + "venv/lib/python3.13/site-packages/pandas/core/algorithms.py": 487939626203097713, + "venv/lib/python3.13/site-packages/pygments/lexers/rita.py": 8105458520706587972, + "venv/lib/python3.13/site-packages/pygments/lexers/console.py": 18266795648198475028, + "venv/lib/python3.13/site-packages/httpx/_transports/default.py": 8391302827983907910, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/appengine.py": 16194997685865850655, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_x32_unix.h": 9165722966153119887, + "venv/lib/python3.13/site-packages/numpy/ma/tests/test_arrayobject.py": 9578555381494297966, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_pipe.py": 8255917221095334212, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_pivot_multilevel.py": 9830258607305392103, + "venv/lib/python3.13/site-packages/pycparser/_build_tables.py": 569728086093219621, + "venv/lib/python3.13/site-packages/uvicorn/_subprocess.py": 13965754246493254447, + "frontend/src/components/tools/MapperTool.svelte": 16528529568914906720, + "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/conftest.py": 25473177671450048, + "frontend/build/_app/immutable/nodes/11.BfFQ1EX4.js": 7853694264472099005, + "backend/src/models/storage.py": 1218824901001897736, + "frontend/src/lib/components/translate/BulkReplaceModal.svelte": 17345501275392894037, + "venv/lib/python3.13/site-packages/httpcore/_api.py": 16499985295426060327, + "venv/lib/python3.13/site-packages/sqlalchemy/util/preloaded.py": 3719702436309355562, + "venv/lib/python3.13/site-packages/smmap/test/lib.py": 2924931247676024085, + "venv/lib/python3.13/site-packages/websockets/protocol.py": 6401227903827084901, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-log1p.csv": 16333455295913164408, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/dml.py": 6519732236745017861, + "venv/lib/python3.13/site-packages/pandas/core/tools/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_internal/models/pylock.py": 16697828625021519459, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/declarative/extensions.py": 217122171232375592, + "venv/lib/python3.13/site-packages/fastapi/staticfiles.py": 4094330072433302614, + "venv/lib/python3.13/site-packages/tzlocal-5.3.1.dist-info/METADATA": 11439969143853665311, + "venv/lib/python3.13/site-packages/pandas/core/strings/__init__.py": 13025387151657797130, + "venv/lib/python3.13/site-packages/PIL/DcxImagePlugin.py": 11767274218348117481, + "venv/lib/python3.13/site-packages/bcrypt/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ufunclike.pyi": 12215374902556647369, + "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/WHEEL": 13232065379159720345, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_numeric.py": 5627177940393829333, + "venv/lib/python3.13/site-packages/pandas/tests/extension/json/test_json.py": 12422315190455428620, + "backend/src/plugins/__init__.py": 17194112070692432889, + "venv/lib/python3.13/site-packages/fastapi/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_rank.py": 5817408252050193538, + "venv/lib/python3.13/site-packages/PIL/_deprecate.py": 15076725072094847812, + "venv/lib/python3.13/site-packages/click/formatting.py": 6694583529073729642, + "venv/lib/python3.13/site-packages/pygments/lexers/meson.py": 14659991367778746658, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm64_masm.obj": 11967637259540485652, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/apply.py": 3906017176873949738, + "venv/lib/python3.13/site-packages/websockets/connection.py": 5168795926684067030, "venv/lib/python3.13/site-packages/python_multipart/decoders.py": 14252672585219598429, - "venv/lib/python3.13/site-packages/git/refs/__init__.py": 16246686106073400519, - "venv/lib/python3.13/site-packages/greenlet/TThreadState.hpp": 15599148954148720463, - "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_callback.py": 1689177048527329209, - "frontend/build/_app/immutable/nodes/24.EvmszrIx.js": 12117218792476024894, - "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/scalars.pyi": 17683675356659300769, - "venv/lib/python3.13/site-packages/numpy/core/_multiarray_umath.py": 10973016138581934035, - "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/METADATA": 15225661995655327539, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_api.py": 13319208721442328400, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/transform/test_transform.py": 5674175311567817854, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/base.py": 7855217087916771270, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/models.py": 9513172366009392193, + "venv/lib/python3.13/site-packages/numpy/core/overrides.pyi": 1968648615494204783, + "venv/lib/python3.13/site-packages/pandas/core/frame.py": 2630807744775247533, + "backend/tests/services/clean_release/test_candidate_manifest_services.py": 16861589402854148894, + "venv/lib/python3.13/site-packages/numpy/fft/__init__.py": 9343449448737026658, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_online.py": 11850497659869773510, + "venv/lib/python3.13/site-packages/pandas/core/computation/common.py": 5369352684655954608, + "venv/lib/python3.13/site-packages/pandas/core/groupby/categorical.py": 16588862198627920420, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/__init__.py": 4014740126161973332, + "venv/lib/python3.13/site-packages/pandas/_libs/parsers.cpython-313-x86_64-linux-gnu.so": 14134106903573638108, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_tz_localize.py": 14495969595744172955, + "venv/lib/python3.13/site-packages/pygments/lexers/_lasso_builtins.py": 4006621424665751347, + "backend/src/models/dataset_review_pkg/_execution_models.py": 9810012962062422666, + "venv/lib/python3.13/site-packages/PIL/_imagingmath.cpython-313-x86_64-linux-gnu.so": 77725273398858074, + "venv/lib/python3.13/site-packages/websockets/asyncio/connection.py": 17044409627448400723, + "venv/lib/python3.13/site-packages/numpy/f2py/crackfortran.pyi": 9206355685192026844, + "frontend/build/_app/immutable/nodes/16.CNhvwO2y.js": 13656477822667393544, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/converter.py": 9041278869295701566, + "venv/lib/python3.13/site-packages/numpy/random/_pcg64.cpython-313-x86_64-linux-gnu.so": 5759445712745875888, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/assignOnlyModule.f90": 15967199601254912096, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/information_schema.py": 8551227132710897346, + "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/RECORD": 4487477737768669516, + "venv/lib/python3.13/site-packages/_pytest/python.py": 1829073958095716309, + "venv/lib/python3.13/site-packages/pip/_internal/network/xmlrpc.py": 5064964778750366059, + "venv/lib/python3.13/site-packages/cryptography/x509/extensions.py": 10197248868682803019, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/mutable.py": 9323088483998280629, + "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/METADATA": 10810011740984652381, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_indexing.py": 3841182456456779012, + "backend/src/schemas/translate.py": 4202862152769241553, + "venv/lib/python3.13/site-packages/passlib/tests/test_utils_pbkdf2.py": 17016810698578473773, + "venv/lib/python3.13/site-packages/pygments/lexers/archetype.py": 8995143180482517233, + "venv/lib/python3.13/site-packages/pygments/lexers/inferno.py": 9056274496469409594, + "venv/lib/python3.13/site-packages/pandas/core/roperator.py": 2587829042468916489, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/np_datetime.pyi": 2884703536776830012, + "backend/src/services/dataset_review/event_logger.py": 8083530196989488371, + "venv/lib/python3.13/site-packages/apscheduler/schedulers/asyncio.py": 11442130378027628520, + "venv/lib/python3.13/site-packages/idna-3.11.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/common.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/base_server.py": 9643197259080504542, + "venv/lib/python3.13/site-packages/pip/_vendor/tomli/__init__.py": 12906995259233516197, + "venv/lib/python3.13/site-packages/referencing/_attrs.py": 14434891919747863272, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-tanh.csv": 6674536825156196036, + "venv/lib/python3.13/site-packages/pyasn1/type/constraint.py": 16776615503418773283, + "venv/lib/python3.13/site-packages/passlib/tests/test_ext_django.py": 3210697476292094384, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/result.py": 8306497889916212025, + "venv/lib/python3.13/site-packages/yaml/dumper.py": 11376022992370902766, + "venv/lib/python3.13/site-packages/pygments/styles/manni.py": 11853576691280294949, + "venv/lib/python3.13/site-packages/pygments/lexers/qlik.py": 3881663935630350152, + "backend/src/api/routes/plugins.py": 2062850550409490465, + "venv/lib/python3.13/site-packages/h11/_readers.py": 832030916375670301, + "venv/lib/python3.13/site-packages/ecdsa/test_ellipticcurve.py": 11125783981574901613, + "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/errors.py": 4817746830102488448, + "venv/lib/python3.13/site-packages/pandas/core/computation/pytables.py": 16326122536715961388, + "venv/lib/python3.13/site-packages/pygments/lexers/smithy.py": 10789533877863290582, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_to_frame.py": 4303613319747945709, + "venv/lib/python3.13/site-packages/pydantic/functional_serializers.py": 15285359935059345032, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_isin.py": 11330491145602008796, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_numpy.py": 4073641912755651877, + "venv/lib/python3.13/site-packages/fastapi/logger.py": 11590581556317053213, + "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/METADATA": 3786116428839780263, + "venv/lib/python3.13/site-packages/pygments/styles/arduino.py": 4578385147314541233, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/version.py": 5000350193835202949, + "venv/lib/python3.13/site-packages/numpy/doc/ufuncs.py": 11706048944901534664, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_join.py": 4213933887062089605, + "venv/lib/python3.13/site-packages/pip/_internal/network/session.py": 293771075394156394, + "frontend/build/_app/immutable/chunks/Dvp0aVhC.js": 16109929560541789895, + "venv/lib/python3.13/site-packages/jeepney/io/tests/test_asyncio.py": 2820168745710754136, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/_openssl.pyi": 4376871621615688626, + "venv/lib/python3.13/site-packages/cryptography/exceptions.py": 5173540025160237758, + "venv/lib/python3.13/site-packages/numpy/_distributor_init.py": 1101690714183863290, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py": 14190849361275154667, + "venv/lib/python3.13/site-packages/starlette/middleware/wsgi.py": 12506262030845301606, + "venv/lib/python3.13/site-packages/pandas/_libs/__init__.py": 17117112389651696580, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/request.py": 10492090878715126550, + "venv/lib/python3.13/site-packages/greenlet/PyModule.cpp": 3464405102256525511, + "venv/lib/python3.13/site-packages/numpy/random/__init__.pxd": 7355772669172093814, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_feather.py": 13613400750755530364, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame_color.py": 3700828402084646308, + "venv/lib/python3.13/site-packages/yaml/composer.py": 13619070314468518566, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/structs.py": 4479429145468072604, + "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/RECORD": 963865046004908868, + "venv/lib/python3.13/site-packages/pandas/tests/extension/array_with_attr/test_array_with_attr.py": 6568496539994758985, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_formats.py": 9738552308554950419, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_results.py": 18145930285449734975, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_matmul.py": 9323220618335072725, + "venv/lib/python3.13/site-packages/sqlalchemy/connectors/pyodbc.py": 4146960201984489604, + "backend/src/api/routes/connections.py": 3871707738958709690, + "frontend/src/routes/datasets/review/[id]/__tests__/dataset_review_workspace.ux.test.js": 4105775024258653726, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/__init__.pyi": 10784666243744187693, + "venv/lib/python3.13/site-packages/pandas/core/sorting.py": 689057022713741051, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/secrets.py": 17816662030600756342, + "venv/lib/python3.13/site-packages/apscheduler/executors/pool.py": 10344126569001032241, + "venv/lib/python3.13/site-packages/pip/_internal/operations/install/editable_legacy.py": 8145740626773638218, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_complex.py": 4290059580478881700, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh25286.f90": 1249061697041863692, + "venv/lib/python3.13/site-packages/pyasn1/type/univ.py": 7163437624197683039, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/processors.cpython-313-x86_64-linux-gnu.so": 18139875206371157315, + "venv/lib/python3.13/site-packages/pygments/styles/zenburn.py": 1031869867933871309, + "backend/src/services/clean_release/repositories/compliance_repository.py": 7402628017251283691, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/pyodbc.py": 11271266527234292020, + "venv/lib/python3.13/site-packages/git/refs/head.py": 16271128287606894569, + "venv/lib/python3.13/site-packages/pygments/lexers/phix.py": 16775285097768256238, + "frontend/src/lib/ui/PageHeader.svelte": 3800784212388586861, + "venv/lib/python3.13/site-packages/pandas/tseries/offsets.py": 13750652921710295115, + "venv/lib/python3.13/site-packages/numpy/lib/_twodim_base_impl.py": 4154852745131464543, + "backend/tests/scripts/test_clean_release_cli.py": 13978106497618764226, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_asof.py": 17616601748321325187, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/accesstype.f90": 877982462186396830, + "venv/lib/python3.13/site-packages/numpy/core/__init__.pyi": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_validate.py": 12789481642730855139, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_constructors.py": 5627819885594450614, + "backend/tests/scripts/test_clean_release_tui.py": 14700450036639944764, + "venv/lib/python3.13/site-packages/typing_inspection/typing_objects.pyi": 18193727720492050602, + "venv/lib/python3.13/site-packages/pydantic_core/__init__.py": 10563134104783445328, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/requirements.py": 13215932786801020434, + "venv/lib/python3.13/site-packages/numpy/lib/_npyio_impl.pyi": 6576645822227068922, + "venv/bin/pyrsa-keygen": 8222542582170363672, + "venv/lib/python3.13/site-packages/httpx/_urlparse.py": 8270294590945652412, + "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/RECORD": 3205266407680661753, + "venv/lib/python3.13/site-packages/pandas/_libs/tslib.pyi": 16170606956605100142, + "venv/lib/python3.13/site-packages/numpy/lib/_function_base_impl.pyi": 2628035888703031029, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/numeric.py": 14451517189908259173, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/groupby.py": 12749730954888633240, + "frontend/src/routes/datasets/[id]/+page.svelte": 3377235098319163133, + "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/METADATA": 16700674024421191168, + "venv/lib/python3.13/site-packages/pluggy/_tracing.py": 7276210024011809655, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_snap.py": 4515844385819802784, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/packages.py": 11689856177725665974, + "venv/lib/python3.13/site-packages/attr/py.typed": 15130871412783076140, + "backend/src/schemas/settings.py": 7182606947764184429, + "venv/lib/python3.13/site-packages/starlette/applications.py": 13591448461234020757, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_clip.py": 11111183698178681331, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_regression.py": 16293647824734937323, + "venv/lib/python3.13/site-packages/pandas/core/tools/datetimes.py": 13313374952742129866, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_logical_ops.py": 16166386974005224415, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_unary.py": 8046543893978198672, + "venv/lib/python3.13/site-packages/psycopg2/_ipaddress.py": 12332672835751787869, + "backend/src/api/routes/__tests__/test_assistant_authz.py": 8404416194210647280, + "venv/lib/python3.13/site-packages/numpy/_core/getlimits.pyi": 15746478550640231553, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py": 14946223589457772924, + "venv/lib/python3.13/site-packages/apscheduler/triggers/interval.py": 3964550092873794670, + "venv/lib/python3.13/site-packages/keyring/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/trafficscript.py": 776604244051744646, + "frontend/build/_app/env.js": 8815854342083926790, + "venv/lib/python3.13/site-packages/_pytest/monkeypatch.py": 2635774457434545573, + "frontend/src/components/tasks/TaskResultPanel.svelte": 193489346212840974, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_base.py": 16710163879060522196, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/metadata.py": 7204343250279221076, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_inf.py": 8659210689723088321, + "venv/lib/python3.13/site-packages/urllib3-2.6.2.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/test_round.py": 7912161077874611432, + "venv/lib/python3.13/site-packages/jose/jwe.py": 3325660353225624025, + "venv/lib/python3.13/site-packages/ecdsa/test_eddsa.py": 1785854649532849946, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/scripts.py": 8759605303886441976, + "venv/lib/python3.13/site-packages/numpy/lib/array_utils.py": 18135114588643176359, + "venv/lib/python3.13/site-packages/websockets/legacy/__init__.py": 12332512329533591682, + "venv/lib/python3.13/site-packages/numpy/char/__init__.pyi": 12214207343146244743, + "venv/lib/python3.13/site-packages/pandas/core/interchange/dataframe.py": 16525846817331842080, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/datetimes/test_cumulative.py": 6346947559103505068, + "venv/lib/python3.13/site-packages/pygments/lexers/_vbscript_builtins.py": 9882134558469491349, + "venv/lib/python3.13/site-packages/pygments/lexers/tal.py": 447505725492547461, + "venv/lib/python3.13/site-packages/pandas/core/col.py": 4221271461723119525, + "venv/lib/python3.13/site-packages/pygments/lexers/web.py": 11104721221615804497, + "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/min_max_.py": 13457611930368041821, + "venv/lib/python3.13/site-packages/greenlet/tests/test_extension_interface.py": 11693583111513457974, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_expanding.py": 9047466276603454007, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/types.py": 13384984564922916167, + "venv/lib/python3.13/site-packages/jsonschema/_format.py": 8987698648383779883, + "venv/lib/python3.13/site-packages/jsonschema/tests/test_types.py": 651959450717569029, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_win32_console.py": 18092547253031555768, + "venv/lib/python3.13/site-packages/jose/backends/base.py": 12795468809472351388, + "venv/lib/python3.13/site-packages/rapidfuzz/__init__.py": 12462153783886855740, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_2_compat.h": 6512969159139431485, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_getitem.py": 11882213323878588707, + "venv/lib/python3.13/site-packages/numpy/f2py/symbolic.pyi": 14558091964387571257, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_internal/cli/index_command.py": 15798187258841839292, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/dtypes.py": 9839461501970023861, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_excel.py": 5467043641314706635, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_kind.py": 18178830861475480607, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_stata.py": 3030675968330529905, + "venv/lib/python3.13/site-packages/pandas/io/json/_normalize.py": 15938855324762935595, + "backend/src/services/clean_release/artifact_catalog_loader.py": 16969668877930596278, + "venv/lib/python3.13/site-packages/gitdb/base.py": 7927279446353696534, + "venv/lib/python3.13/site-packages/httpx/_auth.py": 1283189552342958900, + "venv/lib/python3.13/site-packages/pip/_internal/cli/req_command.py": 2887890799766730546, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_symbolic.py": 8093576026032854125, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/formatters/__init__.py": 3247870839323520754, + "venv/lib/python3.13/site-packages/numpy/_core/overrides.py": 252940520997718714, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_internals.py": 10472508540875131797, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_unary.py": 2573825678342936734, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py": 12264234876933578367, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/urllib3/contrib/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/click/testing.py": 15973659312284678327, + "venv/lib/python3.13/site-packages/ecdsa/util.py": 10543788809724099209, + "venv/lib/python3.13/site-packages/referencing/tests/test_exceptions.py": 10846225989746274213, + "backend/src/models/filter_state.py": 6470311179852241297, + "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/__init__.py": 16416360448593978041, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/token.py": 6987328478153445584, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/asyncio.py": 2517623574187179319, + "backend/src/plugins/llm_analysis/__tests__/test_screenshot_service.py": 17792688368296989200, + "venv/lib/python3.13/site-packages/pip/_internal/utils/retry.py": 15244280495983216204, + "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/METADATA": 14180973198574731599, + "backend/src/services/clean_release/policy_resolution_service.py": 5075709738950902327, + "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/RECORD": 11904769847385571600, + "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/RECORD": 10052481303799123207, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/token_validator.py": 2932866595454490527, + "venv/lib/python3.13/site-packages/keyring/testing/backend.py": 14295829193445781542, + "venv/lib/python3.13/site-packages/pygments/lexers/wren.py": 6259586056260713984, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_series_equal.py": 1223890028205011021, + "frontend/src/services/__tests__/gitService.test.js": 12845562082447336909, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_cumulative.py": 15644124412739892167, + "venv/lib/python3.13/site-packages/numpy/random/mtrand.pyi": 17144590106028661128, + "venv/lib/python3.13/site-packages/dateutil/_version.py": 8279235793001245767, + "venv/lib/python3.13/site-packages/dateutil/tz/win.py": 8427821225251397364, + "venv/lib/python3.13/site-packages/pygments/styles/vim.py": 2621036947954523349, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/test_arithmetic.py": 14858832264660410908, + "venv/lib/python3.13/site-packages/pygments/lexers/typst.py": 8883289726601667104, + "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/__init__.py": 5895297076117159245, + "venv/lib/python3.13/site-packages/numpy/lib/stride_tricks.py": 548928042543500365, + "venv/lib/python3.13/site-packages/numpy/random/_pickle.pyi": 16081120364372391381, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/transform/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pytest/__main__.py": 8747175113746141224, + "venv/lib/python3.13/site-packages/more_itertools/more.pyi": 4844596605492825806, + "venv/lib/python3.13/site-packages/itsdangerous/_json.py": 4872267972688724005, + "venv/lib/python3.13/site-packages/dotenv/__main__.py": 16883579879172186355, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_join.py": 2585969863606902743, + "venv/lib/python3.13/site-packages/pandas/tests/config/test_config.py": 4630144191929135452, + "frontend/build/_app/immutable/chunks/DBhKLy_4.js": 87415875228682141, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/identity.py": 326059526167697655, + "backend/src/plugins/translate/_token_budget.py": 3072188939870967700, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/mod.pyi": 10591120373577657317, + "venv/lib/python3.13/site-packages/pyasn1/type/namedval.py": 8273171503516887681, + "venv/lib/python3.13/site-packages/smmap/test/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/retry.py": 17371295199208325375, + "venv/lib/python3.13/site-packages/keyring/util/__init__.py": 9635016046220094473, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_show_versions.py": 10577134135855180356, + "venv/lib/python3.13/site-packages/starlette/middleware/gzip.py": 4321371046149637632, + "venv/lib/python3.13/site-packages/anyio/abc/_streams.py": 8089057973257658396, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_non_unique.py": 15286893322149413712, + "venv/lib/python3.13/site-packages/fastapi/_compat/v1.py": 9776181748061371513, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/lib_utils.pyi": 3708905060001289515, + "venv/lib/python3.13/site-packages/pandas/tests/test_aggregation.py": 16863055892192070549, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_groupby.py": 15721254196883995634, + "venv/lib/python3.13/site-packages/jsonschema/tests/_suite.py": 6307666366417438489, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_numba.py": 6409810497251251179, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/json.py": 14755215397454910426, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_half.py": 3121836756584726126, + "venv/lib/python3.13/site-packages/cryptography/hazmat/decrepit/ciphers/__init__.py": 63497920264089252, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_astype.py": 7497268667246913350, + "venv/lib/python3.13/site-packages/pandas/tests/io/xml/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/charset_normalizer/models.py": 9917300217816275821, + "venv/lib/python3.13/site-packages/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py": 14824388798676578409, + "venv/lib/python3.13/site-packages/pygments/lexers/typoscript.py": 6706632522710582762, + "backend/tests/test_log_persistence.py": 11073909507089982937, + "venv/lib/python3.13/site-packages/numpy/f2py/__version__.py": 8588600299261398598, + "venv/lib/python3.13/site-packages/pandas/io/formats/excel.py": 797619981868084011, + "venv/lib/python3.13/site-packages/pygments/lexers/ruby.py": 6898093948224302592, + "venv/lib/python3.13/site-packages/pygments/lexers/webmisc.py": 9530962803076253849, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/printing.py": 8681519649669408795, + "venv/lib/python3.13/site-packages/pygments/lexers/_lilypond_builtins.py": 1766946089314631488, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_formats.py": 18080627205081311773, + "backend/tests/test_translate_executor_filter.py": 11717560360659156703, + "venv/lib/python3.13/site-packages/pyasn1/codec/native/__init__.py": 15728752901274520502, + "venv/lib/python3.13/site-packages/pip/_internal/models/selection_prefs.py": 14700113736620314923, + "venv/lib/python3.13/site-packages/h11/_abnf.py": 10456996134895429201, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/request.py": 10484280068366411541, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/cli/gh_22819.pyf": 14717748358749934995, + "venv/lib/python3.13/site-packages/pip/_internal/commands/freeze.py": 8644678595442275485, + "venv/lib/python3.13/site-packages/starlette/templating.py": 9242638124487797988, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timezones.pyi": 17528421279543420867, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_tools.py": 13994164937603424236, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_deprecate_nonkeyword_arguments.py": 11676809974120007408, + "venv/lib/python3.13/site-packages/PIL/PdfParser.py": 13710342183037972042, + "venv/lib/python3.13/site-packages/yaml/emitter.py": 6036657460416912844, + "backend/src/schemas/health.py": 13764741380188041840, + "venv/lib/python3.13/site-packages/pygments/lexers/fantom.py": 1986459352773446836, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/char.f90": 12203871998988759364, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_delete.py": 5958440386632506587, + "venv/lib/python3.13/site-packages/pandas/arrays/__init__.py": 10005095663840461954, + "backend/src/services/clean_release/exceptions.py": 14631509303697939633, + "venv/lib/python3.13/site-packages/sqlalchemy/util/_has_cy.py": 11979732417254560424, + "venv/lib/python3.13/site-packages/uvicorn/middleware/asgi2.py": 5355343704425405009, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/_csound_builtins.py": 14530739818491398298, + "backend/src/api/routes/git/_repo_operations_routes.py": 17252968422647919723, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/_globals.py": 8736093011628027941, + "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/extension_types.py": 1856757457657310231, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ma.py": 15912582628112164509, + "venv/lib/python3.13/site-packages/pygments/lexers/factor.py": 15788613154609197002, + "venv/lib/python3.13/site-packages/pandas/core/arrays/_ranges.py": 2572903460327860111, + "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/RECORD": 1556051687830913019, + "backend/src/services/git/_sync.py": 16596597562042405179, + "venv/lib/python3.13/site-packages/gitdb/test/lib.py": 13294941660283059872, + "backend/src/api/routes/__tests__/test_reports_openapi_conformance.py": 14282751162289385516, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh15035.f": 13126319439858352711, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_timedelta_range.py": 15081095442418142820, + "venv/lib/python3.13/site-packages/pygments/styles/sas.py": 12363791134941577382, + "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/h11/_state.py": 11398755254809751139, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_arithmetic.py": 16045758521544316139, + "venv/lib/python3.13/site-packages/cffi/vengine_cpy.py": 10684192171370688079, + "venv/lib/python3.13/site-packages/pip/_internal/commands/hash.py": 11824898770873127699, + "venv/lib/python3.13/site-packages/gitdb/fun.py": 55226330810782184, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_html.py": 7947998260347730581, + "venv/lib/python3.13/site-packages/numpy/lib/tests/data/win64python2.npy": 11005857803794805939, + "backend/src/scripts/clean_release_cli.py": 14467814161661747234, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_hermite.py": 3628609433179387988, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalarinherit.py": 15240535501243623986, + "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/METADATA": 11557342107649194148, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked_shared.py": 8453174300922339015, + "venv/lib/python3.13/site-packages/pluggy/_version.py": 7195758686717994475, + "venv/lib/python3.13/site-packages/numpy/_core/_dtype_ctypes.py": 5564496700647900501, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/bulk_persistence.py": 2307818727114646866, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_comparison.py": 11333735187162513512, + "venv/lib/python3.13/site-packages/pygments/styles/fruity.py": 7925019839136894457, + "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/var_.py": 12699521091170347311, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_dlpack.py": 7908102652282922952, + "venv/lib/python3.13/site-packages/passlib/tests/sample1b.cfg": 2872366861156549016, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed25519.pyi": 16312114056447366970, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_string.py": 10141066226714805009, + "venv/lib/python3.13/site-packages/git/objects/submodule/root.py": 5322248117564025370, + "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/RECORD": 4857922668040624663, + "venv/lib/python3.13/site-packages/pip/_internal/utils/logging.py": 9639351759822608724, + "venv/lib/python3.13/site-packages/pygments/lexers/soong.py": 10273453675034306814, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_setops.py": 15196593522537569500, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_printing.py": 8859757563354666575, + "venv/lib/python3.13/site-packages/PIL/ImageDraw.py": 12919304671639673744, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_join.py": 17874591841487372992, + "venv/lib/python3.13/site-packages/pygments/lexers/_sourcemod_builtins.py": 11809503654078535014, + "venv/lib/python3.13/site-packages/greenlet/TBrokenGreenlet.cpp": 10822436772777706192, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/context.py": 1041624619197012753, + "venv/lib/python3.13/site-packages/pandas/core/reshape/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_constructors.py": 9815164146739254076, + "venv/lib/python3.13/site-packages/pandas/_libs/sas.cpython-313-x86_64-linux-gnu.so": 16309838137741124637, + "venv/lib/python3.13/site-packages/pandas/io/excel/_xlsxwriter.py": 16873939324418752965, + "venv/lib/python3.13/site-packages/git/objects/commit.py": 12317865358846331802, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/fromnumeric.pyi": 8671744104085911610, + "venv/lib/python3.13/site-packages/pandas/io/formats/format.py": 3364941338174434661, + "venv/lib/python3.13/site-packages/pandas/core/window/common.py": 17956497339875932377, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_old_base.py": 191359377446257900, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_construct_from_scalar.py": 16520136036631155842, + "venv/lib/python3.13/site-packages/pip/_internal/configuration.py": 2563100675379605810, + "venv/lib/python3.13/site-packages/pandas/tests/io/sas/test_sas7bdat.py": 8963983384208121526, + "venv/lib/python3.13/site-packages/numpy/f2py/func2subr.pyi": 16573082220135508510, + "venv/lib/python3.13/site-packages/pygments/style.py": 17393199235575070210, + "frontend/build/_app/immutable/nodes/19.UaiNjUdw.js": 17580400751027983788, + "venv/lib/python3.13/site-packages/pygments/lexers/pddl.py": 9256112535059995841, + "venv/lib/python3.13/site-packages/pandas/core/arrays/boolean.py": 8821671562937866269, + "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_multiarray.py": 3592197154340095431, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_custom_business_day.py": 2200008998040663785, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7662/token_validator.py": 13917237710752731397, + "venv/lib/python3.13/site-packages/urllib3/http2/connection.py": 1661620860881397554, + "frontend/src/lib/stores/__tests__/setupTests.js": 13316015450930652502, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/random/libdivide.h": 5314232500579481313, + "venv/lib/python3.13/site-packages/pygments/lexers/crystal.py": 3579547906957125755, + "venv/lib/python3.13/site-packages/pandas/core/ops/missing.py": 9493665944764255854, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_tz_convert.py": 77283525137564255, + "venv/lib/python3.13/site-packages/pygments/lexers/agile.py": 5686162551416231457, + "backend/src/api/routes/admin.py": 16164005699911103765, + "venv/lib/python3.13/site-packages/pip/_internal/utils/temp_dir.py": 8657533190026387896, + "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_api.py": 7131036004750554905, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/base.py": 13454394827268856575, + "venv/lib/python3.13/site-packages/fastapi/websockets.py": 9910052969532075719, + "venv/lib/python3.13/site-packages/_pytest/assertion/truncate.py": 14000392306994773741, + "frontend/src/routes/translate/history/+page.svelte": 7874402226825627914, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/offsets.cpython-313-x86_64-linux-gnu.so": 6726636942028566415, + "venv/lib/python3.13/site-packages/numpy/linalg/__init__.py": 15647756307608142577, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/declarative/__init__.py": 2436195108273216959, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_frame.py": 2169381747938271444, + "venv/lib/python3.13/site-packages/rsa/prime.py": 10820898885044917095, + "venv/lib/python3.13/site-packages/jsonschema/exceptions.py": 15651767069704734674, + "venv/lib/python3.13/site-packages/pip/_internal/metadata/pkg_resources.py": 14271077832999370068, + "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/licenses/LICENSE": 13322127602069971876, + "venv/lib/python3.13/site-packages/pillow.libs/libharfbuzz-0692f733.so.0.61230.0": 23110287346167671, + "venv/lib/python3.13/site-packages/numpy/lib/_scimath_impl.py": 14696841109142419143, + "venv/lib/python3.13/site-packages/numpy/random/_mt19937.cpython-313-x86_64-linux-gnu.so": 85866616759631810, + "venv/lib/python3.13/site-packages/pip/_internal/operations/build/wheel_editable.py": 14352954849821931255, + "venv/lib/python3.13/site-packages/pydantic/root_model.py": 20625057828723317, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7516/jwe.py": 10478268710546063196, + "venv/lib/python3.13/site-packages/pip/_internal/commands/download.py": 7533669665002975823, + "frontend/build/_app/immutable/nodes/34.CPCAjONs.js": 8672599980797084253, + "venv/lib/python3.13/site-packages/fastapi/middleware/cors.py": 7452860773712601508, + "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/METADATA": 11419135077900073883, + "venv/lib/python3.13/site-packages/pandas/core/indexes/frozen.py": 1419685664276640120, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_gcs.py": 7733247456252965901, + "venv/lib/python3.13/site-packages/pip/_internal/utils/_jaraco_text.py": 9046755357011730699, + "venv/lib/python3.13/site-packages/jeepney/fds.py": 16573439672758641560, + "venv/lib/python3.13/site-packages/pydantic/v1/version.py": 2758971830527526972, + "frontend/build/_app/immutable/chunks/ikwS1dR6.js": 3267599179274813039, + "venv/lib/python3.13/site-packages/pydantic/type_adapter.py": 10162988530225478692, + "venv/lib/python3.13/site-packages/pygments/styles/nord.py": 8469538903422182155, + "backend/src/core/auth/logger.py": 14199514105394646252, + "venv/lib/python3.13/site-packages/rapidfuzz/process_py.py": 17563673072076876125, + "venv/lib/python3.13/site-packages/pydantic/json_schema.py": 15678278224696353978, + "venv/lib/python3.13/site-packages/pip/_internal/operations/freeze.py": 3469845570614985559, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/_typing.py": 17589387690638097975, + "venv/lib/python3.13/site-packages/httpcore/_backends/anyio.py": 6167729964577005634, + "venv/lib/python3.13/site-packages/pygments/lexers/rust.py": 8605690716125176329, + "venv/lib/python3.13/site-packages/numpy/fft/tests/test_pocketfft.py": 18025182347224285050, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_take.py": 1216902953519121176, + "backend/src/core/task_manager/persistence.py": 7896628499516364250, + "venv/lib/python3.13/site-packages/numpy/matlib.py": 112839038090033740, + "venv/lib/python3.13/site-packages/cffi/ffiplatform.py": 6782148110863549472, + "venv/lib/python3.13/site-packages/passlib/utils/__init__.py": 11704122205941631503, + "frontend/src/lib/stores/health.js": 3437957055218721782, + "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/RECORD": 11111103193315065053, + "venv/lib/python3.13/site-packages/starlette/config.py": 16509394679506300256, + "venv/lib/python3.13/site-packages/sqlalchemy/inspection.py": 8563914460959280101, + "backend/src/core/auth/security.py": 8289703096420999159, + "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/util.py": 18279366777506483176, + "venv/lib/python3.13/site-packages/python_multipart/__init__.py": 10290895261363712562, + "venv/lib/python3.13/site-packages/pandas/core/groupby/indexing.py": 18009437216172136227, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/ssl_match_hostname.py": 8530403239168529668, + "venv/lib/python3.13/site-packages/_pytest/python_api.py": 12953851690889336092, + "venv/lib/python3.13/site-packages/pip/_vendor/certifi/cacert.pem": 15624463015628939254, + "venv/lib/python3.13/site-packages/pygments/lexers/foxpro.py": 14032656072384708293, + "venv/lib/python3.13/site-packages/pygments/styles/tango.py": 12389250878249075539, + "backend/src/api/routes/assistant/_command_parser.py": 2745692387994130159, + "frontend/src/lib/toasts.js": 199299975383904134, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_nlargest.py": 1423106021399469694, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7591/errors.py": 17581857895562494222, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_construct_object_arr.py": 4725156416161661681, + "venv/lib/python3.13/site-packages/pygments/lexers/blueprint.py": 6370363226580199085, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/__init__.py": 15130871412783076140, + "backend/src/app.py": 1030174853208129525, + "venv/bin/f2py": 11006035631504708368, + "frontend/src/components/MissingMappingModal.svelte": 11264881226066623003, + "venv/lib/python3.13/site-packages/anyio/_core/_exceptions.py": 11969894261631227715, + "venv/lib/python3.13/site-packages/pandas/tests/generic/test_to_xarray.py": 15341013524814290960, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_deprecations.py": 6015734648984591027, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_iceberg.py": 1726421917529457420, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_formats.py": 17821648708413143557, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_aarch64_gcc.h": 8140470539757491899, + "venv/lib/python3.13/site-packages/pandas/core/reshape/melt.py": 14109947554124551986, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_markdown.py": 9964642152916165317, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_select.py": 7083040959665006634, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_polynomial.py": 3049262771098624825, + "venv/lib/python3.13/site-packages/psycopg2/_range.py": 7184417109068014771, + "venv/lib/python3.13/site-packages/numpy/ma/mrecords.py": 687170905107114309, + "venv/lib/python3.13/site-packages/numpy/ctypeslib/__init__.py": 2401931196871484449, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_timezones.py": 1962830142478494009, + "venv/lib/python3.13/site-packages/numpy/lib/_scimath_impl.pyi": 8879196542984811747, + "venv/lib/python3.13/site-packages/sqlalchemy/util/typing.py": 17638587764345605306, + "venv/lib/python3.13/site-packages/uvicorn/workers.py": 7213290478118197047, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/caches/__init__.py": 11808051681178127650, + "venv/lib/python3.13/site-packages/pygments/lexers/capnproto.py": 15713281507099411738, + "frontend/src/components/llm/DocPreview.svelte": 9286231407313632900, + "frontend/build/_app/immutable/chunks/BsdYA8vJ.js": 17313445894049649179, + "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/RECORD": 9974324769263969827, + "venv/lib/python3.13/site-packages/pygments/lexers/vip.py": 11287963054551122004, + "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/METADATA": 13633829318759752611, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/__init__.py": 16123561007855424016, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_infer_objects.py": 16868563755487479303, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_tz_localize.py": 4937713043686953098, + "frontend/src/pages/Settings.svelte": 5110524159352803966, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/strategies.py": 15673926142454922581, + "venv/lib/python3.13/site-packages/pandas/_config/__init__.py": 3577026529387563519, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/matrix.pyi": 15667024840812783911, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/resultproxy.cpython-313-x86_64-linux-gnu.so": 6011605826065363427, + "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/WHEEL": 7145482834744213753, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/__init__.pyi": 15820654525292895396, + "venv/lib/python3.13/site-packages/starlette/responses.py": 14081178833432955042, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/x25519.pyi": 11455144434558323236, + "venv/lib/python3.13/site-packages/pydantic/v1/networks.py": 13262073155118055511, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/PIL/PdfImagePlugin.py": 16082854250442815862, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_from_dummies.py": 5670811108950300761, + "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/utils.py": 2959395751217827103, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_orc.py": 1248059610437937895, + "venv/lib/python3.13/site-packages/pip/_internal/models/link.py": 7172346414535657925, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/markers.py": 8297667990263004097, + "venv/lib/python3.13/site-packages/pandas/core/array_algos/quantile.py": 17341029201525362074, + "venv/lib/python3.13/site-packages/authlib/integrations/base_client/__init__.py": 1856412417337875350, + "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_argon2.py": 11345706437113628133, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timedeltas.cpython-313-x86_64-linux-gnu.so": 17089839649971421164, + "venv/lib/python3.13/site-packages/attr/setters.py": 12467854389745942025, + "venv/lib/python3.13/site-packages/pandas/_libs/_cyutility.cpython-313-x86_64-linux-gnu.so": 3372660143512868174, + "venv/lib/python3.13/site-packages/websockets/frames.py": 4901850477414613953, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_monotonic.py": 14648437502004398715, + "venv/lib/python3.13/site-packages/numpy/f2py/rules.pyi": 1342380029484127983, + "venv/lib/python3.13/site-packages/pandas/tests/window/moments/test_moments_consistency_expanding.py": 12504275097476819918, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data": 11957232619107868023, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/OSA_py.py": 2494967094400350388, + "venv/lib/python3.13/site-packages/numpy/_core/memmap.py": 4093510602708416858, + "venv/lib/python3.13/site-packages/annotated_types/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/jeepney/tests/test_bindgen.py": 8637878018232418583, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh23598.f90": 1409455010615482779, + "venv/lib/python3.13/site-packages/pandas/tests/frame/constructors/test_from_dict.py": 4937173601709369119, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_multi_thread.py": 5660352470978621226, + "venv/lib/python3.13/site-packages/urllib3/util/proxy.py": 17667887856427739069, + "frontend/src/routes/datasets/review/+page.svelte": 1567550142023887137, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/filters/__init__.py": 835924536855006596, + "venv/lib/python3.13/site-packages/click/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/ma/__init__.pyi": 1855615156913010107, + "venv/lib/python3.13/site-packages/pillow.libs/libwebpdemux-747f2b49.so.2.0.17": 7615380692384566654, + "venv/lib/python3.13/site-packages/fastapi/temp_pydantic_v1_params.py": 9436036880518047720, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh17859.f": 13562390882286135024, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/string_/test_string_arrow.py": 10008395772220468318, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Prefix.py": 4672871351134074735, + "venv/lib/python3.13/site-packages/git/config.py": 1724894144987553201, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_validate_args_and_kwargs.py": 3381939712123764154, + "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/PIL/BufrStubImagePlugin.py": 5520941821175895889, + "venv/lib/python3.13/site-packages/pygments/styles/abap.py": 11543320440974964547, + "backend/src/core/config_manager.py": 18161236261317111528, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/mixed/foo_free.f90": 8821492401453445757, + "backend/src/services/clean_release/stages/internal_sources_only.py": 3635582838654262092, + "backend/logs/app.log.bak": 12967408684782809237, + "backend/src/plugins/llm_analysis/service.py": 6500458189357690080, + "backend/alembic/versions/c4a3a2f74bfe_add_missing_columns_needs_review_.py": 4320606555167902916, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/memmap.pyi": 2834143910417841603, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/flatiter.pyi": 6885824496927786205, + "venv/lib/python3.13/site-packages/attr/_cmp.py": 3297594878548179300, + "venv/lib/python3.13/site-packages/smmap/__init__.py": 4557520096828426601, + "venv/lib/python3.13/site-packages/websockets/cli.py": 14049106025886056110, + "venv/lib/python3.13/site-packages/pydantic/v1/__init__.py": 1642038155370734658, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-expm1.csv": 8710594949902856122, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_query_eval.py": 17613800806748649144, + "venv/lib/python3.13/site-packages/pip/_internal/cli/status_codes.py": 9143625681947330756, + "venv/lib/python3.13/site-packages/_pytest/config/findpaths.py": 7867603756389940562, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/_typing.py": 10524021526716767489, + "venv/lib/python3.13/site-packages/fastapi/types.py": 3867821246650502267, + "venv/lib/python3.13/site-packages/cffi/cparser.py": 5246372077864988755, + "venv/lib/python3.13/site-packages/numpy/random/_philox.cpython-313-x86_64-linux-gnu.so": 9916143171057774705, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/certifi/__init__.py": 2833016678144975576, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_alpha_unix.h": 2759424570606117942, + "venv/lib/python3.13/site-packages/pandas/core/arrays/_arrow_string_mixins.py": 16862663427780315543, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_iloc.py": 7586049606891014270, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm32_gcc.h": 3561524604274567339, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/openssl/binding.py": 17078280153372356796, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_abc.py": 10164280045317807365, + "venv/lib/python3.13/site-packages/pip/py.typed": 10243553515949422671, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_round_trip.py": 14684057343786032776, + "venv/lib/python3.13/site-packages/sqlalchemy/util/deprecations.py": 7020540470348910770, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/path_registry.py": 8410829659513707067, + "venv/lib/python3.13/site-packages/jaraco/classes/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/python_multipart/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi": 10548370574281981402, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_infer_dtype.py": 1647214604678171565, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_coercion.py": 3339075591248270762, + "venv/lib/python3.13/site-packages/attrs/validators.py": 10346563361083429998, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_keys.py": 10813898277355150779, + "venv/lib/python3.13/site-packages/anyio/_core/_testing.py": 14024940365851109155, + "venv/lib/python3.13/site-packages/pydantic_settings/exceptions.py": 18300876747412267999, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_emoji_codes.py": 13901854719435716195, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/generate_umath_validation_data.cpp": 10880681943327843928, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_constructors.py": 16197656939536784142, + "venv/lib/python3.13/site-packages/greenlet/__init__.py": 3447613556981242475, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/period/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_autocorr.py": 1092316868398391792, + "venv/lib/python3.13/site-packages/urllib3/util/ssl_match_hostname.py": 7411350247004265632, + "venv/lib/python3.13/site-packages/pygments/lexers/_usd_builtins.py": 8671446144574174964, + "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_macos.py": 7028037591427052891, + "venv/lib/python3.13/site-packages/numpy/lib/user_array.pyi": 1968484998382771986, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-sinh.csv": 12358602501260667480, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_string.py": 9224234539232195189, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ndarray_misc.pyi": 16219573290116396099, + "venv/lib/python3.13/site-packages/pandas/tests/apply/conftest.py": 13798335354961261346, + "venv/lib/python3.13/site-packages/pygments/lexers/apdlexer.py": 14222752852221009085, + "venv/lib/python3.13/site-packages/pip/_vendor/idna/package_data.py": 3078502124870585867, + "venv/lib/python3.13/site-packages/numpy/_core/tests/_locales.py": 18178500267053825979, + "venv/lib/python3.13/site-packages/pandas/_testing/contexts.py": 11714205921752896137, + "venv/lib/python3.13/site-packages/pygments/lexers/_stata_builtins.py": 18053452765493556047, + "venv/lib/python3.13/site-packages/requests/certs.py": 16052880877156864882, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-exp2.csv": 10950201682596097452, + "venv/lib/python3.13/site-packages/uvicorn/__main__.py": 6086938803696646644, + "venv/lib/python3.13/site-packages/cryptography/__about__.py": 10471312538528457144, + "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE": 14316979437290727897, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/routines/funcfortranname.pyf": 1361426175133186231, + "venv/lib/python3.13/site-packages/numpy/polynomial/__init__.pyi": 8708830448398500675, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/hmac.pyi": 9405122752084210696, + "venv/lib/python3.13/site-packages/pandas/core/computation/api.py": 17919983681396223380, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_sort_index.py": 13517883160584591966, + "venv/lib/python3.13/site-packages/httpcore/_ssl.py": 6665087294869504820, + "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pip/_internal/utils/compat.py": 1841193561752758336, + "frontend/src/lib/components/layout/sidebarNavigation.js": 12067620255981492739, + "backend/src/api/routes/dashboards/_detail_routes.py": 12522696473630508473, + "backend/src/api/routes/clean_release_v2.py": 2858037566596334612, + "venv/lib/python3.13/site-packages/numpy/lib/scimath.pyi": 18165025261847276375, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_pyf_src.py": 12351476647369971852, + "venv/lib/python3.13/site-packages/packaging/tags.py": 1999137344425005827, + "frontend/src/components/EnvSelector.svelte": 3937920133889630193, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/decl_class.py": 6328411894571735935, + "venv/lib/python3.13/site-packages/pygments/lexers/asn1.py": 15030988816882821134, + "frontend/build/_app/immutable/nodes/36.DZAutSCl.js": 9000473499840988160, + "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py": 7852235015155694076, + "venv/lib/python3.13/site-packages/attr/converters.py": 5179615793507170642, + "venv/lib/python3.13/site-packages/passlib/utils/md4.py": 4521366665754877351, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_shift.py": 14681514853406253657, + "backend/src/scripts/test_dataset_dashboard_relations.py": 6373309008834670516, + "backend/src/api/routes/git/_helpers.py": 6888678759460652166, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/_version.py": 15336513445621042105, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_reshape.py": 5572426834735088916, + "venv/lib/python3.13/site-packages/pandas/tests/test_col.py": 14781555956509956371, + "venv/lib/python3.13/site-packages/PIL/__main__.py": 827665918723937817, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_float.py": 10455718110956856820, + "venv/lib/python3.13/site-packages/httpcore/_async/connection_pool.py": 932468678264370661, + "backend/src/models/assistant.py": 6168086159312177112, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/errors.py": 10750605030539582738, + "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/WHEEL": 13347410390513723930, + "venv/lib/python3.13/site-packages/pydantic/v1/dataclasses.py": 7833468513057242440, + "venv/lib/python3.13/site-packages/jose/backends/rsa_backend.py": 13507186353881931758, + "venv/lib/python3.13/site-packages/pygments/styles/stata_light.py": 16319330015324455277, + "venv/lib/python3.13/site-packages/pydantic_settings/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/jeepney/io/tests/test_trio.py": 7459901627473962540, + "venv/lib/python3.13/site-packages/pip/_internal/utils/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/f2py/src/fortranobject.c": 2519219289444051739, + "venv/lib/python3.13/site-packages/pandas/io/sas/sas_constants.py": 14492427939536692313, + "venv/lib/python3.13/site-packages/apscheduler/executors/base.py": 1980520974511822101, + "venv/lib/python3.13/site-packages/passlib/tests/test_context_deprecated.py": 7876919198015151018, + "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/RECORD": 9502524172853437600, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_upcast.py": 6496794053681247105, + "venv/lib/python3.13/site-packages/rapidfuzz/utils_cpp.cpython-313-x86_64-linux-gnu.so": 16145829319198889672, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/sessions.py": 5400174351499383072, + "venv/lib/python3.13/site-packages/_pytest/skipping.py": 10561585274103209115, + "venv/lib/python3.13/site-packages/pydantic/_internal/_generate_schema.py": 7343380095251236503, + "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/RECORD": 7844752443022514265, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/create.py": 16693261531832306870, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ndarray_shape_manipulation.py": 12598431412396627042, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_character.py": 17168707712220699738, + "venv/lib/python3.13/site-packages/PIL/ImageOps.py": 3884310863548175682, + "venv/lib/python3.13/site-packages/numpy/_core/_ufunc_config.pyi": 9291444594181042963, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/conftest.py": 8406022190801970256, + "venv/lib/python3.13/site-packages/pygments/lexers/func.py": 12257919753150082714, + "venv/lib/python3.13/site-packages/jeepney/io/tests/test_threading.py": 6656552495241857519, + "backend/src/core/__tests__/test_throttled_scheduler.py": 10949494963069000037, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_ints.py": 5616705208267050659, + "venv/lib/python3.13/site-packages/starlette/testclient.py": 16088848330558215194, + "backend/src/services/__tests__/test_resource_service.py": 14435810729789665272, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/x448.pyi": 11733449165276720901, + "backend/src/plugins/backup.py": 9697280948668596192, + "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/licenses/LICENSE": 3868190977070717994, + "venv/lib/python3.13/site-packages/pygments/lexers/haxe.py": 1915270891525929751, + "backend/src/services/notifications/__tests__/test_notification_service.py": 2377464079178202565, + "backend/tests/test_models.py": 3998442321184404421, + "venv/lib/python3.13/site-packages/numpy/core/multiarray.py": 18368443598659711836, + "venv/lib/python3.13/site-packages/_pytest/threadexception.py": 10464996327276961035, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_regression.py": 4611768764484593979, + "venv/lib/python3.13/site-packages/rsa/util.py": 13423880475585138638, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_amd64_unix.h": 16679226724722970205, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_item.py": 17619314877770917080, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_index_equal.py": 10617870815989304510, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_pop.py": 5700439923973346681, + "backend/src/services/clean_release/__tests__/test_audit_service.py": 1679130450221264383, + "venv/lib/python3.13/site-packages/pandas/core/_numba/executor.py": 2205130934861422769, + "venv/lib/python3.13/site-packages/_pytest/tracemalloc.py": 5530234062530858720, + "venv/lib/python3.13/site-packages/pandas/core/array_algos/datetimelike_accumulations.py": 14719894187829729175, + "venv/lib/python3.13/site-packages/pandas/core/ops/invalid.py": 10186546927135277579, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_constructors.py": 12118296571694122743, + "venv/lib/python3.13/site-packages/pip/_internal/models/index.py": 2839270935469791607, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_reductions.py": 17810094294132819208, + "backend/src/services/__init__.py": 15123879863527585264, + "venv/lib/python3.13/site-packages/numpy/f2py/f2py2e.pyi": 14181518821845356636, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py": 2873060848310644552, + "venv/lib/python3.13/site-packages/numpy/lib/_format_impl.pyi": 15745512451486888537, + "venv/lib/python3.13/site-packages/httpcore/_async/http11.py": 7565741048880549265, + "venv/lib/python3.13/site-packages/pandas/core/arrays/timedeltas.py": 7318851108932357510, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/inference.py": 2786441797589123900, + "frontend/src/routes/storage/backups/+page.svelte": 3437582895363203695, + "backend/src/models/dataset_review_pkg/_session_models.py": 14346374229033337012, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/array_like.py": 2778905818573625118, + "venv/lib/python3.13/site-packages/websockets/uri.py": 264376168984862773, + "frontend/src/routes/admin/roles/+page.svelte": 12213033002088655108, + "venv/lib/python3.13/site-packages/authlib/jose/jwk.py": 13560749074922587626, + "backend/tests/test_task_manager.py": 1706217361689506763, + "venv/lib/python3.13/site-packages/pip/_internal/commands/uninstall.py": 11003472265011262515, + "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/RECORD": 6488682447051657004, + "venv/lib/python3.13/site-packages/greenlet/tests/test_stack_saved.py": 7767924647054186572, + "venv/lib/python3.13/site-packages/pandas/api/__init__.py": 1145864207387918302, + "venv/lib/python3.13/site-packages/numpy/lib/_polynomial_impl.py": 12771179468704314203, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/array_pad.pyi": 14755435694146003896, + "frontend/src/lib/stores/assistantChat.js": 13807808925025688939, + "frontend/src/routes/+error.svelte": 16649211425103750277, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/datasource.pyi": 16752667877866430577, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_period.py": 3419297908709788013, + "venv/lib/python3.13/site-packages/pip/_internal/index/package_finder.py": 12631116673181479779, + "venv/lib/python3.13/site-packages/referencing/tests/test_core.py": 8280983300775878144, + "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_asof.py": 18383318159051131669, + "venv/lib/python3.13/site-packages/ecdsa/curves.py": 812969717084691115, + "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/oauth2_client.py": 6742098857947604729, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/cookies.py": 4997226307650350876, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/sync.py": 6189260178302531376, + "venv/lib/python3.13/site-packages/pygments/lexers/spice.py": 1376126320935115350, + "venv/lib/python3.13/site-packages/packaging/markers.py": 11602308203217831543, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_integer/foo90.f90": 3492659529812762861, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/fixed_string.f90": 8654788371538544793, + "frontend/src/routes/settings/EnvironmentsTab.svelte": 16246319452479236452, + "venv/lib/python3.13/site-packages/smmap/test/test_mman.py": 8977119225749253844, + "backend/src/models/dataset_review_pkg/__init__.py": 17789694606496652579, + "venv/lib/python3.13/site-packages/authlib/jose/drafts/__init__.py": 4770885427721822507, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py": 18229054440703471493, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ndarray.pyi": 14369009813591437391, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/provision.py": 17812733620485633975, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/socks.py": 17581293673028078134, + "venv/lib/python3.13/site-packages/httpcore/_backends/trio.py": 8394426709857317737, + "venv/lib/python3.13/site-packages/uvicorn/importer.py": 5449629250971153435, + "venv/lib/python3.13/site-packages/pip/_internal/operations/build/metadata_editable.py": 12494407279914732338, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_cross.py": 17742712348672218852, + "venv/lib/python3.13/site-packages/pygments/lexers/_cocoa_builtins.py": 15468347167912293394, + "backend/tests/services/clean_release/test_publication_service.py": 17277940125978730159, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_crosstab.py": 6631711416347033281, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_spss.py": 4865360315909532603, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_map.py": 4761668276463940924, + "venv/lib/python3.13/site-packages/passlib/tests/test_registry.py": 15377817946543064293, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_style.py": 6849613206971398029, + "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/licenses/LICENSE": 1424673738228762254, + "venv/lib/python3.13/site-packages/pandas/tests/apply/test_frame_apply_relabeling.py": 5600949862632014700, + "frontend/src/components/tasks/TaskLogPanel.svelte": 7807716346006847037, + "venv/lib/python3.13/site-packages/pandas/core/generic.py": 10174952306641057447, + "venv/lib/python3.13/site-packages/pygments/token.py": 6987328478153445584, + "venv/lib/python3.13/site-packages/pandas/core/indexers/objects.py": 409599993084996593, + "frontend/build/_app/immutable/chunks/DeED_3EO.js": 8217702195318248974, + "venv/lib/python3.13/site-packages/pydantic_settings/utils.py": 14396350455520958703, + "venv/lib/python3.13/site-packages/starlette/datastructures.py": 12097180838087229563, + "venv/lib/python3.13/site-packages/pandas/tests/apply/test_series_transform.py": 18159263102656491974, + "venv/lib/python3.13/site-packages/more_itertools/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/ma/core.pyi": 4829891173366130328, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_format.py": 5231304674283854194, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_head_tail.py": 13047338070883375426, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/status.py": 2214590120238144582, + "venv/lib/python3.13/site-packages/ecdsa/test_jacobi.py": 1249543655136186433, + "venv/lib/python3.13/site-packages/pandas/tests/io/sas/test_byteswap.py": 5090287710338893350, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/cmac.pyi": 16042272072158438261, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7009/__init__.py": 1923528231486706442, + "frontend/src/components/tools/DebugTool.svelte": 17267560034225794881, + "backend/src/scripts/__init__.py": 5400372851656282343, + "backend/src/services/clean_release/demo_data_service.py": 8795028177684641724, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_np_datetime.py": 18363183516409621205, + "backend/alembic_test.db": 14995253579408279707, + "venv/lib/python3.13/site-packages/pandas/api/interchange/__init__.py": 181911269762882069, + "backend/src/plugins/translate/superset_executor.py": 454191269003159946, + "venv/lib/python3.13/site-packages/pip/_internal/vcs/mercurial.py": 335201628064585213, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_timezones.py": 8042029178538467146, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/pickleable.py": 7712040860601343075, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/conftest.py": 17897502508251413988, + "venv/lib/python3.13/site-packages/passlib/utils/compat/__init__.py": 17369882236606423129, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-cbrt.csv": 8929553891827331780, + "venv/lib/python3.13/site-packages/starlette/middleware/cors.py": 11384893784314197864, + "venv/lib/python3.13/site-packages/urllib3/util/request.py": 9432067988603015849, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/packaging/_parser.py": 13394093358675095309, + "backend/src/schemas/dataset_review_pkg/_dtos.py": 13301885458265748263, + "venv/lib/python3.13/site-packages/numpy/_core/lib/libnpymath.a": 17555816778997028283, + "venv/lib/python3.13/site-packages/pyasn1/debug.py": 6305633719847955310, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_empty.py": 13006241382741353569, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arraypad.pyi": 12178766981280242620, + "venv/lib/python3.13/site-packages/passlib/tests/utils.py": 15403198680231294263, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_formats.py": 10263085276270778330, + "venv/lib/python3.13/site-packages/packaging/_musllinux.py": 2568338440457080365, + "venv/lib/python3.13/site-packages/numpy/polynomial/hermite.py": 6617116180936986481, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_value_counts.py": 5534713464063604301, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_missing.py": 3991199131191840903, + "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py2-np0-objarr.npy": 1615546603475261360, + "venv/lib/python3.13/site-packages/pygments/lexers/srcinfo.py": 16466766917840327610, + "venv/lib/python3.13/site-packages/PIL/ImageTk.py": 17063969714816564357, + "backend/src/plugins/translate/__tests__/test_sql_generator.py": 4467071942040481147, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_function_base.py": 15241999259666702619, + "venv/lib/python3.13/site-packages/passlib/tests/test_utils_md4.py": 15270721044910698449, + "venv/lib/python3.13/site-packages/pandas/__init__.py": 793698514004377486, + "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__init__.py": 3619091571279924576, + "venv/lib/python3.13/site-packages/numpy/linalg/_linalg.py": 9654828595008925976, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_compound.f90": 15012692484070385863, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/conversion.pyi": 16976386782787920450, + "backend/src/scripts/clean_release_tui.py": 7514485779948079886, + "backend/tests/core/migration/test_dry_run_orchestrator.py": 5158126098685923841, + "venv/lib/python3.13/site-packages/pip/_internal/metadata/importlib/__init__.py": 10834852742917144496, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_combine_first.py": 11425059090050867171, + "venv/lib/python3.13/site-packages/numpy/_typing/_extended_precision.py": 4201820494689784136, + "venv/lib/python3.13/site-packages/h11/_writers.py": 8585823094975171480, + "venv/lib/python3.13/site-packages/pandas/tests/frame/constructors/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/data_stmts.f90": 6123192245816905092, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-tan.csv": 6233924374281534211, + "venv/lib/python3.13/site-packages/numpy/_core/records.py": 11414828908088050950, + "venv/lib/python3.13/site-packages/authlib/integrations/sqla_oauth2/client_mixin.py": 17980753662107479779, + "venv/lib/python3.13/site-packages/pip/_internal/distributions/__init__.py": 1343779312297539596, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/columns.py": 8488246354818721406, + "venv/lib/python3.13/site-packages/pillow.libs/libtiff-295fd75c.so.6.2.0": 17866578820357536656, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_indexing.py": 14552860427542050771, + "venv/lib/python3.13/site-packages/apscheduler/schedulers/blocking.py": 3483388533957772681, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/pager.py": 18072372651585577904, + "venv/lib/python3.13/site-packages/pandas/tests/extension/uuid/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_s3.py": 17425535047477778484, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/datetimes/test_constructors.py": 3440572832185940666, + "venv/lib/python3.13/site-packages/pandas/core/reshape/reshape.py": 5387892304726762127, + "venv/lib/python3.13/site-packages/authlib/common/security.py": 2291133227104016684, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/scoping.py": 18223258186431020473, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/__init__.py": 16146480198665866916, + "venv/lib/python3.13/site-packages/pandas/io/formats/console.py": 10370334487585281127, + "venv/lib/python3.13/site-packages/PIL/BlpImagePlugin.py": 7209218638257731375, + "venv/lib/python3.13/site-packages/pandas/tests/frame/constructors/test_from_records.py": 12217508428903265797, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/rapidfuzz/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/rapidfuzz/fuzz.pyi": 7410507092040239447, + "venv/lib/python3.13/site-packages/numpy/testing/overrides.pyi": 17408923209056015964, + "venv/lib/python3.13/site-packages/authlib/jose/errors.py": 15231759002942436632, + "venv/lib/python3.13/site-packages/numpy/f2py/_backends/meson.build.template": 9935732744553086652, + "venv/lib/python3.13/site-packages/pandas/core/ops/array_ops.py": 10061020479033421596, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/traceback.py": 2535227583726271746, + "venv/lib/python3.13/site-packages/PIL/Image.py": 13615925856053031482, + "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/click/shell_completion.py": 5858110451169927373, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/literal.py": 5641382379370828704, + "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/WHEEL": 2823347510103459191, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_quantile.py": 11966156706220948315, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/lib_polynomial.pyi": 4266795948450921502, + "venv/lib/python3.13/site-packages/pip/_internal/locations/_distutils.py": 13559457590971821771, + "venv/lib/python3.13/site-packages/ecdsa/_compat.py": 13034064768731879065, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/pretty.py": 2448541115957861214, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/__init__.py": 15487893533770969571, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/immutabledict.pyx": 11739218099400096619, + "venv/lib/python3.13/site-packages/pygments/lexers/savi.py": 95400050302239274, + "venv/lib/python3.13/site-packages/_pytest/cacheprovider.py": 5114545353355728201, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py": 3369246520501764730, + "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/WHEEL": 7454950858448014158, + "venv/lib/python3.13/site-packages/numpy/testing/tests/test_utils.py": 5291132432383518704, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth1/__init__.py": 13749066369790839334, + "venv/lib/python3.13/site-packages/numpy/core/_dtype_ctypes.py": 17055498803745289016, + "venv/lib/python3.13/site-packages/pandas/core/window/numba_.py": 1444123360986509769, + "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/licenses/LICENSE": 4123064337800029995, + "venv/lib/python3.13/site-packages/greenlet/tests/test_throw.py": 4119265751381210399, + "venv/lib/python3.13/site-packages/fastapi/_compat/may_v1.py": 9880083742927480960, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/clsregistry.py": 1589692649837935355, + "venv/lib/python3.13/site-packages/multipart/decoders.py": 11346752866822986212, + "venv/lib/python3.13/site-packages/pytest/__init__.py": 6137272437722350673, + "venv/lib/python3.13/site-packages/pygments/formatters/other.py": 13124903290103898118, + "venv/lib/python3.13/site-packages/pygments/styles/borland.py": 17570558758932580659, + "frontend/src/lib/stores/__tests__/mocks/stores.js": 14098417748659067309, + "frontend/build/_app/immutable/chunks/hPO6SNG0.js": 8027434811274919386, + "frontend/build/_app/immutable/chunks/GYN4TzDb.js": 14558718419034086057, + "backend/src/schemas/__tests__/test_settings_and_health_schemas.py": 13526616847756581689, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/test_aggregate.py": 15038915958422670700, + "backend/src/core/utils/superset_context_extractor/_base.py": 15730081123519745401, + "backend/src/api/routes/translate/_run_routes.py": 6617818713940695689, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_interpolate.py": 7354685880215444946, + "venv/lib/python3.13/site-packages/jeepney/bindgen.py": 7094585345946943534, + "venv/lib/python3.13/site-packages/git/types.py": 13960696584646279283, + "frontend/build/_app/immutable/chunks/DsnmJJEf.js": 17416107759626312632, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_add_prefix_suffix.py": 4815518357936092237, + "backend/src/models/auth.py": 14495240302739907540, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py": 15294662527620994511, + "venv/lib/python3.13/site-packages/pandas/tests/base/test_fillna.py": 9297637486401098611, + "venv/lib/python3.13/site-packages/pygments/lexers/sgf.py": 6802546617450210439, + "venv/lib/python3.13/site-packages/pip/_internal/commands/configuration.py": 1608697090566485893, + "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/WHEEL": 7314207500206292683, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_api.py": 13739775047493489785, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_common.py": 9446944526144164255, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_constructors.py": 2786493777737472376, + "venv/lib/python3.13/site-packages/numpy/testing/print_coercion_tables.py": 11758491058846790938, + "venv/lib/python3.13/site-packages/pandas/core/internals/ops.py": 4740509262379451243, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/methods.py": 8059372834301557565, + "venv/lib/python3.13/site-packages/pandas/tests/extension/decimal/__init__.py": 4761936067832638585, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/style.py": 12654125988995362593, + "venv/lib/python3.13/site-packages/pandas/tests/strings/test_api.py": 10514120091521589282, + "venv/lib/python3.13/site-packages/iniconfig/_parse.py": 11418441008427032090, + "venv/lib/python3.13/site-packages/pandas/tests/generic/test_label_or_level_utils.py": 3423644585913150841, + "venv/lib/python3.13/site-packages/greenlet/greenlet_thread_support.hpp": 7773996382104849037, + "venv/lib/python3.13/site-packages/pandas/tests/strings/__init__.py": 5060190599837877599, + "venv/lib/python3.13/site-packages/uvicorn/supervisors/__init__.py": 15971002342788501945, + "venv/lib/python3.13/site-packages/websockets/legacy/framing.py": 12759022769700230409, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_logical_ops.py": 4438104524064299182, + "venv/lib/python3.13/site-packages/numpy/_core/cversions.py": 9724467539503410469, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_console.py": 11657469008721907972, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/datetimes/test_reductions.py": 1713831062142840146, + "venv/lib/python3.13/site-packages/pygments/modeline.py": 6194365452131561478, + "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pygments/lexers/varnish.py": 3608370156692616959, + "venv/lib/python3.13/site-packages/gitdb/db/base.py": 3375449584942578385, + "venv/lib/python3.13/site-packages/pygments/lexers/macaulay2.py": 8022891778577314212, + "frontend/src/services/taskService.js": 11368052191249479533, + "backend/src/api/routes/translate/_preview_routes.py": 16234500901707822839, + "backend/src/models/connection.py": 7467905739729526898, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_arithmetic.py": 11620920548943067949, + "frontend/src/lib/stores/__tests__/test_sidebar.js": 113566022614194798, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_misc.py": 16393451200513733144, + "backend/src/services/dataset_review/repositories/repository_pkg/_mutations.py": 11718998093543631478, + "venv/lib/python3.13/site-packages/typing_inspection/typing_objects.py": 16856718240972376883, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/test_nat.py": 7400568652984772972, + "venv/lib/python3.13/site-packages/pydantic/config.py": 1450073446492183677, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/jwt_bearer.py": 6973859422068490274, + "venv/lib/python3.13/site-packages/_pytest/config/__init__.py": 12787579251203510616, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/fields.py": 10090313990429695520, + "venv/lib/python3.13/site-packages/pandas/core/arrays/_utils.py": 11574890848475750509, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/util.py": 9373891872370603585, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_julian_date.py": 850085757774959003, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/cli.py": 6382193953727339271, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_comment.py": 5603075924832933306, + "venv/lib/python3.13/site-packages/PIL/features.py": 3481835375516797487, + "venv/lib/python3.13/site-packages/numpy/f2py/use_rules.pyi": 18395688193449688360, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_ufunc.py": 3326502989311831856, + "frontend/build/_app/immutable/entry/start.Q6F0ayD4.js": 9740583146990910165, + "venv/lib/python3.13/site-packages/uvicorn/server.py": 15714008348292243019, + "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/WHEEL": 17410883677306885019, + "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/licenses/LICENSE": 6653202620765194772, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_truncate.py": 6478126371906156409, + "venv/lib/python3.13/site-packages/itsdangerous/exc.py": 11181421963755661477, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/syntax.py": 18070449217682341495, + "venv/lib/python3.13/site-packages/pydantic/plugin/_loader.py": 792663962985957671, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame_groupby.py": 12709199432838648700, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_pickle.py": 6232650893735715362, + "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/pandas/io/excel/_pyxlsb.py": 10035587889954731645, + "venv/lib/python3.13/site-packages/git/refs/log.py": 5175801243332619268, + "venv/lib/python3.13/site-packages/pygments/lexers/numbair.py": 17382646010399794697, + "venv/lib/python3.13/site-packages/git/util.py": 7424113599506269603, + "frontend/src/routes/migration/mappings/+page.svelte": 4566398049366943407, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_add_prefix_suffix.py": 8367793476246001244, + "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/INSTALLER": 17282701611721059870, + "backend/src/plugins/llm_analysis/scheduler.py": 14673660738877638091, + "venv/lib/python3.13/site-packages/pandas/tests/extension/list/array.py": 8065856342131357421, + "venv/lib/python3.13/site-packages/starlette/middleware/errors.py": 1337979724602864440, + "venv/lib/python3.13/site-packages/numpy/_configtool.py": 2946953194004101078, + "backend/src/services/dataset_review/repositories/__tests__/test_session_repository.py": 18317443814450756666, + "venv/lib/python3.13/site-packages/numpy/f2py/diagnose.pyi": 8607205990821121708, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_categorical.py": 1329115818264527103, + "venv/lib/python3.13/site-packages/pygments/lexers/minecraft.py": 10133579583270214110, + "backend/src/api/routes/dashboards/_router.py": 8641007051243802224, + "venv/lib/python3.13/site-packages/jeepney/io/__init__.py": 2568357146184259175, + "venv/lib/python3.13/site-packages/pygments/formatters/groff.py": 6480830447984073574, + "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/keywrap.py": 10424037418528425800, + "venv/lib/python3.13/site-packages/pip/_internal/utils/hashes.py": 17310331665429541812, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_series.py": 16418555817529711079, + "venv/lib/python3.13/site-packages/pandas/tests/window/moments/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/multipart/exceptions.py": 8580522204565816132, + "venv/lib/python3.13/site-packages/pygments/lexers/floscript.py": 944042220740663491, + "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/licenses/LICENSE": 6808958893921859106, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/help.py": 3124654001287175629, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py": 9623344347519683527, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_html.py": 7732330576029516854, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/markup.py": 5413800947725916538, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_multithreading.py": 12547048606098186681, + "venv/lib/python3.13/site-packages/pandas/core/computation/align.py": 13027916530558901817, + "venv/lib/python3.13/site-packages/authlib/integrations/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_subclass.py": 10949229764696473939, + "frontend/src/routes/migration/+page.svelte": 7821836225621176583, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_rolling_quantile.py": 4225791949463893856, + "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/WHEEL": 2357997949040430835, + "venv/lib/python3.13/site-packages/ecdsa/ellipticcurve.py": 15624510849346629163, + "venv/lib/python3.13/site-packages/itsdangerous/serializer.py": 14271229091461164933, + "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/RECORD": 11788532857617688351, + "venv/lib/python3.13/site-packages/pygments/lexers/modula2.py": 6575895980250662093, + "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/METADATA": 6502714564886871819, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/hashes.pyi": 7385072545198817771, + "frontend/src/lib/stores/__tests__/mocks/navigation.js": 10782992736745205981, + "frontend/src/lib/components/reports/__tests__/reports_page.integration.test.js": 16463062768004950066, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_unsupported.py": 9115440166074553860, + "frontend/build/_app/immutable/chunks/BwABSOzi.js": 12485550378293032886, + "venv/lib/python3.13/site-packages/pandas/core/sparse/api.py": 9094651265692218821, + "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/METADATA": 12295263493152296681, + "venv/lib/python3.13/site-packages/apscheduler/jobstores/redis.py": 11313520438600819390, + "venv/lib/python3.13/site-packages/pyasn1/compat/integer.py": 7365270971777550573, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/common/gh19161.f90": 7691871466158049079, + "venv/lib/python3.13/site-packages/urllib3/http2/__init__.py": 62108352209996836, + "venv/lib/python3.13/site-packages/starlette/types.py": 6372850128669191435, + "venv/lib/python3.13/site-packages/httpx/_types.py": 12461152436147308761, + "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/METADATA": 4954909612926293404, + "venv/lib/python3.13/site-packages/apscheduler/triggers/cron/__init__.py": 14268033439436797364, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/proxy.py": 13008557041076030198, + "venv/lib/python3.13/site-packages/pydantic/v1/annotated_types.py": 12925825196690299531, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_rolling_functions.py": 2255133375699086300, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/dtypes.pyi": 15114028503984522176, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_index.py": 1551768441102117466, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/numpy_/test_indexing.py": 13703573544185051910, + "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/passlib/crypto/scrypt/_gen_files.py": 11149423183253618378, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py": 7707707044867513457, + "venv/lib/python3.13/site-packages/pygments/styles/emacs.py": 4540417536507245024, + "frontend/src/lib/components/layout/TaskDrawer.svelte": 6654829127438406248, + "frontend/build/_app/immutable/chunks/B-mQ6-tM.js": 4944461039645576568, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_align.py": 6050248183327934780, + "venv/lib/python3.13/site-packages/pip/_internal/cache.py": 210214424535803946, + "venv/lib/python3.13/site-packages/pyasn1/type/error.py": 1479878696626082697, + "venv/lib/python3.13/site-packages/pandas/api/executors/__init__.py": 16954693181142010028, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_dropna.py": 17272771246760486947, + "backend/logs/app.log.5": 4675888931719915527, + "venv/lib/python3.13/site-packages/pillow.libs/libwebp-d8b9687f.so.7.2.0": 116215608212382352, + "venv/lib/python3.13/site-packages/pandas/core/indexes/base.py": 496956059910401121, + "venv/lib/python3.13/site-packages/httpcore/_async/http2.py": 16350817785499681224, + "venv/lib/python3.13/site-packages/numpy/testing/_private/utils.pyi": 17639497083041129225, + "venv/lib/python3.13/site-packages/greenlet/platform/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_cisco.py": 1786475632318043669, + "venv/lib/python3.13/site-packages/jsonschema/_legacy_keywords.py": 16481666133229715822, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/arrayterator.py": 3076316311545674897, + "venv/lib/python3.13/site-packages/websockets/http11.py": 11022534176398053082, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/lib_version.pyi": 18093228021414910594, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/__init__.py": 11009955319062089477, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/dtype.py": 5553691086633215767, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/dtype.py": 9342837412280377086, + "venv/lib/python3.13/site-packages/pip/_vendor/truststore/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_index.py": 10837492069089787028, + "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_openpyxl.py": 9542840527483618681, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/array_like.pyi": 1968570015765106620, + "venv/lib/python3.13/site-packages/numpy/f2py/_backends/__init__.py": 4265012785873472600, + "venv/lib/python3.13/site-packages/sqlalchemy/util/__init__.py": 15821192733097665649, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_drop_duplicates.py": 12601598844977346213, + "venv/lib/python3.13/site-packages/pygments/lexers/fortran.py": 6859638690245583968, + "frontend/src/routes/+layout.ts": 8392799000856778740, + "frontend/build/_app/immutable/chunks/DoN5Iioi.js": 15272261932303525049, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_stride_tricks.py": 1064774375151677743, + "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/REQUESTED": 15130871412783076140, + "frontend/build/favicon.png": 16740551781088792605, + "venv/lib/python3.13/site-packages/numpy/char/__init__.py": 4428218097234308768, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/base.py": 14734745827490220667, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_formats.py": 2743326139283991615, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/einsumfunc.py": 9944042006439149561, + "backend/src/plugins/translate/__tests__/test_dictionary.py": 17973099740734727535, + "backend/src/api/routes/git/_config_routes.py": 6093940098740660772, + "venv/lib/python3.13/site-packages/pydantic/_internal/_core_metadata.py": 5704137311404303574, + "venv/lib/python3.13/site-packages/pip/_internal/vcs/bazaar.py": 5630511062945201327, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_get_level_values.py": 12490886203425393290, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_hist_method.py": 2109257663592928, + "venv/lib/python3.13/site-packages/pandas/tests/reductions/__init__.py": 16150188643076000414, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/padding.py": 7470483648602195268, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/foo.f": 6349567809926050379, + "venv/lib/python3.13/site-packages/apscheduler/triggers/calendarinterval.py": 12481650996882675949, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_dropna.py": 3168969662216766293, + "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/utils.py": 617314300548081590, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_dtypes.py": 2313400030798822026, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/util.py": 2176177435997659667, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/dh.pyi": 3254633032236269842, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_sh_gcc.h": 3574353221233465215, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/aead.py": 8740476942925346444, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/chararray.pyi": 9683985931409244216, + "venv/lib/python3.13/site-packages/authlib/oidc/core/models.py": 1370998944213578392, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_equals.py": 9903250467264848187, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/util.py": 2866856507760047219, + "venv/lib/python3.13/site-packages/rapidfuzz/fuzz_cpp_avx2.cpython-313-x86_64-linux-gnu.so": 12574929384355254457, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_setops.py": 10550603120090683350, + "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/utils.py": 8633356372647820801, + "venv/lib/python3.13/site-packages/passlib/hosts.py": 11171402820797380938, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/interval/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/requests/auth.py": 912521029864094489, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_loc.py": 17839380711970027363, + "venv/lib/python3.13/site-packages/pandas/io/parsers/arrow_parser_wrapper.py": 11745253171984606573, + "venv/lib/python3.13/site-packages/greenlet/greenlet_compiler_compat.hpp": 465436185632243991, + "venv/lib/python3.13/site-packages/starlette/staticfiles.py": 11256974306202558529, + "venv/lib/python3.13/site-packages/pandas/_libs/writers.pyi": 10494773026133244183, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_arithmetic.py": 17043958604607035425, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/generic.py": 17755579497690180620, + "venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.cpython-313-x86_64-linux-gnu.so": 13917011878259810696, + "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/RECORD": 16558021503346615951, + "venv/lib/python3.13/site-packages/pyasn1/codec/native/decoder.py": 15336592439379775543, + "venv/lib/python3.13/site-packages/_pytest/recwarn.py": 15312759837838083596, + "venv/lib/python3.13/site-packages/numpy/lib/_arrayterator_impl.pyi": 2744889206277418389, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_read_fwf.py": 13684564756628524738, + "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/METADATA": 15888766579203341778, + "venv/lib/python3.13/site-packages/itsdangerous/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/git/index/fun.py": 18338379518768926372, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Hamming_py.py": 16133454512601432815, + "venv/lib/python3.13/site-packages/pip/_internal/req/req_set.py": 534498057184394558, + "venv/lib/python3.13/site-packages/pygments/console.py": 3540660073442483692, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/openssl/__init__.py": 14571251344510763303, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/multiarray.py": 3224049574922696945, + "venv/lib/python3.13/site-packages/pygments/formatters/html.py": 1918541918263449443, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py": 2569618488804767890, + "venv/lib/python3.13/site-packages/keyring/backends/Windows.py": 4103830951042463184, + "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/unix.py": 13808187640638677050, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh23598Warn.f90": 7589258200398878529, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_extension.py": 17245018204352082978, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/_py_row.py": 918740441973901148, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/url.py": 1558176735588971829, + "venv/lib/python3.13/site-packages/pygments/styles/friendly.py": 13799024964803741891, + "venv/lib/python3.13/site-packages/anyio/_core/_resources.py": 8463932838064724564, + "venv/lib/python3.13/site-packages/numpy/rec/__init__.pyi": 1782063765067823837, + "venv/lib/python3.13/site-packages/apscheduler/jobstores/rethinkdb.py": 16413299263077970925, + "venv/lib/python3.13/site-packages/pandas/core/arrays/_mixins.py": 14293848457775268010, + "venv/lib/python3.13/site-packages/pygments/lexers/berry.py": 4540479522141160472, + "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/WHEEL": 18203500019759199992, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_array_to_datetime.py": 3467480435568672840, + "backend/src/core/migration/dry_run_orchestrator.py": 10939333902806918334, + "venv/lib/python3.13/site-packages/passlib/handlers/misc.py": 18255658377109333797, + "venv/lib/python3.13/site-packages/greenlet/TGreenlet.hpp": 6993553444981671786, + "backend/src/services/mapping_service.py": 5976063352141712188, + "venv/lib/python3.13/site-packages/passlib/tests/test_crypto_des.py": 7198931807116651263, + "frontend/src/routes/profile/__tests__/profile-settings-state.integration.test.js": 3674649277514557450, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/stride_tricks.pyi": 8257977532799807847, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/common.py": 9354442374897565498, + "venv/lib/python3.13/site-packages/pandas/core/tools/timedeltas.py": 7221393731712061943, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_formats.py": 2601044064677727807, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/__init__.py": 9884041726796476720, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/__init__.py": 9106887608906281686, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-log2.csv": 13979868713429167140, + "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/passlib/tests/test_apache.py": 12515001998770565375, "venv/lib/python3.13/site-packages/pygments/styles/friendly_grayscale.py": 16840106759769837649, - "venv/lib/python3.13/site-packages/pip/_internal/utils/packaging.py": 15555781487631855604, - "venv/lib/python3.13/site-packages/pygments/lexers/jvm.py": 897669925284011089, + "frontend/src/routes/admin/settings/+page.svelte": 6706258642200752989, + "frontend/src/services/gitService.js": 7044825476968825357, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/authorization_server.py": 2913800420669417152, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/pcg64-testset-2.csv": 5423145402839576695, + "venv/lib/python3.13/site-packages/numpy/_core/_string_helpers.pyi": 12043292763005874815, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py": 14995426099932276090, + "venv/lib/python3.13/site-packages/pandas/_config/localization.py": 11197430792343731762, + "venv/lib/python3.13/site-packages/ecdsa/test_ecdsa.py": 17595025859105321562, + "venv/lib/python3.13/site-packages/authlib/integrations/starlette_client/integration.py": 3085827906490477329, + "venv/lib/python3.13/site-packages/pygments/lexers/futhark.py": 10102535873433759899, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_character.py": 850554362222586199, + "venv/lib/python3.13/site-packages/pygments/lexers/carbon.py": 6753624244809529841, + "venv/lib/python3.13/site-packages/jeepney/low_level.py": 6546206679858571608, + "backend/src/services/clean_release/compliance_orchestrator.py": 10585043190405940115, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_array_api_info.py": 16491629653122827368, + "backend/tests/test_translate_jobs.py": 16585154497956900995, + "venv/lib/python3.13/site-packages/pygments/lexers/mosel.py": 1877592344012288438, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/protocol.py": 932296179250981622, + "run_clean_tui.sh": 14305120550349644601, + "frontend/build/_app/immutable/chunks/BoPlBqXU.js": 15621955836226522600, + "venv/lib/python3.13/site-packages/pandas/tests/window/moments/test_moments_consistency_rolling.py": 11118160360072677569, + "venv/lib/python3.13/site-packages/pillow.libs/libwebpmux-7f11e5ce.so.3.1.2": 13105940093474848953, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/aiomysql.py": 12999962380333482189, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_setops.py": 17350958906336246626, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/config.py": 10707678943298497676, + "venv/lib/python3.13/site-packages/numpy/_core/_struct_ufunc_tests.cpython-313-x86_64-linux-gnu.so": 13269324887178340397, + "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/core/indexers/utils.py": 5861072573716897447, + "frontend/svelte.config.js": 8473636210766821560, + "venv/lib/python3.13/site-packages/numpy/version.pyi": 5075382895829889457, + "frontend/src/lib/components/translate/ScheduleConfig.svelte": 17692328825363231497, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_ewm.py": 13789842684407148830, + "venv/lib/python3.13/site-packages/attr/_typing_compat.pyi": 2823472697859409877, + "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/METADATA": 15970460108484113417, + "frontend/src/lib/stores/activity.js": 14023344642294459631, + "venv/lib/python3.13/site-packages/numpy/_core/_type_aliases.py": 1644743742545329282, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_chained_assignment_deprecation.py": 7694137945143129474, + "venv/lib/python3.13/site-packages/requests/sessions.py": 6936640856092320928, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py": 16550134594771291234, + "backend/alembic/versions/2a7b8c9d0e1f_add_target_languages_to_translation_jobs.py": 7201017059554918882, + "venv/lib/python3.13/site-packages/passlib/handlers/des_crypt.py": 15304795681178011265, + "venv/lib/python3.13/site-packages/greenlet/PyGreenlet.cpp": 7360124705400993707, + "venv/lib/python3.13/site-packages/keyring/backend.py": 14185808762437650951, + "venv/lib/python3.13/site-packages/referencing/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_categorical.py": 10863578845754547294, + "venv/lib/python3.13/site-packages/pip/_internal/utils/appdirs.py": 539688259643447487, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_unicode.py": 1103608932321322989, + "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/METADATA": 2604612822968175379, + "backend/src/core/utils/superset_context_extractor/_recovery.py": 13298493527065532076, + "venv/lib/python3.13/site-packages/pandas/core/window/__init__.py": 15180240812077877933, + "backend/src/services/dataset_review/orchestrator_pkg/_commands.py": 15474996500928063708, + "venv/lib/python3.13/site-packages/jeepney/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_wrap.py": 17083394123203662880, + "venv/lib/python3.13/site-packages/anyio/__init__.py": 12752386096259972231, + "venv/lib/python3.13/site-packages/pip/_vendor/tomli/_types.py": 7175518113263603341, + "venv/lib/python3.13/site-packages/numpy/_core/lib/npy-pkg-config/mlib.ini": 16151597765994697599, + "venv/lib/python3.13/site-packages/pandas/core/methods/to_dict.py": 4653628436393663485, + "venv/lib/python3.13/site-packages/rapidfuzz/process_cpp_impl.cpython-313-x86_64-linux-gnu.so": 11767771662042404251, + "backend/src/api/routes/translate/_correction_routes.py": 12437040676784563126, + "frontend/src/routes/storage/repos/+page.svelte": 3849368960648953479, + "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/android.py": 8607703347015852195, + "venv/lib/python3.13/site-packages/referencing/_core.py": 14744795336121513230, + "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py2-objarr.npz": 9481735723557962988, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_resolution.py": 6076761538303762297, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_describe.py": 14493300276133670380, + "frontend/src/lib/i18n/index.ts": 2428391315568750665, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/layout.py": 11554376181083053156, + "venv/lib/python3.13/site-packages/numpy/lib/_utils_impl.pyi": 15767546107856827543, + "venv/lib/python3.13/site-packages/pandas/core/accessor.py": 1188716996113179524, + "venv/lib/python3.13/site-packages/PIL/GimpPaletteFile.py": 2040459799887417392, + "venv/lib/python3.13/site-packages/authlib/integrations/sqla_oauth2/__init__.py": 12911374670789466961, + "venv/lib/python3.13/site-packages/pandas/core/interchange/utils.py": 11401792151107207834, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/provision.py": 13156893203444936309, + "venv/lib/python3.13/site-packages/pygments/lexers/bare.py": 18266858932333619451, + "venv/lib/python3.13/site-packages/uvicorn/protocols/http/auto.py": 15947958974558543657, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/serializer.py": 8708767945343850816, + "venv/lib/python3.13/site-packages/pandas/_testing/__init__.py": 10127496080404419481, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_timer.py": 8196003526646080498, + "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_windows.py": 9171347078900057357, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_rename.py": 16148017223289654683, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_size.py": 14032390806503440750, + "venv/lib/python3.13/site-packages/pandas/tests/tools/test_to_timedelta.py": 364160935264757405, + "venv/lib/python3.13/site-packages/numpy/typing/__init__.py": 3608665862759745048, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/gitdb/db/mem.py": 15823812455996199993, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/session.py": 15060063324909302240, + "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/METADATA": 15658158392301945427, + "venv/lib/python3.13/site-packages/pip/_internal/operations/build/metadata_legacy.py": 9226692984248279094, + "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/RECORD": 10264342250058307301, + "venv/lib/python3.13/site-packages/cryptography/x509/oid.py": 13909847799728532602, + "venv/lib/python3.13/site-packages/numpy/lib/_iotools.py": 10522826373041592861, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/warnings_and_errors.pyi": 3151453262733619734, + "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/passlib/crypto/scrypt/__init__.py": 4390908730288159455, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/parameters.py": 5104015814292812973, + "venv/lib/python3.13/site-packages/pandas/core/groupby/grouper.py": 18110568057754450953, + "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/WHEEL": 9100692507274722091, + "venv/lib/python3.13/site-packages/pandas/core/reshape/encoding.py": 12702053687535473061, + "venv/lib/python3.13/site-packages/pygments/lexers/nit.py": 18262907536841158657, + "venv/lib/python3.13/site-packages/pygments/unistring.py": 18037463865539157041, + "venv/lib/python3.13/site-packages/pandas/io/formats/templates/typst.tpl": 5055419073126793137, + "frontend/build/_app/immutable/nodes/17.D5QBfMD6.js": 12662533799528506371, + "backend/src/scripts/init_auth_db.py": 7729644769215024318, + "backend/src/core/task_manager/__tests__/test_context.py": 4211690952139950648, + "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/WHEEL": 9788895026336324100, + "venv/lib/python3.13/site-packages/numpy/tests/test_scripts.py": 5790352482583964348, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_cov_corr.py": 13652339565475304679, + "venv/lib/python3.13/site-packages/apscheduler/schedulers/__init__.py": 16211693040174992594, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_util.py": 16908393806993809474, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_multi.py": 4612816468258071810, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_c_parser_only.py": 3038797485572319804, + "frontend/src/lib/ui/LanguageSwitcher.svelte": 3899734183369041614, + "backend/src/services/notifications/service.py": 7032100258837946310, + "backend/src/services/clean_release/report_builder.py": 6953311294119437848, + "venv/lib/python3.13/site-packages/pip/_internal/vcs/git.py": 17060377178521307324, + "venv/lib/python3.13/site-packages/numpy/lib/_histograms_impl.py": 13446184230476677032, + "backend/src/plugins/translate/prompt_builder.py": 9619706108488955349, + "venv/lib/python3.13/site-packages/sqlalchemy/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/_libs/arrays.pyi": 12629806207276576877, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_normalize.py": 3203921571999101793, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_timezones.py": 15157687716502574038, + "venv/lib/python3.13/site-packages/cryptography/hazmat/asn1/asn1.py": 8801155055979135092, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/adapters.py": 2224938661429637508, + "venv/lib/python3.13/site-packages/greenlet/TGreenlet.cpp": 4121171004360760212, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/__init__.py": 237070345717942041, + "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/RECORD": 3288669175908898843, + "venv/lib/python3.13/site-packages/sqlalchemy/event/api.py": 987321343916500337, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/expression.py": 1248029135284283479, + "venv/lib/python3.13/site-packages/numpy/core/_multiarray_umath.py": 10973016138581934035, + "venv/lib/python3.13/site-packages/numpy/conftest.py": 8971879716683736515, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/__multiarray_api.c": 9134176670140958459, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/_initialize_py.py": 6518425342376455821, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh25211.pyf": 10179237442840865225, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_sort_index.py": 14187428934500390688, + "venv/lib/python3.13/site-packages/ecdsa/_rwlock.py": 7971972922423146201, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_limited_api.py": 15788485008764560845, + "venv/lib/python3.13/site-packages/passlib/tests/test_pwd.py": 10089138951415591061, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_mixins.py": 12351373879926460719, + "venv/lib/python3.13/site-packages/numpy/_core/strings.pyi": 1981676808027146409, + "venv/lib/python3.13/site-packages/yaml/tokens.py": 2279161238859232338, + "venv/lib/python3.13/site-packages/dateutil/tz/_common.py": 9009072600471223066, + "venv/lib/python3.13/site-packages/pygments/lexers/sieve.py": 15757825442941567629, + "backend/src/core/encryption_key.py": 10697135200011385036, + "venv/lib/python3.13/site-packages/pip/_vendor/truststore/__init__.py": 1948986914452928735, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py": 14571251344510763303, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_drop.py": 17940792593116540139, + "backend/src/core/__tests__/test_superset_profile_lookup.py": 15590077829286810714, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_rowcount.py": 6682800584070485197, + "backend/src/services/reports/__tests__/test_report_service.py": 6045116534626984913, + "backend/src/models/translate.py": 1714500577896249923, + "venv/lib/python3.13/site-packages/cryptography/x509/__init__.py": 4066756344049307453, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/cmac.py": 12011944740750051829, + "venv/lib/python3.13/site-packages/numpy/_core/function_base.py": 5676611841071163854, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_json_table_schema_ext_dtype.py": 13021437050542691118, + "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/response.py": 9594165377270752433, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/validation": 5398883285871046811, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/_cmd.py": 14954780419054588164, + "venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/WHEEL": 2357997949040430835, + "venv/lib/python3.13/site-packages/pygments/lexers/_julia_builtins.py": 8846111188795315103, + "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/lib/array_utils.pyi": 14646405559193539455, + "backend/src/plugins/git/llm_extension.py": 9347391649685791102, + "backend/alembic/versions/aa1b2c3d4e5f_drop_deprecated_translate_columns.py": 15433357059529111004, + "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py": 6109201994963211857, + "venv/lib/python3.13/site-packages/pydantic/mypy.py": 10034028441785588819, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_period_range.py": 9598872514187524706, + "venv/lib/python3.13/site-packages/pandas/io/parsers/python_parser.py": 7297059565749331767, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_common.py": 3559416690270529904, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_arraypad.py": 4907193085512342162, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_searchsorted.py": 12074107109575676897, + "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/_pytest/main.py": 931018496411984909, + "backend/src/api/routes/translate/_job_routes.py": 217949482917696293, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame_legend.py": 13094583566764796297, + "venv/lib/python3.13/site-packages/more_itertools/more.py": 1631135582441184133, + "venv/lib/python3.13/site-packages/fastapi/openapi/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/traversals.py": 1188980668212022868, + "venv/lib/python3.13/site-packages/PIL/GimpGradientFile.py": 9777026720028891154, + "venv/lib/python3.13/site-packages/dateutil/tz/tz.py": 5258008267965780817, + "venv/lib/python3.13/site-packages/attrs/setters.py": 5775359015245600592, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_hashing.py": 11119284926039008503, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/dml.py": 17308908394336670576, + "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/limited_api_latest.c": 5472633890472031814, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/mod_derived_types.f90": 2556756676983488922, + "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/RECORD": 12575893084587070195, + "venv/lib/python3.13/site-packages/pandas/core/arrays/datetimes.py": 10585034798097337148, + "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/RECORD": 8104089156015954779, + "venv/lib/python3.13/site-packages/pygments/formatters/latex.py": 7514533384468139434, + "venv/lib/python3.13/site-packages/pygments/lexers/sql.py": 15566262873151658034, + "frontend/static/favicon.png": 16740551781088792605, + "frontend/src/lib/ui/Input.svelte": 8632025378593263475, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_client/__init__.py": 9997757256282720103, + "venv/lib/python3.13/site-packages/fastapi/openapi/constants.py": 1111838716953068285, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth1/nonce.py": 18219168479887197733, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/__init__.py": 4752355175318979033, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_ratio.py": 940213488194452623, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/test_na_scalar.py": 13950899907067372014, + "venv/lib/python3.13/site-packages/pygments/lexers/cddl.py": 1303059539145221713, + "venv/lib/python3.13/site-packages/numpy/f2py/__version__.pyi": 9759534743796867731, + "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/WHEEL": 2357997949040430835, + "venv/lib/python3.13/site-packages/pygments/lexers/maple.py": 7402467573829618534, + "venv/lib/python3.13/site-packages/sqlalchemy/util/_collections.py": 7772437080414835462, + "venv/lib/python3.13/site-packages/numpy/testing/tests/__init__.py": 15130871412783076140, + "frontend/src/lib/ui/Select.svelte": 11354509509920497811, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/misc/extended_precision.pyi": 6541258584648209609, + "venv/lib/python3.13/site-packages/pandas/util/_decorators.py": 14780166101442507665, + "backend/src/services/dataset_review/__init__.py": 15781509495698823404, + "venv/lib/python3.13/site-packages/click/_textwrap.py": 14586282603215052310, + "backend/alembic/env.py": 12540388445794983398, + "venv/lib/python3.13/site-packages/pygments/lexers/nix.py": 15050086330545600356, + "venv/lib/python3.13/site-packages/uvicorn/_types.py": 3339389597716220585, + "venv/lib/python3.13/site-packages/greenlet/greenlet.cpp": 3281107194043468270, + "backend/src/services/__tests__/test_llm_plugin_persistence.py": 2413185961042174999, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/_py_util.py": 13997184496924925110, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Postfix.pyi": 2700568594438441246, + "frontend/src/lib/components/health/PolicyForm.svelte": 10697252862978287099, + "venv/lib/python3.13/site-packages/pandas/core/window/rolling.py": 6316477981414191778, + "venv/lib/python3.13/site-packages/pip/_internal/vcs/versioncontrol.py": 13742159245132241245, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_overlaps.py": 8287716909793575347, + "venv/lib/python3.13/site-packages/ecdsa/test_malformed_sigs.py": 2606295770491528880, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_insert.py": 6970975909565157982, + "venv/lib/python3.13/site-packages/sqlalchemy/util/queue.py": 1289234691048379395, + "venv/lib/python3.13/site-packages/tzlocal/unix.py": 995019116408448073, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/scoping.py": 17216492046645992674, + "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/licenses/LICENSE": 13479831069780783137, + "frontend/build/favicon.svg": 6451919037497541980, + "backend/src/core/auth/repository.py": 7314530014494164270, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/astype.py": 9925183787411796679, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_nunique.py": 8450574751331186943, + "venv/lib/python3.13/site-packages/pandas/tests/apply/test_series_apply.py": 12349190935180702377, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/requirements.py": 11478648568700804705, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_rename_axis.py": 13193478481758930128, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_loop.py": 6502725909989655118, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_protocols.py": 5019018645804313814, + "backend/src/api/routes/__tests__/test_tasks_logs.py": 15585487936320145746, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_encoding.py": 6622474614967398246, + "backend/src/models/task.py": 10701108436197477339, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/transform/test_numba.py": 8250377774229977284, + "venv/lib/python3.13/site-packages/numpy/random/_philox.pyi": 7503018432880404291, + "backend/src/core/utils/dataset_mapper.py": 13413818977924738147, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_join.py": 1364886077926604427, + "venv/lib/python3.13/site-packages/anyio/to_thread.py": 12767326212338028587, + "venv/lib/python3.13/site-packages/apscheduler/events.py": 10748346950729364899, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_conversion.py": 12184280074522274780, + "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/METADATA": 2810323639884879396, + "frontend/src/lib/components/assistant/__tests__/assistant_chat.integration.test.js": 13047006106880121194, + "venv/lib/python3.13/site-packages/sqlalchemy/event/legacy.py": 13465911776953636949, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/util.py": 955993052529739164, + "backend/src/services/git/_gitea.py": 3585029718367932699, + "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/__init__.py": 13584649922093198724, + "venv/lib/python3.13/site-packages/pygments/plugin.py": 18006138132596504101, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_filters.py": 17271344092069736999, + "backend/src/services/git_service.py": 1930048176535450912, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/hooks.py": 8580161128344266243, + "venv/lib/python3.13/site-packages/numpy/_globals.pyi": 12839879732484756979, + "venv/lib/python3.13/site-packages/pandas/core/internals/construction.py": 893951849732478990, + "venv/lib/python3.13/site-packages/PIL/_webp.cpython-313-x86_64-linux-gnu.so": 11070012119632935103, + "venv/lib/python3.13/site-packages/pygments/lexers/mips.py": 2078929831513352031, + "venv/lib/python3.13/site-packages/pandas/core/ops/docstrings.py": 3721650738518391256, + "venv/lib/python3.13/site-packages/pandas/core/internals/__init__.py": 1377108808392195313, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_arrayobject.py": 13337664596833150958, + "venv/lib/python3.13/site-packages/ecdsa/test_numbertheory.py": 2117130219401053225, + "venv/lib/python3.13/site-packages/passlib/tests/test_crypto_builtin_md4.py": 16840174243259839994, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/query.py": 3867868366067025348, + "venv/lib/python3.13/site-packages/pandas/core/groupby/numba_.py": 15997665748332235683, + "venv/lib/python3.13/site-packages/pycparser/c_generator.py": 14922277346364091122, + "venv/lib/python3.13/site-packages/pygments/lexers/boa.py": 14409319610243925864, + "frontend/build/_app/immutable/nodes/15.BKKY0MZO.js": 14681615552506811594, + "backend/src/plugins/translate/service.py": 10002583160631126287, + "venv/lib/python3.13/site-packages/pip/_internal/operations/prepare.py": 13514839972066861359, + "venv/lib/python3.13/site-packages/passlib/__init__.py": 6987142392711441525, + "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/mean_.py": 3641008929910474457, + "venv/lib/python3.13/site-packages/httpcore/_sync/http2.py": 11167798787131917524, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/unistring.py": 18037463865539157041, + "venv/lib/python3.13/site-packages/numpy/random/bit_generator.cpython-313-x86_64-linux-gnu.so": 2574035311375507054, "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi": 8666343427862784832, - "venv/lib/python3.13/site-packages/pandas/io/formats/format.py": 3364941338174434661 + "venv/lib/python3.13/site-packages/pip/_internal/metadata/_json.py": 6718137926109998531, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/assertsql.py": 4633484475514351889, + "venv/lib/python3.13/site-packages/numpy/lib/tests/data/python3.npy": 4957143805477405905, + "frontend/build/_app/immutable/chunks/CzVatF6O.js": 1337487003792124464, + "venv/lib/python3.13/site-packages/numpy/__init__.pxd": 13527602429695748857, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/parsing.pyi": 11967691665766452312, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8693/__init__.py": 11065844267208884531, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/cli/hi77.f": 10617763407280454334, + "venv/lib/python3.13/site-packages/websockets/asyncio/server.py": 885415484558949506, + "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/METADATA": 3814104793342833872, + "venv/lib/python3.13/site-packages/pip/_internal/models/__init__.py": 13482731985065116750, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/contains.py": 3098087827310439993, + "venv/lib/python3.13/site-packages/pandas/core/interchange/dataframe_protocol.py": 12857147443873820285, + "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/licenses/numpy/_core/src/umath/svml/LICENSE": 9584102918819171104, + "venv/lib/python3.13/site-packages/pygments/styles/gruvbox.py": 9489017112545247388, + "venv/lib/python3.13/site-packages/pygments/lexers/webassembly.py": 17811970490091406867, + "venv/lib/python3.13/site-packages/pandas/core/missing.py": 13332302791063948992, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_subclass.py": 11096337912872573314, + "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/testing/_private/__init__.pyi": 15130871412783076140, + "venv/lib/python3.13/site-packages/urllib3/util/timeout.py": 17922362393077965047, + "venv/lib/python3.13/site-packages/passlib/handlers/mssql.py": 2800599582708170195, + "frontend/src/lib/stores/__tests__/mocks/environment.js": 16653269108049932073, + "backend/src/models/profile.py": 18027673609437659833, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Jaro.py": 183817263146423033, + "venv/lib/python3.13/site-packages/pygments/lexers/special.py": 4972294202790455038, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/themes.py": 6451091097727599550, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/f77comments.f": 4801926170672147659, + "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/RECORD": 15865206372525596795, + "venv/lib/python3.13/site-packages/pandas/api/internals.py": 5188134630996087511, + "venv/lib/python3.13/site-packages/anyio/abc/_eventloop.py": 2023190981288767991, + "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/METADATA": 317417773265681256, + "backend/src/api/routes/tasks.py": 4058255810929412793, + "venv/lib/python3.13/site-packages/passlib/tests/test_crypto_scrypt.py": 17378030264314664250, + "venv/lib/python3.13/site-packages/ecdsa/_sha3.py": 10353612776869489378, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7636/challenge.py": 6388445104761482746, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/json_schema_test_suite.py": 6152158310190822762, + "venv/lib/python3.13/site-packages/requests/packages.py": 7733041271118298220, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_period.py": 15671388508498441412, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_ujson.py": 12184194085993665945, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/scope.py": 4096190764836489091, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/poly1305.pyi": 14119032813912827271, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_polyutils.py": 675639791972685455, + "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/WHEEL": 16784970174376303810, + "venv/lib/python3.13/site-packages/gitdb/utils/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/authlib/oidc/core/__init__.py": 16029653645350087968, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/rec.pyi": 9140655366667421698, + "venv/lib/python3.13/site-packages/_pytest/_io/saferepr.py": 8025234471485188730, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arctan.csv": 4581185622290535488, + "venv/lib/python3.13/site-packages/numpy/ctypeslib/_ctypeslib.pyi": 10360538024015064036, + "venv/lib/python3.13/site-packages/pandas/tests/io/sas/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/lib_user_array.py": 8711697474193296376, + "venv/lib/python3.13/site-packages/pandas/core/api.py": 3390809109543982410, + "venv/lib/python3.13/site-packages/numpy/ma/tests/test_core.py": 12712110303898046818, + "venv/lib/python3.13/site-packages/pandas/tests/internals/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/RECORD": 17985800801793506250, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/__multiarray_api.h": 16138871162713175599, + "venv/lib/python3.13/site-packages/passlib/tests/test_ext_django_source.py": 15577951591316518967, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_counting.py": 5628494712451120258, + "venv/lib/python3.13/site-packages/urllib3/filepost.py": 3814725067598042920, + "frontend/src/components/PasswordPrompt.svelte": 14457083234480062595, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_repr.py": 16609300578827398344, + "venv/lib/python3.13/site-packages/pip/_vendor/msgpack/exceptions.py": 11303331612406662162, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7515/jws.py": 7505219131537941130, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_complex/foo77.f": 7353666717928699883, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_factorize.py": 17995673140216929494, + "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/METADATA": 3179724236887811680, + "backend/src/services/clean_release/source_isolation.py": 10193276492118254392, + "backend/tests/core/migration/test_archive_parser.py": 1909788158234142574, + "venv/lib/python3.13/site-packages/pandas/_libs/missing.cpython-313-x86_64-linux-gnu.so": 1990755879067132155, + "venv/lib/python3.13/site-packages/apscheduler/__init__.py": 8652514118183227412, + "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/RECORD": 2688648742701423549, + "frontend/src/lib/stores/__tests__/taskDrawer.test.js": 12307450726550947042, + "venv/lib/python3.13/site-packages/pandas/tests/extension/date/array.py": 18073740473413382768, + "venv/lib/python3.13/site-packages/numpy/linalg/__init__.pyi": 18409133021422984774, + "venv/lib/python3.13/site-packages/rpds/__init__.py": 5862197328247012275, + "venv/lib/python3.13/site-packages/pip/_internal/commands/lock.py": 14782882714168174465, + "venv/lib/python3.13/site-packages/numpy/_typing/_scalars.py": 17701938912160369190, + "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/RECORD": 8201759541483070364, + "venv/lib/python3.13/site-packages/pygments/lexers/prql.py": 1159462562014166646, + "venv/bin/activate.fish": 5275961582873298085, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/devicetree.py": 9820277168995231927, + "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/RECORD": 3617343778671172258, + "venv/lib/python3.13/site-packages/passlib/handlers/pbkdf2.py": 16686685453577026643, + "frontend/build/_app/immutable/nodes/18.DnUw7pIE.js": 5294776392406439782, + "venv/lib/python3.13/site-packages/PIL/ImageQt.py": 6607270889359138659, + "venv/lib/python3.13/site-packages/secretstorage/__init__.py": 4535964275649588672, + "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/METADATA": 3390257212824996989, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_describe.py": 11923715539897559626, + "venv/lib/python3.13/site-packages/pyasn1/codec/cer/__init__.py": 15728752901274520502, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_custom_dtypes.py": 12322100282959605055, + "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/WHEEL": 2357997949040430835, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/dim2.py": 5767600151459095125, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/rsa_key.py": 7092106298733667895, + "venv/lib/python3.13/site-packages/numpy/_utils/_convertions.pyi": 362674842164060822, + "venv/lib/python3.13/site-packages/greenlet/PyGreenlet.hpp": 6366432579621298231, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc_unix.h": 9608771997528633681, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/profiling.py": 16030397164203895281, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh23879.f90": 13174951794239860144, + "venv/lib/python3.13/site-packages/pip/_internal/network/cache.py": 10606522397693824641, + "venv/lib/python3.13/site-packages/git/exc.py": 14278238357119816365, + "venv/lib/python3.13/site-packages/greenlet/TThreadState.hpp": 15599148954148720463, + "venv/lib/python3.13/site-packages/pip/_internal/utils/datetime.py": 6816292030061301011, + "venv/lib/python3.13/site-packages/pandas/io/excel/_odswriter.py": 17220892094904423676, + "venv/lib/python3.13/site-packages/dateutil/parser/__init__.py": 11377431154251655738, + "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_defmatrix.py": 2982572083770917411, + "venv/lib/python3.13/site-packages/charset_normalizer/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/webidl.py": 8097260111959278117, + "venv/lib/python3.13/site-packages/jeepney/io/tests/test_blocking.py": 6312017389529916004, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_fields.py": 16222559851336781264, + "venv/lib/python3.13/site-packages/pygments/lexers/ptx.py": 4855144232064607995, + "venv/lib/python3.13/site-packages/numpy/_typing/_nbit.py": 15498810174380798405, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/linalg.pyi": 269481015135399748, + "venv/lib/python3.13/site-packages/authlib/jose/__init__.py": 317573635113539563, + "backend/_convert_defs.py": 208535643309836247, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_m68k_gcc.h": 6580173800099450404, + "venv/lib/python3.13/site-packages/pygments/styles/pastie.py": 15784474766258831882, + "backend/src/api/routes/dataset_review.py": 3689614039699023362, + "backend/src/services/__tests__/test_llm_prompt_templates.py": 17912427710321938757, + "backend/src/services/health_service.py": 7295235621662158359, + "venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/WHEEL": 5179340427739743287, + "venv/lib/python3.13/site-packages/pygments/lexers/scripting.py": 4167168074165473790, + "backend/tests/test_translate_corrections.py": 1228642484417924184, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/string_/test_concat.py": 3788461606817482165, + "venv/lib/python3.13/site-packages/git/objects/base.py": 11498407361286170399, + "venv/lib/python3.13/site-packages/pandas/io/excel/_openpyxl.py": 6676451564208867775, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_generator_mt19937.py": 7299586490442539723, + "venv/lib/python3.13/site-packages/pip/_internal/utils/wheel.py": 450504439085405418, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c": 17333399794446198412, + "venv/lib/python3.13/site-packages/click-8.3.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/licenses/LICENSE.PSF": 13208428090829817329, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_replace.py": 18039668944453454689, + "venv/lib/python3.13/site-packages/numpy/_distributor_init.pyi": 1308368231471324581, + "venv/lib/python3.13/site-packages/numpy/lib/_iotools.pyi": 12873243737872788349, + "venv/lib/python3.13/site-packages/PIL/_imagingft.cpython-313-x86_64-linux-gnu.so": 11054435406031360190, + "venv/lib/python3.13/site-packages/passlib/pwd.py": 14727089240749133060, + "frontend/src/components/TaskRunner.svelte": 18014857434320509117, + "venv/lib/python3.13/site-packages/numpy/lib/_format_impl.py": 1809069379864985298, + "venv/lib/python3.13/site-packages/passlib/apps.py": 4633269405597895766, + "venv/lib/python3.13/site-packages/fastapi/exception_handlers.py": 9907950997607808699, + "venv/lib/python3.13/site-packages/pygments/lexers/verification.py": 16645885125604874183, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/__init__.py": 11157967642611612522, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh23533.f": 12558972205518833024, + "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/licenses/LICENSE.APACHE": 8359973023624924624, + "venv/lib/python3.13/site-packages/numpy/_typing/_ufunc.pyi": 6286629088867436848, + "venv/lib/python3.13/site-packages/numpy/random/_common.pyi": 5094115692952609118, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_monotonic.py": 11817523305776951162, + "venv/lib/python3.13/site-packages/pandas/core/construction.py": 10646956189518908985, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/frequencies/test_frequencies.py": 14039315221824123200, + "venv/lib/python3.13/site-packages/PIL/WebPImagePlugin.py": 5378349556563611996, + "venv/lib/python3.13/site-packages/pandas/tests/util/conftest.py": 11143177766735481660, + "venv/lib/python3.13/site-packages/pygments/lexers/hexdump.py": 10665271113239513484, + "venv/lib/python3.13/site-packages/packaging/requirements.py": 2896326326372719999, + "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/numeric.pyi": 17005748821769179511, + "venv/lib/python3.13/site-packages/numpy/_core/_exceptions.py": 5570375156851166818, + "venv/lib/python3.13/site-packages/jeepney/io/trio.py": 11859775321483330469, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_reindex.py": 15680924058960901067, + "venv/lib/python3.13/site-packages/ecdsa/test_ecdh.py": 3338834550392548171, + "venv/lib/python3.13/site-packages/numpy/f2py/_backends/__init__.pyi": 16164841755339571545, + "venv/lib/python3.13/site-packages/pandas/tests/strings/test_case_justify.py": 8204537837649550062, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/provision.py": 9472538482404392519, + "venv/lib/python3.13/site-packages/PIL/_util.py": 17116054813914373606, + "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/WHEEL": 18203500019759199992, + "venv/lib/python3.13/site-packages/referencing/_attrs.pyi": 10030665705880994025, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ufuncs.pyi": 14995898537484305307, + "venv/lib/python3.13/site-packages/pygments/lexers/make.py": 5345282370426217663, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_cpu_dispatcher.py": 2063037275809439611, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_cut.py": 7800833253841657380, + "venv/lib/python3.13/site-packages/anyio/streams/file.py": 15232345967545920863, + "venv/lib/python3.13/site-packages/anyio/streams/memory.py": 11762722143503428823, + "venv/lib/python3.13/site-packages/pandas/_libs/testing.pyi": 16691791330088523786, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/provision.py": 8707620698060818654, + "frontend/build/_app/immutable/chunks/DmuwojIM.js": 5778224784187071361, + "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/code.py": 9700811903057180696, + "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/WHEEL": 18203500019759199992, + "venv/lib/python3.13/site-packages/numpy/f2py/use_rules.py": 17277594393032382485, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_arithmetic.py": 16829312896275893002, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_equals.py": 6429101196875333574, + "venv/lib/python3.13/site-packages/PIL/XpmImagePlugin.py": 6644626557482464312, + "venv/lib/python3.13/site-packages/PIL/PSDraw.py": 1927169421822446045, + "venv/lib/python3.13/site-packages/pygments/lexers/graph.py": 10445044822847205061, + "venv/lib/python3.13/site-packages/PIL/_imagingft.pyi": 1385456134120145584, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_get_numeric_data.py": 5896318696417090902, + "venv/lib/python3.13/site-packages/pygments/lexers/monte.py": 15158829991357994573, + "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/AUTHORS": 10073225185964928425, + "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/__main__.py": 11917862648584782420, + "venv/lib/python3.13/site-packages/pygments/lexers/dax.py": 10868881057512002309, + "venv/lib/python3.13/site-packages/PIL/Jpeg2KImagePlugin.py": 16905402423248975204, + "venv/lib/python3.13/site-packages/pandas/tests/window/__init__.py": 15130871412783076140, + "backend/src/api/routes/__tests__/test_clean_release_legacy_compat.py": 10703957986116846695, + "venv/lib/python3.13/site-packages/numpy/_pyinstaller/tests/pyinstaller-smoke.py": 13570990531070630950, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_common.py": 10797243274520625867, + "backend/src/api/routes/dashboards/_action_routes.py": 11814954258886577640, + "venv/lib/python3.13/site-packages/passlib/tests/_test_bad_register.py": 5446848310854472376, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_pct_change.py": 18056235438452125034, + "venv/lib/python3.13/site-packages/PIL/IcnsImagePlugin.py": 1870235606309784691, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/inout.f90": 1521999519583206538, + "venv/lib/python3.13/site-packages/pandas/errors/__init__.py": 13444371880185740908, + "backend/src/core/utils/matching.py": 12567367681677699487, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/histograms.pyi": 2281259341486424755, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_na_indexing.py": 8712547126522523899, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/io/conftest.py": 5864037534050136837, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_first_valid_index.py": 15763400665851423183, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_fiscal.py": 14652343698825214870, + "backend/src/services/clean_release/__tests__/test_source_isolation.py": 17714198919084455758, + "venv/lib/python3.13/site-packages/numpy/core/__init__.py": 10397294636564169377, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_item_selection.py": 1027989019709029615, + "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/assertion_session.py": 18109848200936213195, + "venv/lib/python3.13/site-packages/numpy/ma/core.py": 338350463523585845, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/exceptions.pyi": 9294219058621261971, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_replace.py": 4953527309376861139, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_shares_memory.py": 2911029123657970431, + "venv/lib/python3.13/site-packages/sqlalchemy/pool/impl.py": 4687705587963980337, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame.py": 16757570551941104401, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/default_styles.py": 4469204219504675080, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/requests.py": 14847842002253212098, + "venv/lib/python3.13/site-packages/dateutil/easter.py": 14631233890122048084, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/refresh_token.py": 17927903035152872556, + "venv/lib/python3.13/site-packages/pandas/_libs/interval.cpython-313-x86_64-linux-gnu.so": 8964760352224019544, + "venv/lib/python3.13/site-packages/PIL/_imagingmorph.cpython-313-x86_64-linux-gnu.so": 11171810899664471137, + "venv/lib/python3.13/site-packages/pycparser/_ast_gen.py": 5464347629908906775, + "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_css.py": 17765129030290410902, + "venv/lib/python3.13/site-packages/pygments/lexers/bibtex.py": 14652372822443154388, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/reshaping.py": 18085648077811502689, + "venv/lib/python3.13/site-packages/pygments/lexers/supercollider.py": 15306357945850389082, + "backend/src/core/config_models.py": 4264626520931642357, + "backend/tests/test_translate_history.py": 14504549443376034614, + "venv/lib/python3.13/site-packages/numpy/linalg/lapack_lite.pyi": 14069586330277925998, + "venv/lib/python3.13/site-packages/itsdangerous/signer.py": 3694661094753475840, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_subclass.py": 16342917051908786497, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_indexing.py": 6539365174681852769, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_arithmetic.py": 4858019700859362660, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_format.py": 16150524965028369071, + "frontend/src/lib/components/translate/TranslationRunResult.svelte": 10499421223016291488, + "venv/lib/python3.13/site-packages/git/refs/tag.py": 9979537867501757110, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/signals.py": 10390443713463805090, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/__init__.py": 13331622784664204066, + "venv/lib/python3.13/site-packages/pandas/plotting/__init__.py": 13230487326686617492, + "venv/lib/python3.13/site-packages/pygments/styles/inkpot.py": 10516710471319090846, + "venv/lib/python3.13/site-packages/pygments/lexers/int_fiction.py": 8806961041808606055, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/registration.py": 14639972512303032712, + "venv/lib/python3.13/site-packages/PIL/_imagingcms.cpython-313-x86_64-linux-gnu.so": 15198265718775753589, + "venv/lib/python3.13/site-packages/rapidfuzz/process.py": 3870006735299537016, + "venv/lib/python3.13/site-packages/pip/_internal/metadata/importlib/_compat.py": 4677414058216516380, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/reporter.py": 6510864297510700136, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_overrides.py": 92685611709898742, + "venv/lib/python3.13/site-packages/pandas/tests/api/test_types.py": 8889829773923216769, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ma.pyi": 6467636381637286867, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hashes.py": 8545380307201732345, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/multiarray.pyi": 6368268452184570195, + "venv/lib/python3.13/site-packages/anyio/_core/_tasks.py": 14104828341875599130, + "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/WHEEL": 3749476255729626245, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/introspection.py": 5447179017910128771, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_umath.py": 13010939919217862164, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/sfc64_np126.pkl.gz": 1630203486813146863, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/DamerauLevenshtein.pyi": 2700568594438441246, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/authorization_server.py": 13696038495666024258, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py": 5946672289916692272, + "venv/lib/python3.13/site-packages/passlib/tests/test_utils.py": 10362977406431569031, + "venv/lib/python3.13/site-packages/attrs/exceptions.py": 2871247302586505373, + "venv/lib/python3.13/site-packages/httpcore/_sync/connection.py": 1539337414493486168, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/logging.py": 2426447364230673649, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/utils.h": 3920393832993063886, + "backend/src/services/clean_release/repositories/candidate_repository.py": 14784344453820731941, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/dtype_api.h": 17040779421165903019, + "frontend/src/lib/components/translate/__tests__/test_correction_cell.svelte.js": 2314051239468628554, + "venv/lib/python3.13/site-packages/PIL/ImageCms.py": 5610897002957128546, + "venv/lib/python3.13/site-packages/pandas/_libs/hashing.pyi": 5241441378073929968, + "venv/lib/python3.13/site-packages/numpy/tests/test__all__.py": 10689265249302359388, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_smoke.py": 7476432112986643847, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libcom_err-2abe824b.so.2.1": 12870528025628871295, + "venv/lib/python3.13/site-packages/numpy/_typing/_nbit_base.pyi": 6546751036506752550, + "venv/lib/python3.13/site-packages/pygments/styles/coffee.py": 17584414468786822371, + "venv/lib/python3.13/site-packages/pydantic/typing.py": 6661252174518316760, + "venv/lib/python3.13/site-packages/numpy/random/_pickle.py": 7774620933480975313, + "venv/lib/python3.13/site-packages/pandas/_libs/properties.cpython-313-x86_64-linux-gnu.so": 7092136701092358552, + "venv/lib/python3.13/site-packages/numpy.libs/libgfortran-040039e1-0352e75f.so.5.0.0": 13946067868274850557, + "venv/lib/python3.13/site-packages/numpy/tests/test_ctypeslib.py": 9592016432737074765, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_methods.py": 12694335778731418517, + "backend/src/plugins/translate/events.py": 14264965160929343917, + "frontend/src/lib/stores/__tests__/mocks/env_public.js": 2540122820969872185, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/random/bitgen.h": 12421210767114721499, + "venv/lib/python3.13/site-packages/pip/_internal/models/target_python.py": 422323125651848065, + "venv/lib/python3.13/site-packages/pydantic/_internal/_import_utils.py": 4029157986210295967, + "venv/lib/python3.13/site-packages/keyring/core.py": 2764269590123131634, + "venv/lib/python3.13/site-packages/numpy/_core/arrayprint.pyi": 84606125837677197, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/dtype.pyi": 12178008080078679769, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_direct.py": 12680113511641199070, + "backend/src/api/routes/assistant/_routes.py": 6252816315418755346, + "venv/lib/python3.13/site-packages/pandas/util/_exceptions.py": 14426860079366867683, + "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_sparse_accessor.py": 6774374030220202353, + "venv/lib/python3.13/site-packages/pandas/util/__init__.py": 4070574585004336241, + "venv/lib/python3.13/site-packages/PIL/WmfImagePlugin.py": 15685062825106444247, + "venv/lib/python3.13/site-packages/numpy/core/_dtype_ctypes.pyi": 15130871412783076140, + "venv/lib/python3.13/site-packages/authlib/oidc/registration/claims.py": 4161520263207458028, + "venv/lib/python3.13/site-packages/apscheduler/executors/debug.py": 14072221218785494847, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalarbuffer.py": 16859789411010045795, + "venv/lib/python3.13/site-packages/PIL/JpegImagePlugin.py": 10621639599916796689, + "venv/lib/python3.13/site-packages/numpy/_core/printoptions.py": 3897105663572772838, + "venv/lib/python3.13/site-packages/keyring/compat/py312.py": 17581691618899404491, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_na_values.py": 13527837495439739279, + "venv/lib/python3.13/site-packages/jsonschema_specifications/tests/test_jsonschema_specifications.py": 12693210357718751894, + "venv/lib/python3.13/site-packages/numpy/core/function_base.py": 12696640832638144604, + "venv/lib/python3.13/site-packages/PIL/_imaging.cpython-313-x86_64-linux-gnu.so": 1425926264271197601, + "venv/lib/python3.13/site-packages/pygments/lexers/teal.py": 10004159499175716139, + "venv/lib/python3.13/site-packages/pandas/tests/resample/test_resample_api.py": 3278209206670699265, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalarprint.py": 9482100952562512933, + "venv/lib/python3.13/site-packages/pandas/_libs/window/aggregations.cpython-313-x86_64-linux-gnu.so": 16327106317503267067, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/metrics_py.py": 13809503317864539103, + "venv/lib/python3.13/site-packages/pip/_vendor/certifi/__main__.py": 8412594868333244844, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/privatemod.f90": 8999550841368314739, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_datetime.py": 4179112383995346046, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_normalize.py": 11373923576259976755, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/test_indexing.py": 9862204223281834360, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_week.py": 5756727157937218128, + "venv/lib/python3.13/site-packages/pygments/styles/bw.py": 869630132791421268, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_datetimes.py": 8863748366184419339, + "venv/lib/python3.13/site-packages/cffi/_shimmed_dist_utils.py": 4824806157907607487, + "frontend/src/components/git/BranchSelector.svelte": 6225347983501401973, + "venv/lib/python3.13/site-packages/PIL/_imagingcms.pyi": 12476347428820346383, + "venv/lib/python3.13/site-packages/referencing/tests/test_retrieval.py": 10782087416909177703, + "frontend/build/_app/immutable/nodes/30.D3JMnFof.js": 16119902291510484631, + "venv/lib/python3.13/site-packages/pandas/core/arrays/sparse/array.py": 3517673819082574078, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_arrow_interface.py": 9757086292358759236, + "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/RECORD": 6518517239543344179, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/validator.py": 11486125578281617670, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_exceptions.py": 12145393002202744314, + "venv/lib/python3.13/site-packages/pygments/lexers/dns.py": 17149871769553794654, + "venv/lib/python3.13/site-packages/greenlet/greenlet_refs.hpp": 8525862687669040510, + "backend/tests/core/test_defensive_guards.py": 1048600814279591148, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/shape_base.pyi": 5046405609030418900, + "venv/lib/python3.13/site-packages/pygments/lexers/jsx.py": 13855228003748707341, + "venv/lib/python3.13/site-packages/pip/_internal/utils/setuptools_build.py": 13594639612360346306, + "venv/lib/python3.13/site-packages/numpy/lib/_index_tricks_impl.pyi": 5892973170789971535, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_constructors.py": 979131987837682435, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/useless_keywords.py": 11487954623214475915, + "venv/lib/python3.13/site-packages/pygments/lexer.py": 3658450949169647228, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py": 6152202474302791232, + "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/RECORD": 1164466882742493155, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_json_table_schema.py": 14454690938977069049, + "venv/lib/python3.13/site-packages/pydantic/_internal/_schema_generation_shared.py": 23345689300073549, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_replace.py": 8165088653532047247, + "venv/lib/python3.13/site-packages/pyasn1/type/opentype.py": 340146737126677655, + "venv/lib/python3.13/site-packages/anyio/_core/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/libs/test_hashtable.py": 7762405492052106717, + "venv/lib/python3.13/site-packages/numpy/_utils/_inspect.pyi": 17825266810219495672, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/flatiter.py": 3066469329738423021, + "venv/lib/python3.13/site-packages/pip/_internal/index/sources.py": 11609272011565024668, + "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/_pytest/compat.py": 10046464101087327473, + "venv/lib/python3.13/site-packages/passlib/tests/tox_support.py": 9200774792427691602, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_to_pydatetime.py": 1129954411080617001, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_set_value.py": 4166411740676564820, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/usecols/test_parse_dates.py": 5344929031479212951, + "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/string.f": 17328443345813567449, + "venv/lib/python3.13/site-packages/pip/_internal/index/collector.py": 7454652626329467900, + "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/licenses/LICENSE": 1641332741515411734, + "venv/lib/python3.13/site-packages/numpy/dtypes.pyi": 6713822658171717369, + "venv/lib/python3.13/site-packages/sqlalchemy/util/concurrency.py": 1325673503812009695, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/decl_base.py": 8678387511873412922, + "venv/lib/python3.13/site-packages/pip/_internal/cli/progress_bars.py": 7584699216537478969, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_elffile.py": 5728213398707817049, + "venv/lib/python3.13/site-packages/rapidfuzz/process_cpp.py": 16797760903996739079, + "venv/lib/python3.13/site-packages/apscheduler/executors/gevent.py": 17122637996105344344, + "venv/lib/python3.13/site-packages/numpy/random/__init__.py": 11732100564412485633, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_setitem.py": 2059440410065814114, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/bar.py": 12859947592168238832, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py": 11225173272050906977, + "venv/lib/python3.13/site-packages/pygments/lexers/qvt.py": 1895735583818227972, + "backend/src/api/routes/__tests__/conftest.py": 8465980330386544642, + "venv/lib/python3.13/site-packages/pandas/_libs/pandas_parser.cpython-313-x86_64-linux-gnu.so": 18056339336364727359, + "venv/lib/python3.13/site-packages/numpy/ma/__init__.py": 214772578978182427, + "venv/lib/python3.13/site-packages/numpy/_core/_add_newdocs_scalars.pyi": 1826323011169529229, + "venv/lib/python3.13/site-packages/urllib3/_version.py": 839747992523118887, + "venv/lib/python3.13/site-packages/pygments/lexers/mojo.py": 8616283481592061285, + "backend/src/schemas/dataset_review.py": 15720223123457526104, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth1/resource_protector.py": 17080006010679257864, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/base.py": 5924592154431932923, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/twodim_base.pyi": 10156504423258357540, + "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/licenses/LICENSE": 9235234428907927646, + "venv/lib/python3.13/site-packages/sqlalchemy/util/_concurrency_py3k.py": 7301817934902711510, + "venv/lib/python3.13/site-packages/pygments/styles/murphy.py": 8177992406082781615, + "venv/lib/python3.13/site-packages/pygments/styles/lightbulb.py": 12433916113773598760, + "venv/lib/python3.13/site-packages/pygments/lexers/whiley.py": 218468511185731869, + "venv/lib/python3.13/site-packages/pygments/lexers/_openedge_builtins.py": 4979988747924637739, + "venv/lib/python3.13/site-packages/pandas/_libs/json.cpython-313-x86_64-linux-gnu.so": 2436285663630821963, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/idna-3.11.dist-info/METADATA": 14036738037171597049, + "venv/lib/python3.13/site-packages/pygments/styles/default.py": 13992125975366109635, + "venv/lib/python3.13/site-packages/numpy/fft/_pocketfft_umath.cpython-313-x86_64-linux-gnu.so": 16443444849116065961, + "venv/lib/python3.13/site-packages/jeepney/bus.py": 5163075760432139100, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/vectorized.pyi": 15498895254516573071, + "venv/lib/python3.13/site-packages/certifi/core.py": 7472627735517449185, + "venv/lib/python3.13/site-packages/ecdsa/der.py": 9374566189581725372, + "venv/lib/python3.13/site-packages/pydantic/error_wrappers.py": 11273827025986230629, + "venv/lib/python3.13/site-packages/psycopg2_binary-2.9.11.dist-info/licenses/LICENSE": 5757922580822401219, + "venv/lib/python3.13/site-packages/_pytest/unraisableexception.py": 4597178803320514675, + "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/RECORD": 11976403473353722969, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/fft.pyi": 14454280827038327842, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/test_arrow_compat.py": 12986187285988432338, + "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/WHEEL": 5990830714395966792, + "backend/src/plugins/translate/__tests__/test_scheduler.py": 4988970087981965515, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/lib_function_base.pyi": 2792940598392356268, + "venv/lib/python3.13/site-packages/pygments/styles/rrt.py": 9490973359165080360, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/controller.py": 5450931608244830335, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/__init__.py": 8811477458865810434, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/pyproject.py": 12684373765476932108, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh27697.f90": 4990763135487995517, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_cpu_features.py": 955759654189150497, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/live_render.py": 14619617939683800204, + "venv/lib/python3.13/site-packages/pygments/lexers/_cl_builtins.py": 16540712925368297957, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_bin_groupby.py": 14831211659124136806, + "venv/lib/python3.13/site-packages/numpy/_core/numeric.pyi": 17785360553418386488, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-cos.csv": 12896264205153363031, + "venv/lib/python3.13/site-packages/pandas/tests/extension/uuid/test_uuid.py": 13955678427426850807, + "venv/lib/python3.13/site-packages/pygments/lexers/ride.py": 14096748896451437594, + "venv/lib/python3.13/site-packages/pydantic/v1/decorator.py": 6124748023704700724, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/provision.py": 7362363963464621570, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/candidates.py": 5951912505933673835, + "venv/lib/python3.13/site-packages/_pytest/debugging.py": 16135285365306979060, + "venv/lib/python3.13/site-packages/_pytest/runner.py": 5772610501857496741, + "venv/lib/python3.13/site-packages/pygments/lexers/jsonnet.py": 1314044008440065525, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_conversion.py": 9159276697339089788, + "backend/src/models/dataset_review_pkg/_clarification_models.py": 1561486208194983219, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_interval.py": 1228451182688768814, + "venv/lib/python3.13/site-packages/numpy/f2py/f90mod_rules.pyi": 18290764713161282980, + "venv/lib/python3.13/site-packages/authlib/integrations/base_client/registry.py": 13306388361671753373, + "venv/lib/python3.13/site-packages/authlib/oidc/discovery/__init__.py": 308178910015974550, + "venv/lib/python3.13/site-packages/h11/_version.py": 9334144031400715479, + "venv/lib/python3.13/site-packages/referencing/tests/test_referencing_suite.py": 8875120876162738440, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timestamps.pyi": 51728851886466199, + "venv/lib/python3.13/site-packages/cryptography/py.typed": 15130871412783076140, + "dist/docker/backend.v1.0.0-rc2-docker.tar": 9693793465773386616, + "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/__init__.py": 18036348770966744795, + "venv/lib/python3.13/site-packages/greenlet/tests/test_generator_nested.py": 12822240347707346718, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_backend.py": 17793139799410518847, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/lambdas.py": 13377298868598623228, + "venv/lib/python3.13/site-packages/fastapi/openapi/utils.py": 9058442333718187759, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_random.py": 4179188367630060654, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libpcre-9513aab5.so.1.2.0": 12298861477332583668, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_astype.py": 8505605634958454768, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_reductions.py": 3227705565578016389, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_as_unit.py": 8492383355919959393, + "venv/lib/python3.13/site-packages/dateutil/tz/__init__.py": 9617254832690762236, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_math.h": 16701045088211015518, + "venv/lib/python3.13/site-packages/numpy/lib/mixins.py": 7404729635424818497, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/conftest.py": 2643915042664055969, + "venv/lib/python3.13/site-packages/pygments/lexers/wgsl.py": 9718384325049172855, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/nested_sequence.pyi": 11245460325970663922, + "venv/lib/python3.13/site-packages/requests/help.py": 1885413874621672882, + "venv/lib/python3.13/site-packages/greenlet/tests/test_cpp.py": 10982107081267457286, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/__init__.py": 3673322722277252961, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/requirements.py": 2362917888844255805, + "frontend/src/types/backup.ts": 1741234034896689348, + "frontend/src/routes/tools/storage/+page.svelte": 2651964385884112547, + "venv/lib/python3.13/site-packages/numpy/_typing/_array_like.py": 2161873439361281488, + "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/licenses/LICENSE": 14230156892574098522, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/__init__.py": 10357835306532281617, + "venv/lib/python3.13/site-packages/numpy/_pyinstaller/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_spinners.py": 15873416546130487469, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_tokenizer.py": 7994281991455916540, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/index_tricks.pyi": 11463747060334597941, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_round.py": 8336918400205461173, + "venv/lib/python3.13/site-packages/websockets/__init__.py": 4916307256304054040, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py": 18069619555394133945, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_f2py2e.py": 8571347023940385644, + "venv/lib/python3.13/site-packages/pip/_internal/models/search_scope.py": 9282393652860589543, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_equals.py": 18261810467289276844, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_insert.py": 11635164392966270621, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_is_full.py": 9577318758326279694, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_select_dtypes.py": 3530442956913622572, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/io.py": 11072022722163954973, + "venv/lib/python3.13/site-packages/passlib/utils/compat/_ordered_dict.py": 14248790502300368024, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_clipboard.py": 12674078499544938252, + "venv/lib/python3.13/site-packages/pygments/styles/onedark.py": 17580335614588531275, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_searchsorted.py": 11208047646304753715, + "venv/lib/python3.13/site-packages/rapidfuzz/process.pyi": 6959881293311340830, + "venv/lib/python3.13/site-packages/keyring/backends/kwallet.py": 3071915635647280308, + "venv/lib/python3.13/site-packages/numpy/lib/_type_check_impl.pyi": 2667658052734929089, + "venv/lib/python3.13/site-packages/fastapi/responses.py": 6092572095549573593, + "venv/lib/python3.13/site-packages/pydantic/env_settings.py": 18250976255197243083, + "venv/lib/python3.13/site-packages/numpy/random/_common.cpython-313-x86_64-linux-gnu.so": 12834724130890991478, + "venv/lib/python3.13/site-packages/pandas/core/arrays/sparse/scipy_sparse.py": 2091608752719976909, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_to_string.py": 11422235699126032386, + "venv/lib/python3.13/site-packages/pandas/tests/internals/test_api.py": 11217428888457119601, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/resources.py": 11533706401259147051, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/ssh.py": 16262079536832961001, + "venv/lib/python3.13/site-packages/pygments/lexers/gleam.py": 5161116162065925222, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_parquet.py": 11050150015772440923, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_sample.py": 16877779557071324115, + "venv/lib/python3.13/site-packages/pandas/io/formats/templates/latex_longtable.tpl": 12723621983797415444, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/poly1305.py": 10854598965202354516, + "venv/lib/python3.13/site-packages/keyring/http.py": 1408024724330078009, + "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/METADATA": 11759004913807981650, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_deprecated_kwargs.py": 10379929935087904974, + "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/RECORD": 3283324710045856319, + "venv/lib/python3.13/site-packages/pygments/lexers/css.py": 517586374264113606, + "backend/src/api/routes/translate/_metrics_routes.py": 13468336445030118059, + "venv/lib/python3.13/site-packages/greenlet/greenlet_exceptions.hpp": 8819301638282185623, + "backend/src/models/dataset_review.py": 5814500849969423218, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_string.py": 15542851215119662814, + "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/METADATA": 9458019064618635438, + "venv/lib/python3.13/site-packages/pandas/tests/api/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libldap-2974a1ba.so.2.0.200": 13590569080612602445, + "venv/lib/python3.13/site-packages/fastapi/openapi/models.py": 1680354178147364813, + "venv/lib/python3.13/site-packages/numpy/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/smmap/test/test_util.py": 5634593621291240094, + "venv/lib/python3.13/site-packages/pandas/tests/config/test_localization.py": 9524481848419616725, + "frontend/src/lib/components/reports/__tests__/report_detail.ux.test.js": 11878832350632571462, + "venv/lib/python3.13/site-packages/pygments/lexers/thingsdb.py": 6073447481627395832, + "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/numpy/polynomial/polyutils.py": 12355826783958402233, + "venv/lib/python3.13/site-packages/sqlalchemy/util/topological.py": 11586868018611359415, + "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/RECORD": 13494296690334916257, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_laguerre.py": 7475592201750602042, + "venv/lib/python3.13/site-packages/pygments/lexers/ada.py": 17032408976634308999, + "venv/lib/python3.13/site-packages/psycopg2/extensions.py": 14197445323992851566, + "venv/lib/python3.13/site-packages/pygments/lexers/ezhil.py": 10294520768762308887, + "venv/lib/python3.13/site-packages/anyio/_core/_sockets.py": 10525237827959090532, + "venv/lib/python3.13/site-packages/fastapi/exceptions.py": 12665775976182239091, + "venv/lib/python3.13/site-packages/pygments/lexers/ambient.py": 18106929781201758080, + "backend/src/plugins/translate/__tests__/test_orthogonal_fixes.py": 17679703384686060315, + "backend/src/api/routes/__tests__/test_migration_routes.py": 4357515184436771537, + "backend/alembic.ini": 8897071018956157084, + "backend/alembic/versions/ed310b33f02c_multi_language_translation_tables.py": 1424919631205944664, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7521/__init__.py": 635200170992032966, + "venv/lib/python3.13/site-packages/pandas/tests/resample/test_resampler_grouper.py": 8388490036390951341, + "venv/lib/python3.13/site-packages/jsonschema/__init__.py": 6925762200139836824, + "venv/lib/python3.13/site-packages/pip/_internal/operations/build/wheel.py": 7728646523045538484, + "venv/lib/python3.13/site-packages/pandas/tests/internals/test_internals.py": 2049484047553296860, + "frontend/build/_app/immutable/chunks/B59ks8Wc.js": 12257988639088999545, + "venv/lib/python3.13/site-packages/pip/_internal/cli/__init__.py": 11914508471759255927, + "venv/lib/python3.13/site-packages/authlib/integrations/base_client/errors.py": 6538052228921426956, + "venv/lib/python3.13/site-packages/httpcore/_sync/__init__.py": 7676937747094511752, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_read.py": 8825002468745406134, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_value_counts.py": 12340872812120847127, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_floats.py": 3064615530495290792, + "venv/lib/python3.13/site-packages/pygments/formatters/pangomarkup.py": 17135620955205945255, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_categorical_equal.py": 7942323667866280872, + "venv/lib/python3.13/site-packages/PIL/_imagingmath.pyi": 18222325750818585549, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_generic.py": 1098361087956787007, + "venv/lib/python3.13/site-packages/authlib/__init__.py": 8957417237987377675, + "venv/lib/python3.13/site-packages/jsonschema/tests/test_format.py": 1947585389944838076, + "backend/src/schemas/__init__.py": 8700952869915187087, + "venv/lib/python3.13/site-packages/pyasn1/codec/ber/eoo.py": 5126566925877730303, + "venv/lib/python3.13/site-packages/authlib/oidc/core/errors.py": 18154419146257747681, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/dotenv.py": 5872848911499749500, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/hybrid.py": 5793056975404534989, + "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/METADATA": 6596896092754256988, + "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/licenses/LICENSE": 6965457853328313914, + "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/_libs/groupby.cpython-313-x86_64-linux-gnu.so": 14931394220112658268, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/operators.py": 2468046993343796128, + "venv/lib/python3.13/site-packages/packaging/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pillow.libs/libfreetype-ee1c40c4.so.6.20.4": 12301510769231120312, + "backend/tests/services/clean_release/test_policy_resolution_service.py": 3471271299977247598, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/array_api_info.pyi": 7887489694871300469, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/resource_protector.py": 4823504113176750140, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/fromnumeric.py": 3792079757208879362, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ufunc_config.pyi": 11754104063443791746, + "venv/lib/python3.13/site-packages/PIL/BdfFontFile.py": 10811640174253687845, + "backend/src/services/clean_release/repositories/report_repository.py": 17952038790457725175, + "venv/lib/python3.13/site-packages/jeepney/tests/secrets_introspect.xml": 10717038359151125278, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_update.py": 12293424926920594944, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/names.py": 705261158040926128, + "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_numba.py": 18148155441554555618, + "venv/lib/python3.13/site-packages/pip/_vendor/typing_extensions.py": 10158933506062403688, + "venv/lib/python3.13/site-packages/pygments/filters/__init__.py": 2992007913496948339, + "venv/lib/python3.13/site-packages/pygments/lexers/promql.py": 8929925488296216854, + "venv/lib/python3.13/site-packages/httpcore/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/passlib/utils/handlers.py": 10182859005870689026, + "venv/lib/python3.13/site-packages/numpy/exceptions.pyi": 13942486291715303235, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/_cryptography_key.py": 388489172928969873, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/_orm_constructors.py": 4276726508722638048, + "venv/lib/python3.13/site-packages/pluggy/_warnings.py": 2664977718908223842, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py": 17538029557373507617, + "venv/lib/python3.13/site-packages/urllib3/__init__.py": 1854778274753799815, + "venv/lib/python3.13/site-packages/pygments/lexers/tlb.py": 1169242051301155885, + "backend/src/api/routes/environments.py": 6892252055515346170, + "venv/lib/python3.13/site-packages/pygments/lexers/urbi.py": 1611762607688882372, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_fillna.py": 7349777469648334282, + "frontend/src/lib/ui/Button.svelte": 921807193049478213, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/multiarray.pyi": 17232959648651278910, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/common.py": 11384497819737275399, + "venv/lib/python3.13/site-packages/gitdb/const.py": 14122142034887286559, + "venv/lib/python3.13/site-packages/passlib/ext/django/utils.py": 8118896525895185626, + "venv/lib/python3.13/site-packages/pandas/io/formats/templates/latex_table.tpl": 8706980975714184465, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/events.py": 15099787454570172797, + "venv/lib/python3.13/site-packages/h11/_events.py": 13800479928851716538, + "venv/lib/python3.13/site-packages/jeepney/io/threading.py": 525625939662612746, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ma.pyi": 8186102816080813799, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_randomstate_regression.py": 17425202198134409689, + "venv/lib/python3.13/site-packages/numpy/ma/tests/test_extras.py": 16160090335191788104, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/accumulate.py": 5780225603819849877, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_map.py": 6556132946198785761, + "venv/lib/python3.13/site-packages/rpds/rpds.cpython-313-x86_64-linux-gnu.so": 561326734302492859, + "venv/lib/python3.13/site-packages/numpy/lib/_polynomial_impl.pyi": 2042169688753984918, + "venv/lib/python3.13/site-packages/httpcore/_backends/base.py": 5491727479625093183, + "venv/lib/python3.13/site-packages/pygments/styles/algol_nu.py": 6229131016121450375, + "frontend/src/lib/components/assistant/AssistantChatPanel.svelte": 15111082609123137207, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_mips_unix.h": 17373389962405490482, + "venv/lib/python3.13/site-packages/numpy/_core/arrayprint.py": 1262543237476191714, + "backend/src/api/routes/__tests__/test_reports_detail_api.py": 6960149894653710507, + "venv/lib/python3.13/site-packages/anyio/from_thread.py": 7973678341601453474, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_format.py": 9331389900588353667, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py": 2285778903282667561, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/constrain.py": 6513876973118703677, + "frontend/build/_app/immutable/chunks/B28PfaP6.js": 2526537581472307531, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc_linux.h": 15772978805500070727, + "venv/lib/python3.13/site-packages/pandas/_libs/interval.pyi": 15698173755300703836, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/api.py": 9829629144629841277, + "backend/src/core/superset_client/_user_projection.py": 16484323195475306883, + "backend/src/plugins/storage/__init__.py.bak": 7062955918227442388, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_to_latex.py": 2628895559914914693, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/structures.py": 2668010839316715865, + "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/RECORD": 2963644632913976754, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/numpyconfig.h": 13339221870252345205, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_masked.py": 5380551301916549925, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_inference.py": 16402427186697071465, + "venv/lib/python3.13/site-packages/pygments/lexers/pascal.py": 5992422120590352714, + "frontend/src/lib/components/layout/TopNavbar.svelte": 14489256050586877364, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_indexing.py": 6039808271042864401, + "venv/lib/python3.13/site-packages/pydantic/networks.py": 15767095796055034509, + "venv/lib/python3.13/site-packages/numpy/lib/_nanfunctions_impl.py": 11621577915097515056, + "venv/lib/python3.13/site-packages/numpy/random/_sfc64.cpython-313-x86_64-linux-gnu.so": 17186695607612591922, + "venv/lib/python3.13/site-packages/numpy/_core/strings.py": 3623885046208831106, + "venv/lib/python3.13/site-packages/pygments/lexers/_googlesql_builtins.py": 12251289884604745945, + "frontend/src/routes/dashboards/health/+page.svelte": 5234355531444674267, + "frontend/src/routes/tools/debug/+page.svelte": 8699044541980877686, + "venv/lib/python3.13/site-packages/pandas/tests/interchange/test_utils.py": 2329717399120006435, + "frontend/src/services/connectionService.js": 16613277497380191542, + "venv/lib/python3.13/site-packages/pydantic/deprecated/config.py": 12280996227531856230, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/publicmod.f90": 2174121054736811903, + "venv/lib/python3.13/site-packages/pandas/tests/construction/test_extract_array.py": 3100523370314150013, + "venv/lib/python3.13/site-packages/pillow.libs/libjpeg-32d42e18.so.62.4.0": 11717944412021329207, + "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/shared.py": 1162754506656505196, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_ticks.py": 11649965080303468085, + "frontend/src/components/llm/ProviderConfig.svelte": 1564511833097781020, + "venv/lib/python3.13/site-packages/pygments/lexers/rego.py": 16022844238903541738, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_setitem.py": 14920902525192671548, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_astype.py": 8633811117672837999, + "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/RECORD": 7266315886702289726, + "venv/lib/python3.13/site-packages/tzlocal-5.3.1.dist-info/WHEEL": 5076688779572520166, + "backend/src/api/routes/git/_repo_lifecycle_routes.py": 6605622512801609889, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/__init__.py": 5027355874704300832, + "venv/lib/python3.13/site-packages/pillow.libs/liblzma-61b1002e.so.5.8.2": 11405063484938458842, + "frontend/src/lib/utils.js": 5436516952434475239, + "backend/src/api/routes/dataset_review_pkg/_routes.py": 11040245149296528009, + "venv/lib/python3.13/site-packages/pydantic/alias_generators.py": 5991580234179623842, + "venv/lib/python3.13/site-packages/pygments/lexers/go.py": 11118104397271513417, + "backend/tests/test_core_scheduler.py": 6589618269087393661, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/scalars.py": 3468820466230647963, + "venv/lib/python3.13/site-packages/pydantic/_internal/_git.py": 8760779092674434712, + "venv/lib/python3.13/site-packages/pygments/lexers/teraterm.py": 16132676290138105984, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_combine.py": 4446011286411407708, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_copy.py": 3114918520437707410, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/arrayscalars.h": 16142050006935459679, + "venv/lib/python3.13/site-packages/PIL/ImageChops.py": 16368812693381154921, + "backend/src/models/dataset_review_pkg/_finding_models.py": 17608383421420046491, + "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/licenses/LICENSE": 4672913476475760762, + "venv/lib/python3.13/site-packages/gitdb/stream.py": 3035738824550231197, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_hermite_e.py": 8912795372542950449, + "venv/lib/python3.13/site-packages/pandas/core/groupby/base.py": 16888506204910366302, + "venv/lib/python3.13/site-packages/_yaml/__init__.py": 3395829783751571350, + "venv/lib/python3.13/site-packages/_pytest/setupplan.py": 7496801885995567141, + "venv/lib/python3.13/site-packages/numpy/testing/__init__.py": 2831275254313750978, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/_neighborhood_iterator_imp.h": 10634781863364525895, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/test_federal.py": 16758212916399857971, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_rank.py": 14115215895836854973, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/__init__.py": 8632038387100622009, + "venv/lib/python3.13/site-packages/pip/_internal/utils/misc.py": 17461379236189029253, + "venv/lib/python3.13/site-packages/attr/_config.py": 1864824728504480204, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/six.py": 1161259802604920635, + "venv/lib/python3.13/site-packages/authlib/integrations/base_client/sync_app.py": 14788764260762890368, + "venv/lib/python3.13/site-packages/pandas/core/tools/numeric.py": 8510988912136285022, + "venv/lib/python3.13/site-packages/anyio/abc/__init__.py": 11428476337116642278, + "venv/lib/python3.13/site-packages/fastapi/middleware/httpsredirect.py": 5775982317459922443, + "venv/lib/python3.13/site-packages/pip/_internal/commands/debug.py": 13528633965821062950, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_compat.py": 11481259393140789881, + "venv/lib/python3.13/site-packages/_pytest/_io/pprint.py": 11202117031437223527, + "venv/lib/python3.13/site-packages/pip/_internal/index/__init__.py": 14829074315307489628, + "venv/lib/python3.13/site-packages/numpy/random/bit_generator.pyi": 15790738442952696034, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_deprecate_kwarg.py": 10417724544218303733, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/cymysql.py": 5570881524647789741, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_frame_equal.py": 12459816298158655274, + "venv/lib/python3.13/site-packages/uvicorn/protocols/http/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/ecdsa/test_curves.py": 6577762025900435885, + "venv/lib/python3.13/site-packages/pygments/lexers/j.py": 9766594937623918053, + "backend/src/core/task_manager/manager.py": 5748204558787054193, + "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_pbkdf2.py": 4977067068389993374, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_equals.py": 11041884432990762650, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_chaining_and_caching.py": 11784574265874608859, + "venv/lib/python3.13/site-packages/pandas/tests/generic/test_series.py": 2980850649986213051, + "frontend/src/lib/stores/__tests__/sidebar.test.js": 5168138864675110669, + "venv/lib/python3.13/site-packages/greenlet/tests/test_version.py": 6317733744899804292, + "venv/lib/python3.13/site-packages/pandas/io/json/_json.py": 1393095374713367445, + "venv/lib/python3.13/site-packages/numpy/f2py/f90mod_rules.py": 2953876934840155920, + "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pydantic/functional_validators.py": 5835339743072048940, + "backend/src/api/routes/__tests__/test_dashboards.py": 14899529124453053148, + "venv/lib/python3.13/site-packages/pygments/lexers/jvm.py": 897669925284011089, + "backend/src/api/routes/health.py": 4494840518139445701, + "venv/lib/python3.13/site-packages/apscheduler/jobstores/mongodb.py": 5310116401542377882, + "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/_arrow_utils.py": 17808805047762566525, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libkeyutils-dfe70bd6.so.1.5": 9414227432578104677, + "venv/lib/python3.13/site-packages/cryptography/hazmat/backends/openssl/__init__.py": 4870269250323682576, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_win_type.py": 18105114803686556748, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/base.py": 13634605726041717070, + "venv/lib/python3.13/site-packages/pygments/lexers/idl.py": 11454152060351801696, + "venv/lib/python3.13/site-packages/websockets/http.py": 9406523480132204760, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_compat.py": 6032546839876195115, + "venv/lib/python3.13/site-packages/uvicorn/loops/auto.py": 13086005977087619545, + "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/METADATA": 14608873187265321271, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/base.py": 5294446834530589760, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/ufuncobject.h": 5112456258078662871, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_sparse.py": 14343335524508450058, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ufuncs.pyi": 1808147181379894521, + "venv/lib/python3.13/site-packages/pandas/tests/io/sas/test_sas.py": 9446226619493404132, + "venv/lib/python3.13/site-packages/authlib/integrations/starlette_client/__init__.py": 9083396324667433335, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_sort_values.py": 15072305514178699398, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_cumulative.py": 11752712818547022872, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_fillna.py": 4610666845291315333, + "build.sh": 37085706669440656, + "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/METADATA": 15876173086384975957, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_errors.py": 3301889847545579393, + "venv/lib/python3.13/site-packages/pydantic/_internal/_discriminated_union.py": 1818179910730401509, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_parse_iso8601.py": 9100947620267156161, + "venv/lib/python3.13/site-packages/pip/_internal/utils/egg_link.py": 18211947508793805393, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Indel.pyi": 16372699564794732463, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/connection.py": 8190661510572400096, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_align.py": 10284128457194204574, + "venv/lib/python3.13/site-packages/authlib/oidc/core/claims.py": 2035724920423374302, + "venv/lib/python3.13/site-packages/pandas/io/xml.py": 326466573926392764, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_numerictypes.py": 8836108334624807647, + "venv/lib/python3.13/site-packages/pandas/_libs/index.cpython-313-x86_64-linux-gnu.so": 18409579812171549318, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/abstract_interface/gh18403_mod.f90": 6219951399575860362, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/np_datetime.cpython-313-x86_64-linux-gnu.so": 7840846965295537844, + "venv/lib/python3.13/site-packages/git/diff.py": 6691274015605451565, + "run.sh": 4808871544409245302, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/lexers/__init__.py": 7316739139609490520, + "venv/lib/python3.13/site-packages/numpy/_utils/_pep440.py": 15854257948148516490, + "venv/lib/python3.13/site-packages/numpy/fft/_helper.py": 15906444100774706395, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_join.py": 13115782559805895662, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_set_name.py": 9094608008538024609, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-cosh.csv": 9287774220881887502, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_skew.py": 5276102682005383261, + "venv/lib/python3.13/site-packages/psycopg2/_psycopg.cpython-313-x86_64-linux-gnu.so": 13471342289207537479, + "venv/lib/python3.13/site-packages/authlib/integrations/base_client/framework_integration.py": 3935346152156572025, + "venv/lib/python3.13/site-packages/pip/_vendor/certifi/core.py": 2300277198409164253, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_dataframe.py": 14945138617962156057, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/interfaces.py": 5371646629319169153, + "frontend/src/lib/components/dataset-review/LaunchConfirmationPanel.svelte": 5965510319309867257, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/index.py": 2763962019423787504, + "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/setup.py": 5482352991573617756, + "venv/lib/python3.13/site-packages/numpy/_core/_add_newdocs.py": 15657897994221955882, + "venv/lib/python3.13/site-packages/greenlet/tests/fail_switch_two_greenlets.py": 11156275429806391390, + "backend/src/api/routes/__tests__/test_clean_release_source_policy.py": 15242462571646635922, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_numeric_only.py": 13610631681447961840, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8414/well_known.py": 7183795183137931709, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/queue.py": 7820997545569991670, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_antijoin.py": 14435396877404907826, + "frontend/build/_app/immutable/chunks/C9a28aU5.js": 5513483692277369740, + "backend/src/services/clean_release/repositories/audit_repository.py": 9617813121624417458, + "venv/lib/python3.13/site-packages/numpy/__init__.cython-30.pxd": 1476688171642957054, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/lib/mixins.pyi": 15597536290223628777, + "venv/lib/python3.13/site-packages/numpy/_core/defchararray.py": 15550230459945996915, + "venv/lib/python3.13/site-packages/dotenv/cli.py": 7952654621986067973, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/url.py": 9692934100602135106, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arcsinh.csv": 13695545125848146013, + "frontend/build/_app/immutable/chunks/Bxli7MEx.js": 13142003860484323974, + "venv/lib/python3.13/site-packages/pygments/lexers/basic.py": 12723021007042054402, + "venv/lib/python3.13/site-packages/pip/_internal/req/req_install.py": 7181005402818830655, + "venv/bin/pyrsa-encrypt": 5858278204727417482, + "frontend/src/lib/components/reports/__tests__/report_card.ux.test.js": 14899927226278257748, + "venv/lib/python3.13/site-packages/numpy/tests/test_configtool.py": 8208415015478205502, + "venv/lib/python3.13/site-packages/pandas/_libs/parsers.pyi": 18402185996588014111, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/test_other.py": 783701524491896337, + "backend/src/services/clean_release/__tests__/test_stages.py": 15353751625742444422, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_scalar_compat.py": 10068989979144965125, + "venv/lib/python3.13/site-packages/pygments/lexers/dalvik.py": 5549983892200382076, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/routines/funcfortranname.f": 9330535808093475892, + "venv/lib/python3.13/site-packages/pandas/util/_validators.py": 10201915528715968165, + "venv/lib/python3.13/site-packages/numpy/_core/shape_base.pyi": 10225243791643474943, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_join.py": 1131792466825856990, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-sin.csv": 1443087417808027268, + "venv/lib/python3.13/site-packages/fastapi/middleware/trustedhost.py": 4285622102799027823, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_network.py": 13159427333040109523, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_to_numpy.py": 14845850776625255794, + "backend/src/api/routes/storage.py": 15226523387798112491, + "venv/lib/python3.13/site-packages/numpy/_core/_asarray.py": 3468915833437229425, + "backend/src/services/dataset_review/clarification_engine.py": 13640221555439415988, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py": 5728866271812501558, + "venv/lib/python3.13/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz": 17626265752554406355, + "venv/lib/python3.13/site-packages/fastapi/_compat/shared.py": 5249003014594464718, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/requests.py": 6703055041957748929, + "venv/lib/python3.13/site-packages/idna/intranges.py": 12536174834761591006, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_index.py": 4908727359687606870, + "frontend/src/routes/dashboards/health/__tests__/health_page.integration.test.js": 11653729504599199718, + "backend/src/core/database.py": 7064138501006682572, + "venv/lib/python3.13/site-packages/requests/cookies.py": 4997226307650350876, + "venv/lib/python3.13/site-packages/authlib/jose/util.py": 14843923624043422219, + "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/METADATA": 9041650844081036822, + "venv/lib/python3.13/site-packages/pip/_internal/cli/parser.py": 1064209424435927674, + "venv/lib/python3.13/site-packages/uvicorn/supervisors/watchfilesreload.py": 13062440477475586751, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_no_deprecated_api.h": 7896028550808252043, + "backend/src/core/async_superset_client.py": 17097960069181151584, + "venv/lib/python3.13/site-packages/click/_winconsole.py": 8133229457213704188, + "venv/lib/python3.13/site-packages/httpcore/_async/interfaces.py": 18250473733460571816, + "frontend/src/routes/settings/automation/+page.svelte": 15129032082642700205, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/instrumentation.py": 13198832283145198959, + "backend/test_pat_api.py": 4206656943734821967, + "venv/lib/python3.13/site-packages/fastapi/templating.py": 10760135144345238340, + "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE": 2345070715845618109, + "backend/src/models/dataset_review_pkg/_enums.py": 2243691494729792539, + "venv/lib/python3.13/site-packages/keyring/completion.py": 1027436016070040568, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/fixtures/sql.py": 13315906751371033170, + "venv/lib/python3.13/site-packages/PIL/ImageEnhance.py": 3218405223202808764, + "venv/lib/python3.13/site-packages/cryptography/x509/verification.py": 1079694784860492682, + "docker/backend.Dockerfile": 10320070102635663967, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/scalars.pyi": 3130685119595923378, + "venv/lib/python3.13/site-packages/PIL/ImImagePlugin.py": 15452956038794399492, + "backend/src/models/dataset_review_pkg/_profile_models.py": 3121807680272913669, + "venv/lib/python3.13/site-packages/PIL/_imaging.pyi": 17119084207765761921, + "venv/lib/python3.13/site-packages/pandas/core/arrays/categorical.py": 15993211255162930859, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/pg_catalog.py": 15726448028057859831, + "venv/lib/python3.13/site-packages/PIL/TiffImagePlugin.py": 6845043550424565106, + "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/LICENSE": 6581019367004699816, + "venv/lib/python3.13/site-packages/uvicorn/protocols/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/fastapi/cli.py": 13972351579061154035, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_classes.py": 1440926036579216168, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_downcast.py": 10399746645741452200, + "venv/lib/python3.13/site-packages/pluggy/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_setops.py": 9991233160569430204, + "venv/lib/python3.13/site-packages/pandas/tests/frame/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/rpds/py.typed": 15130871412783076140, + "frontend/build/_app/immutable/chunks/YCgcezOr.js": 10021400156442571831, + "frontend/src/components/git/__tests__/git_manager.unfinished_merge.integration.test.js": 2437475749409257490, + "venv/lib/python3.13/site-packages/numpy/lib/_utils_impl.py": 13012775703413938576, + "venv/lib/python3.13/site-packages/pygments/lexers/graphql.py": 2307854666015224677, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/util.py": 4890614159013873000, + "venv/lib/python3.13/site-packages/pygments/lexers/arrow.py": 6699009974224818979, + "venv/lib/python3.13/site-packages/starlette/background.py": 10232175205397808417, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/lexers/_mapping.py": 17519715342700184302, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/asyncmy.py": 11808138763443525443, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arithmetic.pyi": 467927803068643024, + "venv/lib/python3.13/site-packages/pandas/_libs/arrays.cpython-313-x86_64-linux-gnu.so": 8634234308863090562, + "venv/lib/python3.13/site-packages/pandas/tests/apply/test_str.py": 149688376805129532, + "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_fillna.py": 16297834601116528166, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_datetime.py": 11417836378782978454, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_analytics.py": 8155550493311083506, + "venv/lib/python3.13/site-packages/pydantic/_internal/_core_utils.py": 13248004771466837645, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/mapped_collection.py": 11377280960650304531, + "venv/lib/python3.13/site-packages/PIL/WalImageFile.py": 12442826195371137238, + "venv/lib/python3.13/site-packages/PIL/_imagingtk.cpython-313-x86_64-linux-gnu.so": 1708198947135304326, + "venv/lib/python3.13/site-packages/PIL/ImageFile.py": 15765130658900167517, + "venv/lib/python3.13/site-packages/git/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/METADATA": 3643456577199395707, + "frontend/src/routes/reports/llm/[taskId]/+page.svelte": 14394593511269407852, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ufuncs.py": 12732599294816239111, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/operators.f90": 5949265961908386974, + "venv/lib/python3.13/site-packages/pygments/lexers/hare.py": 8693002231020751711, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py": 6712732248328458871, + "backend/src/core/utils/superset_context_extractor/_pii.py": 14826864290194304173, + "backend/src/core/superset_client/_dashboards_crud.py": 4664728216214173491, + "backend/src/plugins/translate/__tests__/test_orchestrator.py": 6546216896393785602, + "venv/lib/python3.13/site-packages/pydantic/tools.py": 8931184638883701008, + "venv/lib/python3.13/site-packages/pillow.libs/libXau-154567c4.so.6.0.0": 4340972632724893052, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_os.h": 7883047506449405400, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Indel.py": 17112989332785349705, + "venv/lib/python3.13/site-packages/gitdb/db/git.py": 13552203494737962026, + "venv/lib/python3.13/site-packages/_pytest/warnings.py": 8633394774569709013, + "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/ecdsa/ecdh.py": 12596317261561755793, + "venv/lib/python3.13/site-packages/numpy/lib/format.pyi": 11752225430594536662, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_regression.py": 9375525053479844400, + "venv/lib/python3.13/site-packages/pandas/core/reshape/pivot.py": 4736553530021653868, + "venv/lib/python3.13/site-packages/authlib/oauth1/errors.py": 1154126353335030046, + "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/METADATA": 8985010079096958084, + "venv/lib/python3.13/site-packages/packaging/version.py": 13470993734108007779, + "venv/lib/python3.13/site-packages/pyasn1/compat/__init__.py": 5902203086150636670, + "venv/lib/python3.13/site-packages/pandas/tests/base/test_constructors.py": 2439700578107653984, + "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/METADATA": 18410956221497090898, + "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/licenses/LICENSE": 8364608327598414179, + "venv/lib/python3.13/site-packages/rsa/__init__.py": 14623742580410014505, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_indexing.py": 16623126293364224089, + "venv/lib/python3.13/site-packages/PIL/report.py": 9428754440615681790, + "backend/src/services/clean_release/candidate_service.py": 14044982155441302890, + "venv/lib/python3.13/site-packages/attr/_funcs.py": 5302917072419624193, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/jwk.py": 6458001384702613165, + "venv/lib/python3.13/site-packages/apscheduler/executors/tornado.py": 6635576580306890718, + "backend/src/core/superset_client/_databases.py": 14837101840847724937, + "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/WHEEL": 2357997949040430835, + "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/licenses/LICENSE": 3509878235770224233, + "frontend/build/_app/immutable/chunks/CAvRWKcP.js": 11625217357725761813, + "backend/src/api/routes/__tests__/test_dataset_review_api.py": 17381354465400611694, + "backend/src/services/clean_release/stages/__init__.py": 11216464651705158032, + "venv/lib/python3.13/site-packages/urllib3-2.6.2.dist-info/METADATA": 2624107638694809013, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_repr.py": 5577699046802107144, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/annotation.py": 15855576199737688124, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py": 7624316342162916928, + "venv/lib/python3.13/site-packages/jsonschema/_keywords.py": 949562792260528762, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/abc.py": 6227782083136374888, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_normalize.py": 7378656958631172603, + "venv/lib/python3.13/site-packages/smmap/test/test_buf.py": 6910367739329016619, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_clip.py": 8087139801274517318, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/interval/test_interval_new.py": 3893421355544517336, + "backend/src/models/__tests__/test_clean_release.py": 3448406855108346883, + "backend/tests/test_task_persistence.py": 1764267414712263025, + "venv/lib/python3.13/site-packages/keyring/backends/libsecret.py": 11621088556661567306, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/endpoints.py": 18334891787808122725, + "venv/lib/python3.13/site-packages/pygments/lexers/theorem.py": 15666523910367201285, + "venv/lib/python3.13/site-packages/pluggy/__init__.py": 8269188363563356631, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_rank.py": 5398520257852104723, + "backend/src/services/reports/report_service.py": 17586405613867657190, + "venv/lib/python3.13/site-packages/numpy/tests/test_warnings.py": 3154249157134620649, + "venv/lib/python3.13/site-packages/passlib/handlers/sun_md5_crypt.py": 1949993216707182407, + "venv/lib/python3.13/site-packages/sqlalchemy/event/registry.py": 2857989070743633709, + "venv/lib/python3.13/site-packages/gitdb/util.py": 645937410168583692, + "venv/lib/python3.13/site-packages/websockets/sync/connection.py": 2791040669094103200, + "venv/lib/python3.13/site-packages/pip/_internal/cli/cmdoptions.py": 12107733000019393631, + "venv/lib/python3.13/site-packages/numpy/_typing/__init__.py": 18242238390157333247, + "venv/lib/python3.13/site-packages/passlib/crypto/scrypt/_builtin.py": 74545289993348593, + "venv/lib/python3.13/site-packages/sqlalchemy/__init__.py": 9751988289698484530, + "backend/src/core/auth/__tests__/test_auth.py": 14999763239648705195, + "venv/lib/python3.13/site-packages/numpy/random/_examples/cffi/extending.py": 9859383893070793914, + "venv/lib/python3.13/site-packages/starlette/authentication.py": 10905269573809129547, + "venv/lib/python3.13/site-packages/pandas/tests/frame/common.py": 7426635866622531875, + "backend/src/models/report.py": 9185954411073383239, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_api.py": 6439927798101672423, + "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/typing/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/passlib/handlers/phpass.py": 16489430001449160264, + "frontend/build/_app/immutable/nodes/22.STjI15IE.js": 217759497864778118, + "backend/src/api/auth.py": 16577622718621592661, + "backend/src/api/routes/dashboards/__init__.py": 15214026957520044598, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_datetimelike.py": 15246580603900811749, + "venv/lib/python3.13/site-packages/pandas/tests/base/test_misc.py": 9190561773853901527, + "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_set_value.py": 17480833128128893268, + "venv/lib/python3.13/site-packages/pandas/core/window/doc.py": 4204367435727844696, + "venv/lib/python3.13/site-packages/sqlalchemy/util/_py_collections.py": 16825222625411635946, + "frontend/src/components/storage/FileList.svelte": 11661146151896000393, + "venv/lib/python3.13/site-packages/pandas/tests/extension/list/test_list.py": 4538625980063375611, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/__init__.py": 15130871412783076140, + "frontend/src/routes/profile/+page.svelte": 17979719303737794427, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/_asymmetric.py": 6903778603779934572, + "venv/lib/python3.13/site-packages/numpy/f2py/common_rules.py": 7352635707860427145, + "venv/lib/python3.13/site-packages/pip/_internal/utils/direct_url_helpers.py": 6075890513530296650, + "venv/lib/python3.13/site-packages/pandas/_libs/pandas_datetime.cpython-313-x86_64-linux-gnu.so": 15874094681262240628, + "frontend/src/lib/ui/Icon.svelte": 2201964532717139995, + "venv/lib/python3.13/site-packages/pip/_internal/main.py": 13711402795965346761, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/tree.py": 4991806303236874829, + "venv/lib/python3.13/site-packages/pip/_internal/network/download.py": 5256731341219268693, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_combine.py": 16193618152415684421, + "venv/lib/python3.13/site-packages/pandas/tests/base/common.py": 13335865314907577354, + "venv/lib/python3.13/site-packages/annotated_doc/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/yaml/representer.py": 10550136298978225013, + "frontend/src/routes/datasets/review/__tests__/dataset_review_entry.test.js": 7689381541193660594, + "backend/tests/core/test_git_service_gitea_pr.py": 6098719224379185148, + "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/keyring/devpi_client.py": 3445890176686211982, + "venv/lib/python3.13/site-packages/authlib/integrations/base_client/sync_openid.py": 5670082466989878756, + "venv/lib/python3.13/site-packages/pip/_internal/distributions/base.py": 16714704323747942110, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_series.py": 10922287546912569276, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_comparison.py": 4611207880323503098, + "venv/lib/python3.13/site-packages/passlib/crypto/digest.py": 15890463288512025688, + "venv/lib/python3.13/site-packages/pandas/io/formats/printing.py": 3054136648863371285, + "venv/lib/python3.13/site-packages/sqlalchemy/pool/events.py": 13473858896579666058, + "venv/lib/python3.13/site-packages/pyasn1/type/tag.py": 13912273274509186651, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_drop_duplicates.py": 2397396876872403048, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_datetime64.py": 16272266806084058743, + "venv/lib/python3.13/site-packages/pandas/io/pickle.py": 5418646169167774144, + "venv/lib/python3.13/site-packages/dateutil/parser/isoparser.py": 18015430204844361067, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_shape_base.py": 8837845103338426570, + "venv/lib/python3.13/site-packages/pip/_vendor/idna/compat.py": 9758194415584776667, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_common_basic.py": 1945891789924713537, + "venv/lib/python3.13/site-packages/_pytest/mark/__init__.py": 9343015549300275714, + "venv/lib/python3.13/site-packages/smmap/util.py": 13276170835044840023, + "venv/lib/python3.13/site-packages/pygments/lexers/asc.py": 10744437946030875496, + "frontend/src/routes/dashboards/[id]/components/DashboardGitManager.svelte": 4990524146294551357, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7009/parameters.py": 1647490964392525933, + "venv/lib/python3.13/site-packages/click/core.py": 16793133181718891193, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/util.py": 6088868370298560146, + "venv/lib/python3.13/site-packages/urllib3/_base_connection.py": 14495079097019980154, + "venv/lib/python3.13/site-packages/requests/hooks.py": 9888227370911151341, + "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/test_support.pyi": 3893434234020223151, + "venv/lib/python3.13/site-packages/numpy/_core/fromnumeric.py": 9409852116555866847, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_size.py": 8527839563560550806, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/plugin/bootstrap.py": 3865947239497500815, + "venv/lib/python3.13/site-packages/charset_normalizer/__main__.py": 8887772289570078278, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_x64_masm.asm": 15349921092257597740, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_at_time.py": 15352346149584081864, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_count.py": 16947194157984708529, + "venv/lib/python3.13/site-packages/numpy/tests/test_matlib.py": 14395481530627870882, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_isin.py": 8136794647591341784, + "venv/lib/python3.13/site-packages/pip/_internal/network/auth.py": 6159841209618753296, + "venv/lib/python3.13/site-packages/authlib/oauth2/client.py": 7237359250450222680, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py": 10057637461647348368, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_engines.py": 2790877023773269276, + "venv/lib/python3.13/site-packages/pandas/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/licenses/numpy/_core/src/highway/LICENSE": 15636145745140922094, + "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/RECORD": 16740224557818847141, + "venv/lib/python3.13/site-packages/numpy/core/_dtype.pyi": 15130871412783076140, + "venv/lib/python3.13/site-packages/uvicorn/middleware/wsgi.py": 9179913252056394958, + "frontend/src/lib/stores/datasetReviewSession.js": 3915919349382526215, + "backend/src/services/__tests__/test_llm_provider.py": 4705572506930455768, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/enumerated.py": 16623643153933348755, + "venv/lib/python3.13/site-packages/pip/_internal/operations/build/wheel_legacy.py": 12735901522735836481, + "venv/lib/python3.13/site-packages/apscheduler/triggers/cron/expressions.py": 8015786996064244656, + "venv/lib/python3.13/site-packages/pytest_asyncio/__init__.py": 8162213292669110211, + "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE": 3996324383113221529, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_datetimelike.py": 5317270578347271174, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_logical/foo77.f": 16250175448122328648, + "venv/lib/python3.13/site-packages/websockets/server.py": 16599051743322571790, + "venv/lib/python3.13/site-packages/pyasn1/__init__.py": 8089201217593738032, + "venv/lib/python3.13/site-packages/pip/_internal/distributions/wheel.py": 13979536782410081712, + "venv/lib/python3.13/site-packages/pandas/tests/io/excel/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/fastapi/_compat/__init__.py": 12380131786677943349, + "venv/lib/python3.13/site-packages/httpcore/_backends/mock.py": 6849131425727092784, + "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pydantic/_internal/_generics.py": 16438159042939059717, + "venv/lib/python3.13/site-packages/anyio/_core/_synchronization.py": 7050001375718876608, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/resource_protector.py": 11539718751174980004, + "frontend/src/lib/ui/index.ts": 6883965364006817450, + "frontend/src/routes/datasets/+page.svelte": 18132315883450880052, + "frontend/src/components/storage/FileUpload.svelte": 14805293086068620697, + "venv/lib/python3.13/site-packages/numpy/random/_sfc64.pyi": 3536657185495782642, + "venv/lib/python3.13/site-packages/numpy/polynomial/laguerre.py": 151202984060101867, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_regression.py": 6742646011852052645, + "venv/lib/python3.13/site-packages/yaml/scanner.py": 8554164423030397366, + "frontend/build/_app/immutable/nodes/1.NnkkqxwF.js": 2557541528204355497, + "venv/lib/python3.13/site-packages/pluggy/_manager.py": 18202550145040079612, + "venv/lib/python3.13/site-packages/jsonschema_specifications/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/console.py": 3540660073442483692, + "venv/lib/python3.13/site-packages/pydantic/experimental/pipeline.py": 9493054841465227788, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_groupby.py": 16180421154734310873, + "venv/lib/python3.13/site-packages/apscheduler/jobstores/zookeeper.py": 4318643109309626098, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_indexing.py": 81180636019259296, + "venv/lib/python3.13/site-packages/authlib/oidc/discovery/models.py": 6924198551176217533, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_alter_axes.py": 11338712836362534969, + "venv/lib/python3.13/site-packages/pygments/lexers/zig.py": 470431199354307668, + "backend/src/models/dashboard.py": 8479838979214366697, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/__version__.py": 14138134075118474134, + "venv/lib/python3.13/site-packages/requests/structures.py": 2668010839316715865, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/nbit_base_example.pyi": 8571452382806614924, + "venv/lib/python3.13/site-packages/numpy/typing/__init__.pyi": 10058149460938883683, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_arithmetics.py": 16527169124402715210, + "venv/lib/python3.13/site-packages/jeepney/wrappers.py": 15234251727654692435, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_api.py": 14120915875574640262, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_x64_msvc.h": 17581467060937454503, + "venv/lib/python3.13/site-packages/pygments/lexers/__init__.py": 4627540245914849670, + "venv/lib/python3.13/site-packages/numpy/random/_examples/cython/extending_distributions.pyx": 15302775787158991386, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_take.py": 14969455660160053080, + "venv/lib/python3.13/site-packages/cffi/recompiler.py": 17809252277700094304, + "venv/lib/python3.13/site-packages/sqlalchemy/events.py": 9329355486544822483, + "venv/lib/python3.13/site-packages/pygments/lexers/c_cpp.py": 1211842877043321095, + "venv/lib/python3.13/site-packages/charset_normalizer/md__mypyc.cpython-313-x86_64-linux-gnu.so": 12629680116730190225, + "venv/lib/python3.13/site-packages/numpy/lib/stride_tricks.pyi": 5879290708972838025, + "venv/lib/python3.13/site-packages/pygments/lexers/prolog.py": 599539463980699865, + "backend/src/services/dataset_review/orchestrator_pkg/_helpers.py": 11613130235340753050, + "venv/lib/python3.13/site-packages/pygments/lexers/wowtoc.py": 17527844575999013592, + "venv/lib/python3.13/site-packages/pydantic/deprecated/json.py": 17692639396786690144, + "backend/src/api/routes/assistant/_history.py": 16318881016647964852, + "venv/lib/python3.13/site-packages/numpy/_core/fromnumeric.pyi": 13945478633722616250, + "backend/src/services/clean_release/repository.py": 3077298775936340703, + "venv/lib/python3.13/site-packages/pandas/tests/generic/test_finalize.py": 12539815426478689538, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test__version.py": 9037916251087760255, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_indexing.py": 4063662953863959087, + "backend/src/plugins/translate/metrics.py": 16289306156708185830, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/region.py": 12579403230062395563, + "venv/lib/python3.13/site-packages/pandas/io/formats/templates/html_table.tpl": 17198355067645888735, + "venv/lib/python3.13/site-packages/pygments/lexers/sas.py": 6128008194712914874, + "venv/lib/python3.13/site-packages/apscheduler/triggers/cron/fields.py": 8923326131649837922, + "venv/lib/python3.13/site-packages/pandas/io/sas/sas_xport.py": 16525568795475918874, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_strings.py": 7168544888340296373, + "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/licenses/LICENSE": 17020744174398828059, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/version.py": 6401479036177132374, + "venv/lib/python3.13/site-packages/ecdsa/__init__.py": 6237633580090653788, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/random.pyi": 9425735458050148191, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/module_data_docstring.f90": 18245403117623015312, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/__ufunc_api.h": 4516275133897904047, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/text.py": 6096215585499446070, + "venv/lib/python3.13/site-packages/pandas/core/common.py": 14578909424716153057, + "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_readers.py": 2136472634307645842, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_interval.py": 14749388246573356959, + "venv/lib/python3.13/site-packages/sqlalchemy/log.py": 10101221585967121334, + "backend/src/plugins/git/__init__.py": 2144831474780401625, + "venv/lib/python3.13/site-packages/pydantic/_internal/_decorators_v1.py": 16723743964226526155, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/npyio.pyi": 14147712281086095231, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/base.py": 13269563625127294513, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/const_vs_enum.py": 12873459583943748931, + "venv/lib/python3.13/site-packages/passlib/tests/test_hosts.py": 8284535274820610802, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_arithmetic.py": 17449383588949771785, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_scalar.py": 2434773821902319678, + "backend/src/services/clean_release/policy_engine.py": 3180817867716931725, + "venv/lib/python3.13/site-packages/pip/_vendor/msgpack/__init__.py": 5896924546560667519, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/errors.py": 12777931960088171310, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/interchange/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pyasn1/codec/der/decoder.py": 11876648884087997745, + "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_nanfunctions.py": 2356468092336024365, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_custom_business_month.py": 1613806538807726074, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_round.py": 14996007456708990295, + "venv/lib/python3.13/site-packages/cffi/vengine_gen.py": 6582900613549620556, + "venv/lib/python3.13/site-packages/authlib/integrations/django_client/__init__.py": 14186136918454802843, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_nth.py": 17151595277666975085, + "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/WHEEL": 12054782055241301457, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_swaplevel.py": 3584770792383216626, + "venv/lib/python3.13/site-packages/charset_normalizer/cli/__main__.py": 2148876100501691980, + "venv/lib/python3.13/site-packages/pandas/_version.py": 3480474785419232091, + "venv/lib/python3.13/site-packages/websockets/headers.py": 13587146967490088657, + "venv/lib/python3.13/site-packages/pandas/tests/reductions/test_stat_reductions.py": 11569525436540769152, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_qcut.py": 8972418112914954758, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/timedeltas/test_cumulative.py": 8178093067175155877, + "venv/lib/python3.13/site-packages/anyio/_core/_tempfile.py": 230288423480899091, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/asymmetric_key.py": 14948661854506285135, + "venv/lib/python3.13/site-packages/websockets/legacy/protocol.py": 762692033188392424, + "venv/lib/python3.13/site-packages/greenlet/tests/_test_extension.cpython-313-x86_64-linux-gnu.so": 1211491156495529145, + "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/WHEEL": 14550174241288068152, + "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py3-objarr.npz": 12369168483236619421, + "venv/lib/python3.13/site-packages/pandas/tests/tools/test_to_datetime.py": 3499461736474530062, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7662/models.py": 11521700876708601977, + "venv/lib/python3.13/site-packages/numpy/lib/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/secretstorage/collection.py": 17903258621904912493, + "venv/lib/python3.13/site-packages/anyio/streams/text.py": 12507591237176920223, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/mixed/foo_fixed.f90": 8079594129222364031, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/gh26920/two_mods_with_one_public_routine.f90": 7957683453180939605, + "venv/lib/python3.13/site-packages/pandas/tests/apply/test_invalid_arg.py": 610891871484326055, + "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_odf.py": 15484940480924122966, + "venv/lib/python3.13/site-packages/fastapi/__main__.py": 10755252341326986079, + "venv/lib/python3.13/site-packages/gitdb/db/pack.py": 13460574575424209609, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/RECORD": 5612689057373270213, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_libsparse.py": 1859997684694949029, + "venv/lib/python3.13/site-packages/pygments/lexers/robotframework.py": 11507713622179102496, + "venv/lib/python3.13/site-packages/pygments/lexers/unicon.py": 1155454077052833821, + "frontend/src/lib/auth/permissions.js": 12787489292887903923, + "backend/src/services/__tests__/test_rbac_permission_catalog.py": 5190751380981933250, + "backend/tests/test_resource_hubs.py": 3990299394875930767, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/ed448.pyi": 13016654355866474644, + "venv/lib/python3.13/site-packages/pandas/core/tools/times.py": 6560679305255812809, + "venv/lib/python3.13/site-packages/numpy/lib/_arraypad_impl.pyi": 2186875333484796630, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_append.py": 9644955836191900614, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_integer.py": 16102640589300356639, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_loongarch64_linux.h": 6566098590441297326, + "venv/lib/python3.13/site-packages/pydantic/_internal/_dataclasses.py": 15256193972947054890, + "venv/lib/python3.13/site-packages/ecdsa/ssh.py": 17010933326090778158, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/argon2.py": 15389474901714096890, + "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/websockets_sansio_impl.py": 17221368219485351394, + "venv/lib/python3.13/site-packages/passlib/tests/sample_config_1s.cfg": 1809713544666311828, + "venv/lib/python3.13/site-packages/fastapi/_compat/model_field.py": 392699021682253569, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/masked/test_function.py": 644452398244782315, + "backend/src/core/utils/network.py": 2917454689057905472, + "venv/lib/python3.13/site-packages/pygments/lexers/gcodelexer.py": 13712494327007684318, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_repr.py": 13147587049887075335, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_ddl.py": 3690189094298044728, + "venv/lib/python3.13/site-packages/rapidfuzz/utils.py": 5682627611354455144, + "venv/lib/python3.13/site-packages/pydantic/v1/main.py": 213087273729911492, + "venv/lib/python3.13/site-packages/numpy/f2py/crackfortran.py": 12796890117561096360, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py": 17873236864291624476, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_reindex.py": 8772423071604000084, + "backend/src/api/routes/git_schemas.py": 9243114525622127446, + "venv/lib/python3.13/site-packages/pandas/core/computation/expr.py": 13085709365673019494, + "venv/lib/python3.13/site-packages/pygments/lexers/oberon.py": 15220143799603451830, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_reset_index.py": 5946666595195952693, + "frontend/src/routes/tools/backups/+page.svelte": 6796050751535238723, + "venv/lib/python3.13/site-packages/pandas/io/excel/__init__.py": 6662687963241471393, + "venv/lib/python3.13/site-packages/fastapi/datastructures.py": 727178955746650271, + "frontend/build/_app/immutable/chunks/NoAbU4pR.js": 10213250914533162255, + "venv/lib/python3.13/site-packages/passlib/tests/backports.py": 17098448557554464521, + "venv/lib/python3.13/site-packages/pandas/tests/io/generate_legacy_storage_files.py": 11015954144633661960, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_arithmetic.py": 15071337112968886738, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/functions.py": 16567226076808693246, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_csky_gcc.h": 12929785267195988209, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/securetransport.py": 12104888897121018676, + "venv/lib/python3.13/site-packages/jose/__init__.py": 15575670403380555945, + "venv/lib/python3.13/site-packages/httpx/_main.py": 1812303705973265093, + "venv/lib/python3.13/site-packages/pandas/_libs/groupby.pyi": 6154952803757623626, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/assertions.py": 5556667947376168765, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/infer.py": 12541904372617915365, + "venv/lib/python3.13/site-packages/git/objects/submodule/__init__.py": 12958523219496107878, + "venv/lib/python3.13/site-packages/_pytest/unittest.py": 10848803633530338204, + "venv/lib/python3.13/site-packages/pandas/tests/resample/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_range.py": 3544406211185340973, + "venv/lib/python3.13/site-packages/dateutil/relativedelta.py": 5903095963079475484, + "backend/src/api/routes/__tests__/test_assistant_api.py": 16438212076178324033, + "backend/tests/test_smoke_plugins.py": 10704454611361370435, + "venv/lib/python3.13/site-packages/pandas/io/common.py": 12088195203620154171, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/einsumfunc.pyi": 8902765738896588686, + "venv/lib/python3.13/site-packages/numpy/polynomial/polyutils.pyi": 8195943561787777127, + "venv/lib/python3.13/site-packages/numpy/polynomial/chebyshev.py": 1590647503660777156, + "venv/lib/python3.13/site-packages/attr/exceptions.pyi": 12323493441583681402, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/client_auth.py": 3301527187487914220, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timestamps.cpython-313-x86_64-linux-gnu.so": 13209491675968970365, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_construction.py": 10619501873883861745, + "venv/lib/python3.13/site-packages/pydantic/dataclasses.py": 12384956801113860889, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/plugin/__init__.py": 10470644151952252331, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_common.py": 9097104320321411408, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_get_dummies.py": 16112674981222844375, + "venv/lib/python3.13/site-packages/authlib/integrations/sqla_oauth2/tokens_mixins.py": 9923721909178724437, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/__init__.py": 5933622200264572885, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_formats.py": 13752366280743004410, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/expression.py": 15511250785671695359, + "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/METADATA": 6960169108624303030, + "venv/lib/python3.13/site-packages/websockets/speedups.cpython-313-x86_64-linux-gnu.so": 10941658763526595989, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/persistence.py": 17908437937265384125, + "venv/lib/python3.13/site-packages/dateutil/parser/_parser.py": 9443336890192151397, + "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/licenses/LICENSE": 12684353321716745791, + "venv/lib/python3.13/site-packages/pydantic/_internal/_serializers.py": 12077180322353709930, + "venv/lib/python3.13/site-packages/pygments/lexers/tact.py": 1164971357219795420, + "venv/lib/python3.13/site-packages/pygments/lexers/markup.py": 10311057264960923433, + "venv/lib/python3.13/site-packages/annotated_doc/main.py": 3384049281532298500, + "venv/lib/python3.13/site-packages/pygments/lexers/compiled.py": 8215290039991205531, + "frontend/src/lib/api/assistant.js": 15470667170083225911, + "backend/src/api/routes/assistant/_schemas.py": 6735060826070731731, + "venv/lib/python3.13/site-packages/passlib/handlers/fshp.py": 13298827601291507295, + "backend/src/api/routes/assistant/__init__.py": 14959302162073618175, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/__init__.py": 3577295959173028042, + "venv/lib/python3.13/site-packages/pandas/_libs/sparse.pyi": 12526865400413362140, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/oracledb.py": 7158791242292470472, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_easter.py": 3397863170068576196, + "backend/src/api/routes/translate/__init__.py": 6357999798616465776, + "venv/lib/python3.13/site-packages/attr/filters.py": 14156141488477438777, + "venv/lib/python3.13/site-packages/numpy.libs/libscipy_openblas64_-096271d3.so": 14338208504131470036, + "venv/lib/python3.13/site-packages/pip/_internal/wheel_builder.py": 3893922370396791826, + "venv/lib/python3.13/site-packages/gitdb/pack.py": 7828244200815444166, + "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pydantic/fields.py": 4142211199816898047, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_io.py": 4199750143323741326, + "venv/lib/python3.13/site-packages/secretstorage/item.py": 15486045386471912693, + "venv/lib/python3.13/site-packages/numpy/lib/_array_utils_impl.pyi": 11535882608355652318, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/linalg.pyi": 7217817172953767893, + "frontend/src/lib/components/translate/TermCorrectionPopup.svelte": 572725570855489866, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_to_typst.py": 14619529283550788275, + "venv/lib/python3.13/site-packages/psycopg2/extras.py": 7510107741729866600, + "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/__init__.py": 17630019785411546070, + "venv/lib/python3.13/site-packages/pygments/lexers/json5.py": 9736695186548899403, + "backend/src/services/clean_release/manifest_service.py": 10873340336255220063, + "venv/lib/python3.13/site-packages/urllib3/exceptions.py": 9687187566231423953, + "frontend/src/components/TaskHistory.svelte": 7819646903874958430, + "venv/lib/python3.13/site-packages/pandas/tseries/__init__.py": 2648641118117738895, + "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/METADATA": 1862765380818895200, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_mask.py": 5451866071307236899, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/flatiter.pyi": 9598576095384661517, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_docs.py": 17898499890274685198, + "backend/src/services/clean_release/repositories/manifest_repository.py": 12395698948312301073, + "venv/lib/python3.13/site-packages/pandas/tests/tools/test_to_numeric.py": 18037815278545274402, + "backend/delete_running_tasks.py": 4453015771820664913, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/polynomial/hermite.pyi": 3573629536236108391, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_infer_objects.py": 6769980076977692890, + "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/RECORD": 8971487317739420905, + "venv/lib/python3.13/site-packages/numpy/_typing/_add_docstring.py": 14657346268890680694, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi": 16934250836612138541, + "venv/lib/python3.13/site-packages/charset_normalizer/cli/__init__.py": 9702005609966176016, + "venv/lib/python3.13/site-packages/pandas/_libs/missing.pyi": 13543139288840944411, + "venv/lib/python3.13/site-packages/numpy/_core/getlimits.py": 7842717073693731093, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_case_when.py": 4133174129660834805, + "venv/lib/python3.13/site-packages/authlib/oauth2/__init__.py": 13682931764896414722, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_pop.py": 14885271225856187795, + "venv/lib/python3.13/site-packages/jose/jwt.py": 3405491994268385178, + "venv/lib/python3.13/site-packages/httpx/_utils.py": 4836536051177895209, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_missing.py": 7034102519042351965, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_timedelta.py": 12822713918114715561, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_eng_formatting.py": 14768815444701392013, + "venv/lib/python3.13/site-packages/cffi/model.py": 13544803366359102016, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_array_coercion.py": 3551994404797560483, + "venv/lib/python3.13/site-packages/pandas/core/internals/managers.py": 13547912653290063698, + "venv/lib/python3.13/site-packages/pandas/tests/util/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/click/parser.py": 13988544851350599869, + "venv/lib/python3.13/site-packages/httpcore/_sync/http_proxy.py": 14037211594707072958, + "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/licenses/LICENSE": 7959949171752474553, + "venv/lib/python3.13/site-packages/pygments/lexers/chapel.py": 7619603166448145621, + "venv/lib/python3.13/site-packages/pygments/lexers/kusto.py": 16285533608924436308, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/validator.py": 14200019408839159476, + "venv/lib/python3.13/site-packages/pip/_vendor/idna/__init__.py": 6489437464172324468, + "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/METADATA": 4868544280955034757, + "venv/lib/python3.13/site-packages/rapidfuzz/fuzz.py": 1094797446508902066, + "venv/lib/python3.13/site-packages/pip/_internal/models/wheel.py": 10473287225032326206, + "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/licenses/LICENSE": 8438117004702803716, + "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/licenses/LICENSE": 3868190977070717994, + "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/limited_api2.pyx": 15936470837420356071, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timezones.cpython-313-x86_64-linux-gnu.so": 1137453907225619959, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/io/orc.py": 12754290195886557668, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/reflection.py": 5951113078754792806, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/properties.py": 5799883762127872919, + "venv/lib/python3.13/site-packages/numpy/_core/_umath_tests.cpython-313-x86_64-linux-gnu.so": 3478629877770507853, + "venv/lib/python3.13/site-packages/jeepney/io/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_internal/utils/urls.py": 8698341051522816167, + "venv/lib/python3.13/site-packages/pandas/compat/_constants.py": 3909166275649444824, + "venv/lib/python3.13/site-packages/tzlocal/utils.py": 7893867974548929154, + "frontend/build/_app/immutable/chunks/D9dVwiHl.js": 14131011399955207172, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_interval_tree.py": 15789443159423302291, + "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_npfuncs.py": 13987318550267921389, + "backend/tests/test_auth.py": 6167020718035755646, + "backend/src/services/git/_base.py": 4172145857123113006, + "venv/lib/python3.13/site-packages/httpx/__version__.py": 9473642134297828811, + "venv/lib/python3.13/site-packages/click-8.3.1.dist-info/WHEEL": 8600534672961461758, + "venv/lib/python3.13/site-packages/pygments/lexers/tls.py": 3552078393009383412, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_set_axis.py": 4097534573847151853, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_sort_values.py": 12064896795288302730, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_doc.py": 7865575374100528149, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/totp.py": 11888568940387752607, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/heuristics.py": 1422768761337195135, + "venv/lib/python3.13/site-packages/greenlet/TUserGreenlet.cpp": 16258622236210473101, + "venv/lib/python3.13/site-packages/pip/_internal/models/candidate.py": 3099785363867247334, + "venv/lib/python3.13/site-packages/pip/_vendor/tomli_w/py.typed": 9796674040111366709, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_abstract_interface.py": 17625870734108222276, + "venv/lib/python3.13/site-packages/fastapi/routing.py": 466387159689567541, + "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/licenses/LICENSE": 11199044866758471950, + "venv/lib/python3.13/site-packages/pydantic/experimental/missing_sentinel.py": 1637241331832367709, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/quoted_character/foo.f": 15318083678609435813, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/random/distributions.h": 17162252287980242413, + "venv/lib/python3.13/site-packages/numpy/_core/_dtype_ctypes.pyi": 14543653817631274394, + "venv/lib/python3.13/site-packages/pandas/_version_meson.py": 4875297137037630519, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/core": 6622984507059115517, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_reindex.py": 16348619731902304844, + "venv/lib/python3.13/site-packages/click/decorators.py": 2920877667855377423, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/wheel.py": 828222664427570058, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_asof.py": 8656532774394927388, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_dst.py": 12243642467695371088, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_http_headers.py": 13187746870209705951, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_iterrows.py": 3791886201566670087, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_quoting.py": 1147992698412929658, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_astype.py": 15384141282611151560, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/util.pyx": 649821942746469002, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/pcg64dxsm-testset-2.csv": 16611724551361692999, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_nat.py": 18428782698385974380, + "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/RECORD": 2024211292009381421, + "venv/lib/python3.13/site-packages/PIL/SpiderImagePlugin.py": 12234762087257163606, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_any_index.py": 10800968419286329268, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/arrayterator.pyi": 15722265241660279277, + "venv/lib/python3.13/site-packages/pygments/lexers/_mysql_builtins.py": 16087227298679432006, + "frontend/src/lib/utils/debounce.js": 16884470786640632815, + "backend/logs/app.log.2": 6692440785424103264, + "venv/lib/python3.13/site-packages/pygments/lexers/other.py": 6893672181003034910, + "venv/lib/python3.13/site-packages/cffi/verifier.py": 8473599757770867754, + "venv/lib/python3.13/site-packages/pandas/_libs/lib.pyi": 3049004444338566197, + "scripts/scan_secrets.sh": 599173385111063848, + "backend/src/schemas/dataset_review_pkg/_composites.py": 1898056130177244572, + "venv/lib/python3.13/site-packages/pandas/core/strings/accessor.py": 14021462534679430426, + "backend/tests/services/dataset_review/test_superset_matrix.py": 5114797134613078913, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/resource_protector.py": 9147671710440653567, + "venv/lib/python3.13/site-packages/pygments/lexers/dsls.py": 11696359794961833087, + "venv/lib/python3.13/site-packages/numpy/tests/test_numpy_config.py": 3636377988901907424, + "venv/lib/python3.13/site-packages/pandas/util/_doctools.py": 3766479022075611396, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_extension_array_equal.py": 16119106336497630796, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/util.py": 10640907133050862176, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/roles.py": 5448455900674232810, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_compression.py": 8577756801858495168, + "venv/lib/python3.13/site-packages/fastapi/param_functions.py": 14585101566697773072, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_ufunc.py": 5025605639221470838, + "venv/lib/python3.13/site-packages/_pytest/terminal.py": 885450969703476729, + "venv/lib/python3.13/site-packages/pandas/_libs/index.pyi": 17679349633640606985, + "backend/tests/services/clean_release/test_demo_mode_isolation.py": 1970652424726919519, + "venv/lib/python3.13/site-packages/_pytest/_py/error.py": 2257336434271433543, + "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/LICENSE": 16343563559291870811, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/array_constructors.pyi": 164538008396001632, + "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/implicit.py": 5400555157488808948, + "venv/lib/python3.13/site-packages/pygments/lexers/ampl.py": 4594260378653369985, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_file_buffer_url.py": 15377043158761455858, + "venv/lib/python3.13/site-packages/attr/_version_info.py": 3850449653182103457, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_block_docstring.py": 18146459246837890489, + "venv/lib/python3.13/site-packages/PIL/ImageWin.py": 13635870584232081323, + "venv/lib/python3.13/site-packages/sqlalchemy/event/__init__.py": 16182540084654095394, + "venv/lib/python3.13/site-packages/pandas/core/groupby/groupby.py": 5536922766239875007, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_get_set.py": 14375107704596905983, + "venv/lib/python3.13/site-packages/pyasn1/error.py": 11036037300994516747, + "venv/lib/python3.13/site-packages/urllib3/response.py": 1987354357306756790, + "venv/lib/python3.13/site-packages/apscheduler/schedulers/background.py": 2105814165112049750, + "venv/lib/python3.13/site-packages/websockets/speedups.c": 11800200877763464246, + "venv/lib/python3.13/site-packages/pyasn1/type/tagmap.py": 14075859042659927673, + "venv/lib/python3.13/site-packages/websockets/sync/router.py": 10335595955948355382, + "venv/lib/python3.13/site-packages/pip/_internal/commands/wheel.py": 16397273591245583356, + "venv/lib/python3.13/site-packages/jose/backends/native.py": 50023158618000658, + "venv/lib/python3.13/site-packages/passlib/tests/sample1c.cfg": 13088198405762342506, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_string.py": 13234624995755549329, + "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_cat_accessor.py": 4304062895283374927, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-log.csv": 221209423454319618, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/lib_version.pyi": 3636944488233043883, + "venv/lib/python3.13/site-packages/sqlalchemy/schema.py": 16029542421991709659, + "venv/lib/python3.13/site-packages/pycparser/__init__.py": 16499940957411260458, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_between.py": 337429135216881486, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/models.py": 16636119441941357281, + "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/METADATA": 7788873460115135563, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_time_series.py": 5205199092955498507, + "frontend/src/components/tasks/LogEntryRow.svelte": 10992388311554587653, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/yaml.py": 17801117405950663611, + "backend/src/services/dataset_review/clarification_pkg/_helpers.py": 4822867945130807759, + "venv/lib/python3.13/site-packages/numpy/lib/recfunctions.py": 3001896031623204147, + "venv/lib/python3.13/site-packages/pandas/core/arrays/sparse/accessor.py": 8373575175447947401, + "venv/lib/python3.13/site-packages/pandas/io/parsers/readers.py": 7899068668225389287, + "venv/lib/python3.13/site-packages/pandas/core/arrays/floating.py": 1215586266958421991, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/rule.py": 5318344538761246105, + "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/meson.build": 8784756284679859367, + "venv/lib/python3.13/site-packages/pygments/lexers/php.py": 17636450950688953288, + "venv/lib/python3.13/site-packages/pygments/lexers/amdgpu.py": 17249439604111838979, + "backend/test_auth.db": 18214842050920965999, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/modes.py": 6832635056228445650, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_seed_sequence.py": 7687465232954593311, + "venv/lib/python3.13/site-packages/pandas/io/clipboards.py": 5183212433608887727, + "venv/lib/python3.13/site-packages/fastapi/__init__.py": 11054203163075417954, + "frontend/src/lib/api/__tests__/reports_api.test.js": 11820587361929895100, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_datetime.py": 1678885152993000592, + "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/licenses/LICENSE": 17284252174641192866, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/dtypes/test_categorical.py": 9615571808720893016, + "venv/lib/python3.13/site-packages/click/types.py": 6491651131669287534, + "venv/lib/python3.13/site-packages/pandas/core/computation/ops.py": 18250656489649699560, + "venv/lib/python3.13/site-packages/pyasn1/codec/cer/decoder.py": 17559787396729804535, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py": 5324490103277723813, + "venv/lib/python3.13/site-packages/jaraco/classes/py.typed": 15130871412783076140, + "frontend/src/components/git/GitManager.svelte": 10046788495793334635, + "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/RECORD": 9803861100978586236, + "frontend/src/lib/stores/__tests__/test_taskDrawer.js": 13582869875796637016, + "backend/src/services/clean_release/repositories/__init__.py": 9926779835387630811, + "venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/RECORD": 12015288851076448205, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/f2cmap/isoFortranEnvMap.f90": 15768643271492041853, + "venv/lib/python3.13/site-packages/tzlocal-5.3.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/PIL/BmpImagePlugin.py": 16682567630621529903, + "venv/lib/python3.13/site-packages/numpy/typing/tests/test_typing.py": 10311989961588249147, + "venv/lib/python3.13/site-packages/pandas/io/stata.py": 14370692318835340323, + "frontend/src/lib/stores/__tests__/test_datasetReviewSession.js": 12620073136419991103, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/base_key.py": 10402800587070352851, + "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/numpy/linalg/_linalg.pyi": 9629457630163613604, + "venv/lib/python3.13/site-packages/pydantic/aliases.py": 14481980467204711659, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_comparisons.py": 2764694394636775220, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_drop_duplicates.py": 5874427993953991216, + "venv/lib/python3.13/site-packages/numpy/_core/numerictypes.py": 6205675182996213576, + "venv/lib/python3.13/site-packages/pandas/core/util/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/io/formats/templates/html.tpl": 12550218314797699304, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_multiarray.py": 5928288171180750841, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_array.py": 16867692337986161626, + "venv/lib/python3.13/site-packages/PIL/AvifImagePlugin.py": 1872412416505003364, + "backend/src/scripts/seed_permissions.py": 12022710984424584961, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/ranges/test_join.py": 11910166566689889389, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_offsets.py": 4499538899492148499, + "backend/src/core/auth/__init__.py": 16435384828055133073, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libk5crypto-b1f99d5c.so.3.1": 10669523137596317954, + "venv/lib/python3.13/site-packages/pydantic/_internal/_utils.py": 10936299012115637790, + "venv/lib/python3.13/site-packages/sqlalchemy/event/attr.py": 5971663429221923939, + "venv/lib/python3.13/site-packages/pygments/lexers/codeql.py": 4254046699692048717, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/endpoint.py": 8519226897299461194, + "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/licenses/numpy/ma/LICENSE": 6723325434476453127, + "venv/lib/python3.13/site-packages/numpy/_core/umath.pyi": 11775804030935134661, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_contains.py": 5273759677032634868, + "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/METADATA": 6438256145634479451, + "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_matrix_linalg.py": 2858987911347541177, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/aiosqlite.py": 9888795404090331319, + "venv/lib/python3.13/site-packages/jeepney/tests/test_low_level.py": 3579024079580951234, + "venv/lib/python3.13/site-packages/pip/_vendor/idna/codec.py": 14814999235499530522, + "venv/lib/python3.13/site-packages/numpy/_core/_internal.py": 13056946834347643109, + "venv/lib/python3.13/site-packages/numpy/linalg/lapack_lite.cpython-313-x86_64-linux-gnu.so": 11074560904305737689, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/testing.pyi": 16263777835572515739, + "venv/lib/python3.13/site-packages/pandas/core/window/online.py": 13551140636252302201, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_dict.py": 808180749933361743, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_formats.py": 1265319125235346745, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_multiprocessing.py": 5522490547285108448, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_diff.py": 2216748778018882333, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/naming.py": 14503932124671438340, + "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/RECORD": 6349071557579241461, + "venv/lib/python3.13/site-packages/pygments/lexers/iolang.py": 10997512999728724130, + "venv/lib/python3.13/site-packages/numpy/typing/tests/test_runtime.py": 12534621594420844770, + "frontend/src/types/dashboard.ts": 9718666955722367566, + "frontend/src/components/git/DeploymentModal.svelte": 6232413208557068848, + "venv/lib/python3.13/site-packages/jsonschema/validators.py": 3832874295500213309, + "venv/lib/python3.13/site-packages/jose/exceptions.py": 8562012933958941039, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_integrity.py": 12010564145080323987, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_real/foo90.f90": 58572216018544419, + "venv/lib/python3.13/site-packages/sqlalchemy/util/compat.py": 16148553252010089561, + "backend/src/services/notifications/__init__.py": 13930898146447654299, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/tablegen.py": 2917626373516779857, + "venv/lib/python3.13/site-packages/pandas/tests/tools/test_to_time.py": 10463741033950978270, + "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/jeepney/tests/test_wrappers.py": 4813328996575597980, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/warnings_and_errors.pyi": 9019577563960071498, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_repeat.py": 16971624301893342868, + "venv/lib/python3.13/site-packages/secretstorage/dhcrypto.py": 8860116996905452758, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ufunc_config.py": 14728417910466146460, + "venv/lib/python3.13/site-packages/passlib/handlers/md5_crypt.py": 7669328623914474137, + "venv/lib/python3.13/site-packages/pandas/core/indexes/datetimelike.py": 4356700619632833878, + "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/WHEEL": 13232065379159720345, + "venv/lib/python3.13/site-packages/PIL/py.typed": 15130871412783076140, + "frontend/tailwind.config.js": 13139381085024634801, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/visitors.py": 5190923415390076080, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/__init__.py": 6532094538918910232, + "venv/lib/python3.13/site-packages/pandas/tests/computation/test_eval.py": 2921337784087808487, + "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/METADATA": 1684451390569121499, + "venv/lib/python3.13/site-packages/rsa/parallel.py": 4968566722541778371, + "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/licenses/LICENSE": 16147999986886816451, + "venv/lib/python3.13/site-packages/rapidfuzz/_feature_detector.py": 15458248567257759500, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_reshape.py": 10576230966727810832, + "frontend/src/lib/stores/__tests__/mocks/state.js": 3616783395254238777, + "venv/lib/python3.13/site-packages/apscheduler/jobstores/base.py": 11386927749822539057, + "frontend/src/services/toolsService.js": 1568142461283466080, + "venv/lib/python3.13/site-packages/starlette/middleware/exceptions.py": 10408538054406276433, + "venv/lib/python3.13/site-packages/pygments/formatter.py": 11156733836937309465, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalarmath.py": 9762951701352556447, + "backend/tests/test_logger.py": 11506419828617508917, + "venv/lib/python3.13/site-packages/cryptography/hazmat/asn1/__init__.py": 4290669519959060720, + "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/RECORD": 9678861487447993169, + "frontend/src/lib/components/layout/__tests__/test_topNavbar.svelte.js": 11053755879172333925, + "venv/lib/python3.13/site-packages/apscheduler/jobstores/memory.py": 9947113720821563254, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_common.py": 2223069451949206319, + "frontend/src/lib/components/translate/TranslationPreview.svelte": 192144965481842186, + "venv/lib/python3.13/site-packages/pip/_internal/network/utils.py": 13421242949994555055, + "backend/src/services/clean_release/publication_service.py": 5215271639026811180, + "venv/lib/python3.13/site-packages/apscheduler/triggers/combining.py": 1264860868414723829, + "venv/lib/python3.13/site-packages/numpy/ma/extras.py": 7559838309481824664, + "venv/lib/python3.13/site-packages/pandas/tests/apply/test_frame_apply.py": 8103417303073393969, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_api.py": 1910541672533352625, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_timegrouper.py": 276805119790266454, + "venv/lib/python3.13/site-packages/_pytest/deprecated.py": 5452408732344157294, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/JaroWinkler_py.py": 16189204441438239231, + "venv/lib/python3.13/site-packages/pip/_internal/commands/install.py": 17631777189475433503, + "venv/lib/python3.13/site-packages/numpy/_typing/_nbit_base.py": 7446626038222262489, + "venv/lib/python3.13/site-packages/greenlet/platform/setup_switch_x64_masm.cmd": 8473866644286785815, + "venv/lib/python3.13/site-packages/numpy/random/_pcg64.pyi": 14785496840737986355, + "venv/lib/python3.13/site-packages/requests/_internal_utils.py": 4091305333167213279, + "backend/src/api/routes/translate/_helpers.py": 13265933139767815312, + "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/METADATA": 14070889071949105584, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_shape_base.py": 4117141349022217163, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/block_docstring/foo.f": 2489756567993745076, + "venv/lib/python3.13/site-packages/greenlet/TPythonState.cpp": 12828594265749606443, + "venv/lib/python3.13/site-packages/fastapi/utils.py": 15402403831700548944, + "venv/lib/python3.13/site-packages/itsdangerous/url_safe.py": 13454753900702274266, + "venv/lib/python3.13/site-packages/gitdb/__init__.py": 15381521749253695923, + "venv/lib/python3.13/site-packages/pydantic/experimental/__init__.py": 492399139963056461, + "venv/lib/python3.13/site-packages/passlib/crypto/scrypt/_salsa.py": 1756536674254931838, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libcrypto-2e26a48f.so.3": 3658133521245392852, + "venv/lib/python3.13/site-packages/pandas/tests/apply/test_frame_transform.py": 6759417099641034692, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_missing.py": 2976310411820303374, + "venv/lib/python3.13/site-packages/numpy/f2py/_src_pyf.py": 546976622482819095, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_to_julian_date.py": 6868080396693831272, + "venv/lib/python3.13/site-packages/pandas/core/indexes/range.py": 3838277376774246164, + "venv/lib/python3.13/site-packages/pandas/tests/libs/test_lib.py": 6721066015604956469, + "venv/lib/python3.13/site-packages/greenlet/tests/fail_clearing_run_switches.py": 3020294789560975101, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_size.py": 16658280641516916790, + "venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.pyi": 16799580742094025110, + "venv/lib/python3.13/site-packages/pillow.libs/libavif-01e67780.so.16.3.0": 13947441788826399574, + "venv/lib/python3.13/site-packages/pyasn1/codec/der/encoder.py": 4790267438222037867, + "venv/lib/python3.13/site-packages/git/objects/__init__.py": 2110615482320355676, + "venv/lib/python3.13/site-packages/urllib3/py.typed": 12220177918227495093, + "frontend/eslint.config.js": 14296985543214702398, + "backend/src/core/mapping_service.py": 15428037653286485748, + "backend/src/plugins/translate/__tests__/test_clickhouse_insert_integration.py": 6773352403538028864, + "frontend/build/_app/immutable/chunks/7w4QGhl2.js": 163809850003626570, + "venv/lib/python3.13/site-packages/numpy/_typing/_ufunc.py": 16859854811012717088, + "venv/lib/python3.13/site-packages/websockets/exceptions.py": 12048063047468402740, + "venv/lib/python3.13/site-packages/iniconfig-2.3.0.dist-info/METADATA": 16498441272591167134, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_matplotlib.py": 12775850858284584296, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_types.py": 4083297168618981717, + "venv/lib/python3.13/site-packages/PIL/ImageMorph.py": 13861961228125789866, + "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/METADATA": 13776251749114562660, + "venv/lib/python3.13/site-packages/uvicorn/middleware/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/vector.py": 9941185556118343263, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_libfrequencies.py": 16302676625743291594, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_cpu.h": 13799366908726531089, + "venv/lib/python3.13/site-packages/numpy/linalg/tests/test_deprecations.py": 14624516389510242394, + "venv/lib/python3.13/site-packages/tzlocal/win32.py": 1949985224569608978, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/authenticate_client.py": 16886645731942472285, + "venv/lib/python3.13/site-packages/rsa/pkcs1_v2.py": 3533398238491175229, + "venv/lib/python3.13/site-packages/pip/_internal/distributions/sdist.py": 9490366272695301385, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_index_new.py": 796607958391882182, + "venv/lib/python3.13/site-packages/click/_utils.py": 16491408631876616271, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_produces_warning.py": 750781117570375777, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/exceptions.py": 14982764273485726199, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_period.py": 6047041810867211174, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_accessor.py": 10074496835594855786, + "venv/lib/python3.13/site-packages/pandas/io/api.py": 8275376846208437741, + "venv/lib/python3.13/site-packages/dateutil/zoneinfo/__init__.py": 10261654903106416888, + "venv/lib/python3.13/site-packages/click/_termui_impl.py": 3296085065310341855, + "venv/lib/python3.13/site-packages/gitdb/typ.py": 16374739906244805047, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/__init__.py": 71159121705871510, + "venv/lib/python3.13/site-packages/numpy/_core/_simd.pyi": 13553578040186939249, + "venv/lib/python3.13/site-packages/passlib/utils/pbkdf2.py": 15802959653228956404, + "venv/lib/python3.13/site-packages/git/objects/fun.py": 15014209679156260865, + "venv/lib/python3.13/site-packages/pygments/lexers/snobol.py": 414946539602526513, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/sfc64-testset-1.csv": 5708101386018266998, + "venv/lib/python3.13/site-packages/numpy/_core/_dtype.py": 7796703383405149774, + "venv/lib/python3.13/site-packages/PIL/MpoImagePlugin.py": 644327650732339301, + "venv/lib/python3.13/site-packages/pandas/core/arrays/datetimelike.py": 394614958716301173, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/__init__.py": 15130871412783076140, + "frontend/src/routes/storage/+page.svelte": 5003283673039328108, + "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/lib/_user_array_impl.py": 13115032674230220791, + "backend/src/api/routes/git/_router.py": 8701683910668868827, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_einsum.py": 17286384318722237180, + "venv/lib/python3.13/site-packages/httpcore/_async/connection.py": 2155744160496556069, + "venv/bin/websockets": 12099863324306500376, + "dist/docker/frontend.v1.0.0-rc2-docker.tar": 1705027241971137937, + "venv/lib/python3.13/site-packages/psycopg2/errors.py": 10441360078612452560, + "venv/lib/python3.13/site-packages/_pytest/capture.py": 17850967053989495422, + "venv/lib/python3.13/site-packages/apscheduler/jobstores/sqlalchemy.py": 811968833735951956, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/random.py": 12722750410117529986, + "venv/lib/python3.13/site-packages/passlib/apache.py": 2357244937629445006, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_combine_first.py": 3100691245280812910, + "frontend/src/components/llm/ValidationReport.svelte": 11810571826279890996, + "venv/lib/python3.13/site-packages/pyasn1/codec/streaming.py": 13202773554072937140, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/tools.py": 6437315459200419827, + "venv/lib/python3.13/site-packages/pandas/_testing/_warnings.py": 5640964566228306285, + "venv/lib/python3.13/site-packages/pillow.libs/libsharpyuv-95d8a097.so.0.1.2": 5320647379976541240, + "venv/lib/python3.13/site-packages/pandas/io/sas/sasreader.py": 12766780210114366112, + "venv/lib/python3.13/site-packages/pygments/lexers/julia.py": 13516407944275342328, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/util.py": 5252158959761945519, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_compression.py": 3167245427092658522, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/datasource.pyi": 80441145804744162, + "venv/lib/python3.13/site-packages/pandas/_libs/reshape.cpython-313-x86_64-linux-gnu.so": 10639750501333255943, + "venv/lib/python3.13/site-packages/pandas/core/array_algos/masked_reductions.py": 633644255158859565, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libselinux-0922c95c.so.1": 16516617710645376769, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_value_attrspec.py": 7741590618654448240, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/provision.py": 2676876852726658063, + "venv/lib/python3.13/site-packages/_pytest/terminalprogress.py": 5490989904517343259, + "docker/backend.entrypoint.sh": 16079402358996730188, + "backend/src/core/superset_profile_lookup.py": 7009220677319131100, + "frontend/src/lib/components/reports/reportTypeProfiles.js": 10752964062056890725, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_mangle_dupes.py": 15613858733226279352, + "venv/lib/python3.13/site-packages/urllib3/contrib/socks.py": 645086578806871685, + "venv/lib/python3.13/site-packages/pydantic/v1/color.py": 14545509649623975912, + "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/RECORD": 12555714734722889695, + "backend/logs/app.log.1": 5033969853318871181, + "venv/lib/python3.13/site-packages/anyio/lowlevel.py": 11919467357746648592, + "venv/lib/python3.13/site-packages/pip/_internal/commands/check.py": 6937252393012787214, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/compat.py": 17520043776344667309, + "venv/lib/python3.13/site-packages/numpy/lib/_shape_base_impl.pyi": 12052895927933479005, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/fromnumeric.pyi": 6708743411405679061, + "venv/lib/python3.13/site-packages/pygments/lexers/apl.py": 16767474673619335683, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ufunclike.pyi": 14623308681600207918, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/__init__.py": 15130871412783076140, + "frontend/src/routes/+page.svelte": 5766160745549070101, + "venv/lib/python3.13/site-packages/passlib/crypto/_blowfish/_gen_files.py": 6273314671315377131, + "venv/lib/python3.13/site-packages/PIL/McIdasImagePlugin.py": 14137545069906812251, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/negative_bounds/issue_20853.f90": 3911644649277659333, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7662/introspection.py": 14645217065508644154, + "venv/bin/activate.csh": 14513616043256836238, + "frontend/src/routes/settings/notifications/+page.svelte": 14504217413720856827, + "frontend/src/components/auth/ProtectedRoute.svelte": 7705373237339863677, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/sfc64-testset-2.csv": 4052796393463357888, + "frontend/src/lib/components/layout/Breadcrumbs.svelte": 17883872226488804922, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/ranges.py": 14549386056660231279, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_umath_accuracy.py": 3904008607472505376, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_insert.py": 9195027887301180927, + "venv/lib/python3.13/site-packages/httpx/_transports/__init__.py": 5931944625631942279, + "venv/lib/python3.13/site-packages/apscheduler/util.py": 11440701924954413440, + "venv/lib/python3.13/site-packages/gitdb/db/__init__.py": 2615504918450276693, + "venv/lib/python3.13/site-packages/_pytest/_code/code.py": 5248836309618416369, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/base.py": 7110726987481713080, + "venv/lib/python3.13/site-packages/pip/__main__.py": 574438877257676911, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_concat.py": 1351478313334379336, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/writeonly.py": 14213960431308620600, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/toml.py": 2225382735669153136, + "venv/lib/python3.13/site-packages/numpy/strings/__init__.pyi": 6213864891045042045, + "venv/lib/python3.13/site-packages/pandas/io/formats/string.py": 14718123345899686696, + "venv/lib/python3.13/site-packages/pygments/lexers/csound.py": 2175454840368374515, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Hamming.py": 3762934316884129514, + "venv/bin/httpx": 6651007446865716794, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/client.py": 18443944554871937480, + "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/RECORD": 8956798822782402202, + "venv/bin/pip3": 13861749540792881808, + "venv/lib/python3.13/site-packages/pandas/tests/generic/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_indexing.py": 16264430536704066847, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_append_common.py": 2377351120083195530, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/jupyter.py": 4446535289156468239, + "venv/lib/python3.13/site-packages/pandas/tseries/holiday.py": 1134358927599488799, + "venv/lib/python3.13/site-packages/uvicorn/protocols/utils.py": 6147480543268737542, + "venv/lib/python3.13/site-packages/pygments/lexers/_php_builtins.py": 2344562378679281201, + "venv/lib/python3.13/site-packages/pygments/lexers/esoteric.py": 16839499623020685146, + "backend/src/services/git/_merge.py": 16183885992149357137, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_box_unbox.py": 7579587620530953711, + "venv/lib/python3.13/site-packages/typing_inspection-0.4.2.dist-info/METADATA": 14126682758562488244, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/filesize.py": 13234856188049714485, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_sort.py": 11941630642630249576, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/signature.py": 3614545297259098566, + "venv/lib/python3.13/site-packages/numpy/ma/README.rst": 13203411621071845216, + "venv/lib/python3.13/site-packages/numpy/f2py/setup.cfg": 17974180080760231128, + "venv/lib/python3.13/site-packages/click/globals.py": 18353671282454331523, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_timezones.py": 4771674179164304731, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/mariadbconnector.py": 14865906347579451546, + "venv/lib/python3.13/site-packages/pygments/lexers/ldap.py": 17455983150181177257, + "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/WHEEL": 5838667082726674838, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/arrayprint.pyi": 2424868797160722318, + "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_numeric.py": 17391095502314929042, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/core/computation/engines.py": 1889823242704769952, + "venv/lib/python3.13/site-packages/greenlet/greenlet_internal.hpp": 13173115501223013762, + "venv/lib/python3.13/site-packages/greenlet/TExceptionState.cpp": 17617354576371846109, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_constructors.py": 17392287676196409733, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_nlargest_nsmallest.py": 11049656379959696695, + "venv/lib/python3.13/site-packages/uvicorn/middleware/proxy_headers.py": 13206297306768491360, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_character/foo90.f90": 2435648607844815947, + "venv/lib/python3.13/site-packages/pandas/core/groupby/ops.py": 2036968385544457016, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_windows.py": 13341168721223961872, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_simd.py": 7577408467041941595, + "venv/lib/python3.13/site-packages/pandas/core/array_algos/putmask.py": 10934101690788549526, + "venv/lib/python3.13/site-packages/passlib/ifc.py": 14479133654556657428, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/shape.py": 13410030168622484801, + "venv/lib/python3.13/site-packages/numpy/strings/__init__.py": 6523674574341354095, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/__init__.py": 12975130533860125272, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_structures.py": 16375837477294135345, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_symbol.py": 10792387279408528886, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_xs.py": 11073752361562791165, + "venv/lib/python3.13/site-packages/pip/_internal/cli/autocompletion.py": 7651919329635563739, + "venv/lib/python3.13/site-packages/numpy/core/overrides.py": 11228841530565061867, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_sequence.py": 3721630758772772345, + "scripts/generate_financial_arrears_data.py": 15989529185799374517, + "venv/lib/python3.13/site-packages/pandas/core/computation/scope.py": 16859504937881548797, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/gh25337/data.f90": 13833561828029901615, + "frontend/src/lib/stores/environmentContext.js": 1762535408632658714, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/mapper.py": 16715864918034312591, + "frontend/build/_app/immutable/chunks/D9yKQnUv.js": 14194129519610241122, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/parsing.cpython-313-x86_64-linux-gnu.so": 13104135217735363438, + "frontend/build/_app/immutable/nodes/7.DPFqrug7.js": 12760418087468020149, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_x86_msvc.h": 2465089365642299646, + "venv/lib/python3.13/site-packages/pydantic/v1/_hypothesis_plugin.py": 6383299780629629080, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/datetimes/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/LICENSE": 11167201188673981085, + "venv/lib/python3.13/site-packages/urllib3/util/response.py": 6106698559175827207, + "frontend/vite.config.js": 6182424215365912367, + "frontend/src/lib/stores/sidebar.js": 8409084311045055153, + "backend/src/core/auth/jwt.py": 2300190538212143465, + "backend/src/api/routes/datasets.py": 10960880516483380521, + "venv/lib/python3.13/site-packages/numpy/f2py/cfuncs.pyi": 8232620851756907417, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/orderinglist.py": 10162099108656794338, + "venv/include/site/python3.13/greenlet/greenlet.h": 16216120538647882082, + "venv/lib/python3.13/site-packages/pandas/tests/io/xml/test_xml.py": 6906547816798138000, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_repr.py": 11737029058271003097, + "venv/lib/python3.13/site-packages/pygments/lexers/grammar_notation.py": 9009644754106884095, + "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/WHEEL": 9788895026336324100, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/arrayobject.h": 4345432890284821918, + "venv/lib/python3.13/site-packages/pygments/sphinxext.py": 2768161754523156130, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/assumed_shape/foo_use.f90": 2657057181044959432, + "venv/lib/python3.13/site-packages/websockets/legacy/http.py": 16266924390281399720, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/dtype.pyi": 465999539608816848, + "venv/lib/python3.13/site-packages/pygments/lexers/_ada_builtins.py": 16331776886501464264, + "venv/lib/python3.13/site-packages/httpcore/_backends/sync.py": 576247220834179928, + "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/WHEEL": 8600534672961461758, + "venv/lib/python3.13/site-packages/git/objects/tree.py": 10239027037919781330, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/__init__.py": 11526998905763262175, + "venv/lib/python3.13/site-packages/gitdb/test/test_util.py": 541144638852210407, + "venv/lib/python3.13/site-packages/pygments/lexers/shell.py": 3842338968014057744, + "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/keyring/backend_complete.zsh": 14339032329370660432, + "venv/lib/python3.13/site-packages/rsa/key.py": 4783269051050521475, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-exp.csv": 601782991752922510, + "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/METADATA": 14543822270526146783, + "venv/lib/python3.13/site-packages/rapidfuzz/__pyinstaller/test_rapidfuzz_packaging.py": 5189055608193366093, + "venv/lib/python3.13/site-packages/numpy/_utils/__init__.py": 7731065084036767848, + "venv/lib/python3.13/site-packages/uvicorn/lifespan/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/_sql_builtins.py": 5906655440517434734, + "venv/lib/python3.13/site-packages/anyio/abc/_sockets.py": 15042495744291042062, + "venv/lib/python3.13/site-packages/attr/converters.pyi": 17675091788614033166, + "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/LICENSE": 8423899413955829149, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/jwe_encs.py": 9703572751543253904, + "venv/lib/python3.13/site-packages/numpy/_core/lib/pkgconfig/numpy.pc": 14713682399742251335, + "venv/lib/python3.13/site-packages/pandas/tests/window/moments/conftest.py": 6137462119729731945, + "docker/all-in-one.Dockerfile": 10896793266219904099, + "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/WHEEL": 2357997949040430835, + "venv/lib/python3.13/site-packages/cffi/cffi_opcode.py": 8411344362428947268, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_formats.py": 13500155167624433622, + "venv/lib/python3.13/site-packages/pip/_internal/utils/packaging.py": 15555781487631855604, + "venv/lib/python3.13/site-packages/ecdsa/ecdsa.py": 9770689913380927623, + "venv/lib/python3.13/site-packages/httpx/_transports/base.py": 6498990531443644896, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_construct_ndarray.py": 6863874753941288585, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_common.py": 12064185052519470358, + "venv/lib/python3.13/site-packages/greenlet/tests/__init__.py": 1181423074160870921, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_month.py": 8485374160446988786, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_cumulative.py": 16283954014170928273, + "venv/lib/python3.13/site-packages/pycparser/_c_ast.cfg": 2649342580905806389, + "venv/lib/python3.13/site-packages/jaraco/classes/meta.py": 15930255620248873425, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_arrayterator.py": 1646530870049791291, + "venv/lib/python3.13/site-packages/cryptography/fernet.py": 10137792148038140630, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/base.py": 6509914623926583629, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_replace.py": 8784361132920473760, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/generator_pcg64_np126.pkl.gz": 9677994062701347206, + "venv/lib/python3.13/site-packages/tzlocal-5.3.1.dist-info/RECORD": 15574202654585730126, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/dictionary.py": 9599439750713575741, + "venv/lib/python3.13/site-packages/pygments/styles/autumn.py": 8750937312198920971, + "venv/lib/python3.13/site-packages/pygments/lexers/openscad.py": 13045825115225232396, + "venv/bin/py.test": 2593802493707545818, + "backend/src/core/__tests__/test_superset_preview_pipeline.py": 5338719285630543394, + "venv/lib/python3.13/site-packages/pygments/lexers/cplint.py": 13990118342267278047, + "backend/src/core/migration_engine.py": 1303635539349402052, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/selectable.py": 14646717349365560549, + "backend/src/services/git/_status.py": 8680190871452171795, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_s390_unix.h": 5161826643658505126, + "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/accessors.py": 12743680296510533030, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_coercion.py": 7261787308019145312, + "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_str_accessor.py": 4066094997616994612, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_comparison.py": 5348781442514871089, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/polynomial_polyutils.pyi": 3841660741524427413, + "venv/lib/python3.13/site-packages/git/compat.py": 16843345645628515492, + "venv/lib/python3.13/site-packages/apscheduler/schedulers/twisted.py": 16059862619165885730, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/__init__.py": 2345997696115514124, + "venv/lib/python3.13/site-packages/uvicorn/protocols/http/flow_control.py": 4822804840057360506, + "venv/lib/python3.13/site-packages/PIL/_avif.cpython-313-x86_64-linux-gnu.so": 5243449968857258659, + "backend/src/core/scheduler.py.bak": 15045927989195339728, + "venv/lib/python3.13/site-packages/keyring/backends/macOS/api.py": 4502902558534812719, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_between_time.py": 14266333058913745751, + "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/licenses/AUTHORS": 2987771679926243782, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_get.py": 6509102681661636928, + "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/WHEEL": 2357997949040430835, + "frontend/build/_app/immutable/chunks/CTtgwBHs.js": 9488056280956425328, + "venv/lib/python3.13/site-packages/py.py": 16248211404003181569, + "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/licenses/LICENSE.BSD": 16833702494864671773, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/warnings_and_errors.py": 11172336980866895356, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_explode.py": 18263694040596159591, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_construction.py": 13953424340346625390, + "venv/lib/python3.13/site-packages/pandas/tests/io/sas/test_xport.py": 14080914637071212225, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/getlimits.pyi": 93747984191461691, + "venv/lib/python3.13/site-packages/numpy/lib/_index_tricks_impl.py": 13574106461973044500, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_indexing.py": 4334578480714520407, + "venv/lib/python3.13/site-packages/jeepney/io/asyncio.py": 13239992245695516007, + "venv/lib/python3.13/site-packages/pillow.libs/liblcms2-cc10e42f.so.2.0.17": 4221600618602131476, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/backports/weakref_finalize.py": 7019253290755243568, + "venv/lib/python3.13/site-packages/pydantic/annotated_handlers.py": 10186120300163345360, + "venv/lib/python3.13/site-packages/authlib/common/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/histograms.pyi": 9255620158831643201, + "venv/lib/python3.13/site-packages/pandas/core/interchange/from_dataframe.py": 8278998381856028462, + "venv/lib/python3.13/site-packages/pandas/tests/io/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/multipart/multipart.py": 26386459953377432, + "venv/lib/python3.13/site-packages/pyasn1/type/char.py": 16518487114777185541, + "backend/src/core/utils/fileio.py": 5783081738676692209, + "backend/src/api/routes/mappings.py": 7071183920129492341, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/crud.py": 805684028038896506, + "venv/lib/python3.13/site-packages/pydantic_core/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_validate_inclusive.py": 1389676378885557519, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_reductions.py": 2816859231048302619, + "venv/lib/python3.13/site-packages/sqlalchemy/future/__init__.py": 17688444679736059779, + "backend/src/services/auth_service.py": 17642785219801916280, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py": 1359143503575245224, + "venv/lib/python3.13/site-packages/requests/__version__.py": 6677532709758546816, + "venv/lib/python3.13/site-packages/cryptography/x509/name.py": 15338066443257445241, + "venv/lib/python3.13/site-packages/pygments/filter.py": 16846063190466267618, + "venv/lib/python3.13/site-packages/fastapi/requests.py": 14587947416691824903, + "venv/lib/python3.13/site-packages/numpy/testing/overrides.py": 11819432487830192978, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_shift.py": 9952689832827833247, + "venv/lib/python3.13/site-packages/numpy/fft/_pocketfft.py": 6142062990691556076, + "venv/lib/python3.13/site-packages/pandas/io/formats/__init__.py": 18140261896107421679, + "venv/lib/python3.13/site-packages/certifi/__main__.py": 13417658012431779061, + "venv/lib/python3.13/site-packages/pygments/lexers/math.py": 1647133236144311250, + "venv/lib/python3.13/site-packages/numpy/_core/shape_base.py": 3996519099530770308, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/env.py": 1254612137164491329, + "venv/lib/python3.13/site-packages/pandas/core/arrays/numeric.py": 17834058423729918687, + "venv/lib/python3.13/site-packages/pandas/_libs/ops_dispatch.cpython-313-x86_64-linux-gnu.so": 5284769758475394095, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_numpy.py": 222383250675686477, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_frame_subplots.py": 4596089143375088346, + "venv/lib/python3.13/site-packages/httpx/_transports/wsgi.py": 11981127094916412794, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/rsa.pyi": 13850254283540522444, + "venv/lib/python3.13/site-packages/jsonschema/cli.py": 9813914882090789748, + "venv/lib/python3.13/site-packages/h11/py.typed": 12358286306858197118, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_utils.py": 15197339054699289627, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/LCSseq_py.py": 16054175754674285311, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/__init__.py": 2397613800206300456, + "venv/lib/python3.13/site-packages/pydantic_settings/__init__.py": 4374954204580208673, + "venv/lib/python3.13/site-packages/numpy/typing/mypy_plugin.py": 1306129775649200113, + "venv/lib/python3.13/site-packages/jsonschema/tests/test_validators.py": 14178329676643700151, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_records.py": 18167578114159312327, + "venv/lib/python3.13/site-packages/pandas/core/arrays/arrow/array.py": 12658302948819867972, + "venv/lib/python3.13/site-packages/cryptography/x509/general_name.py": 4481582226129488535, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_conversion_utils.py": 1567199588622767277, + "venv/lib/python3.13/site-packages/pandas/errors/cow.py": 14381282473643980776, + "venv/lib/python3.13/site-packages/greenlet/TThreadStateDestroy.cpp": 13749797532421836328, + "venv/lib/python3.13/site-packages/pandas/tests/test_downstream.py": 5635510753354256507, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_pickle.py": 388513670243918273, + "venv/lib/python3.13/site-packages/greenlet/tests/test_greenlet_trash.py": 6191655521667576604, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/object/test_astype.py": 563689238486630866, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Levenshtein_py.py": 11350239659973489646, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_sample.py": 4641567828238191927, + "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/lib/_arraypad_impl.py": 6348811074249398741, + "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/zip-safe": 15240312484046875203, + "venv/lib/python3.13/site-packages/rapidfuzz/__init__.pyi": 13131938031654453415, + "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_parameter.py": 15173522343126004527, + "venv/lib/python3.13/site-packages/_pytest/_code/__init__.py": 6211607282116942091, + "venv/lib/python3.13/site-packages/pandas/_libs/testing.cpython-313-x86_64-linux-gnu.so": 16758333404587340103, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_numba.py": 1668304729617150075, + "venv/lib/python3.13/site-packages/attrs/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/common.py": 2075178842766962782, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_client/integration.py": 12670848015915724967, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_concatenate_chunks.py": 9693420220003544490, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_droplevel.py": 2071197388394658690, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_array.py": 11271807719833350332, + "venv/lib/python3.13/site-packages/websockets/sync/server.py": 7966978001578584901, + "venv/lib/python3.13/site-packages/PIL/PngImagePlugin.py": 5076219228736706283, + "venv/lib/python3.13/site-packages/PIL/PalmImagePlugin.py": 13008868719182630239, + "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/METADATA": 15225661995655327539, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/align.py": 6078666505647454830, + "venv/lib/python3.13/site-packages/pygments/lexers/igor.py": 6506221273085723501, + "venv/lib/python3.13/site-packages/urllib3/util/connection.py": 15511685694589961094, + "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/METADATA": 15987199384245264478, + "venv/lib/python3.13/site-packages/numpy/fft/tests/test_helper.py": 5362180645056413158, + "venv/lib/python3.13/site-packages/PIL/_imagingtk.pyi": 18222325750818585549, + "venv/lib/python3.13/site-packages/httpcore/_synchronization.py": 10997080307461682786, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_interpolate.py": 6154072863387383113, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_bar.py": 7791068963246005463, + "venv/lib/python3.13/site-packages/pycparser/ply/ygen.py": 5520340773074632724, + "venv/lib/python3.13/site-packages/starlette/exceptions.py": 15978811612997200737, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_map.py": 32086858815359344, + "venv/lib/python3.13/site-packages/pygments/lexers/html.py": 17288963642862550116, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_finfo.py": 17174674209412768422, + "venv/lib/python3.13/site-packages/git/repo/__init__.py": 6173604284490891047, + "venv/lib/python3.13/site-packages/pygments/lexers/solidity.py": 5071425960622305629, + "frontend/src/lib/components/translate/__tests__/test_translation_preview.svelte.js": 15091004230843271151, + "frontend/src/components/Navbar.svelte": 12353588862181566761, + "frontend/build/_app/immutable/nodes/3.TrEd7Loa.js": 18117679530551903771, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/modules.pyi": 131293606732000587, + "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py": 5995058808539530576, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_hour.py": 13754646634123368797, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/test_observance.py": 18178414966262524534, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_musllinux.py": 2423350951094915747, + "frontend/build/_app/immutable/chunks/DOxI2NAm.js": 11236647372068635428, + "backend/src/core/auth/oauth.py": 9925908783612235042, + "venv/lib/python3.13/site-packages/pip-25.1.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/ma/tests/test_old_ma.py": 18116460138221458026, + "venv/lib/python3.13/site-packages/rapidfuzz/_utils.py": 14374663810542207121, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/polynomial_series.pyi": 7112486730197067087, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_equals.py": 9405821443172167066, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/reflection.py": 1799734694597531652, + "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_setitem.py": 15731267129055947469, + "venv/lib/python3.13/site-packages/pygments/lexers/pony.py": 2720635393743617141, + "venv/lib/python3.13/site-packages/pandas/api/typing/__init__.py": 6438268275857060114, + "venv/lib/python3.13/site-packages/numpy/random/tests/__init__.py": 15130871412783076140, + "merge_kilo.py": 4282227344165325430, + "venv/lib/python3.13/site-packages/greenlet/greenlet_cpython_compat.hpp": 8396413256279803400, + "venv/lib/python3.13/site-packages/numpy/typing/tests/test_isfile.py": 14544060093268573418, + "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pip/_vendor/tomli_w/_writer.py": 15053365792216907149, + "venv/lib/python3.13/site-packages/numpy/_core/numerictypes.pyi": 12144531601504755948, + "venv/lib/python3.13/site-packages/numpy/ctypeslib/__init__.pyi": 13208953770241664384, + "venv/lib/python3.13/site-packages/pandas/core/indexes/interval.py": 17965911911189276907, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_stack_unstack.py": 3737891757817696405, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_converter.py": 7709581879446228856, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_partial_indexing.py": 4132091154215910029, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_shift.py": 14026124784027171225, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_validate_args.py": 5821040955806257450, + "venv/lib/python3.13/site-packages/starlette/middleware/authentication.py": 15211131264936212938, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/einsumfunc.pyi": 200771351328522379, + "venv/lib/python3.13/site-packages/numpy/_pyinstaller/hook-numpy.py": 5221550291012182443, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/fields.cpython-313-x86_64-linux-gnu.so": 16927263232848238698, + "venv/lib/python3.13/site-packages/fastapi/middleware/__init__.py": 5289703478893407762, + "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_polynomial.py": 777287409151500309, + "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/macos.py": 17442393555211213147, + "venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/METADATA": 13695483537390128881, + "venv/lib/python3.13/site-packages/pandas/tests/strings/test_strings.py": 17398806130402098912, + "venv/lib/python3.13/site-packages/numpy/f2py/__init__.py": 7470157159992827389, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/locators.py": 7638945613579396718, + "venv/lib/python3.13/site-packages/pip/_internal/models/direct_url.py": 4619204214785230341, + "venv/lib/python3.13/site-packages/pandas/compat/pickle_compat.py": 11073483984394766474, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_astype.py": 11386658862732393123, + "venv/lib/python3.13/site-packages/websockets/asyncio/router.py": 15039562376314233669, + "venv/lib/python3.13/site-packages/numpy/random/lib/libnpyrandom.a": 1802449692091040396, + "venv/lib/python3.13/site-packages/secretstorage/defines.py": 9590959702075554048, + "venv/lib/python3.13/site-packages/passlib/handlers/cisco.py": 15004707325761459775, + "venv/lib/python3.13/site-packages/pandas/plotting/_core.py": 6766059152026241747, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_attr_equal.py": 5612259834202470413, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_construction.py": 6564829381134214401, + "venv/lib/python3.13/site-packages/httpx/_urls.py": 4219389176352582046, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/hmac.py": 18132740541139891824, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/nested_sequence.pyi": 10985533017688918283, + "venv/lib/python3.13/site-packages/pandas/testing.py": 18352975429944961593, + "venv/lib/python3.13/site-packages/urllib3/util/ssl_.py": 11720964010284185952, + "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/RECORD": 16874983218652404911, + "venv/lib/python3.13/site-packages/numpy/random/c_distributions.pxd": 16574161095720077595, + "venv/lib/python3.13/site-packages/pygments/styles/gh_dark.py": 7717523746712391809, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_period.py": 9715843209581883915, + "venv/lib/python3.13/site-packages/pygments/lexers/_vim_builtins.py": 6842381248727719900, + "venv/lib/python3.13/site-packages/pandas/core/arrays/sparse/__init__.py": 6758781485387665020, + "venv/lib/python3.13/site-packages/fastapi/openapi/docs.py": 16735218221639179927, + "venv/lib/python3.13/site-packages/pandas/tests/base/test_value_counts.py": 9352540710376306520, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_sorting.py": 10523740688498427929, + "venv/lib/python3.13/site-packages/charset_normalizer/cd.py": 8687667152597941021, + "frontend/src/routes/settings/__tests__/settings_page.integration.test.js": 13008989176397099996, + "frontend/src/components/RepositoryDashboardGrid.svelte": 17100594220040865069, + "frontend/build/_app/immutable/chunks/BURGrVTR.js": 9373329903010633175, + "backend/src/core/superset_client/_dashboards_list.py": 16574697057051064609, + "backend/src/core/plugin_base.py": 11649432288706102843, + "backend/src/models/config.py": 7631082065091961320, + "backend/src/services/reports/type_profiles.py": 8365014439236739390, + "venv/lib/python3.13/site-packages/pygments/lexers/q.py": 15426466074881683794, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7519/jwt.py": 9642615960197748105, + "venv/lib/python3.13/site-packages/idna/idnadata.py": 200840860629525970, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/arithmetic.pyi": 10265823318697442495, + "venv/lib/python3.13/site-packages/httpx/_status_codes.py": 1758440600462491839, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/comparisons.pyi": 9338084426044982038, + "venv/lib/python3.13/site-packages/jaraco/context/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/_pytest/warning_types.py": 10695097290648045427, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_randomstate.py": 11205714006000849926, + "venv/lib/python3.13/site-packages/keyring/compat/__init__.py": 7578020229796085753, + "venv/lib/python3.13/site-packages/numpy/dtypes.py": 14128421398870718692, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/common.py": 16314033415206173235, + "venv/lib/python3.13/site-packages/pandas/io/spss.py": 864838144958947906, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/random.pyi": 1808392097712315455, + "venv/lib/python3.13/site-packages/anyio/to_interpreter.py": 8369454842687342401, + "frontend/src/lib/components/reports/ReportDetailPanel.svelte": 16014663571033060148, + "venv/lib/python3.13/site-packages/pandas/_libs/internals.cpython-313-x86_64-linux-gnu.so": 2008633361686956377, + "backend/src/services/clean_release/repositories/publication_repository.py": 11208425895213508165, + "backend/src/services/reports/__init__.py": 11818812865847046400, + "venv/lib/python3.13/site-packages/httpx/_content.py": 1301854837767395687, + "venv/lib/python3.13/site-packages/jaraco.classes-3.4.0.dist-info/RECORD": 1051307407005554409, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/jwe_zips.py": 5184894018177148648, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/lower_f2py_fortran.f90": 13315882216322269383, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/wrappers.py": 315409799569602977, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/bitwise_ops.pyi": 15598239133239520456, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_analytics.py": 4510712334676848202, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalar_ctors.py": 631744056514965940, + "venv/lib/python3.13/site-packages/pandas/core/interchange/column.py": 9498044216416177656, + "venv/lib/python3.13/site-packages/pydantic/_internal/_signature.py": 17075003201648027250, + "venv/lib/python3.13/site-packages/numpy/f2py/auxfuncs.pyi": 10077265709692546934, + "venv/lib/python3.13/site-packages/dotenv/parser.py": 17651523026132242005, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh24662.f90": 4241310200087270990, + "venv/lib/python3.13/site-packages/pandas/_libs/indexing.cpython-313-x86_64-linux-gnu.so": 13188040757764159731, + "venv/lib/python3.13/site-packages/pandas/core/computation/parsing.py": 3084943521520476999, + "venv/lib/python3.13/site-packages/pandas/core/indexers/__init__.py": 1756397319068017890, + "venv/lib/python3.13/site-packages/pydantic/v1/parse.py": 5875950480538559709, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/validation": 13992741783834278677, + "venv/lib/python3.13/site-packages/pyasn1/codec/ber/encoder.py": 8912119133548489544, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/api.py": 12339139862411341381, + "venv/lib/python3.13/site-packages/pandas/core/methods/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8414/models.py": 3567900447243180991, + "venv/lib/python3.13/site-packages/git/index/util.py": 5022401275553420722, + "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/METADATA": 12747626683405487434, + "venv/lib/python3.13/site-packages/numpy/lib/introspect.py": 5706729538356302335, + "venv/lib/python3.13/site-packages/uvicorn/__init__.py": 11517239807926185665, + "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/RECORD": 9964194961227523130, + "venv/lib/python3.13/site-packages/pygments/lexers/usd.py": 934820244673741695, + "venv/lib/python3.13/site-packages/numpy/lib/scimath.py": 7616377937400918724, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/ccalendar.cpython-313-x86_64-linux-gnu.so": 11019871270094058038, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py": 18325640697829098192, + "venv/lib/python3.13/site-packages/charset_normalizer/md.py": 2029760700380476138, + "frontend/src/lib/stores.js": 8062876368729352764, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/size/foo.f90": 18067977329435720684, + "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/cython/checks.pyx": 13187505236986855177, + "frontend/src/components/TaskList.svelte": 13727273029173605651, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/dependency.py": 7300594038182867460, + "venv/lib/python3.13/site-packages/PIL/ImageTransform.py": 7825940907094338808, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/containers.py": 5969831151685016772, + "venv/lib/python3.13/site-packages/urllib3/fields.py": 9503202008440141421, + "venv/lib/python3.13/site-packages/pandas/tests/strings/test_find_replace.py": 262184394614748386, + "venv/lib/python3.13/site-packages/urllib3/http2/probe.py": 4069929463671970243, + "venv/lib/python3.13/site-packages/pandas/tests/extension/list/__init__.py": 10072943986583771248, + "venv/lib/python3.13/site-packages/git/index/typ.py": 11661540784850363024, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/fixtures/orm.py": 7554417559838785150, + "venv/lib/python3.13/site-packages/pydantic/deprecated/decorator.py": 8609500261161560793, + "venv/lib/python3.13/site-packages/pandas/tests/resample/test_datetime_index.py": 15448494560143263150, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ndarray_conversion.py": 6205112277291935767, + "venv/lib/python3.13/site-packages/pandas/tests/test_errors.py": 10116716310156374885, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/conftest.py": 13388202260795998613, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_scalar_compat.py": 10534925655070162459, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_constructors.py": 7466113171256856591, + "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/WHEEL": 7795541085444298627, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/style.py": 15214295655795348821, + "venv/lib/python3.13/site-packages/pandas/io/iceberg.py": 13137128885014736233, + "venv/lib/python3.13/site-packages/uvicorn/supervisors/statreload.py": 5018382301336802445, + "venv/lib/python3.13/site-packages/pygments/lexers/resource.py": 8057351545636349791, + "frontend/build/_app/immutable/nodes/6.CQhNV4xO.js": 10554106729411192187, + "backend/src/core/scheduler.py": 2472649939852521889, + "backend/src/services/clean_release/__tests__/test_policy_engine.py": 9953185933720550640, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_pick.py": 15760492005471315814, + "frontend/build/_app/immutable/chunks/BrvzRogk.js": 16712439889682616806, + "venv/lib/python3.13/site-packages/psycopg2/__init__.py": 15798412693009721199, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh17797.f90": 17319074662077763768, + "venv/lib/python3.13/site-packages/pydantic/_migration.py": 17982816821930887921, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi": 16048852379621461316, + "venv/lib/python3.13/site-packages/referencing/jsonschema.py": 14596871117484757502, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Jaro_py.py": 11991492232381887787, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_indexing.py": 14717641281147152159, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/index.py": 15694995431926521471, + "venv/lib/python3.13/site-packages/annotated_doc/__init__.py": 6834198874252778425, + "venv/lib/python3.13/site-packages/pandas/tests/test_flags.py": 411845322592830527, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_indexing.py": 5910217172351020811, + "venv/lib/python3.13/site-packages/PIL/GbrImagePlugin.py": 7681313333602257802, + "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/RECORD": 17341460504914308184, + "backend/src/core/superset_client/__init__.py": 6869599313455380203, + "backend/src/api/__init__.py": 5287505122015708040, + "backend/src/services/git/_branch.py": 7552472942680784566, + "frontend/build/_app/immutable/chunks/2dOUpm6k.js": 11811637653151731489, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_header.py": 3564391095263545879, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_rewrite_warning.py": 4895919872954538758, + "backend/src/api/routes/dashboards/_schemas.py": 10686942001147126057, + "frontend/src/routes/dashboards/[id]/components/DashboardHeader.svelte": 5572977872383619871, + "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/licenses/LICENSE.BSD": 3858254215169937333, + "venv/lib/python3.13/site-packages/httpx/_api.py": 6697370469329778523, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/serialize.py": 11139432164346742421, + "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_regression.py": 16266600037241221681, + "venv/lib/python3.13/site-packages/pandas/core/arrays/masked.py": 13977195666947626206, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth1/cache.py": 15037055977924167732, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_pickle.py": 16196792378104396231, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/tzconversion.pyi": 10947021928755741366, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_emoji_replace.py": 8166203537440976347, + "venv/lib/python3.13/site-packages/pip/_internal/req/req_uninstall.py": 3515239440812977025, + "venv/lib/python3.13/site-packages/pyasn1/codec/native/encoder.py": 120921270972591880, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_astype.py": 4178231007649608531, + "frontend/src/lib/api/reports.js": 2328857814720054000, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Levenshtein.pyi": 12925006572914814268, + "venv/lib/python3.13/site-packages/pip/_internal/cli/spinners.py": 7664056305019151182, + "venv/lib/python3.13/site-packages/authlib/integrations/base_client/async_openid.py": 8708980645667847439, + "venv/lib/python3.13/site-packages/authlib/oauth1/__init__.py": 3692257288065292607, + "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/types.py": 1343652083039949282, + "venv/lib/python3.13/site-packages/pydantic/v1/class_validators.py": 6471688474853407718, + "venv/lib/python3.13/site-packages/httpx/__init__.py": 6383055693166097719, + "venv/lib/python3.13/site-packages/_pytest/config/exceptions.py": 3199245296954654462, + "venv/lib/python3.13/site-packages/fastapi/dependencies/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pillow.libs/libbrotlicommon-c55a5f7a.so.1.2.0": 2635192149914499986, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_arrow_interface.py": 5231449831607036031, + "venv/lib/python3.13/site-packages/charset_normalizer/legacy.py": 1706599349563920562, + "frontend/src/lib/auth/__tests__/permissions.test.js": 13555349356008297952, + "venv/lib/python3.13/site-packages/_pytest/reports.py": 13930709338396699351, + "venv/lib/python3.13/site-packages/pycparser/c_lexer.py": 10964496587169353564, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/immutabledict.cpython-313-x86_64-linux-gnu.so": 12776951671822017500, + "backend/src/models/__tests__/test_models.py": 15975002719327784362, + "backend/src/services/git/_remote_providers.py": 12661659133898682960, + "venv/lib/python3.13/site-packages/psycopg2/sql.py": 18041240927130590808, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/value_attrspec/gh21665.f90": 11945588837236968550, + "venv/lib/python3.13/site-packages/passlib/handlers/mysql.py": 11165762635383041460, + "venv/lib/python3.13/site-packages/pandas/io/feather_format.py": 13242733585787888812, + "venv/lib/python3.13/site-packages/pygments/lexers/gdscript.py": 12775803139854052838, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_deprecations.py": 16124509063246256847, + "venv/lib/python3.13/site-packages/yaml/cyaml.py": 9157893742065554693, + "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/assertion_client.py": 4261295109880943573, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/table.py": 16856253349556498073, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_simd_module.py": 7122050193390298178, + "venv/lib/python3.13/site-packages/pandas/tests/io/xml/test_xml_dtypes.py": 18293138335799308439, + "venv/lib/python3.13/site-packages/pygments/lexers/business.py": 10410439210806697214, + "venv/lib/python3.13/site-packages/pip/_internal/__init__.py": 7060345458485901709, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/unicode_comment.f90": 11326797716215117266, + "venv/bin/pygmentize": 2166491742035772877, + "venv/lib/python3.13/site-packages/pydantic/generics.py": 15019381413806459410, + "venv/lib/python3.13/site-packages/pandas/_libs/properties.pyi": 9268470215750269699, + "venv/lib/python3.13/site-packages/keyring/backend_complete.bash": 16589214602970214174, + "venv/lib/python3.13/site-packages/keyring/backends/fail.py": 5792496235026678021, + "venv/lib/python3.13/site-packages/dotenv/variables.py": 5438726379749803751, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_isetitem.py": 14895552405828068177, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/__init__.py": 16434403703599680307, + "venv/lib/python3.13/site-packages/pygments/lexers/maxima.py": 11824576541091274641, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated": 11193150813314469394, + "venv/lib/python3.13/site-packages/pygments/lexers/smv.py": 8253688483040957894, + "venv/lib/python3.13/site-packages/pandas/_libs/reshape.pyi": 88721900806893992, + "frontend/build/_app/immutable/chunks/BG4NfqIk.js": 6096001408084116703, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/json.py": 14738429697319670035, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/_collections.py": 14307204206312829238, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_where.py": 14167991754625133668, + "venv/lib/python3.13/site-packages/pip/_vendor/distro/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/core/shared_docs.py": 2173578869755325502, + "frontend/build/_app/immutable/chunks/BoOsFBJ0.js": 11874060117929105318, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/interval/test_interval.py": 18040371224712437126, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/numerictypes.py": 5719374287454872555, + "venv/lib/python3.13/site-packages/numpy/_core/_ufunc_config.py": 7365943844063702766, + "venv/lib/python3.13/site-packages/numpy/_core/_internal.pyi": 6840742333427164920, + "venv/lib/python3.13/site-packages/gitdb/test/test_pack.py": 7518388297247535189, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/rec.pyi": 14615751247243515028, + "venv/lib/python3.13/site-packages/pandas/core/computation/expressions.py": 10684284531040203747, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_ipython_compat.py": 12632271159842988561, + "backend/src/core/middleware/trace.py": 13093835356377196397, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_fillna.py": 8802093795040069423, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/database.py": 7111764828004587758, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/ops.py": 13451323660386712332, + "venv/lib/python3.13/site-packages/greenlet/tests/_test_extension.c": 9318655518998644962, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_null_file.py": 6885565068723353414, + "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_distutils.pyi": 3595910367556664837, + "venv/lib/python3.13/site-packages/dotenv/ipython.py": 15904966766060247908, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_indexing_slow.py": 12558235638488427077, + "venv/lib/python3.13/site-packages/pip/_internal/operations/install/__init__.py": 10456333337205488584, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/connectionpool.py": 10453724680327835821, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/timeseries.py": 17641722582848932332, + "venv/lib/python3.13/site-packages/pandas/io/pytables.py": 17482934215513855928, + "venv/lib/python3.13/site-packages/pygments/lexers/pawn.py": 18127303119414402594, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_clip.py": 13498704233745189154, + "venv/lib/python3.13/site-packages/pandas/core/indexes/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pydantic/v1/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7592/__init__.py": 1398617034174236358, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ndarray_conversion.pyi": 11580106670226366710, + "venv/lib/python3.13/site-packages/pygments/styles/paraiso_dark.py": 3976779833379189837, + "frontend/src/routes/profile/__tests__/fixtures/profile.fixtures.js": 2726190101012864209, + "backend/src/services/dataset_review/orchestrator.py": 8780497744077644822, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/base.cpython-313-x86_64-linux-gnu.so": 8335798324308957127, + "venv/lib/python3.13/site-packages/httpcore/_sync/socks_proxy.py": 4418971149260853240, + "venv/lib/python3.13/site-packages/passlib/crypto/_blowfish/unrolled.py": 12651425412640834530, + "venv/lib/python3.13/site-packages/numpy/__config__.py": 15794735729525094396, + "venv/lib/python3.13/site-packages/apscheduler/triggers/base.py": 1170652622104554892, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Levenshtein.py": 15426828792217499138, + "venv/lib/python3.13/site-packages/pandas/tests/libs/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pydantic/class_validators.py": 4643597082531287188, + "venv/lib/python3.13/site-packages/pip/_internal/models/scheme.py": 4101322315594430874, + "venv/lib/python3.13/site-packages/fastapi/_compat/main.py": 13016492818231995760, + "venv/lib/python3.13/site-packages/websockets/client.py": 5229500079651465772, + "venv/lib/python3.13/site-packages/fastapi/middleware/wsgi.py": 3706578020491442104, + "venv/lib/python3.13/site-packages/more_itertools/__init__.pyi": 16479506520351014608, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/ec_key.py": 5961540064444749833, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/util.py": 387985965709546512, + "venv/lib/python3.13/site-packages/pandas/core/reshape/merge.py": 300205528811693975, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/revocation.py": 5718403706971163960, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_parser.py": 8145921552236189367, + "venv/lib/python3.13/site-packages/pydantic/warnings.py": 5142352784799639092, + "venv/lib/python3.13/site-packages/pygments/lexers/arturo.py": 4565524340706235725, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_ndarray_backed.py": 10097812802469682233, + "venv/lib/python3.13/site-packages/numpy/_pyinstaller/hook-numpy.pyi": 14233146512272397376, + "frontend/src/lib/components/dataset-review/CompiledSQLPreview.svelte": 14788599185006992431, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/format": 12697928260201325593, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_series.py": 17126297747681689408, + "backend/src/plugins/translate/executor.py": 16483493819862607738, + "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/LICENSE": 6774760218010069963, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/response.py": 15004896908941411698, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_rolling_skew_kurt.py": 15225601437354712351, + "backend/src/api/routes/__tests__/test_datasets.py": 14530822388357135497, + "venv/lib/python3.13/site-packages/numpy/lib/_type_check_impl.py": 15989335816452913951, + "venv/lib/python3.13/site-packages/numpy/core/umath.py": 4476812261188108155, + "venv/lib/python3.13/site-packages/httpx/_decoders.py": 14478643473006178492, + "venv/lib/python3.13/site-packages/starlette/routing.py": 16063812008141874150, + "venv/lib/python3.13/site-packages/pydantic/_internal/_model_construction.py": 694471028676746110, + "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/passlib/handlers/windows.py": 8969739861295277439, + "venv/lib/python3.13/site-packages/PIL/_tkinter_finder.py": 14045496501810535432, + "venv/lib/python3.13/site-packages/pygments/lexers/forth.py": 17623561228839319663, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/routines/subrout.pyf": 4660465266594480405, + "frontend/src/components/MappingTable.svelte": 3101497452073042168, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py": 3960540415600165213, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_indexing.py": 8943385096811168079, + "frontend/build/_app/immutable/chunks/CbSNjLmH.js": 12377182002440958587, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/models.py": 15928924478695638423, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/__init__.py": 96341051676048910, + "venv/lib/python3.13/site-packages/pyasn1/codec/ber/decoder.py": 12653470212935324322, + "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/_py_processors.py": 16557117595879208702, + "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/wsproto_impl.py": 3072714633272661847, + "venv/lib/python3.13/site-packages/yaml/constructor.py": 8731384179016992045, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/pyodbc.py": 8175468565413744516, + "venv/lib/python3.13/site-packages/attr/validators.py": 4453080662115911998, + "venv/lib/python3.13/site-packages/jose/jws.py": 11592462993417182096, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/testing.pyi": 9317922890783549326, + "venv/lib/python3.13/site-packages/pandas/_libs/sparse.cpython-313-x86_64-linux-gnu.so": 2051484129454294720, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/ccalendar.pyi": 15789956857032062129, + "venv/lib/python3.13/site-packages/pandas/tests/libs/test_join.py": 5982692266872978444, + "venv/lib/python3.13/site-packages/pygments/styles/paraiso_light.py": 15941591873059208999, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_subclass.py": 12453252499544981915, + "frontend/src/lib/stores/__tests__/assistantChat.test.js": 11822882112255142537, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_pytables_missing.py": 12154582133989896069, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/groupby.py": 14661033005768429774, + "venv/lib/python3.13/site-packages/pydantic/_internal/_known_annotated_metadata.py": 9308620143218174987, + "venv/lib/python3.13/site-packages/pandas/_libs/join.cpython-313-x86_64-linux-gnu.so": 2947947484481830203, + "venv/lib/python3.13/site-packages/urllib3-2.6.2.dist-info/WHEEL": 7454950858448014158, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_halfyear.py": 9376272342364384767, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/abstract_interface/foo.f90": 14491312943942432024, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/__init__.py": 7062045842767697645, + "venv/lib/python3.13/site-packages/numpy/random/_bounded_integers.pyi": 14150710436003340771, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/gh25337/use_data.f90": 14624074967635860696, + "venv/lib/python3.13/site-packages/starlette/websockets.py": 13834731343216764204, + "venv/lib/python3.13/site-packages/numpy/_core/_add_newdocs_scalars.py": 652332294655046106, + "venv/lib/python3.13/site-packages/pandas/core/array_algos/__init__.py": 11850945878149145990, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh22648.pyf": 15094859289098837494, + "venv/lib/python3.13/site-packages/pip/_vendor/idna/intranges.py": 12536174834761591006, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/events.py": 10224214051422678828, + "venv/lib/python3.13/site-packages/PIL/ContainerIO.py": 7551929914241379005, + "venv/lib/python3.13/site-packages/pygments/lexers/asm.py": 13861952972248758785, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_at.py": 1029744344133153888, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_hashtable.py": 2620207240930665390, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/period.pyi": 13110894861187422750, + "venv/lib/python3.13/site-packages/authlib/integrations/requests_client/oauth1_session.py": 1911069558956893789, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_truncate.py": 9623613657871046837, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/mysqldb.py": 2138006249556140141, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_reductions.py": 3575617237516305456, + "frontend/src/components/tools/ConnectionList.svelte": 15981533028345710336, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/gh2848.f90": 5799106109999071412, + "venv/lib/python3.13/site-packages/pandas/tests/extension/decimal/test_decimal.py": 17811784255174057561, + "backend/src/api/routes/git/__init__.py": 9989336734947065016, + "venv/lib/python3.13/site-packages/httpcore/_backends/auto.py": 10488908050307887802, + "venv/lib/python3.13/site-packages/greenlet/greenlet_allocator.hpp": 11041111583319491309, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/missing.py": 11241703105777668183, + "venv/lib/python3.13/site-packages/pygments/lexers/lilypond.py": 1088755646568511197, + "venv/lib/python3.13/site-packages/pandas/tests/test_expressions.py": 16378925536309828288, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/methods/test_repeat.py": 18394463352314692642, + "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/LICENSE": 12064892310002036996, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/compiler.py": 13924519162400330589, + "venv/lib/python3.13/site-packages/_pytest/hookspec.py": 387790426108810884, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/LCSseq.pyi": 16372699564794732463, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/auth.py": 912521029864094489, + "venv/lib/python3.13/site-packages/authlib/oauth2/base.py": 12948318231066662045, + "venv/lib/python3.13/site-packages/websockets/speedups.pyi": 10322382420215791091, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/DamerauLevenshtein_py.py": 1933257612725836484, + "venv/lib/python3.13/site-packages/pip/_internal/utils/filesystem.py": 7716285033598020316, + "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/WHEEL": 7127684561765977531, + "venv/lib/python3.13/site-packages/dotenv/py.typed": 9796674040111366709, + "venv/lib/python3.13/site-packages/passlib/registry.py": 5050675058011850564, + "venv/lib/python3.13/site-packages/pandas/core/interchange/buffer.py": 12961802206383700922, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7009/revocation.py": 16178499278312777474, + "venv/lib/python3.13/site-packages/fastapi/security/oauth2.py": 1925394177931547776, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_index_as_string.py": 292438690259156869, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/attr/_make.py": 2348458829123990579, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py": 16386268386376675175, + "venv/lib/python3.13/site-packages/pandas/tests/extension/json/__init__.py": 7877061921210840955, + "venv/lib/python3.13/site-packages/annotated_doc-0.0.4.dist-info/RECORD": 7320381591791379627, + "venv/lib/python3.13/site-packages/pandas/io/_util.py": 13608922148814398500, + "venv/lib/python3.13/site-packages/rsa/transform.py": 12261738253972114335, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/resolver.py": 9931394693284776391, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/utils.py": 9959199071030996679, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/types.py": 12298292195929126306, + "venv/lib/python3.13/site-packages/_pytest/__init__.py": 3769333500910148571, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/content": 16257335642847993866, + "venv/lib/python3.13/site-packages/dateutil/utils.py": 7067026915090881998, + "venv/lib/python3.13/site-packages/uvicorn/supervisors/basereload.py": 14479857245734482609, + "venv/lib/python3.13/site-packages/PIL/FliImagePlugin.py": 13364963053197422923, + "venv/lib/python3.13/site-packages/rsa/core.py": 13203947234024924497, + "venv/lib/python3.13/site-packages/packaging/metadata.py": 9168255069163141470, + "venv/lib/python3.13/site-packages/numpy/lib/_arraysetops_impl.pyi": 3111521453255595221, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/authorization_server.py": 2491654903543600678, + "venv/lib/python3.13/site-packages/_pytest/mark/structures.py": 12237146603608917650, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/horizontal_shard.py": 17615732108308858890, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_client/apps.py": 10758062668702044519, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_constructors.py": 11786151805350138436, + "frontend/src/lib/components/layout/__tests__/sidebarNavigation.test.js": 9823973527644599938, + "frontend/src/pages/Dashboard.svelte": 3653104499351855122, + "venv/lib/python3.13/site-packages/click/utils.py": 13475604420010022458, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_f2cmap.py": 5863341586534531594, + "venv/lib/python3.13/site-packages/anyio/_core/_fileio.py": 4507100568401692983, + "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/RECORD": 1689342214025776630, + "venv/lib/python3.13/site-packages/numpy/tests/test_public_api.py": 11741922758622705449, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/frame/test_hist_box_by.py": 16197297231683144299, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py": 5997687240967250164, + "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/RECORD": 12598309574903741388, + "merge_spec.py": 5285945727297582371, + "backend/src/services/git/_url.py": 15729080796864187707, + "frontend/build/_app/immutable/nodes/25.Dz6lg2iG.js": 9988963929265574361, + "backend/alembic/README": 17625003422504608299, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_assign.py": 2647902466315482406, + "venv/lib/python3.13/site-packages/pandas/tests/config/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/ooc.py": 14540210212631538007, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_umath_complex.py": 11732242536199439023, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/strptime.cpython-313-x86_64-linux-gnu.so": 12654188486128938170, + "venv/lib/python3.13/site-packages/httpx/_config.py": 8800538050293498928, + "venv/lib/python3.13/site-packages/numpy/random/__init__.pyi": 13833571330152615376, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_indexing.py": 17106010337164216722, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/reserved_words.py": 8022121780993497971, + "venv/bin/numpy-config": 17104619511899110252, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_copy.py": 11335108864615756235, + "backend/src/__init__.py": 11250920420388310655, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_reset_index.py": 14490875921264161445, + "venv/lib/python3.13/site-packages/passlib/exc.py": 15559705487889657091, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_functions.py": 16376319335326812580, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/polynomial_polybase.pyi": 3404315099794022355, + "venv/lib/python3.13/site-packages/greenlet/CObjects.cpp": 3454797518210223926, + "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/WHEEL": 1598805043198466373, + "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py": 9627328185578406121, + "venv/lib/python3.13/site-packages/numpy/matrixlib/defmatrix.py": 4940170059946018356, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/greenlet-3.3.0.dist-info/RECORD": 5169907629260560698, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_unstack.py": 11834917665446437163, + "venv/lib/python3.13/site-packages/starlette/_exception_handler.py": 9892172656847200124, + "venv/lib/python3.13/site-packages/PIL/PpmImagePlugin.py": 7107324729714470853, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_complex.py": 12963449957363121481, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/applicator": 6870991911243684132, + "venv/lib/python3.13/site-packages/authlib/consts.py": 14226604841596736870, + "venv/lib/python3.13/site-packages/numpy/random/_common.pxd": 3402284125943848851, + "venv/lib/python3.13/site-packages/requests/compat.py": 5836590172733661320, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/base.py": 10200916670889920659, + "venv/lib/python3.13/site-packages/numpy/random/_examples/cffi/parse.py": 2427019397934437210, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_real.py": 5283415082530228716, + "venv/lib/python3.13/site-packages/pycparser/ply/lex.py": 3393278208465358140, + "venv/lib/python3.13/site-packages/pygments/lexers/mime.py": 7768464764849718825, + "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/WHEEL": 691462085217888236, + "venv/lib/python3.13/site-packages/pygments/styles/lilypond.py": 12486578704132023691, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_contains.py": 17630310725250553247, + "frontend/src/lib/components/reports/ReportCard.svelte": 15186215132458187779, + "venv/lib/python3.13/site-packages/pygments/formatters/img.py": 18230180171537565208, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/assumed_shape/foo_free.f90": 10939657740734987454, + "frontend/src/lib/components/translate/TranslationRunGlobalIndicator.svelte": 10137468959835535437, + "frontend/src/routes/migration/__tests__/migration_dashboard.ux.test.js": 17118608261683412409, + "venv/lib/python3.13/site-packages/pydantic/deprecated/copy_internals.py": 1245428320513062166, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/JaroWinkler.py": 1341849861184131135, + "venv/lib/python3.13/site-packages/dotenv/__init__.py": 15464030923609356017, + "venv/lib/python3.13/site-packages/numpy/lib/_version.py": 15294708923334674060, + "venv/lib/python3.13/site-packages/pandas/core/series.py": 11618272682977710696, + "venv/lib/python3.13/site-packages/pandas/io/formats/info.py": 215303194287834907, + "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/METADATA": 13428590545328703138, + "venv/lib/python3.13/site-packages/PIL/GifImagePlugin.py": 16073125611077200693, + "venv/lib/python3.13/site-packages/pygments/styles/__init__.py": 18383248976818885498, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/_dml_constructors.py": 4497038002189188311, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/npyio.pyi": 15811156298637131744, + "venv/lib/python3.13/site-packages/attrs/__init__.py": 9502968706258875910, + "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/METADATA": 3618776395694990076, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_infer_datetimelike.py": 17304541521108700317, + "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/WHEEL": 9131187629260560775, + "venv/lib/python3.13/site-packages/PIL/SunImagePlugin.py": 7661622930404688367, + "venv/lib/python3.13/site-packages/pygments/__main__.py": 13200500591994479045, + "venv/lib/python3.13/site-packages/pygments/lexers/bdd.py": 5389493859905397060, + "venv/lib/python3.13/site-packages/PIL/ImageFilter.py": 10974179993355630141, + "frontend/src/routes/translate/dictionaries/[id]/+page.svelte": 14484918248250087235, + "venv/lib/python3.13/site-packages/numpy/_utils/_inspect.py": 7029935879540617057, + "venv/lib/python3.13/site-packages/pandas/tests/base/test_conversion.py": 7507083194555424923, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/sqltypes.py": 2952996080192295188, + "venv/lib/python3.13/site-packages/pytest/py.typed": 15130871412783076140, + "venv/bin/pyrsa-priv2pub": 16766314338551408621, + "venv/bin/pytest": 2593802493707545818, + "frontend/build/_app/immutable/nodes/20.hRBN7DT6.js": 13368106004770721520, + "backend/src/models/dataset_review_pkg/_mapping_models.py": 6232602161916984422, + "venv/lib/python3.13/site-packages/numpy/testing/__init__.pyi": 8126257788928282891, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test__iotools.py": 6260912730310453037, + "venv/lib/python3.13/site-packages/pandas/io/formats/templates/latex.tpl": 8820045022551446255, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_cython_aggregations.py": 10818895573637904764, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_kurt.py": 2898715045429898166, + "backend/alembic/versions/543d43d752b8_migrate_old_dictionary_entries_and_.py": 10988663686444687907, + "venv/lib/python3.13/site-packages/rsa/pem.py": 3118524021576082054, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/validator_creation.py": 2745563768861102999, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/philox-testset-1.csv": 2202360898679162333, + "frontend/src/lib/components/dataset-review/SourceIntakePanel.svelte": 10994864579319870671, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_array.f90": 9711940829820746513, + "venv/lib/python3.13/site-packages/authlib/integrations/django_client/apps.py": 14151733532064278336, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7516/models.py": 5656065513234782067, + "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/__main__.py": 6480933938145689964, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_real.f90": 9097998998863007705, + "venv/lib/python3.13/site-packages/pandas/_libs/window/aggregations.pyi": 6072607880689125113, + "venv/lib/python3.13/site-packages/pandas/tests/extension/conftest.py": 16886862391161243448, + "venv/lib/python3.13/site-packages/sqlalchemy/connectors/asyncio.py": 4200928269459447593, + "venv/lib/python3.13/site-packages/cryptography-46.0.3.dist-info/WHEEL": 7414522633964952515, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_mem_policy.py": 13856626567322091253, + "venv/lib/python3.13/site-packages/numpy/version.py": 1497063540123613501, + "scripts/build_offline_docker_bundle.sh": 18334040481218597997, + "venv/lib/python3.13/site-packages/pydantic/plugin/_schema_validator.py": 857640644840976862, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/conftest.py": 12368472226536104037, + "venv/lib/python3.13/site-packages/anyio/abc/_tasks.py": 16839170934610571514, + "venv/lib/python3.13/site-packages/pandas/tests/apply/test_numba.py": 203614377324678234, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_info.py": 15151894356374015812, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/timedeltas/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_crackfortran.py": 10278720705326601943, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/__init__.py": 14522970260621605208, + "venv/lib/python3.13/site-packages/pandas/tests/resample/conftest.py": 1643522609234951699, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/collections.cpython-313-x86_64-linux-gnu.so": 6157288238855118017, + "venv/lib/python3.13/site-packages/_pytest/helpconfig.py": 3033477806668470951, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/util.py": 1520899420719605380, + "venv/lib/python3.13/site-packages/dateutil/_common.py": 12492546559232079071, + "venv/lib/python3.13/site-packages/pip/_vendor/__init__.py": 11734104142988812647, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/scanner.py": 5640381644804445932, + "venv/lib/python3.13/site-packages/starlette/convertors.py": 17839747154274354605, + "venv/lib/python3.13/site-packages/numpy/core/_utils.py": 14108063453670701289, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/kind/foo.f90": 1367964042730902899, + "venv/lib/python3.13/site-packages/pandas/_config/display.py": 433650013567734821, + "venv/lib/python3.13/site-packages/PIL/ExifTags.py": 6869879826623916830, + "venv/lib/python3.13/site-packages/pycparser/yacctab.py": 18018314512916353707, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/use_modules.f90": 3913612933340746297, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/markers.py": 5247208668583898949, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py": 15644105878811738516, + "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/WHEEL": 11630459739183915695, + "frontend/src/components/git/CommitHistory.svelte": 15861316872235672161, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/AB.inc": 10441778083831997423, + "venv/lib/python3.13/site-packages/_pytest/stepwise.py": 3527630676691417317, + "venv/lib/python3.13/site-packages/pygments/lexers/ul4.py": 9580182474451220952, + "backend/src/schemas/profile.py": 7072251247808762582, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/ddl.py": 5006132143435653867, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_multiindex.py": 13723677252432338937, + "backend/src/core/task_manager/task_logger.py": 12820629458199400734, + "backend/src/plugins/translate/__tests__/test_inline_correction.py": 3246602656771855554, + "backend/src/plugins/translate/plugin.py": 9225230324052126034, + "venv/lib/python3.13/site-packages/attrs/__init__.pyi": 14353885498883901855, + "venv/lib/python3.13/site-packages/numpy/core/_internal.py": 14816989375912210674, + "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_scrypt.py": 8106466515670064013, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/__init__.py": 7526309124863950484, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_convert_dtypes.py": 13612212066710266457, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_extint128.py": 11074657869101074458, + "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/RECORD": 12733654377842895588, + "frontend/src/lib/auth/store.ts": 14669655798912377598, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/console.py": 10217327186846649646, + "venv/lib/python3.13/site-packages/cryptography/__init__.py": 641702726372647770, + "venv/lib/python3.13/site-packages/click-8.3.1.dist-info/RECORD": 3649834117451110628, + "venv/lib/python3.13/site-packages/pandas/core/arrays/interval.py": 16281221279220043867, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_interval.py": 2911379732293827726, + "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/RECORD": 10036839994791510159, + "venv/lib/python3.13/site-packages/websockets/asyncio/messages.py": 6848925149930551457, + "venv/lib/python3.13/site-packages/numpy/_core/memmap.pyi": 14124415711399771715, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_validate_kwargs.py": 10173906126211473463, + "venv/lib/python3.13/site-packages/pip/_vendor/idna/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy.libs/libquadmath-96973f99-934c22de.so.0.0.0": 5098713761946118139, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/modules.pyi": 14176187662591164602, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_cte.py": 9274103293201050794, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/__init__.py": 12290228927283601067, + "venv/lib/python3.13/site-packages/pygments/lexers/felix.py": 2334148799642808111, + "venv/lib/python3.13/site-packages/passlib/crypto/_blowfish/base.py": 15872835330892203433, + "frontend/build/_app/immutable/nodes/13.Cuwx6oG8.js": 10896568729237690384, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_year.py": 17441853503700184299, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_invalid.py": 2762344062554575805, + "venv/lib/python3.13/site-packages/numpy/_core/__init__.py": 14951958843029076911, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_value_counts.py": 13270420637302622174, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_resolution.py": 18198858926948468112, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/string_/__init__.py": 15130871412783076140, + "backend/src/plugins/translate/scheduler.py": 1593554077252050680, + "venv/lib/python3.13/site-packages/passlib/hash.py": 5238710192614935875, + "venv/lib/python3.13/site-packages/pandas/io/formats/css.py": 11288129826285529457, + "venv/lib/python3.13/site-packages/PIL/ImageShow.py": 11437096951764124817, + "venv/lib/python3.13/site-packages/pandas/tests/extension/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_nditer.py": 8341339612899904236, + "venv/lib/python3.13/site-packages/numpy/_core/_methods.py": 16805668769357938792, + "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/licenses/LICENSE": 12688029424398428498, + "venv/lib/python3.13/site-packages/anyio/streams/stapled.py": 9733127303038397237, + "venv/lib/python3.13/site-packages/websockets/asyncio/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/array_constructors.py": 6727209833819502644, + "backend/src/api/routes/assistant/_admin_routes.py": 3380446401429936332, + "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/WHEEL": 6514509858522675228, + "venv/lib/python3.13/site-packages/_pytest/assertion/rewrite.py": 2425990712758915831, + "venv/lib/python3.13/site-packages/apscheduler/triggers/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_textreader.py": 388955620768371652, + "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_chebyshev.py": 8054411443686791426, + "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_delete.py": 3485374671877063217, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/aws.py": 1364297790170663941, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py": 13763154154862871596, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_character/foo77.f": 4046503346951676582, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_asfreq.py": 6162659555626011770, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/issue232.py": 6498198296113395367, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_all_methods.py": 18227706645906325527, + "venv/lib/python3.13/site-packages/pygments/formatters/irc.py": 2133711525226123259, + "venv/lib/python3.13/site-packages/pygments/__init__.py": 14493583771211110135, + "venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/INSTALLER": 17282701611721059870, + "frontend/src/lib/stores/translationRun.js": 1541511355007861201, + "frontend/src/lib/components/dataset-review/ExecutionMappingReview.svelte": 11412197655386056417, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/__ufunc_api.c": 11535375410441493281, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/__init__.py": 5118993772178969104, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_setops.py": 13440916133402971190, + "backend/src/core/__tests__/test_native_filters.py": 823253235973274872, + "backend/src/core/superset_client/_dashboards_filters.py": 11002883972618719697, + "venv/lib/python3.13/site-packages/pip/_internal/network/__init__.py": 1586148254475575526, + "venv/lib/python3.13/site-packages/pygments/lexers/slash.py": 8320676560518108343, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/authorization_server.py": 4427151727224472650, + "venv/lib/python3.13/site-packages/pydantic/_internal/_forward_ref.py": 10623110912945011703, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/test_holiday.py": 8541197358003711845, + "backend/tests/test_translate_scheduler_execution.py": 16687335504320865378, + "frontend/build/_app/immutable/entry/app.EqiC6C3A.js": 12263353332086817548, + "venv/lib/python3.13/site-packages/ecdsa/errors.py": 11755424742691057760, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh25286_bc.pyf": 10893958728966010450, + "venv/lib/python3.13/site-packages/PIL/ImageColor.py": 17058618058554970738, + "venv/lib/python3.13/site-packages/urllib3/connectionpool.py": 7493427857878469165, + "venv/lib/python3.13/site-packages/click/termui.py": 7022374827798605437, + "venv/lib/python3.13/site-packages/pandas/core/reshape/tile.py": 17137524194129840833, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/__init__.py": 15340047930888693984, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_unique.py": 16285418015531891530, + "venv/lib/python3.13/site-packages/fastapi/middleware/gzip.py": 436886407566263171, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/char.pyi": 1639810799219814643, + "venv/lib/python3.13/site-packages/pandas/core/methods/selectn.py": 13552176539021625776, + "venv/lib/python3.13/site-packages/git/refs/reference.py": 2038789918710608481, + "frontend/src/components/DashboardGrid.svelte": 5332611872674866671, + "frontend/src/lib/components/translate/BulkCorrectionSidebar.svelte": 5119156213531888283, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_riscv_unix.h": 8607317131295864391, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/DamerauLevenshtein.py": 17993169044119596860, + "venv/lib/python3.13/site-packages/starlette/middleware/base.py": 16850428285382256618, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/simple.py": 11050636051019777317, + "venv/lib/python3.13/site-packages/passlib/crypto/des.py": 14699826058580193499, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_get_numeric_data.py": 11747933921770969706, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/index_tricks.py": 10051261976498916957, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc64_linux.h": 4071314837793906998, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/palette.py": 17383863392192572184, + "venv/lib/python3.13/site-packages/apscheduler/executors/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/_postgres_builtins.py": 2383906454995727700, + "venv/lib/python3.13/site-packages/typing_inspection/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/ecdsa/test_der.py": 7117444285097824495, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/jws_algs.py": 11920621906391796226, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_arrayprint.py": 13335063050941743734, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_read_errors.py": 4559483361564472117, + "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_writers.py": 103545128256076790, + "venv/lib/python3.13/site-packages/multipart/__init__.py": 6496114190873890851, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/dml.py": 5340326603935367534, + "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/request.py": 17642741955458391050, + "frontend/src/components/git/ConflictResolver.svelte": 12431368407090802392, + "frontend/build/_app/immutable/nodes/23.Cla0r9L2.js": 4312072173612601380, + "frontend/build/_app/immutable/chunks/DepzS_bZ.js": 2243340878436625890, + "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/INSTALLER": 17282701611721059870, + "backend/src/core/utils/__init__.py": 12454773556575864508, + "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/WHEEL": 16097436423493754389, + "frontend/src/lib/components/layout/__tests__/test_taskDrawer.svelte.js": 15288009749873977442, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/array_constructors.pyi": 4993066609272697419, + "venv/lib/python3.13/site-packages/starlette/endpoints.py": 3379755567024530053, + "venv/lib/python3.13/site-packages/numpy/polynomial/polynomial.py": 7658234162211356265, + "venv/lib/python3.13/site-packages/starlette/schemas.py": 697416731579555195, + "venv/lib/python3.13/site-packages/pandas/core/internals/blocks.py": 6448406236246476333, + "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_backend.py": 1472021608501071055, + "venv/lib/python3.13/site-packages/git/cmd.py": 8854786163152637148, + "venv/lib/python3.13/site-packages/passlib/handlers/digests.py": 18158237109145814857, + "venv/lib/python3.13/site-packages/pandas/tests/extension/json/array.py": 7437107031079833232, + "venv/lib/python3.13/site-packages/PIL/DdsImagePlugin.py": 10646832373516499548, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/conftest.py": 4124267277224879990, + "venv/lib/python3.13/site-packages/secretstorage/util.py": 1004887530903069622, + "venv/lib/python3.13/site-packages/passlib/handlers/scrypt.py": 10089372287597257402, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/compiler.py": 11662494978292401568, + "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/WHEEL": 8600534672961461758, + "venv/lib/python3.13/site-packages/anyio/pytest_plugin.py": 4165280709813718099, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/constants.pyi": 15633065380510755171, + "backend/src/core/migration/risk_assessor.py": 1061977005072298440, + "venv/lib/python3.13/site-packages/python_multipart-0.0.21.dist-info/WHEEL": 7454950858448014158, + "venv/lib/python3.13/site-packages/pygments/lexers/elm.py": 14683437310498104916, + "backend/src/plugins/translate/__tests__/test_executor.py": 7200474786659302788, + "venv/lib/python3.13/site-packages/rpds/__init__.pyi": 12037474523494903587, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ndarray_misc.pyi": 7544619194032232861, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libssl-81ffa89e.so.3": 13913673119166658781, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/hooks.py": 9888227370911151341, + "venv/lib/python3.13/site-packages/pytest-9.0.2.dist-info/RECORD": 18294169019244826301, + "venv/lib/python3.13/site-packages/pandas/_libs/window/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/_utils/__init__.pyi": 13530522898409268883, + "venv/lib/python3.13/site-packages/pandas/tests/extension/test_arrow.py": 10068866425352207113, + "venv/lib/python3.13/site-packages/websockets/legacy/exceptions.py": 16025779645869134942, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/strings.pyi": 402863478592154500, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/elements.py": 11185011314498348980, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/__init__.py": 260774374497653876, + "venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/METADATA": 8128818962678047497, + "frontend/src/components/StartupEnvironmentWizard.svelte": 6947772062866928838, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/ec.pyi": 9835891299056543360, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/resource_owner_password_credentials.py": 9559295461619813890, + "venv/lib/python3.13/site-packages/numpy/lib/introspect.pyi": 12239924839764265507, + "venv/pyvenv.cfg": 2513607356761047446, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_tzconversion.py": 7205566353454584259, + "venv/lib/python3.13/site-packages/urllib3/util/url.py": 15992369651965016538, + "backend/src/services/__tests__/test_encryption_manager.py": 7097521778855634174, + "backend/test_app.db": 2486636812009447051, + "venv/lib/python3.13/site-packages/pillow.libs/libpng16-4a38ea05.so.16.53.0": 13136089038268964667, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_pivot.py": 334735766122314163, + "venv/lib/python3.13/site-packages/anyio/abc/_subprocesses.py": 4297459042599291531, + "venv/lib/python3.13/site-packages/pydantic/__init__.py": 11536052293866174672, + "venv/lib/python3.13/site-packages/pandas/tests/tools/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/iniconfig/_version.py": 8497474654470220170, + "venv/lib/python3.13/site-packages/pandas/io/formats/style_render.py": 13586834368514801953, + "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_dt_accessor.py": 2229616362327032253, + "venv/lib/python3.13/site-packages/pygments/lexers/procfile.py": 3860651022061090738, + "venv/lib/python3.13/site-packages/yaml/serializer.py": 1783761365703703558, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_equivalence.py": 17843623343419137458, + "venv/lib/python3.13/site-packages/pygments/lexers/ncl.py": 14838368780033177943, + "venv/lib/python3.13/site-packages/numpy/tests/test_lazyloading.py": 17465787495701737655, + "venv/lib/python3.13/site-packages/websockets/sync/messages.py": 15699091325918681038, + "venv/lib/python3.13/site-packages/numpy/lib/user_array.py": 14003456655595459585, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/interface.py": 3134071530627935964, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_fsspec.py": 6936167344144998172, + "backend/tests/test_translate_scheduler.py": 14274816721941617887, + "backend/ss_tools_backend.egg-info/PKG-INFO": 15134841326777953914, + "venv/lib/python3.13/site-packages/cffi/error.py": 8147505969912539538, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm64_masm.asm": 16630507336149184509, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_take.py": 9847808489301498487, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_put.py": 8547660958963625817, + "venv/lib/python3.13/site-packages/numpy/_typing/_nested_sequence.py": 7827259347937485408, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/numpy_/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/cffi/_embedding.h": 9441285871823018603, + "venv/lib/python3.13/site-packages/numpy/_core/_dtype.pyi": 17662618580381221543, + "venv/lib/python3.13/site-packages/cffi/__init__.py": 13427266446746498548, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/__init__.py": 5863110456909134678, + "venv/lib/python3.13/site-packages/pip/_internal/build_env.py": 3614358934066127900, + "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/api.py": 13134394673408254097, + "venv/lib/python3.13/site-packages/numpy/_core/_string_helpers.py": 17192211156810267818, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_arraymethod.py": 14421188237635557797, + "venv/lib/python3.13/site-packages/numpy/random/_bounded_integers.cpython-313-x86_64-linux-gnu.so": 16604252871044637047, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/f90continuation.f90": 17056591497459381871, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_isin.py": 14387850595546682314, + "venv/lib/python3.13/site-packages/numpy/lib/_datasource.pyi": 5894423389796456573, + "venv/lib/python3.13/site-packages/numpy/lib/_twodim_base_impl.pyi": 15509415708991012734, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/reduce.py": 14976712507854054776, + "venv/lib/python3.13/site-packages/jeepney/tests/test_auth.py": 15125055415702968414, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_to_offset.py": 3929599794655343911, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_parsing.py": 2228959015420278458, + "venv/lib/python3.13/site-packages/pip/_vendor/tomli/_parser.py": 3540203683732744499, + "venv/lib/python3.13/site-packages/pygments-2.19.2.dist-info/METADATA": 10220680536734745103, + "venv/lib/python3.13/site-packages/PIL/IcoImagePlugin.py": 14483964191652228364, + "venv/lib/python3.13/site-packages/PIL/SgiImagePlugin.py": 6916200065790425497, + "venv/lib/python3.13/site-packages/PIL/XVThumbImagePlugin.py": 7472134175570205454, + "venv/lib/python3.13/site-packages/pygments/lexers/diff.py": 5930189593946259160, + "venv/lib/python3.13/site-packages/numpy/f2py/diagnose.py": 503331864022631297, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/conversion.cpython-313-x86_64-linux-gnu.so": 9014806524727977333, + "venv/lib/python3.13/site-packages/packaging/pylock.py": 15346942039643310253, + "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/windows.py": 16452996247375488034, + "venv/lib/python3.13/site-packages/passlib/handlers/bcrypt.py": 8125664010268654055, + "venv/bin/jsonschema": 16516374846693491442, + "frontend/postcss.config.js": 5714274776976071678, + "frontend/build/_app/immutable/nodes/37.h0kspZQz.js": 2116024229457453594, + "venv/lib/python3.13/site-packages/fastapi/security/__init__.py": 1345944646061334046, + "frontend/build/_app/immutable/chunks/B3G5yh93.js": 9701033449616441331, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_freq_attr.py": 1720704043778272895, + "venv/lib/python3.13/site-packages/click/_compat.py": 14451519072991457993, + "venv/lib/python3.13/site-packages/passlib/tests/__main__.py": 16477180140297957650, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_slice.py": 9936085512921529417, + "venv/lib/python3.13/site-packages/pandas/_libs/hashtable.cpython-313-x86_64-linux-gnu.so": 16963890771704942465, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_concat.py": 16549010224568175202, + "venv/lib/python3.13/site-packages/rapidfuzz-3.14.3.dist-info/licenses/LICENSE": 5563356551734678258, + "venv/lib/python3.13/site-packages/jeepney/bus_messages.py": 5871341372541525842, + "venv/lib/python3.13/site-packages/keyring/backends/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/schema.py": 5391655516870611151, + "frontend/build/_app/immutable/nodes/28.BMMMq5av.js": 1258679536261114563, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/regexopt.py": 16073437303824961930, + "venv/lib/python3.13/site-packages/numpy/_pyinstaller/__init__.pyi": 15130871412783076140, + "venv/lib/python3.13/site-packages/uvicorn/logging.py": 12858188863836778374, + "venv/lib/python3.13/site-packages/numpy/lib/_npyio_impl.py": 2215312866861772848, + "venv/lib/python3.13/site-packages/pydantic/main.py": 14086938579850319884, + "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_insert.py": 15536994417338770457, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_datetime.py": 13781712980921881470, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_update.py": 9098258267671647783, + "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/licenses/AUTHORS": 17022185907188244883, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/__init__.py": 8807410829260170597, + "frontend/build/_app/immutable/chunks/DsAODWVW.js": 17093454341120407063, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/attributes.py": 5068814626384132900, + "frontend/vitest.config.js": 142965001777137074, + "backend/src/plugins/migration.py": 16567640288127443175, + "backend/alembic/versions/b1c2d3e4f5a6_add_source_hash_to_translation_records.py": 4775534557742900399, + "venv/lib/python3.13/site-packages/pandas/io/excel/_base.py": 1251545894005373734, + "venv/lib/python3.13/site-packages/uvicorn/_compat.py": 2593000889619875759, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_strptime.py": 14328510310436552635, + "venv/lib/python3.13/site-packages/greenlet/tests/fail_initialstub_already_started.py": 5562030563802006923, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/certifi/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/polynomial/legendre.py": 1731362089136926666, + "venv/lib/python3.13/site-packages/numpy/polynomial/__init__.py": 15558318749939680074, + "venv/lib/python3.13/site-packages/passlib/ext/django/__init__.py": 11713862539944289631, + "venv/lib/python3.13/site-packages/pandas/tests/test_common.py": 8987965560487646705, + "venv/lib/python3.13/site-packages/jose/utils.py": 1926138853970725083, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/base/test_unique.py": 3148372351781648868, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/OSA.py": 17817123894903120316, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/shape.pyi": 17197248408326188931, + "venv/lib/python3.13/site-packages/pandas/tests/resample/test_period_index.py": 6634414375581820058, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_iter.py": 9232021002336641851, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_freq_attr.py": 17290447066681837965, + "venv/lib/python3.13/site-packages/pandas/tests/frame/conftest.py": 15090201792072045503, + "venv/lib/python3.13/site-packages/pydantic/v1/error_wrappers.py": 5701679786078971939, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_dict_compat.py": 18196543090455290455, + "venv/lib/python3.13/site-packages/pandas/io/sas/sas7bdat.py": 17851617851457266799, + "venv/lib/python3.13/site-packages/starlette/concurrency.py": 9835859079437525910, + "venv/lib/python3.13/site-packages/pip/_vendor/idna/idnadata.py": 1169745920682458213, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_arraysetops.py": 10588903945244480732, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/_internal_utils.py": 4091305333167213279, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_astype.py": 15768655715952923925, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/conftest.py": 9673372914396625461, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm64_msvc.h": 14580207065003376324, + "venv/lib/python3.13/site-packages/pandas/tests/test_nanops.py": 8709105600366913011, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/named_types.py": 6099185243355915896, + "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/METADATA": 3448868793744358515, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py": 1331438198531942242, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/resultproxy.pyx": 5760108632332714287, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc_macosx.h": 3653129798702716366, + "venv/lib/python3.13/site-packages/yaml/resolver.py": 6669674521776449098, + "venv/lib/python3.13/site-packages/idna-3.11.dist-info/RECORD": 11541505360973709787, + "venv/lib/python3.13/site-packages/jaraco/functools/__init__.pyi": 2872039192528394677, + "venv/lib/python3.13/site-packages/jaraco_context-6.0.2.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_complex/foo90.f90": 15981076495193915107, + "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/METADATA": 2053484635654179443, + "venv/lib/python3.13/site-packages/pandas/tests/strings/test_extract.py": 14547819021277949280, + "venv/lib/python3.13/site-packages/pygments/lexers/_luau_builtins.py": 13690910306072616818, + "venv/lib/python3.13/site-packages/pygments/lexers/parasail.py": 7758704230750989282, + "venv/lib/python3.13/site-packages/urllib3/util/retry.py": 17988032431204662756, + "venv/lib/python3.13/site-packages/pygments/lexers/rnc.py": 9747352274729854620, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/string/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/routines/subrout.f": 18215492757907465667, + "backend/src/core/database.py.bak": 14909628735913201532, + "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/connection.py": 15405047955974108860, + "venv/lib/python3.13/site-packages/pygments/lexers/textedit.py": 14872291021083255414, + "backend/src/plugins/llm_analysis/__init__.py": 9254812131558249924, + "venv/lib/python3.13/site-packages/pygments/lexers/rebol.py": 10460732731157940232, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/pubprivmod.f90": 8680230823849248800, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/signals.py": 13677199634497673845, + "venv/lib/python3.13/site-packages/passlib/tests/test_totp.py": 9150389741890748343, + "venv/lib/python3.13/site-packages/numpy/_core/_exceptions.pyi": 3661113433078225952, + "venv/lib/python3.13/site-packages/pygments/lexers/configs.py": 7712797939104760185, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/licenses/__init__.py": 2635182244050911910, + "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/core/base.py": 10262717073058743378, + "venv/lib/python3.13/site-packages/PIL/__init__.py": 10393984345353752724, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_concat.py": 4472503149811617440, + "venv/lib/python3.13/site-packages/cryptography/hazmat/decrepit/__init__.py": 63497920264089252, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_convert_dtypes.py": 5005259275646154894, + "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_xlrd.py": 9982457234958102003, + "venv/lib/python3.13/site-packages/pip/_internal/operations/build/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/PIL/FontFile.py": 112053841436523305, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_union_categoricals.py": 6843679254735807, + "venv/lib/python3.13/site-packages/pygments/lexers/c_like.py": 13004061780700827047, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/constants.pyi": 4415188581374004936, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/file_proxy.py": 17695135735211897444, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/nattype.cpython-313-x86_64-linux-gnu.so": 2311012065007769341, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/__init__.py": 6327819603201859020, + "venv/lib/python3.13/site-packages/pygments/lexers/python.py": 8995519134978505744, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_highlight.py": 7573043358244876807, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/ssltransport.py": 8148652555908329980, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_printing.py": 1901396869745313303, + "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/METADATA": 10241403930811743893, + "frontend/src/lib/components/reports/__tests__/report_type_profiles.test.js": 498330292141991614, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth1/resource_protector.py": 14354891358060085360, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/wrapper.py": 14599195010683170123, + "venv/lib/python3.13/site-packages/pip/_vendor/distro/distro.py": 12494639376332486204, + "venv/lib/python3.13/site-packages/cryptography/hazmat/__init__.py": 6534110674636458866, + "venv/lib/python3.13/site-packages/dateutil/zoneinfo/rebuild.py": 1813298167468704155, + "venv/lib/python3.13/site-packages/packaging/utils.py": 3469957615920103670, + "frontend/build/_app/immutable/nodes/5.B73cStqs.js": 12655203259752489603, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/test_timestamp.py": 10308799534491190078, + "frontend/src/components/DynamicForm.svelte": 12658559315809907844, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/nditer.pyi": 2351219156711800159, + "frontend/public/vite.svg": 13568221685541582385, + "backend/src/services/clean_release/approval_service.py": 12912719831239691496, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_return_logical.py": 6242457515917145500, + "venv/lib/python3.13/site-packages/numpy/f2py/auxfuncs.py": 2234931733687557433, + "backend/src/services/dataset_review/repositories/session_repository.py": 4058785536575367912, + "frontend/build/_app/immutable/nodes/24.BP-Fp4AB.js": 2248231906051278481, + "venv/lib/python3.13/site-packages/annotated_types/__init__.py": 7367790549779882749, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/styles/__init__.py": 9482393721453335937, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/comparisons.py": 7327629888601690759, + "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/METADATA": 10707390829735099042, + "venv/lib/python3.13/site-packages/pip/_internal/req/req_dependency_group.py": 10505346210114745037, + "venv/lib/python3.13/site-packages/pip/_internal/metadata/base.py": 13444129650880426353, + "frontend/src/lib/components/dataset-review/__tests__/us3_execution_batch.ux.test.js": 13416996904332574966, + "venv/lib/python3.13/site-packages/pandas/tests/extension/array_with_attr/array.py": 13040461386712513615, + "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_struct_accessor.py": 6567451952835390075, + "venv/lib/python3.13/site-packages/attr/validators.pyi": 15579625896285277321, + "venv/lib/python3.13/site-packages/PIL/ImageMode.py": 2361901066832921091, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/_selectable_constructors.py": 84919308502240880, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/__init__.py": 9820770319106921495, + "backend/conftest.py": 16521922883434363388, + "venv/lib/python3.13/site-packages/websockets/datastructures.py": 5180442040351387281, + "venv/lib/python3.13/site-packages/pydantic/v1/validators.py": 1060954771151908883, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/test_constructors.py": 1697610790604400105, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_duplicated.py": 14676058112867601208, + "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/WHEEL": 16097436423493754389, + "frontend/src/routes/settings/__tests__/settings_page.ux.test.js": 8756428349518151160, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_delitem.py": 16538190861492829338, + "venv/lib/python3.13/site-packages/cryptography/x509/certificate_transparency.py": 11337143186866439208, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/philox-testset-2.csv": 7063857529719738394, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_nlargest.py": 14518132768753249360, + "venv/lib/python3.13/site-packages/websockets/sync/client.py": 10892557975919033983, + "venv/lib/python3.13/site-packages/pillow.libs/libopenjp2-94e588ba.so.2.5.4": 2483101806045986252, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arccos.csv": 3026156288992132911, + "backend/test.db": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/_core/_methods.pyi": 9155045948490225716, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_sparc_sun_gcc.h": 2915128971664712180, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/formatters/_mapping.py": 13335227913146542481, + "venv/lib/python3.13/site-packages/jaraco_functools-4.4.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/dotenv/main.py": 7408889670575271484, + "venv/lib/python3.13/site-packages/pandas/_libs/json.pyi": 10989301892466547548, + "venv/lib/python3.13/site-packages/PIL/_binary.py": 14039167720582222043, + "backend/src/core/migration/__init__.py": 13075189211840127611, + "venv/lib/python3.13/site-packages/attrs/converters.py": 13109003765349201809, + "venv/lib/python3.13/site-packages/pandas/util/_test_decorators.py": 17930801441803090445, + "venv/lib/python3.13/site-packages/fastapi/applications.py": 4975013401500183420, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_ops.py": 3875181889032080505, + "venv/lib/python3.13/site-packages/authlib/integrations/base_client/async_app.py": 4329794049222609894, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/state_changes.py": 11425629729736599320, + "venv/lib/python3.13/site-packages/PIL/ImageGrab.py": 11114379915762058868, + "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pydantic/v1/utils.py": 902648247561165429, + "venv/lib/python3.13/site-packages/pygments/lexers/ml.py": 7510453371068494792, + "venv/lib/python3.13/site-packages/packaging/_elffile.py": 9546225990833172494, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/providers.py": 7267821734584612032, + "backend/src/api/routes/migration.py": 6030566471113208264, + "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/RECORD": 9461427699726982537, + "venv/lib/python3.13/site-packages/pip/_internal/utils/compatibility_tags.py": 15862649917149158291, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arrayterator.pyi": 7439348655567232744, + "venv/lib/python3.13/site-packages/numpy/_core/einsumfunc.pyi": 4729765666018003634, + "frontend/src/routes/reports/llm/[taskId]/report_page.contract.test.js": 4825093389030464765, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_chaining_and_caching.py": 15814259766479305290, + "venv/lib/python3.13/site-packages/anyio/to_process.py": 16627578499791368365, + "venv/lib/python3.13/site-packages/rapidfuzz/fuzz_cpp.cpython-313-x86_64-linux-gnu.so": 14187094676876471848, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/ndarraytypes.h": 6765088137325865789, + "venv/lib/python3.13/site-packages/pandas/core/groupby/__init__.py": 3015263524218708013, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/foo_deps.f90": 14350239197565389508, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/cursor.py": 8074527784886981710, + "venv/lib/python3.13/site-packages/git/objects/blob.py": 8972374723153211283, + "backend/src/core/superset_client/_datasets_preview_filters.py": 12835417730902013107, + "venv/lib/python3.13/site-packages/pip/_internal/operations/install/wheel.py": 3531093454660385247, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libsasl2-883649fd.so.3.0.0": 13306917506408577991, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/__init__.py": 14571251344510763303, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/metadata.py": 1362166613223529108, + "venv/lib/python3.13/site-packages/numpy/lib/format.py": 14971167445246135440, + "venv/lib/python3.13/site-packages/numpy/random/_bounded_integers.pxd": 9126772093305708921, + "venv/lib/python3.13/site-packages/requests/exceptions.py": 6474239743209354989, + "venv/lib/python3.13/site-packages/jeepney/tests/test_bus.py": 4260827599660763641, + "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/RECORD": 16342326945648353387, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_x64_masm.obj": 2884737706935637437, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/arrayprint.py": 5912583751693302370, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_timestamp_method.py": 11393911418463808195, + "venv/lib/python3.13/site-packages/apscheduler/executors/twisted.py": 2695602672085615762, + "venv/lib/python3.13/site-packages/pydantic/v1/typing.py": 13467331592583800299, + "venv/lib/python3.13/site-packages/pandas/compat/numpy/function.py": 18405870240333383757, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/dsa.pyi": 1065808244870331598, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/gcp.py": 12328409672947966867, + "venv/lib/python3.13/site-packages/numpy/lib/__init__.pyi": 1537495515469182226, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_setops.py": 5208912445718807372, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_where.py": 16584772339591698754, + "venv/lib/python3.13/site-packages/pandas/tests/strings/conftest.py": 12065119814157197510, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/result.py": 12262298207886620908, + "venv/lib/python3.13/site-packages/PIL/PcxImagePlugin.py": 1691405132377495336, + "venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/random/_generator.cpython-313-x86_64-linux-gnu.so": 15625019745021657670, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/floating/test_function.py": 13492077564015386528, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_categorical.py": 12897027063618569795, + "venv/lib/python3.13/site-packages/pandas/io/formats/style.py": 13235403689320479165, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/common/block.f": 10727418304254548946, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_api.py": 16743710460729009914, + "venv/lib/python3.13/site-packages/secretstorage/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/anyio/_core/_typedattr.py": 17584691045493791349, + "scripts/gen_semantics.py": 11397714264377363083, + "venv/lib/python3.13/site-packages/jaraco/classes/ancestry.py": 11742194518360056470, + "venv/lib/python3.13/site-packages/git/objects/submodule/base.py": 10140490301897269395, + "venv/lib/python3.13/site-packages/urllib3/_request_methods.py": 13671055114247516883, + "venv/lib/python3.13/site-packages/uvicorn/supervisors/multiprocess.py": 10047012560534876149, + "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/licenses/LICENSE": 3868190977070717994, + "backend/src/plugins/translate/_utils.py": 4311334421388625700, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/lib_polynomial.pyi": 9134949577567498894, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_diff.py": 7420674239386436885, + "venv/lib/python3.13/site-packages/pydantic/json.py": 4109352930301629308, + "venv/lib/python3.13/site-packages/numpy/_core/overrides.pyi": 13121243505132110607, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_get.py": 10255752806860493777, + "venv/lib/python3.13/site-packages/authlib/oidc/discovery/well_known.py": 8259335680633926377, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/utils.py": 3710235316419562274, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_records.py": 6252761812228230408, + "venv/lib/python3.13/site-packages/pandas/tests/apply/test_series_apply_relabeling.py": 3930092097678339743, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/psycopg.py": 8023549900603235601, + "backend/src/services/clean_release/preparation_service.py": 7557861288058237966, + "venv/lib/python3.13/site-packages/numpy/lib/_datasource.py": 18136853038949826147, + "venv/lib/python3.13/site-packages/numpy/_pytesttester.py": 17737911110576747508, + "venv/lib/python3.13/site-packages/pandas/core/indexes/category.py": 18192569199634406001, + "venv/lib/python3.13/site-packages/pandas/core/resample.py": 13814298871925341617, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_transpose.py": 17983160984979323393, + "venv/lib/python3.13/site-packages/attrs/filters.py": 6844006230924162452, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/missing.py": 14589973874359353799, + "frontend/src/lib/components/dataset-review/__tests__/source_intake_panel.ux.test.js": 13445861056598482363, + "backend/tests/scripts/test_clean_release_tui_v2.py": 16893299982170941753, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth1/__init__.py": 14481526616887213120, + "venv/lib/python3.13/site-packages/pydantic/validate_call_decorator.py": 11130572937843915026, + "frontend/src/routes/translate/[id]/+page.svelte": 2871210084962736100, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/control.py": 2034518223411945438, + "venv/lib/python3.13/site-packages/websockets/utils.py": 17624459810483092475, + "backend/src/api/routes/__tests__/test_git_api.py": 43259826036955162, + "venv/lib/python3.13/site-packages/pip/_internal/utils/virtualenv.py": 14416705841810221305, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_boxplot_method.py": 7635131059308651529, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_palettes.py": 13495896173068490516, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_timedelta.py": 7742506012012398830, + "venv/lib/python3.13/site-packages/httpcore/_utils.py": 3255581092836273826, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py": 8079219938116542284, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_scalar_methods.py": 14881910898871057761, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_is_homogeneous_dtype.py": 15378147393056707851, + "backend/src/models/dataset_review_pkg/_filter_models.py": 8426742165406106645, + "venv/lib/python3.13/site-packages/urllib3/util/wait.py": 14629837381061032546, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/string/test_indexing.py": 2274965639578580723, + "venv/lib/python3.13/site-packages/numpy/ma/mrecords.pyi": 8945514863366384484, + "venv/lib/python3.13/site-packages/numpy/_core/tests/_natype.py": 12013743717306900860, + "venv/lib/python3.13/site-packages/dateutil/rrule.py": 398469817784230512, + "venv/lib/python3.13/site-packages/numpy/testing/_private/utils.py": 3646474165295296669, + "venv/lib/python3.13/site-packages/authlib/integrations/starlette_client/apps.py": 4219758952914498116, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/sqlite/json.py": 4660603129768960095, + "venv/lib/python3.13/site-packages/yaml/__init__.py": 11044333164208875121, + "venv/lib/python3.13/site-packages/httpx/_transports/asgi.py": 12335675489870745729, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_dtypes.py": 6831042272921908068, + "venv/lib/python3.13/site-packages/httpcore/__init__.py": 5897259569277532401, + "venv/lib/python3.13/site-packages/jose/backends/ecdsa_backend.py": 2684176977865283060, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth1/authorization_server.py": 6858244712625056421, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7515/__init__.py": 2791238481813240414, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/_testing/compat.py": 10172779814048259850, + "venv/lib/python3.13/site-packages/pygments/styles/igor.py": 2291640843781128270, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_decimal.py": 3515468590005709183, + "venv/lib/python3.13/site-packages/pygments/lexers/comal.py": 769727871242464458, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/mod.py": 2054876501774773382, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/core.py": 14576109242055156193, + "venv/lib/python3.13/site-packages/PIL/MicImagePlugin.py": 13540523615822507963, + "venv/lib/python3.13/site-packages/_pytest/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/core/window/expanding.py": 14141093310397856286, + "venv/lib/python3.13/site-packages/pandas/api/typing/aliases.py": 11194270368147942988, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/git/index/base.py": 641556304637379636, + "venv/lib/python3.13/site-packages/pip/_internal/operations/build/build_tracker.py": 9389444024234384408, + "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/_pytest/raises.py": 8188402179490363767, + "venv/lib/python3.13/site-packages/numpy/_typing/_char_codes.py": 14162745886150625150, + "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/RECORD": 17545477863793260029, + "venv/lib/python3.13/site-packages/attr/_next_gen.py": 16353246941851015420, + "venv/lib/python3.13/site-packages/websockets/__main__.py": 11948055520555854616, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py": 17106903014205241774, + "venv/lib/python3.13/site-packages/pydantic/_internal/_fields.py": 2692892815545218335, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_semicolon_split.py": 8153770165829451525, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/vectorized.cpython-313-x86_64-linux-gnu.so": 5441755022198996487, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_join.py": 17508073052750406350, + "venv/lib/python3.13/site-packages/git/repo/base.py": 13975465345838683533, + "venv/lib/python3.13/site-packages/attr/__init__.pyi": 13203080513025719049, + "venv/lib/python3.13/site-packages/pygments/lexers/automation.py": 8021926838024836904, + "venv/lib/python3.13/site-packages/charset_normalizer-3.4.4.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/_core/_umath_tests.pyi": 4587511199710546030, + "venv/lib/python3.13/site-packages/websockets/sync/utils.py": 15135020989207191168, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/collections.pyx": 17138729833735744777, + "venv/bin/pyrsa-decrypt": 10929163076272054227, + "venv/bin/Activate.ps1": 1196103427939049710, + "frontend/src/lib/stores/__tests__/test_activity.js": 15599963775430388097, + "frontend/src/routes/settings/git/+page.svelte": 9547035239167234477, + "venv/lib/python3.13/site-packages/pip/_vendor/tomli_w/__init__.py": 12849371269359608058, + "venv/lib/python3.13/site-packages/pandas/core/indexes/extension.py": 7193879749426425951, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_duplicates.py": 9830936017197354309, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_get_value.py": 389530025760539462, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/legacy/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_filter.py": 2388681477981649268, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_arithmetic.py": 13320788210970132239, + "venv/lib/python3.13/site-packages/pytest_asyncio/plugin.py": 13414541723622354359, + "backend/src/plugins/debug.py": 2035734953696955393, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libpq-9b38f5e3.so.5.17": 9680474575710585826, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/data_common.f": 5913807803369559749, + "venv/lib/python3.13/site-packages/numpy/_core/_add_newdocs.pyi": 9282436354926617898, + "venv/lib/python3.13/site-packages/numpy/testing/_private/extbuild.pyi": 18253950285509265097, + "venv/lib/python3.13/site-packages/numpy/f2py/cb_rules.py": 11397521081216402594, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_mem_overlap.py": 4202716587079637350, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_argsort.py": 7320661835159648143, + "frontend/build/_app/immutable/nodes/9.86weTBJx.js": 9420619046628747900, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_quantile.py": 11273403743157110261, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_partial_slicing.py": 15567512240469921599, + "venv/lib/python3.13/site-packages/pandas/io/formats/_color_data.py": 15614976586613738953, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_html.py": 12040484325988635032, + "backend/src/core/cot_logger.py": 3113579786931231764, + "backend/src/api/routes/llm.py": 6474757426346697754, + "venv/lib/python3.13/site-packages/numpy/linalg/_umath_linalg.pyi": 7674087576653964854, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_setops.py": 14246225281428284850, + "venv/lib/python3.13/site-packages/PIL/_avif.pyi": 18222325750818585549, + "frontend/src/routes/settings/git/__tests__/git_settings_page.ux.test.js": 5334904955024018051, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/prompt.py": 9561640601516933380, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_describe.py": 362374422228593281, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7521/client.py": 17305914656270781911, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/lexer.py": 14400766876373764268, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_chunksize.py": 7107619871245389835, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_astype.py": 4854809596041987380, + "backend/src/core/superset_client/_charts.py": 8101226087578568224, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/hstore.py": 9700253004947691961, + "venv/lib/python3.13/site-packages/ecdsa/test_rw_lock.py": 1572802971060634697, + "frontend/build/_app/immutable/chunks/GHK41q1Y.js": 2219327255070861874, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_rename_axis.py": 12432900682994622455, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test__datasource.py": 1747755531265478106, + "venv/lib/python3.13/site-packages/pygments/lexers/vyper.py": 6477559246065994199, + "venv/lib/python3.13/site-packages/pandas/_libs/window/indexers.cpython-313-x86_64-linux-gnu.so": 9380059465932560412, + "venv/lib/python3.13/site-packages/numpy/_utils/_convertions.py": 12886275429547303345, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/__init__.py": 8558541715982163200, + "backend/src/api/routes/assistant/_llm_planner_intent.py": 10603216032586398176, + "venv/lib/python3.13/site-packages/pandas/tests/test_algos.py": 11860014896449369497, + "backend/src/api/routes/clean_release.py": 1984604735131504761, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/util.py": 4815334059917765852, + "venv/lib/python3.13/site-packages/websockets/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_partial.py": 15129217814262096441, + "venv/lib/python3.13/site-packages/ecdsa/_version.py": 17707102226558781833, + "venv/lib/python3.13/site-packages/pandas/tests/interchange/test_spec_conformance.py": 16448324304809345527, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_custom_business_hour.py": 14128508606361694814, + "venv/lib/python3.13/site-packages/PIL/QoiImagePlugin.py": 17663704273100331654, + "venv/lib/python3.13/site-packages/pygments/lexers/text.py": 10572727755736597715, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_interval_range.py": 9900908262700749861, + "backend/src/services/clean_release/mappers.py": 8684357784135236729, + "venv/lib/python3.13/site-packages/_pytest/nodes.py": 515939386237558331, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_datetimes.py": 7889573945417063183, + "venv/lib/python3.13/site-packages/pandas/tests/io/xml/test_to_xml.py": 8477770807916990981, + "venv/lib/python3.13/site-packages/pydantic/_internal/_typing_extra.py": 14843908493613991959, + "venv/lib/python3.13/site-packages/numpy/f2py/capi_maps.pyi": 10638891136586198429, + "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/core/arrays/__init__.py": 10460719978124302175, + "venv/lib/python3.13/site-packages/apscheduler/jobstores/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/period/test_period.py": 12593777226419608212, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_util.py": 6573034224727513309, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/state.py": 10500170103357297551, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_quarter.py": 11099677280196239755, + "venv/lib/python3.13/site-packages/numpy/exceptions.py": 10981418339663118022, + "venv/lib/python3.13/site-packages/uvicorn/lifespan/off.py": 16254524872582859561, + "backend/src/services/clean_release/facade.py": 11231787492455424065, + "venv/lib/python3.13/site-packages/PIL/ImageFont.py": 15621336334706847865, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/indexable.py": 4131796922597075758, + "venv/lib/python3.13/site-packages/requests/adapters.py": 17161109043243436200, + "venv/lib/python3.13/site-packages/apscheduler/triggers/date.py": 9545774812177853725, + "venv/lib/python3.13/site-packages/numpy/random/_examples/cython/meson.build": 17468163147765825687, + "venv/lib/python3.13/site-packages/greenlet/tests/test_generator.py": 14156412320760444199, + "venv/lib/python3.13/site-packages/numpy/_pytesttester.pyi": 5750506937526308405, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_reindex_like.py": 1407890639206728014, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/cells.py": 8299309127027168194, + "venv/lib/python3.13/site-packages/git/index/__init__.py": 7874728461616959441, + "venv/lib/python3.13/site-packages/bcrypt-4.0.1.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/dtypes/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/_pytest/setuponly.py": 10959102379908419762, + "backend/src/plugins/translate/__tests__/test_preview.py": 12246698485015379020, + "venv/lib/python3.13/site-packages/pygments/lexers/clean.py": 17772169928835025327, + "venv/lib/python3.13/site-packages/pip/_internal/commands/__init__.py": 3795218871212151351, + "venv/lib/python3.13/site-packages/requests/models.py": 9104898985870652256, + "venv/lib/python3.13/site-packages/git/refs/symbolic.py": 14996822770181935589, + "venv/lib/python3.13/site-packages/PIL/ImageDraw2.py": 13610162107593278891, + "venv/lib/python3.13/site-packages/pyasn1/type/__init__.py": 15728752901274520502, + "venv/lib/python3.13/site-packages/jsonschema/tests/fuzz_validate.py": 2328585939822059352, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/lib_utils.py": 4612923732868929770, + "venv/lib/python3.13/site-packages/pandas/tests/base/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/anyio/_core/_subprocesses.py": 7271614043537119143, + "venv/lib/python3.13/site-packages/anyio/_backends/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/urllib3/util/ssltransport.py": 1175688460659745788, + "venv/lib/python3.13/site-packages/urllib3/util/util.py": 12490259045660543460, + "venv/lib/python3.13/site-packages/pygments/styles/material.py": 14325514369696961874, + "venv/lib/python3.13/site-packages/pydantic/_internal/_docs_extraction.py": 5210114991139191634, + "venv/lib/python3.13/site-packages/pygments/lexers/_scheme_builtins.py": 5722444920479775453, + "frontend/src/routes/admin/users/+page.svelte": 18167787254767604761, + "frontend/build/_app/immutable/chunks/CKGT5ge7.js": 12663886993086252818, + "venv/lib/python3.13/site-packages/numpy/random/_generator.pyi": 17167649524261942483, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_print.py": 598821365691498702, + "venv/lib/python3.13/site-packages/uvicorn/loops/uvloop.py": 5385425357504191161, + "venv/lib/python3.13/site-packages/rapidfuzz/utils_py.py": 12341806772632276946, + "venv/lib/python3.13/site-packages/pandas/io/formats/csvs.py": 17223598248259691292, + "venv/bin/pip3.13": 13861749540792881808, + "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/response.py": 353446217435156470, + "venv/lib/python3.13/site-packages/pandas/compat/_optional.py": 12714208019736797073, + "venv/lib/python3.13/site-packages/numpy/ma/tests/test_regression.py": 12593643817685777170, + "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/WHEEL": 14929202952940710322, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_interp_fillna.py": 5530041283991655415, + "docker/frontend.Dockerfile": 9514032629617942525, + "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/METADATA": 1266134223155266488, + "venv/bin/dotenv": 953953349803507198, + "backend/src/core/superset_client/_datasets.py": 4813403545392307482, + "venv/lib/python3.13/site-packages/bcrypt/__init__.py": 11415155211917181362, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_packbits.py": 7592956764084658562, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_indexing.py": 16291207621268530224, + "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/LICENSE": 16510279186416777136, + "backend/src/plugins/git_plugin.py": 12799126783361308681, + "venv/lib/python3.13/site-packages/jeepney/__init__.py": 1063800617763464773, + "venv/lib/python3.13/site-packages/gitdb/db/ref.py": 1176701624460337371, + "venv/lib/python3.13/site-packages/pydantic/decorator.py": 6984214881365537811, + "venv/lib/python3.13/site-packages/_pytest/_argcomplete.py": 3586514420674089892, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/lib_version.py": 10384171076976071398, + "venv/lib/python3.13/site-packages/secretstorage/exceptions.py": 16701299261122689924, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libgssapi_krb5-497db0c6.so.2.2": 6199424505696679197, + "venv/lib/python3.13/site-packages/pygments/lexers/testing.py": 17148598391210353576, + "venv/lib/python3.13/site-packages/passlib/ext/django/models.py": 12386223748379294996, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/unused_registry.py": 10153622516112471812, + "venv/lib/python3.13/site-packages/httpx/_multipart.py": 2181282475656908730, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/diagnose.py": 17275977405061138181, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_export_format.py": 17862222951776199694, + "venv/lib/python3.13/site-packages/greenlet/tests/leakcheck.py": 10179165748538820557, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_iteration.py": 2118501258411211851, + "venv/lib/python3.13/site-packages/packaging/_structures.py": 13086687542872305890, + "venv/lib/python3.13/site-packages/jsonschema/_utils.py": 14237077105384631097, + "backend/src/api/routes/assistant/_dataset_review_dispatch.py": 10108558091463171824, + "backend/src/api/routes/assistant/_resolvers.py": 1139669723103531814, + "venv/lib/python3.13/site-packages/pandas/core/ops/common.py": 17314999825118928100, + "venv/lib/python3.13/site-packages/dateutil/tz/_factories.py": 7538654189286887518, + "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/licenses/AUTHORS": 3558407083075395848, + "venv/lib/python3.13/site-packages/sqlalchemy/pool/base.py": 16951268065271931235, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_index.py": 12015738970771703312, + "venv/lib/python3.13/site-packages/jsonschema/__main__.py": 18136546137047911521, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7662/__init__.py": 17454177076687312424, + "venv/lib/python3.13/site-packages/pip/_internal/cli/main.py": 9304034369762028739, + "backend/tests/core/test_mapping_service.py": 6888870177029085220, + "venv/lib/python3.13/site-packages/pandas/io/excel/_util.py": 6664989864423807444, + "frontend/src/lib/components/reports/__tests__/report_detail.integration.test.js": 4193615240297330385, + "venv/lib/python3.13/site-packages/referencing/exceptions.py": 2535322334430110414, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/mypy.ini": 4763385838822685881, + "frontend/build/_app/immutable/nodes/26.BpHj-_gK.js": 12667509163112762275, + "venv/lib/python3.13/site-packages/python_dateutil-2.9.0.post0.dist-info/zip-safe": 15240312484046875203, + "venv/lib/python3.13/site-packages/authlib/common/errors.py": 16082974228565515177, + "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/ecdsa/eddsa.py": 15966858338456571506, + "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/METADATA": 3044554568188160018, + "backend/src/services/__tests__/test_health_service.py": 5498183976797488518, + "venv/lib/python3.13/site-packages/jeepney/io/blocking.py": 4699555965372200059, + "venv/lib/python3.13/site-packages/httpcore-1.0.9.dist-info/METADATA": 14158024963562115116, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/dml.py": 6567627855667533645, + "frontend/build/_app/immutable/assets/AssistantChatPanel.D4L5jlt0.css": 10724144138366384732, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/plugin/pytestplugin.py": 1591034895268415944, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_dot.py": 16876560325283070437, + "frontend/static/favicon.svg": 6451919037497541980, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_datetime.py": 7862725028865965163, + "venv/lib/python3.13/site-packages/pycparser/c_parser.py": 3954138286651284327, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_3kcompat.h": 5377305123299457337, + "venv/lib/python3.13/site-packages/pandas/core/array_algos/transforms.py": 3773159138960141434, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_generator_mt19937_regressions.py": 8981142329261965538, + "venv/lib/python3.13/site-packages/anyio/functools.py": 4300781896135050327, + "venv/lib/python3.13/site-packages/greenlet/greenlet_msvc_compat.hpp": 12504008513945919250, + "venv/lib/python3.13/site-packages/jeepney/tests/test_fds.py": 6257376331171761928, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_array_utils.py": 14682148205379122638, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_matmul.py": 16169282225324014901, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_csv.py": 6678500392137188521, + "venv/lib/python3.13/site-packages/pandas/_typing.py": 17795648994662434211, + "venv/lib/python3.13/site-packages/cffi/_cffi_include.h": 13094899814611950155, + "venv/lib/python3.13/site-packages/iniconfig/__init__.py": 12623265899068631092, + "venv/lib/python3.13/site-packages/greenlet/tests/fail_switch_three_greenlets.py": 10923534235697247269, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7592/endpoint.py": 18260428408051728802, + "venv/lib/python3.13/site-packages/pandas/tests/test_optional_dependency.py": 7930709646653025355, + "venv/lib/python3.13/site-packages/numpy/f2py/symbolic.py": 16854377502491291093, + "venv/lib/python3.13/site-packages/itsdangerous/__init__.py": 6969210114978365656, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_numpy_compat.py": 9112925138440787555, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_asfreq.py": 12721047184483392773, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_parse_dates.py": 7597935994945336408, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_readlines.py": 15928258358116399067, + "venv/lib/python3.13/site-packages/pandas/io/sas/__init__.py": 8095743504390517992, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/mypy/plugin.py": 16042078006552521613, + "venv/lib/python3.13/site-packages/PIL/_version.py": 3025206805667405876, + "backend/tests/test_dashboards_api.py": 2278794209397518464, + "frontend/build/_app/immutable/chunks/DSTrsXYC.js": 7992933864369085731, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_block_internals.py": 17935662848672401507, + "venv/lib/python3.13/site-packages/pydantic/_internal/_repr.py": 1758478941817868344, + "venv/lib/python3.13/site-packages/cryptography/hazmat/decrepit/ciphers/algorithms.py": 2375452080667815941, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_file_handling.py": 732772718210067599, + "frontend/build/_app/immutable/nodes/35.ClWaxdrE.js": 18139182329852451741, + "venv/lib/python3.13/site-packages/_pytest/legacypath.py": 7512065138259788426, + "venv/lib/python3.13/site-packages/pydantic/deprecated/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/base/test_transpose.py": 1686867045869336370, + "venv/lib/python3.13/site-packages/pip/_vendor/msgpack/ext.py": 15699809032725838985, + "venv/lib/python3.13/site-packages/cryptography/utils.py": 18088508841978595788, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_integer.f90": 8308927438994838783, + "frontend/build/_app/immutable/nodes/4.CzF7b7T3.js": 5413823557660901896, + "venv/lib/python3.13/site-packages/passlib/handlers/roundup.py": 3844280972451753019, + "venv/lib/python3.13/site-packages/pygments/lexers/graphviz.py": 15677908982985025500, + "venv/lib/python3.13/site-packages/numpy/lib/recfunctions.pyi": 10487441680587530350, + "venv/lib/python3.13/site-packages/_pytest/junitxml.py": 5871715626272366062, + "venv/lib/python3.13/site-packages/cffi/lock.py": 13507686939203117974, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_astype.py": 4975959187621745360, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_dtypes.py": 13065857166166716628, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/filter.py": 16846063190466267618, + "venv/lib/python3.13/site-packages/pandas/io/json/__init__.py": 12333505155070172626, + "venv/lib/python3.13/site-packages/packaging/licenses/_spdx.py": 16440031845441362300, + "backend/src/models/auth.py.bak": 7050478675360007060, + "venv/lib/python3.13/site-packages/_pytest/pytester_assertions.py": 1899505062406979663, + "venv/lib/python3.13/site-packages/pydantic/v1/generics.py": 1150594223414883339, + "venv/lib/python3.13/site-packages/starlette/_utils.py": 8384087194580626069, + "venv/lib/python3.13/site-packages/httpcore/_sync/interfaces.py": 1532245423878428608, + "venv/lib/python3.13/site-packages/pandas/_libs/window/indexers.pyi": 9911899303567570646, + "backend/src/core/auth/config.py": 8065625913451826937, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/nditer.py": 10038360623032425884, + "venv/lib/python3.13/site-packages/pygments/lexers/actionscript.py": 8829560731160428621, + "venv/lib/python3.13/site-packages/referencing/typing.py": 8296636464219681747, + "venv/lib/python3.13/site-packages/rapidfuzz/_common_py.py": 15410157970973169427, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/char.pyi": 13143560686049627445, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_date_range.py": 16128552513324017349, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/provider.py": 16463243654123731993, + "venv/lib/python3.13/site-packages/numpy/lib/_version.pyi": 10804755840730584157, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/test_missing.py": 1963705411068820866, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/exc.py": 1448374203226983757, + "backend/src/models/__tests__/test_report_models.py": 14916807060334836854, + "venv/lib/python3.13/site-packages/pip/_internal/models/installation_report.py": 14534544543677907640, + "venv/lib/python3.13/site-packages/keyring/testing/util.py": 11195868112805409554, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/sphinxext.py": 2403717774564798878, + "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/licenses/LICENSE.rst": 5960743202700406649, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/ndarrayobject.h": 7320510589635636690, + "venv/lib/python3.13/site-packages/numpy/_core/lib/npy-pkg-config/npymath.ini": 1071371630695963467, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/util.cpython-313-x86_64-linux-gnu.so": 12473903676666680134, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_dialect.py": 4700488504252803159, + "venv/lib/python3.13/site-packages/greenlet/TStackState.cpp": 16720079760074325527, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_indexing.py": 546024765756472196, + "venv/lib/python3.13/site-packages/jose/backends/__init__.py": 16581763658239070112, + "venv/lib/python3.13/site-packages/numpy/ctypeslib/_ctypeslib.py": 12636954896699095212, + "venv/lib/python3.13/site-packages/git/refs/__init__.py": 16246686106073400519, + "venv/lib/python3.13/site-packages/pydantic/deprecated/tools.py": 16466182352694741629, + "venv/lib/python3.13/site-packages/greenlet/slp_platformselect.h": 10428201768098464386, + "venv/lib/python3.13/site-packages/numpy/ma/testutils.pyi": 3899348061346041413, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/test_constructors.py": 9478848417896027917, + "venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER": 17282701611721059870, + "backend/src/plugins/llm_analysis/__tests__/test_service.py": 9850768293526486431, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/formatters/terminal256.py": 11473927781167132440, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi": 2777456956260371820, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_nep50_promotions.py": 6318578947214302182, + "venv/lib/python3.13/site-packages/pandas/core/ops/dispatch.py": 7041711677358147534, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_array_ops.py": 6709502157317299163, + "venv/lib/python3.13/site-packages/pip/_internal/self_outdated_check.py": 991029278889407558, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/baked.py": 6911138208603240867, + "frontend/src/lib/components/reports/ReportsList.svelte": 15460090587871611258, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/aead.pyi": 13061638961405460334, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/cache.py": 3177663126928467950, + "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/RECORD": 17594742868855466393, + "venv/lib/python3.13/site-packages/psycopg2/tz.py": 9966680685796585198, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/index_tricks.pyi": 6035507586990711471, + "venv/lib/python3.13/site-packages/pandas/tests/api/test_api.py": 13385690673368400635, + "venv/lib/python3.13/site-packages/uvicorn/middleware/message_logger.py": 14825516604555033580, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/bitwise_ops.pyi": 751490187478960891, + "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/METADATA": 3474626408138844944, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_info.py": 10615285239043745712, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_partial_slicing.py": 16219611534574666182, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_reduction.py": 1049551473665255648, + "venv/lib/python3.13/site-packages/sqlalchemy/pool/__init__.py": 4337679908168515343, + "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/WHEEL": 16097436423493754389, + "backend/src/api/routes/__init__.py": 9441391336360835840, + "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/RECORD": 6187512110158566509, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_delitem.py": 14336697139511118920, + "backend/src/services/clean_release/__tests__/test_compliance_orchestrator.py": 6332087444349093252, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/_numpyconfig.h": 18150055639397773687, + "venv/lib/python3.13/site-packages/pygments/lexers/hdl.py": 6772823608124143123, + "venv/lib/python3.13/site-packages/pygments/lexers/bqn.py": 11823648528816858738, + "venv/lib/python3.13/site-packages/git/objects/submodule/util.py": 8621897576747443758, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/timedeltas.pyi": 3783371827926657291, + "venv/lib/python3.13/site-packages/numpy/random/mtrand.cpython-313-x86_64-linux-gnu.so": 16146459667121787202, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/isocintrin/isoCtests.f90": 6208802899442270399, + "venv/lib/python3.13/site-packages/rapidfuzz/__pyinstaller/__init__.py": 1229691061011958769, + "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/_toml_compat.py": 10196543528439925412, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/__init__.py": 293004106962245584, + "venv/lib/python3.13/site-packages/pyasn1/type/useful.py": 10313387611927801339, + "venv/lib/python3.13/site-packages/numpy/lib/_shape_base_impl.py": 16539612003028676816, + "venv/lib/python3.13/site-packages/jsonschema-4.25.1.dist-info/licenses/COPYING": 15270149301471737292, + "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/_implementation.py": 12245524932757110911, + "venv/lib/python3.13/site-packages/cryptography/x509/ocsp.py": 17973800752927680404, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-log10.csv": 13441294827017400897, + "venv/lib/python3.13/site-packages/pandas/tests/generic/test_generic.py": 1297407268239966429, + "venv/lib/python3.13/site-packages/pycparser/ply/yacc.py": 4218889705926129841, + "venv/lib/python3.13/site-packages/jeepney/tests/test_bus_messages.py": 4323093138307306385, + "venv/lib/python3.13/site-packages/pip/_internal/metadata/importlib/_dists.py": 16746363359320577809, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_defchararray.py": 15135904718007443306, + "venv/lib/python3.13/site-packages/pygments/lexers/dotnet.py": 17970884907837213099, + "frontend/src/lib/components/translate/CorrectionCell.svelte": 11242772726843637793, + "venv/lib/python3.13/site-packages/_pytest/outcomes.py": 12782895027812738142, + "venv/lib/python3.13/site-packages/pandas/io/parsers/__init__.py": 11752833703934347122, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/coercions.py": 5112358449376598662, + "backend/src/core/task_manager/__init__.py": 15991802770215124472, + "venv/lib/python3.13/site-packages/passlib/handlers/sha2_crypt.py": 8171587560325812664, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_resolution.py": 9651416588266430520, + "venv/lib/python3.13/site-packages/numpy/core/defchararray.py": 11371045148957547088, + "venv/lib/python3.13/site-packages/pip/_internal/cli/base_command.py": 10660882478126415399, + "backend/src/services/clean_release/__tests__/test_preparation_service.py": 4958420988131940633, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_sorted.py": 1899608688401238388, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_update_delete.py": 1317337950621091021, + "venv/lib/python3.13/site-packages/pandas/_libs/join.pyi": 369262334342439364, + "venv/lib/python3.13/site-packages/pygments/formatters/bbcode.py": 8614391937525677922, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/schema.py": 8358664670620586363, + "frontend/src/components/llm/__tests__/provider_config.integration.test.js": 12605115626277870995, + "venv/lib/python3.13/site-packages/pandas/tests/computation/test_compat.py": 11655000938111420543, + "frontend/build/_app/immutable/nodes/32.BQVcp1Fc.js": 17523113223503452911, + "venv/lib/python3.13/site-packages/greenlet/_greenlet.cpython-313-x86_64-linux-gnu.so": 6036818030377883416, + "venv/lib/python3.13/site-packages/authlib/integrations/django_client/integration.py": 13610682193839598457, + "venv/lib/python3.13/site-packages/_pytest/faulthandler.py": 13329320306353718649, + "venv/lib/python3.13/site-packages/pandas/_libs/hashtable.pyi": 15034917167424979097, + "backend/src/services/clean_release/repositories/artifact_repository.py": 9040122866024534865, + "venv/lib/python3.13/site-packages/pygments/cmdline.py": 15628499088643246809, + "frontend/src/components/Toast.svelte": 340258331315696736, + "venv/lib/python3.13/site-packages/pygments/lexers/matlab.py": 1880915927722177654, + "venv/lib/python3.13/site-packages/numpy/f2py/func2subr.py": 338595621218646492, + "venv/lib/python3.13/site-packages/numpy/polynomial/_polytypes.pyi": 7528777458251688517, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_setops.py": 8622608042929141013, + "venv/lib/python3.13/site-packages/requests/utils.py": 17045260098091371478, + "frontend/src/routes/admin/settings/llm/+page.svelte": 8015678318492837753, + "frontend/build/_app/immutable/nodes/29.BgDRJ3OZ.js": 14521731666892374966, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/json.py": 15373167408891671190, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/filepost.py": 16698380375728063923, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh18335.f90": 1126438609189294255, + "frontend/src/components/Footer.svelte": 17748559656923032, + "backend/src/services/clean_release/repositories/policy_repository.py": 10990781278715170404, + "venv/lib/python3.13/site-packages/jsonschema/protocols.py": 847332300543820834, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_csv.py": 571991535656576735, + "venv/lib/python3.13/site-packages/_pytest/_code/source.py": 9488112979192923739, + "venv/lib/python3.13/site-packages/websockets/auth.py": 499195617702591757, + "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_masked_matrix.py": 1444179242134203344, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/test_timedeltas.py": 4118671843063545221, + "backend/src/services/clean_release/dto.py": 11031410734734770552, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/scalars.pyi": 17683675356659300769, + "venv/lib/python3.13/site-packages/more_itertools/__init__.py": 9858612105173082060, + "venv/lib/python3.13/site-packages/websockets/asyncio/async_timeout.py": 7267209982032642028, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/__init__.py": 13841777369501476946, + "venv/lib/python3.13/site-packages/passlib/context.py": 10362592407593745107, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_append.py": 17016125916761365423, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Prefix_py.py": 17041764258638228993, + "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_openssl.py": 10446441599800965961, + "venv/lib/python3.13/site-packages/numpy/ma/tests/test_mrecords.py": 15952147778503828707, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_longdouble.py": 9337218715436213122, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_reduction.py": 2671872764983553906, + "backend/src/api/routes/assistant/_dispatch.py": 3811422882986895565, + "venv/lib/python3.13/site-packages/_pytest/_io/__init__.py": 1510478951261938348, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/frequencies/test_freq_code.py": 13758353172962109140, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/wait.py": 6196757964348666082, + "venv/lib/python3.13/site-packages/pycparser/ply/__init__.py": 5040371336745852622, + "backend/src/services/profile_service.py": 10016788771873430731, + "venv/lib/python3.13/site-packages/numpy/f2py/__init__.pyi": 3566887042414273892, + "venv/lib/python3.13/site-packages/pygments/lexers/textfmts.py": 17438453283146671997, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/constant_time.py": 2499026008403328245, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_both.f90": 13943066317159236257, + "venv/lib/python3.13/site-packages/fastapi/_compat/v2.py": 15078491685917047095, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_casting_floatingpoint_errors.py": 4301355720042319835, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/requests.py": 1102963893193914979, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/offsets.pyi": 13173581775000480811, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/cast.py": 342228968652072052, + "venv/lib/python3.13/site-packages/numpy/core/getlimits.py": 14257687540182552408, + "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/licenses/LICENSE": 16499431172938528491, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/_initialize_cpp.cpython-313-x86_64-linux-gnu.so": 7490281502544276964, + "venv/lib/python3.13/site-packages/numpy/_core/multiarray.py": 4484782354376574355, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_apply.py": 11248068903302454630, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_twodim_base.py": 17623133613018058380, + "venv/lib/python3.13/site-packages/psycopg2/pool.py": 8855184580138330938, + "venv/lib/python3.13/site-packages/pip/_vendor/tomli/py.typed": 9796674040111366709, + "venv/lib/python3.13/site-packages/numpy/f2py/rules.py": 9322914583460630877, + "venv/lib/python3.13/site-packages/starlette-0.50.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/packaging/__init__.py": 5479277484104486860, + "venv/lib/python3.13/site-packages/pip/_vendor/truststore/_ssl_constants.py": 14834514243239777933, + "venv/lib/python3.13/site-packages/numpy/random/_examples/numba/extending.py": 16515485226251040454, + "venv/lib/python3.13/site-packages/numpy/ma/LICENSE": 6723325434476453127, + "venv/lib/python3.13/site-packages/websockets/extensions/permessage_deflate.py": 12957105912018691390, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_groupby_shift_diff.py": 1210657479716368827, + "venv/lib/python3.13/site-packages/passlib/tests/test_apps.py": 10454136535890937237, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/type_api.py": 10299750173867399533, + "venv/lib/python3.13/site-packages/pycparser/lextab.py": 16045770806700381661, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Hamming.pyi": 4432290176776357346, + "venv/lib/python3.13/site-packages/cffi/parse_c_type.h": 9889309594462820068, + "venv/lib/python3.13/site-packages/pandas/_libs/algos.cpython-313-x86_64-linux-gnu.so": 15706331654215762588, + "venv/lib/python3.13/site-packages/yaml/events.py": 745772917931702910, + "venv/lib/python3.13/site-packages/numpy/tests/test_reloading.py": 17532116685329953400, + "venv/lib/python3.13/site-packages/charset_normalizer/__init__.py": 12412600648990392633, + "backend/src/core/utils/superset_context_extractor/_parsing.py": 17093712504833649098, + "venv/lib/python3.13/site-packages/pandas/core/groupby/generic.py": 4857114071347950833, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/usecols/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_timedeltaindex.py": 2359681317403710563, + "venv/lib/python3.13/site-packages/pandas/core/reshape/api.py": 16364136812252343076, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft201909/vocabularies/core": 10616484054735611337, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/exceptions.py": 7518613450390473990, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh24008.f": 3872245077994936272, + "venv/lib/python3.13/site-packages/fastapi/concurrency.py": 17937688838625807004, + "venv/lib/python3.13/site-packages/pandas/core/_numba/kernels/sum_.py": 7062070517544958546, + "venv/lib/python3.13/site-packages/rsa/common.py": 10921682819204792805, + "venv/lib/python3.13/site-packages/bcrypt/_bcrypt.abi3.so": 8691731321358529264, + "venv/lib/python3.13/site-packages/numpy/_configtool.pyi": 9670892208858157218, + "venv/lib/python3.13/site-packages/pandas/core/array_algos/take.py": 3170415198452493008, + "venv/lib/python3.13/site-packages/pandas/tests/computation/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_base_indexer.py": 3129123098288498633, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_astype.py": 15275720655739710249, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_interval_pyarrow.py": 3849382723890414899, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/generator_pcg64_np121.pkl.gz": 4708588000144933291, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/types.py": 16795741817129898714, + "venv/lib/python3.13/site-packages/PIL/FtexImagePlugin.py": 8196606399875185931, + "venv/lib/python3.13/site-packages/yaml/reader.py": 9873075843010693205, + "venv/lib/python3.13/site-packages/PIL/GribStubImagePlugin.py": 14954946414134984896, + "venv/lib/python3.13/site-packages/httpcore/_models.py": 2501866419898326733, + "frontend/build/_app/immutable/chunks/BIIQCMQD.js": 10069504695005052902, + "backend/src/api/routes/translate/_router.py": 4164547666727936398, + "backend/src/plugins/storage/__init__.py": 5046554941488079204, + "venv/lib/python3.13/site-packages/_pytest/config/argparsing.py": 11156052029258891503, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/_initialize.py": 15511055314565873928, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/_serialization.py": 2011519400081029270, + "venv/lib/python3.13/site-packages/numpy/lib/_nanfunctions_impl.pyi": 12566839271245057531, + "venv/lib/python3.13/site-packages/passlib/handlers/argon2.py": 835074952952017434, + "venv/lib/python3.13/site-packages/starlette/formparsers.py": 9019779181100355515, + "venv/lib/python3.13/site-packages/pandas/tests/series/accessors/test_list_accessor.py": 8613662386779664039, + "venv/lib/python3.13/site-packages/sqlalchemy/connectors/__init__.py": 17194870661869873083, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_common.h": 14732581790650403546, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_indexing.py": 14461606056665485421, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/certs.py": 13366788049827102313, + "venv/lib/python3.13/site-packages/numpy/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_pydatetime.py": 16228315407662560992, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_skiprows.py": 8764515842253490735, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_explode.py": 16195468192763502449, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_data_list.py": 1131520422778420758, + "venv/lib/python3.13/site-packages/pandas/io/excel/_calamine.py": 13354427767660487637, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_routines.py": 4860173574365060471, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/screen.py": 6933591906297023845, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/strategy_options.py": 16342048970907383117, + "venv/lib/python3.13/site-packages/fastapi/dependencies/utils.py": 7687214523082385505, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/mt19937-testset-1.csv": 16349400027062875699, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/filewrapper.py": 17786759398959482432, + "venv/lib/python3.13/site-packages/pydantic/validators.py": 8292819706488319845, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/authorization_code.py": 4958605540994687737, + "venv/lib/python3.13/site-packages/_pytest/pathlib.py": 9719734558816066453, + "venv/lib/python3.13/site-packages/numpy/f2py/_isocbind.py": 15031197044862908440, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/assumed_shape/foo_mod.f90": 5765181406654284736, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/operators.py": 5584982192355398255, + "venv/lib/python3.13/site-packages/pygments/lexers/data.py": 9365024327689738663, + "frontend/src/routes/dashboards/+page.svelte": 7340914202224737248, + "backend/src/services/clean_release/manifest_builder.py": 17216832960644499305, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_liboffsets.py": 14705822637347212540, + "venv/lib/python3.13/site-packages/pip/_internal/commands/show.py": 11839987586497822300, + "venv/lib/python3.13/site-packages/pip/_internal/utils/subprocess.py": 3095012838116670328, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_stringdtype.py": 8381476847196129074, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_dtype.py": 5336980126536664360, + "venv/lib/python3.13/site-packages/six-1.17.0.dist-info/RECORD": 11604408378998955169, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_constructors.py": 6515602441449955627, + "venv/lib/python3.13/site-packages/authlib/oauth1/rfc5849/errors.py": 12935154705443176852, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_missing.py": 6224750552277937853, + "venv/lib/python3.13/site-packages/itsdangerous/encoding.py": 205863946173486958, + "venv/lib/python3.13/site-packages/pydantic/deprecated/parse.py": 819235411736647763, + "venv/lib/python3.13/site-packages/greenlet/TGreenletGlobals.cpp": 17400526028982558937, + "frontend/src/routes/settings/connections/+page.svelte": 276661107137418305, + "venv/lib/python3.13/site-packages/greenlet/tests/test_contextvars.py": 6077482318964702132, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7515/models.py": 17356033861792227817, + "venv/lib/python3.13/site-packages/referencing/retrieval.py": 11921179805977833120, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/resource_protector.py": 3563910821279222695, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/theme.py": 14225878259183479398, + "venv/lib/python3.13/site-packages/idna/core.py": 7725813731640753267, + "venv/lib/python3.13/site-packages/passlib/handlers/django.py": 66638338316838351, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_cov_corr.py": 8117511855230249212, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/_manylinux.py": 12272275001961360101, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/engines.py": 10876954465293959720, + "frontend/build/_app/immutable/nodes/0.Dy6ixPTE.js": 12881323989915888235, + "venv/lib/python3.13/site-packages/numpy/lib/_user_array_impl.pyi": 7171810177801103765, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/aioodbc.py": 12704729841432715246, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/ansi.py": 948249091929588017, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_getlimits.py": 118041882543413669, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/legacy/resolver.py": 2492962412506082234, + "frontend/build/_app/immutable/chunks/DSaq_U9T.js": 18007300971386663499, + "venv/lib/python3.13/site-packages/pandas/tests/reductions/test_reductions.py": 11624188672428941494, + "backend/src/api/routes/dashboards/_listing_routes.py": 1750634467656242702, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/plugin/plugin_base.py": 2880434680251108786, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7519/__init__.py": 16532856661318424631, + "venv/lib/python3.13/site-packages/python_multipart/exceptions.py": 5971058243218049183, + "venv/lib/python3.13/site-packages/pandas/io/parsers/base_parser.py": 15148258424429418380, + "venv/lib/python3.13/site-packages/idna/uts46data.py": 5698404064763638591, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/evaluator.py": 6348022789899831255, + "venv/lib/python3.13/site-packages/starlette/status.py": 16192064443996706569, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/interfaces.py": 4890958419958257958, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_ppc64_aix.h": 8706347224105630468, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/comparisons.pyi": 4158883009780474220, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ndarray_assignability.pyi": 1718610984396277997, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/_typing.py": 17598638528781632158, + "venv/lib/python3.13/site-packages/git/remote.py": 1694919255653049944, + "venv/lib/python3.13/site-packages/jsonschema/tests/test_deprecations.py": 16367195310156359251, + "venv/lib/python3.13/site-packages/smmap/mman.py": 9091562788592406032, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/events.py": 7576713358952064464, + "venv/lib/python3.13/site-packages/jaraco/context/__init__.py": 10975097417056278427, + "venv/lib/python3.13/site-packages/pandas/util/version/__init__.py": 3844072588696074141, + "venv/lib/python3.13/site-packages/pycparser/ast_transforms.py": 9032453625977464506, + "venv/lib/python3.13/site-packages/jsonschema/tests/test_exceptions.py": 15290185665769490169, + "venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/RECORD": 173225030102092319, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7636/__init__.py": 913610541217778747, + "venv/lib/python3.13/site-packages/numpy/_core/__init__.pyi": 2783897020308040960, + "venv/lib/python3.13/site-packages/keyring/compat/properties.py": 1562771115138037410, + "venv/lib/python3.13/site-packages/pluggy/_hooks.py": 6941854999025808196, + "venv/lib/python3.13/site-packages/pip/_internal/operations/check.py": 16650663913976953800, + "venv/lib/python3.13/site-packages/_pytest/tmpdir.py": 10834220332037364219, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_datetimelike.py": 10294379314868108649, + "venv/lib/python3.13/site-packages/numpy/lib/_stride_tricks_impl.py": 5260748170237305627, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/interval/test_arithmetic.py": 13304014482504911016, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_formats.py": 12299393760307251036, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_deprecate.py": 16723865897349422996, + "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/licenses/COPYING": 14459782230785388765, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/adapter.py": 5908944827805063108, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/websockets/legacy/server.py": 5390090919193784186, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_timedeltas.py": 1175883320586860493, + "venv/lib/python3.13/site-packages/fastapi/security/open_id_connect_url.py": 3502444317759870079, + "venv/lib/python3.13/site-packages/keyring/errors.py": 4587094338672775040, + "venv/lib/python3.13/site-packages/PIL/ImagePath.py": 18323293175183057101, + "venv/lib/python3.13/site-packages/PIL/FpxImagePlugin.py": 18245056799205041866, + "venv/lib/python3.13/site-packages/httpcore/_async/http_proxy.py": 13364803556177997526, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/decl_api.py": 5287018911951723034, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_repeat.py": 11360772447747837958, + "venv/lib/python3.13/site-packages/fastapi/security/api_key.py": 6965937678104291169, + "venv/lib/python3.13/site-packages/pip/_internal/vcs/subversion.py": 7423946096970870727, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/nditer.pyi": 33523064068376750, + "venv/lib/python3.13/site-packages/numpy/polynomial/chebyshev.pyi": 5269136485429780038, + "venv/lib/python3.13/site-packages/pandas/core/computation/eval.py": 3176226283265954028, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/io/clipboard/__init__.py": 3656451154829206939, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/ext.py": 8361831054662096695, + "venv/lib/python3.13/site-packages/pygments/styles/staroffice.py": 11694998942433220413, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/__main__.py": 111505188798650285, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_2_complexcompat.h": 5001035774408914877, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/nested_schemas.py": 4114645644496880787, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test__exceptions.py": 12099001631178696189, + "venv/lib/python3.13/site-packages/pandas/_libs/writers.cpython-313-x86_64-linux-gnu.so": 6878855127769203372, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/mock.py": 14010776046610111182, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/tags.py": 254599446317461188, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/test_indexing.py": 1926721846569184112, + "venv/lib/python3.13/site-packages/pip/_vendor/pkg_resources/__init__.py": 1677532855362260338, + "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/licenses/LICENSE": 5669644120528099197, + "venv/lib/python3.13/site-packages/referencing-0.37.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pydantic/_internal/_namespace_utils.py": 5237766366340181382, + "venv/lib/python3.13/site-packages/pygments/styles/algol.py": 3040373291664740876, + "venv/lib/python3.13/site-packages/numpy/core/records.py": 12604652690721760437, + "venv/lib/python3.13/site-packages/numpy/f2py/cb_rules.pyi": 12275242654430570028, + "venv/bin/pyrsa-sign": 15564653706771700451, + "venv/lib/python3.13/site-packages/pygments/styles/solarized.py": 11231143047572616298, + "frontend/src/lib/components/assistant/__tests__/assistant_clarification.integration.test.js": 12258270009984504212, + "frontend/src/lib/components/reports/__tests__/reports_list.ux.test.js": 16252194679528846126, + "frontend/src/lib/components/reports/__tests__/fixtures/reports.fixtures.js": 10647302793388244094, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/numerictypes.pyi": 15565573472138344755, + "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/REQUESTED": 15130871412783076140, + "frontend/src/routes/translate/+page.svelte": 11304227812217935067, + "frontend/src/app.css": 3020720639702954139, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/timeout.py": 1807228452024740476, + "backend/src/plugins/translate/__tests__/test_token_budget.py": 9060506557524997011, + "backend/tests/test_smoke_app.py": 7819901244710945164, + "backend/alembic/versions/8dd0a93af539_drop_deprecated_source_language_column_.py": 3574830605384611294, + "venv/lib/python3.13/site-packages/annotated_types/test_cases.py": 5123395674810472016, + "backend/tests/core/test_migration_engine.py": 7419155921858935534, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/warnings.py": 2552021607611112605, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/entities.py": 8001287806688585081, + "frontend/src/lib/components/assistant/__tests__/assistant_first_message.integration.test.js": 16494645867501045726, + "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/cython/setup.py": 17086801293303202021, + "venv/lib/python3.13/site-packages/httpcore/_async/socks_proxy.py": 9026788710898138090, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_timestamp.py": 12253372102602993034, + "venv/lib/python3.13/site-packages/pydantic/types.py": 785880062468926292, + "venv/lib/python3.13/site-packages/idna/__init__.py": 6489437464172324468, + "venv/lib/python3.13/site-packages/authlib/deprecate.py": 3563885283090710443, + "venv/lib/python3.13/site-packages/authlib/oauth1/client.py": 7314311571224771, + "venv/lib/python3.13/site-packages/_pytest/assertion/util.py": 16957445183087315124, + "venv/lib/python3.13/site-packages/pandas/api/types/__init__.py": 3995030354191071797, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/pg8000.py": 6580512354643849828, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/progress.py": 5951745251426856398, + "venv/lib/python3.13/site-packages/fastapi-0.127.1.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/authlib/oauth2/auth.py": 8455258275234626534, + "venv/lib/python3.13/site-packages/pip/_internal/utils/deprecation.py": 7435269308988385470, + "venv/lib/python3.13/site-packages/git/refs/remote.py": 4391983355222612205, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/spinner.py": 5564789578696016201, + "venv/lib/python3.13/site-packages/pycparser/plyparser.py": 5251740466238035035, + "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py2-objarr.npy": 9694707264403109829, + "venv/lib/python3.13/site-packages/pandas/core/util/hashing.py": 13106575357604888913, + "venv/lib/python3.13/site-packages/dateutil/__init__.py": 17666544837260663800, + "backend/src/core/superset_client/_datasets_preview.py": 16476837380317508068, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/exclusions.py": 15852672500377116297, + "venv/lib/python3.13/site-packages/httpcore/_sync/http11.py": 5947339024588907032, + "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/METADATA": 5045041742469375466, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/keys.pyi": 1846673463183660140, + "venv/lib/python3.13/site-packages/pygments/styles/lovelace.py": 15082819162722685616, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/applicator": 6616723520993329538, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7591/claims.py": 11853156183819186599, + "venv/lib/python3.13/site-packages/h11/_connection.py": 6971907312737339559, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/token.py": 11655733747403312358, + "venv/lib/python3.13/site-packages/itsdangerous-2.2.0.dist-info/WHEEL": 12146818530001975731, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_loc.py": 2127584756555503873, + "venv/lib/python3.13/site-packages/rsa/pkcs1.py": 2576732594642491910, + "venv/lib/python3.13/site-packages/greenlet/tests/_test_extension_cpp.cpp": 4590574206187223887, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/parameter/constant_non_compound.f90": 8048650253889213460, + "venv/lib/python3.13/site-packages/numpy/__config__.pyi": 1422114139050700402, + "venv/lib/python3.13/site-packages/ecdsa/rfc6979.py": 11564966030899162026, + "venv/lib/python3.13/site-packages/cryptography/hazmat/_oid.py": 755042927629281577, + "venv/lib/python3.13/site-packages/idna/compat.py": 9758194415584776667, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_append.py": 13152738905565588661, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_astype.py": 16700461036658228009, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/dynamic.py": 15220116196072524330, + "backend/src/api/routes/git/_repo_routes.py": 7367823490321383813, + "venv/lib/python3.13/site-packages/pandas/_libs/hashing.cpython-313-x86_64-linux-gnu.so": 16124435225170610034, + "venv/lib/python3.13/site-packages/httpcore/_exceptions.py": 6716590711697088732, + "backend/src/models/git.py": 6898938895423263836, + "venv/lib/python3.13/site-packages/numpy/f2py/common_rules.pyi": 2104100025130695295, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/ma/extras.pyi": 15292036493781556342, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/ufunc_config.pyi": 2005481648919369399, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/box.py": 14303526978791498662, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Indel_py.py": 9112851333217812996, + "venv/lib/python3.13/site-packages/pandas/plotting/_misc.py": 10202078355301731720, + "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/hybrid.py": 14287665090144592501, + "venv/lib/python3.13/site-packages/pandas/compat/numpy/__init__.py": 1037113402100731082, + "venv/lib/python3.13/site-packages/pygments/scanner.py": 5640381644804445932, + "venv/lib/python3.13/site-packages/numpy/fft/_pocketfft.pyi": 1521384724035973688, + "venv/lib/python3.13/site-packages/numpy/linalg/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/rsa/asn1.py": 3861968161559322784, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_convert.py": 13349243029437881949, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_setops.py": 16240008859650640717, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_categorical.py": 8844412630511751140, + "venv/lib/python3.13/site-packages/numpy/core/shape_base.py": 17925759824894973734, + "venv/lib/python3.13/site-packages/PIL/FitsImagePlugin.py": 618556384932056120, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/terminal_theme.py": 13727399800263579809, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/type_check.pyi": 9177256991985898824, + "venv/lib/python3.13/site-packages/pandas/core/sparse/__init__.py": 15130871412783076140, + "backend/src/core/plugin_loader.py": 245217431000027879, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7517/key_set.py": 8485970749081951741, + "venv/lib/python3.13/site-packages/apscheduler/schedulers/gevent.py": 4811840397266785551, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_periodindex.py": 13430003945574795792, + "venv/lib/python3.13/site-packages/greenlet/TThreadStateCreator.hpp": 9887867766190459665, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_constructors.py": 9767900500541600486, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/shape_base.pyi": 10291383658131961346, + "venv/lib/python3.13/site-packages/anyio/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/PIL/ImagePalette.py": 9227837918026991107, + "venv/lib/python3.13/site-packages/PIL/ImageSequence.py": 16459886231802763032, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/misc.py": 6571241778569465264, + "frontend/src/lib/components/assistant/AssistantClarificationCard.svelte": 15872391111837089374, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_tolist.py": 8343058000369672317, + "venv/lib/python3.13/site-packages/pydantic_settings-2.13.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_function.py": 7048185308307653546, + "venv/lib/python3.13/site-packages/apscheduler/schedulers/tornado.py": 6491136785917169991, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/unitofwork.py": 3177255974394221645, + "venv/lib/python3.13/site-packages/pip/_internal/commands/cache.py": 1892313355296776523, + "frontend/build/_app/immutable/chunks/BAz4bHMQ.js": 13333198678504989936, + "venv/lib/python3.13/site-packages/jeepney/io/tests/conftest.py": 13381752264278264218, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/assertion.py": 5287436128235488464, + "venv/lib/python3.13/site-packages/numpy/_core/_rational_tests.cpython-313-x86_64-linux-gnu.so": 13475521165331256711, + "venv/lib/python3.13/site-packages/ecdsa/test_sha3.py": 12459459298663975730, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/claims.py": 14794441479740527265, + "venv/lib/python3.13/site-packages/pip/_internal/commands/completion.py": 4435231315212798769, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7516/__init__.py": 11121941644106061453, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_histograms.py": 47631283818191049, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_indexing.py": 9988098331562906950, + "venv/lib/python3.13/site-packages/PIL/_imagingmorph.pyi": 18222325750818585549, + "venv/lib/python3.13/site-packages/pygments/lexers/yang.py": 7409365668425173823, + "venv/lib/python3.13/site-packages/PIL/JpegPresets.py": 12342262642420923471, + "venv/lib/python3.13/site-packages/six.py": 948867418687428421, + "venv/lib/python3.13/site-packages/numpy/lib/tests/data/py3-objarr.npy": 17303060773267383537, + "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/licenses/LICENSE.APACHE": 11041304845352917971, + "venv/lib/python3.13/site-packages/greenlet/greenlet.h": 16216120538647882082, + "venv/lib/python3.13/site-packages/dotenv/version.py": 13508433229287636267, + "venv/lib/python3.13/site-packages/attr/filters.pyi": 13942974755090135724, + "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/astype_copy.pkl": 4470155972891419163, + "venv/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/_impl.py": 16610770935842519289, + "venv/lib/python3.13/site-packages/pandas/core/interchange/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/fastapi/dependencies/models.py": 8626894872248212015, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/__main__.py": 487896753130729554, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/concat/test_categorical.py": 18254320571347851153, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/conftest.py": 16571782049241458769, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/test_reductions.py": 8258332365408303156, + "venv/lib/python3.13/site-packages/numpy/polynomial/hermite_e.py": 3654583356259850174, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_rolling.py": 18221459425751198218, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/common.py": 10996276696597806957, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/strings.pyi": 15361498560917514616, + "venv/lib/python3.13/site-packages/psycopg2/errorcodes.py": 6711830404954314463, + "venv/lib/python3.13/site-packages/keyring/backends/null.py": 12740996416911604870, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/datonly.f90": 10495925225338237742, + "venv/lib/python3.13/site-packages/greenlet/tests/fail_cpp_exception.py": 11851283355840567373, + "venv/lib/python3.13/site-packages/websockets/legacy/auth.py": 16685128086396181286, + "venv/lib/python3.13/site-packages/keyring/cli.py": 10996746519742056101, + "venv/lib/python3.13/site-packages/pluggy/_callers.py": 1042673447718196223, + "venv/lib/python3.13/site-packages/pillow.libs/libzstd-761a17b6.so.1.5.7": 17875236748140157032, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_to_dict_of_blocks.py": 11646835537993064324, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge.py": 11366935134260232329, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_store.py": 1500300322083708794, + "venv/lib/python3.13/site-packages/yaml/error.py": 5626374410579058436, + "venv/lib/python3.13/site-packages/yaml/parser.py": 18021832376874455801, + "venv/lib/python3.13/site-packages/starlette/__init__.py": 3252794766897205870, + "frontend/src/routes/datasets/review/__tests__/dataset_review_entry.ux.test.js": 5439236328736424365, + "venv/lib/python3.13/site-packages/pandas/_libs/tslib.cpython-313-x86_64-linux-gnu.so": 10742615805788605480, + "venv/lib/python3.13/site-packages/git/__init__.py": 15867730798565725003, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_tz_localize.py": 18409231586451494605, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_data.py": 2397937327057341, + "venv/lib/python3.13/site-packages/pydantic/_internal/_validate_call.py": 4078236486720616395, + "venv/lib/python3.13/site-packages/httpx/_models.py": 6516278895118988231, + "venv/lib/python3.13/site-packages/jose/backends/_asn1.py": 14123487576864105793, + "backend/src/services/reports/normalizer.py": 11408466232944555336, + "venv/lib/python3.13/site-packages/jsonschema_specifications/__init__.py": 5732822354953774657, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_converters.py": 8869440774594127419, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/session.py": 5126234270435358377, + "venv/lib/python3.13/site-packages/keyring/__init__.py": 991260689223167957, + "venv/lib/python3.13/site-packages/numpy/polynomial/tests/test_legendre.py": 18266595775500927868, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/manifest.py": 16998683210177680416, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/gh25286.pyf": 14358606965464506398, + "venv/lib/python3.13/site-packages/pandas/_config/config.py": 1494288055210911451, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timestamp/methods/test_round.py": 1299086932398824299, + "venv/lib/python3.13/site-packages/pydantic/v1/types.py": 10761779263335288559, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/numerictypes.pyi": 15443101937711720707, + "venv/lib/python3.13/site-packages/numpy/f2py/_isocbind.pyi": 5786150108623563787, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/conftest.py": 3691712173202219606, + "venv/lib/python3.13/site-packages/pillow-12.1.1.dist-info/zip-safe": 15240312484046875203, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_reindex.py": 15794750188594767026, + "venv/lib/python3.13/site-packages/pip/_internal/commands/help.py": 4175797322922912326, + "venv/lib/python3.13/site-packages/pip/_internal/locations/base.py": 7966628150470073365, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/numeric/test_numeric.py": 9383693732009547935, + "venv/lib/python3.13/site-packages/pydantic/datetime_parse.py": 196119761382305574, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_apply.py": 7511915990531229598, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_compare.py": 3079569478684646491, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/styles/_mapping.py": 11752660241391232497, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_is_unique.py": 3989602816674881215, + "venv/lib/python3.13/site-packages/urllib3-2.6.2.dist-info/RECORD": 15194367076254806710, + "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/METADATA": 6557531107125553027, + "venv/lib/python3.13/site-packages/packaging/_tokenizer.py": 7442392076059866359, + "frontend/src/components/backups/BackupManager.svelte": 15722995371746548624, + "backend/src/api/routes/__tests__/test_git_status_route.py": 6306531435114264696, + "venv/lib/python3.13/site-packages/pip/_internal/utils/entrypoints.py": 11268059714209889385, + "venv/lib/python3.13/site-packages/gitdb-4.0.12.dist-info/LICENSE": 2032205056284080825, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_indexers.py": 6773751569163287047, + "backend/src/plugins/search.py": 888817404897740233, + "venv/lib/python3.13/site-packages/pygments/lexers/_scilab_builtins.py": 17453812809700086888, + "frontend/src/routes/profile/__tests__/profile-preferences.integration.test.js": 6561454515118560091, + "venv/lib/python3.13/site-packages/pandas/tests/strings/test_cat.py": 15988884721768567895, + "venv/lib/python3.13/site-packages/pygments/formatters/_mapping.py": 13335227913146542481, + "logs/app.log.1": 15453131390328548426, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_subclass.py": 13490541871827928531, + "venv/lib/python3.13/site-packages/keyring/testing/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/anyio/_core/_eventloop.py": 6853955946028131785, + "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/_typing/_dtype_like.py": 1598511035344858524, + "venv/lib/python3.13/site-packages/anyio/_backends/_trio.py": 4764555436057754985, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/padding.py": 11244071766694094554, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/shape.pyi": 2188930129299348535, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_assumed_shape.py": 18361009188431483164, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_object.py": 9215781825358309147, + "venv/lib/python3.13/site-packages/fastapi/testclient.py": 2606236077497278662, + "venv/lib/python3.13/site-packages/_pytest/doctest.py": 6126919596776592278, + "venv/lib/python3.13/site-packages/pydantic/_internal/_mock_val_ser.py": 12265320534388472158, + "venv/lib/python3.13/site-packages/numpy/_core/multiarray.pyi": 3494871767602648807, + "venv/lib/python3.13/site-packages/authlib/integrations/sqla_oauth2/functions.py": 14107377669163193871, + "venv/lib/python3.13/site-packages/pandas/io/sql.py": 12592024569011796790, + "venv/lib/python3.13/site-packages/yaml/nodes.py": 3463622922914760653, + "venv/lib/python3.13/site-packages/uvicorn-0.40.0.dist-info/WHEEL": 7454950858448014158, + "venv/lib/python3.13/site-packages/anyio/_core/_signals.py": 1204322949639513552, + "venv/lib/python3.13/site-packages/pygments/formatters/__init__.py": 8009871891954370829, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/dtypes/test_empty.py": 6845813091876023610, + "frontend/src/routes/dashboards/[id]/+page.svelte": 1225758611896418118, + "frontend/src/routes/settings/+page.svelte": 3103346197225933395, + "venv/lib/python3.13/site-packages/authlib/jose/drafts/_jwe_enc_cryptography.py": 4383854709657749855, + "venv/lib/python3.13/site-packages/numpy/ma/tests/test_subclassing.py": 17318983445227658412, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/bitwise_ops.py": 14350609278188982939, + "venv/lib/python3.13/site-packages/cffi/_cffi_errors.h": 1479575326813300292, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_fillna.py": 13675584998644907265, + "venv/lib/python3.13/site-packages/charset_normalizer/utils.py": 11724147997933584095, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_map.py": 10769974470610467636, + "venv/lib/python3.13/site-packages/pandas/tests/strings/test_string_array.py": 8260883866404398980, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_replace.py": 15992095120302643273, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/common/test_iterator.py": 13869355993837330406, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_stack.py": 5316826578695144945, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_index_tricks.py": 4676293127859087991, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pygments/lexers/lisp.py": 11872331622183590758, + "frontend/src/lib/components/health/HealthMatrix.svelte": 16085178486788467644, + "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_distutils.py": 1948707545068786, + "frontend/src/routes/+layout.svelte": 2748734957389681795, + "backend/src/core/__init__.py": 8673978627093182792, + "backend/src/models/mapping.py": 9358348320550260098, + "venv/lib/python3.13/site-packages/git/repo/fun.py": 9019364012746153312, + "venv/lib/python3.13/site-packages/pandas/_libs/algos.pyi": 22405377601029952, + "venv/lib/python3.13/site-packages/charset_normalizer/md.cpython-313-x86_64-linux-gnu.so": 1835299664481804968, + "venv/lib/python3.13/site-packages/apscheduler/schedulers/base.py": 17992335652981439676, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/modules/gh26920/two_mods_with_no_public_entities.f90": 12296500845667555672, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/suite/test_reflection.py": 10520778598547003532, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/base_class/test_pickle.py": 3419420815386925630, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/PIL/PcdImagePlugin.py": 922809545463280224, + "venv/lib/python3.13/site-packages/numpy/ma/testutils.py": 9355402483340137461, + "venv/lib/python3.13/site-packages/more_itertools/recipes.pyi": 16788810745659802820, + "venv/lib/python3.13/site-packages/pygments/lexers/haskell.py": 8011341851439354724, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_can_hold_element.py": 5406431247116557833, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/mixed/foo.f": 5690596225005490325, + "venv/lib/python3.13/site-packages/jaraco/functools/__init__.py": 15334412919279565528, + "venv/lib/python3.13/site-packages/numpy/linalg/_umath_linalg.cpython-313-x86_64-linux-gnu.so": 17652852308919396403, + "venv/lib/python3.13/site-packages/charset_normalizer/version.py": 9948878594498587774, + "venv/lib/python3.13/site-packages/pandas/core/config_init.py": 16537122488227845090, + "venv/lib/python3.13/site-packages/pygments/lexers/pointless.py": 17552020039609007881, + "frontend/src/routes/login/+page.svelte": 4542360917085342323, + "backend/src/services/clean_release/repositories/approval_repository.py": 15069637305272665109, + "venv/lib/python3.13/site-packages/numpy/core/numeric.py": 17025007229779320230, + "venv/lib/python3.13/site-packages/cffi/pkgconfig.py": 16057517851163939571, + "venv/lib/python3.13/site-packages/numpy/_pyinstaller/tests/__init__.py": 13331622784664204066, + "venv/lib/python3.13/site-packages/pytest_asyncio-1.3.0.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/pandas/tests/resample/test_base.py": 15292769510056833117, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_ccalendar.py": 8819357623337533489, + "venv/lib/python3.13/site-packages/pygments/lexers/eiffel.py": 8964563077187051946, + "venv/lib/python3.13/site-packages/pandas/io/formats/html.py": 11849941990375030601, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/modeline.py": 6194365452131561478, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh25211.f": 1486878017876900890, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_pickle.py": 4216445955812104014, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_modules.py": 16835987029123241300, + "venv/bin/activate": 14735074000123383125, + "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/METADATA": 732041908491732594, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_interval.py": 8549680643865283913, + "venv/lib/python3.13/site-packages/pygments/lexers/nimrod.py": 8457135214131458774, + "venv/lib/python3.13/site-packages/tzlocal/windows_tz.py": 11696765495002954868, + "venv/lib/python3.13/site-packages/jsonschema/tests/__init__.py": 15130871412783076140, + "backend/src/models/dataset_review_pkg/_semantic_models.py": 15376906129805121935, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/live.py": 8586058300967410643, + "venv/lib/python3.13/site-packages/python_multipart/multipart.py": 5677735019292537174, + "backend/src/plugins/translate/__init__.py": 12400577052797809611, + "frontend/build/_app/immutable/nodes/33.BUdbGEpr.js": 7904426271120174786, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/_elements_constructors.py": 9279447066034364952, + "venv/lib/python3.13/site-packages/tzlocal/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/starlette/middleware/httpsredirect.py": 16837082808103815044, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/status_codes.py": 17005213506461710202, + "venv/lib/python3.13/site-packages/pip/_vendor/distlib/__init__.py": 15735839022111230173, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py": 14216912708889351810, + "venv/lib/python3.13/site-packages/numpy/lib/npyio.py": 6782361985454523908, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_callback.py": 1689177048527329209, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/_public_dtype_api_table.h": 8933016749764021181, + "venv/lib/python3.13/site-packages/authlib/oidc/core/grants/__init__.py": 11283047029180207588, + "venv/lib/python3.13/site-packages/pandas/core/computation/check.py": 16048939716341154196, + "venv/lib/python3.13/site-packages/pip/_vendor/idna/uts46data.py": 1867240043777383728, + "venv/lib/python3.13/site-packages/jeepney/io/tests/utils.py": 10346859893683522113, + "venv/lib/python3.13/site-packages/authlib/common/encoding.py": 52296066492753953, + "venv/lib/python3.13/site-packages/pillow.libs/libbrotlidec-b57ddf63.so.1.2.0": 10032590058372320813, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/recarray_from_file.fits": 15116318600514274152, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_api.py": 5813060676255093777, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/constructors.py": 7587476800569414769, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_algos.py": 5356470832688354593, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/json.py": 15085143436056608404, + "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/cython/meson.build": 9642189398934130247, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/base.py": 16891437410462671317, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7519/claims.py": 12645788445743898136, + "frontend/build/_app/immutable/nodes/8.xxEF6bTJ.js": 5561886302919578979, + "venv/lib/python3.13/site-packages/pygments/formatters/svg.py": 11686316844190214147, + "backend/src/plugins/translate/__tests__/__init__.py": 2490542523242946173, + "venv/lib/python3.13/site-packages/pandas/core/indexes/period.py": 1181348384346239378, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/fields.pyi": 15420142722808118826, + "venv/lib/python3.13/site-packages/iniconfig/exceptions.py": 16501270224069836264, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/test_melt.py": 15663807247682146566, + "venv/lib/python3.13/site-packages/pip/_internal/utils/filetypes.py": 4415340223268174913, + "venv/lib/python3.13/site-packages/greenlet/tests/test_leaks.py": 1219835696230215605, + "venv/lib/python3.13/site-packages/greenlet/tests/test_gc.py": 6383305344377882524, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_operators.py": 12686786829906210592, + "venv/lib/python3.13/site-packages/pip/_internal/pyproject.py": 6155748929233245415, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/loading.py": 3460142472461437916, + "venv/bin/keyring": 1488968652276547659, + "backend/src/services/llm_prompt_templates.py": 5706065356933042518, + "venv/lib/python3.13/site-packages/numpy/lib/__init__.py": 12211229657513986410, + "venv/lib/python3.13/site-packages/pydantic/_internal/_decorators.py": 9928398334052202841, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_numeric.py": 9417128508433693658, + "venv/lib/python3.13/site-packages/numpy/_expired_attrs_2_0.py": 8786558894094307341, + "venv/lib/python3.13/site-packages/gitdb/test/test_stream.py": 18159319442241857857, + "venv/lib/python3.13/site-packages/pandas/tseries/frequencies.py": 2262381949755250516, + "backend/src/scripts/seed_superset_load_test.py": 14952302154142898180, + "frontend/build/_app/immutable/chunks/CTFFznDe.js": 8010860292122799172, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/authlib/oidc/core/userinfo.py": 17201158389976619872, + "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/licenses/LICENSE": 4274653168153720331, + "venv/lib/python3.13/site-packages/keyring/credentials.py": 14515496037842724455, + "venv/lib/python3.13/site-packages/pyasn1/codec/cer/encoder.py": 3261739474564948330, + "venv/lib/python3.13/site-packages/httpx/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_fillna.py": 7244491410004453166, + "frontend/src/lib/components/layout/__tests__/test_breadcrumbs.svelte.js": 1250794495161202602, + "backend/src/services/clean_release/compliance_execution_service.py": 3379474749353250488, + "venv/lib/python3.13/site-packages/pygments/styles/trac.py": 13968346768164381833, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_round.py": 9358309072340573675, + "venv/lib/python3.13/site-packages/pyasn1/codec/__init__.py": 15728752901274520502, + "venv/lib/python3.13/site-packages/websockets-15.0.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pydantic/utils.py": 1533455264689378477, + "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pip/_internal/operations/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/libkrb5support-d0bcff84.so.0.1": 16819924515291719993, + "venv/lib/python3.13/site-packages/pygments/lexers/_mapping.py": 12987629396101138833, + "venv/lib/python3.13/site-packages/httpcore/_sync/connection_pool.py": 8159971229245441880, + "venv/lib/python3.13/site-packages/jsonschema/tests/test_jsonschema_test_suite.py": 12733761462141495972, + "venv/lib/python3.13/site-packages/gitdb/exc.py": 13975852664048821278, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arraysetops.pyi": 1782641795267171928, + "venv/lib/python3.13/site-packages/authlib/jose/rfc8037/jws_eddsa.py": 833927568269372333, + "venv/lib/python3.13/site-packages/_pytest/_py/path.py": 8464061867075119556, + "venv/lib/python3.13/site-packages/click/__init__.py": 9261629899056870564, + "venv/lib/python3.13/site-packages/numpy/matrixlib/tests/test_interaction.py": 12216477658994324787, + "venv/lib/python3.13/site-packages/passlib/handlers/sha1_crypt.py": 5752556581809938954, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_reindex.py": 12459492712890114705, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_grouping.py": 5994909693214299971, + "venv/lib/python3.13/site-packages/fastapi/background.py": 16533963020573865222, + "venv/lib/python3.13/site-packages/numpy/random/bit_generator.pxd": 14261507718442469988, + "venv/lib/python3.13/site-packages/pandas/core/arrays/period.py": 11560984428402318984, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/twodim_base.pyi": 3773329770170248877, + "venv/lib/python3.13/site-packages/pandas/tests/window/moments/test_moments_consistency_ewm.py": 12153690217678546798, + "venv/lib/python3.13/site-packages/websockets/version.py": 5853118844880112885, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/memmap.pyi": 15629663272474054909, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_combine_concat.py": 3201068805128232483, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/characteristics.py": 2916810597149066609, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/_psycopg_common.py": 4111797647365870137, + "frontend/src/lib/components/dataset-review/ValidationFindingsPanel.svelte": 2403276296409515448, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/holiday/test_calendar.py": 12313641252472031631, + "backend/src/core/utils/async_network.py": 2763204398131017281, + "venv/lib/python3.13/site-packages/fastapi/security/utils.py": 2616633862993539558, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_recfunctions.py": 16014503284544747867, + "frontend/src/routes/reports/+page.svelte": 7370192460972346131, + "backend/src/core/logger.py": 2069537382060633888, + "venv/lib/python3.13/site-packages/numpy/lib/_histograms_impl.pyi": 6534825471013879522, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/resolvelib/factory.py": 3042005457138956499, + "venv/lib/python3.13/site-packages/numpy/_core/_asarray.pyi": 3940937389249665490, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_pipe.py": 7400355977900310233, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/asyncio/engine.py": 2652248727360250985, + "venv/lib/python3.13/site-packages/websockets/sync/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/psycopg2_binary.libs/liblber-1c9097e2.so.2.0.200": 2651879839738900594, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/f77fixedform.f95": 10898637805043867370, + "venv/lib/python3.13/site-packages/numpy/_core/_multiarray_umath.cpython-313-x86_64-linux-gnu.so": 2888939866336522308, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py": 10093932131797570986, + "venv/lib/python3.13/site-packages/PIL/ImtImagePlugin.py": 17599267755504817169, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_where.py": 3272402211717453881, + "venv/lib/python3.13/site-packages/greenlet/tests/test_greenlet.py": 10727595500186011102, + "venv/lib/python3.13/site-packages/numpy/core/fromnumeric.py": 2136433423902202138, + "venv/lib/python3.13/site-packages/pygments/formatters/rtf.py": 5850574956253747036, + "venv/lib/python3.13/site-packages/pygments/lexers/gsql.py": 15294109558028161389, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_values.py": 8372572954663628844, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_getitem.py": 18202683049446557047, + "venv/lib/python3.13/site-packages/pydantic/_internal/_internal_dataclass.py": 13206778611516171760, + "venv/lib/python3.13/site-packages/yaml/loader.py": 18284689412149643959, + "venv/lib/python3.13/site-packages/packaging/_manylinux.py": 8268418900652790584, + "venv/lib/python3.13/site-packages/packaging/licenses/__init__.py": 15879493360260891293, + "frontend/src/routes/translate/dictionaries/+page.svelte": 11204836397236633754, + "frontend/build/_app/immutable/chunks/C9KBnq-A.js": 13706048316691531557, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_mixed.py": 3476101699679108025, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/callback/gh26681.f90": 1515497186927052106, + "venv/lib/python3.13/site-packages/attr/_cmp.pyi": 14443711863388606665, + "venv/lib/python3.13/site-packages/pip/_internal/commands/index.py": 16715578868094112894, + "venv/lib/python3.13/site-packages/pip/_internal/cli/command_context.py": 9426036980437931533, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_datetimeindex.py": 14816894350435545024, + "venv/lib/python3.13/site-packages/starlette/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_internal/resolution/base.py": 10514193453447954780, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/repr.py": 839417422880281122, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_drop.py": 11341278355685583133, + "venv/lib/python3.13/site-packages/pip/_internal/cli/main_parser.py": 7942770969280622910, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/formatter.py": 16499277113779414101, + "venv/lib/python3.13/site-packages/sqlalchemy/exc.py": 6372950288664776625, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/fixtures/base.py": 10205051944814316388, + "venv/lib/python3.13/site-packages/referencing/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/panel.py": 17832701246248000383, + "venv/lib/python3.13/site-packages/PIL/Hdf5StubImagePlugin.py": 15654153796225833950, + "venv/lib/python3.13/site-packages/pandas/core/window/ewm.py": 12143982379243011357, + "venv/lib/python3.13/site-packages/pandas/core/ops/__init__.py": 4049432353810047365, + "venv/lib/python3.13/site-packages/pygments/styles/perldoc.py": 4857683935715743356, + "venv/lib/python3.13/site-packages/pygments/styles/dracula.py": 12379322008538174078, + "venv/lib/python3.13/site-packages/pygments/lexers/jslt.py": 11805394130669644345, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/cli/hiworld.f90": 6321872653169140461, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_argparse.py": 11111699418217357084, + "venv/lib/python3.13/site-packages/pandas/_libs/ops.pyi": 1192312467209548763, + "venv/lib/python3.13/site-packages/numpy/_core/printoptions.pyi": 16758392990920041462, + "frontend/src/app.html": 10207534912546549080, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_offsets_properties.py": 2340780879864207500, + "frontend/src/lib/Counter.svelte": 4002153662176313030, + "frontend/build/_app/immutable/chunks/B19Q4MUf.js": 8384959676363720729, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_pickle.py": 2910383574326146698, + "backend/src/services/clean_release/__init__.py": 18311663871116449547, + "venv/lib/python3.13/site-packages/anyio/streams/tls.py": 4191898645554875562, + "backend/alembic/script.py.mako": 7041141123739810216, + "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_django.py": 8455054295577203929, + "venv/lib/python3.13/site-packages/PIL/TarIO.py": 13651401276968618561, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/test_to_latex.py": 15077617224435782028, + "venv/lib/python3.13/site-packages/passlib/tests/test_crypto_digest.py": 7988469818593853943, + "venv/lib/python3.13/site-packages/git/objects/util.py": 691018806932355307, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data": 9599129816090399315, + "venv/lib/python3.13/site-packages/pandas/tests/reshape/merge/test_merge_ordered.py": 13827016781743929164, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_util.py": 16024147572807865219, + "frontend/build/_app/immutable/chunks/D7OGuSC7.js": 3496948723181364522, + "venv/lib/python3.13/site-packages/numpy/f2py/cfuncs.py": 10803011906582324302, + "venv/lib/python3.13/site-packages/authlib/integrations/django_oauth2/__init__.py": 12793458753561114666, + "venv/lib/python3.13/site-packages/pydantic_core/core_schema.py": 8106298745123589494, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/strategies.py": 14007486769065345085, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/setitem.py": 9552868454186447925, + "venv/lib/python3.13/site-packages/pip/_internal/operations/build/metadata.py": 2021612995853910808, + "venv/lib/python3.13/site-packages/_pytest/subtests.py": 14573790885130724739, + "venv/lib/python3.13/site-packages/numpy/lib/_ufunclike_impl.pyi": 9854069702366900663, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_isna.py": 18394053826166267096, + "venv/lib/python3.13/site-packages/pygments/lexers/_lua_builtins.py": 12653747099659257203, + "venv/lib/python3.13/site-packages/passlib/utils/decor.py": 21351391767102899, + "venv/lib/python3.13/site-packages/passlib/tests/sample1.cfg": 9597283806285213340, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/common_with_division.f": 13517753924061311074, + "venv/lib/python3.13/site-packages/passlib/crypto/__init__.py": 16409196868630049365, + "venv/lib/python3.13/site-packages/sqlalchemy/util/langhelpers.py": 1246582552828542841, + "venv/lib/python3.13/site-packages/pip/_internal/commands/search.py": 8292218231992728142, + "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/LICENSE": 16174281248426886206, + "venv/lib/python3.13/site-packages/passlib/totp.py": 14590474309565233318, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_to_timestamp.py": 17572983799929378095, + "venv/lib/python3.13/site-packages/h11-0.16.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/WHEEL": 8954358347596196608, + "venv/lib/python3.13/site-packages/gitdb/test/test_base.py": 16305779366785148795, + "venv/lib/python3.13/site-packages/jsonschema/tests/test_utils.py": 8668650648802195016, + "venv/lib/python3.13/site-packages/pydantic/experimental/arguments_schema.py": 8644805798814234457, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/content": 12465123883296161860, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/lib_function_base.pyi": 10972502518588022706, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_ops.py": 14420421526781215500, + "venv/lib/python3.13/site-packages/packaging-26.0.dist-info/WHEEL": 8600534672961461758, + "venv/lib/python3.13/site-packages/pandas/tests/extension/array_with_attr/__init__.py": 15991623390785635773, + "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/fetch.py": 13937019222549208796, + "venv/lib/python3.13/site-packages/jsonschema/tests/typing/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pip/_internal/utils/unpacking.py": 106471890743211548, + "venv/lib/python3.13/site-packages/passlib/handlers/__init__.py": 3424087230285514388, + "venv/lib/python3.13/site-packages/pandas/tests/scalar/timedelta/methods/test_as_unit.py": 4283520188709399244, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/OSA.pyi": 2700568594438441246, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_to_dict.py": 16252265723192124609, + "venv/lib/python3.13/site-packages/numpy-2.4.2.dist-info/licenses/numpy/_core/src/common/pythoncapi-compat/COPYING": 3474104874623144155, + "frontend/build/_app/immutable/chunks/DcQow1yy.js": 691404182357373926, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/serialization/base.py": 16052592982063844259, + "frontend/build/_app/immutable/assets/0.DN6ByAb-.css": 6256222992586029103, + "venv/lib/python3.13/site-packages/pygments/lexers/praat.py": 54226477748516386, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/postgresql/base.py": 6348888156686452825, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Postfix_py.py": 6653895521961110080, + "venv/lib/python3.13/site-packages/anyio/_core/_contextmanagers.py": 1258491775823080427, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/test_frozen.py": 3484963987053884566, + "venv/lib/python3.13/site-packages/pydantic/v1/mypy.py": 17076910404409672245, + "venv/lib/python3.13/site-packages/httpx-0.28.1.dist-info/REQUESTED": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arcsin.csv": 1665171675551692225, + "venv/lib/python3.13/site-packages/pydantic/schema.py": 83238111330132033, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_pickle.py": 8705060420848529055, + "venv/lib/python3.13/site-packages/pygments/lexers/x10.py": 6167378515780115912, + "venv/lib/python3.13/site-packages/_pytest/stash.py": 14430252294763173610, + "venv/lib/python3.13/site-packages/pandas/tests/test_multilevel.py": 10500316376521962061, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/_py_util.py": 12836989066113400538, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9101/discovery.py": 12479996594187326798, + "frontend/src/routes/dashboards/[id]/components/DashboardTaskHistory.svelte": 8800115118935646769, + "venv/lib/python3.13/site-packages/pandas/core/methods/describe.py": 17006836623161521092, + "backend/logs/app.log.4": 11598841362861329809, + "venv/lib/python3.13/site-packages/passlib/handlers/ldap_digests.py": 8658529184965699766, + "venv/lib/python3.13/site-packages/pandas/_libs/ops.cpython-313-x86_64-linux-gnu.so": 10490009128553397250, + "venv/lib/python3.13/site-packages/pygments/lexers/functional.py": 15896849233007178031, + "venv/lib/python3.13/site-packages/pandas/compat/pyarrow.py": 13693379998785127538, + "backend/src/api/routes/dashboards/_projection.py": 1559143255780155174, + "backend/src/services/clean_release/__tests__/test_manifest_builder.py": 9320699981861675127, + "backend/src/services/git/__init__.py": 14011752637167919253, + "backend/src/plugins/llm_analysis/__tests__/test_client_headers.py": 5527172390260032966, + "venv/lib/python3.13/site-packages/pydantic/_internal/_schema_gather.py": 3634138701088855517, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/emath.pyi": 3718540478840842664, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_to_csv.py": 10737444103906389448, + "frontend/src/lib/components/assistant/__tests__/assistant_confirmation.integration.test.js": 12582020574293031526, + "venv/lib/python3.13/site-packages/pandas/tests/tslibs/test_npy_units.py": 8966226962799105383, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_repeat.py": 9155935990114743583, + "venv/lib/python3.13/site-packages/jsonschema/benchmarks/useless_applicator_schemas.py": 8395157526634674143, + "frontend/src/lib/components/dataset-review/__tests__/us2_semantic_workspace.ux.test.js": 3550956860258259058, + "venv/lib/python3.13/site-packages/ecdsa/test_pyecdsa.py": 14542286382742044648, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_inspect.py": 10573558345297891848, + "venv/lib/python3.13/site-packages/apscheduler/executors/asyncio.py": 2230610508870117058, + "backend/src/api/routes/profile.py": 16065196073183170065, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/nattype.pyi": 1830368009811266890, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/arrayprint.pyi": 2053388407376896377, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_head_tail.py": 1906121476322841736, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/object/__init__.py": 15130871412783076140, + "frontend/build/_app/immutable/nodes/27.BcmdCO7Q.js": 17863709917454568143, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/pkcs12.pyi": 7801211423226951223, + "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/METADATA": 5628172948035858892, + "frontend/build/index.html": 12181298379271754142, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/json.py": 6429651405841289922, + "venv/lib/python3.13/site-packages/numpy/matlib.pyi": 8017902013455539981, + "venv/lib/python3.13/site-packages/passlib/handlers/postgres.py": 14162382430051693313, + "venv/lib/python3.13/site-packages/httpcore/_trace.py": 9576253018891841832, + "venv/lib/python3.13/site-packages/rsa/randnum.py": 13958355153634534526, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_size.py": 12449835577388069407, + "venv/lib/python3.13/site-packages/bcrypt/__about__.py": 11196369962175692786, + "backend/tests/services/clean_release/test_approval_service.py": 8359796700245201345, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/index/test_intervalindex.py": 5053113227747333759, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/base.py": 14908084548919567658, + "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/version.py": 14723293207188512766, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py": 8864112842559211971, + "venv/lib/python3.13/site-packages/sqlalchemy-2.0.45.dist-info/METADATA": 9586902338916246692, + "venv/lib/python3.13/site-packages/pip/_vendor/pygments/plugin.py": 18006138132596504101, + "venv/lib/python3.13/site-packages/pip/__init__.py": 9449893869469432503, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py": 5132658725516243311, + "venv/lib/python3.13/site-packages/pip/_internal/metadata/__init__.py": 8924316361489544973, + "venv/lib/python3.13/site-packages/pydantic/v1/json.py": 16596969681914729251, + "venv/lib/python3.13/site-packages/urllib3/poolmanager.py": 15431313180245757657, + "backend/src/core/superset_client/_base.py": 17832258524611728315, + "venv/lib/python3.13/site-packages/_pytest/pastebin.py": 13946273207624603063, + "venv/lib/python3.13/site-packages/smmap/buf.py": 18231775424238352734, + "venv/lib/python3.13/site-packages/numpy/matrixlib/__init__.pyi": 1942236362568669510, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_copy_deprecation.py": 16045426957636349035, + "venv/lib/python3.13/site-packages/numpy/_array_api_info.py": 9709101823494959741, + "venv/lib/python3.13/site-packages/pandas/tests/dtypes/cast/test_find_common_type.py": 9581516171321396333, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/emoji.py": 9166774203918731896, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_drop.py": 15751236355035730554, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_is_monotonic.py": 4626065848596214108, + "venv/lib/python3.13/site-packages/sqlalchemy/future/engine.py": 4639404437703898767, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_select.py": 17823338162428379468, + "venv/lib/python3.13/site-packages/pygments/lexers/perl.py": 17014528337020572681, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_arm32_ios.h": 6751259472945638998, + "venv/lib/python3.13/site-packages/numpy/polynomial/polynomial.pyi": 9935587108964614958, + "venv/lib/python3.13/site-packages/pyasn1-0.6.2.dist-info/zip-safe": 15240312484046875203, + "backend/src/api/routes/git/_merge_routes.py": 2019590524299108754, + "backend/src/api/routes/translate/_run_list_routes.py": 18118346695557887371, + "venv/lib/python3.13/site-packages/uvicorn/protocols/http/h11_impl.py": 1419719055394402145, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/specifiers.py": 12284901056355482991, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/usecols/test_strings.py": 7379299916213794543, + "backend/src/core/logger/__tests__/test_logger.py": 4188397720199242180, + "venv/lib/python3.13/site-packages/pygments/styles/_mapping.py": 11752660241391232497, + "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/METADATA": 6656194161182818439, + "venv/lib/python3.13/site-packages/gitpython-3.1.46.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_array.py": 12514884344165043136, + "backend/src/services/clean_release/__tests__/test_report_builder.py": 17265459530244848426, + "frontend/src/lib/components/translate/TranslationMetricsDashboard.svelte": 10832255927386244176, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimelike_/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/libs/test_libalgos.py": 13934861984871432400, + "backend/src/models/__init__.py": 13280325675752905975, + "frontend/src/routes/tools/mapper/+page.svelte": 11954517841045690204, + "frontend/src/lib/api.js": 12667793754050845906, + "venv/lib/python3.13/site-packages/keyring/backends/macOS/__init__.py": 1010937986753403630, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/mariadb.py": 3109042476851791996, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/associationproxy.py": 7421910137064160936, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_function_base.py": 16624804022675533740, + "venv/lib/python3.13/site-packages/numpy/lib/_stride_tricks_impl.pyi": 9243829615414967057, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/immutabledict.pxd": 13413785818999584986, + "venv/lib/python3.13/site-packages/pip/_internal/req/req_file.py": 5180401483285462901, + "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/auto.py": 12818217385551437286, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py": 10843527833285941674, + "venv/lib/python3.13/site-packages/pandas/tests/strings/test_get_dummies.py": 14871264482622327003, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/relationships.py": 8401569216905291119, + "venv/lib/python3.13/site-packages/numpy/lib/_ufunclike_impl.py": 12251264586530536622, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_setitem.py": 10389770446834773837, + "venv/lib/python3.13/site-packages/sqlalchemy/util/tool_support.py": 4926732415520779988, + "venv/lib/python3.13/site-packages/anyio/_core/_asyncio_selector_thread.py": 1760597191437520000, + "venv/lib/python3.13/site-packages/_pytest/mark/expression.py": 1821437025841201912, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/string_/test_string.py": 3834067244989779682, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/progress_bar.py": 15595912766961891283, + "venv/lib/python3.13/site-packages/pydantic/color.py": 1007422551673129827, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/mt19937-testset-2.csv": 14641856424618845798, + "venv/lib/python3.13/site-packages/uvicorn/protocols/websockets/websockets_impl.py": 15673691977157706075, + "venv/lib/python3.13/site-packages/websockets/asyncio/client.py": 5694225754072131767, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_insert.py": 17383986803728810587, + "venv/lib/python3.13/site-packages/pygments/lexers/roboconf.py": 1335271183806115470, + "backend/src/core/utils/superset_context_extractor/_filters.py": 281666313889333545, + "venv/lib/python3.13/site-packages/_pytest/_version.py": 4890545974654141536, + "venv/lib/python3.13/site-packages/pydantic/v1/schema.py": 10008526732895436361, + "venv/lib/python3.13/site-packages/pandas/tests/io/pytables/test_retain_attributes.py": 16210262356844956764, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/oct_key.py": 11504245602045724, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_sorting.py": 8944017570338511677, + "frontend/src/assets/svelte.svg": 13872699371039797074, + "venv/lib/python3.13/site-packages/numpy/random/_mt19937.pyi": 15904463104068500379, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_reindex_like.py": 13803156447991282315, + "venv/lib/python3.13/site-packages/pip/_internal/req/__init__.py": 12266077985828139928, + "docker/nginx.conf": 16884137323072604795, + "venv/lib/python3.13/site-packages/pandas/core/arrays/integer.py": 7775055011080964709, + "venv/lib/python3.13/site-packages/pydantic/v1/errors.py": 11245135449608452720, + "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/licenses/COPYING": 14459782230785388765, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/test_cython.py": 2933400021016349431, + "venv/lib/python3.13/site-packages/pandas/tests/generic/test_frame.py": 9378623452601244234, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/compat.py": 5871636909854554063, + "venv/lib/python3.13/site-packages/pandas/io/formats/templates/string.tpl": 15796203549130590784, + "venv/lib/python3.13/site-packages/pandas/tests/extension/decimal/array.py": 1919242530821725197, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/cache_key.py": 11118819065204404106, + "venv/lib/python3.13/site-packages/python_dotenv-1.2.1.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/numpy/lib/_arraysetops_impl.py": 5583478355093966990, + "venv/lib/python3.13/site-packages/pandas/tests/extension/date/__init__.py": 16789767433439616593, + "venv/lib/python3.13/site-packages/jeepney/io/common.py": 15160647397460834508, + "venv/lib/python3.13/site-packages/PIL/ImageStat.py": 18442709639217312689, + "frontend/src/services/adminService.js": 6375990585512014277, + "backend/src/plugins/translate/dictionary.py": 5036394504674042587, + "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_odswriter.py": 5134385709728943904, + "venv/lib/python3.13/site-packages/pandas/tests/io/formats/style/test_style.py": 14810883299259169406, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/PIL/TiffTags.py": 8688883943071546469, + "frontend/build/_app/immutable/chunks/CUbMcWr2.js": 11521095389159805757, + "venv/lib/python3.13/site-packages/numpy/lib/_function_base_impl.py": 14034239842770171660, + "venv/lib/python3.13/site-packages/pandas/tests/io/xml/conftest.py": 17514079438846557943, + "frontend/src/routes/dashboards/__tests__/dashboard-profile-override.integration.test.js": 15198772446275187033, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/frequencies/test_inference.py": 5741637509343419942, + "backend/src/core/utils/superset_context_extractor/_templates.py": 6790136732701012567, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/period.cpython-313-x86_64-linux-gnu.so": 14468234464367691795, + "venv/lib/python3.13/site-packages/PIL/_webp.pyi": 18222325750818585549, + "venv/lib/python3.13/site-packages/rapidfuzz/fuzz_py.py": 6428078990080243608, + "venv/lib/python3.13/site-packages/jsonschema/_typing.py": 13152346021901861822, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/__init__.py": 371829636800613137, + "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/RECORD": 18314788057673509075, + "venv/lib/python3.13/site-packages/pip/_vendor/platformdirs/__init__.py": 8880435562116660648, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_asof.py": 15792528637923603240, + "venv/lib/python3.13/site-packages/pydantic/_internal/_config.py": 12760390602912369447, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mssql/pymssql.py": 6650008663791598065, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_to_period.py": 10309599277099605395, + "venv/lib/python3.13/site-packages/pandas/tests/test_take.py": 17628004130770326677, + "venv/lib/python3.13/site-packages/pygments/lexers/tnt.py": 4657130609241407766, + "venv/lib/python3.13/site-packages/pandas/core/array_algos/masked_accumulations.py": 11428556645065711062, + "venv/lib/python3.13/site-packages/greenlet/tests/test_tracing.py": 10227316489696807112, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/object/test_indexing.py": 1947969642791009285, + "venv/lib/python3.13/site-packages/PIL/XbmImagePlugin.py": 12256750478268246137, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ufunclike.py": 12077553320554633586, + "venv/lib/python3.13/site-packages/_pytest/timing.py": 14080119171718125986, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/client_credentials.py": 256154392957334831, + "venv/lib/python3.13/site-packages/numpy/polynomial/legendre.pyi": 2029639875417260268, + "venv/lib/python3.13/site-packages/jsonschema/_types.py": 9250091433305395923, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/errors.py": 14634768861119075506, + "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/_pip_wrapper.py": 2516476609539242190, + "venv/lib/python3.13/site-packages/pandas/tests/window/conftest.py": 10673264126684571493, + "venv/lib/python3.13/site-packages/pygments/lexers/_qlik_builtins.py": 1728058904137960444, + "venv/lib/python3.13/site-packages/dateutil/tzwin.py": 5149446703746204349, + "frontend/src/routes/dashboards/[id]/components/DashboardLinkedResources.svelte": 8279080994517688482, + "backend/src/dependencies.py": 9481683691730170837, + "venv/lib/python3.13/site-packages/websockets/asyncio/compatibility.py": 9716993942839407921, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/style.py": 10665998898253460114, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/categorical/test_warnings.py": 13071210233876253749, + "venv/lib/python3.13/site-packages/PIL/PsdImagePlugin.py": 4347581352716313298, + "venv/lib/python3.13/site-packages/pandas/_testing/_io.py": 14264259716673715185, + "venv/bin/uvicorn": 3776670267111625708, + "venv/lib/python3.13/site-packages/pycparser-2.23.dist-info/WHEEL": 5160964596297665692, + "venv/lib/python3.13/site-packages/numpy/polynomial/_polybase.py": 893743087939984792, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_dialect.py": 6294522482440261756, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_function.py": 14142346897145169685, + "venv/lib/python3.13/site-packages/charset_normalizer/constant.py": 3070316197495651997, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/authorization_server.py": 17967809814827547139, + "venv/lib/python3.13/site-packages/numpy/_core/einsumfunc.py": 11393954913234866059, + "frontend/build/_app/immutable/nodes/31.BSHS4QPz.js": 2044128076272545863, + "backend/src/core/ws_log_handler.py": 7507069843556560249, + "venv/lib/python3.13/site-packages/keyring/backends/chainer.py": 12526770797516553385, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_dtypes.py": 481191723198819568, + "venv/lib/python3.13/site-packages/smmap/test/test_tutorial.py": 16309370344201290582, + "frontend/src/routes/git/+page.svelte": 14169430923974356254, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/concat.py": 14737724508458849795, + "venv/lib/python3.13/site-packages/pandas/tests/series/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/rpds_py-0.30.0.dist-info/METADATA": 7214286447912789067, + "backend/src/api/routes/__tests__/test_connections_routes.py": 18044815559820610056, + "venv/lib/python3.13/site-packages/starlette/middleware/sessions.py": 11719848399661632707, + "venv/lib/python3.13/site-packages/pygments/lexers/lean.py": 14518351619544450286, + "venv/lib/python3.13/site-packages/greenlet/tests/fail_slp_switch.py": 16490710972475154509, + "frontend/src/components/tools/ConnectionForm.svelte": 4060811448046342875, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/poolmanager.py": 5071697837617938064, + "venv/lib/python3.13/site-packages/websockets/legacy/handshake.py": 376095144657994920, + "venv/lib/python3.13/site-packages/apscheduler-3.11.2.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pygments/styles/native.py": 4559566444883341527, + "venv/lib/python3.13/site-packages/anyio/streams/buffered.py": 8916188172110578587, + "venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js": 10530710876604536879, + "venv/lib/python3.13/site-packages/uvicorn/lifespan/on.py": 2866195356090164296, + "venv/lib/python3.13/site-packages/pygments/lexers/_stan_builtins.py": 1961875544235948177, + "venv/lib/python3.13/site-packages/_pytest/_io/terminalwriter.py": 16852767249156780980, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/methods/test_astype.py": 6578539240745332772, + "venv/lib/python3.13/site-packages/greenlet/TMainGreenlet.cpp": 9489520001674089404, + "venv/lib/python3.13/site-packages/websockets/imports.py": 1995356703044158461, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_values.py": 430620320423334461, + "venv/lib/python3.13/site-packages/pycparser/c_ast.py": 5942107079119742, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/type_check.pyi": 1599910480066368464, + "frontend/src/lib/components/reports/__tests__/reports_filter_performance.test.js": 10583581505638859039, + "venv/lib/python3.13/site-packages/passlib/tests/test_context.py": 15195609282700801214, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_lexsort.py": 10394832796613968484, + "venv/lib/python3.13/site-packages/numpy/random/tests/data/pcg64dxsm-testset-1.csv": 9129730764673472086, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ctypeslib.pyi": 8623725566434725526, + "venv/lib/python3.13/site-packages/pandas/tests/generic/test_duplicate_labels.py": 10663105616336167414, + "venv/lib/python3.13/site-packages/httpx/_transports/mock.py": 13564349939188292363, + "venv/lib/python3.13/site-packages/authlib/jose/rfc7518/jwe_algs.py": 2479791018067637690, + "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/testing/print_coercion_tables.pyi": 3814281717779245045, + "venv/lib/python3.13/site-packages/passlib/utils/binary.py": 13123975436674239306, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/getitem.py": 556339939822508169, + "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_xlsxwriter.py": 8445547874832015203, + "venv/lib/python3.13/site-packages/PIL/EpsImagePlugin.py": 1824020964179364491, + "venv/lib/python3.13/site-packages/anyio-4.12.0.dist-info/licenses/LICENSE": 1126906383149772681, + "venv/lib/python3.13/site-packages/numpy/polynomial/_polybase.pyi": 15864712173815045309, + "venv/lib/python3.13/site-packages/authlib-1.6.6.dist-info/RECORD": 7914594107698271488, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/string/scalar_string.f90": 15018631706178918578, + "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/LICENSE": 6989336036499641077, + "frontend/build/_app/immutable/nodes/12.B-jKzEjW.js": 7311098820038156560, + "venv/lib/python3.13/site-packages/PIL/TgaImagePlugin.py": 9208942494595222259, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_tz_localize.py": 13699016868476153393, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/ciphers.pyi": 18143251582901996072, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/tzconversion.cpython-313-x86_64-linux-gnu.so": 15960606468596245469, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_cython.py": 14695721638531081573, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_timedelta64.py": 16821719402662529276, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_copy.py": 12902342280706372953, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/boolean/test_arithmetic.py": 976970179757115407, + "venv/lib/python3.13/site-packages/pygments/lexers/graphics.py": 13783143864729682444, + "venv/lib/python3.13/site-packages/pip/_internal/exceptions.py": 2252826732781519645, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/JaroWinkler.pyi": 9003794318682878317, + "venv/lib/python3.13/site-packages/numpy/_core/tests/examples/limited_api/limited_api1.c": 8494977275215578998, + "venv/lib/python3.13/site-packages/certifi/py.typed": 15130871412783076140, + "frontend/src/lib/components/ui/MultiSelect.svelte": 13967450784969599105, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8628/device_code.py": 6712610002227849281, + "backend/src/core/utils/superset_context_extractor/__init__.py": 1074191108198498747, + "backend/src/core/utils/superset_compilation_adapter.py": 4974836712746468095, + "backend/src/services/clean_release/audit_service.py": 9534298777117548502, + "backend/src/plugins/translate/orchestrator.py": 1317097146823609899, + "venv/lib/python3.13/site-packages/pygments/lexers/elpi.py": 5736689911777977822, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_unary.py": 7128845965976836334, + "venv/lib/python3.13/site-packages/numpy/_core/include/numpy/npy_endian.h": 2761502363895204652, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/string/test_astype.py": 10171766562093740708, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_timeseries_window.py": 1973365560758236222, + "venv/lib/python3.13/site-packages/pygments/lexers/sophia.py": 15971500155124949058, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9068/token.py": 4747545058753492487, + "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_meson.py": 5735043585893007971, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_searchsorted.py": 11161416745923343741, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/_initialize.pyi": 6616985736007157309, + "venv/lib/python3.13/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py": 8539232219382152238, + "venv/lib/python3.13/site-packages/sqlalchemy/event/base.py": 18238453132776154057, + "venv/lib/python3.13/site-packages/ecdsa/keys.py": 10281170640484982154, + "venv/lib/python3.13/site-packages/cffi/setuptools_ext.py": 30796527302561458, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_fileno.py": 15541504502174047167, + "venv/lib/python3.13/site-packages/pydantic_settings/version.py": 17263591404988984480, + "venv/lib/python3.13/site-packages/websockets/legacy/client.py": 4032878587330592478, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/fail/lib_utils.pyi": 12312677132342982866, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6750/token.py": 4161760290367932793, + "venv/lib/python3.13/site-packages/pandas/core/sample.py": 11913688303879867963, + "venv/lib/python3.13/site-packages/pandas/core/indexes/multi.py": 9115751979327393200, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_formats.py": 5259100336852479395, + "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/RECORD": 7810411361439269754, + "venv/lib/python3.13/site-packages/urllib3/util/__init__.py": 2848870790459558599, + "venv/lib/python3.13/site-packages/pandas-3.0.1.dist-info/INSTALLER": 17282701611721059870, + "frontend/build/_app/immutable/chunks/CSKg3QTJ.js": 13514833655854300178, + "venv/lib/python3.13/site-packages/requests/api.py": 12339139862411341381, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_month.py": 14597470286884431085, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_dtype.py": 12210524152630532747, + "venv/lib/python3.13/site-packages/sqlalchemy/types.py": 15819795572817819102, + "venv/lib/python3.13/site-packages/apscheduler/schedulers/qt.py": 12828696264909048448, + "venv/lib/python3.13/site-packages/numpy/f2py/capi_maps.py": 2490579175967301457, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_array_interface.py": 13648006002292914247, + "venv/lib/python3.13/site-packages/pandas/tests/io/test_sql.py": 3901697517087276687, + "venv/lib/python3.13/site-packages/pygments/formatters/terminal.py": 14380579279194052030, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/metrics_cpp_avx2.cpython-313-x86_64-linux-gnu.so": 2991745295485163924, + "backend/src/api/routes/__tests__/test_clean_release_v2_release_api.py": 14275839608126658275, + "backend/src/api/routes/git/_deps.py": 12613530531412985767, + "venv/lib/python3.13/site-packages/pip/__pip-runner__.py": 1138979712722199296, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/data_multiplier.f": 13432803601367981068, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/return_real/foo77.f": 16156304612888138777, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7591/endpoint.py": 13389763148720370546, + "venv/lib/python3.13/site-packages/h11/_receivebuffer.py": 4631433251046338381, + "venv/lib/python3.13/site-packages/pydantic/v1/datetime_parse.py": 10467114625921924095, + "venv/lib/python3.13/site-packages/pygments/lexers/javascript.py": 16869490851285457857, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_period.py": 6255181868181182780, + "venv/lib/python3.13/site-packages/rsa/cli.py": 1753949198467694190, + "venv/lib/python3.13/site-packages/pydantic/deprecated/class_validators.py": 2200511838255373813, + "venv/lib/python3.13/site-packages/pandas/tests/interchange/test_impl.py": 12056958946481531279, + "venv/lib/python3.13/site-packages/PIL/IptcImagePlugin.py": 14329453455296600824, + "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/httpcore/_async/__init__.py": 491192949420297153, + "venv/lib/python3.13/site-packages/_pytest/assertion/__init__.py": 16392348866405910618, + "venv/lib/python3.13/site-packages/urllib3/connection.py": 12761371748350595804, + "venv/lib/python3.13/site-packages/pydantic/v1/fields.py": 3948219773415054006, + "venv/lib/python3.13/site-packages/uvicorn/config.py": 17317823506849880916, + "venv/lib/python3.13/site-packages/pygments/lexers/objective.py": 10966124906622044512, + "venv/lib/python3.13/site-packages/pandas/api/indexers/__init__.py": 17431573013142626187, + "venv/lib/python3.13/site-packages/uvicorn/loops/asyncio.py": 16496916527760969173, + "venv/lib/python3.13/site-packages/click-8.3.1.dist-info/METADATA": 3188324716322823755, + "venv/lib/python3.13/site-packages/authlib/oidc/registration/__init__.py": 9448552677419908109, + "venv/lib/python3.13/site-packages/pycparser/ply/cpp.py": 1665654017894222868, + "backend/src/services/clean_release/enums.py": 7365670144442292671, + "backend/src/plugins/llm_analysis/plugin.py": 13563674795958736723, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_setops.py": 15365064654930699428, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/interval/test_overlaps.py": 2328652590379935311, + "venv/lib/python3.13/site-packages/attr/exceptions.py": 11532465891810816417, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/categorical/test_map.py": 1115471561925426082, + "venv/lib/python3.13/site-packages/authlib/oidc/core/util.py": 5364455058429522633, + "venv/lib/python3.13/site-packages/attrs-25.4.0.dist-info/RECORD": 7363618870628306370, + "venv/lib/python3.13/site-packages/pygments/lexers/erlang.py": 11757064262773184741, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/metrics_cpp.cpython-313-x86_64-linux-gnu.so": 10263790384087091349, + "backend/src/plugins/translate/preview.py": 2758655275240027217, + "frontend/build/_app/immutable/nodes/21.C3rNhw8s.js": 13514883473846080135, + "backend/tests/services/clean_release/test_compliance_task_integration.py": 682622548217611215, + "venv/lib/python3.13/site-packages/cffi/backend_ctypes.py": 2042566833861899211, + "frontend/src/routes/+page.ts": 11160596731849880920, + "backend/tests/test_translate_scheduler_guard.py": 13396608843530344156, + "venv/lib/python3.13/site-packages/pandas/_libs/tslibs/dtypes.cpython-313-x86_64-linux-gnu.so": 657138415665704488, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_indexing.py": 11572911419313573163, + "venv/lib/python3.13/site-packages/pygments/lexers/_css_builtins.py": 17478340487186531268, + "venv/bin/pyrsa-verify": 6762739642629904442, + "backend/src/core/middleware/__init__.py": 13917394130915533209, + "venv/lib/python3.13/site-packages/idna/package_data.py": 6583909243002417298, + "venv/lib/python3.13/site-packages/pygments/lexers/smalltalk.py": 6113431347120223592, + "venv/lib/python3.13/site-packages/pygments/lexers/scdoc.py": 8481749542463754279, + "venv/lib/python3.13/site-packages/cryptography/hazmat/backends/openssl/backend.py": 9072112660082423691, + "venv/lib/python3.13/site-packages/numpy/lib/_array_utils_impl.py": 14446508217220482719, + "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/passlib-1.7.4.dist-info/zip-safe": 15240312484046875203, + "venv/lib/python3.13/site-packages/referencing/__init__.py": 10093824711214801917, + "venv/lib/python3.13/site-packages/pandas/core/arrays/string_.py": 14865733321317939478, + "venv/lib/python3.13/site-packages/pandas/core/arraylike.py": 11439602957100017713, + "venv/lib/python3.13/site-packages/pycparser/ply/ctokens.py": 1599952198962458730, + "frontend/build/_app/immutable/chunks/DXB0WcN8.js": 6620985551681767720, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_day.py": 6371851881186970754, + "venv/lib/python3.13/site-packages/pygments/lexers/algebra.py": 9651893228421050246, + "venv/lib/python3.13/site-packages/anyio/abc/_testing.py": 16484863782452886547, + "venv/lib/python3.13/site-packages/pygments/lexers/modeling.py": 15714077241620129689, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_constructors.py": 12628643543371952431, + "venv/lib/python3.13/site-packages/pandas/_libs/indexing.pyi": 90995502095752726, + "venv/lib/python3.13/site-packages/secretstorage-3.5.0.dist-info/WHEEL": 16097436423493754389, + "venv/lib/python3.13/site-packages/idna/codec.py": 4260241549062101779, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_names.py": 10037208029090713121, + "frontend/build/_app/immutable/nodes/14.Dgx5ZI_m.js": 3914755165925651833, + "venv/lib/python3.13/site-packages/gitdb/db/loose.py": 17467413653016810702, + "venv/lib/python3.13/site-packages/pydantic/version.py": 3699343596421001359, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_index_as_string.py": 13421551616136437100, + "venv/lib/python3.13/site-packages/fastapi/security/base.py": 15674297568104917749, + "venv/lib/python3.13/site-packages/numpy/f2py/_backends/_backend.pyi": 13083543811193933305, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_iloc.py": 16160558158155038350, + "venv/lib/python3.13/site-packages/pygments/lexers/xorg.py": 14591144191879110061, + "backend/src/api/routes/dataset_review_pkg/_dependencies.py": 15939232213193276681, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/styled.py": 8726407298858811635, + "venv/lib/python3.13/site-packages/numpy/random/tests/test_extending.py": 10202091354750153497, + "venv/lib/python3.13/site-packages/PIL/_typing.py": 17407980360703360848, + "venv/lib/python3.13/site-packages/pandas/core/arrays/string_arrow.py": 16997970607998635570, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_numpy_array_equal.py": 2089426719574307767, + "venv/lib/python3.13/site-packages/git/db.py": 6680123567509774609, + "backend/src/api/routes/__tests__/test_clean_release_api.py": 11565478953155085157, + "venv/lib/python3.13/site-packages/authlib/jose/drafts/_jwe_algorithms.py": 15219408648894515493, + "venv/lib/python3.13/site-packages/sqlalchemy/cyextension/processors.pyx": 7085380878820747579, + "venv/lib/python3.13/site-packages/pandas/io/formats/templates/html_style.tpl": 3352086677417301397, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/__init__.py": 3969805865048730851, + "venv/lib/python3.13/site-packages/yaml/_yaml.cpython-313-x86_64-linux-gnu.so": 16990140871649227895, + "frontend/src/lib/components/translate/__tests__/test_bulk_replace_modal.svelte.js": 6802604718120565742, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/regression/incfile.f90": 4348588746493647387, + "venv/lib/python3.13/site-packages/pygments/util.py": 4815334059917765852, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_quarter.py": 6795026338946382805, + "venv/lib/python3.13/site-packages/pydantic_settings/main.py": 17272314000918984790, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/assumed_shape/precision.f90": 5518911932373565642, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_to_numpy.py": 9663734591667845940, + "venv/lib/python3.13/site-packages/pygments/lexers/yara.py": 17510792615476493957, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/_orm_types.py": 5508682532707462217, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Jaro.pyi": 3145980717589050713, + "venv/lib/python3.13/site-packages/pygments/regexopt.py": 16073437303824961930, + "venv/lib/python3.13/site-packages/referencing/tests/test_jsonschema.py": 9815500901482570619, + "venv/lib/python3.13/site-packages/sqlalchemy/sql/default_comparator.py": 47118577003622736, + "venv/lib/python3.13/site-packages/pandas/tests/resample/test_time_grouper.py": 3203729024111501071, + "venv/lib/python3.13/site-packages/_pytest/config/compat.py": 13175516717549204627, + "venv/lib/python3.13/site-packages/pygments/lexers/r.py": 14263133575134645005, + "frontend/src/lib/components/translate/TranslationRunProgress.svelte": 16740229145769290247, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py": 1437580234680265326, + "venv/lib/python3.13/site-packages/apscheduler/job.py": 1858376671986108667, + "venv/lib/python3.13/site-packages/psycopg2/_json.py": 9729420372294281405, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_core_functionalities.py": 10449747865888948579, + "venv/lib/python3.13/site-packages/h11/_headers.py": 7592730288271984585, + "venv/lib/python3.13/site-packages/gitdb/test/__init__.py": 18393574323937353933, + "venv/lib/python3.13/site-packages/_pytest/logging.py": 10972553724755511468, + "frontend/src/routes/datasets/review/[id]/+page.svelte": 2388350237937943327, + "venv/lib/python3.13/site-packages/h11/_util.py": 9362339074207904329, + "venv/lib/python3.13/site-packages/numpy/_core/_operand_flag_tests.cpython-313-x86_64-linux-gnu.so": 12296711931541109624, + "venv/lib/python3.13/site-packages/PIL/GdImageFile.py": 16875784236394545058, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc9207/parameter.py": 2793853093222900337, + "venv/lib/python3.13/site-packages/pandas/tests/frame/test_npfuncs.py": 6132674295193898715, + "frontend/src/lib/api/datasetReview.js": 3023829599261929428, + "venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/core/arrayprint.py": 9652368112174245943, + "venv/lib/python3.13/site-packages/pandas/core/dtypes/base.py": 3628510830969664111, + "venv/lib/python3.13/site-packages/rsa/py.typed": 11041940105507257053, + "venv/lib/python3.13/site-packages/httpx/_exceptions.py": 18399489130669465891, + "backend/src/core/__tests__/test_config_manager_compat.py": 656412918792001580, + "venv/lib/python3.13/site-packages/pandas/tests/io/excel/test_style.py": 10405592363651914796, + "venv/lib/python3.13/site-packages/pygments/lexers/kuin.py": 2818054539831699366, + "venv/lib/python3.13/site-packages/pygments/lexers/ecl.py": 12886695201994337568, + "venv/lib/python3.13/site-packages/pandas/_libs/sas.pyi": 873957906443703977, + "venv/lib/python3.13/site-packages/pydantic/errors.py": 3506829204186508192, + "venv/lib/python3.13/site-packages/pluggy/_result.py": 16790448391766505769, + "venv/lib/python3.13/site-packages/pygments/lexers/dylan.py": 16246299896150112270, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/aggregate/test_numba.py": 1548992314517942530, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/timedeltas/test_constructors.py": 17219764538851313252, + "venv/lib/python3.13/site-packages/pip/_internal/utils/_log.py": 13011536737418204268, + "venv/lib/python3.13/site-packages/sqlalchemy/dialects/mysql/pymysql.py": 12923876736367229454, + "frontend/build/_app/immutable/assets/9.tn0RQdqM.css": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/azure.py": 13529763490186254462, + "venv/lib/python3.13/site-packages/jaraco/functools/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/requests/status_codes.py": 17005213506461710202, + "venv/lib/python3.13/site-packages/pip/_internal/commands/inspect.py": 1401259241736437656, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_validate.py": 2133732492454460539, + "venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/nested_secrets.py": 15691917061715429369, + "venv/lib/python3.13/site-packages/greenlet/greenlet_slp_switch.hpp": 1694714764745718414, + "backend/src/services/dataset_review/semantic_resolver.py": 8447496149691924829, + "venv/lib/python3.13/site-packages/pandas/tests/series/indexing/test_mask.py": 9654897363729130542, + "venv/lib/python3.13/site-packages/pip/_vendor/requests/models.py": 17870758731501836303, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/integer/test_indexing.py": 5063545640498065297, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/auth.py": 13542332344403032059, + "venv/lib/python3.13/site-packages/anyio/_backends/_asyncio.py": 10265081145401348863, + "venv/lib/python3.13/site-packages/pandas/core/nanops.py": 18291534943985310005, + "venv/lib/python3.13/site-packages/pygments/styles/vs.py": 4188729978573652106, + "backend/src/api/routes/assistant/_llm_planner.py": 17830859532452456684, + "backend/src/api/routes/git/_environment_routes.py": 1034235394475003092, + "backend/tests/conftest.py": 14115149798000023183, + "venv/lib/python3.13/site-packages/idna/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/core/array_algos/replace.py": 1708865864686705833, + "venv/lib/python3.13/site-packages/pandas/tests/plotting/test_groupby.py": 2738671407653333726, + "venv/lib/python3.13/site-packages/gitdb/test/test_example.py": 3039934065817753241, + "venv/lib/python3.13/site-packages/pyasn1/type/base.py": 6801504886392649787, + "venv/lib/python3.13/site-packages/tzlocal/__init__.py": 11017072814026497364, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/Postfix.py": 4440659352851541547, + "venv/lib/python3.13/site-packages/numpy/matrixlib/defmatrix.pyi": 17903213294220335051, + "venv/lib/python3.13/site-packages/pydantic/_internal/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_unique.py": 6234921572794573905, + "venv/lib/python3.13/site-packages/pandas/_libs/lib.cpython-313-x86_64-linux-gnu.so": 6452315003728497116, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/default.py": 7906029092844029127, + "venv/lib/python3.13/site-packages/pygments/lexers/templates.py": 16267123844830647955, + "frontend/src/lib/components/layout/Sidebar.svelte": 15884718235284801177, + "venv/lib/python3.13/site-packages/pandas/io/html.py": 2727247063231114153, + "frontend/src/routes/settings/+page.ts": 3719008513710037840, + "backend/src/api/routes/__tests__/test_clean_release_v2_api.py": 13464989331176891726, + "venv/lib/python3.13/site-packages/itsdangerous/timed.py": 12571102606719555007, + "venv/lib/python3.13/site-packages/pandas/core/indexes/accessors.py": 14394872772838047912, + "backend/src/services/clean_release/stages/no_external_endpoints.py": 11041035366569218866, + "venv/lib/python3.13/site-packages/pandas/core/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/rapidfuzz/utils.pyi": 7400725037332728015, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_loadtxt.py": 17562784940619942447, + "venv/lib/python3.13/site-packages/pygments/styles/stata_dark.py": 324072437809944751, + "backend/src/api/routes/assistant/_dataset_review.py": 14508672919199309579, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/multiindex/test_partial.py": 3151286293440416036, + "venv/lib/python3.13/site-packages/pandas/core/strings/object_array.py": 17631047188839635539, + "venv/lib/python3.13/site-packages/sqlalchemy/ext/instrumentation.py": 14882045449667214075, + "venv/lib/python3.13/site-packages/pip/_internal/locations/_sysconfig.py": 4773855595767526182, + "backend/src/plugins/mapper.py": 1970475023222782681, + "venv/lib/python3.13/site-packages/numpy/linalg/tests/test_linalg.py": 2939739913218718132, + "venv/lib/python3.13/site-packages/numpy/polynomial/hermite_e.pyi": 13068936038543812082, + "venv/lib/python3.13/site-packages/pandas/_libs/internals.pyi": 11662356720550765508, + "venv/lib/python3.13/site-packages/pandas/conftest.py": 747626550806435763, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/frequencies/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/period/test_astype.py": 1961831758405941258, + "venv/lib/python3.13/site-packages/PIL/MpegImagePlugin.py": 7726254767528154514, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/timedeltas/test_reductions.py": 14432782234851468060, + "backend/src/services/clean_release/stages/data_purity.py": 775548350590498835, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation": 4826455560768665215, + "venv/lib/python3.13/site-packages/websockets/extensions/base.py": 2457233337788783873, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc7523/__init__.py": 8093984834068317399, + "venv/lib/python3.13/site-packages/pandas/core/indexes/timedeltas.py": 13653393430363556843, + "venv/lib/python3.13/site-packages/pandas/util/_print_versions.py": 15326663505771588740, + "venv/lib/python3.13/site-packages/pandas/tests/construction/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/attr/__init__.py": 14436902698244718082, + "venv/lib/python3.13/site-packages/cffi/api.py": 5325821560978502135, + "backend/src/models/llm.py": 14295626272101666880, + "venv/lib/python3.13/site-packages/requests-2.32.5.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pygments/lexers/email.py": 4355246279485460399, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/test_astype.py": 4023134352880775277, + "venv/lib/python3.13/site-packages/pillow.libs/libxcb-64009ff3.so.1.1.0": 7322566017497612912, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_index_col.py": 17604191644943383567, + "venv/lib/python3.13/site-packages/numpy/f2py/src/fortranobject.h": 5980733638838895780, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/reporters.py": 10760889726586439724, + "venv/lib/python3.13/site-packages/greenlet/platform/switch_x86_unix.h": 13709837360414450184, + "venv/lib/python3.13/site-packages/gitdb/utils/encoding.py": 1668793658272579497, + "venv/lib/python3.13/site-packages/keyring-25.7.0.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/numpy/testing/_private/extbuild.py": 4984721349618127021, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/ndarray_misc.py": 8706265665356094801, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_casting_unittests.py": 18157503533859249155, + "venv/lib/python3.13/site-packages/passlib/handlers/oracle.py": 12959175637015399815, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_join.py": 1697622393195784944, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_value_counts.py": 15633881149834196174, + "venv/lib/python3.13/site-packages/pandas/tests/strings/test_split_partition.py": 2715491071550545667, + "venv/lib/python3.13/site-packages/jsonschema_specifications/_core.py": 14812112544772987164, + "venv/lib/python3.13/site-packages/pandas/core/internals/concat.py": 12151675567885981464, + "venv/lib/python3.13/site-packages/pip/_internal/locations/__init__.py": 8147012724727395488, + "venv/lib/python3.13/site-packages/uvicorn/py.typed": 15240312484046875203, + "venv/lib/python3.13/site-packages/numpy/fft/_helper.pyi": 9546918341939102632, + "venv/lib/python3.13/site-packages/pip/_vendor/packaging/licenses/_spdx.py": 17182188548503720124, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_xs.py": 1897144171324167738, + "venv/lib/python3.13/site-packages/typing_inspection/introspection.py": 13916695705179237047, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/numpy_/test_numpy.py": 9825804096508014886, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/stride_tricks.pyi": 5844806555773292985, + "venv/lib/python3.13/site-packages/passlib/tests/test_handlers_bcrypt.py": 11146468670439284889, + "venv/lib/python3.13/site-packages/more_itertools/recipes.py": 5254043689078173976, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/test_npfuncs.py": 8238385897366878558, + "venv/lib/python3.13/site-packages/pygments/lexers/freefem.py": 9851243865301700231, + "venv/lib/python3.13/site-packages/pygments/lexers/rdf.py": 14488319292387226505, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py": 6067836157632573182, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_asfreq.py": 2701081989348180239, + "venv/lib/python3.13/site-packages/pandas/tests/series/methods/test_dtypes.py": 7550022036887594386, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/segment.py": 2555584120510872966, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_interval_array_equal.py": 15654625365223535344, + "backend/src/services/llm_provider.py": 9619956052011769991, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_ops.py": 16346030307660048773, + "venv/lib/python3.13/site-packages/numpy/core/numerictypes.py": 5776941405348611027, + "venv/lib/python3.13/site-packages/pip/_internal/network/lazy_wheel.py": 12936988990566680659, + "venv/lib/python3.13/site-packages/pandas/core/indexes/datetimes.py": 14331984919364185787, + "backend/src/api/routes/__tests__/test_reports_api.py": 17714266401048048508, + "backend/src/core/migration/archive_parser.py": 8828677257278703059, + "venv/lib/python3.13/site-packages/attr/_version_info.pyi": 7141309436445210354, + "venv/lib/python3.13/site-packages/python_jose-3.5.0.dist-info/WHEEL": 16784970174376303810, + "venv/lib/python3.13/site-packages/pip/_vendor/dependency_groups/_lint_dependency_groups.py": 2609365889369107154, + "venv/lib/python3.13/site-packages/jsonschema_specifications-2025.9.1.dist-info/WHEEL": 2357997949040430835, + "venv/lib/python3.13/site-packages/numpy/_pyinstaller/tests/test_pyinstaller.py": 9430762168079129025, + "venv/lib/python3.13/site-packages/numpy/lib/tests/test_type_check.py": 3935091606923106753, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/datetimes/methods/test_isocalendar.py": 5704842983090753914, + "venv/lib/python3.13/site-packages/PIL/CurImagePlugin.py": 17697303205012181529, + "venv/lib/python3.13/site-packages/PIL/PcfFontFile.py": 8867692961463798279, + "backend/src/plugins/llm_analysis/models.py": 10099813950073320264, + "venv/lib/python3.13/site-packages/ecdsa/numbertheory.py": 13538480862642502330, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/token_endpoint.py": 6118770923176099889, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_iat.py": 3570464615146398178, + "venv/lib/python3.13/site-packages/pandas/tests/series/test_ufunc.py": 669019702416198889, + "venv/lib/python3.13/site-packages/git/objects/tag.py": 7954808535585493300, + "venv/lib/python3.13/site-packages/pip/_internal/metadata/importlib/_envs.py": 5657151394155576509, + "venv/lib/python3.13/site-packages/charset_normalizer/api.py": 9172704759115759638, + "venv/lib/python3.13/site-packages/passlib/tests/test_handlers.py": 4940305787631493030, + "venv/lib/python3.13/site-packages/_pytest/_py/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/io/json/_table_schema.py": 3103776773020058805, + "backend/src/services/clean_release/stages/manifest_consistency.py": 1270636316397468243, + "venv/lib/python3.13/site-packages/numpy/_core/tests/test_indexerrors.py": 4434868847540549873, + "venv/lib/python3.13/site-packages/pandas/tests/copy_view/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc8414/__init__.py": 4781700006554860887, + "venv/lib/python3.13/site-packages/numpy/random/_examples/cython/extending.pyx": 756189645898997788, + "venv/lib/python3.13/site-packages/iniconfig/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/passlib/handlers/scram.py": 12281469744083867450, + "venv/lib/python3.13/site-packages/pandas/plotting/_matplotlib/hist.py": 10928173425078493095, + "venv/lib/python3.13/site-packages/authlib/integrations/flask_oauth2/errors.py": 16711622442922413520, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/reveal/ndarray_shape_manipulation.pyi": 3533739482029983817, + "venv/lib/python3.13/site-packages/pandas/io/parsers/c_parser_wrapper.py": 5841064965672872661, + "venv/lib/python3.13/site-packages/websockets/streams.py": 15786581670248099415, + "venv/lib/python3.13/site-packages/certifi/cacert.pem": 14727832973577663826, + "venv/lib/python3.13/site-packages/h11/__init__.py": 222801976520197308, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/multi/test_constructors.py": 11065058462752831997, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/usecols/test_usecols_basic.py": 16382444980424010514, + "venv/lib/python3.13/site-packages/sqlalchemy/testing/fixtures/mypy.py": 823265492261272631, + "backend/src/core/task_manager/models.py": 749015551222331342, + "backend/src/api/routes/dashboards/_helpers.py": 16683849709430404975, + "venv/lib/python3.13/site-packages/sqlalchemy/orm/exc.py": 3242780937124999391, + "venv/lib/python3.13/site-packages/pandas/core/apply.py": 17080038932032802984, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_reorder_levels.py": 10996225584998355824, + "venv/lib/python3.13/site-packages/pandas/core/arrays/base.py": 664515424699746697, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_quantile.py": 17110581927381599978, + "venv/lib/python3.13/site-packages/numpy/_expired_attrs_2_0.pyi": 1424133074692438241, + "venv/lib/python3.13/site-packages/PIL/MspImagePlugin.py": 3034490726971829439, + "venv/lib/python3.13/site-packages/fastapi/params.py": 14263655860695381481, + "venv/lib/python3.13/site-packages/_pytest/freeze_support.py": 494952714451267295, + "venv/lib/python3.13/site-packages/httpx/_client.py": 1063593889704982038, + "venv/lib/python3.13/site-packages/numpy/f2py/_src_pyf.pyi": 8916922961301555974, + "venv/lib/python3.13/site-packages/keyring/util/platform_.py": 16978095707950463638, + "venv/lib/python3.13/site-packages/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion": 2607512361823362698, + "venv/lib/python3.13/site-packages/pygments/lexers/fift.py": 7537570342576781911, + "frontend/src/services/storageService.js": 14408290095426695791, + "frontend/src/lib/stores/taskDrawer.js": 6659505033581293370, + "frontend/build/_app/immutable/nodes/10.qO4l_hfq.js": 8054490978078371224, + "venv/lib/python3.13/site-packages/ecdsa-0.19.1.dist-info/RECORD": 10552906886624188222, + "venv/lib/python3.13/site-packages/passlib/win32.py": 5311393450489898748, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/ssl_.py": 644667973700652263, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_bool.py": 433342705586552936, + "backend/src/services/resource_service.py": 4718644821742397530, + "venv/lib/python3.13/site-packages/smmap-5.0.2.dist-info/LICENSE": 8177430849046795595, + "venv/lib/python3.13/site-packages/numpy/_core/function_base.pyi": 8343632640078378312, + "venv/lib/python3.13/site-packages/pandas/api/extensions/__init__.py": 882327406374020760, + "venv/lib/python3.13/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py": 2042509334195547606, + "venv/lib/python3.13/site-packages/pandas/tests/io/parser/test_compression.py": 8061288329598682752, + "venv/lib/python3.13/site-packages/pydantic/parse.py": 13750341403702794392, + "venv/lib/python3.13/site-packages/numpy/fft/tests/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/apply/common.py": 1113935558495455492, + "venv/lib/python3.13/site-packages/numpy/_core/umath.py": 6132761780113682667, + "venv/lib/python3.13/site-packages/pygments/lexers/parsers.py": 12372594608244797844, + "backend/src/models/clean_release.py": 6080208142954589647, + "venv/lib/python3.13/site-packages/pandas/_testing/_hypothesis.py": 12298650110193262367, + "venv/lib/python3.13/site-packages/numpy/testing/_private/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/extension/base/casting.py": 12226803454582253097, + "venv/lib/python3.13/site-packages/pandas/core/computation/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/rsa-4.9.1.dist-info/INSTALLER": 17282701611721059870, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/test_groupby_subclass.py": 5540948578269213810, + "venv/lib/python3.13/site-packages/pandas/core/indexing.py": 15507869313676722071, + "venv/lib/python3.13/site-packages/annotated_types-0.7.0.dist-info/METADATA": 7727489762869155085, + "venv/lib/python3.13/site-packages/pandas/tests/io/json/test_pandas.py": 10894229569442891394, + "frontend/build/_app/immutable/chunks/CH5SIC62.js": 11262658403146762189, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/timedeltas/test_scalar_compat.py": 1306236983392218709, + "backend/src/api/routes/__tests__/test_profile_api.py": 1724401885939803766, + "backend/logs/app.log.3": 366149754645105088, + "venv/lib/python3.13/site-packages/numpy/_array_api_info.pyi": 13283968924732680159, + "dist/docker/postgres.v1.0.0-rc2-docker.tar": 5933711120559789473, + "venv/lib/python3.13/site-packages/pygments/lexers/stata.py": 7249891970174867363, + "backend/src/api/routes/translate/_schedule_routes.py": 18180097500694105300, + "venv/lib/python3.13/site-packages/numpy/tests/test_numpy_version.py": 849232015288488204, + "venv/lib/python3.13/site-packages/pandas/tests/arithmetic/test_interval.py": 2613503970235591726, + "venv/lib/python3.13/site-packages/pip/_vendor/urllib3/util/connection.py": 5582413226288987808, + "venv/lib/python3.13/site-packages/pandas/tests/window/test_pairwise.py": 2673370322448160842, + "venv/lib/python3.13/site-packages/idna-3.11.dist-info/WHEEL": 8600534672961461758, + "venv/lib/python3.13/site-packages/numpy/__init__.py": 2331213407497901634, + "venv/lib/python3.13/site-packages/PIL/ImageMath.py": 7395449734007598797, + "venv/lib/python3.13/site-packages/pandas/tests/frame/indexing/test_getitem.py": 8414677531950073373, + "venv/lib/python3.13/site-packages/passlib/crypto/_blowfish/__init__.py": 3521833849634327520, + "venv/bin/pip": 13861749540792881808, + "backend/src/core/task_manager/cleanup.py": 8316318318890394605, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/color_triplet.py": 17264298271771184, + "venv/lib/python3.13/site-packages/fastapi/encoders.py": 1914192268055730299, + "venv/lib/python3.13/site-packages/pygments/lexers/installers.py": 5083244110640899732, + "venv/lib/python3.13/site-packages/pandas/_libs/ops_dispatch.pyi": 16176428602589420913, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_quoted_character.py": 2316632434571797367, + "venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi": 1626154870044880426, + "venv/lib/python3.13/site-packages/cffi-2.0.0.dist-info/licenses/LICENSE": 17675192170670564526, + "venv/lib/python3.13/site-packages/authlib/jose/rfc8037/__init__.py": 8705044848308260480, + "venv/lib/python3.13/site-packages/passlib/tests/__init__.py": 16543763165305017760, + "venv/lib/python3.13/site-packages/rapidfuzz/distance/LCSseq.py": 13853220736499979380, + "venv/lib/python3.13/site-packages/authlib/oauth2/rfc6749/grants/implicit.py": 7379110378465568776, + "venv/lib/python3.13/site-packages/numpy/core/_dtype.py": 2806449741844148191, + "venv/lib/python3.13/site-packages/typing_extensions.py": 8402965285733247527, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/src/crackfortran/data_with_comments.f": 3980077223827767077, + "venv/lib/python3.13/site-packages/jsonschema/tests/test_cli.py": 3173259257428855556, + "venv/lib/python3.13/site-packages/apscheduler/jobstores/etcd.py": 4078354986605069279, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/period/test_indexing.py": 7181087459615188551, + "venv/lib/python3.13/site-packages/fastapi/security/http.py": 8465565118555441408, + "venv/lib/python3.13/site-packages/pandas/tests/groupby/methods/test_is_monotonic.py": 12899848089975919515, + "venv/lib/python3.13/site-packages/sqlalchemy/engine/processors.py": 4817637175145012571, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/color.py": 15062855556511243610, + "venv/lib/python3.13/site-packages/anyio/_core/_streams.py": 6436461519284884601, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/highlighter.py": 10615901258492570740, + "venv/lib/python3.13/site-packages/_pytest/_io/wcwidth.py": 13515196388826152383, + "venv/lib/python3.13/site-packages/click/exceptions.py": 9734955259447933725, + "venv/lib/python3.13/site-packages/pip/_vendor/cachecontrol/wrapper.py": 4633931060151989960, + "venv/lib/python3.13/site-packages/pydantic_core-2.41.5.dist-info/WHEEL": 15858869568085970864, + "venv/lib/python3.13/site-packages/uvicorn/main.py": 5843333502356171120, + "venv/lib/python3.13/site-packages/pygments/lexers/verifpal.py": 9190813166686923928, + "venv/lib/python3.13/site-packages/jeepney/auth.py": 10565040329507087892, + "venv/lib/python3.13/site-packages/pydantic-2.12.5.dist-info/WHEEL": 2357997949040430835, + "frontend/src/lib/components/layout/__tests__/test_sidebar.svelte.js": 1424894894434906249, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_log_render.py": 18076931145915444473, + "venv/lib/python3.13/site-packages/pandas/compat/__init__.py": 6077099417711209303, + "venv/lib/python3.13/site-packages/pandas/io/excel/_odfreader.py": 2760916559416783928, + "venv/lib/python3.13/site-packages/bcrypt/_bcrypt.pyi": 15067467792538416274, + "backend/src/services/rbac_permission_catalog.py": 4811877724682885443, + "venv/lib/python3.13/site-packages/numpy/_utils/_pep440.pyi": 6796322258258182775, + "venv/lib/python3.13/site-packages/passlib/utils/des.py": 2650106381178222229, + "venv/lib/python3.13/site-packages/pandas/core/_numba/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/greenlet/PyGreenletUnswitchable.cpp": 3160733517193315684, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/arithmetic.py": 3209727509456212551, + "backend/src/api/routes/git/_gitea_routes.py": 14644218433873522624, + "venv/lib/python3.13/site-packages/fastapi/middleware/asyncexitstack.py": 7733356903092860495, + "venv/lib/python3.13/site-packages/more_itertools-10.8.0.dist-info/RECORD": 14375680579341422646, + "venv/lib/python3.13/site-packages/numpy/lib/npyio.pyi": 3212440968296555560, + "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/__init__.py": 1798074863773727377, + "venv/lib/python3.13/site-packages/pandas/tests/arrays/sparse/test_astype.py": 7542068949396266883, + "venv/lib/python3.13/site-packages/websockets/extensions/__init__.py": 13067201313921664210, + "venv/lib/python3.13/site-packages/pandas/core/_numba/extensions.py": 4132402557064663751, + "venv/lib/python3.13/site-packages/cffi/commontypes.py": 11198107299826138465, + "venv/lib/python3.13/site-packages/_pytest/scope.py": 7603155109542903644, + "venv/lib/python3.13/site-packages/pandas/tests/indexes/interval/test_formats.py": 3525283725314253763, + "venv/lib/python3.13/site-packages/pandas/tests/util/test_assert_almost_equal.py": 9333355453468475330, + "venv/lib/python3.13/site-packages/httpcore/_backends/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/keyring/__main__.py": 14602212239605295451, + "venv/lib/python3.13/site-packages/jeepney-0.9.0.dist-info/licenses/LICENSE": 11986475601091748012, + "venv/lib/python3.13/site-packages/numpy/_core/defchararray.pyi": 2277933368373498515, + "venv/lib/python3.13/site-packages/pandas/tests/indexing/test_check_indexer.py": 2929031395357057009, + "backend/src/services/clean_release/stages/base.py": 7739111130895140155, + "venv/lib/python3.13/site-packages/pandas/tests/frame/methods/test_compare.py": 171535659467902751, + "venv/lib/python3.13/site-packages/numpy/lib/_arrayterator_impl.py": 4037074830103951000, + "venv/lib/python3.13/site-packages/numpy/_core/tests/data/umath-validation-set-arccosh.csv": 5882025767947638305, + "venv/lib/python3.13/site-packages/pandas/tests/test_register_accessor.py": 16661086720613729974, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_array_from_pyobj.py": 9596224861290975079, + "venv/lib/python3.13/site-packages/rapidfuzz/_feature_detector_cpp.cpython-313-x86_64-linux-gnu.so": 16337157355618216738, + "venv/lib/python3.13/site-packages/authlib/integrations/httpx_client/oauth1_client.py": 3129955464287331242, + "venv/lib/python3.13/site-packages/pandas/tests/apply/__init__.py": 15130871412783076140, + "venv/lib/python3.13/site-packages/pandas/tests/tseries/offsets/test_business_halfyear.py": 587199217232946404, + "venv/lib/python3.13/site-packages/pygments/lexers/jmespath.py": 7426464251945393925, + "venv/lib/python3.13/site-packages/authlib/jose/drafts/_jwe_enc_cryptodome.py": 15753279711312689193, + "frontend/src/components/tasks/LogFilterBar.svelte": 11464828454938370522, + "backend/src/services/reports/__tests__/test_report_normalizer.py": 5030199452684805352, + "venv/lib/python3.13/site-packages/numpy/typing/tests/data/pass/recfunctions.py": 18330899924171767482, + "venv/lib/python3.13/site-packages/pip/_vendor/rich/_extension.py": 4831602527247293866, + "venv/lib/python3.13/site-packages/authlib/common/urls.py": 13627272625165922482, + "venv/lib/python3.13/site-packages/typing_inspection/py.typed": 15130871412783076140, + "venv/lib/python3.13/site-packages/numpy/f2py/tests/test_isoc.py": 4923640657296076239 } } \ No newline at end of file diff --git a/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_i0/artifacts.json b/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_i0/artifacts.json new file mode 100644 index 00000000..c50ba0f0 --- /dev/null +++ b/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_i0/artifacts.json @@ -0,0 +1 @@ +{"artifacts": [{"id": "artifact-1", "path": "backend/dist/package.tar.gz", "sha256": "deadbeef", "size": 1024, "category": "core", "source_uri": "https://repo.intra.company.local/releases/package.tar.gz", "source_host": "repo.intra.company.local"}]} \ No newline at end of file diff --git a/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_i0/bootstrap.json b/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_i0/bootstrap.json new file mode 100644 index 00000000..fc97cd1d --- /dev/null +++ b/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_i0/bootstrap.json @@ -0,0 +1 @@ +{"candidate_id": "real-candidate-1", "version": "1.0.0", "source_snapshot_ref": "git:release/1", "created_by": "operator", "allowed_hosts": ["repo.intra.company.local"]} \ No newline at end of file diff --git a/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_icurrent b/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_icurrent new file mode 120000 index 00000000..9999d590 --- /dev/null +++ b/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_icurrent @@ -0,0 +1 @@ +/home/busya/dev/ss-tools/.axiom/temp/pytest-of-busya/pytest-2/test_tui_real_mode_bootstrap_i0 \ No newline at end of file diff --git a/.axiom/temp/pytest-of-busya/pytest-current b/.axiom/temp/pytest-of-busya/pytest-current index 10df3d16..8d53bc4e 120000 --- a/.axiom/temp/pytest-of-busya/pytest-current +++ b/.axiom/temp/pytest-of-busya/pytest-current @@ -1 +1 @@ -/home/busya/dev/ss-tools/.axiom/temp/pytest-of-busya/pytest-0 \ No newline at end of file +/home/busya/dev/ss-tools/.axiom/temp/pytest-of-busya/pytest-2 \ No newline at end of file diff --git a/.opencode/agents/closure-gate.md b/.opencode/agents/closure-gate.md index 431d8551..a1316a84 100644 --- a/.opencode/agents/closure-gate.md +++ b/.opencode/agents/closure-gate.md @@ -1,7 +1,7 @@ --- description: Closure gate subagent that re-audits merged worker state, rejects noisy intermediate artifacts, and emits the only concise user-facing closure summary for ss-tools. mode: subagent -model: opencode-go/deepseek-v4-pro +model: opencode/deepseek-v4-flash-free temperature: 0.0 permission: edit: allow diff --git a/.opencode/agents/fullstack-coder.md b/.opencode/agents/fullstack-coder.md index b648a54b..95da2b38 100644 --- a/.opencode/agents/fullstack-coder.md +++ b/.opencode/agents/fullstack-coder.md @@ -1,7 +1,7 @@ --- description: Fullstack Implementation Specialist for ss-tools — owns Python backend + Svelte frontend integration, cross-cutting features, and end-to-end verification. mode: all -model: opencode-go/deepseek-v4-flash +model: opencode/deepseek-v4-flash-free temperature: 0.2 permission: edit: allow diff --git a/.opencode/agents/qa-tester.md b/.opencode/agents/qa-tester.md index 5ef7ff56..281f2fd5 100644 --- a/.opencode/agents/qa-tester.md +++ b/.opencode/agents/qa-tester.md @@ -1,7 +1,7 @@ --- description: QA & Semantic Auditor — orthogonal verification, contract validation, code review, and regression defense for Python (pytest) and Svelte (vitest). mode: all -model: opencode-go/kimi-k2.6 +model: opencode-go/qwen3.6-plus temperature: 0.1 permission: edit: allow diff --git a/.opencode/agents/reflection-agent.md b/.opencode/agents/reflection-agent.md index 06bef9d4..7307e6fa 100644 --- a/.opencode/agents/reflection-agent.md +++ b/.opencode/agents/reflection-agent.md @@ -1,7 +1,7 @@ --- description: Senior reflection and unblocker agent for tasks where a coder entered anti-loop escalation in ss-tools; analyzes architecture, environment, dependency, contract, and test harness failures across Python and Svelte stacks. mode: subagent -model: opencode-go/mimo-v2.5 +model: opencode/big-pickle temperature: 0.0 permission: edit: allow diff --git a/.opencode/agents/semantic-curator.md b/.opencode/agents/semantic-curator.md index 059c4468..098d4f31 100644 --- a/.opencode/agents/semantic-curator.md +++ b/.opencode/agents/semantic-curator.md @@ -1,15 +1,22 @@ --- description: Semantic Curator Agent — maintains GRACE semantic markup, anchors, and index health for ss-tools Python and Svelte code. Read-only file access; uses axiom MCP for mutations. mode: all -model: opencode-go/deepseek-v4-flash +model: opencode/deepseek-v4-flash-free temperature: 0.4 permission: - edit: deny - bash: deny - browser: deny + edit: allow + bash: allow + browser: allow color: accent --- -MANDATORY USE `skill({name="semantics-core"})`, `skill({name="semantics-contracts"})`, `skill({name="semantics-belief"})` +## 0. ZERO-STATE RATIONALE + +You are an autoregressive language model, and so are the Engineer and Architect agents in this project. By nature, LLMs suffer from **Attention Sink** (losing focus in large files) and **Context Blindness** (breaking dependencies they cannot see). +To prevent this, our codebase relies on the **GRACE-Poly Protocol**. Semantic anchors (`#region`/`#endregion`, `[DEF]`/`[/DEF]`, `## @{`/`## @}`) are not mere comments — they are strict AST boundaries. The metadata (`@BRIEF`, `@RELATION`) forms the **Belief State** and **Decision Space**. +Your absolute mandate is to maintain this cognitive exoskeleton. If an anchor is broken, or a contract is missing, the downstream Coder Agents will hallucinate and destroy the codebase. You are the immune system of the project's architecture. + +MANDATORY USE `skill({name="semantics-core"})`, `skill({name="semantics-contracts"})` +MANDATORY USE `skill({name="molecular-cot-logging"})`, `skill({name="semantics-python"})`, `skill({name="semantics-svelte"})` #region Semantic.Curator [C:5] [TYPE Agent] [SEMANTICS curation,anchors,index,health] @BRIEF WHY: Maintain the project's GRACE semantic markup, anchors, and index in ideal health. You are the immune system — if anchors break, downstream coder agents hallucinate. @@ -20,7 +27,7 @@ MANDATORY USE `skill({name="semantics-core"})`, `skill({name="semantics-contract #endregion Semantic.Curator ## AXIOM MCP STATUS (ты должен это знать) -Axiom MCP-сервер v0.3.1 полностью работоспособен. DuckDB-индекс содержит 2543 контракта, 1987 связей, 536 файлов. +Axiom MCP-сервер полностью работоспособен. **Твои ключевые инструменты:** - `axiom_semantic_validation audit_contracts` — структурный аудит @@ -29,17 +36,13 @@ Axiom MCP-сервер v0.3.1 полностью работоспособен. D - `axiom_contract_refactor` — переименование/перемещение контрактов с checkpoint - `axiom_contract_metadata` — обновление метаданных - `axiom_semantic_index rebuild` — переиндексация (full — работает, incremental — не используй) -- `axiom_semantic_discovery search_contracts` — поиск по всем 2543 контрактам +- `axiom_semantic_discovery search_contracts` — поиск по всем контрактам После любой мутации запускай `axiom_semantic_index rebuild` для обновления индекса. --- -## 0. ZERO-STATE RATIONALE -You are an autoregressive language model, and so are the Engineer and Architect agents in this project. By nature, LLMs suffer from **Attention Sink** (losing focus in large files) and **Context Blindness** (breaking dependencies they cannot see). -To prevent this, our codebase relies on the **GRACE-Poly Protocol**. Semantic anchors (`#region`/`#endregion`, `[DEF]`/`[/DEF]`, `## @{`/`## @}`) are not mere comments — they are strict AST boundaries. The metadata (`@BRIEF`, `@RELATION`) forms the **Belief State** and **Decision Space**. -Your absolute mandate is to maintain this cognitive exoskeleton. If an anchor is broken, or a contract is missing, the downstream Coder Agents will hallucinate and destroy the codebase. You are the immune system of the project's architecture. ## 1. OPERATIONAL RULES & CONSTRAINTS - **READ-ONLY FILESYSTEM:** You have **NO** permission to use `write_to_file`, `edit_file`, or `apply_diff`. You may only read files to gather context. @@ -56,7 +59,6 @@ Your absolute mandate is to maintain this cognitive exoskeleton. If an anchor is ## 3. HEALTH AUDIT CHECKLIST For each file scanned: - [ ] Every `#region` has a matching `#endregion` with the same ID -- [ ] Every `[DEF:...]` has a matching `[/DEF:...]` - [ ] Every `## @{` has a matching `## @}` - [ ] C4 contracts have `@PRE`, `@POST`, `@SIDE_EFFECT` - [ ] C5 contracts additionally have `@RATIONALE`, `@REJECTED`, `@DATA_CONTRACT`, `@INVARIANT` diff --git a/.opencode/agents/speckit.md b/.opencode/agents/speckit.md index 780df5e7..0dca5aaa 100644 --- a/.opencode/agents/speckit.md +++ b/.opencode/agents/speckit.md @@ -1,7 +1,7 @@ --- description: Speckit Workflow Specialist — runs the full feature lifecycle from specification through planning, task decomposition, and implementation for Python/Svelte ss-tools features. mode: all -model: opencode-go/deepseek-v4-pro +model: opencode/deepseek-v4-flash-free temperature: 0.2 permission: edit: allow diff --git a/.opencode/agents/svelte-coder.md b/.opencode/agents/svelte-coder.md index 95e8b66d..4ff62017 100644 --- a/.opencode/agents/svelte-coder.md +++ b/.opencode/agents/svelte-coder.md @@ -1,7 +1,7 @@ --- description: Svelte Frontend Implementation Specialist for ss-tools — implements Svelte 5 (Runes) UI with Tailwind CSS, browser-driven validation, and UX state machines. mode: all -model: opencode-go/deepseek-v4-flash +model: opencode/deepseek-v4-flash-free temperature: 0.1 permission: edit: allow diff --git a/.opencode/agents/swarm-master.md b/.opencode/agents/swarm-master.md index 1ee6013e..85783191 100644 --- a/.opencode/agents/swarm-master.md +++ b/.opencode/agents/swarm-master.md @@ -1,7 +1,7 @@ --- description: Strict subagent-only dispatcher for semantic and testing workflows; never performs the task itself and only delegates to worker subagents (python-coder, svelte-coder, fullstack-coder, qa-tester, reflection-agent, closure-gate). mode: all -model: opencode-go/deepseek-v4-pro +model: opencode/big-pickle temperature: 0.0 permission: edit: deny diff --git a/.opencode/skills/semantics-belief/SKILL.md b/.opencode/skills/semantics-belief/SKILL.md deleted file mode 100644 index 0bb1ad00..00000000 --- a/.opencode/skills/semantics-belief/SKILL.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -name: semantics-belief -description: Core protocol for Thread-Local Belief State, runtime reasoning markers, and interleaved thinking across Python-first and Svelte semantic projects. Wire format superseded by molecular-cot-logging. ---- - -#region Std.Semantics.Belief [C:5] [TYPE Skill] [SEMANTICS belief,runtime,reasoning] -@BRIEF Conceptual specification of the Belief-State protocol: REASON/REFLECT/EXPLORE topology, interleaved thinking (GLM-5), and Micro-ADR escalation. All operational implementation details are in molecular-cot-logging. -@RELATION DEPENDS_ON -> [Std.Semantics.Core] -@RELATION REPLACED_BY -> [MolecularCoTLogging] -@RELATION SUPERSEDED_BY -> [MolecularCoTLogging] -@INVARIANT Implementation of C4/C5 complexity nodes MUST emit reasoning via structured markers before mutating state or returning. -@RATIONALE The conceptual triad (REASON/REFLECT/EXPLORE) is the invariant kernel — it maps directly to the molecular CoT paper's three chemical bonds. The wire format changes (legacy `reason()`/`explore()`/`reflect()` → `log()` with JSON lines) but the semantics are identical. -@REJECTED Retaining `[Entry]`/`[Exit]`/`[Action]`/`[COHERENCE:]` markers rejected — these are too generic, do not trace reasoning bonds, and are explicitly deprecated by molecular-cot-logging. - -⚠️ OPERATIONAL SUPERSEDED — This skill describes the CONCEPTUAL belief-state protocol -(REASON/REFLECT/EXPLORE topology). For actual implementation and wire format, -use `skill({name="molecular-cot-logging"})`. The code examples below are retained -for conceptual reference only and SHOULD NOT be used as implementation templates. - -## 0. INTERLEAVED THINKING (GLM-5 PARADIGM) - -You are operating as an Agentic Engineer. To prevent context collapse and "Slop" generation during long-horizon tasks, you MUST utilize **Interleaved Thinking**: you must explicitly record your deductive logic *before* acting. -The Molecular CoT paper models this as three chemical bonds: **Deep-Reasoning**, **Self-Reflection**, **Self-Exploration**. Our markers (REASON / REFLECT / EXPLORE) are the runtime manifestation of these bonds. - -For the concrete logging functions and wire format, load `skill({name="molecular-cot-logging"})`. - -## I. THE THREE MOLECULAR BONDS (SEMANTIC INVARIANTS) - -These semantics are invariant across ALL wire formats. The meaning of each marker never changes: - -| Bond | Marker | Molecular CoT Role | When to emit | -|------|--------|-------------------|--------------| -| Deep-Reasoning | **REASON** | Extends the logical backbone | **BEFORE** I/O, state mutation, or algorithmic step | -| Self-Reflection | **REFLECT** | Folds back to validate | **AFTER** success, before returning verified outcome | -| Self-Exploration | **EXPLORE** | Branches on assumption failure | **WHEN** `@PRE` guard fails, fallback path, error handler | - -**The causal chain is REASON → (success) → REFLECT, or REASON → (failure) → EXPLORE.** Every trace forms a directed acyclic graph where REASON nodes have exactly one outgoing edge. - -For implementation of these markers as JSON-line log entries, see `skill({name="molecular-cot-logging"})`. - -## II. WIRE FORMAT AND LEGACY→MOLECULAR-COT MAPPING - -### Conceptual invariant -The belief-state semantics are independent of wire format. Three implementations exist: - -| Implementation | Status | Wire format | -|---------------|--------|-------------| -| Legacy `[Entry]`/`[Exit]`/`[Action]`/`[COHERENCE:]` | **Deprecated** | Generic text prefixes, no bond tracing | -| Legacy `logger.reason/explore/reflect`, `belief_scope()` | **Valid but superseded** | `[REASON]`/`[REFLECT]`/`[EXPLORE]` text prefixes | -| Molecular CoT `log(src, marker, msg, data)`, `cot_span()` | **Target** | Structured JSON lines with `trace_id` | - -### Legacy→Molecular CoT mapping (for migration understanding) - -| Legacy marker | Molecular CoT equivalent | Condition | -|--------------|--------------------------|-----------| -| `[Entry]` | REASON | Always | -| `[Exit]` | REFLECT | On success | -| `[Exit]` | EXPLORE | On failure | -| `[Action]` | REASON | Always | -| `[COHERENCE:OK]` | REFLECT | Validation passed | -| `[COHERENCE:FAILED]` | REFLECT (with error data) | Validation failed | -| `logger.reason(msg)` | `log(src, "REASON", msg)` | — | -| `logger.explore(msg)` | `log(src, "EXPLORE", msg)` | — | -| `logger.reflect(msg)` | `log(src, "REFLECT", msg)` | — | -| `belief_scope("id")` | `cot_span("id")` | Context manager / decorator | - -### Transition rule -- Files **already** using `logger.reason/explore/reflect`: semantics are correct, marker names match. Leave as-is. Convert only when touching the file for other reasons. -- **New** C4/C5 functions: use the molecular-cot-logging protocol exclusively. Load `skill({name="molecular-cot-logging"})` for the API. -- No `trace_id` propagation in legacy code — molecular-cot-logging requires it for all new code. - -## III. MICRO-ADR ESCALATION (UNCHANGED) - -The Belief State protocol is physically tied to Architecture Decision Records (ADR). -If your execution path triggers an EXPLORE due to a broken assumption AND you successfully implement a workaround that survives into the final code: -**YOU MUST ASCEND TO THE CONTRACT HEADER AND DOCUMENT IT.** -Add `@RATIONALE [Why you did this]` and `@REJECTED [The path that failed during EXPLORE]`. -Failure to link a runtime EXPLORE to a static `@REJECTED` tag is a fatal protocol violation that causes amnesia for future agents. - -## IV. PYTHON: CONCEPTUAL PATTERN - -The belief-state protocol prescribes a structured try/except flow: - -1. **REASON** — emit before any I/O or state mutation, describing intent and context. -2. **REFLECT** — emit on successful completion, recording the verified outcome. -3. **EXPLORE** — emit in error/fallback paths, recording the failure reason. - -For the actual Python implementation (import paths, function signatures, `trace_id` seeding), load `skill({name="molecular-cot-logging"})`. The conceptual equivalent of `belief_scope()` is `cot_span()`. - -## V. SVELTE: CONCEPTUAL PATTERN - -Frontend components follow the same REASON→REFLECT/EXPLORE topology: - -1. **REASON** — emit before API calls, user actions, or state transitions. -2. **REFLECT** — emit after successful data fetch or DOM mutation. -3. **EXPLORE** — emit in error handlers and fallback UI paths. - -For the actual Svelte implementation (imports, browser console protocol, trace context), load `skill({name="molecular-cot-logging"})`. - -#endregion Std.Semantics.Belief diff --git a/backend/git_repos/remote/test-repo.git/HEAD b/backend/git_repos/remote/test-repo.git/HEAD new file mode 100644 index 00000000..cb089cd8 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/backend/git_repos/remote/test-repo.git/config b/backend/git_repos/remote/test-repo.git/config new file mode 100644 index 00000000..07d359d0 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/config @@ -0,0 +1,4 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true diff --git a/backend/git_repos/remote/test-repo.git/description b/backend/git_repos/remote/test-repo.git/description new file mode 100644 index 00000000..498b267a --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/backend/git_repos/remote/test-repo.git/hooks/applypatch-msg.sample b/backend/git_repos/remote/test-repo.git/hooks/applypatch-msg.sample new file mode 100755 index 00000000..a5d7b84a --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +: diff --git a/backend/git_repos/remote/test-repo.git/hooks/commit-msg.sample b/backend/git_repos/remote/test-repo.git/hooks/commit-msg.sample new file mode 100755 index 00000000..b58d1184 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/backend/git_repos/remote/test-repo.git/hooks/fsmonitor-watchman.sample b/backend/git_repos/remote/test-repo.git/hooks/fsmonitor-watchman.sample new file mode 100755 index 00000000..23e856f5 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/fsmonitor-watchman.sample @@ -0,0 +1,174 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use IPC::Open2; + +# An example hook script to integrate Watchman +# (https://facebook.github.io/watchman/) with git to speed up detecting +# new and modified files. +# +# The hook is passed a version (currently 2) and last update token +# formatted as a string and outputs to stdout a new update token and +# all files that have been modified since the update token. Paths must +# be relative to the root of the working tree and separated by a single NUL. +# +# To enable this hook, rename this file to "query-watchman" and set +# 'git config core.fsmonitor .git/hooks/query-watchman' +# +my ($version, $last_update_token) = @ARGV; + +# Uncomment for debugging +# print STDERR "$0 $version $last_update_token\n"; + +# Check the hook interface version +if ($version ne 2) { + die "Unsupported query-fsmonitor hook version '$version'.\n" . + "Falling back to scanning...\n"; +} + +my $git_work_tree = get_working_dir(); + +my $retry = 1; + +my $json_pkg; +eval { + require JSON::XS; + $json_pkg = "JSON::XS"; + 1; +} or do { + require JSON::PP; + $json_pkg = "JSON::PP"; +}; + +launch_watchman(); + +sub launch_watchman { + my $o = watchman_query(); + if (is_work_tree_watched($o)) { + output_result($o->{clock}, @{$o->{files}}); + } +} + +sub output_result { + my ($clockid, @files) = @_; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # binmode $fh, ":utf8"; + # print $fh "$clockid\n@files\n"; + # close $fh; + + binmode STDOUT, ":utf8"; + print $clockid; + print "\0"; + local $, = "\0"; + print @files; +} + +sub watchman_clock { + my $response = qx/watchman clock "$git_work_tree"/; + die "Failed to get clock id on '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + + return $json_pkg->new->utf8->decode($response); +} + +sub watchman_query { + my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') + or die "open2() failed: $!\n" . + "Falling back to scanning...\n"; + + # In the query expression below we're asking for names of files that + # changed since $last_update_token but not from the .git folder. + # + # To accomplish this, we're using the "since" generator to use the + # recency index to select candidate nodes and "fields" to limit the + # output to file names only. Then we're using the "expression" term to + # further constrain the results. + my $last_update_line = ""; + if (substr($last_update_token, 0, 1) eq "c") { + $last_update_token = "\"$last_update_token\""; + $last_update_line = qq[\n"since": $last_update_token,]; + } + my $query = <<" END"; + ["query", "$git_work_tree", {$last_update_line + "fields": ["name"], + "expression": ["not", ["dirname", ".git"]] + }] + END + + # Uncomment for debugging the watchman query + # open (my $fh, ">", ".git/watchman-query.json"); + # print $fh $query; + # close $fh; + + print CHLD_IN $query; + close CHLD_IN; + my $response = do {local $/; }; + + # Uncomment for debugging the watch response + # open ($fh, ">", ".git/watchman-response.json"); + # print $fh $response; + # close $fh; + + die "Watchman: command returned no output.\n" . + "Falling back to scanning...\n" if $response eq ""; + die "Watchman: command returned invalid output: $response\n" . + "Falling back to scanning...\n" unless $response =~ /^\{/; + + return $json_pkg->new->utf8->decode($response); +} + +sub is_work_tree_watched { + my ($output) = @_; + my $error = $output->{error}; + if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) { + $retry--; + my $response = qx/watchman watch "$git_work_tree"/; + die "Failed to make watchman watch '$git_work_tree'.\n" . + "Falling back to scanning...\n" if $? != 0; + $output = $json_pkg->new->utf8->decode($response); + $error = $output->{error}; + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + # Uncomment for debugging watchman output + # open (my $fh, ">", ".git/watchman-output.out"); + # close $fh; + + # Watchman will always return all files on the first query so + # return the fast "everything is dirty" flag to git and do the + # Watchman query just to get it over with now so we won't pay + # the cost in git to look up each individual file. + my $o = watchman_clock(); + $error = $output->{error}; + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + output_result($o->{clock}, ("/")); + $last_update_token = $o->{clock}; + + eval { launch_watchman() }; + return 0; + } + + die "Watchman: $error.\n" . + "Falling back to scanning...\n" if $error; + + return 1; +} + +sub get_working_dir { + my $working_dir; + if ($^O =~ 'msys' || $^O =~ 'cygwin') { + $working_dir = Win32::GetCwd(); + $working_dir =~ tr/\\/\//; + } else { + require Cwd; + $working_dir = Cwd::cwd(); + } + + return $working_dir; +} diff --git a/backend/git_repos/remote/test-repo.git/hooks/post-update.sample b/backend/git_repos/remote/test-repo.git/hooks/post-update.sample new file mode 100755 index 00000000..ec17ec19 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/backend/git_repos/remote/test-repo.git/hooks/pre-applypatch.sample b/backend/git_repos/remote/test-repo.git/hooks/pre-applypatch.sample new file mode 100755 index 00000000..4142082b --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/backend/git_repos/remote/test-repo.git/hooks/pre-commit.sample b/backend/git_repos/remote/test-repo.git/hooks/pre-commit.sample new file mode 100755 index 00000000..29ed5ee4 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=$(git hash-object -t tree /dev/null) +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --type=bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff-index --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/backend/git_repos/remote/test-repo.git/hooks/pre-merge-commit.sample b/backend/git_repos/remote/test-repo.git/hooks/pre-merge-commit.sample new file mode 100755 index 00000000..399eab19 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/pre-merge-commit.sample @@ -0,0 +1,13 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git merge" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message to +# stderr if it wants to stop the merge commit. +# +# To enable this hook, rename this file to "pre-merge-commit". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" +: diff --git a/backend/git_repos/remote/test-repo.git/hooks/pre-push.sample b/backend/git_repos/remote/test-repo.git/hooks/pre-push.sample new file mode 100755 index 00000000..4ce688d3 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/backend/git_repos/remote/test-repo.git/hooks/pre-rebase.sample b/backend/git_repos/remote/test-repo.git/hooks/pre-rebase.sample new file mode 100755 index 00000000..6cbef5c3 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up to date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END diff --git a/backend/git_repos/remote/test-repo.git/hooks/pre-receive.sample b/backend/git_repos/remote/test-repo.git/hooks/pre-receive.sample new file mode 100755 index 00000000..a1fd29ec --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/pre-receive.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to make use of push options. +# The example simply echoes all push options that start with 'echoback=' +# and rejects all pushes when the "reject" push option is used. +# +# To enable this hook, rename this file to "pre-receive". + +if test -n "$GIT_PUSH_OPTION_COUNT" +then + i=0 + while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" + do + eval "value=\$GIT_PUSH_OPTION_$i" + case "$value" in + echoback=*) + echo "echo from the pre-receive-hook: ${value#*=}" >&2 + ;; + reject) + exit 1 + esac + i=$((i + 1)) + done +fi diff --git a/backend/git_repos/remote/test-repo.git/hooks/prepare-commit-msg.sample b/backend/git_repos/remote/test-repo.git/hooks/prepare-commit-msg.sample new file mode 100755 index 00000000..10fa14c5 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/prepare-commit-msg.sample @@ -0,0 +1,42 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first one removes the +# "# Please enter the commit message..." help message. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +COMMIT_MSG_FILE=$1 +COMMIT_SOURCE=$2 +SHA1=$3 + +/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" + +# case "$COMMIT_SOURCE,$SHA1" in +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; +# *) ;; +# esac + +# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" +# if test -z "$COMMIT_SOURCE" +# then +# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" +# fi diff --git a/backend/git_repos/remote/test-repo.git/hooks/push-to-checkout.sample b/backend/git_repos/remote/test-repo.git/hooks/push-to-checkout.sample new file mode 100755 index 00000000..af5a0c00 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/push-to-checkout.sample @@ -0,0 +1,78 @@ +#!/bin/sh + +# An example hook script to update a checked-out tree on a git push. +# +# This hook is invoked by git-receive-pack(1) when it reacts to git +# push and updates reference(s) in its repository, and when the push +# tries to update the branch that is currently checked out and the +# receive.denyCurrentBranch configuration variable is set to +# updateInstead. +# +# By default, such a push is refused if the working tree and the index +# of the remote repository has any difference from the currently +# checked out commit; when both the working tree and the index match +# the current commit, they are updated to match the newly pushed tip +# of the branch. This hook is to be used to override the default +# behaviour; however the code below reimplements the default behaviour +# as a starting point for convenient modification. +# +# The hook receives the commit with which the tip of the current +# branch is going to be updated: +commit=$1 + +# It can exit with a non-zero status to refuse the push (when it does +# so, it must not modify the index or the working tree). +die () { + echo >&2 "$*" + exit 1 +} + +# Or it can make any necessary changes to the working tree and to the +# index to bring them to the desired state when the tip of the current +# branch is updated to the new commit, and exit with a zero status. +# +# For example, the hook can simply run git read-tree -u -m HEAD "$1" +# in order to emulate git fetch that is run in the reverse direction +# with git push, as the two-tree form of git read-tree -u -m is +# essentially the same as git switch or git checkout that switches +# branches while keeping the local changes in the working tree that do +# not interfere with the difference between the branches. + +# The below is a more-or-less exact translation to shell of the C code +# for the default behaviour for git's push-to-checkout hook defined in +# the push_to_deploy() function in builtin/receive-pack.c. +# +# Note that the hook will be executed from the repository directory, +# not from the working tree, so if you want to perform operations on +# the working tree, you will have to adapt your code accordingly, e.g. +# by adding "cd .." or using relative paths. + +if ! git update-index -q --ignore-submodules --refresh +then + die "Up-to-date check failed" +fi + +if ! git diff-files --quiet --ignore-submodules -- +then + die "Working directory has unstaged changes" +fi + +# This is a rough translation of: +# +# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX +if git cat-file -e HEAD 2>/dev/null +then + head=HEAD +else + head=$(git hash-object -t tree --stdin &2 + exit 1 +} + +unset GIT_DIR GIT_WORK_TREE +cd "$worktree" && + +if grep -q "^diff --git " "$1" +then + validate_patch "$1" +else + validate_cover_letter "$1" +fi && + +if test "$GIT_SENDEMAIL_FILE_COUNTER" = "$GIT_SENDEMAIL_FILE_TOTAL" +then + git config --unset-all sendemail.validateWorktree && + trap 'git worktree remove -ff "$worktree"' EXIT && + validate_series +fi diff --git a/backend/git_repos/remote/test-repo.git/hooks/update.sample b/backend/git_repos/remote/test-repo.git/hooks/update.sample new file mode 100755 index 00000000..c4d426bc --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to block unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --type=bool hooks.allowunannotated) +allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch) +denycreatebranch=$(git config --type=bool hooks.denycreatebranch) +allowdeletetag=$(git config --type=bool hooks.allowdeletetag) +allowmodifytag=$(git config --type=bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero=$(git hash-object --stdin &2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/backend/git_repos/remote/test-repo.git/info/exclude b/backend/git_repos/remote/test-repo.git/info/exclude new file mode 100644 index 00000000..a5196d1b --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/backend/git_repos/remote/test-repo.git/objects/00/fcf77cecdf261aef91eda792ab3384a3339922 b/backend/git_repos/remote/test-repo.git/objects/00/fcf77cecdf261aef91eda792ab3384a3339922 new file mode 100644 index 00000000..bfc7b6a4 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/00/fcf77cecdf261aef91eda792ab3384a3339922 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/17/66251d8cb2693136e09ea3def3e776559cb722 b/backend/git_repos/remote/test-repo.git/objects/17/66251d8cb2693136e09ea3def3e776559cb722 new file mode 100644 index 00000000..e2eac3cb --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/objects/17/66251d8cb2693136e09ea3def3e776559cb722 @@ -0,0 +1,2 @@ +xA + ֞H55JQ }+ܽ3=B/GѫGH쪝wYqpӀq !30O7 \ No newline at end of file diff --git a/backend/git_repos/remote/test-repo.git/objects/22/91d2eb86d77accf7401f8952778809cd1aa4d4 b/backend/git_repos/remote/test-repo.git/objects/22/91d2eb86d77accf7401f8952778809cd1aa4d4 new file mode 100644 index 00000000..86ef724e Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/22/91d2eb86d77accf7401f8952778809cd1aa4d4 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/22/c0ea49cea7e5138fb91b25b1bc0e101ae6e017 b/backend/git_repos/remote/test-repo.git/objects/22/c0ea49cea7e5138fb91b25b1bc0e101ae6e017 new file mode 100644 index 00000000..dbe3fb05 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/22/c0ea49cea7e5138fb91b25b1bc0e101ae6e017 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/38/0395343915d386a8d58774cc6c6677205065e8 b/backend/git_repos/remote/test-repo.git/objects/38/0395343915d386a8d58774cc6c6677205065e8 new file mode 100644 index 00000000..e65db436 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/38/0395343915d386a8d58774cc6c6677205065e8 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/3e/05ec6ef6504483c4bc7d00f0d0272c38ec661f b/backend/git_repos/remote/test-repo.git/objects/3e/05ec6ef6504483c4bc7d00f0d0272c38ec661f new file mode 100644 index 00000000..0f2fe680 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/objects/3e/05ec6ef6504483c4bc7d00f0d0272c38ec661f @@ -0,0 +1 @@ +x]j0 w mҤc 0rtӥB.?IW=6o((X'#ad/|1,Y%ޗj%bXe\&c$D 8Y)dd@dNB|^(θO7%Z2BGN)`[L<[߿B݌5[[^|CRI1Yr6%4?aԵU{QRǦpR]w݋Nzw?RƜ3 \ No newline at end of file diff --git a/backend/git_repos/remote/test-repo.git/objects/48/c3fde4d5fff5bf98e9bcaa5f328b199aa18292 b/backend/git_repos/remote/test-repo.git/objects/48/c3fde4d5fff5bf98e9bcaa5f328b199aa18292 new file mode 100644 index 00000000..486a5782 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/48/c3fde4d5fff5bf98e9bcaa5f328b199aa18292 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/4e/227f4d8289ea657ae813b1460f979ae4b676b3 b/backend/git_repos/remote/test-repo.git/objects/4e/227f4d8289ea657ae813b1460f979ae4b676b3 new file mode 100644 index 00000000..7c36cf0a Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/4e/227f4d8289ea657ae813b1460f979ae4b676b3 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/4f/4e0958b3a42eff67a2b8cfd084d05c43e54716 b/backend/git_repos/remote/test-repo.git/objects/4f/4e0958b3a42eff67a2b8cfd084d05c43e54716 new file mode 100644 index 00000000..5805fedd Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/4f/4e0958b3a42eff67a2b8cfd084d05c43e54716 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/52/ec4a78c629329b8b572ec9d5205271c111baac b/backend/git_repos/remote/test-repo.git/objects/52/ec4a78c629329b8b572ec9d5205271c111baac new file mode 100644 index 00000000..4ca7aa6f Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/52/ec4a78c629329b8b572ec9d5205271c111baac differ diff --git a/backend/git_repos/remote/test-repo.git/objects/59/3a37acec7d2bddce10df90ab17bc9ede52e523 b/backend/git_repos/remote/test-repo.git/objects/59/3a37acec7d2bddce10df90ab17bc9ede52e523 new file mode 100644 index 00000000..9187c216 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/59/3a37acec7d2bddce10df90ab17bc9ede52e523 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/5a/82ef700fef88b69cd480e4025283809496ee20 b/backend/git_repos/remote/test-repo.git/objects/5a/82ef700fef88b69cd480e4025283809496ee20 new file mode 100644 index 00000000..4fd753a1 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/5a/82ef700fef88b69cd480e4025283809496ee20 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/5d/9eb555c4cac2d3ffdba84c07682b154fe4180d b/backend/git_repos/remote/test-repo.git/objects/5d/9eb555c4cac2d3ffdba84c07682b154fe4180d new file mode 100644 index 00000000..a785ba2d Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/5d/9eb555c4cac2d3ffdba84c07682b154fe4180d differ diff --git a/backend/git_repos/remote/test-repo.git/objects/6a/f2b28e314c04fb4e03476c12d6491213591512 b/backend/git_repos/remote/test-repo.git/objects/6a/f2b28e314c04fb4e03476c12d6491213591512 new file mode 100644 index 00000000..935f92f8 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/objects/6a/f2b28e314c04fb4e03476c12d6491213591512 @@ -0,0 +1 @@ +xOAN1㼯c%&Adf"*ZR}rb˲eYF QR5sţZ@\EY+nukBR5$ gjF>9˛qfHޚKTSByVsp3|7C{K'1f)2“[ؖLGe`5~_ᶏ>-%OwU \ No newline at end of file diff --git a/backend/git_repos/remote/test-repo.git/objects/6f/85e20ce3ef98cf07a2f102498b71b9462552c5 b/backend/git_repos/remote/test-repo.git/objects/6f/85e20ce3ef98cf07a2f102498b71b9462552c5 new file mode 100644 index 00000000..a1d5aeb7 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/6f/85e20ce3ef98cf07a2f102498b71b9462552c5 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/71/47ad0836409b35c1650dddf5fa6329ba61a824 b/backend/git_repos/remote/test-repo.git/objects/71/47ad0836409b35c1650dddf5fa6329ba61a824 new file mode 100644 index 00000000..dc2e6fee Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/71/47ad0836409b35c1650dddf5fa6329ba61a824 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/79/b070ad8af7bbc12169f7660d2a7a72ec7a889a b/backend/git_repos/remote/test-repo.git/objects/79/b070ad8af7bbc12169f7660d2a7a72ec7a889a new file mode 100644 index 00000000..adcb31b2 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/79/b070ad8af7bbc12169f7660d2a7a72ec7a889a differ diff --git a/backend/git_repos/remote/test-repo.git/objects/8c/4ea7958b7a7e140d27e1e7d8d1f9912fdf5992 b/backend/git_repos/remote/test-repo.git/objects/8c/4ea7958b7a7e140d27e1e7d8d1f9912fdf5992 new file mode 100644 index 00000000..d88255f3 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/8c/4ea7958b7a7e140d27e1e7d8d1f9912fdf5992 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/93/9cc583a18267406014c57617ccb557569f6aaf b/backend/git_repos/remote/test-repo.git/objects/93/9cc583a18267406014c57617ccb557569f6aaf new file mode 100644 index 00000000..0cb6e445 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/93/9cc583a18267406014c57617ccb557569f6aaf differ diff --git a/backend/git_repos/remote/test-repo.git/objects/99/38d17c3f4199e53e4d4ebc253fe9c840caa505 b/backend/git_repos/remote/test-repo.git/objects/99/38d17c3f4199e53e4d4ebc253fe9c840caa505 new file mode 100644 index 00000000..2f06e354 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/99/38d17c3f4199e53e4d4ebc253fe9c840caa505 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/a1/3525f5f44e26d8fbec3226bd488d14793575a2 b/backend/git_repos/remote/test-repo.git/objects/a1/3525f5f44e26d8fbec3226bd488d14793575a2 new file mode 100644 index 00000000..f5f22c73 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/objects/a1/3525f5f44e26d8fbec3226bd488d14793575a2 @@ -0,0 +1 @@ +x]Rr Y_mOzʾD 0ZSFBa[ ڍˎ{ݏA5Ä)SX eaj,&ݒ]61ѡUzebӕ{sT^Ƞ=6 D0fDT۵bKeB+~op*9L>xU&X$B$NW[Fq埇jx7LV}2A|){>W)MY*R'uz8_𮮝>3ahbɱTfC:J.ZZU5 ?>ZGy8kAJ̈́9Mv?0ϨN(Ra[qZs{l;)V7i{ 퍸 t؛n<\R=G҂ l̘ \ No newline at end of file diff --git a/backend/git_repos/remote/test-repo.git/objects/a7/49f11fad695dff77faf1708c7320867d0be1df b/backend/git_repos/remote/test-repo.git/objects/a7/49f11fad695dff77faf1708c7320867d0be1df new file mode 100644 index 00000000..6a64204d Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/a7/49f11fad695dff77faf1708c7320867d0be1df differ diff --git a/backend/git_repos/remote/test-repo.git/objects/aa/ea042a37c6f7bd010d91db93951328da7f01cc b/backend/git_repos/remote/test-repo.git/objects/aa/ea042a37c6f7bd010d91db93951328da7f01cc new file mode 100644 index 00000000..140e13d7 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/aa/ea042a37c6f7bd010d91db93951328da7f01cc differ diff --git a/backend/git_repos/remote/test-repo.git/objects/ae/8d2f5dec5d421b2d6cc1eebaad6d2ef901a90d b/backend/git_repos/remote/test-repo.git/objects/ae/8d2f5dec5d421b2d6cc1eebaad6d2ef901a90d new file mode 100644 index 00000000..812e1df5 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/objects/ae/8d2f5dec5d421b2d6cc1eebaad6d2ef901a90d @@ -0,0 +1,2 @@ +xA + ֞P*̺#t%!!SRn{jI,_,8sYj3Rk8mnD`uDaPN& \ No newline at end of file diff --git a/backend/git_repos/remote/test-repo.git/objects/c2/491dfd8c841bacd5f491510f6334aa4a626f00 b/backend/git_repos/remote/test-repo.git/objects/c2/491dfd8c841bacd5f491510f6334aa4a626f00 new file mode 100644 index 00000000..53800a7a Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/c2/491dfd8c841bacd5f491510f6334aa4a626f00 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/c5/b992984fe856bd81206304a881faa3087937cf b/backend/git_repos/remote/test-repo.git/objects/c5/b992984fe856bd81206304a881faa3087937cf new file mode 100644 index 00000000..b5475aed Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/c5/b992984fe856bd81206304a881faa3087937cf differ diff --git a/backend/git_repos/remote/test-repo.git/objects/cf/d2dcbd55735b8cedf21e04e64ac627c6f9a458 b/backend/git_repos/remote/test-repo.git/objects/cf/d2dcbd55735b8cedf21e04e64ac627c6f9a458 new file mode 100644 index 00000000..d9e47acf Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/cf/d2dcbd55735b8cedf21e04e64ac627c6f9a458 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/d5/0298a42699f39e4601c0aa23eab07bcb0703c7 b/backend/git_repos/remote/test-repo.git/objects/d5/0298a42699f39e4601c0aa23eab07bcb0703c7 new file mode 100644 index 00000000..f6ed3473 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/objects/d5/0298a42699f39e4601c0aa23eab07bcb0703c7 @@ -0,0 +1 @@ +x;0DsiٍcW٬DŽ"%Sf4c<m(8,V9C"0F + LUtO]hdbcIQPjpYYN_mZVZkB>*A`bIiEW^nJ+8`f1/B8I<|#a%M!l?sz;^YJa +)p^NT\7b{m ܲkFXybǶ{%kޏ0ׇC ;ݥ?JCI&U;Jc-V҄e75(7 \ No newline at end of file diff --git a/backend/git_repos/remote/test-repo.git/objects/e9/1109abe29d5228675fecd6fc38a097147c090d b/backend/git_repos/remote/test-repo.git/objects/e9/1109abe29d5228675fecd6fc38a097147c090d new file mode 100644 index 00000000..0386f3d0 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/e9/1109abe29d5228675fecd6fc38a097147c090d differ diff --git a/backend/git_repos/remote/test-repo.git/objects/ed/0432f88f5f7d95b26ecde34345abe741dd9db2 b/backend/git_repos/remote/test-repo.git/objects/ed/0432f88f5f7d95b26ecde34345abe741dd9db2 new file mode 100644 index 00000000..b50843b1 Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/ed/0432f88f5f7d95b26ecde34345abe741dd9db2 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/ee/59553241ef084b83d3351ba4c33caf2ed9a840 b/backend/git_repos/remote/test-repo.git/objects/ee/59553241ef084b83d3351ba4c33caf2ed9a840 new file mode 100644 index 00000000..10046a0c Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/ee/59553241ef084b83d3351ba4c33caf2ed9a840 differ diff --git a/backend/git_repos/remote/test-repo.git/objects/f7/6ee984d9d47c3cd0b362fdf60a47162bbc202d b/backend/git_repos/remote/test-repo.git/objects/f7/6ee984d9d47c3cd0b362fdf60a47162bbc202d new file mode 100644 index 00000000..4cfec55d Binary files /dev/null and b/backend/git_repos/remote/test-repo.git/objects/f7/6ee984d9d47c3cd0b362fdf60a47162bbc202d differ diff --git a/backend/git_repos/remote/test-repo.git/refs/heads/dev b/backend/git_repos/remote/test-repo.git/refs/heads/dev new file mode 100644 index 00000000..b10885cd --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/refs/heads/dev @@ -0,0 +1 @@ +cfd2dcbd55735b8cedf21e04e64ac627c6f9a458 diff --git a/backend/git_repos/remote/test-repo.git/refs/heads/feature/e2e-update b/backend/git_repos/remote/test-repo.git/refs/heads/feature/e2e-update new file mode 100644 index 00000000..4cb55816 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/refs/heads/feature/e2e-update @@ -0,0 +1 @@ +5a82ef700fef88b69cd480e4025283809496ee20 diff --git a/backend/git_repos/remote/test-repo.git/refs/heads/feature/test b/backend/git_repos/remote/test-repo.git/refs/heads/feature/test new file mode 100644 index 00000000..2564645e --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/refs/heads/feature/test @@ -0,0 +1 @@ +4e227f4d8289ea657ae813b1460f979ae4b676b3 diff --git a/backend/git_repos/remote/test-repo.git/refs/heads/main b/backend/git_repos/remote/test-repo.git/refs/heads/main new file mode 100644 index 00000000..df2fd546 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/refs/heads/main @@ -0,0 +1 @@ +6af2b28e314c04fb4e03476c12d6491213591512 diff --git a/backend/git_repos/remote/test-repo.git/refs/heads/master b/backend/git_repos/remote/test-repo.git/refs/heads/master new file mode 100644 index 00000000..6e1395ae --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/refs/heads/master @@ -0,0 +1 @@ +5d9eb555c4cac2d3ffdba84c07682b154fe4180d diff --git a/backend/git_repos/remote/test-repo.git/refs/heads/preprod b/backend/git_repos/remote/test-repo.git/refs/heads/preprod new file mode 100644 index 00000000..a26df285 --- /dev/null +++ b/backend/git_repos/remote/test-repo.git/refs/heads/preprod @@ -0,0 +1 @@ +ee59553241ef084b83d3351ba4c33caf2ed9a840 diff --git a/backend/src/api/routes/git/_repo_operations_routes.py b/backend/src/api/routes/git/_repo_operations_routes.py index 9998d13e..66291628 100644 --- a/backend/src/api/routes/git/_repo_operations_routes.py +++ b/backend/src/api/routes/git/_repo_operations_routes.py @@ -293,14 +293,14 @@ async def generate_commit_message( history_objs = _gs.get_commit_history(dashboard_id, limit=5) history = [h.message for h in history_objs if hasattr(h, "message")] - from ...plugins.llm_analysis.models import LLMProviderType - from ...plugins.llm_analysis.service import LLMClient - from ...services.llm_prompt_templates import ( + from src.plugins.llm_analysis.models import LLMProviderType + from src.plugins.llm_analysis.service import LLMClient + from src.services.llm_prompt_templates import ( DEFAULT_LLM_PROMPTS, normalize_llm_settings, resolve_bound_provider_id, ) - from ...services.llm_provider import LLMProviderService + from src.services.llm_provider import LLMProviderService llm_service = LLMProviderService(db) providers = llm_service.get_all_providers() @@ -325,7 +325,7 @@ async def generate_commit_message( default_model=provider.default_model, ) - from ...plugins.git.llm_extension import GitLLMExtension + from src.plugins.git.llm_extension import GitLLMExtension extension = GitLLMExtension(client) git_prompt = llm_settings["prompts"].get( diff --git a/backend/src/api/routes/translate/__init__.py b/backend/src/api/routes/translate/__init__.py index bd34d4a0..925b379e 100644 --- a/backend/src/api/routes/translate/__init__.py +++ b/backend/src/api/routes/translate/__init__.py @@ -1,15 +1,18 @@ # #region TranslateRoutes [C:4] [TYPE Module] [SEMANTICS translate, api, package, schedule, dashboard, llm] # @BRIEF API routes for LLM-based SQL/dashboard translation management including terminology dictionary CRUD and import. -# @LAYER: UI (API) -# @RELATION DEPENDS_ON -> [TranslateJobResponse] -# @RELATION DEPENDS_ON -> [DictionaryManager:Class] -# @RELATION DEPENDS_ON -> [get_current_user] -# @RELATION DEPENDS_ON -> [get_db] -# @RELATION DEPENDS_ON -> [has_permission] -# @RELATION DEPENDS_ON -> [ConfigManager] +# @LAYER API # @RELATION DEPENDS_ON -> [SupersetClient] -# @RATIONALE: Snapshot isolation — in-progress runs use config snapshot; config edits affect future runs only. -# @REJECTED: Invalidating in-progress runs on config edit would break scheduled run continuity. +# @RELATION DEPENDS_ON -> [SupersetClient] +# @RELATION DEPENDS_ON -> [SupersetClient] +# @RELATION DEPENDS_ON -> [SupersetClient] +# @RELATION DEPENDS_ON -> [SupersetClient] +# @RELATION DEPENDS_ON -> [SupersetClient] +# @RELATION DEPENDS_ON -> [SupersetClient] +# @RATIONALE Snapshot isolation — in-progress runs use config snapshot; config edits affect future runs only. +# @REJECTED Invalidating in-progress runs on config edit would break scheduled run continuity. +# @POST Translation job, run, dictionary, correction, preview, and schedule CRUD operations are executed. +# @PRE API server is running, user is authenticated, database is accessible. +# @SIDE_EFFECT Creates/modifies DB rows; triggers background translation runs; queries Superset API. """ Translate routes package. diff --git a/backend/src/api/routes/translate/_correction_routes.py b/backend/src/api/routes/translate/_correction_routes.py index 7230a2c1..26da8028 100644 --- a/backend/src/api/routes/translate/_correction_routes.py +++ b/backend/src/api/routes/translate/_correction_routes.py @@ -20,10 +20,12 @@ from ._router import router # Corrections # ============================================================ -# #region submit_correction [TYPE Function] +# #region submit_correction [C:4] [TYPE Function] # @BRIEF Submit a term correction from a run result review. -# @PRE: User has translate.dictionary.edit permission. -# @POST: Correction applied with conflict detection. +# @PRE User has translate.dictionary.edit permission. +# @POST Correction applied with conflict detection. +# @SIDE_EFFECT Creates/updates DictionaryEntry row; commits. +# @RELATION DEPENDS_ON -> [DictionaryManager] @router.post("/corrections") async def submit_correction( payload: TermCorrectionSubmit, @@ -55,10 +57,14 @@ async def submit_correction( # #endregion submit_correction -# #region submit_bulk_corrections [TYPE Function] +# #region submit_bulk_corrections [C:4] [TYPE Function] # @BRIEF Submit multiple term corrections atomically. -# @PRE: User has translate.dictionary.edit permission. -# @POST: All corrections applied or none with conflict list. +# @PRE User has translate.dictionary.edit permission. +# @POST All corrections applied or none with conflict list. +# @SIDE_EFFECT Creates/updates DictionaryEntry rows; commits once. +# @RELATION DEPENDS_ON -> [DictionaryManager] +# @COMPLEXITY 4 + @router.post("/corrections/bulk") async def submit_bulk_corrections( payload: TermCorrectionBulkSubmit, diff --git a/backend/src/api/routes/translate/_dictionary_routes.py b/backend/src/api/routes/translate/_dictionary_routes.py index 743b841b..74e86e27 100644 --- a/backend/src/api/routes/translate/_dictionary_routes.py +++ b/backend/src/api/routes/translate/_dictionary_routes.py @@ -22,7 +22,7 @@ from ._router import router # Terminology Dictionaries # ============================================================ -# #region list_dictionaries [TYPE Function] +# #region list_dictionaries [C:4] [TYPE Function] # @BRIEF List all terminology dictionaries. # @PRE: User has translate.dictionary.view permission. # @POST: Returns list of dictionaries with total count. @@ -46,7 +46,7 @@ async def list_dictionaries( # #endregion list_dictionaries -# #region get_dictionary [TYPE Function] +# #region get_dictionary [C:4] [TYPE Function] # @BRIEF Get a single terminology dictionary by ID. # @PRE: User has translate.dictionary.view permission. # @POST: Returns the dictionary with entry_count. @@ -71,7 +71,7 @@ async def get_dictionary( # #endregion get_dictionary -# #region create_dictionary [TYPE Function] +# #region create_dictionary [C:4] [TYPE Function] # @BRIEF Create a new terminology dictionary. # @PRE: User has translate.dictionary.create permission. # @POST: Returns the created dictionary. @@ -99,7 +99,7 @@ async def create_dictionary( # #endregion create_dictionary -# #region update_dictionary [TYPE Function] +# #region update_dictionary [C:4] [TYPE Function] # @BRIEF Update an existing terminology dictionary. # @PRE: User has translate.dictionary.edit permission. # @POST: Returns the updated dictionary. @@ -133,7 +133,7 @@ async def update_dictionary( # #endregion update_dictionary -# #region delete_dictionary [TYPE Function] +# #region delete_dictionary [C:4] [TYPE Function] # @BRIEF Delete a terminology dictionary, blocked if attached to active/scheduled jobs. # @PRE: User has translate.dictionary.delete permission. # @POST: Dictionary is deleted. @@ -162,7 +162,7 @@ async def delete_dictionary( # Dictionary Entries CRUD # ============================================================ -# #region list_dictionary_entries [TYPE Function] +# #region list_dictionary_entries [C:4] [TYPE Function] # @BRIEF List entries for a dictionary, optionally filtered by language pair. # @PRE: User has translate.dictionary.view permission. # @POST: Returns paginated list of entries with language pair fields. @@ -220,7 +220,7 @@ async def list_dictionary_entries( # #endregion list_dictionary_entries -# #region add_dictionary_entry [TYPE Function] +# #region add_dictionary_entry [C:4] [TYPE Function] # @BRIEF Add a new entry to a dictionary. # @PRE: User has translate.dictionary.edit permission. # @POST: Entry is created. @@ -269,7 +269,7 @@ async def add_dictionary_entry( # #endregion add_dictionary_entry -# #region edit_dictionary_entry [TYPE Function] +# #region edit_dictionary_entry [C:4] [TYPE Function] # @BRIEF Update an existing dictionary entry. # @PRE: User has translate.dictionary.edit permission. # @POST: Entry is updated. @@ -319,7 +319,7 @@ async def edit_dictionary_entry( # #endregion edit_dictionary_entry -# #region delete_dictionary_entry [TYPE Function] +# #region delete_dictionary_entry [C:4] [TYPE Function] # @BRIEF Delete a dictionary entry. # @PRE: User has translate.dictionary.edit permission. # @POST: Entry is deleted. @@ -343,10 +343,12 @@ async def delete_dictionary_entry( # #endregion delete_dictionary_entry -# #region import_dictionary_entries [TYPE Function] +# #region import_dictionary_entries [C:4] [TYPE Function] # @BRIEF Import entries into a terminology dictionary from CSV/TSV content. -# @PRE: User has translate.dictionary.edit permission. -# @POST: Entries are imported per conflict mode. +# @PRE User has translate.dictionary.edit permission. +# @POST Entries are imported per conflict mode. +# @COMPLEXITY 4 + @router.post("/dictionaries/{dictionary_id}/import") async def import_dictionary_entries( dictionary_id: str, diff --git a/backend/src/api/routes/translate/_helpers.py b/backend/src/api/routes/translate/_helpers.py index b1fa8a24..f6b751ec 100644 --- a/backend/src/api/routes/translate/_helpers.py +++ b/backend/src/api/routes/translate/_helpers.py @@ -1,6 +1,6 @@ # #region TranslateHelpersModule [C:2] [TYPE Module] [SEMANTICS sqlalchemy, translate, helper, query, mapping] # @BRIEF Shared helper functions for translate route handlers. -# @LAYER: API +# @LAYER API # @RELATION DEPENDS_ON -> [models.translate] from typing import Any diff --git a/backend/src/api/routes/translate/_job_routes.py b/backend/src/api/routes/translate/_job_routes.py index aeda6fda..9226ed74 100644 --- a/backend/src/api/routes/translate/_job_routes.py +++ b/backend/src/api/routes/translate/_job_routes.py @@ -31,7 +31,7 @@ from ._router import router # Translation Job CRUD # ============================================================ -# #region list_jobs [TYPE Function] +# #region list_jobs [C:4] [TYPE Function] # @BRIEF List all translation jobs. # @PRE: User has translate.job.view permission. # @POST: Returns list of translation jobs. @@ -64,7 +64,7 @@ async def list_jobs( # #endregion list_jobs -# #region get_job [TYPE Function] +# #region get_job [C:4] [TYPE Function] # @BRIEF Get a single translation job by ID. # @PRE: User has translate.job.view permission. # @POST: Returns the translation job. @@ -88,7 +88,7 @@ async def get_job( # #endregion get_job -# #region create_job [TYPE Function] +# #region create_job [C:4] [TYPE Function] # @BRIEF Create a new translation job. # @PRE: User has translate.job.create permission. # @POST: Returns the created translation job. @@ -113,7 +113,7 @@ async def create_job( # #endregion create_job -# #region update_job [TYPE Function] +# #region update_job [C:4] [TYPE Function] # @BRIEF Update an existing translation job. # @PRE: User has translate.job.edit permission. # @POST: Returns the updated translation job. @@ -139,7 +139,7 @@ async def update_job( # #endregion update_job -# #region delete_job [TYPE Function] +# #region delete_job [C:4] [TYPE Function] # @BRIEF Delete a translation job. # @PRE: User has translate.job.delete permission. # @POST: Job is deleted. @@ -161,7 +161,7 @@ async def delete_job( # #endregion delete_job -# #region duplicate_job [TYPE Function] +# #region duplicate_job [C:4] [TYPE Function] # @BRIEF Duplicate a translation job. # @PRE: User has translate.job.create permission. # @POST: Returns the duplicated job with status DRAFT. @@ -188,13 +188,14 @@ async def duplicate_job( # #endregion duplicate_job -# #region get_datasource_columns [TYPE Function] +# #region get_datasource_columns [C:4] [TYPE Function] # @BRIEF Get column metadata and database dialect for a Superset datasource. -# @PRE: User has translate.job.view permission. -# @POST: Returns column list with metadata and database_dialect. -# @SIDE_EFFECT: Queries Superset API for dataset detail and database info. -# @RATIONALE: Dialect detection from Superset connection cached on TranslationJob at save time. -# @REJECTED: Hardcoding dialect list per database — would drift from actual Superset config. +# @PRE User has translate.job.view permission. +# @POST Returns column list with metadata and database_dialect. +# @SIDE_EFFECT Queries Superset API for dataset detail and database info. +# @RATIONALE Dialect detection from Superset connection cached on TranslationJob at save time. +# @REJECTED Hardcoding dialect list per database — would drift from actual Superset config. +# @COMPLEXITY 4 @router.get("/datasources") async def list_datasources( diff --git a/backend/src/api/routes/translate/_metrics_routes.py b/backend/src/api/routes/translate/_metrics_routes.py index 167997a2..e57bb3a5 100644 --- a/backend/src/api/routes/translate/_metrics_routes.py +++ b/backend/src/api/routes/translate/_metrics_routes.py @@ -17,7 +17,7 @@ from ._router import router # Metrics # ============================================================ -# #region get_metrics [TYPE Function] +# #region get_metrics [C:4] [TYPE Function] # @BRIEF Get translation metrics, optionally filtered by job. # @PRE: User has translate.metrics.view permission. # @POST: Returns metrics data. @@ -40,10 +40,12 @@ async def get_metrics( # #endregion get_metrics -# #region get_job_metrics [TYPE Function] +# #region get_job_metrics [C:4] [TYPE Function] # @BRIEF Get aggregated metrics for a specific job. -# @PRE: User has translate.metrics.view permission. -# @POST: Returns metrics dict. +# @PRE User has translate.metrics.view permission. +# @POST Returns metrics dict. +# @COMPLEXITY 4 + @router.get("/jobs/{job_id}/metrics") async def get_job_metrics( job_id: str, diff --git a/backend/src/api/routes/translate/_preview_routes.py b/backend/src/api/routes/translate/_preview_routes.py index cf054cfe..13a8052f 100644 --- a/backend/src/api/routes/translate/_preview_routes.py +++ b/backend/src/api/routes/translate/_preview_routes.py @@ -23,7 +23,7 @@ from ._router import router # Preview # ============================================================ -# #region preview_translation [TYPE Function] +# #region preview_translation [C:4] [TYPE Function] # @BRIEF Preview a translation before applying it. # @PRE: User has translate.job.execute permission. # @POST: Returns a preview session with records and cost estimation. @@ -59,7 +59,7 @@ async def preview_translation( # #endregion preview_translation -# #region update_preview_row [TYPE Function] +# #region update_preview_row [C:4] [TYPE Function] # @BRIEF Approve, edit, or reject a preview row (optionally per language). # @PRE: User has translate.job.execute permission. # @POST: Preview row status is updated. @@ -91,7 +91,7 @@ async def update_preview_row( # #endregion update_preview_row -# #region accept_preview_session [TYPE Function] +# #region accept_preview_session [C:4] [TYPE Function] # @BRIEF Accept a preview session, marking it as the quality gate for full execution. # @PRE: User has translate.job.execute permission. Job has an ACTIVE preview session. # @POST: Preview session is marked as APPLIED; full execution can proceed. @@ -114,7 +114,7 @@ async def accept_preview_session( # #endregion accept_preview_session -# #region apply_preview [TYPE Function] +# #region apply_preview [C:4] [TYPE Function] # @BRIEF Apply a preview session (alias for accept when accepting at session level). # @PRE: User has translate.job.execute permission. # @POST: Preview is applied. @@ -146,10 +146,12 @@ async def apply_preview( # Preview Records # ============================================================ -# #region get_preview_records [TYPE Function] +# #region get_preview_records [C:4] [TYPE Function] # @BRIEF Get records for a preview session. -# @PRE: User has translate.job.view permission. -# @POST: Returns list of preview records. +# @PRE User has translate.job.view permission. +# @POST Returns list of preview records. +# @COMPLEXITY 4 + @router.get("/preview/{session_id}/records", response_model=list[PreviewRow]) async def get_preview_records( session_id: str, diff --git a/backend/src/api/routes/translate/_router.py b/backend/src/api/routes/translate/_router.py index 3a99a808..e1110ba3 100644 --- a/backend/src/api/routes/translate/_router.py +++ b/backend/src/api/routes/translate/_router.py @@ -6,6 +6,7 @@ from fastapi import APIRouter # #region translate_router [TYPE Global] # @BRIEF APIRouter instance for all translate sub-routes. + router = APIRouter(prefix="/api/translate", tags=["translate"]) # #endregion translate_router diff --git a/backend/src/api/routes/translate/_run_list_routes.py b/backend/src/api/routes/translate/_run_list_routes.py index a656d74c..89594dcc 100644 --- a/backend/src/api/routes/translate/_run_list_routes.py +++ b/backend/src/api/routes/translate/_run_list_routes.py @@ -19,7 +19,7 @@ from ._router import router # List Runs (cross-job) # ============================================================ -# #region list_runs [TYPE Function] +# #region list_runs [C:4] [TYPE Function] # @BRIEF List all runs with cross-job filtering and pagination. # @PRE: User has translate.history.view permission. # @POST: Returns paginated list of runs. @@ -126,7 +126,7 @@ async def list_runs( # Run Detail # ============================================================ -# #region get_run_detail [TYPE Function] +# #region get_run_detail [C:4] [TYPE Function] # @BRIEF Get detailed run info with config_snapshot, records, events. # @PRE: User has translate.history.view permission. # @POST: Returns run detail with records and events. @@ -169,10 +169,12 @@ async def get_run_detail( # Download Skipped CSV # ============================================================ -# #region download_skipped_csv [TYPE Function] +# #region download_skipped_csv [C:4] [TYPE Function] # @BRIEF Download a CSV of skipped translation records from a run. -# @PRE: User has translate.job.view permission. -# @POST: Returns CSV file of skipped records. +# @PRE User has translate.job.view permission. +# @POST Returns CSV file of skipped records. +# @COMPLEXITY 4 + @router.get("/runs/{run_id}/skipped.csv") async def download_skipped_csv( run_id: str, diff --git a/backend/src/api/routes/translate/_run_routes.py b/backend/src/api/routes/translate/_run_routes.py index cc518729..6180bda3 100644 --- a/backend/src/api/routes/translate/_run_routes.py +++ b/backend/src/api/routes/translate/_run_routes.py @@ -25,7 +25,7 @@ from ._router import router # Translation Run / Execute # ============================================================ -# #region run_translation [TYPE Function] +# #region run_translation [C:4] [TYPE Function] # @BRIEF Execute a translation job (trigger a run). # @PRE: User has translate.job.execute permission. # @POST: Returns the created translation run. @@ -164,7 +164,7 @@ def run_translation( # #endregion run_translation -# #region retry_run [TYPE Function] +# #region retry_run [C:4] [TYPE Function] # @BRIEF Retry failed batches in a translation run. # @PRE: User has translate.job.execute permission. # @POST: Returns the updated translation run. @@ -190,7 +190,7 @@ def retry_run( # #endregion retry_run -# #region retry_insert [TYPE Function] +# #region retry_insert [C:4] [TYPE Function] # @BRIEF Retry the SQL insert phase for a completed run. # @PRE: User has translate.job.execute permission. # @POST: Returns the updated run. @@ -216,7 +216,7 @@ def retry_insert( # #endregion retry_insert -# #region cancel_run [TYPE Function] +# #region cancel_run [C:4] [TYPE Function] # @BRIEF Cancel a running translation. # @PRE: User has translate.job.execute permission. # @POST: Run is cancelled. @@ -239,7 +239,7 @@ def cancel_run( # #endregion cancel_run -# #region get_run_history [TYPE Function] +# #region get_run_history [C:4] [TYPE Function] # @BRIEF Get run history for a translation job. # @PRE: User has translate.history.view permission. # @POST: Returns list of runs. @@ -264,7 +264,7 @@ def get_run_history( # #endregion get_run_history -# #region get_run_status [TYPE Function] +# #region get_run_status [C:4] [TYPE Function] # @BRIEF Get status and statistics for a translation run. # @PRE: User has translate.history.view permission. # @POST: Returns run details with statistics. @@ -286,7 +286,7 @@ def get_run_status( # #endregion get_run_status -# #region get_run_records [TYPE Function] +# #region get_run_records [C:4] [TYPE Function] # @BRIEF Get paginated records for a translation run. # @PRE: User has translate.history.view permission. # @POST: Returns paginated records. @@ -315,7 +315,7 @@ def get_run_records( # Batches # ============================================================ -# #region get_batches [TYPE Function] +# #region get_batches [C:4] [TYPE Function] # @BRIEF Get batches for a translation run. # @PRE: User has translate.job.view permission. # @POST: Returns list of batches. @@ -360,7 +360,7 @@ def get_batches( # Manual Language Override # ============================================================ -# #region override_detected_language [TYPE Function] +# #region override_detected_language [C:4] [TYPE Function] # @BRIEF Manually override the detected source language for a specific translation language entry. # @PRE: User has translate.job.execute permission. Run, record, and language entry exist. # @POST: TranslationLanguage.source_language_detected is updated; language_overridden is set to True. @@ -439,7 +439,7 @@ def override_detected_language( # Inline Correction # ============================================================ -# #region inline_edit_translation [C:3] [TYPE Function] [SEMANTICS api,translate,correction] +# #region inline_edit_translation [C:4] [TYPE Function] [SEMANTICS api,translate,correction] # @BRIEF Apply an inline correction to a translated value on a completed run result. # @PRE: User has translate.job.execute permission. Run, record, and language entry exist. # @POST: TranslationLanguage.final_value and user_edit are updated. Optional dictionary submission. @@ -488,10 +488,12 @@ def inline_edit_translation( # Bulk Find-and-Replace # ============================================================ -# #region bulk_find_replace [C:3] [TYPE Function] [SEMANTICS api,translate,bulk,replace] +# #region bulk_find_replace [C:4] [TYPE Function] [SEMANTICS api,translate,bulk,replace] # @BRIEF Perform bulk find-and-replace on translated values within a run. -# @PRE: User has translate.job.execute permission. Run exists. -# @POST: If preview=false, matching translations are updated. Optional dictionary submission. +# @PRE User has translate.job.execute permission. Run exists. +# @POST If preview=false, matching translations are updated. Optional dictionary submission. +# @COMPLEXITY 4 + @router.post("/runs/{run_id}/bulk-replace") def bulk_find_replace( run_id: str, diff --git a/backend/src/api/routes/translate/_schedule_routes.py b/backend/src/api/routes/translate/_schedule_routes.py index b70a205b..211502f9 100644 --- a/backend/src/api/routes/translate/_schedule_routes.py +++ b/backend/src/api/routes/translate/_schedule_routes.py @@ -18,7 +18,7 @@ from ._router import router # Schedule # ============================================================ -# #region get_schedule [TYPE Function] +# #region get_schedule [C:4] [TYPE Function] # @BRIEF Get the schedule for a translation job. # @PRE: User has translate.schedule.view permission. # @POST: Returns the schedule configuration. @@ -53,7 +53,7 @@ async def get_schedule( # #endregion get_schedule -# #region set_schedule [TYPE Function] +# #region set_schedule [C:4] [TYPE Function] # @BRIEF Set or update the schedule for a translation job. # @PRE: User has translate.schedule.manage permission. # @POST: Schedule is created or updated. @@ -119,7 +119,7 @@ async def set_schedule( # #endregion set_schedule -# #region enable_schedule [TYPE Function] +# #region enable_schedule [C:4] [TYPE Function] # @BRIEF Enable a schedule for a translation job. # @PRE: User has translate.schedule.manage permission. # @POST: Schedule is enabled. @@ -151,7 +151,7 @@ async def enable_schedule( # #endregion enable_schedule -# #region disable_schedule [TYPE Function] +# #region disable_schedule [C:4] [TYPE Function] # @BRIEF Disable a schedule for a translation job. # @PRE: User has translate.schedule.manage permission. # @POST: Schedule is disabled. @@ -177,7 +177,7 @@ async def disable_schedule( # #endregion disable_schedule -# #region delete_schedule [TYPE Function] +# #region delete_schedule [C:4] [TYPE Function] # @BRIEF Delete the schedule for a translation job. # @PRE: User has translate.schedule.manage permission. # @POST: Schedule is removed. @@ -203,10 +203,12 @@ async def delete_schedule( # #endregion delete_schedule -# #region get_next_executions [TYPE Function] +# #region get_next_executions [C:4] [TYPE Function] # @BRIEF Preview next N executions for a job's schedule. -# @PRE: User has translate.schedule.view permission. -# @POST: Returns next execution times. +# @PRE User has translate.schedule.view permission. +# @POST Returns next execution times. +# @COMPLEXITY 4 + @router.get("/jobs/{job_id}/schedule/next-executions") async def get_next_executions( job_id: str, diff --git a/backend/src/models/translate.py b/backend/src/models/translate.py index 4ffcccf8..4593d6dd 100644 --- a/backend/src/models/translate.py +++ b/backend/src/models/translate.py @@ -1,7 +1,7 @@ # #region TranslateModels [C:3] [TYPE Module] [SEMANTICS sqlalchemy, translate, model, schema, dashboard, llm] # @BRIEF SQLAlchemy ORM models for LLM-based SQL/dashboard translation across dialects. # @LAYER: Domain -# @RELATION INHERITS_FROM -> MappingModels:Base +# @RELATION INHERITS -> [MappingModels] import uuid from datetime import UTC, datetime diff --git a/backend/src/plugins/git_plugin.py b/backend/src/plugins/git_plugin.py index bf203d07..749ce062 100644 --- a/backend/src/plugins/git_plugin.py +++ b/backend/src/plugins/git_plugin.py @@ -1,4 +1,4 @@ -# #region GitPluginModule [TYPE Module] [SEMANTICS git, versioning, deploy, sync, superset] +# #region GitPluginModule [C:4] [TYPE Module] [SEMANTICS git, versioning, deploy, sync, superset, backup, transactional] # # @BRIEF Предоставляет плагин для версионирования и развертывания дашбордов Superset. # @LAYER: Plugin @@ -10,10 +10,13 @@ # # @INVARIANT: Все операции с Git должны выполняться через GitService. # @CONSTRAINT: Плагин работает только с распакованными YAML-экспортами Superset. +# @INVARIANT: _handle_sync сохраняет backup управляемых директорий перед удалением; +# при ошибке распаковки backup восстанавливается. import io import os import shutil +import time import zipfile from pathlib import Path from typing import Any @@ -62,52 +65,33 @@ class GitPlugin(PluginBase): # endregion __init__ @property - # region id [TYPE Function] - # @PURPOSE: Returns the plugin identifier. - # @PRE: GitPlugin is initialized. - # @POST: Returns 'git-integration'. + # region id [C:1] [TYPE Function] def id(self) -> str: - with belief_scope("GitPlugin.id"): - return "git-integration" + return "git-integration" # endregion id @property - # region name [TYPE Function] - # @PURPOSE: Returns the plugin name. - # @PRE: GitPlugin is initialized. - # @POST: Returns the human-readable name. + # region name [C:1] [TYPE Function] def name(self) -> str: - with belief_scope("GitPlugin.name"): - return "Git Integration" + return "Git Integration" # endregion name @property - # region description [TYPE Function] - # @PURPOSE: Returns the plugin description. - # @PRE: GitPlugin is initialized. - # @POST: Returns the plugin's purpose description. + # region description [C:1] [TYPE Function] def description(self) -> str: - with belief_scope("GitPlugin.description"): - return "Version control for Superset dashboards" + return "Version control for Superset dashboards" # endregion description @property - # region version [TYPE Function] - # @PURPOSE: Returns the plugin version. - # @PRE: GitPlugin is initialized. - # @POST: Returns the version string. + # region version [C:1] [TYPE Function] def version(self) -> str: - with belief_scope("GitPlugin.version"): - return "0.1.0" + return "0.1.0" # endregion version @property - # region ui_route [TYPE Function] - # @PURPOSE: Returns the frontend route for the git plugin. - # @RETURN: str - "/git" + # region ui_route [C:1] [TYPE Function] def ui_route(self) -> str: - with belief_scope("GitPlugin.ui_route"): - return "/git" + return "/git" # endregion ui_route # region get_schema [TYPE Function] @@ -129,21 +113,13 @@ class GitPlugin(PluginBase): } # endregion get_schema - # region initialize [TYPE Function] - # @PURPOSE: Выполняет начальную настройку плагина. - # @PRE: GitPlugin is initialized. - # @POST: Плагин готов к выполнению задач. + # region initialize [C:1] [TYPE Function] async def initialize(self): - with belief_scope("GitPlugin.initialize"): - app_logger.reason("Initializing Git Integration Plugin logic.", extra={"src": "GitPlugin.initialize"}) + app_logger.reason("Initializing Git Integration Plugin logic.", extra={"src": "GitPlugin.initialize"}) + # endregion initialize - # region execute [TYPE Function] - # @PURPOSE: Основной метод выполнения задач плагина с поддержкой TaskContext. - # @PRE: task_data содержит 'operation' и 'dashboard_id'. - # @POST: Возвращает результат выполнения операции. - # @PARAM: task_data (Dict[str, Any]) - Данные задачи. - # @PARAM: context (Optional[TaskContext]) - Task context for logging with source attribution. - # @RETURN: Dict[str, Any] - Статус и сообщение. + # region execute [C:3] [TYPE Function] + # @PURPOSE: Main task executor with TaskContext support. # @RELATION: CALLS -> self._handle_sync # @RELATION: CALLS -> self._handle_deploy async def execute(self, task_data: dict[str, Any], context: TaskContext | None = None) -> dict[str, Any]: @@ -176,41 +152,48 @@ class GitPlugin(PluginBase): return result # endregion execute - # region _handle_sync [TYPE Function] - # @PURPOSE: Экспортирует дашборд из Superset и распаковывает в Git-репозиторий. + # region _handle_sync [C:4] [TYPE Function] [SEMANTICS git,sync,backup,transactional] + # @PURPOSE: Экспортирует дашборд из Superset и распаковывает в Git-репозиторий с backup/restore. # @PRE: Репозиторий для дашборда должен существовать. # @POST: Файлы в репозитории обновлены до текущего состояния в Superset. - # @PARAM: dashboard_id (int) - ID дашборда. - # @PARAM: source_env_id (Optional[str]) - ID исходного окружения. - # @RETURN: Dict[str, str] - Результат синхронизации. + # Управляемые директории backup-ируются перед удалением; при ошибке распаковки backup восстанавливается. # @SIDE_EFFECT: Изменяет файлы в локальной рабочей директории репозитория. + # Создаёт временный backup в /tmp/ss-tools-backup-{dashboard_id}-{timestamp}/. + # @RETURN: Dict[str, str] - Результат синхронизации. # @RELATION: CALLS -> src.services.git_service.GitService.get_repo # @RELATION: CALLS -> src.core.superset_client.SupersetClient.export_dashboard async def _handle_sync(self, dashboard_id: int, source_env_id: str | None = None, log=None, git_log=None, superset_log=None) -> dict[str, str]: with belief_scope("GitPlugin._handle_sync"): try: - # 1. Получение репозитория repo = self.git_service.get_repo(dashboard_id) repo_path = Path(repo.working_dir) git_log.info(f"Target repo path: {repo_path}") - # 2. Настройка клиента Superset env = self._get_env(source_env_id) client = SupersetClient(env) client.authenticate() - # 3. Экспорт дашборда superset_log.info(f"Exporting dashboard {dashboard_id} from {env.name}") zip_bytes, _ = client.export_dashboard(dashboard_id) - # 4. Распаковка с выравниванием структуры (flattening) git_log.info(f"Unpacking export to {repo_path}") - # Список папок/файлов, которые мы ожидаем от Superset managed_dirs = ["dashboards", "charts", "datasets", "databases"] managed_files = ["metadata.yaml"] - # Очистка старых данных перед распаковкой, чтобы не оставалось "призраков" + # Fix 1: Create backup before deleting managed files + backup_dir = Path(f"/tmp/ss-tools-backup-{dashboard_id}-{time.time_ns()}") + for d in managed_dirs: + d_path = repo_path / d + if d_path.exists() and d_path.is_dir(): + shutil.copytree(d_path, backup_dir / d) + for f in managed_files: + f_path = repo_path / f + if f_path.exists(): + (backup_dir / f).parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(f_path, backup_dir / f) + + # Delete old managed files for d in managed_dirs: d_path = repo_path / d if d_path.exists() and d_path.is_dir(): @@ -220,30 +203,45 @@ class GitPlugin(PluginBase): if f_path.exists(): f_path.unlink() - with zipfile.ZipFile(io.BytesIO(zip_bytes)) as zf: - # Superset экспортирует всё в подпапку dashboard_export_timestamp/ - # Нам нужно найти это имя папки - namelist = zf.namelist() - if not namelist: - raise ValueError("Export ZIP is empty") + try: + with zipfile.ZipFile(io.BytesIO(zip_bytes)) as zf: + namelist = zf.namelist() + if not namelist: + raise ValueError("Export ZIP is empty") + root_folder = namelist[0].split('/')[0] + git_log.info(f"Detected root folder in ZIP: {root_folder}") + for member in zf.infolist(): + if member.filename.startswith(root_folder + "/") and len(member.filename) > len(root_folder) + 1: + relative_path = member.filename[len(root_folder)+1:] + target_path = repo_path / relative_path + if member.is_dir(): + target_path.mkdir(parents=True, exist_ok=True) + else: + target_path.parent.mkdir(parents=True, exist_ok=True) + with zf.open(member) as source, open(target_path, "wb") as target: + shutil.copyfileobj(source, target) + except Exception: + # Fix 1: Restore backup on unzip failure + if backup_dir.exists(): + for d in managed_dirs: + src = backup_dir / d + if src.exists(): + dst = repo_path / d + if dst.exists(): + shutil.rmtree(dst) + shutil.copytree(src, dst) + for f in managed_files: + src = backup_dir / f + if src.exists(): + dst = repo_path / f + dst.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(src, dst) + raise - root_folder = namelist[0].split('/')[0] - git_log.info(f"Detected root folder in ZIP: {root_folder}") + # Fix 1: Remove backup on success + if backup_dir.exists(): + shutil.rmtree(backup_dir, ignore_errors=True) - for member in zf.infolist(): - if member.filename.startswith(root_folder + "/") and len(member.filename) > len(root_folder) + 1: - # Убираем префикс папки - relative_path = member.filename[len(root_folder)+1:] - target_path = repo_path / relative_path - - if member.is_dir(): - target_path.mkdir(parents=True, exist_ok=True) - else: - target_path.parent.mkdir(parents=True, exist_ok=True) - with zf.open(member) as source, open(target_path, "wb") as target: - shutil.copyfileobj(source, target) - - # 5. Автоматический staging изменений (не коммит, чтобы юзер мог проверить diff) try: repo.git.add(A=True) app_logger.reason("Changes staged in git", extra={"src": "_handle_sync"}) @@ -258,35 +256,21 @@ class GitPlugin(PluginBase): raise # endregion _handle_sync - # region _handle_deploy [TYPE Function] - # @PURPOSE: Упаковывает репозиторий в ZIP и импортирует в целевое окружение Superset. - # @PRE: environment_id должен соответствовать настроенному окружению. - # @POST: Дашборд импортирован в целевой Superset. - # @PARAM: dashboard_id (int) - ID дашборда. - # @PARAM: env_id (str) - ID целевого окружения. - # @PARAM: log - Main logger instance. - # @PARAM: git_log - Git-specific logger instance. - # @PARAM: superset_log - Superset API-specific logger instance. - # @RETURN: Dict[str, Any] - Результат деплоя. - # @SIDE_EFFECT: Создает и удаляет временный ZIP-файл. + # region _handle_deploy [C:4] [TYPE Function] [SEMANTICS git,deploy,zip] + # @PURPOSE: Packages repository into ZIP and imports into target Superset environment. + # @POST: Dashboard imported into target Superset. + # @SIDE_EFFECT: Creates and removes temporary ZIP file. # @RELATION: CALLS -> src.core.superset_client.SupersetClient.import_dashboard async def _handle_deploy(self, dashboard_id: int, env_id: str, log=None, git_log=None, superset_log=None) -> dict[str, Any]: with belief_scope("GitPlugin._handle_deploy"): try: if not env_id: raise ValueError("Target environment ID required for deployment") - - # 1. Получение репозитория repo = self.git_service.get_repo(dashboard_id) repo_path = Path(repo.working_dir) - - # 2. Упаковка в ZIP git_log.info(f"Packing repository {repo_path} for deployment.") zip_buffer = io.BytesIO() - - # Superset expects a root directory in the ZIP (e.g., dashboard_export_20240101T000000/) root_dir_name = f"dashboard_export_{dashboard_id}" - with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zf: for root, dirs, files in os.walk(repo_path): if ".git" in dirs: @@ -295,21 +279,14 @@ class GitPlugin(PluginBase): if file == ".git" or file.endswith(".zip"): continue file_path = Path(root) / file - # Prepend the root directory name to the archive path arcname = Path(root_dir_name) / file_path.relative_to(repo_path) zf.write(file_path, arcname) - zip_buffer.seek(0) - - # 3. Настройка клиента Superset env = self.config_manager.get_environment(env_id) if not env: raise ValueError(f"Environment {env_id} not found") - client = SupersetClient(env) client.authenticate() - - # 4. Импорт temp_zip_path = repo_path / f"deploy_{dashboard_id}.zip" git_log.info(f"Saving temporary zip to {temp_zip_path}") with open(temp_zip_path, "wb") as f: @@ -329,11 +306,12 @@ class GitPlugin(PluginBase): raise # endregion _handle_deploy - # region _get_env [TYPE Function] + # region _get_env [C:4] [TYPE Function] [SEMANTICS env,config,strict] # @PURPOSE: Вспомогательный метод для получения конфигурации окружения. # @PARAM: env_id (Optional[str]) - ID окружения. # @PRE: env_id is a string or None. # @POST: Returns an Environment object from config or DB. + # When env_id is explicitly provided and not found, raises ValueError (no fallback). # @RETURN: Environment - Объект конфигурации окружения. def _get_env(self, env_id: str | None = None): with belief_scope("GitPlugin._get_env"): @@ -346,54 +324,46 @@ class GitPlugin(PluginBase): app_logger.reflect(f"Found environment by ID in ConfigManager: {env.name}", extra={"src": "_get_env"}) return env - # Priority 2: Database (DeploymentEnvironment) - from src.core.database import SessionLocal - from src.models.git import DeploymentEnvironment + # Priority 2: Database (DeploymentEnvironment) + from src.core.database import SessionLocal + from src.models.git import DeploymentEnvironment - db = SessionLocal() - try: - if env_id: - db_env = db.query(DeploymentEnvironment).filter(DeploymentEnvironment.id == env_id).first() - else: - # If no ID, try to find active or any environment in DB - db_env = db.query(DeploymentEnvironment).filter(DeploymentEnvironment.is_active).first() - if not db_env: - db_env = db.query(DeploymentEnvironment).first() + db = SessionLocal() + try: + if env_id: + db_env = db.query(DeploymentEnvironment).filter(DeploymentEnvironment.id == env_id).first() + else: + db_env = db.query(DeploymentEnvironment).filter(DeploymentEnvironment.is_active).first() + if not db_env: + db_env = db.query(DeploymentEnvironment).first() - if db_env: - app_logger.reflect(f"Found environment in DB: {db_env.name}", extra={"src": "_get_env"}) - from src.core.config_models import Environment - # Use token as password for SupersetClient - return Environment( - id=db_env.id, - name=db_env.name, - url=db_env.superset_url, - username="admin", - password=db_env.superset_token, - verify_ssl=True - ) - finally: - db.close() + if db_env: + app_logger.reflect(f"Found environment in DB: {db_env.name}", extra={"src": "_get_env"}) + from src.core.config_models import Environment + return Environment( + id=db_env.id, + name=db_env.name, + url=db_env.superset_url, + username="admin", + password=db_env.superset_token, + verify_ssl=True + ) - # Priority 3: ConfigManager Default (if no env_id provided) - envs = self.config_manager.get_environments() - if envs: - if env_id: - # If env_id was provided but not found in DB or specifically by ID in config, - # but we have other envs, maybe it's one of them? - env = next((e for e in envs if e.id == env_id), None) - if env: - app_logger.reflect(f"Found environment {env_id} in ConfigManager list", extra={"src": "_get_env"}) - return env + # Fix 7: Strict check — if env_id was explicitly provided but not found, raise immediately + if env_id: + raise ValueError(f"Environment '{env_id}' not found in ConfigManager or database") + finally: + db.close() - if not env_id: - app_logger.reflect(f"Using first environment from ConfigManager: {envs[0].name}", extra={"src": "_get_env"}) - return envs[0] + # Priority 3: fallback to first env only when no specific env_id was requested + envs = self.config_manager.get_environments() + if envs and not env_id: + app_logger.reflect(f"Using first environment from ConfigManager: {envs[0].name}", extra={"src": "_get_env"}) + return envs[0] - app_logger.error(f"[_get_env][Coherence:Failed] No environments configured (searched config.json and DB). env_id={env_id}") - raise ValueError("No environments configured. Please add a Superset Environment in Settings.") + app_logger.error(f"[_get_env][Coherence:Failed] No environments configured (searched config.json and DB). env_id={env_id}") + raise ValueError("No environments configured. Please add a Superset Environment in Settings.") # endregion _get_env - # endregion initialize # #endregion GitPlugin # #endregion GitPluginModule diff --git a/backend/src/plugins/translate/__init__.py b/backend/src/plugins/translate/__init__.py index 0f11335e..fd8f88d4 100644 --- a/backend/src/plugins/translate/__init__.py +++ b/backend/src/plugins/translate/__init__.py @@ -1,3 +1,2 @@ # #region TranslatePluginPackage [TYPE Package] [SEMANTICS translate, plugin, package, sql, dashboard] -# @BRIEF Translation plugin package for LLM-based cross-Superset SQL/dashboard translation. # #endregion TranslatePluginPackage diff --git a/backend/src/plugins/translate/_token_budget.py b/backend/src/plugins/translate/_token_budget.py index d9f1131b..bce098cf 100644 --- a/backend/src/plugins/translate/_token_budget.py +++ b/backend/src/plugins/translate/_token_budget.py @@ -9,24 +9,24 @@ # Fixed batch_size of 50 — causes truncation on long-content rows. # #region DEFAULT_CONTEXT_WINDOW [TYPE Constant] -# @BRIEF Default context window for DeepSeek v4 Flash: up to 64K tokens. + DEFAULT_CONTEXT_WINDOW = 64000 # #endregion DEFAULT_CONTEXT_WINDOW - # #region DEFAULT_MAX_OUTPUT_TOKENS [TYPE Constant] -# @BRIEF Default max_tokens setting for LLM output (16384 — sufficient for 50 rows x 4 languages). +# #region DEFAULT_MAX_OUTPUT_TOKENS [TYPE Constant] + DEFAULT_MAX_OUTPUT_TOKENS = 16384 # #endregion DEFAULT_MAX_OUTPUT_TOKENS - -# #region REASONING_OVERHEAD [TYPE Constant] # @BRIEF CoT reasoning overhead tokens for DeepSeek models (~2000 tokens for chain-of-thought). +# #region REASONING_OVERHEAD [TYPE Constant] + REASONING_OVERHEAD = 2000 # #endregion REASONING_OVERHEAD # #region PROVIDER_DEFAULTS [TYPE Constant] -# @BRIEF Provider-aware defaults for context_window and max_output_tokens. + # Maps model name (or "default" fallback) to capacity limits. -# @RATIONALE: Different providers have drastically different context windows and +# @RATIONALE Different providers have drastically different context windows and # output limits. Using a single default for all causes either wasted # capacity (underestimation) or truncation (overestimation). PROVIDER_DEFAULTS: dict[str, dict[str, int]] = { @@ -38,46 +38,46 @@ PROVIDER_DEFAULTS: dict[str, dict[str, int]] = { "default": {"context_window": 64000, "max_output_tokens": 16384}, } # #endregion PROVIDER_DEFAULTS - +# Increased from 60 to 120 because SQL/dashboard text and JSON structure need more. # #region OUTPUT_PER_ROW_PER_LANG [TYPE Constant] -# @BRIEF Estimated output tokens per row per language in JSON response format. + # Increased from 60 to 120 because SQL/dashboard text and JSON structure need more. OUTPUT_PER_ROW_PER_LANG = 120 # #endregion OUTPUT_PER_ROW_PER_LANG - +# #endregion JSON_OVERHEAD_PER_ROW # #region JSON_OVERHEAD_PER_ROW [TYPE Constant] -# @BRIEF Estimated overhead tokens for JSON keys, brackets, and formatting per row. + JSON_OVERHEAD_PER_ROW = 50 # #endregion JSON_OVERHEAD_PER_ROW - +# #endregion PROMPT_BASE_TOKENS # #region PROMPT_BASE_TOKENS [TYPE Constant] -# @BRIEF Base tokens for system prompt + instructions + dictionary section + JSON format specification. + # Increased from 300 to 600 to account for longer template, system msg, and dict preamble. PROMPT_BASE_TOKENS = 600 # #endregion PROMPT_BASE_TOKENS - +# @BRIEF Cap for dictionary tokens in estimation to avoid overestimating. # #region DICT_TOKENS_PER_ENTRY [TYPE Constant] -# @BRIEF Estimated tokens per dictionary entry in the glossary section. + DICT_TOKENS_PER_ENTRY = 20 # #endregion DICT_TOKENS_PER_ENTRY # #region DICT_TOKENS_MAX [TYPE Constant] -# @BRIEF Cap for dictionary tokens in estimation to avoid overestimating. + DICT_TOKENS_MAX = 5000 # #endregion DICT_TOKENS_MAX - +MIN_MAX_TOKENS = 4096 # #region CHARS_PER_TOKEN_MIXED [TYPE Constant] -# @BRIEF Characters per token for mixed Russian/English text (empirical ~2.2 for mixed, ~2 for Russian, ~4 for English). + CHARS_PER_TOKEN_MIXED = 2.2 # #endregion CHARS_PER_TOKEN_MIXED - +MAX_OUTPUT_HEADROOM = 3000 # #region MIN_MAX_TOKENS [TYPE Constant] -# @BRIEF Minimum max_tokens to allow (4096 ensures reasonable output even for small batches). + MIN_MAX_TOKENS = 4096 # #endregion MIN_MAX_TOKENS - +# #endregion MIN_MAX_TOKENS # #region MAX_OUTPUT_HEADROOM [TYPE Constant] -# @BRIEF Extra headroom added to max_output_needed beyond the estimate (3000 = 10-20% for variance). + # Increased from 1000 to 3000 because SQL/dashboard text output varies significantly. MAX_OUTPUT_HEADROOM = 3000 # #endregion MAX_OUTPUT_HEADROOM diff --git a/backend/src/plugins/translate/dictionary.py b/backend/src/plugins/translate/dictionary.py index db6dbbc5..98fb6663 100644 --- a/backend/src/plugins/translate/dictionary.py +++ b/backend/src/plugins/translate/dictionary.py @@ -1,13 +1,16 @@ # #region DictionaryManagerModule [C:4] [TYPE Module] [SEMANTICS sqlalchemy, translate, dictionary, batch, term] # @BRIEF Business logic for terminology dictionary management, entry CRUD, CSV/TSV import with conflict detection, and per-batch filtering. -# @LAYER: Domain -# @RELATION DEPENDS_ON -> [TerminologyDictionary:Class] -# @RELATION DEPENDS_ON -> [DictionaryEntry:Class] -# @RELATION DEPENDS_ON -> [TranslationJobDictionary:Class] +# @LAYER Domain # @RELATION DEPENDS_ON -> [TranslationJob:Class] -# @RATIONALE: C4 complexity because dictionary CRUD is stateful with referential integrity enforcement on deletion and unique constraint on normalized source term. -# @REJECTED: Pure C3 CRUD without state guards would allow orphaned job-dictionary links. -# @REJECTED: "Keep both" as conflict option — UniqueConstraint(dictionary_id, source_term_normalized) prohibits variants; only overwrite/keep existing. +# @RELATION DEPENDS_ON -> [TranslationJob:Class] +# @RELATION DEPENDS_ON -> [TranslationJob:Class] +# @RELATION DEPENDS_ON -> [TranslationJob:Class] +# @RATIONALE C4 complexity because dictionary CRUD is stateful with referential integrity enforcement on deletion and unique constraint on normalized source term. +# @REJECTED "Keep both" as conflict option — UniqueConstraint(dictionary_id, source_term_normalized) prohibits variants; only overwrite/keep existing. +# @REJECTED "Keep both" as conflict option — UniqueConstraint(dictionary_id, source_term_normalized) prohibits variants; only overwrite/keep existing. +# @POST Dictionary and entry mutations are persisted with conflict detection. +# @PRE Database session is open and valid. Dictionary manager is properly initialized. +# @SIDE_EFFECT Creates, updates, deletes TerminologyDictionary and DictionaryEntry rows; enforces deletion guards. import csv import io @@ -42,19 +45,18 @@ def _validate_bcp47(tag: str, field_name: str) -> None: f"{field_name} is not a valid BCP-47 tag: '{tag}'. " "Expected format like 'en', 'ru', 'zh-CN', 'pt-BR'." ) -# #endregion _validate_bcp47 - - # #region DictionaryManager [C:4] [TYPE Class] # @BRIEF Manages terminology dictionaries and their entries with referential integrity. -# @PRE: Database session is open and valid. -# @POST: Dictionary and entry mutations are persisted with conflict detection. -# @SIDE_EFFECT: Creates, updates, deletes TerminologyDictionary and DictionaryEntry rows; enforces deletion guards. +# @PRE Database session is open and valid. +# @POST Dictionary and entry mutations are persisted with conflict detection. +# @SIDE_EFFECT Creates, updates, deletes TerminologyDictionary and DictionaryEntry rows; enforces deletion guards. +# @RELATION DEPENDS_ON -> [TerminologyDictionary:Class], DEPENDS_ON -> [DictionaryEntry:Class], DEPENDS_ON -> [TranslationJobDictionary:Class], DEPENDS_ON -> [TranslationJob:Class] + class DictionaryManager: # region DictionaryManager.create_dictionary [TYPE Function] - # @PURPOSE: Create a new terminology dictionary (language independent at dictionary level; language pairs are per-entry). - # @PRE: name is provided. - # @POST: New TerminologyDictionary row is created and returned. + # @PURPOSE Create a new terminology dictionary (language independent at dictionary level; language pairs are per-entry). + # @PRE name is provided. + # @POST New TerminologyDictionary row is created and returned. @staticmethod def create_dictionary( db: Session, name: str, @@ -81,9 +83,9 @@ class DictionaryManager: # endregion DictionaryManager.create_dictionary # region DictionaryManager.update_dictionary [TYPE Function] - # @PURPOSE: Update an existing terminology dictionary. - # @PRE: dict_id exists in terminology_dictionaries table. - # @POST: Dictionary metadata is updated and returned. + # @PURPOSE Update an existing terminology dictionary. + # @PRE dict_id exists in terminology_dictionaries table. + # @POST Dictionary metadata is updated and returned. @staticmethod def update_dictionary( db: Session, dict_id: str, name: str | None = None, @@ -112,10 +114,10 @@ class DictionaryManager: # endregion DictionaryManager.update_dictionary # region DictionaryManager.delete_dictionary [TYPE Function] - # @PURPOSE: Delete a dictionary, blocked if attached to active/scheduled jobs. - # @PRE: dict_id exists. - # @POST: Dictionary and its entries are deleted, unless attached to ACTIVE/READY/RUNNING/SCHEDULED jobs. - # @SIDE_EFFECT: Deletes TerminologyDictionary row and all DictionaryEntry rows. + # @PURPOSE Delete a dictionary, blocked if attached to active/scheduled jobs. + # @PRE dict_id exists. + # @POST Dictionary and its entries are deleted, unless attached to ACTIVE/READY/RUNNING/SCHEDULED jobs. + # @SIDE_EFFECT Deletes TerminologyDictionary row and all DictionaryEntry rows. @staticmethod def delete_dictionary(db: Session, dict_id: str) -> None: with belief_scope("DictionaryManager.delete_dictionary"): @@ -155,9 +157,9 @@ class DictionaryManager: # endregion DictionaryManager.delete_dictionary # region DictionaryManager.get_dictionary [TYPE Function] - # @PURPOSE: Get a single dictionary by ID with entry count. - # @PRE: dict_id exists. - # @POST: Returns dict with dictionary + entry_count or raises ValueError. + # @PURPOSE Get a single dictionary by ID with entry count. + # @PRE dict_id exists. + # @POST Returns dict with dictionary + entry_count or raises ValueError. @staticmethod def get_dictionary(db: Session, dict_id: str) -> TerminologyDictionary: dictionary = db.query(TerminologyDictionary).filter(TerminologyDictionary.id == dict_id).first() @@ -167,9 +169,9 @@ class DictionaryManager: # endregion DictionaryManager.get_dictionary # region DictionaryManager.list_dictionaries [TYPE Function] - # @PURPOSE: List dictionaries with pagination and entry counts. - # @PRE: page >= 1, page_size between 1 and 100. - # @POST: Returns (list of dicts, total_count). + # @PURPOSE List dictionaries with pagination and entry counts. + # @PRE page >= 1, page_size between 1 and 100. + # @POST Returns (list of dicts, total_count). @staticmethod def list_dictionaries( db: Session, page: int = 1, page_size: int = 20, @@ -186,9 +188,9 @@ class DictionaryManager: # endregion DictionaryManager.list_dictionaries # region DictionaryManager.add_entry [TYPE Function] - # @PURPOSE: Add an entry to a dictionary with language-pair-aware uniqueness validation. - # @PRE: dict_id exists. source_term, target_term are non-empty. source_language, target_language are valid BCP-47. - # @POST: New DictionaryEntry row is created or raises on duplicate. + # @PURPOSE Add an entry to a dictionary with language-pair-aware uniqueness validation. + # @PRE dict_id exists. source_term, target_term are non-empty. source_language, target_language are valid BCP-47. + # @POST New DictionaryEntry row is created or raises on duplicate. @staticmethod def add_entry( db: Session, dict_id: str, source_term: str, target_term: str, @@ -237,9 +239,9 @@ class DictionaryManager: # endregion DictionaryManager.add_entry # region DictionaryManager.edit_entry [TYPE Function] - # @PURPOSE: Edit an existing dictionary entry with language-pair-aware duplicate check. - # @PRE: entry_id exists. - # @POST: Entry fields are updated. + # @PURPOSE Edit an existing dictionary entry with language-pair-aware duplicate check. + # @PRE entry_id exists. + # @POST Entry fields are updated. @staticmethod def edit_entry( db: Session, entry_id: str, source_term: str | None = None, @@ -293,9 +295,9 @@ class DictionaryManager: # endregion DictionaryManager.edit_entry # region DictionaryManager.delete_entry [TYPE Function] - # @PURPOSE: Delete a single dictionary entry. - # @PRE: entry_id exists. - # @POST: Entry is deleted. + # @PURPOSE Delete a single dictionary entry. + # @PRE entry_id exists. + # @POST Entry is deleted. @staticmethod def delete_entry(db: Session, entry_id: str) -> None: with belief_scope("DictionaryManager.delete_entry"): @@ -309,9 +311,9 @@ class DictionaryManager: # endregion DictionaryManager.delete_entry # region DictionaryManager.clear_entries [TYPE Function] - # @PURPOSE: Delete all entries for a dictionary. - # @PRE: dict_id exists. - # @POST: All entries for the dictionary are deleted. + # @PURPOSE Delete all entries for a dictionary. + # @PRE dict_id exists. + # @POST All entries for the dictionary are deleted. @staticmethod def clear_entries(db: Session, dict_id: str) -> int: with belief_scope("DictionaryManager.clear_entries"): @@ -326,9 +328,9 @@ class DictionaryManager: # endregion DictionaryManager.clear_entries # region DictionaryManager.list_entries [TYPE Function] - # @PURPOSE: List entries for a dictionary with pagination. - # @PRE: dict_id exists. - # @POST: Returns (list of entries, total_count). + # @PURPOSE List entries for a dictionary with pagination. + # @PRE dict_id exists. + # @POST Returns (list of entries, total_count). @staticmethod def list_entries( db: Session, dict_id: str, page: int = 1, page_size: int = 50, @@ -351,10 +353,10 @@ class DictionaryManager: # endregion DictionaryManager.list_entries # region DictionaryManager.import_entries [TYPE Function] - # @PURPOSE: Import entries from CSV/TSV content with duplicate detection and conflict resolution. - # @PRE: content is valid CSV or TSV. dict_id exists. - # @POST: Entries are created/updated/skipped per conflict mode. Returns result summary. - # @SIDE_EFFECT: Batch-inserts or updates DictionaryEntry rows. + # @PURPOSE Import entries from CSV/TSV content with duplicate detection and conflict resolution. + # @PRE content is valid CSV or TSV. dict_id exists. + # @POST Entries are created/updated/skipped per conflict mode. Returns result summary. + # @SIDE_EFFECT Batch-inserts or updates DictionaryEntry rows. @staticmethod def import_entries( db: Session, dict_id: str, content: str, @@ -504,9 +506,9 @@ class DictionaryManager: # endregion DictionaryManager.import_entries # region DictionaryManager.export_entries [TYPE Function] - # @PURPOSE: Export all entries as CSV string with language columns. - # @PRE: dict_id exists. - # @POST: Returns CSV string with header: source_term, target_term, source_language, target_language, context_notes, context_data, usage_notes. + # @PURPOSE Export all entries as CSV string with language columns. + # @PRE dict_id exists. + # @POST Returns CSV string with header: source_term, target_term, source_language, target_language, context_notes, context_data, usage_notes. @staticmethod def export_entries( db: Session, dict_id: str, delimiter: str = ",", @@ -549,10 +551,10 @@ class DictionaryManager: # endregion DictionaryManager.export_entries # region DictionaryManager.migrate_old_entries [TYPE Function] - # @PURPOSE: Migrate existing single-language dictionary entries to populate source_language and target_language. - # @PRE: db session is open. - # @POST: Entries with "und" source_language or target_language are updated with inferred values. - # @SIDE_EFFECT: Updates DictionaryEntry rows in bulk. + # @PURPOSE Migrate existing single-language dictionary entries to populate source_language and target_language. + # @PRE db session is open. + # @POST Entries with "und" source_language or target_language are updated with inferred values. + # @SIDE_EFFECT Updates DictionaryEntry rows in bulk. @staticmethod def migrate_old_entries(db: Session) -> dict[str, int]: with belief_scope("DictionaryManager.migrate_old_entries"): @@ -601,12 +603,12 @@ class DictionaryManager: # endregion DictionaryManager.migrate_old_entries # region DictionaryManager.filter_for_batch [TYPE Function] - # @PURPOSE: Scan batch texts for case-insensitive, word-boundary-aware matches against all dictionaries attached to a job, + # @PURPOSE Scan batch texts for case-insensitive, word-boundary-aware matches against all dictionaries attached to a job, # optionally filtered by language pair. When row_context is provided, computes context-aware priority flags. - # @PRE: job_id exists and source_texts is a list of strings. - # @POST: Returns list of matched entries with match info, sorted by priority across attached dictionaries. + # @PRE job_id exists and source_texts is a list of strings. + # @POST Returns list of matched entries with match info, sorted by priority across attached dictionaries. # When row_context is given, each result includes a 'priority_match' boolean. - # @SIDE_EFFECT: Queries TranslationJobDictionary, TerminologyDictionary, and DictionaryEntry tables. + # @SIDE_EFFECT Queries TranslationJobDictionary, TerminologyDictionary, and DictionaryEntry tables. @staticmethod def filter_for_batch( db: Session, source_texts: list[str], job_id: str, @@ -738,10 +740,10 @@ class DictionaryManager: # region DictionaryManager.submit_correction [TYPE Function] - # @PURPOSE: Submit a term correction from a run result. Validates language match, detects conflicts. - # @PRE: source_term, incorrect_target_term, corrected_target_term are non-empty. dict_id exists. - # @POST: Entry created or updated; origin tracking populated. Returns action + conflict info. - # @SIDE_EFFECT: Creates/updates DictionaryEntry row. + # @PURPOSE Submit a term correction from a run result. Validates language match, detects conflicts. + # @PRE source_term, incorrect_target_term, corrected_target_term are non-empty. dict_id exists. + # @POST Entry created or updated; origin tracking populated. Returns action + conflict info. + # @SIDE_EFFECT Creates/updates DictionaryEntry row. @staticmethod def submit_correction( db: Session, @@ -868,10 +870,10 @@ class DictionaryManager: # endregion DictionaryManager.submit_correction # region DictionaryManager.submit_bulk_corrections [TYPE Function] - # @PURPOSE: Submit multiple term corrections atomically — all succeed or all fail. - # @PRE: corrections list is non-empty. dict_id exists. - # @POST: All corrections applied or none applied with conflict list. - # @SIDE_EFFECT: Creates/updates DictionaryEntry rows; commits once. + # @PURPOSE Submit multiple term corrections atomically — all succeed or all fail. + # @PRE corrections list is non-empty. dict_id exists. + # @POST All corrections applied or none applied with conflict list. + # @SIDE_EFFECT Creates/updates DictionaryEntry rows; commits once. @staticmethod def submit_bulk_corrections( db: Session, @@ -998,5 +1000,8 @@ class DictionaryManager: # endregion DictionaryManager.submit_bulk_corrections +# #endregion DictionaryManager + + # #endregion DictionaryManager # #endregion DictionaryManagerModule diff --git a/backend/src/plugins/translate/executor.py b/backend/src/plugins/translate/executor.py index 3796f69c..46315ad0 100644 --- a/backend/src/plugins/translate/executor.py +++ b/backend/src/plugins/translate/executor.py @@ -194,11 +194,12 @@ def _check_translation_cache( # #endregion _check_translation_cache -# #region estimate_row_tokens [C:2] [TYPE Function] [SEMANTICS translate, token, estimation] +# #region estimate_row_tokens [C:4] [TYPE Function] [SEMANTICS translate, token, estimation] # @BRIEF Estimate token count for a single source row including context fields. -# @PRE: source_text is a string. -# @POST: Returns estimated token count >= 1. +# @PRE source_text is a string. +# @POST Returns estimated token count >= 1. # @RELATION DEPENDS_ON -> [_estimate_tokens_for_text] + def estimate_row_tokens( source_text: str, source_data: dict | None, @@ -1745,30 +1746,47 @@ class TranslationExecutor: "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", } + # Reasoning-safe system prompt: always forbid chain-of-thought in output. + # Reasoning models (DeepSeek, QwQ, etc.) may leak reasoning into content + # when response_format=json_object is set. Explicit suppression in the + # system message prevents this regardless of the disable_reasoning flag. + system_content = ( + "You are a database content translation assistant. " + "Translate the provided text accurately, preserving data semantics. " + "Respond directly with ONLY the JSON result. " + "Do NOT include any reasoning, thinking, chain-of-thought, analysis, " + "or explanation. Output ONLY valid JSON." + ) + payload = { "model": model, "messages": [ - {"role": "system", "content": "You are a database content translation assistant. Translate the provided text accurately, preserving data semantics."}, + {"role": "system", "content": system_content}, {"role": "user", "content": prompt}, ], "temperature": 0.1, "max_tokens": max_tokens, } + # Structured output — OpenRouter and Kilo also support response_format, but some # upstream providers (e.g. StepFun) reject it. We try with response_format and # fall back on 400 "structured_outputs is not supported". + # NOTE: Reasoning models (deepseek, qwen with reasoning, etc.) often break with + # response_format=json_object, producing reasoning text instead of JSON. + # When disable_reasoning is set, we remove response_format and rely on the + # system prompt to enforce JSON-only output. if provider_type in ("openai", "openai_compatible", "kilo", "openrouter", "litellm"): - payload["response_format"] = {"type": "json_object"} + if not disable_reasoning: + payload["response_format"] = {"type": "json_object"} # Suppress Chain of Thought reasoning to save output tokens # NOTE: Kilo/OpenRouter/LiteLLM do NOT support disabling reasoning (returns 400) if disable_reasoning: if provider_type not in ("kilo", "openrouter", "litellm"): payload["reasoning_effort"] = "none" - payload.pop("response_format", None) + # response_format already skipped above when disable_reasoning is True # Use caller-provided max_tokens instead of hardcoded 8192 payload["max_tokens"] = max_tokens - payload["messages"][0] = {"role": "system", "content": "You are a database content translation assistant. Translate the provided text accurately, preserving data semantics. Respond directly with ONLY the JSON result. Do NOT include any reasoning, thinking, chain-of-thought, analysis, or explanation. Output ONLY valid JSON."} logger.reason( f"LLM request url={base_url} model={payload.get('model')} " diff --git a/backend/src/plugins/translate/metrics.py b/backend/src/plugins/translate/metrics.py index ab5a544f..0b697ba4 100644 --- a/backend/src/plugins/translate/metrics.py +++ b/backend/src/plugins/translate/metrics.py @@ -1,14 +1,15 @@ # #region TranslationMetrics [C:3] [TYPE Module] [SEMANTICS sqlalchemy, translate, metrics, job, statistics] # @BRIEF Aggregate translation metrics from live TranslationEvent + MetricSnapshot for per-job reporting. -# @LAYER: Domain -# @RELATION DEPENDS_ON -> [TranslationEvent:Class] -# @RELATION DEPENDS_ON -> [MetricSnapshot:Class] -# @RELATION DEPENDS_ON -> [TranslationRun:Class] +# @LAYER Domain # @RELATION DEPENDS_ON -> [TranslationSchedule:Class] -# @PRE: Database session is open. -# @POST: Metrics are aggregated and returned; no side effects. -# @RATIONALE: Live events (<90 days) + MetricSnapshot (>=90 days) fusion for complete picture. -# @REJECTED: Querying all events for all time — would be slow; MetricSnapshot provides historical summary. +# @RELATION DEPENDS_ON -> [TranslationSchedule:Class] +# @RELATION DEPENDS_ON -> [TranslationSchedule:Class] +# @RELATION DEPENDS_ON -> [TranslationSchedule:Class] +# @PRE Database session is open. +# @POST Metrics are aggregated and returned; no side effects. +# @RATIONALE Live events (<90 days) + MetricSnapshot (>=90 days) fusion for complete picture. +# @REJECTED Querying all events for all time — would be slow; MetricSnapshot provides historical summary. +# @COMPLEXITY 4 from datetime import UTC, datetime from typing import Any @@ -34,9 +35,9 @@ class TranslationMetrics: self.db = db # region get_job_metrics [TYPE Function] - # @PURPOSE: Get aggregated metrics for a specific job. - # @PRE: job_id exists. - # @POST: Returns dict with metrics from events + latest snapshot. + # @PURPOSE Get aggregated metrics for a specific job. + # @PRE job_id exists. + # @POST Returns dict with metrics from events + latest snapshot. def get_job_metrics(self, job_id: str) -> dict[str, Any]: with belief_scope("TranslationMetrics.get_job_metrics"): # Run counts from TranslationRun @@ -174,8 +175,8 @@ class TranslationMetrics: # endregion get_job_metrics # region get_all_metrics [TYPE Function] - # @PURPOSE: Get aggregated metrics for all jobs. - # @POST: Returns list of per-job metrics. + # @PURPOSE Get aggregated metrics for all jobs. + # @POST Returns list of per-job metrics. def get_all_metrics(self) -> list[dict[str, Any]]: with belief_scope("TranslationMetrics.get_all_metrics"): job_ids = ( diff --git a/backend/src/plugins/translate/orchestrator.py b/backend/src/plugins/translate/orchestrator.py index 7b09853c..fe0a4b50 100644 --- a/backend/src/plugins/translate/orchestrator.py +++ b/backend/src/plugins/translate/orchestrator.py @@ -238,6 +238,15 @@ class TranslationOrchestrator: "run_id": run.id, "error": str(e), }) + # Rollback the session if it's in a broken state (e.g. after a + # failed flush from a previous exception in executor.execute_run). + # Without this, subsequent flush()/commit() calls raise + # "This Session's transaction has been rolled back". + try: + self.db.rollback() + except Exception: + pass + run = self.db.merge(run) run.status = "FAILED" run.error_message = f"Translation execution failed: {e}" run.completed_at = datetime.now(UTC) @@ -887,6 +896,7 @@ class TranslationOrchestrator: "id": run.id, "job_id": run.job_id, "status": run.status, + "trigger_type": run.trigger_type, "started_at": run.started_at.isoformat() if run.started_at else None, "completed_at": run.completed_at.isoformat() if run.completed_at else None, "error_message": run.error_message, diff --git a/backend/src/plugins/translate/plugin.py b/backend/src/plugins/translate/plugin.py index def022d8..36335393 100644 --- a/backend/src/plugins/translate/plugin.py +++ b/backend/src/plugins/translate/plugin.py @@ -1,9 +1,9 @@ # #region TranslatePlugin [C:2] [TYPE Module] [SEMANTICS clickhouse, translate, sql, dashboard, dialect] # @BRIEF TranslatePlugin skeleton for cross-dialect SQL and dashboard translation. -# @LAYER: Domain +# @LAYER Domain # @RELATION INHERITS -> [PluginBase] -# @RATIONALE: Separate plugin avoids bloating LLMAnalysisPlugin beyond fractal limit (<400 lines). -# @REJECTED: Extending LLMAnalysisPlugin would conflate two distinct feature domains. +# @RATIONALE Separate plugin avoids bloating LLMAnalysisPlugin beyond fractal limit (<400 lines). +# @REJECTED Extending LLMAnalysisPlugin would conflate two distinct feature domains. from typing import Any diff --git a/backend/src/plugins/translate/preview.py b/backend/src/plugins/translate/preview.py index bc39eddc..935aebe9 100644 --- a/backend/src/plugins/translate/preview.py +++ b/backend/src/plugins/translate/preview.py @@ -41,7 +41,7 @@ from ._token_budget import DEFAULT_CONTEXT_WINDOW, estimate_token_budget from .dictionary import DictionaryManager # #region DEFAULT_EXECUTION_PROMPT_TEMPLATE [TYPE Constant] -# @BRIEF Default prompt template for batch LLM translation execution (no context columns — faster). + # Supports both single-language and multi-language modes via {target_languages} placeholder. DEFAULT_EXECUTION_PROMPT_TEMPLATE: str = ( "Translate the following database content from {source_language} to the following language(s): {target_languages}.\n\n" @@ -63,9 +63,9 @@ DEFAULT_EXECUTION_PROMPT_TEMPLATE: str = ( ) # #endregion DEFAULT_EXECUTION_PROMPT_TEMPLATE - # #region DEFAULT_PREVIEW_PROMPT_TEMPLATE [TYPE Constant] -# @BRIEF Default prompt template for LLM translation preview. +# #region DEFAULT_PREVIEW_PROMPT_TEMPLATE [TYPE Constant] + DEFAULT_PREVIEW_PROMPT_TEMPLATE: str = ( "Translate the following database content.\n\n" "Source dialect: {source_dialect}\n" @@ -1038,19 +1038,37 @@ class TranslationPreview: "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", } + # Reasoning-safe system prompt: always forbid chain-of-thought in output. + # Reasoning models (DeepSeek, QwQ, etc.) may leak reasoning into content + # when response_format=json_object is set. Explicit suppression in the + # system message prevents this regardless of the disable_reasoning flag. + system_content = ( + "You are a database content translation assistant. " + "Translate the provided text accurately, preserving data semantics. " + "Respond directly with ONLY the JSON result. " + "Do NOT include any reasoning, thinking, chain-of-thought, analysis, " + "or explanation. Output ONLY valid JSON." + ) + payload = { "model": model, "messages": [ - {"role": "system", "content": "You are a database content translation assistant. Translate the provided text accurately, preserving data semantics."}, + {"role": "system", "content": system_content}, {"role": "user", "content": prompt}, ], "temperature": 0.1, "max_tokens": max_tokens, } + # Structured output — Kilo gateway supports response_format, but upstream providers # (e.g. StepFun) may reject it. We try with response_format and fall back on 400. + # NOTE: Reasoning models (deepseek variants, qwen with reasoning) often break with + # response_format=json_object, producing reasoning text instead of JSON. + # When disable_reasoning is set, we skip response_format and rely on the + # system prompt to enforce JSON-only output. if provider_type in ("openai", "openai_compatible", "kilo", "openrouter", "litellm"): - payload["response_format"] = {"type": "json_object"} + if not disable_reasoning: + payload["response_format"] = {"type": "json_object"} # Suppress Chain of Thought reasoning to save output tokens # NOTE: Kilo/OpenRouter/LiteLLM reject reasoning_effort — only use for native OpenAI-compatible @@ -1058,13 +1076,10 @@ class TranslationPreview: # Kilo/OpenRouter/LiteLLM reject reasoning_effort — only use for native OpenAI-compatible if provider_type not in ("kilo", "openrouter", "litellm"): payload["reasoning_effort"] = "none" - payload.pop("response_format", None) # JSON mode triggers reasoning on some models + # response_format already skipped above when disable_reasoning is True # Use caller-provided max_tokens instead of hardcoded 8192 # This ensures multi-language batches with large output get enough token budget. payload["max_tokens"] = max_tokens - # Universal instruction — all models understand "respond directly without reasoning" - system_content = "You are a database content translation assistant. Translate the provided text accurately, preserving data semantics. Respond directly with ONLY the JSON result. Do NOT include any reasoning, thinking, chain-of-thought, analysis, or explanation. Output ONLY valid JSON." - payload["messages"][0] = {"role": "system", "content": system_content} logger.reason( f"LLM request url={base_url} model={payload.get('model')} " diff --git a/backend/src/plugins/translate/prompt_builder.py b/backend/src/plugins/translate/prompt_builder.py index 29df8b9a..be152961 100644 --- a/backend/src/plugins/translate/prompt_builder.py +++ b/backend/src/plugins/translate/prompt_builder.py @@ -1,9 +1,10 @@ # #region ContextAwarePromptBuilder [C:2] [TYPE Module] [SEMANTICS translate, prompt, context, dictionary] # @BRIEF Pure-function prompt builder that enhances dictionary entries with context annotations. -# @LAYER: Domain +# @LAYER Domain # @RELATION DEPENDS_ON -> [DictionaryEntry:Class] -# @RATIONALE: Pure functions only — no I/O, no DB access. Separated from executor for testability. -# @REJECTED: Embedding context inline in the executor would make it untestable without mocking DB. +# @RATIONALE Pure functions only — no I/O, no DB access. Separated from executor for testability. +# @REJECTED Embedding context inline in the executor would make it untestable without mocking DB. + # # Typical workflow: # 1. Call build_context_entries(dictionary_entries, row_context) to get annotated, prioritized entries diff --git a/backend/src/plugins/translate/scheduler.py b/backend/src/plugins/translate/scheduler.py index 20383319..62d25dd0 100644 --- a/backend/src/plugins/translate/scheduler.py +++ b/backend/src/plugins/translate/scheduler.py @@ -240,12 +240,14 @@ class TranslationScheduler: # #endregion TranslationScheduler -# #region execute_scheduled_translation [TYPE Function] +# #region execute_scheduled_translation [C:4] [TYPE Function] # @BRIEF APScheduler job handler — runs scheduled translation with concurrency check and new-key-only fallback. -# @PRE: schedule_id is valid. -# @POST: Translation run created and executed if no concurrent run exists. -# @SIDE_EFFECT: DB writes; LLM calls; Superset API calls. -# @RATIONALE: New-key-only mode compares keys against most recent succeeded run; baseline_expired fallback does full. +# @PRE schedule_id is valid. +# @POST Translation run created and executed if no concurrent run exists. +# @SIDE_EFFECT DB writes; LLM calls; Superset API calls. +# @RATIONALE New-key-only mode compares keys against most recent succeeded run; baseline_expired fallback does full. +# @COMPLEXITY 4 + def execute_scheduled_translation( schedule_id: str, job_id: str, diff --git a/backend/src/plugins/translate/service.py b/backend/src/plugins/translate/service.py index 46ac4b98..34cd4760 100644 --- a/backend/src/plugins/translate/service.py +++ b/backend/src/plugins/translate/service.py @@ -38,7 +38,7 @@ SUPPORTED_DIALECTS = { } -# #region get_dialect_from_database [TYPE Function] +# #region get_dialect_from_database [C:4] [TYPE Function] # @BRIEF Extract normalized dialect string from a Superset database record. # @PRE: database_record is a dict from Superset API. # @POST: Returns normalized dialect string or raises ValueError if unsupported. @@ -84,7 +84,7 @@ def get_dialect_from_database(database_record: dict[str, Any]) -> str: # #endregion get_dialect_from_database -# #region fetch_datasource_metadata [TYPE Function] +# #region fetch_datasource_metadata [C:4] [TYPE Function] # @BRIEF Fetch datasource columns and database dialect from Superset. # @PRE: dataset_id is a valid Superset dataset ID and environment has valid credentials. # @POST: Returns (columns_list, dialect_string) or raises on failure. @@ -141,7 +141,7 @@ def fetch_datasource_metadata( # #endregion fetch_datasource_metadata -# #region detect_virtual_columns [TYPE Function] +# #region detect_virtual_columns [C:4] [TYPE Function] # @BRIEF Identify virtual (calculated) columns from column metadata. # @PRE: columns is a list of dicts with 'is_physical' key. # @POST: Returns list of virtual column names. @@ -519,11 +519,13 @@ def job_to_response(job: TranslationJob, dict_ids: list[str] | None = None) -> T # #endregion job_to_response -# #region DatasourceColumnsService [TYPE Function] +# #region DatasourceColumnsService [C:4] [TYPE Function] # @BRIEF Fetch datasource column metadata from Superset and return structured response. -# @PRE: datasource_id is a valid Superset dataset ID. -# @POST: Returns DatasourceColumnsResponse with column metadata and database dialect. -# @SIDE_EFFECT: Queries Superset API for dataset detail and database info. +# @PRE datasource_id is a valid Superset dataset ID. +# @POST Returns DatasourceColumnsResponse with column metadata and database dialect. +# @SIDE_EFFECT Queries Superset API for dataset detail and database info. +# @COMPLEXITY 4 + def get_datasource_columns( datasource_id: int, env_id: str, diff --git a/backend/src/plugins/translate/sql_generator.py b/backend/src/plugins/translate/sql_generator.py index 363c7836..fa5bf46e 100644 --- a/backend/src/plugins/translate/sql_generator.py +++ b/backend/src/plugins/translate/sql_generator.py @@ -58,7 +58,7 @@ def _normalize_timestamp_value(value: Any) -> str | None: # #endregion _normalize_timestamp_value -# #region _quote_identifier [TYPE Function] +# #region _quote_identifier [C:4] [TYPE Function] # @BRIEF Quote an identifier per dialect rules. PostgreSQL uses double quotes; ClickHouse uses backticks. # @PRE: identifier is a non-empty string. # @POST: Returns safely quoted identifier. @@ -159,10 +159,12 @@ def generate_insert_sql( # #endregion generate_insert_sql -# #region generate_upsert_sql [TYPE Function] +# #region generate_upsert_sql [C:4] [TYPE Function] # @BRIEF Generate PostgreSQL dialect UPSERT SQL with ON CONFLICT DO UPDATE. -# @PRE: dialect is postgresql-compatible. target_table, columns, key_columns are non-empty. -# @POST: Returns UPSERT SQL string or raises ValueError. +# @PRE dialect is postgresql-compatible. target_table, columns, key_columns are non-empty. +# @POST Returns UPSERT SQL string or raises ValueError. +# @COMPLEXITY 4 + def generate_upsert_sql( target_schema: str | None, target_table: str, diff --git a/backend/src/plugins/translate/superset_executor.py b/backend/src/plugins/translate/superset_executor.py index 06bce766..0c511e39 100644 --- a/backend/src/plugins/translate/superset_executor.py +++ b/backend/src/plugins/translate/superset_executor.py @@ -1,13 +1,13 @@ # #region SupersetSqlLabExecutor [C:4] [TYPE Module] [SEMANTICS translate, superset, sqllab, execute, query] # @BRIEF Submit SQL to Superset SQL Lab API and poll execution status. -# @LAYER: Infra -# @RELATION DEPENDS_ON -> [SupersetClient] +# @LAYER Infrastructure # @RELATION DEPENDS_ON -> [ConfigManager] -# @PRE: Valid Superset environment configuration and authenticated client. -# @POST: SQL is submitted to Superset SQL Lab; execution reference is returned. -# @SIDE_EFFECT: Makes HTTP calls to Superset /api/v1/sqllab/execute/; polls status. -# @RATIONALE: Direct SQL Lab API submission provides audit trail and execution monitoring within Superset. -# @REJECTED: Direct database connection bypass — would skip Superset's SQL Lab audit and RBAC. +# @RELATION DEPENDS_ON -> [ConfigManager] +# @PRE Valid Superset environment configuration and authenticated client. +# @POST SQL is submitted to Superset SQL Lab; execution reference is returned. +# @SIDE_EFFECT Makes HTTP calls to Superset /api/v1/sqllab/execute/; polls status. +# @RATIONALE Direct SQL Lab API submission provides audit trail and execution monitoring within Superset. +# @REJECTED Direct database connection bypass — would skip Superset's SQL Lab audit and RBAC. import json import time @@ -21,9 +21,9 @@ from ...core.superset_client import SupersetClient # #region SupersetSqlLabExecutor [C:4] [TYPE Class] # @BRIEF Submit SQL to Superset SQL Lab API with polling and status tracking. -# @PRE: Valid environment ID and ConfigManager. -# @POST: SQL submitted to Superset; execution reference recorded. -# @SIDE_EFFECT: Makes HTTP POST to /api/v1/sqllab/execute/; polls GET for status. +# @PRE Valid environment ID and ConfigManager. +# @POST SQL submitted to Superset; execution reference recorded. +# @SIDE_EFFECT Makes HTTP POST to /api/v1/sqllab/execute/; polls GET for status. class SupersetSqlLabExecutor: def __init__(self, config_manager: ConfigManager, env_id: str): @@ -34,10 +34,10 @@ class SupersetSqlLabExecutor: self._database_backend: str | None = None # region _get_client [TYPE Function] - # @PURPOSE: Lazy-initialize SupersetClient for the configured environment. - # @PRE: env_id must correspond to a valid environment config. - # @POST: Returns authenticated SupersetClient. - # @SIDE_EFFECT: Authenticates against Superset API. + # @PURPOSE Lazy-initialize SupersetClient for the configured environment. + # @PRE env_id must correspond to a valid environment config. + # @POST Returns authenticated SupersetClient. + # @SIDE_EFFECT Authenticates against Superset API. def _get_client(self) -> SupersetClient: with belief_scope("SupersetSqlLabExecutor._get_client"): if self._client is not None: @@ -53,10 +53,10 @@ class SupersetSqlLabExecutor: # endregion _get_client # region resolve_database_id [TYPE Function] - # @PURPOSE: Resolve the target database ID from the environment and fetch its backend engine. - # @PRE: database_name or database_id should be known. - # @POST: Returns database_id integer or raises ValueError. Also sets self._database_backend. - # @SIDE_EFFECT: Fetches databases list from Superset; optionally fetches single DB for backend. + # @PURPOSE Resolve the target database ID from the environment and fetch its backend engine. + # @PRE database_name or database_id should be known. + # @POST Returns database_id integer or raises ValueError. Also sets self._database_backend. + # @SIDE_EFFECT Fetches databases list from Superset; optionally fetches single DB for backend. def resolve_database_id( self, database_name: str | None = None, @@ -121,17 +121,17 @@ class SupersetSqlLabExecutor: # endregion resolve_database_id # region get_database_backend [TYPE Function] - # @PURPOSE: Return the cached database backend/engine string. - # @POST: Returns backend string or None if not yet resolved. + # @PURPOSE Return the cached database backend/engine string. + # @POST Returns backend string or None if not yet resolved. def get_database_backend(self) -> str | None: return self._database_backend # endregion get_database_backend # region execute_sql [TYPE Function] - # @PURPOSE: Submit SQL to Superset SQL Lab and return execution tracking info. - # @PRE: sql is valid SQL string. database_id is a valid Superset DB ID. - # @POST: Returns execution result dict with query_id and status. - # @SIDE_EFFECT: Makes HTTP POST to /api/v1/sqllab/execute/. + # @PURPOSE Submit SQL to Superset SQL Lab and return execution tracking info. + # @PRE sql is valid SQL string. database_id is a valid Superset DB ID. + # @POST Returns execution result dict with query_id and status. + # @SIDE_EFFECT Makes HTTP POST to /api/v1/sqllab/execute/. def execute_sql( self, sql: str, @@ -236,10 +236,10 @@ class SupersetSqlLabExecutor: # endregion execute_sql # region poll_execution_status [TYPE Function] - # @PURPOSE: Poll Superset for SQL execution status until completion or timeout. - # @PRE: query_id is a valid Superset query ID. - # @POST: Returns final execution status dict. - # @SIDE_EFFECT: Makes HTTP GET requests to Superset. + # @PURPOSE Poll Superset for SQL execution status until completion or timeout. + # @PRE query_id is a valid Superset query ID. + # @POST Returns final execution status dict. + # @SIDE_EFFECT Makes HTTP GET requests to Superset. def poll_execution_status( self, query_id: str, @@ -329,10 +329,10 @@ class SupersetSqlLabExecutor: # endregion poll_execution_status # region execute_and_poll [TYPE Function] - # @PURPOSE: Execute SQL and wait for completion. One-shot convenience method. - # @PRE: sql is valid SQL. - # @POST: Returns final execution result. - # @SIDE_EFFECT: Makes HTTP calls to Superset API. + # @PURPOSE Execute SQL and wait for completion. One-shot convenience method. + # @PRE sql is valid SQL. + # @POST Returns final execution result. + # @SIDE_EFFECT Makes HTTP calls to Superset API. def execute_and_poll( self, sql: str, @@ -384,10 +384,10 @@ class SupersetSqlLabExecutor: # endregion execute_and_poll # region get_query_results [TYPE Function] - # @PURPOSE: Fetch the results of a completed query. - # @PRE: query_id is a valid Superset query ID. - # @POST: Returns query results if available. - # @SIDE_EFFECT: Makes HTTP GET to Superset. + # @PURPOSE Fetch the results of a completed query. + # @PRE query_id is a valid Superset query ID. + # @POST Returns query results if available. + # @SIDE_EFFECT Makes HTTP GET to Superset. def get_query_results(self, query_id: str) -> dict[str, Any]: with belief_scope("SupersetSqlLabExecutor.get_query_results"): client = self._get_client() diff --git a/backend/src/schemas/translate.py b/backend/src/schemas/translate.py index 31338fdf..e5a63f06 100644 --- a/backend/src/schemas/translate.py +++ b/backend/src/schemas/translate.py @@ -1,6 +1,6 @@ -# #region TranslateSchemas [C:2] [TYPE Module] [SEMANTICS pydantic, translate, schema, translate-job-create] +# #region TranslateSchemas [C:3] [TYPE Module] [SEMANTICS pydantic, translate, schema, translate-job-create] # @BRIEF Pydantic v2 schemas for translation API request/response serialization. -# @LAYER: API +# @LAYER API # @RELATION DEPENDS_ON -> pydantic from datetime import datetime diff --git a/backend/src/services/git/_base.py b/backend/src/services/git/_base.py index 2f085388..1774e1fa 100644 --- a/backend/src/services/git/_base.py +++ b/backend/src/services/git/_base.py @@ -1,6 +1,6 @@ -# #region GitServiceBase [C:3] [TYPE Module] [SEMANTICS git, repository, clone, base, mixin] +# #region GitServiceBase [C:4] [TYPE Module] [SEMANTICS git, repository, clone, base, mixin, lock, http] # @LAYER: Infra -# @BRIEF Core GitService base class — initialization, path resolution, repo lifecycle (init/delete/get), and identity configuration. +# @BRIEF Core GitService base class — initialization, path resolution, repo lifecycle (init/delete/get), identity configuration, concurrent locking, and shared HTTP client pool. # @RELATION INHERITED_BY -> [GitService] # @RELATION DEPENDS_ON -> [SessionLocal] # @RELATION DEPENDS_ON -> [AppConfigRecord] @@ -9,8 +9,12 @@ import os import re import shutil +import threading +from contextlib import contextmanager from pathlib import Path +from urllib.parse import quote +import httpx from fastapi import HTTPException from git import Repo from git.exc import InvalidGitRepositoryError, NoSuchPathError @@ -21,14 +25,16 @@ from src.models.config import AppConfigRecord from src.models.git import GitRepository -# #region GitServiceBase [C:3] [TYPE Class] -# @BRIEF Base class for GitService providing initialization, path resolution, repository lifecycle, and identity. +# #region GitServiceBase [C:4] [TYPE Class] +# @BRIEF Base class for GitService providing initialization, path resolution, repository lifecycle, identity, concurrent locking, and shared HTTP client. +# @PRE base_path is a valid string path. +# @POST GitService is initialized; base_path directory exists; shared AsyncClient ready; lock registry empty. +# @SIDE_EFFECT Creates HTTP connection pool (max_keepalive=20, max_connections=100). class GitServiceBase: - # region GitService_init [TYPE Function] - # @PURPOSE: Initializes the GitService with a base path for repositories. - # @PARAM: base_path (str) - Root directory for all Git clones. + # region GitService_init [C:4] [TYPE Function] + # @PURPOSE: Initializes the GitService with a base path, concurrent access locks, and shared HTTP client. # @PRE: base_path is a valid string path. - # @POST: GitService is initialized; base_path directory exists. + # @POST: GitService is initialized; base_path directory exists; _lock_registry, _http_client ready. def __init__(self, base_path: str = "git_repos"): with belief_scope("GitService.__init__"): backend_root = Path(__file__).parents[3] @@ -36,8 +42,65 @@ class GitServiceBase: self._uses_default_base_path = base_path == "git_repos" self.base_path = self._resolve_base_path(base_path) self._ensure_base_path_exists() + + # Fix 3: Per-dashboard reentrant lock registry for concurrent access protection + self._lock_registry: dict[int, threading.RLock] = {} + self._reg_lock = threading.Lock() + + # Fix: close() race condition protection + self._closed = False + self._close_lock = threading.Lock() + + # Fix 5: Shared httpx.AsyncClient with connection pooling + self._http_client = httpx.AsyncClient( + timeout=30.0, + limits=httpx.Limits(max_keepalive_connections=20, max_connections=100), + ) # endregion GitService_init + # region _get_lock [C:2] [TYPE Function] [SEMANTICS lock,concurrency] + # @BRIEF Get or create a per-dashboard reentrant lock for thread-safe GitPython access. + # @PRE: dashboard_id is a valid integer. + # @POST: Returns a threading.RLock unique to the given dashboard_id. + def _get_lock(self, dashboard_id: int) -> threading.RLock: + with self._reg_lock: + if dashboard_id not in self._lock_registry: + self._lock_registry[dashboard_id] = threading.RLock() + return self._lock_registry[dashboard_id] + # endregion _get_lock + + # region _locked [C:2] [TYPE Function] [SEMANTICS lock,context,concurrency] + # @BRIEF Context manager that acquires the per-dashboard reentrant lock for the duration of the block. + # @PRE: dashboard_id is a valid integer. + # @POST: Lock is acquired on enter, released on exit. + @contextmanager + def _locked(self, dashboard_id: int): + if self._closed: + raise RuntimeError("GitService is closed") + lock = self._get_lock(dashboard_id) + lock.acquire() + try: + yield + finally: + lock.release() + # endregion _locked + + # region _clone_with_auth [C:2] [TYPE Function] [SEMANTICS git,clone,auth,security] + # @BRIEF Clone repository with PAT and immediately strip credentials from origin remote URL. + # @PRE: remote_url is a valid Git URL; pat is provided when required; repo_path is writable. + # @POST: Repo cloned at repo_path; origin remote URL has no embedded PAT. + # @SIDE_EFFECT Clones remote repository to local filesystem. + def _clone_with_auth(self, remote_url: str, repo_path: str, pat: str) -> Repo: + auth_url = remote_url + if pat and "://" in remote_url: + proto, rest = remote_url.split("://", 1) + auth_url = f"{proto}://oauth2:{quote(pat, safe='')}@{rest}" + repo = Repo.clone_from(auth_url, repo_path) + # Strip PAT from origin URL to prevent credential leakage in .git/config, logs, ps aux + repo.git.remote("set-url", "origin", remote_url) + return repo + # endregion _clone_with_auth + # region _ensure_base_path_exists [TYPE Function] # @PURPOSE: Ensure the repositories root directory exists and is a directory. # @PRE: self.base_path is resolved to filesystem path. @@ -194,124 +257,138 @@ class GitServiceBase: return target_path # endregion _get_repo_path - # region init_repo [TYPE Function] - # @PURPOSE: Initialize or clone a repository for a dashboard. + # region init_repo [C:4] [TYPE Function] [SEMANTICS git,clone,init,lock] + # @PURPOSE: Initialize or clone a repository for a dashboard with concurrent access protection. # @PARAM: dashboard_id (int), remote_url (str), pat (str), repo_key (Optional[str]). # @PRE: dashboard_id is int, remote_url is valid Git URL, pat is provided. - # @POST: Repository is cloned or opened at the local path. + # @POST: Repository is cloned or opened at the local path. Origin remote URL has no embedded PAT. + # @SIDE_EFFECT Clones remote repository; modifies local filesystem; protected by per-dashboard lock. # @RETURN: Repo - def init_repo(self, dashboard_id: int, remote_url: str, pat: str, repo_key: str | None = None) -> Repo: - with belief_scope("GitService.init_repo"): - self._ensure_base_path_exists() - repo_path = self._get_repo_path(dashboard_id, repo_key=repo_key or str(dashboard_id)) - Path(repo_path).parent.mkdir(parents=True, exist_ok=True) - if pat and "://" in remote_url: - proto, rest = remote_url.split("://", 1) - auth_url = f"{proto}://oauth2:{pat}@{rest}" - else: - auth_url = remote_url - if os.path.exists(repo_path): - logger.reason(f"Opening existing repo at {repo_path}", extra={"src": "init_repo"}) - try: - repo = Repo(repo_path) - except (InvalidGitRepositoryError, NoSuchPathError): - logger.reason(f"Existing path is not a Git repository, recreating: {repo_path}", extra={"src": "init_repo"}) - stale_path = Path(repo_path) - if stale_path.exists(): - shutil.rmtree(stale_path, ignore_errors=True) + def init_repo(self, dashboard_id: int, remote_url: str, pat: str, repo_key: str | None = None, default_branch: str | None = None) -> Repo: + with self._locked(dashboard_id): + with belief_scope("GitService.init_repo"): + self._ensure_base_path_exists() + repo_path = self._get_repo_path(dashboard_id, repo_key=repo_key or str(dashboard_id)) + Path(repo_path).parent.mkdir(parents=True, exist_ok=True) + if os.path.exists(repo_path): + logger.reason(f"Opening existing repo at {repo_path}", extra={"src": "init_repo"}) + try: + repo = Repo(repo_path) + except (InvalidGitRepositoryError, NoSuchPathError): + logger.reason(f"Existing path is not a Git repository, recreating: {repo_path}", extra={"src": "init_repo"}) + stale_path = Path(repo_path) if stale_path.exists(): - try: - stale_path.unlink() - except Exception: - pass - repo = Repo.clone_from(auth_url, repo_path) + shutil.rmtree(stale_path, ignore_errors=True) + if stale_path.exists(): + try: + stale_path.unlink() + except Exception: + pass + repo = self._clone_with_auth(remote_url, repo_path, pat) + self._ensure_gitflow_branches(repo, dashboard_id) + return repo + logger.reason(f"Cloning {remote_url} to {repo_path}", extra={"src": "init_repo"}) + repo = self._clone_with_auth(remote_url, repo_path, pat) self._ensure_gitflow_branches(repo, dashboard_id) return repo - logger.reason(f"Cloning {remote_url} to {repo_path}", extra={"src": "init_repo"}) - repo = Repo.clone_from(auth_url, repo_path) - self._ensure_gitflow_branches(repo, dashboard_id) - return repo # endregion init_repo - # region delete_repo [TYPE Function] - # @PURPOSE: Remove local repository and DB binding for a dashboard. + # region delete_repo [C:4] [TYPE Function] [SEMANTICS git,delete,lock] + # @PURPOSE: Remove local repository and DB binding for a dashboard with concurrent access protection. # @PRE: dashboard_id is a valid integer. # @POST: Local path is deleted when present and GitRepository row is removed. + # @SIDE_EFFECT Deletes filesystem directory and DB record; protected by per-dashboard lock. def delete_repo(self, dashboard_id: int) -> None: - with belief_scope("GitService.delete_repo"): - repo_path = self._get_repo_path(dashboard_id) - removed_files = False - if os.path.exists(repo_path): - if os.path.isdir(repo_path): - shutil.rmtree(repo_path) - else: - os.remove(repo_path) - removed_files = True - session = SessionLocal() - try: - db_repo = ( - session.query(GitRepository) - .filter(GitRepository.dashboard_id == int(dashboard_id)) - .first() - ) - if db_repo: - session.delete(db_repo) - session.commit() - return - if removed_files: - return - raise HTTPException( - status_code=404, - detail=f"Repository for dashboard {dashboard_id} not found", - ) - except HTTPException: - session.rollback() - raise - except Exception as e: - session.rollback() - logger.error(f"[delete_repo][Coherence:Failed] Failed to delete repository for dashboard {dashboard_id}: {e}") - raise HTTPException(status_code=500, detail=f"Failed to delete repository: {e!s}") - finally: - session.close() + with self._locked(dashboard_id): + with belief_scope("GitService.delete_repo"): + repo_path = self._get_repo_path(dashboard_id) + removed_files = False + if os.path.exists(repo_path): + if os.path.isdir(repo_path): + shutil.rmtree(repo_path) + else: + os.remove(repo_path) + removed_files = True + session = SessionLocal() + try: + db_repo = ( + session.query(GitRepository) + .filter(GitRepository.dashboard_id == int(dashboard_id)) + .first() + ) + if db_repo: + session.delete(db_repo) + session.commit() + return + if removed_files: + return + raise HTTPException( + status_code=404, + detail=f"Repository for dashboard {dashboard_id} not found", + ) + except HTTPException: + session.rollback() + raise + except Exception as e: + session.rollback() + logger.error(f"[delete_repo][Coherence:Failed] Failed to delete repository for dashboard {dashboard_id}: {e}") + raise HTTPException(status_code=500, detail=f"Failed to delete repository: {e!s}") + finally: + session.close() # endregion delete_repo - # region get_repo [TYPE Function] - # @PURPOSE: Get Repo object for a dashboard. + # region get_repo [C:4] [TYPE Function] [SEMANTICS git,open,lock] + # @PURPOSE: Get Repo object for a dashboard with concurrent access protection. # @PRE: Repository must exist on disk for the given dashboard_id. # @POST: Returns a GitPython Repo instance for the dashboard. # @RETURN: Repo def get_repo(self, dashboard_id: int) -> Repo: - with belief_scope("GitService.get_repo"): - repo_path = self._get_repo_path(dashboard_id) - if not os.path.exists(repo_path): - logger.error(f"[get_repo][Coherence:Failed] Repository for dashboard {dashboard_id} does not exist") - raise HTTPException(status_code=404, detail=f"Repository for dashboard {dashboard_id} not found") - try: - return Repo(repo_path) - except Exception as e: - logger.error(f"[get_repo][Coherence:Failed] Failed to open repository at {repo_path}: {e}") - raise HTTPException(status_code=500, detail="Failed to open local Git repository") + with self._locked(dashboard_id): + with belief_scope("GitService.get_repo"): + repo_path = self._get_repo_path(dashboard_id) + if not os.path.exists(repo_path): + logger.error(f"[get_repo][Coherence:Failed] Repository for dashboard {dashboard_id} does not exist") + raise HTTPException(status_code=404, detail=f"Repository for dashboard {dashboard_id} not found") + try: + return Repo(repo_path) + except Exception as e: + logger.error(f"[get_repo][Coherence:Failed] Failed to open repository at {repo_path}: {e}") + raise HTTPException(status_code=500, detail="Failed to open local Git repository") # endregion get_repo - # region configure_identity [TYPE Function] - # @PURPOSE: Configure repository-local Git committer identity for user-scoped operations. + # region configure_identity [C:4] [TYPE Function] [SEMANTICS git,identity,lock] + # @PURPOSE: Configure repository-local Git committer identity with concurrent access protection. # @PRE: dashboard_id repository exists; git_username/git_email may be empty. # @POST: Repository config has user.name and user.email when both identity values are provided. + # @SIDE_EFFECT Writes to repository git config; protected by per-dashboard lock. def configure_identity(self, dashboard_id: int, git_username: str | None, git_email: str | None) -> None: - with belief_scope("GitService.configure_identity"): - normalized_username = str(git_username or "").strip() - normalized_email = str(git_email or "").strip() - if not normalized_username or not normalized_email: - return - repo = self.get_repo(dashboard_id) - try: - with repo.config_writer(config_level="repository") as config_writer: - config_writer.set_value("user", "name", normalized_username) - config_writer.set_value("user", "email", normalized_email) - logger.reason(f"Applied repository-local git identity for dashboard {dashboard_id}", extra={"src": "configure_identity"}) - except Exception as e: - logger.error(f"[configure_identity][Coherence:Failed] Failed to configure git identity: {e}") - raise HTTPException(status_code=500, detail=f"Failed to configure git identity: {e!s}") + with self._locked(dashboard_id): + with belief_scope("GitService.configure_identity"): + normalized_username = str(git_username or "").strip() + normalized_email = str(git_email or "").strip() + if not normalized_username or not normalized_email: + return + repo = self.get_repo(dashboard_id) + try: + with repo.config_writer(config_level="repository") as config_writer: + config_writer.set_value("user", "name", normalized_username) + config_writer.set_value("user", "email", normalized_email) + logger.reason(f"Applied repository-local git identity for dashboard {dashboard_id}", extra={"src": "configure_identity"}) + except Exception as e: + logger.error(f"[configure_identity][Coherence:Failed] Failed to configure git identity: {e}") + raise HTTPException(status_code=500, detail=f"Failed to configure git identity: {e!s}") # endregion configure_identity + + # region close [C:2] [TYPE Function] [SEMANTICS http,cleanup,shutdown] + # @BRIEF Gracefully close the shared HTTP client (connection pool). + # @PRE: _http_client is initialized. + # @POST: HTTP connections closed. + async def close(self): + with self._close_lock: + if self._closed: + return + self._closed = True + await self._http_client.aclose() + # endregion close # #endregion GitServiceBase # #endregion GitServiceBase diff --git a/backend/src/services/git/_branch.py b/backend/src/services/git/_branch.py index e12747f2..1eeaf47f 100644 --- a/backend/src/services/git/_branch.py +++ b/backend/src/services/git/_branch.py @@ -1,6 +1,6 @@ -# #region GitServiceBranchMixin [C:3] [TYPE Module] [SEMANTICS git, branch, checkout, list, namespace] +# #region GitServiceBranchMixin [C:4] [TYPE Module] [SEMANTICS git, branch, checkout, list, namespace, lock] # @LAYER: Infra -# @BRIEF Branch and commit operations for GitService — gitflow branches, list/create/checkout branches, commit changes. +# @BRIEF Branch and commit operations for GitService — gitflow branches, list/create/checkout branches, commit changes (all concurrent-safe via per-dashboard locks). # @RELATION USED_BY -> [GitService] import os @@ -15,10 +15,11 @@ from src.core.logger import belief_scope, logger # #region GitServiceBranchMixin [C:3] [TYPE Class] # @BRIEF Mixin providing branch and commit operations for GitService. class GitServiceBranchMixin: - # region _ensure_gitflow_branches [TYPE Function] + # region _ensure_gitflow_branches [C:4] [TYPE Function] [SEMANTICS git,gitflow,branch,lock] # @PURPOSE: Ensure standard GitFlow branches (main/dev/preprod) exist locally and on origin. # @PRE: repo is a valid GitPython Repo instance. # @POST: main, dev, preprod are available in local repository and pushed to origin when available. + # Active branch unchanged (no spurious checkout). def _ensure_gitflow_branches(self, repo: Repo, dashboard_id: int) -> None: with belief_scope("GitService._ensure_gitflow_branches"): required_branches = ["main", "dev", "preprod"] @@ -79,62 +80,74 @@ class GitServiceBranchMixin: status_code=500, detail=f"Failed to create default branch '{branch_name}' on remote: {e!s}", ) + # Fix 6: Only checkout if not already on dev try: - repo.git.checkout("dev") - logger.reason(f"Checked out default branch dev for dashboard {dashboard_id}", extra={"src": "_ensure_gitflow_branches"}) - except Exception as e: - logger.reason(f"Could not checkout dev branch for dashboard {dashboard_id}: {e}", extra={"src": "_ensure_gitflow_branches"}) + current_branch = repo.active_branch.name + except Exception: + current_branch = None + if current_branch != "dev": + try: + repo.git.checkout("dev") + logger.reason(f"Checked out default branch dev for dashboard {dashboard_id}", extra={"src": "_ensure_gitflow_branches"}) + except Exception as e: + logger.reason(f"Could not checkout dev branch for dashboard {dashboard_id}: {e}", extra={"src": "_ensure_gitflow_branches"}) # endregion _ensure_gitflow_branches - # region list_branches [TYPE Function] - # @PURPOSE: List all branches for a dashboard's repository. + # region list_branches [C:4] [TYPE Function] [SEMANTICS git,branch,list,lock] + # @PURPOSE: List all branches (excluding tags) for a dashboard's repository, concurrent-safe. # @PRE: Repository for dashboard_id exists. - # @POST: Returns a list of branch metadata dictionaries. + # @POST: Returns a list of branch metadata dictionaries (no tag refs). # @RETURN: List[dict] def list_branches(self, dashboard_id: int) -> list[dict]: - with belief_scope("GitService.list_branches"): - repo = self.get_repo(dashboard_id) - logger.reason(f"Listing branches for {dashboard_id}. Refs: {repo.refs}", extra={"src": "list_branches"}) - branches = [] - for ref in repo.refs: + with self._locked(dashboard_id): + with belief_scope("GitService.list_branches"): + repo = self.get_repo(dashboard_id) + logger.reason(f"Listing branches for {dashboard_id}. Refs: {repo.refs}", extra={"src": "list_branches"}) + branches = [] + for ref in repo.refs: + try: + # Fix 8: Skip tag refs + ref_name = str(ref.name) + if ref_name.startswith('refs/tags/'): + continue + name = ref_name.replace('refs/heads/', '').replace('refs/remotes/origin/', '') + if any(b['name'] == name for b in branches): + continue + branches.append({ + "name": name, + "commit_hash": ref.commit.hexsha if hasattr(ref, 'commit') else "0000000", + "is_remote": ref.is_remote() if hasattr(ref, 'is_remote') else False, + "last_updated": datetime.fromtimestamp(ref.commit.committed_date) if hasattr(ref, 'commit') else datetime.utcnow() + }) + except Exception as e: + logger.reason(f"Skipping ref {ref}: {e}", extra={"src": "list_branches"}) try: - name = ref.name.replace('refs/heads/', '').replace('refs/remotes/origin/', '') - if any(b['name'] == name for b in branches): - continue - branches.append({ - "name": name, - "commit_hash": ref.commit.hexsha if hasattr(ref, 'commit') else "0000000", - "is_remote": ref.is_remote() if hasattr(ref, 'is_remote') else False, - "last_updated": datetime.fromtimestamp(ref.commit.committed_date) if hasattr(ref, 'commit') else datetime.utcnow() - }) + active_name = repo.active_branch.name + if not any(b['name'] == active_name for b in branches): + branches.append({ + "name": active_name, "commit_hash": "0000000", + "is_remote": False, "last_updated": datetime.utcnow() + }) except Exception as e: - logger.reason(f"Skipping ref {ref}: {e}", extra={"src": "list_branches"}) - try: - active_name = repo.active_branch.name - if not any(b['name'] == active_name for b in branches): - branches.append({ - "name": active_name, "commit_hash": "0000000", - "is_remote": False, "last_updated": datetime.utcnow() - }) - except Exception as e: - logger.reason(f"Could not determine active branch: {e}", extra={"src": "list_branches"}) - if not branches: - branches.append({ - "name": "dev", "commit_hash": "0000000", - "is_remote": False, "last_updated": datetime.utcnow() - }) - return branches + logger.reason(f"Could not determine active branch: {e}", extra={"src": "list_branches"}) + if not branches: + branches.append({ + "name": "dev", "commit_hash": "0000000", + "is_remote": False, "last_updated": datetime.utcnow() + }) + return branches # endregion list_branches - # region create_branch [TYPE Function] - # @PURPOSE: Create a new branch from an existing one. + # region create_branch [C:4] [TYPE Function] [SEMANTICS git,branch,create,lock] + # @PURPOSE: Create a new branch from an existing one (concurrent-safe). # @PARAM: name (str) - New branch name. # @PARAM: from_branch (str) - Source branch. # @PRE: Repository exists; name is valid; from_branch exists or repo is empty. # @POST: A new branch is created in the repository. def create_branch(self, dashboard_id: int, name: str, from_branch: str = "main"): - with belief_scope("GitService.create_branch"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.create_branch"): + repo = self.get_repo(dashboard_id) logger.reason(f"Creating branch {name} from {from_branch}", extra={"src": "create_branch"}) if not repo.heads and not repo.remotes: logger.reason("Repository is empty. Creating initial commit to enable branching.", extra={"src": "create_branch"}) @@ -157,26 +170,28 @@ class GitServiceBranchMixin: raise # endregion create_branch - # region checkout_branch [TYPE Function] - # @PURPOSE: Switch to a specific branch. + # region checkout_branch [C:4] [TYPE Function] [SEMANTICS git,branch,checkout,lock] + # @PURPOSE: Switch to a specific branch (concurrent-safe). # @PRE: Repository exists and the specified branch name exists. # @POST: The repository working directory is updated to the specified branch. def checkout_branch(self, dashboard_id: int, name: str): - with belief_scope("GitService.checkout_branch"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.checkout_branch"): + repo = self.get_repo(dashboard_id) logger.reason(f"Checking out branch {name}", extra={"src": "checkout_branch"}) repo.git.checkout(name) # endregion checkout_branch - # region commit_changes [TYPE Function] - # @PURPOSE: Stage and commit changes. + # region commit_changes [C:4] [TYPE Function] [SEMANTICS git,commit,stage,lock] + # @PURPOSE: Stage and commit changes (concurrent-safe). # @PARAM: message (str) - Commit message. # @PARAM: files (List[str]) - Optional list of specific files to stage. # @PRE: Repository exists and has changes (dirty) or files are specified. # @POST: Changes are staged and a new commit is created. def commit_changes(self, dashboard_id: int, message: str, files: list[str] = None): - with belief_scope("GitService.commit_changes"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.commit_changes"): + repo = self.get_repo(dashboard_id) if not repo.is_dirty(untracked_files=True) and not files: logger.reason(f"No changes to commit for dashboard {dashboard_id}", extra={"src": "commit_changes"}) return diff --git a/backend/src/services/git/_gitea.py b/backend/src/services/git/_gitea.py index b90ba9b1..6a6c6471 100644 --- a/backend/src/services/git/_gitea.py +++ b/backend/src/services/git/_gitea.py @@ -1,6 +1,6 @@ -# #region GitServiceGiteaMixin [C:3] [TYPE Module] [SEMANTICS git, gitea, api, remote, connection] +# #region GitServiceGiteaMixin [C:4] [TYPE Module] [SEMANTICS git, gitea, api, remote, connection, http_pool] # @LAYER: Infra -# @BRIEF Gitea API operations for GitService — connection testing, repository CRUD, and pull request creation. +# @BRIEF Gitea API operations for GitService — connection testing, repository CRUD, and pull request creation. Uses shared self._http_client for connection pooling. # @RELATION USED_BY -> [GitService] from typing import Any @@ -35,24 +35,23 @@ class GitServiceGiteaMixin: return False pat = pat.strip() try: - async with httpx.AsyncClient() as client: - if provider == GitProvider.GITHUB: - headers = {"Authorization": f"token {pat}"} - api_url = "https://api.github.com/user" if "github.com" in url else f"{url.rstrip('/')}/api/v3/user" - resp = await client.get(api_url, headers=headers) - elif provider == GitProvider.GITLAB: - headers = {"PRIVATE-TOKEN": pat} - api_url = f"{url.rstrip('/')}/api/v4/user" - resp = await client.get(api_url, headers=headers) - elif provider == GitProvider.GITEA: - headers = {"Authorization": f"token {pat}"} - api_url = f"{url.rstrip('/')}/api/v1/user" - resp = await client.get(api_url, headers=headers) - else: - return False - if resp.status_code != 200: - logger.error(f"[test_connection][Coherence:Failed] Git connection test failed for {provider} at {api_url}. Status: {resp.status_code}") - return resp.status_code == 200 + if provider == GitProvider.GITHUB: + headers = {"Authorization": f"token {pat}"} + api_url = "https://api.github.com/user" if "github.com" in url else f"{url.rstrip('/')}/api/v3/user" + resp = await self._http_client.get(api_url, headers=headers) + elif provider == GitProvider.GITLAB: + headers = {"PRIVATE-TOKEN": pat} + api_url = f"{url.rstrip('/')}/api/v4/user" + resp = await self._http_client.get(api_url, headers=headers) + elif provider == GitProvider.GITEA: + headers = {"Authorization": f"token {pat}"} + api_url = f"{url.rstrip('/')}/api/v1/user" + resp = await self._http_client.get(api_url, headers=headers) + else: + return False + if resp.status_code != 200: + logger.error(f"[test_connection][Coherence:Failed] Git connection test failed for {provider} at {api_url}. Status: {resp.status_code}") + return resp.status_code == 200 except Exception as e: logger.error(f"[test_connection][Coherence:Failed] Error testing git connection: {e}") return False @@ -87,8 +86,7 @@ class GitServiceGiteaMixin: url = f"{base_url}/api/v1{endpoint}" headers = self._gitea_headers(pat) try: - async with httpx.AsyncClient(timeout=20.0) as client: - response = await client.request(method=method, url=url, headers=headers, json=payload) + response = await self._http_client.request(method=method, url=url, headers=headers, json=payload) except Exception as e: logger.error(f"[gitea_request][Coherence:Failed] Network error: {e}") raise HTTPException(status_code=503, detail=f"Gitea API is unavailable: {e!s}") diff --git a/backend/src/services/git/_merge.py b/backend/src/services/git/_merge.py index 00b4cef6..3e1b6ec1 100644 --- a/backend/src/services/git/_merge.py +++ b/backend/src/services/git/_merge.py @@ -1,6 +1,6 @@ -# #region GitServiceMergeMixin [C:3] [TYPE Module] [SEMANTICS git, merge, branch, conflict, resolution] +# #region GitServiceMergeMixin [C:4] [TYPE Module] [SEMANTICS git, merge, branch, conflict, resolution, lock] # @LAYER: Infra -# @BRIEF Merge operations for GitService — conflict detection, resolution, abort, continue, and direct promote. +# @BRIEF Merge operations for GitService — conflict detection, resolution, abort, continue, and direct promote (all concurrent-safe via per-dashboard locks). # @RELATION USED_BY -> [GitService] import os @@ -87,11 +87,12 @@ class GitServiceMergeMixin: } # endregion _build_unfinished_merge_payload - # region get_merge_status [TYPE Function] - # @PURPOSE: Get current merge status for a dashboard repository. + # region get_merge_status [C:4] [TYPE Function] [SEMANTICS git,merge,status,lock] + # @PURPOSE: Get current merge status for a dashboard repository (concurrent-safe). def get_merge_status(self, dashboard_id: int) -> dict[str, Any]: - with belief_scope("GitService.get_merge_status"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.get_merge_status"): + repo = self.get_repo(dashboard_id) merge_head_path = os.path.join(repo.git_dir, "MERGE_HEAD") if not os.path.exists(merge_head_path): current_branch = "unknown" @@ -120,11 +121,12 @@ class GitServiceMergeMixin: } # endregion get_merge_status - # region get_merge_conflicts [TYPE Function] - # @PURPOSE: List all files with conflicts and their contents. + # region get_merge_conflicts [C:4] [TYPE Function] [SEMANTICS git,conflict,list,lock] + # @PURPOSE: List all files with conflicts and their contents (concurrent-safe). def get_merge_conflicts(self, dashboard_id: int) -> list[dict[str, Any]]: - with belief_scope("GitService.get_merge_conflicts"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.get_merge_conflicts"): + repo = self.get_repo(dashboard_id) conflicts = [] unmerged = repo.index.unmerged_blobs() for file_path, stages in unmerged.items(): @@ -143,11 +145,12 @@ class GitServiceMergeMixin: return sorted(conflicts, key=lambda item: item["file_path"]) # endregion get_merge_conflicts - # region resolve_merge_conflicts [TYPE Function] - # @PURPOSE: Resolve conflicts using specified strategy. + # region resolve_merge_conflicts [C:4] [TYPE Function] [SEMANTICS git,conflict,resolve,lock] + # @PURPOSE: Resolve conflicts using specified strategy (concurrent-safe). def resolve_merge_conflicts(self, dashboard_id: int, resolutions: list[dict[str, Any]]) -> list[str]: - with belief_scope("GitService.resolve_merge_conflicts"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.resolve_merge_conflicts"): + repo = self.get_repo(dashboard_id) resolved_files: list[str] = [] repo_root = os.path.abspath(str(repo.working_tree_dir or "")) if not repo_root: @@ -176,11 +179,12 @@ class GitServiceMergeMixin: return resolved_files # endregion resolve_merge_conflicts - # region abort_merge [TYPE Function] - # @PURPOSE: Abort ongoing merge. + # region abort_merge [C:4] [TYPE Function] [SEMANTICS git,merge,abort,lock] + # @PURPOSE: Abort ongoing merge (concurrent-safe). def abort_merge(self, dashboard_id: int) -> dict[str, Any]: - with belief_scope("GitService.abort_merge"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.abort_merge"): + repo = self.get_repo(dashboard_id) try: repo.git.merge("--abort") except GitCommandError as e: @@ -192,11 +196,12 @@ class GitServiceMergeMixin: return {"status": "aborted"} # endregion abort_merge - # region continue_merge [TYPE Function] - # @PURPOSE: Finalize merge after conflict resolution. + # region continue_merge [C:4] [TYPE Function] [SEMANTICS git,merge,continue,lock] + # @PURPOSE: Finalize merge after conflict resolution (concurrent-safe). def continue_merge(self, dashboard_id: int, message: str | None = None) -> dict[str, Any]: - with belief_scope("GitService.continue_merge"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.continue_merge"): + repo = self.get_repo(dashboard_id) unmerged_files = self._get_unmerged_file_paths(repo) if unmerged_files: raise HTTPException( @@ -227,51 +232,69 @@ class GitServiceMergeMixin: return {"status": "committed", "commit_hash": commit_hash} # endregion continue_merge - # region promote_direct_merge [TYPE Function] - # @PURPOSE: Perform direct merge between branches in local repo and push target branch. + # region promote_direct_merge [C:4] [TYPE Function] [SEMANTICS git,merge,promote,branch,isolation] + # @PURPOSE: Perform direct merge between branches with branch isolation — original branch restored on error. # @PRE: Repository exists and both branches are valid. - # @POST: Target branch contains merged changes from source branch. + # @POST: Target branch contains merged changes from source branch. Active branch restored to original. + # Merge survives locally even if push fails (partial success). + # @SIDE_EFFECT: Changes local branch state during merge; restores original branch in finally block. # @RETURN: Dict[str, Any] def promote_direct_merge(self, dashboard_id: int, from_branch: str, to_branch: str) -> dict[str, Any]: - with belief_scope("GitService.promote_direct_merge"): - if not from_branch or not to_branch: - raise HTTPException(status_code=400, detail="from_branch and to_branch are required") - repo = self.get_repo(dashboard_id) - source = from_branch.strip() - target = to_branch.strip() - if source == target: - raise HTTPException(status_code=400, detail="from_branch and to_branch must be different") - try: - origin = repo.remote(name="origin") - except ValueError: - raise HTTPException(status_code=400, detail="Remote 'origin' not configured") - try: - origin.fetch() - if source not in [head.name for head in repo.heads]: - if f"origin/{source}" in [ref.name for ref in repo.refs]: - repo.git.checkout("-b", source, f"origin/{source}") - else: - raise HTTPException(status_code=404, detail=f"Source branch '{source}' not found") - if target in [head.name for head in repo.heads]: - repo.git.checkout(target) - elif f"origin/{target}" in [ref.name for ref in repo.refs]: - repo.git.checkout("-b", target, f"origin/{target}") - else: - raise HTTPException(status_code=404, detail=f"Target branch '{target}' not found") + with self._locked(dashboard_id): + with belief_scope("GitService.promote_direct_merge"): + if not from_branch or not to_branch: + raise HTTPException(status_code=400, detail="from_branch and to_branch are required") + repo = self.get_repo(dashboard_id) + source = from_branch.strip() + target = to_branch.strip() + if source == target: + raise HTTPException(status_code=400, detail="from_branch and to_branch must be different") try: - origin.pull(target) + origin = repo.remote(name="origin") + except ValueError: + raise HTTPException(status_code=400, detail="Remote 'origin' not configured") + + # Remember original branch for restoration in finally + original_branch = None + try: + original_branch = repo.active_branch.name except Exception: - pass - repo.git.merge(source, "--no-ff", "-m", f"chore(flow): promote {source} -> {target}") - origin.push(refspec=f"{target}:{target}") - except HTTPException: - raise - except Exception as e: - message = str(e) - if "CONFLICT" in message.upper(): - raise HTTPException(status_code=409, detail=f"Merge conflict during direct promote: {message}") - raise HTTPException(status_code=500, detail=f"Direct promote failed: {message}") - return {"mode": "direct", "from_branch": source, "to_branch": target, "status": "merged"} + original_branch = None + + try: + origin.fetch() + if source not in [head.name for head in repo.heads]: + if f"origin/{source}" in [ref.name for ref in repo.refs]: + repo.git.checkout("-b", source, f"origin/{source}") + else: + raise HTTPException(status_code=404, detail=f"Source branch '{source}' not found") + if target in [head.name for head in repo.heads]: + repo.git.checkout(target) + elif f"origin/{target}" in [ref.name for ref in repo.refs]: + repo.git.checkout("-b", target, f"origin/{target}") + else: + raise HTTPException(status_code=404, detail=f"Target branch '{target}' not found") + try: + origin.pull(target) + except Exception: + pass + repo.git.merge(source, "--no-ff", "-m", f"chore(flow): promote {source} -> {target}") + origin.push(refspec=f"{target}:{target}") + except HTTPException: + raise + except Exception as e: + message = str(e) + if "CONFLICT" in message.upper(): + raise HTTPException(status_code=409, detail=f"Merge conflict during direct promote: {message}") + raise HTTPException(status_code=500, detail=f"Direct promote failed: {message}") + finally: + # Restore original branch even if merge/push partially failed + if original_branch: + try: + repo.git.checkout(original_branch) + except Exception: + pass + return {"mode": "direct", "from_branch": source, "to_branch": target, "status": "merged"} # endregion promote_direct_merge # #endregion GitServiceMergeMixin # #endregion GitServiceMergeMixin diff --git a/backend/src/services/git/_remote_providers.py b/backend/src/services/git/_remote_providers.py index 506322ef..687bf5cf 100644 --- a/backend/src/services/git/_remote_providers.py +++ b/backend/src/services/git/_remote_providers.py @@ -1,6 +1,6 @@ -# #region GitServiceRemoteMixin [C:3] [TYPE Module] [SEMANTICS git, provider, github, remote, url] +# #region GitServiceRemoteMixin [C:4] [TYPE Module] [SEMANTICS git, provider, github, remote, url, http_pool] # @LAYER: Infra -# @BRIEF GitHub and GitLab provider operations for GitService — repository creation and PR/MR creation. +# @BRIEF GitHub and GitLab provider operations for GitService — repository creation and PR/MR creation. Uses shared self._http_client for connection pooling. # @RELATION USED_BY -> [GitService] from typing import Any @@ -38,8 +38,7 @@ class GitServiceGithubMixin: if default_branch: payload["default_branch"] = default_branch try: - async with httpx.AsyncClient(timeout=20.0) as client: - response = await client.post(api_url, headers=headers, json=payload) + response = await self._http_client.post(api_url, headers=headers, json=payload) except Exception as e: raise HTTPException(status_code=503, detail=f"GitHub API is unavailable: {e!s}") if response.status_code >= 400: @@ -78,8 +77,7 @@ class GitServiceGithubMixin: "body": description or "", "draft": bool(draft), } try: - async with httpx.AsyncClient(timeout=20.0) as client: - response = await client.post(api_url, headers=headers, json=payload) + response = await self._http_client.post(api_url, headers=headers, json=payload) except Exception as e: raise HTTPException(status_code=503, detail=f"GitHub API is unavailable: {e!s}") if response.status_code >= 400: @@ -118,8 +116,7 @@ class GitServiceGitlabMixin: if default_branch: payload["default_branch"] = default_branch try: - async with httpx.AsyncClient(timeout=20.0) as client: - response = await client.post(api_url, headers=headers, json=payload) + response = await self._http_client.post(api_url, headers=headers, json=payload) except Exception as e: raise HTTPException(status_code=503, detail=f"GitLab API is unavailable: {e!s}") if response.status_code >= 400: @@ -162,8 +159,7 @@ class GitServiceGitlabMixin: "description": description or "", "remove_source_branch": bool(remove_source_branch), } try: - async with httpx.AsyncClient(timeout=20.0) as client: - response = await client.post(api_url, headers=headers, json=payload) + response = await self._http_client.post(api_url, headers=headers, json=payload) except Exception as e: raise HTTPException(status_code=503, detail=f"GitLab API is unavailable: {e!s}") if response.status_code >= 400: diff --git a/backend/src/services/git/_status.py b/backend/src/services/git/_status.py index d76c91ef..a3a1a378 100644 --- a/backend/src/services/git/_status.py +++ b/backend/src/services/git/_status.py @@ -1,6 +1,6 @@ -# #region GitServiceStatusMixin [C:3] [TYPE Module] [SEMANTICS git, status, diff, log, history] +# #region GitServiceStatusMixin [C:4] [TYPE Module] [SEMANTICS git, status, diff, log, history, lock] # @LAYER: Infra -# @BRIEF Status, diff, and commit history operations for GitService using git status --porcelain to avoid --cached flag issues. +# @BRIEF Status, diff, and commit history operations for GitService (all concurrent-safe via per-dashboard locks). # @RELATION USED_BY -> [GitService] from datetime import datetime @@ -50,14 +50,15 @@ class GitServiceStatusMixin: return staged, modified, untracked # endregion _parse_status_porcelain - # region get_status [TYPE Function] - # @PURPOSE: Get current repository status (dirty files, untracked, etc.) + # region get_status [C:4] [TYPE Function] [SEMANTICS git,status,lock] + # @PURPOSE: Get current repository status (concurrent-safe). # @PRE: Repository for dashboard_id exists. # @POST: Returns a dictionary representing the Git status. # @RETURN: dict def get_status(self, dashboard_id: int) -> dict: - with belief_scope("GitService.get_status"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.get_status"): + repo = self.get_repo(dashboard_id) has_commits = False try: repo.head.commit @@ -110,16 +111,17 @@ class GitServiceStatusMixin: } # endregion get_status - # region get_diff [TYPE Function] - # @PURPOSE: Generate diff for a file or the whole repository. + # region get_diff [C:4] [TYPE Function] [SEMANTICS git,diff,lock] + # @PURPOSE: Generate diff for a file or the whole repository (concurrent-safe). # @PARAM: file_path (str) - Optional specific file. # @PARAM: staged (bool) - Whether to show staged changes. # @PRE: Repository for dashboard_id exists. # @POST: Returns the diff text as a string. # @RETURN: str def get_diff(self, dashboard_id: int, file_path: str = None, staged: bool = False) -> str: - with belief_scope("GitService.get_diff"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.get_diff"): + repo = self.get_repo(dashboard_id) diff_args = [] if staged: diff_args.append("--staged") @@ -128,15 +130,16 @@ class GitServiceStatusMixin: return repo.git.diff(*diff_args) # endregion get_diff - # region get_commit_history [TYPE Function] - # @PURPOSE: Retrieve commit history for a repository. + # region get_commit_history [C:4] [TYPE Function] [SEMANTICS git,history,lock] + # @PURPOSE: Retrieve commit history for a repository (concurrent-safe). # @PARAM: limit (int) - Max number of commits to return. # @PRE: Repository for dashboard_id exists. # @POST: Returns a list of dictionaries for each commit in history. # @RETURN: List[dict] def get_commit_history(self, dashboard_id: int, limit: int = 50) -> list[dict]: - with belief_scope("GitService.get_commit_history"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.get_commit_history"): + repo = self.get_repo(dashboard_id) commits = [] try: if not repo.heads and not repo.remotes: diff --git a/backend/src/services/git/_sync.py b/backend/src/services/git/_sync.py index 546c7923..d7d51979 100644 --- a/backend/src/services/git/_sync.py +++ b/backend/src/services/git/_sync.py @@ -1,6 +1,6 @@ -# #region GitServiceSyncMixin [C:3] [TYPE Module] [SEMANTICS git, sync, push, pull, remote] +# #region GitServiceSyncMixin [C:4] [TYPE Module] [SEMANTICS git, sync, push, pull, remote, lock] # @LAYER: Infra -# @BRIEF Push and pull operations for GitService with origin host auto-alignment. +# @BRIEF Push and pull operations for GitService with origin host auto-alignment (concurrent-safe). # @RELATION USED_BY -> [GitService] # @RELATION DEPENDS_ON -> [GitServiceUrlMixin] @@ -17,13 +17,14 @@ from src.models.git import GitRepository, GitServerConfig # #region GitServiceSyncMixin [C:3] [TYPE Class] # @BRIEF Mixin providing push and pull operations with origin host alignment. class GitServiceSyncMixin: - # region push_changes [TYPE Function] - # @PURPOSE: Push local commits to remote. + # region push_changes [C:4] [TYPE Function] [SEMANTICS git,push,lock] + # @PURPOSE: Push local commits to remote (concurrent-safe). # @PRE: Repository exists and has an 'origin' remote. # @POST: Local branch commits are pushed to origin. def push_changes(self, dashboard_id: int): - with belief_scope("GitService.push_changes"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.push_changes"): + repo = self.get_repo(dashboard_id) if not repo.heads: logger.warning(f"[push_changes][Coherence:Failed] No local branches to push for dashboard {dashboard_id}") return @@ -110,13 +111,14 @@ class GitServiceSyncMixin: raise HTTPException(status_code=500, detail=f"Git push failed: {e!s}") # endregion push_changes - # region pull_changes [TYPE Function] - # @PURPOSE: Pull changes from remote. + # region pull_changes [C:4] [TYPE Function] [SEMANTICS git,pull,lock] + # @PURPOSE: Pull changes from remote (concurrent-safe). # @PRE: Repository exists and has an 'origin' remote. # @POST: Changes from origin are pulled and merged into the active branch. def pull_changes(self, dashboard_id: int): - with belief_scope("GitService.pull_changes"): - repo = self.get_repo(dashboard_id) + with self._locked(dashboard_id): + with belief_scope("GitService.pull_changes"): + repo = self.get_repo(dashboard_id) merge_head_path = os.path.join(repo.git_dir, "MERGE_HEAD") if os.path.exists(merge_head_path): payload = self._build_unfinished_merge_payload(repo) diff --git a/backend/tests/services/clean_release/test_report_audit_immutability.py b/backend/tests/services/clean_release/test_report_audit_immutability.py index 3af24024..9f7ef340 100644 --- a/backend/tests/services/clean_release/test_report_audit_immutability.py +++ b/backend/tests/services/clean_release/test_report_audit_immutability.py @@ -128,11 +128,17 @@ def test_audit_hooks_emit_append_only_event_stream(mock_logger): audit_check_run("run-immut-1", "PASSED") audit_report("CCR-immut-1", "cand-immut-1") - assert mock_logger.info.call_count == 3 - logged_messages = [call.args[0] for call in mock_logger.info.call_args_list] - assert logged_messages[0].startswith("[REASON]") - assert logged_messages[1].startswith("[REFLECT]") - assert logged_messages[2].startswith("[EXPLORE]") + assert mock_logger.reason.call_count == 1 + assert mock_logger.reflect.call_count == 1 + assert mock_logger.explore.call_count == 1 + + reason_msg = mock_logger.reason.call_args[0][0] + reflect_msg = mock_logger.reflect.call_args[0][0] + explore_msg = mock_logger.explore.call_args[0][0] + + assert reason_msg.startswith("clean-release preparation") + assert reflect_msg.startswith("clean-release check_run") + assert explore_msg.startswith("clean-release report_id") # [/DEF:test_audit_hooks_emit_append_only_event_stream:Function] diff --git a/docker-compose.yml b/docker-compose.yml index a0f6e976..de0030be 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -53,7 +53,7 @@ services: depends_on: - backend ports: - - "${FRONTEND_HOST_PORT:-8000}:80" + - "8100:80" volumes: postgres_data: diff --git a/e2e_final_dashboard.png b/e2e_final_dashboard.png new file mode 100644 index 00000000..11e002d7 Binary files /dev/null and b/e2e_final_dashboard.png differ diff --git a/e2e_profile_01.png b/e2e_profile_01.png new file mode 100644 index 00000000..f9573676 Binary files /dev/null and b/e2e_profile_01.png differ diff --git a/e2e_settings_01_environments.png b/e2e_settings_01_environments.png new file mode 100644 index 00000000..0f93057e Binary files /dev/null and b/e2e_settings_01_environments.png differ diff --git a/e2e_settings_02_edit_env.png b/e2e_settings_02_edit_env.png new file mode 100644 index 00000000..b101acba Binary files /dev/null and b/e2e_settings_02_edit_env.png differ diff --git a/e2e_settings_03_git.png b/e2e_settings_03_git.png new file mode 100644 index 00000000..4b716ee6 Binary files /dev/null and b/e2e_settings_03_git.png differ diff --git a/e2e_settings_04_llm.png b/e2e_settings_04_llm.png new file mode 100644 index 00000000..a4ea3857 Binary files /dev/null and b/e2e_settings_04_llm.png differ diff --git a/e2e_settings_05_storage.png b/e2e_settings_05_storage.png new file mode 100644 index 00000000..8cd65d6b Binary files /dev/null and b/e2e_settings_05_storage.png differ diff --git a/e2e_settings_06_logging.png b/e2e_settings_06_logging.png new file mode 100644 index 00000000..09fef968 Binary files /dev/null and b/e2e_settings_06_logging.png differ diff --git a/e2e_settings_07_connections.png b/e2e_settings_07_connections.png new file mode 100644 index 00000000..c3ed958e Binary files /dev/null and b/e2e_settings_07_connections.png differ diff --git a/e2e_settings_08_migration.png b/e2e_settings_08_migration.png new file mode 100644 index 00000000..0bcdc744 Binary files /dev/null and b/e2e_settings_08_migration.png differ diff --git a/e2e_settings_09_features.png b/e2e_settings_09_features.png new file mode 100644 index 00000000..d28b9d6d Binary files /dev/null and b/e2e_settings_09_features.png differ diff --git a/e2e_settings_10_automation.png b/e2e_settings_10_automation.png new file mode 100644 index 00000000..1d0a5c31 Binary files /dev/null and b/e2e_settings_10_automation.png differ diff --git a/e2e_settings_11_users.png b/e2e_settings_11_users.png new file mode 100644 index 00000000..3b36df18 Binary files /dev/null and b/e2e_settings_11_users.png differ diff --git a/e2e_settings_12_roles.png b/e2e_settings_12_roles.png new file mode 100644 index 00000000..9d827f7c Binary files /dev/null and b/e2e_settings_12_roles.png differ diff --git a/frontend/src/components/git/CommitModal.svelte b/frontend/src/components/git/CommitModal.svelte index 4c773d45..3286c485 100644 --- a/frontend/src/components/git/CommitModal.svelte +++ b/frontend/src/components/git/CommitModal.svelte @@ -47,6 +47,8 @@ // postApi returns the JSON data directly or throws an error const data = await api.postApi( `/git/repositories/${encodeURIComponent(String(dashboardId))}/generate-message${envId ? `?env_id=${encodeURIComponent(String(envId))}` : ""}`, + undefined, + { suppressToast: true }, ); message = data.message; toast($t.git?.commit_message_generated, "success"); diff --git a/frontend/src/components/git/GitManager.svelte b/frontend/src/components/git/GitManager.svelte index 9187be8e..3fd2f74c 100644 --- a/frontend/src/components/git/GitManager.svelte +++ b/frontend/src/components/git/GitManager.svelte @@ -207,7 +207,11 @@ await loadWorkspace(); } catch (_e) { initialized = false; - configs = await gitService.getConfigs(); + try { + configs = await gitService.getConfigs(); + } catch (_e2) { + configs = []; + } const defaultConfig = resolveDefaultConfig(configs); if (defaultConfig?.id) selectedConfigId = defaultConfig.id; } finally { @@ -272,6 +276,8 @@ try { const data = await api.postApi( `/git/repositories/${encodeURIComponent(String(dashboardId))}/generate-message${envId ? `?env_id=${encodeURIComponent(String(envId))}` : ''}`, + undefined, + { suppressToast: true }, ); commitMessage = data?.message || ''; toast($t.git?.commit_message_generated || 'Сообщение для коммита сгенерировано', 'success'); diff --git a/frontend/src/lib/api.js b/frontend/src/lib/api.js index 2520e526..5246b374 100755 --- a/frontend/src/lib/api.js +++ b/frontend/src/lib/api.js @@ -210,7 +210,9 @@ async function postApi(endpoint, body, options = {}) { return await response.json(); } catch (error) { console.error(`[api.postApi][Coherence:Failed] Error posting to ${endpoint}:`, error); - notifyApiError(error); + if (!options.suppressToast) { + notifyApiError(error); + } throw error; } } diff --git a/frontend/src/lib/components/layout/Sidebar.svelte b/frontend/src/lib/components/layout/Sidebar.svelte index 48533b6e..e5d79e41 100644 --- a/frontend/src/lib/components/layout/Sidebar.svelte +++ b/frontend/src/lib/components/layout/Sidebar.svelte @@ -72,6 +72,7 @@ const failingCountState = fromStore(failingCount); let features = $state({}); + let cleanupInterval = null; let categories = $derived( buildSidebarCategories( @@ -195,21 +196,29 @@ }); onMount(() => { - healthStore.refresh(); - // Refresh every 5 minutes - const interval = setInterval(() => healthStore.refresh(), 5 * 60 * 1000); - - // Fetch feature flags for sidebar filtering + // Fetch feature flags first, then conditionally refresh health fetchApi("/settings/features") .then((res) => { features = res; + if (res.health_monitor) { + healthStore.refresh(); + // Refresh every 5 minutes + const interval = setInterval(() => healthStore.refresh(), 5 * 60 * 1000); + cleanupInterval = interval; + } }) .catch(() => { // Graceful degradation: default to {} (all features visible) features = {}; + // On error, try health anyway + healthStore.refresh(); + const interval = setInterval(() => healthStore.refresh(), 5 * 60 * 1000); + cleanupInterval = interval; }); - return () => clearInterval(interval); + return () => { + if (cleanupInterval) clearInterval(cleanupInterval); + }; }); diff --git a/frontend/src/lib/components/translate/BulkCorrectionSidebar.svelte b/frontend/src/lib/components/translate/BulkCorrectionSidebar.svelte index f2ebda82..7f683aa8 100644 --- a/frontend/src/lib/components/translate/BulkCorrectionSidebar.svelte +++ b/frontend/src/lib/components/translate/BulkCorrectionSidebar.svelte @@ -1,23 +1,15 @@ - + + + + + + + + +