Agentic Loop Build Matrix#

The agentic loop cycles through a build matrix — a set of build configurations that each define a preset, a sanitizer, a build directory, and an optional test command. This ensures the loop doesn’t only test one build type but regularly exercises ASAN, TSan, profile, and release builds across both Windows (Stevedore) and Linux (Rancher Desktop).

Configuration#

The build matrix is defined in the project’s AgenticLoop.config.json under the buildMatrix key. Each platform (windows, linux) has an array of entries:

{
  "buildMatrix": {
    "windows": [
      {
        "name": "clangcl-debug",
        "sanitizer": "asan",
        "buildDir": "build-clangcl-debug",
        "buildType": "Debug",
        "testCommand": "ctest --test-dir build-clangcl-debug --output-on-failure -C Debug"
      },
      {
        "name": "clangcl-profile",
        "sanitizer": "none",
        "buildDir": "build-clangcl-profile",
        "buildType": "RelWithDebInfo",
        "testCommand": "ctest --test-dir build-clangcl-profile --output-on-failure -C RelWithDebInfo"
      },
      {
        "name": "clangcl-release",
        "sanitizer": "none",
        "buildDir": "build-clangcl-release",
        "buildType": "Release",
        "testCommand": null
      }
    ],
    "linux": [
      {
        "name": "linux-debug-asan-clang",
        "sanitizer": "asan",
        "buildDir": "build-asan-clang",
        "buildType": "Debug",
        "testCommand": "ctest --test-dir build-asan-clang --output-on-failure -C Debug"
      },
      {
        "name": "linux-debug-tsan-clang",
        "sanitizer": "tsan",
        "buildDir": "build-tsan-clang",
        "buildType": "Debug",
        "testCommand": "ctest --test-dir build-tsan-clang --output-on-failure -C Debug"
      },
      {
        "name": "linux-profile-clang",
        "sanitizer": "none",
        "buildDir": "build-profile-clang",
        "buildType": "RelWithDebInfo",
        "testCommand": "ctest --test-dir build-profile-clang --output-on-failure -C RelWithDebInfo"
      },
      {
        "name": "linux-release-clang",
        "sanitizer": "none",
        "buildDir": "build-release-clang",
        "buildType": "Release",
        "testCommand": null
      }
    ]
  }
}

Entry Fields#

Field

Type

Description

name

string

Build configuration name (maps to a CMake preset or Build-Windows.config.psd1 entry)

sanitizer

string

asan, tsan, or none — controls env vars set before tests

buildDir

string

Build directory (used for Linux --build-dir and for ctest --test-dir)

buildType

string

CMake build type (Debug, RelWithDebInfo, Release) — used for ctest -C

testCommand

string|null

Shell command for tests, or null to skip tests for this config

Backward Compatibility#

The module also accepts the legacy buildConfigurations format (arrays of strings). Strings are normalized to entries with sanitizer: "none" and no per-config test command. This means existing configs keep working without changes.

Sanitizer-Aware Test Execution#

When a matrix entry has sanitizer: "asan" or sanitizer: "tsan", the module automatically sets the appropriate environment variables before running the test command, then restores the original environment:

Sanitizer

Env Var

Value

asan

ASAN_OPTIONS

detect_leaks=1:halt_on_error=1:abort_on_error=1:allocator_may_return_null=1

tsan

TSAN_OPTIONS

halt_on_error=1:abort_on_error=1:second_deadlock_stack=1

none

No env vars set

This ensures sanitizer-instrumented tests actually catch memory errors and data races, rather than silently passing because the sanitizer was not configured to halt on error.

Windows ASAN Note#

On Windows, the ASAN debug binary needs clang_rt.asan_dynamic-x86_64.dll next to the executable. The build copies it automatically. The ASAN_OPTIONS env var must use a relative log_path — an absolute C:\... path breaks ASAN option parsing at the drive-letter colon. The module’s default ASAN_OPTIONS does not set log_path, avoiding this issue.

Full Matrix Sweep#

In addition to cycling one config per build trigger, the loop supports a full matrix sweep — every N iterations, it runs ALL configs in sequence instead of just one. This ensures all configs are exercised regularly, not just the one that happens to be next in the cycle.

Enable it in the config:

{
  "intervals": {
    "fullMatrixEveryNIterations": 5
  }
}
  • 0 = disabled (cycle one config per build trigger, the default)

  • 5 = every 5th iteration, run all configs in sequence

Build Matrix Cycling#

Without a full sweep, the loop cycles through the matrix entries one at a time. After every buildEveryNTasks completed tasks, the next entry in the matrix is used:

Build #

Windows

Linux

1

clangcl-debug (ASAN)

linux-debug-asan-clang (ASAN)

2

clangcl-profile

linux-debug-tsan-clang (TSan)

3

clangcl-release

linux-profile-clang

4

clangcl-debug (cycles back)

linux-release-clang

5

clangcl-profile

linux-debug-asan-clang (cycles back)

This ensures that over multiple build triggers, every config is exercised.

Cross-Platform Support#

The same config file works on both Windows and Linux. The loop automatically selects the windows or linux matrix based on the platform it’s running on:

  • Windows: builds go through Build-Windows-Container.ps1 (Stevedore container). The config name maps to a preset via Build-Windows.config.psd1.

  • Linux: builds go through cmake-configure-build.sh (Rancher Desktop container or native). The config name is the CMake preset, and the buildDir field overrides the preset’s binaryDir to ensure each config has its own build directory.

Module API#

PowerShell (WindowsAgenticLoop.Common.psm1)#

The PowerShell functions implementing this behaviour (Resolve-BuildMatrixEntry, Get-SanitizerEnvVars, Invoke-SanitizerTestCommand, and the high-level Invoke-AgenticLoop) are documented in the module API reference, windows-agentic-loop.md.

Bash (agentic-loop.sh)#

Function

Purpose

get_sanitizer_env_vars <sanitizer>

Echo env var assignment string for the sanitizer

invoke_sanitizer_tests <cmd> <sanitizer>

Set env vars, run tests, restore env

resolve_build_matrix_entry <config_json> <index> <platform>

Parse a matrix entry to globals MATRIX_*

count_build_matrix <config_json> <platform>

Count matrix entries for a platform

get_matrix_entry_name <config_json> <index> <platform>

Get entry name by index (backward-compatible)

run_agentic_loop <config_json> <repo_root> <platform>

Full loop with build matrix, sanitizer tests, quality gates