scripts/internal/continuous-co-review/test-evidence-recorder.ps1

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

# T111 (DEC-197-I010-004, maintainer send-back 2026-07-08): implementer TEST EVIDENCE for the reviewer.
#
# ROOT CAUSE this closes: the outbound budget policy told the reviewer to "use implementer validation
# evidence first" while the worktree carried NO such evidence, and the falsification stance (rightly)
# forbids trusting prose claims - so on a large change-set the reviewer re-ran the full test pyramid
# and died at the harness budget ceiling (four consecutive budget/quota deaths on 2026-07-08).
#
# The fix: the implementer's ACTUAL test runs are recorded here as MACHINE-OBSERVED evidence
# (suite, counts, exit code, duration, timestamp) bound to the reviewed-state DIGEST of the tree the
# tests ran against. The orchestrator injects the record into the reviewer worktree ONLY on an exact
# digest match (.review/implementer-evidence.json), and the slim prompt lets digest-matched evidence
# substitute for re-running those suites. Hand-written claims never gain evidence standing - only
# this recorder's output does, and only for the exact tree under review (a post-evidence edit changes
# the digest and orphans the record). Evidence lives under .specrew/review/ (digest-EXCLUDED runtime
# state), so recording evidence never changes the tree identity it certifies.

function Get-ContinuousCoReviewTestEvidenceDirectory {
    param([Parameter(Mandatory)][string]$RepoRoot)
    return (Join-Path $RepoRoot '.specrew/review/test-evidence')
}

function Write-ContinuousCoReviewTestEvidence {
    <#
        Records one MACHINE-OBSERVED suite run against the CURRENT working-tree digest. Call this
        right after the real test invocation, in the same session, with the numbers the runner
        reported (never hand-typed). FAIL-SOFT by contract: the recorder must never break a test
        run - any internal failure warns and returns $null.
    #>

    param(
        [Parameter(Mandatory)][string]$RepoRoot,
        [Parameter(Mandatory)][string]$Suite,
        [Parameter(Mandatory)][int]$Passed,
        [int]$Failed = 0,
        [int]$Skipped = 0,
        [Parameter(Mandatory)][int]$ExitCode,
        [double]$DurationSeconds = 0,
        [string]$Command,
        [datetime]$Now = [datetime]::UtcNow
    )
    try {
        $resolved = (Resolve-Path -LiteralPath $RepoRoot).Path
        if (-not (Get-Command -Name 'Get-ContinuousCoReviewReviewedStateDigest' -ErrorAction SilentlyContinue)) {
            $lp = Join-Path $PSScriptRoot '_load.ps1'
            if (Test-Path -LiteralPath $lp -PathType Leaf) { . $lp }
        }
        $dg = Get-ContinuousCoReviewReviewedStateDigest -RepoRoot $resolved
        if ($null -eq $dg -or -not [bool]$dg.ok) {
            $reason = if ($null -ne $dg) { [string]$dg.failure_reason } else { 'digest-unavailable' }
            [Console]::Error.WriteLine("[co-review] WARN TEST_EVIDENCE_NOT_RECORDED reviewed-state digest failed ($reason); the run stays valid but leaves no reviewer evidence.")
            return $null
        }
        $treeId = [string]$dg.tree_id

        $dir = Get-ContinuousCoReviewTestEvidenceDirectory -RepoRoot $resolved
        if (-not (Test-Path -LiteralPath $dir -PathType Container)) {
            $null = New-Item -ItemType Directory -Path $dir -Force
        }
        $path = Join-Path $dir ($treeId + '.json')

        $record = $null
        if (Test-Path -LiteralPath $path -PathType Leaf) {
            try { $record = Get-Content -LiteralPath $path -Raw | ConvertFrom-Json } catch { $record = $null }
        }
        if ($null -eq $record -or -not ($record.PSObject.Properties.Name -contains 'suites')) {
            $record = [pscustomobject]@{
                schema_version          = '1.0'
                reviewed_digest_tree_id = $treeId
                recorded_at             = $Now.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
                suites                  = @()
            }
        }

        $entry = [pscustomobject]@{
            suite                   = $Suite
            passed                  = $Passed
            failed                  = $Failed
            skipped                 = $Skipped
            exit_code               = $ExitCode
            duration_seconds        = [math]::Round($DurationSeconds, 3)
            command                 = if ([string]::IsNullOrWhiteSpace($Command)) { $null } else { $Command }
            # EMBEDDED digest identity (review finding f3, run 20260714T172315119): every embedded entry
            # carries the digest it certifies, so the injectable check can enforce envelope AND embedded.
            reviewed_digest_tree_id = $treeId
            recorded_at             = $Now.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
        }
        # Same-suite re-record REPLACES (the latest run for this exact tree wins); others accumulate.
        $kept = @(@($record.suites) | Where-Object { $null -ne $_ -and [string]$_.suite -ne $Suite })
        $record.suites = @($kept + $entry)
        $record.recorded_at = $entry.recorded_at

        [System.IO.File]::WriteAllText($path, ($record | ConvertTo-Json -Depth 8))
        return $record
    }
    catch {
        [Console]::Error.WriteLine("[co-review] WARN TEST_EVIDENCE_NOT_RECORDED $($_.Exception.Message)")
        return $null
    }
}

function Get-ContinuousCoReviewTestEvidenceForDigest {
    <# The digest-keyed lookup: returns the record ONLY when it certifies exactly this tree id and carries at least
       one non-empty evidence array. Accepts BOTH the T111 `suites` array (implementer-supplied counts) AND the T018
       `runs` array (universal recorded-run records written by Invoke-ContinuousCoReviewRecordedRun) - the T019
       step-6 unblock so exact-digest recorded runs are injectable as reviewer-visible evidence, not just suites. #>

    param(
        [Parameter(Mandatory)][string]$RepoRoot,
        [Parameter(Mandatory)][string]$DigestTreeId
    )
    $dir = Get-ContinuousCoReviewTestEvidenceDirectory -RepoRoot $RepoRoot
    $path = Join-Path $dir ($DigestTreeId + '.json')
    if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { return $null }
    try {
        $record = Get-Content -LiteralPath $path -Raw | ConvertFrom-Json
        if ($null -eq $record) { return $null }
        if (-not ($record.PSObject.Properties.Name -contains 'reviewed_digest_tree_id')) { return $null }
        if ([string]$record.reviewed_digest_tree_id -ne $DigestTreeId) { return $null }
        $hasSuites = ($record.PSObject.Properties.Name -contains 'suites') -and (@($record.suites).Count -gt 0)
        $hasRuns = ($record.PSObject.Properties.Name -contains 'runs') -and (@($record.runs).Count -gt 0)
        if (-not ($hasSuites -or $hasRuns)) { return $null }
        # ENVELOPE AND EVERY EMBEDDED DIGEST (review finding f3, run 20260714T172315119 - the binding T019
        # rule Test-ContinuousCoReviewEvidenceInjectable encodes): a record keyed for digest B carrying an
        # entry recorded at digest A (or carrying NO embedded identity) is REFUSED - fail closed, so the
        # reviewer gets NO evidence rather than wrong evidence. Legacy suite entries without an embedded
        # digest retire with their orphaned digests; the current writers stamp every entry.
        if (-not (Get-Command -Name 'Test-ContinuousCoReviewEvidenceInjectable' -ErrorAction SilentlyContinue)) {
            $ric = Join-Path $PSScriptRoot 'review-identity-contracts.ps1'
            if (Test-Path -LiteralPath $ric -PathType Leaf) { . $ric }
        }
        if (-not (Get-Command -Name 'Test-ContinuousCoReviewEvidenceInjectable' -ErrorAction SilentlyContinue)) { return $null }   # cannot validate -> cannot inject
        $embedded = @()
        if ($hasSuites) { foreach ($s in @($record.suites)) { $embedded += [string]$(if ($null -ne $s -and ($s.PSObject.Properties.Name -contains 'reviewed_digest_tree_id')) { $s.reviewed_digest_tree_id } else { '' }) } }
        if ($hasRuns) { foreach ($r in @($record.runs)) { $embedded += [string]$(if ($null -ne $r -and ($r.PSObject.Properties.Name -contains 'reviewed_digest_tree_id')) { $r.reviewed_digest_tree_id } else { '' }) } }
        $inj = Test-ContinuousCoReviewEvidenceInjectable -EnvelopeDigest ([string]$record.reviewed_digest_tree_id) -ReviewDigest $DigestTreeId -EmbeddedDigests $embedded
        if (-not [bool]$inj.injectable) {
            [Console]::Error.WriteLine("[co-review] WARN EVIDENCE_NOT_INJECTABLE $($inj.classification) for digest $DigestTreeId - refusing the record (fail closed; no evidence beats wrong evidence).")
            return $null
        }
        return $record
    }
    catch { return $null }
}

function Copy-ContinuousCoReviewImplementerEvidence {
    <#
        Projects digest-matched evidence to a controller-owned reviewer-readable path. The legacy
        WorktreePath form remains for older callers; production campaigns pass OutputPath outside
        the frozen target. Returns $true only when a matching record was projected.
    #>

    param(
        [Parameter(Mandatory)][string]$RepoRoot,
        [string]$WorktreePath,
        [string]$OutputPath,
        [AllowEmptyString()][string]$DigestTreeId,
        # The ACTUAL SELECTED VerificationPlan (the FR-049 supplier seam; maintainer wiring directive
        # 2026-07-15): plan-run joinability is enforced against ITS command set and NOTHING else - the
        # validating plan is NEVER derived from the evidence itself. Absent (no supplier configured), every
        # plan-identified run is withheld fail-closed as `selected-plan-unavailable`.
        [AllowNull()]$Plan = $null
    )
    try {
        if ([string]::IsNullOrWhiteSpace($DigestTreeId)) { return $false }
        $record = Get-ContinuousCoReviewTestEvidenceForDigest -RepoRoot $RepoRoot -DigestTreeId $DigestTreeId
        if ($null -eq $record) { return $false }
        if ([string]::IsNullOrWhiteSpace($OutputPath)) {
            if ([string]::IsNullOrWhiteSpace($WorktreePath)) { return $false }
            $reviewDir = Join-Path $WorktreePath '.review'
            if (-not (Test-Path -LiteralPath $reviewDir -PathType Container)) { return $false }
            $OutputPath = Join-Path $reviewDir 'implementer-evidence.json'
        }
        $outputFull = [IO.Path]::GetFullPath($OutputPath)
        $outputParent = [IO.Path]::GetDirectoryName($outputFull)
        if (-not [IO.Directory]::Exists($outputParent) -or [IO.File]::Exists($outputFull)) { return $false }

        # FR-048 JOIN AT THE INJECTION BOUNDARY (review finding f1, run 20260714T201103653; maintainer wiring
        # directive 2026-07-15): the duplicate / joinability contract gates the PRODUCTION copy, not only
        # tests. Plan-identified runs are joined via Test-ContinuousCoReviewPlanEvidenceInjectable against the
        # ACTUAL SELECTED VerificationPlan the caller passes - NEVER a plan derived from the evidence itself
        # (self-derivation would validate any smuggled command_id against its own record). When plan-identified
        # evidence exists and NO selected plan is available, every such run is WITHHELD fail-closed as
        # `selected-plan-unavailable`. Non-injectable runs are surfaced in `withheld_runs` (a reviewer-visible
        # refusal, never a silent drop); attempt-superseded HISTORY stays visible (finding f2: a failure never
        # becomes missing evidence). Identity-less self-evidence runs are not plan evidence and pass through
        # under the envelope + embedded-digest checks the lookup already enforced. Fail-closed if the join
        # validator is missing.
        if (($record.PSObject.Properties.Name -contains 'runs') -and ($null -ne $record.runs)) {
            $idRuns = @(@($record.runs) | Where-Object { $null -ne $_ -and (@($_.PSObject.Properties.Name) -contains 'command_id') -and -not [string]::IsNullOrWhiteSpace([string]$_.command_id) })
            if ($idRuns.Count -gt 0) {
                $joined = $null
                if ($null -ne $Plan) {
                    if (-not (Get-Command -Name 'Test-ContinuousCoReviewPlanEvidenceInjectable' -ErrorAction SilentlyContinue)) {
                        $vpc = Join-Path $PSScriptRoot 'verification-plan-contract.ps1'
                        if (Test-Path -LiteralPath $vpc -PathType Leaf) { . $vpc }
                    }
                    if (-not (Get-Command -Name 'Test-ContinuousCoReviewPlanEvidenceInjectable' -ErrorAction SilentlyContinue)) {
                        [Console]::Error.WriteLine('[co-review] WARN IMPLEMENTER_EVIDENCE_NOT_INJECTED plan-evidence join validator unavailable; refusing to inject unjoined plan runs.')
                        return $false
                    }
                    $joined = @(Test-ContinuousCoReviewPlanEvidenceInjectable -PlanEvidence $idRuns -Plan $Plan -CurrentDigest $DigestTreeId)
                }
                $withheld = @()
                $keepRuns = New-Object System.Collections.Generic.List[object]
                foreach ($r in @($record.runs)) {
                    if ($null -eq $r) { continue }
                    $rid = if (@($r.PSObject.Properties.Name) -contains 'command_id') { [string]$r.command_id } else { '' }
                    if ([string]::IsNullOrWhiteSpace($rid)) { $keepRuns.Add($r) | Out-Null; continue }
                    if ($null -eq $Plan) {
                        # plan-identified evidence with NO selected plan: fail-closed, surfaced.
                        $withheld += [pscustomobject]@{ command_id = $rid; classification = 'selected-plan-unavailable' }
                        continue
                    }
                    $jidx = [Array]::IndexOf($idRuns, $r)
                    $cls = if ($jidx -ge 0 -and $jidx -lt $joined.Count) { [string]$joined[$jidx].classification } else { 'unjoined' }
                    if ($cls -in @('exact-digest-command-joined', 'attempt-superseded-history')) { $keepRuns.Add($r) | Out-Null }
                    else { $withheld += [pscustomobject]@{ command_id = $rid; classification = $cls } }
                }
                if (@($withheld).Count -gt 0) {
                    foreach ($w in $withheld) { [Console]::Error.WriteLine(("[co-review] WARN PLAN_RUN_WITHHELD command_id '{0}' ({1}) - refused at the injection boundary (FR-048 join)." -f $w.command_id, $w.classification)) }
                    $record = $record | Select-Object *   # shallow copy: the origin-side durable record stays untouched
                    $record.runs = $keepRuns.ToArray()
                    $record | Add-Member -NotePropertyName 'withheld_runs' -NotePropertyValue @($withheld) -Force
                    $hasSuitesLeft = ($record.PSObject.Properties.Name -contains 'suites') -and (@($record.suites).Count -gt 0)
                    if ((@($record.runs).Count -eq 0) -and (-not $hasSuitesLeft)) { return $false }
                }
            }
        }
        # FR-009/SC-002 ORIGIN-PATH HYGIENE (review finding f5, run 20260714T190233598): the injected COPY is
        # reviewer-visible, so every origin-absolute path in it (working_directory, argument vectors like a
        # docker volume mount, artifact paths) is relativized to '<project>' against the governance AND git
        # origin roots - identity hygiene plus no origin route for the confined reviewer. The origin-side
        # durable record is untouched. Fail-closed: if the relativizer is unavailable, inject NOTHING (a leak
        # is worse than absent evidence).
        if (-not (Get-Command -Name 'ConvertTo-ContinuousCoReviewOriginRelativized' -ErrorAction SilentlyContinue)) {
            $wr = Join-Path $PSScriptRoot 'worktree-reviewer.ps1'
            if (Test-Path -LiteralPath $wr -PathType Leaf) { . $wr }
        }
        if (-not (Get-Command -Name 'ConvertTo-ContinuousCoReviewOriginRelativized' -ErrorAction SilentlyContinue)) {
            [Console]::Error.WriteLine('[co-review] WARN IMPLEMENTER_EVIDENCE_NOT_INJECTED origin-relativizer unavailable; refusing to inject an unscrubbed evidence copy.')
            return $false
        }
        $originRoots = @((Resolve-Path -LiteralPath $RepoRoot).Path)
        try {
            $gitTop = (& git -C $RepoRoot rev-parse --show-toplevel 2>$null)
            if (-not [string]::IsNullOrWhiteSpace([string]$gitTop)) { $originRoots += ([string]$gitTop) }
        }
        catch { $null = $_ }
        $scrubbed = ConvertTo-ContinuousCoReviewOriginRelativized -Content ($record | ConvertTo-Json -Depth 8) -OriginRoots $originRoots
        $stream = [IO.File]::Open($outputFull, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::Read)
        try {
            $bytes = [Text.UTF8Encoding]::new($false).GetBytes($scrubbed)
            $stream.Write($bytes, 0, $bytes.Length)
            $stream.Flush($true)
        }
        finally { $stream.Dispose() }
        return $true
    }
    catch {
        [Console]::Error.WriteLine("[co-review] WARN IMPLEMENTER_EVIDENCE_NOT_INJECTED $($_.Exception.Message)")
        return $false
    }
}

# ============================ T018 — universal recorded-run runner (FR-015 amended 2026-07-13) ============================
# LANGUAGE/FRAMEWORK-NEUTRAL: executes ANY declared verification command and records only DIRECT observations, bound to
# the exact reviewed-tree digest. Rich test counts come ONLY from a schema-valid SpecrewTestResult the command PRODUCED
# during the run (never from console-output parsing; there is NO caller-supplied-count parameter). FAIL-LOUD. The
# Specrew self-review is just ONE downstream - the core runner carries NO Pester/pytest/Jest/etc. knowledge.

function Test-ContinuousCoReviewSpecrewTestResult {
    # Validate a parsed object against contracts/specrew-test-result.schema.json (lightweight in-code validator - no
    # external JSON-schema engine). Returns @{ valid; reason; result; counts }.
    param([Parameter(Mandatory)][AllowNull()]$Object)
    if ($null -eq $Object) { return @{ valid = $false; reason = 'null-result' } }
    if ($Object -isnot [System.Management.Automation.PSCustomObject]) { return @{ valid = $false; reason = 'not-an-object' } }
    $props = @($Object.PSObject.Properties.Name)
    if (@($props | Where-Object { $_ -notin @('schema_version', 'result', 'counts') }).Count -gt 0) { return @{ valid = $false; reason = 'unknown-property' } }
    if (($props -notcontains 'schema_version') -or ([string]$Object.schema_version -ne '1.0')) { return @{ valid = $false; reason = 'bad-schema_version' } }
    if ($props -notcontains 'result') { return @{ valid = $false; reason = 'missing-result' } }
    if (([string]$Object.result) -notin @('passed', 'failed', 'errored', 'skipped')) { return @{ valid = $false; reason = 'bad-result-enum' } }
    $counts = $null
    if ($props -contains 'counts') {
        # PRESENCE-based null rejection (review finding f3, run 20260714T180554025): the schema types counts
        # as an OBJECT when present - a present-but-null counts is schema-INVALID, never treated as absent
        # (which would grant structured-result standing to a malformed artifact).
        if ($null -eq $Object.counts) { return @{ valid = $false; reason = 'counts-null-not-object' } }
        if ($Object.counts -isnot [System.Management.Automation.PSCustomObject]) { return @{ valid = $false; reason = 'counts-not-an-object' } }
        $cprops = @($Object.counts.PSObject.Properties.Name)
        if (@($cprops | Where-Object { $_ -notin @('passed', 'failed', 'skipped') }).Count -gt 0) { return @{ valid = $false; reason = 'unknown-count' } }
        $counts = [ordered]@{}
        foreach ($k in @('passed', 'failed', 'skipped')) {
            if ($cprops -contains $k) {
                $v = $Object.counts.$k
                # BEYOND the authoritative maximum (review finding f1, run 20260714T193411985): PS parses a
                # JSON integer past Int64.MaxValue as BigInteger. The contract now carries an EXPLICIT
                # maximum (Int64.MaxValue) so every schema-valid count is representable + serializable
                # verbatim; a beyond-range count is a NAMED contract violation, never a confusing
                # non-integer rejection and never a lossy narrowing.
                if ($v -is [System.Numerics.BigInteger]) {
                    if ($v -lt 0) { return @{ valid = $false; reason = "negative-count:$k" } }
                    return @{ valid = $false; reason = "count-exceeds-authoritative-maximum:$k" }
                }
                if (($v -isnot [int]) -and ($v -isnot [long])) { return @{ valid = $false; reason = "non-integer-count:$k" } }
                if ([long]$v -lt 0) { return @{ valid = $false; reason = "negative-count:$k" } }
                # PRESERVE the schema-valid range verbatim (finding f3, run 20260714T180554025): a count
                # beyond Int32 is recorded as-is - narrowing to [int] threw instead of recording.
                $counts[$k] = [long]$v
            }
        }
    }
    return @{ valid = $true; reason = 'ok'; result = [string]$Object.result; counts = $counts }
}

# ENGINE CAP on any persisted output tail (maintainer decision 2026-07-14): no caller - and no authorized
# diagnostic disclosure - can persist more than this many bytes of (redacted) output text per stream.
$script:ContinuousCoReviewMaxOutputTailBytes = 8192
function Get-ContinuousCoReviewMaxOutputTailBytes { return $script:ContinuousCoReviewMaxOutputTailBytes }

function Test-ContinuousCoReviewDiagnosticDisclosure {
    <#
        Validate a HUMAN-AUTHORIZED diagnostic-disclosure request (maintainer decision 2026-07-14, run
        20260714T130410888 finding f1): raw verification output is PRIVATE BY DEFAULT; a reviewer who cannot
        reach an accurate conclusion without diagnostics may request disclosure, and a HUMAN authorizes it.
        The object is REQUIRED to carry: authorized_by (the human identity), reason (why the absence blocks
        an accurate conclusion), and command_id (the ONE named command it is scoped to). max_tail_bytes is
        optional and always clamped to the engine cap. Returns { valid; reason; authorized_by;
        disclosure_reason; command_id; max_tail_bytes }.
    #>

    param([Parameter(Mandatory)][AllowNull()]$Disclosure)
    if ($null -eq $Disclosure) { return [pscustomobject]@{ valid = $false; reason = 'disclosure is null' } }
    $get = {
        param($Name)
        if ($Disclosure -is [System.Collections.IDictionary]) { if ($Disclosure.Contains($Name)) { return $Disclosure[$Name] } return $null }
        $p = $Disclosure.PSObject.Properties[$Name]; if ($null -ne $p) { return $p.Value } return $null
    }
    $by = [string](& $get 'authorized_by')
    if ([string]::IsNullOrWhiteSpace($by)) { return [pscustomobject]@{ valid = $false; reason = 'authorized_by is required (the HUMAN who authorized the disclosure; disclosure is never automatic)' } }
    $why = [string](& $get 'reason')
    if ([string]::IsNullOrWhiteSpace($why)) { return [pscustomobject]@{ valid = $false; reason = 'reason is required (why the absence of diagnostics prevents an accurate conclusion)' } }
    $cid = [string](& $get 'command_id')
    if ([string]::IsNullOrWhiteSpace($cid)) { return [pscustomobject]@{ valid = $false; reason = 'command_id is required (disclosure is scoped to ONE named command, never blanket)' } }
    $cap = Get-ContinuousCoReviewMaxOutputTailBytes
    $reqBytes = & $get 'max_tail_bytes'
    $bytes = $cap
    if ($null -ne $reqBytes) {
        try { $bytes = [int]$reqBytes } catch { return [pscustomobject]@{ valid = $false; reason = 'max_tail_bytes must be an integer when supplied' } }
        if ($bytes -le 0) { return [pscustomobject]@{ valid = $false; reason = 'max_tail_bytes must be positive (a zero/negative disclosure discloses nothing - omit the disclosure instead)' } }
        if ($bytes -gt $cap) { $bytes = $cap }   # BOUNDED: a request can never buy an unbounded dump.
    }
    return [pscustomobject]@{ valid = $true; reason = $null; authorized_by = $by; disclosure_reason = $why; command_id = $cid; max_tail_bytes = $bytes }
}

function Get-ContinuousCoReviewRedactedOutputText {
    # SECRET-PATTERN REDACTION for output text destined for a persisted, reviewer-visible record (review finding
    # f3, run 20260714T123137002): credential-shaped content is masked BEFORE serialization so a command that
    # prints a token/password/credential-URL does not persist it into digest-bound evidence. Pattern-based
    # redaction is inherently a best-effort layer - the STRUCTURAL guarantees are the child-environment
    # allowlist (secrets never reach a plan child) and full tail SUPPRESSION for supplier-declared plan
    # commands (-TailBytes 0); this masks the recognizable credential shapes in the tails that remain.
    param([AllowNull()][string]$Text)
    if ([string]::IsNullOrEmpty($Text)) { return '' }
    $t = [string]$Text
    # KEY=VALUE / KEY: VALUE where the key smells like a credential - keep the name, mask the value.
    $t = [regex]::Replace($t, '(?im)([A-Z0-9_\-\.]*(?:TOKEN|SECRET|PASSWORD|PASSWD|PWD|API[_-]?KEY|ACCESS[_-]?KEY|CLIENT[_-]?SECRET|CREDENTIAL|AUTH)[A-Z0-9_\-\.]*\s*[=:]\s*)(\S+)', '$1[redacted]')
    # Authorization headers + bearer/basic tokens.
    $t = [regex]::Replace($t, '(?im)\b(authorization\s*:\s*)(\S.*)$', '$1[redacted]')
    $t = [regex]::Replace($t, '(?i)\b(bearer|basic)\s+[A-Za-z0-9+/=_\-\.]{8,}', '$1 [redacted]')
    # URL userinfo credentials (scheme://user:password@host).
    $t = [regex]::Replace($t, '(?i)\b([a-z][a-z0-9+.\-]*://[^/\s:@]+):([^@\s/]+)@', '$1:[redacted]@')
    return $t
}

function Get-ContinuousCoReviewBoundedOutputMeta {
    # BOUNDED + REDACTED metadata for a captured output stream: byte count + sha256 + a bounded, REDACTED tail -
    # never the full raw dump (an unbounded reviewer-visible artifact + potential secret leak). TailBytes <= 0 is
    # the SUPPRESSION mode (review finding f3; the DEFAULT since the 2026-07-14 maintainer decision): NO output
    # text is persisted at all - count/hash only - because arbitrary output may carry secrets no pattern can
    # recognize. byte_count/sha256 always describe the RAW output (the integrity facts); the tail, when present,
    # is a REDACTED excerpt, so it is diagnostic text - not a hash preimage. tail_disclosure states HONESTLY what
    # happened to the text: 'suppressed' (private by default), 'bounded-redacted-tail' (an explicit caller
    # opt-in), or 'authorized-diagnostic' (a human-authorized, command-scoped disclosure; potentially sensitive).
    param([AllowNull()][string]$Text, [int]$TailBytes = 0, [string]$TailDisclosureLabel = 'bounded-redacted-tail')
    $t = if ($null -eq $Text) { '' } else { [string]$Text }
    $bytes = [System.Text.Encoding]::UTF8.GetBytes($t)
    $sha = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($bytes)).Replace('-', '').ToLowerInvariant()
    if ($TailBytes -le 0) {
        return [ordered]@{ byte_count = $bytes.Length; sha256 = $sha; truncated = ($bytes.Length -gt 0); truncated_tail = ''; tail_disclosure = 'suppressed' }
    }
    $cap = Get-ContinuousCoReviewMaxOutputTailBytes
    if ($TailBytes -gt $cap) { $TailBytes = $cap }   # ENGINE CAP: no caller persists an unbounded tail.
    $truncated = $bytes.Length -gt $TailBytes
    $rawTail = if (-not $truncated) { $t } else { [System.Text.Encoding]::UTF8.GetString($bytes, $bytes.Length - $TailBytes, $TailBytes) }
    return [ordered]@{ byte_count = $bytes.Length; sha256 = $sha; truncated = $truncated; truncated_tail = (Get-ContinuousCoReviewRedactedOutputText -Text $rawTail); tail_disclosure = $TailDisclosureLabel }
}

# ============================ HARNESS / CORE SEAM (harness-purity refactor 2026-07-15) ============================
# The recorded-run runner was one function doing seven jobs (spawn, bounded drain, result lifecycle, artifact
# digesting, identity tagging, record assembly, persistence) - high cyclomatic complexity + harness (process/IO)
# welded to the CORE honesty semantics, so the observation semantics could not be unit-tested without spawning a
# real process. It is now split:
# - Invoke-ContinuousCoReviewBoundedProcess : the HARNESS primitive - launch + bounded-memory concurrent drain,
# returns RAW observed facts (exit/timing + byte counts/hashes/bounded raw tails). No honesty semantics.
# - Get-ContinuousCoReviewOutputMetaFromFacts + New-ContinuousCoReviewRunRecordObject : the PURE core - map raw
# facts to the durable record (redaction, disclosure labeling, command_succeeded, classification, identity).
# No process, no clock, no filesystem - fully unit-testable over synthetic facts.
# - Invoke-ContinuousCoReviewRecordedRun : thin glue - digest-bind, stale-result guard, call the harness, read
# + validate the result file, digest artifacts, call the pure assembler, persist. The observable behavior is
# byte-for-byte the pre-refactor behavior (the existing recorded-run + plan-runner suites are the safety net;
# new pure-core suites cover the assembler without spawning).

function Invoke-ContinuousCoReviewBoundedProcess {
    <#
        HARNESS PRIMITIVE (no honesty semantics): launch a process and drain BOTH pipes with BOUNDED MEMORY -
        a running UTF-8 byte count, an incremental SHA-256 (surrogate-pair carry keeps whole-stream hash
        fidelity), and a bounded shift-buffer holding only the last TailBytes bytes (everything past the bounds
        is read + DISCARDED so the child never blocks). Kills the whole process tree on the deadline. Returns
        RAW observed facts only: { exit_code; timed_out; duration_seconds; stdout=@{byte_count;sha256;truncated;
        raw_tail}; stderr=@{...} }. The raw_tail is UNREDACTED bounded diagnostic text - redaction/disclosure
        labeling is a CORE concern applied downstream, never here. FAILS LOUD only on process START failure.
    #>

    param(
        [Parameter(Mandatory)][string]$Executable,
        [string[]]$Arguments = @(),
        [Parameter(Mandatory)][string]$WorkingDirectory,
        [int]$TimeoutSeconds = 0,
        [System.Collections.IDictionary]$ChildEnvironment,
        [int]$TailBytes = 0
    )
    $psi = [System.Diagnostics.ProcessStartInfo]::new()
    $psi.FileName = $Executable
    foreach ($a in @($Arguments)) { [void]$psi.ArgumentList.Add([string]$a) }
    $psi.WorkingDirectory = $WorkingDirectory
    $psi.UseShellExecute = $false; $psi.CreateNoWindow = $true
    $psi.RedirectStandardOutput = $true; $psi.RedirectStandardError = $true
    # CHILD-ENVIRONMENT ALLOWLIST with EXECUTION semantics (review finding f2, run 20260714T123137002): a
    # CONSTRUCTED environment CLEARS the inherited ambient first, so every unlisted ambient value (hence every
    # ambient secret) is structurally ABSENT from the child. Absent parameter -> the child inherits the ambient
    # environment unchanged (the legacy T018 self-evidence behavior).
    if ($PSBoundParameters.ContainsKey('ChildEnvironment') -and $null -ne $ChildEnvironment) {
        $psi.Environment.Clear()
        foreach ($k in @($ChildEnvironment.Keys)) {
            if ([string]::IsNullOrWhiteSpace([string]$k)) { continue }
            $psi.Environment[[string]$k] = [string]$ChildEnvironment[$k]
        }
    }
    $sw = [System.Diagnostics.Stopwatch]::StartNew()
    $proc = [System.Diagnostics.Process]::new(); $proc.StartInfo = $psi
    try { [void]$proc.Start() } catch { throw "bounded-process: failed to START '$Executable' (fail-loud): $($_.Exception.Message)" }

    $utf8 = [System.Text.Encoding]::UTF8
    $cap = Get-ContinuousCoReviewMaxOutputTailBytes
    $tailKeep = if ($TailBytes -gt 0) { [Math]::Min($TailBytes, $cap) } else { 0 }   # ENGINE CAP: no unbounded retained tail.
    $bufOut = [char[]]::new(8192); $bufErr = [char[]]::new(8192)
    $outEof = $false; $errEof = $false
    $bytesOut = 0L; $bytesErr = 0L
    $hashOut = [System.Security.Cryptography.IncrementalHash]::CreateHash([System.Security.Cryptography.HashAlgorithmName]::SHA256)
    $hashErr = [System.Security.Cryptography.IncrementalHash]::CreateHash([System.Security.Cryptography.HashAlgorithmName]::SHA256)
    # Direct assignment, NEVER via an if-expression: a byte[] returned from an if/scriptblock ENUMERATES into a
    # boxed Object[], and every later [byte[]] binding then converts it to a FRESH COPY - the ring writes land in
    # the copy and the tail reads back as NULs (the enumeration trap the contract's RawProp accessor documents).
    $tailOut = $null; $tailErr = $null
    if ($tailKeep -gt 0) { $tailOut = [byte[]]::new($tailKeep); $tailErr = [byte[]]::new($tailKeep) }
    $tailOutFill = 0; $tailErrFill = 0
    $carryOut = ''; $carryErr = ''
    $appendTail = {
        param([byte[]]$Ring, [int]$Fill, [byte[]]$Chunk)
        $c = $Ring.Length
        if ($Chunk.Length -ge $c) { [System.Buffer]::BlockCopy($Chunk, $Chunk.Length - $c, $Ring, 0, $c); return $c }
        $overflow = ($Fill + $Chunk.Length) - $c
        if ($overflow -gt 0) { [System.Buffer]::BlockCopy($Ring, $overflow, $Ring, 0, $Fill - $overflow); $Fill -= $overflow }
        [System.Buffer]::BlockCopy($Chunk, 0, $Ring, $Fill, $Chunk.Length)
        return $Fill + $Chunk.Length
    }
    $tOut = $proc.StandardOutput.ReadAsync($bufOut, 0, $bufOut.Length)
    $tErr = $proc.StandardError.ReadAsync($bufErr, 0, $bufErr.Length)
    $timedOut = $false
    $deadlineMs = if ($TimeoutSeconds -gt 0) { [long]$TimeoutSeconds * 1000 } else { [long]::MaxValue }
    while (-not ($outEof -and $errEof)) {
        if ($sw.ElapsedMilliseconds -ge $deadlineMs) { $timedOut = $true; break }
        $pending = @(); if (-not $outEof) { $pending += $tOut }; if (-not $errEof) { $pending += $tErr }
        $slice = if ($deadlineMs -eq [long]::MaxValue) { 250 } else { [int][Math]::Max(1, [Math]::Min(250, $deadlineMs - $sw.ElapsedMilliseconds)) }
        $idx = [System.Threading.Tasks.Task]::WaitAny([System.Threading.Tasks.Task[]]$pending, $slice)
        if ($idx -lt 0) { continue }
        $done = $pending[$idx]
        if (-not $outEof -and $done -eq $tOut) {
            $n = 0; try { $n = $tOut.GetAwaiter().GetResult() } catch { $n = 0 }
            if ($n -le 0) { $outEof = $true }
            else {
                $text = $carryOut + [string]::new($bufOut, 0, $n); $carryOut = ''
                if ($text.Length -gt 0 -and [char]::IsHighSurrogate($text[$text.Length - 1])) { $carryOut = [string]$text[$text.Length - 1]; $text = $text.Substring(0, $text.Length - 1) }
                if ($text.Length -gt 0) {
                    $chunk = $utf8.GetBytes($text); $bytesOut += $chunk.Length; $hashOut.AppendData($chunk)
                    if ($tailKeep -gt 0) { $tailOutFill = & $appendTail $tailOut $tailOutFill $chunk }
                }
                $tOut = $proc.StandardOutput.ReadAsync($bufOut, 0, $bufOut.Length)
            }
        }
        elseif (-not $errEof -and $done -eq $tErr) {
            $n = 0; try { $n = $tErr.GetAwaiter().GetResult() } catch { $n = 0 }
            if ($n -le 0) { $errEof = $true }
            else {
                $text = $carryErr + [string]::new($bufErr, 0, $n); $carryErr = ''
                if ($text.Length -gt 0 -and [char]::IsHighSurrogate($text[$text.Length - 1])) { $carryErr = [string]$text[$text.Length - 1]; $text = $text.Substring(0, $text.Length - 1) }
                if ($text.Length -gt 0) {
                    $chunk = $utf8.GetBytes($text); $bytesErr += $chunk.Length; $hashErr.AppendData($chunk)
                    if ($tailKeep -gt 0) { $tailErrFill = & $appendTail $tailErr $tailErrFill $chunk }
                }
                $tErr = $proc.StandardError.ReadAsync($bufErr, 0, $bufErr.Length)
            }
        }
    }
    if ($timedOut) {
        try { $proc.Kill($true) } catch { $null = $_ }   # ENTIRE process tree
        try { [void]$proc.WaitForExit(5000) } catch { $null = $_ }
    }
    else { $proc.WaitForExit() }
    $sw.Stop()
    # Flush any dangling high surrogate (stream ended mid-pair) - encodes as the replacement bytes, exactly like
    # the old whole-string GetBytes of a lone surrogate.
    foreach ($flush in @(@($carryOut, $true), @($carryErr, $false))) {
        $c = [string]$flush[0]
        if (-not [string]::IsNullOrEmpty($c)) {
            $chunk = $utf8.GetBytes($c)
            if ([bool]$flush[1]) { $bytesOut += $chunk.Length; $hashOut.AppendData($chunk); if ($tailKeep -gt 0) { $tailOutFill = & $appendTail $tailOut $tailOutFill $chunk } }
            else { $bytesErr += $chunk.Length; $hashErr.AppendData($chunk); if ($tailKeep -gt 0) { $tailErrFill = & $appendTail $tailErr $tailErrFill $chunk } }
        }
    }
    $shaOut = [System.BitConverter]::ToString($hashOut.GetHashAndReset()).Replace('-', '').ToLowerInvariant(); $hashOut.Dispose()
    $shaErr = [System.BitConverter]::ToString($hashErr.GetHashAndReset()).Replace('-', '').ToLowerInvariant(); $hashErr.Dispose()
    $exitCode = if ($timedOut) { $null } else { try { [int]$proc.ExitCode } catch { $null } }
    try { $proc.Dispose() } catch { $null = $_ }
    $rawTailOut = if ($tailKeep -gt 0 -and $tailOutFill -gt 0) { $utf8.GetString($tailOut, 0, $tailOutFill) } else { '' }
    $rawTailErr = if ($tailKeep -gt 0 -and $tailErrFill -gt 0) { $utf8.GetString($tailErr, 0, $tailErrFill) } else { '' }
    return [pscustomobject]@{
        exit_code        = $exitCode
        timed_out        = $timedOut
        duration_seconds = [math]::Round($sw.Elapsed.TotalSeconds, 3)
        stdout           = [pscustomobject]@{ byte_count = $bytesOut; sha256 = $shaOut; truncated = ($bytesOut -gt $tailKeep); raw_tail = $rawTailOut }
        stderr           = [pscustomobject]@{ byte_count = $bytesErr; sha256 = $shaErr; truncated = ($bytesErr -gt $tailKeep); raw_tail = $rawTailErr }
    }
}

function Get-ContinuousCoReviewOutputMetaFromFacts {
    # PURE: raw stream facts (byte_count/sha256/raw_tail from the harness) + the effective tail policy -> the
    # persisted stdout/stderr meta. TailBytes<=0 SUPPRESSES text (count/hash only); >0 keeps a REDACTED bounded
    # tail. Same shape + semantics as Get-ContinuousCoReviewBoundedOutputMeta, over already-bounded facts.
    param([Parameter(Mandatory)]$Facts, [int]$TailBytes = 0, [string]$TailDisclosureLabel = 'bounded-redacted-tail')
    $byteCount = [long]$Facts.byte_count
    if ($TailBytes -le 0) {
        return [ordered]@{ byte_count = $byteCount; sha256 = [string]$Facts.sha256; truncated = ($byteCount -gt 0); truncated_tail = ''; tail_disclosure = 'suppressed' }
    }
    return [ordered]@{ byte_count = $byteCount; sha256 = [string]$Facts.sha256; truncated = [bool]$Facts.truncated; truncated_tail = (Get-ContinuousCoReviewRedactedOutputText -Text ([string]$Facts.raw_tail)); tail_disclosure = $TailDisclosureLabel }
}

function New-ContinuousCoReviewRunRecordObject {
    <#
        PURE record ASSEMBLER (no process, no clock, no filesystem): given the harness-observed facts + the
        run identity + the validated-result outcome + the disclosure decision, build the durable record object.
        This is the CORE honesty logic - command_succeeded, missing-diagnostics surfacing, required-result
        classification, the auditable disclosure record, and plan identity - made unit-testable over synthetic
        facts. Returns the ordered record hashtable (the caller persists it).
    #>

    param(
        [Parameter(Mandatory)][string]$Executable,
        [AllowNull()][string]$DeclaredExecutable,
        [string[]]$Arguments = @(),
        [Parameter(Mandatory)][string]$WorkingDirectory,
        [Parameter(Mandatory)][string]$TreeId,
        [Parameter(Mandatory)][datetime]$StartedAt,
        [Parameter(Mandatory)][datetime]$Now,
        [Parameter(Mandatory)]$Process,   # the Invoke-...BoundedProcess result
        [int]$EffectiveTailBytes = 0,
        [string]$TailLabel = 'bounded-redacted-tail',
        [AllowNull()]$TestResult = $null,
        [AllowNull()]$RequiredResultFailure = $null,   # UNTYPED: a [string] default of $null coerces to '' and '' -ne $null is TRUE (a false required-result failure). Untyped keeps a real $null.
        [object[]]$Artifacts = @(),
        [AllowNull()]$DisclosureInfo = $null,   # applied only when non-null
        [AllowNull()][string]$CommandId = $null,
        [AllowNull()]$Provenance = $null,
        [AllowNull()][string[]]$EnvRefs = $null
    )
    $exitCode = $Process.exit_code
    $timedOut = [bool]$Process.timed_out
    $hasRequiredFailure = -not [string]::IsNullOrEmpty([string]$RequiredResultFailure)
    $commandSucceeded = ((-not $timedOut) -and ($null -ne $exitCode) -and ([int]$exitCode -eq 0))
    if ($hasRequiredFailure) { $commandSucceeded = $false }   # a required-result miss is NEVER success

    $commandBlock = [ordered]@{ executable = $Executable; arguments = @($Arguments); working_directory = $WorkingDirectory }
    if (-not [string]::IsNullOrWhiteSpace($DeclaredExecutable) -and ([string]$DeclaredExecutable -ne [string]$Executable)) {
        $commandBlock['declared_executable'] = [string]$DeclaredExecutable
    }
    $entry = [ordered]@{
        command                 = $commandBlock
        reviewed_digest_tree_id = $TreeId
        started_at              = $StartedAt.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
        ended_at                = $StartedAt.AddSeconds([double]$Process.duration_seconds).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
        duration_seconds        = [math]::Round([double]$Process.duration_seconds, 3)
        exit_code               = $exitCode
        timed_out               = $timedOut
        command_succeeded       = $commandSucceeded
        stdout_meta             = (Get-ContinuousCoReviewOutputMetaFromFacts -Facts $Process.stdout -TailBytes $EffectiveTailBytes -TailDisclosureLabel $TailLabel)
        stderr_meta             = (Get-ContinuousCoReviewOutputMetaFromFacts -Facts $Process.stderr -TailBytes $EffectiveTailBytes -TailDisclosureLabel $TailLabel)
        artifacts               = @($Artifacts)
        counts_available        = ($null -ne $TestResult)
        test_result             = $TestResult
        recorded_at             = $Now.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
    }
    # HONEST MISSING-DIAGNOSTICS (maintainer decision 2026-07-14): a FAILED command whose output text was
    # suppressed states it cannot be diagnosed from this record alone - the failure stays a failure; missing
    # diagnostics NEVER become a clean result.
    if ((-not $commandSucceeded) -and ($EffectiveTailBytes -le 0)) { $entry['failure_diagnostics'] = 'insufficient-without-disclosure' }
    # REQUIRED-RESULT FAILURE CLASSIFICATION on the REAL record (finding f1, run 20260714T215545754).
    if ($hasRequiredFailure) {
        $entry['classification'] = 'required-result-missing-or-invalid'
        $entry['failure_reason'] = [string]$RequiredResultFailure
    }
    # AUDITABLE DISCLOSURE RECORD (durable BY DESIGN - the audit trail the reviewer reads; labeled sensitive).
    if ($null -ne $DisclosureInfo) {
        $entry['diagnostic_disclosure'] = [ordered]@{
            authorized_by         = [string]$DisclosureInfo.authorized_by
            reason                = [string]$DisclosureInfo.disclosure_reason
            command_id            = [string]$DisclosureInfo.command_id
            disclosed_at          = $Now.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
            tail_bytes            = [int]$DisclosureInfo.max_tail_bytes
            potentially_sensitive = $true
            durability            = 'durable-digest-bound'
        }
    }
    # PLAN IDENTITY persisted INTO the durable record (review finding f5, run 20260714T123137002): the T019
    # join binds on command_id + reviewed digest, so identity/provenance/env_ref NAMES must survive serialization.
    if (-not [string]::IsNullOrWhiteSpace($CommandId)) { $entry['command_id'] = [string]$CommandId }
    if ($null -ne $Provenance) { $entry['provenance'] = $Provenance }
    if ($null -ne $EnvRefs) { $entry['env_refs'] = @(@($EnvRefs) | Where-Object { $_ -is [string] -and -not [string]::IsNullOrWhiteSpace($_) }) }
    return $entry
}

function Invoke-ContinuousCoReviewRecordedRun {
    <#
        The universal, language/framework-NEUTRAL recorded-run runner (FR-015). Thin glue over the HARNESS
        (Invoke-...BoundedProcess) and the PURE CORE (New-...RunRecordObject): binds evidence to the exact
        reviewed-tree digest, guards the stale result, executes, reads + validates the run-produced
        SpecrewTestResult, digests artifacts, assembles the record, and persists it. No console parsing; no
        caller counts; FAIL-LOUD on digest-unavailable, start failure, a REQUIRED-but-missing/invalid result,
        or a recording failure.
    #>

    param(
        [Parameter(Mandatory)][string]$RepoRoot,
        [Parameter(Mandatory)][string]$Executable,
        [string[]]$Arguments = @(),
        [string]$WorkingDirectory,
        [int]$TimeoutSeconds = 0,
        [string]$ResultPath,          # OPTIONAL: where the command WRITES its SpecrewTestResult JSON (produced THIS run)
        [switch]$RequireResult,       # a missing/invalid result at -ResultPath then FAILS LOUD (never a richer claim)
        [string[]]$ArtifactPath = @(),
        [int]$OutputTailBytes = 0,    # DEFAULT 0 (maintainer decision 2026-07-14): output text is PRIVATE BY DEFAULT - count/hash only. >0 is an explicit caller opt-in, clamped to the engine cap.
        [string]$CommandId,           # OPTIONAL plan identity - persisted INTO the durable record (the T019 join key)
        $Provenance,                  # OPTIONAL provenance object - persisted INTO the durable record
        [string[]]$EnvRefs,           # OPTIONAL env var NAMES (never values) - persisted INTO the durable record
        [System.Collections.IDictionary]$ChildEnvironment,   # OPTIONAL constructed child environment (allowlist semantics)
        [string]$DeclaredExecutable,  # OPTIONAL supplier-declared executable name (the resolved full path is what executes)
        [AllowNull()]$DiagnosticDisclosure = $null           # OPTIONAL human-authorized, command_id-scoped disclosure { authorized_by; reason; command_id; max_tail_bytes? } - NEVER automatic
    )
    # DIAGNOSTIC DISCLOSURE (maintainer decision 2026-07-14): validated FAIL-LOUD (a malformed authorization
    # is a caller error, never silently ignored and never silently honored), and applied ONLY when its
    # command_id EXACTLY matches THIS run's CommandId - scoped to the one named command, never blanket.
    $disclosureApplies = $false; $disclosureInfo = $null
    if ($null -ne $DiagnosticDisclosure) {
        $disclosureInfo = Test-ContinuousCoReviewDiagnosticDisclosure -Disclosure $DiagnosticDisclosure
        if (-not [bool]$disclosureInfo.valid) {
            throw "recorded-run: the supplied DiagnosticDisclosure is INVALID ($($disclosureInfo.reason)) - failing loudly rather than guessing at a disclosure authorization."
        }
        $disclosureApplies = (-not [string]::IsNullOrWhiteSpace($CommandId)) -and ([string]$disclosureInfo.command_id -ceq [string]$CommandId)
    }
    $effectiveTailBytes = if ($disclosureApplies) { [int]$disclosureInfo.max_tail_bytes } else { $OutputTailBytes }
    $tailLabel = if ($disclosureApplies) { 'authorized-diagnostic' } else { 'bounded-redacted-tail' }
    $resolved = (Resolve-Path -LiteralPath $RepoRoot).Path
    $cwd = if ([string]::IsNullOrWhiteSpace($WorkingDirectory)) { $resolved } else { (Resolve-Path -LiteralPath $WorkingDirectory).Path }
    if (-not (Get-Command -Name 'Get-ContinuousCoReviewReviewedStateDigest' -ErrorAction SilentlyContinue)) {
        $lp = Join-Path $PSScriptRoot '_load.ps1'; if (Test-Path -LiteralPath $lp -PathType Leaf) { . $lp }
    }
    # DIGEST BINDING (fail-loud): evidence binds to the exact reviewed-tree digest of the tree the command runs against.
    $dg = Get-ContinuousCoReviewReviewedStateDigest -RepoRoot $resolved
    if (($null -eq $dg) -or (-not [bool]$dg.ok)) {
        $reason = if ($null -ne $dg) { [string]$dg.failure_reason } else { 'digest-unavailable' }
        throw "recorded-run: cannot bind evidence - reviewed-state digest failed ($reason); refusing to record unbound evidence (fail-loud, FR-015)."
    }
    $treeId = [string]$dg.tree_id

    # STALE-RESULT REJECTION: delete any PRE-EXISTING result file BEFORE the run, so a stale result can never be read
    # as this run's. A rich claim must come from a result THIS run produced. DELETION IS VERIFIED (review finding
    # f1, run 20260714T182921446): an undeletable stale result (lock/permission) previously survived the silent
    # delete and could be accepted as this run's rich result - now the run REFUSES to execute (fail-loud, zero
    # side effects) rather than risk a stale claim.
    $resultFull = $null
    if (-not [string]::IsNullOrWhiteSpace($ResultPath)) {
        $resultFull = if ([System.IO.Path]::IsPathRooted($ResultPath)) { $ResultPath } else { Join-Path $cwd $ResultPath }
        if (Test-Path -LiteralPath $resultFull -PathType Leaf) {
            Remove-Item -LiteralPath $resultFull -Force -ErrorAction SilentlyContinue
            if (Test-Path -LiteralPath $resultFull -PathType Leaf) {
                throw "recorded-run: a PRE-EXISTING result at '$ResultPath' could not be deleted before the run - refusing to execute (an undeletable stale result could be read as this run's; fail-loud, FR-015)."
            }
        }
    }

    # EXECUTE via the HARNESS primitive (process-tree contained via Kill(entireProcessTree) on timeout; the
    # bounded-memory concurrent drain lives entirely in Invoke-...BoundedProcess). It returns RAW observed
    # facts; all honesty semantics (redaction, disclosure, classification) are applied by the pure assembler.
    # PRODUCTION CLOCK BOUNDARY (review finding f1, T066 attempt 05): capture this command's
    # own start immediately before spawn. A plan-level caller timestamp is not an observed
    # command start and made later serial commands appear to overlap the first command.
    $startedAt = [datetime]::UtcNow
    $procParams = @{ Executable = $Executable; Arguments = @($Arguments); WorkingDirectory = $cwd; TimeoutSeconds = $TimeoutSeconds; TailBytes = $effectiveTailBytes }
    if ($PSBoundParameters.ContainsKey('ChildEnvironment') -and $null -ne $ChildEnvironment) { $procParams.ChildEnvironment = $ChildEnvironment }
    $procFacts = try { Invoke-ContinuousCoReviewBoundedProcess @procParams } catch { throw "recorded-run: $($_.Exception.Message)" }

    # OPTIONAL SpecrewTestResult - rich counts ONLY from a schema-valid result THIS run produced.
    # OBSERVED-FACTS PRESERVATION (review finding f1, run 20260714T215545754): a REQUIRED-result miss/invalid
    # no longer throws away the process observation - the failure is NOTED here, the FULL real record
    # (timestamps, duration, exit code, output byte counts/hashes) is built and PERSISTED with
    # command_succeeded=$false + the required-result classification, and THEN the fail-loud throw fires.
    # FR-015's directly-observed facts and FR-048's record-every-attempt both survive the failure.
    $testResult = $null
    $requiredResultFailure = $null
    if (-not [string]::IsNullOrWhiteSpace($ResultPath)) {
        if (-not (Test-Path -LiteralPath $resultFull -PathType Leaf)) {
            if ($RequireResult) { $requiredResultFailure = "recorded-run: a SpecrewTestResult was REQUIRED at '$ResultPath' but the command produced none - failing loudly (FR-015), never inferring counts from console output." }
        }
        else {
            $obj = $null
            try { $obj = Get-Content -LiteralPath $resultFull -Raw -Encoding UTF8 | ConvertFrom-Json } catch { $obj = $null }
            # TRANSIENT RESULT LIFECYCLE (review finding f3, run 20260714T182921446): the reviewed digest is
            # bound BEFORE execution, so a result file LEFT in the tree flips the current digest the moment the
            # evidence is recorded - orphaning the very evidence it carried. The result file is a TRANSPORT: its
            # validated content persists in the durable record; the file itself is deleted right after reading
            # (valid OR invalid), restoring the tree to the digest the evidence certifies. A cleanup failure
            # only WARNS - the digest then flips and this evidence honestly orphans itself (a digest mismatch is
            # never wrong evidence), rather than failing a completed run.
            try { Remove-Item -LiteralPath $resultFull -Force -ErrorAction Stop }
            catch { [Console]::Error.WriteLine("[co-review] WARN RESULT_FILE_NOT_CLEANED '$ResultPath' could not be deleted after reading; the reviewed digest will differ from the bound digest and this evidence orphans itself.") }
            $v = Test-ContinuousCoReviewSpecrewTestResult -Object $obj
            if (-not $v.valid) {
                if ($RequireResult) { $requiredResultFailure = "recorded-run: the SpecrewTestResult at '$ResultPath' is INVALID ($($v.reason)) - failing loudly rather than degrading to a richer pass claim (FR-015)." }
            }
            else { $testResult = [ordered]@{ result = $v.result; counts = $v.counts; source = 'specrew-test-result' } }
        }
    }

    # OUTPUT-ARTIFACT digests (I/O; the pure assembler receives the finished digest records).
    $artifacts = @()
    foreach ($ap in @($ArtifactPath)) {
        if ([string]::IsNullOrWhiteSpace($ap)) { continue }
        $apFull = if ([System.IO.Path]::IsPathRooted($ap)) { $ap } else { Join-Path $cwd $ap }
        if (Test-Path -LiteralPath $apFull -PathType Leaf) {
            $fbytes = [System.IO.File]::ReadAllBytes($apFull)
            $fsha = [System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::HashData($fbytes)).Replace('-', '').ToLowerInvariant()
            $artifacts += [ordered]@{ path = $ap; sha256 = $fsha; byte_count = $fbytes.Length }
        }
    }

    # Capture record time only after the process result, optional structured result, and
    # artifact facts have been observed. The pure assembler still accepts injected clocks
    # for deterministic core fixtures; this production wrapper never accepts caller time.
    $recordedAt = [datetime]::UtcNow

    # ASSEMBLE the durable record via the PURE core (no process/clock/filesystem in there).
    $recordParams = @{
        Executable = $Executable; Arguments = @($Arguments); WorkingDirectory = $cwd; TreeId = $treeId
        StartedAt = $startedAt; Now = $recordedAt; Process = $procFacts
        EffectiveTailBytes = $effectiveTailBytes; TailLabel = $tailLabel
        TestResult = $testResult; RequiredResultFailure = $requiredResultFailure; Artifacts = @($artifacts)
    }
    if ($disclosureApplies) { $recordParams.DisclosureInfo = $disclosureInfo }
    if ($PSBoundParameters.ContainsKey('DeclaredExecutable')) { $recordParams.DeclaredExecutable = $DeclaredExecutable }
    if ($PSBoundParameters.ContainsKey('CommandId') -and -not [string]::IsNullOrWhiteSpace($CommandId)) { $recordParams.CommandId = $CommandId }
    if ($PSBoundParameters.ContainsKey('Provenance') -and $null -ne $Provenance) { $recordParams.Provenance = $Provenance }
    if ($PSBoundParameters.ContainsKey('EnvRefs') -and $null -ne $EnvRefs) { $recordParams.EnvRefs = $EnvRefs }
    $entry = New-ContinuousCoReviewRunRecordObject @recordParams
    $commandSucceeded = [bool]$entry.command_succeeded
    if ($PSBoundParameters.ContainsKey('Provenance') -and $null -ne $Provenance) { $entry['provenance'] = $Provenance }
    if ($PSBoundParameters.ContainsKey('EnvRefs') -and $null -ne $EnvRefs) { $entry['env_refs'] = @(@($EnvRefs) | Where-Object { $_ -is [string] -and -not [string]::IsNullOrWhiteSpace($_) }) }

    # WRITE bound to the digest, into the digest-keyed store's `runs` array. FAIL LOUD if recording fails.
    try {
        Save-ContinuousCoReviewRunRecord -RepoRoot $resolved -TreeId $treeId -Entry ([pscustomobject]$entry) -CommandId $CommandId -Executable $Executable -Arguments @($Arguments) -WorkingDirectory $cwd -RecordedAt ([string]$entry.recorded_at)
    }
    catch {
        throw "recorded-run: evidence RECORDING failed for '$Executable' bound to ${treeId} (fail-loud, FR-015): $($_.Exception.Message)"
    }
    # FAIL LOUD on a required-result miss - AFTER the real observed record persisted (finding f1).
    if ($null -ne $requiredResultFailure) {
        throw ($requiredResultFailure + ' (the full observed process record was persisted with command_succeeded=false)')
    }
    return [pscustomobject]$entry
}

function Save-ContinuousCoReviewRunRecord {
    <#
        Persist ONE run/attempt entry into the digest-keyed store's `runs` array. SHARED by the real
        recorded-run writer AND the synthetic verification-failure records (review finding f4, run
        20260714T182921446): FR-048's record-every-attempt means the DURABLE store, not only an in-memory
        return - an unpersisted attempted-failure is missing reviewer evidence. IDENTITY (finding f5 run
        20260714T123137002 + finding f2 run 20260714T201103653): command_id when present is the plan JOIN
        key and (command_id, attempt) is the durable identity - same-id re-runs APPEND with an increasing
        attempt sequence so a failure is never erased by a later success; two distinct ids with the same
        invocation both persist. Identity-less runs (implementer self-evidence) keep latest-run-wins on
        executable + arguments + working_directory. Throws on write failure.
    #>

    param(
        [Parameter(Mandatory)][string]$RepoRoot,
        [Parameter(Mandatory)][string]$TreeId,
        [Parameter(Mandatory)]$Entry,
        [AllowNull()][AllowEmptyString()][string]$CommandId,
        [Parameter(Mandatory)][string]$Executable,
        [string[]]$Arguments = @(),
        [AllowNull()][AllowEmptyString()][string]$WorkingDirectory,
        [Parameter(Mandatory)][string]$RecordedAt
    )
    $dir = Get-ContinuousCoReviewTestEvidenceDirectory -RepoRoot $RepoRoot
    if (-not (Test-Path -LiteralPath $dir -PathType Container)) { $null = New-Item -ItemType Directory -Path $dir -Force }
    $path = Join-Path $dir ($TreeId + '.json')
    $record = $null
    if (Test-Path -LiteralPath $path -PathType Leaf) { try { $record = Get-Content -LiteralPath $path -Raw | ConvertFrom-Json } catch { $record = $null } }
    if ($null -eq $record) { $record = [pscustomobject]@{ schema_version = '1.0'; reviewed_digest_tree_id = $TreeId; recorded_at = $RecordedAt } }
    $existingRuns = @()
    if (($record.PSObject.Properties.Name -contains 'runs') -and ($null -ne $record.runs)) { $existingRuns = @($record.runs) }
    if (-not [string]::IsNullOrWhiteSpace($CommandId)) {
        # ATTEMPT HISTORY for plan-identified runs (review finding f2, run 20260714T201103653): a re-run of the
        # SAME command_id at the same digest APPENDS with a monotonically increasing per-id `attempt` sequence -
        # it never replaces the prior record, so an earlier non-zero/timeout/required-result FAILURE can never
        # be erased by a later success (FR-048: every attempt recorded; a failure never becomes missing
        # evidence). command_id stays the plan JOIN key; (command_id, attempt) is the durable identity, and the
        # join treats the LATEST attempt as authoritative with earlier attempts surfaced as history.
        $maxAttempt = 0
        foreach ($er in $existingRuns) {
            if ($null -eq $er) { continue }
            $erProps = @($er.PSObject.Properties.Name)
            if (($erProps -contains 'command_id') -and ([string]$er.command_id -ceq $CommandId)) {
                $erAttempt = if ($erProps -contains 'attempt') { [int]$er.attempt } else { 1 }
                if ($erAttempt -gt $maxAttempt) { $maxAttempt = $erAttempt }
            }
        }
        $entryObj = [pscustomobject]$Entry
        $entryObj | Add-Member -NotePropertyName 'attempt' -NotePropertyValue ($maxAttempt + 1) -Force
        $record | Add-Member -NotePropertyName 'runs' -NotePropertyValue @(@($existingRuns | Where-Object { $null -ne $_ }) + @($entryObj)) -Force
    }
    else {
        # Identity-less runs (the implementer's OWN dev-loop self-evidence, e.g. the recorded registry runs)
        # keep the T111-style LATEST-RUN-WINS replacement on the invocation key - they are not plan attempts,
        # and the freshest run for this exact tree is the record's meaning.
        $entryKey = 'invocation:' + $Executable + ' ' + (@($Arguments) -join ' ') + '|wd:' + [string]$WorkingDirectory
        $kept = @($existingRuns | Where-Object {
                if ($null -eq $_) { return $false }
                $exId = ''
                if (@($_.PSObject.Properties | ForEach-Object { $_.Name }) -contains 'command_id') { $exId = [string]$_.command_id }
                if (-not [string]::IsNullOrWhiteSpace($exId)) { return $true }   # plan attempts are never displaced by an identity-less run
                $exWd = ''
                try { $exWd = [string]$_.command.working_directory } catch { $exWd = '' }
                $exKey = 'invocation:' + [string]$_.command.executable + ' ' + (@($_.command.arguments) -join ' ') + '|wd:' + $exWd
                return ($exKey -ne $entryKey)
            })
        $record | Add-Member -NotePropertyName 'runs' -NotePropertyValue @(@($kept) + @([pscustomobject]$Entry)) -Force
    }
    $record | Add-Member -NotePropertyName 'reviewed_digest_tree_id' -NotePropertyValue $TreeId -Force
    $record | Add-Member -NotePropertyName 'recorded_at' -NotePropertyValue $RecordedAt -Force
    [System.IO.File]::WriteAllText($path, ($record | ConvertTo-Json -Depth 12))
}