private/ActionHelpers.ps1

# Shared Action constructor (data-model.md -> Action) so every category file builds the same
# shape instead of repeating a pscustomobject literal eight times.

function New-OctaAction {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][ValidateSet('Registry', 'Service', 'ScheduledTask', 'Package')]
        [string]$TargetType,
        [Parameter(Mandatory)][string]$TargetIdentifier,
        $CurrentValue,
        $PlannedValue,
        [bool]$Reversible = $true,
        [string]$IrreversibleReason = $null,
        [bool]$HeuristicMatch = $false,
        [ValidateSet('Safe', 'Moderate', 'Risky')]
        [string]$RiskLevel = 'Safe'
    )

    if (-not $Reversible -and -not $IrreversibleReason) {
        throw "New-OctaAction: IrreversibleReason is required when Reversible = `$false (FR-007) for target '$TargetIdentifier'."
    }

    return [pscustomobject]@{
        TargetType         = $TargetType
        TargetIdentifier   = $TargetIdentifier
        CurrentValue       = $CurrentValue
        PlannedValue       = $PlannedValue
        Reversible         = $Reversible
        IrreversibleReason = $IrreversibleReason
        HeuristicMatch     = $HeuristicMatch
        RiskLevel          = $RiskLevel
    }
}

function Split-OctaActionsByConfirmationGroup {
    <#
        .SYNOPSIS
        Partitions {Category,Action} entries into the three independent confirmation gates
        Invoke-OctaCategory uses: Normal (Safe/Moderate, non-heuristic), Heuristic
        (HeuristicMatch, e.g. oem-suites), and Risky (RiskLevel -eq 'Risky'). Risky takes
        precedence when an action happens to be both, so it only needs one gate, not two -
        FR-017, FR-026.
    #>

    [CmdletBinding()]
    param(
        [AllowEmptyCollection()][object[]]$Entries = @()
    )

    $risky = @($Entries | Where-Object { $_.Action.RiskLevel -eq 'Risky' })
    $heuristic = @($Entries | Where-Object { $_.Action.HeuristicMatch -and $_.Action.RiskLevel -ne 'Risky' })
    $normal = @($Entries | Where-Object { -not $_.Action.HeuristicMatch -and $_.Action.RiskLevel -ne 'Risky' })

    return [pscustomobject]@{ Normal = $normal; Heuristic = $heuristic; Risky = $risky }
}