Skip to content

05_17_04: Monorepo Tools - AudioLab Workspace Management

Executive Summary

Tools for managing AudioLab's component-based monorepo. Handles package organization, dependency tracking, selective builds, and coordinated releases across L0-L3 architecture layers.

Key Features: - Multi-package workspace management - Dependency graph and validation - Selective builds (affected packages only) - Topological build ordering - Layer-based organization (L0 → L1 → L2 → L3) - Change impact analysis


Architecture

Workspace Structure

AudioLab Monorepo
├── L0: Kernels (Atomic DSP operations)
├── L1: Atoms (Basic processing blocks)
│   └── depends on: L0
├── L2: Cells (Complex components)
│   └── depends on: L0, L1
├── L3: Engines (Complete processors)
│   └── depends on: L0, L1, L2
└── Core Libraries (Foundation)

Dependency Rules

  • L1 can depend on L0 only
  • L2 can depend on L0, L1
  • L3 can depend on L0, L1, L2
  • No circular dependencies
  • Core libraries are leaf nodes

Packages

L0 - Kernels

  • Path: 3 - COMPONENTS/05_MODULES/05_04_KERNELS_L0
  • Type: Library
  • Dependencies: None
  • Description: Atomic DSP operations

L1 - Atoms

  • Path: 3 - COMPONENTS/05_MODULES/05_07_ATOMS_L1
  • Type: Library
  • Dependencies: kernels
  • Description: Basic processing blocks

L2 - Cells

  • Path: 3 - COMPONENTS/05_MODULES/05_10_CELLS_L2
  • Type: Library
  • Dependencies: atoms, kernels
  • Description: Complex components

L3 - Engines

  • Path: 3 - COMPONENTS/05_MODULES/05_13_ENGINES_L3
  • Type: Library
  • Dependencies: cells, atoms, kernels
  • Description: Complete processors

Core Libraries

  • type-system: Type system and parameter types
  • core-interfaces: Core interfaces and base classes
  • math-primitives: Math and DSP primitives
  • buffer-management: Buffer and memory management

Usage

List Packages

python monorepo_manager.py list

Output:

AudioLab Monorepo Packages
============================================================

L0:
  - kernels            [library]

L1:
  - atoms              [library] (deps: kernels)

L2:
  - cells              [library] (deps: atoms, kernels)

L3:
  - engines            [library] (deps: cells, atoms, kernels)

other:
  - core-interfaces    [library]
  - type-system        [library]
  - math-primitives    [library]
  - buffer-management  [library]

Check Status

python monorepo_manager.py status

Output:

Package Status
============================================================

✓ kernels            3 - COMPONENTS/05_MODULES/05_04_KERNELS_L0
✓ atoms              3 - COMPONENTS/05_MODULES/05_07_ATOMS_L1
✓ cells              3 - COMPONENTS/05_MODULES/05_10_CELLS_L2
✓ engines            3 - COMPONENTS/05_MODULES/05_13_ENGINES_L3
✓ core-interfaces    2 - FOUNDATION/04_CORE/04_01_core_interfaces

Generate Dependency Graph

python monorepo_manager.py graph

Generates dependency-graph.dot:

digraph AudioLab {
  rankdir=LR;
  node [shape=box];

  subgraph cluster_L0 {
    label="L0";
    "kernels";
  }

  subgraph cluster_L1 {
    label="L1";
    "atoms";
  }

  "atoms" -> "kernels";
  "cells" -> "atoms";
  "cells" -> "kernels";
  "engines" -> "cells";
  "engines" -> "atoms";
  "engines" -> "kernels";
}

Convert to SVG:

dot -Tsvg dependency-graph.dot -o dependency-graph.svg

Find Affected Packages

# Show packages affected by changes since main
python monorepo_manager.py affected --base main

Output:

Changed packages: atoms, kernels
Affected packages: atoms, cells, engines, kernels

Impact: If kernels changes, all L1-L3 must be rebuilt.

Build Affected Packages Only

# Build only packages affected by changes
python monorepo_manager.py build --affected

Output:

Building 4 affected packages: atoms, cells, engines, kernels
Build order: kernels → atoms → cells → engines

Building kernels...
  cd 3 - COMPONENTS/05_MODULES/05_04_KERNELS_L0 && cmake --build build
Building atoms...
  cd 3 - COMPONENTS/05_MODULES/05_07_ATOMS_L1 && cmake --build build
...

Build Specific Packages

# Build specific packages in correct order
python monorepo_manager.py build atoms cells

Selective Builds

Strategy

Affected-only builds: Only build packages that: 1. Have source changes 2. Depend on packages with changes (downstream)

Change Detection

# Detect changed files
git diff --name-only main

# Map files → packages
changed_packages = []
for file in changed_files:
    if file.startswith('3 - COMPONENTS/05_MODULES/05_07_ATOMS_L1'):
        changed_packages.append('atoms')

# Find affected packages (downstream dependencies)
affected = get_affected_packages(changed_packages)

Build Order

Topological sort based on dependency graph:

kernels (no deps)
atoms (depends on kernels)
cells (depends on atoms, kernels)
engines (depends on cells, atoms, kernels)


Dependency Management

Validation

On commit:

# Check for circular dependencies
python monorepo_manager.py validate-deps

Enforced rules: - ✅ L1 → L0 only - ✅ L2 → L0, L1 - ✅ L3 → L0, L1, L2 - ❌ No L0 → L1 (reverse dependency) - ❌ No circular dependencies

Graph Analysis

# Build dependency graph
graph = {
    'atoms': {'kernels'},
    'cells': {'atoms', 'kernels'},
    'engines': {'cells', 'atoms', 'kernels'},
}

# Find affected packages
def get_affected(changed):
    affected = set(changed)
    changed_flag = True
    while changed_flag:
        changed_flag = False
        for pkg, deps in graph.items():
            if pkg not in affected and deps.intersection(affected):
                affected.add(pkg)
                changed_flag = True
    return affected

CI/CD Integration

GitHub Actions

- name: Detect affected packages
  run: |
    python monorepo_manager.py affected > affected.txt

- name: Build affected only
  run: |
    python monorepo_manager.py build --affected

- name: Test affected only
  run: |
    for pkg in $(cat affected.txt); do
      cd $pkg && ctest
    done

Benefits

  • Faster CI: Only build/test what changed
  • Parallel builds: Independent packages build in parallel
  • Efficient caching: Cache by package + commit hash

Versioning Strategy

Coordinated Versioning

All packages share same version:

kernels:  2.1.0
atoms:    2.1.0
cells:    2.1.0
engines:  2.1.0

Benefits: - Simplified dependency management - Clear compatibility guarantees - Easier releases

On release: - All packages bump version together - Single changelog for all changes


Best Practices

DO ✅

  • Keep packages small and focused
  • Minimize cross-package dependencies
  • Use interfaces for loose coupling
  • Build in topological order
  • Test affected packages on changes
  • Cache build artifacts
  • Use selective builds in CI/CD

DON'T ❌

  • Create circular dependencies
  • Skip dependency validation
  • Build all packages on small changes
  • Mix concerns across packages
  • Ignore layer boundaries (L0-L3)

Performance

Selective Build Savings

Example scenario: - Change in kernels package - Traditional: Build all 4 layers (100%) - Selective: Build affected only (100% - kernels changed)

Example scenario 2: - Change in engines package only - Traditional: Build all 4 layers (100%) - Selective: Build engines only (25%) - Savings: 75% build time

Parallel Builds

Independent packages build in parallel:

kernels (start)
atoms + core-libs (parallel)
cells (depends on atoms)
engines (depends on cells)


Configuration Files

  • monorepo_config.yaml: Workspace configuration
  • monorepo_manager.py: Management CLI tool

Integration

With Build System

# CMake integration
cmake -B build
python monorepo_manager.py build --affected

With Version Control

# Find affected by changes
git diff main...HEAD
python monorepo_manager.py affected

With CI/CD

  • Detect affected packages
  • Build/test affected only
  • Cache per package
  • Parallel execution

Part of AudioLab Version Control System (05_17_VERSION_CONTROL)