scene-capability-engine 3.6.32 → 3.6.36
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 +86 -1
- package/README.md +119 -122
- package/README.zh.md +123 -121
- package/bin/scene-capability-engine.js +11 -0
- package/docs/README.md +21 -32
- package/docs/auto-refactor-index.md +384 -0
- package/docs/command-reference.md +94 -2
- package/docs/magicball-adaptation-task-checklist-v1.md +385 -0
- package/docs/magicball-app-bundle-sqlite-and-command-draft.md +539 -0
- package/docs/magicball-capability-iteration-api.md +2 -0
- package/docs/magicball-capability-iteration-ui.md +2 -0
- package/docs/magicball-capability-library.md +2 -0
- package/docs/magicball-cli-invocation-examples.md +336 -0
- package/docs/magicball-frontend-state-and-command-mapping.md +244 -0
- package/docs/magicball-integration-doc-index.md +137 -0
- package/docs/magicball-integration-issue-tracker.md +218 -0
- package/docs/magicball-mode-home-and-ontology-empty-state-playbook.md +249 -0
- package/docs/magicball-sce-adaptation-guide.md +203 -0
- package/docs/magicball-three-mode-alignment-plan.md +551 -0
- package/docs/magicball-ui-surface-checklist.md +126 -0
- package/docs/magicball-write-auth-adaptation-guide.md +328 -0
- package/docs/refactor-completion-roadmap.md +116 -0
- package/docs/zh/README.md +27 -30
- package/docs/zh/refactor-completion-roadmap.md +116 -0
- package/lib/app/registry-config.js +73 -0
- package/lib/app/registry-sync-service.js +228 -0
- package/lib/auto/archive-schema-service.js +276 -0
- package/lib/auto/archive-summary.js +60 -0
- package/lib/auto/batch-goal-input-service.js +543 -0
- package/lib/auto/batch-output.js +201 -0
- package/lib/auto/batch-summary-storage-service.js +110 -0
- package/lib/auto/close-loop-batch-service.js +116 -0
- package/lib/auto/close-loop-controller-service.js +287 -0
- package/lib/auto/close-loop-program-service.js +283 -0
- package/lib/auto/close-loop-recovery-service.js +191 -0
- package/lib/auto/close-loop-session-storage-service.js +50 -0
- package/lib/auto/controller-lock-service.js +55 -0
- package/lib/auto/controller-output.js +32 -0
- package/lib/auto/controller-queue-service.js +127 -0
- package/lib/auto/controller-session-storage-service.js +105 -0
- package/lib/auto/governance-advisory-service.js +208 -0
- package/lib/auto/governance-close-loop-service.js +411 -0
- package/lib/auto/governance-maintenance-presenter.js +162 -0
- package/lib/auto/governance-maintenance-service.js +112 -0
- package/lib/auto/governance-session-presenter.js +70 -0
- package/lib/auto/governance-session-storage-service.js +198 -0
- package/lib/auto/governance-signals.js +139 -0
- package/lib/auto/governance-stats-presenter.js +337 -0
- package/lib/auto/governance-stats-service.js +115 -0
- package/lib/auto/governance-summary.js +703 -0
- package/lib/auto/handoff-capability-matrix-service.js +281 -0
- package/lib/auto/handoff-evidence-review-service.js +251 -0
- package/lib/auto/handoff-release-evidence-service.js +190 -0
- package/lib/auto/handoff-release-gate-history-loaders-service.js +502 -0
- package/lib/auto/handoff-release-gate-history-service.js +257 -0
- package/lib/auto/handoff-reporting-service.js +1407 -0
- package/lib/auto/handoff-run-service.js +486 -0
- package/lib/auto/handoff-snapshots-service.js +645 -0
- package/lib/auto/observability-service.js +132 -0
- package/lib/auto/output-writer.js +34 -0
- package/lib/auto/program-auto-remediation-service.js +130 -0
- package/lib/auto/program-diagnostics.js +138 -0
- package/lib/auto/program-governance-helpers.js +306 -0
- package/lib/auto/program-governance-loop-service.js +413 -0
- package/lib/auto/program-output.js +106 -0
- package/lib/auto/program-summary.js +183 -0
- package/lib/auto/recovery-memory-service.js +684 -0
- package/lib/auto/recovery-selection-service.js +52 -0
- package/lib/auto/retention-policy.js +98 -0
- package/lib/auto/session-persistence-service.js +106 -0
- package/lib/auto/session-presenter.js +105 -0
- package/lib/auto/session-prune-service.js +190 -0
- package/lib/auto/session-query-service.js +249 -0
- package/lib/auto/spec-protection.js +141 -0
- package/lib/commands/app.js +911 -0
- package/lib/commands/assurance.js +212 -0
- package/lib/commands/auto.js +1091 -11063
- package/lib/commands/mode.js +321 -0
- package/lib/commands/ontology.js +415 -0
- package/lib/commands/pm.js +422 -0
- package/lib/ontology/seed-profiles.js +160 -0
- package/lib/state/sce-state-store.js +3369 -1200
- package/package.json +1 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
async function buildAutoObservabilitySnapshot(projectPath, options = {}, dependencies = {}) {
|
|
2
|
+
const {
|
|
3
|
+
normalizeStatsWindowDays,
|
|
4
|
+
normalizeStatusFilter,
|
|
5
|
+
statsCloseLoopSessions,
|
|
6
|
+
statsCloseLoopBatchSummarySessions,
|
|
7
|
+
statsCloseLoopControllerSessions,
|
|
8
|
+
statsGovernanceCloseLoopSessions,
|
|
9
|
+
buildAutoGovernanceStats,
|
|
10
|
+
buildAutoKpiTrend,
|
|
11
|
+
calculatePercent,
|
|
12
|
+
schemaVersion,
|
|
13
|
+
now = () => new Date().toISOString()
|
|
14
|
+
} = dependencies;
|
|
15
|
+
|
|
16
|
+
const days = normalizeStatsWindowDays(options.days);
|
|
17
|
+
const statusFilter = normalizeStatusFilter(options.status);
|
|
18
|
+
const normalizedStatsOptions = {
|
|
19
|
+
days,
|
|
20
|
+
status: statusFilter.length > 0 ? statusFilter.join(',') : undefined
|
|
21
|
+
};
|
|
22
|
+
const normalizedTrendOptions = {
|
|
23
|
+
weeks: options.weeks,
|
|
24
|
+
mode: options.trendMode,
|
|
25
|
+
period: options.trendPeriod
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const [
|
|
29
|
+
sessionStats,
|
|
30
|
+
batchStats,
|
|
31
|
+
controllerStats,
|
|
32
|
+
governanceSessionStats,
|
|
33
|
+
governanceHealth,
|
|
34
|
+
trend
|
|
35
|
+
] = await Promise.all([
|
|
36
|
+
statsCloseLoopSessions(projectPath, normalizedStatsOptions),
|
|
37
|
+
statsCloseLoopBatchSummarySessions(projectPath, normalizedStatsOptions),
|
|
38
|
+
statsCloseLoopControllerSessions(projectPath, normalizedStatsOptions),
|
|
39
|
+
statsGovernanceCloseLoopSessions(projectPath, normalizedStatsOptions),
|
|
40
|
+
buildAutoGovernanceStats(projectPath, normalizedStatsOptions),
|
|
41
|
+
buildAutoKpiTrend(projectPath, normalizedTrendOptions)
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
const totalSessions =
|
|
45
|
+
(Number(sessionStats.total_sessions) || 0) +
|
|
46
|
+
(Number(batchStats.total_sessions) || 0) +
|
|
47
|
+
(Number(controllerStats.total_sessions) || 0) +
|
|
48
|
+
(Number(governanceSessionStats.total_sessions) || 0);
|
|
49
|
+
const completedSessions =
|
|
50
|
+
(Number(sessionStats.completed_sessions) || 0) +
|
|
51
|
+
(Number(batchStats.completed_sessions) || 0) +
|
|
52
|
+
(Number(controllerStats.completed_sessions) || 0) +
|
|
53
|
+
(Number(governanceSessionStats.completed_sessions) || 0);
|
|
54
|
+
const failedSessions =
|
|
55
|
+
(Number(sessionStats.failed_sessions) || 0) +
|
|
56
|
+
(Number(batchStats.failed_sessions) || 0) +
|
|
57
|
+
(Number(controllerStats.failed_sessions) || 0) +
|
|
58
|
+
(Number(governanceSessionStats.failed_sessions) || 0);
|
|
59
|
+
const governanceWeeklyOpsStop = governanceSessionStats &&
|
|
60
|
+
governanceSessionStats.release_gate &&
|
|
61
|
+
governanceSessionStats.release_gate.weekly_ops_stop &&
|
|
62
|
+
typeof governanceSessionStats.release_gate.weekly_ops_stop === 'object'
|
|
63
|
+
? governanceSessionStats.release_gate.weekly_ops_stop
|
|
64
|
+
: null;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
mode: 'auto-observability-snapshot',
|
|
68
|
+
generated_at: now(),
|
|
69
|
+
schema_version: schemaVersion,
|
|
70
|
+
criteria: {
|
|
71
|
+
days,
|
|
72
|
+
status_filter: statusFilter,
|
|
73
|
+
trend_weeks: trend.weeks,
|
|
74
|
+
trend_mode: trend.mode,
|
|
75
|
+
trend_period: trend.period_unit
|
|
76
|
+
},
|
|
77
|
+
highlights: {
|
|
78
|
+
total_sessions: totalSessions,
|
|
79
|
+
completed_sessions: completedSessions,
|
|
80
|
+
failed_sessions: failedSessions,
|
|
81
|
+
completion_rate_percent: calculatePercent(completedSessions, totalSessions),
|
|
82
|
+
failure_rate_percent: calculatePercent(failedSessions, totalSessions),
|
|
83
|
+
governance_risk_level: governanceHealth && governanceHealth.health
|
|
84
|
+
? governanceHealth.health.risk_level
|
|
85
|
+
: 'unknown',
|
|
86
|
+
governance_weekly_ops_stop_sessions: Number(
|
|
87
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.sessions
|
|
88
|
+
) || 0,
|
|
89
|
+
governance_weekly_ops_stop_session_rate_percent: Number(
|
|
90
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.session_rate_percent
|
|
91
|
+
) || 0,
|
|
92
|
+
governance_weekly_ops_high_pressure_sessions: Number(
|
|
93
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.high_pressure_sessions
|
|
94
|
+
) || 0,
|
|
95
|
+
governance_weekly_ops_high_pressure_rate_percent: Number(
|
|
96
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.high_pressure_session_rate_percent
|
|
97
|
+
) || 0,
|
|
98
|
+
governance_weekly_ops_config_warning_positive_sessions: Number(
|
|
99
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.config_warning_positive_sessions
|
|
100
|
+
) || 0,
|
|
101
|
+
governance_weekly_ops_auth_tier_pressure_sessions: Number(
|
|
102
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.auth_tier_pressure_sessions
|
|
103
|
+
) || 0,
|
|
104
|
+
governance_weekly_ops_dialogue_authorization_pressure_sessions: Number(
|
|
105
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.dialogue_authorization_pressure_sessions
|
|
106
|
+
) || 0,
|
|
107
|
+
governance_weekly_ops_runtime_block_rate_high_sessions: Number(
|
|
108
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.runtime_block_rate_high_sessions
|
|
109
|
+
) || 0,
|
|
110
|
+
governance_weekly_ops_runtime_ui_mode_violation_high_sessions: Number(
|
|
111
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.runtime_ui_mode_violation_high_sessions
|
|
112
|
+
) || 0,
|
|
113
|
+
governance_weekly_ops_runtime_ui_mode_violation_total_sum: Number(
|
|
114
|
+
governanceWeeklyOpsStop && governanceWeeklyOpsStop.runtime_ui_mode_violation_total_sum
|
|
115
|
+
) || 0,
|
|
116
|
+
kpi_anomaly_count: Array.isArray(trend.anomalies) ? trend.anomalies.length : 0
|
|
117
|
+
},
|
|
118
|
+
snapshots: {
|
|
119
|
+
close_loop_session: sessionStats,
|
|
120
|
+
batch_session: batchStats,
|
|
121
|
+
controller_session: controllerStats,
|
|
122
|
+
governance_session: governanceSessionStats,
|
|
123
|
+
governance_weekly_ops_stop: governanceWeeklyOpsStop,
|
|
124
|
+
governance_health: governanceHealth,
|
|
125
|
+
kpi_trend: trend
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = {
|
|
131
|
+
buildAutoObservabilitySnapshot
|
|
132
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
async function maybeWriteOutput(result, outCandidate, projectPath, dependencies = {}) {
|
|
2
|
+
const pathModule = dependencies.pathModule;
|
|
3
|
+
const fs = dependencies.fs;
|
|
4
|
+
if (!outCandidate) {
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const outputPath = pathModule.isAbsolute(outCandidate)
|
|
9
|
+
? outCandidate
|
|
10
|
+
: pathModule.join(projectPath, outCandidate);
|
|
11
|
+
await fs.ensureDir(pathModule.dirname(outputPath));
|
|
12
|
+
await fs.writeJson(outputPath, result, { spaces: 2 });
|
|
13
|
+
result.output_file = outputPath;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function maybeWriteTextOutput(result, content, outCandidate, projectPath, dependencies = {}) {
|
|
17
|
+
const pathModule = dependencies.pathModule;
|
|
18
|
+
const fs = dependencies.fs;
|
|
19
|
+
if (!outCandidate) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const outputPath = pathModule.isAbsolute(outCandidate)
|
|
24
|
+
? outCandidate
|
|
25
|
+
: pathModule.join(projectPath, outCandidate);
|
|
26
|
+
await fs.ensureDir(pathModule.dirname(outputPath));
|
|
27
|
+
await fs.writeFile(outputPath, content, 'utf8');
|
|
28
|
+
result.output_file = outputPath;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
maybeWriteOutput,
|
|
33
|
+
maybeWriteTextOutput
|
|
34
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
async function applyProgramGateAutoRemediation(summary, context = {}, dependencies = {}) {
|
|
2
|
+
const {
|
|
3
|
+
collectSpecNamesFromBatchSummary,
|
|
4
|
+
pruneSpecSessions,
|
|
5
|
+
readSpecSessionEntries,
|
|
6
|
+
now = () => new Date().toISOString(),
|
|
7
|
+
cwd = () => process.cwd()
|
|
8
|
+
} = dependencies;
|
|
9
|
+
|
|
10
|
+
const projectPath = context && context.projectPath ? context.projectPath : cwd();
|
|
11
|
+
const options = context && context.options && typeof context.options === 'object'
|
|
12
|
+
? context.options
|
|
13
|
+
: {};
|
|
14
|
+
const gate = summary && summary.program_gate && typeof summary.program_gate === 'object'
|
|
15
|
+
? summary.program_gate
|
|
16
|
+
: null;
|
|
17
|
+
const policy = gate && gate.policy && typeof gate.policy === 'object' ? gate.policy : {};
|
|
18
|
+
const reasons = Array.isArray(gate && gate.reasons) ? gate.reasons : [];
|
|
19
|
+
const actions = [];
|
|
20
|
+
const nextRunPatch = {};
|
|
21
|
+
|
|
22
|
+
const maxAgentBudget = Number(policy.max_agent_budget);
|
|
23
|
+
if (
|
|
24
|
+
reasons.some(reason => `${reason || ''}`.includes('agent_budget')) &&
|
|
25
|
+
Number.isFinite(maxAgentBudget) &&
|
|
26
|
+
maxAgentBudget > 0
|
|
27
|
+
) {
|
|
28
|
+
const currentAgentBudget = Number(options.batchAgentBudget || (summary && summary.batch_parallel) || 0);
|
|
29
|
+
nextRunPatch.batchAgentBudget = maxAgentBudget;
|
|
30
|
+
nextRunPatch.batchParallel = Math.max(1, Math.min(currentAgentBudget || maxAgentBudget, maxAgentBudget));
|
|
31
|
+
actions.push({
|
|
32
|
+
type: 'reduce-agent-budget',
|
|
33
|
+
applied: true,
|
|
34
|
+
details: `Set batchAgentBudget=${nextRunPatch.batchAgentBudget}, batchParallel=${nextRunPatch.batchParallel}.`
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const maxTotalSubSpecs = Number(policy.max_total_sub_specs);
|
|
39
|
+
if (
|
|
40
|
+
reasons.some(reason => `${reason || ''}`.includes('total_sub_specs')) &&
|
|
41
|
+
Number.isFinite(maxTotalSubSpecs) &&
|
|
42
|
+
maxTotalSubSpecs > 0
|
|
43
|
+
) {
|
|
44
|
+
const avgSubSpecs = Number(summary && summary.metrics && summary.metrics.average_sub_specs_per_goal) || 1;
|
|
45
|
+
const totalGoals = Number(summary && summary.total_goals) || 2;
|
|
46
|
+
const suggestedProgramGoals = Math.max(2, Math.min(totalGoals, Math.floor(maxTotalSubSpecs / Math.max(1, avgSubSpecs))));
|
|
47
|
+
nextRunPatch.programGoals = suggestedProgramGoals;
|
|
48
|
+
actions.push({
|
|
49
|
+
type: 'shrink-goal-width',
|
|
50
|
+
applied: true,
|
|
51
|
+
details: `Set programGoals=${suggestedProgramGoals} using max_total_sub_specs=${maxTotalSubSpecs}.`
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const maxElapsedMinutes = Number(policy.max_elapsed_minutes);
|
|
56
|
+
if (
|
|
57
|
+
reasons.some(reason => `${reason || ''}`.includes('program_elapsed_minutes')) &&
|
|
58
|
+
Number.isFinite(maxElapsedMinutes) &&
|
|
59
|
+
maxElapsedMinutes > 0
|
|
60
|
+
) {
|
|
61
|
+
const totalGoals = Number(summary && summary.total_goals) || 2;
|
|
62
|
+
const reducedProgramGoals = Math.max(2, Math.min(totalGoals, Math.ceil(totalGoals * 0.8)));
|
|
63
|
+
nextRunPatch.programGoals = Math.min(
|
|
64
|
+
Number(nextRunPatch.programGoals) || reducedProgramGoals,
|
|
65
|
+
reducedProgramGoals
|
|
66
|
+
);
|
|
67
|
+
nextRunPatch.batchRetryRounds = 0;
|
|
68
|
+
actions.push({
|
|
69
|
+
type: 'time-budget-constrain',
|
|
70
|
+
applied: true,
|
|
71
|
+
details: `Set programGoals=${nextRunPatch.programGoals}, batchRetryRounds=0 for elapsed budget ${maxElapsedMinutes}m.`
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let appliedSpecPrune = null;
|
|
76
|
+
const specBudget = summary && summary.spec_session_budget && summary.spec_session_budget.enabled
|
|
77
|
+
? summary.spec_session_budget
|
|
78
|
+
: null;
|
|
79
|
+
if (specBudget && specBudget.over_limit_after && Number.isFinite(Number(specBudget.max_total))) {
|
|
80
|
+
try {
|
|
81
|
+
const currentRunSpecNames = collectSpecNamesFromBatchSummary(summary || {});
|
|
82
|
+
appliedSpecPrune = await pruneSpecSessions(projectPath, {
|
|
83
|
+
keep: Number(specBudget.max_total),
|
|
84
|
+
olderThanDays: null,
|
|
85
|
+
dryRun: false,
|
|
86
|
+
protectActive: true,
|
|
87
|
+
protectWindowDays: options.specSessionProtectWindowDays,
|
|
88
|
+
additionalProtectedSpecs: currentRunSpecNames
|
|
89
|
+
});
|
|
90
|
+
summary.spec_session_auto_prune = appliedSpecPrune;
|
|
91
|
+
const specsAfter = await readSpecSessionEntries(projectPath);
|
|
92
|
+
const totalAfter = specsAfter.length;
|
|
93
|
+
const prunedCount = Number(appliedSpecPrune && appliedSpecPrune.deleted_count) || 0;
|
|
94
|
+
summary.spec_session_budget = {
|
|
95
|
+
...specBudget,
|
|
96
|
+
total_after: totalAfter,
|
|
97
|
+
pruned_count: (Number(specBudget.pruned_count) || 0) + prunedCount,
|
|
98
|
+
estimated_created: Math.max(0, totalAfter + ((Number(specBudget.pruned_count) || 0) + prunedCount) - specBudget.total_before),
|
|
99
|
+
over_limit_after: totalAfter > specBudget.max_total,
|
|
100
|
+
hard_fail_triggered: Boolean(specBudget.hard_fail && totalAfter > specBudget.max_total)
|
|
101
|
+
};
|
|
102
|
+
actions.push({
|
|
103
|
+
type: 'trigger-spec-prune',
|
|
104
|
+
applied: true,
|
|
105
|
+
details: `Pruned specs to enforce max_total=${specBudget.max_total}. deleted=${appliedSpecPrune.deleted_count}`
|
|
106
|
+
});
|
|
107
|
+
} catch (error) {
|
|
108
|
+
actions.push({
|
|
109
|
+
type: 'trigger-spec-prune',
|
|
110
|
+
applied: false,
|
|
111
|
+
error: error.message
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const hasPatch = Object.keys(nextRunPatch).length > 0;
|
|
117
|
+
return {
|
|
118
|
+
enabled: true,
|
|
119
|
+
attempted_at: now(),
|
|
120
|
+
reason_count: reasons.length,
|
|
121
|
+
reasons,
|
|
122
|
+
actions,
|
|
123
|
+
next_run_patch: hasPatch ? nextRunPatch : null,
|
|
124
|
+
applied_spec_prune: appliedSpecPrune
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = {
|
|
129
|
+
applyProgramGateAutoRemediation
|
|
130
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
function buildProgramFailureClusters(results = [], normalizeFailureSignatureFromError = (value) => String(value || 'unknown')) {
|
|
2
|
+
const failedStatuses = new Set(['failed', 'error', 'unknown', 'stopped']);
|
|
3
|
+
const source = Array.isArray(results) ? results : [];
|
|
4
|
+
const clusters = new Map();
|
|
5
|
+
|
|
6
|
+
for (const item of source) {
|
|
7
|
+
if (!item || typeof item !== 'object') {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const status = String(item.status || 'unknown').trim().toLowerCase();
|
|
11
|
+
if (!failedStatuses.has(status)) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const signatureSeed = normalizeFailureSignatureFromError(item.error);
|
|
16
|
+
const signature = status + ':' + signatureSeed;
|
|
17
|
+
if (!clusters.has(signature)) {
|
|
18
|
+
clusters.set(signature, {
|
|
19
|
+
signature,
|
|
20
|
+
status,
|
|
21
|
+
count: 0,
|
|
22
|
+
goal_indexes: [],
|
|
23
|
+
example_goal: item.goal || null,
|
|
24
|
+
example_error: item.error || null
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const cluster = clusters.get(signature);
|
|
29
|
+
cluster.count += 1;
|
|
30
|
+
const sourceIndex = Number.isInteger(item.source_index) ? item.source_index + 1 : Number(item.index);
|
|
31
|
+
if (Number.isInteger(sourceIndex) && sourceIndex > 0) {
|
|
32
|
+
cluster.goal_indexes.push(sourceIndex);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return [...clusters.values()]
|
|
37
|
+
.sort((left, right) => {
|
|
38
|
+
if (right.count !== left.count) {
|
|
39
|
+
return right.count - left.count;
|
|
40
|
+
}
|
|
41
|
+
return String(left.signature).localeCompare(String(right.signature));
|
|
42
|
+
})
|
|
43
|
+
.map((cluster) => ({
|
|
44
|
+
...cluster,
|
|
45
|
+
goal_indexes: cluster.goal_indexes.slice(0, 20)
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function buildProgramRemediationActions(summary, failureClusters) {
|
|
50
|
+
const failedGoals = Number(summary && summary.failed_goals) || 0;
|
|
51
|
+
const retry = summary && summary.batch_retry ? summary.batch_retry : {};
|
|
52
|
+
const actions = [];
|
|
53
|
+
|
|
54
|
+
if (failedGoals === 0) {
|
|
55
|
+
return [{
|
|
56
|
+
priority: 'monitor',
|
|
57
|
+
action: 'No remediation required. Program converged successfully.',
|
|
58
|
+
reason: 'All goals completed in the current run.',
|
|
59
|
+
suggested_command: null,
|
|
60
|
+
strategy_patch: {}
|
|
61
|
+
}];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
actions.push({
|
|
65
|
+
priority: 'high',
|
|
66
|
+
action: 'Resume unresolved goals from latest program/batch summary.',
|
|
67
|
+
reason: failedGoals + ' goals are unresolved after the current run.',
|
|
68
|
+
suggested_command: 'sce auto close-loop-recover latest --json',
|
|
69
|
+
strategy_patch: {
|
|
70
|
+
batchAutonomous: true,
|
|
71
|
+
continueOnError: true,
|
|
72
|
+
batchRetryUntilComplete: true
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if ((Number(retry.max_rounds) || 0) > 0 && (Number(retry.performed_rounds) || 0) >= (Number(retry.max_rounds) || 0)) {
|
|
77
|
+
actions.push({
|
|
78
|
+
priority: 'high',
|
|
79
|
+
action: 'Increase retry ceiling or split the program into smaller sub-goal groups.',
|
|
80
|
+
reason: 'Retry rounds were exhausted before convergence.',
|
|
81
|
+
suggested_command: 'sce auto close-loop-recover latest --batch-retry-max-rounds 15 --json',
|
|
82
|
+
strategy_patch: {
|
|
83
|
+
batchRetryUntilComplete: true,
|
|
84
|
+
batchRetryMaxRounds: 15
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const failureText = (Array.isArray(failureClusters) ? failureClusters : [])
|
|
90
|
+
.map((cluster) => String(cluster.signature) + ' ' + (cluster.example_error || ''))
|
|
91
|
+
.join(' | ')
|
|
92
|
+
.toLowerCase();
|
|
93
|
+
|
|
94
|
+
if (/timeout|timed out|deadline|terminated|killed/.test(failureText)) {
|
|
95
|
+
actions.push({
|
|
96
|
+
priority: 'medium',
|
|
97
|
+
action: 'Reduce parallel pressure and increase orchestration timeout budget.',
|
|
98
|
+
reason: 'Failure clusters indicate timeout/resource-pressure symptoms.',
|
|
99
|
+
suggested_command: 'sce auto close-loop-recover latest --batch-parallel 2 --batch-agent-budget 2 --json',
|
|
100
|
+
strategy_patch: {
|
|
101
|
+
batchParallel: 2,
|
|
102
|
+
batchAgentBudget: 2,
|
|
103
|
+
batchPriority: 'complex-first',
|
|
104
|
+
batchAgingFactor: 2
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (/dod|test|validation|checklist|compliance/.test(failureText)) {
|
|
110
|
+
actions.push({
|
|
111
|
+
priority: 'medium',
|
|
112
|
+
action: 'Run strict quality gates early to surface deterministic failures.',
|
|
113
|
+
reason: 'Failure clusters indicate DoD/test/compliance gate issues.',
|
|
114
|
+
suggested_command: 'sce auto close-loop-recover latest --dod-tests "npm run test:smoke" --dod-tasks-closed --json',
|
|
115
|
+
strategy_patch: {
|
|
116
|
+
dodTests: 'npm run test:smoke',
|
|
117
|
+
dodTasksClosed: true
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return actions.slice(0, 5);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function buildProgramDiagnostics(summary, normalizeFailureSignatureFromError) {
|
|
126
|
+
const failureClusters = buildProgramFailureClusters(summary && summary.results, normalizeFailureSignatureFromError);
|
|
127
|
+
return {
|
|
128
|
+
failed_goal_count: Number(summary && summary.failed_goals) || 0,
|
|
129
|
+
failure_clusters: failureClusters,
|
|
130
|
+
remediation_actions: buildProgramRemediationActions(summary, failureClusters)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = {
|
|
135
|
+
buildProgramFailureClusters,
|
|
136
|
+
buildProgramRemediationActions,
|
|
137
|
+
buildProgramDiagnostics
|
|
138
|
+
};
|