@aiwg/cli 2026.9.24 → 2026.10.0

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 (207) 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 +88 -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 +24 -2
  15. package/dist/src/decision/calibration/registry.js +9 -0
  16. package/dist/src/decision/compose.js +74 -15
  17. package/dist/src/decision/context-live-promotion.js +344 -0
  18. package/dist/src/decision/context-live-qualification.js +591 -0
  19. package/dist/src/decision/context-pruning.js +847 -0
  20. package/dist/src/decision/driver.js +430 -0
  21. package/dist/src/decision/egress-live-qualification.js +874 -0
  22. package/dist/src/decision/ensemble/contract.js +64 -1
  23. package/dist/src/decision/ensemble/index.js +1 -0
  24. package/dist/src/decision/ensemble/report.js +8 -42
  25. package/dist/src/decision/ensemble/runtime.js +571 -0
  26. package/dist/src/decision/ensemble-study/artifacts.js +85 -0
  27. package/dist/src/decision/ensemble-study/calibration.js +443 -0
  28. package/dist/src/decision/ensemble-study/corpus.js +229 -0
  29. package/dist/src/decision/ensemble-study/multifact-artifacts.js +23 -0
  30. package/dist/src/decision/ensemble-study/multifact-study.js +596 -0
  31. package/dist/src/decision/ensemble-study/multifact.js +241 -0
  32. package/dist/src/decision/ensemble-study/protocol.js +77 -0
  33. package/dist/src/decision/ensemble-study/score.js +462 -0
  34. package/dist/src/decision/ensemble-study/staged.js +87 -0
  35. package/dist/src/decision/ensemble-study/statistics.js +84 -0
  36. package/dist/src/decision/evaluate.js +227 -14
  37. package/dist/src/decision/export.js +4 -0
  38. package/dist/src/decision/feature-export.js +1074 -0
  39. package/dist/src/decision/graph-index.js +16 -0
  40. package/dist/src/decision/graph-live-qualification.js +1041 -0
  41. package/dist/src/decision/graph-live-workload.js +245 -0
  42. package/dist/src/decision/graph-skill-bridge.js +21 -3
  43. package/dist/src/decision/heldout/calibration.js +140 -0
  44. package/dist/src/decision/heldout/collector.js +430 -0
  45. package/dist/src/decision/heldout/contract.js +309 -0
  46. package/dist/src/decision/heldout/d29-generator-ids.js +12 -0
  47. package/dist/src/decision/heldout/d29-passage-baseline-v2.js +93 -0
  48. package/dist/src/decision/heldout/d29-passage-baseline-v3.js +100 -0
  49. package/dist/src/decision/heldout/d29-passage-baseline.js +66 -0
  50. package/dist/src/decision/heldout/d29-pools-v8.js +575 -0
  51. package/dist/src/decision/heldout/d29-pools.js +288 -0
  52. package/dist/src/decision/heldout/d29-v6.js +169 -0
  53. package/dist/src/decision/heldout/d29-v7.js +202 -0
  54. package/dist/src/decision/heldout/d29-v8.js +586 -0
  55. package/dist/src/decision/heldout/generator-registry.js +67 -0
  56. package/dist/src/decision/heldout/generators.js +696 -0
  57. package/dist/src/decision/heldout/index.js +8 -0
  58. package/dist/src/decision/heldout/journal.js +346 -0
  59. package/dist/src/decision/heldout/study-generators.js +64 -0
  60. package/dist/src/decision/heldout/types.js +2 -0
  61. package/dist/src/decision/host-policy.js +61 -0
  62. package/dist/src/decision/index.js +11 -0
  63. package/dist/src/decision/interop-profile.v1.json +112 -0
  64. package/dist/src/decision/interop.js +653 -0
  65. package/dist/src/decision/issue-triage/index.js +3 -0
  66. package/dist/src/decision/issue-triage/runtime.js +1069 -0
  67. package/dist/src/decision/issue-triage/types.js +2 -0
  68. package/dist/src/decision/patterns/catalog.js +28 -8
  69. package/dist/src/decision/patterns/playground.js +28 -11
  70. package/dist/src/decision/patterns/specs.js +19 -10
  71. package/dist/src/decision/preprocessed-evidence.js +724 -0
  72. package/dist/src/decision/qualification/capture.js +8 -2
  73. package/dist/src/decision/qualification/gate-evidence.js +19 -4
  74. package/dist/src/decision/qualification/quality.js +57 -49
  75. package/dist/src/decision/qualification/release.js +76 -3
  76. package/dist/src/decision/qualification/runner.js +5 -2
  77. package/dist/src/decision/routing/contract.js +175 -0
  78. package/dist/src/decision/routing/index.js +5 -0
  79. package/dist/src/decision/routing/report.js +216 -0
  80. package/dist/src/decision/routing/runtime.js +685 -0
  81. package/dist/src/decision/routing/types.js +2 -0
  82. package/dist/src/decision/sdlc-screening.js +1157 -0
  83. package/dist/src/decision/sensitivity/comparative.js +154 -0
  84. package/dist/src/decision/sensitivity/contract.js +218 -0
  85. package/dist/src/decision/sensitivity/index.js +5 -0
  86. package/dist/src/decision/sensitivity/runtime.js +912 -0
  87. package/dist/src/decision/sensitivity/types.js +2 -0
  88. package/dist/src/decision/validate.js +26 -0
  89. package/dist/src/extensions/commands/definitions.js +38 -0
  90. package/dist/src/extensions/manifest.js +44 -0
  91. package/dist/src/gates/discovery.js +315 -0
  92. package/dist/src/gates/driver.js +388 -0
  93. package/dist/src/gates/evaluate.js +719 -0
  94. package/dist/src/gates/floors.js +255 -0
  95. package/dist/src/gates/index.js +17 -0
  96. package/dist/src/gates/providers/evidence.js +38 -0
  97. package/dist/src/gates/providers/index.js +21 -0
  98. package/dist/src/gates/providers/isolated-child-source.js +446 -0
  99. package/dist/src/gates/providers/loader.js +1069 -0
  100. package/dist/src/gates/providers/paired.js +52 -0
  101. package/dist/src/gates/providers/proportion.js +60 -0
  102. package/dist/src/gates/providers/registry.js +40 -0
  103. package/dist/src/gates/providers/scalar.js +41 -0
  104. package/dist/src/gates/providers/screening.js +53 -0
  105. package/dist/src/gates/providers/types.js +25 -0
  106. package/dist/src/gates/registry.js +1003 -0
  107. package/dist/src/gates/report.js +102 -0
  108. package/dist/src/gates/schema.js +56 -0
  109. package/dist/src/gates/stats/binomial.js +26 -0
  110. package/dist/src/gates/stats/bootstrap.js +62 -0
  111. package/dist/src/gates/stats/clopper-pearson.js +95 -0
  112. package/dist/src/gates/stats/digest.js +26 -0
  113. package/dist/src/gates/stats/error.js +5 -0
  114. package/dist/src/gates/stats/index.js +9 -0
  115. package/dist/src/gates/stats/normal.js +35 -0
  116. package/dist/src/gates/stats/paired.js +120 -0
  117. package/dist/src/gates/stats/splits.js +30 -0
  118. package/dist/src/gates/types.js +8 -0
  119. package/dist/src/governance/redaction.js +14 -4
  120. package/dist/src/mcp/muse-native-config.mjs +123 -0
  121. package/dist/src/mcp/tools/decision.mjs +188 -0
  122. package/dist/src/mcp/tools/subsystems.mjs +2 -0
  123. package/dist/src/providers/capability-matrix.yaml +2 -1
  124. package/dist/src/providers/provider-definitions.js +2 -1
  125. package/dist/src/sessions/adapters/muse.js +336 -39
  126. package/dist/src/sessions/batch-import.js +7 -0
  127. package/dist/src/sessions/workspace-discovery.js +13 -2
  128. package/package.json +1 -1
  129. package/schemas/decision/ConformalFrozenOpenData.v2.schema.json +371 -0
  130. package/schemas/decision/ConformalOpenDataReport.v2.schema.json +421 -0
  131. package/schemas/decision/ConformalPreregistration.v2.schema.json +529 -0
  132. package/schemas/decision/ContextPruningCandidate.v1.schema.json +47 -0
  133. package/schemas/decision/ContextPruningEvaluationReport.v1.schema.json +758 -0
  134. package/schemas/decision/ContextPruningPreregistration.v1.schema.json +175 -0
  135. package/schemas/decision/ContextPruningReceipt.v1.schema.json +72 -0
  136. package/schemas/decision/D17Multifact.v1.schema.json +1851 -0
  137. package/schemas/decision/D17StudyAnalysis.v1.schema.json +42 -0
  138. package/schemas/decision/D17StudyAnalysis.v2.schema.json +51 -0
  139. package/schemas/decision/D17StudyCalibration.v1.schema.json +819 -0
  140. package/schemas/decision/D17StudyDryRun.v1.schema.json +169 -0
  141. package/schemas/decision/D17StudyDryRun.v2.schema.json +239 -0
  142. package/schemas/decision/D17StudyGold.v1.schema.json +108 -0
  143. package/schemas/decision/D17StudyGuide.v1.schema.json +63 -0
  144. package/schemas/decision/D17StudyNativeTemplates.v1.schema.json +619 -0
  145. package/schemas/decision/D17StudyProtocol.v1.schema.json +241 -0
  146. package/schemas/decision/D17StudyReport.v1.schema.json +902 -0
  147. package/schemas/decision/D17StudyReport.v2.schema.json +1319 -0
  148. package/schemas/decision/D17StudyReview.v1.schema.json +175 -0
  149. package/schemas/decision/D17StudySplits.v1.schema.json +217 -0
  150. package/schemas/decision/D29Study.v1.schema.json +1903 -0
  151. package/schemas/decision/D29Study.v2.schema.json +1344 -0
  152. package/schemas/decision/D29Study.v3.schema.json +609 -0
  153. package/schemas/decision/D29Study.v4.schema.json +1133 -0
  154. package/schemas/decision/D29Study.v6.schema.json +1205 -0
  155. package/schemas/decision/D29Study.v7.schema.json +1471 -0
  156. package/schemas/decision/D29Study.v8.schema.json +671 -0
  157. package/schemas/decision/D29Study.v9.schema.json +778 -0
  158. package/schemas/decision/DecisionComparativeReplayAuditSample.v1.schema.json +137 -0
  159. package/schemas/decision/DecisionComparativeReplayCorpus.v1.schema.json +258 -0
  160. package/schemas/decision/DecisionComparativeReplayFreeze.v1.schema.json +74 -0
  161. package/schemas/decision/DecisionComparativeReplayGold.v1.schema.json +50 -0
  162. package/schemas/decision/DecisionComparativeReplayGoldAsset.v1.schema.json +37 -0
  163. package/schemas/decision/DecisionComparativeReplayIntegrity.v1.schema.json +81 -0
  164. package/schemas/decision/DecisionComparativeReplayMembers.v1.schema.json +52 -0
  165. package/schemas/decision/DecisionComparativeReplayPreregistration.v1.schema.json +113 -0
  166. package/schemas/decision/DecisionComparativeReplayReport.v1.schema.json +407 -0
  167. package/schemas/decision/DecisionComparativeReplayReviewPackets.v1.schema.json +260 -0
  168. package/schemas/decision/DecisionComparativeReplaySplit.v1.schema.json +84 -0
  169. package/schemas/decision/DecisionFeatureObservation.v1.schema.json +564 -0
  170. package/schemas/decision/DecisionFeatureSet.v1.schema.json +155 -0
  171. package/schemas/decision/DecisionInteropMapping.v1.schema.json +85 -0
  172. package/schemas/decision/DecisionIssueTriageEvaluationManifest.v1.schema.json +174 -0
  173. package/schemas/decision/DecisionIssueTriageEvaluationReport.v1.schema.json +280 -0
  174. package/schemas/decision/DecisionIssueTriagePilotPack.v1.schema.json +114 -0
  175. package/schemas/decision/DecisionRouteReceipt.v1.schema.json +155 -0
  176. package/schemas/decision/DecisionRoutingPolicy.v1.schema.json +132 -0
  177. package/schemas/decision/DecisionRoutingShadowReport.v1.schema.json +170 -0
  178. package/schemas/decision/DecisionRoutingTask.v1.schema.json +49 -0
  179. package/schemas/decision/DecisionRuleset.schema.json +8 -2
  180. package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +8 -2
  181. package/schemas/decision/DecisionSensitivityPlan.v1.schema.json +191 -0
  182. package/schemas/decision/DecisionSensitivityReport.v1.schema.json +163 -0
  183. package/schemas/decision/HeldoutApproval.v1.schema.json +312 -0
  184. package/schemas/decision/HeldoutAttempt.v1.schema.json +211 -0
  185. package/schemas/decision/HeldoutBaseline.v1.schema.json +90 -0
  186. package/schemas/decision/HeldoutCalibrationPhase.v1.schema.json +87 -0
  187. package/schemas/decision/HeldoutCorpus.v1.schema.json +167 -0
  188. package/schemas/decision/HeldoutEvent.v1.schema.json +41 -0
  189. package/schemas/decision/HeldoutFrozen.v1.schema.json +142 -0
  190. package/schemas/decision/HeldoutPreregistration.v1.schema.json +222 -0
  191. package/schemas/decision/HeldoutSpendEvent.v1.schema.json +122 -0
  192. package/schemas/decision/HeldoutSpendHead.v1.schema.json +25 -0
  193. package/schemas/decision/HeldoutSpendHead.v2.schema.json +46 -0
  194. package/schemas/decision/HeldoutSummary.v1.schema.json +101 -0
  195. package/schemas/decision/OpaDecisionLogExport.v1.schema.json +65 -0
  196. package/schemas/decision/PreprocessedEvidence.v1.schema.json +306 -0
  197. package/schemas/decision/RulesetResult.v1alpha2.schema.json +175 -0
  198. package/schemas/decision/SdlcEvidenceScreening.v1.schema.json +499 -0
  199. package/schemas/decision/SdlcScreeningPreregistration.v1.schema.json +132 -0
  200. package/schemas/decision/SdlcScreeningPreregistration.v2.schema.json +138 -0
  201. package/schemas/decision/SdlcScreeningRelease.v1.schema.json +654 -0
  202. package/schemas/decision/SdlcScreeningRelease.v2.schema.json +654 -0
  203. package/tools/agents/deploy-agents.mjs +1 -1
  204. package/tools/agents/providers/base.mjs +26 -7
  205. package/tools/agents/providers/factory.mjs +10 -2
  206. package/tools/agents/providers/muse-hooks.mjs +1 -29
  207. package/tools/agents/providers/muse.mjs +6 -3
@@ -0,0 +1,443 @@
1
+ import { heldoutDigest } from '../heldout/contract.js';
2
+ import { CalibrationRegistry, calibrationArtifactDigest } from '../calibration/registry.js';
3
+ import { evaluateBinaryCalibration, freezeQualificationSplit, wilsonScoreInterval } from '../qualification/quality.js';
4
+ import { D17_CALIBRATION } from './protocol.js';
5
+ import { D17_SLICES, d17TextOracle } from './corpus.js';
6
+ import { validateD17Artifact } from './artifacts.js';
7
+ export class D17CalibrationError extends Error {
8
+ reason;
9
+ constructor(reason) {
10
+ super(`D17 calibration refused (${reason})`);
11
+ this.reason = reason;
12
+ }
13
+ }
14
+ const refuse = (reason) => { throw new D17CalibrationError(reason); };
15
+ const MODEL = 'jev-1.13.0';
16
+ const DIGEST = /^sha256:[0-9a-f]{64}$/;
17
+ const time = (value) => typeof value === 'string' ? Date.parse(value) : Number.NaN;
18
+ export const D17_COMPATIBILITY_POLICY = Object.freeze({
19
+ unknown: 'defer', incompatible: 'fail', shadowRequired: 'shadow', unusableCalibration: 'require-approval'
20
+ });
21
+ /** The staged scope is preregistered; no other scope may fit, register or apply a D17 calibration. */
22
+ export function assertD17Staged(prepared) {
23
+ const analysis = prepared.analysis, plan = prepared.preregistration.calibration;
24
+ if (analysis.schemaVersion !== 'decision-d17-analysis/v2' || analysis.scope !== 'staged-calibrated'
25
+ || heldoutDigest(analysis.calibration) !== heldoutDigest(D17_CALIBRATION)
26
+ || plan.scope !== 'calibrated' || plan.allowedModes.join(',') !== 'staged'
27
+ || (plan.calibrationPhaseSplits ?? []).join(',') !== D17_CALIBRATION.phaseSplits.join(','))
28
+ refuse('staged-scope');
29
+ validateD17Artifact('analysisV2', analysis);
30
+ }
31
+ /** Native P(yes) from the terminal successful attempt of one request, or null when it has no usable distribution. */
32
+ export function d17NativeYes(attempts, rowId, requestId) {
33
+ const terminal = attempts.filter(attempt => attempt.rowId === rowId && attempt.requestId === requestId)
34
+ .sort((a, b) => a.ordinal - b.ordinal).at(-1)?.result;
35
+ const decision = terminal?.receipt?.spec.evaluations.q0;
36
+ const uncertainty = decision?.spec.uncertainty;
37
+ if (terminal?.disposition !== 'success' || terminal.servedModel !== MODEL || decision?.spec.status !== 'success'
38
+ || uncertainty?.source !== 'provider' || uncertainty.profile !== 'typesafe-distribution-v1' || !uncertainty.distribution)
39
+ return null;
40
+ const distribution = uncertainty.distribution;
41
+ if (Object.keys(distribution).sort().join(',') !== 'no,yes' || !['yes', 'no'].includes(String(decision.spec.value)))
42
+ refuse('native-distribution');
43
+ const yes = distribution.yes;
44
+ if (typeof yes !== 'number' || !Number.isFinite(yes) || yes < 0 || yes > 1)
45
+ refuse('native-distribution');
46
+ return yes;
47
+ }
48
+ /** Attempts grouped by row, so per-row reads stay linear in the journal size. */
49
+ export function d17AttemptsByRow(attempts) {
50
+ const grouped = new Map();
51
+ for (const attempt of attempts)
52
+ grouped.set(attempt.rowId, [...(grouped.get(attempt.rowId) ?? []), attempt]);
53
+ return grouped;
54
+ }
55
+ /** Raw inputs of both calibrators for one row; null when a required call is unusable. */
56
+ export function d17RawProbabilities(attempts, row) {
57
+ const champion = d17NativeYes(attempts, row.id, 'champion');
58
+ const members = D17_CALIBRATION.calibrators.aggregate.requests.map(request => d17NativeYes(attempts, row.id, request));
59
+ return { champion, members, mean: members.every(value => value !== null) ? members.reduce((sum, n) => sum + n, 0) / members.length : null };
60
+ }
61
+ const laplace = (yes, n) => (yes + 1) / (n + 2);
62
+ function merge(a, b) {
63
+ const n = a.n + b.n, yes = a.yes + b.yes;
64
+ return { lower: Math.min(a.lower, b.lower), upper: Math.max(a.upper, b.upper), n, yes, probability: laplace(yes, n) };
65
+ }
66
+ /** Deterministic isotonic fit: PAV on raw frequencies, minimum block support, then monotone Laplace smoothing. */
67
+ export function fitD17Isotonic(samples, minimumBlockN = D17_CALIBRATION.minimumBlockN) {
68
+ if (!samples.length || samples.some(item => !Number.isFinite(item.score) || item.score < 0 || item.score > 1 || ![0, 1].includes(item.label))) {
69
+ refuse('calibration-samples');
70
+ }
71
+ const sorted = [...samples].sort((a, b) => a.score - b.score);
72
+ const blocks = [];
73
+ for (const item of sorted) {
74
+ const last = blocks.at(-1);
75
+ if (last && last.lower === item.score) {
76
+ last.n++;
77
+ last.yes += item.label;
78
+ last.probability = laplace(last.yes, last.n);
79
+ }
80
+ else
81
+ blocks.push({ lower: item.score, upper: item.score, n: 1, yes: item.label, probability: laplace(item.label, 1) });
82
+ }
83
+ // Pool adjacent violators on raw frequencies (cross-multiplied to stay exact).
84
+ const pooled = [];
85
+ for (const block of blocks) {
86
+ pooled.push({ ...block });
87
+ while (pooled.length > 1 && pooled.at(-2).yes * pooled.at(-1).n > pooled.at(-1).yes * pooled.at(-2).n) {
88
+ const right = pooled.pop(), left = pooled.pop();
89
+ pooled.push(merge(left, right));
90
+ }
91
+ }
92
+ // Minimum support: a sparse block joins its right neighbour (the last block joins its left one).
93
+ for (let i = pooled.findIndex(block => block.n < minimumBlockN); i !== -1 && pooled.length > 1; i = pooled.findIndex(block => block.n < minimumBlockN)) {
94
+ const j = i < pooled.length - 1 ? i + 1 : i - 1, [a, b] = [Math.min(i, j), Math.max(i, j)];
95
+ pooled.splice(a, 2, merge(pooled[a], pooled[b]));
96
+ }
97
+ // Laplace smoothing can reorder blocks with different support; pool again on the smoothed values.
98
+ for (let i = 0; i < pooled.length - 1;) {
99
+ if (pooled[i].probability > pooled[i + 1].probability) {
100
+ pooled.splice(i, 2, merge(pooled[i], pooled[i + 1]));
101
+ i = Math.max(0, i - 1);
102
+ }
103
+ else
104
+ i++;
105
+ }
106
+ return pooled;
107
+ }
108
+ /** Step-function application: the last block whose lower score is at or below the input, else the first block. */
109
+ export function applyD17Mapping(mapping, score) {
110
+ if (!Number.isFinite(score) || score < 0 || score > 1 || !mapping.blocks.length)
111
+ refuse('calibration-input');
112
+ let selected = mapping.blocks[0];
113
+ for (const block of mapping.blocks)
114
+ if (block.lower <= score)
115
+ selected = block;
116
+ return selected.probability;
117
+ }
118
+ function validateMapping(mapping, role, prepared) {
119
+ try {
120
+ validateD17Artifact('calibration', mapping);
121
+ }
122
+ catch {
123
+ refuse('mapping-schema');
124
+ }
125
+ const value = mapping, calibrator = D17_CALIBRATION.calibrators[role];
126
+ if (value.schemaVersion !== 'decision-d17-calibration-mapping/v1' || value.role !== role || value.calibrator.id !== calibrator.id
127
+ || value.calibrator.version !== calibrator.version || value.method !== D17_CALIBRATION.method
128
+ || value.splitDigest !== prepared.splitManifest.splits.calibration.digest
129
+ || value.definitionDigest !== heldoutDigest(prepared.corpus.definitions)
130
+ || value.blocks.reduce((sum, block) => sum + block.n, 0) !== value.n
131
+ || value.blocks.some((block, i) => block.n < D17_CALIBRATION.minimumBlockN && value.blocks.length > 1
132
+ || block.probability !== laplace(block.yes, block.n) || block.yes > block.n || block.lower > block.upper
133
+ || i > 0 && (block.lower <= value.blocks[i - 1].upper || block.probability < value.blocks[i - 1].probability))
134
+ || value.qualification.method !== D17_CALIBRATION.qualificationMetrics.method
135
+ || value.qualification.folds !== D17_CALIBRATION.qualificationMetrics.folds)
136
+ refuse('mapping-pin');
137
+ }
138
+ /** Library fitting accepts only attempts pinned to this exact D17 study, corpus and preregistration. */
139
+ function assertStudyAttempts(prepared, attempts) {
140
+ const corpusDigest = heldoutDigest(prepared.corpus), preregistrationDigest = heldoutDigest(prepared.preregistration);
141
+ if (attempts.some(attempt => attempt.study !== 'D17' || attempt.corpusDigest !== corpusDigest
142
+ || attempt.preregistrationDigest !== preregistrationDigest))
143
+ refuse('attempt-membership');
144
+ }
145
+ /** Training samples for one calibrator from the given rows. */
146
+ function samplesFor(role, rows, grouped, prepared) {
147
+ const label = (row) => Number(prepared.gold.labels[row.id] === 'yes');
148
+ return rows.flatMap(row => {
149
+ const raw = d17RawProbabilities(grouped.get(row.id) ?? [], row);
150
+ if (role === 'aggregate')
151
+ return raw.mean === null ? [] : [{ score: raw.mean, label: label(row) }];
152
+ return [raw.champion, ...raw.members].filter((value) => value !== null).map(score => ({ score, label: label(row) }));
153
+ });
154
+ }
155
+ /** Deterministic slice-stratified folds: calibration rows in record-ID order within each slice, index modulo folds. */
156
+ export function d17CalibrationFolds(rows, folds = D17_CALIBRATION.qualificationMetrics.folds) {
157
+ const assigned = new Map();
158
+ for (const slice of D17_SLICES) {
159
+ rows.filter(row => row.slice === slice).map(row => row.id).sort().forEach((id, index) => assigned.set(id, index % folds));
160
+ }
161
+ return assigned;
162
+ }
163
+ /**
164
+ * Calibrated quality of one calibrator over the calibration split. `probability(row, score)` supplies the calibrated
165
+ * value for a row's raw score: the full-split mapping (in-sample) or the mapping fitted without the row's fold (out-of-fold).
166
+ */
167
+ function calibrationMetrics(prepared, grouped, role, probability) {
168
+ const rows = prepared.corpus.rows.filter(row => row.split === D17_CALIBRATION.fitSplit);
169
+ const splits = ['tuning', 'calibration', 'test'].map(split => freezeQualificationSplit(split, prepared.corpus.rows.filter(row => row.split === split).map(row => row.id)));
170
+ let observed = 0;
171
+ const perSlice = new Map();
172
+ const samples = rows.map(row => {
173
+ const rowAttempts = grouped.get(row.id) ?? [];
174
+ const raw = d17RawProbabilities(rowAttempts, row), score = role === 'member' ? raw.champion : raw.mean;
175
+ const requests = role === 'member' ? ['champion'] : [...D17_CALIBRATION.calibrators.aggregate.requests];
176
+ const own = rowAttempts.filter(attempt => requests.includes(attempt.requestId));
177
+ const known = (key) => own.some(a => a.result?.[key] == null) || !own.length
178
+ ? null : own.reduce((sum, a) => sum + a.result[key], 0);
179
+ if (score !== null) {
180
+ observed++;
181
+ perSlice.set(row.slice, (perSlice.get(row.slice) ?? 0) + 1);
182
+ }
183
+ const p = score === null ? 0.5 : probability(row, score);
184
+ return { id: row.id, slice: row.slice, label: Number(prepared.gold.labels[row.id] === 'yes'), probability: p,
185
+ accepted: score !== null && p !== 0.5, latencyMs: own.reduce((sum, a) => sum + (a.result?.latencyMs ?? 0), 0),
186
+ inputTokens: known('inputTokens'), outputTokens: known('outputTokens'), costUsd: known('providerCostUsd'),
187
+ calls: own.length, retries: own.filter(a => a.ordinal > 1).length, fallbacks: 0 };
188
+ });
189
+ const measured = evaluateBinaryCalibration(splits, samples);
190
+ const selectiveRisk = measured.overall.selectiveRisk ?? refuse('calibration-metrics-unknown');
191
+ const level = Math.round(D17_CALIBRATION.profile.confidenceInterval.level * 10000);
192
+ const interval = (events, n) => {
193
+ const [lower, upper] = wilsonScoreInterval({ events, n, levelBps: level });
194
+ return { lower, upper };
195
+ };
196
+ const accepted = samples.filter(sample => sample.accepted);
197
+ const errors = accepted.filter(sample => (sample.probability >= 0.5 ? 1 : 0) !== sample.label).length;
198
+ return { totalSamples: observed, perSliceSamples: Math.min(...D17_SLICES.map(slice => perSlice.get(slice) ?? 0)),
199
+ calibrationError: measured.overall.expectedCalibrationError, selectiveRisk,
200
+ confidenceIntervals: { selectiveRisk: interval(errors, accepted.length), coverage: interval(accepted.length, samples.length) } };
201
+ }
202
+ /**
203
+ * Fits both preregistered calibrators from calibration-split rows only. Tuning and test rows never contribute. The final
204
+ * mapping uses the full calibration split; its qualification metrics are slice-stratified 5-fold out-of-fold, so a
205
+ * calibrator is gated on rows it was not fitted on (in-sample metrics are recorded alongside, for comparison only).
206
+ */
207
+ export function fitD17Calibration(prepared, attempts) {
208
+ assertD17Staged(prepared);
209
+ assertStudyAttempts(prepared, attempts);
210
+ const rows = prepared.corpus.rows.filter(row => row.split === D17_CALIBRATION.fitSplit);
211
+ const members = new Set(rows.map(row => row.id));
212
+ const used = attempts.filter(attempt => members.has(attempt.rowId)).map(attempt => heldoutDigest(attempt)).sort();
213
+ const grouped = d17AttemptsByRow(attempts.filter(attempt => members.has(attempt.rowId)));
214
+ const observedRows = (role) => rows.filter(row => samplesFor(role, [row], grouped, prepared).length).length;
215
+ const { minimumTotalSamples } = D17_CALIBRATION.profile;
216
+ if (observedRows('member') < minimumTotalSamples || observedRows('aggregate') < minimumTotalSamples)
217
+ refuse('calibration-support');
218
+ const folds = d17CalibrationFolds(rows), { qualificationMetrics } = D17_CALIBRATION;
219
+ const base = { method: D17_CALIBRATION.method, model: MODEL, splitDigest: prepared.splitManifest.splits.calibration.digest,
220
+ definitionDigest: heldoutDigest(prepared.corpus.definitions), evidenceDigest: heldoutDigest(used) };
221
+ const build = (role) => {
222
+ const samples = samplesFor(role, rows, grouped, prepared), blocks = fitD17Isotonic(samples);
223
+ const foldBlocks = Array.from({ length: qualificationMetrics.folds }, (_, k) => fitD17Isotonic(samplesFor(role, rows.filter(row => folds.get(row.id) !== k), grouped, prepared)));
224
+ const mapping = { schemaVersion: 'decision-d17-calibration-mapping/v1', role,
225
+ calibrator: { id: D17_CALIBRATION.calibrators[role].id, version: '1' }, ...base, n: samples.length, rows: observedRows(role), blocks,
226
+ qualification: { method: qualificationMetrics.method, folds: qualificationMetrics.folds,
227
+ outOfFold: calibrationMetrics(prepared, grouped, role, (row, score) => applyD17Mapping({ blocks: foldBlocks[folds.get(row.id)] }, score)),
228
+ inSample: calibrationMetrics(prepared, grouped, role, (_row, score) => applyD17Mapping({ blocks }, score)) } };
229
+ validateMapping(mapping, role, prepared);
230
+ return mapping;
231
+ };
232
+ return { member: build('member'), aggregate: build('aggregate') };
233
+ }
234
+ function artifactFor(role, mapping, sealedAt, seal) {
235
+ const mappingDigest = heldoutDigest(mapping), calibrator = D17_CALIBRATION.calibrators[role];
236
+ const draft = { schemaVersion: 'decision-calibration-artifact/v1',
237
+ id: `d17-${role}-${mappingDigest.slice(7, 23)}`,
238
+ identity: { provider: 'jev', backend: 'api', actualModel: MODEL, primitive: 'choice', definitionDigest: mapping.definitionDigest,
239
+ adapterVersion: '1.0.0', dataset: { id: 'd17-calibration', hash: mapping.splitDigest },
240
+ slice: { id: 'd17-all-calibration-slices', hash: mapping.splitDigest },
241
+ calibrator: { id: calibrator.id, version: calibrator.version, parametersDigest: mappingDigest } },
242
+ splitProvenance: { id: 'd17-calibration', hash: mapping.splitDigest, holdoutAccessedAt: null },
243
+ profile: structuredClone(D17_CALIBRATION.profile),
244
+ metrics: structuredClone(mapping.qualification.outOfFold), effectiveAt: sealedAt,
245
+ limitations: [
246
+ `Synthetic D17 calibration-split fit (${role === 'member' ? 'single fresh call' : 'mean of three ensemble members'}); no production authority.`,
247
+ 'Metrics are slice-stratified 5-fold out-of-fold on the calibration split; the deployed mapping is fitted on the full split. In-sample metrics are in the mapping, for comparison only.',
248
+ 'Decile ECE; selective risk counts calibrated non-tie answers. Wilson intervals describe binomial rates, not ECE.',
249
+ `Observation source: ${seal.source}; calibration evidence: ${mapping.evidenceDigest}.`,
250
+ `Sealed phase: ${seal.calibrationPhaseRecordDigest}; prior approval: ${seal.priorApprovalDigest}.`,
251
+ ], approval: { state: 'observed', reference: null } };
252
+ return { ...draft, digest: calibrationArtifactDigest(draft) };
253
+ }
254
+ function calibrationSet(prepared, seal, artifacts, mappings) {
255
+ const pin = (role) => ({ artifactId: artifacts[role].id, artifactDigest: artifacts[role].digest, mappingDigest: heldoutDigest(mappings[role]) });
256
+ const set = { schemaVersion: 'decision-d17-calibration-set/v1', corpusDigest: heldoutDigest(prepared.corpus),
257
+ preregistrationDigest: heldoutDigest(prepared.preregistration), calibrationPhaseRecordDigest: seal.calibrationPhaseRecordDigest,
258
+ priorApprovalDigest: seal.priorApprovalDigest, member: pin('member'), aggregate: pin('aggregate') };
259
+ validateD17Artifact('calibration', set);
260
+ return set;
261
+ }
262
+ /**
263
+ * The 40 development assessments must be complete, unambiguous and agree with both the generator gold and the
264
+ * independent text oracle. `before` bounds every development review time; `testStagesBlank` refuses any test audit
265
+ * recorded before the test phase (a blind test audit belongs after test collection).
266
+ */
267
+ export function validateD17DevelopmentReview(prepared, review, trustedDigest, options = {}) {
268
+ try {
269
+ validateD17Artifact('review', review);
270
+ }
271
+ catch {
272
+ refuse('development-review');
273
+ }
274
+ const value = review, template = prepared.reviewTemplate;
275
+ if (typeof trustedDigest !== 'string' || heldoutDigest(value) !== trustedDigest || !value.reviewer?.trim() || !value.preregistrationReview?.trim()
276
+ || value.assessments.length !== template.assessments.length)
277
+ refuse('development-review');
278
+ const limit = options.before === undefined ? null : time(options.before);
279
+ value.assessments.forEach((item, i) => {
280
+ const expected = template.assessments[i];
281
+ if (item.assessmentId !== expected.assessmentId || item.rowId !== expected.rowId || item.stage !== expected.stage
282
+ || item.slice !== expected.slice || item.payload !== expected.payload || item.inputDigest !== expected.inputDigest)
283
+ refuse('development-review');
284
+ if (item.stage === 'development') {
285
+ const gold = prepared.gold.labels[item.rowId];
286
+ if (item.goldAmbiguousOrIncorrect !== false)
287
+ refuse('development-gold-invalid');
288
+ if (!Number.isFinite(time(item.reviewedAt)) || !item.rationale?.trim() || item.goldAuditLabel !== gold
289
+ || d17TextOracle(String(item.payload)) !== gold)
290
+ refuse('development-review-incomplete');
291
+ if (limit !== null && !(time(item.reviewedAt) < limit))
292
+ refuse('development-review-time');
293
+ }
294
+ else if (options.testStagesBlank && [item.reviewedAt, item.goldAuditLabel, item.goldAmbiguousOrIncorrect,
295
+ item.blindedResultAudit, item.rationale].some(field => field !== null))
296
+ refuse('test-review-before-test-phase');
297
+ });
298
+ return value;
299
+ }
300
+ /**
301
+ * Approvals carry no timestamp, so ordering is proved by digest: the calibration-phase approval reference must cite the
302
+ * completed development review digest, which therefore existed before the approval that authorized any spend.
303
+ */
304
+ export function assertD17ReviewBoundToApproval(approval, trustedDevelopmentReviewDigest) {
305
+ if (typeof trustedDevelopmentReviewDigest !== 'string' || !DIGEST.test(trustedDevelopmentReviewDigest)
306
+ || typeof approval?.approvalReference !== 'string' || !approval.approvalReference.includes(trustedDevelopmentReviewDigest)) {
307
+ refuse('development-review-not-bound');
308
+ }
309
+ }
310
+ /** A staged prepared study that reproduces the sealed bundle exactly. */
311
+ function assertSealedStudy(sealed, prepared, trustedApprovalDigest, trustedSealDigest) {
312
+ assertD17Staged(prepared);
313
+ const calibration = sealed.bundle.approval.calibration;
314
+ if (heldoutDigest(prepared.corpus) !== heldoutDigest(sealed.bundle.corpus)
315
+ || heldoutDigest(prepared.preregistration) !== heldoutDigest(sealed.bundle.preregistration))
316
+ refuse('frozen-study-mismatch');
317
+ if (calibration.mode !== 'staged' || calibration.phase !== 'calibration' || heldoutDigest(sealed.bundle.approval) !== trustedApprovalDigest
318
+ || heldoutDigest(sealed.record) !== trustedSealDigest || sealed.record.approvalDigest !== trustedApprovalDigest)
319
+ refuse('calibration-phase-seal');
320
+ }
321
+ /** Fit both calibrators from a verified seal and emit observed artifacts plus unapproved operator forms. */
322
+ export function d17CalibrationHandoffFromSealed(input) {
323
+ const { sealed, prepared, trustedApprovalDigest, trustedCalibrationPhaseRecordDigest: sealDigest } = input;
324
+ assertSealedStudy(sealed, prepared, trustedApprovalDigest, sealDigest);
325
+ validateD17DevelopmentReview(prepared, input.developmentReview, input.trustedDevelopmentReviewDigest, { before: sealed.record.sealedAt, testStagesBlank: true });
326
+ assertD17ReviewBoundToApproval(sealed.bundle.approval, input.trustedDevelopmentReviewDigest);
327
+ const members = new Set(prepared.corpus.rows.filter(row => row.split === D17_CALIBRATION.fitSplit).map(row => row.id));
328
+ const attempts = sealed.attempts.filter(attempt => members.has(attempt.rowId));
329
+ const mappings = fitD17Calibration(prepared, attempts);
330
+ const seal = { calibrationPhaseRecordDigest: sealDigest, priorApprovalDigest: trustedApprovalDigest, source: sealed.source };
331
+ const artifacts = { member: artifactFor('member', mappings.member, sealed.record.sealedAt, seal),
332
+ aggregate: artifactFor('aggregate', mappings.aggregate, sealed.record.sealedAt, seal) };
333
+ const set = calibrationSet(prepared, seal, artifacts, mappings);
334
+ const review = { schemaVersion: 'decision-d17-calibration-review/v1', approved: null, reviewer: null,
335
+ calibrationSetDigest: heldoutDigest(set), memberArtifactDigest: artifacts.member.digest,
336
+ aggregateArtifactDigest: artifacts.aggregate.digest, approvalReference: null, reviewedAt: null };
337
+ validateD17Artifact('calibration', review);
338
+ return { mappings, artifacts, set, calibrationSetDigest: heldoutDigest(set), reviewTemplate: review,
339
+ approval: testApprovalTemplate(sealed.bundle, heldoutDigest(set), sealDigest, trustedApprovalDigest) };
340
+ }
341
+ function testApprovalTemplate(bundle, setDigest, sealDigest, priorApprovalDigest) {
342
+ const prior = bundle.approval;
343
+ // Unapproved form: operator attestations stay null; the shared budget and original spend floors are carried.
344
+ return { ...structuredClone(prior), approved: false, runId: null, approvalReference: null, exactHeadCi: null, sourceCommit: null,
345
+ calibration: { mode: 'staged', phase: 'test', calibrationArtifactDigest: setDigest, calibrationPhaseRecordDigest: sealDigest, priorApprovalDigest } };
346
+ }
347
+ /** D09 qualification at `at`: approved, compatible, unexpired, within the frozen profile and its selective-risk interval. */
348
+ export function qualifyD17Calibration(artifact, at, registry = new CalibrationRegistry()) {
349
+ if (heldoutDigest(artifact.profile) !== heldoutDigest(D17_CALIBRATION.profile))
350
+ refuse('calibration-profile');
351
+ if (!registry.artifactHistory().some(item => item.digest === artifact.digest)) {
352
+ try {
353
+ registry.registerArtifact(artifact);
354
+ }
355
+ catch {
356
+ refuse('calibration-artifact');
357
+ }
358
+ }
359
+ let compatibility;
360
+ try {
361
+ compatibility = registry.resolve({ runId: `d17-qualification-${artifact.digest.slice(7)}-${at}`, requestedAlias: artifact.identity.actualModel,
362
+ actualIdentity: artifact.identity, calibrationArtifactId: artifact.id, at }, D17_COMPATIBILITY_POLICY);
363
+ }
364
+ catch {
365
+ return refuse('calibration-unqualified');
366
+ }
367
+ const risk = artifact.metrics.confidenceIntervals.selectiveRisk;
368
+ if (compatibility.action !== 'allow' || compatibility.artifactDigest !== artifact.digest || !risk
369
+ || risk.upper > artifact.profile.maximumSelectiveRisk)
370
+ refuse('calibration-unqualified');
371
+ return compatibility;
372
+ }
373
+ /** Operator review approves both observed artifacts; the approved pair forms a new set that binds the test phase. */
374
+ export function registerD17CalibrationFromHandoff(handoff, review, trustedReviewDigest) {
375
+ try {
376
+ validateD17Artifact('calibration', review);
377
+ }
378
+ catch {
379
+ refuse('calibration-review');
380
+ }
381
+ const value = review;
382
+ if (value?.schemaVersion !== 'decision-d17-calibration-review/v1' || value.approved !== true || typeof trustedReviewDigest !== 'string'
383
+ || heldoutDigest(value) !== trustedReviewDigest || value.calibrationSetDigest !== handoff.calibrationSetDigest
384
+ || value.memberArtifactDigest !== handoff.artifacts.member.digest || value.aggregateArtifactDigest !== handoff.artifacts.aggregate.digest
385
+ || !(time(value.reviewedAt) >= time(handoff.artifacts.member.effectiveAt))
386
+ // The reviewer must be the reviewer the approvals name; the identity is bound into each approved artifact below.
387
+ || value.reviewer !== handoff.approval.reviewer)
388
+ refuse('calibration-review');
389
+ const approve = (artifact) => {
390
+ const { digest: _observed, ...payload } = structuredClone(artifact);
391
+ payload.approval = { state: 'approved', reference: `reviewer=${value.reviewer}; ${value.approvalReference}` };
392
+ return { ...payload, digest: calibrationArtifactDigest(payload) };
393
+ };
394
+ const artifacts = { member: approve(handoff.artifacts.member), aggregate: approve(handoff.artifacts.aggregate) };
395
+ const compatibility = { member: qualifyD17Calibration(artifacts.member, value.reviewedAt),
396
+ aggregate: qualifyD17Calibration(artifacts.aggregate, value.reviewedAt) };
397
+ const set = { ...handoff.set,
398
+ member: { ...handoff.set.member, artifactId: artifacts.member.id, artifactDigest: artifacts.member.digest },
399
+ aggregate: { ...handoff.set.aggregate, artifactId: artifacts.aggregate.id, artifactDigest: artifacts.aggregate.digest } };
400
+ validateD17Artifact('calibration', set);
401
+ const setDigest = heldoutDigest(set);
402
+ return { ...handoff, artifacts, compatibility, set, calibrationSetDigest: setDigest,
403
+ approval: { ...handoff.approval, calibration: { ...handoff.approval.calibration, calibrationArtifactDigest: setDigest } } };
404
+ }
405
+ /**
406
+ * Test-phase verification. Nothing in the registered files is trusted: the calibration set is re-derived from the sealed
407
+ * calibration phase (re-fit, out-of-fold metrics, operator review, approval), and the registered set, artifacts and
408
+ * mappings must be canonically identical to that derivation and to the approved binding. The scoring clock cannot
409
+ * precede the calibration review or the recorded first test access, and both artifacts must qualify at that clock.
410
+ */
411
+ export function verifyD17CalibrationSet(prepared, approvedCalibration, context) {
412
+ assertD17Staged(prepared);
413
+ const approved = approvedCalibration;
414
+ if (approved?.mode !== 'staged' || approved.phase !== 'test' || !DIGEST.test(approved.calibrationArtifactDigest)
415
+ || !DIGEST.test(approved.calibrationPhaseRecordDigest) || !DIGEST.test(approved.priorApprovalDigest))
416
+ refuse('approved-calibration');
417
+ if (!context || !Number.isSafeInteger(context.nowEpochMs) || !context.sealed || !context.registered)
418
+ refuse('calibration-context');
419
+ if (approved.calibrationArtifactDigest !== context.trustedCalibrationSetDigest)
420
+ refuse('calibration-set');
421
+ const derived = registerD17CalibrationFromHandoff(d17CalibrationHandoffFromSealed({ sealed: context.sealed, prepared,
422
+ trustedApprovalDigest: approved.priorApprovalDigest, trustedCalibrationPhaseRecordDigest: approved.calibrationPhaseRecordDigest,
423
+ developmentReview: context.developmentReview, trustedDevelopmentReviewDigest: context.trustedDevelopmentReviewDigest }), context.calibrationReview, context.trustedCalibrationReviewDigest);
424
+ if (derived.calibrationSetDigest !== context.trustedCalibrationSetDigest)
425
+ refuse('calibration-set');
426
+ const same = (a, b) => heldoutDigest(a) === heldoutDigest(b);
427
+ const { registered } = context;
428
+ if (!same(registered.set, derived.set) || ['member', 'aggregate'].some(role => !same(registered.artifacts?.[role], derived.artifacts[role]) || !same(registered.mappings?.[role], derived.mappings[role]))) {
429
+ refuse('calibration-registered-mismatch');
430
+ }
431
+ const review = context.calibrationReview;
432
+ if (!(context.nowEpochMs >= time(review.reviewedAt)))
433
+ refuse('scoring-clock');
434
+ if (context.testPhaseAccessAt !== null && !(context.nowEpochMs >= time(context.testPhaseAccessAt)))
435
+ refuse('scoring-clock');
436
+ const at = new Date(context.nowEpochMs).toISOString(), registry = new CalibrationRegistry();
437
+ const resolved = (role) => ({ artifact: derived.artifacts[role], mapping: derived.mappings[role],
438
+ compatibility: qualifyD17Calibration(derived.artifacts[role], at, registry) });
439
+ const member = resolved('member'), aggregate = resolved('aggregate');
440
+ return { set: derived.set, setDigest: derived.calibrationSetDigest, member, aggregate, qualifiedAt: at,
441
+ developmentReviewDigest: context.trustedDevelopmentReviewDigest, calibrationReviewDigest: context.trustedCalibrationReviewDigest };
442
+ }
443
+ //# sourceMappingURL=calibration.js.map