diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index dc9d0fa4..4660574a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,5 +1,5 @@ # Branch protection: require every status check this workflow reports, for example: -# Ban type ignore suppressions +# Ban suppressions and legacy annotations # ruff-format, ruff-check, ty, pytest # GitHub may prefix with the workflow name (e.g. "CI / ruff-format"); use the names the branch protection UI offers after a run. @@ -17,8 +17,8 @@ concurrency: cancel-in-progress: true jobs: - no-type-ignore-suppressions: - name: Ban type ignore suppressions + no-suppressions-or-legacy-annotations: + name: Ban suppressions and legacy annotations runs-on: ubuntu-latest timeout-minutes: 15 permissions: @@ -30,10 +30,10 @@ jobs: repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} fetch-depth: 1 - - name: "Fail on type: ignore (no suppressions allowed)" + - name: "Fail on type ignores and legacy future annotations" run: | - if grep -rE '# type: ignore|# ty: ignore' --include='*.py' . --exclude-dir=.venv --exclude-dir=.git; then - echo "::error::type: ignore / ty: ignore comments are not allowed. Fix the underlying type errors instead." + if grep -rE '# type: ignore|# ty: ignore|from __future__ import annotations' --include='*.py' . --exclude-dir=.venv --exclude-dir=.git; then + echo "::error::type: ignore / ty: ignore comments and legacy future annotations are not allowed. Fix the underlying type/import issue instead." exit 1 fi exit 0 diff --git a/AGENTS.md b/AGENTS.md index e19df493..beda930f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,10 +16,11 @@ - GitHub CI remains check-only for Ruff (`ruff format --check`, `ruff check`) so branch protection verifies committed code. - Fall back to individual repair commands when debugging local failures: `uv run ruff format`, `uv run ruff check --fix`, `uv run ty check`, `uv run pytest -v --tb=short`. Use GitHub-style checks only when verifying enforcement locally: `uv run ruff format --check`, `uv run ruff check`. - Do not add `# type: ignore` or `# ty: ignore`; fix the underlying type issue. +- Do not add `from __future__ import annotations`; Python 3.14 native lazy annotations are the project standard. - All 5 check IDs are represented in `scripts/ci.sh` / `scripts/ci.ps1` and enforced in `tests.yml` on push/merge (parallel jobs: suppression grep, ruff-format, ruff-check, ty, pytest). - GitHub CI runs on `push`, `pull_request`, and `merge_group` so required checks validate merge queue candidates before they land. - Repository protection should use rulesets: a non-bypassable main integrity ruleset requires pull requests, merge queue, required checks, and blocks direct/force pushes to `main`; a separate review ruleset may allow `Alishahryar1`/admins to bypass review only. -- Required status checks: set **required status checks** to **all** of those statuses (e.g. **Ban type ignore suppressions**, **ruff-format**, **ruff-check**, **ty**, **pytest**—use the exact labels GitHub shows, which may be prefixed with **CI /**). Remove **ci** from required checks if it was previously added for the old gate job. +- Required status checks: set **required status checks** to **all** of those statuses (e.g. **Ban suppressions and legacy annotations**, **ruff-format**, **ruff-check**, **ty**, **pytest**—use the exact labels GitHub shows, which may be prefixed with **CI /**). Remove **ci** from required checks if it was previously added for the old gate job. ## IDENTITY & CONTEXT @@ -37,6 +38,8 @@ - **Performance**: Use list accumulation for strings (not `+=` in loops), cache env vars at init, prefer iterative over recursive when stack depth matters. - **Platform-agnostic naming**: Use generic names (e.g. `PLATFORM_EDIT`) not platform-specific ones (e.g. `TELEGRAM_EDIT`) in shared code. - **No type ignores**: Do not add `# type: ignore` or `# ty: ignore`. Fix the underlying type issue. +- **Python 3.14 annotations**: Do not use `from __future__ import annotations`; rely on native lazy annotations and fix circular import boundaries instead of hiding them with annotation stringization. +- **Imports**: Prefer top-level imports. Avoid `TYPE_CHECKING` and local imports for first-party or required dependencies; if a top-level import creates a cycle, move shared types/protocols to a neutral owner. - **Complete migrations**: When moving modules, update imports to the new owner and remove old compatibility shims in the same change unless preserving a published interface is explicitly required. - **Maximum Test Coverage**: There should be maximum test coverage for everything, preferably live smoke test coverage to catch bugs early diff --git a/CLAUDE.md b/CLAUDE.md index 11f15e1d..fff5c364 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,10 +16,11 @@ - GitHub CI remains check-only for Ruff (`ruff format --check`, `ruff check`) so branch protection verifies committed code. - Fall back to individual repair commands when debugging local failures: `uv run ruff format`, `uv run ruff check --fix`, `uv run ty check`, `uv run pytest -v --tb=short`. Use GitHub-style checks only when verifying enforcement locally: `uv run ruff format --check`, `uv run ruff check`. - Do not add `# type: ignore` or `# ty: ignore`; fix the underlying type issue. +- Do not add `from __future__ import annotations`; Python 3.14 native lazy annotations are the project standard. - All 5 check IDs are represented in `scripts/ci.sh` / `scripts/ci.ps1` and enforced in `tests.yml` on push/merge (parallel jobs: suppression grep, ruff-format, ruff-check, ty, pytest). - GitHub CI runs on `push`, `pull_request`, and `merge_group` so required checks validate merge queue candidates before they land. - Repository protection should use rulesets: a non-bypassable main integrity ruleset requires pull requests, merge queue, required checks, and blocks direct/force pushes to `main`; a separate review ruleset may allow `Alishahryar1`/admins to bypass review only. -- Required status checks: set **required status checks** to **all** of those statuses (e.g. **Ban type ignore suppressions**, **ruff-format**, **ruff-check**, **ty**, **pytest**—use the exact labels GitHub shows, which may be prefixed with **CI /**). Remove **ci** from required checks if it was previously added for the old gate job. +- Required status checks: set **required status checks** to **all** of those statuses (e.g. **Ban suppressions and legacy annotations**, **ruff-format**, **ruff-check**, **ty**, **pytest**—use the exact labels GitHub shows, which may be prefixed with **CI /**). Remove **ci** from required checks if it was previously added for the old gate job. ## IDENTITY & CONTEXT @@ -37,6 +38,8 @@ - **Performance**: Use list accumulation for strings (not `+=` in loops), cache env vars at init, prefer iterative over recursive when stack depth matters. - **Platform-agnostic naming**: Use generic names (e.g. `PLATFORM_EDIT`) not platform-specific ones (e.g. `TELEGRAM_EDIT`) in shared code. - **No type ignores**: Do not add `# type: ignore` or `# ty: ignore`. Fix the underlying type issue. +- **Python 3.14 annotations**: Do not use `from __future__ import annotations`; rely on native lazy annotations and fix circular import boundaries instead of hiding them with annotation stringization. +- **Imports**: Prefer top-level imports. Avoid `TYPE_CHECKING` and local imports for first-party or required dependencies; if a top-level import creates a cycle, move shared types/protocols to a neutral owner. - **Complete migrations**: When moving modules, update imports to the new owner and remove old compatibility shims in the same change unless preserving a published interface is explicitly required. - **Maximum Test Coverage**: There should be maximum test coverage for everything, preferably live smoke test coverage to catch bugs early diff --git a/api/admin_config/manifest.py b/api/admin_config/manifest.py index 2ff5c082..d2f61b31 100644 --- a/api/admin_config/manifest.py +++ b/api/admin_config/manifest.py @@ -1,7 +1,5 @@ """Admin UI configuration manifest.""" -from __future__ import annotations - from collections.abc import Iterable from dataclasses import dataclass from typing import Literal diff --git a/api/admin_config/persistence.py b/api/admin_config/persistence.py index a2919fe3..fb3edd0f 100644 --- a/api/admin_config/persistence.py +++ b/api/admin_config/persistence.py @@ -1,7 +1,5 @@ """Managed env persistence, validation preview, and rendering.""" -from __future__ import annotations - import os from collections.abc import Mapping from typing import Any diff --git a/api/admin_config/provider_manifest.py b/api/admin_config/provider_manifest.py index dbe12d56..65f5127f 100644 --- a/api/admin_config/provider_manifest.py +++ b/api/admin_config/provider_manifest.py @@ -1,7 +1,5 @@ """Catalog-derived Admin UI provider fields.""" -from __future__ import annotations - from typing import Any from config.provider_catalog import PROVIDER_CATALOG diff --git a/api/admin_config/sources.py b/api/admin_config/sources.py index 0cf72300..7a250f11 100644 --- a/api/admin_config/sources.py +++ b/api/admin_config/sources.py @@ -1,7 +1,5 @@ """Admin config source loading and source precedence.""" -from __future__ import annotations - import os from io import StringIO from pathlib import Path diff --git a/api/admin_config/status.py b/api/admin_config/status.py index f442bd97..332b6215 100644 --- a/api/admin_config/status.py +++ b/api/admin_config/status.py @@ -1,7 +1,5 @@ """Provider configuration status for the Admin UI.""" -from __future__ import annotations - from collections.abc import Mapping from typing import Any diff --git a/api/admin_config/validation.py b/api/admin_config/validation.py index ae25c9df..6ac1a338 100644 --- a/api/admin_config/validation.py +++ b/api/admin_config/validation.py @@ -1,7 +1,5 @@ """Settings-backed Admin UI config validation.""" -from __future__ import annotations - from collections.abc import Mapping from typing import Any diff --git a/api/admin_config/values.py b/api/admin_config/values.py index b2ace6ce..dec4c4e6 100644 --- a/api/admin_config/values.py +++ b/api/admin_config/values.py @@ -1,7 +1,5 @@ """Admin config value state and API response assembly.""" -from __future__ import annotations - import os from typing import Any diff --git a/api/admin_routes.py b/api/admin_routes.py index 834dc2d2..622a4e4d 100644 --- a/api/admin_routes.py +++ b/api/admin_routes.py @@ -1,7 +1,5 @@ """Local admin UI routes and APIs.""" -from __future__ import annotations - import inspect import ipaddress from pathlib import Path diff --git a/api/admin_urls.py b/api/admin_urls.py index e37ea117..0963022d 100644 --- a/api/admin_urls.py +++ b/api/admin_urls.py @@ -1,7 +1,5 @@ """Helpers for presenting local admin URLs.""" -from __future__ import annotations - from config.settings import Settings diff --git a/api/gateway_model_ids.py b/api/gateway_model_ids.py index bb0564a1..d02e7024 100644 --- a/api/gateway_model_ids.py +++ b/api/gateway_model_ids.py @@ -1,7 +1,5 @@ """Gateway-safe model id encoding for Claude Code model discovery.""" -from __future__ import annotations - from dataclasses import dataclass GATEWAY_MODEL_ID_PREFIX = "anthropic" diff --git a/api/handlers/messages.py b/api/handlers/messages.py index 37ae5c43..28a2cb04 100644 --- a/api/handlers/messages.py +++ b/api/handlers/messages.py @@ -1,7 +1,5 @@ """Claude Messages API product flow.""" -from __future__ import annotations - from collections.abc import AsyncIterator, Callable from dataclasses import dataclass, replace diff --git a/api/handlers/responses.py b/api/handlers/responses.py index 09ad0e60..eacb5944 100644 --- a/api/handlers/responses.py +++ b/api/handlers/responses.py @@ -1,7 +1,5 @@ """OpenAI Responses API product flow for Codex clients.""" -from __future__ import annotations - from collections.abc import Callable from fastapi.responses import JSONResponse diff --git a/api/handlers/token_count.py b/api/handlers/token_count.py index 241a7e5a..74ea6196 100644 --- a/api/handlers/token_count.py +++ b/api/handlers/token_count.py @@ -1,7 +1,5 @@ """Anthropic token-count API product flow.""" -from __future__ import annotations - import uuid from fastapi import HTTPException diff --git a/api/model_catalog.py b/api/model_catalog.py index 60caea50..c26564e4 100644 --- a/api/model_catalog.py +++ b/api/model_catalog.py @@ -1,7 +1,5 @@ """Model-list response construction for Claude-compatible clients.""" -from __future__ import annotations - from config.model_refs import configured_chat_model_refs from config.settings import Settings from providers.runtime import ProviderRuntime diff --git a/api/model_router.py b/api/model_router.py index efb3aecd..32ee093b 100644 --- a/api/model_router.py +++ b/api/model_router.py @@ -1,7 +1,5 @@ """Model routing for Claude-compatible requests.""" -from __future__ import annotations - from dataclasses import dataclass from loguru import logger diff --git a/api/models/openai_responses.py b/api/models/openai_responses.py index 66a16f4d..ec80b446 100644 --- a/api/models/openai_responses.py +++ b/api/models/openai_responses.py @@ -1,7 +1,5 @@ """Pydantic models for OpenAI Responses-compatible ingress.""" -from __future__ import annotations - from typing import Any from pydantic import BaseModel, ConfigDict diff --git a/api/provider_execution.py b/api/provider_execution.py index 4d019eb3..785bfdf6 100644 --- a/api/provider_execution.py +++ b/api/provider_execution.py @@ -1,7 +1,5 @@ """Shared provider execution primitive for API product handlers.""" -from __future__ import annotations - import uuid from collections.abc import AsyncIterator, Callable from typing import Any diff --git a/api/request_errors.py b/api/request_errors.py index cb193bab..3ea2798d 100644 --- a/api/request_errors.py +++ b/api/request_errors.py @@ -1,7 +1,5 @@ """Shared API request validation and safe error logging.""" -from __future__ import annotations - import traceback from typing import Any diff --git a/api/response_streams.py b/api/response_streams.py index 5651880e..fc0c6e3f 100644 --- a/api/response_streams.py +++ b/api/response_streams.py @@ -1,7 +1,5 @@ """FastAPI streaming response wrappers for public API wire formats.""" -from __future__ import annotations - from collections.abc import AsyncIterator, Mapping from fastapi.responses import StreamingResponse diff --git a/api/runtime.py b/api/runtime.py index 33be21e1..e463f4d7 100644 --- a/api/runtime.py +++ b/api/runtime.py @@ -1,29 +1,30 @@ """Application runtime composition and lifecycle ownership.""" -from __future__ import annotations - import asyncio import logging import os +import traceback from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any +from typing import Any from fastapi import FastAPI from loguru import logger +import cli.managed as cli_managed +import messaging.limiter as messaging_limiter +import messaging.session as messaging_session +import messaging.trees as messaging_trees +import messaging.workflow as messaging_workflow_module from api.admin_urls import local_admin_url from config.env_files import ANTHROPIC_AUTH_TOKEN_ENV, process_env_key_is_effective from config.paths import default_claude_workspace_path from config.settings import Settings, get_settings +from messaging.platforms import factory as messaging_platform_factory +from messaging.platforms.factory import MessagingPlatformOptions +from messaging.platforms.ports import MessagingPlatformComponents, MessagingRuntime from providers.exceptions import ServiceUnavailableError from providers.runtime import ProviderRuntime -if TYPE_CHECKING: - from cli.managed import ManagedClaudeSessionManager - from messaging.platforms.ports import MessagingPlatformComponents, MessagingRuntime - from messaging.session import SessionStore - from messaging.workflow import MessagingWorkflow - _SHUTDOWN_TIMEOUT_S = 5.0 @@ -92,8 +93,8 @@ class AppRuntime: settings: Settings _provider_runtime: ProviderRuntime | None = field(default=None, init=False) messaging_runtime: MessagingRuntime | None = None - messaging_workflow: MessagingWorkflow | None = None - cli_manager: ManagedClaudeSessionManager | None = None + messaging_workflow: messaging_workflow_module.MessagingWorkflow | None = None + cli_manager: cli_managed.ManagedClaudeSessionManager | None = None @classmethod def for_app( @@ -179,12 +180,7 @@ class AppRuntime: async def _start_messaging_if_configured(self) -> None: try: - from messaging.platforms.factory import ( - MessagingPlatformOptions, - create_messaging_components, - ) - - components = create_messaging_components( + components = messaging_platform_factory.create_messaging_components( self.settings.messaging_platform, MessagingPlatformOptions( telegram_bot_token=self.settings.telegram_bot_token, @@ -217,8 +213,6 @@ class AppRuntime: except Exception as e: if self.settings.log_api_error_tracebacks: logger.error("Failed to start messaging platform: {}", e) - import traceback - logger.error(traceback.format_exc()) else: logger.error( @@ -229,10 +223,6 @@ class AppRuntime: async def _start_messaging_workflow( self, components: MessagingPlatformComponents ) -> None: - from cli.managed import ManagedClaudeSessionManager - from messaging.session import SessionStore - from messaging.workflow import MessagingWorkflow - workspace = ( os.path.abspath(self.settings.allowed_dir) if self.settings.allowed_dir @@ -247,7 +237,7 @@ class AppRuntime: allowed_dirs = [workspace] if self.settings.allowed_dir else [] plans_dir_abs = os.path.abspath(os.path.join(data_path, "plans")) plans_directory = os.path.relpath(plans_dir_abs, workspace) - self.cli_manager = ManagedClaudeSessionManager( + self.cli_manager = cli_managed.ManagedClaudeSessionManager( workspace_path=workspace, api_url=api_url, allowed_dirs=allowed_dirs, @@ -257,12 +247,12 @@ class AppRuntime: log_messaging_error_details=self.settings.log_messaging_error_details, ) - session_store = SessionStore( + session_store = messaging_session.SessionStore( storage_path=os.path.join(data_path, "sessions.json"), message_log_cap=self.settings.max_message_log_entries_per_chat, ) self.messaging_runtime = components.runtime - self.messaging_workflow = MessagingWorkflow( + self.messaging_workflow = messaging_workflow_module.MessagingWorkflow( platform_name=components.name, outbound=components.outbound, voice_cancellation=components.voice_cancellation, @@ -280,7 +270,9 @@ class AppRuntime: await components.runtime.start() logger.info("{} platform started with messaging workflow", components.name) - def _restore_tree_state(self, session_store: SessionStore) -> None: + def _restore_tree_state( + self, session_store: messaging_session.SessionStore + ) -> None: conversation_snapshot = session_store.load_conversation_snapshot() if conversation_snapshot.is_empty: return @@ -291,10 +283,9 @@ class AppRuntime: "Restoring {} conversation trees...", len(conversation_snapshot.trees), ) - from messaging.trees import TreeQueueManager self.messaging_workflow.replace_tree_queue( - TreeQueueManager.from_snapshot( + messaging_trees.TreeQueueManager.from_snapshot( conversation_snapshot, queue_update_callback=self.messaging_workflow.update_queue_positions, node_started_callback=self.messaging_workflow.mark_node_processing, @@ -313,24 +304,18 @@ class AppRuntime: async def _shutdown_limiter(self) -> None: verbose = self.settings.log_api_error_tracebacks try: - from messaging.limiter import MessagingRateLimiter + await best_effort( + "MessagingRateLimiter.shutdown_instance", + messaging_limiter.MessagingRateLimiter.shutdown_instance(), + timeout_s=2.0, + log_verbose_errors=verbose, + ) except Exception as e: if verbose: logger.debug( - "Rate limiter shutdown skipped (import failed): {}: {}", - type(e).__name__, - e, + "Rate limiter shutdown skipped: {}: {}", type(e).__name__, e ) else: logger.debug( - "Rate limiter shutdown skipped (import failed): exc_type={}", - type(e).__name__, + "Rate limiter shutdown skipped: exc_type={}", type(e).__name__ ) - return - - await best_effort( - "MessagingRateLimiter.shutdown_instance", - MessagingRateLimiter.shutdown_instance(), - timeout_s=2.0, - log_verbose_errors=verbose, - ) diff --git a/api/validation_log.py b/api/validation_log.py index 9ccdff0f..87d812d9 100644 --- a/api/validation_log.py +++ b/api/validation_log.py @@ -1,7 +1,5 @@ """Safe metadata summaries for HTTP 422 validation logging (no raw text content).""" -from __future__ import annotations - from typing import Any diff --git a/api/web_server_tools.py b/api/web_server_tools.py index cedaf956..da4de693 100644 --- a/api/web_server_tools.py +++ b/api/web_server_tools.py @@ -1,7 +1,5 @@ """Compatibility re-exports for :mod:`api.web_tools` (web_search / web_fetch).""" -from __future__ import annotations - import httpx from api.web_tools.egress import ( diff --git a/api/web_tools/egress.py b/api/web_tools/egress.py index 2f2a7d32..1aec7223 100644 --- a/api/web_tools/egress.py +++ b/api/web_tools/egress.py @@ -1,7 +1,5 @@ """Egress policy for user-controlled web_fetch URLs (SSRF guard).""" -from __future__ import annotations - import ipaddress import socket from dataclasses import dataclass diff --git a/api/web_tools/outbound.py b/api/web_tools/outbound.py index 4f93f9bf..d0435611 100644 --- a/api/web_tools/outbound.py +++ b/api/web_tools/outbound.py @@ -1,7 +1,5 @@ """Outbound HTTP for web_search / web_fetch (client, body caps, logging).""" -from __future__ import annotations - import asyncio import socket from collections.abc import AsyncIterator diff --git a/api/web_tools/parsers.py b/api/web_tools/parsers.py index 198b4123..797af7c2 100644 --- a/api/web_tools/parsers.py +++ b/api/web_tools/parsers.py @@ -1,7 +1,5 @@ """HTML parsing for web_search / web_fetch.""" -from __future__ import annotations - import html import re from html.parser import HTMLParser diff --git a/api/web_tools/request.py b/api/web_tools/request.py index 93874c35..fb6f3782 100644 --- a/api/web_tools/request.py +++ b/api/web_tools/request.py @@ -1,7 +1,5 @@ """Detect forced Anthropic web server tool requests.""" -from __future__ import annotations - from api.models.anthropic import MessagesRequest, Tool diff --git a/api/web_tools/streaming.py b/api/web_tools/streaming.py index db5e9b8e..4c7de9c0 100644 --- a/api/web_tools/streaming.py +++ b/api/web_tools/streaming.py @@ -1,7 +1,5 @@ """SSE streaming for local web_search / web_fetch server tool results.""" -from __future__ import annotations - import uuid from collections.abc import AsyncIterator from datetime import UTC, datetime diff --git a/cli/claude_env.py b/cli/claude_env.py index 97827f9a..1a0e482f 100644 --- a/cli/claude_env.py +++ b/cli/claude_env.py @@ -1,7 +1,5 @@ """Shared Claude Code environment policy for FCC client surfaces.""" -from __future__ import annotations - CLAUDE_CODE_AUTO_COMPACT_WINDOW = "190000" CLAUDE_BINARY_NAME = "claude" CLAUDE_NO_AUTH_SENTINEL = "fcc-no-auth" diff --git a/cli/entrypoints.py b/cli/entrypoints.py index ae1b36c4..64b8ef86 100644 --- a/cli/entrypoints.py +++ b/cli/entrypoints.py @@ -1,7 +1,5 @@ """CLI entry points for the installed package.""" -from __future__ import annotations - import os import shutil import threading diff --git a/cli/launchers/claude.py b/cli/launchers/claude.py index 9dba9d64..793c3764 100644 --- a/cli/launchers/claude.py +++ b/cli/launchers/claude.py @@ -1,7 +1,5 @@ """Installed `fcc-claude` launcher.""" -from __future__ import annotations - import os import sys from collections.abc import Mapping, Sequence diff --git a/cli/launchers/codex.py b/cli/launchers/codex.py index 2ad15195..f6055bce 100644 --- a/cli/launchers/codex.py +++ b/cli/launchers/codex.py @@ -1,7 +1,5 @@ """Installed `fcc-codex` launcher.""" -from __future__ import annotations - import json import os import sys diff --git a/cli/launchers/codex_model_catalog.py b/cli/launchers/codex_model_catalog.py index 604bdd00..1bb103c1 100644 --- a/cli/launchers/codex_model_catalog.py +++ b/cli/launchers/codex_model_catalog.py @@ -1,7 +1,5 @@ """Build Codex model catalogs from the FCC model-list route.""" -from __future__ import annotations - import json import uuid from collections.abc import Mapping diff --git a/cli/launchers/common.py b/cli/launchers/common.py index d65441a1..af52e312 100644 --- a/cli/launchers/common.py +++ b/cli/launchers/common.py @@ -1,7 +1,5 @@ """Shared process helpers for installed client CLI launchers.""" -from __future__ import annotations - import shutil import subprocess import sys diff --git a/cli/managed/claude.py b/cli/managed/claude.py index 72908af4..ae3b9cb1 100644 --- a/cli/managed/claude.py +++ b/cli/managed/claude.py @@ -1,7 +1,5 @@ """Managed Claude Code task command, environment, and stdout parsing.""" -from __future__ import annotations - import json from collections.abc import Iterable, Mapping from dataclasses import dataclass, field diff --git a/cli/managed/diagnostics.py b/cli/managed/diagnostics.py index 8bfe587d..85c6063c 100644 --- a/cli/managed/diagnostics.py +++ b/cli/managed/diagnostics.py @@ -1,7 +1,5 @@ """Managed Claude Code diagnostic classification.""" -from __future__ import annotations - from dataclasses import dataclass _BENIGN_STDERR_MARKERS = ("claude.ai connectors are disabled",) diff --git a/cli/process_registry.py b/cli/process_registry.py index c88f8a8f..3b289641 100644 --- a/cli/process_registry.py +++ b/cli/process_registry.py @@ -5,8 +5,6 @@ FastAPI lifespan cleanup doesn't run to completion. We only track processes we spawn so we don't accidentally kill unrelated system processes. """ -from __future__ import annotations - import atexit import os import signal diff --git a/config/env_files.py b/config/env_files.py index 98b274d7..c6830219 100644 --- a/config/env_files.py +++ b/config/env_files.py @@ -1,7 +1,5 @@ """Dotenv file discovery and explicit dotenv override helpers.""" -from __future__ import annotations - import os from collections.abc import Mapping from pathlib import Path diff --git a/config/env_template.py b/config/env_template.py index 78040369..3cf31b77 100644 --- a/config/env_template.py +++ b/config/env_template.py @@ -1,7 +1,5 @@ """Canonical env template loading for init and Admin UI defaults.""" -from __future__ import annotations - import importlib.resources from pathlib import Path diff --git a/config/model_refs.py b/config/model_refs.py index 019496a8..57dc2695 100644 --- a/config/model_refs.py +++ b/config/model_refs.py @@ -1,7 +1,5 @@ """Provider-prefixed model reference helpers.""" -from __future__ import annotations - from dataclasses import dataclass from typing import Protocol diff --git a/config/provider_catalog.py b/config/provider_catalog.py index c4eb23f8..e2496861 100644 --- a/config/provider_catalog.py +++ b/config/provider_catalog.py @@ -4,8 +4,6 @@ Adapter factories live in :mod:`providers.runtime.factory`; this module stays fr provider implementation imports (see contract tests). """ -from __future__ import annotations - from dataclasses import dataclass from typing import Literal diff --git a/config/provider_ids.py b/config/provider_ids.py index fd08ab7a..7aab9412 100644 --- a/config/provider_ids.py +++ b/config/provider_ids.py @@ -1,7 +1,5 @@ """Canonical provider id tuple (re-exported from the provider catalog).""" -from __future__ import annotations - from .provider_catalog import SUPPORTED_PROVIDER_IDS __all__ = ("SUPPORTED_PROVIDER_IDS",) diff --git a/core/anthropic/native_messages_request.py b/core/anthropic/native_messages_request.py index 40b54f27..54bac10f 100644 --- a/core/anthropic/native_messages_request.py +++ b/core/anthropic/native_messages_request.py @@ -3,8 +3,6 @@ Provider adapters supply policy via parameters (defaults, OpenRouter post-steps). """ -from __future__ import annotations - from collections.abc import Sequence from typing import Any diff --git a/core/anthropic/native_sse_block_policy.py b/core/anthropic/native_sse_block_policy.py index 90aaec05..d39467a2 100644 --- a/core/anthropic/native_sse_block_policy.py +++ b/core/anthropic/native_sse_block_policy.py @@ -4,8 +4,6 @@ Used by :class:`OpenRouterProvider` and line-mode :class:`providers.transports.anthropic_messages.AnthropicMessagesTransport` providers. """ -from __future__ import annotations - import copy import json from dataclasses import dataclass, field diff --git a/core/anthropic/provider_stream_error.py b/core/anthropic/provider_stream_error.py index 76f2f8b7..951f33c9 100644 --- a/core/anthropic/provider_stream_error.py +++ b/core/anthropic/provider_stream_error.py @@ -1,7 +1,5 @@ """Canonical Anthropic-style SSE sequence for provider-side streaming errors.""" -from __future__ import annotations - import uuid from collections.abc import Iterator from typing import Any diff --git a/core/anthropic/request_serialization.py b/core/anthropic/request_serialization.py index af69670b..0603446a 100644 --- a/core/anthropic/request_serialization.py +++ b/core/anthropic/request_serialization.py @@ -1,7 +1,5 @@ """Shared Anthropic request serialization helpers.""" -from __future__ import annotations - import json from typing import Any diff --git a/core/anthropic/server_tool_sse.py b/core/anthropic/server_tool_sse.py index 1c8727d1..2de1a82d 100644 --- a/core/anthropic/server_tool_sse.py +++ b/core/anthropic/server_tool_sse.py @@ -3,8 +3,6 @@ Shared by :mod:`api.web_tools` and stream contract tests to avoid drift. """ -from __future__ import annotations - from typing import Final SERVER_TOOL_USE: Final = "server_tool_use" diff --git a/core/anthropic/sse_aggregation.py b/core/anthropic/sse_aggregation.py index 6abd485b..49391dd9 100644 --- a/core/anthropic/sse_aggregation.py +++ b/core/anthropic/sse_aggregation.py @@ -7,8 +7,6 @@ back the same shape the real Anthropic API returns for a non-streaming ``messages.create()`` call. """ -from __future__ import annotations - import json import uuid from collections.abc import AsyncIterator diff --git a/core/anthropic/stream_contracts.py b/core/anthropic/stream_contracts.py index ba2b4d81..22572967 100644 --- a/core/anthropic/stream_contracts.py +++ b/core/anthropic/stream_contracts.py @@ -3,8 +3,6 @@ Used by default CI contract tests and by opt-in live smoke scenarios. """ -from __future__ import annotations - import json from collections.abc import Iterable from dataclasses import dataclass diff --git a/core/anthropic/streaming/emitter.py b/core/anthropic/streaming/emitter.py index de97e7ac..b4dabb30 100644 --- a/core/anthropic/streaming/emitter.py +++ b/core/anthropic/streaming/emitter.py @@ -1,7 +1,5 @@ """Anthropic SSE serialization helpers.""" -from __future__ import annotations - import json from typing import Any diff --git a/core/anthropic/streaming/ledger.py b/core/anthropic/streaming/ledger.py index 8415dee8..241e3733 100644 --- a/core/anthropic/streaming/ledger.py +++ b/core/anthropic/streaming/ledger.py @@ -1,7 +1,5 @@ """Anthropic stream state ledger.""" -from __future__ import annotations - import hashlib import json import uuid diff --git a/core/anthropic/streaming/recovery.py b/core/anthropic/streaming/recovery.py index 088b33b1..c26f1a97 100644 --- a/core/anthropic/streaming/recovery.py +++ b/core/anthropic/streaming/recovery.py @@ -1,7 +1,5 @@ """Shared retry and recovery policy for Anthropic streams.""" -from __future__ import annotations - import json import time from collections.abc import Callable diff --git a/core/anthropic/streaming/transient_errors.py b/core/anthropic/streaming/transient_errors.py index 371d37ae..93bae305 100644 --- a/core/anthropic/streaming/transient_errors.py +++ b/core/anthropic/streaming/transient_errors.py @@ -1,7 +1,5 @@ """Provider-neutral transient upstream error classification.""" -from __future__ import annotations - import json from collections.abc import Mapping from contextlib import suppress diff --git a/core/openai_responses/adapter.py b/core/openai_responses/adapter.py index 1b73ea90..f05cdf46 100644 --- a/core/openai_responses/adapter.py +++ b/core/openai_responses/adapter.py @@ -1,7 +1,5 @@ """Facade for OpenAI Responses protocol adaptation.""" -from __future__ import annotations - from collections.abc import AsyncIterable, AsyncIterator, Mapping from typing import Any, ClassVar diff --git a/core/openai_responses/anthropic_sse.py b/core/openai_responses/anthropic_sse.py index 4233980d..5ad95098 100644 --- a/core/openai_responses/anthropic_sse.py +++ b/core/openai_responses/anthropic_sse.py @@ -1,7 +1,5 @@ """Anthropic SSE parsing used by the Responses stream adapter.""" -from __future__ import annotations - import json from collections.abc import AsyncIterable, AsyncIterator from dataclasses import dataclass diff --git a/core/openai_responses/errors.py b/core/openai_responses/errors.py index c39985b6..784a9747 100644 --- a/core/openai_responses/errors.py +++ b/core/openai_responses/errors.py @@ -1,7 +1,5 @@ """Errors and error envelopes for OpenAI Responses compatibility.""" -from __future__ import annotations - from typing import Any diff --git a/core/openai_responses/events.py b/core/openai_responses/events.py index 0cf16c95..0cbec427 100644 --- a/core/openai_responses/events.py +++ b/core/openai_responses/events.py @@ -1,7 +1,5 @@ """OpenAI Responses SSE event formatting.""" -from __future__ import annotations - import json from collections.abc import Mapping from typing import Any diff --git a/core/openai_responses/ids.py b/core/openai_responses/ids.py index a6019974..0ef53f00 100644 --- a/core/openai_responses/ids.py +++ b/core/openai_responses/ids.py @@ -1,7 +1,5 @@ """Identifier helpers for OpenAI Responses payloads.""" -from __future__ import annotations - import uuid diff --git a/core/openai_responses/input.py b/core/openai_responses/input.py index c85d02dd..5fefdd8d 100644 --- a/core/openai_responses/input.py +++ b/core/openai_responses/input.py @@ -1,7 +1,5 @@ """Convert OpenAI Responses requests into Anthropic Messages payloads.""" -from __future__ import annotations - from collections.abc import Mapping from typing import Any diff --git a/core/openai_responses/items.py b/core/openai_responses/items.py index d4e14484..e3f9bd52 100644 --- a/core/openai_responses/items.py +++ b/core/openai_responses/items.py @@ -1,7 +1,5 @@ """Responses object and output item builders.""" -from __future__ import annotations - from typing import Any diff --git a/core/openai_responses/reasoning.py b/core/openai_responses/reasoning.py index 9f4ec45e..d867ddf6 100644 --- a/core/openai_responses/reasoning.py +++ b/core/openai_responses/reasoning.py @@ -1,7 +1,5 @@ """Reasoning and thinking conversion helpers for OpenAI Responses.""" -from __future__ import annotations - from collections.abc import Mapping from typing import Any diff --git a/core/openai_responses/stream.py b/core/openai_responses/stream.py index 8de78c72..97383f70 100644 --- a/core/openai_responses/stream.py +++ b/core/openai_responses/stream.py @@ -1,7 +1,5 @@ """Translate Anthropic SSE streams into OpenAI Responses SSE streams.""" -from __future__ import annotations - from collections.abc import AsyncIterable, AsyncIterator, Mapping from typing import Any diff --git a/core/openai_responses/streaming/assembler.py b/core/openai_responses/streaming/assembler.py index c707b673..8f7308fd 100644 --- a/core/openai_responses/streaming/assembler.py +++ b/core/openai_responses/streaming/assembler.py @@ -1,7 +1,5 @@ """Anthropic SSE to OpenAI Responses stream assembly.""" -from __future__ import annotations - import json import time import uuid diff --git a/core/openai_responses/streaming/blocks.py b/core/openai_responses/streaming/blocks.py index 3226b48e..581f7b3a 100644 --- a/core/openai_responses/streaming/blocks.py +++ b/core/openai_responses/streaming/blocks.py @@ -1,7 +1,5 @@ """Block state for OpenAI Responses streaming assembly.""" -from __future__ import annotations - from dataclasses import dataclass, field from typing import Literal diff --git a/core/openai_responses/streaming/completion.py b/core/openai_responses/streaming/completion.py index 0165d1aa..f118fa9e 100644 --- a/core/openai_responses/streaming/completion.py +++ b/core/openai_responses/streaming/completion.py @@ -1,7 +1,5 @@ """Block finalization for OpenAI Responses streams.""" -from __future__ import annotations - from collections.abc import Callable from ..errors import ResponsesConversionError diff --git a/core/openai_responses/streaming/error_mapping.py b/core/openai_responses/streaming/error_mapping.py index fcf71fdf..ad5878b9 100644 --- a/core/openai_responses/streaming/error_mapping.py +++ b/core/openai_responses/streaming/error_mapping.py @@ -1,7 +1,5 @@ """Responses stream error mapping.""" -from __future__ import annotations - from collections.abc import Mapping from typing import Any diff --git a/core/openai_responses/streaming/event_builders.py b/core/openai_responses/streaming/event_builders.py index 4fcb8233..bad91224 100644 --- a/core/openai_responses/streaming/event_builders.py +++ b/core/openai_responses/streaming/event_builders.py @@ -1,7 +1,5 @@ """OpenAI Responses SSE event builders.""" -from __future__ import annotations - from typing import Any from ..events import format_response_sse_event diff --git a/core/openai_responses/streaming/ledger.py b/core/openai_responses/streaming/ledger.py index c468f9cc..8791bd84 100644 --- a/core/openai_responses/streaming/ledger.py +++ b/core/openai_responses/streaming/ledger.py @@ -1,7 +1,5 @@ """Output ledger for OpenAI Responses streaming assembly.""" -from __future__ import annotations - from collections.abc import Mapping from typing import Any diff --git a/core/openai_responses/tools.py b/core/openai_responses/tools.py index 80f29853..8b898e64 100644 --- a/core/openai_responses/tools.py +++ b/core/openai_responses/tools.py @@ -1,7 +1,5 @@ """Tool conversion helpers for the OpenAI Responses adapter.""" -from __future__ import annotations - import hashlib import json import re diff --git a/core/openai_responses/usage.py b/core/openai_responses/usage.py index 232bcb34..a6a2e28c 100644 --- a/core/openai_responses/usage.py +++ b/core/openai_responses/usage.py @@ -1,7 +1,5 @@ """Usage helpers for OpenAI Responses payloads.""" -from __future__ import annotations - from typing import Protocol _DISALLOWED_SPECIAL: tuple[str, ...] = () diff --git a/core/rate_limit.py b/core/rate_limit.py index 8d9bd81c..b60b491a 100644 --- a/core/rate_limit.py +++ b/core/rate_limit.py @@ -1,7 +1,5 @@ """Shared strict sliding-window rate limiting primitives.""" -from __future__ import annotations - import asyncio import time from collections import deque diff --git a/core/trace.py b/core/trace.py index 77a9458a..471f0392 100644 --- a/core/trace.py +++ b/core/trace.py @@ -5,8 +5,6 @@ Conversation and Claude Code prompts are logged verbatim unless values live unde sanitized credential keys (e.g. ``api_key``, ``authorization``). """ -from __future__ import annotations - import asyncio from collections.abc import AsyncGenerator, AsyncIterator, Mapping from typing import Any diff --git a/messaging/command_context.py b/messaging/command_context.py index d8f74606..26e3be5d 100644 --- a/messaging/command_context.py +++ b/messaging/command_context.py @@ -1,7 +1,5 @@ """Typed dependency surface for messaging slash commands.""" -from __future__ import annotations - from typing import Protocol from .managed_protocols import ManagedClaudeSessionManagerProtocol diff --git a/messaging/command_dispatcher.py b/messaging/command_dispatcher.py index 0944c87f..a84d5224 100644 --- a/messaging/command_dispatcher.py +++ b/messaging/command_dispatcher.py @@ -1,7 +1,5 @@ """Command parsing and dispatch for messaging handlers.""" -from __future__ import annotations - from .command_context import MessagingCommandContext from .commands import handle_clear_command, handle_stats_command, handle_stop_command from .models import IncomingMessage diff --git a/messaging/commands.py b/messaging/commands.py index 26f9c9ec..3991f7dd 100644 --- a/messaging/commands.py +++ b/messaging/commands.py @@ -3,16 +3,10 @@ Commands depend on MessagingCommandContext instead of the concrete workflow. """ -from __future__ import annotations - -from typing import TYPE_CHECKING - from loguru import logger from .command_context import MessagingCommandContext - -if TYPE_CHECKING: - from messaging.models import IncomingMessage +from .models import IncomingMessage async def handle_stop_command( diff --git a/messaging/managed_protocols.py b/messaging/managed_protocols.py index c4fc08fd..e1498801 100644 --- a/messaging/managed_protocols.py +++ b/messaging/managed_protocols.py @@ -1,7 +1,5 @@ """Protocols for messaging-owned managed Claude sessions.""" -from __future__ import annotations - from collections.abc import AsyncGenerator from typing import Any, Protocol, runtime_checkable diff --git a/messaging/node_event_pipeline.py b/messaging/node_event_pipeline.py index 6d0dbdf1..c654d80b 100644 --- a/messaging/node_event_pipeline.py +++ b/messaging/node_event_pipeline.py @@ -1,7 +1,5 @@ """CLI event handling for a single queued node (transcript + session + errors).""" -from __future__ import annotations - from collections.abc import Awaitable, Callable from typing import Any diff --git a/messaging/node_runner.py b/messaging/node_runner.py index 6cf75877..0d7de158 100644 --- a/messaging/node_runner.py +++ b/messaging/node_runner.py @@ -1,7 +1,5 @@ """Run queued messaging nodes through a managed CLI session.""" -from __future__ import annotations - import asyncio from collections.abc import Callable diff --git a/messaging/platforms/discord.py b/messaging/platforms/discord.py index 3b30a9d9..f23dbd86 100644 --- a/messaging/platforms/discord.py +++ b/messaging/platforms/discord.py @@ -1,7 +1,5 @@ """Discord messaging runtime.""" -from __future__ import annotations - import asyncio import contextlib from collections.abc import Awaitable, Callable diff --git a/messaging/platforms/discord_inbound.py b/messaging/platforms/discord_inbound.py index 7d3ba1d6..ba358382 100644 --- a/messaging/platforms/discord_inbound.py +++ b/messaging/platforms/discord_inbound.py @@ -1,7 +1,5 @@ """Discord inbound event normalization.""" -from __future__ import annotations - from typing import Any from loguru import logger diff --git a/messaging/platforms/discord_io.py b/messaging/platforms/discord_io.py index ca5b116b..4aeddb08 100644 --- a/messaging/platforms/discord_io.py +++ b/messaging/platforms/discord_io.py @@ -1,7 +1,5 @@ """Discord outbound delivery.""" -from __future__ import annotations - from collections.abc import Awaitable, Callable from typing import Any, cast diff --git a/messaging/platforms/factory.py b/messaging/platforms/factory.py index 504da806..60b96cd7 100644 --- a/messaging/platforms/factory.py +++ b/messaging/platforms/factory.py @@ -1,7 +1,5 @@ """Messaging platform component factory.""" -from __future__ import annotations - from dataclasses import dataclass from loguru import logger diff --git a/messaging/platforms/outbox.py b/messaging/platforms/outbox.py index ae2c3395..6ed85f01 100644 --- a/messaging/platforms/outbox.py +++ b/messaging/platforms/outbox.py @@ -1,7 +1,5 @@ """Shared queued delivery helper for messaging platforms.""" -from __future__ import annotations - import asyncio from collections.abc import Awaitable, Callable from typing import Any, cast diff --git a/messaging/platforms/ports.py b/messaging/platforms/ports.py index 51134cee..35dcfa50 100644 --- a/messaging/platforms/ports.py +++ b/messaging/platforms/ports.py @@ -1,7 +1,5 @@ """Messaging platform ports used by the customer-facing workflow.""" -from __future__ import annotations - from collections.abc import Awaitable, Callable from dataclasses import dataclass from typing import Any, Protocol, runtime_checkable diff --git a/messaging/platforms/telegram.py b/messaging/platforms/telegram.py index b2112d01..8c6df412 100644 --- a/messaging/platforms/telegram.py +++ b/messaging/platforms/telegram.py @@ -1,12 +1,10 @@ """Telegram messaging runtime.""" -from __future__ import annotations - import asyncio import contextlib import os from collections.abc import Awaitable, Callable -from typing import TYPE_CHECKING, Any +from typing import Any # Opt-in to future behavior for python-telegram-bot (retry_after as timedelta). os.environ["PTB_TIMEDELTA"] = "1" @@ -25,11 +23,8 @@ from .telegram_inbound import ( from .telegram_io import TelegramMessenger from .voice_flow import VoiceNoteFlow -if TYPE_CHECKING: - from telegram import Update - from telegram.ext import ContextTypes - try: + from telegram import Update from telegram.ext import ( Application, CommandHandler, diff --git a/messaging/platforms/telegram_inbound.py b/messaging/platforms/telegram_inbound.py index acbe12c9..3311d2d0 100644 --- a/messaging/platforms/telegram_inbound.py +++ b/messaging/platforms/telegram_inbound.py @@ -1,19 +1,13 @@ """Telegram inbound event normalization.""" -from __future__ import annotations - -from typing import TYPE_CHECKING - from loguru import logger +from telegram import Update +from telegram.ext import ContextTypes from ..models import IncomingMessage from ..rendering.telegram_markdown import format_status from .voice_flow import VoiceNoteRequest, audio_suffix_from_metadata -if TYPE_CHECKING: - from telegram import Update - from telegram.ext import ContextTypes - def telegram_text_message_from_update( update: Update, diff --git a/messaging/platforms/telegram_io.py b/messaging/platforms/telegram_io.py index a30f9b93..ff71937f 100644 --- a/messaging/platforms/telegram_io.py +++ b/messaging/platforms/telegram_io.py @@ -1,7 +1,5 @@ """Telegram outbound delivery.""" -from __future__ import annotations - import asyncio from collections.abc import Awaitable, Callable from datetime import timedelta diff --git a/messaging/platforms/voice_flow.py b/messaging/platforms/voice_flow.py index 51e672e5..c1d63758 100644 --- a/messaging/platforms/voice_flow.py +++ b/messaging/platforms/voice_flow.py @@ -1,7 +1,5 @@ """Shared voice-note flow for messaging platform adapters.""" -from __future__ import annotations - import contextlib import tempfile from collections.abc import Awaitable, Callable diff --git a/messaging/rendering/markdown_tables.py b/messaging/rendering/markdown_tables.py index e2dd8947..f5d7c959 100644 --- a/messaging/rendering/markdown_tables.py +++ b/messaging/rendering/markdown_tables.py @@ -1,7 +1,5 @@ """Shared Markdown table pre-normalization for platform renderers.""" -from __future__ import annotations - import re _TABLE_SEP_RE = re.compile(r"^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$") diff --git a/messaging/rendering/profiles.py b/messaging/rendering/profiles.py index 53b9ef10..591d5456 100644 --- a/messaging/rendering/profiles.py +++ b/messaging/rendering/profiles.py @@ -1,7 +1,5 @@ """Platform rendering profiles for messaging transcripts and status text.""" -from __future__ import annotations - from collections.abc import Callable from dataclasses import dataclass diff --git a/messaging/safe_diagnostics.py b/messaging/safe_diagnostics.py index add30c53..edd3d62f 100644 --- a/messaging/safe_diagnostics.py +++ b/messaging/safe_diagnostics.py @@ -1,7 +1,5 @@ """Helpers for redacting user-derived content from log lines.""" -from __future__ import annotations - def format_exception_for_log(exc: BaseException, *, log_full_message: bool) -> str: """Return exception type and optionally ``str(exc)`` for operator diagnostics.""" diff --git a/messaging/session/message_log.py b/messaging/session/message_log.py index 98d93e1b..eece6851 100644 --- a/messaging/session/message_log.py +++ b/messaging/session/message_log.py @@ -1,7 +1,5 @@ """Per-chat message ID log used by messaging clear commands.""" -from __future__ import annotations - from datetime import UTC, datetime from typing import Any diff --git a/messaging/session/persistence.py b/messaging/session/persistence.py index 142c7db2..5154734f 100644 --- a/messaging/session/persistence.py +++ b/messaging/session/persistence.py @@ -1,7 +1,5 @@ """Atomic JSON persistence for messaging session state.""" -from __future__ import annotations - import contextlib import json import os diff --git a/messaging/session/store.py b/messaging/session/store.py index 55ec158b..b8f0b0af 100644 --- a/messaging/session/store.py +++ b/messaging/session/store.py @@ -1,7 +1,5 @@ """Persistent messaging conversation state store.""" -from __future__ import annotations - import threading from loguru import logger diff --git a/messaging/transcript/buffer.py b/messaging/transcript/buffer.py index 6af43b7f..a99e88ba 100644 --- a/messaging/transcript/buffer.py +++ b/messaging/transcript/buffer.py @@ -1,7 +1,5 @@ """Transcript event application and open-block tracking.""" -from __future__ import annotations - from typing import Any from .context import RenderCtx diff --git a/messaging/transcript/context.py b/messaging/transcript/context.py index 3e1f76f3..a88d3c32 100644 --- a/messaging/transcript/context.py +++ b/messaging/transcript/context.py @@ -1,7 +1,5 @@ """Rendering context used by transcript segments.""" -from __future__ import annotations - from collections.abc import Callable from dataclasses import dataclass diff --git a/messaging/transcript/renderer.py b/messaging/transcript/renderer.py index b4b35f76..7a1eccb8 100644 --- a/messaging/transcript/renderer.py +++ b/messaging/transcript/renderer.py @@ -1,7 +1,5 @@ """Render and truncate ordered transcript segments.""" -from __future__ import annotations - from collections import deque from collections.abc import Iterable diff --git a/messaging/transcript/segments.py b/messaging/transcript/segments.py index cf3d62a7..783ee5ac 100644 --- a/messaging/transcript/segments.py +++ b/messaging/transcript/segments.py @@ -1,7 +1,5 @@ """Transcript segment types for messaging UI output.""" -from __future__ import annotations - import json from abc import ABC, abstractmethod from dataclasses import dataclass, field diff --git a/messaging/transcript/subagents.py b/messaging/transcript/subagents.py index 1ec561bc..6cbb2c0a 100644 --- a/messaging/transcript/subagents.py +++ b/messaging/transcript/subagents.py @@ -1,7 +1,5 @@ """Task/subagent display state for messaging transcripts.""" -from __future__ import annotations - from typing import Any from loguru import logger diff --git a/messaging/trees/graph.py b/messaging/trees/graph.py index 85323c7b..df4cc249 100644 --- a/messaging/trees/graph.py +++ b/messaging/trees/graph.py @@ -1,7 +1,5 @@ """In-memory graph for one messaging conversation tree.""" -from __future__ import annotations - from loguru import logger from ..models import IncomingMessage diff --git a/messaging/trees/node.py b/messaging/trees/node.py index 7c9e7993..ad577075 100644 --- a/messaging/trees/node.py +++ b/messaging/trees/node.py @@ -1,7 +1,5 @@ """Message tree node model.""" -from __future__ import annotations - from dataclasses import dataclass, field from datetime import UTC, datetime from enum import Enum diff --git a/messaging/trees/queue.py b/messaging/trees/queue.py index 8ef3a07e..cefc4580 100644 --- a/messaging/trees/queue.py +++ b/messaging/trees/queue.py @@ -1,7 +1,5 @@ """FIFO queue state for one messaging conversation tree.""" -from __future__ import annotations - import asyncio from collections import deque diff --git a/messaging/trees/runtime.py b/messaging/trees/runtime.py index 48b83bfb..65adac77 100644 --- a/messaging/trees/runtime.py +++ b/messaging/trees/runtime.py @@ -1,7 +1,5 @@ """Runtime state for one messaging conversation tree.""" -from __future__ import annotations - import asyncio from contextlib import asynccontextmanager diff --git a/messaging/trees/snapshot.py b/messaging/trees/snapshot.py index 41a02f91..135dbaf6 100644 --- a/messaging/trees/snapshot.py +++ b/messaging/trees/snapshot.py @@ -1,7 +1,5 @@ """Serializable messaging conversation snapshots.""" -from __future__ import annotations - from dataclasses import dataclass, field from datetime import datetime from typing import Any diff --git a/messaging/turn_intake.py b/messaging/turn_intake.py index 01802dfd..e7366e6a 100644 --- a/messaging/turn_intake.py +++ b/messaging/turn_intake.py @@ -1,7 +1,5 @@ """Inbound messaging turn intake and queue admission.""" -from __future__ import annotations - from collections.abc import Awaitable, Callable from loguru import logger diff --git a/messaging/ui_updates.py b/messaging/ui_updates.py index 581fa98e..55c0ff4d 100644 --- a/messaging/ui_updates.py +++ b/messaging/ui_updates.py @@ -1,7 +1,5 @@ """Throttled platform UI updates driven by transcript rendering.""" -from __future__ import annotations - import time from collections.abc import Callable diff --git a/messaging/voice.py b/messaging/voice.py index 0dd4b128..6d067757 100644 --- a/messaging/voice.py +++ b/messaging/voice.py @@ -1,7 +1,5 @@ """Platform-neutral voice note helpers.""" -from __future__ import annotations - import asyncio from pathlib import Path diff --git a/messaging/workflow.py b/messaging/workflow.py index f94a4c59..d87167c9 100644 --- a/messaging/workflow.py +++ b/messaging/workflow.py @@ -1,7 +1,5 @@ """Messaging workflow coordinator for Discord and Telegram prompts.""" -from __future__ import annotations - from loguru import logger from core.trace import trace_event diff --git a/providers/cerebras/client.py b/providers/cerebras/client.py index 451de003..5dbe1028 100644 --- a/providers/cerebras/client.py +++ b/providers/cerebras/client.py @@ -1,7 +1,5 @@ """Cerebras Inference provider (OpenAI-compatible chat completions).""" -from __future__ import annotations - from typing import Any from providers.base import ProviderConfig diff --git a/providers/cloudflare/client.py b/providers/cloudflare/client.py index 723584c7..1614d1f8 100644 --- a/providers/cloudflare/client.py +++ b/providers/cloudflare/client.py @@ -1,7 +1,5 @@ """Cloudflare Workers AI provider using OpenAI-compatible chat completions.""" -from __future__ import annotations - from collections.abc import Iterator, Mapping from typing import Any from urllib.parse import quote diff --git a/providers/codestral/client.py b/providers/codestral/client.py index ee6f7920..2897fc83 100644 --- a/providers/codestral/client.py +++ b/providers/codestral/client.py @@ -1,7 +1,5 @@ """Mistral Codestral provider (OpenAI-compatible chat on codestral.mistral.ai).""" -from __future__ import annotations - from typing import Any from providers.base import ProviderConfig diff --git a/providers/deepseek/client.py b/providers/deepseek/client.py index b0e43a80..b6fffc15 100644 --- a/providers/deepseek/client.py +++ b/providers/deepseek/client.py @@ -1,7 +1,5 @@ """DeepSeek provider implementation (OpenAI-compatible Chat Completions).""" -from __future__ import annotations - from collections.abc import Mapping from typing import Any diff --git a/providers/deepseek/compat.py b/providers/deepseek/compat.py index 9d02cd28..4e400f26 100644 --- a/providers/deepseek/compat.py +++ b/providers/deepseek/compat.py @@ -1,7 +1,5 @@ """DeepSeek Anthropic-to-OpenAI chat request policy.""" -from __future__ import annotations - from collections.abc import Mapping from types import SimpleNamespace from typing import Any diff --git a/providers/fireworks/client.py b/providers/fireworks/client.py index da6c493c..3790a648 100644 --- a/providers/fireworks/client.py +++ b/providers/fireworks/client.py @@ -1,7 +1,5 @@ """Fireworks AI provider using native Anthropic-compatible Messages.""" -from __future__ import annotations - from typing import Any from providers.base import ProviderConfig diff --git a/providers/gemini/client.py b/providers/gemini/client.py index d06c36d2..d78e77ce 100644 --- a/providers/gemini/client.py +++ b/providers/gemini/client.py @@ -1,7 +1,5 @@ """Google AI Studio Gemini provider (OpenAI-compatible chat completions).""" -from __future__ import annotations - from copy import deepcopy from typing import Any diff --git a/providers/gemini/quirks.py b/providers/gemini/quirks.py index 321eae6f..4aceb55c 100644 --- a/providers/gemini/quirks.py +++ b/providers/gemini/quirks.py @@ -1,7 +1,5 @@ """Gemini request-body quirks for the OpenAI-compatible transport.""" -from __future__ import annotations - from copy import deepcopy from typing import Any, cast diff --git a/providers/groq/client.py b/providers/groq/client.py index 2c791419..cab1f6b6 100644 --- a/providers/groq/client.py +++ b/providers/groq/client.py @@ -1,7 +1,5 @@ """Groq provider implementation (OpenAI-compatible chat completions).""" -from __future__ import annotations - from typing import Any from providers.base import ProviderConfig diff --git a/providers/kimi/client.py b/providers/kimi/client.py index b82543f8..5e1d9b5b 100644 --- a/providers/kimi/client.py +++ b/providers/kimi/client.py @@ -1,7 +1,5 @@ """Kimi (Moonshot) provider using native Anthropic-compatible Messages.""" -from __future__ import annotations - from typing import Any import httpx diff --git a/providers/minimax/client.py b/providers/minimax/client.py index 872f7b44..d53eb2c5 100644 --- a/providers/minimax/client.py +++ b/providers/minimax/client.py @@ -1,7 +1,5 @@ """MiniMax provider implementation (Anthropic-compatible Messages API).""" -from __future__ import annotations - from typing import Any from providers.base import ProviderConfig diff --git a/providers/mistral/client.py b/providers/mistral/client.py index e5ec179f..cb5a75d0 100644 --- a/providers/mistral/client.py +++ b/providers/mistral/client.py @@ -1,7 +1,5 @@ """Mistral La Plateforme provider implementation (OpenAI-compatible chat completions).""" -from __future__ import annotations - from typing import Any from providers.base import ProviderConfig diff --git a/providers/model_listing.py b/providers/model_listing.py index a4435f33..978e44dc 100644 --- a/providers/model_listing.py +++ b/providers/model_listing.py @@ -1,7 +1,5 @@ """Provider model-list response parsing helpers.""" -from __future__ import annotations - from collections.abc import Iterable, Mapping, Sequence from dataclasses import dataclass from typing import Any diff --git a/providers/nvidia_nim/request_options.py b/providers/nvidia_nim/request_options.py index cc802944..61ca3e97 100644 --- a/providers/nvidia_nim/request_options.py +++ b/providers/nvidia_nim/request_options.py @@ -1,7 +1,5 @@ """NVIDIA NIM request option injection.""" -from __future__ import annotations - from typing import Any from config.nim import NimSettings diff --git a/providers/nvidia_nim/retry.py b/providers/nvidia_nim/retry.py index 50f15e46..3b105fd0 100644 --- a/providers/nvidia_nim/retry.py +++ b/providers/nvidia_nim/retry.py @@ -1,7 +1,5 @@ """NVIDIA NIM retry-body downgrade helpers.""" -from __future__ import annotations - from collections.abc import Callable from copy import deepcopy from typing import Any diff --git a/providers/nvidia_nim/tool_schema.py b/providers/nvidia_nim/tool_schema.py index ca4aed86..11186e89 100644 --- a/providers/nvidia_nim/tool_schema.py +++ b/providers/nvidia_nim/tool_schema.py @@ -1,7 +1,5 @@ """NVIDIA NIM tool schema sanitization and private argument aliases.""" -from __future__ import annotations - from typing import Any _SCHEMA_VALUE_KEYS = frozenset( diff --git a/providers/nvidia_nim/voice.py b/providers/nvidia_nim/voice.py index f26255a6..bc46da3c 100644 --- a/providers/nvidia_nim/voice.py +++ b/providers/nvidia_nim/voice.py @@ -1,7 +1,5 @@ """NVIDIA NIM / Riva offline ASR for voice notes (provider-owned transport).""" -from __future__ import annotations - from pathlib import Path from loguru import logger diff --git a/providers/open_router/client.py b/providers/open_router/client.py index 677b32f6..d2d7beb0 100644 --- a/providers/open_router/client.py +++ b/providers/open_router/client.py @@ -1,7 +1,5 @@ """OpenRouter provider implementation.""" -from __future__ import annotations - from collections.abc import Iterator from typing import Any diff --git a/providers/opencode/client.py b/providers/opencode/client.py index 1de48311..56891773 100644 --- a/providers/opencode/client.py +++ b/providers/opencode/client.py @@ -1,7 +1,5 @@ """OpenCode Zen provider implementation (OpenAI-compatible Chat Completions).""" -from __future__ import annotations - from typing import Any from providers.base import ProviderConfig diff --git a/providers/runtime/cache.py b/providers/runtime/cache.py index 45d6a13e..401ff503 100644 --- a/providers/runtime/cache.py +++ b/providers/runtime/cache.py @@ -1,7 +1,5 @@ """Provider instance cache and cleanup.""" -from __future__ import annotations - from collections.abc import Callable, MutableMapping from config.settings import Settings diff --git a/providers/runtime/config.py b/providers/runtime/config.py index 63d4d69f..3959a76d 100644 --- a/providers/runtime/config.py +++ b/providers/runtime/config.py @@ -1,7 +1,5 @@ """Provider configuration construction from neutral catalog metadata.""" -from __future__ import annotations - from config.provider_catalog import ProviderDescriptor from config.settings import Settings from providers.base import ProviderConfig diff --git a/providers/runtime/discovery.py b/providers/runtime/discovery.py index e425d1a8..91d3ee99 100644 --- a/providers/runtime/discovery.py +++ b/providers/runtime/discovery.py @@ -1,7 +1,5 @@ """Provider model-list discovery and background refresh.""" -from __future__ import annotations - import asyncio from collections.abc import Callable from contextlib import suppress diff --git a/providers/runtime/factory.py b/providers/runtime/factory.py index a986b6ee..442e32b1 100644 --- a/providers/runtime/factory.py +++ b/providers/runtime/factory.py @@ -1,7 +1,5 @@ """Provider factory wiring and lazy adapter construction.""" -from __future__ import annotations - from collections.abc import Callable from config.provider_catalog import ( diff --git a/providers/runtime/model_cache.py b/providers/runtime/model_cache.py index 5494cbdb..1db8d2d6 100644 --- a/providers/runtime/model_cache.py +++ b/providers/runtime/model_cache.py @@ -1,7 +1,5 @@ """Provider model-list metadata cache.""" -from __future__ import annotations - from collections.abc import Iterable from config.provider_catalog import SUPPORTED_PROVIDER_IDS diff --git a/providers/runtime/runtime.py b/providers/runtime/runtime.py index 8b60ae60..7bc9d889 100644 --- a/providers/runtime/runtime.py +++ b/providers/runtime/runtime.py @@ -1,7 +1,5 @@ """App-scoped provider runtime orchestration.""" -from __future__ import annotations - from collections.abc import Iterable, MutableMapping from config.settings import Settings diff --git a/providers/runtime/validation.py b/providers/runtime/validation.py index 327b2749..6131138d 100644 --- a/providers/runtime/validation.py +++ b/providers/runtime/validation.py @@ -1,7 +1,5 @@ """Configured provider model validation.""" -from __future__ import annotations - import asyncio from collections import defaultdict from collections.abc import Callable diff --git a/providers/transports/anthropic_messages/http.py b/providers/transports/anthropic_messages/http.py index c4571d38..23dae5cd 100644 --- a/providers/transports/anthropic_messages/http.py +++ b/providers/transports/anthropic_messages/http.py @@ -1,7 +1,5 @@ """HTTP helpers for native Anthropic Messages transports.""" -from __future__ import annotations - from typing import Any import httpx diff --git a/providers/transports/anthropic_messages/recovery.py b/providers/transports/anthropic_messages/recovery.py index 1e2a139a..00c6c8f2 100644 --- a/providers/transports/anthropic_messages/recovery.py +++ b/providers/transports/anthropic_messages/recovery.py @@ -1,7 +1,5 @@ """Native Anthropic Messages recovery event construction.""" -from __future__ import annotations - from collections.abc import AsyncIterator, Callable from typing import Any diff --git a/providers/transports/anthropic_messages/request_policy.py b/providers/transports/anthropic_messages/request_policy.py index 29fd8cd1..f24d044d 100644 --- a/providers/transports/anthropic_messages/request_policy.py +++ b/providers/transports/anthropic_messages/request_policy.py @@ -1,7 +1,5 @@ """Request-body policy for native Anthropic-compatible providers.""" -from __future__ import annotations - from collections.abc import Callable, Iterable from dataclasses import dataclass from typing import Any, Literal diff --git a/providers/transports/anthropic_messages/stream.py b/providers/transports/anthropic_messages/stream.py index 7f14f8d8..9f460870 100644 --- a/providers/transports/anthropic_messages/stream.py +++ b/providers/transports/anthropic_messages/stream.py @@ -1,7 +1,5 @@ """Native Anthropic Messages upstream adapter.""" -from __future__ import annotations - from collections.abc import AsyncIterator from typing import Any diff --git a/providers/transports/anthropic_messages/transport.py b/providers/transports/anthropic_messages/transport.py index 480b93fd..8042bc8c 100644 --- a/providers/transports/anthropic_messages/transport.py +++ b/providers/transports/anthropic_messages/transport.py @@ -1,7 +1,5 @@ """Shared transport for providers with native Anthropic Messages endpoints.""" -from __future__ import annotations - from collections.abc import AsyncIterator, Iterator from typing import Any, Literal diff --git a/providers/transports/http.py b/providers/transports/http.py index 0a59586d..ef785252 100644 --- a/providers/transports/http.py +++ b/providers/transports/http.py @@ -1,7 +1,5 @@ """Shared HTTP helpers for provider transports.""" -from __future__ import annotations - import inspect from typing import Any diff --git a/providers/transports/openai_chat/recovery.py b/providers/transports/openai_chat/recovery.py index 90987277..b9ef091c 100644 --- a/providers/transports/openai_chat/recovery.py +++ b/providers/transports/openai_chat/recovery.py @@ -1,7 +1,5 @@ """OpenAI-chat stream recovery event construction.""" -from __future__ import annotations - from collections.abc import Awaitable, Callable, Iterator from typing import Any diff --git a/providers/transports/openai_chat/request_policy.py b/providers/transports/openai_chat/request_policy.py index 421ed802..4f575c4c 100644 --- a/providers/transports/openai_chat/request_policy.py +++ b/providers/transports/openai_chat/request_policy.py @@ -1,7 +1,5 @@ """Request-body policy for OpenAI-compatible chat providers.""" -from __future__ import annotations - from collections.abc import Callable, Iterable from copy import deepcopy from dataclasses import dataclass, field diff --git a/providers/transports/openai_chat/stream.py b/providers/transports/openai_chat/stream.py index fa7a33a8..376e335d 100644 --- a/providers/transports/openai_chat/stream.py +++ b/providers/transports/openai_chat/stream.py @@ -1,7 +1,5 @@ """OpenAI-chat upstream adapter.""" -from __future__ import annotations - import asyncio import uuid from collections.abc import AsyncIterator, Iterator diff --git a/providers/transports/openai_chat/tool_calls.py b/providers/transports/openai_chat/tool_calls.py index e31aae9c..80276f9d 100644 --- a/providers/transports/openai_chat/tool_calls.py +++ b/providers/transports/openai_chat/tool_calls.py @@ -1,7 +1,5 @@ """OpenAI-chat tool-call assembly helpers.""" -from __future__ import annotations - import json import uuid from collections.abc import Callable, Iterator diff --git a/providers/transports/openai_chat/transport.py b/providers/transports/openai_chat/transport.py index 3b474db4..dfe5002f 100644 --- a/providers/transports/openai_chat/transport.py +++ b/providers/transports/openai_chat/transport.py @@ -1,7 +1,5 @@ """OpenAI-compatible chat transport base.""" -from __future__ import annotations - from abc import abstractmethod from collections.abc import AsyncIterator, Iterator from typing import Any diff --git a/providers/zai/client.py b/providers/zai/client.py index c5c2a97e..fd89414a 100644 --- a/providers/zai/client.py +++ b/providers/zai/client.py @@ -1,7 +1,5 @@ """Z.ai provider implementation (Anthropic-compatible Messages API).""" -from __future__ import annotations - from typing import Any from providers.base import ProviderConfig diff --git a/pyproject.toml b/pyproject.toml index 67ce95db..b4f50969 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "free-claude-code" -version = "2.5.2" +version = "2.5.3" description = "Middleware between Claude Code CLI (Anthropic API) and NVIDIA NIM" readme = "README.md" requires-python = ">=3.14.0" diff --git a/scripts/ci.ps1 b/scripts/ci.ps1 index 5bb58292..f7412b8c 100644 --- a/scripts/ci.ps1 +++ b/scripts/ci.ps1 @@ -27,7 +27,7 @@ Requires uv on PATH when running ruff, ty, or pytest checks. Local ruff checks repair formatting and autofixable lint before later checks. Checks (in order): - suppressions Ban # type: ignore / # ty: ignore suppressions + suppressions Ban type ignores and legacy future annotations ruff-format uv run ruff format ruff-check uv run ruff check --fix ty uv run ty check @@ -125,8 +125,8 @@ function Test-SelectedChecksNeedUv { } function Invoke-SuppressionsCheck { - Write-Step "Ban type ignore suppressions" - $pattern = '# type: ignore|# ty: ignore' + Write-Step "Ban suppressions and legacy annotations" + $pattern = '# type: ignore|# ty: ignore|from __future__ import annotations' Write-Host "+ Get-ChildItem -Recurse -Filter *.py (excluding .venv, .git) | Select-String '$pattern'" if (-not $DryRun) { @@ -140,7 +140,7 @@ function Invoke-SuppressionsCheck { if ($matches) { $matches | ForEach-Object { Write-Host $_.Line } - throw "type: ignore / ty: ignore comments are not allowed. Fix the underlying type errors instead." + throw "type: ignore / ty: ignore comments and legacy future annotations are not allowed. Fix the underlying type/import issue instead." } } } diff --git a/scripts/ci.sh b/scripts/ci.sh index 1c7b9871..81021e01 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -16,7 +16,7 @@ Requires uv on PATH when running ruff, ty, or pytest checks. Local ruff checks repair formatting and autofixable lint before later checks. Checks (in order): - suppressions Ban # type: ignore / # ty: ignore suppressions + suppressions Ban type ignores and legacy future annotations ruff-format uv run ruff format ruff-check uv run ruff check --fix ty uv run ty check @@ -124,13 +124,14 @@ selected_checks_need_uv() { } run_suppressions() { - step "Ban type ignore suppressions" - print_command grep -rE '# type: ignore|# ty: ignore' --include='*.py' . \ + step "Ban suppressions and legacy annotations" + pattern='# type: ignore|# ty: ignore|from __future__ import annotations' + print_command grep -rE "$pattern" --include='*.py' . \ --exclude-dir=.venv --exclude-dir=.git if [ "$dry_run" -eq 0 ]; then - if grep -rE '# type: ignore|# ty: ignore' --include='*.py' . \ + if grep -rE "$pattern" --include='*.py' . \ --exclude-dir=.venv --exclude-dir=.git; then - fail "type: ignore / ty: ignore comments are not allowed. Fix the underlying type errors instead." + fail "type: ignore / ty: ignore comments and legacy future annotations are not allowed. Fix the underlying type/import issue instead." fi fi } diff --git a/smoke/capabilities.py b/smoke/capabilities.py index 36847fd6..9c20ac45 100644 --- a/smoke/capabilities.py +++ b/smoke/capabilities.py @@ -6,8 +6,6 @@ subfeature contracts, owning module boundary, and coverage owners that protect them while the internals are refactored. """ -from __future__ import annotations - from dataclasses import dataclass diff --git a/smoke/conftest.py b/smoke/conftest.py index 264324aa..557decd6 100644 --- a/smoke/conftest.py +++ b/smoke/conftest.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from collections.abc import Iterator from typing import Any diff --git a/smoke/features.py b/smoke/features.py index 7e2f6c82..1da3ef32 100644 --- a/smoke/features.py +++ b/smoke/features.py @@ -6,8 +6,6 @@ product E2E scenario when that behavior is a user-facing product path. Liveness and route probes live in ``smoke/prereq`` and do not count as product coverage. """ -from __future__ import annotations - from dataclasses import dataclass from typing import Literal diff --git a/smoke/lib/child_process.py b/smoke/lib/child_process.py index 9fcd26d3..e43549cb 100644 --- a/smoke/lib/child_process.py +++ b/smoke/lib/child_process.py @@ -6,8 +6,6 @@ already executed under the project environment (``uv run pytest``), so children should use the same interpreter. """ -from __future__ import annotations - import subprocess import sys from collections.abc import Mapping, Sequence diff --git a/smoke/lib/claude_cli_matrix.py b/smoke/lib/claude_cli_matrix.py index 6b58df10..491b0edf 100644 --- a/smoke/lib/claude_cli_matrix.py +++ b/smoke/lib/claude_cli_matrix.py @@ -1,7 +1,5 @@ """Claude Code CLI characterization helpers for provider smoke matrices.""" -from __future__ import annotations - import json import os import re diff --git a/smoke/lib/config.py b/smoke/lib/config.py index 7c7232da..0d55425b 100644 --- a/smoke/lib/config.py +++ b/smoke/lib/config.py @@ -1,7 +1,5 @@ """Smoke-suite configuration loaded from the real developer environment.""" -from __future__ import annotations - import os from collections.abc import Mapping from dataclasses import dataclass diff --git a/smoke/lib/e2e.py b/smoke/lib/e2e.py index b937e359..7fe6de26 100644 --- a/smoke/lib/e2e.py +++ b/smoke/lib/e2e.py @@ -1,7 +1,5 @@ """Reusable product E2E smoke drivers.""" -from __future__ import annotations - import asyncio import json import os diff --git a/smoke/lib/http.py b/smoke/lib/http.py index 71e2cb07..1814f564 100644 --- a/smoke/lib/http.py +++ b/smoke/lib/http.py @@ -1,7 +1,5 @@ """HTTP helpers for live smoke requests.""" -from __future__ import annotations - from typing import Any import httpx diff --git a/smoke/lib/local_providers.py b/smoke/lib/local_providers.py index ba2be5d3..a0136b23 100644 --- a/smoke/lib/local_providers.py +++ b/smoke/lib/local_providers.py @@ -1,7 +1,5 @@ """Helpers for local-provider smoke availability checks.""" -from __future__ import annotations - from urllib.parse import urljoin import httpx diff --git a/smoke/lib/report.py b/smoke/lib/report.py index 8f499c46..25860b7f 100644 --- a/smoke/lib/report.py +++ b/smoke/lib/report.py @@ -1,7 +1,5 @@ """Small JSON report writer for smoke runs.""" -from __future__ import annotations - import json import time from dataclasses import asdict, dataclass diff --git a/smoke/lib/report_summary.py b/smoke/lib/report_summary.py index 3c916446..86eed35a 100644 --- a/smoke/lib/report_summary.py +++ b/smoke/lib/report_summary.py @@ -1,7 +1,5 @@ """Summarize smoke JSON reports for local and workflow triage.""" -from __future__ import annotations - import json from collections import Counter from dataclasses import dataclass diff --git a/smoke/lib/server.py b/smoke/lib/server.py index 11f5ea56..bc07fd0d 100644 --- a/smoke/lib/server.py +++ b/smoke/lib/server.py @@ -1,7 +1,5 @@ """Subprocess lifecycle helpers for local smoke servers.""" -from __future__ import annotations - import os import socket import subprocess diff --git a/smoke/lib/skips.py b/smoke/lib/skips.py index 73d66426..a5ef6365 100644 --- a/smoke/lib/skips.py +++ b/smoke/lib/skips.py @@ -1,7 +1,5 @@ """Skip helpers for expected live-smoke environment gaps.""" -from __future__ import annotations - import httpx import pytest diff --git a/smoke/prereq/test_api_prereq_live.py b/smoke/prereq/test_api_prereq_live.py index bdc5ce16..da42b009 100644 --- a/smoke/prereq/test_api_prereq_live.py +++ b/smoke/prereq/test_api_prereq_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any import httpx diff --git a/smoke/prereq/test_auth_prereq_live.py b/smoke/prereq/test_auth_prereq_live.py index 0062f90f..ba353205 100644 --- a/smoke/prereq/test_auth_prereq_live.py +++ b/smoke/prereq/test_auth_prereq_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from pathlib import Path import httpx diff --git a/smoke/prereq/test_cli_prereq_live.py b/smoke/prereq/test_cli_prereq_live.py index 751c88f9..de20e6b8 100644 --- a/smoke/prereq/test_cli_prereq_live.py +++ b/smoke/prereq/test_cli_prereq_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os import shutil from pathlib import Path diff --git a/smoke/prereq/test_client_shapes_prereq_live.py b/smoke/prereq/test_client_shapes_prereq_live.py index 47a94592..3f173cb0 100644 --- a/smoke/prereq/test_client_shapes_prereq_live.py +++ b/smoke/prereq/test_client_shapes_prereq_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import pytest from smoke.lib.config import SmokeConfig, auth_headers diff --git a/smoke/prereq/test_local_provider_endpoints_prereq_live.py b/smoke/prereq/test_local_provider_endpoints_prereq_live.py index affedf9b..cbffa9c7 100644 --- a/smoke/prereq/test_local_provider_endpoints_prereq_live.py +++ b/smoke/prereq/test_local_provider_endpoints_prereq_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import pytest from smoke.lib.config import SmokeConfig diff --git a/smoke/prereq/test_messaging_prereq_live.py b/smoke/prereq/test_messaging_prereq_live.py index 652cdddb..a53a3a42 100644 --- a/smoke/prereq/test_messaging_prereq_live.py +++ b/smoke/prereq/test_messaging_prereq_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os import time diff --git a/smoke/prereq/test_provider_prereq_live.py b/smoke/prereq/test_provider_prereq_live.py index b08e808d..ddd82a7a 100644 --- a/smoke/prereq/test_provider_prereq_live.py +++ b/smoke/prereq/test_provider_prereq_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import time import httpx diff --git a/smoke/prereq/test_tools_prereq_live.py b/smoke/prereq/test_tools_prereq_live.py index 824e1459..b73251f8 100644 --- a/smoke/prereq/test_tools_prereq_live.py +++ b/smoke/prereq/test_tools_prereq_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import pytest from core.anthropic.stream_contracts import ( diff --git a/smoke/prereq/test_voice_prereq_live.py b/smoke/prereq/test_voice_prereq_live.py index 50ce9e59..868231e9 100644 --- a/smoke/prereq/test_voice_prereq_live.py +++ b/smoke/prereq/test_voice_prereq_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import math import os import wave diff --git a/smoke/product/test_api_product_live.py b/smoke/product/test_api_product_live.py index 0ae2d937..54465cb0 100644 --- a/smoke/product/test_api_product_live.py +++ b/smoke/product/test_api_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any import httpx diff --git a/smoke/product/test_auth_product_live.py b/smoke/product/test_auth_product_live.py index 6132b9ac..6bd259cf 100644 --- a/smoke/product/test_auth_product_live.py +++ b/smoke/product/test_auth_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import httpx import pytest diff --git a/smoke/product/test_cli_package_product_live.py b/smoke/product/test_cli_package_product_live.py index 4ebc996f..c9ff48f3 100644 --- a/smoke/product/test_cli_package_product_live.py +++ b/smoke/product/test_cli_package_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import asyncio import os from pathlib import Path diff --git a/smoke/product/test_client_product_live.py b/smoke/product/test_client_product_live.py index 8a841e0b..2269a79c 100644 --- a/smoke/product/test_client_product_live.py +++ b/smoke/product/test_client_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import shutil from pathlib import Path diff --git a/smoke/product/test_config_extensibility_product_live.py b/smoke/product/test_config_extensibility_product_live.py index 4178eefd..e7a04549 100644 --- a/smoke/product/test_config_extensibility_product_live.py +++ b/smoke/product/test_config_extensibility_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os import pytest diff --git a/smoke/product/test_live_platform_product_live.py b/smoke/product/test_live_platform_product_live.py index bcb242bd..7c727843 100644 --- a/smoke/product/test_live_platform_product_live.py +++ b/smoke/product/test_live_platform_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os import time diff --git a/smoke/product/test_local_provider_product_live.py b/smoke/product/test_local_provider_product_live.py index 5d3797ce..3b5b2147 100644 --- a/smoke/product/test_local_provider_product_live.py +++ b/smoke/product/test_local_provider_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import pytest from smoke.lib.config import SmokeConfig diff --git a/smoke/product/test_messaging_product_live.py b/smoke/product/test_messaging_product_live.py index 79711a65..6ba945ef 100644 --- a/smoke/product/test_messaging_product_live.py +++ b/smoke/product/test_messaging_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import json import pytest diff --git a/smoke/product/test_nvidia_nim_cli_product_live.py b/smoke/product/test_nvidia_nim_cli_product_live.py index 674db1be..910c3799 100644 --- a/smoke/product/test_nvidia_nim_cli_product_live.py +++ b/smoke/product/test_nvidia_nim_cli_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import shutil from pathlib import Path diff --git a/smoke/product/test_openrouter_free_cli_product_live.py b/smoke/product/test_openrouter_free_cli_product_live.py index ac36b8d0..994803b1 100644 --- a/smoke/product/test_openrouter_free_cli_product_live.py +++ b/smoke/product/test_openrouter_free_cli_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import shutil from pathlib import Path diff --git a/smoke/product/test_provider_product_live.py b/smoke/product/test_provider_product_live.py index 2d2f6e12..e8bd9668 100644 --- a/smoke/product/test_provider_product_live.py +++ b/smoke/product/test_provider_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any import httpx diff --git a/smoke/product/test_voice_product_live.py b/smoke/product/test_voice_product_live.py index 0327168d..176c79c2 100644 --- a/smoke/product/test_voice_product_live.py +++ b/smoke/product/test_voice_product_live.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os from pathlib import Path diff --git a/tests/api/test_admin.py b/tests/api/test_admin.py index bb98f585..795719ab 100644 --- a/tests/api/test_admin.py +++ b/tests/api/test_admin.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from pathlib import Path from unittest.mock import patch diff --git a/tests/api/test_anthropic_request_passthrough.py b/tests/api/test_anthropic_request_passthrough.py index b9533d38..5c47072a 100644 --- a/tests/api/test_anthropic_request_passthrough.py +++ b/tests/api/test_anthropic_request_passthrough.py @@ -1,7 +1,5 @@ """Pydantic passthrough of Anthropic protocol fields (e.g. ``cache_control``).""" -from __future__ import annotations - from api.models.anthropic import ( ContentBlockServerToolUse, ContentBlockText, diff --git a/tests/api/test_api_handlers.py b/tests/api/test_api_handlers.py index 71cfee93..16090bfc 100644 --- a/tests/api/test_api_handlers.py +++ b/tests/api/test_api_handlers.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import json from collections.abc import AsyncIterator from typing import Any diff --git a/tests/api/test_openai_responses.py b/tests/api/test_openai_responses.py index 909a11ca..9d210c9f 100644 --- a/tests/api/test_openai_responses.py +++ b/tests/api/test_openai_responses.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any from unittest.mock import MagicMock, patch diff --git a/tests/api/test_safe_logging.py b/tests/api/test_safe_logging.py index 94c59448..4903c1bd 100644 --- a/tests/api/test_safe_logging.py +++ b/tests/api/test_safe_logging.py @@ -1,7 +1,5 @@ """Tests that API and SSE logging avoid raw sensitive payloads by default.""" -from __future__ import annotations - from unittest.mock import MagicMock, patch import pytest diff --git a/tests/cli/test_cli_ownership.py b/tests/cli/test_cli_ownership.py index ba6ef96b..b38f1189 100644 --- a/tests/cli/test_cli_ownership.py +++ b/tests/cli/test_cli_ownership.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from pathlib import Path from cli.managed.session import ManagedClaudeSession diff --git a/tests/cli/test_codex_model_catalog.py b/tests/cli/test_codex_model_catalog.py index fec6b6a3..a0a5c54b 100644 --- a/tests/cli/test_codex_model_catalog.py +++ b/tests/cli/test_codex_model_catalog.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import json import shutil import subprocess diff --git a/tests/cli/test_managed_claude.py b/tests/cli/test_managed_claude.py index c43d2b60..4fccc4f7 100644 --- a/tests/cli/test_managed_claude.py +++ b/tests/cli/test_managed_claude.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import os from cli.managed.claude import ( diff --git a/tests/contracts/test_admin_provider_manifest.py b/tests/contracts/test_admin_provider_manifest.py index 74e4f450..acbc80e5 100644 --- a/tests/contracts/test_admin_provider_manifest.py +++ b/tests/contracts/test_admin_provider_manifest.py @@ -1,7 +1,5 @@ """Ensure admin UI manifest exposes every catalog credential/proxy binding.""" -from __future__ import annotations - from api.admin_config.manifest import FIELD_BY_KEY from config.provider_catalog import PROVIDER_CATALOG from config.settings import Settings diff --git a/tests/contracts/test_architecture_contracts.py b/tests/contracts/test_architecture_contracts.py index 81a08ebb..5b4e3604 100644 --- a/tests/contracts/test_architecture_contracts.py +++ b/tests/contracts/test_architecture_contracts.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import re import tomllib from pathlib import Path diff --git a/tests/contracts/test_capability_map.py b/tests/contracts/test_capability_map.py index ccf3e8f2..33a4f1b6 100644 --- a/tests/contracts/test_capability_map.py +++ b/tests/contracts/test_capability_map.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from smoke.capabilities import ( CAPABILITY_CONTRACTS, capability_names, diff --git a/tests/contracts/test_feature_manifest.py b/tests/contracts/test_feature_manifest.py index 86970ae4..fb0fc897 100644 --- a/tests/contracts/test_feature_manifest.py +++ b/tests/contracts/test_feature_manifest.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import re from pathlib import Path diff --git a/tests/contracts/test_import_boundaries.py b/tests/contracts/test_import_boundaries.py index 8bbc95af..bcfedaa2 100644 --- a/tests/contracts/test_import_boundaries.py +++ b/tests/contracts/test_import_boundaries.py @@ -1,7 +1,5 @@ """Package import contract tests (static AST; dynamic ``importlib`` loads are not scanned).""" -from __future__ import annotations - import ast from pathlib import Path @@ -16,6 +14,24 @@ _API_ALLOWED_PROVIDER_MODULES = frozenset( ) +def test_python314_native_annotations_do_not_use_legacy_future_import() -> None: + repo_root = Path(__file__).resolve().parents[2] + offenders: list[str] = [] + for path in repo_root.rglob("*.py"): + if ".git" in path.parts or ".venv" in path.parts: + continue + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + for node in ast.walk(tree): + if not isinstance(node, ast.ImportFrom): + continue + if node.module != "__future__": + continue + if any(alias.name == "annotations" for alias in node.names): + offenders.append(path.relative_to(repo_root).as_posix()) + + assert sorted(offenders) == [] + + def test_api_and_messaging_do_not_import_provider_common() -> None: repo_root = Path(__file__).resolve().parents[2] assert not (repo_root / "providers" / "common").exists() diff --git a/tests/contracts/test_local_provider_smoke.py b/tests/contracts/test_local_provider_smoke.py index a5ea7b32..78c67b75 100644 --- a/tests/contracts/test_local_provider_smoke.py +++ b/tests/contracts/test_local_provider_smoke.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from dataclasses import dataclass import httpx diff --git a/tests/contracts/test_nvidia_nim_cli_matrix.py b/tests/contracts/test_nvidia_nim_cli_matrix.py index dcf052ed..6b0fbc81 100644 --- a/tests/contracts/test_nvidia_nim_cli_matrix.py +++ b/tests/contracts/test_nvidia_nim_cli_matrix.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import json import subprocess from pathlib import Path diff --git a/tests/contracts/test_provider_catalog_order.py b/tests/contracts/test_provider_catalog_order.py index d21bbcfb..a8b6fca7 100644 --- a/tests/contracts/test_provider_catalog_order.py +++ b/tests/contracts/test_provider_catalog_order.py @@ -1,7 +1,5 @@ """Freeze ``PROVIDER_CATALOG`` insertion order used as canonical provider ranking.""" -from __future__ import annotations - from config.provider_catalog import PROVIDER_CATALOG, SUPPORTED_PROVIDER_IDS _EXPECTED_PROVIDER_ORDER: tuple[str, ...] = ( diff --git a/tests/contracts/test_smoke_child_process.py b/tests/contracts/test_smoke_child_process.py index 26a4a06e..4db6c9cc 100644 --- a/tests/contracts/test_smoke_child_process.py +++ b/tests/contracts/test_smoke_child_process.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import subprocess from pathlib import Path diff --git a/tests/contracts/test_smoke_config.py b/tests/contracts/test_smoke_config.py index 46d20ac2..035a30fb 100644 --- a/tests/contracts/test_smoke_config.py +++ b/tests/contracts/test_smoke_config.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from pathlib import Path from types import SimpleNamespace diff --git a/tests/contracts/test_smoke_tiers.py b/tests/contracts/test_smoke_tiers.py index 85a4855b..2b56c7b5 100644 --- a/tests/contracts/test_smoke_tiers.py +++ b/tests/contracts/test_smoke_tiers.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import json from pathlib import Path diff --git a/tests/contracts/test_stream_contracts.py b/tests/contracts/test_stream_contracts.py index 009a3219..2750dded 100644 --- a/tests/contracts/test_stream_contracts.py +++ b/tests/contracts/test_stream_contracts.py @@ -3,8 +3,6 @@ integration tests. """ -from __future__ import annotations - from collections.abc import Iterable from core.anthropic import ( diff --git a/tests/core/anthropic/test_native_sse_block_policy.py b/tests/core/anthropic/test_native_sse_block_policy.py index f1b66588..cea7b24a 100644 --- a/tests/core/anthropic/test_native_sse_block_policy.py +++ b/tests/core/anthropic/test_native_sse_block_policy.py @@ -1,7 +1,5 @@ """Unit tests for shared native Anthropic SSE thinking policy / block remapping.""" -from __future__ import annotations - import json from core.anthropic.native_sse_block_policy import ( diff --git a/tests/core/openai_responses/test_conversion.py b/tests/core/openai_responses/test_conversion.py index e17ca163..00d8e8bf 100644 --- a/tests/core/openai_responses/test_conversion.py +++ b/tests/core/openai_responses/test_conversion.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import pytest from core.openai_responses import OpenAIResponsesAdapter diff --git a/tests/core/openai_responses/test_sse.py b/tests/core/openai_responses/test_sse.py index 9f887f49..584a2777 100644 --- a/tests/core/openai_responses/test_sse.py +++ b/tests/core/openai_responses/test_sse.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from collections.abc import AsyncIterator from typing import Any diff --git a/tests/core/test_trace.py b/tests/core/test_trace.py index 1268922b..8d1b84ca 100644 --- a/tests/core/test_trace.py +++ b/tests/core/test_trace.py @@ -1,7 +1,5 @@ """Structured TRACE logging assertions.""" -from __future__ import annotations - import json from pathlib import Path diff --git a/tests/messaging/test_stream_transcript_contract.py b/tests/messaging/test_stream_transcript_contract.py index db214e03..2bea3b3b 100644 --- a/tests/messaging/test_stream_transcript_contract.py +++ b/tests/messaging/test_stream_transcript_contract.py @@ -1,7 +1,5 @@ """Messaging-specific assertions built on neutral Anthropic stream contracts.""" -from __future__ import annotations - from core.anthropic import AnthropicStreamLedger from core.anthropic.stream_contracts import ( assert_anthropic_stream_contract, diff --git a/tests/messaging/test_transcription_nim.py b/tests/messaging/test_transcription_nim.py index b15f89b7..a2e851b8 100644 --- a/tests/messaging/test_transcription_nim.py +++ b/tests/messaging/test_transcription_nim.py @@ -1,7 +1,5 @@ """Tests for NVIDIA NIM voice transcription wiring.""" -from __future__ import annotations - from pathlib import Path from unittest.mock import patch diff --git a/tests/providers/test_model_validation.py b/tests/providers/test_model_validation.py index c4d51a3a..0e7136f1 100644 --- a/tests/providers/test_model_validation.py +++ b/tests/providers/test_model_validation.py @@ -1,5 +1,3 @@ -from __future__ import annotations - import asyncio from collections.abc import AsyncIterator from types import SimpleNamespace diff --git a/tests/scripts/test_ci_scripts.py b/tests/scripts/test_ci_scripts.py index 2a680e03..7f1f0d7b 100644 --- a/tests/scripts/test_ci_scripts.py +++ b/tests/scripts/test_ci_scripts.py @@ -59,10 +59,13 @@ def _powershell_interpreter() -> str: def test_ci_sh_runs_ci_checks_in_order() -> None: text = _script_text("ci.sh") + legacy_future_import = "from __future__ import " + "annotations" assert 'CHECK_ORDER="suppressions ruff-format ruff-check ty pytest"' in text assert "grep -rE" in text - assert "Fix the underlying type errors instead" in text + assert "Fix the underlying type/import issue instead" in text + assert legacy_future_import in text + assert "legacy future annotations are not allowed" in text assert "--exclude-dir=.venv" in text assert "--exclude-dir=.git" in text assert "uv run ruff format" in text @@ -146,7 +149,7 @@ def test_ci_sh_suppression_only_does_not_require_uv() -> None: ) assert result.returncode == 0 - assert "Ban type ignore suppressions" in result.stdout + assert "Ban suppressions and legacy annotations" in result.stdout assert "uv is required" not in result.stderr @@ -180,6 +183,7 @@ def test_ci_sh_fail_fast_runs_checks_sequentially() -> None: def test_ci_ps1_runs_ci_checks_in_order() -> None: text = _script_text("ci.ps1") + legacy_future_import = "from __future__ import " + "annotations" assert '"suppressions"' in text assert '"ruff-format"' in text @@ -187,7 +191,9 @@ def test_ci_ps1_runs_ci_checks_in_order() -> None: assert '"ty"' in text assert '"pytest"' in text assert "Select-String -Pattern" in text - assert "Fix the underlying type errors instead" in text + assert "Fix the underlying type/import issue instead" in text + assert legacy_future_import in text + assert "legacy future annotations are not allowed" in text assert ".venv" in text assert ".git" in text assert '"run", "ruff", "format"' in text @@ -276,7 +282,7 @@ def test_ci_ps1_suppression_only_does_not_require_uv() -> None: ) assert result.returncode == 0 - assert "Ban type ignore suppressions" in result.stdout + assert "Ban suppressions and legacy annotations" in result.stdout assert "uv is required" not in result.stderr diff --git a/uv.lock b/uv.lock index beda0f55..30abee02 100644 --- a/uv.lock +++ b/uv.lock @@ -561,7 +561,7 @@ wheels = [ [[package]] name = "free-claude-code" -version = "2.5.2" +version = "2.5.3" source = { editable = "." } dependencies = [ { name = "aiohttp" },