prodex 0.2.115

OpenAI profile pooling and safe auto-rotate for Codex CLI and Claude Code
name: CI

on:
  push:
    branches:
      - main
  pull_request:

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 30

    steps:
      - name: Check out repository
        uses: actions/checkout@v6

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt

      - name: Cache Rust dependencies
        uses: Swatinem/rust-cache@v2

      - name: Check formatting
        run: cargo fmt --check

      - name: Run auto-rotate integration tests
        run: cargo test -q --test auto_rotate

      - name: Run parallel-safe main integration tests
        run: |
          set -euo pipefail
          # Keep env-mutating tests out of the shared process so the rest of the
          # suite can use libtest's default parallelism.
          cargo test --test main_internal -- \
            --skip prepare_managed_codex_home_does_not_reseed_populated_legacy_sessions_every_run \
            --skip runtime_fault_injection_consumes_budget \
            --skip remove_profile_deletes_managed_home_by_default \
            --skip app_paths_discover_uses_prodex_root_for_default_shared_codex_home \
            --skip app_paths_discover_resolves_relative_shared_codex_home_inside_prodex_root \
            --skip runtime_proxy_claude_launch_env_uses_foundry_compat_with_profile_config_dir \
            --skip runtime_proxy_claude_launch_env_honors_model_override \
            --skip runtime_proxy_claude_launch_env_keeps_custom_picker_entry_for_unknown_override \
            --skip runtime_proxy_claude_launch_env_uses_codex_config_model_by_default \
            --skip runtime_proxy_claude_launch_env_maps_alias_backed_override_to_builtin_picker_value \
            --skip runtime_proxy_claude_target_model_maps_builtin_aliases_to_pinned_gpt_models \
            --skip runtime_proxy_claude_reasoning_effort_override_normalizes_env \
            --skip runtime_proxy_claude_reasoning_effort_override_ignores_invalid_env \
            --skip translate_runtime_anthropic_messages_request_honors_reasoning_override_env \
            --skip runtime_doctor_state_collects_persisted_degradation_and_orphans \
            --skip runtime_state_snapshot_save_returns_error_on_injected_failure \
            --skip runtime_proxy_sheds_long_lived_queue_overload_fast \
            --skip runtime_proxy_absorbs_brief_long_lived_queue_burst \
            --skip runtime_proxy_does_not_rotate_after_first_sse_chunk_reset \
            --skip runtime_proxy_does_not_rotate_after_multi_chunk_sse_stall \
            --skip runtime_proxy_retries_after_websocket_reuse_silent_hang \
            --skip runtime_proxy_retries_same_compact_owner_after_websocket_reuse_watchdog \
            --skip runtime_proxy_bound_previous_response_without_turn_state_fails_as_transport_after_websocket_reuse_watchdog \
            --skip runtime_proxy_stale_websocket_previous_response_reuse_fails_as_transport \
            --skip runtime_proxy_logs_local_writer_disconnect_after_first_chunk \
            --skip prodex_update_command_prefers_cargo_for_native_installations \
            --skip prodex_update_command_prefers_npm_for_npm_installations \
            --skip current_prodex_release_source_is_npm_when_wrapped_by_npm \
            --skip current_prodex_release_source_defaults_to_crates_io \
            --skip format_info_prodex_version_reports_up_to_date_from_cache \
            --skip format_info_prodex_version_reports_available_update_from_cache \
            --skip wait_for_existing_runtime_broker_recovery_or_exit_yields_after_live_unhealthy_registry_clears \
            --skip runtime_broker_startup_grace_covers_ready_timeout

      - name: Run serialized env-sensitive main integration tests
        run: |
          set -euo pipefail
          for test_name in \
            prepare_managed_codex_home_does_not_reseed_populated_legacy_sessions_every_run \
            runtime_fault_injection_consumes_budget \
            remove_profile_deletes_managed_home_by_default \
            app_paths_discover_uses_prodex_root_for_default_shared_codex_home \
            app_paths_discover_resolves_relative_shared_codex_home_inside_prodex_root \
            runtime_proxy_claude_launch_env_uses_foundry_compat_with_profile_config_dir \
            runtime_proxy_claude_launch_env_honors_model_override \
            runtime_proxy_claude_launch_env_keeps_custom_picker_entry_for_unknown_override \
            runtime_proxy_claude_launch_env_uses_codex_config_model_by_default \
            runtime_proxy_claude_launch_env_maps_alias_backed_override_to_builtin_picker_value \
            runtime_proxy_claude_target_model_maps_builtin_aliases_to_pinned_gpt_models \
            runtime_proxy_claude_reasoning_effort_override_normalizes_env \
            runtime_proxy_claude_reasoning_effort_override_ignores_invalid_env \
            translate_runtime_anthropic_messages_request_honors_reasoning_override_env \
            runtime_doctor_state_collects_persisted_degradation_and_orphans \
            runtime_state_snapshot_save_returns_error_on_injected_failure \
            runtime_proxy_sheds_long_lived_queue_overload_fast \
            runtime_proxy_absorbs_brief_long_lived_queue_burst \
            runtime_proxy_does_not_rotate_after_first_sse_chunk_reset \
            runtime_proxy_does_not_rotate_after_multi_chunk_sse_stall \
            runtime_proxy_retries_after_websocket_reuse_silent_hang \
            runtime_proxy_retries_same_compact_owner_after_websocket_reuse_watchdog \
            runtime_proxy_bound_previous_response_without_turn_state_fails_as_transport_after_websocket_reuse_watchdog \
            runtime_proxy_stale_websocket_previous_response_reuse_fails_as_transport \
            runtime_proxy_logs_local_writer_disconnect_after_first_chunk \
            prodex_update_command_prefers_cargo_for_native_installations \
            prodex_update_command_prefers_npm_for_npm_installations \
            current_prodex_release_source_is_npm_when_wrapped_by_npm \
            current_prodex_release_source_defaults_to_crates_io \
            format_info_prodex_version_reports_up_to_date_from_cache \
            format_info_prodex_version_reports_available_update_from_cache \
            wait_for_existing_runtime_broker_recovery_or_exit_yields_after_live_unhealthy_registry_clears \
            runtime_broker_startup_grace_covers_ready_timeout
          do
            cargo test -q --test main_internal "${test_name}" -- --test-threads=1
          done

  runtime-stress:
    runs-on: ubuntu-latest
    timeout-minutes: 30

    steps:
      - name: Check out repository
        uses: actions/checkout@v6

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt

      - name: Cache Rust dependencies
        uses: Swatinem/rust-cache@v2

      - name: Run parallel-safe runtime proxy stress tests
        run: |
          set -euo pipefail
          # The main test job already covers runtime_proxy_ once. Keep one extra focused
          # stress pass here, but leave the slow continuation-heavy cases to the dedicated
          # rerun step below so they are not paid twice in this loop as well. Keep the
          # process-global and race-prone cases out of the shared process so the rest of
          # runtime_proxy_ can use libtest's default parallelism.
          for iteration in 1; do
            echo "runtime_proxy_ stress iteration ${iteration}"
            cargo test --test main_internal runtime_proxy_ -- \
              --skip runtime_proxy_persists_previous_response_affinity_across_restart \
              --skip runtime_proxy_persists_session_affinity_across_restart_for_compact \
              --skip runtime_proxy_claude_launch_env_uses_foundry_compat_with_profile_config_dir \
              --skip runtime_proxy_claude_launch_env_honors_model_override \
              --skip runtime_proxy_claude_launch_env_keeps_custom_picker_entry_for_unknown_override \
              --skip runtime_proxy_claude_launch_env_uses_codex_config_model_by_default \
              --skip runtime_proxy_claude_launch_env_maps_alias_backed_override_to_builtin_picker_value \
              --skip runtime_proxy_claude_target_model_maps_builtin_aliases_to_pinned_gpt_models \
              --skip runtime_proxy_claude_reasoning_effort_override_normalizes_env \
              --skip runtime_proxy_claude_reasoning_effort_override_ignores_invalid_env \
              --skip runtime_proxy_retries_after_websocket_reuse_silent_hang \
              --skip runtime_proxy_does_not_rotate_after_multi_chunk_sse_stall \
              --skip runtime_proxy_sheds_long_lived_queue_overload_fast \
              --skip runtime_proxy_absorbs_brief_long_lived_queue_burst \
              --skip runtime_proxy_does_not_rotate_after_first_sse_chunk_reset \
              --skip runtime_proxy_retries_same_compact_owner_after_websocket_reuse_watchdog \
              --skip runtime_proxy_bound_previous_response_without_turn_state_fails_as_transport_after_websocket_reuse_watchdog \
              --skip runtime_proxy_stale_websocket_previous_response_reuse_fails_as_transport \
              --skip runtime_proxy_logs_local_writer_disconnect_after_first_chunk \
              --skip runtime_proxy_broker_health_endpoint_reports_registered_metadata \
              --skip runtime_proxy_uses_current_profile_without_extra_runtime_quota_probe \
              --skip runtime_proxy_reuses_rotated_profile_without_reprobing_quota \
              --skip runtime_proxy_keeps_previous_response_affinity_for_http_requests \
              --skip runtime_proxy_keeps_previous_response_affinity_for_websocket_requests
          done

      - name: Run serialized runtime-sensitive stress tests
        run: |
          set -euo pipefail
          for test_name in \
            runtime_proxy_claude_launch_env_uses_foundry_compat_with_profile_config_dir \
            runtime_proxy_claude_launch_env_honors_model_override \
            runtime_proxy_claude_launch_env_keeps_custom_picker_entry_for_unknown_override \
            runtime_proxy_claude_launch_env_uses_codex_config_model_by_default \
            runtime_proxy_claude_launch_env_maps_alias_backed_override_to_builtin_picker_value \
            runtime_proxy_claude_target_model_maps_builtin_aliases_to_pinned_gpt_models \
            runtime_proxy_claude_reasoning_effort_override_normalizes_env \
            runtime_proxy_claude_reasoning_effort_override_ignores_invalid_env \
            runtime_proxy_sheds_long_lived_queue_overload_fast \
            runtime_proxy_absorbs_brief_long_lived_queue_burst \
            runtime_proxy_does_not_rotate_after_first_sse_chunk_reset \
            runtime_proxy_retries_same_compact_owner_after_websocket_reuse_watchdog \
            runtime_proxy_bound_previous_response_without_turn_state_fails_as_transport_after_websocket_reuse_watchdog \
            runtime_proxy_stale_websocket_previous_response_reuse_fails_as_transport \
            runtime_proxy_logs_local_writer_disconnect_after_first_chunk \
            runtime_proxy_broker_health_endpoint_reports_registered_metadata \
            runtime_proxy_uses_current_profile_without_extra_runtime_quota_probe \
            runtime_proxy_reuses_rotated_profile_without_reprobing_quota \
            runtime_proxy_keeps_previous_response_affinity_for_http_requests \
            runtime_proxy_keeps_previous_response_affinity_for_websocket_requests
          do
            cargo test -q --test main_internal "${test_name}" -- --test-threads=1
          done

      - name: Rerun continuation-heavy tests
        run: |
          set -euo pipefail
          for iteration in 1 2; do
            echo "continuation-heavy iteration ${iteration}"
            cargo test --test main_internal runtime_proxy_persists_previous_response_affinity_across_restart -- --test-threads=1
            cargo test --test main_internal runtime_proxy_persists_session_affinity_across_restart_for_compact -- --test-threads=1
            cargo test --test main_internal runtime_proxy_retries_after_websocket_reuse_silent_hang -- --test-threads=1
            cargo test --test main_internal runtime_proxy_does_not_rotate_after_multi_chunk_sse_stall -- --test-threads=1
          done