prizmkit 1.1.70 → 1.1.74
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/agents/prizm-dev-team-dev.md +11 -1
- package/bundled/dev-pipeline/lib/common.sh +427 -0
- package/bundled/dev-pipeline/lib/heartbeat.sh +101 -36
- package/bundled/dev-pipeline/run-feature.sh +109 -29
- package/bundled/dev-pipeline/scripts/parse-stream-progress.py +198 -3
- package/bundled/dev-pipeline/scripts/update-feature-status.py +27 -3
- package/bundled/dev-pipeline/templates/agent-prompts/dev-implement.md +21 -0
- package/bundled/dev-pipeline/templates/bootstrap-tier2.md +1 -1
- package/bundled/dev-pipeline/templates/bootstrap-tier3.md +5 -9
- package/bundled/dev-pipeline/templates/sections/feature-context.md +3 -18
- package/bundled/dev-pipeline/templates/sections/phase-commit-full.md +11 -0
- package/bundled/dev-pipeline/templates/sections/phase-commit.md +11 -0
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-agent-suffix.md +1 -1
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-base.md +6 -12
- package/bundled/dev-pipeline/templates/sections/phase-context-snapshot-lite-suffix.md +10 -3
- package/bundled/dev-pipeline/templates/sections/phase-implement-agent.md +1 -0
- package/bundled/dev-pipeline/templates/sections/phase-specify-plan-full.md +4 -8
- package/bundled/dev-pipeline-windows/lib/common.ps1 +61 -1
- package/bundled/dev-pipeline-windows/lib/pipeline.ps1 +325 -16
- package/bundled/dev-pipeline-windows/scripts/parse-stream-progress.py +198 -3
- package/bundled/dev-pipeline-windows/scripts/update-feature-status.py +27 -3
- package/bundled/dev-pipeline-windows/templates/agent-prompts/dev-implement.md +21 -0
- package/bundled/dev-pipeline-windows/templates/agent-prompts/reviewer-review.md +1 -1
- package/bundled/dev-pipeline-windows/templates/bootstrap-prompt.md +27 -0
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier1.md +543 -14
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier2.md +664 -14
- package/bundled/dev-pipeline-windows/templates/bootstrap-tier3.md +741 -14
- package/bundled/dev-pipeline-windows/templates/bugfix-bootstrap-prompt.md +2 -2
- package/bundled/dev-pipeline-windows/templates/feature-list-schema.json +1 -1
- package/bundled/dev-pipeline-windows/templates/refactor-bootstrap-prompt.md +1 -1
- package/bundled/dev-pipeline-windows/templates/refactor-list-schema.json +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/context-budget-rules.md +3 -3
- package/bundled/dev-pipeline-windows/templates/sections/failure-capture.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/feature-context.md +3 -18
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-auto.md +239 -40
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification-opencli.md +75 -26
- package/bundled/dev-pipeline-windows/templates/sections/phase-browser-verification.md +142 -36
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit-full.md +13 -2
- package/bundled/dev-pipeline-windows/templates/sections/phase-commit.md +12 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-agent-suffix.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-base.md +7 -17
- package/bundled/dev-pipeline-windows/templates/sections/phase-context-snapshot-lite-suffix.md +10 -3
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan-full.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-critic-plan.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-agent.md +3 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-full.md +7 -3
- package/bundled/dev-pipeline-windows/templates/sections/phase-implement-lite.md +1 -3
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-agent.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-plan-lite.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-agent.md +1 -1
- package/bundled/dev-pipeline-windows/templates/sections/phase-review-full.md +2 -2
- package/bundled/dev-pipeline-windows/templates/sections/phase-specify-plan-full.md +13 -17
- package/bundled/dev-pipeline-windows/templates/sections/phase0-test-baseline.md +2 -4
- package/bundled/dev-pipeline-windows/templates/sections/subagent-timeout-recovery.md +1 -1
- package/bundled/skills/_metadata.json +1 -1
- package/package.json +1 -1
|
@@ -335,6 +335,242 @@ function Invoke-PrizmPipeline {
|
|
|
335
335
|
return $LASTEXITCODE -eq 0
|
|
336
336
|
}
|
|
337
337
|
|
|
338
|
+
function Get-PrizmFeatureSlugFromList {
|
|
339
|
+
param([string]$ListPath, [string]$FeatureId)
|
|
340
|
+
if (-not (Test-Path $ListPath)) { return '' }
|
|
341
|
+
try { $data = Get-Content $ListPath -Raw | ConvertFrom-Json } catch { return '' }
|
|
342
|
+
foreach ($feature in @($data.features)) {
|
|
343
|
+
if ($feature.id -eq $FeatureId) {
|
|
344
|
+
$number = ([string]$feature.id).Replace('F-', '').Replace('f-', '').PadLeft(3, '0')
|
|
345
|
+
$title = ([string]$feature.title).ToLowerInvariant()
|
|
346
|
+
$title = [regex]::Replace($title, '[^a-z0-9\s-]', '')
|
|
347
|
+
$title = [regex]::Replace($title.Trim(), '[\s]+', '-')
|
|
348
|
+
$title = [regex]::Replace($title, '-+', '-').Trim('-')
|
|
349
|
+
if ($title) { return "$number-$title" }
|
|
350
|
+
return $number
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return ''
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function Test-PrizmCheckpointComplete {
|
|
357
|
+
param([string]$CheckpointPath)
|
|
358
|
+
if (-not (Test-Path $CheckpointPath)) { return $false }
|
|
359
|
+
try { $checkpoint = Get-Content $CheckpointPath -Raw | ConvertFrom-Json } catch { return $false }
|
|
360
|
+
if (-not $checkpoint.steps) { return $false }
|
|
361
|
+
foreach ($step in @($checkpoint.steps)) {
|
|
362
|
+
if ($step.status -notin @('completed', 'skipped')) { return $false }
|
|
363
|
+
}
|
|
364
|
+
return $true
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function Get-PrizmFeatureTitleFromList {
|
|
368
|
+
param([string]$ListPath, [string]$FeatureId)
|
|
369
|
+
if (-not (Test-Path $ListPath)) { return '' }
|
|
370
|
+
try { $data = Get-Content $ListPath -Raw | ConvertFrom-Json } catch { return '' }
|
|
371
|
+
foreach ($feature in @($data.features)) {
|
|
372
|
+
if ($feature.id -eq $FeatureId) { return [string]$feature.title }
|
|
373
|
+
}
|
|
374
|
+
return ''
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function Get-PrizmTitleWords {
|
|
378
|
+
param([string]$Text)
|
|
379
|
+
$matches = [regex]::Matches(([string]$Text).ToLowerInvariant(), '[a-z0-9]{3,}')
|
|
380
|
+
$words = @()
|
|
381
|
+
foreach ($match in $matches) { $words += [string]$match.Value }
|
|
382
|
+
return @($words)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function Test-PrizmCommitMatchesFeatureTitle {
|
|
386
|
+
param([string]$Subject, [string]$FeatureTitle)
|
|
387
|
+
$titleWords = @(Get-PrizmTitleWords $FeatureTitle)
|
|
388
|
+
if ($titleWords.Count -eq 0) { return $false }
|
|
389
|
+
$subjectWords = @(Get-PrizmTitleWords $Subject)
|
|
390
|
+
$subjectSet = @{}
|
|
391
|
+
foreach ($word in $subjectWords) { $subjectSet[$word] = $true }
|
|
392
|
+
$required = if ($titleWords.Count -le 3) { $titleWords.Count } else { [Math]::Max(3, [int][Math]::Ceiling($titleWords.Count * 0.75)) }
|
|
393
|
+
$matched = 0
|
|
394
|
+
foreach ($word in $titleWords) {
|
|
395
|
+
if ($subjectSet.ContainsKey($word)) { $matched++ }
|
|
396
|
+
}
|
|
397
|
+
return $matched -ge $required
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function Get-PrizmFeatureCommit {
|
|
401
|
+
param([string]$ProjectRoot, [string]$BaseCommit, [string]$FeatureId, [bool]$AllowFallback = $false, [string]$FeatureTitle = '')
|
|
402
|
+
$range = if ($BaseCommit) { "$BaseCommit..HEAD" } else { 'HEAD' }
|
|
403
|
+
$lines = & git -C $ProjectRoot log $range '--format=%H%x09%s' 2>$null
|
|
404
|
+
if ($LASTEXITCODE -ne 0) { return '' }
|
|
405
|
+
foreach ($line in @($lines)) {
|
|
406
|
+
$parts = ([string]$line).Split("`t", 2)
|
|
407
|
+
if ($parts.Count -lt 2) { continue }
|
|
408
|
+
$subject = $parts[1]
|
|
409
|
+
if ($subject.Contains($FeatureId) -and $subject -notmatch '^wip(\(|:)') { return $parts[0] }
|
|
410
|
+
}
|
|
411
|
+
if ($AllowFallback -and $FeatureTitle) {
|
|
412
|
+
foreach ($line in @($lines)) {
|
|
413
|
+
$parts = ([string]$line).Split("`t", 2)
|
|
414
|
+
if ($parts.Count -lt 2) { continue }
|
|
415
|
+
$subject = $parts[1]
|
|
416
|
+
if ($subject -notmatch '^wip(\(|:)' -and (Test-PrizmCommitMatchesFeatureTitle $subject $FeatureTitle)) { return $parts[0] }
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return ''
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function Get-PrizmFeatureSemanticCompletion {
|
|
423
|
+
param([string]$ProjectRoot, [string]$ListPath, [string]$FeatureId, [string]$BaseCommit, [string]$PrizmkitDir)
|
|
424
|
+
$slug = Get-PrizmFeatureSlugFromList $ListPath $FeatureId
|
|
425
|
+
if (-not $slug) { return $null }
|
|
426
|
+
$checkpointPath = Join-Path $PrizmkitDir "specs\$slug\workflow-checkpoint.json"
|
|
427
|
+
if (-not (Test-PrizmCheckpointComplete $checkpointPath)) { return $null }
|
|
428
|
+
$featureTitle = Get-PrizmFeatureTitleFromList $ListPath $FeatureId
|
|
429
|
+
$commitSha = Get-PrizmFeatureCommit $ProjectRoot $BaseCommit $FeatureId $true $featureTitle
|
|
430
|
+
if (-not $commitSha) { return $null }
|
|
431
|
+
return [pscustomobject]@{ Slug = $slug; CommitSha = $commitSha; CheckpointPath = $checkpointPath }
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function Save-PrizmPostCompletionDirtyArtifacts {
|
|
435
|
+
param([string]$ProjectRoot, [string]$ArtifactDir, [string]$ItemId, [string]$SessionId)
|
|
436
|
+
$status = & git -C $ProjectRoot status --porcelain --untracked-files=all 2>$null
|
|
437
|
+
if ([string]::IsNullOrWhiteSpace(($status -join "`n"))) { return $true }
|
|
438
|
+
|
|
439
|
+
New-Item -ItemType Directory -Force -Path $ArtifactDir | Out-Null
|
|
440
|
+
($status -join "`n") | Set-Content -Path (Join-Path $ArtifactDir 'post-completion-status.txt') -Encoding UTF8
|
|
441
|
+
& git -C $ProjectRoot diff --binary | Set-Content -Path (Join-Path $ArtifactDir 'post-completion-dirty.patch') -Encoding UTF8
|
|
442
|
+
if ($LASTEXITCODE -ne 0) { return $false }
|
|
443
|
+
& git -C $ProjectRoot diff --cached --binary | Set-Content -Path (Join-Path $ArtifactDir 'post-completion-staged.patch') -Encoding UTF8
|
|
444
|
+
if ($LASTEXITCODE -ne 0) { return $false }
|
|
445
|
+
|
|
446
|
+
$untracked = & git -C $ProjectRoot ls-files --others --exclude-standard 2>$null
|
|
447
|
+
$manifest = Join-Path $ArtifactDir 'post-completion-untracked.txt'
|
|
448
|
+
@($untracked) | Set-Content -Path $manifest -Encoding UTF8
|
|
449
|
+
$untrackedDir = Join-Path $ArtifactDir 'untracked'
|
|
450
|
+
foreach ($rel in @($untracked)) {
|
|
451
|
+
if (-not $rel) { continue }
|
|
452
|
+
$source = Join-Path $ProjectRoot $rel
|
|
453
|
+
$dest = Join-Path $untrackedDir $rel
|
|
454
|
+
$destParent = Split-Path $dest -Parent
|
|
455
|
+
if ($destParent) { New-Item -ItemType Directory -Force -Path $destParent | Out-Null }
|
|
456
|
+
if (Test-Path $source -PathType Leaf) { Copy-Item -LiteralPath $source -Destination $dest -Force }
|
|
457
|
+
elseif (Test-Path $source -PathType Container) { New-Item -ItemType Directory -Force -Path $dest | Out-Null }
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
@(
|
|
461
|
+
'# Post-completion dirty changes preserved',
|
|
462
|
+
'',
|
|
463
|
+
"- Feature: $ItemId",
|
|
464
|
+
"- Session: $SessionId",
|
|
465
|
+
'- Reason: workflow checkpoint and feature commit were already complete, but delayed post-commit activity left the working tree dirty.',
|
|
466
|
+
'',
|
|
467
|
+
'## Recovery guidance',
|
|
468
|
+
'',
|
|
469
|
+
'The finalized feature commit was kept unchanged for merge. Review these follow-up artifacts separately; do not assume they were merged:',
|
|
470
|
+
'',
|
|
471
|
+
'- `post-completion-status.txt` — original dirty working tree status',
|
|
472
|
+
'- `post-completion-dirty.patch` — unstaged tracked changes',
|
|
473
|
+
'- `post-completion-staged.patch` — staged changes',
|
|
474
|
+
'- `post-completion-untracked.txt` and `untracked/` — untracked files copied before cleanup'
|
|
475
|
+
) | Set-Content -Path (Join-Path $ArtifactDir 'post-completion-findings.md') -Encoding UTF8
|
|
476
|
+
|
|
477
|
+
& git -C $ProjectRoot reset --hard *> $null
|
|
478
|
+
if ($LASTEXITCODE -ne 0) { return $false }
|
|
479
|
+
foreach ($rel in @($untracked)) {
|
|
480
|
+
if (-not $rel -or $rel -like '.prizmkit/*') { continue }
|
|
481
|
+
$target = Join-Path $ProjectRoot $rel
|
|
482
|
+
if (Test-Path $target) { Remove-Item -LiteralPath $target -Recurse -Force -ErrorAction SilentlyContinue }
|
|
483
|
+
}
|
|
484
|
+
$remaining = & git -C $ProjectRoot status --porcelain --untracked-files=all 2>$null | Where-Object { $_ -notmatch '^\?\? \.prizmkit/' }
|
|
485
|
+
return [string]::IsNullOrWhiteSpace(($remaining -join "`n"))
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function Write-PrizmRuntimeFailureLog {
|
|
489
|
+
param(
|
|
490
|
+
[string]$FailureLog,
|
|
491
|
+
[string]$FeatureId,
|
|
492
|
+
[string]$SessionId,
|
|
493
|
+
[string]$SessionStatus,
|
|
494
|
+
[int]$ExitCode,
|
|
495
|
+
[string]$StaleKillMarker,
|
|
496
|
+
[string]$ProgressJson,
|
|
497
|
+
[string]$CheckpointPath,
|
|
498
|
+
[string]$ProjectRoot,
|
|
499
|
+
[string]$BaseCommit
|
|
500
|
+
)
|
|
501
|
+
if (-not $FailureLog -or (Test-Path $FailureLog)) { return }
|
|
502
|
+
$dir = Split-Path $FailureLog -Parent
|
|
503
|
+
if ($dir) { New-Item -ItemType Directory -Force -Path $dir | Out-Null }
|
|
504
|
+
$stale = if (Test-Path $StaleKillMarker) { Get-Content $StaleKillMarker -Raw } else { 'No stale-kill marker.' }
|
|
505
|
+
$progressLines = @('Progress data unavailable.')
|
|
506
|
+
if (Test-Path $ProgressJson) {
|
|
507
|
+
try {
|
|
508
|
+
$progress = Get-Content $ProgressJson -Raw | ConvertFrom-Json
|
|
509
|
+
$progressLines = @()
|
|
510
|
+
foreach ($key in @('fatal_error_code','api_error_status','api_error_code','current_phase','current_tool','last_text_snippet','terminal_result_text')) {
|
|
511
|
+
if ($progress.PSObject.Properties[$key] -and $progress.$key) { $progressLines += "- ${key}: $($progress.$key)" }
|
|
512
|
+
}
|
|
513
|
+
if ($progressLines.Count -eq 0) { $progressLines = @('Progress data contained no terminal fields.') }
|
|
514
|
+
} catch { $progressLines = @("Progress parse error: $($_.Exception.Message)") }
|
|
515
|
+
}
|
|
516
|
+
$checkpointLines = @('No checkpoint file found.')
|
|
517
|
+
if (Test-Path $CheckpointPath) {
|
|
518
|
+
try {
|
|
519
|
+
$checkpoint = Get-Content $CheckpointPath -Raw | ConvertFrom-Json
|
|
520
|
+
$steps = @($checkpoint.steps)
|
|
521
|
+
$complete = @($steps | Where-Object { $_.status -in @('completed','skipped') }).Count
|
|
522
|
+
$checkpointLines = @("$complete/$($steps.Count) steps completed_or_skipped")
|
|
523
|
+
foreach ($step in $steps) {
|
|
524
|
+
if ($step.status -notin @('completed','skipped')) { $checkpointLines += "- incomplete: $($step.id) $($step.skill) = $($step.status)" }
|
|
525
|
+
}
|
|
526
|
+
} catch { $checkpointLines = @("Checkpoint parse error: $($_.Exception.Message)") }
|
|
527
|
+
}
|
|
528
|
+
$latestCommit = (& git -C $ProjectRoot rev-parse --short HEAD 2>$null | Select-Object -First 1)
|
|
529
|
+
if (-not $latestCommit) { $latestCommit = 'unavailable' }
|
|
530
|
+
$featureCommit = if (Get-PrizmFeatureCommit $ProjectRoot $BaseCommit $FeatureId $false) { 'yes' } else { 'no' }
|
|
531
|
+
$dirty = & git -C $ProjectRoot status --short 2>$null
|
|
532
|
+
if ([string]::IsNullOrWhiteSpace(($dirty -join "`n"))) { $dirty = @('clean') }
|
|
533
|
+
@(
|
|
534
|
+
'# Runtime-synthesized failure log',
|
|
535
|
+
'',
|
|
536
|
+
'## Session',
|
|
537
|
+
'',
|
|
538
|
+
"- feature_id: $FeatureId",
|
|
539
|
+
"- session_id: $SessionId",
|
|
540
|
+
"- session_status: $SessionStatus",
|
|
541
|
+
"- exit_code: $ExitCode",
|
|
542
|
+
'',
|
|
543
|
+
'## Stale kill marker',
|
|
544
|
+
'',
|
|
545
|
+
'```json',
|
|
546
|
+
$stale,
|
|
547
|
+
'```',
|
|
548
|
+
'',
|
|
549
|
+
'## Progress',
|
|
550
|
+
'',
|
|
551
|
+
$progressLines,
|
|
552
|
+
'',
|
|
553
|
+
'## Checkpoint',
|
|
554
|
+
'',
|
|
555
|
+
$checkpointLines,
|
|
556
|
+
'',
|
|
557
|
+
'## Git state',
|
|
558
|
+
'',
|
|
559
|
+
"- feature_commit_exists: $featureCommit",
|
|
560
|
+
"- latest_commit: $latestCommit",
|
|
561
|
+
'',
|
|
562
|
+
'```text',
|
|
563
|
+
$dirty,
|
|
564
|
+
'```',
|
|
565
|
+
'',
|
|
566
|
+
'## Recommended recovery action',
|
|
567
|
+
'',
|
|
568
|
+
'- If this is an AI runtime/provider error before checkpoint completion, retry the session with a fresh context.',
|
|
569
|
+
'- If checkpoint completion and a feature commit both exist, inspect post-completion artifacts and finalize manually rather than rebuilding from scratch.',
|
|
570
|
+
'- If the working tree is dirty, preserve or review those changes before any reset or merge.'
|
|
571
|
+
) | Set-Content -Path $FailureLog -Encoding UTF8
|
|
572
|
+
}
|
|
573
|
+
|
|
338
574
|
function New-PrizmDefaultDevBranchName {
|
|
339
575
|
param([string]$Kind, [string]$CurrentItemId)
|
|
340
576
|
$timestamp = Get-Date -Format 'yyyyMMddHHmm'
|
|
@@ -575,7 +811,32 @@ function Invoke-PrizmPipeline {
|
|
|
575
811
|
$childAdvanced = ($childSignature -and $childSignature -ne $previousChildActivitySignature)
|
|
576
812
|
$previousChildActivitySignature = $childSignature
|
|
577
813
|
|
|
578
|
-
|
|
814
|
+
$effectiveStaleKillThreshold = Get-PrizmEffectiveStaleKillThreshold -ProgressFile $progressJson -BaseThreshold $staleKillThreshold
|
|
815
|
+
|
|
816
|
+
# Check for error-loop: agent is actively producing output but results are
|
|
817
|
+
# all read-offset errors or wasted calls.
|
|
818
|
+
$errorLoopDetected = $false
|
|
819
|
+
if ($effectiveStaleKillThreshold -gt 0 -and $growth -gt 0 -and (Test-Path $progressJson)) {
|
|
820
|
+
try {
|
|
821
|
+
$progress = Get-Content $progressJson -Raw | ConvertFrom-Json
|
|
822
|
+
$errors = $progress.errors
|
|
823
|
+
if ($errors -is [array] -and $errors.Count -ge 5) {
|
|
824
|
+
$recent = $errors[-5..-1]
|
|
825
|
+
$allBad = ($recent | Where-Object {
|
|
826
|
+
$_.type -in @("read_offset_overflow", "wasted_call")
|
|
827
|
+
}).Count -eq 5
|
|
828
|
+
if ($allBad) {
|
|
829
|
+
$errorLoopDetected = $true
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
} catch {
|
|
833
|
+
# Ignore JSON parse errors — progress file may be incomplete or malformed
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
if ($errorLoopDetected) {
|
|
838
|
+
$staleSeconds = $effectiveStaleKillThreshold
|
|
839
|
+
} elseif ($growth -gt 0 -or $childAdvanced) {
|
|
579
840
|
$staleSeconds = 0
|
|
580
841
|
} else {
|
|
581
842
|
$staleSeconds += $waitSeconds
|
|
@@ -587,7 +848,17 @@ function Invoke-PrizmPipeline {
|
|
|
587
848
|
break
|
|
588
849
|
}
|
|
589
850
|
|
|
590
|
-
$
|
|
851
|
+
$fatalErrorCode = Get-PrizmProgressFatalErrorCode -ProgressFile $progressJson
|
|
852
|
+
if ($fatalErrorCode) {
|
|
853
|
+
$wasStaleKilled = $true
|
|
854
|
+
Write-PrizmWarn "Session hit fatal AI runtime error: $fatalErrorCode"
|
|
855
|
+
$fatalErrorMarker = Join-Path $logsDir 'fatal-error.json'
|
|
856
|
+
Write-PrizmFatalErrorMarker $fatalErrorMarker $fatalErrorCode $staleSeconds $effectiveStaleKillThreshold
|
|
857
|
+
Write-PrizmFatalErrorMarker $staleKillMarker $fatalErrorCode $staleSeconds $effectiveStaleKillThreshold
|
|
858
|
+
Stop-PrizmSessionProcess $pidPath
|
|
859
|
+
if ($staleKillGraceSeconds -gt 0) { Start-Sleep -Seconds $staleKillGraceSeconds }
|
|
860
|
+
break
|
|
861
|
+
}
|
|
591
862
|
if ($effectiveStaleKillThreshold -gt 0 -and $staleSeconds -ge $effectiveStaleKillThreshold) {
|
|
592
863
|
$wasStaleKilled = $true
|
|
593
864
|
Write-PrizmWarn "Session stale-killed (no progress for ${effectiveStaleKillThreshold}s)"
|
|
@@ -619,9 +890,23 @@ function Invoke-PrizmPipeline {
|
|
|
619
890
|
Stop-PrizmProgressParser $parserProcess
|
|
620
891
|
|
|
621
892
|
$wasInfraError = ($exitCode -ne 0 -and (Test-PrizmInfraError -SessionLog $sessionLog -ProgressJson $progressJson))
|
|
893
|
+
$wasAiRuntimeError = Test-PrizmAiRuntimeError -SessionLog $sessionLog -ProgressJson $progressJson
|
|
894
|
+
$semanticCompletion = if ($Kind -eq 'feature' -and $isGitRepository) {
|
|
895
|
+
Get-PrizmFeatureSemanticCompletion $paths.ProjectRoot $listPath $CurrentItemId $baseCommit $paths.PrizmkitDir
|
|
896
|
+
} else { $null }
|
|
622
897
|
|
|
623
898
|
$status = 'crashed'
|
|
624
|
-
if ($
|
|
899
|
+
if ($semanticCompletion) {
|
|
900
|
+
$status = 'success'
|
|
901
|
+
if ($exitCode -ne 0 -or $wasStaleKilled -or $wasTimedOut -or $wasAiRuntimeError) {
|
|
902
|
+
Write-PrizmWarn "Session ended with a failure signal after semantic completion; treating as finalized success"
|
|
903
|
+
Write-PrizmWarn "Semantic completion commit: $($semanticCompletion.CommitSha)"
|
|
904
|
+
}
|
|
905
|
+
} elseif ($wasAiRuntimeError) {
|
|
906
|
+
$status = 'infra_error'
|
|
907
|
+
Write-PrizmWarn "AI session failed due to structured AI runtime/context error"
|
|
908
|
+
Write-PrizmWarn "AI runtime errors are retried without consuming code retry budget"
|
|
909
|
+
} elseif ($wasTimedOut) {
|
|
625
910
|
$status = 'timed_out'
|
|
626
911
|
Write-PrizmWarn "AI session timed out after $timeoutSeconds seconds"
|
|
627
912
|
} elseif ($wasInfraError) {
|
|
@@ -653,13 +938,17 @@ function Invoke-PrizmPipeline {
|
|
|
653
938
|
$mergeSucceeded = $true
|
|
654
939
|
$itemListStatus = ''
|
|
655
940
|
if ($status -eq 'success') {
|
|
656
|
-
$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)
|
|
657
|
-
if ($updateResult -and $updateResult.PSObject.Properties['new_status']) {
|
|
658
|
-
$itemListStatus = [string]$updateResult.new_status
|
|
659
|
-
}
|
|
660
|
-
|
|
661
941
|
if (Test-PrizmGitDirty $paths.ProjectRoot) {
|
|
662
|
-
if ($
|
|
942
|
+
if ($semanticCompletion) {
|
|
943
|
+
$artifactDir = Join-Path $paths.PrizmkitDir "specs\$($semanticCompletion.Slug)"
|
|
944
|
+
if (Save-PrizmPostCompletionDirtyArtifacts $paths.ProjectRoot $artifactDir $CurrentItemId $sessionId) {
|
|
945
|
+
Write-PrizmWarn "Post-completion dirty changes preserved under $artifactDir"
|
|
946
|
+
Write-PrizmWarn "They were not included in the finalized feature commit."
|
|
947
|
+
} else {
|
|
948
|
+
Write-PrizmWarn "Could not safely preserve post-completion dirty changes; preserving dev branch for manual finalization"
|
|
949
|
+
$status = 'finalization_needed'
|
|
950
|
+
}
|
|
951
|
+
} elseif ($hadDirtyBaseline) {
|
|
663
952
|
Write-PrizmInfo "Auto-committing pipeline bookkeeping artifacts only."
|
|
664
953
|
Invoke-PrizmGitIncludeBookkeepingArtifacts $paths.ProjectRoot $stateDir $listPath
|
|
665
954
|
} else {
|
|
@@ -668,13 +957,20 @@ function Invoke-PrizmPipeline {
|
|
|
668
957
|
}
|
|
669
958
|
}
|
|
670
959
|
|
|
671
|
-
if ($
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
960
|
+
if ($status -eq 'success') {
|
|
961
|
+
$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)
|
|
962
|
+
if ($updateResult -and $updateResult.PSObject.Properties['new_status']) {
|
|
963
|
+
$itemListStatus = [string]$updateResult.new_status
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
if ($isGitRepository -and $devBranchName) {
|
|
967
|
+
if (Merge-PrizmDevBranch $paths.ProjectRoot $devBranchName $originalBranch $autoPush) {
|
|
968
|
+
$devBranchName = ''
|
|
969
|
+
} else {
|
|
970
|
+
$mergeSucceeded = $false
|
|
971
|
+
$status = 'merge_conflict'
|
|
972
|
+
Write-PrizmWarn "Auto-merge failed - dev branch preserved for inspection"
|
|
973
|
+
}
|
|
678
974
|
}
|
|
679
975
|
}
|
|
680
976
|
} elseif ($isGitRepository -and $devBranchName) {
|
|
@@ -685,7 +981,20 @@ function Invoke-PrizmPipeline {
|
|
|
685
981
|
Restore-PrizmOriginalBranch $paths.ProjectRoot $originalBranch $devBranchName | Out-Null
|
|
686
982
|
}
|
|
687
983
|
|
|
984
|
+
if ($status -eq 'success' -and $mergeSucceeded -and $isGitRepository) {
|
|
985
|
+
Invoke-PrizmGitCommitPath $paths.ProjectRoot $listPath "chore($CurrentItemId): update $idName status" | Out-Null
|
|
986
|
+
}
|
|
987
|
+
|
|
688
988
|
if ($status -ne 'success') {
|
|
989
|
+
if ($Kind -eq 'feature') {
|
|
990
|
+
$failureSlug = if ($semanticCompletion) { [string]$semanticCompletion.Slug } else { Get-PrizmFeatureSlugFromList $listPath $CurrentItemId }
|
|
991
|
+
if ($failureSlug) {
|
|
992
|
+
$featureArtifactDir = Join-Path $paths.PrizmkitDir "specs\$failureSlug"
|
|
993
|
+
$failureLog = Join-Path $featureArtifactDir 'failure-log.md'
|
|
994
|
+
$checkpointPath = Join-Path $featureArtifactDir 'workflow-checkpoint.json'
|
|
995
|
+
Write-PrizmRuntimeFailureLog $failureLog $CurrentItemId $sessionId $status $exitCode $staleKillMarker $progressJson $checkpointPath $paths.ProjectRoot $baseCommit
|
|
996
|
+
}
|
|
997
|
+
}
|
|
689
998
|
$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)
|
|
690
999
|
if ($updateResult -and $updateResult.PSObject.Properties['new_status']) {
|
|
691
1000
|
$itemListStatus = [string]$updateResult.new_status
|