Skip to content

Commit a2ffa47

Browse files
authored
Merge branch 'main' into fix/server-managed-handoff-shield
2 parents a3e5bf7 + fb67680 commit a2ffa47

File tree

112 files changed

+8357
-1709
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+8357
-1709
lines changed

.agents/skills/code-change-verification/SKILL.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,25 @@ Ensure work is only marked complete after formatting, linting, type checking, an
1414
1. Keep this skill at `./.agents/skills/code-change-verification` so it loads automatically for the repository.
1515
2. macOS/Linux: `bash .agents/skills/code-change-verification/scripts/run.sh`.
1616
3. Windows: `powershell -ExecutionPolicy Bypass -File .agents/skills/code-change-verification/scripts/run.ps1`.
17-
4. If any command fails, fix the issue, rerun the script, and report the failing output.
18-
5. Confirm completion only when all commands succeed with no remaining issues.
17+
4. The scripts run `make format` first, then run `make lint`, `make typecheck`, and `make tests` in parallel with fail-fast semantics.
18+
5. While the parallel steps are still running, the scripts emit periodic heartbeat updates so you can tell that work is still in progress.
19+
6. If any command fails, fix the issue, rerun the script, and report the failing output.
20+
7. Confirm completion only when all commands succeed with no remaining issues.
1921

2022
## Manual workflow
2123

2224
- If dependencies are not installed or have changed, run `make sync` first to install dev requirements via `uv`.
23-
- Run from the repository root in this order: `make format`, `make lint`, `make typecheck`, `make tests`.
25+
- Run from the repository root with `make format` first, then `make lint`, `make typecheck`, and `make tests`.
2426
- Do not skip steps; stop and fix issues immediately when a command fails.
27+
- If you run the steps manually, you may parallelize `make lint`, `make typecheck`, and `make tests` after `make format` completes, but you must stop the remaining steps as soon as one fails.
2528
- Re-run the full stack after applying fixes so the commands execute in the required order.
2629

2730
## Resources
2831

2932
### scripts/run.sh
3033

31-
- Executes the full verification sequence with fail-fast semantics from the repository root. Prefer this entry point to ensure the required commands run in the correct order.
34+
- Executes `make format` first, then runs `make lint`, `make typecheck`, and `make tests` in parallel with fail-fast semantics from the repository root. It also emits periodic heartbeat updates while the parallel steps are still running. Prefer this entry point to preserve the required ordering while reducing total runtime.
3235

3336
### scripts/run.ps1
3437

35-
- Windows-friendly wrapper that runs the same verification sequence with fail-fast semantics. Use from PowerShell with execution policy bypass if required by your environment.
38+
- Windows-friendly wrapper that runs the same sequence with `make format` first and the remaining steps in parallel with fail-fast semantics, plus periodic heartbeat updates while work is still running. Use from PowerShell with execution policy bypass if required by your environment.

.agents/skills/code-change-verification/scripts/run.ps1

Lines changed: 178 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,198 @@ try {
1111
}
1212

1313
if (-not $repoRoot) {
14-
$repoRoot = Resolve-Path (Join-Path $scriptDir "..\\..\\..\\..")
14+
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..\\..\\..\\..")).Path
15+
} else {
16+
$repoRoot = ([string]$repoRoot).Trim()
1517
}
1618

1719
Set-Location $repoRoot
1820

21+
$logDir = Join-Path ([System.IO.Path]::GetTempPath()) ("code-change-verification-" + [System.Guid]::NewGuid().ToString("N"))
22+
New-Item -ItemType Directory -Path $logDir | Out-Null
23+
24+
$steps = New-Object System.Collections.Generic.List[object]
25+
$heartbeatIntervalSeconds = 10
26+
if ($env:CODE_CHANGE_VERIFICATION_HEARTBEAT_SECONDS) {
27+
$heartbeatIntervalSeconds = [int]$env:CODE_CHANGE_VERIFICATION_HEARTBEAT_SECONDS
28+
}
29+
30+
function Resolve-MakeInvocation {
31+
$command = Get-Command make -ErrorAction Stop
32+
33+
while ($command.CommandType -eq [System.Management.Automation.CommandTypes]::Alias) {
34+
$command = $command.ResolvedCommand
35+
}
36+
37+
if ($command.CommandType -in @(
38+
[System.Management.Automation.CommandTypes]::Application,
39+
[System.Management.Automation.CommandTypes]::ExternalScript
40+
)) {
41+
$commandPath = if ($command.Path) { $command.Path } else { $command.Source }
42+
return [PSCustomObject]@{
43+
FilePath = $commandPath
44+
ArgumentList = @()
45+
}
46+
}
47+
48+
if ($command.CommandType -eq [System.Management.Automation.CommandTypes]::Function) {
49+
$shellPath = (Get-Process -Id $PID).Path
50+
if (-not $shellPath) {
51+
throw "Unable to resolve the current PowerShell executable for make wrapper launches."
52+
}
53+
54+
$wrapperPath = Join-Path $logDir "invoke-make.ps1"
55+
$escapedRepoRoot = $repoRoot -replace "'", "''"
56+
$wrapperTemplate = @'
57+
Set-StrictMode -Version Latest
58+
$ErrorActionPreference = "Stop"
59+
Set-Location -LiteralPath '{0}'
60+
function global:make {{
61+
{1}
62+
}}
63+
& make @args
64+
exit $LASTEXITCODE
65+
'@
66+
$wrapperScript = $wrapperTemplate -f $escapedRepoRoot, $command.Definition.TrimEnd()
67+
Set-Content -Path $wrapperPath -Value $wrapperScript -Encoding UTF8
68+
69+
return [PSCustomObject]@{
70+
FilePath = $shellPath
71+
ArgumentList = @("-NoLogo", "-NoProfile", "-File", $wrapperPath)
72+
}
73+
}
74+
75+
throw "code-change-verification: make must resolve to an application, script, alias, or function."
76+
}
77+
78+
$script:MakeInvocation = Resolve-MakeInvocation
79+
1980
function Invoke-MakeStep {
2081
param(
2182
[Parameter(Mandatory = $true)][string]$Step
2283
)
2384

2485
Write-Host "Running make $Step..."
25-
& make $Step
86+
& $script:MakeInvocation.FilePath @($script:MakeInvocation.ArgumentList + $Step)
2687

2788
if ($LASTEXITCODE -ne 0) {
28-
Write-Error "code-change-verification: make $Step failed with exit code $LASTEXITCODE."
29-
exit $LASTEXITCODE
89+
Write-Host "code-change-verification: make $Step failed with exit code $LASTEXITCODE."
90+
return $LASTEXITCODE
3091
}
92+
93+
return 0
94+
}
95+
96+
function Start-MakeStep {
97+
param(
98+
[Parameter(Mandatory = $true)][string]$Step
99+
)
100+
101+
$stdoutLogPath = Join-Path $logDir "$Step.stdout.log"
102+
$stderrLogPath = Join-Path $logDir "$Step.stderr.log"
103+
Write-Host "Running make $Step..."
104+
$process = Start-Process -FilePath $script:MakeInvocation.FilePath -ArgumentList @($script:MakeInvocation.ArgumentList + $Step) -RedirectStandardOutput $stdoutLogPath -RedirectStandardError $stderrLogPath -PassThru
105+
$steps.Add([PSCustomObject]@{
106+
Name = $Step
107+
Process = $process
108+
StdoutLogPath = $stdoutLogPath
109+
StderrLogPath = $stderrLogPath
110+
StartTime = Get-Date
111+
})
31112
}
32113

33-
Invoke-MakeStep -Step "format"
34-
Invoke-MakeStep -Step "lint"
35-
Invoke-MakeStep -Step "typecheck"
36-
Invoke-MakeStep -Step "tests"
114+
function Stop-RunningSteps {
115+
foreach ($step in $steps) {
116+
if ($null -eq $step.Process) {
117+
continue
118+
}
119+
120+
& taskkill /PID $step.Process.Id /T /F *> $null
121+
}
122+
123+
foreach ($step in $steps) {
124+
if ($null -eq $step.Process) {
125+
continue
126+
}
127+
128+
try {
129+
$step.Process.WaitForExit()
130+
} catch {
131+
}
132+
}
133+
}
134+
135+
function Wait-ForParallelSteps {
136+
$pending = New-Object System.Collections.Generic.List[object]
137+
foreach ($step in $steps) {
138+
$pending.Add($step)
139+
}
140+
$nextHeartbeatAt = (Get-Date).AddSeconds($heartbeatIntervalSeconds)
141+
142+
while ($pending.Count -gt 0) {
143+
foreach ($step in @($pending)) {
144+
$step.Process.Refresh()
145+
if (-not $step.Process.HasExited) {
146+
continue
147+
}
148+
149+
$duration = [int]((Get-Date) - $step.StartTime).TotalSeconds
150+
if ($step.Process.ExitCode -eq 0) {
151+
Write-Host "make $($step.Name) passed in ${duration}s."
152+
[void]$pending.Remove($step)
153+
continue
154+
}
155+
156+
Write-Host "code-change-verification: make $($step.Name) failed with exit code $($step.Process.ExitCode) after ${duration}s."
157+
if (Test-Path $step.StderrLogPath) {
158+
Write-Host "--- $($step.Name) stderr log (last 80 lines) ---"
159+
Get-Content $step.StderrLogPath -Tail 80
160+
}
161+
if (Test-Path $step.StdoutLogPath) {
162+
Write-Host "--- $($step.Name) stdout log (last 80 lines) ---"
163+
Get-Content $step.StdoutLogPath -Tail 80
164+
}
165+
166+
Stop-RunningSteps
167+
return $step.Process.ExitCode
168+
}
169+
170+
if ($pending.Count -gt 0) {
171+
if ((Get-Date) -ge $nextHeartbeatAt) {
172+
$running = @()
173+
foreach ($step in $pending) {
174+
$elapsed = [int]((Get-Date) - $step.StartTime).TotalSeconds
175+
$running += "$($step.Name) (${elapsed}s)"
176+
}
177+
Write-Host ("code-change-verification: still running: " + ($running -join ", ") + ".")
178+
$nextHeartbeatAt = (Get-Date).AddSeconds($heartbeatIntervalSeconds)
179+
}
180+
Start-Sleep -Seconds 1
181+
}
182+
}
183+
184+
return 0
185+
}
186+
187+
$exitCode = 0
188+
189+
try {
190+
$exitCode = Invoke-MakeStep -Step "format"
191+
if ($exitCode -eq 0) {
192+
Write-Host "Running make lint, make typecheck, and make tests in parallel..."
193+
Start-MakeStep -Step "lint"
194+
Start-MakeStep -Step "typecheck"
195+
Start-MakeStep -Step "tests"
196+
197+
$exitCode = Wait-ForParallelSteps
198+
}
199+
} finally {
200+
Stop-RunningSteps
201+
Remove-Item $logDir -Recurse -Force -ErrorAction SilentlyContinue
202+
}
203+
204+
if ($exitCode -ne 0) {
205+
exit $exitCode
206+
}
37207

38208
Write-Host "code-change-verification: all commands passed."

0 commit comments

Comments
 (0)