scripts/decisions-split.ps1

<#
.SYNOPSIS
    Split the legacy Squad decisions ledger into per-iteration ledgers (F-051 Iteration 2b).
 
.DESCRIPTION
    Multi-session mode keeps `.squad/decisions.md` readable for backwards compatibility,
    but mirrors iteration-scoped entries into `.squad/decisions/iteration-NNN/decisions.md`
    so concurrently active iterations have a smaller merge-conflict surface.
#>


Set-StrictMode -Version Latest

. (Join-Path $PSScriptRoot 'internal\atomic-write.ps1')

function Get-SpecrewDecisionsLedgerPath {
    param([Parameter(Mandatory = $true)][string]$ProjectRoot)
    return (Join-Path $ProjectRoot '.squad/decisions.md')
}

function Get-SpecrewIterationDecisionsPath {
    param(
        [Parameter(Mandatory = $true)][string]$ProjectRoot,
        [Parameter(Mandatory = $true)][string]$IterationNumber
    )

    $normalized = Normalize-SpecrewDecisionIterationNumber -IterationNumber $IterationNumber
    return (Join-Path $ProjectRoot ('.squad/decisions/iteration-{0}/decisions.md' -f $normalized))
}

function Normalize-SpecrewDecisionIterationNumber {
    param([Parameter(Mandatory = $true)][string]$IterationNumber)

    $trimmed = $IterationNumber.Trim()
    if ($trimmed -match '^\d+$') {
        return ([int]$trimmed).ToString('000')
    }

    return $trimmed
}

function Get-SpecrewDecisionEntries {
    param([Parameter(Mandatory = $true)][AllowEmptyString()][string]$Content)

    if ([string]::IsNullOrWhiteSpace($Content)) { return @() }

    $matches = [regex]::Matches($Content, '(?ms)^##\s+.+?(?=^##\s+|\z)')
    $entries = New-Object System.Collections.Generic.List[string]
    foreach ($match in $matches) {
        $text = $match.Value.Trim()
        if (-not [string]::IsNullOrWhiteSpace($text)) {
            $entries.Add($text) | Out-Null
        }
    }

    return $entries.ToArray()
}

function Get-SpecrewDecisionEntryIterationNumber {
    param([Parameter(Mandatory = $true)][string]$Entry)

    $patterns = @(
        '(?im)^\s*-\s+\*\*Iteration Number\*\*:\s*(?<iteration>[A-Za-z0-9_-]+)\s*$',
        '(?im)^\s*-\s+\*\*Affected Iteration\*\*:\s*(?<iteration>[A-Za-z0-9_-]+)\s*$',
        '(?im)^\s*-\s+\*\*Iteration\*\*:\s*(?<iteration>[A-Za-z0-9_-]+)\s*$'
    )

    foreach ($pattern in $patterns) {
        if ($Entry -match $pattern) {
            $raw = $Matches['iteration'].Trim()
            if ($raw -notin @('', '(none)', 'none', 'n/a')) {
                return (Normalize-SpecrewDecisionIterationNumber -IterationNumber $raw)
            }
        }
    }

    return $null
}

function ConvertTo-SpecrewDecisionFileContent {
    param(
        [Parameter(Mandatory = $true)][string]$IterationNumber,
        [AllowEmptyCollection()][AllowNull()][string[]]$Entries
    )

    $lines = New-Object System.Collections.Generic.List[string]
    $lines.Add(('# Decisions: Iteration {0}' -f $IterationNumber)) | Out-Null
    $lines.Add('') | Out-Null
    $lines.Add('Mirrored from `.squad/decisions.md` for F-051 multi-session conflict reduction.') | Out-Null
    $lines.Add('') | Out-Null

    foreach ($entry in @($Entries | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique)) {
        $lines.Add($entry.Trim()) | Out-Null
        $lines.Add('') | Out-Null
    }

    return (($lines -join [Environment]::NewLine).TrimEnd() + [Environment]::NewLine)
}

function Split-SpecrewDecisionsByIteration {
    param(
        [Parameter(Mandatory = $true)][string]$ProjectRoot,
        [switch]$PassThru
    )

    $ledgerPath = Get-SpecrewDecisionsLedgerPath -ProjectRoot $ProjectRoot
    if (-not (Test-Path -LiteralPath $ledgerPath -PathType Leaf)) {
        if ($PassThru) {
            return [pscustomobject]@{ written_count = 0; iteration_numbers = @(); source_path = $ledgerPath }
        }
        return
    }

    $content = Get-Content -LiteralPath $ledgerPath -Raw -Encoding UTF8
    $groups = [ordered]@{}
    foreach ($entry in Get-SpecrewDecisionEntries -Content $content) {
        $iteration = Get-SpecrewDecisionEntryIterationNumber -Entry $entry
        if ([string]::IsNullOrWhiteSpace($iteration)) { continue }
        if (-not $groups.Contains($iteration)) {
            $groups[$iteration] = New-Object System.Collections.Generic.List[string]
        }
        $groups[$iteration].Add($entry) | Out-Null
    }

    $written = 0
    foreach ($iteration in $groups.Keys) {
        $targetPath = Get-SpecrewIterationDecisionsPath -ProjectRoot $ProjectRoot -IterationNumber $iteration
        $targetDir = Split-Path -Parent $targetPath
        if (-not (Test-Path -LiteralPath $targetDir -PathType Container)) {
            New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
        }

        $nextContent = ConvertTo-SpecrewDecisionFileContent -IterationNumber $iteration -Entries $groups[$iteration].ToArray()
        $currentContent = if (Test-Path -LiteralPath $targetPath -PathType Leaf) { Get-Content -LiteralPath $targetPath -Raw -Encoding UTF8 } else { $null }
        if ($currentContent -ne $nextContent) {
            Write-SpecrewFileAtomic -Path $targetPath -Content $nextContent
            $written++
        }
    }

    if ($PassThru) {
        return [pscustomobject]@{
            written_count     = $written
            iteration_numbers = @($groups.Keys)
            source_path       = $ledgerPath
        }
    }
}

if ($MyInvocation.InvocationName -ne '.') {
    $projectRoot = if ($args.Count -gt 0 -and -not [string]::IsNullOrWhiteSpace([string]$args[0])) { [string]$args[0] } else { (Get-Location).Path }
    Split-SpecrewDecisionsByIteration -ProjectRoot $projectRoot -PassThru | ConvertTo-Json -Depth 5
}