scene-capability-engine 3.0.2 → 3.0.3

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.
@@ -1538,6 +1538,8 @@ function registerAutoCommands(program) {
1538
1538
  .option('--min-capability-semantic <n>', 'Minimum Moqui capability semantic completeness percent (default: 100)', parseFloat)
1539
1539
  .option('--require-capability-semantic', 'Require capability semantic completeness gate (default: enabled)')
1540
1540
  .option('--no-require-capability-semantic', 'Disable capability semantic completeness gate (not recommended)')
1541
+ .option('--require-capability-lexicon', 'Require capability lexicon normalization gate (default: enabled)')
1542
+ .option('--no-require-capability-lexicon', 'Disable capability lexicon normalization gate (not recommended)')
1541
1543
  .option('--format <type>', 'Matrix report format: json|markdown (default: json)', 'json')
1542
1544
  .option('--out <path>', 'Write matrix report output file')
1543
1545
  .option('--remediation-queue-out <path>', `Write remediation queue lines (default: ${AUTO_HANDOFF_MOQUI_REMEDIATION_QUEUE_FILE})`, AUTO_HANDOFF_MOQUI_REMEDIATION_QUEUE_FILE)
@@ -1586,6 +1588,9 @@ function registerAutoCommands(program) {
1586
1588
  if (result.gates && result.gates.capability_semantic) {
1587
1589
  console.log(chalk.gray(` Semantic gate: ${result.gates.capability_semantic.passed ? 'pass' : 'fail'}`));
1588
1590
  }
1591
+ if (result.gates && result.gates.capability_lexicon) {
1592
+ console.log(chalk.gray(` Lexicon gate: ${result.gates.capability_lexicon.passed ? 'pass' : 'fail'}`));
1593
+ }
1589
1594
  if (result.remediation_queue && result.remediation_queue.file) {
1590
1595
  console.log(chalk.gray(` Remediation queue: ${result.remediation_queue.file} (${result.remediation_queue.goal_count})`));
1591
1596
  }
@@ -1748,6 +1753,15 @@ function registerAutoCommands(program) {
1748
1753
  ? `${moquiSummary.valid_rate_percent}%`
1749
1754
  : 'n/a';
1750
1755
  console.log(chalk.gray(` Portfolio: ${moquiSummary.portfolio_passed === true ? 'pass' : 'fail'} | avg=${scoreText} | valid-rate=${validRateText}`));
1756
+ const entityRateText = formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'entity_coverage', 'rate_percent', '%');
1757
+ const ruleClosedRateText = formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'business_rule_closed', 'rate_percent', '%');
1758
+ const decisionClosedRateText = formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'decision_closed', 'rate_percent', '%');
1759
+ console.log(chalk.gray(` Coverage: entity=${entityRateText} | rule-closed=${ruleClosedRateText} | decision-closed=${decisionClosedRateText}`));
1760
+ const regressionText = formatAutoHandoffMoquiCoverageRegressions(
1761
+ moquiBaseline && moquiBaseline.compare ? moquiBaseline.compare : {},
1762
+ 3
1763
+ );
1764
+ console.log(chalk.gray(` Regressions: ${regressionText}`));
1751
1765
  }
1752
1766
  }
1753
1767
  if (result.current_overview && result.current_overview.scene_package_batch) {
@@ -1883,11 +1897,14 @@ function registerAutoCommands(program) {
1883
1897
  .option('--no-require-ontology-validation', 'Gate: disable manifest ontology_validation requirement (not recommended)')
1884
1898
  .option('--require-moqui-baseline', 'Gate: require Moqui baseline portfolio to pass (default: enabled)')
1885
1899
  .option('--no-require-moqui-baseline', 'Gate: disable Moqui baseline portfolio requirement (not recommended)')
1900
+ .option('--max-moqui-matrix-regressions <n>', 'Gate: maximum allowed Moqui matrix regression signals (default: 0)', parseInt)
1886
1901
  .option('--require-scene-package-batch', 'Gate: require scene package publish-batch dry-run gate to pass when applicable (default: enabled)')
1887
1902
  .option('--no-require-scene-package-batch', 'Gate: disable scene package publish-batch dry-run requirement (not recommended)')
1888
1903
  .option('--min-capability-coverage <n>', 'Gate: minimum Moqui capability coverage percent (default: 100)', parseFloat)
1889
1904
  .option('--require-capability-coverage', 'Gate: require capability coverage threshold when capabilities are declared (default: enabled)')
1890
1905
  .option('--no-require-capability-coverage', 'Gate: disable capability coverage requirement (not recommended)')
1906
+ .option('--require-capability-lexicon', 'Gate: require capability lexicon normalization (unknown expected/provided aliases not allowed, default: enabled)')
1907
+ .option('--no-require-capability-lexicon', 'Gate: disable capability lexicon normalization requirement (not recommended)')
1891
1908
  .option('--require-release-gate-preflight', 'Gate: require release-gate preflight signal to be available and unblocked (default: disabled/advisory)')
1892
1909
  .option('--no-require-release-gate-preflight', 'Gate: disable release-gate preflight hard requirement (default)')
1893
1910
  .option('--release-evidence-window <n>', 'Release evidence trend window size (2-50, default: 5)', parseInt)
@@ -1939,6 +1956,15 @@ function registerAutoCommands(program) {
1939
1956
  ? `${baselineSummary.valid_rate_percent}%`
1940
1957
  : 'n/a';
1941
1958
  console.log(chalk.gray(` Portfolio: ${baselineSummary.portfolio_passed ? 'pass' : 'fail'} | avg=${scoreText} | valid-rate=${validRateText}`));
1959
+ const entityRateText = formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'entity_coverage', 'rate_percent', '%');
1960
+ const ruleClosedRateText = formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'business_rule_closed', 'rate_percent', '%');
1961
+ const decisionClosedRateText = formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'decision_closed', 'rate_percent', '%');
1962
+ console.log(chalk.gray(` Coverage: entity=${entityRateText} | rule-closed=${ruleClosedRateText} | decision-closed=${decisionClosedRateText}`));
1963
+ const regressionText = formatAutoHandoffMoquiCoverageRegressions(
1964
+ result.moqui_baseline && result.moqui_baseline.compare ? result.moqui_baseline.compare : {},
1965
+ 3
1966
+ );
1967
+ console.log(chalk.gray(` Regressions: ${regressionText}`));
1942
1968
  }
1943
1969
  if (result.moqui_baseline.output && result.moqui_baseline.output.json) {
1944
1970
  console.log(chalk.gray(` Baseline report: ${result.moqui_baseline.output.json}`));
@@ -8096,6 +8122,13 @@ function buildAutoHandoffRegressionSnapshot(report) {
8096
8122
  ? scenePackageBatch.status
8097
8123
  : gateActual.scene_package_batch_status
8098
8124
  );
8125
+ const moquiBaseline = payload && payload.moqui_baseline ? payload.moqui_baseline : {};
8126
+ const moquiCompare = moquiBaseline && moquiBaseline.compare ? moquiBaseline.compare : {};
8127
+ const moquiMatrixRegressionCount = Number(
8128
+ gateActual.moqui_matrix_regression_count !== undefined
8129
+ ? gateActual.moqui_matrix_regression_count
8130
+ : buildAutoHandoffMoquiCoverageRegressions(moquiCompare).length
8131
+ );
8099
8132
  let sceneBatchPassed = null;
8100
8133
  if (sceneBatchStatus && sceneBatchStatus !== 'skipped') {
8101
8134
  sceneBatchPassed = sceneBatchStatus === 'passed';
@@ -8119,6 +8152,7 @@ function buildAutoHandoffRegressionSnapshot(report) {
8119
8152
  ontology_undecided_decisions: Number.isFinite(ontologyUndecidedDecisions) ? ontologyUndecidedDecisions : null,
8120
8153
  ontology_business_rule_pass_rate_percent: Number.isFinite(businessRulePassRate) ? businessRulePassRate : null,
8121
8154
  ontology_decision_resolved_rate_percent: Number.isFinite(decisionResolvedRate) ? decisionResolvedRate : null,
8155
+ moqui_matrix_regression_count: Number.isFinite(moquiMatrixRegressionCount) ? moquiMatrixRegressionCount : null,
8122
8156
  scene_package_batch_status: sceneBatchStatus || null,
8123
8157
  scene_package_batch_passed: typeof sceneBatchPassed === 'boolean' ? sceneBatchPassed : null,
8124
8158
  scene_package_batch_failure_count: Number.isFinite(sceneBatchFailureCount) ? sceneBatchFailureCount : null,
@@ -8193,6 +8227,12 @@ function buildAutoHandoffRegressionComparison(currentSnapshot, previousSnapshot)
8193
8227
  )
8194
8228
  ? currentSnapshot.scene_package_batch_failure_count - previousSnapshot.scene_package_batch_failure_count
8195
8229
  : null;
8230
+ const deltaMoquiMatrixRegressionCount = (
8231
+ Number.isFinite(currentSnapshot.moqui_matrix_regression_count) &&
8232
+ Number.isFinite(previousSnapshot.moqui_matrix_regression_count)
8233
+ )
8234
+ ? currentSnapshot.moqui_matrix_regression_count - previousSnapshot.moqui_matrix_regression_count
8235
+ : null;
8196
8236
 
8197
8237
  let trend = 'stable';
8198
8238
  if (
@@ -8212,7 +8252,8 @@ function buildAutoHandoffRegressionComparison(currentSnapshot, previousSnapshot)
8212
8252
  (deltaOntologyQualityScore !== null && deltaOntologyQualityScore < 0) ||
8213
8253
  (deltaOntologyUnmappedRules !== null && deltaOntologyUnmappedRules > 0) ||
8214
8254
  (deltaOntologyUndecidedDecisions !== null && deltaOntologyUndecidedDecisions > 0) ||
8215
- (deltaSceneBatchFailureCount !== null && deltaSceneBatchFailureCount > 0)
8255
+ (deltaSceneBatchFailureCount !== null && deltaSceneBatchFailureCount > 0) ||
8256
+ (deltaMoquiMatrixRegressionCount !== null && deltaMoquiMatrixRegressionCount > 0)
8216
8257
  ) {
8217
8258
  trend = 'degraded';
8218
8259
  }
@@ -8229,6 +8270,7 @@ function buildAutoHandoffRegressionComparison(currentSnapshot, previousSnapshot)
8229
8270
  ontology_undecided_decisions: deltaOntologyUndecidedDecisions,
8230
8271
  ontology_business_rule_pass_rate_percent: deltaBusinessRulePassRate,
8231
8272
  ontology_decision_resolved_rate_percent: deltaDecisionResolvedRate,
8273
+ moqui_matrix_regression_count: deltaMoquiMatrixRegressionCount,
8232
8274
  scene_package_batch_failure_count: deltaSceneBatchFailureCount
8233
8275
  }
8234
8276
  };
@@ -8249,6 +8291,7 @@ function buildAutoHandoffRegressionWindowTrend(series = []) {
8249
8291
  ontology_undecided_decisions: null,
8250
8292
  ontology_business_rule_pass_rate_percent: null,
8251
8293
  ontology_decision_resolved_rate_percent: null,
8294
+ moqui_matrix_regression_count: null,
8252
8295
  scene_package_batch_failure_count: null
8253
8296
  },
8254
8297
  has_baseline: false
@@ -8290,6 +8333,9 @@ function buildAutoHandoffRegressionAggregates(series = []) {
8290
8333
  const sceneBatchFailures = snapshots
8291
8334
  .map(item => Number(item.scene_package_batch_failure_count))
8292
8335
  .filter(value => Number.isFinite(value));
8336
+ const moquiMatrixRegressions = snapshots
8337
+ .map(item => Number(item.moqui_matrix_regression_count))
8338
+ .filter(value => Number.isFinite(value));
8293
8339
  const sceneBatchApplicables = snapshots.filter(item => typeof item.scene_package_batch_passed === 'boolean');
8294
8340
  const sceneBatchPassedCount = sceneBatchApplicables.filter(item => item.scene_package_batch_passed === true).length;
8295
8341
  const sceneBatchFailedCount = sceneBatchApplicables.filter(item => item.scene_package_batch_passed === false).length;
@@ -8332,6 +8378,9 @@ function buildAutoHandoffRegressionAggregates(series = []) {
8332
8378
  const averageSceneBatchFailures = sceneBatchFailures.length > 0
8333
8379
  ? Number((sceneBatchFailures.reduce((sum, value) => sum + value, 0) / sceneBatchFailures.length).toFixed(2))
8334
8380
  : null;
8381
+ const averageMoquiMatrixRegressions = moquiMatrixRegressions.length > 0
8382
+ ? Number((moquiMatrixRegressions.reduce((sum, value) => sum + value, 0) / moquiMatrixRegressions.length).toFixed(2))
8383
+ : null;
8335
8384
  const sceneBatchPassRate = sceneBatchApplicables.length > 0
8336
8385
  ? Number(((sceneBatchPassedCount / sceneBatchApplicables.length) * 100).toFixed(2))
8337
8386
  : null;
@@ -8356,6 +8405,8 @@ function buildAutoHandoffRegressionAggregates(series = []) {
8356
8405
  scene_package_batch_pass_rate_percent: sceneBatchPassRate,
8357
8406
  avg_scene_package_batch_failure_count: averageSceneBatchFailures,
8358
8407
  max_scene_package_batch_failure_count: sceneBatchFailures.length > 0 ? Math.max(...sceneBatchFailures) : null,
8408
+ avg_moqui_matrix_regression_count: averageMoquiMatrixRegressions,
8409
+ max_moqui_matrix_regression_count: moquiMatrixRegressions.length > 0 ? Math.max(...moquiMatrixRegressions) : null,
8359
8410
  risk_levels: riskLevels
8360
8411
  };
8361
8412
  }
@@ -8382,6 +8433,9 @@ function buildAutoHandoffRegressionRiskLayers(series = []) {
8382
8433
  const sceneBatchFailures = scoped
8383
8434
  .map(item => Number(item.scene_package_batch_failure_count))
8384
8435
  .filter(value => Number.isFinite(value));
8436
+ const moquiMatrixRegressions = scoped
8437
+ .map(item => Number(item.moqui_matrix_regression_count))
8438
+ .filter(value => Number.isFinite(value));
8385
8439
  const sceneBatchApplicable = scoped.filter(item => typeof item.scene_package_batch_passed === 'boolean');
8386
8440
  const sceneBatchPassed = sceneBatchApplicable.filter(item => item.scene_package_batch_passed === true).length;
8387
8441
 
@@ -8400,6 +8454,8 @@ function buildAutoHandoffRegressionRiskLayers(series = []) {
8400
8454
  avg_failed_goals: avg(failedGoals),
8401
8455
  avg_ontology_quality_score: avg(ontologyScores),
8402
8456
  avg_scene_package_batch_failure_count: avg(sceneBatchFailures),
8457
+ avg_moqui_matrix_regression_count: avg(moquiMatrixRegressions),
8458
+ max_moqui_matrix_regression_count: moquiMatrixRegressions.length > 0 ? Math.max(...moquiMatrixRegressions) : null,
8403
8459
  scene_package_batch_pass_rate_percent: sceneBatchApplicable.length > 0
8404
8460
  ? Number(((sceneBatchPassed / sceneBatchApplicable.length) * 100).toFixed(2))
8405
8461
  : null
@@ -8432,6 +8488,7 @@ function buildAutoHandoffRegressionRecommendations(payload = {}) {
8432
8488
  const ontologyUnmappedRules = Number(current.ontology_unmapped_rules);
8433
8489
  const ontologyUndecidedDecisions = Number(current.ontology_undecided_decisions);
8434
8490
  const sceneBatchFailureCount = Number(current.scene_package_batch_failure_count);
8491
+ const moquiMatrixRegressionCount = Number(current.moqui_matrix_regression_count);
8435
8492
  const sceneBatchPassed = current.scene_package_batch_passed;
8436
8493
 
8437
8494
  if (trend === 'degraded' || windowTrend === 'degraded') {
@@ -8465,6 +8522,12 @@ function buildAutoHandoffRegressionRecommendations(payload = {}) {
8465
8522
  '`sce scene package-publish-batch --manifest docs/handoffs/handoff-manifest.json --dry-run --json`.'
8466
8523
  );
8467
8524
  }
8525
+ if (Number.isFinite(moquiMatrixRegressionCount) && moquiMatrixRegressionCount > 0) {
8526
+ push(
8527
+ 'Recover Moqui matrix regressions and rerun baseline gate: ' +
8528
+ '`sce scene moqui-baseline --include-all --compare-with .kiro/reports/release-evidence/moqui-template-baseline.json --json`.'
8529
+ );
8530
+ }
8468
8531
 
8469
8532
  if ((payload.window && Number(payload.window.actual) > 0) && (payload.window.requested !== payload.window.actual)) {
8470
8533
  push('Increase regression coverage with `sce auto handoff regression --window 10 --json`.');
@@ -8483,6 +8546,137 @@ function formatAutoHandoffRegressionValue(value, fallback = 'n/a') {
8483
8546
  return `${value}`;
8484
8547
  }
8485
8548
 
8549
+ function getAutoHandoffMoquiCoverageMatrix(summary = {}) {
8550
+ if (!summary || typeof summary !== 'object') {
8551
+ return {};
8552
+ }
8553
+ return summary.coverage_matrix && typeof summary.coverage_matrix === 'object'
8554
+ ? summary.coverage_matrix
8555
+ : {};
8556
+ }
8557
+
8558
+ function getAutoHandoffMoquiCoverageMetric(summary = {}, metricName = '', field = 'rate_percent') {
8559
+ const matrix = getAutoHandoffMoquiCoverageMatrix(summary);
8560
+ const metric = matrix && matrix[metricName] && typeof matrix[metricName] === 'object'
8561
+ ? matrix[metricName]
8562
+ : {};
8563
+ const value = Number(metric[field]);
8564
+ return Number.isFinite(value) ? value : null;
8565
+ }
8566
+
8567
+ function formatAutoHandoffMoquiCoverageMetric(summary = {}, metricName = '', field = 'rate_percent', suffix = '') {
8568
+ const value = getAutoHandoffMoquiCoverageMetric(summary, metricName, field);
8569
+ if (!Number.isFinite(value)) {
8570
+ return 'n/a';
8571
+ }
8572
+ return `${value}${suffix}`;
8573
+ }
8574
+
8575
+ function getAutoHandoffMoquiCoverageDeltaMatrix(compare = {}) {
8576
+ if (!compare || typeof compare !== 'object') {
8577
+ return {};
8578
+ }
8579
+ return compare.coverage_matrix_deltas && typeof compare.coverage_matrix_deltas === 'object'
8580
+ ? compare.coverage_matrix_deltas
8581
+ : {};
8582
+ }
8583
+
8584
+ function getAutoHandoffMoquiCoverageDeltaMetric(compare = {}, metricName = '', field = 'rate_percent') {
8585
+ const matrix = getAutoHandoffMoquiCoverageDeltaMatrix(compare);
8586
+ const metric = matrix && matrix[metricName] && typeof matrix[metricName] === 'object'
8587
+ ? matrix[metricName]
8588
+ : {};
8589
+ const value = Number(metric[field]);
8590
+ return Number.isFinite(value) ? value : null;
8591
+ }
8592
+
8593
+ function formatAutoHandoffMoquiCoverageDeltaMetric(compare = {}, metricName = '', field = 'rate_percent', suffix = '') {
8594
+ const value = getAutoHandoffMoquiCoverageDeltaMetric(compare, metricName, field);
8595
+ if (!Number.isFinite(value)) {
8596
+ return 'n/a';
8597
+ }
8598
+ return `${value}${suffix}`;
8599
+ }
8600
+
8601
+ function getAutoHandoffMoquiCoverageMetricLabel(metricName = '') {
8602
+ const labels = {
8603
+ graph_valid: 'graph-valid',
8604
+ score_passed: 'score-passed',
8605
+ entity_coverage: 'entity-coverage',
8606
+ relation_coverage: 'relation-coverage',
8607
+ business_rule_coverage: 'business-rule-coverage',
8608
+ business_rule_closed: 'business-rule-closed',
8609
+ decision_coverage: 'decision-coverage',
8610
+ decision_closed: 'decision-closed',
8611
+ baseline_passed: 'baseline-passed'
8612
+ };
8613
+ return labels[metricName] || metricName;
8614
+ }
8615
+
8616
+ function buildAutoHandoffMoquiCoverageRegressions(compare = {}) {
8617
+ const source = compare && typeof compare === 'object' ? compare : {};
8618
+ const predefined = Array.isArray(source.coverage_matrix_regressions)
8619
+ ? source.coverage_matrix_regressions
8620
+ : null;
8621
+ if (predefined) {
8622
+ const normalized = predefined
8623
+ .map(item => {
8624
+ const metric = normalizeHandoffText(item && item.metric);
8625
+ const deltaRate = Number(item && item.delta_rate_percent);
8626
+ if (!metric || !Number.isFinite(deltaRate) || deltaRate >= 0) {
8627
+ return null;
8628
+ }
8629
+ return {
8630
+ metric,
8631
+ label: normalizeHandoffText(item && item.label) || getAutoHandoffMoquiCoverageMetricLabel(metric),
8632
+ delta_rate_percent: Number(deltaRate.toFixed(2))
8633
+ };
8634
+ })
8635
+ .filter(Boolean);
8636
+ if (normalized.length > 0) {
8637
+ return normalized.sort((a, b) => {
8638
+ if (a.delta_rate_percent !== b.delta_rate_percent) {
8639
+ return a.delta_rate_percent - b.delta_rate_percent;
8640
+ }
8641
+ return `${a.metric}`.localeCompare(`${b.metric}`);
8642
+ });
8643
+ }
8644
+ }
8645
+
8646
+ const deltaMatrix = getAutoHandoffMoquiCoverageDeltaMatrix(source);
8647
+ return Object.entries(deltaMatrix)
8648
+ .map(([metric, value]) => {
8649
+ const deltaRate = Number(value && value.rate_percent);
8650
+ if (!Number.isFinite(deltaRate) || deltaRate >= 0) {
8651
+ return null;
8652
+ }
8653
+ return {
8654
+ metric,
8655
+ label: getAutoHandoffMoquiCoverageMetricLabel(metric),
8656
+ delta_rate_percent: Number(deltaRate.toFixed(2))
8657
+ };
8658
+ })
8659
+ .filter(Boolean)
8660
+ .sort((a, b) => {
8661
+ if (a.delta_rate_percent !== b.delta_rate_percent) {
8662
+ return a.delta_rate_percent - b.delta_rate_percent;
8663
+ }
8664
+ return `${a.metric}`.localeCompare(`${b.metric}`);
8665
+ });
8666
+ }
8667
+
8668
+ function formatAutoHandoffMoquiCoverageRegressions(compare = {}, limit = 3) {
8669
+ const regressions = buildAutoHandoffMoquiCoverageRegressions(compare);
8670
+ if (regressions.length === 0) {
8671
+ return 'none';
8672
+ }
8673
+ const maxItems = Number.isFinite(Number(limit)) && Number(limit) > 0 ? Number(limit) : regressions.length;
8674
+ return regressions
8675
+ .slice(0, maxItems)
8676
+ .map(item => `${item.label}:${item.delta_rate_percent}%`)
8677
+ .join(' | ');
8678
+ }
8679
+
8486
8680
  function renderAutoHandoffRegressionAsciiBar(value, max = 100, width = 20) {
8487
8681
  const parsed = Number(value);
8488
8682
  if (!Number.isFinite(parsed)) {
@@ -8530,7 +8724,8 @@ function renderAutoHandoffRegressionMarkdown(payload = {}) {
8530
8724
  `avg_success=${formatAutoHandoffRegressionValue(scoped.avg_spec_success_rate_percent)}, ` +
8531
8725
  `avg_failed_goals=${formatAutoHandoffRegressionValue(scoped.avg_failed_goals)}, ` +
8532
8726
  `avg_ontology_quality=${formatAutoHandoffRegressionValue(scoped.avg_ontology_quality_score)}, ` +
8533
- `scene_batch_pass_rate=${formatAutoHandoffRegressionValue(scoped.scene_package_batch_pass_rate_percent)}%`
8727
+ `scene_batch_pass_rate=${formatAutoHandoffRegressionValue(scoped.scene_package_batch_pass_rate_percent)}%, ` +
8728
+ `avg_moqui_matrix_regressions=${formatAutoHandoffRegressionValue(scoped.avg_moqui_matrix_regression_count, '0')}`
8534
8729
  );
8535
8730
  });
8536
8731
 
@@ -8551,6 +8746,7 @@ function renderAutoHandoffRegressionMarkdown(payload = {}) {
8551
8746
  `- Ontology quality delta: ${formatAutoHandoffRegressionValue(delta.ontology_quality_score)}`,
8552
8747
  `- Ontology unmapped rules delta: ${formatAutoHandoffRegressionValue(delta.ontology_unmapped_rules)}`,
8553
8748
  `- Ontology undecided decisions delta: ${formatAutoHandoffRegressionValue(delta.ontology_undecided_decisions)}`,
8749
+ `- Moqui matrix regression count delta: ${formatAutoHandoffRegressionValue(delta.moqui_matrix_regression_count)}`,
8554
8750
  `- Scene package batch failure count delta: ${formatAutoHandoffRegressionValue(delta.scene_package_batch_failure_count)}`,
8555
8751
  '',
8556
8752
  '## Window Trend',
@@ -8559,6 +8755,7 @@ function renderAutoHandoffRegressionMarkdown(payload = {}) {
8559
8755
  `- Success rate delta: ${formatAutoHandoffRegressionValue(windowTrend.delta && windowTrend.delta.spec_success_rate_percent)}`,
8560
8756
  `- Risk level rank delta: ${formatAutoHandoffRegressionValue(windowTrend.delta && windowTrend.delta.risk_level_rank)}`,
8561
8757
  `- Failed goals delta: ${formatAutoHandoffRegressionValue(windowTrend.delta && windowTrend.delta.failed_goals)}`,
8758
+ `- Moqui matrix regression count delta: ${formatAutoHandoffRegressionValue(windowTrend.delta && windowTrend.delta.moqui_matrix_regression_count)}`,
8562
8759
  '',
8563
8760
  '## Aggregates',
8564
8761
  '',
@@ -8578,6 +8775,8 @@ function renderAutoHandoffRegressionMarkdown(payload = {}) {
8578
8775
  `- Scene package batch pass rate: ${formatAutoHandoffRegressionValue(aggregates.scene_package_batch_pass_rate_percent)}%`,
8579
8776
  `- Scene package batch failed sessions: ${formatAutoHandoffRegressionValue(aggregates.scene_package_batch_failed_count, '0')}`,
8580
8777
  `- Avg scene package batch failure count: ${formatAutoHandoffRegressionValue(aggregates.avg_scene_package_batch_failure_count)}`,
8778
+ `- Avg Moqui matrix regression count: ${formatAutoHandoffRegressionValue(aggregates.avg_moqui_matrix_regression_count)}`,
8779
+ `- Max Moqui matrix regression count: ${formatAutoHandoffRegressionValue(aggregates.max_moqui_matrix_regression_count)}`,
8581
8780
  `- Risk levels: low=${formatAutoHandoffRegressionValue(riskLevels.low, '0')}, medium=${formatAutoHandoffRegressionValue(riskLevels.medium, '0')}, high=${formatAutoHandoffRegressionValue(riskLevels.high, '0')}, unknown=${formatAutoHandoffRegressionValue(riskLevels.unknown, '0')}`,
8582
8781
  '',
8583
8782
  '## Trend Series',
@@ -9426,6 +9625,13 @@ function buildAutoHandoffEvidenceSnapshot(entry = {}) {
9426
9625
  const ontologyMetrics = ontology && typeof ontology.metrics === 'object'
9427
9626
  ? ontology.metrics
9428
9627
  : {};
9628
+ const moquiBaseline = entry && typeof entry.moqui_baseline === 'object'
9629
+ ? entry.moqui_baseline
9630
+ : {};
9631
+ const moquiCompare = moquiBaseline && typeof moquiBaseline.compare === 'object'
9632
+ ? moquiBaseline.compare
9633
+ : {};
9634
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
9429
9635
  const scenePackageBatch = entry && typeof entry.scene_package_batch === 'object'
9430
9636
  ? entry.scene_package_batch
9431
9637
  : {};
@@ -9490,6 +9696,7 @@ function buildAutoHandoffEvidenceSnapshot(entry = {}) {
9490
9696
  entry.capability_coverage.summary &&
9491
9697
  entry.capability_coverage.summary.passed === true
9492
9698
  ),
9699
+ moqui_matrix_regression_count: moquiMatrixRegressions.length,
9493
9700
  generated_at: normalizeHandoffText(entry.merged_at)
9494
9701
  };
9495
9702
  }
@@ -9534,6 +9741,9 @@ function renderAutoHandoffEvidenceReviewMarkdown(payload = {}) {
9534
9741
  const failureSummary = currentOverview.failure_summary && typeof currentOverview.failure_summary === 'object'
9535
9742
  ? currentOverview.failure_summary
9536
9743
  : {};
9744
+ const currentPolicy = currentOverview.policy && typeof currentOverview.policy === 'object'
9745
+ ? currentOverview.policy
9746
+ : {};
9537
9747
  const ontology = currentOverview.ontology_validation && typeof currentOverview.ontology_validation === 'object'
9538
9748
  ? currentOverview.ontology_validation
9539
9749
  : {};
@@ -9549,9 +9759,16 @@ function renderAutoHandoffEvidenceReviewMarkdown(payload = {}) {
9549
9759
  const moquiSummary = moquiBaseline && moquiBaseline.summary && typeof moquiBaseline.summary === 'object'
9550
9760
  ? moquiBaseline.summary
9551
9761
  : {};
9762
+ const moquiScopeBreakdown = moquiSummary && moquiSummary.scope_breakdown && typeof moquiSummary.scope_breakdown === 'object'
9763
+ ? moquiSummary.scope_breakdown
9764
+ : {};
9765
+ const moquiGapFrequency = Array.isArray(moquiSummary && moquiSummary.gap_frequency)
9766
+ ? moquiSummary.gap_frequency
9767
+ : [];
9552
9768
  const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
9553
9769
  ? moquiBaseline.compare
9554
9770
  : {};
9771
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
9555
9772
  const moquiDeltas = moquiCompare && moquiCompare.deltas && typeof moquiCompare.deltas === 'object'
9556
9773
  ? moquiCompare.deltas
9557
9774
  : {};
@@ -9634,7 +9851,8 @@ function renderAutoHandoffEvidenceReviewMarkdown(payload = {}) {
9634
9851
  `avg_success=${formatAutoHandoffRegressionValue(scoped.avg_spec_success_rate_percent)}, ` +
9635
9852
  `avg_failed_goals=${formatAutoHandoffRegressionValue(scoped.avg_failed_goals)}, ` +
9636
9853
  `avg_ontology_quality=${formatAutoHandoffRegressionValue(scoped.avg_ontology_quality_score)}, ` +
9637
- `scene_batch_pass_rate=${formatAutoHandoffRegressionValue(scoped.scene_package_batch_pass_rate_percent)}%`
9854
+ `scene_batch_pass_rate=${formatAutoHandoffRegressionValue(scoped.scene_package_batch_pass_rate_percent)}%, ` +
9855
+ `avg_moqui_matrix_regressions=${formatAutoHandoffRegressionValue(scoped.avg_moqui_matrix_regression_count, '0')}`
9638
9856
  );
9639
9857
  });
9640
9858
 
@@ -9700,10 +9918,24 @@ function renderAutoHandoffEvidenceReviewMarkdown(payload = {}) {
9700
9918
  `- Avg score: ${formatAutoHandoffRegressionValue(moquiSummary.avg_score)}`,
9701
9919
  `- Valid-rate: ${formatAutoHandoffRegressionValue(moquiSummary.valid_rate_percent)}%`,
9702
9920
  `- Baseline failed templates: ${formatAutoHandoffRegressionValue(moquiSummary.baseline_failed)}`,
9921
+ `- Matrix regression count: ${formatAutoHandoffRegressionValue(moquiMatrixRegressions.length, '0')}`,
9922
+ `- Matrix regression gate (max): ${formatAutoHandoffRegressionValue(currentPolicy.max_moqui_matrix_regressions)}`,
9923
+ `- Scope mix (moqui/suite/other): ${formatAutoHandoffRegressionValue(moquiScopeBreakdown.moqui_erp, '0')}/${formatAutoHandoffRegressionValue(moquiScopeBreakdown.scene_orchestration, '0')}/${formatAutoHandoffRegressionValue(moquiScopeBreakdown.other, '0')}`,
9924
+ `- Entity coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'entity_coverage', 'rate_percent', '%')}`,
9925
+ `- Relation coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'relation_coverage', 'rate_percent', '%')}`,
9926
+ `- Business-rule coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'business_rule_coverage', 'rate_percent', '%')}`,
9927
+ `- Business-rule closed: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'business_rule_closed', 'rate_percent', '%')}`,
9928
+ `- Decision coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'decision_coverage', 'rate_percent', '%')}`,
9929
+ `- Decision closed: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'decision_closed', 'rate_percent', '%')}`,
9703
9930
  `- Delta avg score: ${formatAutoHandoffRegressionValue(moquiDeltas.avg_score)}`,
9704
9931
  `- Delta valid-rate: ${formatAutoHandoffRegressionValue(moquiDeltas.valid_rate_percent)}%`,
9932
+ `- Delta entity coverage: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'entity_coverage', 'rate_percent', '%')}`,
9933
+ `- Delta business-rule closed: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'business_rule_closed', 'rate_percent', '%')}`,
9934
+ `- Delta decision closed: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'decision_closed', 'rate_percent', '%')}`,
9935
+ `- Matrix regressions: ${formatAutoHandoffMoquiCoverageRegressions(moquiCompare, 5)}`,
9705
9936
  `- Newly failed templates: ${Array.isArray(moquiFailedTemplates.newly_failed) && moquiFailedTemplates.newly_failed.length > 0 ? moquiFailedTemplates.newly_failed.join(', ') : 'none'}`,
9706
9937
  `- Recovered templates: ${Array.isArray(moquiFailedTemplates.recovered) && moquiFailedTemplates.recovered.length > 0 ? moquiFailedTemplates.recovered.join(', ') : 'none'}`,
9938
+ `- Top baseline gaps: ${moquiGapFrequency.length > 0 ? moquiGapFrequency.slice(0, 3).map(item => `${item.gap}:${item.count}`).join(' | ') : 'none'}`,
9707
9939
  `- Baseline JSON: ${formatAutoHandoffRegressionValue(moquiBaseline.output && moquiBaseline.output.json)}`,
9708
9940
  '',
9709
9941
  '## Current Scene Package Batch',
@@ -9860,9 +10092,16 @@ function renderAutoHandoffReleaseNotesDraft(payload = {}, context = {}) {
9860
10092
  const moquiSummary = moquiBaseline && moquiBaseline.summary && typeof moquiBaseline.summary === 'object'
9861
10093
  ? moquiBaseline.summary
9862
10094
  : {};
10095
+ const moquiScopeBreakdown = moquiSummary && moquiSummary.scope_breakdown && typeof moquiSummary.scope_breakdown === 'object'
10096
+ ? moquiSummary.scope_breakdown
10097
+ : {};
10098
+ const moquiGapFrequency = Array.isArray(moquiSummary && moquiSummary.gap_frequency)
10099
+ ? moquiSummary.gap_frequency
10100
+ : [];
9863
10101
  const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
9864
10102
  ? moquiBaseline.compare
9865
10103
  : {};
10104
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
9866
10105
  const moquiDeltas = moquiCompare && moquiCompare.deltas && typeof moquiCompare.deltas === 'object'
9867
10106
  ? moquiCompare.deltas
9868
10107
  : {};
@@ -9933,7 +10172,8 @@ function renderAutoHandoffReleaseNotesDraft(payload = {}, context = {}) {
9933
10172
  `- ${level}: count=${formatAutoHandoffRegressionValue(scoped.count, '0')}, ` +
9934
10173
  `avg_success=${formatAutoHandoffRegressionValue(scoped.avg_spec_success_rate_percent)}, ` +
9935
10174
  `avg_failed_goals=${formatAutoHandoffRegressionValue(scoped.avg_failed_goals)}, ` +
9936
- `avg_ontology_quality=${formatAutoHandoffRegressionValue(scoped.avg_ontology_quality_score)}`
10175
+ `avg_ontology_quality=${formatAutoHandoffRegressionValue(scoped.avg_ontology_quality_score)}, ` +
10176
+ `avg_moqui_matrix_regressions=${formatAutoHandoffRegressionValue(scoped.avg_moqui_matrix_regression_count, '0')}`
9937
10177
  );
9938
10178
  });
9939
10179
 
@@ -9965,9 +10205,23 @@ function renderAutoHandoffReleaseNotesDraft(payload = {}, context = {}) {
9965
10205
  `- Moqui baseline avg score: ${formatAutoHandoffRegressionValue(moquiSummary.avg_score)}`,
9966
10206
  `- Moqui baseline valid-rate: ${formatAutoHandoffRegressionValue(moquiSummary.valid_rate_percent)}%`,
9967
10207
  `- Moqui baseline failed templates: ${formatAutoHandoffRegressionValue(moquiSummary.baseline_failed)}`,
10208
+ `- Moqui matrix regression count: ${formatAutoHandoffRegressionValue(moquiMatrixRegressions.length, '0')}`,
10209
+ `- Moqui matrix regression gate (max): ${formatAutoHandoffRegressionValue(currentPolicy.max_moqui_matrix_regressions)}`,
10210
+ `- Moqui scope mix (moqui/suite/other): ${formatAutoHandoffRegressionValue(moquiScopeBreakdown.moqui_erp, '0')}/${formatAutoHandoffRegressionValue(moquiScopeBreakdown.scene_orchestration, '0')}/${formatAutoHandoffRegressionValue(moquiScopeBreakdown.other, '0')}`,
10211
+ `- Moqui entity coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'entity_coverage', 'rate_percent', '%')}`,
10212
+ `- Moqui relation coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'relation_coverage', 'rate_percent', '%')}`,
10213
+ `- Moqui business-rule coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'business_rule_coverage', 'rate_percent', '%')}`,
10214
+ `- Moqui business-rule closed: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'business_rule_closed', 'rate_percent', '%')}`,
10215
+ `- Moqui decision coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'decision_coverage', 'rate_percent', '%')}`,
10216
+ `- Moqui decision closed: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'decision_closed', 'rate_percent', '%')}`,
9968
10217
  `- Moqui baseline avg score delta: ${formatAutoHandoffRegressionValue(moquiDeltas.avg_score)}`,
9969
10218
  `- Moqui baseline valid-rate delta: ${formatAutoHandoffRegressionValue(moquiDeltas.valid_rate_percent)}%`,
10219
+ `- Moqui entity coverage delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'entity_coverage', 'rate_percent', '%')}`,
10220
+ `- Moqui business-rule closed delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'business_rule_closed', 'rate_percent', '%')}`,
10221
+ `- Moqui decision closed delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'decision_closed', 'rate_percent', '%')}`,
10222
+ `- Moqui matrix regressions: ${formatAutoHandoffMoquiCoverageRegressions(moquiCompare, 5)}`,
9970
10223
  `- Moqui newly failed templates: ${Array.isArray(moquiFailedTemplates.newly_failed) && moquiFailedTemplates.newly_failed.length > 0 ? moquiFailedTemplates.newly_failed.join(', ') : 'none'}`,
10224
+ `- Moqui top baseline gaps: ${moquiGapFrequency.length > 0 ? moquiGapFrequency.slice(0, 3).map(item => `${item.gap}:${item.count}`).join(' | ') : 'none'}`,
9971
10225
  `- Scene package batch status: ${formatAutoHandoffRegressionValue(scenePackageBatch.status)}`,
9972
10226
  `- Scene package batch selected: ${formatAutoHandoffRegressionValue(scenePackageBatchSummary.selected)}`,
9973
10227
  `- Scene package batch failed: ${formatAutoHandoffRegressionValue(scenePackageBatchSummary.failed)}`,
@@ -10279,6 +10533,17 @@ function normalizeHandoffMinCapabilitySemantic(semanticCandidate) {
10279
10533
  return Number(parsed.toFixed(2));
10280
10534
  }
10281
10535
 
10536
+ function normalizeHandoffMaxMoquiMatrixRegressions(valueCandidate) {
10537
+ if (valueCandidate === undefined || valueCandidate === null || valueCandidate === '') {
10538
+ return 0;
10539
+ }
10540
+ const parsed = Number(valueCandidate);
10541
+ if (!Number.isInteger(parsed) || parsed < 0) {
10542
+ throw new Error('--max-moqui-matrix-regressions must be an integer >= 0.');
10543
+ }
10544
+ return parsed;
10545
+ }
10546
+
10282
10547
  function normalizeHandoffOptionalNonNegativeInteger(valueCandidate, optionName) {
10283
10548
  if (valueCandidate === undefined || valueCandidate === null || valueCandidate === '') {
10284
10549
  return null;
@@ -10307,6 +10572,7 @@ function buildAutoHandoffRunPolicy(options = {}) {
10307
10572
  max_risk_level: normalizeHandoffRiskLevel(options.maxRiskLevel),
10308
10573
  min_ontology_score: normalizeHandoffMinOntologyScore(options.minOntologyScore),
10309
10574
  min_capability_coverage_percent: normalizeHandoffMinCapabilityCoverage(options.minCapabilityCoverage),
10575
+ max_moqui_matrix_regressions: normalizeHandoffMaxMoquiMatrixRegressions(options.maxMoquiMatrixRegressions),
10310
10576
  max_unmapped_rules: normalizeHandoffOptionalNonNegativeInteger(
10311
10577
  options.maxUnmappedRules,
10312
10578
  '--max-unmapped-rules'
@@ -10319,6 +10585,7 @@ function buildAutoHandoffRunPolicy(options = {}) {
10319
10585
  require_moqui_baseline: options.requireMoquiBaseline !== false,
10320
10586
  require_scene_package_batch: options.requireScenePackageBatch !== false,
10321
10587
  require_capability_coverage: options.requireCapabilityCoverage !== false,
10588
+ require_capability_lexicon: options.requireCapabilityLexicon !== false,
10322
10589
  require_release_gate_preflight: options.requireReleaseGatePreflight === true,
10323
10590
  dependency_batching: options.dependencyBatching !== false,
10324
10591
  release_evidence_window: normalizeHandoffReleaseEvidenceWindow(options.releaseEvidenceWindow)
@@ -10635,6 +10902,10 @@ function evaluateAutoHandoffMoquiBaselineGateReasons(policy = {}, moquiBaseline
10635
10902
  const summary = baseline && baseline.summary && typeof baseline.summary === 'object'
10636
10903
  ? baseline.summary
10637
10904
  : {};
10905
+ const compare = baseline && baseline.compare && typeof baseline.compare === 'object'
10906
+ ? baseline.compare
10907
+ : {};
10908
+ const matrixRegressions = buildAutoHandoffMoquiCoverageRegressions(compare);
10638
10909
  const status = `${baseline && baseline.status ? baseline.status : 'missing'}`.trim().toLowerCase();
10639
10910
  if (!baseline || baseline.generated !== true) {
10640
10911
  const reason = baseline && baseline.reason ? baseline.reason : 'moqui baseline snapshot missing';
@@ -10653,6 +10924,15 @@ function evaluateAutoHandoffMoquiBaselineGateReasons(policy = {}, moquiBaseline
10653
10924
  `valid_rate=${Number.isFinite(validRate) ? `${validRate}%` : 'n/a'})`
10654
10925
  );
10655
10926
  }
10927
+ if (Number.isInteger(policy.max_moqui_matrix_regressions)) {
10928
+ const limit = Number(policy.max_moqui_matrix_regressions);
10929
+ if (matrixRegressions.length > limit) {
10930
+ reasons.push(
10931
+ `moqui baseline matrix regressions ${matrixRegressions.length} > allowed ${limit} ` +
10932
+ `(${matrixRegressions.slice(0, 3).map(item => `${item.label}:${item.delta_rate_percent}%`).join(' | ')})`
10933
+ );
10934
+ }
10935
+ }
10656
10936
  return reasons;
10657
10937
  }
10658
10938
 
@@ -10734,6 +11014,57 @@ function evaluateAutoHandoffCapabilityCoverageGateReasons(policy = {}, capabilit
10734
11014
  return reasons;
10735
11015
  }
10736
11016
 
11017
+ function evaluateAutoHandoffCapabilityLexiconGateReasons(policy = {}, capabilityCoverage = null) {
11018
+ const reasons = [];
11019
+ if (policy.require_capability_lexicon !== true) {
11020
+ return reasons;
11021
+ }
11022
+
11023
+ const coverage = capabilityCoverage && typeof capabilityCoverage === 'object'
11024
+ ? capabilityCoverage
11025
+ : null;
11026
+ if (!coverage) {
11027
+ reasons.push('capability lexicon snapshot missing');
11028
+ return reasons;
11029
+ }
11030
+ if (coverage.status === 'error') {
11031
+ reasons.push(`capability lexicon errored: ${coverage.error || 'unknown error'}`);
11032
+ return reasons;
11033
+ }
11034
+ if (coverage.status === 'skipped') {
11035
+ const totalCapabilities = Number(
11036
+ coverage &&
11037
+ coverage.summary &&
11038
+ coverage.summary.total_capabilities !== undefined
11039
+ ? coverage.summary.total_capabilities
11040
+ : 0
11041
+ );
11042
+ if (Number.isFinite(totalCapabilities) && totalCapabilities <= 0) {
11043
+ return reasons;
11044
+ }
11045
+ reasons.push(`capability lexicon skipped: ${coverage.reason || 'unknown reason'}`);
11046
+ return reasons;
11047
+ }
11048
+
11049
+ const normalization = coverage.normalization && typeof coverage.normalization === 'object'
11050
+ ? coverage.normalization
11051
+ : {};
11052
+ const expectedUnknownCount = Array.isArray(normalization.expected_unknown)
11053
+ ? normalization.expected_unknown.length
11054
+ : 0;
11055
+ const providedUnknownCount = Array.isArray(normalization.provided_unknown)
11056
+ ? normalization.provided_unknown.length
11057
+ : 0;
11058
+ if (expectedUnknownCount > 0) {
11059
+ reasons.push(`capability_lexicon_expected_unknown_count ${expectedUnknownCount} > allowed 0`);
11060
+ }
11061
+ if (providedUnknownCount > 0) {
11062
+ reasons.push(`capability_lexicon_provided_unknown_count ${providedUnknownCount} > allowed 0`);
11063
+ }
11064
+
11065
+ return reasons;
11066
+ }
11067
+
10737
11068
  function evaluateAutoHandoffCapabilitySemanticGateReasons(policy = {}, capabilityCoverage = null) {
10738
11069
  const reasons = [];
10739
11070
  if (policy.require_capability_semantic !== true) {
@@ -10854,6 +11185,13 @@ function buildAutoHandoffRunFailureSummary(result = {}) {
10854
11185
  const releaseGateReasons = releaseGatePreflight && Array.isArray(releaseGatePreflight.reasons)
10855
11186
  ? releaseGatePreflight.reasons
10856
11187
  : [];
11188
+ const moquiBaseline = result && result.moqui_baseline && typeof result.moqui_baseline === 'object'
11189
+ ? result.moqui_baseline
11190
+ : null;
11191
+ const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
11192
+ ? moquiBaseline.compare
11193
+ : {};
11194
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
10857
11195
  const highlights = [];
10858
11196
  if (typeof result.error === 'string' && result.error.trim()) {
10859
11197
  highlights.push(`error: ${result.error.trim()}`);
@@ -10869,6 +11207,11 @@ function buildAutoHandoffRunFailureSummary(result = {}) {
10869
11207
  if (failedPhase && failedPhase.id) {
10870
11208
  highlights.push(`phase: ${failedPhase.id}${failedPhase.error ? ` (${failedPhase.error})` : ''}`);
10871
11209
  }
11210
+ if (moquiMatrixRegressions.length > 0) {
11211
+ highlights.push(
11212
+ `moqui_matrix_regression: ${moquiMatrixRegressions.slice(0, 3).map(item => `${item.label}:${item.delta_rate_percent}%`).join(' | ')}`
11213
+ );
11214
+ }
10872
11215
  return {
10873
11216
  status: normalizeHandoffText(result && result.status),
10874
11217
  failed_phase: failedPhase
@@ -10880,6 +11223,7 @@ function buildAutoHandoffRunFailureSummary(result = {}) {
10880
11223
  : null,
10881
11224
  gate_failed: gates.passed === false,
10882
11225
  gate_reasons: gateReasons,
11226
+ moqui_matrix_regressions: moquiMatrixRegressions,
10883
11227
  release_gate_preflight_blocked: Boolean(releaseGatePreflight && releaseGatePreflight.blocked === true),
10884
11228
  release_gate_preflight_reasons: releaseGateReasons,
10885
11229
  highlights
@@ -10958,10 +11302,13 @@ function evaluateAutoHandoffRunGates(context = {}) {
10958
11302
  min_spec_success_rate: 100,
10959
11303
  max_risk_level: 'high',
10960
11304
  min_ontology_score: 0,
11305
+ max_moqui_matrix_regressions: 0,
10961
11306
  max_unmapped_rules: null,
10962
11307
  max_undecided_decisions: null,
10963
11308
  require_ontology_validation: true,
10964
- require_scene_package_batch: true
11309
+ require_scene_package_batch: true,
11310
+ require_capability_coverage: true,
11311
+ require_capability_lexicon: true
10965
11312
  };
10966
11313
  const dryRun = Boolean(context.dryRun);
10967
11314
  const specStatus = context.specStatus || {
@@ -10974,6 +11321,10 @@ function evaluateAutoHandoffRunGates(context = {}) {
10974
11321
  const moquiBaseline = context.moquiBaseline && typeof context.moquiBaseline === 'object'
10975
11322
  ? context.moquiBaseline
10976
11323
  : null;
11324
+ const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
11325
+ ? moquiBaseline.compare
11326
+ : {};
11327
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
10977
11328
  const scenePackageBatch = context.scenePackageBatch && typeof context.scenePackageBatch === 'object'
10978
11329
  ? context.scenePackageBatch
10979
11330
  : null;
@@ -11008,6 +11359,7 @@ function evaluateAutoHandoffRunGates(context = {}) {
11008
11359
  reasons.push(...evaluateAutoHandoffMoquiBaselineGateReasons(policy, moquiBaseline));
11009
11360
  reasons.push(...evaluateAutoHandoffScenePackageBatchGateReasons(policy, scenePackageBatch));
11010
11361
  reasons.push(...evaluateAutoHandoffCapabilityCoverageGateReasons(policy, capabilityCoverage));
11362
+ reasons.push(...evaluateAutoHandoffCapabilityLexiconGateReasons(policy, capabilityCoverage));
11011
11363
 
11012
11364
  return {
11013
11365
  passed: reasons.length === 0,
@@ -11048,6 +11400,10 @@ function evaluateAutoHandoffRunGates(context = {}) {
11048
11400
  moquiBaseline.summary &&
11049
11401
  moquiBaseline.summary.portfolio_passed === true
11050
11402
  ),
11403
+ moqui_matrix_regression_count: moquiMatrixRegressions.length,
11404
+ max_moqui_matrix_regressions: Number.isInteger(policy.max_moqui_matrix_regressions)
11405
+ ? Number(policy.max_moqui_matrix_regressions)
11406
+ : null,
11051
11407
  scene_package_batch_status: normalizeHandoffText(scenePackageBatch && scenePackageBatch.status),
11052
11408
  scene_package_batch_passed: Boolean(scenePackageBatch && scenePackageBatch.status === 'passed'),
11053
11409
  capability_coverage_status: normalizeHandoffText(capabilityCoverage && capabilityCoverage.status),
@@ -11055,7 +11411,22 @@ function evaluateAutoHandoffRunGates(context = {}) {
11055
11411
  Number(capabilityCoverage && capabilityCoverage.summary ? capabilityCoverage.summary.coverage_percent : null)
11056
11412
  )
11057
11413
  ? Number(capabilityCoverage.summary.coverage_percent)
11058
- : null
11414
+ : null,
11415
+ capability_expected_unknown_count: Array.isArray(
11416
+ capabilityCoverage && capabilityCoverage.normalization
11417
+ ? capabilityCoverage.normalization.expected_unknown
11418
+ : null
11419
+ )
11420
+ ? capabilityCoverage.normalization.expected_unknown.length
11421
+ : null,
11422
+ capability_provided_unknown_count: Array.isArray(
11423
+ capabilityCoverage && capabilityCoverage.normalization
11424
+ ? capabilityCoverage.normalization.provided_unknown
11425
+ : null
11426
+ )
11427
+ ? capabilityCoverage.normalization.provided_unknown.length
11428
+ : null,
11429
+ require_capability_lexicon: policy.require_capability_lexicon === true
11059
11430
  },
11060
11431
  reasons
11061
11432
  };
@@ -11187,6 +11558,10 @@ function buildAutoHandoffRunRecommendations(projectPath, result) {
11187
11558
  const moquiSummary = moquiBaseline && moquiBaseline.summary && typeof moquiBaseline.summary === 'object'
11188
11559
  ? moquiBaseline.summary
11189
11560
  : null;
11561
+ const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
11562
+ ? moquiBaseline.compare
11563
+ : {};
11564
+ const moquiCoverageRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
11190
11565
  if (moquiBaseline && moquiBaseline.status === 'error') {
11191
11566
  push('sce scene moqui-baseline --json');
11192
11567
  } else if (moquiSummary && moquiSummary.portfolio_passed === false) {
@@ -11199,6 +11574,26 @@ function buildAutoHandoffRunRecommendations(projectPath, result) {
11199
11574
  '--dry-run --ontology-task-queue-out .kiro/auto/ontology-remediation.lines --json'
11200
11575
  );
11201
11576
  }
11577
+ if (moquiCoverageRegressions.length > 0) {
11578
+ push(
11579
+ `Recover Moqui matrix regressions before next handoff run: ` +
11580
+ `${moquiCoverageRegressions.slice(0, 3).map(item => `${item.label}:${item.delta_rate_percent}%`).join(' | ')}`
11581
+ );
11582
+ push(
11583
+ 'sce scene moqui-baseline --include-all ' +
11584
+ '--compare-with .kiro/reports/release-evidence/moqui-template-baseline.json --json'
11585
+ );
11586
+ if (manifestPath) {
11587
+ push(
11588
+ `sce auto handoff run --manifest ${manifestCli} ` +
11589
+ '--max-moqui-matrix-regressions 0 --json'
11590
+ );
11591
+ push(
11592
+ `sce scene package-publish-batch --manifest ${manifestCli} ` +
11593
+ '--dry-run --ontology-task-queue-out .kiro/auto/ontology-remediation.lines --json'
11594
+ );
11595
+ }
11596
+ }
11202
11597
 
11203
11598
  const scenePackageBatch = result && result.scene_package_batch && typeof result.scene_package_batch === 'object'
11204
11599
  ? result.scene_package_batch
@@ -11237,6 +11632,26 @@ function buildAutoHandoffRunRecommendations(projectPath, result) {
11237
11632
  ) {
11238
11633
  push('replace deprecated manifest capabilities with canonical Moqui capability ids and rerun `sce auto handoff run --json`');
11239
11634
  }
11635
+ if (
11636
+ capabilityNormalization &&
11637
+ Array.isArray(capabilityNormalization.expected_unknown) &&
11638
+ capabilityNormalization.expected_unknown.length > 0
11639
+ ) {
11640
+ push(
11641
+ 'normalize unknown manifest capabilities and rerun strict gates via ' +
11642
+ '`sce auto handoff capability-matrix --manifest docs/handoffs/handoff-manifest.json --fail-on-gap --json`'
11643
+ );
11644
+ }
11645
+ if (
11646
+ capabilityNormalization &&
11647
+ Array.isArray(capabilityNormalization.provided_unknown) &&
11648
+ capabilityNormalization.provided_unknown.length > 0
11649
+ ) {
11650
+ push(
11651
+ 'normalize unknown template capabilities via ' +
11652
+ '`node scripts/moqui-lexicon-audit.js --manifest docs/handoffs/handoff-manifest.json --template-dir .kiro/templates/scene-packages --fail-on-gap --json`'
11653
+ );
11654
+ }
11240
11655
 
11241
11656
  return recommendations;
11242
11657
  }
@@ -11340,6 +11755,20 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11340
11755
  const moquiFailedTemplates = moquiCompare && moquiCompare.failed_templates && typeof moquiCompare.failed_templates === 'object'
11341
11756
  ? moquiCompare.failed_templates
11342
11757
  : {};
11758
+ const moquiScopeBreakdown = moquiSummary && moquiSummary.scope_breakdown && typeof moquiSummary.scope_breakdown === 'object'
11759
+ ? moquiSummary.scope_breakdown
11760
+ : {};
11761
+ const moquiCoverageMatrix = moquiSummary && moquiSummary.coverage_matrix && typeof moquiSummary.coverage_matrix === 'object'
11762
+ ? moquiSummary.coverage_matrix
11763
+ : {};
11764
+ const moquiCoverageMatrixDeltas = moquiCompare && moquiCompare.coverage_matrix_deltas
11765
+ && typeof moquiCompare.coverage_matrix_deltas === 'object'
11766
+ ? moquiCompare.coverage_matrix_deltas
11767
+ : {};
11768
+ const moquiCoverageMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
11769
+ const moquiGapFrequency = Array.isArray(moquiSummary && moquiSummary.gap_frequency)
11770
+ ? moquiSummary.gap_frequency
11771
+ : [];
11343
11772
  const scenePackageBatch = result && result.scene_package_batch && typeof result.scene_package_batch === 'object'
11344
11773
  ? result.scene_package_batch
11345
11774
  : {};
@@ -11394,7 +11823,9 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11394
11823
  risk_level: normalizeHandoffText(gateActual.risk_level),
11395
11824
  ontology_quality_score: toNumber(gateActual.ontology_quality_score),
11396
11825
  ontology_business_rule_unmapped: toNumber(gateActual.ontology_business_rule_unmapped),
11397
- ontology_decision_undecided: toNumber(gateActual.ontology_decision_undecided)
11826
+ ontology_decision_undecided: toNumber(gateActual.ontology_decision_undecided),
11827
+ capability_expected_unknown_count: toNumber(gateActual.capability_expected_unknown_count),
11828
+ capability_provided_unknown_count: toNumber(gateActual.capability_provided_unknown_count)
11398
11829
  }
11399
11830
  },
11400
11831
  release_gate_preflight: result && result.release_gate_preflight && typeof result.release_gate_preflight === 'object'
@@ -11443,7 +11874,14 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11443
11874
  valid_rate_percent: toNumber(moquiSummary.valid_rate_percent),
11444
11875
  baseline_passed: toNumber(moquiSummary.baseline_passed),
11445
11876
  baseline_failed: toNumber(moquiSummary.baseline_failed),
11446
- portfolio_passed: moquiSummary.portfolio_passed === true
11877
+ portfolio_passed: moquiSummary.portfolio_passed === true,
11878
+ scope_breakdown: {
11879
+ moqui_erp: toNumber(moquiScopeBreakdown.moqui_erp),
11880
+ scene_orchestration: toNumber(moquiScopeBreakdown.scene_orchestration),
11881
+ other: toNumber(moquiScopeBreakdown.other)
11882
+ },
11883
+ coverage_matrix: moquiCoverageMatrix,
11884
+ gap_frequency: moquiGapFrequency
11447
11885
  },
11448
11886
  compare: Object.keys(moquiCompare).length === 0
11449
11887
  ? null
@@ -11457,6 +11895,8 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11457
11895
  baseline_passed: toNumber(moquiDeltas.baseline_passed),
11458
11896
  baseline_failed: toNumber(moquiDeltas.baseline_failed)
11459
11897
  },
11898
+ coverage_matrix_deltas: moquiCoverageMatrixDeltas,
11899
+ coverage_matrix_regressions: moquiCoverageMatrixRegressions,
11460
11900
  failed_templates: {
11461
11901
  newly_failed: Array.isArray(moquiFailedTemplates.newly_failed) ? moquiFailedTemplates.newly_failed : [],
11462
11902
  recovered: Array.isArray(moquiFailedTemplates.recovered) ? moquiFailedTemplates.recovered : []
@@ -11536,6 +11976,16 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11536
11976
  },
11537
11977
  continued_from: result && result.continued_from ? result.continued_from : null,
11538
11978
  policy: {
11979
+ max_moqui_matrix_regressions: Number.isFinite(
11980
+ Number(result && result.policy ? result.policy.max_moqui_matrix_regressions : null)
11981
+ )
11982
+ ? Number(result.policy.max_moqui_matrix_regressions)
11983
+ : null,
11984
+ require_capability_lexicon: Boolean(
11985
+ result &&
11986
+ result.policy &&
11987
+ result.policy.require_capability_lexicon === true
11988
+ ),
11539
11989
  require_release_gate_preflight: Boolean(
11540
11990
  result &&
11541
11991
  result.policy &&
@@ -11671,6 +12121,17 @@ async function writeAutoHandoffRunReport(projectPath, result, outCandidate = nul
11671
12121
  function buildAutoHandoffMoquiBaselinePhaseDetails(payload) {
11672
12122
  const baseline = payload && typeof payload === 'object' ? payload : {};
11673
12123
  const summary = baseline.summary && typeof baseline.summary === 'object' ? baseline.summary : null;
12124
+ const compare = baseline.compare && typeof baseline.compare === 'object' ? baseline.compare : {};
12125
+ const regressions = buildAutoHandoffMoquiCoverageRegressions(compare);
12126
+ const scopeBreakdown = summary && summary.scope_breakdown && typeof summary.scope_breakdown === 'object'
12127
+ ? summary.scope_breakdown
12128
+ : null;
12129
+ const coverageMatrix = summary && summary.coverage_matrix && typeof summary.coverage_matrix === 'object'
12130
+ ? summary.coverage_matrix
12131
+ : null;
12132
+ const gapFrequency = summary && Array.isArray(summary.gap_frequency)
12133
+ ? summary.gap_frequency
12134
+ : [];
11674
12135
  return {
11675
12136
  status: baseline.status || 'unknown',
11676
12137
  generated: baseline.generated === true,
@@ -11681,7 +12142,20 @@ function buildAutoHandoffMoquiBaselinePhaseDetails(payload) {
11681
12142
  : null,
11682
12143
  valid_rate_percent: summary && Number.isFinite(Number(summary.valid_rate_percent))
11683
12144
  ? Number(summary.valid_rate_percent)
11684
- : null
12145
+ : null,
12146
+ scope_breakdown: scopeBreakdown,
12147
+ coverage_matrix: coverageMatrix,
12148
+ gap_frequency_top: gapFrequency.slice(0, 5),
12149
+ entity_coverage_rate_percent: getAutoHandoffMoquiCoverageMetric(summary, 'entity_coverage', 'rate_percent'),
12150
+ relation_coverage_rate_percent: getAutoHandoffMoquiCoverageMetric(summary, 'relation_coverage', 'rate_percent'),
12151
+ business_rule_closed_rate_percent: getAutoHandoffMoquiCoverageMetric(summary, 'business_rule_closed', 'rate_percent'),
12152
+ decision_closed_rate_percent: getAutoHandoffMoquiCoverageMetric(summary, 'decision_closed', 'rate_percent'),
12153
+ coverage_matrix_deltas: getAutoHandoffMoquiCoverageDeltaMatrix(compare),
12154
+ coverage_matrix_regressions: regressions,
12155
+ entity_coverage_delta_rate_percent: getAutoHandoffMoquiCoverageDeltaMetric(compare, 'entity_coverage', 'rate_percent'),
12156
+ business_rule_closed_delta_rate_percent: getAutoHandoffMoquiCoverageDeltaMetric(compare, 'business_rule_closed', 'rate_percent'),
12157
+ decision_closed_delta_rate_percent: getAutoHandoffMoquiCoverageDeltaMetric(compare, 'decision_closed', 'rate_percent'),
12158
+ matrix_regression_count: regressions.length
11685
12159
  };
11686
12160
  }
11687
12161
 
@@ -11778,6 +12252,15 @@ async function buildAutoHandoffMoquiBaselineSnapshot(projectPath) {
11778
12252
  const failedTemplates = compare && compare.failed_templates && typeof compare.failed_templates === 'object'
11779
12253
  ? compare.failed_templates
11780
12254
  : {};
12255
+ const scopeBreakdown = summary && summary.scope_breakdown && typeof summary.scope_breakdown === 'object'
12256
+ ? summary.scope_breakdown
12257
+ : {};
12258
+ const coverageMatrix = summary && summary.coverage_matrix && typeof summary.coverage_matrix === 'object'
12259
+ ? summary.coverage_matrix
12260
+ : {};
12261
+ const gapFrequency = summary && Array.isArray(summary.gap_frequency)
12262
+ ? summary.gap_frequency
12263
+ : [];
11781
12264
 
11782
12265
  return {
11783
12266
  status: summary.portfolio_passed === true ? 'passed' : 'failed',
@@ -11789,13 +12272,22 @@ async function buildAutoHandoffMoquiBaselineSnapshot(projectPath) {
11789
12272
  valid_rate_percent: Number.isFinite(Number(summary.valid_rate_percent)) ? Number(summary.valid_rate_percent) : null,
11790
12273
  baseline_passed: Number(summary.baseline_passed) || 0,
11791
12274
  baseline_failed: Number(summary.baseline_failed) || 0,
11792
- portfolio_passed: summary.portfolio_passed === true
12275
+ portfolio_passed: summary.portfolio_passed === true,
12276
+ scope_breakdown: {
12277
+ moqui_erp: Number(scopeBreakdown.moqui_erp) || 0,
12278
+ scene_orchestration: Number(scopeBreakdown.scene_orchestration) || 0,
12279
+ other: Number(scopeBreakdown.other) || 0
12280
+ },
12281
+ coverage_matrix: coverageMatrix,
12282
+ gap_frequency: gapFrequency
11793
12283
  },
11794
12284
  compare: compare
11795
12285
  ? {
11796
12286
  previous_generated_at: compare.previous_generated_at || null,
11797
12287
  previous_template_root: compare.previous_template_root || null,
11798
12288
  deltas: compare.deltas || null,
12289
+ coverage_matrix_deltas: compare.coverage_matrix_deltas || null,
12290
+ coverage_matrix_regressions: buildAutoHandoffMoquiCoverageRegressions(compare),
11799
12291
  failed_templates: {
11800
12292
  previous: Array.isArray(failedTemplates.previous) ? failedTemplates.previous : [],
11801
12293
  current: Array.isArray(failedTemplates.current) ? failedTemplates.current : [],
@@ -12604,9 +13096,23 @@ function buildAutoHandoffCapabilityMatrixRecommendations(result = {}) {
12604
13096
  const coverageSummary = capabilityCoverage && capabilityCoverage.summary && typeof capabilityCoverage.summary === 'object'
12605
13097
  ? capabilityCoverage.summary
12606
13098
  : {};
13099
+ const coverageNormalization = capabilityCoverage && capabilityCoverage.normalization &&
13100
+ typeof capabilityCoverage.normalization === 'object'
13101
+ ? capabilityCoverage.normalization
13102
+ : {};
13103
+ const expectedUnknownCount = Array.isArray(coverageNormalization.expected_unknown)
13104
+ ? coverageNormalization.expected_unknown.length
13105
+ : 0;
13106
+ const providedUnknownCount = Array.isArray(coverageNormalization.provided_unknown)
13107
+ ? coverageNormalization.provided_unknown.length
13108
+ : 0;
12607
13109
  const baseline = result && result.moqui_baseline && typeof result.moqui_baseline === 'object'
12608
13110
  ? result.moqui_baseline
12609
13111
  : {};
13112
+ const baselineCompare = baseline && baseline.compare && typeof baseline.compare === 'object'
13113
+ ? baseline.compare
13114
+ : {};
13115
+ const baselineRegressions = buildAutoHandoffMoquiCoverageRegressions(baselineCompare);
12610
13116
 
12611
13117
  if (templateDiff.compatibility === 'needs-sync') {
12612
13118
  push(`Sync template library and rerun: sce auto handoff template-diff --manifest ${manifestCli} --json`);
@@ -12614,6 +13120,12 @@ function buildAutoHandoffCapabilityMatrixRecommendations(result = {}) {
12614
13120
  if (baseline.status === 'error' || (baseline.summary && baseline.summary.portfolio_passed === false)) {
12615
13121
  push('Rebuild Moqui baseline: sce scene moqui-baseline --json');
12616
13122
  }
13123
+ if (baselineRegressions.length > 0) {
13124
+ push(
13125
+ `Recover Moqui matrix regressions: ` +
13126
+ `${baselineRegressions.slice(0, 3).map(item => `${item.label}:${item.delta_rate_percent}%`).join(' | ')}`
13127
+ );
13128
+ }
12617
13129
  if (capabilityCoverage.status === 'skipped') {
12618
13130
  push('Declare `capabilities` in handoff manifest to enable capability matrix coverage gates.');
12619
13131
  }
@@ -12629,6 +13141,13 @@ function buildAutoHandoffCapabilityMatrixRecommendations(result = {}) {
12629
13141
  `sce scene package-ontology-backfill-batch --manifest ${manifestCli} --json`
12630
13142
  );
12631
13143
  }
13144
+ if (expectedUnknownCount > 0 || providedUnknownCount > 0) {
13145
+ push(
13146
+ `Normalize capability lexicon gaps with strict audit: ` +
13147
+ `node scripts/moqui-lexicon-audit.js --manifest ${manifestCli} ` +
13148
+ '--template-dir .kiro/templates/scene-packages --fail-on-gap --json'
13149
+ );
13150
+ }
12632
13151
  if (result.remediation_queue && result.remediation_queue.file) {
12633
13152
  push(
12634
13153
  `Replay remediation queue: sce auto close-loop-batch ${quoteCliArg(result.remediation_queue.file)} --format lines --json`
@@ -12660,12 +13179,31 @@ function renderAutoHandoffCapabilityMatrixMarkdown(payload = {}) {
12660
13179
  const baselineSummary = moquiBaseline && moquiBaseline.summary && typeof moquiBaseline.summary === 'object'
12661
13180
  ? moquiBaseline.summary
12662
13181
  : {};
13182
+ const baselineCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
13183
+ ? moquiBaseline.compare
13184
+ : {};
13185
+ const baselineScopeBreakdown = baselineSummary && baselineSummary.scope_breakdown && typeof baselineSummary.scope_breakdown === 'object'
13186
+ ? baselineSummary.scope_breakdown
13187
+ : {};
13188
+ const baselineGapFrequency = Array.isArray(baselineSummary && baselineSummary.gap_frequency)
13189
+ ? baselineSummary.gap_frequency
13190
+ : [];
12663
13191
  const capabilityCoverage = payload && payload.capability_coverage && typeof payload.capability_coverage === 'object'
12664
13192
  ? payload.capability_coverage
12665
13193
  : {};
12666
13194
  const coverageSummary = capabilityCoverage && capabilityCoverage.summary && typeof capabilityCoverage.summary === 'object'
12667
13195
  ? capabilityCoverage.summary
12668
13196
  : {};
13197
+ const coverageNormalization = capabilityCoverage && capabilityCoverage.normalization &&
13198
+ typeof capabilityCoverage.normalization === 'object'
13199
+ ? capabilityCoverage.normalization
13200
+ : {};
13201
+ const expectedUnknownCount = Array.isArray(coverageNormalization.expected_unknown)
13202
+ ? coverageNormalization.expected_unknown.length
13203
+ : 0;
13204
+ const providedUnknownCount = Array.isArray(coverageNormalization.provided_unknown)
13205
+ ? coverageNormalization.provided_unknown.length
13206
+ : 0;
12669
13207
  const coverage = Array.isArray(capabilityCoverage && capabilityCoverage.coverage)
12670
13208
  ? capabilityCoverage.coverage
12671
13209
  : [];
@@ -12685,6 +13223,7 @@ function renderAutoHandoffCapabilityMatrixMarkdown(payload = {}) {
12685
13223
  `- Capabilities: ${handoff.capability_count !== undefined ? handoff.capability_count : 'n/a'}`,
12686
13224
  `- Min capability coverage: ${policy.min_capability_coverage_percent !== undefined ? `${policy.min_capability_coverage_percent}%` : 'n/a'}`,
12687
13225
  `- Min capability semantic completeness: ${policy.min_capability_semantic_percent !== undefined ? `${policy.min_capability_semantic_percent}%` : 'n/a'}`,
13226
+ `- Capability lexicon gate: ${policy.require_capability_lexicon === false ? 'disabled' : 'enabled'}`,
12688
13227
  '',
12689
13228
  '## Gates',
12690
13229
  '',
@@ -12703,6 +13242,18 @@ function renderAutoHandoffCapabilityMatrixMarkdown(payload = {}) {
12703
13242
  `- Portfolio passed: ${baselineSummary.portfolio_passed === true ? 'yes' : (baselineSummary.portfolio_passed === false ? 'no' : 'n/a')}`,
12704
13243
  `- Avg score: ${formatAutoHandoffRegressionValue(baselineSummary.avg_score)}`,
12705
13244
  `- Valid-rate: ${formatAutoHandoffRegressionValue(baselineSummary.valid_rate_percent)}%`,
13245
+ `- Scope mix (moqui/suite/other): ${formatAutoHandoffRegressionValue(baselineScopeBreakdown.moqui_erp, '0')}/${formatAutoHandoffRegressionValue(baselineScopeBreakdown.scene_orchestration, '0')}/${formatAutoHandoffRegressionValue(baselineScopeBreakdown.other, '0')}`,
13246
+ `- Entity coverage: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'entity_coverage', 'rate_percent', '%')}`,
13247
+ `- Relation coverage: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'relation_coverage', 'rate_percent', '%')}`,
13248
+ `- Business-rule coverage: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'business_rule_coverage', 'rate_percent', '%')}`,
13249
+ `- Business-rule closed: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'business_rule_closed', 'rate_percent', '%')}`,
13250
+ `- Decision coverage: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'decision_coverage', 'rate_percent', '%')}`,
13251
+ `- Decision closed: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'decision_closed', 'rate_percent', '%')}`,
13252
+ `- Entity coverage delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(baselineCompare, 'entity_coverage', 'rate_percent', '%')}`,
13253
+ `- Business-rule closed delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(baselineCompare, 'business_rule_closed', 'rate_percent', '%')}`,
13254
+ `- Decision closed delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(baselineCompare, 'decision_closed', 'rate_percent', '%')}`,
13255
+ `- Matrix regressions: ${formatAutoHandoffMoquiCoverageRegressions(baselineCompare, 5)}`,
13256
+ `- Top baseline gaps: ${baselineGapFrequency.length > 0 ? baselineGapFrequency.slice(0, 3).map(item => `${item.gap}:${item.count}`).join(' | ') : 'none'}`,
12706
13257
  '',
12707
13258
  '## Capability Coverage',
12708
13259
  '',
@@ -12713,6 +13264,8 @@ function renderAutoHandoffCapabilityMatrixMarkdown(payload = {}) {
12713
13264
  `- Uncovered capabilities: ${formatAutoHandoffRegressionValue(coverageSummary.uncovered_capabilities, '0')}`,
12714
13265
  `- Semantic complete: ${formatAutoHandoffRegressionValue(coverageSummary.semantic_complete_percent)}%`,
12715
13266
  `- Semantic passed: ${coverageSummary.semantic_passed === true ? 'yes' : (coverageSummary.semantic_passed === false ? 'no' : 'n/a')}`,
13267
+ `- Expected unknown capability aliases: ${expectedUnknownCount}`,
13268
+ `- Provided unknown capability aliases: ${providedUnknownCount}`,
12716
13269
  '',
12717
13270
  '| Capability | Covered | Semantic Complete | Missing Semantic Dimensions | Matched Templates |',
12718
13271
  '| --- | --- | --- | --- | --- |'
@@ -12767,6 +13320,7 @@ async function buildAutoHandoffCapabilityMatrix(projectPath, options = {}) {
12767
13320
  min_capability_semantic_percent: normalizeHandoffMinCapabilitySemantic(options.minCapabilitySemantic),
12768
13321
  require_capability_coverage: true,
12769
13322
  require_capability_semantic: options.requireCapabilitySemantic !== false,
13323
+ require_capability_lexicon: options.requireCapabilityLexicon !== false,
12770
13324
  require_moqui_baseline: true
12771
13325
  };
12772
13326
 
@@ -12791,11 +13345,16 @@ async function buildAutoHandoffCapabilityMatrix(projectPath, options = {}) {
12791
13345
  policy,
12792
13346
  capabilityCoverage
12793
13347
  );
13348
+ const lexiconGateReasons = evaluateAutoHandoffCapabilityLexiconGateReasons(
13349
+ policy,
13350
+ capabilityCoverage
13351
+ );
12794
13352
  const reasons = [
12795
13353
  ...templateSyncReasons,
12796
13354
  ...baselineGateReasons.map(item => `moqui-baseline:${item}`),
12797
13355
  ...capabilityGateReasons.map(item => `capability-coverage:${item}`),
12798
- ...semanticGateReasons.map(item => `capability-semantic:${item}`)
13356
+ ...semanticGateReasons.map(item => `capability-semantic:${item}`),
13357
+ ...lexiconGateReasons.map(item => `capability-lexicon:${item}`)
12799
13358
  ];
12800
13359
 
12801
13360
  const result = {
@@ -12840,6 +13399,10 @@ async function buildAutoHandoffCapabilityMatrix(projectPath, options = {}) {
12840
13399
  capability_semantic: {
12841
13400
  passed: semanticGateReasons.length === 0,
12842
13401
  reasons: semanticGateReasons
13402
+ },
13403
+ capability_lexicon: {
13404
+ passed: lexiconGateReasons.length === 0,
13405
+ reasons: lexiconGateReasons
12843
13406
  }
12844
13407
  },
12845
13408
  remediation_queue: null,
@@ -12880,6 +13443,7 @@ function collectAutoHandoffMoquiRemediationGoals(result) {
12880
13443
  const baselineCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
12881
13444
  ? moquiBaseline.compare
12882
13445
  : null;
13446
+ const baselineRegressions = buildAutoHandoffMoquiCoverageRegressions(baselineCompare || {});
12883
13447
  const baselineFailedTemplates = baselineCompare && baselineCompare.failed_templates && typeof baselineCompare.failed_templates === 'object'
12884
13448
  ? baselineCompare.failed_templates
12885
13449
  : {};
@@ -12898,6 +13462,22 @@ function collectAutoHandoffMoquiRemediationGoals(result) {
12898
13462
  pushGoal(`remediate moqui template ${templateId} ontology semantics and close baseline gaps`);
12899
13463
  }
12900
13464
  }
13465
+ if (baselineRegressions.length > 0) {
13466
+ for (const item of baselineRegressions.slice(0, 5)) {
13467
+ pushGoal(
13468
+ `recover moqui matrix regression ${item.label} (${item.delta_rate_percent}%) by closing ontology semantic gaps`
13469
+ );
13470
+ if (item.metric === 'business_rule_closed') {
13471
+ pushGoal('remap governance_contract.business_rules for Moqui templates until closure regression is recovered');
13472
+ }
13473
+ if (item.metric === 'decision_closed') {
13474
+ pushGoal('resolve undecided governance_contract.decision_logic entries in Moqui templates');
13475
+ }
13476
+ if (item.metric === 'entity_coverage' || item.metric === 'relation_coverage') {
13477
+ pushGoal('backfill ontology_model entities/relations for regressed Moqui templates');
13478
+ }
13479
+ }
13480
+ }
12901
13481
 
12902
13482
  const scenePackageBatch = result && result.scene_package_batch && typeof result.scene_package_batch === 'object'
12903
13483
  ? result.scene_package_batch
@@ -13007,6 +13587,16 @@ function collectAutoHandoffMoquiRemediationGoals(result) {
13007
13587
  pushGoal(`align unknown manifest capability ${raw} with Moqui capability lexicon`);
13008
13588
  }
13009
13589
  }
13590
+ if (
13591
+ capabilityNormalization &&
13592
+ Array.isArray(capabilityNormalization.provided_unknown) &&
13593
+ capabilityNormalization.provided_unknown.length > 0
13594
+ ) {
13595
+ for (const item of capabilityNormalization.provided_unknown) {
13596
+ const raw = item && item.raw ? item.raw : '(unknown)';
13597
+ pushGoal(`align unknown template capability ${raw} with Moqui capability lexicon`);
13598
+ }
13599
+ }
13010
13600
 
13011
13601
  return goals;
13012
13602
  }
@@ -13229,6 +13819,13 @@ async function runAutoHandoff(projectPath, options = {}) {
13229
13819
  if (capabilityCoverageGateReasons.length > 0) {
13230
13820
  throw new Error(`handoff capability coverage gate failed: ${capabilityCoverageGateReasons.join('; ')}`);
13231
13821
  }
13822
+ const capabilityLexiconGateReasons = evaluateAutoHandoffCapabilityLexiconGateReasons(
13823
+ result.policy,
13824
+ result.moqui_capability_coverage
13825
+ );
13826
+ if (capabilityLexiconGateReasons.length > 0) {
13827
+ throw new Error(`handoff capability lexicon gate failed: ${capabilityLexiconGateReasons.join('; ')}`);
13828
+ }
13232
13829
  } catch (capabilityCoverageError) {
13233
13830
  failAutoHandoffRunPhase(capabilityCoveragePhase, capabilityCoverageError);
13234
13831
  if (!result.moqui_capability_coverage) {
@@ -14550,6 +15147,18 @@ function normalizeGovernanceHandoffQualitySnapshot(snapshotCandidate) {
14550
15147
  latest_ontology_quality_score: toGovernanceReleaseGateNumber(snapshotCandidate.latest_ontology_quality_score),
14551
15148
  latest_capability_coverage_percent: toGovernanceReleaseGateNumber(snapshotCandidate.latest_capability_coverage_percent),
14552
15149
  latest_capability_coverage_passed: parseAutoHandoffGateBoolean(snapshotCandidate.latest_capability_coverage_passed, null),
15150
+ latest_capability_expected_unknown_count: toGovernanceReleaseGateNumber(
15151
+ snapshotCandidate.latest_capability_expected_unknown_count
15152
+ ),
15153
+ latest_capability_provided_unknown_count: toGovernanceReleaseGateNumber(
15154
+ snapshotCandidate.latest_capability_provided_unknown_count
15155
+ ),
15156
+ latest_moqui_matrix_regression_count: toGovernanceReleaseGateNumber(
15157
+ snapshotCandidate.latest_moqui_matrix_regression_count
15158
+ ),
15159
+ latest_moqui_matrix_regression_gate_max: toGovernanceReleaseGateNumber(
15160
+ snapshotCandidate.latest_moqui_matrix_regression_gate_max
15161
+ ),
14553
15162
  latest_release_gate_preflight_blocked: parseAutoHandoffGateBoolean(
14554
15163
  snapshotCandidate.latest_release_gate_preflight_blocked,
14555
15164
  null
@@ -14558,6 +15167,21 @@ function normalizeGovernanceHandoffQualitySnapshot(snapshotCandidate) {
14558
15167
  gate_pass_rate_percent: toGovernanceReleaseGateNumber(snapshotCandidate.gate_pass_rate_percent),
14559
15168
  capability_coverage_pass_rate_percent: toGovernanceReleaseGateNumber(
14560
15169
  snapshotCandidate.capability_coverage_pass_rate_percent
15170
+ ),
15171
+ capability_expected_unknown_positive_rate_percent: toGovernanceReleaseGateNumber(
15172
+ snapshotCandidate.capability_expected_unknown_positive_rate_percent
15173
+ ),
15174
+ capability_provided_unknown_positive_rate_percent: toGovernanceReleaseGateNumber(
15175
+ snapshotCandidate.capability_provided_unknown_positive_rate_percent
15176
+ ),
15177
+ avg_moqui_matrix_regression_count: toGovernanceReleaseGateNumber(
15178
+ snapshotCandidate.avg_moqui_matrix_regression_count
15179
+ ),
15180
+ max_moqui_matrix_regression_count: toGovernanceReleaseGateNumber(
15181
+ snapshotCandidate.max_moqui_matrix_regression_count
15182
+ ),
15183
+ moqui_matrix_regression_positive_rate_percent: toGovernanceReleaseGateNumber(
15184
+ snapshotCandidate.moqui_matrix_regression_positive_rate_percent
14561
15185
  )
14562
15186
  };
14563
15187
  }
@@ -17226,6 +17850,10 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17226
17850
  latest_ontology_quality_score: null,
17227
17851
  latest_capability_coverage_percent: null,
17228
17852
  latest_capability_coverage_passed: null,
17853
+ latest_capability_expected_unknown_count: null,
17854
+ latest_capability_provided_unknown_count: null,
17855
+ latest_moqui_matrix_regression_count: null,
17856
+ latest_moqui_matrix_regression_gate_max: null,
17229
17857
  latest_scene_package_batch_passed: null,
17230
17858
  latest_release_gate_preflight_blocked: null,
17231
17859
  latest_failure_highlights: [],
@@ -17234,6 +17862,11 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17234
17862
  capability_coverage_pass_rate_percent: null,
17235
17863
  scene_package_batch_pass_rate_percent: null,
17236
17864
  avg_ontology_quality_score: null,
17865
+ capability_expected_unknown_positive_rate_percent: null,
17866
+ capability_provided_unknown_positive_rate_percent: null,
17867
+ avg_moqui_matrix_regression_count: null,
17868
+ max_moqui_matrix_regression_count: null,
17869
+ moqui_matrix_regression_positive_rate_percent: null,
17237
17870
  parse_error: null
17238
17871
  };
17239
17872
  if (!(await fs.pathExists(evidenceFile))) {
@@ -17298,6 +17931,101 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17298
17931
  const latestFailureHighlights = Array.isArray(latestFailureSummary.highlights)
17299
17932
  ? latestFailureSummary.highlights.map(item => `${item || ''}`.trim()).filter(Boolean).slice(0, 5)
17300
17933
  : [];
17934
+ const deriveMoquiMatrixRegressionCount = entry => {
17935
+ if (!entry || typeof entry !== 'object') {
17936
+ return null;
17937
+ }
17938
+ const gate = entry.gate && typeof entry.gate === 'object'
17939
+ ? entry.gate
17940
+ : {};
17941
+ const gateActual = gate.actual && typeof gate.actual === 'object'
17942
+ ? gate.actual
17943
+ : {};
17944
+ const explicit = toNumber(gateActual.moqui_matrix_regression_count);
17945
+ if (Number.isFinite(explicit)) {
17946
+ return Math.max(0, explicit);
17947
+ }
17948
+ const moquiBaseline = entry.moqui_baseline && typeof entry.moqui_baseline === 'object'
17949
+ ? entry.moqui_baseline
17950
+ : {};
17951
+ const compare = moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
17952
+ ? moquiBaseline.compare
17953
+ : null;
17954
+ if (!compare) {
17955
+ return null;
17956
+ }
17957
+ return buildAutoHandoffMoquiCoverageRegressions(compare).length;
17958
+ };
17959
+ const deriveMoquiMatrixRegressionGateMax = entry => {
17960
+ if (!entry || typeof entry !== 'object') {
17961
+ return null;
17962
+ }
17963
+ const gate = entry.gate && typeof entry.gate === 'object'
17964
+ ? entry.gate
17965
+ : {};
17966
+ const gateActual = gate.actual && typeof gate.actual === 'object'
17967
+ ? gate.actual
17968
+ : {};
17969
+ const explicit = toNumber(gateActual.max_moqui_matrix_regressions);
17970
+ if (Number.isFinite(explicit)) {
17971
+ return Math.max(0, explicit);
17972
+ }
17973
+ const policy = entry.policy && typeof entry.policy === 'object'
17974
+ ? entry.policy
17975
+ : {};
17976
+ const fromPolicy = toNumber(policy.max_moqui_matrix_regressions);
17977
+ return Number.isFinite(fromPolicy) ? Math.max(0, fromPolicy) : null;
17978
+ };
17979
+ const deriveCapabilityExpectedUnknownCount = entry => {
17980
+ if (!entry || typeof entry !== 'object') {
17981
+ return null;
17982
+ }
17983
+ const gate = entry.gate && typeof entry.gate === 'object'
17984
+ ? entry.gate
17985
+ : {};
17986
+ const gateActual = gate.actual && typeof gate.actual === 'object'
17987
+ ? gate.actual
17988
+ : {};
17989
+ const explicit = toNumber(gateActual.capability_expected_unknown_count);
17990
+ if (Number.isFinite(explicit)) {
17991
+ return Math.max(0, explicit);
17992
+ }
17993
+ const coverage = entry.capability_coverage && typeof entry.capability_coverage === 'object'
17994
+ ? entry.capability_coverage
17995
+ : {};
17996
+ const normalization = coverage.normalization && typeof coverage.normalization === 'object'
17997
+ ? coverage.normalization
17998
+ : {};
17999
+ const list = Array.isArray(normalization.expected_unknown) ? normalization.expected_unknown : null;
18000
+ return list ? list.length : null;
18001
+ };
18002
+ const deriveCapabilityProvidedUnknownCount = entry => {
18003
+ if (!entry || typeof entry !== 'object') {
18004
+ return null;
18005
+ }
18006
+ const gate = entry.gate && typeof entry.gate === 'object'
18007
+ ? entry.gate
18008
+ : {};
18009
+ const gateActual = gate.actual && typeof gate.actual === 'object'
18010
+ ? gate.actual
18011
+ : {};
18012
+ const explicit = toNumber(gateActual.capability_provided_unknown_count);
18013
+ if (Number.isFinite(explicit)) {
18014
+ return Math.max(0, explicit);
18015
+ }
18016
+ const coverage = entry.capability_coverage && typeof entry.capability_coverage === 'object'
18017
+ ? entry.capability_coverage
18018
+ : {};
18019
+ const normalization = coverage.normalization && typeof coverage.normalization === 'object'
18020
+ ? coverage.normalization
18021
+ : {};
18022
+ const list = Array.isArray(normalization.provided_unknown) ? normalization.provided_unknown : null;
18023
+ return list ? list.length : null;
18024
+ };
18025
+ const latestMoquiMatrixRegressionCount = deriveMoquiMatrixRegressionCount(latest);
18026
+ const latestMoquiMatrixRegressionGateMax = deriveMoquiMatrixRegressionGateMax(latest);
18027
+ const latestCapabilityExpectedUnknownCount = deriveCapabilityExpectedUnknownCount(latest);
18028
+ const latestCapabilityProvidedUnknownCount = deriveCapabilityProvidedUnknownCount(latest);
17301
18029
 
17302
18030
  let failedRuns = 0;
17303
18031
  let gateKnownRuns = 0;
@@ -17306,7 +18034,14 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17306
18034
  let capabilityPassedRuns = 0;
17307
18035
  let sceneBatchKnownRuns = 0;
17308
18036
  let sceneBatchPassedRuns = 0;
18037
+ let capabilityExpectedUnknownKnownRuns = 0;
18038
+ let capabilityExpectedUnknownPositiveRuns = 0;
18039
+ let capabilityProvidedUnknownKnownRuns = 0;
18040
+ let capabilityProvidedUnknownPositiveRuns = 0;
18041
+ let moquiMatrixKnownRuns = 0;
18042
+ let moquiMatrixPositiveRuns = 0;
17309
18043
  const ontologyScores = [];
18044
+ const moquiMatrixRegressionCounts = [];
17310
18045
  for (const entry of sessions) {
17311
18046
  const status = normalizeHandoffText(entry && entry.status);
17312
18047
  if (status && !['completed', 'dry-run', 'dry_run'].includes(status)) {
@@ -17359,6 +18094,28 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17359
18094
  if (Number.isFinite(ontologyScore)) {
17360
18095
  ontologyScores.push(ontologyScore);
17361
18096
  }
18097
+ const capabilityExpectedUnknownCount = deriveCapabilityExpectedUnknownCount(entry);
18098
+ if (Number.isFinite(capabilityExpectedUnknownCount)) {
18099
+ capabilityExpectedUnknownKnownRuns += 1;
18100
+ if (capabilityExpectedUnknownCount > 0) {
18101
+ capabilityExpectedUnknownPositiveRuns += 1;
18102
+ }
18103
+ }
18104
+ const capabilityProvidedUnknownCount = deriveCapabilityProvidedUnknownCount(entry);
18105
+ if (Number.isFinite(capabilityProvidedUnknownCount)) {
18106
+ capabilityProvidedUnknownKnownRuns += 1;
18107
+ if (capabilityProvidedUnknownCount > 0) {
18108
+ capabilityProvidedUnknownPositiveRuns += 1;
18109
+ }
18110
+ }
18111
+ const moquiMatrixRegressionCount = deriveMoquiMatrixRegressionCount(entry);
18112
+ if (Number.isFinite(moquiMatrixRegressionCount)) {
18113
+ moquiMatrixKnownRuns += 1;
18114
+ moquiMatrixRegressionCounts.push(Math.max(0, moquiMatrixRegressionCount));
18115
+ if (moquiMatrixRegressionCount > 0) {
18116
+ moquiMatrixPositiveRuns += 1;
18117
+ }
18118
+ }
17362
18119
  }
17363
18120
 
17364
18121
  return {
@@ -17379,6 +18136,18 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17379
18136
  ),
17380
18137
  latest_capability_coverage_percent: toNumber(latestCapabilitySummary.coverage_percent),
17381
18138
  latest_capability_coverage_passed: parseAutoHandoffGateBoolean(latestCapabilitySummary.passed, null),
18139
+ latest_capability_expected_unknown_count: Number.isFinite(latestCapabilityExpectedUnknownCount)
18140
+ ? latestCapabilityExpectedUnknownCount
18141
+ : null,
18142
+ latest_capability_provided_unknown_count: Number.isFinite(latestCapabilityProvidedUnknownCount)
18143
+ ? latestCapabilityProvidedUnknownCount
18144
+ : null,
18145
+ latest_moqui_matrix_regression_count: Number.isFinite(latestMoquiMatrixRegressionCount)
18146
+ ? latestMoquiMatrixRegressionCount
18147
+ : null,
18148
+ latest_moqui_matrix_regression_gate_max: Number.isFinite(latestMoquiMatrixRegressionGateMax)
18149
+ ? latestMoquiMatrixRegressionGateMax
18150
+ : null,
17382
18151
  latest_scene_package_batch_passed: parseAutoHandoffGateBoolean(latestSceneSummary.batch_gate_passed, null),
17383
18152
  latest_release_gate_preflight_blocked: parseAutoHandoffGateBoolean(latestPreflight.blocked, null),
17384
18153
  latest_failure_highlights: latestFailureHighlights,
@@ -17389,6 +18158,27 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17389
18158
  avg_ontology_quality_score: ontologyScores.length > 0
17390
18159
  ? Number((ontologyScores.reduce((sum, item) => sum + item, 0) / ontologyScores.length).toFixed(2))
17391
18160
  : null,
18161
+ capability_expected_unknown_positive_rate_percent: toPercent(
18162
+ capabilityExpectedUnknownPositiveRuns,
18163
+ capabilityExpectedUnknownKnownRuns
18164
+ ),
18165
+ capability_provided_unknown_positive_rate_percent: toPercent(
18166
+ capabilityProvidedUnknownPositiveRuns,
18167
+ capabilityProvidedUnknownKnownRuns
18168
+ ),
18169
+ avg_moqui_matrix_regression_count: moquiMatrixRegressionCounts.length > 0
18170
+ ? Number((
18171
+ moquiMatrixRegressionCounts.reduce((sum, item) => sum + item, 0) /
18172
+ moquiMatrixRegressionCounts.length
18173
+ ).toFixed(2))
18174
+ : null,
18175
+ max_moqui_matrix_regression_count: moquiMatrixRegressionCounts.length > 0
18176
+ ? Number(Math.max(...moquiMatrixRegressionCounts).toFixed(2))
18177
+ : null,
18178
+ moqui_matrix_regression_positive_rate_percent: toPercent(
18179
+ moquiMatrixPositiveRuns,
18180
+ moquiMatrixKnownRuns
18181
+ ),
17392
18182
  parse_error: null
17393
18183
  };
17394
18184
  }
@@ -17417,10 +18207,46 @@ function deriveGovernanceRiskLevel(summary) {
17417
18207
  const handoffFailureRate = Number(handoffQuality.failure_rate_percent);
17418
18208
  const handoffGatePassRate = Number(handoffQuality.gate_pass_rate_percent);
17419
18209
  const handoffCapabilityPassRate = Number(handoffQuality.capability_coverage_pass_rate_percent);
18210
+ const handoffLatestCapabilityExpectedUnknownCount = Number(
18211
+ handoffQuality.latest_capability_expected_unknown_count
18212
+ );
18213
+ const handoffLatestCapabilityProvidedUnknownCount = Number(
18214
+ handoffQuality.latest_capability_provided_unknown_count
18215
+ );
18216
+ const handoffCapabilityExpectedUnknownPositiveRate = Number(
18217
+ handoffQuality.capability_expected_unknown_positive_rate_percent
18218
+ );
18219
+ const handoffCapabilityProvidedUnknownPositiveRate = Number(
18220
+ handoffQuality.capability_provided_unknown_positive_rate_percent
18221
+ );
18222
+ const handoffLatestMoquiMatrixRegressionCount = Number(handoffQuality.latest_moqui_matrix_regression_count);
18223
+ const handoffLatestMoquiMatrixRegressionGateMax = Number(handoffQuality.latest_moqui_matrix_regression_gate_max);
18224
+ const handoffMoquiMatrixRegressionPositiveRate = Number(handoffQuality.moqui_matrix_regression_positive_rate_percent);
17420
18225
  const handoffPreflightBlocked = parseAutoHandoffGateBoolean(
17421
18226
  handoffQuality.latest_release_gate_preflight_blocked,
17422
18227
  null
17423
18228
  );
18229
+ const handoffMatrixRegressionPositive = (
18230
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
18231
+ handoffLatestMoquiMatrixRegressionCount > 0
18232
+ );
18233
+ const handoffMatrixRegressionOverGate = (
18234
+ handoffMatrixRegressionPositive &&
18235
+ Number.isFinite(handoffLatestMoquiMatrixRegressionGateMax) &&
18236
+ handoffLatestMoquiMatrixRegressionCount > handoffLatestMoquiMatrixRegressionGateMax
18237
+ );
18238
+ const handoffMatrixRegressionPressureHigh = (
18239
+ Number.isFinite(handoffMoquiMatrixRegressionPositiveRate) &&
18240
+ handoffMoquiMatrixRegressionPositiveRate >= 50
18241
+ );
18242
+ const handoffCapabilityLexiconUnknownPositive = (
18243
+ (Number.isFinite(handoffLatestCapabilityExpectedUnknownCount) && handoffLatestCapabilityExpectedUnknownCount > 0) ||
18244
+ (Number.isFinite(handoffLatestCapabilityProvidedUnknownCount) && handoffLatestCapabilityProvidedUnknownCount > 0)
18245
+ );
18246
+ const handoffCapabilityLexiconUnknownPressureHigh = (
18247
+ (Number.isFinite(handoffCapabilityExpectedUnknownPositiveRate) && handoffCapabilityExpectedUnknownPositiveRate >= 50) ||
18248
+ (Number.isFinite(handoffCapabilityProvidedUnknownPositiveRate) && handoffCapabilityProvidedUnknownPositiveRate >= 50)
18249
+ );
17424
18250
 
17425
18251
  let riskLevel = 'low';
17426
18252
  if (failureRate >= 40 || pendingGoals >= 5) {
@@ -17461,7 +18287,11 @@ function deriveGovernanceRiskLevel(summary) {
17461
18287
  handoffFailed ||
17462
18288
  handoffHighFailureRate ||
17463
18289
  handoffPreflightBlocked === true ||
17464
- (handoffLatestGatePassed === false && handoffSevereQualityDrop)
18290
+ handoffMatrixRegressionOverGate ||
18291
+ handoffCapabilityLexiconUnknownPositive ||
18292
+ (handoffLatestGatePassed === false && handoffSevereQualityDrop) ||
18293
+ (handoffMatrixRegressionPositive && handoffMatrixRegressionPressureHigh) ||
18294
+ handoffCapabilityLexiconUnknownPressureHigh
17465
18295
  ) {
17466
18296
  riskLevel = 'high';
17467
18297
  } else if (
@@ -17470,7 +18300,11 @@ function deriveGovernanceRiskLevel(summary) {
17470
18300
  (Number.isFinite(handoffGatePassRate) && handoffGatePassRate < 85) ||
17471
18301
  (Number.isFinite(handoffCapabilityPassRate) && handoffCapabilityPassRate < 85) ||
17472
18302
  (Number.isFinite(handoffLatestOntologyScore) && handoffLatestOntologyScore < 85) ||
17473
- (Number.isFinite(handoffFailureRate) && handoffFailureRate > 0)
18303
+ (Number.isFinite(handoffFailureRate) && handoffFailureRate > 0) ||
18304
+ handoffMatrixRegressionPositive ||
18305
+ (Number.isFinite(handoffMoquiMatrixRegressionPositiveRate) && handoffMoquiMatrixRegressionPositiveRate > 0) ||
18306
+ (Number.isFinite(handoffCapabilityExpectedUnknownPositiveRate) && handoffCapabilityExpectedUnknownPositiveRate > 0) ||
18307
+ (Number.isFinite(handoffCapabilityProvidedUnknownPositiveRate) && handoffCapabilityProvidedUnknownPositiveRate > 0)
17474
18308
  )
17475
18309
  ) {
17476
18310
  riskLevel = 'medium';
@@ -17505,6 +18339,22 @@ function buildGovernanceConcerns(summary) {
17505
18339
  const handoffLatestOntologyScore = Number(handoffQuality.latest_ontology_quality_score);
17506
18340
  const handoffFailureRate = Number(handoffQuality.failure_rate_percent);
17507
18341
  const handoffCapabilityPassRate = Number(handoffQuality.capability_coverage_pass_rate_percent);
18342
+ const handoffLatestCapabilityExpectedUnknownCount = Number(
18343
+ handoffQuality.latest_capability_expected_unknown_count
18344
+ );
18345
+ const handoffLatestCapabilityProvidedUnknownCount = Number(
18346
+ handoffQuality.latest_capability_provided_unknown_count
18347
+ );
18348
+ const handoffCapabilityExpectedUnknownPositiveRate = Number(
18349
+ handoffQuality.capability_expected_unknown_positive_rate_percent
18350
+ );
18351
+ const handoffCapabilityProvidedUnknownPositiveRate = Number(
18352
+ handoffQuality.capability_provided_unknown_positive_rate_percent
18353
+ );
18354
+ const handoffLatestMoquiMatrixRegressionCount = Number(handoffQuality.latest_moqui_matrix_regression_count);
18355
+ const handoffLatestMoquiMatrixRegressionGateMax = Number(handoffQuality.latest_moqui_matrix_regression_gate_max);
18356
+ const handoffMaxMoquiMatrixRegressionCount = Number(handoffQuality.max_moqui_matrix_regression_count);
18357
+ const handoffMoquiMatrixRegressionPositiveRate = Number(handoffQuality.moqui_matrix_regression_positive_rate_percent);
17508
18358
  const handoffPreflightBlocked = parseAutoHandoffGateBoolean(
17509
18359
  handoffQuality.latest_release_gate_preflight_blocked,
17510
18360
  null
@@ -17567,6 +18417,61 @@ function buildGovernanceConcerns(summary) {
17567
18417
  if (Number.isFinite(handoffCapabilityPassRate) && handoffCapabilityPassRate < 85) {
17568
18418
  concerns.push(`Handoff capability coverage pass rate is low at ${handoffCapabilityPassRate}%.`);
17569
18419
  }
18420
+ if (
18421
+ Number.isFinite(handoffLatestCapabilityExpectedUnknownCount) &&
18422
+ handoffLatestCapabilityExpectedUnknownCount > 0
18423
+ ) {
18424
+ concerns.push(
18425
+ `Latest handoff manifest capability unknown count is ${handoffLatestCapabilityExpectedUnknownCount}.`
18426
+ );
18427
+ }
18428
+ if (
18429
+ Number.isFinite(handoffLatestCapabilityProvidedUnknownCount) &&
18430
+ handoffLatestCapabilityProvidedUnknownCount > 0
18431
+ ) {
18432
+ concerns.push(
18433
+ `Latest handoff template capability unknown count is ${handoffLatestCapabilityProvidedUnknownCount}.`
18434
+ );
18435
+ }
18436
+ if (
18437
+ Number.isFinite(handoffCapabilityExpectedUnknownPositiveRate) &&
18438
+ handoffCapabilityExpectedUnknownPositiveRate > 0
18439
+ ) {
18440
+ concerns.push(
18441
+ `Handoff manifest capability unknown positive rate is ${handoffCapabilityExpectedUnknownPositiveRate}%.`
18442
+ );
18443
+ }
18444
+ if (
18445
+ Number.isFinite(handoffCapabilityProvidedUnknownPositiveRate) &&
18446
+ handoffCapabilityProvidedUnknownPositiveRate > 0
18447
+ ) {
18448
+ concerns.push(
18449
+ `Handoff template capability unknown positive rate is ${handoffCapabilityProvidedUnknownPositiveRate}%.`
18450
+ );
18451
+ }
18452
+ if (Number.isFinite(handoffLatestMoquiMatrixRegressionCount) && handoffLatestMoquiMatrixRegressionCount > 0) {
18453
+ concerns.push(
18454
+ `Latest handoff Moqui matrix regression count is ${handoffLatestMoquiMatrixRegressionCount}.`
18455
+ );
18456
+ }
18457
+ if (
18458
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
18459
+ Number.isFinite(handoffLatestMoquiMatrixRegressionGateMax) &&
18460
+ handoffLatestMoquiMatrixRegressionCount > handoffLatestMoquiMatrixRegressionGateMax
18461
+ ) {
18462
+ concerns.push(
18463
+ `Latest handoff Moqui matrix regressions exceed gate (${handoffLatestMoquiMatrixRegressionCount} > ` +
18464
+ `${handoffLatestMoquiMatrixRegressionGateMax}).`
18465
+ );
18466
+ }
18467
+ if (Number.isFinite(handoffMoquiMatrixRegressionPositiveRate) && handoffMoquiMatrixRegressionPositiveRate > 0) {
18468
+ concerns.push(
18469
+ `Handoff Moqui matrix regression positive rate is ${handoffMoquiMatrixRegressionPositiveRate}%.`
18470
+ );
18471
+ }
18472
+ if (Number.isFinite(handoffMaxMoquiMatrixRegressionCount) && handoffMaxMoquiMatrixRegressionCount > 0) {
18473
+ concerns.push(`Handoff Moqui matrix regression max is ${handoffMaxMoquiMatrixRegressionCount}.`);
18474
+ }
17570
18475
  }
17571
18476
 
17572
18477
  return concerns;
@@ -17594,6 +18499,21 @@ function buildGovernanceRecommendations(summary) {
17594
18499
  const handoffLatestGatePassed = parseAutoHandoffGateBoolean(handoffQuality.latest_gate_passed, null);
17595
18500
  const handoffFailureRate = Number(handoffQuality.failure_rate_percent);
17596
18501
  const handoffCapabilityPassRate = Number(handoffQuality.capability_coverage_pass_rate_percent);
18502
+ const handoffLatestCapabilityExpectedUnknownCount = Number(
18503
+ handoffQuality.latest_capability_expected_unknown_count
18504
+ );
18505
+ const handoffLatestCapabilityProvidedUnknownCount = Number(
18506
+ handoffQuality.latest_capability_provided_unknown_count
18507
+ );
18508
+ const handoffCapabilityExpectedUnknownPositiveRate = Number(
18509
+ handoffQuality.capability_expected_unknown_positive_rate_percent
18510
+ );
18511
+ const handoffCapabilityProvidedUnknownPositiveRate = Number(
18512
+ handoffQuality.capability_provided_unknown_positive_rate_percent
18513
+ );
18514
+ const handoffLatestMoquiMatrixRegressionCount = Number(handoffQuality.latest_moqui_matrix_regression_count);
18515
+ const handoffLatestMoquiMatrixRegressionGateMax = Number(handoffQuality.latest_moqui_matrix_regression_gate_max);
18516
+ const handoffMoquiMatrixRegressionPositiveRate = Number(handoffQuality.moqui_matrix_regression_positive_rate_percent);
17597
18517
  const handoffPreflightBlocked = parseAutoHandoffGateBoolean(
17598
18518
  handoffQuality.latest_release_gate_preflight_blocked,
17599
18519
  null
@@ -17635,6 +18555,15 @@ function buildGovernanceRecommendations(summary) {
17635
18555
  '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --dry-run --json`.'
17636
18556
  );
17637
18557
  } else if (handoffTotalRuns > 0) {
18558
+ const handoffMoquiMatrixRegressionPositive = (
18559
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
18560
+ handoffLatestMoquiMatrixRegressionCount > 0
18561
+ );
18562
+ const handoffMoquiMatrixRegressionOverGate = (
18563
+ handoffMoquiMatrixRegressionPositive &&
18564
+ Number.isFinite(handoffLatestMoquiMatrixRegressionGateMax) &&
18565
+ handoffLatestMoquiMatrixRegressionCount > handoffLatestMoquiMatrixRegressionGateMax
18566
+ );
17638
18567
  if (
17639
18568
  (handoffLatestStatus && !['completed', 'dry-run', 'dry_run'].includes(handoffLatestStatus)) ||
17640
18569
  handoffLatestGatePassed === false ||
@@ -17655,6 +18584,38 @@ function buildGovernanceRecommendations(summary) {
17655
18584
  '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --dry-run --json`.'
17656
18585
  );
17657
18586
  }
18587
+ if (
18588
+ (Number.isFinite(handoffLatestCapabilityExpectedUnknownCount) && handoffLatestCapabilityExpectedUnknownCount > 0) ||
18589
+ (Number.isFinite(handoffLatestCapabilityProvidedUnknownCount) && handoffLatestCapabilityProvidedUnknownCount > 0) ||
18590
+ (Number.isFinite(handoffCapabilityExpectedUnknownPositiveRate) && handoffCapabilityExpectedUnknownPositiveRate > 0) ||
18591
+ (Number.isFinite(handoffCapabilityProvidedUnknownPositiveRate) && handoffCapabilityProvidedUnknownPositiveRate > 0)
18592
+ ) {
18593
+ recommendations.push(
18594
+ 'Normalize capability lexicon gaps with ' +
18595
+ '`node scripts/moqui-lexicon-audit.js --manifest docs/handoffs/handoff-manifest.json ' +
18596
+ '--template-dir .kiro/templates/scene-packages --fail-on-gap --json`.'
18597
+ );
18598
+ recommendations.push(
18599
+ 'Re-run strict handoff lexicon gates with ' +
18600
+ '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --dry-run --json`.'
18601
+ );
18602
+ }
18603
+ if (
18604
+ handoffMoquiMatrixRegressionPositive ||
18605
+ handoffMoquiMatrixRegressionOverGate ||
18606
+ (Number.isFinite(handoffMoquiMatrixRegressionPositiveRate) && handoffMoquiMatrixRegressionPositiveRate > 0)
18607
+ ) {
18608
+ recommendations.push(
18609
+ 'Recover Moqui matrix regressions via ' +
18610
+ '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json ' +
18611
+ '--dry-run --max-moqui-matrix-regressions 0 --json`.'
18612
+ );
18613
+ recommendations.push(
18614
+ 'Inspect Moqui baseline matrix drift with ' +
18615
+ '`sce scene moqui-baseline --include-all ' +
18616
+ '--compare-with .kiro/reports/release-evidence/moqui-template-baseline.json --json`.'
18617
+ );
18618
+ }
17658
18619
  }
17659
18620
 
17660
18621
  return Array.from(new Set(recommendations));
@@ -19002,12 +19963,64 @@ function evaluateGovernanceReleaseGateBlockState(assessment) {
19002
19963
  if (handoffSnapshot.latest_capability_coverage_passed === false) {
19003
19964
  reasons.push('handoff-capability-coverage-failed');
19004
19965
  }
19966
+ if (
19967
+ Number.isFinite(handoffSnapshot.latest_capability_expected_unknown_count) &&
19968
+ handoffSnapshot.latest_capability_expected_unknown_count > 0
19969
+ ) {
19970
+ reasons.push(
19971
+ `handoff-capability-expected-unknown-positive:${handoffSnapshot.latest_capability_expected_unknown_count}`
19972
+ );
19973
+ }
19974
+ if (
19975
+ Number.isFinite(handoffSnapshot.latest_capability_provided_unknown_count) &&
19976
+ handoffSnapshot.latest_capability_provided_unknown_count > 0
19977
+ ) {
19978
+ reasons.push(
19979
+ `handoff-capability-provided-unknown-positive:${handoffSnapshot.latest_capability_provided_unknown_count}`
19980
+ );
19981
+ }
19982
+ if (
19983
+ Number.isFinite(handoffSnapshot.capability_expected_unknown_positive_rate_percent) &&
19984
+ handoffSnapshot.capability_expected_unknown_positive_rate_percent > 0
19985
+ ) {
19986
+ reasons.push(
19987
+ `handoff-capability-expected-unknown-positive-rate:` +
19988
+ `${handoffSnapshot.capability_expected_unknown_positive_rate_percent}`
19989
+ );
19990
+ }
19991
+ if (
19992
+ Number.isFinite(handoffSnapshot.capability_provided_unknown_positive_rate_percent) &&
19993
+ handoffSnapshot.capability_provided_unknown_positive_rate_percent > 0
19994
+ ) {
19995
+ reasons.push(
19996
+ `handoff-capability-provided-unknown-positive-rate:` +
19997
+ `${handoffSnapshot.capability_provided_unknown_positive_rate_percent}`
19998
+ );
19999
+ }
19005
20000
  if (handoffSnapshot.latest_release_gate_preflight_blocked === true) {
19006
20001
  reasons.push('handoff-release-preflight-blocked');
19007
20002
  }
19008
20003
  if (Number.isFinite(handoffSnapshot.failure_rate_percent) && handoffSnapshot.failure_rate_percent > 0) {
19009
20004
  reasons.push(`handoff-failure-rate-positive:${handoffSnapshot.failure_rate_percent}`);
19010
20005
  }
20006
+ const handoffLatestMoquiMatrixRegressionCount = Number(handoffSnapshot.latest_moqui_matrix_regression_count);
20007
+ const handoffLatestMoquiMatrixRegressionGateMax = Number(handoffSnapshot.latest_moqui_matrix_regression_gate_max);
20008
+ if (
20009
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
20010
+ handoffLatestMoquiMatrixRegressionCount > 0
20011
+ ) {
20012
+ reasons.push(`handoff-moqui-matrix-regressions-positive:${handoffLatestMoquiMatrixRegressionCount}`);
20013
+ }
20014
+ if (
20015
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
20016
+ Number.isFinite(handoffLatestMoquiMatrixRegressionGateMax) &&
20017
+ handoffLatestMoquiMatrixRegressionCount > handoffLatestMoquiMatrixRegressionGateMax
20018
+ ) {
20019
+ reasons.push(
20020
+ `handoff-moqui-matrix-regressions-over-gate:` +
20021
+ `${handoffLatestMoquiMatrixRegressionCount}/${handoffLatestMoquiMatrixRegressionGateMax}`
20022
+ );
20023
+ }
19011
20024
  }
19012
20025
 
19013
20026
  const blockedByReleaseGate = Boolean(
@@ -19024,7 +20037,13 @@ function evaluateGovernanceReleaseGateBlockState(assessment) {
19024
20037
  || `${item}` === 'handoff-latest-gate-failed'
19025
20038
  || `${item}`.startsWith('handoff-ontology-score-low:')
19026
20039
  || `${item}` === 'handoff-capability-coverage-failed'
20040
+ || `${item}`.startsWith('handoff-capability-expected-unknown-positive:')
20041
+ || `${item}`.startsWith('handoff-capability-provided-unknown-positive:')
20042
+ || `${item}`.startsWith('handoff-capability-expected-unknown-positive-rate:')
20043
+ || `${item}`.startsWith('handoff-capability-provided-unknown-positive-rate:')
19027
20044
  || `${item}` === 'handoff-release-preflight-blocked'
20045
+ || `${item}`.startsWith('handoff-moqui-matrix-regressions-positive:')
20046
+ || `${item}`.startsWith('handoff-moqui-matrix-regressions-over-gate:')
19028
20047
  ));
19029
20048
  const blocked = reasons.length > 0 && (blockedByReleaseGate || blockedByHandoffQuality);
19030
20049
  return {
@@ -19066,6 +20085,29 @@ function buildGovernanceCloseLoopRecommendations(finalAssessment, stopReason, st
19066
20085
  '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --continue-from latest --continue-strategy failed-only --json`.'
19067
20086
  );
19068
20087
  }
20088
+ if (reasons.some(item => `${item}`.startsWith('handoff-moqui-matrix-regressions-'))) {
20089
+ base.push(
20090
+ 'Recover Moqui matrix regressions with ' +
20091
+ '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json ' +
20092
+ '--dry-run --max-moqui-matrix-regressions 0 --json`.'
20093
+ );
20094
+ base.push(
20095
+ 'Inspect Moqui matrix drift with ' +
20096
+ '`sce scene moqui-baseline --include-all ' +
20097
+ '--compare-with .kiro/reports/release-evidence/moqui-template-baseline.json --json`.'
20098
+ );
20099
+ }
20100
+ if (reasons.some(item => `${item}`.startsWith('handoff-capability-') && `${item}`.includes('unknown'))) {
20101
+ base.push(
20102
+ 'Normalize capability lexicon gaps with ' +
20103
+ '`node scripts/moqui-lexicon-audit.js --manifest docs/handoffs/handoff-manifest.json ' +
20104
+ '--template-dir .kiro/templates/scene-packages --fail-on-gap --json`.'
20105
+ );
20106
+ base.push(
20107
+ 'Re-run strict handoff gates after lexicon normalization with ' +
20108
+ '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --dry-run --json`.'
20109
+ );
20110
+ }
19069
20111
  return Array.from(new Set(base));
19070
20112
  }
19071
20113