@aiwg/cli 2026.9.24 → 2026.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/agentic/code/providers/capability-matrix.yaml +2 -1
  2. package/dist/src/artifacts/discovery-shadow-evaluator.js +177 -0
  3. package/dist/src/artifacts/discovery-shadow.js +195 -0
  4. package/dist/src/artifacts/index-builder.js +47 -9
  5. package/dist/src/artifacts/query-engine.js +94 -1
  6. package/dist/src/artifacts/types.js +1 -0
  7. package/dist/src/cli/handlers/decision.js +177 -0
  8. package/dist/src/cli/handlers/gates.js +154 -0
  9. package/dist/src/cli/handlers/index.js +5 -1
  10. package/dist/src/cli/handlers/sessions.js +10 -7
  11. package/dist/src/cli/handlers/utilities.js +20 -0
  12. package/dist/src/config/aiwg-config.js +15 -2
  13. package/dist/src/decision/acceptance.js +25 -0
  14. package/dist/src/decision/adapters/jev.js +29 -3
  15. package/dist/src/decision/ask.js +352 -0
  16. package/dist/src/decision/calibration/registry.js +9 -0
  17. package/dist/src/decision/compose.js +74 -15
  18. package/dist/src/decision/context-live-promotion.js +344 -0
  19. package/dist/src/decision/context-live-qualification.js +591 -0
  20. package/dist/src/decision/context-pruning.js +847 -0
  21. package/dist/src/decision/driver.js +432 -0
  22. package/dist/src/decision/egress-live-qualification.js +874 -0
  23. package/dist/src/decision/ensemble/contract.js +64 -1
  24. package/dist/src/decision/ensemble/index.js +1 -0
  25. package/dist/src/decision/ensemble/report.js +8 -42
  26. package/dist/src/decision/ensemble/runtime.js +571 -0
  27. package/dist/src/decision/ensemble-study/artifacts.js +85 -0
  28. package/dist/src/decision/ensemble-study/calibration.js +443 -0
  29. package/dist/src/decision/ensemble-study/corpus.js +229 -0
  30. package/dist/src/decision/ensemble-study/multifact-artifacts.js +23 -0
  31. package/dist/src/decision/ensemble-study/multifact-study.js +596 -0
  32. package/dist/src/decision/ensemble-study/multifact.js +241 -0
  33. package/dist/src/decision/ensemble-study/protocol.js +77 -0
  34. package/dist/src/decision/ensemble-study/score.js +462 -0
  35. package/dist/src/decision/ensemble-study/staged.js +87 -0
  36. package/dist/src/decision/ensemble-study/statistics.js +84 -0
  37. package/dist/src/decision/evaluate.js +227 -14
  38. package/dist/src/decision/export.js +4 -0
  39. package/dist/src/decision/feature-export.js +1074 -0
  40. package/dist/src/decision/graph-index.js +16 -0
  41. package/dist/src/decision/graph-live-qualification.js +1041 -0
  42. package/dist/src/decision/graph-live-workload.js +245 -0
  43. package/dist/src/decision/graph-skill-bridge.js +21 -3
  44. package/dist/src/decision/heldout/calibration.js +140 -0
  45. package/dist/src/decision/heldout/collector.js +430 -0
  46. package/dist/src/decision/heldout/contract.js +309 -0
  47. package/dist/src/decision/heldout/d29-generator-ids.js +12 -0
  48. package/dist/src/decision/heldout/d29-passage-baseline-v2.js +93 -0
  49. package/dist/src/decision/heldout/d29-passage-baseline-v3.js +100 -0
  50. package/dist/src/decision/heldout/d29-passage-baseline.js +66 -0
  51. package/dist/src/decision/heldout/d29-pools-v8.js +575 -0
  52. package/dist/src/decision/heldout/d29-pools.js +288 -0
  53. package/dist/src/decision/heldout/d29-v6.js +169 -0
  54. package/dist/src/decision/heldout/d29-v7.js +202 -0
  55. package/dist/src/decision/heldout/d29-v8.js +586 -0
  56. package/dist/src/decision/heldout/generator-registry.js +67 -0
  57. package/dist/src/decision/heldout/generators.js +696 -0
  58. package/dist/src/decision/heldout/index.js +8 -0
  59. package/dist/src/decision/heldout/journal.js +346 -0
  60. package/dist/src/decision/heldout/study-generators.js +64 -0
  61. package/dist/src/decision/heldout/types.js +2 -0
  62. package/dist/src/decision/host-policy.js +61 -0
  63. package/dist/src/decision/index.js +13 -0
  64. package/dist/src/decision/interop-profile.v1.json +112 -0
  65. package/dist/src/decision/interop.js +653 -0
  66. package/dist/src/decision/issue-triage/index.js +3 -0
  67. package/dist/src/decision/issue-triage/runtime.js +1069 -0
  68. package/dist/src/decision/issue-triage/types.js +2 -0
  69. package/dist/src/decision/jev-credentials.js +121 -0
  70. package/dist/src/decision/patterns/catalog.js +28 -8
  71. package/dist/src/decision/patterns/playground.js +28 -11
  72. package/dist/src/decision/patterns/specs.js +19 -10
  73. package/dist/src/decision/preprocessed-evidence.js +724 -0
  74. package/dist/src/decision/qualification/capture.js +8 -2
  75. package/dist/src/decision/qualification/gate-evidence.js +19 -4
  76. package/dist/src/decision/qualification/quality.js +57 -49
  77. package/dist/src/decision/qualification/release.js +76 -3
  78. package/dist/src/decision/qualification/runner.js +5 -2
  79. package/dist/src/decision/routing/contract.js +175 -0
  80. package/dist/src/decision/routing/index.js +5 -0
  81. package/dist/src/decision/routing/report.js +216 -0
  82. package/dist/src/decision/routing/runtime.js +685 -0
  83. package/dist/src/decision/routing/types.js +2 -0
  84. package/dist/src/decision/sdlc-screening.js +1157 -0
  85. package/dist/src/decision/sensitivity/comparative.js +154 -0
  86. package/dist/src/decision/sensitivity/contract.js +218 -0
  87. package/dist/src/decision/sensitivity/index.js +5 -0
  88. package/dist/src/decision/sensitivity/runtime.js +912 -0
  89. package/dist/src/decision/sensitivity/types.js +2 -0
  90. package/dist/src/decision/validate.js +26 -0
  91. package/dist/src/extensions/commands/definitions.js +38 -0
  92. package/dist/src/extensions/manifest.js +44 -0
  93. package/dist/src/gates/discovery.js +315 -0
  94. package/dist/src/gates/driver.js +388 -0
  95. package/dist/src/gates/evaluate.js +719 -0
  96. package/dist/src/gates/floors.js +255 -0
  97. package/dist/src/gates/index.js +17 -0
  98. package/dist/src/gates/providers/evidence.js +38 -0
  99. package/dist/src/gates/providers/index.js +21 -0
  100. package/dist/src/gates/providers/isolated-child-source.js +446 -0
  101. package/dist/src/gates/providers/loader.js +1069 -0
  102. package/dist/src/gates/providers/paired.js +52 -0
  103. package/dist/src/gates/providers/proportion.js +60 -0
  104. package/dist/src/gates/providers/registry.js +40 -0
  105. package/dist/src/gates/providers/scalar.js +41 -0
  106. package/dist/src/gates/providers/screening.js +53 -0
  107. package/dist/src/gates/providers/types.js +25 -0
  108. package/dist/src/gates/registry.js +1003 -0
  109. package/dist/src/gates/report.js +102 -0
  110. package/dist/src/gates/schema.js +56 -0
  111. package/dist/src/gates/stats/binomial.js +26 -0
  112. package/dist/src/gates/stats/bootstrap.js +62 -0
  113. package/dist/src/gates/stats/clopper-pearson.js +95 -0
  114. package/dist/src/gates/stats/digest.js +26 -0
  115. package/dist/src/gates/stats/error.js +5 -0
  116. package/dist/src/gates/stats/index.js +9 -0
  117. package/dist/src/gates/stats/normal.js +35 -0
  118. package/dist/src/gates/stats/paired.js +120 -0
  119. package/dist/src/gates/stats/splits.js +30 -0
  120. package/dist/src/gates/types.js +8 -0
  121. package/dist/src/governance/redaction.js +14 -4
  122. package/dist/src/mcp/muse-native-config.mjs +123 -0
  123. package/dist/src/mcp/tools/decision.mjs +228 -0
  124. package/dist/src/mcp/tools/subsystems.mjs +2 -0
  125. package/dist/src/providers/capability-matrix.yaml +2 -1
  126. package/dist/src/providers/provider-definitions.js +2 -1
  127. package/dist/src/sessions/adapters/muse.js +336 -39
  128. package/dist/src/sessions/batch-import.js +7 -0
  129. package/dist/src/sessions/workspace-discovery.js +13 -2
  130. package/package.json +1 -1
  131. package/schemas/decision/ConformalFrozenOpenData.v2.schema.json +371 -0
  132. package/schemas/decision/ConformalOpenDataReport.v2.schema.json +421 -0
  133. package/schemas/decision/ConformalPreregistration.v2.schema.json +529 -0
  134. package/schemas/decision/ContextPruningCandidate.v1.schema.json +47 -0
  135. package/schemas/decision/ContextPruningEvaluationReport.v1.schema.json +758 -0
  136. package/schemas/decision/ContextPruningPreregistration.v1.schema.json +175 -0
  137. package/schemas/decision/ContextPruningReceipt.v1.schema.json +72 -0
  138. package/schemas/decision/D17Multifact.v1.schema.json +1851 -0
  139. package/schemas/decision/D17StudyAnalysis.v1.schema.json +42 -0
  140. package/schemas/decision/D17StudyAnalysis.v2.schema.json +51 -0
  141. package/schemas/decision/D17StudyCalibration.v1.schema.json +819 -0
  142. package/schemas/decision/D17StudyDryRun.v1.schema.json +169 -0
  143. package/schemas/decision/D17StudyDryRun.v2.schema.json +239 -0
  144. package/schemas/decision/D17StudyGold.v1.schema.json +108 -0
  145. package/schemas/decision/D17StudyGuide.v1.schema.json +63 -0
  146. package/schemas/decision/D17StudyNativeTemplates.v1.schema.json +619 -0
  147. package/schemas/decision/D17StudyProtocol.v1.schema.json +241 -0
  148. package/schemas/decision/D17StudyReport.v1.schema.json +902 -0
  149. package/schemas/decision/D17StudyReport.v2.schema.json +1319 -0
  150. package/schemas/decision/D17StudyReview.v1.schema.json +175 -0
  151. package/schemas/decision/D17StudySplits.v1.schema.json +217 -0
  152. package/schemas/decision/D29Study.v1.schema.json +1903 -0
  153. package/schemas/decision/D29Study.v2.schema.json +1344 -0
  154. package/schemas/decision/D29Study.v3.schema.json +609 -0
  155. package/schemas/decision/D29Study.v4.schema.json +1133 -0
  156. package/schemas/decision/D29Study.v6.schema.json +1205 -0
  157. package/schemas/decision/D29Study.v7.schema.json +1471 -0
  158. package/schemas/decision/D29Study.v8.schema.json +671 -0
  159. package/schemas/decision/D29Study.v9.schema.json +778 -0
  160. package/schemas/decision/DecisionComparativeReplayAuditSample.v1.schema.json +137 -0
  161. package/schemas/decision/DecisionComparativeReplayCorpus.v1.schema.json +258 -0
  162. package/schemas/decision/DecisionComparativeReplayFreeze.v1.schema.json +74 -0
  163. package/schemas/decision/DecisionComparativeReplayGold.v1.schema.json +50 -0
  164. package/schemas/decision/DecisionComparativeReplayGoldAsset.v1.schema.json +37 -0
  165. package/schemas/decision/DecisionComparativeReplayIntegrity.v1.schema.json +81 -0
  166. package/schemas/decision/DecisionComparativeReplayMembers.v1.schema.json +52 -0
  167. package/schemas/decision/DecisionComparativeReplayPreregistration.v1.schema.json +113 -0
  168. package/schemas/decision/DecisionComparativeReplayReport.v1.schema.json +407 -0
  169. package/schemas/decision/DecisionComparativeReplayReviewPackets.v1.schema.json +260 -0
  170. package/schemas/decision/DecisionComparativeReplaySplit.v1.schema.json +84 -0
  171. package/schemas/decision/DecisionFeatureObservation.v1.schema.json +564 -0
  172. package/schemas/decision/DecisionFeatureSet.v1.schema.json +155 -0
  173. package/schemas/decision/DecisionInteropMapping.v1.schema.json +85 -0
  174. package/schemas/decision/DecisionIssueTriageEvaluationManifest.v1.schema.json +174 -0
  175. package/schemas/decision/DecisionIssueTriageEvaluationReport.v1.schema.json +280 -0
  176. package/schemas/decision/DecisionIssueTriagePilotPack.v1.schema.json +114 -0
  177. package/schemas/decision/DecisionRouteReceipt.v1.schema.json +155 -0
  178. package/schemas/decision/DecisionRoutingPolicy.v1.schema.json +132 -0
  179. package/schemas/decision/DecisionRoutingShadowReport.v1.schema.json +170 -0
  180. package/schemas/decision/DecisionRoutingTask.v1.schema.json +49 -0
  181. package/schemas/decision/DecisionRuleset.schema.json +8 -2
  182. package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +8 -2
  183. package/schemas/decision/DecisionSensitivityPlan.v1.schema.json +191 -0
  184. package/schemas/decision/DecisionSensitivityReport.v1.schema.json +163 -0
  185. package/schemas/decision/HeldoutApproval.v1.schema.json +312 -0
  186. package/schemas/decision/HeldoutAttempt.v1.schema.json +211 -0
  187. package/schemas/decision/HeldoutBaseline.v1.schema.json +90 -0
  188. package/schemas/decision/HeldoutCalibrationPhase.v1.schema.json +87 -0
  189. package/schemas/decision/HeldoutCorpus.v1.schema.json +167 -0
  190. package/schemas/decision/HeldoutEvent.v1.schema.json +41 -0
  191. package/schemas/decision/HeldoutFrozen.v1.schema.json +142 -0
  192. package/schemas/decision/HeldoutPreregistration.v1.schema.json +222 -0
  193. package/schemas/decision/HeldoutSpendEvent.v1.schema.json +122 -0
  194. package/schemas/decision/HeldoutSpendHead.v1.schema.json +25 -0
  195. package/schemas/decision/HeldoutSpendHead.v2.schema.json +46 -0
  196. package/schemas/decision/HeldoutSummary.v1.schema.json +101 -0
  197. package/schemas/decision/OpaDecisionLogExport.v1.schema.json +65 -0
  198. package/schemas/decision/PreprocessedEvidence.v1.schema.json +306 -0
  199. package/schemas/decision/RulesetResult.v1alpha2.schema.json +175 -0
  200. package/schemas/decision/SdlcEvidenceScreening.v1.schema.json +499 -0
  201. package/schemas/decision/SdlcScreeningPreregistration.v1.schema.json +132 -0
  202. package/schemas/decision/SdlcScreeningPreregistration.v2.schema.json +138 -0
  203. package/schemas/decision/SdlcScreeningRelease.v1.schema.json +654 -0
  204. package/schemas/decision/SdlcScreeningRelease.v2.schema.json +654 -0
  205. package/tools/agents/deploy-agents.mjs +1 -1
  206. package/tools/agents/providers/base.mjs +40 -18
  207. package/tools/agents/providers/factory.mjs +10 -2
  208. package/tools/agents/providers/muse-hooks.mjs +1 -29
  209. package/tools/agents/providers/muse.mjs +6 -3
@@ -295,7 +295,50 @@ export function validateEnsembleAggregate(value) {
295
295
  semantic(problems, 'ensemble aggregate rejected');
296
296
  return aggregate;
297
297
  }
298
- /** A D17 report may keep or tighten the upstream #2037/#2048 decision, never loosen it. */
298
+ /** D17 findings derived from the carried #2037/#2048 integrity fields alone. */
299
+ export function integrityMetadataFindings(integrity, minimumPairs) {
300
+ const findings = [];
301
+ if (integrity.integrity_state !== 'verified')
302
+ findings.push('integrity-not-verified');
303
+ if (integrity.integrity_mode === 'standard')
304
+ findings.push('integrity-mode-standard');
305
+ if (integrity.trusted_score_source === 'local-unverified')
306
+ findings.push('untrusted-score-source');
307
+ if (integrity.fresh_workspace_required && !integrity.fresh_workspace_verified)
308
+ findings.push('fresh-workspace-unverified');
309
+ if (integrity.uncertainty === null)
310
+ findings.push('uncertainty-missing');
311
+ if (integrity.paired_baseline === null)
312
+ findings.push('paired-baseline-missing');
313
+ if (integrity.weak_signal_reason !== null)
314
+ findings.push('weak-signal');
315
+ if (integrity.compromise_labels.length > 0)
316
+ findings.push('compromised');
317
+ if (integrity.sample_n < minimumPairs)
318
+ findings.push('insufficient-samples');
319
+ return findings;
320
+ }
321
+ /** The finding for one paired delta against its preregistered threshold, or null when it passes. */
322
+ export function pairedDeltaFinding(threshold, item) {
323
+ if (!item)
324
+ return `paired-delta-missing:${threshold.metric}`;
325
+ if (item.delta === null || !Number.isFinite(item.delta))
326
+ return `paired-delta-unknown:${threshold.metric}`;
327
+ if (!Number.isSafeInteger(item.pairs) || item.pairs < threshold.minimumPairs)
328
+ return `paired-delta-insufficient:${threshold.metric}`;
329
+ if (threshold.comparison === 'delta-at-least' ? item.delta < threshold.bound : item.delta > threshold.bound)
330
+ return `paired-delta-failed:${threshold.metric}`;
331
+ return null;
332
+ }
333
+ /** Upstream ROLLBACK or any compromise gives ROLLBACK; upstream HOLD or any finding gives HOLD. */
334
+ export function ensembleIntegrityDecision(integrity, findingCount) {
335
+ const upstream = integrity.release_gate.decision;
336
+ const compromised = integrity.integrity_state === 'compromised' || integrity.compromise_labels.length > 0;
337
+ return upstream === 'ROLLBACK' || compromised ? 'ROLLBACK' : upstream === 'HOLD' || findingCount > 0 ? 'HOLD' : 'PROMOTE';
338
+ }
339
+ /** A D17 report may keep or tighten the upstream #2037/#2048 decision, never loosen it. Every
340
+ * finding, passed flag and the decision are recomputed from the carried integrity fields and the
341
+ * preregistered thresholds; D09-derived findings need the record and are rechecked at promotion. */
299
342
  export function validateEnsembleIntegrityReport(value) {
300
343
  checkEnsembleSchema('integrityReport', value);
301
344
  const report = value;
@@ -307,6 +350,26 @@ export function validateEnsembleIntegrityReport(value) {
307
350
  problems.push(`D17 cannot upgrade ${report.upstreamDecision} to ${report.decision}`);
308
351
  if (report.decision === 'PROMOTE' && (report.findings.length > 0 || report.pairedDeltas.some(item => !item.passed)))
309
352
  problems.push('PROMOTE requires every D17 check to pass');
353
+ const metrics = report.pairedDeltas.map(item => item.metric);
354
+ if (canonicalJson(metrics) !== canonicalJson([...REQUIRED_PAIRED_METRICS].sort(compareEnsembleKeys)))
355
+ problems.push('paired deltas must list every required metric once in canonical order');
356
+ if (canonicalJson(report.findings) !== canonicalJson([...new Set(report.findings)].sort()))
357
+ problems.push('findings must be unique and sorted');
358
+ const expected = integrityMetadataFindings(report.integrity, Math.max(...report.pairedDeltas.map(item => item.minimumPairs)));
359
+ for (const item of report.pairedDeltas) {
360
+ const finding = pairedDeltaFinding(item, item);
361
+ if (item.passed !== (finding === null))
362
+ problems.push(`paired delta ${item.metric} passed flag does not match its threshold`);
363
+ // A missing observation is carried as delta null / pairs 0, so either label records the failure.
364
+ if (finding && !(finding === `paired-delta-unknown:${item.metric}` && report.findings.includes(`paired-delta-missing:${item.metric}`)))
365
+ expected.push(finding);
366
+ }
367
+ for (const finding of expected)
368
+ if (!report.findings.includes(finding))
369
+ problems.push(`finding ${finding} is missing`);
370
+ const decision = ensembleIntegrityDecision(report.integrity, report.findings.length);
371
+ if (report.decision !== decision)
372
+ problems.push(`decision ${report.decision} does not follow from its integrity and findings (${decision})`);
310
373
  if (digest !== ensembleContractDigest(payload))
311
374
  problems.push('integrity report digest does not match its content');
312
375
  semantic(problems, 'ensemble integrity report rejected');
@@ -3,4 +3,5 @@ export * from './contract.js';
3
3
  export * from './aggregate.js';
4
4
  export * from './drift.js';
5
5
  export * from './report.js';
6
+ export * from './runtime.js';
6
7
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,4 @@
1
- import { championChallengerEligibilityProblems, ensembleContractDigest, EnsembleContractError, validateChampionChallenger, validateEnsembleIntegrityReport, } from './contract.js';
1
+ import { championChallengerEligibilityProblems, ensembleContractDigest, EnsembleContractError, ensembleIntegrityDecision, integrityMetadataFindings, pairedDeltaFinding, validateChampionChallenger, validateEnsembleIntegrityReport, } from './contract.js';
2
2
  /** Extends the eval-integrity report with paired champion/challenger evidence. D17 findings can
3
3
  * only hold or roll back: HOLD and ROLLBACK from the upstream gate are never upgraded. */
4
4
  export function buildEnsembleIntegrityReport(input) {
@@ -10,24 +10,8 @@ export function buildEnsembleIntegrityReport(input) {
10
10
  else
11
11
  for (const problem of championChallengerEligibilityProblems(record, input.eligibility))
12
12
  findings.add(`d09-${problem}`);
13
- if (integrity.integrity_state !== 'verified')
14
- findings.add('integrity-not-verified');
15
- if (integrity.integrity_mode === 'standard')
16
- findings.add('integrity-mode-standard');
17
- if (integrity.trusted_score_source === 'local-unverified')
18
- findings.add('untrusted-score-source');
19
- if (integrity.fresh_workspace_required && !integrity.fresh_workspace_verified)
20
- findings.add('fresh-workspace-unverified');
21
- if (integrity.uncertainty === null)
22
- findings.add('uncertainty-missing');
23
- if (integrity.paired_baseline === null)
24
- findings.add('paired-baseline-missing');
25
- if (integrity.weak_signal_reason !== null)
26
- findings.add('weak-signal');
27
- if (integrity.compromise_labels.length > 0)
28
- findings.add('compromised');
29
- if (integrity.sample_n < Math.max(...record.pairedMetrics.map(item => item.minimumPairs)))
30
- findings.add('insufficient-samples');
13
+ for (const finding of integrityMetadataFindings(integrity, Math.max(...record.pairedMetrics.map(item => item.minimumPairs))))
14
+ findings.add(finding);
31
15
  const observed = new Map();
32
16
  for (const item of input.pairedDeltas) {
33
17
  if (observed.has(item.metric))
@@ -36,31 +20,13 @@ export function buildEnsembleIntegrityReport(input) {
36
20
  }
37
21
  const pairedDeltas = record.pairedMetrics.map(threshold => {
38
22
  const item = observed.get(threshold.metric);
39
- const delta = item?.delta ?? null;
40
- const pairs = item?.pairs ?? 0;
41
- let passed = true;
42
- if (!item) {
43
- findings.add(`paired-delta-missing:${threshold.metric}`);
44
- passed = false;
45
- }
46
- else if (delta === null || !Number.isFinite(delta)) {
47
- findings.add(`paired-delta-unknown:${threshold.metric}`);
48
- passed = false;
49
- }
50
- else if (!Number.isSafeInteger(pairs) || pairs < threshold.minimumPairs) {
51
- findings.add(`paired-delta-insufficient:${threshold.metric}`);
52
- passed = false;
53
- }
54
- else if (threshold.comparison === 'delta-at-least' ? delta < threshold.bound : delta > threshold.bound) {
55
- findings.add(`paired-delta-failed:${threshold.metric}`);
56
- passed = false;
57
- }
58
- return { ...threshold, delta, pairs, passed };
23
+ const finding = pairedDeltaFinding(threshold, item);
24
+ if (finding)
25
+ findings.add(finding);
26
+ return { ...threshold, delta: item?.delta ?? null, pairs: item?.pairs ?? 0, passed: finding === null };
59
27
  }).sort((a, b) => a.metric < b.metric ? -1 : a.metric > b.metric ? 1 : 0);
60
28
  const upstreamDecision = integrity.release_gate.decision;
61
- const compromised = integrity.integrity_state === 'compromised' || integrity.compromise_labels.length > 0;
62
- const decision = upstreamDecision === 'ROLLBACK' || compromised ? 'ROLLBACK'
63
- : upstreamDecision === 'HOLD' || findings.size > 0 ? 'HOLD' : 'PROMOTE';
29
+ const decision = ensembleIntegrityDecision(integrity, findings.size);
64
30
  const payload = {
65
31
  schemaVersion: 'decision-ensemble-integrity-report/v1',
66
32
  subject: { kind: 'champion-challenger', id: record.id, digest, eligibilityId: record.eligibilityId },