# OpenHuman Two-Lane CI Model: Understanding CI Lite vs CI Full and the 80% Coverage Gate

> Explore OpenHuman's two-lane CI model: CI Lite enforces 80% diff coverage on main, CI Full runs complete tests on release. Learn how this ensures code quality before deployment.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: deep-dive
- Published: 2026-08-29

---

**OpenHuman uses a dual-track continuous integration system where CI Lite enforces an 80% diff-coverage gate on every pull request to `main` using targeted test runs, while CI Full executes the complete test suite—including Playwright and cross-platform desktop E2E tests—on the `release` branch before production deployment.**

The `tinyhumansai/openhuman` repository implements a sophisticated CI strategy that balances rapid developer feedback with rigorous release quality. By splitting validation into two distinct lanes, the project ensures that daily development moves quickly while guaranteeing that every release candidate undergoes comprehensive testing. This architecture centers on a strict **diff-coverage gate** that requires all new code to be sufficiently exercised by tests before merging.

## The Two-Lane CI Architecture

OpenHuman’s pipeline is explicitly divided into **CI Lite** and **CI Full**, each defined in separate workflow files and triggered by different branch events.

### CI Lite: Fast Feedback for Development

**CI Lite** is defined in [`.github/workflows/ci-lite.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/ci-lite.yml) and runs on every push to `main` and every pull request targeting `main` or `release`. This lane prioritizes speed by testing only what changed:

- **Frontend testing** – Vitest executes only tests importing files modified under `app/src/` using the `--changed` flag.
- **Rust testing** – `cargo llvm-cov` runs with a libtest filter derived from changed source directories (`src/<a>/<b>/…`).
- **Quality checks** – Lint, format, and static analysis run in parallel.
- **Coverage enforcement** – The **80% diff-coverage gate** blocks merges if changed lines lack sufficient test coverage.

### CI Full: Comprehensive Release Validation

**CI Full** is defined in [`.github/workflows/ci-full.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/ci-full.yml) and triggers only on pushes to the long-lived `release` branch and PRs targeting `release`. This lane executes the exhaustive test matrix:

- Complete Vitest and Rust unit test suites.
- Rust mock-backend E2E tests.
- **Playwright UI tests** (run as a non-blocking signal; flaky tests do not block merge).
- Full desktop-E2E matrix across Linux, macOS, and Windows.

Results aggregate behind the **CI Full Gate** check, which must pass before a release is cut.

## How the 80% Diff-Coverage Gate Works

The coverage gate is enforced exclusively in the CI Lite lane to ensure all new code entering `main` meets quality standards. The implementation relies on a four-step process coordinated by shell scripts in the repository.

### Step 1: Identify Changed Lines

GitHub Actions provides the list of files modified in a pull request. The system uses this list to scope all subsequent operations, ensuring the pipeline ignores unchanged legacy code when calculating coverage percentages.

### Step 2: Execute Targeted Tests

The scripts [`scripts/ci/vitest-changed-coverage.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/vitest-changed-coverage.sh) (frontend) and [`scripts/ci/rust-coverage-changed.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/rust-coverage-changed.sh) (backend) invoke the appropriate test runners with change-aware filters:

- **Frontend**: Vitest runs with `--changed` to select only relevant tests.
- **Rust**: `cargo llvm-cov` receives flags limiting execution to test targets matching the changed source directories.

### Step 3: Calculate and Enforce Coverage

Both scripts generate LCOV reports. The `diff-cover` utility then calculates the percentage of changed lines covered by tests. If this percentage falls below **80%**, the script exits with a non-zero status, failing the CI Lite job and preventing the pull request from merging.

### Fallback for Configuration Changes

When changes touch configuration files (e.g., [`Cargo.toml`](https://github.com/tinyhumansai/openhuman/blob/main/Cargo.toml), lockfiles, or Vitest config), the changed-file strategy becomes insufficient. In these cases, the scripts automatically fallback to running the **full test suite** to guarantee coverage on the broader impact of these modifications.

## Promotion Flow from Main to Release

Code moves from development to production through a structured promotion process:

1. Developers merge feature branches into `main` after passing the CI Lite diff-coverage gate.
2. Maintainers trigger the promotion workflow ([`promote-main-to-release.yml`](https://github.com/tinyhumansai/openhuman/blob/main/promote-main-to-release.yml)), creating a merge commit from `main` into `release`.
3. The **CI Full** lane runs on this commit, providing the final safety net with complete UI and desktop E2E validation.
4. Only after CI Full passes is a production release cut from the `release` branch.

## Running Coverage Checks Locally

Developers can verify diff-coverage locally before submitting a pull request using the same scripts invoked in CI:

```bash

# Install required tooling

cargo install cargo-llvm-cov diff-cover
npm install -g vitest

# Execute the coverage scripts

./scripts/ci/vitest-changed-coverage.sh
./scripts/ci/rust-coverage-changed.sh

```

Both commands print the percentage coverage of changed lines and abort with exit code 1 if the result falls below the 80% threshold, mirroring the behavior of the CI Lite gate.

## Key Implementation Files

The two-lane CI model and coverage gate rely on these specific files in the `tinyhumansai/openhuman` repository:

- **[`.github/workflows/ci-lite.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/ci-lite.yml)** – Defines the fast CI lane for `main` branch pushes and pull requests.
- **[`.github/workflows/ci-full.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/ci-full.yml)** – Defines the comprehensive CI lane for `release` branch validation.
- **[`scripts/ci/vitest-changed-coverage.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/vitest-changed-coverage.sh)** – Runs Vitest on changed files and computes frontend diff coverage.
- **[`scripts/ci/rust-coverage-changed.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/rust-coverage-changed.sh)** – Runs `cargo llvm-cov` on changed Rust modules and enforces the 80% threshold.
- **[`AGENTS.md`](https://github.com/tinyhumansai/openhuman/blob/main/AGENTS.md)** (line 66) – Documents the high-level CI architecture for contributor reference.

## Summary

- **CI Lite** provides rapid feedback on every PR to `main` by running scoped tests and enforcing an **80% diff-coverage gate** via [`.github/workflows/ci-lite.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/ci-lite.yml).
- **CI Full** runs the exhaustive test suite—including Playwright and desktop E2E tests—only on the `release` branch via [`.github/workflows/ci-full.yml`](https://github.com/tinyhumansai/openhuman/blob/main/.github/workflows/ci-full.yml).
- The coverage gate uses `diff-cover` on LCOV reports generated by [`scripts/ci/vitest-changed-coverage.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/vitest-changed-coverage.sh) and [`scripts/ci/rust-coverage-changed.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/rust-coverage-changed.sh).
- Configuration changes trigger a fallback to full-suite testing to ensure accurate coverage metrics.
- Code promotes from `main` to `release` through a manual workflow that triggers the CI Full gate as the final quality checkpoint.

## Frequently Asked Questions

### What is the 80% diff-coverage gate in OpenHuman?

The 80% diff-coverage gate is a quality checkpoint enforced in the CI Lite lane that requires at least 80% of lines modified in a pull request to be covered by tests. It is implemented using the `diff-cover` utility on LCOV reports generated by targeted Vitest and `cargo llvm-cov` runs.

### When does CI Full run instead of CI Lite?

CI Full runs on every push to the `release` branch and on pull requests targeting `release`, whereas CI Lite runs on pushes to `main` and PRs targeting `main` or `release`. CI Full executes the complete test matrix including cross-platform desktop E2E tests, while CI Lite runs only tests relevant to changed files.

### How can I check if my changes meet the coverage requirement before opening a PR?

Run the [`scripts/ci/vitest-changed-coverage.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/vitest-changed-coverage.sh) and [`scripts/ci/rust-coverage-changed.sh`](https://github.com/tinyhumansai/openhuman/blob/main/scripts/ci/rust-coverage-changed.sh) scripts locally after installing `cargo-llvm-cov`, `diff-cover`, and Vitest. These scripts calculate coverage only on your changed lines and exit with an error if the result is below 80%, identical to the CI Lite behavior.

### What happens if I modify configuration files like Cargo.toml?

If your changes touch configuration files (lockfiles, [`Cargo.toml`](https://github.com/tinyhumansai/openhuman/blob/main/Cargo.toml), Vitest config), the CI Lite scripts detect this and fallback to running the full test suite rather than the scoped changed-file tests. This ensures that broad configuration changes are validated against the entire codebase to prevent coverage gaps.