param( [string]$RunId = "", [string]$RunStatus = "", [switch]$Enqueue ) . (Join-Path $PSScriptRoot "Common.ps1") $ralphRoot = Get-RalphRoot $repoRoot = Get-RepoRoot $automationConfig = Get-RalphAutomationConfig if ($null -eq $automationConfig -or -not ($automationConfig.PSObject.Properties.Name -contains "auto_followup")) { throw "automation config missing auto_followup block" } if ([string]::IsNullOrWhiteSpace($RunId)) { $current = Read-JsonFile -Path (Get-RalphStateFile -Name "current_run.json") } else { $runDir = Join-Path $ralphRoot ("runs\" + $RunId) if (-not (Test-Path $runDir)) { throw "Run directory not found: $runDir" } $currentRunFile = Get-RalphStateFile -Name "current_run.json" $current = Read-JsonFile -Path $currentRunFile if ([string]$current.run_id -ne $RunId) { $summaryPath = Join-Path $runDir "SUMMARY.md" $taskPath = Join-Path $runDir "TASK.md" $acceptancePath = Join-Path $runDir "ACCEPTANCE.md" $contextPath = Join-Path $runDir "CONTEXT.md" if (-not (Test-Path $taskPath)) { throw "Run does not contain TASK.md: $runDir" } $current = [ordered]@{ run_id = $RunId run_dir = $runDir task_directory = "" status = "unknown" latest_message = $(if (Test-Path $summaryPath) { (Get-Content -Raw $summaryPath) } else { "" }) } } } if ([string]::IsNullOrWhiteSpace($RunStatus)) { $RunStatus = [string]$current.status } $runDirResolved = [string]$current.run_dir if ([string]::IsNullOrWhiteSpace($runDirResolved)) { throw "current run state is missing run_dir" } $taskDirectory = [string]$current.task_directory if ([string]::IsNullOrWhiteSpace($taskDirectory) -or -not (Test-Path $taskDirectory)) { $taskLeaf = "" try { $taskLeaf = Split-Path -Leaf ([string]$current.task_directory) } catch { $taskLeaf = "" } $searchRoots = @( (Join-Path $ralphRoot "tasks\\processing"), (Join-Path $ralphRoot "tasks\\failed"), (Join-Path $ralphRoot "tasks\\completed") ) foreach ($root in $searchRoots) { if (-not (Test-Path $root)) { continue } $candidate = $null if (-not [string]::IsNullOrWhiteSpace($taskLeaf)) { $candidate = Join-Path $root $taskLeaf if (Test-Path $candidate) { $taskDirectory = $candidate break } } $matches = @(Get-ChildItem -Path $root -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -like ("*" + [string]$current.run_id + "*") -or $_.Name -like ("*" + $taskLeaf + "*") } | Select-Object -First 1) if ($matches.Count -gt 0) { $taskDirectory = $matches[0].FullName break } } } if ([string]::IsNullOrWhiteSpace($taskDirectory) -or -not (Test-Path $taskDirectory)) { throw "could not resolve task directory for followup generation" } $targetRelative = "docs\\autopilot" if ($automationConfig.auto_followup.PSObject.Properties.Name -contains "target_directory" -and -not [string]::IsNullOrWhiteSpace([string]$automationConfig.auto_followup.target_directory)) { $targetRelative = [string]$automationConfig.auto_followup.target_directory } $targetDirectory = Join-Path $repoRoot $targetRelative Ensure-Directory -Path $targetDirectory $outputFile = Join-Path $runDirResolved "NEXT_TASK_MANUAL.md" $extraSections = @( "## PREVIOUS OUTCOME SUMMARY`n$([string]$current.latest_message)", "## RUN SUMMARY FILE`n$(Join-Path $runDirResolved 'SUMMARY.md')", "## RUN OUTPUTS DIRECTORY`n$(Join-Path $runDirResolved 'outputs')", "## RUN REVIEWS DIRECTORY`n$(Join-Path $runDirResolved 'reviews')" ) & (Join-Path $PSScriptRoot "Invoke-CodexNextTask.ps1") ` -TaskDirectory $taskDirectory ` -RunDirectory $runDirResolved ` -OutputFile $outputFile ` -RunStatus $RunStatus ` -WorkingDirectory $repoRoot ` -ExtraSections $extraSections | Out-Null $slugBase = Convert-ToRalphSlug -Text ([IO.Path]::GetFileNameWithoutExtension($outputFile)) $finalMdPath = Join-Path $targetDirectory ((Get-Date -Format "yyyyMMdd-HHmmss") + "-" + $slugBase + ".md") Copy-Item -Path $outputFile -Destination $finalMdPath -Force $result = [ordered]@{ generated = $true source_markdown = $finalMdPath queued = $false } if ($Enqueue) { $taskInfo = New-RalphTaskPackFromMarkdown ` -MarkdownPath $finalMdPath ` -AdditionalMetadata @{ auto_generated = $true parent_run_id = [string]$current.run_id parent_task_directory = $taskDirectory followup_generation = 1 source_run_status = $RunStatus } $result.queued = $true $result.task_directory = $taskInfo.task_directory $result.title = $taskInfo.title Add-RalphEvent -RunId ([string]$current.run_id) -Stage "auto_followup" -Status "queued" -Actor "codex_master" -Message ("Manual backfill followup queued: " + $taskInfo.title) -Data @{ task_directory = $taskInfo.task_directory source_markdown = $finalMdPath } Send-TelegramNotification ` -EventName "task_queued" ` -Title "Auto-followup queued" ` -Message ($taskInfo.title + "`n" + $taskInfo.task_directory) ` -RunId ([string]$current.run_id) ` -Stage "auto_followup" ` -Status "queued" | Out-Null } ($result | ConvertTo-Json -Depth 20)