workflows/default/systems/runtime/post-phase-task-groups.ps1

<#
.SYNOPSIS
Post-processing script for the task-groups kickstart phase.

.DESCRIPTION
Runs after Claude creates task-groups.json. Injects metadata into the JSON file,
then generates a deterministic roadmap-overview.md with Gantt chart and cost comparison.

Called as a post_script from the kickstart phase pipeline.

.PARAMETER BotRoot
Path to the .bot directory.

.PARAMETER ProductDir
Path to the workspace/product directory.

.PARAMETER Settings
Parsed settings object (used for cost config).

.PARAMETER Model
Claude model name used for metadata.

.PARAMETER ProcessId
Process registry ID for metadata.
#>


param(
    [Parameter(Mandatory)]
    [string]$BotRoot,

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

    [Parameter(Mandatory)]
    $Settings,

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

    [string]$ProcessId
)

# Inject metadata into task-groups.json
$groupsPath = Join-Path $ProductDir "task-groups.json"
$groupsJson = Get-Content $groupsPath -Raw | ConvertFrom-Json
$groupsJson | Add-Member -NotePropertyName "generated_at" -NotePropertyValue (Get-Date).ToUniversalTime().ToString("o") -Force
$groupsJson | Add-Member -NotePropertyName "model" -NotePropertyValue $Model -Force
$groupsJson | Add-Member -NotePropertyName "process_id" -NotePropertyValue $ProcessId -Force
$groupsJson | Add-Member -NotePropertyName "generator" -NotePropertyValue "dotbot-kickstart" -Force
$groupsJson | ConvertTo-Json -Depth 10 | Set-Content -Path $groupsPath -Encoding utf8NoBOM

# ===== Generate roadmap-overview.md (deterministic, no LLM) =====
try {
    $costDefaults = @{ hourly_rate = 50; ai_cost_per_task = 0.50; ai_speedup_factor = 10; currency = "USD" }
    $costConfig = if ($Settings.costs) { $Settings.costs } else { $costDefaults }
    $hourlyRate = if ($costConfig.hourly_rate) { [decimal]$costConfig.hourly_rate } else { 50 }
    $aiCostPerTask = if ($costConfig.ai_cost_per_task) { [decimal]$costConfig.ai_cost_per_task } else { 0.50 }
    $aiSpeedupFactor = if ($costConfig.ai_speedup_factor) { [decimal]$costConfig.ai_speedup_factor } else { 10 }
    $currency = if ($costConfig.currency) { $costConfig.currency } else { "USD" }

    $groupsData = Get-Content $groupsPath -Raw | ConvertFrom-Json
    $sortedGroups = $groupsData.groups | Sort-Object { $_.order }

    $totalEffortDays = ($sortedGroups | ForEach-Object { if ($_.effort_days) { $_.effort_days } else { 3 } } | Measure-Object -Sum).Sum
    $totalTasks = ($sortedGroups | ForEach-Object { $_.estimated_task_count } | Measure-Object -Sum).Sum

    $roadmap = [System.Collections.ArrayList]::new()
    [void]$roadmap.Add("---")
    [void]$roadmap.Add("generated_at: `"$((Get-Date).ToUniversalTime().ToString("o"))`"")
    [void]$roadmap.Add("model: `"$Model`"")
    [void]$roadmap.Add("process_id: `"$ProcessId`"")
    [void]$roadmap.Add("phase: `"phase-2b-roadmap`"")
    [void]$roadmap.Add("generator: `"dotbot-kickstart`"")
    [void]$roadmap.Add("---")
    [void]$roadmap.Add("")
    [void]$roadmap.Add("# Roadmap Overview")
    [void]$roadmap.Add("")
    [void]$roadmap.Add("**Project:** $($groupsData.project_name)")
    [void]$roadmap.Add("**Generated:** $(Get-Date -Format 'yyyy-MM-dd')")
    [void]$roadmap.Add("**Groups:** $($sortedGroups.Count) | **Estimated Tasks:** $totalTasks | **Effort:** $totalEffortDays developer-days")
    [void]$roadmap.Add("")

    # Executive summary from mission.md
    $missionPath = Join-Path $ProductDir "mission.md"
    if (Test-Path $missionPath) {
        $missionContent = Get-Content $missionPath -Raw
        if ($missionContent -match '(?ms)## Executive Summary\s*\n(.+?)(?=\n## |\z)') {
            [void]$roadmap.Add("## Executive Summary")
            [void]$roadmap.Add("")
            [void]$roadmap.Add($matches[1].Trim())
            [void]$roadmap.Add("")
        } elseif ($missionContent -match '(?m)^#[^#].*\n+(.+)') {
            [void]$roadmap.Add("## Executive Summary")
            [void]$roadmap.Add("")
            [void]$roadmap.Add($matches[1].Trim())
            [void]$roadmap.Add("")
        }
    }

    # Mermaid gantt chart — AI-assisted timeline
    [void]$roadmap.Add("## Timeline (AI-Assisted)")
    [void]$roadmap.Add("")
    [void]$roadmap.Add('```mermaid')
    [void]$roadmap.Add("gantt")
    [void]$roadmap.Add(" title $($groupsData.project_name) — AI-Assisted Timeline")
    [void]$roadmap.Add(" dateFormat YYYY-MM-DD")
    [void]$roadmap.Add(" axisFormat %b %d")
    [void]$roadmap.Add("")

    $today = Get-Date
    $groupEndDates = @{}

    foreach ($group in $sortedGroups) {
        $effortDays = if ($group.effort_days) { [int]$group.effort_days } else { 3 }
        $aiDays = [math]::Ceiling($effortDays / $aiSpeedupFactor)
        if ($aiDays -lt 1) { $aiDays = 1 }

        # Determine start date based on dependencies
        $startDate = $today
        if ($group.depends_on -and $group.depends_on.Count -gt 0) {
            foreach ($depId in $group.depends_on) {
                if ($groupEndDates.ContainsKey($depId) -and $groupEndDates[$depId] -gt $startDate) {
                    $startDate = $groupEndDates[$depId]
                }
            }
        }

        $endDate = $startDate.AddDays($aiDays)
        $groupEndDates[$group.id] = $endDate

        $startStr = $startDate.ToString("yyyy-MM-dd")
        # Sanitize name for Mermaid (remove special chars)
        $safeName = $group.name -replace '[:#]', ''

        [void]$roadmap.Add(" section $safeName")
        [void]$roadmap.Add(" $safeName :$($group.id), $startStr, ${aiDays}d")
    }

    [void]$roadmap.Add('```')
    [void]$roadmap.Add("")

    # Human vs AI comparison table
    [void]$roadmap.Add("## Human vs AI-Assisted Comparison")
    [void]$roadmap.Add("")
    [void]$roadmap.Add("| Group | Human (days) | AI (days) | Human Cost | AI Cost | Speedup |")
    [void]$roadmap.Add("|-------|-------------|-----------|------------|---------|---------|")

    $totalHumanDays = 0
    $totalAiDays = 0
    $totalHumanCost = [decimal]0
    $totalAiCost = [decimal]0

    foreach ($group in $sortedGroups) {
        $effortDays = if ($group.effort_days) { [int]$group.effort_days } else { 3 }
        $aiDays = [math]::Ceiling($effortDays / $aiSpeedupFactor)
        if ($aiDays -lt 1) { $aiDays = 1 }
        $taskCount = if ($group.estimated_task_count) { [int]$group.estimated_task_count } else { 3 }

        $humanCost = [decimal]($effortDays * 8 * $hourlyRate)
        $aiLaborCost = [decimal]($aiDays * 8 * $hourlyRate)
        $aiApiCost = [decimal]($taskCount * $aiCostPerTask)
        $groupAiCost = $aiLaborCost + $aiApiCost

        $totalHumanDays += $effortDays
        $totalAiDays += $aiDays
        $totalHumanCost += $humanCost
        $totalAiCost += $groupAiCost

        $speedup = if ($aiDays -gt 0) { "{0:N1}x" -f ($effortDays / $aiDays) } else { "N/A" }
        $safeName = $group.name -replace '\|', '/'

        [void]$roadmap.Add("| $safeName | $effortDays | $aiDays | $currency $("{0:N0}" -f $humanCost) | $currency $("{0:N0}" -f $groupAiCost) | $speedup |")
    }

    $totalSpeedup = if ($totalAiDays -gt 0) { "{0:N1}x" -f ($totalHumanDays / $totalAiDays) } else { "N/A" }
    [void]$roadmap.Add("| **Total** | **$totalHumanDays** | **$totalAiDays** | **$currency $("{0:N0}" -f $totalHumanCost)** | **$currency $("{0:N0}" -f $totalAiCost)** | **$totalSpeedup** |")
    [void]$roadmap.Add("")

    $savings = $totalHumanCost - $totalAiCost
    $savingsPercent = if ($totalHumanCost -gt 0) { [math]::Round(($savings / $totalHumanCost) * 100) } else { 0 }
    [void]$roadmap.Add("**Estimated savings:** $currency $("{0:N0}" -f $savings) ($savingsPercent%)")
    [void]$roadmap.Add("")

    # Implementation groups detail
    [void]$roadmap.Add("## Implementation Groups")
    [void]$roadmap.Add("")

    foreach ($group in $sortedGroups) {
        $depStr = if ($group.depends_on -and $group.depends_on.Count -gt 0) {
            " | Depends on: $(($group.depends_on) -join ', ')"
        } else { "" }

        [void]$roadmap.Add("### $($group.order). $($group.name)")
        [void]$roadmap.Add("")
        [void]$roadmap.Add("$($group.description)")
        [void]$roadmap.Add("")
        $effortDays = if ($group.effort_days) { $group.effort_days } else { "?" }
        [void]$roadmap.Add("- **Estimated tasks:** $($group.estimated_task_count) | **Effort:** $effortDays days$depStr")
        [void]$roadmap.Add("")
    }

    $overviewPath = Join-Path $ProductDir "roadmap-overview.md"
    $roadmap -join "`n" | Set-Content -Path $overviewPath -Encoding UTF8
    Write-Status "Roadmap overview generated: $overviewPath" -Type Success
} catch {
    Write-Status "Warning: could not generate roadmap overview: $($_.Exception.Message)" -Type Warn
}