scripts/internal/continuous-co-review/worktree-review-detached-entry.ps1

[CmdletBinding()]
param(
    [Parameter(Mandatory)][string]$RepoRoot,
    [Parameter(Mandatory)][string]$RunDir,
    [Parameter(Mandatory)][string]$RunId,
    [Parameter(Mandatory)][string]$RegistryPath,
    [string]$BaselineRef,
    [string]$CodeWriterHost,
    [string]$RequestedHost,
    [int]$TimeoutSeconds = 900
)
# iter-008 — the DETACHED entry the worktree-navigator spawns (non-blocking). Runs the orchestrator (which writes
# result.out + status.json to $RunDir + disposes the ephemeral worktree), then flips the pending REGISTRY status
# (running -> the orchestrator's terminal status) so the navigator's existing reap consumes it exactly like the
# legacy supervisor path. All heavy work lives here, off the Stop budget.
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest

# Win32_Process.Create (the Windows detached spawn in co-review-service.ps1) gives THIS process NO inherited stdio
# - the parent deliberately inherits NOTHING so it cannot hold the host's stdout pipe open (the 20-min Stop). So
# self-redirect the PS streams to entry.out.log here, so a dot-source/load/run error is still diagnosable. Best-
# effort: a transcript failure must never abort the review (the orchestrator's status.json is the reliable record).
try { Start-Transcript -LiteralPath (Join-Path $RunDir 'entry.out.log') -Force -ErrorAction Stop | Out-Null } catch { $null = $_ }

. (Join-Path $PSScriptRoot 'worktree-review-orchestrator.ps1')

function Update-WorktreeRunRegistryStatus {
    param([string]$Path, [string]$Status, [hashtable]$Extra)
    try {
        if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return }
        $reg = Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json
        $reg.status = $Status
        if ($Extra) { foreach ($k in $Extra.Keys) { $reg | Add-Member -NotePropertyName $k -NotePropertyValue $Extra[$k] -Force } }
        $json = $reg | ConvertTo-Json -Depth 10
        if (Get-Command -Name 'Write-SpecrewFileAtomic' -ErrorAction SilentlyContinue) { Write-SpecrewFileAtomic -Path $Path -Content $json }
        else { [System.IO.File]::WriteAllText($Path, $json) }
    }
    catch { $null = $_ }
}

try {
    $params = @{ RepoRoot = $RepoRoot; RunDir = $RunDir; RunId = $RunId; TimeoutSeconds = $TimeoutSeconds }
    if (-not [string]::IsNullOrWhiteSpace($BaselineRef)) { $params.BaselineRef = $BaselineRef }
    if (-not [string]::IsNullOrWhiteSpace($CodeWriterHost)) { $params.CodeWriterHost = $CodeWriterHost }
    if (-not [string]::IsNullOrWhiteSpace($RequestedHost)) { $params.RequestedHost = $RequestedHost }
    $st = Invoke-ContinuousCoReviewWorktreeReviewRun @params
    $term = if ($null -ne $st -and ($st.PSObject.Properties.Name -contains 'status')) { [string]$st.status } else { 'failed' }
    $extra = @{}
    if ($null -ne $st -and ($st.PSObject.Properties.Name -contains 'failure_reason')) { $extra.failure_reason = [string]$st.failure_reason }
    # Propagate the reviewed-state digest the orchestrator computed (off the Stop budget) to the registry, so the
    # reap promotes the gate's identity, not the HEAD-tree (the freshness-mismatch bug).
    if ($null -ne $st -and ($st.PSObject.Properties.Name -contains 'reviewed_digest_tree_id')) { $extra.reviewed_digest_tree_id = [string]$st.reviewed_digest_tree_id }
    # T093/FR-035: propagate the independence label so the reap can surface a same-host fallback + the
    # authorize-an-independent-host upgrade ask (the answer upgrades the NEXT run; this one never blocked).
    if ($null -ne $st -and ($st.PSObject.Properties.Name -contains 'reviewer_independence')) { $extra.reviewer_independence = [string]$st.reviewer_independence }
    Update-WorktreeRunRegistryStatus -Path $RegistryPath -Status $term -Extra $extra
}
catch {
    Update-WorktreeRunRegistryStatus -Path $RegistryPath -Status 'failed' -Extra @{ failure_reason = ('detached-entry-exception: ' + $_.Exception.Message) }
}
try { Stop-Transcript -ErrorAction SilentlyContinue | Out-Null } catch { $null = $_ }