Modules/businessdev.ALbuild.Core/Public/Get-ALbuildCacheLockName.ps1

function Get-ALbuildCacheLockName {
    <#
    .SYNOPSIS
        Returns the global mutex name that guards a cache folder against concurrent write/removal.
 
    .DESCRIPTION
        ALbuild caches are shared by every build running on a host - several agents on the same server,
        and (since the runtime factory) several worker threads inside a single agent job. Two operations
        must never overlap on the same cache entry:
 
          * Get-BcArtifact EXTRACTING into it, and
          * Clear-ALbuildCache REMOVING it.
 
        Both sides serialise on a machine-global mutex, and a mutex only serialises anything if both
        sides compute the SAME name. That is the entire reason this helper exists: the name used to be
        an inline expression in Get-BcArtifact, so the prune path had no way to agree with it without
        copying the expression - and a copied hash silently stops matching the moment either copy is
        touched. A prune that no longer matches does not fail loudly; it deletes a folder out from under
        a reader, and the build fails much later with a misleading "package ... could not be found".
 
        The name is derived from the folder path so that unrelated cache entries never contend:
        SHA-256 over the lower-cased path, hex, truncated to 32 characters (well inside the 260-character
        limit on a mutex name), prefixed 'Global\albuild-artifact-' so it spans sessions and services.
        Lower-casing matters because Windows paths are case-insensitive: 'C:\alb' and 'c:\ALB' are the
        same folder and must yield the same lock.
 
        The 'Global\' prefix requires no elevation for the creating account, but a mutex created by one
        user is not writable by another by default; agents on a host run as the same service account, so
        this is not a constraint in practice.
 
    .PARAMETER Path
        The cache folder the lock protects. Does not need to exist - callers lock BEFORE creating it.
 
    .EXAMPLE
        $mutex = New-Object System.Threading.Mutex($false, (Get-ALbuildCacheLockName -Path $targetFolder))
        Acquire the same lock Get-BcArtifact uses for that folder.
 
    .OUTPUTS
        System.String: the fully-qualified mutex name.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory, Position = 0, ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [string] $Path
    )

    process {
        $sha = [System.Security.Cryptography.SHA256]::Create()
        try {
            $bytes = $sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Path.ToLowerInvariant()))
        }
        finally {
            $sha.Dispose()
        }
        $hash = [System.BitConverter]::ToString($bytes).Replace('-', '').Substring(0, 32)
        return "Global\albuild-artifact-$hash"
    }
}