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

SimStudio AI enforces monorepo boundaries through a dedicated TypeScript script (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. 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:

// 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), 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 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:

// 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:

// 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 Core validation script that scans packages/ for forbidden imports.
AGENTS.md Architecture documentation defining the "apps → packages only" rule.
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 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 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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →