prizmkit 1.1.90 → 1.1.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/VERSION.json +3 -3
- package/bundled/dev-pipeline/lib/branch.sh +8 -5
- package/bundled/dev-pipeline/lib/common.sh +122 -0
- package/bundled/dev-pipeline/lib/heartbeat.sh +69 -25
- package/bundled/dev-pipeline/run-bugfix.sh +150 -39
- package/bundled/dev-pipeline/run-feature.sh +98 -33
- package/bundled/dev-pipeline/run-refactor.sh +150 -39
- package/bundled/dev-pipeline/scripts/update-bug-status.py +39 -4
- package/bundled/dev-pipeline/scripts/update-feature-status.py +43 -6
- package/bundled/dev-pipeline/scripts/update-refactor-status.py +36 -4
- package/bundled/dev-pipeline/tests/test_auto_skip.py +92 -3
- package/bundled/dev-pipeline-windows/lib/branch.ps1 +17 -4
- package/bundled/dev-pipeline-windows/lib/common.ps1 +70 -13
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +104 -34
- package/bundled/dev-pipeline-windows/run-recovery.ps1 +7 -3
- package/bundled/dev-pipeline-windows/scripts/update-bug-status.py +39 -4
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +43 -6
- package/bundled/dev-pipeline-windows/scripts/update-refactor-status.py +36 -4
- package/bundled/skills/_metadata.json +1 -1
- package/package.json +1 -1
|
@@ -34,8 +34,17 @@ function Invoke-PrizmPipeline {
|
|
|
34
34
|
$envMaxRetryArgs = @('--max-retries', [string]$parsedEnvMaxRetries)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
$envMaxInfraRetryArgs = @()
|
|
38
|
+
if ($env:MAX_INFRA_RETRIES) {
|
|
39
|
+
$parsedEnvMaxInfraRetries = 0
|
|
40
|
+
if (-not [int]::TryParse($env:MAX_INFRA_RETRIES, [ref]$parsedEnvMaxInfraRetries) -or $parsedEnvMaxInfraRetries -lt 1) {
|
|
41
|
+
throw "MAX_INFRA_RETRIES must be a positive integer: $($env:MAX_INFRA_RETRIES)"
|
|
42
|
+
}
|
|
43
|
+
$envMaxInfraRetryArgs = @('--max-infra-retries', [string]$parsedEnvMaxInfraRetries)
|
|
44
|
+
}
|
|
45
|
+
|
|
37
46
|
if ($command -in @('help','--help','-h')) {
|
|
38
|
-
Write-Host "Usage: .\run-$Kind.ps1 run [item-id] [list-path] [--dry-run] [--mode lite|standard|full] [--critic] [--max-retries N] [--timeout seconds]"
|
|
47
|
+
Write-Host "Usage: .\run-$Kind.ps1 run [item-id] [list-path] [--dry-run] [--mode lite|standard|full] [--critic] [--max-retries N] [--max-infra-retries N] [--timeout seconds]"
|
|
39
48
|
Write-Host " .\run-$Kind.ps1 status [list-path]"
|
|
40
49
|
Write-Host " .\run-$Kind.ps1 reset"
|
|
41
50
|
Write-Host ""
|
|
@@ -55,7 +64,7 @@ function Invoke-PrizmPipeline {
|
|
|
55
64
|
|
|
56
65
|
if ($command -eq 'status') {
|
|
57
66
|
$listPath = if ($remaining.Count -gt 0) { $remaining[0] } else { $defaultList }
|
|
58
|
-
Invoke-PrizmPythonText $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'status') + $envMaxRetryArgs)
|
|
67
|
+
Invoke-PrizmPythonText $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'status') + $envMaxRetryArgs + $envMaxInfraRetryArgs)
|
|
59
68
|
$global:PRIZM_EXIT_CODE = $LASTEXITCODE
|
|
60
69
|
return
|
|
61
70
|
}
|
|
@@ -78,6 +87,8 @@ function Invoke-PrizmPipeline {
|
|
|
78
87
|
}
|
|
79
88
|
$maxRetries = $null
|
|
80
89
|
if ($envMaxRetryArgs.Count -gt 0) { $maxRetries = [int]$envMaxRetryArgs[1] }
|
|
90
|
+
$maxInfraRetries = $null
|
|
91
|
+
if ($envMaxInfraRetryArgs.Count -gt 0) { $maxInfraRetries = [int]$envMaxInfraRetryArgs[1] }
|
|
81
92
|
$verboseEnabled = $env:VERBOSE -in @('1','true','yes','on')
|
|
82
93
|
$timeoutSeconds = 0
|
|
83
94
|
if ($env:SESSION_TIMEOUT) {
|
|
@@ -153,6 +164,15 @@ function Invoke-PrizmPipeline {
|
|
|
153
164
|
$maxRetries = $parsedMaxRetries
|
|
154
165
|
continue
|
|
155
166
|
}
|
|
167
|
+
'^--max-infra-retries$' {
|
|
168
|
+
$i++
|
|
169
|
+
$parsedMaxInfraRetries = 0
|
|
170
|
+
if (-not [int]::TryParse($remaining[$i], [ref]$parsedMaxInfraRetries) -or $parsedMaxInfraRetries -lt 1) {
|
|
171
|
+
throw "--max-infra-retries must be a positive integer: $($remaining[$i])"
|
|
172
|
+
}
|
|
173
|
+
$maxInfraRetries = $parsedMaxInfraRetries
|
|
174
|
+
continue
|
|
175
|
+
}
|
|
156
176
|
'^--timeout$' {
|
|
157
177
|
$i++
|
|
158
178
|
if ($i -ge $remaining.Count) { throw '--timeout requires a seconds value.' }
|
|
@@ -177,12 +197,15 @@ function Invoke-PrizmPipeline {
|
|
|
177
197
|
}
|
|
178
198
|
$maxRetryArgs = @()
|
|
179
199
|
if ($maxRetries -ne $null) { $maxRetryArgs = @('--max-retries', [string]$maxRetries) }
|
|
200
|
+
$maxInfraRetryArgs = @()
|
|
201
|
+
if ($maxInfraRetries -ne $null) { $maxInfraRetryArgs = @('--max-infra-retries', [string]$maxInfraRetries) }
|
|
180
202
|
if ($verboseEnabled) {
|
|
181
203
|
$modeLabel = if ($mode) { $mode } else { 'auto' }
|
|
182
204
|
$criticLabel = if ($critic) { $critic } else { 'plan-default' }
|
|
183
205
|
$retryLabel = if ($maxRetries -ne $null) { [string]$maxRetries } else { 'default' }
|
|
206
|
+
$infraRetryLabel = if ($maxInfraRetries -ne $null) { [string]$maxInfraRetries } else { 'default' }
|
|
184
207
|
Write-PrizmInfo "Verbose mode enabled."
|
|
185
|
-
Write-PrizmInfo "Effective options: mode=$modeLabel critic=$criticLabel maxRetries=$retryLabel timeoutSeconds=$timeoutSeconds staleKillThreshold=$staleKillThreshold dryRun=$dryRun"
|
|
208
|
+
Write-PrizmInfo "Effective options: mode=$modeLabel critic=$criticLabel maxRetries=$retryLabel maxInfraRetries=$infraRetryLabel timeoutSeconds=$timeoutSeconds staleKillThreshold=$staleKillThreshold dryRun=$dryRun"
|
|
186
209
|
}
|
|
187
210
|
|
|
188
211
|
# Validate PRIZMKIT_EFFORT early (fail-fast before any sessions are spawned)
|
|
@@ -241,17 +264,27 @@ function Invoke-PrizmPipeline {
|
|
|
241
264
|
|
|
242
265
|
function Get-PrizmGitDirtyPaths {
|
|
243
266
|
param([string]$ProjectRoot)
|
|
244
|
-
$
|
|
267
|
+
$hiddenToolWorktreeExcludes = Get-PrizmHiddenToolWorktreeExcludes
|
|
268
|
+
$lines = & git -C $ProjectRoot status --porcelain -- . @hiddenToolWorktreeExcludes 2>$null
|
|
245
269
|
$paths = @()
|
|
246
270
|
foreach ($line in $lines) {
|
|
247
271
|
if ([string]::IsNullOrWhiteSpace($line) -or $line.Length -lt 4) { continue }
|
|
248
272
|
$path = $line.Substring(3).Trim()
|
|
249
273
|
if ($path -match ' -> ') { $path = ($path -split ' -> ')[-1].Trim() }
|
|
250
|
-
$paths += (($path -replace '\\', '/').Trim('"').TrimStart([char[]]@('
|
|
274
|
+
$paths += (($path -replace '\\', '/').Trim('"').TrimStart([char[]]@('/')))
|
|
251
275
|
}
|
|
252
276
|
return @($paths)
|
|
253
277
|
}
|
|
254
278
|
|
|
279
|
+
function Test-PrizmPipelineIgnoredPath {
|
|
280
|
+
param([string]$Path)
|
|
281
|
+
$normalizedPath = (($Path -replace '\\', '/').Trim('"').TrimStart([char[]]@('/')))
|
|
282
|
+
$segments = @($normalizedPath -split '/' | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
|
|
283
|
+
if ($segments.Count -lt 2) { return $false }
|
|
284
|
+
if (-not $segments[0].StartsWith('.')) { return $false }
|
|
285
|
+
return $segments[1] -in @('worktree', 'worktrees')
|
|
286
|
+
}
|
|
287
|
+
|
|
255
288
|
function Test-PrizmPipelineBookkeepingPath {
|
|
256
289
|
param(
|
|
257
290
|
[string]$ProjectRoot,
|
|
@@ -270,6 +303,7 @@ function Invoke-PrizmPipeline {
|
|
|
270
303
|
function Test-PrizmGitWorkDirty {
|
|
271
304
|
param([string]$ProjectRoot, [string]$StateDir, [string]$ListPath)
|
|
272
305
|
foreach ($path in (Get-PrizmGitDirtyPaths $ProjectRoot)) {
|
|
306
|
+
if (Test-PrizmPipelineIgnoredPath $path) { continue }
|
|
273
307
|
if (-not (Test-PrizmPipelineBookkeepingPath $ProjectRoot $path $StateDir $ListPath)) {
|
|
274
308
|
return $true
|
|
275
309
|
}
|
|
@@ -279,7 +313,8 @@ function Invoke-PrizmPipeline {
|
|
|
279
313
|
|
|
280
314
|
function Test-PrizmGitDirty {
|
|
281
315
|
param([string]$ProjectRoot)
|
|
282
|
-
$
|
|
316
|
+
$hiddenToolWorktreeExcludes = Get-PrizmHiddenToolWorktreeExcludes
|
|
317
|
+
$dirty = & git -C $ProjectRoot status --porcelain -- . @hiddenToolWorktreeExcludes 2>$null | Select-Object -First 1
|
|
283
318
|
return -not [string]::IsNullOrWhiteSpace([string]$dirty)
|
|
284
319
|
}
|
|
285
320
|
|
|
@@ -294,12 +329,12 @@ function Invoke-PrizmPipeline {
|
|
|
294
329
|
}
|
|
295
330
|
|
|
296
331
|
function Write-PrizmStaleKillMarker {
|
|
297
|
-
param([string]$MarkerPath, [int]$StaleSeconds, [int]$Threshold)
|
|
332
|
+
param([string]$MarkerPath, [int]$StaleSeconds, [int]$Threshold, [string]$Reason = 'stale_session')
|
|
298
333
|
$markerDir = Split-Path $MarkerPath -Parent
|
|
299
334
|
if ($markerDir) { New-Item -ItemType Directory -Force -Path $markerDir | Out-Null }
|
|
300
335
|
[ordered]@{
|
|
301
336
|
killed_at = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
|
|
302
|
-
reason =
|
|
337
|
+
reason = $Reason
|
|
303
338
|
stale_seconds = $StaleSeconds
|
|
304
339
|
threshold = $Threshold
|
|
305
340
|
} | ConvertTo-Json -Compress | Set-Content -Path $MarkerPath -Encoding UTF8
|
|
@@ -307,14 +342,16 @@ function Invoke-PrizmPipeline {
|
|
|
307
342
|
|
|
308
343
|
function Invoke-PrizmGitAutoCommit {
|
|
309
344
|
param([string]$ProjectRoot, [string]$Message)
|
|
310
|
-
|
|
345
|
+
$hiddenToolWorktreeExcludes = Get-PrizmHiddenToolWorktreeExcludes
|
|
346
|
+
& git -C $ProjectRoot add -A -- . @hiddenToolWorktreeExcludes *> $null
|
|
311
347
|
& git -C $ProjectRoot commit --no-verify -m $Message *> $null
|
|
312
348
|
return $LASTEXITCODE -eq 0
|
|
313
349
|
}
|
|
314
350
|
|
|
315
351
|
function Invoke-PrizmGitIncludeRemainingArtifacts {
|
|
316
352
|
param([string]$ProjectRoot, [string]$ItemId)
|
|
317
|
-
|
|
353
|
+
$hiddenToolWorktreeExcludes = Get-PrizmHiddenToolWorktreeExcludes
|
|
354
|
+
& git -C $ProjectRoot add -A -- . @hiddenToolWorktreeExcludes *> $null
|
|
318
355
|
& git -C $ProjectRoot commit --no-verify --amend --no-edit *> $null
|
|
319
356
|
if ($LASTEXITCODE -eq 0) { return }
|
|
320
357
|
& git -C $ProjectRoot commit --no-verify -m "chore($ItemId): include remaining session artifacts" *> $null
|
|
@@ -657,7 +694,11 @@ function Invoke-PrizmPipeline {
|
|
|
657
694
|
$devBranchName = ''
|
|
658
695
|
$hadDirtyBaseline = if ($isGitRepository) { Test-PrizmGitWorkDirty $paths.ProjectRoot $stateDir $listPath } else { $false }
|
|
659
696
|
if ($hadDirtyBaseline) {
|
|
660
|
-
Write-
|
|
697
|
+
Write-PrizmError "Dirty working tree detected before $CurrentItemId; refusing to run on $originalBranch."
|
|
698
|
+
Write-PrizmError "Commit/stash your changes or start from a clean branch, then rerun the pipeline."
|
|
699
|
+
$script:PRIZM_ITEM_LIST_STATUS = ''
|
|
700
|
+
$script:PRIZM_ITEM_EXIT_CODE = 1
|
|
701
|
+
return
|
|
661
702
|
}
|
|
662
703
|
|
|
663
704
|
if ($dryRun) {
|
|
@@ -672,7 +713,7 @@ function Invoke-PrizmPipeline {
|
|
|
672
713
|
}
|
|
673
714
|
}
|
|
674
715
|
} else {
|
|
675
|
-
$start = Invoke-PrizmPythonJson $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'start', $idOption, $CurrentItemId, '--session-id', $sessionId) + $maxRetryArgs)
|
|
716
|
+
$start = Invoke-PrizmPythonJson $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'start', $idOption, $CurrentItemId, '--session-id', $sessionId) + $maxRetryArgs + $maxInfraRetryArgs)
|
|
676
717
|
if ($start.retry_count -ne $null) { $retryCount = [string]$start.retry_count }
|
|
677
718
|
if ($start.resume_from_phase -ne $null) { $resumePhase = [string]$start.resume_from_phase }
|
|
678
719
|
|
|
@@ -683,7 +724,10 @@ function Invoke-PrizmPipeline {
|
|
|
683
724
|
$devBranchName = $candidateBranch
|
|
684
725
|
Write-PrizmInfo "Dev branch: $devBranchName"
|
|
685
726
|
} else {
|
|
686
|
-
Write-
|
|
727
|
+
Write-PrizmError "Failed to create dev branch: $candidateBranch from $originalBranch - aborting to avoid running on $originalBranch"
|
|
728
|
+
$script:PRIZM_ITEM_LIST_STATUS = ''
|
|
729
|
+
$script:PRIZM_ITEM_EXIT_CODE = 1
|
|
730
|
+
return
|
|
687
731
|
}
|
|
688
732
|
}
|
|
689
733
|
}
|
|
@@ -739,8 +783,11 @@ function Invoke-PrizmPipeline {
|
|
|
739
783
|
|
|
740
784
|
$elapsedSeconds = 0
|
|
741
785
|
$staleSeconds = 0
|
|
786
|
+
$staleReason = 'stale_session'
|
|
742
787
|
$previousLogSize = 0
|
|
788
|
+
$previousProgressSignature = ''
|
|
743
789
|
$previousChildActivitySignature = ''
|
|
790
|
+
$previousErrorLoopSignature = ''
|
|
744
791
|
$wasTimedOut = $false
|
|
745
792
|
$staleKillMarker = Join-Path $logsDir 'stale-kill.json'
|
|
746
793
|
$wasStaleKilled = $false
|
|
@@ -758,16 +805,22 @@ function Invoke-PrizmPipeline {
|
|
|
758
805
|
$growth = $currentLogSize - $previousLogSize
|
|
759
806
|
$previousLogSize = $currentLogSize
|
|
760
807
|
|
|
761
|
-
$
|
|
762
|
-
$
|
|
808
|
+
$progressActivity = Get-PrizmProgressActivity -ProgressFile $progressJson
|
|
809
|
+
$progressSignature = [string]$progressActivity.ProgressSignature
|
|
810
|
+
$progressAdvanced = ($progressSignature -and $progressSignature -ne $previousProgressSignature)
|
|
811
|
+
$previousProgressSignature = $progressSignature
|
|
812
|
+
$childSignature = [string]$progressActivity.ChildSignature
|
|
763
813
|
$childAdvanced = ($childSignature -and $childSignature -ne $previousChildActivitySignature)
|
|
764
814
|
$previousChildActivitySignature = $childSignature
|
|
765
815
|
|
|
766
816
|
$effectiveStaleKillThreshold = Get-PrizmEffectiveStaleKillThreshold -ProgressFile $progressJson -BaseThreshold $staleKillThreshold
|
|
767
817
|
|
|
768
818
|
# Check for error-loop: agent is actively producing output but results are
|
|
769
|
-
# all read-offset errors or wasted calls.
|
|
819
|
+
# all read-offset errors or wasted calls. Only a newly advancing error
|
|
820
|
+
# signature counts; stale historical errors must not turn later normal output
|
|
821
|
+
# into a stale kill.
|
|
770
822
|
$errorLoopDetected = $false
|
|
823
|
+
$errorLoopSignature = ''
|
|
771
824
|
if ($effectiveStaleKillThreshold -gt 0 -and $growth -gt 0 -and (Test-Path $progressJson)) {
|
|
772
825
|
try {
|
|
773
826
|
$progress = Get-Content $progressJson -Raw | ConvertFrom-Json
|
|
@@ -778,19 +831,26 @@ function Invoke-PrizmPipeline {
|
|
|
778
831
|
$_.type -in @("read_offset_overflow", "wasted_call")
|
|
779
832
|
}).Count -eq 5
|
|
780
833
|
if ($allBad) {
|
|
781
|
-
$
|
|
834
|
+
$errorLoopSignature = ($recent | ConvertTo-Json -Compress)
|
|
835
|
+
if ($previousErrorLoopSignature -and $errorLoopSignature -ne $previousErrorLoopSignature) {
|
|
836
|
+
$errorLoopDetected = $true
|
|
837
|
+
}
|
|
782
838
|
}
|
|
783
839
|
}
|
|
784
840
|
} catch {
|
|
785
841
|
# Ignore JSON parse errors — progress file may be incomplete or malformed
|
|
786
842
|
}
|
|
787
843
|
}
|
|
844
|
+
$previousErrorLoopSignature = $errorLoopSignature
|
|
788
845
|
|
|
789
846
|
if ($errorLoopDetected) {
|
|
847
|
+
$staleReason = 'error_loop'
|
|
790
848
|
$staleSeconds = $effectiveStaleKillThreshold
|
|
791
|
-
} elseif ($growth -gt 0 -or $childAdvanced) {
|
|
849
|
+
} elseif ($growth -gt 0 -or $childAdvanced -or $progressAdvanced) {
|
|
850
|
+
$staleReason = 'stale_session'
|
|
792
851
|
$staleSeconds = 0
|
|
793
852
|
} else {
|
|
853
|
+
$staleReason = 'stale_session'
|
|
794
854
|
$staleSeconds += $waitSeconds
|
|
795
855
|
}
|
|
796
856
|
|
|
@@ -813,8 +873,12 @@ function Invoke-PrizmPipeline {
|
|
|
813
873
|
}
|
|
814
874
|
if ($effectiveStaleKillThreshold -gt 0 -and $staleSeconds -ge $effectiveStaleKillThreshold) {
|
|
815
875
|
$wasStaleKilled = $true
|
|
816
|
-
|
|
817
|
-
|
|
876
|
+
if ($staleReason -eq 'error_loop') {
|
|
877
|
+
Write-PrizmWarn "Session stale-killed due to repeated read-offset/wasted-call error loop"
|
|
878
|
+
} else {
|
|
879
|
+
Write-PrizmWarn "Session stale-killed (no progress for ${effectiveStaleKillThreshold}s)"
|
|
880
|
+
}
|
|
881
|
+
Write-PrizmStaleKillMarker $staleKillMarker $staleSeconds $effectiveStaleKillThreshold $staleReason
|
|
818
882
|
Stop-PrizmSessionProcess $pidPath
|
|
819
883
|
if ($staleKillGraceSeconds -gt 0) { Start-Sleep -Seconds $staleKillGraceSeconds }
|
|
820
884
|
break
|
|
@@ -841,6 +905,15 @@ function Invoke-PrizmPipeline {
|
|
|
841
905
|
}
|
|
842
906
|
Stop-PrizmProgressParser $parserProcess
|
|
843
907
|
|
|
908
|
+
$staleKillReason = ''
|
|
909
|
+
if (Test-Path $staleKillMarker) {
|
|
910
|
+
try {
|
|
911
|
+
$staleKillData = Get-Content $staleKillMarker -Raw | ConvertFrom-Json
|
|
912
|
+
if ($staleKillData.PSObject.Properties['reason']) { $staleKillReason = [string]$staleKillData.reason }
|
|
913
|
+
} catch {
|
|
914
|
+
$staleKillReason = ''
|
|
915
|
+
}
|
|
916
|
+
}
|
|
844
917
|
$wasInfraError = ($exitCode -ne 0 -and (Test-PrizmInfraError -SessionLog $sessionLog -ProgressJson $progressJson))
|
|
845
918
|
$wasAiRuntimeError = Test-PrizmAiRuntimeError -SessionLog $sessionLog -ProgressJson $progressJson
|
|
846
919
|
$semanticCompletion = if ($Kind -eq 'feature' -and $isGitRepository) {
|
|
@@ -866,16 +939,18 @@ function Invoke-PrizmPipeline {
|
|
|
866
939
|
Write-PrizmWarn "AI session failed due to AI CLI/provider infrastructure error"
|
|
867
940
|
Write-PrizmWarn "Infrastructure errors are retried without consuming code retry budget"
|
|
868
941
|
} elseif ($wasStaleKilled -or (Test-Path $staleKillMarker)) {
|
|
869
|
-
|
|
870
|
-
|
|
942
|
+
if ($staleKillReason -eq 'error_loop') {
|
|
943
|
+
Write-PrizmWarn "Session was killed by heartbeat monitor due to repeated read-offset/wasted-call errors"
|
|
944
|
+
} else {
|
|
945
|
+
Write-PrizmWarn "Session was stale-killed by heartbeat monitor (no progress for too long)"
|
|
946
|
+
}
|
|
947
|
+
Write-PrizmWarn "Heartbeat-killed sessions are treated as failed; dev branch is preserved for inspection"
|
|
871
948
|
} elseif ($exitCode -ne 0) {
|
|
872
949
|
Write-PrizmWarn "AI session exited with code $exitCode"
|
|
873
950
|
} elseif (-not $isGitRepository) {
|
|
874
951
|
Write-PrizmWarn "AI session exited cleanly, but project is not a git repository; cannot verify work was committed."
|
|
875
952
|
} elseif (Test-PrizmGitCommitsSince $paths.ProjectRoot $baseCommit) {
|
|
876
953
|
$status = 'success'
|
|
877
|
-
} elseif ($hadDirtyBaseline) {
|
|
878
|
-
Write-PrizmWarn "AI session exited cleanly but produced no new commits; pre-existing dirty tree will not be auto-committed."
|
|
879
954
|
} elseif (Test-PrizmGitWorkDirty $paths.ProjectRoot $stateDir $listPath) {
|
|
880
955
|
Write-PrizmWarn "AI session exited cleanly but produced no commits; auto-committing dirty work tree."
|
|
881
956
|
if (Invoke-PrizmGitAutoCommit $paths.ProjectRoot "chore($CurrentItemId): auto-commit session work") {
|
|
@@ -891,17 +966,12 @@ function Invoke-PrizmPipeline {
|
|
|
891
966
|
$itemListStatus = ''
|
|
892
967
|
if ($status -eq 'success') {
|
|
893
968
|
if (Test-PrizmGitDirty $paths.ProjectRoot) {
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
Invoke-PrizmGitIncludeBookkeepingArtifacts $paths.ProjectRoot $stateDir $listPath
|
|
897
|
-
} else {
|
|
898
|
-
Write-PrizmInfo "Auto-committing remaining session artifacts."
|
|
899
|
-
Invoke-PrizmGitIncludeRemainingArtifacts $paths.ProjectRoot $CurrentItemId
|
|
900
|
-
}
|
|
969
|
+
Write-PrizmInfo "Auto-committing remaining session artifacts."
|
|
970
|
+
Invoke-PrizmGitIncludeRemainingArtifacts $paths.ProjectRoot $CurrentItemId
|
|
901
971
|
}
|
|
902
972
|
|
|
903
973
|
if ($status -eq 'success') {
|
|
904
|
-
$updateResult = Invoke-PrizmPythonJson $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'update', $idOption, $CurrentItemId, '--session-id', $sessionId, '--session-status', $status) + $maxRetryArgs)
|
|
974
|
+
$updateResult = Invoke-PrizmPythonJson $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'update', $idOption, $CurrentItemId, '--session-id', $sessionId, '--session-status', $status) + $maxRetryArgs + $maxInfraRetryArgs)
|
|
905
975
|
if ($updateResult -and $updateResult.PSObject.Properties['new_status']) {
|
|
906
976
|
$itemListStatus = [string]$updateResult.new_status
|
|
907
977
|
}
|
|
@@ -938,7 +1008,7 @@ function Invoke-PrizmPipeline {
|
|
|
938
1008
|
Write-PrizmRuntimeFailureLog $failureLog $CurrentItemId $sessionId $status $exitCode $staleKillMarker $progressJson $checkpointPath $paths.ProjectRoot $baseCommit
|
|
939
1009
|
}
|
|
940
1010
|
}
|
|
941
|
-
$updateResult = Invoke-PrizmPythonJson $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'update', $idOption, $CurrentItemId, '--session-id', $sessionId, '--session-status', $status) + $maxRetryArgs)
|
|
1011
|
+
$updateResult = Invoke-PrizmPythonJson $python (@((Join-Path $paths.ScriptsDir $updateScript), $listOption, $listPath, '--state-dir', $stateDir, '--action', 'update', $idOption, $CurrentItemId, '--session-id', $sessionId, '--session-status', $status) + $maxRetryArgs + $maxInfraRetryArgs)
|
|
942
1012
|
if ($updateResult -and $updateResult.PSObject.Properties['new_status']) {
|
|
943
1013
|
$itemListStatus = [string]$updateResult.new_status
|
|
944
1014
|
}
|
|
@@ -1018,7 +1088,7 @@ function Invoke-PrizmPipeline {
|
|
|
1018
1088
|
$global:PRIZM_EXIT_CODE = $lastExitCode
|
|
1019
1089
|
return
|
|
1020
1090
|
} elseif ($lastExitCode -ne 0 -and $stopOnFailure) {
|
|
1021
|
-
Write-PrizmInfo "STOP_ON_FAILURE: $nextItemId is $($script:PRIZM_ITEM_LIST_STATUS); retry budget not exhausted, continuing."
|
|
1091
|
+
Write-PrizmInfo "STOP_ON_FAILURE: $nextItemId is $($script:PRIZM_ITEM_LIST_STATUS); code/infra retry budget not exhausted, continuing."
|
|
1022
1092
|
}
|
|
1023
1093
|
}
|
|
1024
1094
|
}
|
|
@@ -121,6 +121,7 @@ $job = Start-Job -ScriptBlock {
|
|
|
121
121
|
$elapsedSeconds = 0
|
|
122
122
|
$staleSeconds = 0
|
|
123
123
|
$previousLogSize = 0
|
|
124
|
+
$previousProgressSignature = ''
|
|
124
125
|
$previousChildActivitySignature = ''
|
|
125
126
|
$wasTimedOut = $false
|
|
126
127
|
$wasStaleKilled = $false
|
|
@@ -136,12 +137,15 @@ while ($true) {
|
|
|
136
137
|
$growth = $currentLogSize - $previousLogSize
|
|
137
138
|
$previousLogSize = $currentLogSize
|
|
138
139
|
|
|
139
|
-
$
|
|
140
|
-
$
|
|
140
|
+
$progressActivity = Get-PrizmProgressActivity -ProgressFile $progressPath
|
|
141
|
+
$progressSignature = [string]$progressActivity.ProgressSignature
|
|
142
|
+
$progressAdvanced = ($progressSignature -and $progressSignature -ne $previousProgressSignature)
|
|
143
|
+
$previousProgressSignature = $progressSignature
|
|
144
|
+
$childSignature = [string]$progressActivity.ChildSignature
|
|
141
145
|
$childAdvanced = ($childSignature -and $childSignature -ne $previousChildActivitySignature)
|
|
142
146
|
$previousChildActivitySignature = $childSignature
|
|
143
147
|
|
|
144
|
-
if ($growth -gt 0 -or $childAdvanced) { $staleSeconds = 0 } else { $staleSeconds += $waitSeconds }
|
|
148
|
+
if ($growth -gt 0 -or $childAdvanced -or $progressAdvanced) { $staleSeconds = 0 } else { $staleSeconds += $waitSeconds }
|
|
145
149
|
|
|
146
150
|
if ($timeoutSeconds -gt 0 -and $elapsedSeconds -ge $timeoutSeconds) {
|
|
147
151
|
$wasTimedOut = $true
|
|
@@ -16,7 +16,7 @@ Usage:
|
|
|
16
16
|
--bug-list <path> --state-dir <path> \
|
|
17
17
|
--action <get_next|start|update|status|pause|reset|clean|unskip> \
|
|
18
18
|
[--bug-id <id>] [--session-status <status>] \
|
|
19
|
-
[--session-id <id>] [--max-retries <n>]
|
|
19
|
+
[--session-id <id>] [--max-retries <n>] [--max-infra-retries <n>]
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
import argparse
|
|
@@ -75,7 +75,8 @@ def parse_args():
|
|
|
75
75
|
help="Session outcome status (required for 'update' action)",
|
|
76
76
|
)
|
|
77
77
|
parser.add_argument("--session-id", default=None, help="Session ID (optional, for 'update' action)")
|
|
78
|
-
parser.add_argument("--max-retries", type=int, default=3, help="Maximum retry count (default: 3)")
|
|
78
|
+
parser.add_argument("--max-retries", type=int, default=3, help="Maximum code retry count (default: 3)")
|
|
79
|
+
parser.add_argument("--max-infra-retries", type=int, default=3, help="Maximum infrastructure retry count (default: 3)")
|
|
79
80
|
parser.add_argument("--project-root", default=None, help="Project root directory. Required for 'clean' action.")
|
|
80
81
|
return parser.parse_args()
|
|
81
82
|
|
|
@@ -98,6 +99,9 @@ def load_bug_status(state_dir, bug_id):
|
|
|
98
99
|
"bug_id": bug_id,
|
|
99
100
|
"retry_count": 0,
|
|
100
101
|
"max_retries": 3,
|
|
102
|
+
"max_infra_retries": 3,
|
|
103
|
+
"infra_error_count": 0,
|
|
104
|
+
"last_infra_error_session_id": None,
|
|
101
105
|
"sessions": [],
|
|
102
106
|
"last_session_id": None,
|
|
103
107
|
"resume_from_phase": None,
|
|
@@ -111,6 +115,9 @@ def load_bug_status(state_dir, bug_id):
|
|
|
111
115
|
"bug_id": bug_id,
|
|
112
116
|
"retry_count": 0,
|
|
113
117
|
"max_retries": 3,
|
|
118
|
+
"max_infra_retries": 3,
|
|
119
|
+
"infra_error_count": 0,
|
|
120
|
+
"last_infra_error_session_id": None,
|
|
114
121
|
"sessions": [],
|
|
115
122
|
"last_session_id": None,
|
|
116
123
|
"resume_from_phase": None,
|
|
@@ -234,6 +241,7 @@ def action_get_next(bug_list_data, state_dir):
|
|
|
234
241
|
"title": chosen.get("title", ""),
|
|
235
242
|
"severity": chosen.get("severity", "medium"),
|
|
236
243
|
"retry_count": chosen_status_data.get("retry_count", 0),
|
|
244
|
+
"infra_error_count": chosen_status_data.get("infra_error_count", 0),
|
|
237
245
|
"resume_from_phase": chosen_status_data.get("resume_from_phase", None),
|
|
238
246
|
}
|
|
239
247
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
@@ -248,6 +256,7 @@ def action_update(args, bug_list_path, state_dir):
|
|
|
248
256
|
session_status = args.session_status
|
|
249
257
|
session_id = args.session_id
|
|
250
258
|
max_retries = args.max_retries
|
|
259
|
+
max_infra_retries = args.max_infra_retries
|
|
251
260
|
|
|
252
261
|
if not bug_id:
|
|
253
262
|
error_out("--bug-id is required for 'update' action")
|
|
@@ -262,6 +271,8 @@ def action_update(args, bug_list_path, state_dir):
|
|
|
262
271
|
new_status = get_bug_status_from_list(bug_list_path, bug_id)
|
|
263
272
|
|
|
264
273
|
if session_status == "success":
|
|
274
|
+
bs["infra_error_count"] = 0
|
|
275
|
+
bs["last_infra_error_session_id"] = None
|
|
265
276
|
new_status = "completed"
|
|
266
277
|
bs["resume_from_phase"] = None
|
|
267
278
|
err = update_bug_in_list(bug_list_path, bug_id, "completed")
|
|
@@ -286,11 +297,25 @@ def action_update(args, bug_list_path, state_dir):
|
|
|
286
297
|
error_out("Failed to update .prizmkit/plans/bug-fix-list.json: {}".format(err))
|
|
287
298
|
return
|
|
288
299
|
elif session_status == "infra_error":
|
|
289
|
-
|
|
290
|
-
|
|
300
|
+
# AI CLI/provider outage, auth failure, gateway error, etc.
|
|
301
|
+
# Infra failures do not consume the code retry budget, but they still
|
|
302
|
+
# need their own bounded budget so a flaky provider cannot loop forever.
|
|
303
|
+
infra_error_count = bs.get("infra_error_count", 0) + 1
|
|
304
|
+
bs["infra_error_count"] = infra_error_count
|
|
291
305
|
bs["last_infra_error_session_id"] = session_id
|
|
306
|
+
bs["max_infra_retries"] = max_infra_retries
|
|
307
|
+
bs["degraded_reason"] = "infra_error"
|
|
308
|
+
if session_id:
|
|
309
|
+
bs["last_session_id"] = session_id
|
|
292
310
|
bs["resume_from_phase"] = None
|
|
293
311
|
|
|
312
|
+
if infra_error_count >= max_infra_retries:
|
|
313
|
+
new_status = "failed"
|
|
314
|
+
if session_id:
|
|
315
|
+
bs["last_failed_session_id"] = session_id
|
|
316
|
+
else:
|
|
317
|
+
new_status = "pending"
|
|
318
|
+
|
|
294
319
|
err = update_bug_in_list(bug_list_path, bug_id, new_status)
|
|
295
320
|
if err:
|
|
296
321
|
error_out("Failed to update .prizmkit/plans/bug-fix-list.json: {}".format(err))
|
|
@@ -338,6 +363,8 @@ def action_update(args, bug_list_path, state_dir):
|
|
|
338
363
|
"session_status": session_status,
|
|
339
364
|
"new_status": new_status,
|
|
340
365
|
"retry_count": bs["retry_count"],
|
|
366
|
+
"infra_error_count": bs.get("infra_error_count", 0),
|
|
367
|
+
"max_infra_retries": max_infra_retries,
|
|
341
368
|
"resume_from_phase": bs.get("resume_from_phase"),
|
|
342
369
|
"updated_at": bs["updated_at"],
|
|
343
370
|
}
|
|
@@ -577,6 +604,8 @@ def action_reset(args, bug_list_path, state_dir):
|
|
|
577
604
|
old_retry = bs.get("retry_count", 0)
|
|
578
605
|
|
|
579
606
|
bs["retry_count"] = 0
|
|
607
|
+
bs["infra_error_count"] = 0
|
|
608
|
+
bs["last_infra_error_session_id"] = None
|
|
580
609
|
bs["sessions"] = []
|
|
581
610
|
bs["last_session_id"] = None
|
|
582
611
|
bs["resume_from_phase"] = None
|
|
@@ -652,6 +681,8 @@ def action_clean(args, bug_list_path, state_dir):
|
|
|
652
681
|
old_retry = bs.get("retry_count", 0)
|
|
653
682
|
|
|
654
683
|
bs["retry_count"] = 0
|
|
684
|
+
bs["infra_error_count"] = 0
|
|
685
|
+
bs["last_infra_error_session_id"] = None
|
|
655
686
|
bs["sessions"] = []
|
|
656
687
|
bs["last_session_id"] = None
|
|
657
688
|
bs["resume_from_phase"] = None
|
|
@@ -740,6 +771,8 @@ def action_start(args, bug_list_path, state_dir):
|
|
|
740
771
|
"bug_id": bug_id,
|
|
741
772
|
"old_status": old_status,
|
|
742
773
|
"new_status": "in_progress",
|
|
774
|
+
"retry_count": bs.get("retry_count", 0),
|
|
775
|
+
"infra_error_count": bs.get("infra_error_count", 0),
|
|
743
776
|
"updated_at": bs["updated_at"],
|
|
744
777
|
}
|
|
745
778
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
@@ -816,6 +849,8 @@ def action_unskip(args, bug_list_path, state_dir):
|
|
|
816
849
|
for bid in to_reset:
|
|
817
850
|
bs = load_bug_status(state_dir, bid)
|
|
818
851
|
bs["retry_count"] = 0
|
|
852
|
+
bs["infra_error_count"] = 0
|
|
853
|
+
bs["last_infra_error_session_id"] = None
|
|
819
854
|
bs["sessions"] = []
|
|
820
855
|
bs["last_session_id"] = None
|
|
821
856
|
bs["resume_from_phase"] = None
|
|
@@ -17,7 +17,7 @@ Usage:
|
|
|
17
17
|
--feature-list <path> --state-dir <path> \
|
|
18
18
|
--action <get_next|start|update|status|pause|reset|clean|complete|unskip> \
|
|
19
19
|
[--feature-id <id>] [--session-status <status>] \
|
|
20
|
-
[--session-id <id>] [--max-retries <n>] \
|
|
20
|
+
[--session-id <id>] [--max-retries <n>] [--max-infra-retries <n>] \
|
|
21
21
|
[--features <filter>]
|
|
22
22
|
"""
|
|
23
23
|
|
|
@@ -94,7 +94,13 @@ def parse_args():
|
|
|
94
94
|
"--max-retries",
|
|
95
95
|
type=int,
|
|
96
96
|
default=3,
|
|
97
|
-
help="Maximum retry count before marking as failed (default: 3)",
|
|
97
|
+
help="Maximum code retry count before marking as failed (default: 3)",
|
|
98
|
+
)
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
"--max-infra-retries",
|
|
101
|
+
type=int,
|
|
102
|
+
default=3,
|
|
103
|
+
help="Maximum infrastructure retry count before marking as failed (default: 3)",
|
|
98
104
|
)
|
|
99
105
|
parser.add_argument(
|
|
100
106
|
"--feature-slug",
|
|
@@ -170,6 +176,9 @@ def load_feature_status(state_dir, feature_id):
|
|
|
170
176
|
"feature_id": feature_id,
|
|
171
177
|
"retry_count": 0,
|
|
172
178
|
"max_retries": 3,
|
|
179
|
+
"max_infra_retries": 3,
|
|
180
|
+
"infra_error_count": 0,
|
|
181
|
+
"last_infra_error_session_id": None,
|
|
173
182
|
"sessions": [],
|
|
174
183
|
"last_session_id": None,
|
|
175
184
|
"resume_from_phase": None,
|
|
@@ -183,6 +192,9 @@ def load_feature_status(state_dir, feature_id):
|
|
|
183
192
|
"feature_id": feature_id,
|
|
184
193
|
"retry_count": 0,
|
|
185
194
|
"max_retries": 3,
|
|
195
|
+
"max_infra_retries": 3,
|
|
196
|
+
"infra_error_count": 0,
|
|
197
|
+
"last_infra_error_session_id": None,
|
|
186
198
|
"sessions": [],
|
|
187
199
|
"last_session_id": None,
|
|
188
200
|
"resume_from_phase": None,
|
|
@@ -565,6 +577,7 @@ def action_get_next(feature_list_data, state_dir, feature_filter=None):
|
|
|
565
577
|
"feature_id": chosen_id,
|
|
566
578
|
"title": chosen.get("title", ""),
|
|
567
579
|
"retry_count": chosen_status_data.get("retry_count", 0),
|
|
580
|
+
"infra_error_count": chosen_status_data.get("infra_error_count", 0),
|
|
568
581
|
"resume_from_phase": chosen_status_data.get("resume_from_phase", None),
|
|
569
582
|
}
|
|
570
583
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
@@ -585,6 +598,7 @@ def action_update(args, feature_list_path, state_dir):
|
|
|
585
598
|
session_status = args.session_status
|
|
586
599
|
session_id = args.session_id
|
|
587
600
|
max_retries = args.max_retries
|
|
601
|
+
max_infra_retries = args.max_infra_retries
|
|
588
602
|
|
|
589
603
|
if not feature_id:
|
|
590
604
|
error_out("--feature-id is required for 'update' action")
|
|
@@ -600,6 +614,8 @@ def action_update(args, feature_list_path, state_dir):
|
|
|
600
614
|
new_status = current_list_status
|
|
601
615
|
|
|
602
616
|
if session_status == "success":
|
|
617
|
+
fs["infra_error_count"] = 0
|
|
618
|
+
fs["last_infra_error_session_id"] = None
|
|
603
619
|
# No-op guard: if this exact successful session was already recorded,
|
|
604
620
|
# avoid rewriting state files again (prevents post-commit dirty changes).
|
|
605
621
|
existing_sessions = fs.get("sessions", [])
|
|
@@ -616,6 +632,8 @@ def action_update(args, feature_list_path, state_dir):
|
|
|
616
632
|
"session_status": session_status,
|
|
617
633
|
"new_status": "completed",
|
|
618
634
|
"retry_count": fs.get("retry_count", 0),
|
|
635
|
+
"infra_error_count": fs.get("infra_error_count", 0),
|
|
636
|
+
"max_infra_retries": max_infra_retries,
|
|
619
637
|
"resume_from_phase": fs.get("resume_from_phase"),
|
|
620
638
|
"updated_at": fs.get("updated_at"),
|
|
621
639
|
"no_op": True,
|
|
@@ -654,15 +672,24 @@ def action_update(args, feature_list_path, state_dir):
|
|
|
654
672
|
return
|
|
655
673
|
elif session_status == "infra_error":
|
|
656
674
|
# AI CLI/provider outage, auth failure, gateway error, etc.
|
|
657
|
-
#
|
|
658
|
-
#
|
|
659
|
-
|
|
660
|
-
fs["infra_error_count"] =
|
|
675
|
+
# Infra failures do not consume the code retry budget, but they still
|
|
676
|
+
# need their own bounded budget so a flaky provider cannot loop forever.
|
|
677
|
+
infra_error_count = fs.get("infra_error_count", 0) + 1
|
|
678
|
+
fs["infra_error_count"] = infra_error_count
|
|
661
679
|
fs["last_infra_error_session_id"] = session_id
|
|
680
|
+
fs["max_infra_retries"] = max_infra_retries
|
|
681
|
+
fs["degraded_reason"] = "infra_error"
|
|
662
682
|
if session_id:
|
|
663
683
|
fs["last_session_id"] = session_id
|
|
664
684
|
fs["resume_from_phase"] = None
|
|
665
685
|
|
|
686
|
+
if infra_error_count >= max_infra_retries:
|
|
687
|
+
new_status = "failed"
|
|
688
|
+
if session_id:
|
|
689
|
+
fs["last_failed_session_id"] = session_id
|
|
690
|
+
else:
|
|
691
|
+
new_status = "pending"
|
|
692
|
+
|
|
666
693
|
err = update_feature_in_list(feature_list_path, feature_id, new_status)
|
|
667
694
|
if err:
|
|
668
695
|
error_out("Failed to update .prizmkit/plans/feature-list.json: {}".format(err))
|
|
@@ -714,6 +741,8 @@ def action_update(args, feature_list_path, state_dir):
|
|
|
714
741
|
"session_status": session_status,
|
|
715
742
|
"new_status": new_status,
|
|
716
743
|
"retry_count": fs["retry_count"],
|
|
744
|
+
"infra_error_count": fs.get("infra_error_count", 0),
|
|
745
|
+
"max_infra_retries": max_infra_retries,
|
|
717
746
|
"resume_from_phase": fs.get("resume_from_phase"),
|
|
718
747
|
"updated_at": fs["updated_at"],
|
|
719
748
|
}
|
|
@@ -1143,6 +1172,8 @@ def action_start(args, feature_list_path, state_dir):
|
|
|
1143
1172
|
"feature_id": feature_id,
|
|
1144
1173
|
"old_status": old_status,
|
|
1145
1174
|
"new_status": "in_progress",
|
|
1175
|
+
"retry_count": fs.get("retry_count", 0),
|
|
1176
|
+
"infra_error_count": fs.get("infra_error_count", 0),
|
|
1146
1177
|
"updated_at": fs["updated_at"],
|
|
1147
1178
|
}
|
|
1148
1179
|
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
@@ -1171,6 +1202,8 @@ def action_reset(args, feature_list_path, state_dir):
|
|
|
1171
1202
|
|
|
1172
1203
|
# Reset runtime fields
|
|
1173
1204
|
fs["retry_count"] = 0
|
|
1205
|
+
fs["infra_error_count"] = 0
|
|
1206
|
+
fs["last_infra_error_session_id"] = None
|
|
1174
1207
|
fs["sessions"] = []
|
|
1175
1208
|
fs["last_session_id"] = None
|
|
1176
1209
|
fs["resume_from_phase"] = None
|
|
@@ -1264,6 +1297,8 @@ def action_clean(args, feature_list_path, state_dir):
|
|
|
1264
1297
|
old_retry = fs.get("retry_count", 0)
|
|
1265
1298
|
|
|
1266
1299
|
fs["retry_count"] = 0
|
|
1300
|
+
fs["infra_error_count"] = 0
|
|
1301
|
+
fs["last_infra_error_session_id"] = None
|
|
1267
1302
|
fs["sessions"] = []
|
|
1268
1303
|
fs["last_session_id"] = None
|
|
1269
1304
|
fs["resume_from_phase"] = None
|
|
@@ -1423,6 +1458,8 @@ def action_unskip(args, feature_list_path, state_dir):
|
|
|
1423
1458
|
for fid in to_reset:
|
|
1424
1459
|
fs = load_feature_status(state_dir, fid)
|
|
1425
1460
|
fs["retry_count"] = 0
|
|
1461
|
+
fs["infra_error_count"] = 0
|
|
1462
|
+
fs["last_infra_error_session_id"] = None
|
|
1426
1463
|
fs["sessions"] = []
|
|
1427
1464
|
fs["last_session_id"] = None
|
|
1428
1465
|
fs["resume_from_phase"] = None
|