Agentic Loop Module (WindowsAgenticLoop.Common.psm1)#

A reusable PowerShell module for the planner/executor agentic loop pattern. Lets any project set up an autonomous coding loop with:

  1. A planner (expensive model, e.g. Claude Fable 5 or GLM 5.2) that analyzes the codebase and writes tasks to BACKLOG.md.

  2. An executor (cheaper model, e.g. Claude Sonnet or DeepSeek v4 Flash) that drains the queue one task at a time, building and testing as it goes.

  3. Builds, tests, and quality gates on configurable intervals, cycling through different build configurations.

  4. A build fixer: when a periodic build fails, the executor-tier model is dispatched with the build log tail to fix it, and the loop stops after N consecutive unfixable failures.

Engines#

Two agent CLI backends are supported; select via config engine, $env:AGENTIC_ENGINE, or the -Engine parameter of Invoke-AgenticLoop:

Engine

Invocation

Role prompts

Permissions

opencode

opencode run --agent <role> --model <model>

.opencode/agents/<role>.md (resolved by opencode)

Configured in opencode.json

claude

claude -p --model <model> (Claude Code CLI)

--append-system-prompt-file from config engines.claude.<role>PromptFile

Planner sandboxed via --allowed-tools (e.g. Read Glob Grep Edit(BACKLOG.md)); executor uses permissionMode (default bypassPermissions — intended for trusted repos/sandboxes)

For claude, engines.claude.plannerFallbackModel maps to --fallback-model so an overloaded planner model (e.g. claude-fable-5) falls back automatically (e.g. to claude-opus-4-8).

Model resolution precedence: $env:AGENTIC_PLANNER_MODEL / $env:AGENTIC_EXECUTOR_MODEL > engines.<engine>.plannerModel/executorModel

legacy models.planner/executor.

Agent invocations retry with linear backoff (agentRetries × agentRetryDelaySeconds) and honor per-role timeouts (plannerTimeoutSeconds / executorTimeoutSeconds, falling back to timeoutSeconds; 0 = no timeout).

Prerequisites#

  • OpenCode CLI and/or Claude Code CLI installed and authenticated

  • PowerShell 7+ (cross-platform)

  • A BACKLOG.md file in the repository root (task format: - [ ] Title for actionable tasks, - [b] Title for blocked/parked ones the executor must skip)

  • jq on Linux (for config parsing in the Bash equivalent, linux/scripts/lib/agentic-loop.sh, which mirrors this module’s engine support)

Installation#

Place the module in your repository’s module path, then import it:

# If using Kataglyphis-ContainerHub as a submodule:
$modulePath = Resolve-Path 'ExternalLib/Kataglyphis-ContainerHub/windows/scripts/modules/WindowsAgenticLoop.Common.psm1'
Import-Module $modulePath -Force

Do not vendor a copy into the consumer repo — resolve it ContainerHub-first (e.g. BeschleunigerBallett’s Resolve-BuildModule.ps1); vendored duplicates are exactly the drift the 2026-08-02 dedup pass removed.

Quick Start#

Create a Run-AgenticLoop.ps1 script in your project:

# scripts/agentic-loop/Run-AgenticLoop.ps1
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
$configPath = Join-Path $PSScriptRoot 'AgenticLoop.config.json'
$config = Get-Content $configPath -Raw | ConvertFrom-Json

Import-Module (Join-Path $repoRoot 'ExternalLib/Kataglyphis-ContainerHub/windows/scripts/modules/WindowsAgenticLoop.Common.psm1') -Force

Initialize-AgenticLoop -ConfigPath $configPath -RepoRoot $repoRoot

Invoke-AgenticLoop -Config $config -RepoRoot $repoRoot

Complete-AgenticLoop

That is the whole wrapper: build configs are selected from the config’s platform-appropriate buildMatrix (legacy buildConfigurations fallback) and the planner / refactor-planner / executor task prompts default to the shared prompt files (see Default Task Prompts). Pass -BuildConfigs / -PlannerPrompt / -ExecutorPrompt / -RefactorPlannerPrompt only to override.

Module API#

Initialization & Lifecycle#

Function

Purpose

Initialize-AgenticLoop -ConfigPath <path> [-DryRun]

Set up logging, platform detection, global trap. Returns context hashtable.

Complete-AgenticLoop [-Iteration <int>] [-TasksCompleted <int>] [-ExitCode <int>]

Write final summary and exit with the accumulated exit code.

Logging#

Function

Purpose

Write-AgenticLog -Message <string> [-Level <string>]

Timestamped log to file + console.

Write-AgenticSection -Title <string>

Decorated section header.

Get-AgenticLogFile

Returns the current log file path.

Platform Detection#

Function

Purpose

Get-AgenticPlatform

Returns 'windows' or 'linux'. Works on all supported PowerShell versions.

Test-IsWindows

$true on Windows, $false otherwise.

Agent Invocation#

Function

Purpose

Resolve-AgenticEngine -Config <object> [-RepoRoot <path>] [-EngineOverride <string>]

Resolve engine + models + prompt files + timeouts into a flat hashtable.

Invoke-AgenticAgent -Role <planner|executor|fixer> -Message <string> -EngineConfig <hashtable>

Engine dispatcher with retry + linear backoff. Returns $true on success.

Invoke-OpenCode -Agent <string> -Model <string> -Message <string>

Passes message via stdin to opencode run.

Invoke-ClaudeCode -Role <string> -Model <string> -Message <string> -EngineConfig <hashtable>

Headless claude -p run with role system prompt, tool sandbox, and fallback model.

Invoke-AgentProcess -Executable <string> -ArgumentList <string[]> -Message <string> [-TimeoutSeconds <int>]

Low-level process runner (stdin prompt, streamed stdout/stderr, timeout).

Invoke-BuildFixer -ConfigurationName <string> -EngineConfig <hashtable>

Dispatch the fixer role with the tail of the loop log after a build failure.

Get-AgenticConfigValue -Object <object> -Name <string> [-Default <object>]

StrictMode-safe config lookup (hashtable or PSCustomObject).

Get-AgentTimeoutForRole -EngineConfig <hashtable> -Role <string>

Per-role timeout resolution.

Default Task Prompts#

The per-phase TASK prompts (the message piped to each agent invocation — not the engine role/system prompts, which stay project-owned) are single-sourced as Markdown files in this repository:

shared/agentic-loop/prompts/planner.md
shared/agentic-loop/prompts/refactor-planner.md
shared/agentic-loop/prompts/executor.md

Function

Purpose

Get-AgenticDefaultPrompt -Role <planner|refactor-planner|executor>

Read the shared default prompt for a role (throws if the file is missing).

Invoke-AgenticLoop’s -PlannerPrompt, -RefactorPlannerPrompt, and -ExecutorPrompt parameters are optional and default to these files, so project wrappers do not need to hard-code prompt text. The Bash library’s default_planner_prompt / default_refactor_planner_prompt / default_executor_prompt (in linux/scripts/lib/agentic-loop.sh) read the same files, keeping both platforms in lockstep — edit the prompt file once and both loops pick it up.

Utility#

Function

Purpose

Get-UncheckedTaskCount [-BacklogPath <path>]

Count actionable - [ ] lines in BACKLOG.md (blocked - [b] entries excluded).

Get-BlockedTaskCount [-BacklogPath <path>]

Count blocked - [b] lines in BACKLOG.md.

Get-UsageLimitWaitSeconds -Output <string>

Detect a Claude usage/session-limit failure and return seconds to sleep until the stated reset (0 = not a limit failure).

Invoke-GitAutoCommit -Message <string> [-RepoRoot <path>] [-Enabled <bool>]

git add -A && git commit with a message. Bash counterpart: invoke_git_auto_commit <message> [repo_root] [enabled].

Build / Test / Quality Wrappers#

Function

Purpose

Invoke-BuildCommand -Command <string> [-Configuration <string>]

Execute a build command, log output, return $true/$false.

Invoke-TestCommand -Command <string> [-RepoRoot <path>]

Execute a test command, log output, return $true/$false.

Invoke-QualityCommand -Command <string> [-RepoRoot <path>]

Execute a quality gate, log output.

Build Matrix & Sanitizer-Aware Testing#

Function

Purpose

Resolve-BuildMatrixEntry -Entry <object>

Normalize a string or JSON object to a hashtable with Name, Sanitizer, TestCommand, BuildDir, BuildType.

Get-SanitizerEnvVars -Sanitizer <string>

Return a hashtable of env vars for the given sanitizer (asan, tsan, or none).

Invoke-SanitizerTestCommand -Command <string> -Sanitizer <string> -RepoRoot <string>

Set sanitizer env vars, run tests, restore env.

See agentic-loop-build-matrix.md for the full build matrix documentation.

High-Level Loop#

Function

Purpose

Invoke-AgenticLoop -Config <object> [-PlannerPrompt <string>] [-ExecutorPrompt <string>] [-BuildConfigs <array>] [...]

Full planner/executor loop with build matrix cycling, sanitizer-aware tests, full matrix sweeps, and quality gates.

Invoke-AgenticLoop Parameters#

Parameter

Type

Description

Config

[object]

Configuration object (from JSON: models, intervals, build, git, logging)

Engine

[string]

Engine override (claude / opencode); empty = config engine / $env:AGENTIC_ENGINE

PlannerPrompt

[string]

Optional prompt message for the planner agent; defaults to the shared planner.md (see Default Task Prompts)

RefactorPlannerPrompt

[string]

Optional prompt for refactor-focus iterations; defaults to the shared refactor-planner.md

ExecutorPrompt

[string]

Optional prompt message for the executor agent; defaults to the shared executor.md

BuildConfigs

[array]

Build matrix entries (string[] or object[] with name, sanitizer, testCommand, buildDir, buildType); optional — defaults to the platform’s buildMatrix entries from Config

OnWindows

[bool]

$true on Windows, $false on Linux — selects build script and test command

RepoRoot

[string]

Repository root directory

MaxIterations

[int]

Override max iterations (-1 = use config, 0 = unlimited)

SkipBuild

[switch]

Skip builds

SkipTests

[switch]

Skip tests

SkipQuality

[switch]

Skip quality gates

PlannerOnly

[switch]

Run planner once and exit

ExecutorOnly

[switch]

Drain the queue and exit

Config JSON Keys#

The config is read from AgenticLoop.config.json. Key sections:

Key

Type

Default

Description

engine

[string]

opencode

Agent CLI backend: opencode or claude

engines.<engine>.plannerModel

[string]

Model ID for the planner (per engine)

engines.<engine>.executorModel

[string]

Model ID for the executor (per engine)

engines.claude.plannerFallbackModel

[string]

--fallback-model for the planner (used when the primary is overloaded)

engines.claude.plannerPromptFile

[string]

Repo-relative role prompt appended via --append-system-prompt-file

engines.claude.executorPromptFile

[string]

Repo-relative role prompt for executor/fixer

engines.claude.plannerAllowedTools

[string]

Space-separated --allowed-tools list sandboxing the planner

engines.claude.permissionMode

[string]

bypassPermissions

Executor permission mode (bypassPermissions maps to --dangerously-skip-permissions)

engines.claude.extraArgs

[string]

Extra CLI args appended to every claude invocation

engines.claude.streamOutput

[bool]

true

Stream live progress (tool calls, per-turn text, final cost) via stream-json; false = silent until completion

models.planner

[string]

opencode-go/glm-5.2

Legacy fallback model ID for the planner agent

models.executor

[string]

opencode-go/deepseek-v4-flash

Legacy fallback model ID for the executor agent

intervals.buildEveryNTasks

[int]

3

Build after every N completed tasks

intervals.qualityEveryNTasks

[int]

5

Quality gate every M tasks

intervals.refactorEveryNIterations

[int]

3

Refactor focus every R iterations

intervals.fullMatrixEveryNIterations

[int]

0

Full matrix sweep every N iterations (0 = disabled)

intervals.maxIterations

[int]

0

Max loop iterations (0 = unlimited)

intervals.maxExecutorRetries

[int]

3

Retries before skipping a stuck task

intervals.loopDelaySeconds

[int]

10

Delay between loop iterations

intervals.timeoutSeconds

[int]

0

Generic agent invocation timeout (0 = none)

intervals.plannerTimeoutSeconds

[int]

0

Planner timeout override

intervals.executorTimeoutSeconds

[int]

0

Executor/fixer timeout override

intervals.agentRetries

[int]

2

Retries per agent invocation (linear backoff)

intervals.agentRetryDelaySeconds

[int]

20

Base backoff delay between agent retries

intervals.fixBuildFailures

[bool]

true

Dispatch the fixer agent after a failed build, then rebuild once

intervals.maxConsecutiveBuildFailures

[int]

3

Stop the loop after N consecutive failed build phases

intervals.waitForUsageLimitReset

[bool]

true

When an agent fails because the Claude usage/session limit was hit, sleep until the reset time stated in the message (+2 min) and retry without burning a retry attempt (capped at 10 waits per invocation)

buildMatrix.windows

[array]

Windows build matrix entries (objects with name, sanitizer, buildDir, buildType, testCommand)

buildMatrix.linux

[array]

Linux build matrix entries

build.windowsTestCommand

[string]

Fallback test command for Windows

build.linuxTestCommand

[string]

Fallback test command for Linux

build.windowsQualityCommand

[string]

Quality command for Windows

build.linuxQualityCommand

[string]

Quality command for Linux

git.autoCommit

[bool]

true

Auto-commit after each completed task

git.commitPrefix

[string]

agentic-loop

Prefix for auto-commit messages

backlog.skipPlannerWhenTasksPending

[bool]

true

Skip the planner phase while BACKLOG.md still has actionable - [ ] tasks. Blocked - [b] entries don’t count, and a zero-progress iteration forces the planner to run next iteration (starvation guard); if the planner ran and the executor still made no progress, the loop stops

backlog.deleteCompletedTasks

[bool]

true

Prune completed (- [x]) task blocks from BACKLOG.md after each task (history lives in git)

Usage Examples#

Basic loop (with config file)#

Import-Module WindowsAgenticLoop.Common -Force
Initialize-AgenticLoop -ConfigPath 'AgenticLoop.config.json'
# ... configure and call Invoke-AgenticLoop ...
Complete-AgenticLoop

Dry run to test configuration#

Initialize-AgenticLoop -ConfigPath 'AgenticLoop.config.json' -DryRun
Invoke-AgenticLoop -Config $cfg -RepoRoot $repoRoot

Planner only (add tasks without executing)#

Invoke-AgenticLoop -Config $cfg -RepoRoot $repoRoot -PlannerOnly

Executor only (drain existing queue)#

Invoke-AgenticLoop -Config $cfg -RepoRoot $repoRoot -ExecutorOnly