Private/Invoke-KadenOperatorModelStackWrite.ps1

function Invoke-KadenOperatorModelStackWrite {
    <#
    .SYNOPSIS
        Tune one stack's managed agents and keep earlier writes when a later file fails.
    #>

    param(
        [Parameter(Mandatory = $true)][string]$ChildRoot,
        [Parameter(Mandatory = $true)]$Report,
        [Parameter(Mandatory = $true)][AllowEmptyCollection()][string[]]$Destinations
    )

    $tuned = New-Object System.Collections.Generic.List[object]
    $missed = New-Object System.Collections.Generic.List[object]
    $changed = $false
    $stackName = [string]$Report.Name
    $operatorInput = '.kaden.model.json#stacks.' + $stackName
    foreach ($destination in @($Destinations)) {
        $result = Resolve-KadenOperatorModelDestinationWrite -ChildRoot $ChildRoot -Destination $destination -Model ([string]$Report.Model) -OperatorInput $operatorInput
        if ($null -ne $result.TunedFile) {
            if ($result.TunedFile.Changed) { $changed = $true }
            $tuned.Add($result.TunedFile)
        } else {
            $missed.Add($result.Missed)
        }
    }

    $stackReport = $Report
    if ($tuned.Count -eq 0 -and @($missed | Where-Object { $_.Kind -eq 'not-written' }).Count -eq 0) {
        $stackReport = New-KadenStackReport -Name $stackName -Disposition 'skipped' -Model ([string]$Report.Model) -Reason 'the latest update no longer installs this agent'
    } elseif ($tuned.Count -eq 0) {
        $stackReport = New-KadenStackReport -Name $stackName -Disposition 'skipped' -Model ([string]$Report.Model) -Reason 'managed agent could not be tuned'
    }

    return [pscustomobject]@{
        Tuned   = @($tuned.ToArray())
        Missed  = @($missed.ToArray())
        Changed = $changed
        Report  = $stackReport
    }
}

function Resolve-KadenOperatorModelDestinationWrite {
    <#
    .SYNOPSIS
        Tune one managed agent, or record why that one file was not tuned.
    #>

    param(
        [Parameter(Mandatory = $true)][string]$ChildRoot,
        [Parameter(Mandatory = $true)][string]$Destination,
        [Parameter(Mandatory = $true)][string]$Model,
        [Parameter(Mandatory = $true)][string]$OperatorInput
    )

    try {
        $full = Resolve-KadenContainedPath -Root $ChildRoot -RelativePath $Destination
        if (-not (Test-Path -LiteralPath $full -PathType Leaf)) {
            return [pscustomobject]@{
                Tuned     = $null
                TunedFile = $null
                Missed    = [pscustomobject]@{
                    Path   = $Destination
                    Reason = 'the latest update no longer installs this agent'
                    Kind   = 'stale'
                }
            }
        }
        $write = Set-KadenAgentFrontmatterModel -LiteralPath $full -Model $Model
    } catch {
        return [pscustomobject]@{
            Tuned     = $null
            TunedFile = $null
            Missed    = [pscustomobject]@{
                Path   = $Destination
                Reason = $_.Exception.Message
                Kind   = 'not-written'
            }
        }
    }

    if ($write.Applied) {
        return [pscustomobject]@{
            Tuned     = $true
            TunedFile = [pscustomobject]@{
                Path          = $Destination
                Model         = $Model
                Source        = 'operator-supplied'
                Changed       = [bool]$write.Changed
                OperatorInput = $OperatorInput
            }
            Missed    = $null
        }
    }

    $failReason = [string]$write.Reason
    if ([string]::IsNullOrWhiteSpace($failReason)) {
        $failReason = 'managed agent could not be tuned'
    }
    return [pscustomobject]@{
        Tuned     = $null
        TunedFile = $null
        Missed    = [pscustomobject]@{
            Path   = $Destination
            Reason = $failReason
            Kind   = 'not-written'
        }
    }
}

function Get-KadenOperatorModelDestinationsByStack {
    <#
    .SYNOPSIS
        Group managed manifest destinations by host stack.
    #>

    param(
        [Parameter(Mandatory = $true)]$Manifest
    )

    $byStack = @{}
    foreach ($file in @($Manifest.files)) {
        $destination = [string]$file.destination
        $stackName = Get-KadenAgentStackName -Destination $destination
        if ([string]::IsNullOrWhiteSpace($stackName)) { continue }
        if (-not $byStack.ContainsKey($stackName)) {
            $byStack[$stackName] = New-Object System.Collections.Generic.List[string]
        }
        $byStack[$stackName].Add($destination)
    }
    return $byStack
}