Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .conductor/registry/scripts/worktree-manager.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -251,18 +251,30 @@ try {
exit 0
}

# teardown
# teardown — retry-with-backoff for transient file-handle races on Windows.
# Mirrors the delay schedule in GitWorktreeDeleter.cs:RemoveWithRetryAsync.
if (-not (Test-Path $worktreePath)) {
# Idempotent: nothing to clean up.
$envelope.success = $true
$envelope | ConvertTo-Json -Compress
exit 0
}

$output = & git worktree remove --force $worktreePath 2>&1
if ($LASTEXITCODE -ne 0) {
$wtDelays = @(0, 200, 500, 1000)
$wtOutput = $null
$wtSuccess = $false
foreach ($wtDelayMs in $wtDelays) {
if ($wtDelayMs -gt 0) { Start-Sleep -Milliseconds $wtDelayMs }
$wtOutput = & git worktree remove --force $worktreePath 2>&1
if ($LASTEXITCODE -eq 0 -or -not (Test-Path $worktreePath)) {
$wtSuccess = $true
break
}
}

if (-not $wtSuccess) {
$envelope.error_code = 'worktree_remove_failed'
$envelope.error_message = "git worktree remove failed: $($output -join "`n")"
$envelope.error_message = "git worktree remove failed: $($wtOutput -join "`n")"
$envelope | ConvertTo-Json -Compress
exit 0
}
Expand Down
32 changes: 28 additions & 4 deletions scripts/Invoke-PolyphonySdlc.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ if ($Intent -ne 'reset') {

$script:LayoutDoc = 'docs/per-run-worktree-layout.md'

# ─── Helper: sanitize $Comment against shell-injection surfaces ──────────────
#
# $Comment flows into a here-string that is passed to pwsh -Command in the
# reset-detach path. Newlines (CR, LF, backtick-continuations) and bare
# backticks can break out of the embedded command string and inject arbitrary
# PowerShell. Strip them at the boundary before any interpolation.

function Get-SanitizedComment {
param([Parameter(Mandatory)][AllowEmptyString()][string]$Value)
# Strip CR and LF characters.
$sanitized = $Value -replace '[\r\n]', ''
# Escape backticks (PowerShell escape character) so they are literal.
$sanitized = $sanitized -replace '`', '``'
return $sanitized
}

# ─── Helper: canonical path comparison (boundary-aware, OS-aware) ─────────────

function Get-CanonicalPath {
Expand Down Expand Up @@ -313,6 +329,9 @@ if ($Intent -eq 'reset') {
}
}

# Sanitize $Comment at the boundary before any shell interpolation.
$Comment = Get-SanitizedComment -Value $Comment

# Auto-detect platform from origin for gh-identity pinning. The reset
# workflow itself does not take a platform input — the projection reset
# executor resolves the platform internally (the PR-abandonment leg
Expand Down Expand Up @@ -830,10 +849,15 @@ $detectedPlatform = $null
$detectedRepository = $null
$detectedRepoOrg = $null
$detectedRepoProject = $null
$remoteUrl = & git -C $mainWorktree remote get-url origin 2>&1
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($remoteUrl)) {
$remoteUrl = $null
$global:LASTEXITCODE = 0
Push-Location -LiteralPath $mainWorktree
try {
$remoteUrl = & git remote get-url origin 2>&1
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($remoteUrl)) {
$remoteUrl = $null
$global:LASTEXITCODE = 0
}
} finally {
Pop-Location
}

if ($remoteUrl) {
Expand Down
Loading