# How SimStudio AI Enforces Monorepo Boundaries: Inside the CI Validation Script

> Learn how SimStudio AI enforces monorepo boundaries using its CI validation script. Discover how forbidden import patterns maintain a strict one-way dependency flow.

- Repository: [Sim/sim](https://github.com/simstudioai/sim)
- Tags: internals
- Published: 2026-05-02

---

**SimStudio AI enforces monorepo boundaries through a dedicated TypeScript script ([`scripts/check-monorepo-boundaries.ts`](https://github.com/simstudioai/sim/blob/main/scripts/check-monorepo-boundaries.ts)) that scans for forbidden import patterns to ensure packages never depend on application code, maintaining a strict one-way dependency flow.**

SimStudio AI (simstudioai/sim) maintains a clean monorepo architecture by preventing cross-contamination between shared libraries and application code. The repository uses an automated boundary-checking mechanism that integrates into CI to enforce strict import rules. This **monorepo boundary enforcement** ensures that packages remain pure and reusable while apps retain the flexibility to consume shared utilities.

## The Boundary Enforcement Script

The core enforcement logic lives in [`scripts/check-monorepo-boundaries.ts`](https://github.com/simstudioai/sim/blob/main/scripts/check-monorepo-boundaries.ts). This script walks every source file inside the `packages/` directory and validates import statements against three specific forbidden patterns.

The script checks for these prohibited import types:

- **`@/` path aliases** — Blocks packages from using the app-specific alias (e.g., `import foo from '@/components'`).
- **Relative imports into apps** — Prevents climbing up to `apps/` via relative paths (e.g., `../../apps/sim/lib`).
- **Bare `apps/` imports** — Disallows absolute references to app directories (e.g., `import foo from 'apps/sim/lib'`).

## How the Script Works

The script uses three regular expressions to detect violations as it scans each line of code in the packages directory:

```typescript
// scripts/check-monorepo-boundaries.ts
const FORBIDDEN_PATTERNS = [
  { pattern: /from\s+['"]@\/(?!\*)/g, description: "'@/' path alias (apps/sim‑only)" },
  { pattern: /from\s+['"]\.\.\/\.\.\/apps\//g, description: 'relative import into apps/' },
  { pattern: /from\s+['"]apps\//g, description: "bare 'apps/' import" },
];

for (const file of files) {
  const lines = (await readFile(file, 'utf8')).split('\n');
  lines.forEach((line, i) => {
    FORBIDDEN_PATTERNS.forEach(({ pattern, description }) => {
      pattern.lastIndex = 0;
      if (pattern.test(line)) {
        offenders.push({
          file: path.relative(ROOT, file),
          line: i + 1,
          description,
          snippet: line.trim(),
        });
      }
    });
  });
}

if (offenders.length) {
  console.error('❌ Monorepo boundary violations found:');
  offenders.forEach(o => console.error(`  ${o.file}:${o.line} — ${o.description}\n    ${o.snippet}`));
  process.exit(1);
}

```

When violations are detected, the script outputs the specific file path, line number, and the offending code snippet before exiting with status code 1, causing the CI build to fail.

## One-Way Dependency Rule

The enforcement creates a **strict architectural boundary**:

- **Packages → Apps**: *Never* allowed. Shared libraries cannot import from the UI or server applications.
- **Apps → Packages**: Fully allowed. Applications can freely import utilities from `@sim/logger`, `@sim/utils`, or other shared packages.

This rule is documented in the repository's architecture guide ([`AGENTS.md`](https://github.com/simstudioai/sim/blob/main/AGENTS.md)), which states: "`apps/* → packages/*` only. Packages never import from `apps/*`." The boundary-checking script serves as the automated enforcement of this documented policy.

## CI Integration and Developer Workflow

The boundary check integrates into the development lifecycle at multiple points:

**CI Pipeline Execution**
The script runs during CI via `bun run check:api-validation` or a dedicated lint step. If any violation is found, the job aborts immediately, preventing boundary breaches from reaching the main branch.

**Local Development**
Developers can run the script locally using [`./scripts/check-monorepo-boundaries.ts`](https://github.com/simstudioai/sim/blob/main/./scripts/check-monorepo-boundaries.ts) or `bun run check-monorepo-boundaries` to catch issues before committing.

**Pre-commit Hooks**
Projects can optionally configure a `husky` hook to run this script automatically, providing immediate feedback during the commit process.

## Code Examples

The following demonstrates a **legal** import pattern where the application consumes a shared package:

```typescript
// apps/sim/app/page.tsx
import { createLogger } from '@sim/logger';   // ✅ allowed: UI imports shared logger

```

Conversely, this snippet shows an **illegal** import that the script catches:

```typescript
// packages/utils/src/date.ts
import { formatDate } from '@/components/date'; // ❌ prohibited: packages cannot use @/ alias

```

Running the check locally on the violating code produces:

```

❌ Monorepo boundary violations found:
  packages/utils/src/date.ts:12 — '@/’ path alias (apps/sim-only)
    import { formatDate } from '@/components/date';

```

## Key Files Supporting Monorepo Boundaries

| File | Role |
|------|------|
| [`scripts/check-monorepo-boundaries.ts`](https://github.com/simstudioai/sim/blob/main/scripts/check-monorepo-boundaries.ts) | Core validation script that scans `packages/` for forbidden imports. |
| [`AGENTS.md`](https://github.com/simstudioai/sim/blob/main/AGENTS.md) | Architecture documentation defining the "apps → packages only" rule. |
| [`package.json`](https://github.com/simstudioai/sim/blob/main/package.json) | Defines the CI entry point for running boundary checks. |
| `packages/**` | Shared libraries that must remain independent of application code. |
| `apps/**` | Next.js UI and realtime server with unrestricted access to packages. |

## Summary

- **Monorepo boundary enforcement** in SimStudio AI relies on [`scripts/check-monorepo-boundaries.ts`](https://github.com/simstudioai/sim/blob/main/scripts/check-monorepo-boundaries.ts) to scan for forbidden import patterns.
- The script blocks three specific patterns: `@/` aliases, relative paths to `apps/`, and bare `apps/` imports.
- A **strict one-way dependency rule** allows apps to import packages but prohibits packages from importing apps.
- Integration occurs in CI pipelines via `bun run` commands and can be run locally for immediate feedback.
- Violations cause immediate build failures with detailed file and line number reporting.

## Frequently Asked Questions

### What is monorepo boundary enforcement?

Monorepo boundary enforcement is an architectural practice that prevents code coupling between different layers of a repository. In SimStudio AI, it ensures that shared packages remain independent of application-specific code, maintaining clean separation and preventing circular dependencies that make codebases harder to maintain.

### How does SimStudio AI prevent packages from importing app code?

SimStudio AI uses a TypeScript script located at [`scripts/check-monorepo-boundaries.ts`](https://github.com/simstudioai/sim/blob/main/scripts/check-monorepo-boundaries.ts) that scans all files in the `packages/` directory. The script tests every import statement against three regular expressions that match `@/` aliases, relative paths traversing into `apps/`, and bare `apps/` imports, failing the build if any are found.

### Can I run the boundary check locally before committing?

Yes. Developers can execute the script locally using [`./scripts/check-monorepo-boundaries.ts`](https://github.com/simstudioai/sim/blob/main/./scripts/check-monorepo-boundaries.ts) or through the npm script `bun run check-monorepo-boundaries`. This allows you to identify and fix boundary violations before pushing code to CI, preventing build failures.

### Where is the monorepo boundary rule documented?

The boundary rule is documented in [`AGENTS.md`](https://github.com/simstudioai/sim/blob/main/AGENTS.md) within the architecture section, which explicitly states that dependencies should flow `apps/* → packages/*` only. This documentation serves as the human-readable specification that the automated script enforces programmatically.