scene-capability-engine 3.6.32 → 3.6.37

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.
Files changed (111) hide show
  1. package/CHANGELOG.md +109 -11
  2. package/README.md +119 -122
  3. package/README.zh.md +123 -121
  4. package/bin/scene-capability-engine.js +12 -1
  5. package/docs/331-poc-adaptation-roadmap.md +3 -3
  6. package/docs/README.md +21 -32
  7. package/docs/auto-refactor-index.md +384 -0
  8. package/docs/command-reference.md +99 -7
  9. package/docs/faq.md +1 -1
  10. package/docs/interactive-customization/331-poc-sce-integration-checklist.md +3 -3
  11. package/docs/interactive-customization/moqui-interactive-template-playbook.md +4 -4
  12. package/docs/interactive-customization/phase-acceptance-evidence.md +2 -2
  13. package/docs/magicball-adaptation-task-checklist-v1.md +385 -0
  14. package/docs/magicball-app-bundle-sqlite-and-command-draft.md +539 -0
  15. package/docs/magicball-capability-iteration-api.md +2 -0
  16. package/docs/magicball-capability-iteration-ui.md +2 -0
  17. package/docs/magicball-capability-library.md +2 -0
  18. package/docs/magicball-cli-invocation-examples.md +336 -0
  19. package/docs/magicball-frontend-state-and-command-mapping.md +244 -0
  20. package/docs/magicball-integration-doc-index.md +137 -0
  21. package/docs/magicball-integration-issue-tracker.md +218 -0
  22. package/docs/magicball-mode-home-and-ontology-empty-state-playbook.md +249 -0
  23. package/docs/magicball-sce-adaptation-guide.md +203 -0
  24. package/docs/magicball-three-mode-alignment-plan.md +551 -0
  25. package/docs/magicball-ui-surface-checklist.md +126 -0
  26. package/docs/magicball-write-auth-adaptation-guide.md +328 -0
  27. package/docs/moqui-standard-rebuild-guide.md +6 -6
  28. package/docs/moqui-template-core-library-playbook.md +1 -1
  29. package/docs/refactor-completion-roadmap.md +116 -0
  30. package/docs/release-checklist.md +50 -27
  31. package/docs/releases/README.md +1 -0
  32. package/docs/releases/v3.6.37.md +22 -0
  33. package/docs/steering-strategy-guide.md +7 -7
  34. package/docs/troubleshooting.md +1 -1
  35. package/docs/zh/README.md +27 -30
  36. package/docs/zh/refactor-completion-roadmap.md +116 -0
  37. package/docs/zh/release-checklist.md +40 -17
  38. package/docs/zh/releases/README.md +1 -0
  39. package/docs/zh/releases/v3.6.37.md +22 -0
  40. package/lib/app/registry-config.js +73 -0
  41. package/lib/app/registry-sync-service.js +228 -0
  42. package/lib/auto/archive-schema-service.js +276 -0
  43. package/lib/auto/archive-summary.js +60 -0
  44. package/lib/auto/batch-goal-input-service.js +543 -0
  45. package/lib/auto/batch-output.js +201 -0
  46. package/lib/auto/batch-summary-storage-service.js +110 -0
  47. package/lib/auto/close-loop-batch-service.js +116 -0
  48. package/lib/auto/close-loop-controller-service.js +287 -0
  49. package/lib/auto/close-loop-program-service.js +283 -0
  50. package/lib/auto/close-loop-recovery-service.js +191 -0
  51. package/lib/auto/close-loop-session-storage-service.js +50 -0
  52. package/lib/auto/controller-lock-service.js +55 -0
  53. package/lib/auto/controller-output.js +32 -0
  54. package/lib/auto/controller-queue-service.js +127 -0
  55. package/lib/auto/controller-session-storage-service.js +105 -0
  56. package/lib/auto/governance-advisory-service.js +208 -0
  57. package/lib/auto/governance-close-loop-service.js +411 -0
  58. package/lib/auto/governance-maintenance-presenter.js +162 -0
  59. package/lib/auto/governance-maintenance-service.js +112 -0
  60. package/lib/auto/governance-session-presenter.js +70 -0
  61. package/lib/auto/governance-session-storage-service.js +198 -0
  62. package/lib/auto/governance-signals.js +139 -0
  63. package/lib/auto/governance-stats-presenter.js +337 -0
  64. package/lib/auto/governance-stats-service.js +115 -0
  65. package/lib/auto/governance-summary.js +703 -0
  66. package/lib/auto/handoff-capability-matrix-service.js +281 -0
  67. package/lib/auto/handoff-evidence-review-service.js +251 -0
  68. package/lib/auto/handoff-release-evidence-service.js +190 -0
  69. package/lib/auto/handoff-release-gate-history-loaders-service.js +502 -0
  70. package/lib/auto/handoff-release-gate-history-service.js +257 -0
  71. package/lib/auto/handoff-reporting-service.js +1407 -0
  72. package/lib/auto/handoff-run-service.js +486 -0
  73. package/lib/auto/handoff-snapshots-service.js +645 -0
  74. package/lib/auto/observability-service.js +132 -0
  75. package/lib/auto/output-writer.js +34 -0
  76. package/lib/auto/program-auto-remediation-service.js +130 -0
  77. package/lib/auto/program-diagnostics.js +138 -0
  78. package/lib/auto/program-governance-helpers.js +306 -0
  79. package/lib/auto/program-governance-loop-service.js +413 -0
  80. package/lib/auto/program-output.js +106 -0
  81. package/lib/auto/program-summary.js +183 -0
  82. package/lib/auto/recovery-memory-service.js +684 -0
  83. package/lib/auto/recovery-selection-service.js +52 -0
  84. package/lib/auto/retention-policy.js +98 -0
  85. package/lib/auto/session-persistence-service.js +106 -0
  86. package/lib/auto/session-presenter.js +105 -0
  87. package/lib/auto/session-prune-service.js +190 -0
  88. package/lib/auto/session-query-service.js +249 -0
  89. package/lib/auto/spec-protection.js +141 -0
  90. package/lib/commands/adopt.js +4 -4
  91. package/lib/commands/app.js +911 -0
  92. package/lib/commands/assurance.js +212 -0
  93. package/lib/commands/auto.js +1093 -11065
  94. package/lib/commands/mode.js +321 -0
  95. package/lib/commands/ontology.js +415 -0
  96. package/lib/commands/pm.js +422 -0
  97. package/lib/ontology/seed-profiles.js +160 -0
  98. package/lib/spec/bootstrap/context-collector.js +1 -1
  99. package/lib/state/sce-state-store.js +3369 -1200
  100. package/lib/steering/adoption-config.js +2 -2
  101. package/lib/steering/compliance-cache.js +2 -2
  102. package/lib/steering/steering-manager.js +4 -4
  103. package/lib/task/task-claimer.js +1 -2
  104. package/lib/workspace/multi/workspace-context-resolver.js +3 -3
  105. package/lib/workspace/multi/workspace-state-manager.js +0 -164
  106. package/lib/workspace/sce-tracking-audit.js +1 -1
  107. package/lib/workspace/takeover-baseline.js +1 -1
  108. package/package.json +1 -1
  109. package/template/.sce/README.md +1 -1
  110. package/template/.sce/steering/CORE_PRINCIPLES.md +1 -1
  111. package/bin/kse.js +0 -3
@@ -0,0 +1,413 @@
1
+ function hasRecoverableProgramGoals(summary, dependencies = {}) {
2
+ const getBatchFailureStatusSet = dependencies.getBatchFailureStatusSet;
3
+ const failedStatuses = getBatchFailureStatusSet();
4
+ const results = Array.isArray(summary && summary.results) ? summary.results : [];
5
+ return results.some(item => failedStatuses.has(`${item && item.status ? item.status : ''}`.trim().toLowerCase()));
6
+ }
7
+
8
+ function applyProgramGovernancePatch(baseOptions, patch) {
9
+ const merged = { ...baseOptions };
10
+ const sourcePatch = patch && typeof patch === 'object' ? patch : {};
11
+ for (const [key, value] of Object.entries(sourcePatch)) {
12
+ if (value === undefined) {
13
+ continue;
14
+ }
15
+ merged[key] = value;
16
+ }
17
+ return merged;
18
+ }
19
+
20
+ function buildProgramGovernanceReplayGoalsResult(baseGoalsResult, round, summary) {
21
+ const source = baseGoalsResult && typeof baseGoalsResult === 'object'
22
+ ? baseGoalsResult
23
+ : { file: '(generated-from-goal)', goals: [] };
24
+ const sourceSummary = summary && typeof summary === 'object' ? summary : {};
25
+ return {
26
+ ...source,
27
+ file: source.file || '(generated-from-goal)',
28
+ resumedFromSummary: {
29
+ file: sourceSummary.batch_session && sourceSummary.batch_session.file
30
+ ? sourceSummary.batch_session.file
31
+ : '(program-governance-replay)',
32
+ strategy: 'program-governance-replay',
33
+ round,
34
+ previous_status: sourceSummary.status || null,
35
+ previous_total_goals: Number(sourceSummary.total_goals) || null,
36
+ previous_processed_goals: Number(sourceSummary.processed_goals) || null
37
+ }
38
+ };
39
+ }
40
+
41
+ async function runProgramGovernanceLoop(context = {}, dependencies = {}) {
42
+ const {
43
+ normalizeProgramGovernMaxRounds,
44
+ normalizeProgramGovernMaxMinutes,
45
+ normalizeProgramGovernAnomalyWeeks,
46
+ normalizeAutoKpiTrendPeriod,
47
+ normalizeProgramGovernUseAction,
48
+ resolveProgramGatePolicy,
49
+ normalizeRecoveryMemoryToken,
50
+ normalizeResumeStrategy,
51
+ normalizeRecoverMaxRounds,
52
+ normalizeRecoverMaxMinutes,
53
+ isSpecSessionBudgetHardFailure,
54
+ isSpecSessionGrowthGuardHardFailure,
55
+ buildAutoKpiTrend,
56
+ buildProgramAnomalyGovernancePatch,
57
+ loadCloseLoopRecoveryMemory,
58
+ buildRecoveryMemorySignature,
59
+ getRecoveryMemoryEntry,
60
+ resolveRecoveryActionSelection,
61
+ loadCloseLoopBatchSummaryPayload,
62
+ executeCloseLoopRecoveryCycle,
63
+ mergeProgramRecoveryIntoProgramSummary,
64
+ executeCloseLoopBatch,
65
+ buildProgramKpiSnapshot,
66
+ buildProgramDiagnostics,
67
+ buildProgramCoordinationSnapshot,
68
+ applyProgramGateOutcome,
69
+ getBatchFailureStatusSet,
70
+ now = () => Date.now(),
71
+ cwd = () => process.cwd()
72
+ } = dependencies;
73
+
74
+ let summary = context.summary && typeof context.summary === 'object' ? context.summary : {};
75
+ const projectPath = context.projectPath || cwd();
76
+ const baseProgramOptions = context.programOptions && typeof context.programOptions === 'object'
77
+ ? context.programOptions
78
+ : {};
79
+ const baseGoalsResult = context.baseGoalsResult && typeof context.baseGoalsResult === 'object'
80
+ ? context.baseGoalsResult
81
+ : { file: '(generated-from-goal)', goals: [] };
82
+ const enabled = Boolean(context.enabled);
83
+ const maxRounds = normalizeProgramGovernMaxRounds(context.maxRounds);
84
+ const maxDurationMinutes = normalizeProgramGovernMaxMinutes(context.maxMinutes);
85
+ const maxDurationMs = maxDurationMinutes * 60 * 1000;
86
+ const anomalyEnabled = context.anomalyEnabled !== false;
87
+ const anomalyWeeks = normalizeProgramGovernAnomalyWeeks(context.anomalyWeeks);
88
+ const anomalyPeriod = normalizeAutoKpiTrendPeriod(context.anomalyPeriod);
89
+ const governUseAction = normalizeProgramGovernUseAction(context.governUseAction);
90
+ const governAutoActionEnabled = context.governAutoActionEnabled !== false;
91
+ const governActionEnabled = governAutoActionEnabled || governUseAction !== null;
92
+ const programGatePolicy = resolveProgramGatePolicy(context.programGatePolicy || {});
93
+ const gateFallbackChain = Array.isArray(context.gateFallbackChain) ? context.gateFallbackChain : [];
94
+ const recoveryMemoryScope = context.recoveryMemoryScope || null;
95
+ const normalizedRecoveryScope = normalizeRecoveryMemoryToken(recoveryMemoryScope || '') || 'default-scope';
96
+ const recoverResumeStrategy = normalizeResumeStrategy(context.recoverResumeStrategy || 'pending');
97
+ const recoverMaxRounds = normalizeRecoverMaxRounds(context.recoverMaxRounds);
98
+ const recoverMaxMinutes = normalizeRecoverMaxMinutes(
99
+ context.recoverMaxMinutes,
100
+ '--program-recover-max-minutes'
101
+ );
102
+ const recoverMaxDurationMs = recoverMaxMinutes === null ? null : recoverMaxMinutes * 60 * 1000;
103
+ const governanceStartedAt = now();
104
+ const history = [];
105
+ let exhausted = false;
106
+ let stopReason = enabled ? 'stable' : 'disabled';
107
+ let settled = false;
108
+
109
+ if (!enabled) {
110
+ return {
111
+ summary,
112
+ governance: {
113
+ enabled: false,
114
+ anomaly_enabled: anomalyEnabled,
115
+ anomaly_weeks: anomalyWeeks,
116
+ anomaly_period: anomalyPeriod,
117
+ auto_action_enabled: governAutoActionEnabled,
118
+ action_selection_enabled: false,
119
+ pinned_action_index: governUseAction,
120
+ max_rounds: maxRounds,
121
+ max_minutes: maxDurationMinutes,
122
+ performed_rounds: 0,
123
+ converged: Boolean(
124
+ summary &&
125
+ summary.program_gate_effective &&
126
+ summary.program_gate_effective.passed &&
127
+ !isSpecSessionBudgetHardFailure(summary) &&
128
+ !isSpecSessionGrowthGuardHardFailure(summary)
129
+ ),
130
+ exhausted: false,
131
+ stop_reason: 'disabled',
132
+ history: []
133
+ }
134
+ };
135
+ }
136
+
137
+ for (let round = 1; round <= maxRounds; round += 1) {
138
+ const elapsedBeforeRound = now() - governanceStartedAt;
139
+ if (elapsedBeforeRound >= maxDurationMs) {
140
+ exhausted = true;
141
+ stopReason = 'time-budget-exhausted';
142
+ break;
143
+ }
144
+
145
+ let trendResult = null;
146
+ let anomalies = [];
147
+ if (anomalyEnabled) {
148
+ trendResult = await buildAutoKpiTrend(projectPath, {
149
+ weeks: anomalyWeeks,
150
+ mode: 'program',
151
+ period: anomalyPeriod
152
+ });
153
+ anomalies = Array.isArray(trendResult.anomalies) ? trendResult.anomalies : [];
154
+ summary.program_kpi_trend = {
155
+ generated_at: trendResult.generated_at,
156
+ weeks: trendResult.weeks,
157
+ period_unit: trendResult.period_unit,
158
+ total_runs: trendResult.total_runs,
159
+ overall: trendResult.overall,
160
+ anomaly_detection: trendResult.anomaly_detection || null
161
+ };
162
+ summary.program_kpi_anomalies = anomalies;
163
+ }
164
+
165
+ const gateFailed = Boolean(
166
+ !summary.program_gate_effective ||
167
+ !summary.program_gate_effective.passed ||
168
+ isSpecSessionBudgetHardFailure(summary) ||
169
+ isSpecSessionGrowthGuardHardFailure(summary)
170
+ );
171
+ const highSeverityAnomalies = anomalies.filter(item => `${item && item.severity ? item.severity : ''}`.trim().toLowerCase() === 'high');
172
+ const anomalyFailed = anomalyEnabled && highSeverityAnomalies.length > 0;
173
+ if (!gateFailed && !anomalyFailed) {
174
+ stopReason = 'stable';
175
+ settled = true;
176
+ break;
177
+ }
178
+
179
+ const gatePatch = summary && summary.program_gate_auto_remediation && summary.program_gate_auto_remediation.next_run_patch
180
+ ? summary.program_gate_auto_remediation.next_run_patch
181
+ : {};
182
+ const anomalyPatch = buildProgramAnomalyGovernancePatch(summary, highSeverityAnomalies, baseProgramOptions);
183
+ let governanceActionSelection = null;
184
+ let governanceActionPatch = {};
185
+ if (governActionEnabled) {
186
+ const recoveryMemory = await loadCloseLoopRecoveryMemory(projectPath);
187
+ const recoverySignature = buildRecoveryMemorySignature(summary, {
188
+ scope: normalizedRecoveryScope
189
+ });
190
+ const recoveryMemoryEntry = getRecoveryMemoryEntry(recoveryMemory.payload, recoverySignature);
191
+ governanceActionSelection = resolveRecoveryActionSelection(summary, governUseAction, {
192
+ recoveryMemoryEntry,
193
+ optionLabel: '--program-govern-use-action'
194
+ });
195
+ governanceActionPatch = governanceActionSelection &&
196
+ governanceActionSelection.appliedPatch &&
197
+ typeof governanceActionSelection.appliedPatch === 'object'
198
+ ? governanceActionSelection.appliedPatch
199
+ : {};
200
+ }
201
+ const roundPatch = {
202
+ ...(governanceActionPatch && typeof governanceActionPatch === 'object' ? governanceActionPatch : {}),
203
+ ...(anomalyPatch.patch || {}),
204
+ ...(gatePatch && typeof gatePatch === 'object' ? gatePatch : {})
205
+ };
206
+ if (Object.keys(roundPatch).length === 0) {
207
+ stopReason = 'no-actionable-patch';
208
+ history.push({
209
+ round,
210
+ status_before: summary.status,
211
+ status_after: summary.status,
212
+ trigger: {
213
+ gate_failed: gateFailed,
214
+ anomaly_failed: anomalyFailed,
215
+ anomaly_count: highSeverityAnomalies.length
216
+ },
217
+ selected_action_index: governanceActionSelection ? governanceActionSelection.selectedIndex : null,
218
+ selected_action: governanceActionSelection && governanceActionSelection.selectedAction
219
+ ? governanceActionSelection.selectedAction.action
220
+ : null,
221
+ selected_action_priority: governanceActionSelection && governanceActionSelection.selectedAction
222
+ ? governanceActionSelection.selectedAction.priority
223
+ : null,
224
+ action_selection_source: governanceActionSelection ? governanceActionSelection.selectionSource : null,
225
+ action_selection_explain: governanceActionSelection ? governanceActionSelection.selectionExplain || null : null,
226
+ execution_mode: 'none',
227
+ applied_patch: null,
228
+ notes: [
229
+ 'No actionable governance patch generated.'
230
+ ]
231
+ });
232
+ break;
233
+ }
234
+
235
+ const roundOptions = applyProgramGovernancePatch(baseProgramOptions, roundPatch);
236
+ roundOptions.out = null;
237
+ roundOptions.programKpiOut = null;
238
+ roundOptions.programAuditOut = null;
239
+
240
+ const statusBefore = summary.status;
241
+ const failedGoalsBefore = Number(summary.failed_goals) || 0;
242
+ const selectedGovernanceActionIndex = governanceActionSelection ? governanceActionSelection.selectedIndex : null;
243
+ let executionMode = 'program-replay';
244
+ let roundSummary = null;
245
+ if (hasRecoverableProgramGoals(summary, { getBatchFailureStatusSet })) {
246
+ executionMode = 'recover-cycle';
247
+ const roundSourceSummary = summary.batch_session && summary.batch_session.file
248
+ ? await loadCloseLoopBatchSummaryPayload(projectPath, summary.batch_session.file)
249
+ : {
250
+ file: '(program-governance-derived-summary)',
251
+ payload: summary
252
+ };
253
+ const recoveryResult = await executeCloseLoopRecoveryCycle({
254
+ projectPath,
255
+ sourceSummary: roundSourceSummary,
256
+ baseOptions: {
257
+ ...roundOptions,
258
+ useAction: selectedGovernanceActionIndex || context.programRecoverUseAction
259
+ },
260
+ recoverAutonomousEnabled: true,
261
+ resumeStrategy: recoverResumeStrategy,
262
+ recoverUntilComplete: true,
263
+ recoverMaxRounds,
264
+ recoverMaxDurationMs,
265
+ recoveryMemoryScope,
266
+ actionCandidate: selectedGovernanceActionIndex || context.programRecoverUseAction
267
+ });
268
+ roundSummary = mergeProgramRecoveryIntoProgramSummary(summary, recoveryResult.summary, {
269
+ enabled: true,
270
+ triggered: true,
271
+ governance_round: round,
272
+ recover_until_complete: true,
273
+ source: 'governance-recover-cycle'
274
+ });
275
+ roundSummary.resource_plan = recoveryResult.summary && recoveryResult.summary.resource_plan
276
+ ? recoveryResult.summary.resource_plan
277
+ : roundSummary.resource_plan;
278
+ roundSummary.batch_parallel = Number(recoveryResult.summary && recoveryResult.summary.batch_parallel) || roundSummary.batch_parallel;
279
+ } else {
280
+ const replayGoalsResult = buildProgramGovernanceReplayGoalsResult(baseGoalsResult, round, summary);
281
+ const replaySummary = await executeCloseLoopBatch(
282
+ replayGoalsResult,
283
+ roundOptions,
284
+ projectPath,
285
+ 'auto-close-loop-program'
286
+ );
287
+ roundSummary = {
288
+ ...replaySummary,
289
+ auto_recovery: summary && summary.auto_recovery ? summary.auto_recovery : null
290
+ };
291
+ }
292
+
293
+ roundSummary.program_kpi = buildProgramKpiSnapshot(roundSummary);
294
+ roundSummary.program_diagnostics = buildProgramDiagnostics(roundSummary);
295
+ roundSummary.program_coordination = buildProgramCoordinationSnapshot(roundSummary);
296
+ await applyProgramGateOutcome(roundSummary, {
297
+ projectPath,
298
+ options: roundOptions,
299
+ programGatePolicy,
300
+ gateFallbackChain,
301
+ enableAutoRemediation: context.programGateAutoRemediate !== false
302
+ });
303
+
304
+ const failedGoalsAfter = Number(roundSummary.failed_goals) || 0;
305
+ history.push({
306
+ round,
307
+ status_before: statusBefore,
308
+ status_after: roundSummary.status,
309
+ trigger: {
310
+ gate_failed: gateFailed,
311
+ anomaly_failed: anomalyFailed,
312
+ anomaly_count: highSeverityAnomalies.length
313
+ },
314
+ selected_action_index: selectedGovernanceActionIndex,
315
+ selected_action: governanceActionSelection && governanceActionSelection.selectedAction
316
+ ? governanceActionSelection.selectedAction.action
317
+ : null,
318
+ selected_action_priority: governanceActionSelection && governanceActionSelection.selectedAction
319
+ ? governanceActionSelection.selectedAction.priority
320
+ : null,
321
+ action_selection_source: governanceActionSelection ? governanceActionSelection.selectionSource : null,
322
+ action_selection_explain: governanceActionSelection ? governanceActionSelection.selectionExplain || null : null,
323
+ execution_mode: executionMode,
324
+ applied_patch: roundPatch,
325
+ patch_reasons: [
326
+ ...(governanceActionSelection && governanceActionSelection.selectionExplain
327
+ ? [`governance-action: ${governanceActionSelection.selectionExplain.reason}`]
328
+ : []),
329
+ ...(Array.isArray(anomalyPatch.reasons) ? anomalyPatch.reasons : []),
330
+ ...(summary.program_gate_auto_remediation && Array.isArray(summary.program_gate_auto_remediation.reasons)
331
+ ? summary.program_gate_auto_remediation.reasons
332
+ : [])
333
+ ],
334
+ failed_goals_before: failedGoalsBefore,
335
+ failed_goals_after: failedGoalsAfter
336
+ });
337
+
338
+ summary = roundSummary;
339
+ if (
340
+ summary.program_gate_effective &&
341
+ summary.program_gate_effective.passed &&
342
+ !isSpecSessionBudgetHardFailure(summary) &&
343
+ !isSpecSessionGrowthGuardHardFailure(summary)
344
+ ) {
345
+ if (!anomalyEnabled) {
346
+ stopReason = 'gate-stable';
347
+ break;
348
+ }
349
+ const postTrend = await buildAutoKpiTrend(projectPath, {
350
+ weeks: anomalyWeeks,
351
+ mode: 'program',
352
+ period: anomalyPeriod
353
+ });
354
+ const postAnomalies = Array.isArray(postTrend.anomalies) ? postTrend.anomalies : [];
355
+ summary.program_kpi_trend = {
356
+ generated_at: postTrend.generated_at,
357
+ weeks: postTrend.weeks,
358
+ period_unit: postTrend.period_unit,
359
+ total_runs: postTrend.total_runs,
360
+ overall: postTrend.overall,
361
+ anomaly_detection: postTrend.anomaly_detection || null
362
+ };
363
+ summary.program_kpi_anomalies = postAnomalies;
364
+ const hasHighPostAnomaly = postAnomalies.some(item => `${item && item.severity ? item.severity : ''}`.trim().toLowerCase() === 'high');
365
+ if (!hasHighPostAnomaly) {
366
+ stopReason = 'stable';
367
+ settled = true;
368
+ break;
369
+ }
370
+ }
371
+ }
372
+
373
+ if (!settled && history.length >= maxRounds && stopReason === 'stable') {
374
+ stopReason = 'round-limit-reached';
375
+ exhausted = true;
376
+ }
377
+ if (!settled && history.length >= maxRounds && stopReason !== 'stable') {
378
+ exhausted = true;
379
+ }
380
+
381
+ return {
382
+ summary,
383
+ governance: {
384
+ enabled: true,
385
+ anomaly_enabled: anomalyEnabled,
386
+ anomaly_weeks: anomalyWeeks,
387
+ anomaly_period: anomalyPeriod,
388
+ auto_action_enabled: governAutoActionEnabled,
389
+ action_selection_enabled: governActionEnabled,
390
+ pinned_action_index: governUseAction,
391
+ max_rounds: maxRounds,
392
+ max_minutes: maxDurationMinutes,
393
+ performed_rounds: history.length,
394
+ converged: Boolean(
395
+ summary &&
396
+ summary.program_gate_effective &&
397
+ summary.program_gate_effective.passed &&
398
+ !isSpecSessionBudgetHardFailure(summary) &&
399
+ !isSpecSessionGrowthGuardHardFailure(summary)
400
+ ),
401
+ exhausted,
402
+ stop_reason: stopReason,
403
+ history
404
+ }
405
+ };
406
+ }
407
+
408
+ module.exports = {
409
+ hasRecoverableProgramGoals,
410
+ applyProgramGovernancePatch,
411
+ buildProgramGovernanceReplayGoalsResult,
412
+ runProgramGovernanceLoop
413
+ };
@@ -0,0 +1,106 @@
1
+ async function maybeWriteProgramKpi(summary, 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, {
13
+ mode: summary.mode === 'auto-close-loop-recover'
14
+ ? 'auto-close-loop-recover-kpi'
15
+ : 'auto-close-loop-program-kpi',
16
+ program_mode: summary.mode,
17
+ status: summary.status,
18
+ program_started_at: summary.program_started_at || null,
19
+ program_completed_at: summary.program_completed_at || null,
20
+ program_elapsed_ms: Number.isFinite(Number(summary.program_elapsed_ms))
21
+ ? Number(summary.program_elapsed_ms)
22
+ : null,
23
+ total_goals: summary.total_goals,
24
+ processed_goals: summary.processed_goals,
25
+ completed_goals: summary.completed_goals,
26
+ failed_goals: summary.failed_goals,
27
+ metrics: summary.metrics,
28
+ program_kpi: summary.program_kpi,
29
+ program_diagnostics: summary.program_diagnostics,
30
+ program_coordination: summary.program_coordination || null,
31
+ auto_recovery: summary.auto_recovery || null,
32
+ program_governance: summary.program_governance || null,
33
+ program_kpi_trend: summary.program_kpi_trend || null,
34
+ program_kpi_anomalies: Array.isArray(summary.program_kpi_anomalies) ? summary.program_kpi_anomalies : [],
35
+ goal_input_guard: summary.goal_input_guard || null,
36
+ spec_session_budget: summary.spec_session_budget || null,
37
+ spec_session_growth_guard: summary.spec_session_growth_guard || null,
38
+ spec_session_auto_prune: summary.spec_session_auto_prune || null,
39
+ program_gate_auto_remediation: summary.program_gate_auto_remediation || null,
40
+ program_gate: summary.program_gate || null,
41
+ program_gate_fallback: summary.program_gate_fallback || null,
42
+ program_gate_fallbacks: summary.program_gate_fallbacks || [],
43
+ program_gate_effective: summary.program_gate_effective || null
44
+ }, { spaces: 2 });
45
+ summary.program_kpi_file = outputPath;
46
+ }
47
+
48
+ async function maybeWriteProgramAudit(summary, outCandidate, projectPath, dependencies = {}) {
49
+ const pathModule = dependencies.pathModule;
50
+ const fs = dependencies.fs;
51
+ const now = dependencies.now || (() => new Date().toISOString());
52
+ if (!outCandidate) {
53
+ return;
54
+ }
55
+ const outputPath = pathModule.isAbsolute(outCandidate)
56
+ ? outCandidate
57
+ : pathModule.join(projectPath, outCandidate);
58
+ await fs.ensureDir(pathModule.dirname(outputPath));
59
+ await fs.writeJson(outputPath, {
60
+ mode: 'auto-close-loop-program-audit',
61
+ generated_at: now(),
62
+ summary_mode: summary && summary.mode ? summary.mode : null,
63
+ status: summary && summary.status ? summary.status : null,
64
+ program_started_at: summary && summary.program_started_at ? summary.program_started_at : null,
65
+ program_completed_at: summary && summary.program_completed_at ? summary.program_completed_at : null,
66
+ program_elapsed_ms: Number.isFinite(Number(summary && summary.program_elapsed_ms))
67
+ ? Number(summary && summary.program_elapsed_ms)
68
+ : null,
69
+ totals: {
70
+ total_goals: Number(summary && summary.total_goals) || 0,
71
+ processed_goals: Number(summary && summary.processed_goals) || 0,
72
+ completed_goals: Number(summary && summary.completed_goals) || 0,
73
+ failed_goals: Number(summary && summary.failed_goals) || 0
74
+ },
75
+ metrics: summary && summary.metrics ? summary.metrics : null,
76
+ batch_retry: summary && summary.batch_retry ? summary.batch_retry : null,
77
+ program_kpi: summary && summary.program_kpi ? summary.program_kpi : null,
78
+ program_diagnostics: summary && summary.program_diagnostics ? summary.program_diagnostics : null,
79
+ program_coordination: summary && summary.program_coordination ? summary.program_coordination : null,
80
+ program_gate: summary && summary.program_gate ? summary.program_gate : null,
81
+ program_gate_fallback: summary && summary.program_gate_fallback ? summary.program_gate_fallback : null,
82
+ program_gate_fallbacks: Array.isArray(summary && summary.program_gate_fallbacks) ? summary.program_gate_fallbacks : [],
83
+ program_gate_effective: summary && summary.program_gate_effective ? summary.program_gate_effective : null,
84
+ auto_recovery: summary && summary.auto_recovery ? summary.auto_recovery : null,
85
+ program_governance: summary && summary.program_governance ? summary.program_governance : null,
86
+ program_kpi_trend: summary && summary.program_kpi_trend ? summary.program_kpi_trend : null,
87
+ program_kpi_anomalies: Array.isArray(summary && summary.program_kpi_anomalies) ? summary.program_kpi_anomalies : [],
88
+ recovery_cycle: summary && summary.recovery_cycle ? summary.recovery_cycle : null,
89
+ recovery_plan: summary && summary.recovery_plan ? summary.recovery_plan : null,
90
+ recovery_memory: summary && summary.recovery_memory ? summary.recovery_memory : null,
91
+ goal_input_guard: summary && summary.goal_input_guard ? summary.goal_input_guard : null,
92
+ spec_session_prune: summary && summary.spec_session_prune ? summary.spec_session_prune : null,
93
+ spec_session_budget: summary && summary.spec_session_budget ? summary.spec_session_budget : null,
94
+ spec_session_growth_guard: summary && summary.spec_session_growth_guard ? summary.spec_session_growth_guard : null,
95
+ spec_session_auto_prune: summary && summary.spec_session_auto_prune ? summary.spec_session_auto_prune : null,
96
+ program_gate_auto_remediation: summary && summary.program_gate_auto_remediation ? summary.program_gate_auto_remediation : null,
97
+ resource_plan: summary && summary.resource_plan ? summary.resource_plan : null,
98
+ results: Array.isArray(summary && summary.results) ? summary.results : []
99
+ }, { spaces: 2 });
100
+ summary.program_audit_file = outputPath;
101
+ }
102
+
103
+ module.exports = {
104
+ maybeWriteProgramKpi,
105
+ maybeWriteProgramAudit
106
+ };