diff --git a/scripts/common.ps1 b/scripts/common.ps1 index 296e6b0b..20b504c3 100644 --- a/scripts/common.ps1 +++ b/scripts/common.ps1 @@ -588,6 +588,42 @@ function Get-LiveConfigValueMap { return $values } +function Get-PortableConfigDrift { + param( + [string]$Source, + [string]$Destination + ) + + $live = [System.Collections.Generic.Dictionary[string, string]]::new( + [System.StringComparer]::Ordinal + ) + if (Test-Path -LiteralPath $Destination -PathType Leaf) { + $live = Get-LiveConfigValueMap -Path $Destination + } + foreach ($entry in Get-PortableConfigEntries -Path $Source) { + $identity = "$($entry.Section)`n$($entry.Key)" + if (-not $live.ContainsKey($identity)) { + $state = "missing" + } + elseif ($live[$identity] -ne $entry.Value) { + $state = "differs" + } + else { + continue + } + $key = if ($entry.Section) { + "$($entry.Section).$($entry.Key)" + } + else { + $entry.Key + } + [pscustomobject]@{ + Key = $key + State = $state + } + } +} + function Merge-PortableConfig { param( [string]$Source, @@ -671,14 +707,7 @@ function Test-PortableConfigInSync { if (-not (Test-Path -LiteralPath $Destination -PathType Leaf)) { return $false } - $live = Get-LiveConfigValueMap -Path $Destination - foreach ($entry in Get-PortableConfigEntries -Path $Source) { - $identity = "$($entry.Section)`n$($entry.Key)" - if (-not $live.ContainsKey($identity) -or $live[$identity] -ne $entry.Value) { - return $false - } - } - return $true + return @(Get-PortableConfigDrift -Source $Source -Destination $Destination).Count -eq 0 } function Copy-PortableItem { diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 3c58a180..126817f3 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -47,6 +47,14 @@ if (-not $Apply) { else { foreach ($state in $changes) { Write-Host " $($state.Item.RepoPath) -> $($state.Item.LivePath)" + if ($state.Item.Type -eq "config" -and $state.Exists) { + foreach ($entry in @(Get-PortableConfigDrift ` + -Source $state.Item.RepoPath ` + -Destination $state.Item.LivePath + )) { + Write-Host " reviewed config key $($entry.State): $($entry.Key)" + } + } } } Write-Host "" diff --git a/scripts/test-all.ps1 b/scripts/test-all.ps1 index 1670d4be..d019a857 100644 --- a/scripts/test-all.ps1 +++ b/scripts/test-all.ps1 @@ -36,17 +36,49 @@ function Assert-TestFileContains { } } +function Assert-TestDriftOutput { + param([string]$Text) + + foreach ($expected in @( + "reviewed config key differs: model_reasoning_effort" + "reviewed config key missing: features.goals" + )) { + if (-not $Text.Contains($expected)) { + throw "expected output to contain: $expected" + } + } + foreach ($unexpected in @( + "machine_setting" + "machine-sensitive-preview-value" + )) { + if ($Text.Contains($unexpected)) { + throw "expected output not to contain: $unexpected" + } + } +} + $testRoot = Join-Path ([System.IO.Path]::GetTempPath()) "compass-test-$([guid]::NewGuid().ToString('N'))" $codexHome = Join-Path $testRoot "codex" $agentsHome = Join-Path $testRoot "agents" $claudeHome = Join-Path $testRoot "claude" try { + . "$PSScriptRoot\common.ps1" + + $emptyConfigPath = Join-Path $testRoot "empty-config.toml" + $directoryTarget = Join-Path $testRoot "config-directory" + Write-TestFile -Path $emptyConfigPath -Content "" + New-Item -ItemType Directory -Path $directoryTarget | Out-Null + if (Test-PortableConfigInSync -Source $emptyConfigPath -Destination $directoryTarget) { + throw "config directory target reported in sync" + } + $machineLabel = "preserve caf$([char]0x00E9)" Write-TestFile -Path (Join-Path $codexHome "AGENTS.md\local.txt") -Content "preserve this backup`n" Write-TestFile -Path (Join-Path $codexHome "auth.json") -Content "leave unlisted state alone`n" Write-TestFile -Path (Join-Path $codexHome "config.toml") -Content @" MODEL = "machine-specific" +model_reasoning_effort = "machine-sensitive-preview-value" machine_setting = "$machineLabel" ["features"] @@ -56,6 +88,30 @@ memories = false trust_level = "trusted" "@ + $configPath = Join-Path $codexHome "config.toml" + $configBeforePreview = Get-Content -Raw -LiteralPath $configPath + $powerShellPath = (Get-Process -Id $PID).Path + $previewOutput = & $powerShellPath ` + -NoProfile ` + -ExecutionPolicy Bypass ` + -File (Join-Path $PSScriptRoot "install.ps1") ` + -CodexHome $codexHome ` + -AgentsHome $agentsHome ` + -ClaudeHome $claudeHome 2>&1 | Out-String + if ($LASTEXITCODE -ne 0) { + throw "install preview failed: $previewOutput" + } + if ((Get-Content -Raw -LiteralPath $configPath) -cne $configBeforePreview) { + throw "install preview changed live config" + } + Assert-TestDriftOutput -Text $previewOutput + + $verifyOutput = & (Join-Path $PSScriptRoot "verify-live.ps1") ` + -CodexHome $codexHome ` + -AgentsHome $agentsHome ` + -ClaudeHome $claudeHome 6>&1 | Out-String + Assert-TestDriftOutput -Text $verifyOutput + & (Join-Path $PSScriptRoot "install.ps1") ` -Apply ` -CodexHome $codexHome ` diff --git a/scripts/verify-live.ps1 b/scripts/verify-live.ps1 index 607a3e92..e8dbafa7 100644 --- a/scripts/verify-live.ps1 +++ b/scripts/verify-live.ps1 @@ -20,7 +20,7 @@ $items = @( -ClaudeHome $claudeHome ) $missing = New-Object System.Collections.Generic.List[string] -$drift = New-Object System.Collections.Generic.List[string] +$drift = New-Object System.Collections.Generic.List[object] foreach ($item in $items) { if (-not (Test-Path -LiteralPath $item.LivePath)) { @@ -28,7 +28,7 @@ foreach ($item in $items) { continue } if (-not (Test-PortableItemInSync -Item $item)) { - $drift.Add($item.LivePath) + $drift.Add($item) } } @@ -48,8 +48,16 @@ if ($missing.Count -gt 0) { if ($drift.Count -gt 0) { Write-Host "" Write-Host "drift:" - foreach ($path in $drift) { - Write-Host " $path" + foreach ($item in $drift) { + Write-Host " $($item.LivePath)" + if ($item.Type -eq "config") { + foreach ($entry in @(Get-PortableConfigDrift ` + -Source $item.RepoPath ` + -Destination $item.LivePath + )) { + Write-Host " reviewed config key $($entry.State): $($entry.Key)" + } + } } }