Private/Wizard/Invoke-CgrRepositoryCopyWizard.ps1

function Invoke-CgrRepositoryCopyWizard {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string] $HostName,

        [Parameter(Mandatory)]
        [scriptblock] $ExecutionGuard
    )

    $cancelResult = {
        [pscustomobject] @{
            PSTypeName = 'CopyGitHubRepo.WizardResult'
            Status = 'Cancelled'
            MutatedGitHub = $false
        }
    }

    $readOptionalReleaseValue = {
        param(
            [Parameter(Mandatory)]
            [string] $Prompt,

            [AllowEmptyString()]
            [string] $CurrentValue,

            [switch] $PositiveInteger
        )

        while ($true) {
            Write-CgrWizardMessage
            Write-CgrWizardMessage -Message 'Press Enter to keep the current value. Enter - to clear it.' -Style Hint
            $inputText = Read-CgrWizardInput `
                -Prompt $Prompt `
                -DefaultValue $CurrentValue `
                -AllowBack `
                -AllowCancel
            $navigation = Resolve-CgrWizardNavigationInput `
                -InputText $inputText `
                -AllowBack `
                -AllowNext `
                -AllowCancel

            if ($null -ne $navigation) {
                if ($navigation.Action -eq 'Next') { $navigation.Value = $CurrentValue }
                return $navigation
            }

            $candidate = $inputText.Trim()
            if ($candidate -eq '-') {
                return ConvertTo-CgrWizardNavigationResult -Action Next -Value ''
            }
            if (-not $PositiveInteger) {
                return ConvertTo-CgrWizardNavigationResult -Action Next -Value $candidate
            }

            $count = 0
            if ([int]::TryParse($candidate, [ref] $count) -and $count -gt 0) {
                return ConvertTo-CgrWizardNavigationResult -Action Next -Value $candidate
            }
            Write-CgrWizardMessage -Message 'Enter a positive whole number, -, Back, or Cancel.' -Status Warning
        }
    }

    Write-CgrWizardMessage -Message 'Copy GitHub Repository' -Style Heading
    Write-CgrWizardMessage -Message 'Discovering repositories and checking GitHub CLI access...' -Style Hint

    $repositories = @(Get-GitHubRepository -HostName $HostName)
    $sourceRepository = $null
    $destinationRepository = $null
    $contentMode = 'Snapshot'
    $commitMessage = 'Initial repository commit'
    $destinationVisibility = $null
    $settingsBehavior = 'Restore'
    $archiveRepositoryName = $null
    $sameNameConfirmation = $null
    $existingDestinationArchiveName = $null
    $existingDestinationConfirmation = $null
    $snapshotReleaseOptions = [pscustomobject] @{
        IncludeReleases = $false
        ReleaseTag = ''
        ReleaseExcludeTag = ''
        IncludePrerelease = $false
        IncludeDraftReleases = $false
        ReleaseCount = ''
    }
    $step = 0

    while ($true) {
        switch ($step) {
            0 {
                $result = Select-CgrWizardRepository -Repositories $repositories -CurrentValue $sourceRepository -AllowCancel
                if ($result.Action -eq 'Cancel') { return & $cancelResult }

                $previousSource = $sourceRepository
                $sourceRepository = $result.Value
                if ($null -eq $sourceRepository) { continue }

                if ($null -eq $previousSource -or $previousSource.FullName -ne $sourceRepository.FullName) {
                    $destinationVisibility = $null
                    $archiveRepositoryName = $null
                    $sameNameConfirmation = $null
                    $existingDestinationArchiveName = $null
                    $existingDestinationConfirmation = $null
                }
                $step = 1
            }

            1 {
                $result = Resolve-CgrWizardDestinationRepository -Repositories $repositories -CurrentValue $destinationRepository -AllowBack -AllowCancel
                if ($result.Action -eq 'Cancel') { return & $cancelResult }
                if ($result.Action -eq 'Back') { $step = 0; continue }

                if ($destinationRepository -ne $result.Value) {
                    $destinationRepository = [string] $result.Value
                    $archiveRepositoryName = $null
                    $sameNameConfirmation = $null
                    $existingDestinationArchiveName = $null
                    $existingDestinationConfirmation = $null
                }

                if ($destinationRepository -ne $sourceRepository.FullName -and (Test-CgrGitHubRepositoryExistence -Repository $destinationRepository -HostName $HostName)) {
                    Write-CgrWizardMessage -Message "Destination '$destinationRepository' already exists." -Status Warning
                    $conflictResult = Read-CgrWizardChoice `
                        -Title 'Existing destination' `
                        -Choices @('Choose another destination', 'Archive and replace existing destination', 'Cancel') `
                        -DefaultValue 'Choose another destination' `
                        -HelpTopic ExistingDestination `
                        -AllowBack `
                        -AllowCancel

                    if ($conflictResult.Action -eq 'Cancel' -or $conflictResult.Value -eq 'Cancel') { return & $cancelResult }
                    if ($conflictResult.Action -eq 'Back') { $step = 0; continue }
                    if ($conflictResult.Value -eq 'Choose another destination') {
                        $destinationRepository = $null
                        $existingDestinationArchiveName = $null
                        $existingDestinationConfirmation = $null
                        continue
                    }

                    if ([string]::IsNullOrWhiteSpace($existingDestinationArchiveName)) {
                        $existingDestinationArchiveName = Get-CgrDefaultArchiveRepositoryName -Repository $destinationRepository
                    }

                    while ($true) {
                        $archiveResult = Read-CgrWizardRepositoryName -Kind Archive -CurrentValue $existingDestinationArchiveName -AllowBack -AllowCancel
                        if ($archiveResult.Action -eq 'Cancel') { return & $cancelResult }
                        if ($archiveResult.Action -eq 'Back') {
                            $existingDestinationArchiveName = $null
                            $existingDestinationConfirmation = $null
                            break
                        }

                        $candidateArchiveName = [string] $archiveResult.Value
                        $destinationOwner = $destinationRepository.Split('/', 2)[0]
                        $candidateArchive = "$destinationOwner/$candidateArchiveName"
                        if (Test-CgrGitHubRepositoryExistence -Repository $candidateArchive -HostName $HostName) {
                            Write-CgrWizardMessage -Message "Archive repository '$candidateArchive' already exists. Choose another archive name." -Status Warning
                            $existingDestinationArchiveName = $candidateArchiveName
                            continue
                        }

                        if ($existingDestinationArchiveName -ne $candidateArchiveName) { $existingDestinationConfirmation = $null }
                        $existingDestinationArchiveName = $candidateArchiveName
                        break
                    }

                    if ([string]::IsNullOrWhiteSpace($existingDestinationArchiveName)) { continue }
                }
                else {
                    $existingDestinationArchiveName = $null
                    $existingDestinationConfirmation = $null
                }
                $step = 2
            }

            2 {
                $result = Read-CgrWizardChoice `
                    -Title 'Content mode' `
                    -Choices @('Snapshot', 'FullHistory') `
                    -DefaultValue Snapshot `
                    -CurrentValue $contentMode `
                    -HelpTopic ContentMode `
                    -AllowBack `
                    -AllowCancel
                if ($result.Action -eq 'Cancel') { return & $cancelResult }
                if ($result.Action -eq 'Back') { $step = 1; continue }
                $contentMode = [string] $result.Value
                $step = 3
            }

            3 {
                $visibilityChoices = @($sourceRepository.Visibility, 'public', 'private', 'internal') | Select-Object -Unique
                $result = Read-CgrWizardChoice `
                    -Title 'Destination visibility' `
                    -Choices $visibilityChoices `
                    -DefaultValue $sourceRepository.Visibility `
                    -CurrentValue $destinationVisibility `
                    -HelpTopic DestinationVisibility `
                    -AllowBack `
                    -AllowCancel
                if ($result.Action -eq 'Cancel') { return & $cancelResult }
                if ($result.Action -eq 'Back') { $step = 2; continue }
                $destinationVisibility = [string] $result.Value
                $step = 4
            }

            4 {
                $result = Read-CgrWizardChoice `
                    -Title 'Supported repository settings' `
                    -Choices @('Restore', 'Skip') `
                    -DefaultValue Restore `
                    -CurrentValue $settingsBehavior `
                    -HelpTopic SupportedSettings `
                    -AllowBack `
                    -AllowCancel
                if ($result.Action -eq 'Cancel') { return & $cancelResult }
                if ($result.Action -eq 'Back') { $step = 3; continue }
                $settingsBehavior = [string] $result.Value
                $step = if ($destinationRepository -eq $sourceRepository.FullName) { 5 } else { 6 }
            }

            5 {
                $archiveResult = Read-CgrWizardRepositoryName -Kind Archive -CurrentValue $archiveRepositoryName -AllowBack -AllowCancel
                if ($archiveResult.Action -eq 'Cancel') { return & $cancelResult }
                if ($archiveResult.Action -eq 'Back') { $step = 4; continue }

                if ($archiveRepositoryName -ne $archiveResult.Value) {
                    $archiveRepositoryName = [string] $archiveResult.Value
                    $sameNameConfirmation = $null
                }

                $expectedConfirmation = "SOURCE=$($sourceRepository.FullName);ARCHIVE=$($sourceRepository.Owner)/$archiveRepositoryName;REPLACEMENT=$destinationRepository"
                while ($true) {
                    Write-CgrWizardMessage -Message 'Same-name replacement preserves the current repository as an archive before creating the replacement.' -Status Warning
                    Write-CgrWizardMessage -Message 'Exact confirmation is required and cannot be bypassed.' -Style Hint
                    Write-CgrWizardMessage -Message 'Required text:' -Style Hint
                    Write-CgrWizardMessage -Message $expectedConfirmation -Style Value
                    if (-not [string]::IsNullOrWhiteSpace($sameNameConfirmation)) {
                        Write-CgrWizardMessage -Message 'A valid confirmation is already stored. Press Enter to reuse it.' -Style Hint
                    }

                    $inputText = Read-CgrWizardInput `
                        -Prompt 'Exact confirmation' `
                        -DefaultValue $sameNameConfirmation `
                        -AllowHelp `
                        -AllowBack `
                        -AllowCancel
                    $navigation = Resolve-CgrWizardNavigationInput `
                        -InputText $inputText `
                        -AllowBack `
                        -AllowNext:(-not [string]::IsNullOrWhiteSpace($sameNameConfirmation)) `
                        -AllowCancel `
                        -AllowHelp

                    if ($null -ne $navigation) {
                        if ($navigation.Action -eq 'Help') { Show-CgrWizardHelp -Topic ExactConfirmation; continue }
                        if ($navigation.Action -eq 'Cancel') { return & $cancelResult }
                        if ($navigation.Action -eq 'Back') { $step = 4; break }
                        if ($navigation.Action -eq 'Next') { $step = 6; break }
                    }

                    if ($inputText -ceq $expectedConfirmation) {
                        $sameNameConfirmation = $inputText
                        $step = 6
                        break
                    }
                    Write-CgrWizardMessage -Message 'The confirmation did not match exactly. Retry, go Back, or Cancel.' -Status Warning
                }
            }

            6 {
                if ($contentMode -eq 'Snapshot') {
                    $commitMessageResult = Read-CgrWizardTextValue `
                        -Title 'Snapshot commit message' `
                        -DefaultValue 'Initial repository commit' `
                        -CurrentValue $commitMessage `
                        -HelpTopic SnapshotCommitMessage `
                        -AllowBack `
                        -AllowCancel
                    if ($commitMessageResult.Action -eq 'Cancel') { return & $cancelResult }
                    if ($commitMessageResult.Action -eq 'Back') {
                        $step = if ($destinationRepository -eq $sourceRepository.FullName) { 5 } else { 4 }
                        continue
                    }
                    $commitMessage = [string] $commitMessageResult.Value

                    $releaseScreen = -1
                    $returnToCommitMessage = $false
                    while ($releaseScreen -le 4) {
                        switch ($releaseScreen) {
                            -1 {
                                Write-CgrWizardMessage
                                Write-CgrWizardMessage -Message 'Snapshot release preservation creates new checkpoint commits from selected release states.' -Style Hint
                                Write-CgrWizardMessage -Message 'It does not preserve original commit identities or ancestry; selected tags are recreated against the new Snapshot checkpoints.' -Style Hint
                                $currentReleaseChoice = if ($snapshotReleaseOptions.IncludeReleases) { 'Preserve selected releases' } else { 'Skip' }
                                $releaseResult = Read-CgrWizardChoice `
                                    -Title 'Snapshot release preservation' `
                                    -Choices @('Skip', 'Preserve selected releases') `
                                    -DefaultValue Skip `
                                    -CurrentValue $currentReleaseChoice `
                                    -AllowBack `
                                    -AllowCancel
                                if ($releaseResult.Action -eq 'Cancel') { return & $cancelResult }
                                if ($releaseResult.Action -eq 'Back') { $returnToCommitMessage = $true; break }
                                $snapshotReleaseOptions.IncludeReleases = $releaseResult.Value -eq 'Preserve selected releases'
                                if (-not $snapshotReleaseOptions.IncludeReleases) { break }
                                $releaseScreen = 0
                            }
                            0 {
                                Write-CgrWizardMessage -Message 'Optional include patterns use the same PowerShell wildcard tag filtering as Copy-GitHubRepository. Separate multiple patterns with commas.' -Style Hint
                                $filterResult = & $readOptionalReleaseValue -Prompt 'Release tag include patterns' -CurrentValue $snapshotReleaseOptions.ReleaseTag
                                if ($filterResult.Action -eq 'Cancel') { return & $cancelResult }
                                if ($filterResult.Action -eq 'Back') { $releaseScreen = -1; continue }
                                $snapshotReleaseOptions.ReleaseTag = [string] $filterResult.Value
                                $releaseScreen = 1
                            }
                            1 {
                                Write-CgrWizardMessage -Message 'Optional exclude patterns are applied after include filtering. Separate multiple patterns with commas.' -Style Hint
                                $filterResult = & $readOptionalReleaseValue -Prompt 'Release tag exclude patterns' -CurrentValue $snapshotReleaseOptions.ReleaseExcludeTag
                                if ($filterResult.Action -eq 'Cancel') { return & $cancelResult }
                                if ($filterResult.Action -eq 'Back') { $releaseScreen = 0; continue }
                                $snapshotReleaseOptions.ReleaseExcludeTag = [string] $filterResult.Value
                                $releaseScreen = 2
                            }
                            2 {
                                $prereleaseChoice = if ($snapshotReleaseOptions.IncludePrerelease) { 'Include prereleases' } else { 'Exclude prereleases' }
                                $filterResult = Read-CgrWizardChoice `
                                    -Title 'Prerelease filtering' `
                                    -Choices @('Exclude prereleases', 'Include prereleases') `
                                    -DefaultValue 'Exclude prereleases' `
                                    -CurrentValue $prereleaseChoice `
                                    -AllowBack `
                                    -AllowCancel
                                if ($filterResult.Action -eq 'Cancel') { return & $cancelResult }
                                if ($filterResult.Action -eq 'Back') { $releaseScreen = 1; continue }
                                $snapshotReleaseOptions.IncludePrerelease = $filterResult.Value -eq 'Include prereleases'
                                $releaseScreen = 3
                            }
                            3 {
                                $draftChoice = if ($snapshotReleaseOptions.IncludeDraftReleases) { 'Include draft releases' } else { 'Exclude draft releases' }
                                $filterResult = Read-CgrWizardChoice `
                                    -Title 'Draft release filtering' `
                                    -Choices @('Exclude draft releases', 'Include draft releases') `
                                    -DefaultValue 'Exclude draft releases' `
                                    -CurrentValue $draftChoice `
                                    -AllowBack `
                                    -AllowCancel
                                if ($filterResult.Action -eq 'Cancel') { return & $cancelResult }
                                if ($filterResult.Action -eq 'Back') { $releaseScreen = 2; continue }
                                $snapshotReleaseOptions.IncludeDraftReleases = $filterResult.Value -eq 'Include draft releases'
                                $releaseScreen = 4
                            }
                            4 {
                                Write-CgrWizardMessage -Message 'Optionally limit the reviewed selection to the newest N releases after the other filters are applied.' -Style Hint
                                $filterResult = & $readOptionalReleaseValue -Prompt 'Release count limit' -CurrentValue $snapshotReleaseOptions.ReleaseCount -PositiveInteger
                                if ($filterResult.Action -eq 'Cancel') { return & $cancelResult }
                                if ($filterResult.Action -eq 'Back') { $releaseScreen = 3; continue }
                                $snapshotReleaseOptions.ReleaseCount = [string] $filterResult.Value
                                $releaseScreen = 5
                            }
                        }
                        if ($returnToCommitMessage -or -not $snapshotReleaseOptions.IncludeReleases) { break }
                    }
                    if ($returnToCommitMessage) { continue }
                }

                $planParameters = @{
                    SourceRepository = $sourceRepository.FullName
                    DestinationRepository = $destinationRepository
                    ContentMode = $contentMode
                    HostName = $HostName
                    PlanOnly = $true
                }
                if ($contentMode -eq 'Snapshot') {
                    $planParameters.CommitMessage = $commitMessage
                    if ($snapshotReleaseOptions.IncludeReleases) {
                        $planParameters.IncludeReleases = $true
                        $releaseTags = @($snapshotReleaseOptions.ReleaseTag -split ',' | ForEach-Object { $_.Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
                        $releaseExcludeTags = @($snapshotReleaseOptions.ReleaseExcludeTag -split ',' | ForEach-Object { $_.Trim() } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
                        if ($releaseTags.Count -gt 0) { $planParameters.ReleaseTag = $releaseTags }
                        if ($releaseExcludeTags.Count -gt 0) { $planParameters.ReleaseExcludeTag = $releaseExcludeTags }
                        if ($snapshotReleaseOptions.IncludePrerelease) { $planParameters.IncludePrerelease = $true }
                        if ($snapshotReleaseOptions.IncludeDraftReleases) { $planParameters.IncludeDraftReleases = $true }
                        if (-not [string]::IsNullOrWhiteSpace($snapshotReleaseOptions.ReleaseCount)) {
                            $planParameters.ReleaseCount = [int] $snapshotReleaseOptions.ReleaseCount
                        }
                    }
                }
                if ($destinationVisibility -ne $sourceRepository.Visibility) { $planParameters.DestinationVisibility = $destinationVisibility }
                if ($settingsBehavior -eq 'Skip') { $planParameters.SkipSettings = $true }
                if ($destinationRepository -eq $sourceRepository.FullName) {
                    $planParameters.ArchiveRepositoryName = $archiveRepositoryName
                }
                elseif (-not [string]::IsNullOrWhiteSpace($existingDestinationArchiveName)) {
                    $planParameters.ExistingDestinationArchiveName = $existingDestinationArchiveName
                }

                $plan = Copy-GitHubRepository @planParameters

                Write-CgrWizardMessage
                Write-CgrWizardMessage -Message 'Repository copy plan' -Style Heading
                Write-CgrWizardMessage -Message (' Source: {0}' -f $plan.SourceRepository) -Style Value
                Write-CgrWizardMessage -Message (' Destination: {0}' -f $plan.DestinationRepository) -Style Value
                Write-CgrWizardMessage -Message (' Content mode: {0}' -f $plan.ContentMode)
                if ($plan.ContentMode -eq 'Snapshot') {
                    if ($plan.IncludeReleases) {
                        Write-CgrWizardMessage -Message ' Snapshot release preservation does not preserve original commit identities or ancestry.' -Status Warning
                        Write-CgrWizardMessage -Message ' Selected release states become newly constructed Snapshot checkpoint commits, and selected tags are recreated against those new commits.' -Style Hint
                        Write-CgrWizardMessage -Message (' Approved source commit: {0}' -f $plan.SourceState.CommitSha) -Style Hint
                        Write-CgrWizardMessage -Message (' Commit message: {0}' -f $plan.CommitMessage) -Style Hint

                        $releaseSelection = $plan.ReleaseSelection
                        $checkpointPlan = $plan.ReleaseCheckpointPlan
                        Write-CgrWizardMessage -Message (' Selected releases: {0} of {1}' -f $releaseSelection.SelectedReleaseCount, $releaseSelection.AvailableReleaseCount) -Style Hint
                        Write-CgrWizardMessage -Message (' Selected release assets: {0}' -f $releaseSelection.SelectedAssetCount) -Style Hint
                        $includePatterns = if (@($releaseSelection.IncludePatterns).Count -gt 0) { @($releaseSelection.IncludePatterns) -join ', ' } else { '(all tags)' }
                        $excludePatterns = if (@($releaseSelection.ExcludePatterns).Count -gt 0) { @($releaseSelection.ExcludePatterns) -join ', ' } else { '(none)' }
                        $releaseLimit = if ($null -ne $releaseSelection.ReleaseCount) { [string] $releaseSelection.ReleaseCount } else { '(none)' }
                        Write-CgrWizardMessage -Message (' Include tag filters: {0}' -f $includePatterns) -Style Hint
                        Write-CgrWizardMessage -Message (' Exclude tag filters: {0}' -f $excludePatterns) -Style Hint
                        Write-CgrWizardMessage -Message (' Prereleases: {0}' -f $(if ($releaseSelection.IncludePrerelease) { 'included' } else { 'excluded' })) -Style Hint
                        Write-CgrWizardMessage -Message (' Draft releases: {0}' -f $(if ($releaseSelection.IncludeDraftReleases) { 'included' } else { 'excluded' })) -Style Hint
                        Write-CgrWizardMessage -Message (' Release count limit: {0}' -f $releaseLimit) -Style Hint
                        Write-CgrWizardMessage -Message (' Snapshot release checkpoints: {0}' -f $checkpointPlan.CheckpointCount) -Style Hint
                        Write-CgrWizardMessage -Message (' Planned Snapshot commits: {0}' -f $checkpointPlan.PlannedSnapshotCommitCount) -Style Hint
                        Write-CgrWizardMessage -Message (' Final current-state checkpoint required: {0}' -f $checkpointPlan.FinalHeadCheckpointRequired) -Style Hint
                        Write-CgrWizardMessage -Message ' Reviewed Snapshot checkpoint plan:' -Style Heading
                        if (@($checkpointPlan.Checkpoints).Count -eq 0) {
                            Write-CgrWizardMessage -Message ' - No release checkpoint commits are selected; the reviewed plan contains only the current Snapshot state.' -Style Hint
                        }
                        else {
                            foreach ($checkpoint in @($checkpointPlan.Checkpoints)) {
                                $tagNames = @($checkpoint.TagNames) -join ', '
                                Write-CgrWizardMessage -Message (' - {0}. source commit {1}; tree {2}; recreated tags: {3}' -f $checkpoint.Order, $checkpoint.SourceCommitSha, $checkpoint.SourceTreeSha, $tagNames)
                            }
                        }
                    }
                    else {
                        Write-CgrWizardMessage -Message ' Snapshot publishes the approved default-branch content as one new root commit without prior Git history.' -Style Hint
                        Write-CgrWizardMessage -Message (' Approved source commit: {0}' -f $plan.SourceState.CommitSha) -Style Hint
                        Write-CgrWizardMessage -Message (' Commit message: {0}' -f $plan.CommitMessage) -Style Hint

                        $historicalRecords = Get-CgrObjectProperty -InputObject $plan.SourceState -Name 'HistoricalRecords'
                        if ($historicalRecords) {
                            $tagCount = [int] (Get-CgrObjectProperty -InputObject $historicalRecords -Name 'TagCount')
                            $releaseCount = [int] (Get-CgrObjectProperty -InputObject $historicalRecords -Name 'ReleaseCount')
                            Write-CgrWizardMessage -Message (' Git tags: {0} (not copied)' -f $tagCount) -Style Hint
                            Write-CgrWizardMessage -Message (' GitHub Releases: {0} (not copied)' -f $releaseCount) -Style Hint
                            if ($plan.Mode -eq 'SameNameReplacement' -and ($tagCount -gt 0 -or $releaseCount -gt 0)) {
                                Write-CgrWizardMessage -Message 'Existing tags and GitHub Releases remain with the archived original repository; they are not recreated on the clean replacement.' -Status Warning
                                Write-CgrWizardMessage -Message 'Create the new release tag and GitHub Release only after the clean replacement has completed and been verified.' -Style Hint
                            }
                        }
                    }
                }
                else {
                    Write-CgrWizardMessage -Message ' FullHistory preserves the approved branches, tags, commits, and reachable Git LFS objects.' -Style Hint
                    Write-CgrWizardMessage -Message (' Approved refs: {0}; reachable commits: {1}' -f @($plan.SourceState.Refs).Count, $plan.SourceState.ReachableCommitCount) -Style Hint
                }
                Write-CgrWizardMessage -Message (' Visibility: {0}' -f $plan.DestinationVisibility)
                if (-not [string]::IsNullOrWhiteSpace([string] $plan.ArchiveRepository)) {
                    Write-CgrWizardMessage -Message (' Archive: {0}' -f $plan.ArchiveRepository) -Style Value
                }
                Write-CgrWizardMessage -Message ' Planned steps:' -Style Heading
                foreach ($plannedStep in @($plan.Steps)) {
                    Write-CgrWizardMessage -Message (' - {0}' -f $plannedStep.Description)
                }

                $confirmation = Read-CgrWizardChoice `
                    -Title 'Repository copy plan' `
                    -Choices @('Execute', 'Cancel') `
                    -DefaultValue Cancel `
                    -HelpTopic MigrationPlan `
                    -AllowBack `
                    -AllowCancel
                if ($confirmation.Action -eq 'Cancel' -or $confirmation.Value -eq 'Cancel') { return & $cancelResult }
                if ($confirmation.Action -eq 'Back') {
                    $step = if ($destinationRepository -eq $sourceRepository.FullName) { 5 } else { 4 }
                    continue
                }
                if ($confirmation.Value -ne 'Execute') { continue }

                if ($plan.Mode -eq 'ExistingDestinationReplacement') {
                    $expectedConfirmation = "DESTINATION=$($plan.DestinationRepository);ARCHIVE=$($plan.ArchiveRepository);REPLACEMENT=$($plan.DestinationRepository)"
                    while ($true) {
                        Write-CgrWizardMessage -Message 'The existing destination will be preserved as an archive before a fresh replacement is created.' -Status Warning
                        Write-CgrWizardMessage -Message 'Exact confirmation is required and cannot be bypassed.' -Style Hint
                        Write-CgrWizardMessage -Message 'Required text:' -Style Hint
                        Write-CgrWizardMessage -Message $expectedConfirmation -Style Value
                        $inputText = Read-CgrWizardInput -Prompt 'Exact confirmation' -AllowHelp -AllowCancel
                        $navigation = Resolve-CgrWizardNavigationInput -InputText $inputText -AllowCancel -AllowHelp
                        if ($null -ne $navigation) {
                            if ($navigation.Action -eq 'Help') { Show-CgrWizardHelp -Topic ExactConfirmation; continue }
                            if ($navigation.Action -eq 'Cancel') { return & $cancelResult }
                        }
                        if ($inputText -ceq $expectedConfirmation) {
                            $existingDestinationConfirmation = $inputText
                            break
                        }
                        Write-CgrWizardMessage -Message 'The confirmation did not match exactly. Retry or Cancel.' -Status Warning
                    }
                }

                if (-not (& $ExecutionGuard $destinationRepository)) { return $plan }

                Write-CgrWizardMessage -Message 'Executing the reviewed repository copy plan...' -Status Info
                try {
                    $executionResult = Invoke-CgrApprovedMigrationPlan `
                        -Plan $plan `
                        -SourceRepository $sourceRepository `
                        -SameNameConfirmation $sameNameConfirmation `
                        -ExistingDestinationConfirmation $existingDestinationConfirmation `
                        -HostName $HostName
                }
                catch {
                    $errorId = ([string] $_.FullyQualifiedErrorId).Split(',', 2)[0]
                    if ($errorId -eq 'SourceStateChangedSincePlanning') {
                        Write-CgrWizardMessage -Message $_.Exception.Message -Status Warning
                        Write-CgrWizardMessage -Message 'A new plan will be created for review. No GitHub mutation was performed from the stale plan.' -Style Hint
                        $sameNameConfirmation = $null
                        $existingDestinationConfirmation = $null
                        $step = 6
                        continue
                    }
                    throw
                }

                Write-CgrWizardCompletionSummary -Result $executionResult
                return $executionResult
            }
        }
    }
}