Payload/scripts/harness/harness-own-slice.ps1

#Requires -Version 5.1
<#
.SYNOPSIS
    Own-slice status with optional -Bootstrap / -ExecuteNext for harness-safe hops.

.DESCRIPTION
    Runs the own-slice checklist (same as harness-own-slice-status.ps1).

    -Bootstrap: if no open turn for the slice, runs harness-turn-start for the first
    incomplete ATDD phase, then prints checklist + NEXT and writes
    sensors/own-slice-next.json. Canonical first hop after "@atdd-developer own ...".

    -ExecuteNext: runs the first harness-safe segment of the printed next command
    via harness-invoke. Does not dispatch AI reviewers or forge artifacts.

.EXAMPLE
    pwsh -File scripts/harness-invoke.ps1 harness-own-slice.ps1 -Feature saved-search-queries -Slice mo-save-list -Bootstrap -ModelProvider cursor -ModelSlug composer-2.5

.EXAMPLE
    pwsh -File scripts/harness-invoke.ps1 harness-own-slice.ps1 -Feature saved-search-queries -Slice mo-save-list -ExecuteNext -ModelProvider cursor -ModelSlug composer-2.5
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [string]$Feature,

    [Parameter(Mandatory = $true)]
    [string]$Slice,

    [string]$FeatureRoot = '',

    [string]$RepoRoot = '',

    [switch]$AllowArbitraryFeatureRoot,

    [switch]$Bootstrap,

    [switch]$ExecuteNext,

    [ValidateSet('RED', 'GREEN', 'MUTATE', 'REFACTOR', 'COMMIT', '')]
    [string]$Phase = '',

    [string]$ModelProvider = '',

    [string]$ModelSlug = '',

    [switch]$Json
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

Import-Module (Join-Path $PSScriptRoot 'lib\HarnessContract.psm1') -Force
Import-Module (Join-Path $PSScriptRoot 'lib\HarnessJournal.psm1') -Force
Test-HarnessContractModuleReady

if ([string]::IsNullOrWhiteSpace($RepoRoot)) {
    $RepoRoot = Get-HarnessRepoRoot -StartPath $PSScriptRoot
}

$featureRootPath = Get-HarnessFeatureRoot -RepoRoot $RepoRoot -Feature $Feature `
    -FeatureRootOverride $FeatureRoot -AllowArbitraryFeatureRoot:$AllowArbitraryFeatureRoot
$exitCode = 0

if ($Bootstrap -and $ExecuteNext) {
    throw 'harness-own-slice: -Bootstrap and -ExecuteNext are mutually exclusive.'
}

if ($Bootstrap) {
    $turnOpen = Test-HarnessSliceTurnOpen -FeatureRoot $featureRootPath -Slice $Slice
    if (-not $turnOpen) {
        if ([string]::IsNullOrWhiteSpace($ModelProvider) -or [string]::IsNullOrWhiteSpace($ModelSlug)) {
            throw 'harness-own-slice -Bootstrap: pass -ModelProvider and -ModelSlug when starting a turn.'
        }
        $loop = Get-ReviewLoopDocument -FeatureRoot $featureRootPath
        $sliceEntry = Get-ImplementationSliceEntry -LoopData $loop.Data -SliceId $Slice
        if (-not $sliceEntry) {
            throw "Slice '$Slice' not found. Run init / repair review-loop before owning."
        }
        $bootPhase = Get-HarnessOwnSliceBootstrapPhase -SliceEntry $sliceEntry -PhaseOverride $Phase
        $turnStart = Join-Path $PSScriptRoot 'harness-turn-start.ps1'
        Write-Output "[harness-own-slice] Bootstrap: starting turn (phase $bootPhase)"
        & $turnStart -Feature $Feature -Slice $Slice -Phase $bootPhase `
            -ModelProvider $ModelProvider -ModelSlug $ModelSlug
        if ($LASTEXITCODE -ne 0 -and $null -ne $LASTEXITCODE) {
            throw "harness-own-slice -Bootstrap: turn-start exited $LASTEXITCODE"
        }
    }
    else {
        Write-Output '[harness-own-slice] Bootstrap: turn already open -- checklist only'
    }
}

if ($ExecuteNext) {
    $before = Get-HarnessOwnSliceReport -FeatureRoot $featureRootPath -Feature $Feature -Slice $Slice -RepoRoot $RepoRoot
    $next = $before.next_step
    if (-not $next) {
        throw 'No next step with a command; own-slice checklist is complete or blocked without a harness hop.'
    }
    if ($next.status -eq 'blocked') {
        throw "Next step '$($next.id)' is blocked ($($next.detail)); -ExecuteNext cannot remediate reviewer cycles."
    }
    $segment = Get-HarnessOwnSliceFirstInvokeSegment -NextCommand $next.next
    if ([string]::IsNullOrWhiteSpace($segment)) {
        throw "Next step '$($next.id)' has no harness-invoke segment for -ExecuteNext: $($next.next)"
    }
    Write-Output "[harness-own-slice] ExecuteNext: $($next.id)"
    Write-Output " $segment"
    $hopCode = Invoke-HarnessOwnSliceNextHop -RepoRoot $RepoRoot -InvokeSegment $segment `
        -ModelProvider $ModelProvider -ModelSlug $ModelSlug
    Write-Output "[harness-own-slice] hop exit code: $hopCode"
    if ($hopCode -ne 0) {
        $exitCode = $hopCode
    }
}

$result = Get-HarnessOwnSliceReport -FeatureRoot $featureRootPath -Feature $Feature -Slice $Slice -RepoRoot $RepoRoot
$todoCount = $result.remaining_count
$artifactPath = Write-HarnessOwnSliceNextArtifact -FeatureRoot $featureRootPath -Report $result

if (-not $Json) {
    $header = if ($Bootstrap) { 'Own-slice bootstrap' } else { 'Own-slice runner' }
    Format-HarnessOwnSliceHumanReport -Report $result -Header $header | ForEach-Object { Write-Output $_ }
    Write-Output " Wrote: $artifactPath"
    Write-Output ''
}

$result | ConvertTo-Json -Depth 6
if ($todoCount -gt 0 -and $exitCode -eq 0) {
    exit 2
}

exit $exitCode