scene-capability-engine 3.6.45 → 3.6.46

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 (56) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/docs/releases/README.md +1 -0
  3. package/docs/releases/v3.6.46.md +23 -0
  4. package/docs/zh/releases/README.md +1 -0
  5. package/docs/zh/releases/v3.6.46.md +23 -0
  6. package/package.json +4 -2
  7. package/scripts/auto-strategy-router.js +231 -0
  8. package/scripts/capability-mapping-report.js +339 -0
  9. package/scripts/check-branding-consistency.js +140 -0
  10. package/scripts/check-sce-tracking.js +54 -0
  11. package/scripts/check-skip-allowlist.js +94 -0
  12. package/scripts/errorbook-registry-health-gate.js +172 -0
  13. package/scripts/errorbook-release-gate.js +132 -0
  14. package/scripts/failure-attribution-repair.js +317 -0
  15. package/scripts/git-managed-gate.js +464 -0
  16. package/scripts/interactive-approval-event-projection.js +400 -0
  17. package/scripts/interactive-approval-workflow.js +829 -0
  18. package/scripts/interactive-authorization-tier-evaluate.js +413 -0
  19. package/scripts/interactive-change-plan-gate.js +225 -0
  20. package/scripts/interactive-context-bridge.js +617 -0
  21. package/scripts/interactive-customization-loop.js +1690 -0
  22. package/scripts/interactive-dialogue-governance.js +842 -0
  23. package/scripts/interactive-feedback-log.js +253 -0
  24. package/scripts/interactive-flow-smoke.js +238 -0
  25. package/scripts/interactive-flow.js +1059 -0
  26. package/scripts/interactive-governance-report.js +1112 -0
  27. package/scripts/interactive-intent-build.js +707 -0
  28. package/scripts/interactive-loop-smoke.js +215 -0
  29. package/scripts/interactive-moqui-adapter.js +304 -0
  30. package/scripts/interactive-plan-build.js +426 -0
  31. package/scripts/interactive-runtime-policy-evaluate.js +495 -0
  32. package/scripts/interactive-work-order-build.js +552 -0
  33. package/scripts/matrix-regression-gate.js +167 -0
  34. package/scripts/moqui-core-regression-suite.js +397 -0
  35. package/scripts/moqui-lexicon-audit.js +651 -0
  36. package/scripts/moqui-matrix-remediation-phased-runner.js +865 -0
  37. package/scripts/moqui-matrix-remediation-queue.js +852 -0
  38. package/scripts/moqui-metadata-extract.js +1340 -0
  39. package/scripts/moqui-rebuild-gate.js +167 -0
  40. package/scripts/moqui-release-summary.js +729 -0
  41. package/scripts/moqui-standard-rebuild.js +1370 -0
  42. package/scripts/moqui-template-baseline-report.js +682 -0
  43. package/scripts/npm-package-runtime-asset-check.js +221 -0
  44. package/scripts/problem-closure-gate.js +441 -0
  45. package/scripts/release-asset-integrity-check.js +216 -0
  46. package/scripts/release-asset-nonempty-normalize.js +166 -0
  47. package/scripts/release-drift-evaluate.js +223 -0
  48. package/scripts/release-drift-signals.js +255 -0
  49. package/scripts/release-governance-snapshot-export.js +132 -0
  50. package/scripts/release-ops-weekly-summary.js +934 -0
  51. package/scripts/release-risk-remediation-bundle.js +315 -0
  52. package/scripts/release-weekly-ops-gate.js +423 -0
  53. package/scripts/state-migration-reconciliation-gate.js +110 -0
  54. package/scripts/state-storage-tiering-audit.js +337 -0
  55. package/scripts/steering-content-audit.js +393 -0
  56. package/scripts/symbol-evidence-locate.js +366 -0
@@ -0,0 +1,852 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const fs = require('fs-extra');
6
+
7
+ const DEFAULT_BASELINE = '.sce/reports/release-evidence/moqui-template-baseline.json';
8
+ const DEFAULT_OUT = '.sce/reports/release-evidence/matrix-remediation-plan.json';
9
+ const DEFAULT_LINES_OUT = '.sce/auto/matrix-remediation.lines';
10
+ const DEFAULT_MARKDOWN_OUT = '.sce/reports/release-evidence/matrix-remediation-plan.md';
11
+ const DEFAULT_BATCH_JSON_OUT = '.sce/auto/matrix-remediation.goals.json';
12
+ const DEFAULT_CAPABILITY_CLUSTER_GOALS_OUT = '.sce/auto/matrix-remediation.capability-clusters.json';
13
+ const DEFAULT_COMMANDS_OUT = '.sce/reports/release-evidence/matrix-remediation-commands.md';
14
+ const DEFAULT_TOP_TEMPLATES = 5;
15
+ const DEFAULT_PHASE_HIGH_PARALLEL = 1;
16
+ const DEFAULT_PHASE_HIGH_AGENT_BUDGET = 2;
17
+ const DEFAULT_PHASE_MEDIUM_PARALLEL = 1;
18
+ const DEFAULT_PHASE_MEDIUM_AGENT_BUDGET = 2;
19
+ const DEFAULT_PHASE_COOLDOWN_SECONDS = 30;
20
+
21
+ function parseArgs(argv) {
22
+ const options = {
23
+ baseline: DEFAULT_BASELINE,
24
+ out: DEFAULT_OUT,
25
+ linesOut: DEFAULT_LINES_OUT,
26
+ markdownOut: DEFAULT_MARKDOWN_OUT,
27
+ batchJsonOut: DEFAULT_BATCH_JSON_OUT,
28
+ capabilityClusterGoalsOut: DEFAULT_CAPABILITY_CLUSTER_GOALS_OUT,
29
+ commandsOut: DEFAULT_COMMANDS_OUT,
30
+ phaseSplit: true,
31
+ phaseHighLinesOut: null,
32
+ phaseMediumLinesOut: null,
33
+ phaseHighGoalsOut: null,
34
+ phaseMediumGoalsOut: null,
35
+ phaseHighParallel: DEFAULT_PHASE_HIGH_PARALLEL,
36
+ phaseHighAgentBudget: DEFAULT_PHASE_HIGH_AGENT_BUDGET,
37
+ phaseMediumParallel: DEFAULT_PHASE_MEDIUM_PARALLEL,
38
+ phaseMediumAgentBudget: DEFAULT_PHASE_MEDIUM_AGENT_BUDGET,
39
+ phaseCooldownSeconds: DEFAULT_PHASE_COOLDOWN_SECONDS,
40
+ minDeltaAbs: 0,
41
+ topTemplates: DEFAULT_TOP_TEMPLATES,
42
+ json: false
43
+ };
44
+
45
+ for (let index = 0; index < argv.length; index += 1) {
46
+ const token = argv[index];
47
+ const next = argv[index + 1];
48
+ if (token === '--baseline' && next) {
49
+ options.baseline = next;
50
+ index += 1;
51
+ } else if (token === '--out' && next) {
52
+ options.out = next;
53
+ index += 1;
54
+ } else if (token === '--lines-out' && next) {
55
+ options.linesOut = next;
56
+ index += 1;
57
+ } else if (token === '--markdown-out' && next) {
58
+ options.markdownOut = next;
59
+ index += 1;
60
+ } else if (token === '--batch-json-out' && next) {
61
+ options.batchJsonOut = next;
62
+ index += 1;
63
+ } else if (token === '--capability-cluster-goals-out' && next) {
64
+ options.capabilityClusterGoalsOut = next;
65
+ index += 1;
66
+ } else if (token === '--commands-out' && next) {
67
+ options.commandsOut = next;
68
+ index += 1;
69
+ } else if (token === '--no-phase-split') {
70
+ options.phaseSplit = false;
71
+ } else if (token === '--phase-high-lines-out' && next) {
72
+ options.phaseHighLinesOut = next;
73
+ index += 1;
74
+ } else if (token === '--phase-medium-lines-out' && next) {
75
+ options.phaseMediumLinesOut = next;
76
+ index += 1;
77
+ } else if (token === '--phase-high-goals-out' && next) {
78
+ options.phaseHighGoalsOut = next;
79
+ index += 1;
80
+ } else if (token === '--phase-medium-goals-out' && next) {
81
+ options.phaseMediumGoalsOut = next;
82
+ index += 1;
83
+ } else if (token === '--phase-high-parallel' && next) {
84
+ options.phaseHighParallel = Number(next);
85
+ index += 1;
86
+ } else if (token === '--phase-high-agent-budget' && next) {
87
+ options.phaseHighAgentBudget = Number(next);
88
+ index += 1;
89
+ } else if (token === '--phase-medium-parallel' && next) {
90
+ options.phaseMediumParallel = Number(next);
91
+ index += 1;
92
+ } else if (token === '--phase-medium-agent-budget' && next) {
93
+ options.phaseMediumAgentBudget = Number(next);
94
+ index += 1;
95
+ } else if (token === '--phase-cooldown-seconds' && next) {
96
+ options.phaseCooldownSeconds = Number(next);
97
+ index += 1;
98
+ } else if (token === '--min-delta-abs' && next) {
99
+ options.minDeltaAbs = Number(next);
100
+ index += 1;
101
+ } else if (token === '--top-templates' && next) {
102
+ options.topTemplates = Number(next);
103
+ index += 1;
104
+ } else if (token === '--json') {
105
+ options.json = true;
106
+ } else if (token === '--help' || token === '-h') {
107
+ printHelpAndExit(0);
108
+ }
109
+ }
110
+
111
+ if (!Number.isFinite(options.minDeltaAbs) || options.minDeltaAbs < 0) {
112
+ throw new Error('--min-delta-abs must be a non-negative number.');
113
+ }
114
+ if (!Number.isFinite(options.topTemplates) || options.topTemplates < 1) {
115
+ throw new Error('--top-templates must be a positive number.');
116
+ }
117
+ if (!Number.isFinite(options.phaseHighParallel) || options.phaseHighParallel < 1) {
118
+ throw new Error('--phase-high-parallel must be a positive number.');
119
+ }
120
+ if (!Number.isFinite(options.phaseHighAgentBudget) || options.phaseHighAgentBudget < 1) {
121
+ throw new Error('--phase-high-agent-budget must be a positive number.');
122
+ }
123
+ if (!Number.isFinite(options.phaseMediumParallel) || options.phaseMediumParallel < 1) {
124
+ throw new Error('--phase-medium-parallel must be a positive number.');
125
+ }
126
+ if (!Number.isFinite(options.phaseMediumAgentBudget) || options.phaseMediumAgentBudget < 1) {
127
+ throw new Error('--phase-medium-agent-budget must be a positive number.');
128
+ }
129
+ if (!Number.isFinite(options.phaseCooldownSeconds) || options.phaseCooldownSeconds < 0) {
130
+ throw new Error('--phase-cooldown-seconds must be a non-negative number.');
131
+ }
132
+
133
+ return options;
134
+ }
135
+
136
+ function printHelpAndExit(code) {
137
+ const lines = [
138
+ 'Usage: node scripts/moqui-matrix-remediation-queue.js [options]',
139
+ '',
140
+ 'Options:',
141
+ ` --baseline <path> Baseline report JSON path (default: ${DEFAULT_BASELINE})`,
142
+ ` --out <path> Remediation plan JSON output (default: ${DEFAULT_OUT})`,
143
+ ` --lines-out <path> Queue lines output for close-loop-batch (default: ${DEFAULT_LINES_OUT})`,
144
+ ` --markdown-out <path> Remediation markdown output (default: ${DEFAULT_MARKDOWN_OUT})`,
145
+ ` --batch-json-out <path> Batch goals JSON output for close-loop-batch (default: ${DEFAULT_BATCH_JSON_OUT})`,
146
+ ` --capability-cluster-goals-out <path> Capability-cluster goals JSON output (default: ${DEFAULT_CAPABILITY_CLUSTER_GOALS_OUT})`,
147
+ ` --commands-out <path> Suggested command list markdown (default: ${DEFAULT_COMMANDS_OUT})`,
148
+ ' --no-phase-split Disable priority split outputs (high/medium phase files)',
149
+ ' --phase-high-lines-out <path> High-priority queue lines output path',
150
+ ' --phase-medium-lines-out <path> Medium-priority queue lines output path',
151
+ ' --phase-high-goals-out <path> High-priority goals JSON output path',
152
+ ' --phase-medium-goals-out <path> Medium-priority goals JSON output path',
153
+ ` --phase-high-parallel <n> Suggested close-loop-batch parallel for high phase (default: ${DEFAULT_PHASE_HIGH_PARALLEL})`,
154
+ ` --phase-high-agent-budget <n> Suggested agent budget for high phase (default: ${DEFAULT_PHASE_HIGH_AGENT_BUDGET})`,
155
+ ` --phase-medium-parallel <n> Suggested close-loop-batch parallel for medium phase (default: ${DEFAULT_PHASE_MEDIUM_PARALLEL})`,
156
+ ` --phase-medium-agent-budget <n> Suggested agent budget for medium phase (default: ${DEFAULT_PHASE_MEDIUM_AGENT_BUDGET})`,
157
+ ` --phase-cooldown-seconds <n> Suggested cooldown seconds between phases (default: ${DEFAULT_PHASE_COOLDOWN_SECONDS})`,
158
+ ' --min-delta-abs <n> Skip regressions with absolute delta < n (default: 0)',
159
+ ` --top-templates <n> Max affected templates listed per remediation (default: ${DEFAULT_TOP_TEMPLATES})`,
160
+ ' --json Print payload as JSON',
161
+ ' -h, --help Show this help'
162
+ ];
163
+ console.log(lines.join('\n'));
164
+ process.exit(code);
165
+ }
166
+
167
+ function resolvePath(cwd, value) {
168
+ return path.isAbsolute(value) ? value : path.resolve(cwd, value);
169
+ }
170
+
171
+ function derivePhasePath(basePath, phaseName) {
172
+ const parsed = path.parse(basePath);
173
+ return path.join(parsed.dir, `${parsed.name}.${phaseName}${parsed.ext}`);
174
+ }
175
+
176
+ function pickRegressions(payload) {
177
+ const compare = payload && payload.compare && typeof payload.compare === 'object'
178
+ ? payload.compare
179
+ : {};
180
+ if (Array.isArray(compare.coverage_matrix_regressions)) {
181
+ return compare.coverage_matrix_regressions;
182
+ }
183
+ if (Array.isArray(compare.regressions)) {
184
+ return compare.regressions;
185
+ }
186
+ return [];
187
+ }
188
+
189
+ function metricToFocus(metric = '') {
190
+ const normalized = `${metric || ''}`.trim().toLowerCase();
191
+ const map = {
192
+ graph_valid: 'ontology graph consistency',
193
+ score_passed: 'semantic score uplift',
194
+ entity_coverage: 'entity modeling coverage',
195
+ relation_coverage: 'relation modeling coverage',
196
+ business_rule_coverage: 'business rule extraction coverage',
197
+ business_rule_closed: 'business rule mapping closure',
198
+ decision_coverage: 'decision logic extraction coverage',
199
+ decision_closed: 'decision closure completeness',
200
+ baseline_passed: 'portfolio baseline closure'
201
+ };
202
+ return map[normalized] || 'template quality closure';
203
+ }
204
+
205
+ function metricToFlag(metric = '') {
206
+ const normalized = `${metric || ''}`.trim().toLowerCase();
207
+ const map = {
208
+ graph_valid: 'graph_valid',
209
+ score_passed: 'score_passed',
210
+ entity_coverage: 'entity_coverage',
211
+ relation_coverage: 'relation_coverage',
212
+ business_rule_coverage: 'business_rule_coverage',
213
+ business_rule_closed: 'business_rule_closed',
214
+ decision_coverage: 'decision_coverage',
215
+ decision_closed: 'decision_closed',
216
+ baseline_passed: 'baseline_passed'
217
+ };
218
+ return map[normalized] || null;
219
+ }
220
+
221
+ function deriveCapabilitiesFromTemplateId(templateId = '') {
222
+ const text = `${templateId || ''}`.toLowerCase();
223
+ const normalized = text
224
+ .replace(/^sce\.scene--/g, '')
225
+ .replace(/^sce\.scene--/g, '')
226
+ .replace(/--\d+\.\d+\.\d+$/g, '')
227
+ .replace(/[._]/g, '-');
228
+ const parts = normalized
229
+ .split('-')
230
+ .map(item => item.trim())
231
+ .filter(item => item.length > 2 && !['scene', 'template', 'moqui', 'suite', 'erp'].includes(item));
232
+ return Array.from(new Set(parts)).slice(0, 6);
233
+ }
234
+
235
+ function collectTemplateCandidates(baselinePayload = {}, metric = '', topTemplates = DEFAULT_TOP_TEMPLATES) {
236
+ const templates = Array.isArray(baselinePayload && baselinePayload.templates)
237
+ ? baselinePayload.templates
238
+ : [];
239
+ const flagName = metricToFlag(metric);
240
+ const filtered = templates.filter((item) => {
241
+ if (!item || !item.baseline || !item.baseline.flags) {
242
+ return false;
243
+ }
244
+ if (!flagName) {
245
+ return item.baseline.flags.baseline_passed !== true;
246
+ }
247
+ return item.baseline.flags[flagName] !== true;
248
+ });
249
+
250
+ const scored = filtered.map((item) => {
251
+ const score = Number(item && item.semantic ? item.semantic.score : null);
252
+ const gaps = Array.isArray(item && item.baseline ? item.baseline.gaps : [])
253
+ ? item.baseline.gaps
254
+ : [];
255
+ const capabilities = Array.isArray(item && item.capabilities_provides)
256
+ ? item.capabilities_provides
257
+ : deriveCapabilitiesFromTemplateId(item && item.template_id ? item.template_id : '');
258
+ return {
259
+ template_id: item && item.template_id ? item.template_id : null,
260
+ score: Number.isFinite(score) ? score : null,
261
+ gaps,
262
+ capabilities
263
+ };
264
+ });
265
+
266
+ scored.sort((a, b) => {
267
+ const scoreA = Number.isFinite(Number(a.score)) ? Number(a.score) : 999;
268
+ const scoreB = Number.isFinite(Number(b.score)) ? Number(b.score) : 999;
269
+ if (scoreA !== scoreB) {
270
+ return scoreA - scoreB;
271
+ }
272
+ return (b.gaps.length || 0) - (a.gaps.length || 0);
273
+ });
274
+
275
+ return scored.slice(0, Math.max(1, Number(topTemplates)));
276
+ }
277
+
278
+ function buildQueueItem(regression = {}, index = 0, baselinePayload = {}, topTemplates = DEFAULT_TOP_TEMPLATES) {
279
+ const metric = regression && regression.metric ? String(regression.metric) : 'unknown_metric';
280
+ const delta = Number(regression && regression.delta_rate_percent);
281
+ const deltaValue = Number.isFinite(delta) ? Number(delta.toFixed(2)) : null;
282
+ const focus = metricToFocus(metric);
283
+ const priority = Number.isFinite(deltaValue) && deltaValue <= -20 ? 'high' : 'medium';
284
+ const templates = collectTemplateCandidates(baselinePayload, metric, topTemplates);
285
+ const templateIds = templates
286
+ .map(item => item.template_id)
287
+ .filter(Boolean);
288
+ const capabilityFocus = Array.from(new Set(
289
+ templates.flatMap(item => Array.isArray(item.capabilities) ? item.capabilities : [])
290
+ )).slice(0, 10);
291
+ const goal = `Recover matrix regression for ${metric} (${deltaValue == null ? 'n/a' : `${deltaValue}%`}) by closing ${focus} in templates: ${templateIds.length > 0 ? templateIds.join(', ') : 'TBD'}.`;
292
+ return {
293
+ id: `matrix-remediate-${index + 1}`,
294
+ metric,
295
+ delta_rate_percent: deltaValue,
296
+ focus,
297
+ priority,
298
+ template_candidates: templates,
299
+ capability_focus: capabilityFocus,
300
+ goal
301
+ };
302
+ }
303
+
304
+ function collectTemplatePriorityMatrix(items = []) {
305
+ const bucket = new Map();
306
+ for (const item of Array.isArray(items) ? items : []) {
307
+ if (!item || !Array.isArray(item.template_candidates)) {
308
+ continue;
309
+ }
310
+ const deltaAbs = Number(item.delta_rate_percent);
311
+ const pressure = Number.isFinite(deltaAbs) ? Math.abs(deltaAbs) : 0;
312
+ const high = item.priority === 'high';
313
+ for (const template of item.template_candidates) {
314
+ const templateId = template && template.template_id ? String(template.template_id).trim() : '';
315
+ if (!templateId) {
316
+ continue;
317
+ }
318
+ if (!bucket.has(templateId)) {
319
+ bucket.set(templateId, {
320
+ template_id: templateId,
321
+ impacted_metrics: new Set(),
322
+ focus_areas: new Set(),
323
+ capabilities: new Set(),
324
+ regression_pressure: 0,
325
+ high_priority_hits: 0,
326
+ medium_priority_hits: 0,
327
+ gap_count: 0,
328
+ semantic_scores: []
329
+ });
330
+ }
331
+ const row = bucket.get(templateId);
332
+ row.impacted_metrics.add(item.metric || 'unknown_metric');
333
+ row.focus_areas.add(item.focus || 'template quality closure');
334
+ for (const cap of Array.isArray(template.capabilities) ? template.capabilities : []) {
335
+ const key = `${cap || ''}`.trim();
336
+ if (key) {
337
+ row.capabilities.add(key);
338
+ }
339
+ }
340
+ row.regression_pressure += pressure;
341
+ if (high) {
342
+ row.high_priority_hits += 1;
343
+ } else {
344
+ row.medium_priority_hits += 1;
345
+ }
346
+ const gapsCount = Array.isArray(template.gaps) ? template.gaps.length : 0;
347
+ row.gap_count = Math.max(row.gap_count, gapsCount);
348
+ const score = Number(template.score);
349
+ if (Number.isFinite(score)) {
350
+ row.semantic_scores.push(score);
351
+ }
352
+ }
353
+ }
354
+
355
+ const output = Array.from(bucket.values()).map((row) => {
356
+ const semanticScore = row.semantic_scores.length > 0
357
+ ? Math.min(...row.semantic_scores)
358
+ : null;
359
+ const semanticPenalty = Number.isFinite(semanticScore)
360
+ ? Math.max(0, 80 - semanticScore)
361
+ : 0;
362
+ const priorityScore = Number(
363
+ (
364
+ row.regression_pressure +
365
+ (row.high_priority_hits * 25) +
366
+ (row.medium_priority_hits * 10) +
367
+ (row.gap_count * 2) +
368
+ semanticPenalty
369
+ ).toFixed(2)
370
+ );
371
+ return {
372
+ template_id: row.template_id,
373
+ priority_score: priorityScore,
374
+ recommended_phase: row.high_priority_hits > 0 ? 'high' : 'medium',
375
+ regression_pressure: Number(row.regression_pressure.toFixed(2)),
376
+ high_priority_hits: row.high_priority_hits,
377
+ medium_priority_hits: row.medium_priority_hits,
378
+ impacted_metric_count: row.impacted_metrics.size,
379
+ impacted_metrics: Array.from(row.impacted_metrics).sort(),
380
+ focus_areas: Array.from(row.focus_areas).sort(),
381
+ capabilities: Array.from(row.capabilities).sort(),
382
+ gap_count: row.gap_count,
383
+ semantic_score: Number.isFinite(semanticScore) ? semanticScore : null
384
+ };
385
+ });
386
+
387
+ output.sort((a, b) => {
388
+ if (b.priority_score !== a.priority_score) {
389
+ return b.priority_score - a.priority_score;
390
+ }
391
+ if (b.high_priority_hits !== a.high_priority_hits) {
392
+ return b.high_priority_hits - a.high_priority_hits;
393
+ }
394
+ return a.template_id.localeCompare(b.template_id);
395
+ });
396
+ return output;
397
+ }
398
+
399
+ function collectCapabilityClusters(items = [], templatePriorityMatrix = []) {
400
+ const bucket = new Map();
401
+ for (const item of Array.isArray(items) ? items : []) {
402
+ const capabilities = Array.isArray(item && item.capability_focus)
403
+ ? item.capability_focus.map(cap => `${cap || ''}`.trim()).filter(Boolean)
404
+ : [];
405
+ if (capabilities.length === 0) {
406
+ continue;
407
+ }
408
+ const deltaAbs = Number(item.delta_rate_percent);
409
+ const pressure = Number.isFinite(deltaAbs) ? Math.abs(deltaAbs) : 0;
410
+ const templateIds = Array.isArray(item.template_candidates)
411
+ ? item.template_candidates.map(candidate => candidate && candidate.template_id ? `${candidate.template_id}`.trim() : '').filter(Boolean)
412
+ : [];
413
+ for (const capability of capabilities) {
414
+ if (!bucket.has(capability)) {
415
+ bucket.set(capability, {
416
+ capability,
417
+ impacted_metrics: new Set(),
418
+ focus_areas: new Set(),
419
+ templates: new Set(),
420
+ total_regression_pressure: 0,
421
+ high_priority_hits: 0,
422
+ medium_priority_hits: 0
423
+ });
424
+ }
425
+ const row = bucket.get(capability);
426
+ row.impacted_metrics.add(item.metric || 'unknown_metric');
427
+ row.focus_areas.add(item.focus || 'template quality closure');
428
+ for (const templateId of templateIds) {
429
+ row.templates.add(templateId);
430
+ }
431
+ row.total_regression_pressure += pressure;
432
+ if (item.priority === 'high') {
433
+ row.high_priority_hits += 1;
434
+ } else {
435
+ row.medium_priority_hits += 1;
436
+ }
437
+ }
438
+ }
439
+
440
+ const output = Array.from(bucket.values()).map((row) => {
441
+ const suggestedTemplates = (Array.isArray(templatePriorityMatrix) ? templatePriorityMatrix : [])
442
+ .filter(item => Array.isArray(item.capabilities) && item.capabilities.includes(row.capability))
443
+ .slice(0, 5)
444
+ .map(item => ({
445
+ template_id: item.template_id,
446
+ recommended_phase: item.recommended_phase,
447
+ priority_score: item.priority_score
448
+ }));
449
+ const priorityScore = Number(
450
+ (
451
+ row.total_regression_pressure +
452
+ (row.high_priority_hits * 20) +
453
+ (row.medium_priority_hits * 8) +
454
+ (row.impacted_metrics.size * 5) +
455
+ (row.templates.size * 2)
456
+ ).toFixed(2)
457
+ );
458
+ return {
459
+ capability: row.capability,
460
+ priority_score: priorityScore,
461
+ regression_pressure: Number(row.total_regression_pressure.toFixed(2)),
462
+ impacted_metric_count: row.impacted_metrics.size,
463
+ impacted_metrics: Array.from(row.impacted_metrics).sort(),
464
+ high_priority_hits: row.high_priority_hits,
465
+ medium_priority_hits: row.medium_priority_hits,
466
+ template_count: row.templates.size,
467
+ templates: Array.from(row.templates).sort(),
468
+ focus_areas: Array.from(row.focus_areas).sort(),
469
+ suggested_templates: suggestedTemplates
470
+ };
471
+ });
472
+
473
+ output.sort((a, b) => {
474
+ if (b.priority_score !== a.priority_score) {
475
+ return b.priority_score - a.priority_score;
476
+ }
477
+ if (b.high_priority_hits !== a.high_priority_hits) {
478
+ return b.high_priority_hits - a.high_priority_hits;
479
+ }
480
+ return a.capability.localeCompare(b.capability);
481
+ });
482
+ return output;
483
+ }
484
+
485
+ function buildMarkdown(payload) {
486
+ const lines = [];
487
+ lines.push('# Matrix Remediation Plan');
488
+ lines.push('');
489
+ lines.push(`- Generated at: ${payload.generated_at}`);
490
+ lines.push(`- Baseline: ${payload.baseline.path}`);
491
+ lines.push(`- Regressions selected: ${payload.summary.selected_regressions}`);
492
+ lines.push(`- Queue lines: ${payload.artifacts.lines_out}`);
493
+ lines.push('');
494
+ lines.push('## Queue');
495
+ lines.push('');
496
+ if (!Array.isArray(payload.items) || payload.items.length === 0) {
497
+ lines.push('- none');
498
+ return `${lines.join('\n')}\n`;
499
+ }
500
+ for (const item of payload.items) {
501
+ const capabilities = Array.isArray(item.capability_focus) && item.capability_focus.length > 0
502
+ ? item.capability_focus.join(', ')
503
+ : 'n/a';
504
+ lines.push(`- [${item.priority}] ${item.goal}`);
505
+ lines.push(` - capability focus: ${capabilities}`);
506
+ }
507
+ if (Array.isArray(payload.template_priority_matrix) && payload.template_priority_matrix.length > 0) {
508
+ lines.push('');
509
+ lines.push('## Template Priority Matrix');
510
+ lines.push('');
511
+ for (const item of payload.template_priority_matrix.slice(0, 10)) {
512
+ lines.push(
513
+ `- [${item.recommended_phase}] ${item.template_id} ` +
514
+ `(priority=${item.priority_score}, pressure=${item.regression_pressure}, metrics=${item.impacted_metric_count})`
515
+ );
516
+ }
517
+ }
518
+ if (Array.isArray(payload.capability_clusters) && payload.capability_clusters.length > 0) {
519
+ lines.push('');
520
+ lines.push('## Capability Clusters');
521
+ lines.push('');
522
+ for (const item of payload.capability_clusters.slice(0, 10)) {
523
+ lines.push(
524
+ `- ${item.capability} ` +
525
+ `(priority=${item.priority_score}, metrics=${item.impacted_metric_count}, templates=${item.template_count})`
526
+ );
527
+ }
528
+ }
529
+ return `${lines.join('\n')}\n`;
530
+ }
531
+
532
+ function quoteCliArg(value = '') {
533
+ const text = `${value || ''}`;
534
+ if (!text) {
535
+ return '""';
536
+ }
537
+ if (/^[\w./\\:-]+$/.test(text)) {
538
+ return text;
539
+ }
540
+ return `"${text.replace(/"/g, '\\"')}"`;
541
+ }
542
+
543
+ function buildBatchGoalsPayload(items = []) {
544
+ return {
545
+ goals: (Array.isArray(items) ? items : [])
546
+ .map(item => item && item.goal ? String(item.goal).trim() : '')
547
+ .filter(Boolean)
548
+ };
549
+ }
550
+
551
+ function buildCapabilityClusterGoalsPayload(capabilityClusters = [], items = []) {
552
+ const normalizedItems = Array.isArray(items) ? items : [];
553
+ const clusters = [];
554
+ const allGoals = [];
555
+ const allSeen = new Set();
556
+ for (const cluster of Array.isArray(capabilityClusters) ? capabilityClusters : []) {
557
+ if (!cluster || !cluster.capability) {
558
+ continue;
559
+ }
560
+ const capability = `${cluster.capability}`.trim();
561
+ if (!capability) {
562
+ continue;
563
+ }
564
+ const matchedItems = normalizedItems.filter((item) => (
565
+ Array.isArray(item && item.capability_focus) &&
566
+ item.capability_focus.includes(capability)
567
+ ));
568
+ const goals = [];
569
+ const seen = new Set();
570
+ for (const item of matchedItems) {
571
+ const goal = item && item.goal ? `${item.goal}`.trim() : '';
572
+ if (!goal || seen.has(goal)) {
573
+ continue;
574
+ }
575
+ seen.add(goal);
576
+ goals.push(goal);
577
+ if (!allSeen.has(goal)) {
578
+ allSeen.add(goal);
579
+ allGoals.push(goal);
580
+ }
581
+ }
582
+ const templates = Array.from(new Set(
583
+ matchedItems.flatMap(item => (
584
+ Array.isArray(item && item.template_candidates)
585
+ ? item.template_candidates.map(candidate => candidate && candidate.template_id ? `${candidate.template_id}`.trim() : '').filter(Boolean)
586
+ : []
587
+ ))
588
+ ));
589
+ const metrics = Array.from(new Set(
590
+ matchedItems.map(item => item && item.metric ? `${item.metric}`.trim() : '').filter(Boolean)
591
+ ));
592
+ clusters.push({
593
+ capability,
594
+ priority_score: Number(cluster.priority_score) || 0,
595
+ recommended_phase: cluster.recommended_phase || (
596
+ matchedItems.some(item => item && item.priority === 'high') ? 'high' : 'medium'
597
+ ),
598
+ goal_count: goals.length,
599
+ template_count: templates.length,
600
+ metric_count: metrics.length,
601
+ templates,
602
+ metrics,
603
+ goals
604
+ });
605
+ }
606
+
607
+ return {
608
+ mode: 'moqui-matrix-capability-cluster-goals',
609
+ generated_at: new Date().toISOString(),
610
+ summary: {
611
+ cluster_count: clusters.length,
612
+ goal_count: allGoals.length
613
+ },
614
+ clusters,
615
+ goals: allGoals
616
+ };
617
+ }
618
+
619
+ function buildCommandsMarkdown(payload = {}) {
620
+ const lines = [];
621
+ const artifacts = payload && payload.artifacts ? payload.artifacts : {};
622
+ const executionPolicy = payload && payload.execution_policy ? payload.execution_policy : {};
623
+ const baselinePath = payload && payload.baseline && payload.baseline.path
624
+ ? payload.baseline.path
625
+ : DEFAULT_BASELINE;
626
+ const linesOut = artifacts.lines_out || DEFAULT_LINES_OUT;
627
+ const batchJsonOut = artifacts.batch_json_out || DEFAULT_BATCH_JSON_OUT;
628
+ const capabilityClusterGoalsOut = artifacts.capability_cluster_goals_out || DEFAULT_CAPABILITY_CLUSTER_GOALS_OUT;
629
+ const highLinesOut = artifacts.phase_high_lines_out || null;
630
+ const mediumLinesOut = artifacts.phase_medium_lines_out || null;
631
+ const highGoalsOut = artifacts.phase_high_goals_out || null;
632
+ const mediumGoalsOut = artifacts.phase_medium_goals_out || null;
633
+ const highParallel = Number.isFinite(Number(executionPolicy.phase_high_parallel))
634
+ ? Number(executionPolicy.phase_high_parallel)
635
+ : DEFAULT_PHASE_HIGH_PARALLEL;
636
+ const highAgentBudget = Number.isFinite(Number(executionPolicy.phase_high_agent_budget))
637
+ ? Number(executionPolicy.phase_high_agent_budget)
638
+ : DEFAULT_PHASE_HIGH_AGENT_BUDGET;
639
+ const mediumParallel = Number.isFinite(Number(executionPolicy.phase_medium_parallel))
640
+ ? Number(executionPolicy.phase_medium_parallel)
641
+ : DEFAULT_PHASE_MEDIUM_PARALLEL;
642
+ const mediumAgentBudget = Number.isFinite(Number(executionPolicy.phase_medium_agent_budget))
643
+ ? Number(executionPolicy.phase_medium_agent_budget)
644
+ : DEFAULT_PHASE_MEDIUM_AGENT_BUDGET;
645
+ const cooldownSeconds = Number.isFinite(Number(executionPolicy.phase_cooldown_seconds))
646
+ ? Number(executionPolicy.phase_cooldown_seconds)
647
+ : DEFAULT_PHASE_COOLDOWN_SECONDS;
648
+ lines.push('# Matrix Remediation Commands');
649
+ lines.push('');
650
+ lines.push('## Batch Mode');
651
+ lines.push('');
652
+ lines.push(`- JSON goals: \`sce auto close-loop-batch ${quoteCliArg(batchJsonOut)} --format json --json\``);
653
+ lines.push(`- Lines goals: \`sce auto close-loop-batch ${quoteCliArg(linesOut)} --format lines --json\``);
654
+ lines.push(`- Capability-cluster goals: \`sce auto close-loop-batch ${quoteCliArg(capabilityClusterGoalsOut)} --format json --batch-parallel 1 --batch-agent-budget 2 --json\``);
655
+ lines.push('');
656
+ lines.push('## Capability Cluster Mode');
657
+ lines.push('');
658
+ lines.push(`- Generate/refresh cluster plan: \`node scripts/moqui-matrix-remediation-queue.js --baseline ${quoteCliArg(baselinePath)} --capability-cluster-goals-out ${quoteCliArg(capabilityClusterGoalsOut)} --json\``);
659
+ lines.push(`- Execute by cluster-prioritized goals: \`sce auto close-loop-batch ${quoteCliArg(capabilityClusterGoalsOut)} --format json --batch-parallel 1 --batch-agent-budget 2 --batch-retry-until-complete --json\``);
660
+ lines.push(`- Execute by phased cluster mode: \`node scripts/moqui-matrix-remediation-phased-runner.js --cluster-goals ${quoteCliArg(capabilityClusterGoalsOut)} --json\``);
661
+ lines.push('- npm alias (cluster phased): `npm run run:matrix-remediation-clusters-phased -- --json`');
662
+ if (highLinesOut && mediumLinesOut && highGoalsOut && mediumGoalsOut) {
663
+ lines.push('');
664
+ lines.push('## Rate-Limit Safe Phased Mode');
665
+ lines.push('');
666
+ lines.push('1. High-priority phase (low parallel):');
667
+ lines.push(` - \`sce auto close-loop-batch ${quoteCliArg(highGoalsOut)} --format json --batch-parallel ${highParallel} --batch-agent-budget ${highAgentBudget} --batch-retry-until-complete --batch-retry-max-rounds 3 --json\``);
668
+ lines.push('2. Cooldown before next phase:');
669
+ lines.push(` - \`sleep ${cooldownSeconds}\``);
670
+ lines.push('3. Medium-priority phase:');
671
+ lines.push(` - \`sce auto close-loop-batch ${quoteCliArg(mediumGoalsOut)} --format json --batch-parallel ${mediumParallel} --batch-agent-budget ${mediumAgentBudget} --batch-retry-until-complete --batch-retry-max-rounds 2 --json\``);
672
+ lines.push('4. Optional lines fallback:');
673
+ lines.push(` - high: \`sce auto close-loop-batch ${quoteCliArg(highLinesOut)} --format lines --json\``);
674
+ lines.push(` - medium: \`sce auto close-loop-batch ${quoteCliArg(mediumLinesOut)} --format lines --json\``);
675
+ lines.push('5. One-shot phased runner:');
676
+ lines.push(` - \`node scripts/moqui-matrix-remediation-phased-runner.js --high-goals ${quoteCliArg(highGoalsOut)} --medium-goals ${quoteCliArg(mediumGoalsOut)} --high-lines ${quoteCliArg(highLinesOut)} --medium-lines ${quoteCliArg(mediumLinesOut)} --phase-high-parallel ${highParallel} --phase-high-agent-budget ${highAgentBudget} --phase-medium-parallel ${mediumParallel} --phase-medium-agent-budget ${mediumAgentBudget} --phase-cooldown-seconds ${cooldownSeconds} --json\``);
677
+ lines.push('6. One-shot from baseline (prepare + run):');
678
+ lines.push(` - \`node scripts/moqui-matrix-remediation-phased-runner.js --baseline ${quoteCliArg(baselinePath)} --high-goals ${quoteCliArg(highGoalsOut)} --medium-goals ${quoteCliArg(mediumGoalsOut)} --high-lines ${quoteCliArg(highLinesOut)} --medium-lines ${quoteCliArg(mediumLinesOut)} --phase-high-parallel ${highParallel} --phase-high-agent-budget ${highAgentBudget} --phase-medium-parallel ${mediumParallel} --phase-medium-agent-budget ${mediumAgentBudget} --phase-cooldown-seconds ${cooldownSeconds} --json\``);
679
+ }
680
+ lines.push('');
681
+ lines.push('## Per Goal');
682
+ lines.push('');
683
+ if (!Array.isArray(payload.items) || payload.items.length === 0) {
684
+ lines.push('- none');
685
+ return `${lines.join('\n')}\n`;
686
+ }
687
+ for (const item of payload.items) {
688
+ const goal = item && item.goal ? String(item.goal) : '';
689
+ lines.push(`- \`sce auto close-loop ${quoteCliArg(goal)} --json\``);
690
+ }
691
+ return `${lines.join('\n')}\n`;
692
+ }
693
+
694
+ async function main() {
695
+ const options = parseArgs(process.argv.slice(2));
696
+ const cwd = process.cwd();
697
+ const baselinePath = resolvePath(cwd, options.baseline);
698
+ const outPath = resolvePath(cwd, options.out);
699
+ const linesOutPath = resolvePath(cwd, options.linesOut);
700
+ const markdownOutPath = resolvePath(cwd, options.markdownOut);
701
+ const batchJsonOutPath = resolvePath(cwd, options.batchJsonOut);
702
+ const capabilityClusterGoalsOutPath = resolvePath(cwd, options.capabilityClusterGoalsOut);
703
+ const commandsOutPath = resolvePath(cwd, options.commandsOut);
704
+ const phaseHighLinesOutPath = options.phaseHighLinesOut
705
+ ? resolvePath(cwd, options.phaseHighLinesOut)
706
+ : derivePhasePath(linesOutPath, 'high');
707
+ const phaseMediumLinesOutPath = options.phaseMediumLinesOut
708
+ ? resolvePath(cwd, options.phaseMediumLinesOut)
709
+ : derivePhasePath(linesOutPath, 'medium');
710
+ const phaseHighGoalsOutPath = options.phaseHighGoalsOut
711
+ ? resolvePath(cwd, options.phaseHighGoalsOut)
712
+ : derivePhasePath(batchJsonOutPath, 'high');
713
+ const phaseMediumGoalsOutPath = options.phaseMediumGoalsOut
714
+ ? resolvePath(cwd, options.phaseMediumGoalsOut)
715
+ : derivePhasePath(batchJsonOutPath, 'medium');
716
+ const baselineExists = await fs.pathExists(baselinePath);
717
+ if (!baselineExists) {
718
+ throw new Error(`baseline file not found: ${path.relative(cwd, baselinePath) || baselinePath}`);
719
+ }
720
+
721
+ const baselinePayload = await fs.readJson(baselinePath);
722
+ const regressions = pickRegressions(baselinePayload);
723
+ const minDeltaAbs = Number(options.minDeltaAbs);
724
+ const filtered = regressions.filter((item) => {
725
+ const delta = Number(item && item.delta_rate_percent);
726
+ return Number.isFinite(delta) && Math.abs(delta) >= minDeltaAbs;
727
+ });
728
+ const items = filtered.map((item, index) => buildQueueItem(item, index, baselinePayload, options.topTemplates));
729
+ const highItems = items.filter(item => item && item.priority === 'high');
730
+ const mediumItems = items.filter(item => !item || item.priority !== 'high');
731
+ const templatePriorityMatrix = collectTemplatePriorityMatrix(items);
732
+ const capabilityClusters = collectCapabilityClusters(items, templatePriorityMatrix);
733
+ const capabilityClusterGoals = buildCapabilityClusterGoalsPayload(capabilityClusters, items);
734
+ const queueLines = items.map(item => item.goal);
735
+ const highQueueLines = highItems.map(item => item.goal);
736
+ const mediumQueueLines = mediumItems.map(item => item.goal);
737
+
738
+ const payload = {
739
+ mode: 'moqui-matrix-remediation-queue',
740
+ generated_at: new Date().toISOString(),
741
+ baseline: {
742
+ path: path.relative(cwd, baselinePath) || '.'
743
+ },
744
+ policy: {
745
+ min_delta_abs: minDeltaAbs,
746
+ top_templates: Number(options.topTemplates)
747
+ },
748
+ execution_policy: {
749
+ phase_split: options.phaseSplit === true,
750
+ phase_high_parallel: Number(options.phaseHighParallel),
751
+ phase_high_agent_budget: Number(options.phaseHighAgentBudget),
752
+ phase_medium_parallel: Number(options.phaseMediumParallel),
753
+ phase_medium_agent_budget: Number(options.phaseMediumAgentBudget),
754
+ phase_cooldown_seconds: Number(options.phaseCooldownSeconds)
755
+ },
756
+ summary: {
757
+ regressions_total: regressions.length,
758
+ selected_regressions: items.length,
759
+ phase_high_count: highItems.length,
760
+ phase_medium_count: mediumItems.length,
761
+ template_priority_count: templatePriorityMatrix.length,
762
+ capability_cluster_count: capabilityClusters.length,
763
+ capability_cluster_goal_count: capabilityClusterGoals.summary.goal_count
764
+ },
765
+ items,
766
+ template_priority_matrix: templatePriorityMatrix,
767
+ capability_clusters: capabilityClusters,
768
+ artifacts: {
769
+ out: path.relative(cwd, outPath) || '.',
770
+ lines_out: path.relative(cwd, linesOutPath) || '.',
771
+ markdown_out: path.relative(cwd, markdownOutPath) || '.',
772
+ batch_json_out: path.relative(cwd, batchJsonOutPath) || '.',
773
+ capability_cluster_goals_out: path.relative(cwd, capabilityClusterGoalsOutPath) || '.',
774
+ commands_out: path.relative(cwd, commandsOutPath) || '.',
775
+ phase_high_lines_out: options.phaseSplit ? (path.relative(cwd, phaseHighLinesOutPath) || '.') : null,
776
+ phase_medium_lines_out: options.phaseSplit ? (path.relative(cwd, phaseMediumLinesOutPath) || '.') : null,
777
+ phase_high_goals_out: options.phaseSplit ? (path.relative(cwd, phaseHighGoalsOutPath) || '.') : null,
778
+ phase_medium_goals_out: options.phaseSplit ? (path.relative(cwd, phaseMediumGoalsOutPath) || '.') : null
779
+ }
780
+ };
781
+
782
+ await fs.ensureDir(path.dirname(linesOutPath));
783
+ await fs.writeFile(linesOutPath, queueLines.join('\n') + (queueLines.length > 0 ? '\n' : ''), 'utf8');
784
+ await fs.ensureDir(path.dirname(batchJsonOutPath));
785
+ await fs.writeJson(batchJsonOutPath, buildBatchGoalsPayload(items), { spaces: 2 });
786
+ await fs.ensureDir(path.dirname(capabilityClusterGoalsOutPath));
787
+ await fs.writeJson(capabilityClusterGoalsOutPath, capabilityClusterGoals, { spaces: 2 });
788
+ if (options.phaseSplit) {
789
+ await fs.ensureDir(path.dirname(phaseHighLinesOutPath));
790
+ await fs.writeFile(phaseHighLinesOutPath, highQueueLines.join('\n') + (highQueueLines.length > 0 ? '\n' : ''), 'utf8');
791
+ await fs.ensureDir(path.dirname(phaseMediumLinesOutPath));
792
+ await fs.writeFile(phaseMediumLinesOutPath, mediumQueueLines.join('\n') + (mediumQueueLines.length > 0 ? '\n' : ''), 'utf8');
793
+ await fs.ensureDir(path.dirname(phaseHighGoalsOutPath));
794
+ await fs.writeJson(phaseHighGoalsOutPath, buildBatchGoalsPayload(highItems), { spaces: 2 });
795
+ await fs.ensureDir(path.dirname(phaseMediumGoalsOutPath));
796
+ await fs.writeJson(phaseMediumGoalsOutPath, buildBatchGoalsPayload(mediumItems), { spaces: 2 });
797
+ }
798
+ await fs.ensureDir(path.dirname(outPath));
799
+ await fs.writeJson(outPath, payload, { spaces: 2 });
800
+ await fs.ensureDir(path.dirname(markdownOutPath));
801
+ await fs.writeFile(markdownOutPath, buildMarkdown(payload), 'utf8');
802
+ await fs.ensureDir(path.dirname(commandsOutPath));
803
+ await fs.writeFile(commandsOutPath, buildCommandsMarkdown(payload), 'utf8');
804
+
805
+ if (options.json) {
806
+ process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
807
+ } else {
808
+ process.stdout.write('Moqui matrix remediation queue generated.\n');
809
+ process.stdout.write(`- Regressions selected: ${payload.summary.selected_regressions}\n`);
810
+ process.stdout.write(`- Queue lines: ${payload.artifacts.lines_out}\n`);
811
+ }
812
+ }
813
+
814
+ if (require.main === module) {
815
+ main().catch((error) => {
816
+ console.error(`Moqui matrix remediation queue failed: ${error.message}`);
817
+ process.exit(1);
818
+ });
819
+ }
820
+
821
+ module.exports = {
822
+ DEFAULT_BASELINE,
823
+ DEFAULT_OUT,
824
+ DEFAULT_LINES_OUT,
825
+ DEFAULT_MARKDOWN_OUT,
826
+ DEFAULT_BATCH_JSON_OUT,
827
+ DEFAULT_CAPABILITY_CLUSTER_GOALS_OUT,
828
+ DEFAULT_COMMANDS_OUT,
829
+ DEFAULT_TOP_TEMPLATES,
830
+ DEFAULT_PHASE_HIGH_PARALLEL,
831
+ DEFAULT_PHASE_HIGH_AGENT_BUDGET,
832
+ DEFAULT_PHASE_MEDIUM_PARALLEL,
833
+ DEFAULT_PHASE_MEDIUM_AGENT_BUDGET,
834
+ DEFAULT_PHASE_COOLDOWN_SECONDS,
835
+ parseArgs,
836
+ resolvePath,
837
+ derivePhasePath,
838
+ pickRegressions,
839
+ metricToFocus,
840
+ metricToFlag,
841
+ deriveCapabilitiesFromTemplateId,
842
+ collectTemplateCandidates,
843
+ collectTemplatePriorityMatrix,
844
+ collectCapabilityClusters,
845
+ buildCapabilityClusterGoalsPayload,
846
+ buildQueueItem,
847
+ buildMarkdown,
848
+ quoteCliArg,
849
+ buildBatchGoalsPayload,
850
+ buildCommandsMarkdown,
851
+ main
852
+ };