# How PAI Handles Parallel Loop Execution with Configurable Agents

> Discover how PAI handles parallel loop execution with configurable agents. Learn about its multi-process approach for efficient PRD criteria partitioning and real-time state synchronization.

- Repository: [Daniel Miessler 🛡️/Personal_AI_Infrastructure](https://github.com/danielmiessler/personal_ai_infrastructure)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Personal AI Infrastructure (PAI) executes parallel loop iterations by spawning up to 16 configurable `claude` subprocesses via the `-a` flag, partitioning PRD criteria across agents using domain-aware load balancing, and aggregating results back to the parent process for real-time state synchronization.**

Personal AI Infrastructure (PAI) by Daniel Miessler introduces a sophisticated **parallel loop execution with configurable agents** capability that transforms how autonomous coding loops handle multiple failing criteria. When running in loop mode, the system can distribute work across multiple agent subprocesses instead of processing criteria sequentially, dramatically reducing iteration time for complex PRD verification tasks.

## Activating Parallel Execution via CLI Arguments

Parallel execution is triggered through the command-line interface defined in [`Releases/v3.0/.claude/skills/PAI/Tools/algorithm.ts`](https://github.com/danielmiessler/Personal_AI_Infrastructure/blob/main/Releases/v3.0/.claude/skills/PAI/Tools/algorithm.ts). The algorithm accepts an `-a` or `--agents` flag that specifies the number of parallel workers to spawn.

```typescript
// Lines 36-49 and 140-164 in algorithm.ts
const result = parseArgs({
  options: {
    agents: { type: 'string', short: 'a', default: '1' },
    // ... other options
  },
}).parseSync();

const agentCount = Math.min(Math.max(parseInt(result.agents, 10), 1), 16);

```

The validation logic enforces a hard cap of **16 agents** and ensures at least one agent is always active. When `agentCount` exceeds 1 and multiple criteria are failing, the system switches from the single-agent workflow to the parallel partition strategy.

## Loop State Management for Configurable Agents

The `LoopAlgorithmState` interface in [`algorithm.ts`](https://github.com/danielmiessler/Personal_AI_Infrastructure/blob/main/algorithm.ts) tracks parallel execution metadata. When `createLoopState()` initializes a new loop session, it stores the requested agent count in the `parallelAgents` field.

```typescript
// Lines 430-440 in algorithm.ts
interface LoopAlgorithmState {
  parallelAgents?: number;
  agents: Array<{
    name: string;
    agentType: string;
    status: 'idle' | 'working' | 'complete';
    criteriaIds: string[];
  }>;
  // ... other fields
}

function createLoopState(agentCount: number) {
  return {
    parallelAgents: agentCount,
    agents: Array.from({ length: agentCount }, (_, i) => ({
      name: `Agent-${i + 1}`,
      agentType: 'claude-worker',
      status: 'idle',
      criteriaIds: []
    })),
    // ...
  };
}

```

This state object is persisted to `~/.claude/MEMORY/STATE/algorithms/<session-uuid>.json` after each iteration, enabling the web dashboard to display real-time agent assignments and progress.

## Domain-Aware Criteria Partitioning

The `partitionCriteria()` function implements intelligent load balancing by grouping related criteria before distributing them across agents. This prevents context switching overhead when agents work on unrelated domains.

```typescript
// Context around lines 960-967 in algorithm.ts
function partitionCriteria(criteria: Criterion[], agentCount: number) {
  // Group by domain prefix (e.g., ISC-TIER-1, ISC-SEC-4)
  const domainGroups = groupBy(criteria, c => getDomain(c.id));
  
  // Greedy load balancing: assign largest groups to least loaded agents
  const sortedDomains = Object.entries(domainGroups).sort((a, b) => b[1].length - a[1].length);
  
  const effectiveAgentCount = Math.min(agentCount, sortedDomains.length);
  const assignments: Criterion[][] = Array.from({ length: effectiveAgentCount }, () => []);
  
  for (const [domain, items] of sortedDomains) {
    const shortest = assignments.reduce((min, curr, i) => 
      curr.length < assignments[min].length ? i : min, 0);
    assignments[shortest].push(...items);
  }
  
  return assignments.filter(a => a.length > 0);
}

```

The algorithm caps the effective agent count at the number of domain groups. If only three domains are failing but eight agents are requested, only three agents spawn to avoid idle workers.

## Worker Prompt Generation and Isolation

Each parallel agent receives a constrained **worker prompt** generated by `buildWorkerPrompt()` that isolates the agent to a single criterion. This prevents workers from interfering with the broader algorithm state.

```typescript
// Lines 95-124 in algorithm.ts
function buildWorkerPrompt(prdPath: string, criterion: Criterion, iteration: number, agentIndex: number) {
  return `
You are a loop worker — a focused executor. Your ONLY job is to make ONE criterion pass.

YOUR CRITERION:
  ${criterion.id}: ${criterion.description}

PRD: ${prdPath}
Iteration: ${iteration} | Agent: ${agentIndex}

CONTEXT (from PRD):
${extractContextSection(prdPath)}

RULES — READ CAREFULLY:
- You are a WORKER, not the Algorithm. Do NOT run the Algorithm format.
- Do NOT create ISC criteria (TaskCreate). The criteria already exist.
- Do NOT execute voice curls (curl to localhost:8888).
- Do NOT write to the PRD file at all. Report results via stdout only.
- When finished, output exactly: RESULT: ${criterion.id} PASS
`;
}

```

Workers are explicitly forbidden from writing to the PRD or creating new criteria, ensuring the parent process maintains sole control over state mutations.

## Spawning Agents and Collecting Results

The `runParallelIteration()` function orchestrates the actual parallel execution using `Bun.spawn()` to launch isolated `claude` processes. It then parses stdout to determine which criteria passed.

```typescript
// Lines 78-99 in algorithm.ts (conceptual structure)
async function runParallelIteration(prdPath: string, assignments: Criterion[][], iteration: number) {
  const agents = assignments.map((criteria, index) => {
    const prompt = buildWorkerPrompt(prdPath, criteria[0], iteration, index + 1);
    
    return Bun.spawn(['claude', '--prompt', prompt], {
      stdout: 'pipe',
      stderr: 'pipe',
    });
  });
  
  const results = await Promise.all(agents.map(async (proc, i) => {
    const stdout = await new Response(proc.stdout).text();
    const stderr = await new Response(proc.stderr).text();
    return { assignment: assignments[i], stdout, stderr };
  }));
  
  // Parse results and update PRD
  const passedIds: string[] = [];
  for (const { assignment, stdout } of results) {
    const cId = assignment[0].id;
    if (stdout.includes(`RESULT: ${cId} PASS`) || stdout.includes(`${cId} PASS`)) {
      passedIds.push(cId);
    }
  }
  
  return passedIds;
}

```

After collecting results, the parent process updates the PRD checkboxes and rewrites the front matter with the latest verification summary.

## State Persistence and Dashboard Synchronization

Parallel execution metadata is persisted to disk via `syncCriteriaToState()` and the algorithm state management layer defined in [`Releases/v3.0/.claude/hooks/lib/algorithm-state.ts`](https://github.com/danielmiessler/Personal_AI_Infrastructure/blob/main/Releases/v3.0/.claude/hooks/lib/algorithm-state.ts).

The state file located at `~/.claude/MEMORY/STATE/algorithms/<session-uuid>.json` contains:

- `parallelAgents`: The configured number of concurrent workers
- `agents`: An array tracking each agent's `name`, `status`, `criteriaIds`, and timing data
- `loopHistory`: Per-iteration metrics including pass counts and agent utilization rates

This persistence enables the PAI web dashboard to render real-time progress bars, agent assignment visualizations, and historical performance analytics across parallel loop sessions.

## Summary

PAI implements **parallel loop execution with configurable agents** through a sophisticated orchestration layer that balances load while maintaining strict isolation:

- **CLI Configuration**: The `-a` or `--agents` flag (validated 1-16) activates parallel mode in [`algorithm.ts`](https://github.com/danielmiessler/Personal_AI_Infrastructure/blob/main/algorithm.ts)
- **Intelligent Partitioning**: The `partitionCriteria()` function groups related criteria by domain prefix and applies greedy load balancing to prevent context switching overhead
- **Process Isolation**: Each agent runs as a separate `claude` subprocess spawned via `Bun.spawn()`, receiving a constrained worker prompt that restricts it to a single criterion
- **Result Aggregation**: The parent process parses `RESULT: <criterion-id> PASS` strings from stdout and updates the PRD checkboxes and algorithm state JSON
- **State Persistence**: Execution metadata is written to `~/.claude/MEMORY/STATE/algorithms/<session-uuid>.json` for dashboard visualization

## Frequently Asked Questions

### What is the maximum number of parallel agents PAI supports?

PAI enforces a hard limit of **16 parallel agents** through CLI validation logic in [`algorithm.ts`](https://github.com/danielmiessler/Personal_AI_Infrastructure/blob/main/algorithm.ts) (lines 36-49). This cap prevents system resource exhaustion while still allowing significant parallelization for complex PRD verification tasks. If you specify a number higher than 16, the system automatically clamps it to the maximum.

### How does PAI distribute criteria across multiple agents?

The system uses **domain-aware partitioning** via the `partitionCriteria()` function. It first extracts domain prefixes from criterion IDs (e.g., grouping all `ISC-TIER-*` criteria together), then applies a greedy load-balancing algorithm that assigns domain groups to the agent with the fewest current assignments. This ensures related criteria stay together to minimize context switching while keeping workload distribution roughly equal.

### What happens if there are fewer failing criteria than configured agents?

PAI automatically adjusts the effective agent count to match the workload. The algorithm caps the number of spawned agents at the number of domain groups available (`Math.min(agentCount, sortedDomains.length)`). If only three criteria are failing but eight agents were requested, only three agents spawn, preventing idle processes and unnecessary resource consumption.

### Where does PAI store the state of parallel loop executions?

Execution state persists to a JSON file located at `~/.claude/MEMORY/STATE/algorithms/<session-uuid>.json`. This file stores the `parallelAgents` configuration, per-agent status arrays, criteria assignments, and `loopHistory` timing data. The `syncCriteriaToState()` function in [`algorithm-state.ts`](https://github.com/danielmiessler/Personal_AI_Infrastructure/blob/main/algorithm-state.ts) manages these writes, enabling the PAI dashboard to render real-time progress and historical analytics.