# How to Run the Typecheck Job Efficiently Across CI Lanes in Freebuff

> Slash typecheck job runtime from 15 to 3 minutes in Freebuff. Learn how to efficiently split monorepo type-checking across parallel CI lanes using bun.

- Repository: [Codebuff/freebuff](https://github.com/CodebuffAI/freebuff)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Run `bun run scripts/ci/typecheck-lanes.ts` to split the monorepo's type-checking workload across parallel CI lanes, reducing total runtime from ~15 minutes to ~3 minutes.**

The Freebuff monorepo uses a **lane-based parallelization strategy** to keep TypeScript type-checking fast at scale. Instead of one slow `tsc` process over the entire codebase, the system distributes workspaces across independent runners. This article explains the architecture, shows exact commands, and references the source implementation.

## What Are CI Typecheck Lanes?

In Freebuff, a **lane** is a unit of parallel work that type-checks a subset of workspaces. The [`scripts/ci/typecheck-lanes.ts`](https://github.com/CodebuffAI/freebuff/blob/main/scripts/ci/typecheck-lanes.ts) utility groups packages into lanes and emits shell commands for each. This design lets GitHub Actions spin up multiple runners simultaneously.

The concept originates from the testing documentation and CI workflow, where lanes prevent the "long tail" of monorepo type-checking.

## Where the Typecheck Logic Lives

### Per-Workspace Typecheck Scripts

Every package defines its own type-checking entry point in [`package.json`](https://github.com/CodebuffAI/freebuff/blob/main/package.json):

```json
// main/sdk/package.json
{
  "scripts": {
    "typecheck": "tsc --noEmit -p ."
  }
}

```

```json
// main/packages/llm-providers/package.json
{
  "scripts": {
    "typecheck": "tsc --noEmit -p ."
  }
}

```

```json
// main/evals/package.json
{
  "scripts": {
    "typecheck": "tsc --noEmit -p ."
  }
}

```

Each workspace has an isolated [`tsconfig.json`](https://github.com/CodebuffAI/freebuff/blob/main/tsconfig.json). This isolation is critical—`tsc` never crosses workspace boundaries, so compilation units stay small and cacheable.

### The Lane Generator: [`scripts/ci/typecheck-lanes.ts`](https://github.com/CodebuffAI/freebuff/blob/main/scripts/ci/typecheck-lanes.ts)

This TypeScript script partitions workspaces into lanes and outputs parallel execution commands. The CI workflow references it directly, and you can run it locally to replicate CI behavior.

The script performs three operations:

1. **Discovers workspaces** by reading the root [`package.json`](https://github.com/CodebuffAI/freebuff/blob/main/package.json) workspace list.
2. **Groups workspaces** into balanced lanes (typically 3-5 lanes depending on runner count).
3. **Emits shell commands** like `bun run -C packages/code-map typecheck &`.

From [`docs/testing.md`](https://github.com/CodebuffAI/freebuff/blob/main/docs/testing.md) (around line 80), the lane output resembles:

```bash
lane-0: bun run -C packages/code-map typecheck &
lane-1: bun run -C packages/agent-runtime typecheck &
lane-2: bun run -C packages/llm-providers typecheck &

```

Each line runs in the background, and the controlling process waits for all to complete.

### CI Workflow Integration

The [`.github/workflows/ci.yml`](https://github.com/CodebuffAI/freebuff/blob/main/.github/workflows/ci.yml) job orchestrates the lanes:

- Checks out the repository.
- Installs dependencies with `bun install --frozen-lockfile`.
- Builds the SDK and binary (prerequisites for type-checking).
- Executes `bun run scripts/ci/typecheck-lanes.ts` to run all lanes in parallel across GitHub-hosted runners.

The workflow uses matrix strategy or job parallelism to assign each lane to a separate runner instance.

## Running Typecheck Lanes Locally

### Full Parallel Run (CI-Equivalent)

```bash

# Install dependencies once

bun install --frozen-lockfile

# Execute the lane splitter (spawns background jobs automatically)

bun run scripts/ci/typecheck-lanes.ts

```

The script handles `wait` internally. Exit codes propagate—if any lane fails, the script exits non-zero.

### Dry-Run to Preview Lane Distribution

```bash
node scripts/ci/typecheck-lanes.ts --dry-run

```

Outputs the command list without executing, useful for debugging lane balancing.

### Single Workspace (No Parallelism)

```bash
cd packages/llm-providers
bun run typecheck

```

Direct `tsc` invocation for focused debugging. Bypasses the lane system entirely.

### Manual Parallel Execution

```bash

# Background each lane explicitly

bun run -C packages/code-map typecheck &
bun run -C packages/agent-runtime typecheck &
bun run -C packages/llm-providers typecheck &

wait  # Blocks until all background jobs finish

```

Useful for shell scripts or CI systems without native matrix support.

## Why This Architecture Is Fast

| Factor | Mechanism | Source Location |
|--------|-----------|---------------|
| **Isolated compilation** | Each workspace has its own [`tsconfig.json`](https://github.com/CodebuffAI/freebuff/blob/main/tsconfig.json) and `tsc --noEmit -p .` invocation | `*/package.json` `"typecheck"` scripts |
| **Parallel processes** | Lane script spawns multiple `bun run typecheck` jobs concurrently | [`scripts/ci/typecheck-lanes.ts`](https://github.com/CodebuffAI/freebuff/blob/main/scripts/ci/typecheck-lanes.ts) |
| **Distributed runners** | GitHub Actions runs each lane on separate VMs | [`.github/workflows/ci.yml`](https://github.com/CodebuffAI/freebuff/blob/main/.github/workflows/ci.yml) |
| **Incremental caching** | `node_modules` and bun lockfile cached between CI runs | CI workflow cache configuration |

The [`docs/testing.md`](https://github.com/CodebuffAI/freebuff/blob/main/docs/testing.md) timing table documents ~3 minute total type-check duration versus ~15 minutes for serial execution.

## Configuration Deep-Dive

### Adjusting Lane Count

The lane script accepts environment variables or CLI flags to control parallelism:

```bash

# Force 4 lanes regardless of default detection

LANES=4 bun run scripts/ci/typecheck-lanes.ts

```

Check [`scripts/ci/typecheck-lanes.ts`](https://github.com/CodebuffAI/freebuff/blob/main/scripts/ci/typecheck-lanes.ts) source for the exact parameter interface.

### Workspace Discovery

The script reads workspace definitions from the root [`package.json`](https://github.com/CodebuffAI/freebuff/blob/main/package.json):

```json
{
  "workspaces": [
    "sdk",
    "packages/*",
    "evals"
  ]
}

```

New packages are automatically included in lane distribution—no manual registration required.

### TypeScript Configuration per Lane

Each workspace's [`tsconfig.json`](https://github.com/CodebuffAI/freebuff/blob/main/tsconfig.json) controls its compilation scope. Key settings for speed:

```json
{
  "compilerOptions": {
    "noEmit": true,
    "skipLibCheck": true,
    "incremental": true
  }
}

```

`incremental` enables `.tsbuildinfo` caching. `skipLibCheck` avoids re-checking `node_modules` types on every run.

## Debugging Lane Failures

When a lane fails, the error output includes the workspace path:

```bash

# Example failure

packages/agent-runtime typecheck: src/runtime.ts:42:15 - error TS2345

```

Fix the type error, then re-run that specific workspace:

```bash
bun run -C packages/agent-runtime typecheck

```

No need to re-execute all lanes during iteration.

## Summary

- **Per-workspace scripts** (`"typecheck": "tsc --noEmit -p ."`) isolate compilation units.
- **[`scripts/ci/typecheck-lanes.ts`](https://github.com/CodebuffAI/freebuff/blob/main/scripts/ci/typecheck-lanes.ts)** distributes workloads across parallel lanes.
- **[`.github/workflows/ci.yml`](https://github.com/CodebuffAI/freebuff/blob/main/.github/workflows/ci.yml)** orchestrates lane execution on CI runners.
- **Local execution** via `bun run scripts/ci/typecheck-lanes.ts` matches CI behavior exactly.
- **Performance gains** come from isolated `tsc` processes, parallel runners, and incremental caching.

## Frequently Asked Questions

### How do I add a new package to the typecheck lanes?

Create a [`package.json`](https://github.com/CodebuffAI/freebuff/blob/main/package.json) with a `"typecheck": "tsc --noEmit -p ."` script and a valid [`tsconfig.json`](https://github.com/CodebuffAI/freebuff/blob/main/tsconfig.json). The lane script automatically discovers it via the root workspace glob pattern—no additional configuration needed.

### Can I run typecheck lanes with npm or pnpm instead of bun?

The scripts reference `bun` explicitly. To use another package manager, modify [`scripts/ci/typecheck-lanes.ts`](https://github.com/CodebuffAI/freebuff/blob/main/scripts/ci/typecheck-lanes.ts) to emit `npm run` or `pnpm run` commands, and adjust [`.github/workflows/ci.yml`](https://github.com/CodebuffAI/freebuff/blob/main/.github/workflows/ci.yml) to use that manager's install and execution commands.

### Why does my local lane run take longer than CI?

CI benefits from distributed runners—each lane executes on a separate VM. Locally, all lanes share your machine's CPU. Use `LANES=2` or `LANES=4` to match your core count, or run single-workspace checks during development.

### Where is the timing data for lane performance?

The [`docs/testing.md`](https://github.com/CodebuffAI/freebuff/blob/main/docs/testing.md) file contains a timing table comparing serial versus parallel type-check execution. The ~3 minute figure assumes standard GitHub Actions runner configuration with 3-4 lanes.