Skip to content

Conversation

@Coldaine
Copy link
Owner

@Coldaine Coldaine commented Dec 25, 2025

User description

Implements the CI bifurcation plan described in Proposal B and Issue #329.

Changes

  • Split unit_tests_hosted: Replaced the monolithic job with parallel lint, security, and docs jobs running on ubuntu-latest.
  • Renamed text_injection_tests to build_and_test: This job now runs on the self-hosted runner and handles:
    • Workspace build (leveraging warm cache).
    • Workspace unit tests (cargo test --workspace).
    • Hardware integration tests (with real-injection-tests feature).
  • Updated ci_success: Now depends on the new parallel jobs.

Benefits

  • Faster Feedback: Lint, security, and docs run in parallel on GitHub infrastructure.
  • Better Resource Usage: The self-hosted runner focuses on building and hardware tests, reducing contention.
  • Cleaner Architecture: Explicit separation of concerns between stateless checks and stateful/hardware-dependent tests.

Closes #329


PR Type

Enhancement


Description

  • Split monolithic unit_tests_hosted into three parallel jobs: lint, security, and docs

  • Renamed text_injection_tests to build_and_test with expanded responsibilities

  • Moved build, workspace tests, and hardware integration tests to self-hosted runner

  • Updated ci_success job to depend on new parallel jobs with refined error handling


Diagram Walkthrough

flowchart LR
  A["unit_tests_hosted<br/>monolithic job"] -->|split| B["lint<br/>fmt + clippy"]
  A -->|split| C["security<br/>audit + deny"]
  A -->|split| D["docs<br/>documentation"]
  E["text_injection_tests"] -->|renamed| F["build_and_test<br/>self-hosted"]
  B --> G["ci_success"]
  C --> G
  D --> G
  F --> G
Loading

File Walkthrough

Relevant files
Configuration changes
ci.yml
Restructure CI workflow into parallel and self-hosted jobs

.github/workflows/ci.yml

  • Renamed security_audit job to security for consistency
  • Split unit_tests_hosted into three independent parallel jobs: lint
    (fmt + clippy), security (audit + deny), and docs (documentation
    build)
  • Renamed text_injection_tests to build_and_test and added workspace
    unit tests execution
  • Added WHISPER_MODEL_PATH and WHISPER_MODEL_SIZE environment variables
    to build_and_test job
  • Updated ci_success job dependencies and CI report generation to
    reference new job names with refined error handling logic
+40/-100

Splits the monolithic unit_tests_hosted job into parallel lint, security, and docs jobs on GitHub-hosted runners. Moves the main build and test execution to the self-hosted runner to leverage caching and hardware capabilities.
Copilot AI review requested due to automatic review settings December 25, 2025 06:03
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Dec 25, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🟢
🎫 #329
🟢 Split the unit_tests_hosted job into separate jobs for lint (fmt + clippy), security
(audit + deny), and docs
The new `lint`, `security`, and `docs` jobs should run on `ubuntu-latest`
The new `lint`, `security`, and `docs` jobs should NOT depend on the self-hosted runner
Keep the main `build` and `test` (hardware dependent) on `self-hosted`
Ensure `moonshine_check` runs on `self-hosted`
Ensure the self-hosted job doesn't wait for the hosted jobs unless necessary (fail fast is
good, but parallelism is better)
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

ⓘ Your approaching your monthly quota for Qodo. Upgrade your plan

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🟡
🎫 #329
🟢 Split the former unit_tests_hosted job into separate lint (fmt + clippy), security (audit
+ deny), and docs jobs.
Ensure the new `lint`, `security`, and `docs` jobs run on `ubuntu-latest`.
Ensure `lint`, `security`, and `docs` do not depend on the self-hosted runner.
Keep the main `build` and hardware-dependent `test` on the self-hosted runner.
Ensure workflow dependencies allow parallelism and the self-hosted job does not wait for
hosted jobs unless necessary.
Ensure `moonshine_check` runs on the self-hosted runner.
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Ignored install failure: The cargo-deny installation step uses || true, which can silently mask tool installation
failures and lead to incomplete or misleading security check execution.

Referred Code
    cargo install cargo-deny --locked || true

- name: Run cargo audit
  run: cargo audit

- name: Run cargo deny
  run: cargo deny check

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

qodo-code-review bot commented Dec 25, 2025

CI Feedback 🧐

(Feedback updated until commit 7a0ca5e)

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: Lint (fmt + clippy)

Failed stage: Run clippy [❌]

Failure summary:

The action failed during the cargo clippy --all-targets --locked step because the build process
could not find the ALSA system library required by the alsa-sys v0.3.1 crate.

Specifically:
- The build script for alsa-sys v0.3.1 failed at
/home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/alsa-sys-0.3.1/build.rs:13:18
-
The pkg-config command exited with status code 1 when trying to find the alsa library
- The system
library alsa was not found because the file alsa.pc is not installed
- The PKG_CONFIG_PATH
environment variable is not set

This is a missing system dependency issue - the ALSA development libraries need to be installed on
the runner before building the project.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

417:  No cache found.
418:  ##[group]Run cargo fmt --all -- --check
419:  �[36;1mcargo fmt --all -- --check�[0m
420:  shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
421:  env:
422:  CARGO_TERM_COLOR: always
423:  WHISPER_MODEL_SIZE: tiny
424:  MIN_FREE_DISK_GB: 10
425:  MAX_LOAD_AVERAGE: 5
426:  CARGO_INCREMENTAL: 0
427:  CARGO_PROFILE_DEV_DEBUG: 0
428:  RUST_BACKTRACE: short
429:  RUSTFLAGS: -D warnings
430:  CARGO_UNSTABLE_SPARSE_REGISTRY: true
431:  CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
432:  CACHE_ON_FAILURE: true
433:  ##[endgroup]
434:  ##[group]Run cargo clippy --all-targets --locked
435:  �[36;1mcargo clippy --all-targets --locked�[0m
436:  shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
437:  env:
438:  CARGO_TERM_COLOR: always
439:  WHISPER_MODEL_SIZE: tiny
440:  MIN_FREE_DISK_GB: 10
441:  MAX_LOAD_AVERAGE: 5
442:  CARGO_INCREMENTAL: 0
443:  CARGO_PROFILE_DEV_DEBUG: 0
444:  RUST_BACKTRACE: short
445:  RUSTFLAGS: -D warnings
446:  CARGO_UNSTABLE_SPARSE_REGISTRY: true
447:  CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
448:  CACHE_ON_FAILURE: true
449:  ##[endgroup]
...

454:  �[1m�[92m  Downloaded�[0m env_logger v0.11.8
455:  �[1m�[92m  Downloaded�[0m openssl-probe v0.1.6
456:  �[1m�[92m  Downloaded�[0m erased-serde v0.4.9
457:  �[1m�[92m  Downloaded�[0m event-listener-strategy v0.5.4
458:  �[1m�[92m  Downloaded�[0m pest_meta v2.8.4
459:  �[1m�[92m  Downloaded�[0m fax_derive v0.2.0
460:  �[1m�[92m  Downloaded�[0m futures-util v0.3.31
461:  �[1m�[92m  Downloaded�[0m qt-build-utils v0.8.0
462:  �[1m�[92m  Downloaded�[0m num-conv v0.1.0
463:  �[1m�[92m  Downloaded�[0m num-complex v0.4.6
464:  �[1m�[92m  Downloaded�[0m num-integer v0.1.46
465:  �[1m�[92m  Downloaded�[0m rusty-fork v0.3.1
466:  �[1m�[92m  Downloaded�[0m matchers v0.2.0
467:  �[1m�[92m  Downloaded�[0m parking v2.2.1
468:  �[1m�[92m  Downloaded�[0m sha2 v0.10.9
469:  �[1m�[92m  Downloaded�[0m quick-error v2.0.1
470:  �[1m�[92m  Downloaded�[0m proc-macro-crate v3.4.0
471:  �[1m�[92m  Downloaded�[0m zeroize v1.8.2
472:  �[1m�[92m  Downloaded�[0m memchr v2.7.6
473:  �[1m�[92m  Downloaded�[0m yaml-rust2 v0.10.4
474:  �[1m�[92m  Downloaded�[0m rustls-pki-types v1.13.1
475:  �[1m�[92m  Downloaded�[0m strsim v0.11.1
476:  �[1m�[92m  Downloaded�[0m simd-adler32 v0.3.8
477:  �[1m�[92m  Downloaded�[0m unarray v0.1.4
478:  �[1m�[92m  Downloaded�[0m thiserror-impl v2.0.17
479:  �[1m�[92m  Downloaded�[0m signal-hook v0.3.18
...

533:  �[1m�[92m  Downloaded�[0m serde v1.0.228
534:  �[1m�[92m  Downloaded�[0m rustfft v6.4.1
535:  �[1m�[92m  Downloaded�[0m wait-timeout v0.2.1
536:  �[1m�[92m  Downloaded�[0m utf-8 v0.7.6
537:  �[1m�[92m  Downloaded�[0m unicode-truncate v1.1.0
538:  �[1m�[92m  Downloaded�[0m zbus_xml v5.0.2
539:  �[1m�[92m  Downloaded�[0m zbus-lockstep-macros v0.5.2
540:  �[1m�[92m  Downloaded�[0m zbus-lockstep v0.5.2
541:  �[1m�[92m  Downloaded�[0m wayland-sys v0.31.7
542:  �[1m�[92m  Downloaded�[0m toml_datetime v0.7.5+spec-1.1.0
543:  �[1m�[92m  Downloaded�[0m tokio-test v0.4.4
544:  �[1m�[92m  Downloaded�[0m tiny-keccak v2.0.2
545:  �[1m�[92m  Downloaded�[0m termcolor v1.4.1
546:  �[1m�[92m  Downloaded�[0m serde-untagged v0.1.9
547:  �[1m�[92m  Downloaded�[0m regex-syntax v0.8.8
548:  �[1m�[92m  Downloaded�[0m thiserror-impl v1.0.69
549:  �[1m�[92m  Downloaded�[0m thiserror v1.0.69
550:  �[1m�[92m  Downloaded�[0m tar v0.4.44
551:  �[1m�[92m  Downloaded�[0m strum v0.26.3
552:  �[1m�[92m  Downloaded�[0m tiff v0.10.3
553:  �[1m�[92m  Downloaded�[0m smallvec v2.0.0-alpha.10
554:  �[1m�[92m  Downloaded�[0m semver v1.0.27
555:  �[1m�[92m  Downloaded�[0m sdd v3.0.10
556:  �[1m�[92m  Downloaded�[0m typenum v1.19.0
557:  �[1m�[92m  Downloaded�[0m thiserror v2.0.17
558:  �[1m�[92m  Downloaded�[0m termtree v0.5.1
...

642:  �[1m�[92m  Downloaded�[0m polling v3.11.0
643:  �[1m�[92m  Downloaded�[0m pin-project v1.1.10
644:  �[1m�[92m  Downloaded�[0m ordered-multimap v0.7.3
645:  �[1m�[92m  Downloaded�[0m openssl-sys v0.9.111
646:  �[1m�[92m  Downloaded�[0m predicates-tree v1.0.12
647:  �[1m�[92m  Downloaded�[0m pest_generator v2.8.4
648:  �[1m�[92m  Downloaded�[0m parking_lot_core v0.9.12
649:  �[1m�[92m  Downloaded�[0m parking_lot v0.12.5
650:  �[1m�[92m  Downloaded�[0m ordered-stream v0.2.0
651:  �[1m�[92m  Downloaded�[0m option-ext v0.2.0
652:  �[1m�[92m  Downloaded�[0m num-traits v0.2.19
653:  �[1m�[92m  Downloaded�[0m iana-time-zone v0.1.64
654:  �[1m�[92m  Downloaded�[0m hound v3.5.1
655:  �[1m�[92m  Downloaded�[0m futures-channel v0.3.31
656:  �[1m�[92m  Downloaded�[0m csv v1.4.0
657:  �[1m�[92m  Downloaded�[0m quick-error v1.2.3
658:  �[1m�[92m  Downloaded�[0m primal-check v0.3.4
...

874:  �[1m�[92m   Compiling�[0m toml_datetime v0.7.5+spec-1.1.0
875:  �[1m�[92m   Compiling�[0m serde_json v1.0.146
876:  �[1m�[92m    Checking�[0m allocator-api2 v0.2.21
877:  �[1m�[92m   Compiling�[0m crunchy v0.2.4
878:  �[1m�[92m    Checking�[0m foldhash v0.1.5
879:  �[1m�[92m   Compiling�[0m toml_edit v0.23.9
880:  �[1m�[92m    Checking�[0m hashbrown v0.15.5
881:  �[1m�[92m   Compiling�[0m enumflags2_derive v0.7.12
882:  �[1m�[92m   Compiling�[0m tiny-keccak v2.0.2
883:  �[1m�[92m   Compiling�[0m proc-macro-crate v3.4.0
884:  �[1m�[92m    Checking�[0m mio v1.1.1
885:  �[1m�[92m    Checking�[0m parking v2.2.1
886:  �[1m�[92m   Compiling�[0m zvariant_utils v3.2.1
887:  �[1m�[92m    Checking�[0m itoa v1.0.15
888:  �[1m�[92m   Compiling�[0m getrandom v0.3.4
889:  �[1m�[92m   Compiling�[0m thiserror v2.0.17
890:  �[1m�[92m   Compiling�[0m thiserror-impl v2.0.17
891:  �[1m�[92m   Compiling�[0m async-trait v0.1.89
...

893:  �[1m�[92m   Compiling�[0m zvariant_derive v5.8.0
894:  �[1m�[92m   Compiling�[0m getrandom v0.2.16
895:  �[1m�[92m    Checking�[0m fastrand v2.3.0
896:  �[1m�[92m    Checking�[0m unicode-segmentation v1.12.0
897:  �[1m�[92m   Compiling�[0m ucd-trie v0.1.7
898:  �[1m�[92m   Compiling�[0m typeid v1.0.3
899:  �[1m�[92m   Compiling�[0m pest v2.8.4
900:  �[1m�[92m   Compiling�[0m const-random-macro v0.1.16
901:  �[1m�[92m   Compiling�[0m crc32fast v1.5.0
902:  �[1m�[92m    Checking�[0m ryu v1.0.20
903:  �[1m�[92m    Checking�[0m const-random v0.1.18
904:  �[1m�[92m   Compiling�[0m alsa-sys v0.3.1
905:  �[1m�[92m   Compiling�[0m anyhow v1.0.100
906:  �[1m�[92m   Compiling�[0m erased-serde v0.4.9
907:  �[1m�[92m    Checking�[0m dlv-list v0.5.2
908:  �[1m�[91merror�[0m: failed to run custom build command for `alsa-sys v0.3.1`
909:  note: To improve backtraces for build dependencies, set the CARGO_PROFILE_DEV_BUILD_OVERRIDE_DEBUG=true environment variable to enable debug information generation.
...

934:  --- stderr
935:  thread 'main' (3624) panicked at /home/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/alsa-sys-0.3.1/build.rs:13:18:
936:  pkg-config exited with status code 1
937:  > PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 pkg-config --libs --cflags alsa
938:  The system library `alsa` required by crate `alsa-sys` was not found.
939:  The file `alsa.pc` needs to be installed and the PKG_CONFIG_PATH environment variable must contain its parent directory.
940:  The PKG_CONFIG_PATH environment variable is not set.
941:  HINT: if you have installed the library, try setting PKG_CONFIG_PATH to the directory containing `alsa.pc`.
942:  stack backtrace:
943:  0: __rustc::rust_begin_unwind
944:  1: core::panicking::panic_fmt
945:  2: core::panicking::panic_display
946:  3: build_script_build::main
947:  4: core::ops::function::FnOnce::call_once
948:  note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
949:  �[1m�[33mwarning�[0m: build failed, waiting for other jobs to finish...
950:  ##[error]Process completed with exit code 101.
951:  Post job cleanup.

@qodo-free-for-open-source-projects
Copy link

qodo-free-for-open-source-projects bot commented Dec 25, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Re-evaluate moving unit tests to self-hosted runners
Suggestion Impact:The workflow was refactored to move workspace unit tests (and build/type-check) off the self-hosted runner onto a new GitHub-hosted job (build_and_unit_tests), while restricting the self-hosted job to hardware-specific tests only (hardware_tests). This directly addresses the concern about self-hosted bottlenecks for stateless unit tests.

code diff:

-  # Main build and test job on self-hosted runner
-  build_and_test:
-    name: Build & Test (Self-Hosted)
+  # Build and unit tests on GitHub-hosted (stateless, parallelizable)
+  build_and_unit_tests:
+    name: Build & Unit Tests (Hosted)
+    runs-on: ubuntu-latest
+    needs: [setup-whisper-dependencies]
+    steps:
+      - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0
+
+      - name: Install system dependencies
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y pkg-config libasound2-dev libgtk-3-dev python3-pip python3-venv
+
+      - name: Set up Rust toolchain
+        uses: actions-rust-lang/setup-rust-toolchain@v1
+        with:
+          toolchain: stable
+
+      - name: Type check
+        run: cargo check --workspace --all-targets --locked
+
+      - name: Build
+        run: cargo build --workspace --locked
+
+      - name: Run unit tests
+        env:
+          WHISPER_MODEL_PATH: ${{ needs.setup-whisper-dependencies.outputs.model_path }}
+          WHISPER_MODEL_SIZE: ${{ needs.setup-whisper-dependencies.outputs.model_size }}
+        run: |
+          echo "=== Running Workspace Unit Tests ==="
+          cargo test --workspace --locked
+
+      - name: Run Golden Master pipeline test
+        env:
+          WHISPER_MODEL_PATH: ${{ needs.setup-whisper-dependencies.outputs.model_path }}
+          WHISPER_MODEL_SIZE: ${{ needs.setup-whisper-dependencies.outputs.model_size }}
+        run: |
+          echo "=== Running Golden Master Test ==="
+          pip install faster-whisper
+          export PYTHONPATH=$(python3 -c "import site; print(site.getsitepackages()[0])")
+          cargo test -p coldvox-app --test golden_master -- --nocapture
+
+      - name: Upload test artifacts on failure
+        if: failure()
+        uses: actions/upload-artifact@v6
+        with:
+          name: test-artifacts-build
+          path: |
+            target/debug/deps/
+            target/debug/build/
+          retention-days: 7
+
+  # Hardware tests ONLY - requires live display (self-hosted laptop)
+  hardware_tests:
+    name: Hardware Tests (Self-Hosted)
     runs-on: [self-hosted, Linux, X64, fedora, nobara]
-    needs: [setup-whisper-dependencies]
-    timeout-minutes: 30
+    timeout-minutes: 15
     env:
       # Runner's live KDE Wayland session with XWayland
       DISPLAY: ":0"
@@ -163,42 +214,11 @@
       RUST_LOG: debug
       RUST_TEST_TIME_UNIT: 10000
       RUST_TEST_TIME_INTEGRATION: 30000
-      SCCACHE_DIR: $HOME/.cache/sccache
-      # Build optimizations
-      CARGO_INCREMENTAL: "1"
-      RUSTFLAGS: "-C link-arg=-fuse-ld=mold"
-      WHISPER_MODEL_PATH: ${{ needs.setup-whisper-dependencies.outputs.model_path }}
-      WHISPER_MODEL_SIZE: ${{ needs.setup-whisper-dependencies.outputs.model_size }}
     steps:
       - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.0
       - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca0028893a2d9 # v1
         with:
           toolchain: stable
-
-      # Setup sccache via justfile (installs if missing, enables RUSTC_WRAPPER)
-      - name: Setup sccache
-        run: |
-          # Install just if not available
-          if ! command -v just >/dev/null 2>&1; then
-            cargo install just --locked
-          fi
-
-          # Run setup-sccache recipe (idempotent - installs if missing)
-          just setup-sccache
-
-          # Enable sccache wrapper after installation
-          SCCACHE_BIN=""
-          if command -v sccache >/dev/null 2>&1; then
-            SCCACHE_BIN="$(command -v sccache)"
-          elif [[ -x "$HOME/.cargo/bin/sccache" ]]; then
-            SCCACHE_BIN="$HOME/.cargo/bin/sccache"
-          fi
-
-          if [[ -n "$SCCACHE_BIN" ]]; then
-            "$SCCACHE_BIN" --start-server || true
-            echo "RUSTC_WRAPPER=$SCCACHE_BIN" >> "$GITHUB_ENV"
-            echo "sccache enabled: $SCCACHE_BIN"
-          fi
 
       - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0
 
@@ -235,89 +255,49 @@
             echo "::warning::X11 not accessible via xset"
           fi
 
-      - name: Validate test prerequisites
-        run: |
-          set -euo pipefail
-          echo "=== Test Environment Validation ==="
+      - name: Validate hardware prerequisites
+        run: |
+          set -euo pipefail
+          echo "=== Hardware Test Environment Validation ==="
           echo "DISPLAY: $DISPLAY"
           echo "XAUTHORITY: ${XAUTHORITY:-not set}"
           echo "WAYLAND_DISPLAY: ${WAYLAND_DISPLAY:-not set}"
 
-          # Check if XAUTHORITY file exists
-          if [[ -f "${XAUTHORITY:-}" ]]; then
-            echo "XAUTHORITY file exists: $XAUTHORITY"
-          else
-            echo "::warning::XAUTHORITY file not found at ${XAUTHORITY:-unset}"
-          fi
-
           # Verify X server is accessible
           if ! xset -q >/dev/null 2>&1; then
             echo "::error::Cannot connect to X server on display $DISPLAY"
-            echo "Trying xhost diagnostics..."
-            xhost 2>&1 || true
             exit 1
           fi
           echo "X server is accessible."
-          echo "Available text injection backends:"
-          command -v xdotool >/dev/null && echo "  - xdotool: $(xdotool --version 2>/dev/null || echo 'available')"
+          echo "Available backends:"
+          command -v xdotool >/dev/null && echo "  - xdotool: available"
           command -v ydotool >/dev/null && echo "  - ydotool: available"
-          command -v enigo >/dev/null && echo "  - enigo: available (Rust crate)"
-          echo "GTK development libraries:"
-          pkg-config --exists gtk+-3.0 && echo "  - GTK+ 3.0: available" || echo "  - GTK+ 3.0: not found"
-          echo "System audio:"
-          command -v alsa-info >/dev/null && echo "  - ALSA: available" || echo "  - ALSA: not found"
           echo "=== Validation Complete ==="
 
-      - name: Run Workspace Unit Tests
-        run: |
-          echo "=== Running Workspace Unit Tests ==="
-          cargo test --workspace --locked
-
-      - name: Test with real-injection-tests feature
+      - name: Run real injection tests
         run: |
           dbus-run-session -- bash -lc '
-            # Set per-test timeout to prevent hanging
-            export RUST_TEST_TIME_UNIT="10000"  # 10 second timeout per test
-            export RUST_TEST_TIME_INTEGRATION="30000"  # 30 second for integration tests
-            # Note: atspi feature not enabled - AT-SPI Collection.GetMatches requires
-            # a full desktop session, not available in headless Xvfb environment.
-            # AT-SPI tests will skip gracefully.
+            export RUST_TEST_TIME_UNIT="10000"
+            export RUST_TEST_TIME_INTEGRATION="30000"
             cargo test -p coldvox-text-injection \
               --features real-injection-tests \
               -- --nocapture --test-threads=1
           '
 
-      - name: Build pipeline (default features)
-        run: |
-          dbus-run-session -- bash -c '
-            set -euo pipefail
-            echo "Testing default features..."
-            cargo test -p coldvox-text-injection --locked
-
-            echo "Testing without default features..."
-            cargo test -p coldvox-text-injection --no-default-features --locked
-
-            echo "Testing regex feature only..."
-            cargo test -p coldvox-text-injection --no-default-features --features regex --locked
-          '
-
-      # Build main app to ensure integration compiles
-      - name: Build main application
-        run: cargo build --locked -p coldvox-app
-
       - name: Run Hardware Capability Checks
         env:
           COLDVOX_E2E_REAL_INJECTION: "1"
           COLDVOX_E2E_REAL_AUDIO: "1"
         run: |
           echo "=== Running Hardware Capability Checks ==="
-          # We run the new hardware_check test file, enabling the ignored tests
           cargo test -p coldvox-app --test hardware_check -- --nocapture --include-ignored
 
-      - name: Upload test artifacts on failure
-        if: failure()
-        uses: actions/upload-artifact@v6
-        with:
+      - name: Cleanup
+        if: always()
+        run: |
+          if [[ -n "${DBUS_SESSION_BUS_PID:-}" ]]; then
+            kill "$DBUS_SESSION_BUS_PID" 2>/dev/null || true
+          fi
           name: test-artifacts-build-and-test
           path: |
             target/debug/deps/
@@ -379,7 +359,8 @@
       - lint
       - security
       - docs
-      - build_and_test
+      - build_and_unit_tests
+      - hardware_tests
       - moonshine_check
     if: always()
     steps:
@@ -392,14 +373,16 @@
           echo "- lint:                          ${{ needs.lint.result }}" >> report.md
           echo "- security:                      ${{ needs.security.result }}" >> report.md
           echo "- docs:                          ${{ needs.docs.result }}" >> report.md
-          echo "- build_and_test:                ${{ needs.build_and_test.result }}" >> report.md
+          echo "- build_and_unit_tests:          ${{ needs.build_and_unit_tests.result }}" >> report.md
+          echo "- hardware_tests:                ${{ needs.hardware_tests.result }}" >> report.md
           echo "- moonshine_check:               ${{ needs.moonshine_check.result }} (optional)" >> report.md
           
           if [[ "${{ needs.setup-whisper-dependencies.result }}" != "success" ]]; then echo "::error::Setup Whisper dependencies failed."; exit 1; fi
           if [[ "${{ needs.lint.result }}" != "success" ]]; then echo "::error::Lint checks failed."; exit 1; fi
           if [[ "${{ needs.security.result }}" != "success" ]]; then echo "::warning::Security audit failed - check for vulnerabilities."; fi
           if [[ "${{ needs.docs.result }}" != "success" ]]; then echo "::warning::Documentation build failed."; fi
-          if [[ "${{ needs.build_and_test.result }}" != "success" ]]; then echo "::error::Build and Test failed."; exit 1; fi
+          if [[ "${{ needs.build_and_unit_tests.result }}" != "success" ]]; then echo "::error::Build and Unit Tests failed."; exit 1; fi
+          if [[ "${{ needs.hardware_tests.result }}" != "success" ]]; then echo "::error::Hardware tests failed."; exit 1; fi
           if [[ "${{ needs.moonshine_check.result }}" != "success" ]]; then echo "::warning::Moonshine check failed (optional)."; fi

The suggestion questions moving unit tests to the self-hosted runner, as this
may contradict the goal of offloading stateless tasks and could create a
resource bottleneck. It recommends either reverting this change or explicitly
justifying the trade-off in the PR description.

Examples:

.github/workflows/ci.yml [271-274]
      - name: Run Workspace Unit Tests
        run: |
          echo "=== Running Workspace Unit Tests ==="
          cargo test --workspace --locked

Solution Walkthrough:

Before:

# .github/workflows/ci.yml

unit_tests_hosted:
  name: Unit Tests (Hosted)
  runs-on: ubuntu-latest
  steps:
    - name: Install system dependencies
      ...
    - name: Build
      run: cargo build --workspace
    - name: Run unit and integration tests
      run: cargo test --workspace --locked

text_injection_tests:
  name: Hardware Integration Tests (Self-Hosted)
  runs-on: self-hosted
  steps:
    - name: Test with real-injection-tests feature
      ...

After:

# .github/workflows/ci.yml

lint:
  runs-on: ubuntu-latest
  ...
docs:
  runs-on: ubuntu-latest
  ...

build_and_test:
  name: Build & Test (Self-Hosted)
  runs-on: self-hosted
  steps:
    - name: Run Workspace Unit Tests
      run: cargo test --workspace --locked
    - name: Test with real-injection-tests feature
      ...
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a key architectural trade-off where moving unit tests to the build_and_test job contradicts the stated goal of offloading stateless tasks, impacting the core design of the CI pipeline.

Medium
Possible issue
Fail CI on documentation build failure

Change the documentation build check from a warning to an error. This will cause
the CI pipeline to fail if the documentation build fails.

.github/workflows/ci.yml [401]

-+          if [[ "${{ needs.docs.result }}" != "success" ]]; then echo "::warning::Documentation build failed."; fi
++          if [[ "${{ needs.docs.result }}" != "success" ]]; then echo "::error::Documentation build failed."; exit 1; fi

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: This is a valid suggestion that improves CI robustness by ensuring documentation builds successfully, which is a good practice, though not a critical failure.

Low
  • Update

@qodo-code-review
Copy link

ⓘ Your approaching your monthly quota for Qodo. Upgrade your plan

PR Code Suggestions ✨

No code suggestions found for the PR.

@kiloconnect
Copy link

kiloconnect bot commented Dec 25, 2025

⚠️ 5 Issues Found

Severity Issue Location
CRITICAL Missing ALSA libraries in lint job .github/workflows/ci.yml:139
WARNING Missing system deps in docs job .github/workflows/ci.yml:151
WARNING Silent security tool installation failure .github/workflows/ci.yml:118
WARNING Documentation build should fail CI .github/workflows/ci.yml:401
WARNING Trailing whitespace .github/workflows/ci.yml:379, 387

Recommendation: Fix critical ALSA dependency issue in lint job before merge. Other issues are non-blocking but should be addressed.

Review Details (1 file)

Files: .github/workflows/ci.yml

Progress Since Last Review:

  • ✅ RESOLVED: Unit tests moved to GitHub-hosted build_and_unit_tests job (addresses architectural concern)
  • ⚠️ PARTIALLY RESOLVED: ALSA dependencies added to build_and_unit_tests but missing from lint and docs jobs

Critical Issues:

  1. ALSA in lint job: The lint job runs cargo clippy which compiles code and needs ALSA libraries. Currently fails on ubuntu-latest.

  2. System deps in docs: Missing pkg-config and libasound2-dev may cause doc build failures.

  3. Security tools: Installation uses || true masking failures.

  4. Documentation policy: Should fail CI as error, not warning.

  5. Formatting: Trailing whitespace on multiple lines.

Fix these issues in Kilo Cloud

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a CI bifurcation strategy that splits the monolithic CI workflow into parallel hosted jobs and a consolidated self-hosted job for better resource utilization and faster feedback.

Key Changes:

  • Split lint, security, and documentation checks into parallel jobs on GitHub-hosted runners
  • Consolidate build and test operations into a single self-hosted job with hardware integration tests
  • Rename jobs for clarity (security_auditsecurity, text_injection_testsbuild_and_test)

echo "- docs: ${{ needs.docs.result }}" >> report.md
echo "- build_and_test: ${{ needs.build_and_test.result }}" >> report.md
echo "- moonshine_check: ${{ needs.moonshine_check.result }} (optional)" >> report.md
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace detected on this line. Remove the trailing spaces for cleaner code formatting.

Suggested change

Copilot uses AI. Check for mistakes.
if [[ "${{ needs.docs.result }}" != "success" ]]; then echo "::warning::Documentation build failed."; fi
if [[ "${{ needs.build_and_test.result }}" != "success" ]]; then echo "::error::Build and Test failed."; exit 1; fi
if [[ "${{ needs.moonshine_check.result }}" != "success" ]]; then echo "::warning::Moonshine check failed (optional)."; fi
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace detected on this line. Remove the trailing spaces for cleaner code formatting.

Copilot uses AI. Check for mistakes.
Comment on lines 138 to 139
- name: Run clippy
if: matrix.rust-version == 'stable'
run: cargo clippy --all-targets --locked
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lint job runs cargo clippy --all-targets --locked without installing system dependencies. The workspace includes crates that depend on system libraries (e.g., coldvox-audio uses cpal which requires ALSA development libraries on Linux, coldvox-text-injection may require GTK headers via pkg-config). While clippy with default features might work if these dependencies are optional, consider adding necessary system dependencies (pkg-config, libasound2-dev) or running clippy with specific feature flags to ensure reliable builds on Ubuntu runners.

Copilot uses AI. Check for mistakes.
Comment on lines 150 to 151
- name: Build documentation
if: matrix.rust-version == 'stable'
run: cargo doc --workspace --no-deps --locked
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs job runs cargo doc --workspace without installing system dependencies. The workspace includes crates that may require system libraries to compile (e.g., coldvox-audio uses cpal which requires ALSA development libraries). While documentation building with default features might work if these dependencies are optional, consider adding necessary system dependencies (pkg-config, libasound2-dev) to ensure reliable doc builds on Ubuntu runners.

Copilot uses AI. Check for mistakes.
Comment on lines 271 to 274
- name: Run Workspace Unit Tests
run: |
echo "=== Running Workspace Unit Tests ==="
cargo test --workspace --locked
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Golden Master pipeline test that was previously executed in the unit_tests_hosted job is not present in the new build_and_test job. The original workflow included a specific step to run cargo test -p coldvox-app --test golden_master -- --nocapture with Python dependencies (faster-whisper). Since the build_and_test job now has access to WHISPER_MODEL_PATH and WHISPER_MODEL_SIZE environment variables and runs on the self-hosted runner, this test should be included to maintain test coverage.

Copilot uses AI. Check for mistakes.
Comment on lines 271 to 274
- name: Run Workspace Unit Tests
run: |
echo "=== Running Workspace Unit Tests ==="
cargo test --workspace --locked
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Run Workspace Unit Tests" step does not include validation of the WHISPER_MODEL_PATH and WHISPER_MODEL_SIZE environment variables before executing tests. The original unit_tests_hosted job validated these variables were set and accessible. Consider adding validation similar to the original implementation to ensure tests fail early with clear error messages if the Whisper dependencies are not properly configured.

Copilot uses AI. Check for mistakes.
The laptop has WEAK hardware but a LIVE display. GitHub-hosted
runners have POWERFUL hardware but no display.

Correct split:
- GitHub-hosted: lint, security, docs, build, unit tests
- Self-hosted: ONLY hardware-dependent tests (real-injection, audio)

This minimizes work on the weak laptop while leveraging its unique
capability (live display session).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement CI Bifurcation (Split Hosted vs Self-Hosted)

2 participants