private/Backup-WtwExternalConfig.ps1

function Get-WtwExternalBackupRoot {
    <#
    .SYNOPSIS
        Directory that stores rotating backups of external-tool configs.
    .DESCRIPTION
        Default: ~/.wtw/backups. Tests override $script:WtwBackupRoot.
    #>

    $override = Get-Variable -Name WtwBackupRoot -Scope Script -ErrorAction SilentlyContinue
    if ($override -and $override.Value) { return $override.Value }
    if ($env:WTW_BACKUP_ROOT) { return $env:WTW_BACKUP_ROOT }
    return Join-Path $HOME '.wtw' 'backups'
}

function Backup-WtwExternalConfig {
    <#
    .SYNOPSIS
        Copy an external-system config into ~/.wtw/backups/<system>/ before wtw writes it.
    .DESCRIPTION
        No-ops when the source file is missing. After the copy, prunes that
        file's backup set to: the 3 newest copies, plus the newest copy that is
        at least 3 / 7 / 30 days old.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [Alias('System')]
        [string] $Tool,

        [Parameter(Mandatory)]
        [string] $Path,

        [string] $Label
    )

    if ([string]::IsNullOrWhiteSpace($Path) -or -not (Test-Path -LiteralPath $Path)) {
        return $null
    }

    $safeSystem = ($Tool -replace '[^A-Za-z0-9._-]', '_').Trim('_')
    if (-not $safeSystem) { $safeSystem = 'misc' }

    $leaf = if ($Label) { $Label } else { Split-Path -Path $Path -Leaf }
    $safeLeaf = ($leaf -replace '[^A-Za-z0-9._-]', '_').Trim('_')
    if (-not $safeLeaf) { $safeLeaf = 'config' }

    $dir = Join-Path (Get-WtwExternalBackupRoot) $safeSystem
    if (-not (Test-Path -LiteralPath $dir)) {
        New-Item -ItemType Directory -Force -Path $dir | Out-Null
    }

    $stamp = Get-Date -Format 'yyyyMMdd-HHmmss-fff'
    $dest = Join-Path $dir "$safeLeaf.$stamp.bak"
    Copy-Item -LiteralPath $Path -Destination $dest -Force

    Clear-WtwExternalConfigBackups -Directory $dir -LeafPrefix $safeLeaf
    return $dest
}

function Clear-WtwExternalConfigBackups {
    <#
    .SYNOPSIS
        Keep the last 3 backups plus one snapshot each at 3 / 7 / 30 days.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string] $Directory,

        [Parameter(Mandatory)]
        [string] $LeafPrefix
    )

    if (-not (Test-Path -LiteralPath $Directory)) { return }

    $files = @(
        Get-ChildItem -LiteralPath $Directory -File -Filter "$LeafPrefix.*.bak" -ErrorAction SilentlyContinue |
            Sort-Object LastWriteTime -Descending
    )
    if ($files.Count -eq 0) { return }

    $keep = @{}
    foreach ($recent in ($files | Select-Object -First 3)) {
        $keep[$recent.FullName] = $true
    }

    $now = Get-Date
    foreach ($days in 3, 7, 30) {
        $cutoff = $now.AddDays(-$days)
        $aged = @(
            $files |
                Where-Object { $_.LastWriteTime -le $cutoff } |
                Select-Object -First 1
        )
        if ($aged.Count -gt 0) {
            $keep[$aged[0].FullName] = $true
        }
    }

    foreach ($file in $files) {
        if (-not $keep.ContainsKey($file.FullName)) {
            Remove-Item -LiteralPath $file.FullName -Force -ErrorAction SilentlyContinue
        }
    }
}