Modules/businessdev.ALbuild.Core/Public/Assert-ALbuildModuleComplete.ps1

function Assert-ALbuildModuleComplete {
    <#
    .SYNOPSIS
        Verify an installed businessdev.ALbuild version folder is complete and loadable.
 
    .DESCRIPTION
        The 2026-08 outage was a single .ps1 file lost during a concurrent, non-atomic Install-Module: the
        version still LOOKED installed, so every task imported a module that could not load (0x80131047).
        This check catches exactly that - a version folder that is present but missing a file.
 
        It is cheap enough to run on every task (the steady-state path is: read the '.albuild-complete'
        marker, re-count the module's PowerShell files, compare) yet strict enough to notice a lost file:
 
          1. '<Path>\businessdev.ALbuild.psd1' exists and parses (Import-PowerShellDataFile).
          2. Every path in the manifest's NestedModules exists under <Path>.
          3. The '.albuild-complete' marker exists and its recorded PowerShell-file count + psd1 hash match
             the folder as it is now (a lost/added/edited .ps1|.psm1|.psd1 changes the count or the hash).
 
        A folder with no marker is treated as INCOMPLETE (never trusted) - so a version installed by the
        old, marker-less bootstrap is re-verified/repaired on first use rather than trusted forever.
 
    .PARAMETER Path
        The version folder to verify, e.g. '...\Modules\businessdev.ALbuild\2.18.2'.
 
    .PARAMETER PassThru
        Return a findings object instead of throwing: { Complete, Path, Version, Reason, ExpectedCount,
        ActualCount }. Without it, an incomplete folder throws with the reason.
 
    .OUTPUTS
        Nothing (throws on failure), or a PSCustomObject with -PassThru.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path,
        [switch] $PassThru
    )

    $version = Split-Path -Path $Path -Leaf
    $result = [ordered]@{ Complete = $false; Path = $Path; Version = $version; Reason = ''; ExpectedCount = $null; ActualCount = $null }
    $fail = {
        param([string] $reason)
        $result.Reason = $reason
        if ($PassThru) { return [PSCustomObject]$result }
        throw "businessdev.ALbuild $version at '$Path' is incomplete: $reason"
    }

    if (-not (Test-Path -LiteralPath $Path -PathType Container)) { return (& $fail 'the version folder does not exist') }

    # 1. Manifest present + parseable.
    $manifest = Join-Path $Path 'businessdev.ALbuild.psd1'
    if (-not (Test-Path -LiteralPath $manifest -PathType Leaf)) { return (& $fail 'the module manifest (.psd1) is missing') }
    $psd = $null
    try { $psd = Import-PowerShellDataFile -Path $manifest -ErrorAction Stop }
    catch { return (& $fail "the module manifest does not parse: $($_.Exception.Message)") }

    # 2. Every declared nested module exists.
    foreach ($nested in @($psd.NestedModules)) {
        $nestedPath = Join-Path $Path $nested
        if (-not (Test-Path -LiteralPath $nestedPath -PathType Leaf)) { return (& $fail "nested module '$nested' is missing") }
    }

    # 3. Marker present + inventory matches. The marker (JSON) was written from the freshly-downloaded,
    # complete staging copy at install time; a later-lost file makes the live count/hash disagree.
    $markerPath = Join-Path $Path '.albuild-complete'
    if (-not (Test-Path -LiteralPath $markerPath -PathType Leaf)) { return (& $fail "no '.albuild-complete' marker (installed by an old bootstrap, or never completed)") }
    $marker = $null
    try { $marker = Get-Content -LiteralPath $markerPath -Raw | ConvertFrom-Json }
    catch { return (& $fail "the '.albuild-complete' marker does not parse: $($_.Exception.Message)") }

    $inv = Get-ALbuildModuleInventory -Path $Path
    $result.ExpectedCount = $marker.fileCount
    $result.ActualCount = $inv.FileCount
    if ([int]$marker.fileCount -ne [int]$inv.FileCount) {
        return (& $fail "PowerShell file count is $($inv.FileCount) but the marker recorded $($marker.fileCount) (a file was lost or added)")
    }
    if ("$($marker.manifestHash)" -and "$($marker.manifestHash)" -ne "$($inv.ManifestHash)") {
        return (& $fail 'the manifest hash does not match the marker (the .psd1 changed)')
    }

    $result.Complete = $true
    if ($PassThru) { return [PSCustomObject]$result }
}