#requires -version 5.1 <# run-parallel-100.ps1 - orchestrate 100-article SEO restoration with parallel Sonnet /goal agents. Run from the odoo-expertos repo root. Phases: setup -> SETUP.txt on fix/seo-all (brand, persona, flow, strategy, clusters, z-image probe). Serial. batches -> B01..B10.txt in parallel git worktrees, each on its own fix/seo-batch-bNN branch. final -> FINAL.txt on fix/seo-all (merge all batches, W5b, clusters exec, REVIEW gate, deploy, re-audit). all -> setup -> batches -> final. Examples: .\seo-audit-2026-05-27\run-parallel-100.ps1 # all phases, max 10 concurrent .\seo-audit-2026-05-27\run-parallel-100.ps1 -Phase setup # just setup .\seo-audit-2026-05-27\run-parallel-100.ps1 -Phase batches -MaxConcurrent 5 .\seo-audit-2026-05-27\run-parallel-100.ps1 -Phase final #> param( [ValidateSet('setup','batches','final','all')][string]$Phase = 'all', [int]$MaxConcurrent = 10, [string]$Model = "sonnet" ) $ErrorActionPreference = "Stop" $Repo = (Resolve-Path ".").Path $Goals = Join-Path $Repo "seo-audit-2026-05-27\goals" if(-not (Test-Path (Join-Path $Repo ".git"))){ throw "Run from the odoo-expertos repo root (no .git here: $Repo)" } if(-not (Get-Command claude -ErrorAction SilentlyContinue)){ throw "claude CLI not on PATH" } function ReadGoal($name){ Get-Content -Raw (Join-Path $Goals "$name.txt") } function Run-Setup { Write-Host "=== SETUP (serial) ===" -ForegroundColor Cyan git checkout -B fix/seo-all $g = ReadGoal 'SETUP' claude --model $Model -p "/goal $g" Write-Host "--- SETUP done. Verify BRAND.md + personas + cluster plans + z-image probe exist." -ForegroundColor Green } function Run-Batches { Write-Host "=== BATCHES (parallel, MaxConcurrent=$MaxConcurrent) ===" -ForegroundColor Cyan $ids = 1..10 | ForEach-Object { "B{0:00}" -f $_ } $wt = @{} $parent = Split-Path $Repo -Parent foreach($id in $ids){ $wtDir = Join-Path $parent ("odoo-wt-" + $id.ToLower()) $branch = "fix/seo-batch-" + $id.ToLower() if(-not (Test-Path $wtDir)){ Write-Host "creating worktree $wtDir on $branch (from fix/seo-all)" git worktree add -b $branch $wtDir fix/seo-all } else { Write-Host "worktree exists: $wtDir" } $wt[$id] = $wtDir } $queue = New-Object System.Collections.Queue foreach($id in $ids){ $queue.Enqueue($id) } $running = @{} while($queue.Count -gt 0 -or $running.Count -gt 0){ while($running.Count -lt $MaxConcurrent -and $queue.Count -gt 0){ $id = $queue.Dequeue() $g = ReadGoal $id $wtDir = $wt[$id] Write-Host "--> launching $id in $wtDir" -ForegroundColor Yellow $j = Start-Job -ScriptBlock { param($wtDir, $model, $goal, $id) Set-Location $wtDir $log = Join-Path $wtDir "$id.log" & claude --model $model -p "/goal $goal" *> $log return @{Id=$id; ExitCode=$LASTEXITCODE; Log=$log} } -ArgumentList $wtDir, $Model, $g, $id $running[$id] = $j } Start-Sleep -Seconds 30 foreach($id in @($running.Keys)){ $j = $running[$id] if($j.State -ne 'Running'){ $r = Receive-Job $j -ErrorAction SilentlyContinue Remove-Job $j -Force Write-Host "<-- $id finished (exit $($r.ExitCode); log: $($r.Log))" -ForegroundColor Green $running.Remove($id) } } } Write-Host "--- ALL BATCHES finished. Inspect each fix/seo-batch-bNN branch + its log before FINAL." -ForegroundColor Green } function Run-Final { Write-Host "=== FINAL (serial) ===" -ForegroundColor Cyan git checkout fix/seo-all $g = ReadGoal 'FINAL' claude --model $Model -p "/goal $g" Write-Host "--- FINAL done. Check the live re-audit score." -ForegroundColor Green } switch($Phase){ 'setup' { Run-Setup } 'batches' { Run-Batches } 'final' { Run-Final } 'all' { Run-Setup; Run-Batches; Run-Final } }