scene-capability-engine 3.0.2 → 3.0.4

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',
@@ -8755,6 +8954,24 @@ function buildAutoHandoffReleaseGateHistoryEntry(entry = {}, options = {}) {
8755
8954
  ? entry.scene_package_batch_failure_count
8756
8955
  : signalMap.scene_package_batch_failure_count
8757
8956
  );
8957
+ const capabilityExpectedUnknownCount = parseAutoHandoffGateNumber(
8958
+ entry.capability_expected_unknown_count !== undefined
8959
+ ? entry.capability_expected_unknown_count
8960
+ : (
8961
+ signalMap.capability_expected_unknown_count !== undefined
8962
+ ? signalMap.capability_expected_unknown_count
8963
+ : signalMap.capability_lexicon_expected_unknown_count
8964
+ )
8965
+ );
8966
+ const capabilityProvidedUnknownCount = parseAutoHandoffGateNumber(
8967
+ entry.capability_provided_unknown_count !== undefined
8968
+ ? entry.capability_provided_unknown_count
8969
+ : (
8970
+ signalMap.capability_provided_unknown_count !== undefined
8971
+ ? signalMap.capability_provided_unknown_count
8972
+ : signalMap.capability_lexicon_provided_unknown_count
8973
+ )
8974
+ );
8758
8975
  const releaseGatePreflightAvailable = parseAutoHandoffGateBoolean(
8759
8976
  entry.release_gate_preflight_available !== undefined
8760
8977
  ? entry.release_gate_preflight_available
@@ -8859,6 +9076,12 @@ function buildAutoHandoffReleaseGateHistoryEntry(entry = {}, options = {}) {
8859
9076
  scene_package_batch_status: sceneBatchStatus || null,
8860
9077
  scene_package_batch_passed: typeof sceneBatchPassed === 'boolean' ? sceneBatchPassed : null,
8861
9078
  scene_package_batch_failure_count: Number.isFinite(sceneBatchFailureCount) ? sceneBatchFailureCount : null,
9079
+ capability_expected_unknown_count: Number.isFinite(capabilityExpectedUnknownCount)
9080
+ ? Math.max(0, Number(capabilityExpectedUnknownCount))
9081
+ : null,
9082
+ capability_provided_unknown_count: Number.isFinite(capabilityProvidedUnknownCount)
9083
+ ? Math.max(0, Number(capabilityProvidedUnknownCount))
9084
+ : null,
8862
9085
  release_gate_preflight_available: typeof releaseGatePreflightAvailable === 'boolean'
8863
9086
  ? releaseGatePreflightAvailable
8864
9087
  : null,
@@ -9021,6 +9244,12 @@ function buildAutoHandoffReleaseGateHistoryAggregates(entries = []) {
9021
9244
  let preflightAvailableRuns = 0;
9022
9245
  let preflightBlockedRuns = 0;
9023
9246
  let preflightHardGateRuns = 0;
9247
+ let capabilityExpectedUnknownKnownRuns = 0;
9248
+ let capabilityExpectedUnknownPositiveRuns = 0;
9249
+ let capabilityProvidedUnknownKnownRuns = 0;
9250
+ let capabilityProvidedUnknownPositiveRuns = 0;
9251
+ const capabilityExpectedUnknownCounts = [];
9252
+ const capabilityProvidedUnknownCounts = [];
9024
9253
  const sceneBatchFailureCounts = [];
9025
9254
  let sceneBatchApplicableCount = 0;
9026
9255
  let sceneBatchPassedCount = 0;
@@ -9069,6 +9298,28 @@ function buildAutoHandoffReleaseGateHistoryAggregates(entries = []) {
9069
9298
  if (Number.isFinite(sceneBatchFailureCount)) {
9070
9299
  sceneBatchFailureCounts.push(sceneBatchFailureCount);
9071
9300
  }
9301
+ const capabilityExpectedUnknownCount = parseAutoHandoffGateNumber(
9302
+ entry && entry.capability_expected_unknown_count
9303
+ );
9304
+ if (Number.isFinite(capabilityExpectedUnknownCount)) {
9305
+ const normalizedCount = Math.max(0, Number(capabilityExpectedUnknownCount));
9306
+ capabilityExpectedUnknownKnownRuns += 1;
9307
+ capabilityExpectedUnknownCounts.push(normalizedCount);
9308
+ if (normalizedCount > 0) {
9309
+ capabilityExpectedUnknownPositiveRuns += 1;
9310
+ }
9311
+ }
9312
+ const capabilityProvidedUnknownCount = parseAutoHandoffGateNumber(
9313
+ entry && entry.capability_provided_unknown_count
9314
+ );
9315
+ if (Number.isFinite(capabilityProvidedUnknownCount)) {
9316
+ const normalizedCount = Math.max(0, Number(capabilityProvidedUnknownCount));
9317
+ capabilityProvidedUnknownKnownRuns += 1;
9318
+ capabilityProvidedUnknownCounts.push(normalizedCount);
9319
+ if (normalizedCount > 0) {
9320
+ capabilityProvidedUnknownPositiveRuns += 1;
9321
+ }
9322
+ }
9072
9323
  const preflightAvailable = parseAutoHandoffGateBoolean(
9073
9324
  entry && entry.release_gate_preflight_available,
9074
9325
  null
@@ -9149,6 +9400,24 @@ function buildAutoHandoffReleaseGateHistoryAggregates(entries = []) {
9149
9400
  const maxSceneBatchFailureCount = sceneBatchFailureCounts.length > 0
9150
9401
  ? Number(Math.max(...sceneBatchFailureCounts).toFixed(2))
9151
9402
  : null;
9403
+ const avgCapabilityExpectedUnknownCount = capabilityExpectedUnknownCounts.length > 0
9404
+ ? Number((capabilityExpectedUnknownCounts.reduce((sum, value) => sum + value, 0) / capabilityExpectedUnknownCounts.length).toFixed(2))
9405
+ : null;
9406
+ const maxCapabilityExpectedUnknownCount = capabilityExpectedUnknownCounts.length > 0
9407
+ ? Number(Math.max(...capabilityExpectedUnknownCounts).toFixed(2))
9408
+ : null;
9409
+ const capabilityExpectedUnknownPositiveRate = capabilityExpectedUnknownKnownRuns > 0
9410
+ ? Number(((capabilityExpectedUnknownPositiveRuns / capabilityExpectedUnknownKnownRuns) * 100).toFixed(2))
9411
+ : null;
9412
+ const avgCapabilityProvidedUnknownCount = capabilityProvidedUnknownCounts.length > 0
9413
+ ? Number((capabilityProvidedUnknownCounts.reduce((sum, value) => sum + value, 0) / capabilityProvidedUnknownCounts.length).toFixed(2))
9414
+ : null;
9415
+ const maxCapabilityProvidedUnknownCount = capabilityProvidedUnknownCounts.length > 0
9416
+ ? Number(Math.max(...capabilityProvidedUnknownCounts).toFixed(2))
9417
+ : null;
9418
+ const capabilityProvidedUnknownPositiveRate = capabilityProvidedUnknownKnownRuns > 0
9419
+ ? Number(((capabilityProvidedUnknownPositiveRuns / capabilityProvidedUnknownKnownRuns) * 100).toFixed(2))
9420
+ : null;
9152
9421
  const driftAlertRate = driftKnownRuns > 0
9153
9422
  ? Number(((driftAlertRuns / driftKnownRuns) * 100).toFixed(2))
9154
9423
  : null;
@@ -9181,6 +9450,16 @@ function buildAutoHandoffReleaseGateHistoryAggregates(entries = []) {
9181
9450
  scene_package_batch_pass_rate_percent: sceneBatchPassRate,
9182
9451
  avg_scene_package_batch_failure_count: avgSceneBatchFailureCount,
9183
9452
  max_scene_package_batch_failure_count: maxSceneBatchFailureCount,
9453
+ capability_expected_unknown_known_runs: capabilityExpectedUnknownKnownRuns,
9454
+ capability_expected_unknown_positive_runs: capabilityExpectedUnknownPositiveRuns,
9455
+ capability_expected_unknown_positive_rate_percent: capabilityExpectedUnknownPositiveRate,
9456
+ avg_capability_expected_unknown_count: avgCapabilityExpectedUnknownCount,
9457
+ max_capability_expected_unknown_count: maxCapabilityExpectedUnknownCount,
9458
+ capability_provided_unknown_known_runs: capabilityProvidedUnknownKnownRuns,
9459
+ capability_provided_unknown_positive_runs: capabilityProvidedUnknownPositiveRuns,
9460
+ capability_provided_unknown_positive_rate_percent: capabilityProvidedUnknownPositiveRate,
9461
+ avg_capability_provided_unknown_count: avgCapabilityProvidedUnknownCount,
9462
+ max_capability_provided_unknown_count: maxCapabilityProvidedUnknownCount,
9184
9463
  drift_known_runs: driftKnownRuns,
9185
9464
  drift_alert_total: driftAlertTotal,
9186
9465
  drift_alert_runs: driftAlertRuns,
@@ -9246,6 +9525,8 @@ async function buildAutoHandoffReleaseGateHistoryIndex(projectPath, options = {}
9246
9525
  risk_level: latestEntry.risk_level,
9247
9526
  scene_package_batch_passed: latestEntry.scene_package_batch_passed,
9248
9527
  scene_package_batch_failure_count: latestEntry.scene_package_batch_failure_count,
9528
+ capability_expected_unknown_count: latestEntry.capability_expected_unknown_count,
9529
+ capability_provided_unknown_count: latestEntry.capability_provided_unknown_count,
9249
9530
  release_gate_preflight_available: latestEntry.release_gate_preflight_available,
9250
9531
  release_gate_preflight_blocked: latestEntry.release_gate_preflight_blocked,
9251
9532
  require_release_gate_preflight: latestEntry.require_release_gate_preflight,
@@ -9291,6 +9572,8 @@ function renderAutoHandoffReleaseGateHistoryMarkdown(payload = {}) {
9291
9572
  lines.push(`- Risk level: ${formatAutoHandoffRegressionValue(latest.risk_level)}`);
9292
9573
  lines.push(`- Scene package batch: ${latest.scene_package_batch_passed === true ? 'pass' : (latest.scene_package_batch_passed === false ? 'fail' : 'n/a')}`);
9293
9574
  lines.push(`- Scene package batch failures: ${formatAutoHandoffRegressionValue(latest.scene_package_batch_failure_count)}`);
9575
+ lines.push(`- Capability expected unknown count: ${formatAutoHandoffRegressionValue(latest.capability_expected_unknown_count, '0')}`);
9576
+ lines.push(`- Capability provided unknown count: ${formatAutoHandoffRegressionValue(latest.capability_provided_unknown_count, '0')}`);
9294
9577
  lines.push(`- Release preflight available: ${latest.release_gate_preflight_available === true ? 'yes' : (latest.release_gate_preflight_available === false ? 'no' : 'n/a')}`);
9295
9578
  lines.push(`- Release preflight blocked: ${latest.release_gate_preflight_blocked === true ? 'yes' : (latest.release_gate_preflight_blocked === false ? 'no' : 'n/a')}`);
9296
9579
  lines.push(`- Release preflight hard-gate: ${latest.require_release_gate_preflight === true ? 'enabled' : (latest.require_release_gate_preflight === false ? 'advisory' : 'n/a')}`);
@@ -9312,6 +9595,12 @@ function renderAutoHandoffReleaseGateHistoryMarkdown(payload = {}) {
9312
9595
  lines.push(`- Scene package batch pass rate: ${formatAutoHandoffRegressionValue(aggregates.scene_package_batch_pass_rate_percent)}%`);
9313
9596
  lines.push(`- Scene package batch failed: ${formatAutoHandoffRegressionValue(aggregates.scene_package_batch_failed_count, '0')}`);
9314
9597
  lines.push(`- Avg scene package batch failures: ${formatAutoHandoffRegressionValue(aggregates.avg_scene_package_batch_failure_count)}`);
9598
+ lines.push(`- Capability expected unknown positive rate: ${formatAutoHandoffRegressionValue(aggregates.capability_expected_unknown_positive_rate_percent)}%`);
9599
+ lines.push(`- Avg capability expected unknown count: ${formatAutoHandoffRegressionValue(aggregates.avg_capability_expected_unknown_count)}`);
9600
+ lines.push(`- Max capability expected unknown count: ${formatAutoHandoffRegressionValue(aggregates.max_capability_expected_unknown_count)}`);
9601
+ lines.push(`- Capability provided unknown positive rate: ${formatAutoHandoffRegressionValue(aggregates.capability_provided_unknown_positive_rate_percent)}%`);
9602
+ lines.push(`- Avg capability provided unknown count: ${formatAutoHandoffRegressionValue(aggregates.avg_capability_provided_unknown_count)}`);
9603
+ lines.push(`- Max capability provided unknown count: ${formatAutoHandoffRegressionValue(aggregates.max_capability_provided_unknown_count)}`);
9315
9604
  lines.push(`- Drift alert runs: ${formatAutoHandoffRegressionValue(aggregates.drift_alert_runs, '0')}`);
9316
9605
  lines.push(`- Drift blocked runs: ${formatAutoHandoffRegressionValue(aggregates.drift_blocked_runs, '0')}`);
9317
9606
  lines.push(`- Drift alert rate: ${formatAutoHandoffRegressionValue(aggregates.drift_alert_rate_percent)}%`);
@@ -9343,6 +9632,14 @@ function renderAutoHandoffReleaseGateHistoryMarkdown(payload = {}) {
9343
9632
  const sceneBatchFailures = formatAutoHandoffRegressionValue(
9344
9633
  entry && entry.scene_package_batch_failure_count
9345
9634
  );
9635
+ const capabilityExpectedUnknown = formatAutoHandoffRegressionValue(
9636
+ entry && entry.capability_expected_unknown_count,
9637
+ '0'
9638
+ );
9639
+ const capabilityProvidedUnknown = formatAutoHandoffRegressionValue(
9640
+ entry && entry.capability_provided_unknown_count,
9641
+ '0'
9642
+ );
9346
9643
  const preflightBlocked = entry && entry.release_gate_preflight_blocked === true
9347
9644
  ? 'yes'
9348
9645
  : (entry && entry.release_gate_preflight_blocked === false ? 'no' : 'n/a');
@@ -9355,7 +9652,8 @@ function renderAutoHandoffReleaseGateHistoryMarkdown(payload = {}) {
9355
9652
  : (entry && entry.drift_blocked === false ? 'no' : 'n/a');
9356
9653
  lines.push(
9357
9654
  `- ${tag} | passed=${passed} | risk=${risk} | scene-batch=${sceneBatch} | ` +
9358
- `scene-failures=${sceneBatchFailures} | preflight-blocked=${preflightBlocked} | hard-gate=${preflightHardGate} | ` +
9655
+ `scene-failures=${sceneBatchFailures} | capability-unknown=${capabilityExpectedUnknown}/${capabilityProvidedUnknown} | ` +
9656
+ `preflight-blocked=${preflightBlocked} | hard-gate=${preflightHardGate} | ` +
9359
9657
  `drift-alerts=${driftAlerts} | drift-blocked=${driftBlocked} | ` +
9360
9658
  `success=${successRate} | violations=${violations} | at=${evaluatedAt}`
9361
9659
  );
@@ -9426,6 +9724,13 @@ function buildAutoHandoffEvidenceSnapshot(entry = {}) {
9426
9724
  const ontologyMetrics = ontology && typeof ontology.metrics === 'object'
9427
9725
  ? ontology.metrics
9428
9726
  : {};
9727
+ const moquiBaseline = entry && typeof entry.moqui_baseline === 'object'
9728
+ ? entry.moqui_baseline
9729
+ : {};
9730
+ const moquiCompare = moquiBaseline && typeof moquiBaseline.compare === 'object'
9731
+ ? moquiBaseline.compare
9732
+ : {};
9733
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
9429
9734
  const scenePackageBatch = entry && typeof entry.scene_package_batch === 'object'
9430
9735
  ? entry.scene_package_batch
9431
9736
  : {};
@@ -9490,6 +9795,7 @@ function buildAutoHandoffEvidenceSnapshot(entry = {}) {
9490
9795
  entry.capability_coverage.summary &&
9491
9796
  entry.capability_coverage.summary.passed === true
9492
9797
  ),
9798
+ moqui_matrix_regression_count: moquiMatrixRegressions.length,
9493
9799
  generated_at: normalizeHandoffText(entry.merged_at)
9494
9800
  };
9495
9801
  }
@@ -9534,6 +9840,9 @@ function renderAutoHandoffEvidenceReviewMarkdown(payload = {}) {
9534
9840
  const failureSummary = currentOverview.failure_summary && typeof currentOverview.failure_summary === 'object'
9535
9841
  ? currentOverview.failure_summary
9536
9842
  : {};
9843
+ const currentPolicy = currentOverview.policy && typeof currentOverview.policy === 'object'
9844
+ ? currentOverview.policy
9845
+ : {};
9537
9846
  const ontology = currentOverview.ontology_validation && typeof currentOverview.ontology_validation === 'object'
9538
9847
  ? currentOverview.ontology_validation
9539
9848
  : {};
@@ -9549,9 +9858,16 @@ function renderAutoHandoffEvidenceReviewMarkdown(payload = {}) {
9549
9858
  const moquiSummary = moquiBaseline && moquiBaseline.summary && typeof moquiBaseline.summary === 'object'
9550
9859
  ? moquiBaseline.summary
9551
9860
  : {};
9861
+ const moquiScopeBreakdown = moquiSummary && moquiSummary.scope_breakdown && typeof moquiSummary.scope_breakdown === 'object'
9862
+ ? moquiSummary.scope_breakdown
9863
+ : {};
9864
+ const moquiGapFrequency = Array.isArray(moquiSummary && moquiSummary.gap_frequency)
9865
+ ? moquiSummary.gap_frequency
9866
+ : [];
9552
9867
  const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
9553
9868
  ? moquiBaseline.compare
9554
9869
  : {};
9870
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
9555
9871
  const moquiDeltas = moquiCompare && moquiCompare.deltas && typeof moquiCompare.deltas === 'object'
9556
9872
  ? moquiCompare.deltas
9557
9873
  : {};
@@ -9634,7 +9950,8 @@ function renderAutoHandoffEvidenceReviewMarkdown(payload = {}) {
9634
9950
  `avg_success=${formatAutoHandoffRegressionValue(scoped.avg_spec_success_rate_percent)}, ` +
9635
9951
  `avg_failed_goals=${formatAutoHandoffRegressionValue(scoped.avg_failed_goals)}, ` +
9636
9952
  `avg_ontology_quality=${formatAutoHandoffRegressionValue(scoped.avg_ontology_quality_score)}, ` +
9637
- `scene_batch_pass_rate=${formatAutoHandoffRegressionValue(scoped.scene_package_batch_pass_rate_percent)}%`
9953
+ `scene_batch_pass_rate=${formatAutoHandoffRegressionValue(scoped.scene_package_batch_pass_rate_percent)}%, ` +
9954
+ `avg_moqui_matrix_regressions=${formatAutoHandoffRegressionValue(scoped.avg_moqui_matrix_regression_count, '0')}`
9638
9955
  );
9639
9956
  });
9640
9957
 
@@ -9700,10 +10017,24 @@ function renderAutoHandoffEvidenceReviewMarkdown(payload = {}) {
9700
10017
  `- Avg score: ${formatAutoHandoffRegressionValue(moquiSummary.avg_score)}`,
9701
10018
  `- Valid-rate: ${formatAutoHandoffRegressionValue(moquiSummary.valid_rate_percent)}%`,
9702
10019
  `- Baseline failed templates: ${formatAutoHandoffRegressionValue(moquiSummary.baseline_failed)}`,
10020
+ `- Matrix regression count: ${formatAutoHandoffRegressionValue(moquiMatrixRegressions.length, '0')}`,
10021
+ `- Matrix regression gate (max): ${formatAutoHandoffRegressionValue(currentPolicy.max_moqui_matrix_regressions)}`,
10022
+ `- Scope mix (moqui/suite/other): ${formatAutoHandoffRegressionValue(moquiScopeBreakdown.moqui_erp, '0')}/${formatAutoHandoffRegressionValue(moquiScopeBreakdown.scene_orchestration, '0')}/${formatAutoHandoffRegressionValue(moquiScopeBreakdown.other, '0')}`,
10023
+ `- Entity coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'entity_coverage', 'rate_percent', '%')}`,
10024
+ `- Relation coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'relation_coverage', 'rate_percent', '%')}`,
10025
+ `- Business-rule coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'business_rule_coverage', 'rate_percent', '%')}`,
10026
+ `- Business-rule closed: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'business_rule_closed', 'rate_percent', '%')}`,
10027
+ `- Decision coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'decision_coverage', 'rate_percent', '%')}`,
10028
+ `- Decision closed: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'decision_closed', 'rate_percent', '%')}`,
9703
10029
  `- Delta avg score: ${formatAutoHandoffRegressionValue(moquiDeltas.avg_score)}`,
9704
10030
  `- Delta valid-rate: ${formatAutoHandoffRegressionValue(moquiDeltas.valid_rate_percent)}%`,
10031
+ `- Delta entity coverage: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'entity_coverage', 'rate_percent', '%')}`,
10032
+ `- Delta business-rule closed: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'business_rule_closed', 'rate_percent', '%')}`,
10033
+ `- Delta decision closed: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'decision_closed', 'rate_percent', '%')}`,
10034
+ `- Matrix regressions: ${formatAutoHandoffMoquiCoverageRegressions(moquiCompare, 5)}`,
9705
10035
  `- Newly failed templates: ${Array.isArray(moquiFailedTemplates.newly_failed) && moquiFailedTemplates.newly_failed.length > 0 ? moquiFailedTemplates.newly_failed.join(', ') : 'none'}`,
9706
10036
  `- Recovered templates: ${Array.isArray(moquiFailedTemplates.recovered) && moquiFailedTemplates.recovered.length > 0 ? moquiFailedTemplates.recovered.join(', ') : 'none'}`,
10037
+ `- Top baseline gaps: ${moquiGapFrequency.length > 0 ? moquiGapFrequency.slice(0, 3).map(item => `${item.gap}:${item.count}`).join(' | ') : 'none'}`,
9707
10038
  `- Baseline JSON: ${formatAutoHandoffRegressionValue(moquiBaseline.output && moquiBaseline.output.json)}`,
9708
10039
  '',
9709
10040
  '## Current Scene Package Batch',
@@ -9860,9 +10191,16 @@ function renderAutoHandoffReleaseNotesDraft(payload = {}, context = {}) {
9860
10191
  const moquiSummary = moquiBaseline && moquiBaseline.summary && typeof moquiBaseline.summary === 'object'
9861
10192
  ? moquiBaseline.summary
9862
10193
  : {};
10194
+ const moquiScopeBreakdown = moquiSummary && moquiSummary.scope_breakdown && typeof moquiSummary.scope_breakdown === 'object'
10195
+ ? moquiSummary.scope_breakdown
10196
+ : {};
10197
+ const moquiGapFrequency = Array.isArray(moquiSummary && moquiSummary.gap_frequency)
10198
+ ? moquiSummary.gap_frequency
10199
+ : [];
9863
10200
  const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
9864
10201
  ? moquiBaseline.compare
9865
10202
  : {};
10203
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
9866
10204
  const moquiDeltas = moquiCompare && moquiCompare.deltas && typeof moquiCompare.deltas === 'object'
9867
10205
  ? moquiCompare.deltas
9868
10206
  : {};
@@ -9933,7 +10271,8 @@ function renderAutoHandoffReleaseNotesDraft(payload = {}, context = {}) {
9933
10271
  `- ${level}: count=${formatAutoHandoffRegressionValue(scoped.count, '0')}, ` +
9934
10272
  `avg_success=${formatAutoHandoffRegressionValue(scoped.avg_spec_success_rate_percent)}, ` +
9935
10273
  `avg_failed_goals=${formatAutoHandoffRegressionValue(scoped.avg_failed_goals)}, ` +
9936
- `avg_ontology_quality=${formatAutoHandoffRegressionValue(scoped.avg_ontology_quality_score)}`
10274
+ `avg_ontology_quality=${formatAutoHandoffRegressionValue(scoped.avg_ontology_quality_score)}, ` +
10275
+ `avg_moqui_matrix_regressions=${formatAutoHandoffRegressionValue(scoped.avg_moqui_matrix_regression_count, '0')}`
9937
10276
  );
9938
10277
  });
9939
10278
 
@@ -9965,9 +10304,23 @@ function renderAutoHandoffReleaseNotesDraft(payload = {}, context = {}) {
9965
10304
  `- Moqui baseline avg score: ${formatAutoHandoffRegressionValue(moquiSummary.avg_score)}`,
9966
10305
  `- Moqui baseline valid-rate: ${formatAutoHandoffRegressionValue(moquiSummary.valid_rate_percent)}%`,
9967
10306
  `- Moqui baseline failed templates: ${formatAutoHandoffRegressionValue(moquiSummary.baseline_failed)}`,
10307
+ `- Moqui matrix regression count: ${formatAutoHandoffRegressionValue(moquiMatrixRegressions.length, '0')}`,
10308
+ `- Moqui matrix regression gate (max): ${formatAutoHandoffRegressionValue(currentPolicy.max_moqui_matrix_regressions)}`,
10309
+ `- Moqui scope mix (moqui/suite/other): ${formatAutoHandoffRegressionValue(moquiScopeBreakdown.moqui_erp, '0')}/${formatAutoHandoffRegressionValue(moquiScopeBreakdown.scene_orchestration, '0')}/${formatAutoHandoffRegressionValue(moquiScopeBreakdown.other, '0')}`,
10310
+ `- Moqui entity coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'entity_coverage', 'rate_percent', '%')}`,
10311
+ `- Moqui relation coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'relation_coverage', 'rate_percent', '%')}`,
10312
+ `- Moqui business-rule coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'business_rule_coverage', 'rate_percent', '%')}`,
10313
+ `- Moqui business-rule closed: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'business_rule_closed', 'rate_percent', '%')}`,
10314
+ `- Moqui decision coverage: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'decision_coverage', 'rate_percent', '%')}`,
10315
+ `- Moqui decision closed: ${formatAutoHandoffMoquiCoverageMetric(moquiSummary, 'decision_closed', 'rate_percent', '%')}`,
9968
10316
  `- Moqui baseline avg score delta: ${formatAutoHandoffRegressionValue(moquiDeltas.avg_score)}`,
9969
10317
  `- Moqui baseline valid-rate delta: ${formatAutoHandoffRegressionValue(moquiDeltas.valid_rate_percent)}%`,
10318
+ `- Moqui entity coverage delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'entity_coverage', 'rate_percent', '%')}`,
10319
+ `- Moqui business-rule closed delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'business_rule_closed', 'rate_percent', '%')}`,
10320
+ `- Moqui decision closed delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(moquiCompare, 'decision_closed', 'rate_percent', '%')}`,
10321
+ `- Moqui matrix regressions: ${formatAutoHandoffMoquiCoverageRegressions(moquiCompare, 5)}`,
9970
10322
  `- Moqui newly failed templates: ${Array.isArray(moquiFailedTemplates.newly_failed) && moquiFailedTemplates.newly_failed.length > 0 ? moquiFailedTemplates.newly_failed.join(', ') : 'none'}`,
10323
+ `- Moqui top baseline gaps: ${moquiGapFrequency.length > 0 ? moquiGapFrequency.slice(0, 3).map(item => `${item.gap}:${item.count}`).join(' | ') : 'none'}`,
9971
10324
  `- Scene package batch status: ${formatAutoHandoffRegressionValue(scenePackageBatch.status)}`,
9972
10325
  `- Scene package batch selected: ${formatAutoHandoffRegressionValue(scenePackageBatchSummary.selected)}`,
9973
10326
  `- Scene package batch failed: ${formatAutoHandoffRegressionValue(scenePackageBatchSummary.failed)}`,
@@ -10279,6 +10632,17 @@ function normalizeHandoffMinCapabilitySemantic(semanticCandidate) {
10279
10632
  return Number(parsed.toFixed(2));
10280
10633
  }
10281
10634
 
10635
+ function normalizeHandoffMaxMoquiMatrixRegressions(valueCandidate) {
10636
+ if (valueCandidate === undefined || valueCandidate === null || valueCandidate === '') {
10637
+ return 0;
10638
+ }
10639
+ const parsed = Number(valueCandidate);
10640
+ if (!Number.isInteger(parsed) || parsed < 0) {
10641
+ throw new Error('--max-moqui-matrix-regressions must be an integer >= 0.');
10642
+ }
10643
+ return parsed;
10644
+ }
10645
+
10282
10646
  function normalizeHandoffOptionalNonNegativeInteger(valueCandidate, optionName) {
10283
10647
  if (valueCandidate === undefined || valueCandidate === null || valueCandidate === '') {
10284
10648
  return null;
@@ -10307,6 +10671,7 @@ function buildAutoHandoffRunPolicy(options = {}) {
10307
10671
  max_risk_level: normalizeHandoffRiskLevel(options.maxRiskLevel),
10308
10672
  min_ontology_score: normalizeHandoffMinOntologyScore(options.minOntologyScore),
10309
10673
  min_capability_coverage_percent: normalizeHandoffMinCapabilityCoverage(options.minCapabilityCoverage),
10674
+ max_moqui_matrix_regressions: normalizeHandoffMaxMoquiMatrixRegressions(options.maxMoquiMatrixRegressions),
10310
10675
  max_unmapped_rules: normalizeHandoffOptionalNonNegativeInteger(
10311
10676
  options.maxUnmappedRules,
10312
10677
  '--max-unmapped-rules'
@@ -10319,6 +10684,7 @@ function buildAutoHandoffRunPolicy(options = {}) {
10319
10684
  require_moqui_baseline: options.requireMoquiBaseline !== false,
10320
10685
  require_scene_package_batch: options.requireScenePackageBatch !== false,
10321
10686
  require_capability_coverage: options.requireCapabilityCoverage !== false,
10687
+ require_capability_lexicon: options.requireCapabilityLexicon !== false,
10322
10688
  require_release_gate_preflight: options.requireReleaseGatePreflight === true,
10323
10689
  dependency_batching: options.dependencyBatching !== false,
10324
10690
  release_evidence_window: normalizeHandoffReleaseEvidenceWindow(options.releaseEvidenceWindow)
@@ -10635,6 +11001,10 @@ function evaluateAutoHandoffMoquiBaselineGateReasons(policy = {}, moquiBaseline
10635
11001
  const summary = baseline && baseline.summary && typeof baseline.summary === 'object'
10636
11002
  ? baseline.summary
10637
11003
  : {};
11004
+ const compare = baseline && baseline.compare && typeof baseline.compare === 'object'
11005
+ ? baseline.compare
11006
+ : {};
11007
+ const matrixRegressions = buildAutoHandoffMoquiCoverageRegressions(compare);
10638
11008
  const status = `${baseline && baseline.status ? baseline.status : 'missing'}`.trim().toLowerCase();
10639
11009
  if (!baseline || baseline.generated !== true) {
10640
11010
  const reason = baseline && baseline.reason ? baseline.reason : 'moqui baseline snapshot missing';
@@ -10653,6 +11023,15 @@ function evaluateAutoHandoffMoquiBaselineGateReasons(policy = {}, moquiBaseline
10653
11023
  `valid_rate=${Number.isFinite(validRate) ? `${validRate}%` : 'n/a'})`
10654
11024
  );
10655
11025
  }
11026
+ if (Number.isInteger(policy.max_moqui_matrix_regressions)) {
11027
+ const limit = Number(policy.max_moqui_matrix_regressions);
11028
+ if (matrixRegressions.length > limit) {
11029
+ reasons.push(
11030
+ `moqui baseline matrix regressions ${matrixRegressions.length} > allowed ${limit} ` +
11031
+ `(${matrixRegressions.slice(0, 3).map(item => `${item.label}:${item.delta_rate_percent}%`).join(' | ')})`
11032
+ );
11033
+ }
11034
+ }
10656
11035
  return reasons;
10657
11036
  }
10658
11037
 
@@ -10734,6 +11113,57 @@ function evaluateAutoHandoffCapabilityCoverageGateReasons(policy = {}, capabilit
10734
11113
  return reasons;
10735
11114
  }
10736
11115
 
11116
+ function evaluateAutoHandoffCapabilityLexiconGateReasons(policy = {}, capabilityCoverage = null) {
11117
+ const reasons = [];
11118
+ if (policy.require_capability_lexicon !== true) {
11119
+ return reasons;
11120
+ }
11121
+
11122
+ const coverage = capabilityCoverage && typeof capabilityCoverage === 'object'
11123
+ ? capabilityCoverage
11124
+ : null;
11125
+ if (!coverage) {
11126
+ reasons.push('capability lexicon snapshot missing');
11127
+ return reasons;
11128
+ }
11129
+ if (coverage.status === 'error') {
11130
+ reasons.push(`capability lexicon errored: ${coverage.error || 'unknown error'}`);
11131
+ return reasons;
11132
+ }
11133
+ if (coverage.status === 'skipped') {
11134
+ const totalCapabilities = Number(
11135
+ coverage &&
11136
+ coverage.summary &&
11137
+ coverage.summary.total_capabilities !== undefined
11138
+ ? coverage.summary.total_capabilities
11139
+ : 0
11140
+ );
11141
+ if (Number.isFinite(totalCapabilities) && totalCapabilities <= 0) {
11142
+ return reasons;
11143
+ }
11144
+ reasons.push(`capability lexicon skipped: ${coverage.reason || 'unknown reason'}`);
11145
+ return reasons;
11146
+ }
11147
+
11148
+ const normalization = coverage.normalization && typeof coverage.normalization === 'object'
11149
+ ? coverage.normalization
11150
+ : {};
11151
+ const expectedUnknownCount = Array.isArray(normalization.expected_unknown)
11152
+ ? normalization.expected_unknown.length
11153
+ : 0;
11154
+ const providedUnknownCount = Array.isArray(normalization.provided_unknown)
11155
+ ? normalization.provided_unknown.length
11156
+ : 0;
11157
+ if (expectedUnknownCount > 0) {
11158
+ reasons.push(`capability_lexicon_expected_unknown_count ${expectedUnknownCount} > allowed 0`);
11159
+ }
11160
+ if (providedUnknownCount > 0) {
11161
+ reasons.push(`capability_lexicon_provided_unknown_count ${providedUnknownCount} > allowed 0`);
11162
+ }
11163
+
11164
+ return reasons;
11165
+ }
11166
+
10737
11167
  function evaluateAutoHandoffCapabilitySemanticGateReasons(policy = {}, capabilityCoverage = null) {
10738
11168
  const reasons = [];
10739
11169
  if (policy.require_capability_semantic !== true) {
@@ -10854,6 +11284,13 @@ function buildAutoHandoffRunFailureSummary(result = {}) {
10854
11284
  const releaseGateReasons = releaseGatePreflight && Array.isArray(releaseGatePreflight.reasons)
10855
11285
  ? releaseGatePreflight.reasons
10856
11286
  : [];
11287
+ const moquiBaseline = result && result.moqui_baseline && typeof result.moqui_baseline === 'object'
11288
+ ? result.moqui_baseline
11289
+ : null;
11290
+ const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
11291
+ ? moquiBaseline.compare
11292
+ : {};
11293
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
10857
11294
  const highlights = [];
10858
11295
  if (typeof result.error === 'string' && result.error.trim()) {
10859
11296
  highlights.push(`error: ${result.error.trim()}`);
@@ -10869,6 +11306,11 @@ function buildAutoHandoffRunFailureSummary(result = {}) {
10869
11306
  if (failedPhase && failedPhase.id) {
10870
11307
  highlights.push(`phase: ${failedPhase.id}${failedPhase.error ? ` (${failedPhase.error})` : ''}`);
10871
11308
  }
11309
+ if (moquiMatrixRegressions.length > 0) {
11310
+ highlights.push(
11311
+ `moqui_matrix_regression: ${moquiMatrixRegressions.slice(0, 3).map(item => `${item.label}:${item.delta_rate_percent}%`).join(' | ')}`
11312
+ );
11313
+ }
10872
11314
  return {
10873
11315
  status: normalizeHandoffText(result && result.status),
10874
11316
  failed_phase: failedPhase
@@ -10880,6 +11322,7 @@ function buildAutoHandoffRunFailureSummary(result = {}) {
10880
11322
  : null,
10881
11323
  gate_failed: gates.passed === false,
10882
11324
  gate_reasons: gateReasons,
11325
+ moqui_matrix_regressions: moquiMatrixRegressions,
10883
11326
  release_gate_preflight_blocked: Boolean(releaseGatePreflight && releaseGatePreflight.blocked === true),
10884
11327
  release_gate_preflight_reasons: releaseGateReasons,
10885
11328
  highlights
@@ -10958,10 +11401,13 @@ function evaluateAutoHandoffRunGates(context = {}) {
10958
11401
  min_spec_success_rate: 100,
10959
11402
  max_risk_level: 'high',
10960
11403
  min_ontology_score: 0,
11404
+ max_moqui_matrix_regressions: 0,
10961
11405
  max_unmapped_rules: null,
10962
11406
  max_undecided_decisions: null,
10963
11407
  require_ontology_validation: true,
10964
- require_scene_package_batch: true
11408
+ require_scene_package_batch: true,
11409
+ require_capability_coverage: true,
11410
+ require_capability_lexicon: true
10965
11411
  };
10966
11412
  const dryRun = Boolean(context.dryRun);
10967
11413
  const specStatus = context.specStatus || {
@@ -10974,6 +11420,10 @@ function evaluateAutoHandoffRunGates(context = {}) {
10974
11420
  const moquiBaseline = context.moquiBaseline && typeof context.moquiBaseline === 'object'
10975
11421
  ? context.moquiBaseline
10976
11422
  : null;
11423
+ const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
11424
+ ? moquiBaseline.compare
11425
+ : {};
11426
+ const moquiMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
10977
11427
  const scenePackageBatch = context.scenePackageBatch && typeof context.scenePackageBatch === 'object'
10978
11428
  ? context.scenePackageBatch
10979
11429
  : null;
@@ -11008,6 +11458,7 @@ function evaluateAutoHandoffRunGates(context = {}) {
11008
11458
  reasons.push(...evaluateAutoHandoffMoquiBaselineGateReasons(policy, moquiBaseline));
11009
11459
  reasons.push(...evaluateAutoHandoffScenePackageBatchGateReasons(policy, scenePackageBatch));
11010
11460
  reasons.push(...evaluateAutoHandoffCapabilityCoverageGateReasons(policy, capabilityCoverage));
11461
+ reasons.push(...evaluateAutoHandoffCapabilityLexiconGateReasons(policy, capabilityCoverage));
11011
11462
 
11012
11463
  return {
11013
11464
  passed: reasons.length === 0,
@@ -11048,6 +11499,10 @@ function evaluateAutoHandoffRunGates(context = {}) {
11048
11499
  moquiBaseline.summary &&
11049
11500
  moquiBaseline.summary.portfolio_passed === true
11050
11501
  ),
11502
+ moqui_matrix_regression_count: moquiMatrixRegressions.length,
11503
+ max_moqui_matrix_regressions: Number.isInteger(policy.max_moqui_matrix_regressions)
11504
+ ? Number(policy.max_moqui_matrix_regressions)
11505
+ : null,
11051
11506
  scene_package_batch_status: normalizeHandoffText(scenePackageBatch && scenePackageBatch.status),
11052
11507
  scene_package_batch_passed: Boolean(scenePackageBatch && scenePackageBatch.status === 'passed'),
11053
11508
  capability_coverage_status: normalizeHandoffText(capabilityCoverage && capabilityCoverage.status),
@@ -11055,7 +11510,22 @@ function evaluateAutoHandoffRunGates(context = {}) {
11055
11510
  Number(capabilityCoverage && capabilityCoverage.summary ? capabilityCoverage.summary.coverage_percent : null)
11056
11511
  )
11057
11512
  ? Number(capabilityCoverage.summary.coverage_percent)
11058
- : null
11513
+ : null,
11514
+ capability_expected_unknown_count: Array.isArray(
11515
+ capabilityCoverage && capabilityCoverage.normalization
11516
+ ? capabilityCoverage.normalization.expected_unknown
11517
+ : null
11518
+ )
11519
+ ? capabilityCoverage.normalization.expected_unknown.length
11520
+ : null,
11521
+ capability_provided_unknown_count: Array.isArray(
11522
+ capabilityCoverage && capabilityCoverage.normalization
11523
+ ? capabilityCoverage.normalization.provided_unknown
11524
+ : null
11525
+ )
11526
+ ? capabilityCoverage.normalization.provided_unknown.length
11527
+ : null,
11528
+ require_capability_lexicon: policy.require_capability_lexicon === true
11059
11529
  },
11060
11530
  reasons
11061
11531
  };
@@ -11187,6 +11657,10 @@ function buildAutoHandoffRunRecommendations(projectPath, result) {
11187
11657
  const moquiSummary = moquiBaseline && moquiBaseline.summary && typeof moquiBaseline.summary === 'object'
11188
11658
  ? moquiBaseline.summary
11189
11659
  : null;
11660
+ const moquiCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
11661
+ ? moquiBaseline.compare
11662
+ : {};
11663
+ const moquiCoverageRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
11190
11664
  if (moquiBaseline && moquiBaseline.status === 'error') {
11191
11665
  push('sce scene moqui-baseline --json');
11192
11666
  } else if (moquiSummary && moquiSummary.portfolio_passed === false) {
@@ -11199,6 +11673,26 @@ function buildAutoHandoffRunRecommendations(projectPath, result) {
11199
11673
  '--dry-run --ontology-task-queue-out .kiro/auto/ontology-remediation.lines --json'
11200
11674
  );
11201
11675
  }
11676
+ if (moquiCoverageRegressions.length > 0) {
11677
+ push(
11678
+ `Recover Moqui matrix regressions before next handoff run: ` +
11679
+ `${moquiCoverageRegressions.slice(0, 3).map(item => `${item.label}:${item.delta_rate_percent}%`).join(' | ')}`
11680
+ );
11681
+ push(
11682
+ 'sce scene moqui-baseline --include-all ' +
11683
+ '--compare-with .kiro/reports/release-evidence/moqui-template-baseline.json --json'
11684
+ );
11685
+ if (manifestPath) {
11686
+ push(
11687
+ `sce auto handoff run --manifest ${manifestCli} ` +
11688
+ '--max-moqui-matrix-regressions 0 --json'
11689
+ );
11690
+ push(
11691
+ `sce scene package-publish-batch --manifest ${manifestCli} ` +
11692
+ '--dry-run --ontology-task-queue-out .kiro/auto/ontology-remediation.lines --json'
11693
+ );
11694
+ }
11695
+ }
11202
11696
 
11203
11697
  const scenePackageBatch = result && result.scene_package_batch && typeof result.scene_package_batch === 'object'
11204
11698
  ? result.scene_package_batch
@@ -11237,6 +11731,26 @@ function buildAutoHandoffRunRecommendations(projectPath, result) {
11237
11731
  ) {
11238
11732
  push('replace deprecated manifest capabilities with canonical Moqui capability ids and rerun `sce auto handoff run --json`');
11239
11733
  }
11734
+ if (
11735
+ capabilityNormalization &&
11736
+ Array.isArray(capabilityNormalization.expected_unknown) &&
11737
+ capabilityNormalization.expected_unknown.length > 0
11738
+ ) {
11739
+ push(
11740
+ 'normalize unknown manifest capabilities and rerun strict gates via ' +
11741
+ '`sce auto handoff capability-matrix --manifest docs/handoffs/handoff-manifest.json --fail-on-gap --json`'
11742
+ );
11743
+ }
11744
+ if (
11745
+ capabilityNormalization &&
11746
+ Array.isArray(capabilityNormalization.provided_unknown) &&
11747
+ capabilityNormalization.provided_unknown.length > 0
11748
+ ) {
11749
+ push(
11750
+ 'normalize unknown template capabilities via ' +
11751
+ '`node scripts/moqui-lexicon-audit.js --manifest docs/handoffs/handoff-manifest.json --template-dir .kiro/templates/scene-packages --fail-on-gap --json`'
11752
+ );
11753
+ }
11240
11754
 
11241
11755
  return recommendations;
11242
11756
  }
@@ -11340,6 +11854,20 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11340
11854
  const moquiFailedTemplates = moquiCompare && moquiCompare.failed_templates && typeof moquiCompare.failed_templates === 'object'
11341
11855
  ? moquiCompare.failed_templates
11342
11856
  : {};
11857
+ const moquiScopeBreakdown = moquiSummary && moquiSummary.scope_breakdown && typeof moquiSummary.scope_breakdown === 'object'
11858
+ ? moquiSummary.scope_breakdown
11859
+ : {};
11860
+ const moquiCoverageMatrix = moquiSummary && moquiSummary.coverage_matrix && typeof moquiSummary.coverage_matrix === 'object'
11861
+ ? moquiSummary.coverage_matrix
11862
+ : {};
11863
+ const moquiCoverageMatrixDeltas = moquiCompare && moquiCompare.coverage_matrix_deltas
11864
+ && typeof moquiCompare.coverage_matrix_deltas === 'object'
11865
+ ? moquiCompare.coverage_matrix_deltas
11866
+ : {};
11867
+ const moquiCoverageMatrixRegressions = buildAutoHandoffMoquiCoverageRegressions(moquiCompare);
11868
+ const moquiGapFrequency = Array.isArray(moquiSummary && moquiSummary.gap_frequency)
11869
+ ? moquiSummary.gap_frequency
11870
+ : [];
11343
11871
  const scenePackageBatch = result && result.scene_package_batch && typeof result.scene_package_batch === 'object'
11344
11872
  ? result.scene_package_batch
11345
11873
  : {};
@@ -11394,7 +11922,9 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11394
11922
  risk_level: normalizeHandoffText(gateActual.risk_level),
11395
11923
  ontology_quality_score: toNumber(gateActual.ontology_quality_score),
11396
11924
  ontology_business_rule_unmapped: toNumber(gateActual.ontology_business_rule_unmapped),
11397
- ontology_decision_undecided: toNumber(gateActual.ontology_decision_undecided)
11925
+ ontology_decision_undecided: toNumber(gateActual.ontology_decision_undecided),
11926
+ capability_expected_unknown_count: toNumber(gateActual.capability_expected_unknown_count),
11927
+ capability_provided_unknown_count: toNumber(gateActual.capability_provided_unknown_count)
11398
11928
  }
11399
11929
  },
11400
11930
  release_gate_preflight: result && result.release_gate_preflight && typeof result.release_gate_preflight === 'object'
@@ -11443,7 +11973,14 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11443
11973
  valid_rate_percent: toNumber(moquiSummary.valid_rate_percent),
11444
11974
  baseline_passed: toNumber(moquiSummary.baseline_passed),
11445
11975
  baseline_failed: toNumber(moquiSummary.baseline_failed),
11446
- portfolio_passed: moquiSummary.portfolio_passed === true
11976
+ portfolio_passed: moquiSummary.portfolio_passed === true,
11977
+ scope_breakdown: {
11978
+ moqui_erp: toNumber(moquiScopeBreakdown.moqui_erp),
11979
+ scene_orchestration: toNumber(moquiScopeBreakdown.scene_orchestration),
11980
+ other: toNumber(moquiScopeBreakdown.other)
11981
+ },
11982
+ coverage_matrix: moquiCoverageMatrix,
11983
+ gap_frequency: moquiGapFrequency
11447
11984
  },
11448
11985
  compare: Object.keys(moquiCompare).length === 0
11449
11986
  ? null
@@ -11457,6 +11994,8 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11457
11994
  baseline_passed: toNumber(moquiDeltas.baseline_passed),
11458
11995
  baseline_failed: toNumber(moquiDeltas.baseline_failed)
11459
11996
  },
11997
+ coverage_matrix_deltas: moquiCoverageMatrixDeltas,
11998
+ coverage_matrix_regressions: moquiCoverageMatrixRegressions,
11460
11999
  failed_templates: {
11461
12000
  newly_failed: Array.isArray(moquiFailedTemplates.newly_failed) ? moquiFailedTemplates.newly_failed : [],
11462
12001
  recovered: Array.isArray(moquiFailedTemplates.recovered) ? moquiFailedTemplates.recovered : []
@@ -11536,6 +12075,16 @@ function buildAutoHandoffReleaseEvidenceEntry(projectPath, result, reportFile =
11536
12075
  },
11537
12076
  continued_from: result && result.continued_from ? result.continued_from : null,
11538
12077
  policy: {
12078
+ max_moqui_matrix_regressions: Number.isFinite(
12079
+ Number(result && result.policy ? result.policy.max_moqui_matrix_regressions : null)
12080
+ )
12081
+ ? Number(result.policy.max_moqui_matrix_regressions)
12082
+ : null,
12083
+ require_capability_lexicon: Boolean(
12084
+ result &&
12085
+ result.policy &&
12086
+ result.policy.require_capability_lexicon === true
12087
+ ),
11539
12088
  require_release_gate_preflight: Boolean(
11540
12089
  result &&
11541
12090
  result.policy &&
@@ -11671,6 +12220,17 @@ async function writeAutoHandoffRunReport(projectPath, result, outCandidate = nul
11671
12220
  function buildAutoHandoffMoquiBaselinePhaseDetails(payload) {
11672
12221
  const baseline = payload && typeof payload === 'object' ? payload : {};
11673
12222
  const summary = baseline.summary && typeof baseline.summary === 'object' ? baseline.summary : null;
12223
+ const compare = baseline.compare && typeof baseline.compare === 'object' ? baseline.compare : {};
12224
+ const regressions = buildAutoHandoffMoquiCoverageRegressions(compare);
12225
+ const scopeBreakdown = summary && summary.scope_breakdown && typeof summary.scope_breakdown === 'object'
12226
+ ? summary.scope_breakdown
12227
+ : null;
12228
+ const coverageMatrix = summary && summary.coverage_matrix && typeof summary.coverage_matrix === 'object'
12229
+ ? summary.coverage_matrix
12230
+ : null;
12231
+ const gapFrequency = summary && Array.isArray(summary.gap_frequency)
12232
+ ? summary.gap_frequency
12233
+ : [];
11674
12234
  return {
11675
12235
  status: baseline.status || 'unknown',
11676
12236
  generated: baseline.generated === true,
@@ -11681,7 +12241,20 @@ function buildAutoHandoffMoquiBaselinePhaseDetails(payload) {
11681
12241
  : null,
11682
12242
  valid_rate_percent: summary && Number.isFinite(Number(summary.valid_rate_percent))
11683
12243
  ? Number(summary.valid_rate_percent)
11684
- : null
12244
+ : null,
12245
+ scope_breakdown: scopeBreakdown,
12246
+ coverage_matrix: coverageMatrix,
12247
+ gap_frequency_top: gapFrequency.slice(0, 5),
12248
+ entity_coverage_rate_percent: getAutoHandoffMoquiCoverageMetric(summary, 'entity_coverage', 'rate_percent'),
12249
+ relation_coverage_rate_percent: getAutoHandoffMoquiCoverageMetric(summary, 'relation_coverage', 'rate_percent'),
12250
+ business_rule_closed_rate_percent: getAutoHandoffMoquiCoverageMetric(summary, 'business_rule_closed', 'rate_percent'),
12251
+ decision_closed_rate_percent: getAutoHandoffMoquiCoverageMetric(summary, 'decision_closed', 'rate_percent'),
12252
+ coverage_matrix_deltas: getAutoHandoffMoquiCoverageDeltaMatrix(compare),
12253
+ coverage_matrix_regressions: regressions,
12254
+ entity_coverage_delta_rate_percent: getAutoHandoffMoquiCoverageDeltaMetric(compare, 'entity_coverage', 'rate_percent'),
12255
+ business_rule_closed_delta_rate_percent: getAutoHandoffMoquiCoverageDeltaMetric(compare, 'business_rule_closed', 'rate_percent'),
12256
+ decision_closed_delta_rate_percent: getAutoHandoffMoquiCoverageDeltaMetric(compare, 'decision_closed', 'rate_percent'),
12257
+ matrix_regression_count: regressions.length
11685
12258
  };
11686
12259
  }
11687
12260
 
@@ -11778,6 +12351,15 @@ async function buildAutoHandoffMoquiBaselineSnapshot(projectPath) {
11778
12351
  const failedTemplates = compare && compare.failed_templates && typeof compare.failed_templates === 'object'
11779
12352
  ? compare.failed_templates
11780
12353
  : {};
12354
+ const scopeBreakdown = summary && summary.scope_breakdown && typeof summary.scope_breakdown === 'object'
12355
+ ? summary.scope_breakdown
12356
+ : {};
12357
+ const coverageMatrix = summary && summary.coverage_matrix && typeof summary.coverage_matrix === 'object'
12358
+ ? summary.coverage_matrix
12359
+ : {};
12360
+ const gapFrequency = summary && Array.isArray(summary.gap_frequency)
12361
+ ? summary.gap_frequency
12362
+ : [];
11781
12363
 
11782
12364
  return {
11783
12365
  status: summary.portfolio_passed === true ? 'passed' : 'failed',
@@ -11789,13 +12371,22 @@ async function buildAutoHandoffMoquiBaselineSnapshot(projectPath) {
11789
12371
  valid_rate_percent: Number.isFinite(Number(summary.valid_rate_percent)) ? Number(summary.valid_rate_percent) : null,
11790
12372
  baseline_passed: Number(summary.baseline_passed) || 0,
11791
12373
  baseline_failed: Number(summary.baseline_failed) || 0,
11792
- portfolio_passed: summary.portfolio_passed === true
12374
+ portfolio_passed: summary.portfolio_passed === true,
12375
+ scope_breakdown: {
12376
+ moqui_erp: Number(scopeBreakdown.moqui_erp) || 0,
12377
+ scene_orchestration: Number(scopeBreakdown.scene_orchestration) || 0,
12378
+ other: Number(scopeBreakdown.other) || 0
12379
+ },
12380
+ coverage_matrix: coverageMatrix,
12381
+ gap_frequency: gapFrequency
11793
12382
  },
11794
12383
  compare: compare
11795
12384
  ? {
11796
12385
  previous_generated_at: compare.previous_generated_at || null,
11797
12386
  previous_template_root: compare.previous_template_root || null,
11798
12387
  deltas: compare.deltas || null,
12388
+ coverage_matrix_deltas: compare.coverage_matrix_deltas || null,
12389
+ coverage_matrix_regressions: buildAutoHandoffMoquiCoverageRegressions(compare),
11799
12390
  failed_templates: {
11800
12391
  previous: Array.isArray(failedTemplates.previous) ? failedTemplates.previous : [],
11801
12392
  current: Array.isArray(failedTemplates.current) ? failedTemplates.current : [],
@@ -12604,9 +13195,23 @@ function buildAutoHandoffCapabilityMatrixRecommendations(result = {}) {
12604
13195
  const coverageSummary = capabilityCoverage && capabilityCoverage.summary && typeof capabilityCoverage.summary === 'object'
12605
13196
  ? capabilityCoverage.summary
12606
13197
  : {};
13198
+ const coverageNormalization = capabilityCoverage && capabilityCoverage.normalization &&
13199
+ typeof capabilityCoverage.normalization === 'object'
13200
+ ? capabilityCoverage.normalization
13201
+ : {};
13202
+ const expectedUnknownCount = Array.isArray(coverageNormalization.expected_unknown)
13203
+ ? coverageNormalization.expected_unknown.length
13204
+ : 0;
13205
+ const providedUnknownCount = Array.isArray(coverageNormalization.provided_unknown)
13206
+ ? coverageNormalization.provided_unknown.length
13207
+ : 0;
12607
13208
  const baseline = result && result.moqui_baseline && typeof result.moqui_baseline === 'object'
12608
13209
  ? result.moqui_baseline
12609
13210
  : {};
13211
+ const baselineCompare = baseline && baseline.compare && typeof baseline.compare === 'object'
13212
+ ? baseline.compare
13213
+ : {};
13214
+ const baselineRegressions = buildAutoHandoffMoquiCoverageRegressions(baselineCompare);
12610
13215
 
12611
13216
  if (templateDiff.compatibility === 'needs-sync') {
12612
13217
  push(`Sync template library and rerun: sce auto handoff template-diff --manifest ${manifestCli} --json`);
@@ -12614,6 +13219,12 @@ function buildAutoHandoffCapabilityMatrixRecommendations(result = {}) {
12614
13219
  if (baseline.status === 'error' || (baseline.summary && baseline.summary.portfolio_passed === false)) {
12615
13220
  push('Rebuild Moqui baseline: sce scene moqui-baseline --json');
12616
13221
  }
13222
+ if (baselineRegressions.length > 0) {
13223
+ push(
13224
+ `Recover Moqui matrix regressions: ` +
13225
+ `${baselineRegressions.slice(0, 3).map(item => `${item.label}:${item.delta_rate_percent}%`).join(' | ')}`
13226
+ );
13227
+ }
12617
13228
  if (capabilityCoverage.status === 'skipped') {
12618
13229
  push('Declare `capabilities` in handoff manifest to enable capability matrix coverage gates.');
12619
13230
  }
@@ -12629,6 +13240,13 @@ function buildAutoHandoffCapabilityMatrixRecommendations(result = {}) {
12629
13240
  `sce scene package-ontology-backfill-batch --manifest ${manifestCli} --json`
12630
13241
  );
12631
13242
  }
13243
+ if (expectedUnknownCount > 0 || providedUnknownCount > 0) {
13244
+ push(
13245
+ `Normalize capability lexicon gaps with strict audit: ` +
13246
+ `node scripts/moqui-lexicon-audit.js --manifest ${manifestCli} ` +
13247
+ '--template-dir .kiro/templates/scene-packages --fail-on-gap --json'
13248
+ );
13249
+ }
12632
13250
  if (result.remediation_queue && result.remediation_queue.file) {
12633
13251
  push(
12634
13252
  `Replay remediation queue: sce auto close-loop-batch ${quoteCliArg(result.remediation_queue.file)} --format lines --json`
@@ -12660,12 +13278,31 @@ function renderAutoHandoffCapabilityMatrixMarkdown(payload = {}) {
12660
13278
  const baselineSummary = moquiBaseline && moquiBaseline.summary && typeof moquiBaseline.summary === 'object'
12661
13279
  ? moquiBaseline.summary
12662
13280
  : {};
13281
+ const baselineCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
13282
+ ? moquiBaseline.compare
13283
+ : {};
13284
+ const baselineScopeBreakdown = baselineSummary && baselineSummary.scope_breakdown && typeof baselineSummary.scope_breakdown === 'object'
13285
+ ? baselineSummary.scope_breakdown
13286
+ : {};
13287
+ const baselineGapFrequency = Array.isArray(baselineSummary && baselineSummary.gap_frequency)
13288
+ ? baselineSummary.gap_frequency
13289
+ : [];
12663
13290
  const capabilityCoverage = payload && payload.capability_coverage && typeof payload.capability_coverage === 'object'
12664
13291
  ? payload.capability_coverage
12665
13292
  : {};
12666
13293
  const coverageSummary = capabilityCoverage && capabilityCoverage.summary && typeof capabilityCoverage.summary === 'object'
12667
13294
  ? capabilityCoverage.summary
12668
13295
  : {};
13296
+ const coverageNormalization = capabilityCoverage && capabilityCoverage.normalization &&
13297
+ typeof capabilityCoverage.normalization === 'object'
13298
+ ? capabilityCoverage.normalization
13299
+ : {};
13300
+ const expectedUnknownCount = Array.isArray(coverageNormalization.expected_unknown)
13301
+ ? coverageNormalization.expected_unknown.length
13302
+ : 0;
13303
+ const providedUnknownCount = Array.isArray(coverageNormalization.provided_unknown)
13304
+ ? coverageNormalization.provided_unknown.length
13305
+ : 0;
12669
13306
  const coverage = Array.isArray(capabilityCoverage && capabilityCoverage.coverage)
12670
13307
  ? capabilityCoverage.coverage
12671
13308
  : [];
@@ -12685,6 +13322,7 @@ function renderAutoHandoffCapabilityMatrixMarkdown(payload = {}) {
12685
13322
  `- Capabilities: ${handoff.capability_count !== undefined ? handoff.capability_count : 'n/a'}`,
12686
13323
  `- Min capability coverage: ${policy.min_capability_coverage_percent !== undefined ? `${policy.min_capability_coverage_percent}%` : 'n/a'}`,
12687
13324
  `- Min capability semantic completeness: ${policy.min_capability_semantic_percent !== undefined ? `${policy.min_capability_semantic_percent}%` : 'n/a'}`,
13325
+ `- Capability lexicon gate: ${policy.require_capability_lexicon === false ? 'disabled' : 'enabled'}`,
12688
13326
  '',
12689
13327
  '## Gates',
12690
13328
  '',
@@ -12703,6 +13341,18 @@ function renderAutoHandoffCapabilityMatrixMarkdown(payload = {}) {
12703
13341
  `- Portfolio passed: ${baselineSummary.portfolio_passed === true ? 'yes' : (baselineSummary.portfolio_passed === false ? 'no' : 'n/a')}`,
12704
13342
  `- Avg score: ${formatAutoHandoffRegressionValue(baselineSummary.avg_score)}`,
12705
13343
  `- Valid-rate: ${formatAutoHandoffRegressionValue(baselineSummary.valid_rate_percent)}%`,
13344
+ `- Scope mix (moqui/suite/other): ${formatAutoHandoffRegressionValue(baselineScopeBreakdown.moqui_erp, '0')}/${formatAutoHandoffRegressionValue(baselineScopeBreakdown.scene_orchestration, '0')}/${formatAutoHandoffRegressionValue(baselineScopeBreakdown.other, '0')}`,
13345
+ `- Entity coverage: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'entity_coverage', 'rate_percent', '%')}`,
13346
+ `- Relation coverage: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'relation_coverage', 'rate_percent', '%')}`,
13347
+ `- Business-rule coverage: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'business_rule_coverage', 'rate_percent', '%')}`,
13348
+ `- Business-rule closed: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'business_rule_closed', 'rate_percent', '%')}`,
13349
+ `- Decision coverage: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'decision_coverage', 'rate_percent', '%')}`,
13350
+ `- Decision closed: ${formatAutoHandoffMoquiCoverageMetric(baselineSummary, 'decision_closed', 'rate_percent', '%')}`,
13351
+ `- Entity coverage delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(baselineCompare, 'entity_coverage', 'rate_percent', '%')}`,
13352
+ `- Business-rule closed delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(baselineCompare, 'business_rule_closed', 'rate_percent', '%')}`,
13353
+ `- Decision closed delta: ${formatAutoHandoffMoquiCoverageDeltaMetric(baselineCompare, 'decision_closed', 'rate_percent', '%')}`,
13354
+ `- Matrix regressions: ${formatAutoHandoffMoquiCoverageRegressions(baselineCompare, 5)}`,
13355
+ `- Top baseline gaps: ${baselineGapFrequency.length > 0 ? baselineGapFrequency.slice(0, 3).map(item => `${item.gap}:${item.count}`).join(' | ') : 'none'}`,
12706
13356
  '',
12707
13357
  '## Capability Coverage',
12708
13358
  '',
@@ -12713,6 +13363,8 @@ function renderAutoHandoffCapabilityMatrixMarkdown(payload = {}) {
12713
13363
  `- Uncovered capabilities: ${formatAutoHandoffRegressionValue(coverageSummary.uncovered_capabilities, '0')}`,
12714
13364
  `- Semantic complete: ${formatAutoHandoffRegressionValue(coverageSummary.semantic_complete_percent)}%`,
12715
13365
  `- Semantic passed: ${coverageSummary.semantic_passed === true ? 'yes' : (coverageSummary.semantic_passed === false ? 'no' : 'n/a')}`,
13366
+ `- Expected unknown capability aliases: ${expectedUnknownCount}`,
13367
+ `- Provided unknown capability aliases: ${providedUnknownCount}`,
12716
13368
  '',
12717
13369
  '| Capability | Covered | Semantic Complete | Missing Semantic Dimensions | Matched Templates |',
12718
13370
  '| --- | --- | --- | --- | --- |'
@@ -12767,6 +13419,7 @@ async function buildAutoHandoffCapabilityMatrix(projectPath, options = {}) {
12767
13419
  min_capability_semantic_percent: normalizeHandoffMinCapabilitySemantic(options.minCapabilitySemantic),
12768
13420
  require_capability_coverage: true,
12769
13421
  require_capability_semantic: options.requireCapabilitySemantic !== false,
13422
+ require_capability_lexicon: options.requireCapabilityLexicon !== false,
12770
13423
  require_moqui_baseline: true
12771
13424
  };
12772
13425
 
@@ -12791,11 +13444,16 @@ async function buildAutoHandoffCapabilityMatrix(projectPath, options = {}) {
12791
13444
  policy,
12792
13445
  capabilityCoverage
12793
13446
  );
13447
+ const lexiconGateReasons = evaluateAutoHandoffCapabilityLexiconGateReasons(
13448
+ policy,
13449
+ capabilityCoverage
13450
+ );
12794
13451
  const reasons = [
12795
13452
  ...templateSyncReasons,
12796
13453
  ...baselineGateReasons.map(item => `moqui-baseline:${item}`),
12797
13454
  ...capabilityGateReasons.map(item => `capability-coverage:${item}`),
12798
- ...semanticGateReasons.map(item => `capability-semantic:${item}`)
13455
+ ...semanticGateReasons.map(item => `capability-semantic:${item}`),
13456
+ ...lexiconGateReasons.map(item => `capability-lexicon:${item}`)
12799
13457
  ];
12800
13458
 
12801
13459
  const result = {
@@ -12840,6 +13498,10 @@ async function buildAutoHandoffCapabilityMatrix(projectPath, options = {}) {
12840
13498
  capability_semantic: {
12841
13499
  passed: semanticGateReasons.length === 0,
12842
13500
  reasons: semanticGateReasons
13501
+ },
13502
+ capability_lexicon: {
13503
+ passed: lexiconGateReasons.length === 0,
13504
+ reasons: lexiconGateReasons
12843
13505
  }
12844
13506
  },
12845
13507
  remediation_queue: null,
@@ -12880,6 +13542,7 @@ function collectAutoHandoffMoquiRemediationGoals(result) {
12880
13542
  const baselineCompare = moquiBaseline && moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
12881
13543
  ? moquiBaseline.compare
12882
13544
  : null;
13545
+ const baselineRegressions = buildAutoHandoffMoquiCoverageRegressions(baselineCompare || {});
12883
13546
  const baselineFailedTemplates = baselineCompare && baselineCompare.failed_templates && typeof baselineCompare.failed_templates === 'object'
12884
13547
  ? baselineCompare.failed_templates
12885
13548
  : {};
@@ -12898,6 +13561,22 @@ function collectAutoHandoffMoquiRemediationGoals(result) {
12898
13561
  pushGoal(`remediate moqui template ${templateId} ontology semantics and close baseline gaps`);
12899
13562
  }
12900
13563
  }
13564
+ if (baselineRegressions.length > 0) {
13565
+ for (const item of baselineRegressions.slice(0, 5)) {
13566
+ pushGoal(
13567
+ `recover moqui matrix regression ${item.label} (${item.delta_rate_percent}%) by closing ontology semantic gaps`
13568
+ );
13569
+ if (item.metric === 'business_rule_closed') {
13570
+ pushGoal('remap governance_contract.business_rules for Moqui templates until closure regression is recovered');
13571
+ }
13572
+ if (item.metric === 'decision_closed') {
13573
+ pushGoal('resolve undecided governance_contract.decision_logic entries in Moqui templates');
13574
+ }
13575
+ if (item.metric === 'entity_coverage' || item.metric === 'relation_coverage') {
13576
+ pushGoal('backfill ontology_model entities/relations for regressed Moqui templates');
13577
+ }
13578
+ }
13579
+ }
12901
13580
 
12902
13581
  const scenePackageBatch = result && result.scene_package_batch && typeof result.scene_package_batch === 'object'
12903
13582
  ? result.scene_package_batch
@@ -13007,6 +13686,16 @@ function collectAutoHandoffMoquiRemediationGoals(result) {
13007
13686
  pushGoal(`align unknown manifest capability ${raw} with Moqui capability lexicon`);
13008
13687
  }
13009
13688
  }
13689
+ if (
13690
+ capabilityNormalization &&
13691
+ Array.isArray(capabilityNormalization.provided_unknown) &&
13692
+ capabilityNormalization.provided_unknown.length > 0
13693
+ ) {
13694
+ for (const item of capabilityNormalization.provided_unknown) {
13695
+ const raw = item && item.raw ? item.raw : '(unknown)';
13696
+ pushGoal(`align unknown template capability ${raw} with Moqui capability lexicon`);
13697
+ }
13698
+ }
13010
13699
 
13011
13700
  return goals;
13012
13701
  }
@@ -13229,6 +13918,13 @@ async function runAutoHandoff(projectPath, options = {}) {
13229
13918
  if (capabilityCoverageGateReasons.length > 0) {
13230
13919
  throw new Error(`handoff capability coverage gate failed: ${capabilityCoverageGateReasons.join('; ')}`);
13231
13920
  }
13921
+ const capabilityLexiconGateReasons = evaluateAutoHandoffCapabilityLexiconGateReasons(
13922
+ result.policy,
13923
+ result.moqui_capability_coverage
13924
+ );
13925
+ if (capabilityLexiconGateReasons.length > 0) {
13926
+ throw new Error(`handoff capability lexicon gate failed: ${capabilityLexiconGateReasons.join('; ')}`);
13927
+ }
13232
13928
  } catch (capabilityCoverageError) {
13233
13929
  failAutoHandoffRunPhase(capabilityCoveragePhase, capabilityCoverageError);
13234
13930
  if (!result.moqui_capability_coverage) {
@@ -14550,6 +15246,18 @@ function normalizeGovernanceHandoffQualitySnapshot(snapshotCandidate) {
14550
15246
  latest_ontology_quality_score: toGovernanceReleaseGateNumber(snapshotCandidate.latest_ontology_quality_score),
14551
15247
  latest_capability_coverage_percent: toGovernanceReleaseGateNumber(snapshotCandidate.latest_capability_coverage_percent),
14552
15248
  latest_capability_coverage_passed: parseAutoHandoffGateBoolean(snapshotCandidate.latest_capability_coverage_passed, null),
15249
+ latest_capability_expected_unknown_count: toGovernanceReleaseGateNumber(
15250
+ snapshotCandidate.latest_capability_expected_unknown_count
15251
+ ),
15252
+ latest_capability_provided_unknown_count: toGovernanceReleaseGateNumber(
15253
+ snapshotCandidate.latest_capability_provided_unknown_count
15254
+ ),
15255
+ latest_moqui_matrix_regression_count: toGovernanceReleaseGateNumber(
15256
+ snapshotCandidate.latest_moqui_matrix_regression_count
15257
+ ),
15258
+ latest_moqui_matrix_regression_gate_max: toGovernanceReleaseGateNumber(
15259
+ snapshotCandidate.latest_moqui_matrix_regression_gate_max
15260
+ ),
14553
15261
  latest_release_gate_preflight_blocked: parseAutoHandoffGateBoolean(
14554
15262
  snapshotCandidate.latest_release_gate_preflight_blocked,
14555
15263
  null
@@ -14558,6 +15266,21 @@ function normalizeGovernanceHandoffQualitySnapshot(snapshotCandidate) {
14558
15266
  gate_pass_rate_percent: toGovernanceReleaseGateNumber(snapshotCandidate.gate_pass_rate_percent),
14559
15267
  capability_coverage_pass_rate_percent: toGovernanceReleaseGateNumber(
14560
15268
  snapshotCandidate.capability_coverage_pass_rate_percent
15269
+ ),
15270
+ capability_expected_unknown_positive_rate_percent: toGovernanceReleaseGateNumber(
15271
+ snapshotCandidate.capability_expected_unknown_positive_rate_percent
15272
+ ),
15273
+ capability_provided_unknown_positive_rate_percent: toGovernanceReleaseGateNumber(
15274
+ snapshotCandidate.capability_provided_unknown_positive_rate_percent
15275
+ ),
15276
+ avg_moqui_matrix_regression_count: toGovernanceReleaseGateNumber(
15277
+ snapshotCandidate.avg_moqui_matrix_regression_count
15278
+ ),
15279
+ max_moqui_matrix_regression_count: toGovernanceReleaseGateNumber(
15280
+ snapshotCandidate.max_moqui_matrix_regression_count
15281
+ ),
15282
+ moqui_matrix_regression_positive_rate_percent: toGovernanceReleaseGateNumber(
15283
+ snapshotCandidate.moqui_matrix_regression_positive_rate_percent
14561
15284
  )
14562
15285
  };
14563
15286
  }
@@ -17226,6 +17949,10 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17226
17949
  latest_ontology_quality_score: null,
17227
17950
  latest_capability_coverage_percent: null,
17228
17951
  latest_capability_coverage_passed: null,
17952
+ latest_capability_expected_unknown_count: null,
17953
+ latest_capability_provided_unknown_count: null,
17954
+ latest_moqui_matrix_regression_count: null,
17955
+ latest_moqui_matrix_regression_gate_max: null,
17229
17956
  latest_scene_package_batch_passed: null,
17230
17957
  latest_release_gate_preflight_blocked: null,
17231
17958
  latest_failure_highlights: [],
@@ -17234,6 +17961,11 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17234
17961
  capability_coverage_pass_rate_percent: null,
17235
17962
  scene_package_batch_pass_rate_percent: null,
17236
17963
  avg_ontology_quality_score: null,
17964
+ capability_expected_unknown_positive_rate_percent: null,
17965
+ capability_provided_unknown_positive_rate_percent: null,
17966
+ avg_moqui_matrix_regression_count: null,
17967
+ max_moqui_matrix_regression_count: null,
17968
+ moqui_matrix_regression_positive_rate_percent: null,
17237
17969
  parse_error: null
17238
17970
  };
17239
17971
  if (!(await fs.pathExists(evidenceFile))) {
@@ -17298,6 +18030,101 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17298
18030
  const latestFailureHighlights = Array.isArray(latestFailureSummary.highlights)
17299
18031
  ? latestFailureSummary.highlights.map(item => `${item || ''}`.trim()).filter(Boolean).slice(0, 5)
17300
18032
  : [];
18033
+ const deriveMoquiMatrixRegressionCount = entry => {
18034
+ if (!entry || typeof entry !== 'object') {
18035
+ return null;
18036
+ }
18037
+ const gate = entry.gate && typeof entry.gate === 'object'
18038
+ ? entry.gate
18039
+ : {};
18040
+ const gateActual = gate.actual && typeof gate.actual === 'object'
18041
+ ? gate.actual
18042
+ : {};
18043
+ const explicit = toNumber(gateActual.moqui_matrix_regression_count);
18044
+ if (Number.isFinite(explicit)) {
18045
+ return Math.max(0, explicit);
18046
+ }
18047
+ const moquiBaseline = entry.moqui_baseline && typeof entry.moqui_baseline === 'object'
18048
+ ? entry.moqui_baseline
18049
+ : {};
18050
+ const compare = moquiBaseline.compare && typeof moquiBaseline.compare === 'object'
18051
+ ? moquiBaseline.compare
18052
+ : null;
18053
+ if (!compare) {
18054
+ return null;
18055
+ }
18056
+ return buildAutoHandoffMoquiCoverageRegressions(compare).length;
18057
+ };
18058
+ const deriveMoquiMatrixRegressionGateMax = entry => {
18059
+ if (!entry || typeof entry !== 'object') {
18060
+ return null;
18061
+ }
18062
+ const gate = entry.gate && typeof entry.gate === 'object'
18063
+ ? entry.gate
18064
+ : {};
18065
+ const gateActual = gate.actual && typeof gate.actual === 'object'
18066
+ ? gate.actual
18067
+ : {};
18068
+ const explicit = toNumber(gateActual.max_moqui_matrix_regressions);
18069
+ if (Number.isFinite(explicit)) {
18070
+ return Math.max(0, explicit);
18071
+ }
18072
+ const policy = entry.policy && typeof entry.policy === 'object'
18073
+ ? entry.policy
18074
+ : {};
18075
+ const fromPolicy = toNumber(policy.max_moqui_matrix_regressions);
18076
+ return Number.isFinite(fromPolicy) ? Math.max(0, fromPolicy) : null;
18077
+ };
18078
+ const deriveCapabilityExpectedUnknownCount = entry => {
18079
+ if (!entry || typeof entry !== 'object') {
18080
+ return null;
18081
+ }
18082
+ const gate = entry.gate && typeof entry.gate === 'object'
18083
+ ? entry.gate
18084
+ : {};
18085
+ const gateActual = gate.actual && typeof gate.actual === 'object'
18086
+ ? gate.actual
18087
+ : {};
18088
+ const explicit = toNumber(gateActual.capability_expected_unknown_count);
18089
+ if (Number.isFinite(explicit)) {
18090
+ return Math.max(0, explicit);
18091
+ }
18092
+ const coverage = entry.capability_coverage && typeof entry.capability_coverage === 'object'
18093
+ ? entry.capability_coverage
18094
+ : {};
18095
+ const normalization = coverage.normalization && typeof coverage.normalization === 'object'
18096
+ ? coverage.normalization
18097
+ : {};
18098
+ const list = Array.isArray(normalization.expected_unknown) ? normalization.expected_unknown : null;
18099
+ return list ? list.length : null;
18100
+ };
18101
+ const deriveCapabilityProvidedUnknownCount = entry => {
18102
+ if (!entry || typeof entry !== 'object') {
18103
+ return null;
18104
+ }
18105
+ const gate = entry.gate && typeof entry.gate === 'object'
18106
+ ? entry.gate
18107
+ : {};
18108
+ const gateActual = gate.actual && typeof gate.actual === 'object'
18109
+ ? gate.actual
18110
+ : {};
18111
+ const explicit = toNumber(gateActual.capability_provided_unknown_count);
18112
+ if (Number.isFinite(explicit)) {
18113
+ return Math.max(0, explicit);
18114
+ }
18115
+ const coverage = entry.capability_coverage && typeof entry.capability_coverage === 'object'
18116
+ ? entry.capability_coverage
18117
+ : {};
18118
+ const normalization = coverage.normalization && typeof coverage.normalization === 'object'
18119
+ ? coverage.normalization
18120
+ : {};
18121
+ const list = Array.isArray(normalization.provided_unknown) ? normalization.provided_unknown : null;
18122
+ return list ? list.length : null;
18123
+ };
18124
+ const latestMoquiMatrixRegressionCount = deriveMoquiMatrixRegressionCount(latest);
18125
+ const latestMoquiMatrixRegressionGateMax = deriveMoquiMatrixRegressionGateMax(latest);
18126
+ const latestCapabilityExpectedUnknownCount = deriveCapabilityExpectedUnknownCount(latest);
18127
+ const latestCapabilityProvidedUnknownCount = deriveCapabilityProvidedUnknownCount(latest);
17301
18128
 
17302
18129
  let failedRuns = 0;
17303
18130
  let gateKnownRuns = 0;
@@ -17306,7 +18133,14 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17306
18133
  let capabilityPassedRuns = 0;
17307
18134
  let sceneBatchKnownRuns = 0;
17308
18135
  let sceneBatchPassedRuns = 0;
18136
+ let capabilityExpectedUnknownKnownRuns = 0;
18137
+ let capabilityExpectedUnknownPositiveRuns = 0;
18138
+ let capabilityProvidedUnknownKnownRuns = 0;
18139
+ let capabilityProvidedUnknownPositiveRuns = 0;
18140
+ let moquiMatrixKnownRuns = 0;
18141
+ let moquiMatrixPositiveRuns = 0;
17309
18142
  const ontologyScores = [];
18143
+ const moquiMatrixRegressionCounts = [];
17310
18144
  for (const entry of sessions) {
17311
18145
  const status = normalizeHandoffText(entry && entry.status);
17312
18146
  if (status && !['completed', 'dry-run', 'dry_run'].includes(status)) {
@@ -17359,6 +18193,28 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17359
18193
  if (Number.isFinite(ontologyScore)) {
17360
18194
  ontologyScores.push(ontologyScore);
17361
18195
  }
18196
+ const capabilityExpectedUnknownCount = deriveCapabilityExpectedUnknownCount(entry);
18197
+ if (Number.isFinite(capabilityExpectedUnknownCount)) {
18198
+ capabilityExpectedUnknownKnownRuns += 1;
18199
+ if (capabilityExpectedUnknownCount > 0) {
18200
+ capabilityExpectedUnknownPositiveRuns += 1;
18201
+ }
18202
+ }
18203
+ const capabilityProvidedUnknownCount = deriveCapabilityProvidedUnknownCount(entry);
18204
+ if (Number.isFinite(capabilityProvidedUnknownCount)) {
18205
+ capabilityProvidedUnknownKnownRuns += 1;
18206
+ if (capabilityProvidedUnknownCount > 0) {
18207
+ capabilityProvidedUnknownPositiveRuns += 1;
18208
+ }
18209
+ }
18210
+ const moquiMatrixRegressionCount = deriveMoquiMatrixRegressionCount(entry);
18211
+ if (Number.isFinite(moquiMatrixRegressionCount)) {
18212
+ moquiMatrixKnownRuns += 1;
18213
+ moquiMatrixRegressionCounts.push(Math.max(0, moquiMatrixRegressionCount));
18214
+ if (moquiMatrixRegressionCount > 0) {
18215
+ moquiMatrixPositiveRuns += 1;
18216
+ }
18217
+ }
17362
18218
  }
17363
18219
 
17364
18220
  return {
@@ -17379,6 +18235,18 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17379
18235
  ),
17380
18236
  latest_capability_coverage_percent: toNumber(latestCapabilitySummary.coverage_percent),
17381
18237
  latest_capability_coverage_passed: parseAutoHandoffGateBoolean(latestCapabilitySummary.passed, null),
18238
+ latest_capability_expected_unknown_count: Number.isFinite(latestCapabilityExpectedUnknownCount)
18239
+ ? latestCapabilityExpectedUnknownCount
18240
+ : null,
18241
+ latest_capability_provided_unknown_count: Number.isFinite(latestCapabilityProvidedUnknownCount)
18242
+ ? latestCapabilityProvidedUnknownCount
18243
+ : null,
18244
+ latest_moqui_matrix_regression_count: Number.isFinite(latestMoquiMatrixRegressionCount)
18245
+ ? latestMoquiMatrixRegressionCount
18246
+ : null,
18247
+ latest_moqui_matrix_regression_gate_max: Number.isFinite(latestMoquiMatrixRegressionGateMax)
18248
+ ? latestMoquiMatrixRegressionGateMax
18249
+ : null,
17382
18250
  latest_scene_package_batch_passed: parseAutoHandoffGateBoolean(latestSceneSummary.batch_gate_passed, null),
17383
18251
  latest_release_gate_preflight_blocked: parseAutoHandoffGateBoolean(latestPreflight.blocked, null),
17384
18252
  latest_failure_highlights: latestFailureHighlights,
@@ -17389,6 +18257,27 @@ async function loadGovernanceHandoffQualitySignals(projectPath) {
17389
18257
  avg_ontology_quality_score: ontologyScores.length > 0
17390
18258
  ? Number((ontologyScores.reduce((sum, item) => sum + item, 0) / ontologyScores.length).toFixed(2))
17391
18259
  : null,
18260
+ capability_expected_unknown_positive_rate_percent: toPercent(
18261
+ capabilityExpectedUnknownPositiveRuns,
18262
+ capabilityExpectedUnknownKnownRuns
18263
+ ),
18264
+ capability_provided_unknown_positive_rate_percent: toPercent(
18265
+ capabilityProvidedUnknownPositiveRuns,
18266
+ capabilityProvidedUnknownKnownRuns
18267
+ ),
18268
+ avg_moqui_matrix_regression_count: moquiMatrixRegressionCounts.length > 0
18269
+ ? Number((
18270
+ moquiMatrixRegressionCounts.reduce((sum, item) => sum + item, 0) /
18271
+ moquiMatrixRegressionCounts.length
18272
+ ).toFixed(2))
18273
+ : null,
18274
+ max_moqui_matrix_regression_count: moquiMatrixRegressionCounts.length > 0
18275
+ ? Number(Math.max(...moquiMatrixRegressionCounts).toFixed(2))
18276
+ : null,
18277
+ moqui_matrix_regression_positive_rate_percent: toPercent(
18278
+ moquiMatrixPositiveRuns,
18279
+ moquiMatrixKnownRuns
18280
+ ),
17392
18281
  parse_error: null
17393
18282
  };
17394
18283
  }
@@ -17417,10 +18306,46 @@ function deriveGovernanceRiskLevel(summary) {
17417
18306
  const handoffFailureRate = Number(handoffQuality.failure_rate_percent);
17418
18307
  const handoffGatePassRate = Number(handoffQuality.gate_pass_rate_percent);
17419
18308
  const handoffCapabilityPassRate = Number(handoffQuality.capability_coverage_pass_rate_percent);
18309
+ const handoffLatestCapabilityExpectedUnknownCount = Number(
18310
+ handoffQuality.latest_capability_expected_unknown_count
18311
+ );
18312
+ const handoffLatestCapabilityProvidedUnknownCount = Number(
18313
+ handoffQuality.latest_capability_provided_unknown_count
18314
+ );
18315
+ const handoffCapabilityExpectedUnknownPositiveRate = Number(
18316
+ handoffQuality.capability_expected_unknown_positive_rate_percent
18317
+ );
18318
+ const handoffCapabilityProvidedUnknownPositiveRate = Number(
18319
+ handoffQuality.capability_provided_unknown_positive_rate_percent
18320
+ );
18321
+ const handoffLatestMoquiMatrixRegressionCount = Number(handoffQuality.latest_moqui_matrix_regression_count);
18322
+ const handoffLatestMoquiMatrixRegressionGateMax = Number(handoffQuality.latest_moqui_matrix_regression_gate_max);
18323
+ const handoffMoquiMatrixRegressionPositiveRate = Number(handoffQuality.moqui_matrix_regression_positive_rate_percent);
17420
18324
  const handoffPreflightBlocked = parseAutoHandoffGateBoolean(
17421
18325
  handoffQuality.latest_release_gate_preflight_blocked,
17422
18326
  null
17423
18327
  );
18328
+ const handoffMatrixRegressionPositive = (
18329
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
18330
+ handoffLatestMoquiMatrixRegressionCount > 0
18331
+ );
18332
+ const handoffMatrixRegressionOverGate = (
18333
+ handoffMatrixRegressionPositive &&
18334
+ Number.isFinite(handoffLatestMoquiMatrixRegressionGateMax) &&
18335
+ handoffLatestMoquiMatrixRegressionCount > handoffLatestMoquiMatrixRegressionGateMax
18336
+ );
18337
+ const handoffMatrixRegressionPressureHigh = (
18338
+ Number.isFinite(handoffMoquiMatrixRegressionPositiveRate) &&
18339
+ handoffMoquiMatrixRegressionPositiveRate >= 50
18340
+ );
18341
+ const handoffCapabilityLexiconUnknownPositive = (
18342
+ (Number.isFinite(handoffLatestCapabilityExpectedUnknownCount) && handoffLatestCapabilityExpectedUnknownCount > 0) ||
18343
+ (Number.isFinite(handoffLatestCapabilityProvidedUnknownCount) && handoffLatestCapabilityProvidedUnknownCount > 0)
18344
+ );
18345
+ const handoffCapabilityLexiconUnknownPressureHigh = (
18346
+ (Number.isFinite(handoffCapabilityExpectedUnknownPositiveRate) && handoffCapabilityExpectedUnknownPositiveRate >= 50) ||
18347
+ (Number.isFinite(handoffCapabilityProvidedUnknownPositiveRate) && handoffCapabilityProvidedUnknownPositiveRate >= 50)
18348
+ );
17424
18349
 
17425
18350
  let riskLevel = 'low';
17426
18351
  if (failureRate >= 40 || pendingGoals >= 5) {
@@ -17461,7 +18386,11 @@ function deriveGovernanceRiskLevel(summary) {
17461
18386
  handoffFailed ||
17462
18387
  handoffHighFailureRate ||
17463
18388
  handoffPreflightBlocked === true ||
17464
- (handoffLatestGatePassed === false && handoffSevereQualityDrop)
18389
+ handoffMatrixRegressionOverGate ||
18390
+ handoffCapabilityLexiconUnknownPositive ||
18391
+ (handoffLatestGatePassed === false && handoffSevereQualityDrop) ||
18392
+ (handoffMatrixRegressionPositive && handoffMatrixRegressionPressureHigh) ||
18393
+ handoffCapabilityLexiconUnknownPressureHigh
17465
18394
  ) {
17466
18395
  riskLevel = 'high';
17467
18396
  } else if (
@@ -17470,7 +18399,11 @@ function deriveGovernanceRiskLevel(summary) {
17470
18399
  (Number.isFinite(handoffGatePassRate) && handoffGatePassRate < 85) ||
17471
18400
  (Number.isFinite(handoffCapabilityPassRate) && handoffCapabilityPassRate < 85) ||
17472
18401
  (Number.isFinite(handoffLatestOntologyScore) && handoffLatestOntologyScore < 85) ||
17473
- (Number.isFinite(handoffFailureRate) && handoffFailureRate > 0)
18402
+ (Number.isFinite(handoffFailureRate) && handoffFailureRate > 0) ||
18403
+ handoffMatrixRegressionPositive ||
18404
+ (Number.isFinite(handoffMoquiMatrixRegressionPositiveRate) && handoffMoquiMatrixRegressionPositiveRate > 0) ||
18405
+ (Number.isFinite(handoffCapabilityExpectedUnknownPositiveRate) && handoffCapabilityExpectedUnknownPositiveRate > 0) ||
18406
+ (Number.isFinite(handoffCapabilityProvidedUnknownPositiveRate) && handoffCapabilityProvidedUnknownPositiveRate > 0)
17474
18407
  )
17475
18408
  ) {
17476
18409
  riskLevel = 'medium';
@@ -17505,6 +18438,22 @@ function buildGovernanceConcerns(summary) {
17505
18438
  const handoffLatestOntologyScore = Number(handoffQuality.latest_ontology_quality_score);
17506
18439
  const handoffFailureRate = Number(handoffQuality.failure_rate_percent);
17507
18440
  const handoffCapabilityPassRate = Number(handoffQuality.capability_coverage_pass_rate_percent);
18441
+ const handoffLatestCapabilityExpectedUnknownCount = Number(
18442
+ handoffQuality.latest_capability_expected_unknown_count
18443
+ );
18444
+ const handoffLatestCapabilityProvidedUnknownCount = Number(
18445
+ handoffQuality.latest_capability_provided_unknown_count
18446
+ );
18447
+ const handoffCapabilityExpectedUnknownPositiveRate = Number(
18448
+ handoffQuality.capability_expected_unknown_positive_rate_percent
18449
+ );
18450
+ const handoffCapabilityProvidedUnknownPositiveRate = Number(
18451
+ handoffQuality.capability_provided_unknown_positive_rate_percent
18452
+ );
18453
+ const handoffLatestMoquiMatrixRegressionCount = Number(handoffQuality.latest_moqui_matrix_regression_count);
18454
+ const handoffLatestMoquiMatrixRegressionGateMax = Number(handoffQuality.latest_moqui_matrix_regression_gate_max);
18455
+ const handoffMaxMoquiMatrixRegressionCount = Number(handoffQuality.max_moqui_matrix_regression_count);
18456
+ const handoffMoquiMatrixRegressionPositiveRate = Number(handoffQuality.moqui_matrix_regression_positive_rate_percent);
17508
18457
  const handoffPreflightBlocked = parseAutoHandoffGateBoolean(
17509
18458
  handoffQuality.latest_release_gate_preflight_blocked,
17510
18459
  null
@@ -17567,6 +18516,61 @@ function buildGovernanceConcerns(summary) {
17567
18516
  if (Number.isFinite(handoffCapabilityPassRate) && handoffCapabilityPassRate < 85) {
17568
18517
  concerns.push(`Handoff capability coverage pass rate is low at ${handoffCapabilityPassRate}%.`);
17569
18518
  }
18519
+ if (
18520
+ Number.isFinite(handoffLatestCapabilityExpectedUnknownCount) &&
18521
+ handoffLatestCapabilityExpectedUnknownCount > 0
18522
+ ) {
18523
+ concerns.push(
18524
+ `Latest handoff manifest capability unknown count is ${handoffLatestCapabilityExpectedUnknownCount}.`
18525
+ );
18526
+ }
18527
+ if (
18528
+ Number.isFinite(handoffLatestCapabilityProvidedUnknownCount) &&
18529
+ handoffLatestCapabilityProvidedUnknownCount > 0
18530
+ ) {
18531
+ concerns.push(
18532
+ `Latest handoff template capability unknown count is ${handoffLatestCapabilityProvidedUnknownCount}.`
18533
+ );
18534
+ }
18535
+ if (
18536
+ Number.isFinite(handoffCapabilityExpectedUnknownPositiveRate) &&
18537
+ handoffCapabilityExpectedUnknownPositiveRate > 0
18538
+ ) {
18539
+ concerns.push(
18540
+ `Handoff manifest capability unknown positive rate is ${handoffCapabilityExpectedUnknownPositiveRate}%.`
18541
+ );
18542
+ }
18543
+ if (
18544
+ Number.isFinite(handoffCapabilityProvidedUnknownPositiveRate) &&
18545
+ handoffCapabilityProvidedUnknownPositiveRate > 0
18546
+ ) {
18547
+ concerns.push(
18548
+ `Handoff template capability unknown positive rate is ${handoffCapabilityProvidedUnknownPositiveRate}%.`
18549
+ );
18550
+ }
18551
+ if (Number.isFinite(handoffLatestMoquiMatrixRegressionCount) && handoffLatestMoquiMatrixRegressionCount > 0) {
18552
+ concerns.push(
18553
+ `Latest handoff Moqui matrix regression count is ${handoffLatestMoquiMatrixRegressionCount}.`
18554
+ );
18555
+ }
18556
+ if (
18557
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
18558
+ Number.isFinite(handoffLatestMoquiMatrixRegressionGateMax) &&
18559
+ handoffLatestMoquiMatrixRegressionCount > handoffLatestMoquiMatrixRegressionGateMax
18560
+ ) {
18561
+ concerns.push(
18562
+ `Latest handoff Moqui matrix regressions exceed gate (${handoffLatestMoquiMatrixRegressionCount} > ` +
18563
+ `${handoffLatestMoquiMatrixRegressionGateMax}).`
18564
+ );
18565
+ }
18566
+ if (Number.isFinite(handoffMoquiMatrixRegressionPositiveRate) && handoffMoquiMatrixRegressionPositiveRate > 0) {
18567
+ concerns.push(
18568
+ `Handoff Moqui matrix regression positive rate is ${handoffMoquiMatrixRegressionPositiveRate}%.`
18569
+ );
18570
+ }
18571
+ if (Number.isFinite(handoffMaxMoquiMatrixRegressionCount) && handoffMaxMoquiMatrixRegressionCount > 0) {
18572
+ concerns.push(`Handoff Moqui matrix regression max is ${handoffMaxMoquiMatrixRegressionCount}.`);
18573
+ }
17570
18574
  }
17571
18575
 
17572
18576
  return concerns;
@@ -17594,6 +18598,21 @@ function buildGovernanceRecommendations(summary) {
17594
18598
  const handoffLatestGatePassed = parseAutoHandoffGateBoolean(handoffQuality.latest_gate_passed, null);
17595
18599
  const handoffFailureRate = Number(handoffQuality.failure_rate_percent);
17596
18600
  const handoffCapabilityPassRate = Number(handoffQuality.capability_coverage_pass_rate_percent);
18601
+ const handoffLatestCapabilityExpectedUnknownCount = Number(
18602
+ handoffQuality.latest_capability_expected_unknown_count
18603
+ );
18604
+ const handoffLatestCapabilityProvidedUnknownCount = Number(
18605
+ handoffQuality.latest_capability_provided_unknown_count
18606
+ );
18607
+ const handoffCapabilityExpectedUnknownPositiveRate = Number(
18608
+ handoffQuality.capability_expected_unknown_positive_rate_percent
18609
+ );
18610
+ const handoffCapabilityProvidedUnknownPositiveRate = Number(
18611
+ handoffQuality.capability_provided_unknown_positive_rate_percent
18612
+ );
18613
+ const handoffLatestMoquiMatrixRegressionCount = Number(handoffQuality.latest_moqui_matrix_regression_count);
18614
+ const handoffLatestMoquiMatrixRegressionGateMax = Number(handoffQuality.latest_moqui_matrix_regression_gate_max);
18615
+ const handoffMoquiMatrixRegressionPositiveRate = Number(handoffQuality.moqui_matrix_regression_positive_rate_percent);
17597
18616
  const handoffPreflightBlocked = parseAutoHandoffGateBoolean(
17598
18617
  handoffQuality.latest_release_gate_preflight_blocked,
17599
18618
  null
@@ -17635,6 +18654,15 @@ function buildGovernanceRecommendations(summary) {
17635
18654
  '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --dry-run --json`.'
17636
18655
  );
17637
18656
  } else if (handoffTotalRuns > 0) {
18657
+ const handoffMoquiMatrixRegressionPositive = (
18658
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
18659
+ handoffLatestMoquiMatrixRegressionCount > 0
18660
+ );
18661
+ const handoffMoquiMatrixRegressionOverGate = (
18662
+ handoffMoquiMatrixRegressionPositive &&
18663
+ Number.isFinite(handoffLatestMoquiMatrixRegressionGateMax) &&
18664
+ handoffLatestMoquiMatrixRegressionCount > handoffLatestMoquiMatrixRegressionGateMax
18665
+ );
17638
18666
  if (
17639
18667
  (handoffLatestStatus && !['completed', 'dry-run', 'dry_run'].includes(handoffLatestStatus)) ||
17640
18668
  handoffLatestGatePassed === false ||
@@ -17655,6 +18683,38 @@ function buildGovernanceRecommendations(summary) {
17655
18683
  '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --dry-run --json`.'
17656
18684
  );
17657
18685
  }
18686
+ if (
18687
+ (Number.isFinite(handoffLatestCapabilityExpectedUnknownCount) && handoffLatestCapabilityExpectedUnknownCount > 0) ||
18688
+ (Number.isFinite(handoffLatestCapabilityProvidedUnknownCount) && handoffLatestCapabilityProvidedUnknownCount > 0) ||
18689
+ (Number.isFinite(handoffCapabilityExpectedUnknownPositiveRate) && handoffCapabilityExpectedUnknownPositiveRate > 0) ||
18690
+ (Number.isFinite(handoffCapabilityProvidedUnknownPositiveRate) && handoffCapabilityProvidedUnknownPositiveRate > 0)
18691
+ ) {
18692
+ recommendations.push(
18693
+ 'Normalize capability lexicon gaps with ' +
18694
+ '`node scripts/moqui-lexicon-audit.js --manifest docs/handoffs/handoff-manifest.json ' +
18695
+ '--template-dir .kiro/templates/scene-packages --fail-on-gap --json`.'
18696
+ );
18697
+ recommendations.push(
18698
+ 'Re-run strict handoff lexicon gates with ' +
18699
+ '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --dry-run --json`.'
18700
+ );
18701
+ }
18702
+ if (
18703
+ handoffMoquiMatrixRegressionPositive ||
18704
+ handoffMoquiMatrixRegressionOverGate ||
18705
+ (Number.isFinite(handoffMoquiMatrixRegressionPositiveRate) && handoffMoquiMatrixRegressionPositiveRate > 0)
18706
+ ) {
18707
+ recommendations.push(
18708
+ 'Recover Moqui matrix regressions via ' +
18709
+ '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json ' +
18710
+ '--dry-run --max-moqui-matrix-regressions 0 --json`.'
18711
+ );
18712
+ recommendations.push(
18713
+ 'Inspect Moqui baseline matrix drift with ' +
18714
+ '`sce scene moqui-baseline --include-all ' +
18715
+ '--compare-with .kiro/reports/release-evidence/moqui-template-baseline.json --json`.'
18716
+ );
18717
+ }
17658
18718
  }
17659
18719
 
17660
18720
  return Array.from(new Set(recommendations));
@@ -19002,12 +20062,64 @@ function evaluateGovernanceReleaseGateBlockState(assessment) {
19002
20062
  if (handoffSnapshot.latest_capability_coverage_passed === false) {
19003
20063
  reasons.push('handoff-capability-coverage-failed');
19004
20064
  }
20065
+ if (
20066
+ Number.isFinite(handoffSnapshot.latest_capability_expected_unknown_count) &&
20067
+ handoffSnapshot.latest_capability_expected_unknown_count > 0
20068
+ ) {
20069
+ reasons.push(
20070
+ `handoff-capability-expected-unknown-positive:${handoffSnapshot.latest_capability_expected_unknown_count}`
20071
+ );
20072
+ }
20073
+ if (
20074
+ Number.isFinite(handoffSnapshot.latest_capability_provided_unknown_count) &&
20075
+ handoffSnapshot.latest_capability_provided_unknown_count > 0
20076
+ ) {
20077
+ reasons.push(
20078
+ `handoff-capability-provided-unknown-positive:${handoffSnapshot.latest_capability_provided_unknown_count}`
20079
+ );
20080
+ }
20081
+ if (
20082
+ Number.isFinite(handoffSnapshot.capability_expected_unknown_positive_rate_percent) &&
20083
+ handoffSnapshot.capability_expected_unknown_positive_rate_percent > 0
20084
+ ) {
20085
+ reasons.push(
20086
+ `handoff-capability-expected-unknown-positive-rate:` +
20087
+ `${handoffSnapshot.capability_expected_unknown_positive_rate_percent}`
20088
+ );
20089
+ }
20090
+ if (
20091
+ Number.isFinite(handoffSnapshot.capability_provided_unknown_positive_rate_percent) &&
20092
+ handoffSnapshot.capability_provided_unknown_positive_rate_percent > 0
20093
+ ) {
20094
+ reasons.push(
20095
+ `handoff-capability-provided-unknown-positive-rate:` +
20096
+ `${handoffSnapshot.capability_provided_unknown_positive_rate_percent}`
20097
+ );
20098
+ }
19005
20099
  if (handoffSnapshot.latest_release_gate_preflight_blocked === true) {
19006
20100
  reasons.push('handoff-release-preflight-blocked');
19007
20101
  }
19008
20102
  if (Number.isFinite(handoffSnapshot.failure_rate_percent) && handoffSnapshot.failure_rate_percent > 0) {
19009
20103
  reasons.push(`handoff-failure-rate-positive:${handoffSnapshot.failure_rate_percent}`);
19010
20104
  }
20105
+ const handoffLatestMoquiMatrixRegressionCount = Number(handoffSnapshot.latest_moqui_matrix_regression_count);
20106
+ const handoffLatestMoquiMatrixRegressionGateMax = Number(handoffSnapshot.latest_moqui_matrix_regression_gate_max);
20107
+ if (
20108
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
20109
+ handoffLatestMoquiMatrixRegressionCount > 0
20110
+ ) {
20111
+ reasons.push(`handoff-moqui-matrix-regressions-positive:${handoffLatestMoquiMatrixRegressionCount}`);
20112
+ }
20113
+ if (
20114
+ Number.isFinite(handoffLatestMoquiMatrixRegressionCount) &&
20115
+ Number.isFinite(handoffLatestMoquiMatrixRegressionGateMax) &&
20116
+ handoffLatestMoquiMatrixRegressionCount > handoffLatestMoquiMatrixRegressionGateMax
20117
+ ) {
20118
+ reasons.push(
20119
+ `handoff-moqui-matrix-regressions-over-gate:` +
20120
+ `${handoffLatestMoquiMatrixRegressionCount}/${handoffLatestMoquiMatrixRegressionGateMax}`
20121
+ );
20122
+ }
19011
20123
  }
19012
20124
 
19013
20125
  const blockedByReleaseGate = Boolean(
@@ -19024,7 +20136,13 @@ function evaluateGovernanceReleaseGateBlockState(assessment) {
19024
20136
  || `${item}` === 'handoff-latest-gate-failed'
19025
20137
  || `${item}`.startsWith('handoff-ontology-score-low:')
19026
20138
  || `${item}` === 'handoff-capability-coverage-failed'
20139
+ || `${item}`.startsWith('handoff-capability-expected-unknown-positive:')
20140
+ || `${item}`.startsWith('handoff-capability-provided-unknown-positive:')
20141
+ || `${item}`.startsWith('handoff-capability-expected-unknown-positive-rate:')
20142
+ || `${item}`.startsWith('handoff-capability-provided-unknown-positive-rate:')
19027
20143
  || `${item}` === 'handoff-release-preflight-blocked'
20144
+ || `${item}`.startsWith('handoff-moqui-matrix-regressions-positive:')
20145
+ || `${item}`.startsWith('handoff-moqui-matrix-regressions-over-gate:')
19028
20146
  ));
19029
20147
  const blocked = reasons.length > 0 && (blockedByReleaseGate || blockedByHandoffQuality);
19030
20148
  return {
@@ -19066,6 +20184,29 @@ function buildGovernanceCloseLoopRecommendations(finalAssessment, stopReason, st
19066
20184
  '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --continue-from latest --continue-strategy failed-only --json`.'
19067
20185
  );
19068
20186
  }
20187
+ if (reasons.some(item => `${item}`.startsWith('handoff-moqui-matrix-regressions-'))) {
20188
+ base.push(
20189
+ 'Recover Moqui matrix regressions with ' +
20190
+ '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json ' +
20191
+ '--dry-run --max-moqui-matrix-regressions 0 --json`.'
20192
+ );
20193
+ base.push(
20194
+ 'Inspect Moqui matrix drift with ' +
20195
+ '`sce scene moqui-baseline --include-all ' +
20196
+ '--compare-with .kiro/reports/release-evidence/moqui-template-baseline.json --json`.'
20197
+ );
20198
+ }
20199
+ if (reasons.some(item => `${item}`.startsWith('handoff-capability-') && `${item}`.includes('unknown'))) {
20200
+ base.push(
20201
+ 'Normalize capability lexicon gaps with ' +
20202
+ '`node scripts/moqui-lexicon-audit.js --manifest docs/handoffs/handoff-manifest.json ' +
20203
+ '--template-dir .kiro/templates/scene-packages --fail-on-gap --json`.'
20204
+ );
20205
+ base.push(
20206
+ 'Re-run strict handoff gates after lexicon normalization with ' +
20207
+ '`sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --dry-run --json`.'
20208
+ );
20209
+ }
19069
20210
  return Array.from(new Set(base));
19070
20211
  }
19071
20212