# OpenHuman Two-Lane CI Model: CI Lite vs CI Full and Safety Gates Explained

> Explore OpenHuman's two-lane CI model: CI Lite for quick feedback and CI Full for thorough checks. Understand safety gates protecting your release branch.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: internals
- Published: 2026-09-01

---

**OpenHuman uses a dual-track continuous integration system where CI Lite provides rapid feedback on every push to `main` while CI Full performs exhaustive verification before any code reaches the `release` branch.**

The OpenHuman repository implements a sophisticated continuous integration strategy that balances developer velocity with release safety. By splitting the pipeline into two distinct lanes, the project ensures that contributors receive immediate feedback during development while maintaining rigorous quality standards for production releases. This two-lane CI model employs path-based change detection and multiple safety gates to enforce code quality, test coverage, and architectural integrity.

## Understanding the Two-Lane CI Architecture

OpenHuman's CI/CD pipeline operates on two complementary tracks that run different workloads based on the target branch and the nature of the changes.

### CI Lite: The Fast Feedback Lane

CI Lite triggers on pushes to `main` and pull requests targeting `main` or `release`. This lane focuses on **speed and precision**, running only the checks and tests relevant to the files that actually changed.

The lane uses the `dorny/paths-filter` action in [`.github/workflows/ci-lite.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/ci-lite.yml) to detect which areas of the repository were modified. It sets output flags such as `frontend`, `rust-core`, `rust-tauri`, `i18n`, `docs`, and `scripts` to drive conditional job execution. When frontend files change, only Vitest runs against the affected files. When Rust code changes, only the impacted crates undergo testing via `cargo-llvm-cov`.

Key characteristics of CI Lite include:
- **Partial test execution** scoped to changed files
- **≥ 80% diff-cover** enforcement on modified lines using the `diff-cover` tool
- **Quality checks** including Prettier, ESLint, i18n coverage validation (`pnpm i18n:check`), and generated-docs drift detection (`pnpm docs:check`)
- **Script self-tests** to validate tooling changes

### CI Full: The Exhaustive Verification Lane

CI Full activates on pushes to `release`, pull requests targeting `release`, and the manual *promote-main-to-release* workflow. This lane runs the **complete test matrix** regardless of which files changed, ensuring every release candidate passes comprehensive validation.

The full lane executes:
- **Complete unit suites** via [`.github/workflows/test-reusable.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/test-reusable.yml) covering frontend Vitest, Rust core, and Rust-Tauri
- **Rust E2E tests** against the mock backend with checksum-pinned native test modules (TinyMemory, TinyJuice, etc.)
- **Playwright web E2E** tests using a cached build artifact to avoid rebuilding the heavy web bundle on every run
- **Desktop E2E** on Linux (macOS and Windows currently disabled following the Wry migration)

Only after all these jobs succeed does the **CI Full Gate** permit merging into `release`.

## CI Lite Implementation and Path-Based Optimization

The efficiency of CI Lite relies on intelligent change detection defined in [`.github/workflows/ci-lite.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/ci-lite.yml) starting at line 47.

### Changed-Area Detection

The workflow begins with a `changes` job that produces boolean outputs indicating which domains require testing:

```yaml
- name: Detect changed paths
  id: filter
  uses: dorny/paths-filter@v2
  with:
    filters: |
      frontend:
        - 'frontend/**'
      rust-core:
        - 'src/core/**'
      rust-tauri:
        - 'src-tauri/**'

```

Downstream jobs check these outputs to determine execution. For example, the frontend test job includes `if: needs.changes.outputs.frontend == 'true'`, ensuring Vitest runs only when relevant files change.

### Partial Test Execution and Coverage Gates

When changes occur, the pipeline runs scoped tests rather than the full suite. For Rust components, this means executing `cargo test` only on crates listed in `rust-core-src-files` (or the full suite if `rust-core-full` equals `true`). The `cargo-llvm-cov` integration generates coverage reports specifically for the diff, and the `diff-cover` tool enforces the **≥ 80% coverage threshold** on changed lines.

Quality gates in CI Lite also validate:
- Lint and formatting compliance
- i18n coverage consistency
- Documentation generation drift
- Script functionality via self-tests

## CI Full Implementation and the Final Gate

When code approaches release, [`.github/workflows/ci-full.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/ci-full.yml) (lines 60-84) orchestrates comprehensive validation through the exhaustive lane.

### The Full Test Matrix

CI Full reuses the test workflow defined in [`test-reusable.yml`](https://github.com/tinyhumansai/openhuman/blob/main/test-reusable.yml) with all flags enabled (`run_unit`, `run_rust_core`, `run_rust_tauri`). It then proceeds to integration testing:

1. **Rust E2E**: Boots the mock backend and runs `pnpm test:rust:e2e` against the full native module suite
2. **Playwright Web E2E**: Executes after the `build-playwright-e2e-artifact` job creates and caches the web build
3. **Desktop E2E**: Runs through the reusable workflow [`e2e-reusable.yml`](https://github.com/tinyhumansai/openhuman/blob/main/e2e-reusable.yml) on the Linux matrix

### The CI Full Gate Job

After all test jobs complete, the `ci-full-gate` job aggregates results to enforce the final merge requirement:

```yaml
- name: Require all CI Full jobs to pass
  run: |
    declare -A results=(
      ["Full Unit Suites"]="${{ needs['unit-tests'].result }}"
      ["Rust E2E"]="${{ needs['rust-e2e'].result }}"
      ["Build Playwright E2E Artifact"]="${{ needs['build-playwright-e2e-artifact'].result }}"
      ["Desktop E2E"]="${{ needs['e2e-desktop'].result }}"
    )
    # Fails if any result is not success or skipped

```

This gate ensures that only fully validated code reaches the `release` branch, protecting production releases from partial test passes or transient failures.

## Shared Safety Gates Across Both Lanes

Both CI lanes enforce additional architectural and security constraints through specialized guard jobs defined in [`ci-lite.yml`](https://github.com/tinyhumansai/openhuman/blob/main/ci-lite.yml):

**Feature-Gate Smoke Test** (`rust-feature-gate-smoke`): Builds crates with `--no-default-features` to ensure facade and stub signatures remain in sync when domain gates are disabled.

**Kernel-Floor Guard**: Executes [`scripts/check-kernel-floor.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/check-kernel-floor.sh) to verify that the minimal dependency set required by embedding hosts does not grow unintentionally, keeping the embedded-host profile lightweight.

**Dependency Simulation Guard**: Validates [`scripts/dep-sim.py`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/dep-sim.py) against expected dependency counts to detect mismatches between the simulator and Cargo's actual graph.

**Feature-Forwarding Gate**: Runs `scripts/ci/check-feature-forwarding.mjs` to confirm that the Tauri shell forwards all core product features correctly, ensuring the desktop UI receives the same feature set as the core binary.

**Toolchain-Image Drift Guard**: Executes `scripts/ci/check-toolchain-image.mjs` to verify that CI Docker images and Rust toolchain pins match repository definitions, preventing supply-chain drift.

**Orchestration IP Gate**: Runs [`scripts/ci/orch-ip-gate.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/orch-ip-gate.sh) to block any resurrected local-brain orchestration code from re-entering the repository.

**Test-Inventory Guard**: Validates that every controller domain referenced in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs) has a corresponding test and that no orphaned test files exist.

## Extending the Two-Lane Model

When adding new domains to OpenHuman, developers must update the path filters and conditional logic. To add a new Rust domain called "analytics":

```yaml

# In .github/workflows/ci-lite.yml

# 1. Extend the paths-filter block

filters: |
  analytics:
    - 'src/analytics/**'

# 2. Add conditional job

- name: Rust Analytics Checks
  if: needs.changes.outputs.analytics == 'true'
  runs-on: ubuntu-22.04
  steps:
    - name: Run analytics unit tests
      run: cargo test -p analytics

```

For Playwright tests, ensure spec files reside under `src/**` or `app/**` so the `hashFiles` call in `build-playwright-e2e-artifact` automatically invalidates the cache when they change.

## Summary

- **CI Lite** provides rapid, change-scoped feedback on every push to `main`, running only affected tests and enforcing 80% diff-cover, lint, formatting, and i18n checks.
- **CI Full** executes comprehensive validation including full unit suites, Rust E2E, Playwright web tests, and desktop E2E before any code reaches the `release` branch.
- The **CI Full Gate** job consolidates all exhaustive test results and blocks merges unless every component passes.
- **Shared guard jobs** protect architectural boundaries, feature-gate correctness, kernel dependency limits, and supply-chain integrity across both lanes.
- Path-based detection via `dorny/paths-filter` in [`ci-lite.yml`](https://github.com/tinyhumansai/openhuman/blob/main/ci-lite.yml) drives the conditional execution that makes the fast lane efficient.

## Frequently Asked Questions

### What triggers CI Lite versus CI Full in OpenHuman?

CI Lite triggers on pushes to `main` and pull requests targeting `main` or `release`. CI Full triggers only on pushes to `release`, PRs targeting `release`, and the manual *promote-main-to-release* workflow. This ensures developers get fast feedback during active development while exhaustive verification occurs only when code is ready for release.

### How does OpenHuman enforce the 80% coverage requirement?

The CI Lite lane uses `cargo-llvm-cov` to generate coverage reports for changed Rust files and Vitest for changed frontend files. The `diff-cover` tool then analyzes these reports to ensure modified lines meet the ≥ 80% coverage threshold. If the diff coverage falls below this percentage, the check fails and blocks the pull request.

### What happens if the CI Full Gate fails?

If any job in the exhaustive lane fails—including full unit suites, Rust E2E, Playwright tests, or desktop E2E—the `ci-full-gate` job in [`ci-full.yml`](https://github.com/tinyhumansai/openhuman/blob/main/ci-full.yml) detects the failure and terminates with an error. This prevents the pull request from merging into `release` and blocks the creation of a production release until all issues are resolved and the pipeline passes.

### Can I run CI Full locally or on my feature branch?

While CI Full automatically runs when targeting the `release` branch, developers can manually trigger the full test matrix locally by running the same commands the CI uses: `cargo test` for all Rust workspace members, `pnpm test:rust:e2e` for Rust integration tests, and `pnpm exec playwright test` for web E2E. However, the gated `release` branch protection ensures only CI-validated code reaches production.