Public/Export-HDTVariableProvenance.ps1

function Export-HDTVariableProvenance {
    <#
        .SYNOPSIS
            Writes a resolution's provenance to Gather\provenance.json.
 
        .DESCRIPTION
            provenance.json - every variable and which source set
            it (3.1)" in the log directory of every deployment. This writes it,
            through the injected IFileSystem rather than Set-Content, so the whole
            path is provable with nothing on disk.
 
            THE TIMESTAMP IS FORMATTED BEFORE SERIALISATION, and that is not a
            style choice. ConvertTo-Json renders a raw [datetime] as an ISO 8601
            string under pwsh 7 and as "\/Date(1786579862481)\/" under Windows
            PowerShell 5.1 - and 5.1 is the engine that runs in WinPE, so a raw
            [datetime] would make this file unreadable exactly where it matters.
            The value is rendered with the round-trip 'o' format in the invariant
            culture, which both engines produce identically. The same rule holds
            for every DateTime HDT ever writes to JSON.
 
            The document:
 
              { "schemaVersion": 1,
                "generated": "2026-08-13T00:11:02.4810000Z",
                "variable": [ { "name", "value", "source", "rule", "ruleIndex",
                                "file", "rawValue", "expanded", "order" } ] }
 
            Entries are in resolution order, so the file reads as a transcript of
            what the engine did.
 
        .PARAMETER Resolution
            A Resolve-HDTVariable result.
 
        .PARAMETER Path
            Where to write it. Conventionally <_HDTLogPath>\Gather\provenance.json.
            Parent directories are created by the filesystem service.
 
        .PARAMETER FileSystem
            An IFileSystem - the real adapter in production, New-HDTFakeFileSystem
            in a test.
 
        .PARAMETER Timestamp
            The instant recorded as "generated". Defaults to now, in UTC; a test
            supplies a fixed one so it can assert on an exact document.
 
        .OUTPUTS
            None.
 
        .EXAMPLE
            Export-HDTVariableProvenance -Resolution $result `
                -Path (Join-Path $logPath 'Gather\provenance.json') `
                -FileSystem (New-HDTFileSystem)
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    [OutputType([void])]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNull()]
        [object] $Resolution,

        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [string] $Path,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [object] $FileSystem,

        [Parameter()]
        [datetime] $Timestamp = [datetime]::UtcNow
    )

    Set-StrictMode -Version Latest
    $ErrorActionPreference = 'Stop'

    $entry = New-Object -TypeName System.Collections.ArrayList

    foreach ($record in @(Get-HDTVariableProvenance -Resolution $Resolution)) {
        [void] $entry.Add([pscustomobject] ([ordered] @{
                    name      = $record.Name
                    value     = $record.Value
                    source    = $record.Source
                    rule      = $record.Rule
                    ruleIndex = $record.RuleIndex
                    file      = $record.File
                    rawValue  = $record.RawValue
                    expanded  = $record.Expanded
                    order     = $record.Order
                }))
    }

    # A [string], never the [datetime] itself - see the description.
    $generated = $Timestamp.ToUniversalTime().ToString('o', [System.Globalization.CultureInfo]::InvariantCulture)

    $document = [pscustomobject] ([ordered] @{
            schemaVersion = 1
            generated     = $generated
            variable      = @($entry)
        })

    if ($PSCmdlet.ShouldProcess($Path, 'Write variable provenance')) {
        $FileSystem.WriteAllText($Path, (ConvertTo-Json -InputObject $document -Depth 6))
    }
}