kiro-spec-engine 1.47.0 → 1.47.1
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/CHANGELOG.md +1 -0
- package/README.md +1 -0
- package/README.zh.md +1 -0
- package/docs/autonomous-control-guide.md +9 -0
- package/docs/command-reference.md +15 -0
- package/lib/commands/auto.js +671 -108
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
32
32
|
- **Spec growth/duplicate guardrails**: Added `--spec-session-max-created`, `--spec-session-max-created-per-goal`, and `--spec-session-max-duplicate-goals` (with hard-fail option) plus summary telemetry (`goal_input_guard`, `spec_session_growth_guard`) to reduce runaway autonomous portfolio expansion.
|
|
33
33
|
- **Autonomous KPI trend command**: Added `kse auto kpi trend` to aggregate weekly success/completion, failure, sub-spec, and spec-growth telemetry from persisted autonomous summary sessions.
|
|
34
34
|
- **Autonomous KPI trend period/csv/anomaly enhancement**: Extended `kse auto kpi trend` with `--period week|day`, `--csv` export mode, and JSON anomaly diagnostics (`anomaly_detection`, `anomalies`) for latest-period regression checks.
|
|
35
|
+
- **Program governance stabilization loop**: Added `close-loop-program` governance controls (`--program-govern-until-stable`, `--program-govern-max-rounds`, `--program-govern-max-minutes`, anomaly knobs, `--program-govern-use-action`, `--no-program-govern-auto-action`) so gate/anomaly failures can trigger bounded replay/recover rounds with remediation action execution until stable, with `program_governance`, `program_kpi_trend`, and `program_kpi_anomalies` telemetry.
|
|
35
36
|
- **Close-loop multi-goal batch command**: Added `kse auto close-loop-batch <goals-file>` with file-format autodetect (`json|lines`), `--continue-on-error`, and per-goal summary output so autonomous master/sub execution can scale across multiple goals in one run.
|
|
36
37
|
- **Close-loop batch global scheduler**: Added `--batch-parallel` (`1-20`) to execute multiple goals concurrently in `close-loop-batch`, enabling master/sub portfolios to progress in parallel without manual orchestration handoffs.
|
|
37
38
|
- **Close-loop batch resume from summary**: Added `--resume-from-summary <path>` to recover pending goals from a prior batch run and continue autonomous delivery without rebuilding the entire goal queue manually.
|
package/README.md
CHANGED
|
@@ -531,6 +531,7 @@ kse create-spec <name> # Legacy: create empty Spec folder only
|
|
|
531
531
|
# Autonomous close-loop program (one command, no confirmation loop)
|
|
532
532
|
kse auto close-loop "<goal>" # Auto split goal into master/sub specs and execute to closure
|
|
533
533
|
kse auto close-loop "<goal>" --dry-run --json # Preview decomposition plan only
|
|
534
|
+
kse auto close-loop-program "<goal>" --program-govern-until-stable --program-govern-use-action 1 --json # Program-level recovery + governance with remediation action execution
|
|
534
535
|
|
|
535
536
|
# Spec workflow (recommended)
|
|
536
537
|
kse spec bootstrap --name <spec> --non-interactive # Generate requirements/design/tasks draft
|
package/README.zh.md
CHANGED
|
@@ -448,6 +448,7 @@ kse create-spec <name> # 兼容旧版:仅创建空 Spec 目录
|
|
|
448
448
|
# 自动闭环主从编排(单命令)
|
|
449
449
|
kse auto close-loop "<目标>" # 自动拆分 Master/Sub Spec 并推进到完成态
|
|
450
450
|
kse auto close-loop "<目标>" --dry-run --json # 仅预览拆分与依赖计划
|
|
451
|
+
kse auto close-loop-program "<目标>" --program-govern-until-stable --program-govern-use-action 1 --json # 程序级自动恢复 + 治理循环(含 remediation action 执行)直到稳定
|
|
451
452
|
|
|
452
453
|
# Spec 工作流(推荐)
|
|
453
454
|
kse spec bootstrap --name <spec> --non-interactive # 生成 requirements/design/tasks 初稿
|
|
@@ -300,6 +300,15 @@ Close-loop recovery command:
|
|
|
300
300
|
- `--program-audit-out <path>` exports full recovery/program audit JSON.
|
|
301
301
|
- Output includes `recovered_from_summary`, `recovery_plan` (including `selection_explain`), `recovery_cycle` (with elapsed/budget metadata), and `recovery_memory` (including scope + selection explanation) for full auditability of applied strategy changes.
|
|
302
302
|
|
|
303
|
+
Program governance loop (for `close-loop-program`):
|
|
304
|
+
- `--program-govern-until-stable` enables bounded post-run governance rounds.
|
|
305
|
+
- `--program-govern-max-rounds <n>` and `--program-govern-max-minutes <n>` bound governance loop cost.
|
|
306
|
+
- `--program-govern-anomaly-weeks <n>` + `--program-govern-anomaly-period <week|day>` make governance anomaly-aware using autonomous KPI trend.
|
|
307
|
+
- `--no-program-govern-anomaly` limits governance trigger to gate/budget failures only.
|
|
308
|
+
- `--program-govern-use-action <n>` pins remediation action index for governance rounds.
|
|
309
|
+
- `--no-program-govern-auto-action` disables automatic remediation action selection/execution inside governance.
|
|
310
|
+
- Output includes `program_governance`, `program_kpi_trend`, and `program_kpi_anomalies` for traceable autonomous stabilization history.
|
|
311
|
+
|
|
303
312
|
KPI trend command:
|
|
304
313
|
- `kse auto kpi trend --weeks <n> --mode <all|batch|program|recover> --period <week|day> --json` aggregates periodic success/completion, failure, sub-spec, and spec-growth telemetry from persisted autonomous session summaries.
|
|
305
314
|
- Add `--csv` to print/export trend buckets as CSV (`--out` writes CSV when `--csv` is enabled).
|
|
@@ -337,6 +337,9 @@ kse auto close-loop-program \
|
|
|
337
337
|
--program-gate-fallback-profile prod \
|
|
338
338
|
--program-min-success-rate 95 \
|
|
339
339
|
--program-max-risk-level medium \
|
|
340
|
+
--program-govern-until-stable \
|
|
341
|
+
--program-govern-max-rounds 3 \
|
|
342
|
+
--program-govern-use-action 1 \
|
|
340
343
|
--program-kpi-out .kiro/reports/close-loop-program-kpi.json \
|
|
341
344
|
--program-audit-out .kiro/reports/close-loop-program-audit.json \
|
|
342
345
|
--json
|
|
@@ -521,6 +524,14 @@ Close-loop program (`kse auto close-loop-program "<goal>"`) options:
|
|
|
521
524
|
- `--program-max-agent-budget <n>`: convergence gate max allowed agent budget/effective parallel budget (`1-500`)
|
|
522
525
|
- `--program-max-total-sub-specs <n>`: convergence gate max total sub-specs across program goals (`1-500000`)
|
|
523
526
|
- `--no-program-gate-auto-remediate`: disable automatic remediation hints/prune attempts after gate failure
|
|
527
|
+
- `--program-govern-until-stable`: enable post-run governance loop that keeps replaying/recovering until gate/anomaly stability
|
|
528
|
+
- `--program-govern-max-rounds <n>`: max governance rounds (`1-20`, default `3`)
|
|
529
|
+
- `--program-govern-max-minutes <n>`: elapsed-time budget for governance loop (`1-10080`, default `60`)
|
|
530
|
+
- `--program-govern-anomaly-weeks <n>`: KPI lookback weeks for anomaly-triggered governance (`1-260`, default `8`)
|
|
531
|
+
- `--program-govern-anomaly-period <week|day>`: KPI bucket period for anomaly governance checks (default `week`)
|
|
532
|
+
- `--no-program-govern-anomaly`: disable anomaly-triggered governance and only govern by gate/budget failures
|
|
533
|
+
- `--program-govern-use-action <n>`: pin remediation action index (`1-20`) used in governance rounds
|
|
534
|
+
- `--no-program-govern-auto-action`: disable automatic remediation action selection/execution in governance rounds
|
|
524
535
|
- `--program-min-quality-score <n>`: minimum semantic decomposition quality score before automatic refinement (`0-100`, default `70`)
|
|
525
536
|
- `--program-quality-gate`: fail run when final decomposition quality remains below `--program-min-quality-score`
|
|
526
537
|
- `--recovery-memory-scope <scope>`: scope key for recovery memory isolation (default auto: project + git branch)
|
|
@@ -530,6 +541,10 @@ Close-loop program (`kse auto close-loop-program "<goal>"`) options:
|
|
|
530
541
|
- Program summary includes `program_kpi`, `program_gate`, `program_gate_fallbacks`, `program_gate_effective`, and optional `program_kpi_file` / `program_audit_file` for portfolio-level observability pipelines.
|
|
531
542
|
- `program_gate` now supports unified budget checks (success/risk + elapsed time + agent budget + total sub-spec ceiling).
|
|
532
543
|
- On gate/budget failure, summary can include `program_gate_auto_remediation` with auto patch/prune actions.
|
|
544
|
+
- With `--program-govern-until-stable`, summary additionally includes:
|
|
545
|
+
- `program_governance` (round history, stop reason, exhausted/converged state)
|
|
546
|
+
- `program_governance` includes action-selection metadata (`auto_action_enabled`, `action_selection_enabled`, `pinned_action_index`, per-round `selected_action*`).
|
|
547
|
+
- `program_kpi_trend` and `program_kpi_anomalies` (anomaly-aware governance context)
|
|
533
548
|
- Program summary includes `program_diagnostics` with `failure_clusters` and `remediation_actions` (prioritized follow-up commands for convergence).
|
|
534
549
|
- Program summary includes `program_coordination` (master/sub topology, unresolved goal indexes, scheduler snapshot) and `auto_recovery` metadata.
|
|
535
550
|
|
package/lib/commands/auto.js
CHANGED
|
@@ -416,6 +416,14 @@ function registerAutoCommands(program) {
|
|
|
416
416
|
.option('--program-max-agent-budget <n>', 'Program convergence gate: maximum allowed agent budget/effective parallel budget', parseInt)
|
|
417
417
|
.option('--program-max-total-sub-specs <n>', 'Program convergence gate: maximum total sub-specs generated across program goals', parseInt)
|
|
418
418
|
.option('--no-program-gate-auto-remediate', 'Disable automatic remediation patch/prune suggestions after gate failure')
|
|
419
|
+
.option('--program-govern-until-stable', 'Enable post-run governance loop until gate/anomaly stability is reached')
|
|
420
|
+
.option('--program-govern-max-rounds <n>', 'Max governance rounds when --program-govern-until-stable is enabled (default: 3)', parseInt)
|
|
421
|
+
.option('--program-govern-max-minutes <n>', 'Max elapsed minutes for governance loop (default: 60)', parseInt)
|
|
422
|
+
.option('--program-govern-anomaly-weeks <n>', 'KPI trend lookback weeks for governance anomaly checks (default: 8)', parseInt)
|
|
423
|
+
.option('--program-govern-anomaly-period <period>', 'KPI trend period for governance anomaly checks: week|day', 'week')
|
|
424
|
+
.option('--no-program-govern-anomaly', 'Disable anomaly-triggered governance decisions (gate-only)')
|
|
425
|
+
.option('--program-govern-use-action <n>', 'Pinned remediation action index used by governance rounds (default: memory/default)', parseInt)
|
|
426
|
+
.option('--no-program-govern-auto-action', 'Disable automatic remediation action selection/execution inside governance loop')
|
|
419
427
|
.option('--recovery-memory-scope <scope>', 'Recovery memory scope key (default: auto: project + git branch)')
|
|
420
428
|
.option('--program-audit-out <path>', 'Write program audit JSON with recovery and coordination trace')
|
|
421
429
|
.option('--program-kpi-out <path>', 'Write program KPI snapshot JSON to file')
|
|
@@ -429,6 +437,14 @@ function registerAutoCommands(program) {
|
|
|
429
437
|
const programRecoverMaxRounds = normalizeRecoverMaxRounds(options.programRecoverMaxRounds);
|
|
430
438
|
const programRecoverMaxMinutes = normalizeRecoverMaxMinutes(options.programRecoverMaxMinutes, '--program-recover-max-minutes');
|
|
431
439
|
const programRecoverResumeStrategy = normalizeResumeStrategy(options.programRecoverResumeStrategy);
|
|
440
|
+
const programGovernUntilStable = Boolean(options.programGovernUntilStable);
|
|
441
|
+
const programGovernMaxRounds = normalizeProgramGovernMaxRounds(options.programGovernMaxRounds);
|
|
442
|
+
const programGovernMaxMinutes = normalizeProgramGovernMaxMinutes(options.programGovernMaxMinutes);
|
|
443
|
+
const programGovernAnomalyEnabled = options.programGovernAnomaly !== false;
|
|
444
|
+
const programGovernAnomalyWeeks = normalizeProgramGovernAnomalyWeeks(options.programGovernAnomalyWeeks);
|
|
445
|
+
const programGovernAnomalyPeriod = normalizeAutoKpiTrendPeriod(options.programGovernAnomalyPeriod);
|
|
446
|
+
const programGovernUseAction = normalizeProgramGovernUseAction(options.programGovernUseAction);
|
|
447
|
+
const programGovernAutoActionEnabled = options.programGovernAutoAction !== false;
|
|
432
448
|
const programGatePolicy = resolveProgramGatePolicy({
|
|
433
449
|
profile: options.programGateProfile,
|
|
434
450
|
minSuccessRate: options.programMinSuccessRate,
|
|
@@ -559,60 +575,70 @@ function registerAutoCommands(program) {
|
|
|
559
575
|
summary.program_completed_at = new Date(programCompletedAt).toISOString();
|
|
560
576
|
summary.program_elapsed_ms = Math.max(0, programCompletedAt - programStartedAt);
|
|
561
577
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
maxTotalSubSpecs: programGatePolicy.maxTotalSubSpecs
|
|
578
|
+
await applyProgramGateOutcome(summary, {
|
|
579
|
+
projectPath,
|
|
580
|
+
options: programOptions,
|
|
581
|
+
programGatePolicy,
|
|
582
|
+
gateFallbackChain,
|
|
583
|
+
enableAutoRemediation: options.programGateAutoRemediate !== false
|
|
569
584
|
});
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
for (const fallbackProfile of gateFallbackChain) {
|
|
576
|
-
const fallbackResult = evaluateProgramConvergenceGate(summary, {
|
|
577
|
-
profile: fallbackProfile,
|
|
578
|
-
maxElapsedMinutes: programGatePolicy.maxElapsedMinutes,
|
|
579
|
-
maxAgentBudget: programGatePolicy.maxAgentBudget,
|
|
580
|
-
maxTotalSubSpecs: programGatePolicy.maxTotalSubSpecs
|
|
581
|
-
});
|
|
582
|
-
summary.program_gate_fallbacks.push(fallbackResult);
|
|
583
|
-
if (fallbackResult.passed) {
|
|
584
|
-
effectiveGatePassed = true;
|
|
585
|
-
effectiveGateSource = 'fallback-chain';
|
|
586
|
-
matchedFallbackProfile = fallbackProfile;
|
|
587
|
-
break;
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
summary.program_gate_fallback = summary.program_gate_fallbacks.length > 0
|
|
592
|
-
? summary.program_gate_fallbacks[0]
|
|
593
|
-
: null;
|
|
594
|
-
summary.program_gate_effective = {
|
|
595
|
-
passed: effectiveGatePassed,
|
|
596
|
-
source: effectiveGateSource,
|
|
597
|
-
primary_passed: Boolean(summary.program_gate && summary.program_gate.passed),
|
|
598
|
-
fallback_profile: matchedFallbackProfile,
|
|
599
|
-
fallback_chain: gateFallbackChain,
|
|
600
|
-
fallback_passed: matchedFallbackProfile !== null,
|
|
601
|
-
attempted_fallback_count: summary.program_gate_fallbacks.length
|
|
602
|
-
};
|
|
603
|
-
if (
|
|
604
|
-
options.programGateAutoRemediate !== false &&
|
|
605
|
-
(
|
|
606
|
-
!summary.program_gate_effective.passed ||
|
|
607
|
-
isSpecSessionBudgetHardFailure(summary) ||
|
|
608
|
-
isSpecSessionGrowthGuardHardFailure(summary)
|
|
609
|
-
)
|
|
610
|
-
) {
|
|
611
|
-
summary.program_gate_auto_remediation = await applyProgramGateAutoRemediation(summary, {
|
|
585
|
+
|
|
586
|
+
if (programGovernUntilStable) {
|
|
587
|
+
const governanceResult = await runProgramGovernanceLoop({
|
|
588
|
+
enabled: true,
|
|
589
|
+
summary,
|
|
612
590
|
projectPath,
|
|
613
|
-
|
|
591
|
+
programOptions,
|
|
592
|
+
baseGoalsResult: goalsResult,
|
|
593
|
+
maxRounds: programGovernMaxRounds,
|
|
594
|
+
maxMinutes: programGovernMaxMinutes,
|
|
595
|
+
anomalyEnabled: programGovernAnomalyEnabled,
|
|
596
|
+
anomalyWeeks: programGovernAnomalyWeeks,
|
|
597
|
+
anomalyPeriod: programGovernAnomalyPeriod,
|
|
598
|
+
programGatePolicy,
|
|
599
|
+
gateFallbackChain,
|
|
600
|
+
recoveryMemoryScope,
|
|
601
|
+
recoverResumeStrategy: programRecoverResumeStrategy,
|
|
602
|
+
recoverMaxRounds: programRecoverMaxRounds,
|
|
603
|
+
recoverMaxMinutes: programRecoverMaxMinutes,
|
|
604
|
+
programRecoverUseAction: options.programRecoverUseAction,
|
|
605
|
+
programGateAutoRemediate: options.programGateAutoRemediate !== false,
|
|
606
|
+
governUseAction: programGovernUseAction,
|
|
607
|
+
governAutoActionEnabled: programGovernAutoActionEnabled
|
|
614
608
|
});
|
|
609
|
+
summary = governanceResult.summary;
|
|
610
|
+
summary.program_governance = governanceResult.governance;
|
|
611
|
+
} else {
|
|
612
|
+
summary.program_governance = {
|
|
613
|
+
enabled: false,
|
|
614
|
+
anomaly_enabled: programGovernAnomalyEnabled,
|
|
615
|
+
anomaly_weeks: programGovernAnomalyWeeks,
|
|
616
|
+
anomaly_period: programGovernAnomalyPeriod,
|
|
617
|
+
auto_action_enabled: programGovernAutoActionEnabled,
|
|
618
|
+
action_selection_enabled: false,
|
|
619
|
+
pinned_action_index: programGovernUseAction,
|
|
620
|
+
max_rounds: programGovernMaxRounds,
|
|
621
|
+
max_minutes: programGovernMaxMinutes,
|
|
622
|
+
performed_rounds: 0,
|
|
623
|
+
converged: Boolean(
|
|
624
|
+
summary &&
|
|
625
|
+
summary.program_gate_effective &&
|
|
626
|
+
summary.program_gate_effective.passed &&
|
|
627
|
+
!isSpecSessionBudgetHardFailure(summary) &&
|
|
628
|
+
!isSpecSessionGrowthGuardHardFailure(summary)
|
|
629
|
+
),
|
|
630
|
+
exhausted: false,
|
|
631
|
+
stop_reason: 'disabled',
|
|
632
|
+
history: []
|
|
633
|
+
};
|
|
615
634
|
}
|
|
635
|
+
|
|
636
|
+
const finalProgramCompletedAt = Date.now();
|
|
637
|
+
summary.program_completed_at = new Date(finalProgramCompletedAt).toISOString();
|
|
638
|
+
summary.program_elapsed_ms = Math.max(0, finalProgramCompletedAt - programStartedAt);
|
|
639
|
+
|
|
640
|
+
await maybeWriteProgramKpi(summary, options.programKpiOut, projectPath);
|
|
641
|
+
await maybeWriteOutput(summary, options.out, projectPath);
|
|
616
642
|
await maybeWriteProgramAudit(summary, options.programAuditOut, projectPath);
|
|
617
643
|
|
|
618
644
|
printCloseLoopBatchSummary(summary, programOptions);
|
|
@@ -787,60 +813,13 @@ function registerAutoCommands(program) {
|
|
|
787
813
|
recoveryResult.summary.program_started_at = new Date(recoveryStartedAt).toISOString();
|
|
788
814
|
recoveryResult.summary.program_completed_at = new Date(recoveryCompletedAt).toISOString();
|
|
789
815
|
recoveryResult.summary.program_elapsed_ms = Math.max(0, recoveryCompletedAt - recoveryStartedAt);
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
maxTotalSubSpecs: programGatePolicy.maxTotalSubSpecs
|
|
816
|
+
await applyProgramGateOutcome(recoveryResult.summary, {
|
|
817
|
+
projectPath,
|
|
818
|
+
options,
|
|
819
|
+
programGatePolicy,
|
|
820
|
+
gateFallbackChain,
|
|
821
|
+
enableAutoRemediation: options.programGateAutoRemediate !== false
|
|
797
822
|
});
|
|
798
|
-
let effectiveRecoveryGatePassed = recoveryResult.summary.program_gate.passed;
|
|
799
|
-
let effectiveRecoveryGateSource = 'primary';
|
|
800
|
-
let matchedRecoveryFallbackProfile = null;
|
|
801
|
-
recoveryResult.summary.program_gate_fallbacks = [];
|
|
802
|
-
if (!effectiveRecoveryGatePassed && gateFallbackChain.length > 0) {
|
|
803
|
-
for (const fallbackProfile of gateFallbackChain) {
|
|
804
|
-
const fallbackResult = evaluateProgramConvergenceGate(recoveryResult.summary, {
|
|
805
|
-
profile: fallbackProfile,
|
|
806
|
-
maxElapsedMinutes: programGatePolicy.maxElapsedMinutes,
|
|
807
|
-
maxAgentBudget: programGatePolicy.maxAgentBudget,
|
|
808
|
-
maxTotalSubSpecs: programGatePolicy.maxTotalSubSpecs
|
|
809
|
-
});
|
|
810
|
-
recoveryResult.summary.program_gate_fallbacks.push(fallbackResult);
|
|
811
|
-
if (fallbackResult.passed) {
|
|
812
|
-
effectiveRecoveryGatePassed = true;
|
|
813
|
-
effectiveRecoveryGateSource = 'fallback-chain';
|
|
814
|
-
matchedRecoveryFallbackProfile = fallbackProfile;
|
|
815
|
-
break;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
recoveryResult.summary.program_gate_fallback = recoveryResult.summary.program_gate_fallbacks.length > 0
|
|
820
|
-
? recoveryResult.summary.program_gate_fallbacks[0]
|
|
821
|
-
: null;
|
|
822
|
-
recoveryResult.summary.program_gate_effective = {
|
|
823
|
-
passed: effectiveRecoveryGatePassed,
|
|
824
|
-
source: effectiveRecoveryGateSource,
|
|
825
|
-
primary_passed: Boolean(recoveryResult.summary.program_gate && recoveryResult.summary.program_gate.passed),
|
|
826
|
-
fallback_profile: matchedRecoveryFallbackProfile,
|
|
827
|
-
fallback_chain: gateFallbackChain,
|
|
828
|
-
fallback_passed: matchedRecoveryFallbackProfile !== null,
|
|
829
|
-
attempted_fallback_count: recoveryResult.summary.program_gate_fallbacks.length
|
|
830
|
-
};
|
|
831
|
-
if (
|
|
832
|
-
options.programGateAutoRemediate !== false &&
|
|
833
|
-
(
|
|
834
|
-
!recoveryResult.summary.program_gate_effective.passed ||
|
|
835
|
-
isSpecSessionBudgetHardFailure(recoveryResult.summary) ||
|
|
836
|
-
isSpecSessionGrowthGuardHardFailure(recoveryResult.summary)
|
|
837
|
-
)
|
|
838
|
-
) {
|
|
839
|
-
recoveryResult.summary.program_gate_auto_remediation = await applyProgramGateAutoRemediation(recoveryResult.summary, {
|
|
840
|
-
projectPath,
|
|
841
|
-
options
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
823
|
await maybeWriteProgramAudit(recoveryResult.summary, options.programAuditOut, projectPath);
|
|
845
824
|
|
|
846
825
|
printCloseLoopBatchSummary(recoveryResult.summary, recoveryResult.options || options);
|
|
@@ -1807,7 +1786,7 @@ async function loadCloseLoopBatchSummaryPayload(projectPath, summaryCandidate) {
|
|
|
1807
1786
|
};
|
|
1808
1787
|
}
|
|
1809
1788
|
|
|
1810
|
-
function normalizeRecoveryActionIndex(actionCandidate, maxActions) {
|
|
1789
|
+
function normalizeRecoveryActionIndex(actionCandidate, maxActions, optionLabel = '--use-action') {
|
|
1811
1790
|
if (!Number.isInteger(maxActions) || maxActions <= 0) {
|
|
1812
1791
|
return 1;
|
|
1813
1792
|
}
|
|
@@ -1819,10 +1798,10 @@ function normalizeRecoveryActionIndex(actionCandidate, maxActions) {
|
|
|
1819
1798
|
const parsed = Number(actionCandidate);
|
|
1820
1799
|
const upperBound = Math.max(20, maxActions);
|
|
1821
1800
|
if (!Number.isInteger(parsed) || parsed < 1 || parsed > upperBound) {
|
|
1822
|
-
throw new Error(
|
|
1801
|
+
throw new Error(`${optionLabel} must be an integer between 1 and ${upperBound}.`);
|
|
1823
1802
|
}
|
|
1824
1803
|
if (parsed > maxActions) {
|
|
1825
|
-
throw new Error(
|
|
1804
|
+
throw new Error(`${optionLabel} ${parsed} is out of range. Available remediation actions: 1-${maxActions}.`);
|
|
1826
1805
|
}
|
|
1827
1806
|
return parsed;
|
|
1828
1807
|
}
|
|
@@ -2359,16 +2338,19 @@ function resolveRecoveryActionSelection(summaryPayload, actionCandidate, context
|
|
|
2359
2338
|
const availableActions = Array.isArray(diagnostics.remediation_actions) && diagnostics.remediation_actions.length > 0
|
|
2360
2339
|
? diagnostics.remediation_actions
|
|
2361
2340
|
: buildProgramRemediationActions(summaryPayload || {}, []);
|
|
2341
|
+
const optionLabel = typeof context.optionLabel === 'string' && context.optionLabel.trim()
|
|
2342
|
+
? context.optionLabel.trim()
|
|
2343
|
+
: '--use-action';
|
|
2362
2344
|
let selectedIndex = null;
|
|
2363
2345
|
let selectionSource = 'default';
|
|
2364
2346
|
let memorySelection = null;
|
|
2365
2347
|
let selectionExplain = null;
|
|
2366
2348
|
if (actionCandidate !== undefined && actionCandidate !== null) {
|
|
2367
|
-
selectedIndex = normalizeRecoveryActionIndex(actionCandidate, availableActions.length);
|
|
2349
|
+
selectedIndex = normalizeRecoveryActionIndex(actionCandidate, availableActions.length, optionLabel);
|
|
2368
2350
|
selectionSource = 'explicit';
|
|
2369
2351
|
selectionExplain = {
|
|
2370
2352
|
mode: 'explicit',
|
|
2371
|
-
reason:
|
|
2353
|
+
reason: `${optionLabel} provided`,
|
|
2372
2354
|
selected_index: selectedIndex
|
|
2373
2355
|
};
|
|
2374
2356
|
} else {
|
|
@@ -2884,6 +2866,50 @@ function normalizeProgramMaxTotalSubSpecs(countCandidate) {
|
|
|
2884
2866
|
return parsed;
|
|
2885
2867
|
}
|
|
2886
2868
|
|
|
2869
|
+
function normalizeProgramGovernMaxRounds(roundsCandidate) {
|
|
2870
|
+
if (roundsCandidate === undefined || roundsCandidate === null) {
|
|
2871
|
+
return 3;
|
|
2872
|
+
}
|
|
2873
|
+
const parsed = Number(roundsCandidate);
|
|
2874
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 20) {
|
|
2875
|
+
throw new Error('--program-govern-max-rounds must be an integer between 1 and 20.');
|
|
2876
|
+
}
|
|
2877
|
+
return parsed;
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
function normalizeProgramGovernMaxMinutes(minutesCandidate) {
|
|
2881
|
+
if (minutesCandidate === undefined || minutesCandidate === null) {
|
|
2882
|
+
return 60;
|
|
2883
|
+
}
|
|
2884
|
+
const parsed = Number(minutesCandidate);
|
|
2885
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 10080) {
|
|
2886
|
+
throw new Error('--program-govern-max-minutes must be an integer between 1 and 10080.');
|
|
2887
|
+
}
|
|
2888
|
+
return parsed;
|
|
2889
|
+
}
|
|
2890
|
+
|
|
2891
|
+
function normalizeProgramGovernAnomalyWeeks(weeksCandidate) {
|
|
2892
|
+
if (weeksCandidate === undefined || weeksCandidate === null) {
|
|
2893
|
+
return 8;
|
|
2894
|
+
}
|
|
2895
|
+
const parsed = Number(weeksCandidate);
|
|
2896
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 260) {
|
|
2897
|
+
throw new Error('--program-govern-anomaly-weeks must be an integer between 1 and 260.');
|
|
2898
|
+
}
|
|
2899
|
+
return parsed;
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
function normalizeProgramGovernUseAction(actionCandidate) {
|
|
2903
|
+
if (actionCandidate === undefined || actionCandidate === null) {
|
|
2904
|
+
return null;
|
|
2905
|
+
}
|
|
2906
|
+
const parsed = Number(actionCandidate);
|
|
2907
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 20) {
|
|
2908
|
+
throw new Error('--program-govern-use-action must be an integer between 1 and 20.');
|
|
2909
|
+
}
|
|
2910
|
+
return parsed;
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2887
2913
|
function resolveProgramGatePolicy(policy = {}) {
|
|
2888
2914
|
const profile = normalizeProgramGateProfile(policy.profile);
|
|
2889
2915
|
const profilePolicy = PROGRAM_GATE_PROFILE_POLICY[profile];
|
|
@@ -4172,6 +4198,38 @@ function printCloseLoopBatchSummary(summary, options) {
|
|
|
4172
4198
|
`source=${summary.auto_recovery.selection_source || 'default'})`
|
|
4173
4199
|
));
|
|
4174
4200
|
}
|
|
4201
|
+
if (summary.program_governance && summary.program_governance.enabled) {
|
|
4202
|
+
console.log(chalk.gray(
|
|
4203
|
+
` Program governance: ${summary.program_governance.performed_rounds}/` +
|
|
4204
|
+
`${summary.program_governance.max_rounds} rounds, stop=${summary.program_governance.stop_reason}`
|
|
4205
|
+
));
|
|
4206
|
+
if (summary.program_governance.action_selection_enabled) {
|
|
4207
|
+
console.log(chalk.gray(
|
|
4208
|
+
` Governance action selection: ` +
|
|
4209
|
+
`${summary.program_governance.auto_action_enabled ? 'auto' : 'manual-only'}, ` +
|
|
4210
|
+
`pinned=${summary.program_governance.pinned_action_index || 'none'}`
|
|
4211
|
+
));
|
|
4212
|
+
}
|
|
4213
|
+
if (Array.isArray(summary.program_governance.history) && summary.program_governance.history.length > 0) {
|
|
4214
|
+
const latestRound = summary.program_governance.history[summary.program_governance.history.length - 1];
|
|
4215
|
+
if (latestRound && latestRound.selected_action) {
|
|
4216
|
+
console.log(chalk.gray(
|
|
4217
|
+
` Governance selected action: #${latestRound.selected_action_index || 'n/a'} ${latestRound.selected_action}`
|
|
4218
|
+
));
|
|
4219
|
+
}
|
|
4220
|
+
}
|
|
4221
|
+
if (summary.program_governance.exhausted) {
|
|
4222
|
+
console.log(chalk.yellow(' Program governance exhausted before reaching stable state.'));
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
if (Array.isArray(summary.program_kpi_anomalies) && summary.program_kpi_anomalies.length > 0) {
|
|
4226
|
+
const highCount = summary.program_kpi_anomalies
|
|
4227
|
+
.filter(item => `${item && item.severity ? item.severity : ''}`.trim().toLowerCase() === 'high')
|
|
4228
|
+
.length;
|
|
4229
|
+
console.log(chalk.gray(
|
|
4230
|
+
` Program KPI anomalies: total=${summary.program_kpi_anomalies.length}, high=${highCount}`
|
|
4231
|
+
));
|
|
4232
|
+
}
|
|
4175
4233
|
if (summary.program_coordination) {
|
|
4176
4234
|
console.log(chalk.gray(
|
|
4177
4235
|
` Master/Sub sync: masters=${summary.program_coordination.master_spec_count}, ` +
|
|
@@ -4421,6 +4479,505 @@ function evaluateProgramConvergenceGate(summary, policy = {}) {
|
|
|
4421
4479
|
};
|
|
4422
4480
|
}
|
|
4423
4481
|
|
|
4482
|
+
async function applyProgramGateOutcome(summary, context = {}) {
|
|
4483
|
+
const projectPath = context && context.projectPath ? context.projectPath : process.cwd();
|
|
4484
|
+
const options = context && context.options && typeof context.options === 'object'
|
|
4485
|
+
? context.options
|
|
4486
|
+
: {};
|
|
4487
|
+
const resolvedPolicy = resolveProgramGatePolicy(context && context.programGatePolicy ? context.programGatePolicy : {});
|
|
4488
|
+
const gateFallbackChain = Array.isArray(context && context.gateFallbackChain)
|
|
4489
|
+
? context.gateFallbackChain
|
|
4490
|
+
: [];
|
|
4491
|
+
const enableAutoRemediation = context && context.enableAutoRemediation !== undefined
|
|
4492
|
+
? Boolean(context.enableAutoRemediation)
|
|
4493
|
+
: true;
|
|
4494
|
+
|
|
4495
|
+
summary.program_gate = evaluateProgramConvergenceGate(summary, {
|
|
4496
|
+
profile: resolvedPolicy.profile,
|
|
4497
|
+
minSuccessRate: resolvedPolicy.minSuccessRate,
|
|
4498
|
+
maxRiskLevel: resolvedPolicy.maxRiskLevel,
|
|
4499
|
+
maxElapsedMinutes: resolvedPolicy.maxElapsedMinutes,
|
|
4500
|
+
maxAgentBudget: resolvedPolicy.maxAgentBudget,
|
|
4501
|
+
maxTotalSubSpecs: resolvedPolicy.maxTotalSubSpecs
|
|
4502
|
+
});
|
|
4503
|
+
|
|
4504
|
+
let effectiveGatePassed = summary.program_gate.passed;
|
|
4505
|
+
let effectiveGateSource = 'primary';
|
|
4506
|
+
let matchedFallbackProfile = null;
|
|
4507
|
+
summary.program_gate_fallbacks = [];
|
|
4508
|
+
if (!effectiveGatePassed && gateFallbackChain.length > 0) {
|
|
4509
|
+
for (const fallbackProfile of gateFallbackChain) {
|
|
4510
|
+
const fallbackResult = evaluateProgramConvergenceGate(summary, {
|
|
4511
|
+
profile: fallbackProfile,
|
|
4512
|
+
maxElapsedMinutes: resolvedPolicy.maxElapsedMinutes,
|
|
4513
|
+
maxAgentBudget: resolvedPolicy.maxAgentBudget,
|
|
4514
|
+
maxTotalSubSpecs: resolvedPolicy.maxTotalSubSpecs
|
|
4515
|
+
});
|
|
4516
|
+
summary.program_gate_fallbacks.push(fallbackResult);
|
|
4517
|
+
if (fallbackResult.passed) {
|
|
4518
|
+
effectiveGatePassed = true;
|
|
4519
|
+
effectiveGateSource = 'fallback-chain';
|
|
4520
|
+
matchedFallbackProfile = fallbackProfile;
|
|
4521
|
+
break;
|
|
4522
|
+
}
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
summary.program_gate_fallback = summary.program_gate_fallbacks.length > 0
|
|
4526
|
+
? summary.program_gate_fallbacks[0]
|
|
4527
|
+
: null;
|
|
4528
|
+
summary.program_gate_effective = {
|
|
4529
|
+
passed: effectiveGatePassed,
|
|
4530
|
+
source: effectiveGateSource,
|
|
4531
|
+
primary_passed: Boolean(summary.program_gate && summary.program_gate.passed),
|
|
4532
|
+
fallback_profile: matchedFallbackProfile,
|
|
4533
|
+
fallback_chain: gateFallbackChain,
|
|
4534
|
+
fallback_passed: matchedFallbackProfile !== null,
|
|
4535
|
+
attempted_fallback_count: summary.program_gate_fallbacks.length
|
|
4536
|
+
};
|
|
4537
|
+
|
|
4538
|
+
if (
|
|
4539
|
+
enableAutoRemediation &&
|
|
4540
|
+
(
|
|
4541
|
+
!summary.program_gate_effective.passed ||
|
|
4542
|
+
isSpecSessionBudgetHardFailure(summary) ||
|
|
4543
|
+
isSpecSessionGrowthGuardHardFailure(summary)
|
|
4544
|
+
)
|
|
4545
|
+
) {
|
|
4546
|
+
summary.program_gate_auto_remediation = await applyProgramGateAutoRemediation(summary, {
|
|
4547
|
+
projectPath,
|
|
4548
|
+
options
|
|
4549
|
+
});
|
|
4550
|
+
}
|
|
4551
|
+
|
|
4552
|
+
return summary;
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
function hasRecoverableProgramGoals(summary) {
|
|
4556
|
+
const failedStatuses = getBatchFailureStatusSet();
|
|
4557
|
+
const results = Array.isArray(summary && summary.results) ? summary.results : [];
|
|
4558
|
+
return results.some(item => failedStatuses.has(`${item && item.status ? item.status : ''}`.trim().toLowerCase()));
|
|
4559
|
+
}
|
|
4560
|
+
|
|
4561
|
+
function buildProgramAnomalyGovernancePatch(summary, anomalies, options = {}) {
|
|
4562
|
+
const sourceAnomalies = Array.isArray(anomalies) ? anomalies : [];
|
|
4563
|
+
const highAnomalies = sourceAnomalies.filter(item => `${item && item.severity ? item.severity : ''}`.trim().toLowerCase() === 'high');
|
|
4564
|
+
const patch = {};
|
|
4565
|
+
const reasons = [];
|
|
4566
|
+
|
|
4567
|
+
const anomalyTypes = new Set(highAnomalies.map(item => `${item && item.type ? item.type : ''}`.trim().toLowerCase()));
|
|
4568
|
+
if (anomalyTypes.has('success-rate-drop')) {
|
|
4569
|
+
const currentRetryRounds = normalizeBatchRetryRounds(options.batchRetryRounds);
|
|
4570
|
+
patch.batchRetryRounds = Math.min(5, Math.max(1, currentRetryRounds + 1));
|
|
4571
|
+
patch.batchRetryUntilComplete = true;
|
|
4572
|
+
reasons.push('increase retry rounds due to success-rate-drop anomaly');
|
|
4573
|
+
}
|
|
4574
|
+
|
|
4575
|
+
if (anomalyTypes.has('failed-goals-spike')) {
|
|
4576
|
+
const currentParallelCandidate = options.batchParallel !== undefined && options.batchParallel !== null
|
|
4577
|
+
? options.batchParallel
|
|
4578
|
+
: (summary && summary.batch_parallel ? summary.batch_parallel : 1);
|
|
4579
|
+
const currentParallel = normalizeBatchParallel(currentParallelCandidate);
|
|
4580
|
+
if (currentParallel > 1) {
|
|
4581
|
+
patch.batchParallel = currentParallel - 1;
|
|
4582
|
+
reasons.push(`reduce batch parallel from ${currentParallel} to ${patch.batchParallel} due to failed-goals-spike`);
|
|
4583
|
+
}
|
|
4584
|
+
|
|
4585
|
+
const currentAgentBudgetCandidate = options.batchAgentBudget !== undefined && options.batchAgentBudget !== null
|
|
4586
|
+
? options.batchAgentBudget
|
|
4587
|
+
: (summary && summary.resource_plan ? summary.resource_plan.agent_budget : null);
|
|
4588
|
+
const currentAgentBudget = normalizeBatchAgentBudget(currentAgentBudgetCandidate);
|
|
4589
|
+
if (currentAgentBudget !== null && currentAgentBudget > 1) {
|
|
4590
|
+
patch.batchAgentBudget = currentAgentBudget - 1;
|
|
4591
|
+
reasons.push(`reduce batch agent budget from ${currentAgentBudget} to ${patch.batchAgentBudget} due to failed-goals-spike`);
|
|
4592
|
+
}
|
|
4593
|
+
}
|
|
4594
|
+
|
|
4595
|
+
if (anomalyTypes.has('spec-growth-spike')) {
|
|
4596
|
+
patch.specSessionBudgetHardFail = true;
|
|
4597
|
+
reasons.push('enable spec-session budget hard-fail due to spec-growth-spike');
|
|
4598
|
+
if (options.specSessionMaxCreated === undefined || options.specSessionMaxCreated === null) {
|
|
4599
|
+
const estimatedCreated = Number(summary && summary.spec_session_budget && summary.spec_session_budget.estimated_created) || 0;
|
|
4600
|
+
patch.specSessionMaxCreated = Math.max(1, Math.ceil(estimatedCreated * 0.8));
|
|
4601
|
+
reasons.push(`set specSessionMaxCreated=${patch.specSessionMaxCreated} due to spec-growth-spike`);
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
|
|
4605
|
+
return {
|
|
4606
|
+
patch,
|
|
4607
|
+
reasons,
|
|
4608
|
+
anomaly_count: highAnomalies.length,
|
|
4609
|
+
anomaly_types: [...anomalyTypes]
|
|
4610
|
+
};
|
|
4611
|
+
}
|
|
4612
|
+
|
|
4613
|
+
function applyProgramGovernancePatch(baseOptions, patch) {
|
|
4614
|
+
const merged = { ...baseOptions };
|
|
4615
|
+
const sourcePatch = patch && typeof patch === 'object' ? patch : {};
|
|
4616
|
+
for (const [key, value] of Object.entries(sourcePatch)) {
|
|
4617
|
+
if (value === undefined) {
|
|
4618
|
+
continue;
|
|
4619
|
+
}
|
|
4620
|
+
merged[key] = value;
|
|
4621
|
+
}
|
|
4622
|
+
return merged;
|
|
4623
|
+
}
|
|
4624
|
+
|
|
4625
|
+
function buildProgramGovernanceReplayGoalsResult(baseGoalsResult, round, summary) {
|
|
4626
|
+
const source = baseGoalsResult && typeof baseGoalsResult === 'object'
|
|
4627
|
+
? baseGoalsResult
|
|
4628
|
+
: { file: '(generated-from-goal)', goals: [] };
|
|
4629
|
+
const sourceSummary = summary && typeof summary === 'object' ? summary : {};
|
|
4630
|
+
return {
|
|
4631
|
+
...source,
|
|
4632
|
+
file: source.file || '(generated-from-goal)',
|
|
4633
|
+
resumedFromSummary: {
|
|
4634
|
+
file: sourceSummary.batch_session && sourceSummary.batch_session.file
|
|
4635
|
+
? sourceSummary.batch_session.file
|
|
4636
|
+
: '(program-governance-replay)',
|
|
4637
|
+
strategy: 'program-governance-replay',
|
|
4638
|
+
round,
|
|
4639
|
+
previous_status: sourceSummary.status || null,
|
|
4640
|
+
previous_total_goals: Number(sourceSummary.total_goals) || null,
|
|
4641
|
+
previous_processed_goals: Number(sourceSummary.processed_goals) || null
|
|
4642
|
+
}
|
|
4643
|
+
};
|
|
4644
|
+
}
|
|
4645
|
+
|
|
4646
|
+
async function runProgramGovernanceLoop(context = {}) {
|
|
4647
|
+
let summary = context.summary && typeof context.summary === 'object' ? context.summary : {};
|
|
4648
|
+
const projectPath = context.projectPath || process.cwd();
|
|
4649
|
+
const baseProgramOptions = context.programOptions && typeof context.programOptions === 'object'
|
|
4650
|
+
? context.programOptions
|
|
4651
|
+
: {};
|
|
4652
|
+
const baseGoalsResult = context.baseGoalsResult && typeof context.baseGoalsResult === 'object'
|
|
4653
|
+
? context.baseGoalsResult
|
|
4654
|
+
: { file: '(generated-from-goal)', goals: [] };
|
|
4655
|
+
const enabled = Boolean(context.enabled);
|
|
4656
|
+
const maxRounds = normalizeProgramGovernMaxRounds(context.maxRounds);
|
|
4657
|
+
const maxDurationMinutes = normalizeProgramGovernMaxMinutes(context.maxMinutes);
|
|
4658
|
+
const maxDurationMs = maxDurationMinutes * 60 * 1000;
|
|
4659
|
+
const anomalyEnabled = context.anomalyEnabled !== false;
|
|
4660
|
+
const anomalyWeeks = normalizeProgramGovernAnomalyWeeks(context.anomalyWeeks);
|
|
4661
|
+
const anomalyPeriod = normalizeAutoKpiTrendPeriod(context.anomalyPeriod);
|
|
4662
|
+
const governUseAction = normalizeProgramGovernUseAction(context.governUseAction);
|
|
4663
|
+
const governAutoActionEnabled = context.governAutoActionEnabled !== false;
|
|
4664
|
+
const governActionEnabled = governAutoActionEnabled || governUseAction !== null;
|
|
4665
|
+
const programGatePolicy = resolveProgramGatePolicy(context.programGatePolicy || {});
|
|
4666
|
+
const gateFallbackChain = Array.isArray(context.gateFallbackChain) ? context.gateFallbackChain : [];
|
|
4667
|
+
const recoveryMemoryScope = context.recoveryMemoryScope || null;
|
|
4668
|
+
const normalizedRecoveryScope = normalizeRecoveryMemoryToken(recoveryMemoryScope || '') || 'default-scope';
|
|
4669
|
+
const recoverResumeStrategy = normalizeResumeStrategy(context.recoverResumeStrategy || 'pending');
|
|
4670
|
+
const recoverMaxRounds = normalizeRecoverMaxRounds(context.recoverMaxRounds);
|
|
4671
|
+
const recoverMaxMinutes = normalizeRecoverMaxMinutes(
|
|
4672
|
+
context.recoverMaxMinutes,
|
|
4673
|
+
'--program-recover-max-minutes'
|
|
4674
|
+
);
|
|
4675
|
+
const recoverMaxDurationMs = recoverMaxMinutes === null ? null : recoverMaxMinutes * 60 * 1000;
|
|
4676
|
+
const governanceStartedAt = Date.now();
|
|
4677
|
+
const history = [];
|
|
4678
|
+
let exhausted = false;
|
|
4679
|
+
let stopReason = enabled ? 'stable' : 'disabled';
|
|
4680
|
+
let settled = false;
|
|
4681
|
+
|
|
4682
|
+
if (!enabled) {
|
|
4683
|
+
return {
|
|
4684
|
+
summary,
|
|
4685
|
+
governance: {
|
|
4686
|
+
enabled: false,
|
|
4687
|
+
anomaly_enabled: anomalyEnabled,
|
|
4688
|
+
anomaly_weeks: anomalyWeeks,
|
|
4689
|
+
anomaly_period: anomalyPeriod,
|
|
4690
|
+
auto_action_enabled: governAutoActionEnabled,
|
|
4691
|
+
action_selection_enabled: false,
|
|
4692
|
+
pinned_action_index: governUseAction,
|
|
4693
|
+
max_rounds: maxRounds,
|
|
4694
|
+
max_minutes: maxDurationMinutes,
|
|
4695
|
+
performed_rounds: 0,
|
|
4696
|
+
converged: Boolean(
|
|
4697
|
+
summary &&
|
|
4698
|
+
summary.program_gate_effective &&
|
|
4699
|
+
summary.program_gate_effective.passed &&
|
|
4700
|
+
!isSpecSessionBudgetHardFailure(summary) &&
|
|
4701
|
+
!isSpecSessionGrowthGuardHardFailure(summary)
|
|
4702
|
+
),
|
|
4703
|
+
exhausted: false,
|
|
4704
|
+
stop_reason: 'disabled',
|
|
4705
|
+
history: []
|
|
4706
|
+
}
|
|
4707
|
+
};
|
|
4708
|
+
}
|
|
4709
|
+
|
|
4710
|
+
for (let round = 1; round <= maxRounds; round += 1) {
|
|
4711
|
+
const elapsedBeforeRound = Date.now() - governanceStartedAt;
|
|
4712
|
+
if (elapsedBeforeRound >= maxDurationMs) {
|
|
4713
|
+
exhausted = true;
|
|
4714
|
+
stopReason = 'time-budget-exhausted';
|
|
4715
|
+
break;
|
|
4716
|
+
}
|
|
4717
|
+
|
|
4718
|
+
let trendResult = null;
|
|
4719
|
+
let anomalies = [];
|
|
4720
|
+
if (anomalyEnabled) {
|
|
4721
|
+
trendResult = await buildAutoKpiTrend(projectPath, {
|
|
4722
|
+
weeks: anomalyWeeks,
|
|
4723
|
+
mode: 'program',
|
|
4724
|
+
period: anomalyPeriod
|
|
4725
|
+
});
|
|
4726
|
+
anomalies = Array.isArray(trendResult.anomalies) ? trendResult.anomalies : [];
|
|
4727
|
+
summary.program_kpi_trend = {
|
|
4728
|
+
generated_at: trendResult.generated_at,
|
|
4729
|
+
weeks: trendResult.weeks,
|
|
4730
|
+
period_unit: trendResult.period_unit,
|
|
4731
|
+
total_runs: trendResult.total_runs,
|
|
4732
|
+
overall: trendResult.overall,
|
|
4733
|
+
anomaly_detection: trendResult.anomaly_detection || null
|
|
4734
|
+
};
|
|
4735
|
+
summary.program_kpi_anomalies = anomalies;
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4738
|
+
const gateFailed = Boolean(
|
|
4739
|
+
!summary.program_gate_effective ||
|
|
4740
|
+
!summary.program_gate_effective.passed ||
|
|
4741
|
+
isSpecSessionBudgetHardFailure(summary) ||
|
|
4742
|
+
isSpecSessionGrowthGuardHardFailure(summary)
|
|
4743
|
+
);
|
|
4744
|
+
const highSeverityAnomalies = anomalies.filter(item => `${item && item.severity ? item.severity : ''}`.trim().toLowerCase() === 'high');
|
|
4745
|
+
const anomalyFailed = anomalyEnabled && highSeverityAnomalies.length > 0;
|
|
4746
|
+
if (!gateFailed && !anomalyFailed) {
|
|
4747
|
+
stopReason = 'stable';
|
|
4748
|
+
settled = true;
|
|
4749
|
+
break;
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4752
|
+
const gatePatch = summary && summary.program_gate_auto_remediation && summary.program_gate_auto_remediation.next_run_patch
|
|
4753
|
+
? summary.program_gate_auto_remediation.next_run_patch
|
|
4754
|
+
: {};
|
|
4755
|
+
const anomalyPatch = buildProgramAnomalyGovernancePatch(summary, highSeverityAnomalies, baseProgramOptions);
|
|
4756
|
+
let governanceActionSelection = null;
|
|
4757
|
+
let governanceActionPatch = {};
|
|
4758
|
+
if (governActionEnabled) {
|
|
4759
|
+
const recoveryMemory = await loadCloseLoopRecoveryMemory(projectPath);
|
|
4760
|
+
const recoverySignature = buildRecoveryMemorySignature(summary, {
|
|
4761
|
+
scope: normalizedRecoveryScope
|
|
4762
|
+
});
|
|
4763
|
+
const recoveryMemoryEntry = getRecoveryMemoryEntry(recoveryMemory.payload, recoverySignature);
|
|
4764
|
+
governanceActionSelection = resolveRecoveryActionSelection(summary, governUseAction, {
|
|
4765
|
+
recoveryMemoryEntry,
|
|
4766
|
+
optionLabel: '--program-govern-use-action'
|
|
4767
|
+
});
|
|
4768
|
+
governanceActionPatch = governanceActionSelection &&
|
|
4769
|
+
governanceActionSelection.appliedPatch &&
|
|
4770
|
+
typeof governanceActionSelection.appliedPatch === 'object'
|
|
4771
|
+
? governanceActionSelection.appliedPatch
|
|
4772
|
+
: {};
|
|
4773
|
+
}
|
|
4774
|
+
const roundPatch = {
|
|
4775
|
+
...(governanceActionPatch && typeof governanceActionPatch === 'object' ? governanceActionPatch : {}),
|
|
4776
|
+
...(anomalyPatch.patch || {}),
|
|
4777
|
+
...(gatePatch && typeof gatePatch === 'object' ? gatePatch : {})
|
|
4778
|
+
};
|
|
4779
|
+
if (Object.keys(roundPatch).length === 0) {
|
|
4780
|
+
stopReason = 'no-actionable-patch';
|
|
4781
|
+
history.push({
|
|
4782
|
+
round,
|
|
4783
|
+
status_before: summary.status,
|
|
4784
|
+
status_after: summary.status,
|
|
4785
|
+
trigger: {
|
|
4786
|
+
gate_failed: gateFailed,
|
|
4787
|
+
anomaly_failed: anomalyFailed,
|
|
4788
|
+
anomaly_count: highSeverityAnomalies.length
|
|
4789
|
+
},
|
|
4790
|
+
selected_action_index: governanceActionSelection ? governanceActionSelection.selectedIndex : null,
|
|
4791
|
+
selected_action: governanceActionSelection && governanceActionSelection.selectedAction
|
|
4792
|
+
? governanceActionSelection.selectedAction.action
|
|
4793
|
+
: null,
|
|
4794
|
+
selected_action_priority: governanceActionSelection && governanceActionSelection.selectedAction
|
|
4795
|
+
? governanceActionSelection.selectedAction.priority
|
|
4796
|
+
: null,
|
|
4797
|
+
action_selection_source: governanceActionSelection ? governanceActionSelection.selectionSource : null,
|
|
4798
|
+
action_selection_explain: governanceActionSelection ? governanceActionSelection.selectionExplain || null : null,
|
|
4799
|
+
execution_mode: 'none',
|
|
4800
|
+
applied_patch: null,
|
|
4801
|
+
notes: [
|
|
4802
|
+
'No actionable governance patch generated.'
|
|
4803
|
+
]
|
|
4804
|
+
});
|
|
4805
|
+
break;
|
|
4806
|
+
}
|
|
4807
|
+
|
|
4808
|
+
const roundOptions = applyProgramGovernancePatch(baseProgramOptions, roundPatch);
|
|
4809
|
+
roundOptions.out = null;
|
|
4810
|
+
roundOptions.programKpiOut = null;
|
|
4811
|
+
roundOptions.programAuditOut = null;
|
|
4812
|
+
|
|
4813
|
+
const statusBefore = summary.status;
|
|
4814
|
+
const failedGoalsBefore = Number(summary.failed_goals) || 0;
|
|
4815
|
+
const selectedGovernanceActionIndex = governanceActionSelection ? governanceActionSelection.selectedIndex : null;
|
|
4816
|
+
let executionMode = 'program-replay';
|
|
4817
|
+
let roundSummary = null;
|
|
4818
|
+
if (hasRecoverableProgramGoals(summary)) {
|
|
4819
|
+
executionMode = 'recover-cycle';
|
|
4820
|
+
const roundSourceSummary = summary.batch_session && summary.batch_session.file
|
|
4821
|
+
? await loadCloseLoopBatchSummaryPayload(projectPath, summary.batch_session.file)
|
|
4822
|
+
: {
|
|
4823
|
+
file: '(program-governance-derived-summary)',
|
|
4824
|
+
payload: summary
|
|
4825
|
+
};
|
|
4826
|
+
const recoveryResult = await executeCloseLoopRecoveryCycle({
|
|
4827
|
+
projectPath,
|
|
4828
|
+
sourceSummary: roundSourceSummary,
|
|
4829
|
+
baseOptions: {
|
|
4830
|
+
...roundOptions,
|
|
4831
|
+
useAction: selectedGovernanceActionIndex || context.programRecoverUseAction
|
|
4832
|
+
},
|
|
4833
|
+
recoverAutonomousEnabled: true,
|
|
4834
|
+
resumeStrategy: recoverResumeStrategy,
|
|
4835
|
+
recoverUntilComplete: true,
|
|
4836
|
+
recoverMaxRounds,
|
|
4837
|
+
recoverMaxDurationMs,
|
|
4838
|
+
recoveryMemoryScope,
|
|
4839
|
+
actionCandidate: selectedGovernanceActionIndex || context.programRecoverUseAction
|
|
4840
|
+
});
|
|
4841
|
+
roundSummary = mergeProgramRecoveryIntoProgramSummary(summary, recoveryResult.summary, {
|
|
4842
|
+
enabled: true,
|
|
4843
|
+
triggered: true,
|
|
4844
|
+
governance_round: round,
|
|
4845
|
+
recover_until_complete: true,
|
|
4846
|
+
source: 'governance-recover-cycle'
|
|
4847
|
+
});
|
|
4848
|
+
roundSummary.resource_plan = recoveryResult.summary && recoveryResult.summary.resource_plan
|
|
4849
|
+
? recoveryResult.summary.resource_plan
|
|
4850
|
+
: roundSummary.resource_plan;
|
|
4851
|
+
roundSummary.batch_parallel = Number(recoveryResult.summary && recoveryResult.summary.batch_parallel) || roundSummary.batch_parallel;
|
|
4852
|
+
} else {
|
|
4853
|
+
const replayGoalsResult = buildProgramGovernanceReplayGoalsResult(baseGoalsResult, round, summary);
|
|
4854
|
+
const replaySummary = await executeCloseLoopBatch(
|
|
4855
|
+
replayGoalsResult,
|
|
4856
|
+
roundOptions,
|
|
4857
|
+
projectPath,
|
|
4858
|
+
'auto-close-loop-program'
|
|
4859
|
+
);
|
|
4860
|
+
roundSummary = {
|
|
4861
|
+
...replaySummary,
|
|
4862
|
+
auto_recovery: summary && summary.auto_recovery ? summary.auto_recovery : null
|
|
4863
|
+
};
|
|
4864
|
+
}
|
|
4865
|
+
|
|
4866
|
+
roundSummary.program_kpi = buildProgramKpiSnapshot(roundSummary);
|
|
4867
|
+
roundSummary.program_diagnostics = buildProgramDiagnostics(roundSummary);
|
|
4868
|
+
roundSummary.program_coordination = buildProgramCoordinationSnapshot(roundSummary);
|
|
4869
|
+
await applyProgramGateOutcome(roundSummary, {
|
|
4870
|
+
projectPath,
|
|
4871
|
+
options: roundOptions,
|
|
4872
|
+
programGatePolicy,
|
|
4873
|
+
gateFallbackChain,
|
|
4874
|
+
enableAutoRemediation: context.programGateAutoRemediate !== false
|
|
4875
|
+
});
|
|
4876
|
+
|
|
4877
|
+
const failedGoalsAfter = Number(roundSummary.failed_goals) || 0;
|
|
4878
|
+
history.push({
|
|
4879
|
+
round,
|
|
4880
|
+
status_before: statusBefore,
|
|
4881
|
+
status_after: roundSummary.status,
|
|
4882
|
+
trigger: {
|
|
4883
|
+
gate_failed: gateFailed,
|
|
4884
|
+
anomaly_failed: anomalyFailed,
|
|
4885
|
+
anomaly_count: highSeverityAnomalies.length
|
|
4886
|
+
},
|
|
4887
|
+
selected_action_index: selectedGovernanceActionIndex,
|
|
4888
|
+
selected_action: governanceActionSelection && governanceActionSelection.selectedAction
|
|
4889
|
+
? governanceActionSelection.selectedAction.action
|
|
4890
|
+
: null,
|
|
4891
|
+
selected_action_priority: governanceActionSelection && governanceActionSelection.selectedAction
|
|
4892
|
+
? governanceActionSelection.selectedAction.priority
|
|
4893
|
+
: null,
|
|
4894
|
+
action_selection_source: governanceActionSelection ? governanceActionSelection.selectionSource : null,
|
|
4895
|
+
action_selection_explain: governanceActionSelection ? governanceActionSelection.selectionExplain || null : null,
|
|
4896
|
+
execution_mode: executionMode,
|
|
4897
|
+
applied_patch: roundPatch,
|
|
4898
|
+
patch_reasons: [
|
|
4899
|
+
...(governanceActionSelection && governanceActionSelection.selectionExplain
|
|
4900
|
+
? [`governance-action: ${governanceActionSelection.selectionExplain.reason}`]
|
|
4901
|
+
: []),
|
|
4902
|
+
...(Array.isArray(anomalyPatch.reasons) ? anomalyPatch.reasons : []),
|
|
4903
|
+
...(summary.program_gate_auto_remediation && Array.isArray(summary.program_gate_auto_remediation.reasons)
|
|
4904
|
+
? summary.program_gate_auto_remediation.reasons
|
|
4905
|
+
: [])
|
|
4906
|
+
],
|
|
4907
|
+
failed_goals_before: failedGoalsBefore,
|
|
4908
|
+
failed_goals_after: failedGoalsAfter
|
|
4909
|
+
});
|
|
4910
|
+
|
|
4911
|
+
summary = roundSummary;
|
|
4912
|
+
if (
|
|
4913
|
+
summary.program_gate_effective &&
|
|
4914
|
+
summary.program_gate_effective.passed &&
|
|
4915
|
+
!isSpecSessionBudgetHardFailure(summary) &&
|
|
4916
|
+
!isSpecSessionGrowthGuardHardFailure(summary)
|
|
4917
|
+
) {
|
|
4918
|
+
if (!anomalyEnabled) {
|
|
4919
|
+
stopReason = 'gate-stable';
|
|
4920
|
+
break;
|
|
4921
|
+
}
|
|
4922
|
+
const postTrend = await buildAutoKpiTrend(projectPath, {
|
|
4923
|
+
weeks: anomalyWeeks,
|
|
4924
|
+
mode: 'program',
|
|
4925
|
+
period: anomalyPeriod
|
|
4926
|
+
});
|
|
4927
|
+
const postAnomalies = Array.isArray(postTrend.anomalies) ? postTrend.anomalies : [];
|
|
4928
|
+
summary.program_kpi_trend = {
|
|
4929
|
+
generated_at: postTrend.generated_at,
|
|
4930
|
+
weeks: postTrend.weeks,
|
|
4931
|
+
period_unit: postTrend.period_unit,
|
|
4932
|
+
total_runs: postTrend.total_runs,
|
|
4933
|
+
overall: postTrend.overall,
|
|
4934
|
+
anomaly_detection: postTrend.anomaly_detection || null
|
|
4935
|
+
};
|
|
4936
|
+
summary.program_kpi_anomalies = postAnomalies;
|
|
4937
|
+
const hasHighPostAnomaly = postAnomalies.some(item => `${item && item.severity ? item.severity : ''}`.trim().toLowerCase() === 'high');
|
|
4938
|
+
if (!hasHighPostAnomaly) {
|
|
4939
|
+
stopReason = 'stable';
|
|
4940
|
+
settled = true;
|
|
4941
|
+
break;
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4946
|
+
if (!settled && history.length >= maxRounds && stopReason === 'stable') {
|
|
4947
|
+
stopReason = 'round-limit-reached';
|
|
4948
|
+
exhausted = true;
|
|
4949
|
+
}
|
|
4950
|
+
if (!settled && history.length >= maxRounds && stopReason !== 'stable') {
|
|
4951
|
+
exhausted = true;
|
|
4952
|
+
}
|
|
4953
|
+
|
|
4954
|
+
return {
|
|
4955
|
+
summary,
|
|
4956
|
+
governance: {
|
|
4957
|
+
enabled: true,
|
|
4958
|
+
anomaly_enabled: anomalyEnabled,
|
|
4959
|
+
anomaly_weeks: anomalyWeeks,
|
|
4960
|
+
anomaly_period: anomalyPeriod,
|
|
4961
|
+
auto_action_enabled: governAutoActionEnabled,
|
|
4962
|
+
action_selection_enabled: governActionEnabled,
|
|
4963
|
+
pinned_action_index: governUseAction,
|
|
4964
|
+
max_rounds: maxRounds,
|
|
4965
|
+
max_minutes: maxDurationMinutes,
|
|
4966
|
+
performed_rounds: history.length,
|
|
4967
|
+
converged: Boolean(
|
|
4968
|
+
summary &&
|
|
4969
|
+
summary.program_gate_effective &&
|
|
4970
|
+
summary.program_gate_effective.passed &&
|
|
4971
|
+
!isSpecSessionBudgetHardFailure(summary) &&
|
|
4972
|
+
!isSpecSessionGrowthGuardHardFailure(summary)
|
|
4973
|
+
),
|
|
4974
|
+
exhausted,
|
|
4975
|
+
stop_reason: stopReason,
|
|
4976
|
+
history
|
|
4977
|
+
}
|
|
4978
|
+
};
|
|
4979
|
+
}
|
|
4980
|
+
|
|
4424
4981
|
async function applyProgramGateAutoRemediation(summary, context = {}) {
|
|
4425
4982
|
const projectPath = context && context.projectPath ? context.projectPath : process.cwd();
|
|
4426
4983
|
const options = context && context.options && typeof context.options === 'object'
|
|
@@ -4718,6 +5275,9 @@ async function maybeWriteProgramKpi(summary, outCandidate, projectPath) {
|
|
|
4718
5275
|
program_diagnostics: summary.program_diagnostics,
|
|
4719
5276
|
program_coordination: summary.program_coordination || null,
|
|
4720
5277
|
auto_recovery: summary.auto_recovery || null,
|
|
5278
|
+
program_governance: summary.program_governance || null,
|
|
5279
|
+
program_kpi_trend: summary.program_kpi_trend || null,
|
|
5280
|
+
program_kpi_anomalies: Array.isArray(summary.program_kpi_anomalies) ? summary.program_kpi_anomalies : [],
|
|
4721
5281
|
goal_input_guard: summary.goal_input_guard || null,
|
|
4722
5282
|
spec_session_budget: summary.spec_session_budget || null,
|
|
4723
5283
|
spec_session_growth_guard: summary.spec_session_growth_guard || null,
|
|
@@ -4765,6 +5325,9 @@ async function maybeWriteProgramAudit(summary, outCandidate, projectPath) {
|
|
|
4765
5325
|
program_gate_fallbacks: Array.isArray(summary && summary.program_gate_fallbacks) ? summary.program_gate_fallbacks : [],
|
|
4766
5326
|
program_gate_effective: summary && summary.program_gate_effective ? summary.program_gate_effective : null,
|
|
4767
5327
|
auto_recovery: summary && summary.auto_recovery ? summary.auto_recovery : null,
|
|
5328
|
+
program_governance: summary && summary.program_governance ? summary.program_governance : null,
|
|
5329
|
+
program_kpi_trend: summary && summary.program_kpi_trend ? summary.program_kpi_trend : null,
|
|
5330
|
+
program_kpi_anomalies: Array.isArray(summary && summary.program_kpi_anomalies) ? summary.program_kpi_anomalies : [],
|
|
4768
5331
|
recovery_cycle: summary && summary.recovery_cycle ? summary.recovery_cycle : null,
|
|
4769
5332
|
recovery_plan: summary && summary.recovery_plan ? summary.recovery_plan : null,
|
|
4770
5333
|
recovery_memory: summary && summary.recovery_memory ? summary.recovery_memory : null,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kiro-spec-engine",
|
|
3
|
-
"version": "1.47.
|
|
3
|
+
"version": "1.47.1",
|
|
4
4
|
"description": "kiro-spec-engine (kse) - A CLI tool and npm package for spec-driven development with AI coding assistants. NOT the Kiro IDE desktop application.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|