Payload/scripts/kaden/harness/harness-health.ps1
|
#Requires -Version 5.1 <# .SYNOPSIS One-command harness status check for agents. .DESCRIPTION Prints a concise status summary for a feature/slice: ownership_status, latest turn start/stop, latest verify-local verdict, phase completion, journal event count, and a non-RequireReviewReady validation. Run any time you are uncertain about harness state instead of manually reading the journal. -Slice is optional: omitting it renders the feature-level outer-loop view (one informational journal-sourced line per advisory outer phase under an "Outer loop:" header, Journal events over all rows, quick validation with the plan-named pending downgrade). Its turn/ownership JSON keys are slice-scoped placeholders in that view; read `view` and `outer_phases`. Output is human-readable and also emitted as a JSON object so agents can parse it programmatically. .EXAMPLE pwsh -File scripts/kaden/harness-invoke.ps1 harness-health.ps1 -Feature saved-search-queries -Slice mo-save-list .EXAMPLE pwsh -File scripts/kaden/harness-invoke.ps1 harness-health.ps1 -Feature harness-event-journal #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Feature, # Charter Phase 2 (VIEW-01a): -Slice is optional; omitting it renders the # feature-level outer-loop view for outer-loop-only features. [string]$Slice = '', [string]$FeatureRoot = '', [string]$RepoRoot = '', [int]$StaleTurnMinutes = 8, [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 Import-Module (Join-Path $PSScriptRoot 'lib\HarnessGateProjection.psm1') -Force Test-HarnessContractModuleReady if ([string]::IsNullOrWhiteSpace($RepoRoot)) { $RepoRoot = Get-HarnessRepoRoot -StartPath $PSScriptRoot } $featureRootPath = Get-HarnessFeatureRoot -RepoRoot $RepoRoot -Feature $Feature ` -FeatureRootOverride $FeatureRoot -AllowArbitraryFeatureRoot:$AllowArbitraryFeatureRoot # -- Journal ------------------------------------------------------------------ $journalPath = Join-Path $featureRootPath 'journal\events.jsonl' $allEvents = @() if (Test-Path -LiteralPath $journalPath) { $allEvents = @(Get-HarnessJournalEvents -FeatureRoot $featureRootPath) } # VIEW-01a: the feature view counts every journal row; the slice view counts rows # carrying the slice id. Slice-scoped turn/verify/baseline stats exist only when a # slice is named. $journalCount = @($allEvents).Count $sliceEvents = @() $latestStart = $null $latestStop = $null $latestVerifyVerdict = 'none' $latestVerifySeq = 0 $baselineOk = $false $closeoutOk = $false if ($Slice) { $sliceEvents = @($allEvents | Where-Object { $_.slice -eq $Slice } | Sort-Object { [int](Get-HarnessProperty -Object $_ -Name 'seq' -Default 0) }) # Turn windows $starts = @($sliceEvents | Where-Object { (Get-HarnessProperty -Object $_ -Name 'kind') -eq 'ActivityStarted' }) $stops = @($sliceEvents | Where-Object { (Get-HarnessProperty -Object $_ -Name 'kind') -eq 'ActivityStopped' }) $latestStart = if ($starts.Count -gt 0) { $starts[-1] } else { $null } $latestStop = if ($stops.Count -gt 0) { $stops[-1] } else { $null } # Latest verify-local verdict from journal $verifyEvents = @($sliceEvents | Where-Object { (Get-HarnessProperty -Object $_ -Name 'kind') -eq 'SensorRunRecorded' ` -and (Get-HarnessProperty -Object (Get-HarnessProperty -Object $_ -Name 'payload') -Name 'name') -eq 'verify-local' }) $latestVerify = if ($verifyEvents.Count -gt 0) { $verifyEvents[-1] } else { $null } $latestVerifyVerdict = if ($latestVerify) { Get-HarnessProperty -Object $latestVerify -Name 'outcome' } else { 'none' } $latestVerifySeq = if ($latestVerify) { [int](Get-HarnessProperty -Object $latestVerify -Name 'seq' -Default 0) } else { 0 } # Baseline discipline quick-check (latest turn window) $baselineOk = $false $closeoutOk = $false if ($latestStart) { $latestStartSeq = [int](Get-HarnessProperty -Object $latestStart -Name 'seq' -Default 0) $baselineOk = Test-HarnessTurnBaselineSatisfied -OrderedEvents @($sliceEvents) -LatestStartSeq $latestStartSeq -FeatureRoot $featureRootPath if ($latestStop) { $latestStopSeq = [int](Get-HarnessProperty -Object $latestStop -Name 'seq' -Default 0) $beforeStop = @($sliceEvents | Where-Object { [int](Get-HarnessProperty -Object $_ -Name 'seq' -Default 0) -lt $latestStopSeq } | Select-Object -Last 1) if ($beforeStop.Count -gt 0) { $e = $beforeStop[0] $closeoutOk = ((Get-HarnessProperty -Object $e -Name 'kind') -eq 'SensorRunRecorded') ` -and ((Get-HarnessProperty -Object (Get-HarnessProperty -Object $e -Name 'payload') -Name 'name') -eq 'verify-local') ` -and ((Get-HarnessProperty -Object $e -Name 'outcome') -eq 'PASS') } } } } $viewJournalCount = if ($Slice) { @($sliceEvents).Count } else { $journalCount } # -- Projection ---------------------------------------------------------------- $loopPath = Join-Path $featureRootPath '.review-loop.json' $ownershipStatus = 'unknown' $phases = @{} $sliceShapeRepaired = $false $sliceMaterialized = $false if (Test-Path -LiteralPath $loopPath) { $loop = ConvertFrom-HarnessJson -Json (Get-Content $loopPath -Raw) $beforeId = $null $implBefore = Get-ImplementationPhase -LoopData $loop if ($implBefore -and $implBefore.slices) { $raw = @($implBefore.slices) | Select-Object -First 1 if ($raw) { $beforeId = Get-HarnessProperty -Object $raw -Name 'id' } } $loop = Repair-HarnessReviewLoopShapes -LoopData $loop $impl = Get-ImplementationPhase -LoopData $loop if ($impl) { $sliceEntry = @($impl.slices) | Where-Object { $_.id -eq $Slice } | Select-Object -First 1 if ($sliceEntry) { $sliceMaterialized = $true $ownershipStatus = if ($sliceEntry.ownership_status) { $sliceEntry.ownership_status } else { 'unknown' } foreach ($sp in @($sliceEntry.phases)) { $phases[$sp.id] = $sp.status } if ([string]::IsNullOrWhiteSpace([string]$beforeId) -and $sliceEntry.id -eq $Slice) { $sliceShapeRepaired = $true } } } } # -- Slice registration classification ------------------------------------------ # A slice named in a feature-root slice plan but absent from the projection is # planned-but-unmaterialized, not unknown: the sanctioned recovery is turn-start, # which materializes the projection shell through the journal. Never infer # authorization from a plan filename alone or hand-edit .review-loop.json. # The plan-token scan is shared with validate-review-loop via HarnessContract # (charter Phase 2 extraction; health consumes the same function). $outerLoopView = @(Get-HarnessOuterLoopPhaseView -FeatureRoot $featureRootPath -Events $allEvents) $invokePrefix = 'pwsh -File scripts/kaden/harness-invoke.ps1' $plannedNotMaterialized = $false if ($Slice -and -not $sliceMaterialized -and -not $sliceShapeRepaired) { $plannedNotMaterialized = Test-HarnessSliceNamedInPlan -FeatureRoot $featureRootPath -SliceId $Slice } $sliceRegistration = if (-not $Slice) { 'FEATURE_VIEW' } elseif ($sliceMaterialized) { 'MATERIALIZED' } elseif ($plannedNotMaterialized) { 'PLANNED_NOT_MATERIALIZED' } else { 'UNKNOWN' } # Recovery command placeholders (<cursor|copilot|claude> / <slug>) follow the recorder # example vocabulary; the operator or agent fills them for the active host. $nextCommand = $null if ($sliceRegistration -eq 'PLANNED_NOT_MATERIALIZED' ` -and (Test-HarnessFeatureSlug -Feature $Feature) ` -and (Test-HarnessFeatureSlug -Feature $Slice)) { $nextCommand = "$invokePrefix harness-turn-start.ps1 -Feature $Feature -Slice $Slice -Phase RED -ModelProvider <cursor|copilot|claude> -ModelSlug <slug>" } # -- Quick validation (non-RequireReviewReady) --------------------------------- $quickReport = Invoke-HarnessReviewLoopValidation -RepoRoot $RepoRoot -FeatureRoot $featureRootPath -SliceId $Slice ` -AllowPlannedPending -Events $allEvents $quickVerdict = $quickReport.verdict $quickFindings = @($quickReport.findings | ForEach-Object { $_.code }) # -- Summary output ------------------------------------------------------------ $turnOpen = ($null -ne $latestStart -and ($null -eq $latestStop -or [int](Get-HarnessProperty -Object $latestStart -Name 'seq' -Default 0) -gt [int](Get-HarnessProperty -Object $latestStop -Name 'seq' -Default 0))) $staleTurn = $false $staleDetail = $null if ($turnOpen) { $staleDetail = Get-HarnessStaleTurnDetail -FeatureRoot $featureRootPath -Slice $Slice -StaleTurnMinutes $StaleTurnMinutes $staleTurn = [bool]$staleDetail.Stale } Write-Output '' if (-not $Slice) { # Charter Phase 2 (VIEW-01a): feature-level outer-loop view for # outer-loop-only features - no slice scoping; the operator reads the view, # never the raw journal file (rows are journal-sourced and sanitized). Write-Output "=== Harness Health: $Feature (feature view) ===" Write-Output " Journal events : $journalCount" Write-Output " Outer loop:" foreach ($outerPhase in $outerLoopView) { $detailSuffix = '' if ($outerPhase.detail) { $detailSuffix = " ($($outerPhase.detail))" } Write-Output (" {0}: {1}{2}" -f $outerPhase.phase, $outerPhase.status, $detailSuffix) } } else { Write-Output "=== Harness Health: $Feature / $Slice ===" Write-Output " Ownership status : $ownershipStatus" Write-Output " Turn open : $turnOpen" if ($staleTurn) { $elapsed = [int][Math]::Floor([double]$staleDetail.ElapsedMinutes) $lastLabel = [string]$staleDetail.LastLabel if ([string]::IsNullOrWhiteSpace($lastLabel)) { $lastLabel = [string]$staleDetail.LastKind } Write-Output " STALE TURN : elapsed $elapsed min; last event $lastLabel" } Write-Output " Baseline OK : $baselineOk" Write-Output " Closeout OK : $closeoutOk" Write-Output " Latest verify : $latestVerifyVerdict (seq $latestVerifySeq)" Write-Output " Journal events : $($sliceEvents.Count)" Write-Output " Phase states : $(($phases.GetEnumerator() | Sort-Object Name | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join ', ')" } Write-Output " Quick validation : $quickVerdict$(if ($quickFindings.Count -gt 0) { " [$($quickFindings -join ', ')]" })" if ($turnOpen) { Write-Output " Operator view : $invokePrefix harness-timeline.ps1 -Feature $Feature -Slice $Slice" try { $processEval = Invoke-HarnessOwnershipSessionEval -FeatureRoot $featureRootPath -SliceId $Slice $hintCodes = @($processEval.findings | ForEach-Object { $_.code }) if ($processEval.verdict -eq 'FAIL' -and $hintCodes.Count -gt 0) { Write-Output " Process-eval hint: $($hintCodes -join ', ') (discretionary; not a review_ready gate)" } } catch { Write-Verbose "harness-health: process-eval hint unavailable: $($_.Exception.Message)" } } if ($sliceShapeRepaired) { Write-Output " NOTE : slice shell had null id / collapsed slices object -- repaired in memory from current_slice." Write-Output " Persist: node scripts/format-review-loop.mjs docs/harness/features/$Feature/.review-loop.json" } if ($Slice -and $sliceRegistration -eq 'PLANNED_NOT_MATERIALIZED') { Write-Output " Registration : PLANNED_NOT_MATERIALIZED - the slice is named in a slice plan but not yet materialized in the projection." Write-Output " NEXT : $nextCommand" } elseif ($Slice -and $sliceRegistration -eq 'UNKNOWN') { Write-Output " HINT : SLICE_NOT_FOUND means implementation.slices[] has no id='$Slice' and no slice plan names it." Write-Output " Do not treat as 'feature missing'. Check .review-loop.json shape (slices must be an array)," Write-Output " or materialize the slice through harness-turn-start." } Write-Output '' $result = [pscustomobject]@{ feature = $Feature slice = $Slice view = if ($Slice) { 'slice' } else { 'feature' } ownership_status = $ownershipStatus turn_open = $turnOpen stale_turn = $staleTurn stale_elapsed_minutes = if ($staleDetail) { [int][Math]::Floor([double]$staleDetail.ElapsedMinutes) } else { 0 } baseline_ok = $baselineOk closeout_ok = $closeoutOk latest_verify = $latestVerifyVerdict journal_count = $viewJournalCount phases = $phases outer_phases = $outerLoopView quick_verdict = $quickVerdict quick_findings = $quickFindings slice_shape_repaired = $sliceShapeRepaired slice_registration = $sliceRegistration next_command = $nextCommand } $result | ConvertTo-Json -Depth 5 |