#Requires -Version 5.1
<#
.SYNOPSIS
    day-probe (W-1 day-wake, adopt hub 2026-07-28 "probe-first-resume"; owner YC-021 @S185).
    READ-ONLY probe deciding CHEAP-WAKE vs FULL-RESUME. Prints EXACTLY one DAY-PROBE line.
    ALWAYS exit 0 (INFORM layer - the /day command reads the verdict word, nothing blocks).

.DESCRIPTION
    Floor F1: pick the LAST session marker by the ts WRITTEN INSIDE the file (never mtime -
    Dropbox resync touches mtime). Then check 4 delta signals:
      (a) commits after the anchor        (git rev-list count)
      (b) tracked/untracked dirty files   (git status --porcelain, no allowlist - lean DELTA)
      (c) is covered by (a)+(b): run-folders/artifacts land as commits or dirty files
      (d) closing: sentinel in .claude/WAL.md (San-2)
    Floor F3: anchor = the commit that ADDED the marker file (the stop-seal commit), NOT the
    head-sha inside the marker (trap-one: head-sha predates the seal commit => false DELTA
    100% of the time). Fallback = head-sha field. Anchor must EXIST (cat-file -e) AND be an
    ANCESTOR of HEAD (merge-base --is-ancestor); orphaned (post-squash) => verdict ANCHOR-LOST
    printed LOUDLY (trap-two: fail-safe must have a voice, not die silent).
    Verdict precedence: ANCHOR-LOST > DELTA > CLEAN. Leaning rule: any probe error => DELTA.
    Floor F4 lives in /day (the _day-<i>.md marker); this script only measures.

.PARAMETER RepoRoot
    Repo root; a temp tree here isolates fault-injection (mirror governance-detectors.ps1).
#>
[CmdletBinding()]
param([string]$RepoRoot = (Get-Location).Path)

$sw = [System.Diagnostics.Stopwatch]::StartNew()
$ErrorActionPreference = 'SilentlyContinue'

function Out-Line([string]$verdict, [string]$detail) {
    $script:sw.Stop()
    Write-Host ("DAY-PROBE: {0} | {1} | {2}ms" -f $verdict, $detail, $script:sw.ElapsedMilliseconds)
    exit 0
}

try {
    $sessRoot = Join-Path $RepoRoot '.claude\sessions'
    if (-not (Test-Path $sessRoot)) { Out-Line 'DELTA' 'probe-loi: no sessions dir (lean DELTA)' }

    # newest logic-session = highest N with a _context file
    $sess = Get-ChildItem -Path $sessRoot -Directory -Filter 'session-*' |
        Where-Object { Test-Path (Join-Path $_.FullName ("_context-s-" + ($_.Name -replace '^session-','') + ".md")) } |
        Sort-Object { [int]($_.Name -replace '^session-','') } -Descending | Select-Object -First 1
    if ($null -eq $sess) { Out-Line 'DELTA' 'probe-loi: no logic-session found (lean DELTA)' }

    # last marker by ts INSIDE file (F1). _day markers count too (repeat wakes).
    $markers = @()
    foreach ($f in (Get-ChildItem -Path $sess.FullName -File | Where-Object { $_.Name -match '^_(pause|tiep|snapshot|day)-\d+\.md$' })) {
        $ts = $null; $sha = $null
        foreach ($ln in (Get-Content -Path $f.FullName -TotalCount 8 -Encoding UTF8)) {
            if ($ln -match '^ts:\s*(\S+)') { $ts = $Matches[1] }
            if ($ln -match '^head-sha:\s*([0-9a-f]{7,40})') { $sha = $Matches[1] }
        }
        if ($null -ne $ts) { $markers += [pscustomobject]@{ File = $f.FullName; Name = $f.Name; Ts = $ts; Sha = $sha } }
    }
    if ($markers.Count -eq 0) { Out-Line 'DELTA' ('no marker with in-file ts under ' + $sess.Name + ' (lean DELTA)') }
    $last = $markers | Sort-Object Ts -Descending | Select-Object -First 1

    # F3 anchor: the commit that ADDED the marker (stop-seal), fallback in-file head-sha
    Push-Location $RepoRoot
    try {
        $rel = (Resolve-Path -Relative $last.File) -replace '^\.\\', '' -replace '\\', '/'
        $anchor = (& git log --diff-filter=A --format=%H -1 -- $rel 2>$null | Select-Object -First 1)
        $src = 'seal-commit'
        if ([string]::IsNullOrWhiteSpace($anchor)) { $anchor = $last.Sha; $src = 'in-file-sha' }
        if ([string]::IsNullOrWhiteSpace($anchor)) { Out-Line 'DELTA' ('marker ' + $last.Name + ' has no resolvable anchor (lean DELTA)') }

        & git cat-file -e ($anchor + '^{commit}') 2>$null
        if ($LASTEXITCODE -ne 0) {
            Out-Line 'ANCHOR-LOST' ('anchor=' + $anchor.Substring(0,[Math]::Min(8,$anchor.Length)) + ' (' + $src + ') MISSING from object store - marker=' + $last.Name + ' - route: FULL /tiep (loud fail-safe, F3 trap-two)')
        }
        & git merge-base --is-ancestor $anchor HEAD 2>$null
        if ($LASTEXITCODE -ne 0) {
            Out-Line 'ANCHOR-LOST' ('anchor=' + $anchor.Substring(0,8) + ' (' + $src + ') exists but NOT ancestor of HEAD (post-squash orphan) - marker=' + $last.Name + ' - route: FULL /tiep')
        }

        # signals
        $nCommits = [int](& git rev-list --count ($anchor + '..HEAD') 2>$null)
        $dirty = @(& git status --porcelain 2>$null | Where-Object { $_ -and $_.Trim().Length -gt 0 })
        $nDirty = $dirty.Count
        $walP = Join-Path $RepoRoot '.claude\WAL.md'
        $nClosing = 0
        if (Test-Path $walP) { $nClosing = @(Select-String -Path $walP -Pattern '^closing:' -Encoding UTF8).Count }

        $detail = ('anchor=' + $anchor.Substring(0,8) + '(' + $src + ') alive+ancestor | commits-sau=' + $nCommits + ' | dirty=' + $nDirty + ' | closing=' + $nClosing + ' | marker=' + $last.Name + ' ts=' + $last.Ts)
        if ($nCommits -eq 0 -and $nDirty -eq 0 -and $nClosing -eq 0) { Out-Line 'CLEAN' $detail }
        Out-Line 'DELTA' $detail
    }
    finally { Pop-Location }
}
catch {
    Out-Line 'DELTA' ('probe-loi: ' + $_.Exception.Message.Substring(0, [Math]::Min(80, $_.Exception.Message.Length)) + ' (lean DELTA)')
}
