Payload/scripts/harness/harness-turn-stop.ps1

#Requires -Version 5.1
<#
.SYNOPSIS
    Stop a slice turn with end-of-turn verify-local evidence.

.DESCRIPTION
    This is the harness-first default exitpoint for implementation turns.
    It appends a verify-local sensor row and records ActivityStopped only when
    that closeout verify PASSes. A FAIL verify is recorded (append-only truth)
    but ActivityStopped is withheld so MISSING_CLOSEOUT_VERIFY_LOCAL stays accurate
    and recoverable via a later PASS + stop.

    -Abort closes the turn without closeout verify-local (OPERATOR_ABORT).

.EXAMPLE
    .\scripts\harness-turn-stop.ps1 -Feature saved-search-queries -Slice mo-save-list -Summary "end slice turn"
#>

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

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

    [string]$Summary = 'Turn stop (end verify-local)',

    [switch]$Abort,

    [ValidateSet('cursor', 'copilot', 'other')]
    [string]$ModelProvider = 'copilot',

    [string]$ModelSlug = 'unknown',

    [string]$FeatureRoot = '',

    [string]$RepoRoot = '',

    [switch]$AllowArbitraryFeatureRoot
)

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

$journalScript = Join-Path $PSScriptRoot 'harness-journal.ps1'
if ([string]::IsNullOrWhiteSpace($RepoRoot)) {
    $RepoRoot = Get-HarnessRepoRoot -StartPath $PSScriptRoot
}
$featureRoot = Get-HarnessFeatureRoot -RepoRoot $RepoRoot -Feature $Feature `
    -FeatureRootOverride $FeatureRoot -AllowArbitraryFeatureRoot:$AllowArbitraryFeatureRoot
$journalCommon = @{
    Feature                   = $Feature
    Slice                     = $Slice
    FeatureRoot               = $featureRoot
    RepoRoot                  = $RepoRoot
    AllowArbitraryFeatureRoot = $AllowArbitraryFeatureRoot
    ModelProvider             = $ModelProvider
    ModelSlug                 = $ModelSlug
}

if ($Abort) {
    if (-not (Test-HarnessSliceTurnOpen -FeatureRoot $featureRoot -Slice $Slice)) {
        throw "harness-turn-stop: -Abort refused - slice '$Slice' has no open turn."
    }
    & $journalScript @journalCommon -Action stop -Agent atdd-developer `
        -ActivityType turn -ActivityName stop `
        -Summary 'Turn abort (OPERATOR_ABORT)' -Outcome SKIPPED -SkipReasonCode OPERATOR_ABORT
    try {
        Sync-HarnessOwnSliceNextArtifact -FeatureRoot $featureRoot -Feature $Feature -Slice $Slice -RepoRoot $RepoRoot | Out-Null
    }
    catch {
        Write-Warning "harness-turn-stop: own-slice-next refresh skipped: $($_.Exception.Message)"
    }
    return
}

Assert-HarnessTurnStopProfilePhasesComplete -RepoRoot $RepoRoot -FeatureRoot $featureRoot -Slice $Slice

$sensorScript = Join-Path $PSScriptRoot 'harness-sensor.ps1'

& $sensorScript @journalCommon -Run verify-local -Append
if ($LASTEXITCODE -ne 0) {
    throw "harness-turn-stop: closeout verify-local failed (exit $LASTEXITCODE). ActivityStopped was not recorded; re-run after verify PASSes."
}

$events = @(Get-HarnessJournalEvents -FeatureRoot $featureRoot | Where-Object { $_.slice -eq $Slice })
$last = $events | Select-Object -Last 1
$payloadName = $null
if ($last -and $last.payload) {
    $payloadName = Get-HarnessProperty -Object $last.payload -Name 'name'
}
$closeoutOk = $last `
    -and ((Get-HarnessProperty -Object $last -Name 'kind') -eq 'SensorRunRecorded') `
    -and ($payloadName -eq 'verify-local') `
    -and ((Get-HarnessProperty -Object $last -Name 'outcome') -eq 'PASS')
if (-not $closeoutOk) {
    throw 'harness-turn-stop: latest journal event for this slice is not a verify-local PASS; refusing ActivityStopped.'
}

& $journalScript @journalCommon -Action stop -Agent atdd-developer `
    -ActivityType turn -ActivityName stop -Summary $Summary

# Keep own-slice-next truthful after ActivityStopped.
try {
    Sync-HarnessOwnSliceNextArtifact -FeatureRoot $featureRoot -Feature $Feature -Slice $Slice -RepoRoot $RepoRoot | Out-Null
}
catch {
    Write-Warning "harness-turn-stop: own-slice-next refresh skipped: $($_.Exception.Message)"
}