extensions/specrew-speckit/scripts/sync-boundary-state.ps1
|
[CmdletBinding()] param( [string]$ProjectPath = '.', [Parameter(Mandatory = $true)] [ValidateSet('specify', 'clarify', 'plan', 'tasks', 'before-implement', 'review-signoff', 'retro', 'iteration-closeout', 'feature-closeout')] [string]$BoundaryType, [string]$FeatureRef, [string]$IterationNumber, [string]$TaskId, [string]$AuthCommitHash, [string]$IdentityFocusArea, [string]$IdentityActiveIssues, [string]$IdentityBody ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' # Resolve the internal sync helper. Three layouts (in priority order): # # 0) Explicit override via $env:SPECREW_MODULE_PATH (iter-006 T001). Set by # Specrew.psm1 at import time so agent-spawned child PowerShell processes # dispatch to the actively-imported Dev tree instead of a stale PSGallery install. # # 1) Specrew dev-tree dogfooding: walk up from $PSScriptRoot looking for a dir # that contains BOTH .specrew/config.yml AND scripts/internal/sync-boundary-state.ps1. # # 2) Downstream project: discover via Get-Module -Name Specrew -ListAvailable # (highest version wins). # # iter-006 T001 also adds STALE-INSTALL DETECTION (after resolution): compares the # resolved module's version against the project's `.specrew/config.yml::specrew_version`. # If installed < expected, refuses to dispatch with actionable guidance. $internalScriptPath = $null $resolvedModuleBase = $null $resolvedModuleVersion = $null function _Read-SpecrewVersionFromPsd1 { param([string]$ModuleBase) $psd1Path = Join-Path $ModuleBase 'Specrew.psd1' if (-not (Test-Path -LiteralPath $psd1Path -PathType Leaf)) { return $null } try { $manifest = Import-PowerShellDataFile -LiteralPath $psd1Path return [version][string]$manifest.ModuleVersion } catch { return $null } } # Path 0: explicit env override (iter-006 T001). if (-not [string]::IsNullOrWhiteSpace($env:SPECREW_MODULE_PATH)) { $candidate = Join-Path $env:SPECREW_MODULE_PATH 'scripts\internal\sync-boundary-state.ps1' if (Test-Path -LiteralPath $candidate -PathType Leaf) { $internalScriptPath = $candidate $resolvedModuleBase = $env:SPECREW_MODULE_PATH $resolvedModuleVersion = _Read-SpecrewVersionFromPsd1 -ModuleBase $env:SPECREW_MODULE_PATH } } # Path 1: dev-tree layout (walk up to find Specrew's own repo root). if ([string]::IsNullOrWhiteSpace($internalScriptPath)) { $searchRoot = $PSScriptRoot while (-not [string]::IsNullOrWhiteSpace($searchRoot)) { $candidate = Join-Path $searchRoot 'scripts\internal\sync-boundary-state.ps1' if ((Test-Path -LiteralPath (Join-Path $searchRoot '.specrew\config.yml') -PathType Leaf) -and (Test-Path -LiteralPath $candidate -PathType Leaf)) { $internalScriptPath = $candidate $resolvedModuleBase = $searchRoot $resolvedModuleVersion = _Read-SpecrewVersionFromPsd1 -ModuleBase $searchRoot break } $parent = Split-Path -Parent $searchRoot if ($parent -eq $searchRoot) { break } $searchRoot = $parent } } # Path 2: installed Specrew module (highest version wins). if ([string]::IsNullOrWhiteSpace($internalScriptPath)) { $specrewModule = Get-Module -Name 'Specrew' -ListAvailable -ErrorAction SilentlyContinue | Sort-Object Version -Descending | Select-Object -First 1 if ($null -ne $specrewModule) { $candidate = Join-Path $specrewModule.ModuleBase 'scripts\internal\sync-boundary-state.ps1' if (Test-Path -LiteralPath $candidate -PathType Leaf) { $internalScriptPath = $candidate $resolvedModuleBase = $specrewModule.ModuleBase $resolvedModuleVersion = $specrewModule.Version } } } if ([string]::IsNullOrWhiteSpace($internalScriptPath)) { throw @" Unable to locate the internal sync-boundary-state helper. Checked: 0) `$env:SPECREW_MODULE_PATH (not set or no helper at that path) 1) Dev-tree walk-up from '$PSScriptRoot' (no .specrew/config.yml + scripts/internal/ pair found) 2) Installed Specrew module (Get-Module -Name Specrew -ListAvailable returned nothing usable) Fix one of: - Install Specrew: Install-Module -Name Specrew - Dogfood Dev tree: Import-Module <path>/Specrew.psm1 -Force (auto-sets `$env:SPECREW_MODULE_PATH) - Override manually: `$env:SPECREW_MODULE_PATH = '<path-to-Specrew-repo>' "@ } # iter-006 T001: stale-install detection — compare resolved version against project's expected version. $projectConfigPath = Join-Path $ProjectPath '.specrew\config.yml' if ((Test-Path -LiteralPath $projectConfigPath -PathType Leaf) -and ($null -ne $resolvedModuleVersion)) { $configLines = Get-Content -LiteralPath $projectConfigPath -ErrorAction SilentlyContinue $expectedRaw = $configLines | ForEach-Object { if ($_ -match '^\s*specrew_version:\s*[''"]?([^''"#]+?)[''"]?\s*(?:#.*)?$') { $Matches[1].Trim() } } | Select-Object -First 1 if (-not [string]::IsNullOrWhiteSpace($expectedRaw)) { try { $expectedVersion = [version]$expectedRaw if ($resolvedModuleVersion -lt $expectedVersion) { throw @" Stale Specrew install — boundary-sync dispatch refused (iter-006 T001 stale-install check). Project expects: $expectedVersion (from $projectConfigPath::specrew_version) Resolved: $resolvedModuleVersion (from $resolvedModuleBase) The internal helpers in the resolved module may lack support for boundary types the deployed shim assumes, causing silent contract drift (which is what surfaced in the F-044 iter-006 Antigravity dogfood — sync silently routed to a stale 0.25.0 install and the agent had to patch deployed scaffolders). Fix one of: 1. 'specrew update' — bring the installed module up to project expectations. 2. Import the active Dev tree: Import-Module <dev-path>/Specrew.psm1 -Force (auto-sets `$env:SPECREW_MODULE_PATH so child processes inherit the path). 3. Manual override: `$env:SPECREW_MODULE_PATH = '<dev-tree>' "@ } } catch [System.Management.Automation.RuntimeException] { # Re-throw our own stale-install error throw } catch { # Non-parseable specrew_version — skip the version check silently. } } } . $internalScriptPath $result = Invoke-SpecrewBoundaryStateSync ` -ProjectPath $ProjectPath ` -BoundaryType $BoundaryType ` -FeatureRef $FeatureRef ` -IterationNumber $IterationNumber ` -TaskId $TaskId ` -AuthCommitHash $AuthCommitHash ` -IdentityFocusArea $IdentityFocusArea ` -IdentityActiveIssues $IdentityActiveIssues ` -IdentityBody $IdentityBody if ($null -ne $result) { $result | ConvertTo-Json -Depth 6 | Write-Output } |