param(
    [string]$TaskDirectory = "",
    [string]$RunLabel = "autopilot",
    [string]$Implementer = "",
    [string[]]$Reviewers = @(),
    [switch]$UseCodexMaster = $true,
    [switch]$AutoFix = $true,
    [switch]$DryRun
)

. (Join-Path $PSScriptRoot "Common.ps1")

$ralphRoot = Get-RalphRoot
$repoRoot = Get-RepoRoot
$roots = Get-RalphTaskRoots
$automationConfig = Get-RalphAutomationConfig

if ([string]::IsNullOrWhiteSpace($TaskDirectory)) {
    $TaskDirectory = Join-Path $ralphRoot "tasks\current"
}

if ([string]::IsNullOrWhiteSpace($Implementer)) {
    $Implementer = Get-DefaultImplementer
}

if ($Reviewers.Count -eq 0) {
    $Reviewers = Get-DefaultReviewers
}

$taskPack = Read-TaskPack -TaskDirectory $TaskDirectory
$submissionPath = Join-Path $TaskDirectory "submission.json"
$submissionMetadata = $null
if (Test-Path $submissionPath) {
    try {
        $submissionMetadata = Read-JsonFile -Path $submissionPath
    }
    catch {
        $submissionMetadata = $null
    }
}
$runId = New-RunId -Label $RunLabel
$runDir = Join-Path $ralphRoot ("runs\" + $runId)
$worktreePath = Join-Path $ralphRoot ("worktrees\" + $runId + "-" + $Implementer)

Ensure-Directory -Path $runDir
Ensure-Directory -Path (Join-Path $runDir "prompts")
Ensure-Directory -Path (Join-Path $runDir "outputs")
Ensure-Directory -Path (Join-Path $runDir "reviews")

Copy-Item -Path $taskPack.Files.Task -Destination (Join-Path $runDir "TASK.md") -Force
Copy-Item -Path $taskPack.Files.Acceptance -Destination (Join-Path $runDir "ACCEPTANCE.md") -Force
Copy-Item -Path $taskPack.Files.Context -Destination (Join-Path $runDir "CONTEXT.md") -Force

$script:runState = [ordered]@{
    run_id = $runId
    status = "initializing"
    stage = "initializing"
    started_at = (Get-Date).ToString("o")
    finished_at = $null
    task_directory = $TaskDirectory
    run_dir = $runDir
    worktree = $worktreePath
    implementer = [ordered]@{
        name = $Implementer
        status = "pending"
        started_at = $null
        finished_at = $null
        output_file = ""
    }
    reviewers = @()
    codex_master = [ordered]@{
        enabled = [bool]$UseCodexMaster
        status = $(if ($UseCodexMaster) { "pending" } else { "disabled" })
        started_at = $null
        finished_at = $null
        output_file = ""
        verdict = ""
        acceptance_passed = $false
    }
    fix_pass = [ordered]@{
        enabled = [bool]$AutoFix
        status = $(if ($AutoFix) { "pending" } else { "disabled" })
        started_at = $null
        finished_at = $null
        output_file = ""
    }
    latest_message = "Run initialized"
    errors = @()
}

foreach ($reviewer in $Reviewers) {
    $script:runState.reviewers += [ordered]@{
        name = $reviewer
        status = "pending"
        started_at = $null
        finished_at = $null
        output_file = ""
    }
}

function Get-FollowupGeneration {
    if ($null -eq $submissionMetadata) {
        return 0
    }
    if ($submissionMetadata.PSObject.Properties.Name -contains "followup_generation") {
        try {
            return [int]$submissionMetadata.followup_generation
        }
        catch {
            return 0
        }
    }
    return 0
}

function Save-RunState {
    Set-RalphCurrentRunState -State $script:runState
}

function Set-RunPhase {
    param(
        [Parameter(Mandatory = $true)][string]$Stage,
        [Parameter(Mandatory = $true)][string]$Status,
        [Parameter(Mandatory = $true)][string]$Message
    )

    $script:runState.stage = $Stage
    $script:runState.status = $Status
    $script:runState.latest_message = $Message
    Save-RunState
    Add-RalphEvent -RunId $script:runState.run_id -Stage $Stage -Status $Status -Message $Message
}

function Set-ActorState {
    param(
        [Parameter(Mandatory = $true)][ValidateSet("implementer", "reviewer", "codex_master", "fix_pass")][string]$ActorType,
        [Parameter(Mandatory = $true)][string]$Status,
        [Parameter(Mandatory = $true)][string]$Message,
        [string]$ActorName = "",
        [string]$OutputFile = ""
    )

    $timestamp = (Get-Date).ToString("o")
    $target = $null
    $actorLabel = $ActorName

    switch ($ActorType) {
        "implementer" {
            $target = $script:runState.implementer
            $actorLabel = $script:runState.implementer.name
        }
        "reviewer" {
            $target = $script:runState.reviewers | Where-Object { $_.name -eq $ActorName } | Select-Object -First 1
            $actorLabel = $ActorName
        }
        "codex_master" {
            $target = $script:runState.codex_master
            $actorLabel = "codex_master"
        }
        "fix_pass" {
            $target = $script:runState.fix_pass
            $actorLabel = $script:runState.implementer.name
        }
    }

    if ($null -ne $target) {
        $target.status = $Status
        if ($Status -eq "running" -and -not $target.started_at) {
            $target.started_at = $timestamp
        }
        if ($Status -in @("completed", "failed", "skipped")) {
            $target.finished_at = $timestamp
        }
        if ($OutputFile) {
            $target.output_file = $OutputFile
        }
    }

    $script:runState.latest_message = $Message
    Save-RunState
    Add-RalphEvent -RunId $script:runState.run_id -Stage $ActorType -Status $Status -Actor $actorLabel -Message $Message -Data @{
        output_file = $OutputFile
    }
}

function Invoke-CodexReviewPass {
    param(
        [Parameter(Mandatory = $true)][string]$StageName,
        [Parameter(Mandatory = $true)][string]$PromptFileName,
        [Parameter(Mandatory = $true)][string]$OutputFileName,
        [Parameter(Mandatory = $true)][string]$RunningMessage,
        [Parameter(Mandatory = $true)][string]$CompletedMessage,
        [string[]]$ExtraSections = @()
    )

    $promptPath = Join-Path $runDir ("prompts\" + $PromptFileName)
    $outputPath = Join-Path $runDir ("reviews\" + $OutputFileName)

    New-PromptDocument `
        -TemplatePath (Join-Path $ralphRoot "templates\CODEX_REVIEW_PROMPT.md") `
        -TaskPack $taskPack `
        -OutputPath $promptPath `
        -ExtraSections $ExtraSections

    Set-RunPhase -Stage $StageName -Status "running" -Message $RunningMessage
    Set-ActorState -ActorType "codex_master" -Status "running" -Message $RunningMessage -OutputFile $outputPath
    $verdict = & (Join-Path $PSScriptRoot "Invoke-CodexMaster.ps1") `
        -PromptFile $promptPath `
        -OutputFile $outputPath `
        -WorkingDirectory $worktreePath

    $script:runState.codex_master.verdict = [string]$verdict.verdict
    $script:runState.codex_master.acceptance_passed = [bool]$verdict.acceptance_passed
    Set-ActorState -ActorType "codex_master" -Status "completed" -Message ($CompletedMessage + " (verdict: " + $verdict.verdict + ")") -OutputFile $outputPath
    Set-RunPhase -Stage $StageName -Status "completed" -Message ($CompletedMessage + " (verdict: " + $verdict.verdict + ")")
    Save-RunState
    return $verdict
}

function Invoke-AutoFollowupTask {
    param(
        [Parameter(Mandatory = $true)][string]$OutcomeStatus,
        [Parameter(Mandatory = $true)][string]$OutcomeSummary
    )

    if ($null -eq $automationConfig) {
        return $null
    }
    if (-not ($automationConfig.PSObject.Properties.Name -contains "auto_followup")) {
        return $null
    }

    $autoFollowup = $automationConfig.auto_followup
    $enabled = $false
    try { $enabled = [bool]$autoFollowup.enabled } catch { $enabled = $false }
    if (-not $enabled) {
        return $null
    }
    if (-not $UseCodexMaster) {
        return $null
    }

    $trigger = $false
    if ($OutcomeStatus -eq "failed") {
        try { $trigger = [bool]$autoFollowup.trigger_on_failure } catch { $trigger = $false }
    }
    elseif ($OutcomeStatus -eq "completed") {
        try { $trigger = [bool]$autoFollowup.trigger_on_success } catch { $trigger = $false }
    }
    if (-not $trigger) {
        return $null
    }

    $maxDepth = 0
    try { $maxDepth = [int]$autoFollowup.max_chain_depth } catch { $maxDepth = 0 }
    $currentDepth = Get-FollowupGeneration
    if ($maxDepth -gt 0 -and $currentDepth -ge $maxDepth) {
        return $null
    }

    $targetRelative = "docs\autopilot"
    if ($autoFollowup.PSObject.Properties.Name -contains "target_directory" -and -not [string]::IsNullOrWhiteSpace([string]$autoFollowup.target_directory)) {
        $targetRelative = [string]$autoFollowup.target_directory
    }
    $targetDirectory = Join-Path $repoRoot $targetRelative
    Ensure-Directory -Path $targetDirectory

    $prefix = "AUTOFOLLOWUP"
    if ($autoFollowup.PSObject.Properties.Name -contains "title_prefix" -and -not [string]::IsNullOrWhiteSpace([string]$autoFollowup.title_prefix)) {
        $prefix = [string]$autoFollowup.title_prefix
    }

    $nextPromptOutput = Join-Path $runDir "NEXT_TASK.md"
    $extraSections = @(
        "## PREVIOUS OUTCOME SUMMARY`n$OutcomeSummary",
        "## RUN SUMMARY FILE`n$(Join-Path $runDir 'SUMMARY.md')",
        "## RUN OUTPUTS DIRECTORY`n$(Join-Path $runDir 'outputs')",
        "## RUN REVIEWS DIRECTORY`n$(Join-Path $runDir 'reviews')"
    )

    $null = & (Join-Path $PSScriptRoot "Invoke-CodexNextTask.ps1") `
        -TaskDirectory $TaskDirectory `
        -RunDirectory $runDir `
        -OutputFile $nextPromptOutput `
        -RunStatus $OutcomeStatus `
        -WorkingDirectory $worktreePath `
        -ExtraSections $extraSections

    $dateLabel = Get-Date -Format "yyyyMMdd-HHmmss"
    $fileName = "{0}-{1}.md" -f $dateLabel, (Convert-ToRalphSlug -Text ($prefix + "-" + $taskPack.Task.Substring(0, [Math]::Min($taskPack.Task.Length, 32))))
    $finalMdPath = Join-Path $targetDirectory $fileName
    Copy-Item -Path $nextPromptOutput -Destination $finalMdPath -Force

    $taskInfo = New-RalphTaskPackFromMarkdown `
        -MarkdownPath $finalMdPath `
        -AdditionalMetadata @{
            auto_generated = $true
            parent_run_id = $runId
            parent_task_directory = $TaskDirectory
            followup_generation = ($currentDepth + 1)
            source_run_status = $OutcomeStatus
        }

    Add-RalphEvent -RunId $runId -Stage "auto_followup" -Status "queued" -Actor "codex_master" -Message ("Auto-followup task 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 $runId `
        -Stage "auto_followup" `
        -Status "queued" | Out-Null

    return [ordered]@{
        title = $taskInfo.title
        task_directory = $taskInfo.task_directory
        source_markdown = $finalMdPath
        followup_generation = ($currentDepth + 1)
    }
}

Save-RunState
Add-RalphEvent -RunId $runId -Stage "initializing" -Status "started" -Message "Ralph run initialized" -Data @{
    implementer = $Implementer
    reviewers = $Reviewers
    worktree = $worktreePath
}
Send-TelegramNotification `
    -EventName "run_started" `
    -Title "Run started" `
    -Message ("Implementer: " + $Implementer + "`nReviewers: " + ($Reviewers -join ", ")) `
    -RunId $runId `
    -Stage "initializing" `
    -Status "started" | Out-Null

$gitStatus = & git -C $repoRoot status --short
Write-Utf8File -Path (Join-Path $runDir "repo_status_before.txt") -Content (($gitStatus -join "`n") + "`n")

$gitStat = & git -C $repoRoot diff --stat
Write-Utf8File -Path (Join-Path $runDir "repo_diff_stat_before.txt") -Content (($gitStat -join "`n") + "`n")

$implementerPrompt = Join-Path $runDir "prompts\implementer.md"
New-PromptDocument `
    -TemplatePath (Join-Path $ralphRoot "templates\IMPLEMENTER_PROMPT.md") `
    -TaskPack $taskPack `
    -OutputPath $implementerPrompt `
    -ExtraSections @(
        "## WORKTREE`nEdit only inside this worktree:`n`n$worktreePath",
        "## REQUIRED RUN OUTPUT`nWrite a file named CHANGES.md in this run directory:`n`n$runDir"
    )

$summaryLines = @(
    "# Ralph Run",
    "",
    "- Run ID: $runId",
    "- Implementer: $Implementer",
    "- Reviewers: $(($Reviewers -join ', '))",
    "- Worktree: $worktreePath",
    "- Codex master review: $(if ($UseCodexMaster) { 'enabled' } else { 'disabled' })",
    "- Auto fix pass: $(if ($AutoFix) { 'enabled' } else { 'disabled' })",
    "- Dry run: $(if ($DryRun) { 'yes' } else { 'no' })"
)

if ($DryRun) {
    Set-RunPhase -Stage "dry_run" -Status "completed" -Message "Dry run prepared successfully"
    $script:runState.finished_at = (Get-Date).ToString("o")
    Save-RunState
    Write-Utf8File -Path (Join-Path $runDir "SUMMARY.md") -Content (($summaryLines -join "`n") + "`n")
    Get-Content -Raw -Path (Join-Path $runDir "SUMMARY.md")
    return
}

Set-RunPhase -Stage "worktree" -Status "running" -Message "Creating isolated worktree"
& git -C $repoRoot worktree add --detach $worktreePath HEAD | Out-Null
Set-RunPhase -Stage "worktree" -Status "completed" -Message "Worktree ready"

$reviewOutputs = @()
$codexFinalVerdict = $null
$autoFollowupResult = $null
$script:heartbeatJob = $null
$script:heartbeatSignalPath = ""
$codexReviewSections = @(
    "## IMPLEMENTER WORKTREE`n$worktreePath",
    "## IMPLEMENTER DIFF FILE`n$(Join-Path $runDir 'implementer.patch')",
    "## IMPLEMENTER STATUS FILE`n$(Join-Path $runDir 'implementer_status.txt')"
)

function Start-HeartbeatMonitor {
    param(
        [Parameter(Mandatory = $true)][string]$Stage,
        [Parameter(Mandatory = $true)][string]$Title,
        [Parameter(Mandatory = $true)][string]$Message,
        [int]$IntervalSeconds = 300
    )

    Stop-HeartbeatMonitor

    $signalPath = Join-Path $runDir ("heartbeat-" + $Stage + ".lock")
    Write-Utf8File -Path $signalPath -Content ((Get-Date).ToString("o"))
    $commonPath = Join-Path $PSScriptRoot "Common.ps1"

    $script:heartbeatSignalPath = $signalPath
    $script:heartbeatJob = Start-Job -ArgumentList $commonPath, $signalPath, $Stage, $Title, $Message, $runId, $IntervalSeconds -ScriptBlock {
        param($CommonPath, $SignalPath, $StageName, $TitleText, $MessageText, $RunIdValue, $IntervalValue)
        . $CommonPath
        while (Test-Path $SignalPath) {
            Start-Sleep -Seconds $IntervalValue
            if (-not (Test-Path $SignalPath)) {
                break
            }
            Send-TelegramNotification `
                -EventName "run_heartbeat" `
                -Title $TitleText `
                -Message $MessageText `
                -RunId $RunIdValue `
                -Stage $StageName `
                -Status "running" | Out-Null
        }
    }
}

function Stop-HeartbeatMonitor {
    if (-not [string]::IsNullOrWhiteSpace($script:heartbeatSignalPath) -and (Test-Path $script:heartbeatSignalPath)) {
        Remove-Item -LiteralPath $script:heartbeatSignalPath -Force -ErrorAction SilentlyContinue
    }
    $script:heartbeatSignalPath = ""

    if ($null -ne $script:heartbeatJob) {
        Wait-Job -Job $script:heartbeatJob -Timeout 1 | Out-Null
        if ($script:heartbeatJob.State -eq "Running") {
            Stop-Job -Job $script:heartbeatJob -Force | Out-Null
        }
        Remove-Job -Job $script:heartbeatJob -Force -ErrorAction SilentlyContinue
        $script:heartbeatJob = $null
    }
}

try {
    $implementerOutput = Join-Path $runDir "outputs\implementer.json"
    Set-RunPhase -Stage "implementer" -Status "running" -Message ("Implementer " + $Implementer + " started")
    Set-ActorState -ActorType "implementer" -Status "running" -Message ("Implementer " + $Implementer + " received task pack") -OutputFile $implementerOutput
    Start-HeartbeatMonitor -Stage "implementer" -Title "Run heartbeat" -Message ("Implementer running: " + $Implementer)
    & (Join-Path $PSScriptRoot "Invoke-ClaudeProvider.ps1") `
        -ProviderName $Implementer `
        -PromptFile $implementerPrompt `
        -OutputFile $implementerOutput `
        -WorkingDirectory $worktreePath `
        -AddDirectories @($runDir, $repoRoot)
    Stop-HeartbeatMonitor
    Set-ActorState -ActorType "implementer" -Status "completed" -Message ("Implementer " + $Implementer + " finished first pass") -OutputFile $implementerOutput
    Send-TelegramNotification `
        -EventName "implementer_completed" `
        -Title "Implementer finished" `
        -Message ("Implementer: " + $Implementer) `
        -RunId $runId `
        -Stage "implementer" `
        -Status "completed" | Out-Null

    $worktreeStatus = & git -C $worktreePath status --short
    Write-Utf8File -Path (Join-Path $runDir "implementer_status.txt") -Content (($worktreeStatus -join "`n") + "`n")

    $worktreeDiff = & git -C $worktreePath diff --no-ext-diff
    Write-Utf8File -Path (Join-Path $runDir "implementer.patch") -Content (($worktreeDiff -join "`n") + "`n")

    foreach ($reviewer in $Reviewers) {
        if ($reviewer -eq $Reviewers[0]) {
            Send-TelegramNotification `
                -EventName "reviewers_started" `
                -Title "Reviewers started" `
                -Message (($Reviewers -join ", ")) `
                -RunId $runId `
                -Stage "review" `
                -Status "running" | Out-Null
        }
        $promptPath = Join-Path $runDir ("prompts\review-" + $reviewer + ".md")
        New-PromptDocument `
            -TemplatePath (Join-Path $ralphRoot "templates\REVIEWER_PROMPT.md") `
            -TaskPack $taskPack `
            -OutputPath $promptPath `
            -ExtraSections @(
                "## IMPLEMENTER WORKTREE`n$worktreePath",
                "## IMPLEMENTER DIFF FILE`n$(Join-Path $runDir 'implementer.patch')",
                "## IMPLEMENTER STATUS FILE`n$(Join-Path $runDir 'implementer_status.txt')"
            )

        $reviewOutput = Join-Path $runDir ("reviews\" + $reviewer + ".json")
        Set-RunPhase -Stage "review" -Status "running" -Message ("Reviewer " + $reviewer + " started")
        Set-ActorState -ActorType "reviewer" -ActorName $reviewer -Status "running" -Message ("Reviewer " + $reviewer + " analyzing diff") -OutputFile $reviewOutput
        & (Join-Path $PSScriptRoot "Invoke-ClaudeProvider.ps1") `
            -ProviderName $reviewer `
            -PromptFile $promptPath `
            -OutputFile $reviewOutput `
            -WorkingDirectory $worktreePath `
            -AddDirectories @($runDir, $repoRoot)
        Set-ActorState -ActorType "reviewer" -ActorName $reviewer -Status "completed" -Message ("Reviewer " + $reviewer + " completed review") -OutputFile $reviewOutput
        $reviewOutputs += $reviewOutput
    }
    Set-RunPhase -Stage "review" -Status "completed" -Message "All provider reviews completed"

    $codexReviewSections += "## REVIEW FILES`n$($reviewOutputs -join "`n")"

    if ($UseCodexMaster -and $AutoFix) {
        $null = Invoke-CodexReviewPass `
            -StageName "codex_review_pre_fix" `
            -PromptFileName "codex_master_review_pre_fix.md" `
            -OutputFileName "codex_master_pre_fix.json" `
            -RunningMessage "Codex master pre-fix review started" `
            -CompletedMessage "Codex master pre-fix review completed" `
            -ExtraSections $codexReviewSections
    }

    if ($AutoFix) {
        $fixPrompt = Join-Path $runDir "prompts\fix_pass.md"
        $fixExtraSections = @(
            "## FIX PASS`nRead the reviewer outputs and fix the highest-signal issues only.",
            "## REVIEW FILES`n$($reviewOutputs -join "`n")"
        )
        if ($UseCodexMaster) {
            $fixExtraSections += "## CODEX REVIEW FILE`n$(Join-Path $runDir 'reviews\codex_master_pre_fix.json')"
        }
        else {
            $fixExtraSections += "## CODEX REVIEW FILE`nCodex review disabled for this run."
        }
        $fixExtraSections += "## REQUIRED RUN OUTPUT`nUpdate CHANGES.md in this run directory after the fix pass:`n`n$runDir"

        New-PromptDocument `
            -TemplatePath (Join-Path $ralphRoot "templates\IMPLEMENTER_PROMPT.md") `
            -TaskPack $taskPack `
            -OutputPath $fixPrompt `
            -ExtraSections $fixExtraSections

        Set-RunPhase -Stage "fix_pass" -Status "running" -Message ("Fix pass started with " + $Implementer)
        Set-ActorState -ActorType "fix_pass" -Status "running" -Message ("Implementer " + $Implementer + " applying review fixes") -OutputFile (Join-Path $runDir "outputs\fix_pass.json")
        Send-TelegramNotification `
            -EventName "fix_pass_started" `
            -Title "Fix pass started" `
            -Message ("Implementer: " + $Implementer) `
            -RunId $runId `
            -Stage "fix_pass" `
            -Status "running" | Out-Null
        Start-HeartbeatMonitor -Stage "fix_pass" -Title "Run heartbeat" -Message ("Fix pass running: " + $Implementer)
        & (Join-Path $PSScriptRoot "Invoke-ClaudeProvider.ps1") `
            -ProviderName $Implementer `
            -PromptFile $fixPrompt `
            -OutputFile (Join-Path $runDir "outputs\fix_pass.json") `
            -WorkingDirectory $worktreePath `
            -AddDirectories @($runDir, $repoRoot)
        Stop-HeartbeatMonitor
        Set-ActorState -ActorType "fix_pass" -Status "completed" -Message ("Implementer " + $Implementer + " finished fix pass") -OutputFile (Join-Path $runDir "outputs\fix_pass.json")
        Set-RunPhase -Stage "fix_pass" -Status "completed" -Message "Fix pass completed"

        $finalDiff = & git -C $worktreePath diff --no-ext-diff
        Write-Utf8File -Path (Join-Path $runDir "final.patch") -Content (($finalDiff -join "`n") + "`n")
    }

    $finalStatus = & git -C $worktreePath status --short
    Write-Utf8File -Path (Join-Path $runDir "final_status.txt") -Content (($finalStatus -join "`n") + "`n")

    if ($UseCodexMaster) {
        $finalDiffPath = Join-Path $runDir "final.patch"
        if (-not (Test-Path $finalDiffPath)) {
            $finalDiffPath = Join-Path $runDir "implementer.patch"
        }
        $codexFinalSections = @(
            "## IMPLEMENTER WORKTREE`n$worktreePath",
            "## FINAL DIFF FILE`n$finalDiffPath",
            "## FINAL STATUS FILE`n$(Join-Path $runDir 'final_status.txt')",
            "## REVIEW FILES`n$($reviewOutputs -join "`n")"
        )
        if ($AutoFix) {
            $codexFinalSections += "## FIX PASS OUTPUT`n$(Join-Path $runDir 'outputs\\fix_pass.json')"
            $codexFinalSections += "## PRE-FIX CODEX REVIEW`n$(Join-Path $runDir 'reviews\\codex_master_pre_fix.json')"
        }

        $codexFinalVerdict = Invoke-CodexReviewPass `
            -StageName "codex_review_final" `
            -PromptFileName "codex_master_review_final.md" `
            -OutputFileName "codex_master_final.json" `
            -RunningMessage "Codex master final review started" `
            -CompletedMessage "Codex master final review completed" `
            -ExtraSections $codexFinalSections

        if (([string]$codexFinalVerdict.verdict) -ne "pass" -or -not [bool]$codexFinalVerdict.acceptance_passed) {
            Send-TelegramNotification `
                -EventName "codex_failed" `
                -Title "Codex gate rejected run" `
                -Message ([string]$codexFinalVerdict.summary) `
                -RunId $runId `
                -Stage "codex_review_final" `
                -Status "failed" | Out-Null
            throw ("Codex master rejected the run with verdict '{0}': {1}" -f $codexFinalVerdict.verdict, $codexFinalVerdict.summary)
        }
    }

    $summaryLines += @(
        "",
        "## Outputs",
        "",
        "- Run directory: $runDir",
        "- Worktree: $worktreePath",
        "- Implementer output: $(Join-Path $runDir 'outputs\implementer.json')",
        "- Final status: $(Join-Path $runDir 'final_status.txt')",
        "- Codex final verdict: $(if ($UseCodexMaster -and $null -ne $codexFinalVerdict) { [string]$codexFinalVerdict.verdict } else { 'not-run' })"
    )

    try {
        $autoFollowupResult = Invoke-AutoFollowupTask -OutcomeStatus "completed" -OutcomeSummary "Run completed successfully"
    }
    catch {
        $script:runState.errors += ("Auto-followup generation error: " + $_.Exception.Message)
        Save-RunState
        Add-RalphEvent -RunId $runId -Stage "auto_followup" -Status "failed" -Actor "codex_master" -Message $_.Exception.Message
    }

    if ($null -ne $autoFollowupResult) {
        $summaryLines += @(
            "",
            "## Auto Followup",
            "",
            "- Title: $($autoFollowupResult.title)",
            "- Task directory: $($autoFollowupResult.task_directory)",
            "- Source markdown: $($autoFollowupResult.source_markdown)"
        )
    }

    Write-Utf8File -Path (Join-Path $runDir "SUMMARY.md") -Content (($summaryLines -join "`n") + "`n")
    $script:runState.status = "completed"
    $script:runState.stage = "completed"
    $script:runState.finished_at = (Get-Date).ToString("o")
    $script:runState.latest_message = "Run completed successfully"
    Save-RunState
    Add-RalphEvent -RunId $runId -Stage "completed" -Status "completed" -Message "Ralph run completed successfully" -Data @{
        run_dir = $runDir
        worktree = $worktreePath
    }
    Send-TelegramNotification `
        -EventName "run_completed" `
        -Title "Run completed" `
        -Message ("Implementer: " + $Implementer + "`nCodex verdict: " + $(if ($UseCodexMaster -and $null -ne $codexFinalVerdict) { [string]$codexFinalVerdict.verdict } else { "not-run" })) `
        -RunId $runId `
        -Stage "completed" `
        -Status "completed" | Out-Null
    Get-Content -Raw -Path (Join-Path $runDir "SUMMARY.md")
}
catch {
    Stop-HeartbeatMonitor
    $failureMessage = $_.Exception.Message
    if ($UseCodexMaster -and $null -eq $codexFinalVerdict) {
        try {
            $failureSections = @(
                "## FAILURE CONTEXT`nThe autopilot run failed before final acceptance.",
                "## FAILURE MESSAGE`n$failureMessage",
                "## IMPLEMENTER WORKTREE`n$worktreePath",
                "## IMPLEMENTER DIFF FILE`n$(Join-Path $runDir 'implementer.patch')",
                "## IMPLEMENTER STATUS FILE`n$(Join-Path $runDir 'implementer_status.txt')",
                "## REVIEW FILES`n$($reviewOutputs -join "`n")"
            )
            $null = Invoke-CodexReviewPass `
                -StageName "codex_review_failure" `
                -PromptFileName "codex_master_review_failure.md" `
                -OutputFileName "codex_master_failure.json" `
                -RunningMessage "Codex master failure review started" `
                -CompletedMessage "Codex master failure review completed" `
                -ExtraSections $failureSections
        }
        catch {
            $script:runState.errors += ("Codex failure review error: " + $_.Exception.Message)
            Add-RalphEvent -RunId $runId -Stage "codex_review_failure" -Status "failed" -Actor "codex_master" -Message $_.Exception.Message
        }
    }

    $script:runState.status = "failed"
    $script:runState.stage = "failed"
    $script:runState.finished_at = (Get-Date).ToString("o")
    $script:runState.latest_message = $failureMessage
    $script:runState.errors += $failureMessage
    Save-RunState
    Add-RalphEvent -RunId $runId -Stage "failed" -Status "failed" -Message $failureMessage
    Send-TelegramNotification `
        -EventName "run_failed" `
        -Title "Run failed" `
        -Message $failureMessage `
        -RunId $runId `
        -Stage "failed" `
        -Status "failed" | Out-Null
    try {
        $autoFollowupResult = Invoke-AutoFollowupTask -OutcomeStatus "failed" -OutcomeSummary $failureMessage
    }
    catch {
        $script:runState.errors += ("Auto-followup generation error: " + $_.Exception.Message)
        Save-RunState
        Add-RalphEvent -RunId $runId -Stage "auto_followup" -Status "failed" -Actor "codex_master" -Message $_.Exception.Message
    }
    $summaryLines += @(
        "",
        "## Failure",
        "",
        $failureMessage
    )
    if ($null -ne $autoFollowupResult) {
        $summaryLines += @(
            "",
            "## Auto Followup",
            "",
            "- Title: $($autoFollowupResult.title)",
            "- Task directory: $($autoFollowupResult.task_directory)",
            "- Source markdown: $($autoFollowupResult.source_markdown)"
        )
    }
    Write-Utf8File -Path (Join-Path $runDir "SUMMARY.md") -Content (($summaryLines -join "`n") + "`n")
    throw
}
