fix: harden managed runtime events and voice transcripts #783
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main, rust] | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| quality: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Set up Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: "3.14" | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| - name: Install quality dependencies | |
| run: | | |
| python -m pip install -U pip | |
| python -m pip install pytest pyyaml ruff | |
| - name: Run Ruff error checks | |
| run: python -m ruff check scripts tests | |
| - name: Test quality tooling and workflow contracts | |
| run: python -m pytest -q | |
| web: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Set up Node | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: "24.19.0" | |
| cache: npm | |
| cache-dependency-path: web/package-lock.json | |
| - name: Install Web dependencies | |
| run: npm ci --prefix web | |
| - name: Check Web | |
| run: npm -C web run check | |
| - name: Test Web | |
| run: npm -C web test | |
| - name: Build Web bundle | |
| run: npm -C web run build | |
| - name: Upload Web bundle | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: bundled-web | |
| path: web/dist | |
| if-no-files-found: error | |
| package: | |
| needs: quality | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Set up Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: "3.14" | |
| cache: pip | |
| cache-dependency-path: pyproject.toml | |
| - name: Verify Rust-only release packaging tools | |
| run: | | |
| python -m pip install -U pip twine pytest pyyaml | |
| python -m pytest -q \ | |
| tests/test_build_native_wheel.py \ | |
| tests/test_build_standalone_archive.py \ | |
| tests/test_verify_release_set.py \ | |
| tests/test_smoke_wheel_frontdoor.py | |
| mkdir -p native-wheel-smoke | |
| python scripts/build_native_wheel.py \ | |
| /bin/true \ | |
| --platform-tag manylinux_2_28_x86_64 \ | |
| --output-dir native-wheel-smoke | |
| native_wheel="$(find native-wheel-smoke -maxdepth 1 -name '*.whl' -print -quit)" | |
| python scripts/verify_native_wheel.py \ | |
| "$native_wheel" \ | |
| --platform-tag manylinux_2_28_x86_64 \ | |
| --binary /bin/true | |
| python -m twine check "$native_wheel" | |
| python - <<'PY' | |
| from pathlib import Path | |
| import zipfile | |
| wheel = next(Path("native-wheel-smoke").glob("*.whl")) | |
| assert "-py3-none-manylinux_2_28_x86_64.whl" in wheel.name, wheel | |
| with zipfile.ZipFile(wheel) as archive: | |
| names = set(archive.namelist()) | |
| assert not any(name.endswith((".py", ".pyc")) for name in names) | |
| assert not any(name.startswith("cccc/") for name in names) | |
| assert any(name.endswith(".data/scripts/cccc") for name in names) | |
| print("OK: CI exercises only the Rust-only release wheel shape") | |
| PY | |
| windows-smoke: | |
| needs: web | |
| runs-on: windows-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Download Web bundle | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: bundled-web | |
| path: web/dist | |
| - name: Set up Rust | |
| uses: dtolnay/rust-toolchain@1.88.0 | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Verify Windows PTY UTF-8 message delivery | |
| timeout-minutes: 10 | |
| run: >- | |
| cargo test --package cccc-pair-runtime --lib --locked | |
| manager_windows_tests::npm_style_batch_actor_survives_utf8_message_delivery | |
| -- --test-threads=1 | |
| - name: Verify Windows suspended process launch | |
| timeout-minutes: 2 | |
| run: >- | |
| cargo test --package cccc-windows-process --lib --locked | |
| -- --test-threads=1 | |
| - name: Verify Windows owned Job cleanup | |
| timeout-minutes: 2 | |
| run: >- | |
| cargo test --package cccc-pair-runtime --lib --locked | |
| process_tree::windows_tests:: -- --test-threads=1 | |
| - name: Verify Windows console UTF-8 encoding lifecycle | |
| timeout-minutes: 10 | |
| run: >- | |
| cargo test --package cccc --bin cccc --locked | |
| console_encoding::tests::console_uses_utf8_for_cli_lifetime_and_restores_both_original_pages | |
| -- --test-threads=1 | |
| - name: Verify combined Web startup failure releases its daemon | |
| timeout-minutes: 10 | |
| run: >- | |
| cargo test --package cccc --test integration --locked | |
| daemon_self_launch::combined_web_bind_failure_stops_its_owned_daemon | |
| -- --test-threads=1 | |
| - name: Verify abrupt daemon process-tree cleanup | |
| timeout-minutes: 10 | |
| run: >- | |
| cargo test --package cccc-pair-daemon --lib --locked | |
| process_tree::tests::abrupt_daemon_exit_reaps_child_and_grandchild_without_deleting_history | |
| -- --test-threads=1 | |
| - name: Set up Node for Windows npm entrypoint tests | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: "24.19.0" | |
| - name: Verify Windows Kilo npm managed launch | |
| timeout-minutes: 3 | |
| run: >- | |
| cargo test --package cccc-pair-daemon --lib --locked | |
| opencode::launch_tests:: -- --test-threads=1 | |
| rust-linux: | |
| needs: web | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: bundled-web | |
| path: web/dist | |
| - uses: dtolnay/rust-toolchain@1.88.0 | |
| with: | |
| components: rustfmt, clippy | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Check Rust formatting | |
| run: cargo fmt --all --check | |
| - name: Lint Rust workspace | |
| run: cargo clippy --workspace --all-targets --locked -- -D warnings | |
| - name: Test installer and release assets | |
| run: | | |
| scripts/tests/install_unix.sh | |
| scripts/tests/release_assets.sh | |
| - name: Test Rust workspace | |
| run: | | |
| # Process lifecycle tests run serially in the final step because they | |
| # spawn and stop a combined daemon/Web process. | |
| cargo test --workspace --exclude cccc-pair-daemon --locked -- \ | |
| --skip daemon_self_launch:: | |
| cargo test --package cccc-pair-daemon --locked -- \ | |
| --test-threads=1 | |
| - name: Test combined daemon and Web process lifecycle serially | |
| run: >- | |
| cargo test --package cccc --test integration --locked | |
| daemon_self_launch:: -- --test-threads=1 | |
| - name: Set up Node for offline native-runtime smoke tests | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: "24.19.0" | |
| - name: Install verified native CLI versions | |
| run: npm install --global @openai/codex@0.153.2 @anthropic-ai/claude-code@2.1.261 @kilocode/cli@7.5.14 | |
| - name: Verify native sessions without external model access | |
| timeout-minutes: 5 | |
| env: | |
| CCCC_LAUNCHER_PATH: ${{ github.workspace }}/target/debug/cccc | |
| CCCC_CODEX_EMPTY_LIVE: "1" | |
| CCCC_CLAUDE_EMPTY_LIVE: "1" | |
| CCCC_KILO_MANAGED_LIVE: "1" | |
| CCCC_KILO_MODEL_SYNC_LIVE: "1" | |
| run: | | |
| cargo build --package cccc --bin cccc --locked | |
| cargo test --package cccc-pair-daemon --lib --locked \ | |
| live_codex_empty_actor_and_analyst_resume_with_native_terminal -- --test-threads=1 | |
| cargo test --package cccc-pair-daemon --lib --locked \ | |
| live_claude_empty_session_resumes_without_a_prompt -- --test-threads=1 | |
| cargo test --package cccc-pair-daemon --lib --locked \ | |
| live_kilo -- --test-threads=1 | |
| ci-required: | |
| if: always() | |
| needs: | |
| - quality | |
| - web | |
| - package | |
| - windows-smoke | |
| - rust-linux | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Verify required CI jobs | |
| env: | |
| NEEDS_CONTEXT: ${{ toJSON(needs) }} | |
| shell: bash | |
| run: | | |
| failures="$( | |
| jq -r ' | |
| to_entries[] | |
| | select(.value.result != "success") | |
| | "\(.key)=\(.value.result)" | |
| ' <<<"$NEEDS_CONTEXT" | |
| )" | |
| if [[ -n "$failures" ]]; then | |
| echo "Required CI jobs did not succeed:" | |
| echo "$failures" | |
| exit 1 | |
| fi | |
| echo "All required CI jobs succeeded." |