Private/Get-AvmUpdateBranchName.ps1

function Get-AvmUpdateBranchName {
    <#
    .SYNOPSIS
        Computes a deterministic branch name for a set of AVM update items.

    .DESCRIPTION
        Hashes the sorted list of "<module>@<targetVersion>" pairs so that re-running the
        updater against the same pending updates always produces the same branch name,
        instead of a new timestamp-based branch on every run (Renovate/Dependabot-style
        idempotent PRs). Different pending updates (a different set of modules/versions)
        hash to a different branch name, which is what allows a stale PR to be detected and
        left alone while a new one is opened for the new update set.

    .PARAMETER BranchPrefix
        Branch name prefix (e.g. 'avm-updates').

    .PARAMETER Items
        Plan items going into this branch. Each item must expose 'module' and
        'latestStable' properties.

    .PARAMETER Suffix
        Optional extra path segment inserted between the prefix and the hash (e.g. 'low',
        'review'), matching the existing '<prefix>/<bucket>/<hash>' branch layout used by
        github.splitLowFromHigherRisk.

    .OUTPUTS
        String branch name: '<prefix>[/<suffix>]/<12-char-hash>'.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [string]$BranchPrefix,

        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [object[]]$Items,

        [Parameter()]
        [string]$Suffix
    )

    $pairs  = @($Items | ForEach-Object { "$($_.module)@$($_.latestStable)" } | Sort-Object)
    $joined = $pairs -join '|'

    $sha256 = [System.Security.Cryptography.SHA256]::Create()
    try {
        $bytes     = [System.Text.Encoding]::UTF8.GetBytes($joined)
        $hashBytes = $sha256.ComputeHash($bytes)
        $hash      = -join ($hashBytes | ForEach-Object { $_.ToString('x2') })
    } finally {
        $sha256.Dispose()
    }
    $shortHash = $hash.Substring(0, 12)

    if ($Suffix) { return "$BranchPrefix/$Suffix/$shortHash" }
    return "$BranchPrefix/$shortHash"
}