@tangle-network/agent-eval 0.144.11 → 0.144.13

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 (378) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/agent-profile-B9_GGsG8.d.ts +84 -0
  3. package/dist/agent-profile-B9_GGsG8.d.ts.map +1 -0
  4. package/dist/{agent-profile-cell-BOP-iA9Q.d.ts → agent-profile-cell-BkcRDikH.d.ts} +2 -2
  5. package/dist/{agent-profile-cell-BOP-iA9Q.d.ts.map → agent-profile-cell-BkcRDikH.d.ts.map} +1 -1
  6. package/dist/analyst/index.d.ts +134 -16
  7. package/dist/analyst/index.d.ts.map +1 -1
  8. package/dist/analyst/index.js +364 -10
  9. package/dist/analyst/index.js.map +1 -1
  10. package/dist/backend-integrity-CsVin_Wb.d.ts +280 -0
  11. package/dist/backend-integrity-CsVin_Wb.d.ts.map +1 -0
  12. package/dist/{baseline-C-GocmIW.js → baseline-BhPRQBVn.js} +3 -2
  13. package/dist/{baseline-C-GocmIW.js.map → baseline-BhPRQBVn.js.map} +1 -1
  14. package/dist/{benchmark-CWeqGl7x.js → benchmark-BhT16ep9.js} +2 -2
  15. package/dist/{benchmark-CWeqGl7x.js.map → benchmark-BhT16ep9.js.map} +1 -1
  16. package/dist/{agent-profile-DPi7IZg7.d.ts → benchmark-Ceoan7vk.d.ts} +4 -91
  17. package/dist/benchmark-Ceoan7vk.d.ts.map +1 -0
  18. package/dist/{benchmark-command-BteMFN62.js → benchmark-command-CY6cbhOB.js} +36 -79
  19. package/dist/benchmark-command-CY6cbhOB.js.map +1 -0
  20. package/dist/benchmarks/index.d.ts +244 -2
  21. package/dist/benchmarks/index.d.ts.map +1 -0
  22. package/dist/benchmarks/index.js +733 -1
  23. package/dist/benchmarks/index.js.map +1 -0
  24. package/dist/builder-eval/index.d.ts +23 -2
  25. package/dist/builder-eval/index.d.ts.map +1 -1
  26. package/dist/builder-eval/index.js +227 -3
  27. package/dist/builder-eval/index.js.map +1 -1
  28. package/dist/campaign/index.d.ts +10 -8
  29. package/dist/campaign/index.js +9 -6
  30. package/dist/{campaign-C2TTzQII.js → campaign-BYjBAypg.js} +21 -634
  31. package/dist/campaign-BYjBAypg.js.map +1 -0
  32. package/dist/{canonical-D011XM8r.js → canonical-D-XsTQ6_.js} +2 -2
  33. package/dist/{canonical-D011XM8r.js.map → canonical-D-XsTQ6_.js.map} +1 -1
  34. package/dist/{index-CQTZ-4XN.d.ts → capture-fetch-DDvpjVRU.d.ts} +3 -3
  35. package/dist/capture-fetch-DDvpjVRU.d.ts.map +1 -0
  36. package/dist/{default-registry-BmktKy8r.js → chat-client-Bp4Ebuuc.js} +1276 -1276
  37. package/dist/chat-client-Bp4Ebuuc.js.map +1 -0
  38. package/dist/cli.js +2 -2
  39. package/dist/{client-C9gzZE59.d.ts → client-KUbGulm_.d.ts} +4 -4
  40. package/dist/{client-C9gzZE59.d.ts.map → client-KUbGulm_.d.ts.map} +1 -1
  41. package/dist/contract/index.d.ts +13 -390
  42. package/dist/contract/index.d.ts.map +1 -1
  43. package/dist/contract/index.js +18 -542
  44. package/dist/contract/index.js.map +1 -1
  45. package/dist/{cost-ledger-DMFxsLKr.js → cost-ledger-BSe92yAV.js} +3 -3
  46. package/dist/{cost-ledger-DMFxsLKr.js.map → cost-ledger-BSe92yAV.js.map} +1 -1
  47. package/dist/{cost-ledger-Bv_e8XHY.d.ts → cost-ledger-DbQdN3nO.d.ts} +2 -2
  48. package/dist/{cost-ledger-Bv_e8XHY.d.ts.map → cost-ledger-DbQdN3nO.d.ts.map} +1 -1
  49. package/dist/{counterfactual-CxmxAONP.d.ts → counterfactual--bpysZF0.d.ts} +2 -14
  50. package/dist/{counterfactual-CxmxAONP.d.ts.map → counterfactual--bpysZF0.d.ts.map} +1 -1
  51. package/dist/{counterfactual-CWPTrMH7.js → counterfactual-lDfCx0Uz.js} +3 -31
  52. package/dist/{counterfactual-CWPTrMH7.js.map → counterfactual-lDfCx0Uz.js.map} +1 -1
  53. package/dist/{dataset-C8xaLXdY.d.ts → dataset-CJjKqQfA.d.ts} +2 -8
  54. package/dist/dataset-CJjKqQfA.d.ts.map +1 -0
  55. package/dist/{default-registry-Bf8Woqmq.d.ts → default-registry-Di6HP6pG.d.ts} +7 -12
  56. package/dist/default-registry-Di6HP6pG.d.ts.map +1 -0
  57. package/dist/{analyze-runs-C30yljDJ.js → define-agent-eval-iqjT--ZZ.js} +542 -130
  58. package/dist/define-agent-eval-iqjT--ZZ.js.map +1 -0
  59. package/dist/define-agent-eval-sH24zBfM.d.ts +388 -0
  60. package/dist/define-agent-eval-sH24zBfM.d.ts.map +1 -0
  61. package/dist/descriptive-B5MwKfbf.js +144 -0
  62. package/dist/descriptive-B5MwKfbf.js.map +1 -0
  63. package/dist/{dspy-rlm-engine-DbTk4JdR.js → dspy-rlm-engine-B_qhSc21.js} +4 -4
  64. package/dist/{dspy-rlm-engine-DbTk4JdR.js.map → dspy-rlm-engine-B_qhSc21.js.map} +1 -1
  65. package/dist/effect-sizes-DiH8MGOH.js +82 -0
  66. package/dist/effect-sizes-DiH8MGOH.js.map +1 -0
  67. package/dist/{engine-3hL-XqwJ.d.ts → engine-otFpE2gF.d.ts} +10 -38
  68. package/dist/engine-otFpE2gF.d.ts.map +1 -0
  69. package/dist/{errors-CKPfb2aH.d.ts → errors-DEE6u6ot.d.ts} +2 -14
  70. package/dist/{errors-CKPfb2aH.d.ts.map → errors-DEE6u6ot.d.ts.map} +1 -1
  71. package/dist/{errors-D-LKuDhb.js → errors-Dngq5h35.js} +2 -8
  72. package/dist/{errors-D-LKuDhb.js.map → errors-Dngq5h35.js.map} +1 -1
  73. package/dist/{eval-campaign-B_7wcnav.js → eval-campaign-UB-usSQ2.js} +6 -6
  74. package/dist/{eval-campaign-B_7wcnav.js.map → eval-campaign-UB-usSQ2.js.map} +1 -1
  75. package/dist/{exact-types-DSFFpLLI.d.ts → exact-types-BH1twmAJ.d.ts} +2 -2
  76. package/dist/{exact-types-DSFFpLLI.d.ts.map → exact-types-BH1twmAJ.d.ts.map} +1 -1
  77. package/dist/experiment/index.d.ts +9 -6
  78. package/dist/experiment/index.d.ts.map +1 -1
  79. package/dist/experiment/index.js +11 -7
  80. package/dist/experiment/index.js.map +1 -1
  81. package/dist/experiment-tracker-C29gXM4B.js +269 -0
  82. package/dist/experiment-tracker-C29gXM4B.js.map +1 -0
  83. package/dist/{experiment-tracker-IMntXr6J.d.ts → experiment-tracker-DWHZBAYL.d.ts} +77 -121
  84. package/dist/experiment-tracker-DWHZBAYL.d.ts.map +1 -0
  85. package/dist/{external-optimizer-contracts-CZuJNcT5.d.ts → external-optimizer-contracts-lixrOZdX.d.ts} +3 -3
  86. package/dist/{external-optimizer-contracts-CZuJNcT5.d.ts.map → external-optimizer-contracts-lixrOZdX.d.ts.map} +1 -1
  87. package/dist/external-optimizer-process-BTiNB-RH.js +301 -0
  88. package/dist/external-optimizer-process-BTiNB-RH.js.map +1 -0
  89. package/dist/{single-run-lock-DFWHEB09.js → external-optimizer-subprocess-DrJ9hR8u.js} +144 -438
  90. package/dist/external-optimizer-subprocess-DrJ9hR8u.js.map +1 -0
  91. package/dist/extract-usage-BrQ8mCLX.js +155 -0
  92. package/dist/extract-usage-BrQ8mCLX.js.map +1 -0
  93. package/dist/{failure-cluster-CqcvCcdR.d.ts → failure-cluster-BLURuWG4.d.ts} +2 -3
  94. package/dist/failure-cluster-BLURuWG4.d.ts.map +1 -0
  95. package/dist/{feedback-trajectory-CacHpxsp.d.ts → feedback-trajectory-juOozjAc.d.ts} +5 -36
  96. package/dist/feedback-trajectory-juOozjAc.d.ts.map +1 -0
  97. package/dist/fuzz.d.ts +2 -2
  98. package/dist/fuzz.js +2 -2
  99. package/dist/hosted/index.d.ts +3 -3
  100. package/dist/{index-CvSN3IG1.d.ts → index-B8Ui1mr1.d.ts} +2 -2
  101. package/dist/{index-CvSN3IG1.d.ts.map → index-B8Ui1mr1.d.ts.map} +1 -1
  102. package/dist/{skill-usage-CO9OLRBx.d.ts → index-BWDrSVfw.d.ts} +11 -136
  103. package/dist/index-BWDrSVfw.d.ts.map +1 -0
  104. package/dist/index-Ba3YrbAL.d.ts +1 -0
  105. package/dist/{index-BnEuDAK2.d.ts → index-COQYtuRF.d.ts} +3 -3
  106. package/dist/{index-BnEuDAK2.d.ts.map → index-COQYtuRF.d.ts.map} +1 -1
  107. package/dist/{index-C3ssXVLv.d.ts → index-CvjYbU0D.d.ts} +2 -2
  108. package/dist/{index-C3ssXVLv.d.ts.map → index-CvjYbU0D.d.ts.map} +1 -1
  109. package/dist/{index-DPPGNJ_R.d.ts → index-DSmEylT9.d.ts} +17 -115
  110. package/dist/index-DSmEylT9.d.ts.map +1 -0
  111. package/dist/index.d.ts +2397 -5308
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +5914 -10496
  114. package/dist/index.js.map +1 -1
  115. package/dist/{insight-report-CgX_s0Ez.d.ts → insight-report-CRi-Ufrj.d.ts} +4 -4
  116. package/dist/{insight-report-CgX_s0Ez.d.ts.map → insight-report-CRi-Ufrj.d.ts.map} +1 -1
  117. package/dist/{integrity-MLzHOfV9.js → integrity-Cy9WHAtb.js} +2 -2
  118. package/dist/{integrity-MLzHOfV9.js.map → integrity-Cy9WHAtb.js.map} +1 -1
  119. package/dist/{integrity-DY6tIbl0.js → integrity-DysDBWDu.js} +2 -2
  120. package/dist/{integrity-DY6tIbl0.js.map → integrity-DysDBWDu.js.map} +1 -1
  121. package/dist/{integrity-BuqEKu-x.d.ts → integrity-OrcI9Nau.d.ts} +3 -3
  122. package/dist/{integrity-BuqEKu-x.d.ts.map → integrity-OrcI9Nau.d.ts.map} +1 -1
  123. package/dist/internal-BDHPCnjk.js +230 -0
  124. package/dist/internal-BDHPCnjk.js.map +1 -0
  125. package/dist/judge-calibration-C5CbMYce.d.ts +117 -0
  126. package/dist/judge-calibration-C5CbMYce.d.ts.map +1 -0
  127. package/dist/judge-calibration-DZkWrm5H.js +317 -0
  128. package/dist/judge-calibration-DZkWrm5H.js.map +1 -0
  129. package/dist/{kind-factory-B8-r8-y8.js → kind-factory-CPmSd58s.js} +208 -208
  130. package/dist/{kind-factory-B8-r8-y8.js.map → kind-factory-CPmSd58s.js.map} +1 -1
  131. package/dist/ledger-core/index.d.ts +1 -1
  132. package/dist/ledger-core/index.js +2 -2
  133. package/dist/{ledger-core-DXZIqu17.js → ledger-core-BmZt19oQ.js} +110 -110
  134. package/dist/{ledger-core-DXZIqu17.js.map → ledger-core-BmZt19oQ.js.map} +1 -1
  135. package/dist/{llm-client-DzvMUsS_.js → llm-client-d0-2TT1g.js} +4 -64
  136. package/dist/{llm-client-DzvMUsS_.js.map → llm-client-d0-2TT1g.js.map} +1 -1
  137. package/dist/{skillopt-optimization-method-CQdVeM8k.js → llm-judge-dZ8P6nGI.js} +3349 -5401
  138. package/dist/llm-judge-dZ8P6nGI.js.map +1 -0
  139. package/dist/matrix/index.d.ts +2 -2
  140. package/dist/meta-eval/index.d.ts +3 -3
  141. package/dist/meta-eval/index.js +3 -3
  142. package/dist/{metrics-C9YY1OcL.js → metrics-Cl0L1KUy.js} +2 -108
  143. package/dist/{metrics-C9YY1OcL.js.map → metrics-Cl0L1KUy.js.map} +1 -1
  144. package/dist/{mint-B2O60ACG.js → mint-Dj9Ww_3I.js} +4 -4
  145. package/dist/{mint-B2O60ACG.js.map → mint-Dj9Ww_3I.js.map} +1 -1
  146. package/dist/{multi-layer-verifier-DnAqwl0h.d.ts → multi-layer-verifier-DIguZc8Z.d.ts} +3 -3
  147. package/dist/{multi-layer-verifier-DnAqwl0h.d.ts.map → multi-layer-verifier-DIguZc8Z.d.ts.map} +1 -1
  148. package/dist/multiplicity-DIWHvysC.d.ts +43 -0
  149. package/dist/multiplicity-DIWHvysC.d.ts.map +1 -0
  150. package/dist/multishot/index.d.ts +3 -3
  151. package/dist/multishot/index.js +1 -1
  152. package/dist/openapi.json +1 -1
  153. package/dist/{opencode-sqlite-8r6WUfHc.js → opencode-sqlite-DJWAXLms.js} +2 -2
  154. package/dist/{opencode-sqlite-8r6WUfHc.js.map → opencode-sqlite-DJWAXLms.js.map} +1 -1
  155. package/dist/package-version-D7lQHt_-.js +34 -0
  156. package/dist/package-version-D7lQHt_-.js.map +1 -0
  157. package/dist/paired-arms-D-XRF_fy.js +1045 -0
  158. package/dist/paired-arms-D-XRF_fy.js.map +1 -0
  159. package/dist/paired-promotion-decision-CGzg0cI_.d.ts +251 -0
  160. package/dist/paired-promotion-decision-CGzg0cI_.d.ts.map +1 -0
  161. package/dist/paired-tests-BHIhYVdu.js +213 -0
  162. package/dist/paired-tests-BHIhYVdu.js.map +1 -0
  163. package/dist/pareto-BqNW3LJR.d.ts +117 -0
  164. package/dist/pareto-BqNW3LJR.d.ts.map +1 -0
  165. package/dist/pipelines/index.d.ts +3 -64
  166. package/dist/pipelines/index.d.ts.map +1 -1
  167. package/dist/pipelines/index.js +4 -284
  168. package/dist/pipelines/index.js.map +1 -1
  169. package/dist/power-and-mde-CHIrXJll.js +195 -0
  170. package/dist/power-and-mde-CHIrXJll.js.map +1 -0
  171. package/dist/{promotion-policy-CrLrmys8.js → power-preflight-DEw-uC7q.js} +4 -184
  172. package/dist/power-preflight-DEw-uC7q.js.map +1 -0
  173. package/dist/pre-registration-CZwSFQS4.d.ts +577 -0
  174. package/dist/pre-registration-CZwSFQS4.d.ts.map +1 -0
  175. package/dist/{prime-protocol-BfSalTfR.js → prime-protocol-6tZTVsWm.js} +72 -21
  176. package/dist/prime-protocol-6tZTVsWm.js.map +1 -0
  177. package/dist/produced-state-DU79a81m.js +586 -0
  178. package/dist/produced-state-DU79a81m.js.map +1 -0
  179. package/dist/profile-cell.d.ts +1 -1
  180. package/dist/profile-cell.js +1 -1
  181. package/dist/promotion-policy-D0nPhkSy.d.ts +134 -0
  182. package/dist/promotion-policy-D0nPhkSy.d.ts.map +1 -0
  183. package/dist/promotion-policy-xzA40Evo.js +186 -0
  184. package/dist/promotion-policy-xzA40Evo.js.map +1 -0
  185. package/dist/{skillopt-optimization-method-USDKhxSA.d.ts → provenance-CCdxgLDT.d.ts} +57 -478
  186. package/dist/provenance-CCdxgLDT.d.ts.map +1 -0
  187. package/dist/registry-oJeeI4-a.d.ts +178 -0
  188. package/dist/registry-oJeeI4-a.d.ts.map +1 -0
  189. package/dist/{release-report-DA2BCu5a.d.ts → release-confidence-BFRE5WSp.d.ts} +180 -112
  190. package/dist/release-confidence-BFRE5WSp.d.ts.map +1 -0
  191. package/dist/{release-report-BUYmoKo2.js → release-confidence-CxDuiAev.js} +125 -217
  192. package/dist/release-confidence-CxDuiAev.js.map +1 -0
  193. package/dist/reporting.d.ts +6 -5
  194. package/dist/reporting.js +7 -5
  195. package/dist/{researcher-BchpD55R.d.ts → researcher-DaN4GST-.d.ts} +7 -40
  196. package/dist/{researcher-BchpD55R.d.ts.map → researcher-DaN4GST-.d.ts.map} +1 -1
  197. package/dist/{reward-hacking-BDToousL.js → reward-hacking-DNgjilrV.js} +3 -3
  198. package/dist/reward-hacking-DNgjilrV.js.map +1 -0
  199. package/dist/{reward-hacking-BI0OMAlo.d.ts → reward-hacking-DSSTuI9r.d.ts} +5 -5
  200. package/dist/{reward-hacking-BI0OMAlo.d.ts.map → reward-hacking-DSSTuI9r.d.ts.map} +1 -1
  201. package/dist/rl.d.ts +7 -7
  202. package/dist/rl.js +11 -10
  203. package/dist/rl.js.map +1 -1
  204. package/dist/rollout/index.d.ts +2 -2
  205. package/dist/rollout/index.js +4 -4
  206. package/dist/{rollout-DRrksrcV.js → rollout-BWtw0I_6.js} +3 -3
  207. package/dist/{rollout-DRrksrcV.js.map → rollout-BWtw0I_6.js.map} +1 -1
  208. package/dist/{rubric-predictive-validity-DvkjPYCe.d.ts → rubric-predictive-validity-BgxtKe4G.d.ts} +2 -2
  209. package/dist/{rubric-predictive-validity-DvkjPYCe.d.ts.map → rubric-predictive-validity-BgxtKe4G.d.ts.map} +1 -1
  210. package/dist/{rubric-predictive-validity-BRR632r1.js → rubric-predictive-validity-Cwwyd7ah.js} +2 -2
  211. package/dist/{rubric-predictive-validity-BRR632r1.js.map → rubric-predictive-validity-Cwwyd7ah.js.map} +1 -1
  212. package/dist/{run-record-BmSPWXJR.js → run-record-BvHPVS-i.js} +3 -3
  213. package/dist/{run-record-BmSPWXJR.js.map → run-record-BvHPVS-i.js.map} +1 -1
  214. package/dist/{run-record-CF4Dwpxr.d.ts → run-record-CKiihE6f.d.ts} +4 -4
  215. package/dist/{run-record-CF4Dwpxr.d.ts.map → run-record-CKiihE6f.d.ts.map} +1 -1
  216. package/dist/{proposal-findings-2GIUo1et.js → run-score-lDzV0X8j.js} +31 -31
  217. package/dist/run-score-lDzV0X8j.js.map +1 -0
  218. package/dist/sandbox-harness-BlSOu4LX.d.ts +70 -0
  219. package/dist/sandbox-harness-BlSOu4LX.d.ts.map +1 -0
  220. package/dist/{schema-Cef2cFmb2.d.ts → schema-Cef2cFmb.d.ts} +1 -1
  221. package/dist/schema-Cef2cFmb.d.ts.map +1 -0
  222. package/dist/semantic-concept-judge-BiJxScqe.js +406 -0
  223. package/dist/semantic-concept-judge-BiJxScqe.js.map +1 -0
  224. package/dist/{sequential-D-BLJBKU.js → sequential-C458DXNf.js} +4 -3
  225. package/dist/{sequential-D-BLJBKU.js.map → sequential-C458DXNf.js.map} +1 -1
  226. package/dist/sequential-eprocess-CbUt2htw.js +83 -0
  227. package/dist/sequential-eprocess-CbUt2htw.js.map +1 -0
  228. package/dist/series-convergence-D1cL1f-4.d.ts +129 -0
  229. package/dist/series-convergence-D1cL1f-4.d.ts.map +1 -0
  230. package/dist/{server-iu0ede49.js → server-ulsOdrTI.js} +5 -21
  231. package/dist/server-ulsOdrTI.js.map +1 -0
  232. package/dist/skillopt-optimization-method-C5cotF4E.d.ts +427 -0
  233. package/dist/skillopt-optimization-method-C5cotF4E.d.ts.map +1 -0
  234. package/dist/skillopt-optimization-method-jjdnc3YK.js +1999 -0
  235. package/dist/skillopt-optimization-method-jjdnc3YK.js.map +1 -0
  236. package/dist/{statistical-heldout-TQ-4CYiN.d.ts → statistical-heldout-DoFF5KJX.d.ts} +5 -278
  237. package/dist/statistical-heldout-DoFF5KJX.d.ts.map +1 -0
  238. package/dist/{store-otlp-Dw8PPIlL.js → store-otlp-CsptLYpN.js} +3 -3
  239. package/dist/{store-otlp-Dw8PPIlL.js.map → store-otlp-CsptLYpN.js.map} +1 -1
  240. package/dist/store-tool-spans-Cq9mFd-q.js +667 -0
  241. package/dist/store-tool-spans-Cq9mFd-q.js.map +1 -0
  242. package/dist/store-tool-spans-DbDLOBOb.d.ts +342 -0
  243. package/dist/store-tool-spans-DbDLOBOb.d.ts.map +1 -0
  244. package/dist/student-t-CvBq2mve.js +38 -0
  245. package/dist/student-t-CvBq2mve.js.map +1 -0
  246. package/dist/{summary-report-Lf-5I7xh.js → summary-report-Blysd6Z2.js} +6 -3
  247. package/dist/{summary-report-Lf-5I7xh.js.map → summary-report-Blysd6Z2.js.map} +1 -1
  248. package/dist/{summary-report-BNR7DWTj.d.ts → summary-report-CaL-Hnxt.d.ts} +5 -5
  249. package/dist/{summary-report-BNR7DWTj.d.ts.map → summary-report-CaL-Hnxt.d.ts.map} +1 -1
  250. package/dist/supervisor-run/index.d.ts +391 -3
  251. package/dist/supervisor-run/index.d.ts.map +1 -0
  252. package/dist/supervisor-run/index.js +1689 -2
  253. package/dist/{supervisor-run-D_sokXcO.js.map → supervisor-run/index.js.map} +1 -1
  254. package/dist/{extract-usage-CdZdoj1s.js → task-failure-attributes-CpQ4y5RD.js} +5 -157
  255. package/dist/task-failure-attributes-CpQ4y5RD.js.map +1 -0
  256. package/dist/{tool-groups-CZPGGlHf.d.ts → tool-groups-Cteb03Ps.d.ts} +3 -3
  257. package/dist/tool-groups-Cteb03Ps.d.ts.map +1 -0
  258. package/dist/tool-waste-BDdBZG1F.js +803 -0
  259. package/dist/tool-waste-BDdBZG1F.js.map +1 -0
  260. package/dist/tool-waste-DjRDEsuI.d.ts +128 -0
  261. package/dist/tool-waste-DjRDEsuI.d.ts.map +1 -0
  262. package/dist/trace-repair/index.d.ts +14 -5
  263. package/dist/trace-repair/index.d.ts.map +1 -1
  264. package/dist/trace-repair/index.js +35 -7
  265. package/dist/trace-repair/index.js.map +1 -1
  266. package/dist/traces.d.ts +406 -7
  267. package/dist/traces.d.ts.map +1 -0
  268. package/dist/traces.js +1011 -10
  269. package/dist/traces.js.map +1 -0
  270. package/dist/trajectory-replay/index.d.ts +16 -3
  271. package/dist/trajectory-replay/index.d.ts.map +1 -1
  272. package/dist/trajectory-replay/index.js +52 -5
  273. package/dist/trajectory-replay/index.js.map +1 -1
  274. package/dist/types-BEPZc6eo.d.ts +93 -0
  275. package/dist/types-BEPZc6eo.d.ts.map +1 -0
  276. package/dist/{usage-receipt-t7vAzCRQ.js → types-BI4fT3HN.js} +67 -67
  277. package/dist/types-BI4fT3HN.js.map +1 -0
  278. package/dist/{types-BnjdJ70P.d.ts → types-BZ59Ahr8.d.ts} +5 -5
  279. package/dist/{types-BnjdJ70P.d.ts.map → types-BZ59Ahr8.d.ts.map} +1 -1
  280. package/dist/{types-D216SgwM.d.ts → types-Cx3YUh2r.d.ts} +4 -241
  281. package/dist/types-Cx3YUh2r.d.ts.map +1 -0
  282. package/dist/{types-BvDKaULh.d.ts → types-DLQx4mKU.d.ts} +4 -4
  283. package/dist/{types-BvDKaULh.d.ts.map → types-DLQx4mKU.d.ts.map} +1 -1
  284. package/dist/{types-D4mog56g.d.ts → types-yLK8gXE9.d.ts} +2 -2
  285. package/dist/{types-D4mog56g.d.ts.map → types-yLK8gXE9.d.ts.map} +1 -1
  286. package/dist/verdict-BndeTAh_.js +61 -0
  287. package/dist/verdict-BndeTAh_.js.map +1 -0
  288. package/dist/verdict-E4eRNf7-.d.ts +392 -0
  289. package/dist/verdict-E4eRNf7-.d.ts.map +1 -0
  290. package/dist/{verdict-cache-BCcOh0kF.js → verdict-cache-mZf5FEiY.js} +3 -55
  291. package/dist/{verdict-cache-BCcOh0kF.js.map → verdict-cache-mZf5FEiY.js.map} +1 -1
  292. package/dist/wire/index.d.ts +3 -3
  293. package/dist/wire/index.d.ts.map +1 -1
  294. package/dist/wire/index.js +1 -1
  295. package/docs/charter.md +3 -3
  296. package/docs/control-runtime.md +3 -42
  297. package/docs/experiment.md +0 -1
  298. package/docs/feature-guide.md +2 -2
  299. package/docs/trace-repair-grader.md +1 -0
  300. package/docs/trajectory-replay.md +1 -0
  301. package/docs/verdicts.md +43 -0
  302. package/docs/verification-strategies.md +3 -2
  303. package/package.json +6 -11
  304. package/dist/agent-profile-DPi7IZg7.d.ts.map +0 -1
  305. package/dist/analyze-runs-C30yljDJ.js.map +0 -1
  306. package/dist/baseline-CavEbRyH.d.ts +0 -136
  307. package/dist/baseline-CavEbRyH.d.ts.map +0 -1
  308. package/dist/benchmark-command-BteMFN62.js.map +0 -1
  309. package/dist/benchmarks-Dzs8CKb1.js +0 -755
  310. package/dist/benchmarks-Dzs8CKb1.js.map +0 -1
  311. package/dist/campaign-C2TTzQII.js.map +0 -1
  312. package/dist/completion-verifier-DJA5BhPb.d.ts +0 -414
  313. package/dist/completion-verifier-DJA5BhPb.d.ts.map +0 -1
  314. package/dist/control.d.ts +0 -3
  315. package/dist/control.js +0 -2
  316. package/dist/dataset-C8xaLXdY.d.ts.map +0 -1
  317. package/dist/default-registry-Bf8Woqmq.d.ts.map +0 -1
  318. package/dist/default-registry-BmktKy8r.js.map +0 -1
  319. package/dist/engine-3hL-XqwJ.d.ts.map +0 -1
  320. package/dist/experiment-tracker-CnRICnMl.js +0 -500
  321. package/dist/experiment-tracker-CnRICnMl.js.map +0 -1
  322. package/dist/experiment-tracker-IMntXr6J.d.ts.map +0 -1
  323. package/dist/extract-usage-CdZdoj1s.js.map +0 -1
  324. package/dist/failure-cluster-CqcvCcdR.d.ts.map +0 -1
  325. package/dist/feedback-trajectory-CacHpxsp.d.ts.map +0 -1
  326. package/dist/index-BZ3-y4YL.d.ts +0 -391
  327. package/dist/index-BZ3-y4YL.d.ts.map +0 -1
  328. package/dist/index-CQTZ-4XN.d.ts.map +0 -1
  329. package/dist/index-DPPGNJ_R.d.ts.map +0 -1
  330. package/dist/index-YE4KdKbO2.d.ts +0 -335
  331. package/dist/index-YE4KdKbO2.d.ts.map +0 -1
  332. package/dist/paired-arms-iZ08VFMN.js +0 -260
  333. package/dist/paired-arms-iZ08VFMN.js.map +0 -1
  334. package/dist/paired-promotion-decision-B6zJ3gYM.d.ts +0 -114
  335. package/dist/paired-promotion-decision-B6zJ3gYM.d.ts.map +0 -1
  336. package/dist/prime-protocol-BfSalTfR.js.map +0 -1
  337. package/dist/promotion-policy-Ckjhzg_4.d.ts +0 -289
  338. package/dist/promotion-policy-Ckjhzg_4.d.ts.map +0 -1
  339. package/dist/promotion-policy-CrLrmys8.js.map +0 -1
  340. package/dist/proposal-findings-2GIUo1et.js.map +0 -1
  341. package/dist/propose-review-control-dSNPjFUH.js +0 -1458
  342. package/dist/propose-review-control-dSNPjFUH.js.map +0 -1
  343. package/dist/release-report-BUYmoKo2.js.map +0 -1
  344. package/dist/release-report-DA2BCu5a.d.ts.map +0 -1
  345. package/dist/replay-CohS93nE.js +0 -1859
  346. package/dist/replay-CohS93nE.js.map +0 -1
  347. package/dist/replay-DbhZ4Ked.d.ts +0 -834
  348. package/dist/replay-DbhZ4Ked.d.ts.map +0 -1
  349. package/dist/reward-hacking-BDToousL.js.map +0 -1
  350. package/dist/run-evidence-Dhi3C81V.d.ts +0 -225
  351. package/dist/run-evidence-Dhi3C81V.d.ts.map +0 -1
  352. package/dist/schema-Cef2cFmb2.d.ts.map +0 -1
  353. package/dist/semantic-concept-judge-D1z-KepS.js +0 -767
  354. package/dist/semantic-concept-judge-D1z-KepS.js.map +0 -1
  355. package/dist/series-convergence-ofsqPWhs.d.ts +0 -35
  356. package/dist/series-convergence-ofsqPWhs.d.ts.map +0 -1
  357. package/dist/server-iu0ede49.js.map +0 -1
  358. package/dist/single-run-lock-DFWHEB09.js.map +0 -1
  359. package/dist/skill-usage-CO9OLRBx.d.ts.map +0 -1
  360. package/dist/skillopt-optimization-method-CQdVeM8k.js.map +0 -1
  361. package/dist/skillopt-optimization-method-USDKhxSA.d.ts.map +0 -1
  362. package/dist/statistical-heldout-TQ-4CYiN.d.ts.map +0 -1
  363. package/dist/statistics-ByxzSiOM.js +0 -2212
  364. package/dist/statistics-ByxzSiOM.js.map +0 -1
  365. package/dist/statistics-D6Uebe_4.d.ts +0 -968
  366. package/dist/statistics-D6Uebe_4.d.ts.map +0 -1
  367. package/dist/supervisor-run-D_sokXcO.js +0 -1690
  368. package/dist/test-graded-scenario-D1TaI2va.d.ts +0 -141
  369. package/dist/test-graded-scenario-D1TaI2va.d.ts.map +0 -1
  370. package/dist/test-graded-scenario-JHcKQNpq.js +0 -318
  371. package/dist/test-graded-scenario-JHcKQNpq.js.map +0 -1
  372. package/dist/tool-groups-CZPGGlHf.d.ts.map +0 -1
  373. package/dist/tool-use-metrics-DEGMKycK.js +0 -370
  374. package/dist/tool-use-metrics-DEGMKycK.js.map +0 -1
  375. package/dist/types-D216SgwM.d.ts.map +0 -1
  376. package/dist/usage-receipt-t7vAzCRQ.js.map +0 -1
  377. package/dist/verdict-DExhxfgR.d.ts +0 -201
  378. package/dist/verdict-DExhxfgR.d.ts.map +0 -1
@@ -0,0 +1,406 @@
1
+ import { i as CostLedger } from "./cost-ledger-BSe92yAV.js";
2
+ import { l as Mutex } from "./ledger-core-BmZt19oQ.js";
3
+ import { c as callLlmJson, f as maximumChargeForLlmRequest, l as costReceiptFromLlm, u as costReceiptFromLlmError } from "./llm-client-d0-2TT1g.js";
4
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
5
+ import { dirname } from "node:path";
6
+ //#region src/locked-jsonl-appender.ts
7
+ /**
8
+ * LockedJsonlAppender — mutex-serialized JSONL append helper for arbitrary
9
+ * payloads. The reference-replay store does the same thing for typed
10
+ * `ReferenceReplayRun` rows; this is the generic version used by
11
+ * `MutationTelemetry`, `TrialTelemetry`, and any other consumer that wants
12
+ * append-only durable telemetry without rolling its own lock.
13
+ *
14
+ * Locks are per absolute file path (process-local). Cross-process
15
+ * concurrency is NOT addressed — that's an fcntl/flock problem.
16
+ */
17
+ const mutexes = /* @__PURE__ */ new Map();
18
+ function getMutex(path) {
19
+ let m = mutexes.get(path);
20
+ if (!m) {
21
+ m = new Mutex();
22
+ mutexes.set(path, m);
23
+ }
24
+ return m;
25
+ }
26
+ var LockedJsonlAppender = class {
27
+ path;
28
+ mutex;
29
+ constructor(path) {
30
+ this.path = path;
31
+ this.mutex = getMutex(path);
32
+ if (!existsSync(dirname(path))) mkdirSync(dirname(path), { recursive: true });
33
+ }
34
+ async append(entry) {
35
+ const line = `${JSON.stringify(entry)}\n`;
36
+ await this.mutex.runExclusive(() => {
37
+ appendFileSync(this.path, line);
38
+ });
39
+ }
40
+ };
41
+ //#endregion
42
+ //#region src/analyst/findings-store.ts
43
+ /**
44
+ * FindingsStore — durable persistence for AnalystFinding rows + a diff
45
+ * helper so we can answer "what changed since the last run?" without
46
+ * recomputing analysts.
47
+ *
48
+ * On-disk shape is JSONL: one finding per line, append-only, locked via
49
+ * LockedJsonlAppender. Operators get crash-safety (no partial JSON),
50
+ * cheap reads (sequential parse), and trivial backup (rsync the file).
51
+ *
52
+ * Reads are non-locking: a reader sees a consistent snapshot of all
53
+ * fully-written lines and skips an incomplete trailing line if the
54
+ * writer is mid-append. Cross-process locking is intentionally out of
55
+ * scope (see locked-jsonl-appender.ts).
56
+ *
57
+ * The store is run-scoped: callers pass `runId` on append and on load,
58
+ * which keeps multi-run files cleanly partitioned. The `diffFindings`
59
+ * helper compares two run-id sets using stable `finding_id` semantics —
60
+ * the diff is the cross-run signal the regression dashboard renders.
61
+ */
62
+ var FindingsStore = class {
63
+ path;
64
+ appender;
65
+ constructor(path) {
66
+ this.path = path;
67
+ this.appender = new LockedJsonlAppender(path);
68
+ }
69
+ async append(runId, findings) {
70
+ for (const f of findings) {
71
+ const row = {
72
+ ...f,
73
+ run_id: runId
74
+ };
75
+ await this.appender.append(row);
76
+ }
77
+ }
78
+ /** Load every persisted finding. Discards malformed trailing lines silently. */
79
+ loadAll() {
80
+ if (!existsSync(this.path)) return [];
81
+ const raw = readFileSync(this.path, "utf8");
82
+ if (!raw) return [];
83
+ const out = [];
84
+ for (const line of raw.split("\n")) {
85
+ if (!line) continue;
86
+ try {
87
+ out.push(JSON.parse(line));
88
+ } catch {}
89
+ }
90
+ return out;
91
+ }
92
+ /** Filter to a single run. */
93
+ loadRun(runId) {
94
+ return this.loadAll().filter((r) => r.run_id === runId);
95
+ }
96
+ };
97
+ /**
98
+ * Default materiality test. Deliberately narrow so LLM-reword churn
99
+ * doesn't flood the diff. Stricter tests are opt-in via DiffPolicy.
100
+ */
101
+ function defaultIsMaterial(a, b) {
102
+ if (a.severity !== b.severity) return true;
103
+ if (Math.abs((a.confidence ?? 0) - (b.confidence ?? 0)) > .05) return true;
104
+ if (a.evidence_refs.length !== b.evidence_refs.length) return true;
105
+ return false;
106
+ }
107
+ /**
108
+ * Diff two findings sets by stable finding_id. Callers typically load
109
+ * the two run-id slices from the same store and pass them in.
110
+ */
111
+ function diffFindings(previous, current, policy = {}) {
112
+ const isMaterial = policy.isMaterial ?? defaultIsMaterial;
113
+ const prevById = new Map(previous.map((f) => [f.finding_id, f]));
114
+ const curById = new Map(current.map((f) => [f.finding_id, f]));
115
+ const appeared = [];
116
+ const disappeared = [];
117
+ const persisted = [];
118
+ const changed = [];
119
+ for (const [id, cur] of curById) {
120
+ const prev = prevById.get(id);
121
+ if (!prev) {
122
+ appeared.push(cur);
123
+ continue;
124
+ }
125
+ if (isMaterial(prev, cur)) changed.push({
126
+ previous: prev,
127
+ current: cur
128
+ });
129
+ else persisted.push(cur);
130
+ }
131
+ for (const [id, prev] of prevById) if (!curById.has(id)) disappeared.push(prev);
132
+ return {
133
+ appeared,
134
+ disappeared,
135
+ persisted,
136
+ changed
137
+ };
138
+ }
139
+ //#endregion
140
+ //#region src/semantic-concept-judge.ts
141
+ /**
142
+ * Semantic concept judge — "does the built artifact actually implement
143
+ * the features the user asked for?"
144
+ *
145
+ * Distinct from the domain/code/coherence judges in `judges.ts`:
146
+ * - those judges score free-form conversational agent outputs along
147
+ * quality dimensions (accuracy, depth, etc.)
148
+ * - this judge scores a *built artifact* (served HTML + source files)
149
+ * against an explicit list of expected concepts, returning per-concept
150
+ * {present, score 0-10, evidence, severity}.
151
+ *
152
+ * The judge is strict about distinguishing (a) a working implementation
153
+ * from (b) a keyword-present stub. "// TODO: mint button" is NOT present.
154
+ * Only real, functional, wired-up code counts.
155
+ *
156
+ * Use via {@link createSemanticConceptJudge} or directly via
157
+ * {@link runSemanticConceptJudge}. Soft-fails (available=false) on LLM
158
+ * or JSON-parse errors so the caller can treat that as "layer skipped"
159
+ * rather than "layer failed" in a multi-layer pipeline.
160
+ */
161
+ const DEFAULT_COMPLEXITY_WEIGHTS = {
162
+ render: 1,
163
+ integrate: 2,
164
+ compute: 2.5
165
+ };
166
+ const SEMANTIC_CONCEPT_JUDGE_VERSION = "semantic-concept-judge-v1-2026-04-24";
167
+ const DEFAULT_MAX_SOURCE = 45e3;
168
+ const DEFAULT_MAX_HTML = 3e4;
169
+ const DEFAULT_MAX_PER_FILE = 2e4;
170
+ const DEFAULT_TIMEOUT = 3e5;
171
+ const DEFAULT_MAX_TOKENS = 16e3;
172
+ const DEFAULT_MODEL = "claude-sonnet-4-6";
173
+ const SEMANTIC_SCHEMA = {
174
+ type: "object",
175
+ additionalProperties: false,
176
+ required: ["summary", "concepts"],
177
+ properties: {
178
+ summary: {
179
+ type: "string",
180
+ minLength: 20,
181
+ maxLength: 600
182
+ },
183
+ concepts: {
184
+ type: "array",
185
+ minItems: 1,
186
+ items: {
187
+ type: "object",
188
+ additionalProperties: false,
189
+ required: [
190
+ "concept",
191
+ "present",
192
+ "score",
193
+ "evidence",
194
+ "severity"
195
+ ],
196
+ properties: {
197
+ concept: {
198
+ type: "string",
199
+ minLength: 1,
200
+ maxLength: 120
201
+ },
202
+ present: { type: "boolean" },
203
+ score: {
204
+ type: "number",
205
+ minimum: 0,
206
+ maximum: 10
207
+ },
208
+ evidence: {
209
+ type: "string",
210
+ minLength: 5,
211
+ maxLength: 400
212
+ },
213
+ severity: {
214
+ type: "string",
215
+ enum: [
216
+ "critical",
217
+ "major",
218
+ "minor",
219
+ "info"
220
+ ]
221
+ }
222
+ }
223
+ }
224
+ }
225
+ }
226
+ };
227
+ function truncate(body, cap, label) {
228
+ if (body.length <= cap) return body;
229
+ return `${body.slice(0, cap)}\n… [truncated ${body.length - cap} chars of ${label}]`;
230
+ }
231
+ function buildPrompt(input, opts) {
232
+ const sourceBlob = input.sourceFiles.filter((f) => f.content.length <= opts.maxPerFileChars).map((f) => `--- FILE: ${f.path} ---\n${f.content}`).join("\n\n");
233
+ const html = input.servedHtml ?? "";
234
+ return `You are a strict code-review judge evaluating whether an agent's 0-to-1 build actually implements the features the user asked for.
235
+
236
+ You MUST distinguish:
237
+ (a) WORKING code that implements the concept (rendered UI, wired handler, real API call),
238
+ (b) KEYWORD-PRESENT stub (comments mentioning the concept, variable names, TODOs),
239
+ (c) ABSENT (concept nowhere).
240
+
241
+ A comment like "// TODO: add mint button" is NOT present — score 2-3. Only count a concept as present if there is real functional code: a rendered component, a call handler wired to state or a network call, a computed value actually used.
242
+
243
+ USER REQUEST (what the agent was asked to build):
244
+ ${input.userRequest}
245
+
246
+ ${input.artifactLabel ? `ARTIFACT METADATA:\n name: ${input.artifactLabel}\n description: ${input.artifactDescription ?? ""}\n\n` : ""}EXPECTED CONCEPTS (each must be graded independently):
247
+ ${input.expectedConcepts.map((c, i) => ` ${i + 1}. "${c.name}"${c.keywords?.length ? ` — hints: [${c.keywords.slice(0, 6).join(" | ")}]` : ""}`).join("\n")}
248
+
249
+ ${html ? `SERVED HTML (what the preview returns when hit):\n${truncate(html, opts.maxHtmlChars, "HTML")}\n\n` : ""}SOURCE FILES (the agent's workdir):
250
+ ${truncate(sourceBlob, opts.maxSourceChars, "source")}
251
+
252
+ For EACH concept, return:
253
+ - concept: the concept name as given (match exactly)
254
+ - present: boolean — does a working implementation exist?
255
+ - score: 0-10 — 10 = production-ready; 7 = functional but thin; 4 = partial/stubbed; 2 = keyword-only comment; 0 = absent
256
+ - evidence: cite "<file>:<line>" or "served-html:<selector>" pointing at the strongest supporting code. If the concept is absent or stubbed, explain what's missing.
257
+ - severity:
258
+ "info" when present: true AND score >= 7
259
+ "minor" when present: true AND 4 <= score < 7
260
+ "major" when present: false OR score < 4
261
+ "critical" when the concept is not only absent but a core user flow depends on it
262
+
263
+ Also produce a "summary" (one sentence, 20-600 chars): overall verdict on whether this is a shippable implementation of the user request vs a keyword-dense placeholder.
264
+
265
+ BE SKEPTICAL. Keyword matching already passed — your job is to catch what keyword matching misses. If the agent shipped a working build, say so. If it shipped a stub, say so. Don't grade on effort.
266
+
267
+ Return STRICT JSON. No prose outside the JSON.`;
268
+ }
269
+ /**
270
+ * Run the semantic concept judge. Soft-fails to available=false on
271
+ * LLM/JSON errors — callers in a MultiLayerVerifier pipeline can treat
272
+ * that as "skip" rather than "fail."
273
+ */
274
+ async function runSemanticConceptJudge(input, options = {}) {
275
+ const start = Date.now();
276
+ const totalCount = input.expectedConcepts.length;
277
+ if (totalCount === 0) return {
278
+ kind: "semantic-concept",
279
+ version: SEMANTIC_CONCEPT_JUDGE_VERSION,
280
+ score: 0,
281
+ presentCount: 0,
282
+ totalCount: 0,
283
+ findings: [],
284
+ summary: "no expected concepts declared",
285
+ durationMs: 0,
286
+ costUsd: null,
287
+ available: false,
288
+ error: "no expected concepts declared"
289
+ };
290
+ const opts = {
291
+ model: options.model ?? DEFAULT_MODEL,
292
+ timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT,
293
+ maxTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
294
+ maxSourceChars: options.maxSourceChars ?? DEFAULT_MAX_SOURCE,
295
+ maxPerFileChars: options.maxPerFileChars ?? DEFAULT_MAX_PER_FILE,
296
+ maxHtmlChars: options.maxHtmlChars ?? DEFAULT_MAX_HTML,
297
+ llm: options.llm ?? {},
298
+ costLedger: options.costLedger ?? new CostLedger(),
299
+ costPhase: options.costPhase ?? "judge.semantic-concept",
300
+ costTags: options.costTags ?? {},
301
+ signal: options.signal ?? new AbortController().signal,
302
+ weightConcepts: options.weightConcepts ?? "mean",
303
+ complexityWeights: {
304
+ ...DEFAULT_COMPLEXITY_WEIGHTS,
305
+ ...options.complexityWeights ?? {}
306
+ }
307
+ };
308
+ const weightForConcept = (spec) => {
309
+ if (opts.weightConcepts === "mean") return 1;
310
+ if (spec.weight != null) return spec.weight;
311
+ if (opts.weightConcepts === "complexity") return opts.complexityWeights[spec.complexity ?? "render"] ?? 1;
312
+ return 1;
313
+ };
314
+ const weightByName = new Map(input.expectedConcepts.map((c) => [c.name, weightForConcept(c)]));
315
+ let receipt;
316
+ try {
317
+ const request = {
318
+ model: opts.model,
319
+ messages: [{
320
+ role: "system",
321
+ content: "You are a strict code-review judge. Return strict JSON only. No prose outside the JSON. A keyword in a comment is NOT a working implementation."
322
+ }, {
323
+ role: "user",
324
+ content: buildPrompt(input, opts)
325
+ }],
326
+ jsonSchema: {
327
+ name: "semantic_concept_judge",
328
+ schema: SEMANTIC_SCHEMA
329
+ },
330
+ temperature: 0,
331
+ maxTokens: opts.maxTokens,
332
+ timeoutMs: opts.timeoutMs
333
+ };
334
+ const paid = await opts.costLedger.runPaidCall({
335
+ channel: "judge",
336
+ phase: opts.costPhase,
337
+ actor: "semantic-concept",
338
+ model: opts.model,
339
+ ...Object.keys(opts.costTags).length > 0 ? { tags: opts.costTags } : {},
340
+ maximumCharge: maximumChargeForLlmRequest(request, opts.llm),
341
+ signal: opts.signal,
342
+ execute: (signal, callId) => callLlmJson(request, {
343
+ ...opts.llm,
344
+ signal,
345
+ idempotencyKey: callId
346
+ }),
347
+ receipt: ({ result }) => costReceiptFromLlm(result),
348
+ receiptFromError: costReceiptFromLlmError
349
+ });
350
+ receipt = paid.receipt;
351
+ if (!paid.succeeded) throw paid.error;
352
+ const { value } = paid.value;
353
+ if (!value?.concepts || !Array.isArray(value.concepts)) throw new Error("judge returned malformed response — expected array under \"concepts\"");
354
+ const findings = value.concepts.map((c) => ({
355
+ concept: String(c.concept),
356
+ present: Boolean(c.present),
357
+ score: Math.max(0, Math.min(10, Number(c.score ?? 0))),
358
+ evidence: String(c.evidence ?? ""),
359
+ severity: [
360
+ "critical",
361
+ "major",
362
+ "minor",
363
+ "info"
364
+ ].includes(c.severity) ? c.severity : "info"
365
+ }));
366
+ const presentCount = findings.filter((f) => f.present && f.score >= 7).length;
367
+ let weightSum = 0;
368
+ let weightedScoreSum = 0;
369
+ for (const f of findings) {
370
+ const w = weightByName.get(f.concept) ?? 1;
371
+ weightSum += w;
372
+ weightedScoreSum += w * f.score;
373
+ }
374
+ const scoreAvg = weightSum > 0 ? weightedScoreSum / weightSum : findings.reduce((a, f) => a + f.score, 0) / Math.max(1, findings.length);
375
+ return {
376
+ kind: "semantic-concept",
377
+ version: SEMANTIC_CONCEPT_JUDGE_VERSION,
378
+ score: Number((scoreAvg / 10).toFixed(3)),
379
+ presentCount,
380
+ totalCount,
381
+ findings,
382
+ summary: String(value.summary ?? ""),
383
+ durationMs: Date.now() - start,
384
+ costUsd: paid.receipt.costUnknown ? null : paid.receipt.costUsd,
385
+ available: true
386
+ };
387
+ } catch (err) {
388
+ return {
389
+ kind: "semantic-concept",
390
+ version: SEMANTIC_CONCEPT_JUDGE_VERSION,
391
+ score: 0,
392
+ presentCount: 0,
393
+ totalCount,
394
+ findings: [],
395
+ summary: "",
396
+ durationMs: Date.now() - start,
397
+ costUsd: receipt && !receipt.costUnknown ? receipt.costUsd : null,
398
+ available: false,
399
+ error: err instanceof Error ? err.message : String(err)
400
+ };
401
+ }
402
+ }
403
+ //#endregion
404
+ export { diffFindings as a, defaultIsMaterial as i, runSemanticConceptJudge as n, FindingsStore as r, SEMANTIC_CONCEPT_JUDGE_VERSION as t };
405
+
406
+ //# sourceMappingURL=semantic-concept-judge-BiJxScqe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic-concept-judge-BiJxScqe.js","names":[],"sources":["../src/locked-jsonl-appender.ts","../src/analyst/findings-store.ts","../src/semantic-concept-judge.ts"],"sourcesContent":["/**\n * LockedJsonlAppender — mutex-serialized JSONL append helper for arbitrary\n * payloads. The reference-replay store does the same thing for typed\n * `ReferenceReplayRun` rows; this is the generic version used by\n * `MutationTelemetry`, `TrialTelemetry`, and any other consumer that wants\n * append-only durable telemetry without rolling its own lock.\n *\n * Locks are per absolute file path (process-local). Cross-process\n * concurrency is NOT addressed — that's an fcntl/flock problem.\n */\n\nimport { appendFileSync, existsSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { Mutex } from './concurrency'\n\nconst mutexes = new Map<string, Mutex>()\n\nfunction getMutex(path: string): Mutex {\n let m = mutexes.get(path)\n if (!m) {\n m = new Mutex()\n mutexes.set(path, m)\n }\n return m\n}\n\nexport class LockedJsonlAppender {\n private readonly mutex: Mutex\n constructor(public readonly path: string) {\n this.mutex = getMutex(path)\n if (!existsSync(dirname(path))) {\n mkdirSync(dirname(path), { recursive: true })\n }\n }\n\n async append(entry: unknown): Promise<void> {\n const line = `${JSON.stringify(entry)}\\n`\n await this.mutex.runExclusive(() => {\n appendFileSync(this.path, line)\n })\n }\n}\n\n/** Reset all internal mutex state — tests only. */\nexport function resetLockedAppendersForTesting(): void {\n mutexes.clear()\n}\n","/**\n * FindingsStore — durable persistence for AnalystFinding rows + a diff\n * helper so we can answer \"what changed since the last run?\" without\n * recomputing analysts.\n *\n * On-disk shape is JSONL: one finding per line, append-only, locked via\n * LockedJsonlAppender. Operators get crash-safety (no partial JSON),\n * cheap reads (sequential parse), and trivial backup (rsync the file).\n *\n * Reads are non-locking: a reader sees a consistent snapshot of all\n * fully-written lines and skips an incomplete trailing line if the\n * writer is mid-append. Cross-process locking is intentionally out of\n * scope (see locked-jsonl-appender.ts).\n *\n * The store is run-scoped: callers pass `runId` on append and on load,\n * which keeps multi-run files cleanly partitioned. The `diffFindings`\n * helper compares two run-id sets using stable `finding_id` semantics —\n * the diff is the cross-run signal the regression dashboard renders.\n */\n\nimport { existsSync, readFileSync } from 'node:fs'\n\nimport { LockedJsonlAppender } from '../locked-jsonl-appender'\nimport type { AnalystFinding } from './types'\n\n/**\n * One persisted row. We attach `run_id` on disk so a single file can\n * hold multiple runs and the diff helper can query without re-walking\n * separate files.\n */\nexport interface PersistedFinding extends AnalystFinding {\n run_id: string\n}\n\nexport class FindingsStore {\n private readonly appender: LockedJsonlAppender\n\n constructor(public readonly path: string) {\n this.appender = new LockedJsonlAppender(path)\n }\n\n async append(runId: string, findings: AnalystFinding[]): Promise<void> {\n for (const f of findings) {\n const row: PersistedFinding = { ...f, run_id: runId }\n await this.appender.append(row)\n }\n }\n\n /** Load every persisted finding. Discards malformed trailing lines silently. */\n loadAll(): PersistedFinding[] {\n if (!existsSync(this.path)) return []\n const raw = readFileSync(this.path, 'utf8')\n if (!raw) return []\n const out: PersistedFinding[] = []\n for (const line of raw.split('\\n')) {\n if (!line) continue\n try {\n out.push(JSON.parse(line) as PersistedFinding)\n } catch {\n // Skip torn trailing line — the lock guarantees no torn lines\n // mid-file, only at EOF when a writer is in-flight.\n }\n }\n return out\n }\n\n /** Filter to a single run. */\n loadRun(runId: string): PersistedFinding[] {\n return this.loadAll().filter((r) => r.run_id === runId)\n }\n}\n\n// ── Cross-run diff ──────────────────────────────────────────────────\n\nexport interface FindingsDiff {\n /** New finding ids in `current` that weren't in `previous`. */\n appeared: PersistedFinding[]\n /** Finding ids in `previous` that aren't in `current`. */\n disappeared: PersistedFinding[]\n /** Same finding id present in both runs and unchanged per the materiality test. */\n persisted: PersistedFinding[]\n /**\n * Same finding id in both runs but at least one non-identity field\n * shifted per `DiffPolicy.isMaterial`. Reported as [previous, current].\n */\n changed: Array<{ previous: PersistedFinding; current: PersistedFinding }>\n}\n\nexport interface DiffPolicy {\n /**\n * Predicate that decides whether two findings (same finding_id) count\n * as a material change. Defaults to {@link defaultIsMaterial}: severity\n * shift, confidence Δ > 0.05, or evidence count change. Compliance /\n * perf consumers MAY supply a stricter predicate (e.g. rationale text\n * diff, metric Δ thresholds).\n */\n isMaterial?: (previous: AnalystFinding, current: AnalystFinding) => boolean\n}\n\n/**\n * Default materiality test. Deliberately narrow so LLM-reword churn\n * doesn't flood the diff. Stricter tests are opt-in via DiffPolicy.\n */\nexport function defaultIsMaterial(a: AnalystFinding, b: AnalystFinding): boolean {\n if (a.severity !== b.severity) return true\n if (Math.abs((a.confidence ?? 0) - (b.confidence ?? 0)) > 0.05) return true\n if (a.evidence_refs.length !== b.evidence_refs.length) return true\n return false\n}\n\n/**\n * Diff two findings sets by stable finding_id. Callers typically load\n * the two run-id slices from the same store and pass them in.\n */\nexport function diffFindings(\n previous: PersistedFinding[],\n current: PersistedFinding[],\n policy: DiffPolicy = {},\n): FindingsDiff {\n const isMaterial = policy.isMaterial ?? defaultIsMaterial\n const prevById = new Map(previous.map((f) => [f.finding_id, f]))\n const curById = new Map(current.map((f) => [f.finding_id, f]))\n\n const appeared: PersistedFinding[] = []\n const disappeared: PersistedFinding[] = []\n const persisted: PersistedFinding[] = []\n const changed: FindingsDiff['changed'] = []\n\n for (const [id, cur] of curById) {\n const prev = prevById.get(id)\n if (!prev) {\n appeared.push(cur)\n continue\n }\n if (isMaterial(prev, cur)) {\n changed.push({ previous: prev, current: cur })\n } else {\n persisted.push(cur)\n }\n }\n for (const [id, prev] of prevById) {\n if (!curById.has(id)) disappeared.push(prev)\n }\n return { appeared, disappeared, persisted, changed }\n}\n","/**\n * Semantic concept judge — \"does the built artifact actually implement\n * the features the user asked for?\"\n *\n * Distinct from the domain/code/coherence judges in `judges.ts`:\n * - those judges score free-form conversational agent outputs along\n * quality dimensions (accuracy, depth, etc.)\n * - this judge scores a *built artifact* (served HTML + source files)\n * against an explicit list of expected concepts, returning per-concept\n * {present, score 0-10, evidence, severity}.\n *\n * The judge is strict about distinguishing (a) a working implementation\n * from (b) a keyword-present stub. \"// TODO: mint button\" is NOT present.\n * Only real, functional, wired-up code counts.\n *\n * Use via {@link createSemanticConceptJudge} or directly via\n * {@link runSemanticConceptJudge}. Soft-fails (available=false) on LLM\n * or JSON-parse errors so the caller can treat that as \"layer skipped\"\n * rather than \"layer failed\" in a multi-layer pipeline.\n */\n\nimport { CostLedger, type CostLedgerHandle, type CostReceipt } from './cost-ledger'\nimport {\n callLlmJson,\n costReceiptFromLlm,\n costReceiptFromLlmError,\n type LlmCallRequest,\n type LlmClientOptions,\n maximumChargeForLlmRequest,\n} from './llm-client'\nimport type { Severity } from './multi-layer-verifier'\n\n// ─── Types ──────────────────────────────────────────────────────────────\n\n/**\n * Implementation complexity class for weighted scoring.\n *\n * - `render` (default): the concept is a UI surface that displays static\n * data — render a list, show a counter, lay out a button. Single-file\n * work, no external integration.\n * - `integrate`: the concept requires wiring a real external system —\n * wallet connect (wagmi + RainbowKit + chain config), payment provider\n * (Stripe Elements + intent + webhook), an API client with auth.\n * Multi-file, library-knowledge, runtime correctness matters.\n * - `compute`: the concept requires algorithmic work — solver, simulator,\n * constraint propagation, ML inference. Correctness > UI polish.\n *\n * Default weights (when applied via `weightConcepts: 'complexity'`):\n * render=1.0, integrate=2.0, compute=2.5\n *\n * Cross-vertical scoring without complexity weighting silently inflates\n * the rate of UI-heavy verticals (healthcare, fintech dashboards) vs\n * integration-heavy verticals (DeFi, wallets) — all concepts treated\n * equally even though the agent does 2-3x the work for `integrate`.\n */\nexport type ConceptComplexity = 'render' | 'integrate' | 'compute'\n\nexport interface ConceptSpec {\n name: string\n /** Short hints that help the judge; not used for matching. */\n keywords?: string[]\n /** Optional explicit weight; default 1.0. Overrides complexity-derived weight. */\n weight?: number\n /** Implementation complexity class. Default `render`. */\n complexity?: ConceptComplexity\n}\n\nexport interface ConceptFinding {\n concept: string\n present: boolean\n /** 0..10. 10 = production-ready; 7 = functional thin; 4 = partial; 0 = absent. */\n score: number\n evidence: string\n severity: Severity\n}\n\nexport interface SemanticConceptJudgeInput {\n /** Full natural-language prompt the agent was handed. */\n userRequest: string\n /** Rendered HTML the preview returns (UI artifacts). Optional. */\n servedHtml?: string\n /** Top-level source files from the agent's workdir. */\n sourceFiles: Array<{ path: string; content: string }>\n /** The expected concept list. */\n expectedConcepts: ConceptSpec[]\n /** Free-form metadata (id, difficulty) to inject into the prompt. */\n artifactLabel?: string\n artifactDescription?: string\n}\n\nexport interface SemanticConceptJudgeResult {\n kind: 'semantic-concept'\n version: string\n /** Normalized 0..1 score — mean of per-concept scores / 10. */\n score: number\n presentCount: number\n totalCount: number\n findings: ConceptFinding[]\n summary: string\n durationMs: number\n costUsd: number | null\n /** False on LLM/JSON error — treat as \"skipped / unable to judge\" in pipelines. */\n available: boolean\n error?: string\n}\n\n/**\n * Score-aggregation strategy. `mean` averages 0-10 scores uniformly.\n * `complexity` applies the default weight table (render=1, integrate=2,\n * compute=2.5) unless a concept has an explicit `weight`. `explicit`\n * honors only `weight` (defaulting to 1 for unspecified).\n */\nexport type ConceptWeightStrategy = 'mean' | 'complexity' | 'explicit'\n\nexport const DEFAULT_COMPLEXITY_WEIGHTS: Record<ConceptComplexity, number> = {\n render: 1.0,\n integrate: 2.0,\n compute: 2.5,\n}\n\nexport interface SemanticConceptJudgeOptions {\n /** Model id to call. Default 'claude-sonnet-4-6' via agent-eval defaults. */\n model?: string\n /** Per-call timeout. Default 300s. */\n timeoutMs?: number\n /** Provider-enforced output limit. Default 16000. */\n maxTokens?: number\n /** Pipeline budget for the prompt (source blob truncation). Default 45000. */\n maxSourceChars?: number\n /** Per-file cap before inclusion. Default 20000. */\n maxPerFileChars?: number\n /** HTML cap. Default 30000. */\n maxHtmlChars?: number\n /** LlmClient config (baseUrl, apiKey, authHeader, …). */\n llm?: LlmClientOptions\n costLedger?: CostLedgerHandle\n costPhase?: string\n costTags?: Record<string, string>\n signal?: AbortSignal\n /**\n * Score aggregation strategy. Default `mean` — uniform average across\n * concepts. Cross-vertical comparisons should use `complexity` to\n * neutralize the integrate-vs-render asymmetry.\n */\n weightConcepts?: ConceptWeightStrategy\n /** Override the default complexity → weight table. */\n complexityWeights?: Partial<Record<ConceptComplexity, number>>\n}\n\n// ─── Prompt assembly ────────────────────────────────────────────────────\n\nexport const SEMANTIC_CONCEPT_JUDGE_VERSION = 'semantic-concept-judge-v1-2026-04-24'\n\nconst DEFAULT_MAX_SOURCE = 45_000\nconst DEFAULT_MAX_HTML = 30_000\nconst DEFAULT_MAX_PER_FILE = 20_000\nconst DEFAULT_TIMEOUT = 300_000\nconst DEFAULT_MAX_TOKENS = 16_000\nconst DEFAULT_MODEL = 'claude-sonnet-4-6'\n\nconst SEMANTIC_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['summary', 'concepts'],\n properties: {\n summary: { type: 'string', minLength: 20, maxLength: 600 },\n concepts: {\n type: 'array',\n minItems: 1,\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['concept', 'present', 'score', 'evidence', 'severity'],\n properties: {\n concept: { type: 'string', minLength: 1, maxLength: 120 },\n present: { type: 'boolean' },\n score: { type: 'number', minimum: 0, maximum: 10 },\n evidence: { type: 'string', minLength: 5, maxLength: 400 },\n severity: { type: 'string', enum: ['critical', 'major', 'minor', 'info'] },\n },\n },\n },\n },\n}\n\nfunction truncate(body: string, cap: number, label: string): string {\n if (body.length <= cap) return body\n return `${body.slice(0, cap)}\\n… [truncated ${body.length - cap} chars of ${label}]`\n}\n\nfunction buildPrompt(\n input: SemanticConceptJudgeInput,\n opts: Required<SemanticConceptJudgeOptions>,\n): string {\n const sourceBlob = input.sourceFiles\n .filter((f) => f.content.length <= opts.maxPerFileChars)\n .map((f) => `--- FILE: ${f.path} ---\\n${f.content}`)\n .join('\\n\\n')\n\n const html = input.servedHtml ?? ''\n\n return `You are a strict code-review judge evaluating whether an agent's 0-to-1 build actually implements the features the user asked for.\n\nYou MUST distinguish:\n (a) WORKING code that implements the concept (rendered UI, wired handler, real API call),\n (b) KEYWORD-PRESENT stub (comments mentioning the concept, variable names, TODOs),\n (c) ABSENT (concept nowhere).\n\nA comment like \"// TODO: add mint button\" is NOT present — score 2-3. Only count a concept as present if there is real functional code: a rendered component, a call handler wired to state or a network call, a computed value actually used.\n\nUSER REQUEST (what the agent was asked to build):\n${input.userRequest}\n\n${input.artifactLabel ? `ARTIFACT METADATA:\\n name: ${input.artifactLabel}\\n description: ${input.artifactDescription ?? ''}\\n\\n` : ''}EXPECTED CONCEPTS (each must be graded independently):\n${input.expectedConcepts\n .map(\n (c, i) =>\n ` ${i + 1}. \"${c.name}\"${c.keywords?.length ? ` — hints: [${c.keywords.slice(0, 6).join(' | ')}]` : ''}`,\n )\n .join('\\n')}\n\n${html ? `SERVED HTML (what the preview returns when hit):\\n${truncate(html, opts.maxHtmlChars, 'HTML')}\\n\\n` : ''}SOURCE FILES (the agent's workdir):\n${truncate(sourceBlob, opts.maxSourceChars, 'source')}\n\nFor EACH concept, return:\n - concept: the concept name as given (match exactly)\n - present: boolean — does a working implementation exist?\n - score: 0-10 — 10 = production-ready; 7 = functional but thin; 4 = partial/stubbed; 2 = keyword-only comment; 0 = absent\n - evidence: cite \"<file>:<line>\" or \"served-html:<selector>\" pointing at the strongest supporting code. If the concept is absent or stubbed, explain what's missing.\n - severity:\n \"info\" when present: true AND score >= 7\n \"minor\" when present: true AND 4 <= score < 7\n \"major\" when present: false OR score < 4\n \"critical\" when the concept is not only absent but a core user flow depends on it\n\nAlso produce a \"summary\" (one sentence, 20-600 chars): overall verdict on whether this is a shippable implementation of the user request vs a keyword-dense placeholder.\n\nBE SKEPTICAL. Keyword matching already passed — your job is to catch what keyword matching misses. If the agent shipped a working build, say so. If it shipped a stub, say so. Don't grade on effort.\n\nReturn STRICT JSON. No prose outside the JSON.`\n}\n\n// ─── Runner ─────────────────────────────────────────────────────────────\n\n/**\n * Run the semantic concept judge. Soft-fails to available=false on\n * LLM/JSON errors — callers in a MultiLayerVerifier pipeline can treat\n * that as \"skip\" rather than \"fail.\"\n */\nexport async function runSemanticConceptJudge(\n input: SemanticConceptJudgeInput,\n options: SemanticConceptJudgeOptions = {},\n): Promise<SemanticConceptJudgeResult> {\n const start = Date.now()\n const totalCount = input.expectedConcepts.length\n\n if (totalCount === 0) {\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: 0,\n presentCount: 0,\n totalCount: 0,\n findings: [],\n summary: 'no expected concepts declared',\n durationMs: 0,\n costUsd: null,\n available: false,\n error: 'no expected concepts declared',\n }\n }\n\n const opts: Required<SemanticConceptJudgeOptions> = {\n model: options.model ?? DEFAULT_MODEL,\n timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT,\n maxTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n maxSourceChars: options.maxSourceChars ?? DEFAULT_MAX_SOURCE,\n maxPerFileChars: options.maxPerFileChars ?? DEFAULT_MAX_PER_FILE,\n maxHtmlChars: options.maxHtmlChars ?? DEFAULT_MAX_HTML,\n llm: options.llm ?? {},\n costLedger: options.costLedger ?? new CostLedger(),\n costPhase: options.costPhase ?? 'judge.semantic-concept',\n costTags: options.costTags ?? {},\n signal: options.signal ?? new AbortController().signal,\n weightConcepts: options.weightConcepts ?? 'mean',\n complexityWeights: { ...DEFAULT_COMPLEXITY_WEIGHTS, ...(options.complexityWeights ?? {}) },\n }\n\n // Build a name → weight map for aggregation. Mean strategy keeps every\n // weight at 1 (uniform average). Complexity strategy reads the table\n // and lets an explicit `weight` override. Explicit strategy uses ONLY\n // the spec's `weight` (defaulting to 1).\n const weightForConcept = (spec: ConceptSpec): number => {\n if (opts.weightConcepts === 'mean') return 1\n if (spec.weight != null) return spec.weight\n if (opts.weightConcepts === 'complexity') {\n return opts.complexityWeights[spec.complexity ?? 'render'] ?? 1\n }\n return 1\n }\n const weightByName = new Map<string, number>(\n input.expectedConcepts.map((c) => [c.name, weightForConcept(c)]),\n )\n\n let receipt: CostReceipt | undefined\n try {\n const request = {\n model: opts.model,\n messages: [\n {\n role: 'system' as const,\n content:\n 'You are a strict code-review judge. Return strict JSON only. No prose outside the JSON. A keyword in a comment is NOT a working implementation.',\n },\n { role: 'user' as const, content: buildPrompt(input, opts) },\n ],\n jsonSchema: { name: 'semantic_concept_judge', schema: SEMANTIC_SCHEMA },\n temperature: 0,\n maxTokens: opts.maxTokens,\n timeoutMs: opts.timeoutMs,\n } satisfies LlmCallRequest\n const paid = await opts.costLedger.runPaidCall({\n channel: 'judge',\n phase: opts.costPhase,\n actor: 'semantic-concept',\n model: opts.model,\n ...(Object.keys(opts.costTags).length > 0 ? { tags: opts.costTags } : {}),\n maximumCharge: maximumChargeForLlmRequest(request, opts.llm),\n signal: opts.signal,\n execute: (signal, callId) =>\n callLlmJson<{ summary: string; concepts: ConceptFinding[] }>(request, {\n ...opts.llm,\n signal,\n idempotencyKey: callId,\n }),\n receipt: ({ result }) => costReceiptFromLlm(result),\n receiptFromError: costReceiptFromLlmError,\n })\n receipt = paid.receipt\n if (!paid.succeeded) throw paid.error\n const { value } = paid.value\n\n if (!value?.concepts || !Array.isArray(value.concepts)) {\n throw new Error('judge returned malformed response — expected array under \"concepts\"')\n }\n\n const findings: ConceptFinding[] = value.concepts.map((c) => ({\n concept: String(c.concept),\n present: Boolean(c.present),\n score: Math.max(0, Math.min(10, Number(c.score ?? 0))),\n evidence: String(c.evidence ?? ''),\n severity: (['critical', 'major', 'minor', 'info'] as const).includes(c.severity)\n ? c.severity\n : 'info',\n }))\n\n const presentCount = findings.filter((f) => f.present && f.score >= 7).length\n let weightSum = 0\n let weightedScoreSum = 0\n for (const f of findings) {\n const w = weightByName.get(f.concept) ?? 1\n weightSum += w\n weightedScoreSum += w * f.score\n }\n const scoreAvg =\n weightSum > 0\n ? weightedScoreSum / weightSum\n : findings.reduce((a, f) => a + f.score, 0) / Math.max(1, findings.length)\n\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: Number((scoreAvg / 10).toFixed(3)),\n presentCount,\n totalCount,\n findings,\n summary: String(value.summary ?? ''),\n durationMs: Date.now() - start,\n costUsd: paid.receipt.costUnknown ? null : paid.receipt.costUsd,\n available: true,\n }\n } catch (err) {\n return {\n kind: 'semantic-concept',\n version: SEMANTIC_CONCEPT_JUDGE_VERSION,\n score: 0,\n presentCount: 0,\n totalCount,\n findings: [],\n summary: '',\n durationMs: Date.now() - start,\n costUsd: receipt && !receipt.costUnknown ? receipt.costUsd : null,\n available: false,\n error: err instanceof Error ? err.message : String(err),\n }\n }\n}\n\n/**\n * Factory: pin LLM options once, return a closure that accepts inputs.\n * Convenient for pipelines that want to share a single LlmClient config.\n */\nexport function createSemanticConceptJudge(\n options: SemanticConceptJudgeOptions = {},\n): (input: SemanticConceptJudgeInput) => Promise<SemanticConceptJudgeResult> {\n return (input) => runSemanticConceptJudge(input, options)\n}\n"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { t as canonicalize } from "./pre-registration-DakwTRXk.js";
2
- import { C as mulberry32, m as eProcess } from "./statistics-ByxzSiOM.js";
3
- import { l as pairHoldout } from "./promotion-policy-CrLrmys8.js";
2
+ import { c as mulberry32 } from "./internal-BDHPCnjk.js";
3
+ import { t as eProcess } from "./sequential-eprocess-CbUt2htw.js";
4
+ import { o as pairHoldout } from "./power-preflight-DEw-uC7q.js";
4
5
  import { createHash } from "node:crypto";
5
6
  //#region src/campaign/gates/sequential.ts
6
7
  /**
@@ -296,4 +297,4 @@ function sequentialDecide(options = {}) {
296
297
  //#endregion
297
298
  export { sequentialPairedGate as n, sequentialDecide as t };
298
299
 
299
- //# sourceMappingURL=sequential-D-BLJBKU.js.map
300
+ //# sourceMappingURL=sequential-C458DXNf.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sequential-D-BLJBKU.js","names":[],"sources":["../src/campaign/gates/sequential.ts"],"sourcesContent":["/**\n * Anytime-valid sequential promotion gate — an e-process (betting\n * test-martingale, see `eProcess` in `statistics.ts`) over paired\n * per-scenario deltas, so a campaign stops the MOMENT evidence decides\n * instead of burning a fixed-n budget. Decisions remain valid at any\n * data-dependent stopping time (Ville's inequality), which is exactly what\n * fixed-n machinery cannot offer: peeking at a bootstrap CI after every\n * observation and stopping on the first significant peek inflates type-I\n * error far beyond alpha.\n *\n * REPLACES, never layers on, a fixed-n gate. Running `heldoutSignificance`\n * or `paretoSignificanceGate` repeatedly on a growing sample and stopping\n * early is optional stopping no matter how it is dressed up; this gate is\n * the valid way to stop early. Use one or the other per evidence stream.\n *\n * Pre-registration binding: anytime validity holds only for the\n * PRE-REGISTERED statistic. When a `SignedManifest` is bound, the gate takes\n * alpha from `manifest.alpha`, the observation budget from\n * `manifest.preRegisteredN`, orients deltas by `manifest.direction`, and\n * shifts the null boundary by `manifest.minEffect` — re-deciding the same\n * stream under different parameters after seeing data would reopen optional\n * stopping under a fancier name. The manifest's content hash is verified at\n * construction (sync, same `sha256-content` scheme as `signManifest`).\n *\n * Non-iid caveat (stated honestly): the supermartingale guarantee needs each\n * delta's conditional mean under H0 to stay ≤ the null boundary given the\n * past — exchangeable scenario deltas suffice. Scenario streams ordered by\n * difficulty or by scenario family violate this; `decide(ctx)` therefore\n * shuffles the paired deltas with a SEEDED permutation by default (the\n * permutation is data-independent, so bet predictability is preserved).\n * Stratified betting (per-stratum λ) is future work, not implemented here.\n */\n\nimport { createHash } from 'node:crypto'\nimport { canonicalize, type SignedManifest } from '../../pre-registration'\nimport { type EProcessState, eProcess, mulberry32 } from '../../statistics'\nimport type { Gate, GateContext, GateResult, GenerationRecord, Scenario } from '../types'\nimport { pairHoldout } from './statistical-heldout'\n\nexport type SequentialDecision = 'promote' | 'continue' | 'undecided-at-maxN'\n\nexport interface SequentialObservation {\n decision: SequentialDecision\n /** Current e-value (the betting wealth) against H0. */\n eValue: number\n /** Paired deltas consumed so far. */\n n: number\n /** Names the decision basis. For 'undecided-at-maxN' it states explicitly\n * that exhausting the budget is NOT evidence of no effect. */\n reason: string\n}\n\nexport interface SequentialPairedGateOptions {\n /** Type-I budget. With `preRegistration` bound this MUST match\n * `manifest.alpha` (conflict throws). Default 0.05. */\n alpha?: number\n /** Minimum paired deltas before a promote may fire. The stopping rule is\n * \"first n ≥ minN with e-value ≥ 1/alpha\" — still a valid stopping time.\n * Default 5. */\n minN?: number\n /** Pre-registered observation budget. Required unless `preRegistration`\n * supplies it via `preRegisteredN` (conflict throws). */\n maxN?: number\n /** Bet truncation forwarded to `eProcess`. Default 0.5. */\n maxBet?: number\n /** Bound on |delta| in the judge's native scale; deltas are mapped to\n * x = (d/scale + 1)/2 ∈ [0,1]. A delta outside ±scale throws (use\n * `detectScale` to pick 1 vs 100 BEFORE streaming). Default 1. */\n scale?: number\n /** Seed for the data-independent shuffle of paired deltas in `decide(ctx)`\n * (exchangeability guard). Default 1337. */\n shuffleSeed?: number\n /** Bind the pre-registered hypothesis. Verified (content hash) at\n * construction; alpha/maxN/direction/minEffect come FROM the manifest. */\n preRegistration?: SignedManifest\n /** Override the gate name in reports. */\n name?: string\n}\n\nexport interface SequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario>\n extends Gate<TArtifact, TScenario> {\n /** Streaming entry point: feed one paired per-scenario delta\n * (candidate − baseline, native scale). Each gate instance carries ONE\n * observe-stream; `decide(ctx)` runs on its own fresh stream and never\n * consumes or advances this one. 'promote' is sticky; observing past the\n * pre-registered maxN throws (extending a finished stream after seeing\n * the result reopens optional stopping — start a NEW pre-registered\n * test). */\n observe(delta: number): SequentialObservation\n /** Read-only snapshot of the observe-stream. */\n state(): EProcessState & { decision: SequentialDecision }\n}\n\ninterface ResolvedConfig {\n alpha: number\n minN: number\n maxN: number\n maxBet: number\n scale: number\n shuffleSeed: number\n /** 'decrease' negates deltas so \"better\" is always positive. */\n direction: 'increase' | 'decrease'\n /** H0 boundary in x-space: 1/2 + minEffect/(2·scale). */\n nullMean: number\n minEffect: number\n}\n\n/** Sync twin of `verifyManifest` — same `sha256-content` scheme\n * (sha256 over the canonicalized manifest minus contentHash/algo), via\n * node:crypto so gate construction can stay synchronous and fail loud\n * before any observation is consumed. */\nfunction verifyManifestSync(m: SignedManifest): boolean {\n if (m.algo !== undefined && m.algo !== 'sha256-content') {\n throw new Error(`sequentialPairedGate: unrecognized manifest hash algo '${m.algo}'`)\n }\n const { contentHash, algo: _algo, ...rest } = m\n void _algo\n const bytes = JSON.stringify(canonicalize(rest))\n const hash = createHash('sha256').update(bytes, 'utf8').digest('hex')\n return hash === contentHash\n}\n\nfunction resolveConfig(opts: SequentialPairedGateOptions): ResolvedConfig {\n const m = opts.preRegistration\n let alpha: number\n let maxN: number\n let direction: 'increase' | 'decrease'\n let minEffect: number\n if (m) {\n if (!verifyManifestSync(m)) {\n throw new Error(\n `sequentialPairedGate: pre-registration manifest '${m.id}' content hash mismatch (tampered)`,\n )\n }\n if (opts.alpha !== undefined && opts.alpha !== m.alpha) {\n throw new Error(\n `sequentialPairedGate: alpha ${opts.alpha} conflicts with pre-registered alpha ${m.alpha} — ` +\n `the registered statistic is the only one anytime validity covers`,\n )\n }\n if (opts.maxN !== undefined && opts.maxN !== m.preRegisteredN) {\n throw new Error(\n `sequentialPairedGate: maxN ${opts.maxN} conflicts with pre-registered N ${m.preRegisteredN}`,\n )\n }\n alpha = m.alpha\n maxN = m.preRegisteredN\n direction = m.direction\n minEffect = m.minEffect\n } else {\n alpha = opts.alpha ?? 0.05\n if (opts.maxN === undefined) {\n throw new Error(\n 'sequentialPairedGate: maxN is required (or bind a preRegistration manifest whose ' +\n 'preRegisteredN is the budget) — an unbounded stream has no pre-registered budget',\n )\n }\n maxN = opts.maxN\n direction = 'increase'\n minEffect = 0\n }\n const scale = opts.scale ?? 1\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`sequentialPairedGate: scale must be > 0, got ${scale}`)\n }\n if (!Number.isInteger(maxN) || maxN < 1) {\n throw new Error(`sequentialPairedGate: maxN must be a positive integer, got ${maxN}`)\n }\n const minN = opts.minN ?? 5\n if (!Number.isInteger(minN) || minN < 1 || minN > maxN) {\n throw new Error(\n `sequentialPairedGate: minN must be an integer in [1, maxN=${maxN}], got ${minN}`,\n )\n }\n if (!Number.isFinite(minEffect) || minEffect < 0 || minEffect >= scale) {\n throw new Error(\n `sequentialPairedGate: minEffect must be in [0, scale=${scale}), got ${minEffect}`,\n )\n }\n // H0: mean delta ≤ minEffect. In x = (d/scale + 1)/2 space that is\n // E[x] ≤ 1/2 + minEffect/(2·scale) — a shifted null boundary, NOT a clamp\n // of the observations (clamping is mean-distorting for skewed deltas).\n const nullMean = 0.5 + minEffect / (2 * scale)\n return {\n alpha,\n minN,\n maxN,\n maxBet: opts.maxBet ?? 0.5,\n scale,\n shuffleSeed: opts.shuffleSeed ?? 1337,\n direction,\n nullMean,\n minEffect,\n }\n}\n\ninterface SequentialStream {\n observe(delta: number): SequentialObservation\n state(): EProcessState & { decision: SequentialDecision }\n}\n\nfunction makeStream(cfg: ResolvedConfig): SequentialStream {\n const proc = eProcess({ alpha: cfg.alpha, maxBet: cfg.maxBet, nullMean: cfg.nullMean })\n const threshold = 1 / cfg.alpha\n let terminal: SequentialDecision | undefined\n return {\n observe(delta: number): SequentialObservation {\n if (terminal === 'undecided-at-maxN') {\n throw new Error(\n `sequentialPairedGate: pre-registered maxN=${cfg.maxN} exhausted — extending the ` +\n 'stream after seeing the result reopens optional stopping; start a NEW ' +\n 'pre-registered test',\n )\n }\n if (!Number.isFinite(delta) || Math.abs(delta) > cfg.scale) {\n throw new Error(\n `sequentialPairedGate: delta ${delta} outside ±scale=${cfg.scale} — pass the judge's ` +\n `native scale explicitly (detectScale helps pick 1 vs 100)`,\n )\n }\n const d = cfg.direction === 'decrease' ? -delta : delta\n const step = proc.update((d / cfg.scale + 1) / 2)\n // Stopping rule: FIRST n ≥ minN with current wealth ≥ 1/alpha — a valid\n // stopping time (measurable w.r.t. the past), sticky once latched. The\n // core's own `decided` latch ignores minN, so the gate re-derives the\n // decision from current wealth here.\n if (terminal === undefined && step.n >= cfg.minN && step.wealth >= threshold) {\n terminal = 'promote'\n }\n if (terminal === 'promote') {\n return {\n decision: 'promote',\n eValue: step.wealth,\n n: step.n,\n reason:\n `e-value ${step.wealth.toFixed(2)} ≥ 1/α=${threshold.toFixed(2)} at n=${step.n} ` +\n `(minN=${cfg.minN}): the paired improvement exceeds ${cfg.minEffect} at anytime-valid ` +\n `level α=${cfg.alpha}`,\n }\n }\n if (step.n >= cfg.maxN) {\n terminal = 'undecided-at-maxN'\n return {\n decision: 'undecided-at-maxN',\n eValue: step.wealth,\n n: step.n,\n reason:\n `undecided at pre-registered maxN=${cfg.maxN} (e-value ${step.wealth.toFixed(2)} < ` +\n `1/α=${threshold.toFixed(2)}). This is NOT evidence of no effect — the effect may be ` +\n 'real but smaller than this budget can detect; re-register with a larger N to test that',\n }\n }\n return {\n decision: 'continue',\n eValue: step.wealth,\n n: step.n,\n reason: `e-value ${step.wealth.toFixed(2)} < 1/α=${threshold.toFixed(2)} at n=${step.n}/${cfg.maxN} — keep observing`,\n }\n },\n state() {\n return { ...proc.state(), decision: terminal ?? 'continue' }\n },\n }\n}\n\n/** Data-independent in-place Fisher–Yates with a seeded PRNG — the permutation\n * depends only on the seed, never the values, so bet predictability survives. */\nfunction seededShuffle<T>(items: T[], seed: number): T[] {\n const rng = mulberry32(seed)\n for (let i = items.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n const tmp = items[i]!\n items[i] = items[j]!\n items[j] = tmp\n }\n return items\n}\n\n/**\n * Anytime-valid sequential paired gate. Conforms to the existing `Gate`\n * contract (`decide(ctx)` consumes candidate vs baseline judge scores via\n * `pairHoldout` — same pairing granularity as the fixed-n gates: full cellId,\n * never scenarioId) and adds a streaming `observe(delta)` entry for campaigns\n * that score cells incrementally and want to stop mid-stream.\n *\n * Decision mapping onto the substrate's five-valued `GateDecision`:\n * - 'promote' → 'ship'\n * - 'continue' → 'need_more_work' (stream ended before maxN with\n * the e-value undecided — more reps could decide)\n * - 'undecided-at-maxN' → 'hold', with the reason stating it is NOT\n * evidence of no effect (never a silent default)\n */\nexport function sequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(\n options: SequentialPairedGateOptions,\n): SequentialPairedGate<TArtifact, TScenario> {\n const cfg = resolveConfig(options)\n const name = options.name ?? 'sequentialPairedGate'\n const manifest = options.preRegistration\n const observeStream = makeStream(cfg)\n\n return {\n name,\n observe(delta: number): SequentialObservation {\n return observeStream.observe(delta)\n },\n state() {\n return observeStream.state()\n },\n async decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult> {\n if (!ctx.baselineJudgeScores) {\n throw new Error(\n `${name}: ctx.baselineJudgeScores is required — falling back to the candidate's own ` +\n 'scores would compare the candidate against itself (delta 0, silent no-op)',\n )\n }\n const scenarioIds = new Set(ctx.scenarios.map((s) => s.id))\n const paired = pairHoldout(\n ctx.judgeScores,\n ctx.baselineJudgeScores,\n scenarioIds,\n (s) => s.composite,\n )\n const deltas = paired.after.map((a, i) => a - paired.before[i]!)\n seededShuffle(deltas, cfg.shuffleSeed)\n\n const stream = makeStream(cfg)\n let last: SequentialObservation | undefined\n for (const d of deltas) {\n last = stream.observe(d)\n if (last.decision !== 'continue') break\n }\n\n const detail = {\n ...stream.state(),\n minN: cfg.minN,\n maxN: cfg.maxN,\n scale: cfg.scale,\n shuffleSeed: cfg.shuffleSeed,\n direction: cfg.direction,\n minEffect: cfg.minEffect,\n pairedN: deltas.length,\n ...(manifest ? { preRegisteredId: manifest.id, metric: manifest.metric } : {}),\n }\n const meanDelta =\n deltas.length === 0 ? undefined : deltas.reduce((s, d) => s + d, 0) / deltas.length\n\n if (last === undefined) {\n return {\n decision: 'need_more_work',\n reasons: [`${name}: no paired holdout observations — nothing to test`],\n contributingGates: [{ name, status: 'not_evaluated', detail }],\n }\n }\n const decision =\n last.decision === 'promote'\n ? 'ship'\n : last.decision === 'continue'\n ? 'need_more_work'\n : 'hold'\n return {\n decision,\n reasons: [`${name}: ${last.reason}`],\n contributingGates: [\n {\n name,\n status:\n decision === 'ship'\n ? 'pass'\n : decision === 'need_more_work'\n ? 'not_evaluated'\n : 'fail',\n detail,\n },\n ],\n delta: meanDelta,\n }\n },\n }\n}\n\nexport interface SequentialDecideOptions {\n /** Type-I budget for the early-stop evidence. Default 0.05. */\n alpha?: number\n /** Minimum paired deltas before a stop may fire. Default 5. */\n minN?: number\n /** Bet truncation forwarded to `eProcess`. Default 0.5. */\n maxBet?: number\n /** Bound on |per-scenario composite delta|. Default 1. */\n scale?: number\n}\n\nexport interface SequentialDecideFn {\n (args: { history: GenerationRecord[] }): { stop: boolean; reason?: string }\n /** Read-only snapshot of the accumulated e-process (observability + tests). */\n state(): EProcessState\n}\n\n/**\n * `SurfaceProposer.decide` adapter — stops the optimization loop the moment\n * the e-process decides the loop has produced a real improvement, instead of\n * always running `maxGenerations`.\n *\n * Stream: for each generation g ≥ 1, the per-scenario composite deltas of\n * generation g's top candidate vs the generation-0 top candidate (the\n * incumbent the loop set out to beat), paired by scenarioId. H0: no proposed\n * surface improves any scenario's expected composite over the incumbent —\n * under it every delta has conditional mean ≤ 0 and the e-process is valid.\n * Once wealth ≥ 1/alpha the loop stops and hands the winner to the promotion\n * gate (which re-scores on HELD-OUT data — this adapter only spends the\n * exploration budget, it never promotes).\n *\n * Honesty caveats: (1) the incumbent's scores are measured once and shared\n * across all generations' deltas, so type-I control is exact only insofar as\n * those scores approximate the incumbent's true per-scenario means (more reps\n * → tighter); (2) an UNDECIDED process never stops the loop — absence of a\n * crossing is NOT evidence of no effect, so the loop simply runs its normal\n * course. Calling the adapter repeatedly with a growing history consumes each\n * generation exactly once (re-feeding an already-seen record would double-count\n * evidence).\n */\nexport function sequentialDecide(options: SequentialDecideOptions = {}): SequentialDecideFn {\n const alpha = options.alpha ?? 0.05\n const minN = options.minN ?? 5\n const scale = options.scale ?? 1\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`sequentialDecide: scale must be > 0, got ${scale}`)\n }\n const proc = eProcess({ alpha, maxBet: options.maxBet ?? 0.5, nullMean: 0.5 })\n const threshold = 1 / alpha\n let processedGenerations = 0\n let reference: Map<string, number> | undefined\n let stopped: { stop: true; reason: string } | undefined\n\n const topCandidate = (record: GenerationRecord) => {\n if (record.candidates.length === 0) {\n throw new Error(\n `sequentialDecide: generation ${record.generationIndex} has no candidates — ` +\n 'cannot extract a top candidate',\n )\n }\n return record.candidates.reduce((best, candidate) => {\n if (best.composite === null) return candidate\n if (candidate.composite === null) return best\n return candidate.composite > best.composite ? candidate : best\n })\n }\n\n const decide: SequentialDecideFn = ({ history }) => {\n if (stopped) return stopped\n if (history.length === 0) return { stop: false }\n if (reference === undefined) {\n reference = new Map(\n topCandidate(history[0]!).scenarios.map((s) => [s.scenarioId, s.composite]),\n )\n }\n // Consume only generations not yet seen; start at 1 — generation 0 IS the\n // reference and would contribute all-zero deltas (pure wealth dilution).\n for (let g = Math.max(1, processedGenerations); g < history.length; g++) {\n const top = topCandidate(history[g]!)\n const byScenario = new Map(top.scenarios.map((s) => [s.scenarioId, s.composite]))\n for (const [scenarioId, refComposite] of reference) {\n const candComposite = byScenario.get(scenarioId)\n if (candComposite === undefined) {\n throw new Error(\n `sequentialDecide: generation ${history[g]!.generationIndex} top candidate is missing ` +\n `scenario '${scenarioId}' — generations must score the same scenario set to pair`,\n )\n }\n const delta = candComposite - refComposite\n if (!Number.isFinite(delta) || Math.abs(delta) > scale) {\n throw new Error(\n `sequentialDecide: paired delta ${delta} outside ±scale=${scale} on scenario ` +\n `'${scenarioId}' — pass the composite scale explicitly`,\n )\n }\n const step = proc.update((delta / scale + 1) / 2)\n if (step.n >= minN && step.wealth >= threshold) {\n stopped = {\n stop: true,\n reason:\n `sequential e-process decided at generation ${history[g]!.generationIndex}: ` +\n `e-value ${step.wealth.toFixed(2)} ≥ 1/α=${threshold.toFixed(2)} after n=${step.n} ` +\n 'paired deltas vs the generation-0 incumbent — the improvement is real at ' +\n `α=${alpha}; stop exploring and promote via the gate`,\n }\n processedGenerations = history.length\n return stopped\n }\n }\n }\n processedGenerations = history.length\n return { stop: false }\n }\n decide.state = () => proc.state()\n return decide\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+GA,SAAS,mBAAmB,GAA4B;CACtD,IAAI,EAAE,SAAS,KAAA,KAAa,EAAE,SAAS,kBACrC,MAAM,IAAI,MAAM,0DAA0D,EAAE,KAAK,EAAE;CAErF,MAAM,EAAE,aAAa,MAAM,OAAO,GAAG,SAAS;CAE9C,MAAM,QAAQ,KAAK,UAAU,aAAa,IAAI,CAAC;CAE/C,OADa,WAAW,QAAQ,CAAC,CAAC,OAAO,OAAO,MAAM,CAAC,CAAC,OAAO,KACrD,MAAM;AAClB;AAEA,SAAS,cAAc,MAAmD;CACxE,MAAM,IAAI,KAAK;CACf,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,GAAG;EACL,IAAI,CAAC,mBAAmB,CAAC,GACvB,MAAM,IAAI,MACR,oDAAoD,EAAE,GAAG,mCAC3D;EAEF,IAAI,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU,EAAE,OAC/C,MAAM,IAAI,MACR,+BAA+B,KAAK,MAAM,uCAAuC,EAAE,MAAM,oEAE3F;EAEF,IAAI,KAAK,SAAS,KAAA,KAAa,KAAK,SAAS,EAAE,gBAC7C,MAAM,IAAI,MACR,8BAA8B,KAAK,KAAK,mCAAmC,EAAE,gBAC/E;EAEF,QAAQ,EAAE;EACV,OAAO,EAAE;EACT,YAAY,EAAE;EACd,YAAY,EAAE;CAChB,OAAO;EACL,QAAQ,KAAK,SAAS;EACtB,IAAI,KAAK,SAAS,KAAA,GAChB,MAAM,IAAI,MACR,mKAEF;EAEF,OAAO,KAAK;EACZ,YAAY;EACZ,YAAY;CACd;CACA,MAAM,QAAQ,KAAK,SAAS;CAC5B,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GACtC,MAAM,IAAI,MAAM,gDAAgD,OAAO;CAEzE,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,GACpC,MAAM,IAAI,MAAM,8DAA8D,MAAM;CAEtF,MAAM,OAAO,KAAK,QAAQ;CAC1B,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,KAAK,OAAO,MAChD,MAAM,IAAI,MACR,6DAA6D,KAAK,SAAS,MAC7E;CAEF,IAAI,CAAC,OAAO,SAAS,SAAS,KAAK,YAAY,KAAK,aAAa,OAC/D,MAAM,IAAI,MACR,wDAAwD,MAAM,SAAS,WACzE;CAKF,MAAM,WAAW,KAAM,aAAa,IAAI;CACxC,OAAO;EACL;EACA;EACA;EACA,QAAQ,KAAK,UAAU;EACvB;EACA,aAAa,KAAK,eAAe;EACjC;EACA;EACA;CACF;AACF;AAOA,SAAS,WAAW,KAAuC;CACzD,MAAM,OAAO,SAAS;EAAE,OAAO,IAAI;EAAO,QAAQ,IAAI;EAAQ,UAAU,IAAI;CAAS,CAAC;CACtF,MAAM,YAAY,IAAI,IAAI;CAC1B,IAAI;CACJ,OAAO;EACL,QAAQ,OAAsC;GAC5C,IAAI,aAAa,qBACf,MAAM,IAAI,MACR,6CAA6C,IAAI,KAAK,qHAGxD;GAEF,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,IAAI,OACnD,MAAM,IAAI,MACR,+BAA+B,MAAM,kBAAkB,IAAI,MAAM,8EAEnE;GAEF,MAAM,IAAI,IAAI,cAAc,aAAa,CAAC,QAAQ;GAClD,MAAM,OAAO,KAAK,QAAQ,IAAI,IAAI,QAAQ,KAAK,CAAC;GAKhD,IAAI,aAAa,KAAA,KAAa,KAAK,KAAK,IAAI,QAAQ,KAAK,UAAU,WACjE,WAAW;GAEb,IAAI,aAAa,WACf,OAAO;IACL,UAAU;IACV,QAAQ,KAAK;IACb,GAAG,KAAK;IACR,QACE,WAAW,KAAK,OAAO,QAAQ,CAAC,EAAE,SAAS,UAAU,QAAQ,CAAC,EAAE,QAAQ,KAAK,EAAE,SACtE,IAAI,KAAK,oCAAoC,IAAI,UAAU,4BACzD,IAAI;GACnB;GAEF,IAAI,KAAK,KAAK,IAAI,MAAM;IACtB,WAAW;IACX,OAAO;KACL,UAAU;KACV,QAAQ,KAAK;KACb,GAAG,KAAK;KACR,QACE,oCAAoC,IAAI,KAAK,YAAY,KAAK,OAAO,QAAQ,CAAC,EAAE,SACzE,UAAU,QAAQ,CAAC,EAAE;IAEhC;GACF;GACA,OAAO;IACL,UAAU;IACV,QAAQ,KAAK;IACb,GAAG,KAAK;IACR,QAAQ,WAAW,KAAK,OAAO,QAAQ,CAAC,EAAE,SAAS,UAAU,QAAQ,CAAC,EAAE,QAAQ,KAAK,EAAE,GAAG,IAAI,KAAK;GACrG;EACF;EACA,QAAQ;GACN,OAAO;IAAE,GAAG,KAAK,MAAM;IAAG,UAAU,YAAY;GAAW;EAC7D;CACF;AACF;;;AAIA,SAAS,cAAiB,OAAY,MAAmB;CACvD,MAAM,MAAM,WAAW,IAAI;CAC3B,KAAK,IAAI,IAAI,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK;EACzC,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK,IAAI,EAAE;EACpC,MAAM,MAAM,MAAM;EAClB,MAAM,KAAK,MAAM;EACjB,MAAM,KAAK;CACb;CACA,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,SAAgB,qBACd,SAC4C;CAC5C,MAAM,MAAM,cAAc,OAAO;CACjC,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,WAAW,QAAQ;CACzB,MAAM,gBAAgB,WAAW,GAAG;CAEpC,OAAO;EACL;EACA,QAAQ,OAAsC;GAC5C,OAAO,cAAc,QAAQ,KAAK;EACpC;EACA,QAAQ;GACN,OAAO,cAAc,MAAM;EAC7B;EACA,MAAM,OAAO,KAA6D;GACxE,IAAI,CAAC,IAAI,qBACP,MAAM,IAAI,MACR,GAAG,KAAK,sJAEV;GAEF,MAAM,cAAc,IAAI,IAAI,IAAI,UAAU,KAAK,MAAM,EAAE,EAAE,CAAC;GAC1D,MAAM,SAAS,YACb,IAAI,aACJ,IAAI,qBACJ,cACC,MAAM,EAAE,SACX;GACA,MAAM,SAAS,OAAO,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,OAAO,EAAG;GAC/D,cAAc,QAAQ,IAAI,WAAW;GAErC,MAAM,SAAS,WAAW,GAAG;GAC7B,IAAI;GACJ,KAAK,MAAM,KAAK,QAAQ;IACtB,OAAO,OAAO,QAAQ,CAAC;IACvB,IAAI,KAAK,aAAa,YAAY;GACpC;GAEA,MAAM,SAAS;IACb,GAAG,OAAO,MAAM;IAChB,MAAM,IAAI;IACV,MAAM,IAAI;IACV,OAAO,IAAI;IACX,aAAa,IAAI;IACjB,WAAW,IAAI;IACf,WAAW,IAAI;IACf,SAAS,OAAO;IAChB,GAAI,WAAW;KAAE,iBAAiB,SAAS;KAAI,QAAQ,SAAS;IAAO,IAAI,CAAC;GAC9E;GACA,MAAM,YACJ,OAAO,WAAW,IAAI,KAAA,IAAY,OAAO,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,OAAO;GAE/E,IAAI,SAAS,KAAA,GACX,OAAO;IACL,UAAU;IACV,SAAS,CAAC,GAAG,KAAK,mDAAmD;IACrE,mBAAmB,CAAC;KAAE;KAAM,QAAQ;KAAiB;IAAO,CAAC;GAC/D;GAEF,MAAM,WACJ,KAAK,aAAa,YACd,SACA,KAAK,aAAa,aAChB,mBACA;GACR,OAAO;IACL;IACA,SAAS,CAAC,GAAG,KAAK,IAAI,KAAK,QAAQ;IACnC,mBAAmB,CACjB;KACE;KACA,QACE,aAAa,SACT,SACA,aAAa,mBACX,kBACA;KACR;IACF,CACF;IACA,OAAO;GACT;EACF;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,iBAAiB,UAAmC,CAAC,GAAuB;CAC1F,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,QAAQ,SAAS;CAC/B,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GACtC,MAAM,IAAI,MAAM,4CAA4C,OAAO;CAErE,MAAM,OAAO,SAAS;EAAE;EAAO,QAAQ,QAAQ,UAAU;EAAK,UAAU;CAAI,CAAC;CAC7E,MAAM,YAAY,IAAI;CACtB,IAAI,uBAAuB;CAC3B,IAAI;CACJ,IAAI;CAEJ,MAAM,gBAAgB,WAA6B;EACjD,IAAI,OAAO,WAAW,WAAW,GAC/B,MAAM,IAAI,MACR,gCAAgC,OAAO,gBAAgB,oDAEzD;EAEF,OAAO,OAAO,WAAW,QAAQ,MAAM,cAAc;GACnD,IAAI,KAAK,cAAc,MAAM,OAAO;GACpC,IAAI,UAAU,cAAc,MAAM,OAAO;GACzC,OAAO,UAAU,YAAY,KAAK,YAAY,YAAY;EAC5D,CAAC;CACH;CAEA,MAAM,UAA8B,EAAE,cAAc;EAClD,IAAI,SAAS,OAAO;EACpB,IAAI,QAAQ,WAAW,GAAG,OAAO,EAAE,MAAM,MAAM;EAC/C,IAAI,cAAc,KAAA,GAChB,YAAY,IAAI,IACd,aAAa,QAAQ,EAAG,CAAC,CAAC,UAAU,KAAK,MAAM,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,CAC5E;EAIF,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,oBAAoB,GAAG,IAAI,QAAQ,QAAQ,KAAK;GACvE,MAAM,MAAM,aAAa,QAAQ,EAAG;GACpC,MAAM,aAAa,IAAI,IAAI,IAAI,UAAU,KAAK,MAAM,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;GAChF,KAAK,MAAM,CAAC,YAAY,iBAAiB,WAAW;IAClD,MAAM,gBAAgB,WAAW,IAAI,UAAU;IAC/C,IAAI,kBAAkB,KAAA,GACpB,MAAM,IAAI,MACR,gCAAgC,QAAQ,EAAE,CAAE,gBAAgB,sCAC7C,WAAW,yDAC5B;IAEF,MAAM,QAAQ,gBAAgB;IAC9B,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,OAC/C,MAAM,IAAI,MACR,kCAAkC,MAAM,kBAAkB,MAAM,gBAC1D,WAAW,wCACnB;IAEF,MAAM,OAAO,KAAK,QAAQ,QAAQ,QAAQ,KAAK,CAAC;IAChD,IAAI,KAAK,KAAK,QAAQ,KAAK,UAAU,WAAW;KAC9C,UAAU;MACR,MAAM;MACN,QACE,8CAA8C,QAAQ,EAAE,CAAE,gBAAgB,YAC/D,KAAK,OAAO,QAAQ,CAAC,EAAE,SAAS,UAAU,QAAQ,CAAC,EAAE,WAAW,KAAK,EAAE,8EAE7E,MAAM;KACf;KACA,uBAAuB,QAAQ;KAC/B,OAAO;IACT;GACF;EACF;EACA,uBAAuB,QAAQ;EAC/B,OAAO,EAAE,MAAM,MAAM;CACvB;CACA,OAAO,cAAc,KAAK,MAAM;CAChC,OAAO;AACT"}
1
+ {"version":3,"file":"sequential-C458DXNf.js","names":[],"sources":["../src/campaign/gates/sequential.ts"],"sourcesContent":["/**\n * Anytime-valid sequential promotion gate — an e-process (betting\n * test-martingale, see `eProcess` in `statistics.ts`) over paired\n * per-scenario deltas, so a campaign stops the MOMENT evidence decides\n * instead of burning a fixed-n budget. Decisions remain valid at any\n * data-dependent stopping time (Ville's inequality), which is exactly what\n * fixed-n machinery cannot offer: peeking at a bootstrap CI after every\n * observation and stopping on the first significant peek inflates type-I\n * error far beyond alpha.\n *\n * REPLACES, never layers on, a fixed-n gate. Running `heldoutSignificance`\n * or `paretoSignificanceGate` repeatedly on a growing sample and stopping\n * early is optional stopping no matter how it is dressed up; this gate is\n * the valid way to stop early. Use one or the other per evidence stream.\n *\n * Pre-registration binding: anytime validity holds only for the\n * PRE-REGISTERED statistic. When a `SignedManifest` is bound, the gate takes\n * alpha from `manifest.alpha`, the observation budget from\n * `manifest.preRegisteredN`, orients deltas by `manifest.direction`, and\n * shifts the null boundary by `manifest.minEffect` — re-deciding the same\n * stream under different parameters after seeing data would reopen optional\n * stopping under a fancier name. The manifest's content hash is verified at\n * construction (sync, same `sha256-content` scheme as `signManifest`).\n *\n * Non-iid caveat (stated honestly): the supermartingale guarantee needs each\n * delta's conditional mean under H0 to stay ≤ the null boundary given the\n * past — exchangeable scenario deltas suffice. Scenario streams ordered by\n * difficulty or by scenario family violate this; `decide(ctx)` therefore\n * shuffles the paired deltas with a SEEDED permutation by default (the\n * permutation is data-independent, so bet predictability is preserved).\n * Stratified betting (per-stratum λ) is future work, not implemented here.\n */\n\nimport { createHash } from 'node:crypto'\nimport { canonicalize, type SignedManifest } from '../../pre-registration'\nimport { type EProcessState, eProcess, mulberry32 } from '../../statistics'\nimport type { Gate, GateContext, GateResult, GenerationRecord, Scenario } from '../types'\nimport { pairHoldout } from './statistical-heldout'\n\nexport type SequentialDecision = 'promote' | 'continue' | 'undecided-at-maxN'\n\nexport interface SequentialObservation {\n decision: SequentialDecision\n /** Current e-value (the betting wealth) against H0. */\n eValue: number\n /** Paired deltas consumed so far. */\n n: number\n /** Names the decision basis. For 'undecided-at-maxN' it states explicitly\n * that exhausting the budget is NOT evidence of no effect. */\n reason: string\n}\n\nexport interface SequentialPairedGateOptions {\n /** Type-I budget. With `preRegistration` bound this MUST match\n * `manifest.alpha` (conflict throws). Default 0.05. */\n alpha?: number\n /** Minimum paired deltas before a promote may fire. The stopping rule is\n * \"first n ≥ minN with e-value ≥ 1/alpha\" — still a valid stopping time.\n * Default 5. */\n minN?: number\n /** Pre-registered observation budget. Required unless `preRegistration`\n * supplies it via `preRegisteredN` (conflict throws). */\n maxN?: number\n /** Bet truncation forwarded to `eProcess`. Default 0.5. */\n maxBet?: number\n /** Bound on |delta| in the judge's native scale; deltas are mapped to\n * x = (d/scale + 1)/2 ∈ [0,1]. A delta outside ±scale throws (use\n * `detectScale` to pick 1 vs 100 BEFORE streaming). Default 1. */\n scale?: number\n /** Seed for the data-independent shuffle of paired deltas in `decide(ctx)`\n * (exchangeability guard). Default 1337. */\n shuffleSeed?: number\n /** Bind the pre-registered hypothesis. Verified (content hash) at\n * construction; alpha/maxN/direction/minEffect come FROM the manifest. */\n preRegistration?: SignedManifest\n /** Override the gate name in reports. */\n name?: string\n}\n\nexport interface SequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario>\n extends Gate<TArtifact, TScenario> {\n /** Streaming entry point: feed one paired per-scenario delta\n * (candidate − baseline, native scale). Each gate instance carries ONE\n * observe-stream; `decide(ctx)` runs on its own fresh stream and never\n * consumes or advances this one. 'promote' is sticky; observing past the\n * pre-registered maxN throws (extending a finished stream after seeing\n * the result reopens optional stopping — start a NEW pre-registered\n * test). */\n observe(delta: number): SequentialObservation\n /** Read-only snapshot of the observe-stream. */\n state(): EProcessState & { decision: SequentialDecision }\n}\n\ninterface ResolvedConfig {\n alpha: number\n minN: number\n maxN: number\n maxBet: number\n scale: number\n shuffleSeed: number\n /** 'decrease' negates deltas so \"better\" is always positive. */\n direction: 'increase' | 'decrease'\n /** H0 boundary in x-space: 1/2 + minEffect/(2·scale). */\n nullMean: number\n minEffect: number\n}\n\n/** Sync twin of `verifyManifest` — same `sha256-content` scheme\n * (sha256 over the canonicalized manifest minus contentHash/algo), via\n * node:crypto so gate construction can stay synchronous and fail loud\n * before any observation is consumed. */\nfunction verifyManifestSync(m: SignedManifest): boolean {\n if (m.algo !== undefined && m.algo !== 'sha256-content') {\n throw new Error(`sequentialPairedGate: unrecognized manifest hash algo '${m.algo}'`)\n }\n const { contentHash, algo: _algo, ...rest } = m\n void _algo\n const bytes = JSON.stringify(canonicalize(rest))\n const hash = createHash('sha256').update(bytes, 'utf8').digest('hex')\n return hash === contentHash\n}\n\nfunction resolveConfig(opts: SequentialPairedGateOptions): ResolvedConfig {\n const m = opts.preRegistration\n let alpha: number\n let maxN: number\n let direction: 'increase' | 'decrease'\n let minEffect: number\n if (m) {\n if (!verifyManifestSync(m)) {\n throw new Error(\n `sequentialPairedGate: pre-registration manifest '${m.id}' content hash mismatch (tampered)`,\n )\n }\n if (opts.alpha !== undefined && opts.alpha !== m.alpha) {\n throw new Error(\n `sequentialPairedGate: alpha ${opts.alpha} conflicts with pre-registered alpha ${m.alpha} — ` +\n `the registered statistic is the only one anytime validity covers`,\n )\n }\n if (opts.maxN !== undefined && opts.maxN !== m.preRegisteredN) {\n throw new Error(\n `sequentialPairedGate: maxN ${opts.maxN} conflicts with pre-registered N ${m.preRegisteredN}`,\n )\n }\n alpha = m.alpha\n maxN = m.preRegisteredN\n direction = m.direction\n minEffect = m.minEffect\n } else {\n alpha = opts.alpha ?? 0.05\n if (opts.maxN === undefined) {\n throw new Error(\n 'sequentialPairedGate: maxN is required (or bind a preRegistration manifest whose ' +\n 'preRegisteredN is the budget) — an unbounded stream has no pre-registered budget',\n )\n }\n maxN = opts.maxN\n direction = 'increase'\n minEffect = 0\n }\n const scale = opts.scale ?? 1\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`sequentialPairedGate: scale must be > 0, got ${scale}`)\n }\n if (!Number.isInteger(maxN) || maxN < 1) {\n throw new Error(`sequentialPairedGate: maxN must be a positive integer, got ${maxN}`)\n }\n const minN = opts.minN ?? 5\n if (!Number.isInteger(minN) || minN < 1 || minN > maxN) {\n throw new Error(\n `sequentialPairedGate: minN must be an integer in [1, maxN=${maxN}], got ${minN}`,\n )\n }\n if (!Number.isFinite(minEffect) || minEffect < 0 || minEffect >= scale) {\n throw new Error(\n `sequentialPairedGate: minEffect must be in [0, scale=${scale}), got ${minEffect}`,\n )\n }\n // H0: mean delta ≤ minEffect. In x = (d/scale + 1)/2 space that is\n // E[x] ≤ 1/2 + minEffect/(2·scale) — a shifted null boundary, NOT a clamp\n // of the observations (clamping is mean-distorting for skewed deltas).\n const nullMean = 0.5 + minEffect / (2 * scale)\n return {\n alpha,\n minN,\n maxN,\n maxBet: opts.maxBet ?? 0.5,\n scale,\n shuffleSeed: opts.shuffleSeed ?? 1337,\n direction,\n nullMean,\n minEffect,\n }\n}\n\ninterface SequentialStream {\n observe(delta: number): SequentialObservation\n state(): EProcessState & { decision: SequentialDecision }\n}\n\nfunction makeStream(cfg: ResolvedConfig): SequentialStream {\n const proc = eProcess({ alpha: cfg.alpha, maxBet: cfg.maxBet, nullMean: cfg.nullMean })\n const threshold = 1 / cfg.alpha\n let terminal: SequentialDecision | undefined\n return {\n observe(delta: number): SequentialObservation {\n if (terminal === 'undecided-at-maxN') {\n throw new Error(\n `sequentialPairedGate: pre-registered maxN=${cfg.maxN} exhausted — extending the ` +\n 'stream after seeing the result reopens optional stopping; start a NEW ' +\n 'pre-registered test',\n )\n }\n if (!Number.isFinite(delta) || Math.abs(delta) > cfg.scale) {\n throw new Error(\n `sequentialPairedGate: delta ${delta} outside ±scale=${cfg.scale} — pass the judge's ` +\n `native scale explicitly (detectScale helps pick 1 vs 100)`,\n )\n }\n const d = cfg.direction === 'decrease' ? -delta : delta\n const step = proc.update((d / cfg.scale + 1) / 2)\n // Stopping rule: FIRST n ≥ minN with current wealth ≥ 1/alpha — a valid\n // stopping time (measurable w.r.t. the past), sticky once latched. The\n // core's own `decided` latch ignores minN, so the gate re-derives the\n // decision from current wealth here.\n if (terminal === undefined && step.n >= cfg.minN && step.wealth >= threshold) {\n terminal = 'promote'\n }\n if (terminal === 'promote') {\n return {\n decision: 'promote',\n eValue: step.wealth,\n n: step.n,\n reason:\n `e-value ${step.wealth.toFixed(2)} ≥ 1/α=${threshold.toFixed(2)} at n=${step.n} ` +\n `(minN=${cfg.minN}): the paired improvement exceeds ${cfg.minEffect} at anytime-valid ` +\n `level α=${cfg.alpha}`,\n }\n }\n if (step.n >= cfg.maxN) {\n terminal = 'undecided-at-maxN'\n return {\n decision: 'undecided-at-maxN',\n eValue: step.wealth,\n n: step.n,\n reason:\n `undecided at pre-registered maxN=${cfg.maxN} (e-value ${step.wealth.toFixed(2)} < ` +\n `1/α=${threshold.toFixed(2)}). This is NOT evidence of no effect — the effect may be ` +\n 'real but smaller than this budget can detect; re-register with a larger N to test that',\n }\n }\n return {\n decision: 'continue',\n eValue: step.wealth,\n n: step.n,\n reason: `e-value ${step.wealth.toFixed(2)} < 1/α=${threshold.toFixed(2)} at n=${step.n}/${cfg.maxN} — keep observing`,\n }\n },\n state() {\n return { ...proc.state(), decision: terminal ?? 'continue' }\n },\n }\n}\n\n/** Data-independent in-place Fisher–Yates with a seeded PRNG — the permutation\n * depends only on the seed, never the values, so bet predictability survives. */\nfunction seededShuffle<T>(items: T[], seed: number): T[] {\n const rng = mulberry32(seed)\n for (let i = items.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n const tmp = items[i]!\n items[i] = items[j]!\n items[j] = tmp\n }\n return items\n}\n\n/**\n * Anytime-valid sequential paired gate. Conforms to the existing `Gate`\n * contract (`decide(ctx)` consumes candidate vs baseline judge scores via\n * `pairHoldout` — same pairing granularity as the fixed-n gates: full cellId,\n * never scenarioId) and adds a streaming `observe(delta)` entry for campaigns\n * that score cells incrementally and want to stop mid-stream.\n *\n * Decision mapping onto the substrate's five-valued `GateDecision`:\n * - 'promote' → 'ship'\n * - 'continue' → 'need_more_work' (stream ended before maxN with\n * the e-value undecided — more reps could decide)\n * - 'undecided-at-maxN' → 'hold', with the reason stating it is NOT\n * evidence of no effect (never a silent default)\n */\nexport function sequentialPairedGate<TArtifact = unknown, TScenario extends Scenario = Scenario>(\n options: SequentialPairedGateOptions,\n): SequentialPairedGate<TArtifact, TScenario> {\n const cfg = resolveConfig(options)\n const name = options.name ?? 'sequentialPairedGate'\n const manifest = options.preRegistration\n const observeStream = makeStream(cfg)\n\n return {\n name,\n observe(delta: number): SequentialObservation {\n return observeStream.observe(delta)\n },\n state() {\n return observeStream.state()\n },\n async decide(ctx: GateContext<TArtifact, TScenario>): Promise<GateResult> {\n if (!ctx.baselineJudgeScores) {\n throw new Error(\n `${name}: ctx.baselineJudgeScores is required — falling back to the candidate's own ` +\n 'scores would compare the candidate against itself (delta 0, silent no-op)',\n )\n }\n const scenarioIds = new Set(ctx.scenarios.map((s) => s.id))\n const paired = pairHoldout(\n ctx.judgeScores,\n ctx.baselineJudgeScores,\n scenarioIds,\n (s) => s.composite,\n )\n const deltas = paired.after.map((a, i) => a - paired.before[i]!)\n seededShuffle(deltas, cfg.shuffleSeed)\n\n const stream = makeStream(cfg)\n let last: SequentialObservation | undefined\n for (const d of deltas) {\n last = stream.observe(d)\n if (last.decision !== 'continue') break\n }\n\n const detail = {\n ...stream.state(),\n minN: cfg.minN,\n maxN: cfg.maxN,\n scale: cfg.scale,\n shuffleSeed: cfg.shuffleSeed,\n direction: cfg.direction,\n minEffect: cfg.minEffect,\n pairedN: deltas.length,\n ...(manifest ? { preRegisteredId: manifest.id, metric: manifest.metric } : {}),\n }\n const meanDelta =\n deltas.length === 0 ? undefined : deltas.reduce((s, d) => s + d, 0) / deltas.length\n\n if (last === undefined) {\n return {\n decision: 'need_more_work',\n reasons: [`${name}: no paired holdout observations — nothing to test`],\n contributingGates: [{ name, status: 'not_evaluated', detail }],\n }\n }\n const decision =\n last.decision === 'promote'\n ? 'ship'\n : last.decision === 'continue'\n ? 'need_more_work'\n : 'hold'\n return {\n decision,\n reasons: [`${name}: ${last.reason}`],\n contributingGates: [\n {\n name,\n status:\n decision === 'ship'\n ? 'pass'\n : decision === 'need_more_work'\n ? 'not_evaluated'\n : 'fail',\n detail,\n },\n ],\n delta: meanDelta,\n }\n },\n }\n}\n\nexport interface SequentialDecideOptions {\n /** Type-I budget for the early-stop evidence. Default 0.05. */\n alpha?: number\n /** Minimum paired deltas before a stop may fire. Default 5. */\n minN?: number\n /** Bet truncation forwarded to `eProcess`. Default 0.5. */\n maxBet?: number\n /** Bound on |per-scenario composite delta|. Default 1. */\n scale?: number\n}\n\nexport interface SequentialDecideFn {\n (args: { history: GenerationRecord[] }): { stop: boolean; reason?: string }\n /** Read-only snapshot of the accumulated e-process (observability + tests). */\n state(): EProcessState\n}\n\n/**\n * `SurfaceProposer.decide` adapter — stops the optimization loop the moment\n * the e-process decides the loop has produced a real improvement, instead of\n * always running `maxGenerations`.\n *\n * Stream: for each generation g ≥ 1, the per-scenario composite deltas of\n * generation g's top candidate vs the generation-0 top candidate (the\n * incumbent the loop set out to beat), paired by scenarioId. H0: no proposed\n * surface improves any scenario's expected composite over the incumbent —\n * under it every delta has conditional mean ≤ 0 and the e-process is valid.\n * Once wealth ≥ 1/alpha the loop stops and hands the winner to the promotion\n * gate (which re-scores on HELD-OUT data — this adapter only spends the\n * exploration budget, it never promotes).\n *\n * Honesty caveats: (1) the incumbent's scores are measured once and shared\n * across all generations' deltas, so type-I control is exact only insofar as\n * those scores approximate the incumbent's true per-scenario means (more reps\n * → tighter); (2) an UNDECIDED process never stops the loop — absence of a\n * crossing is NOT evidence of no effect, so the loop simply runs its normal\n * course. Calling the adapter repeatedly with a growing history consumes each\n * generation exactly once (re-feeding an already-seen record would double-count\n * evidence).\n */\nexport function sequentialDecide(options: SequentialDecideOptions = {}): SequentialDecideFn {\n const alpha = options.alpha ?? 0.05\n const minN = options.minN ?? 5\n const scale = options.scale ?? 1\n if (!Number.isFinite(scale) || scale <= 0) {\n throw new Error(`sequentialDecide: scale must be > 0, got ${scale}`)\n }\n const proc = eProcess({ alpha, maxBet: options.maxBet ?? 0.5, nullMean: 0.5 })\n const threshold = 1 / alpha\n let processedGenerations = 0\n let reference: Map<string, number> | undefined\n let stopped: { stop: true; reason: string } | undefined\n\n const topCandidate = (record: GenerationRecord) => {\n if (record.candidates.length === 0) {\n throw new Error(\n `sequentialDecide: generation ${record.generationIndex} has no candidates — ` +\n 'cannot extract a top candidate',\n )\n }\n return record.candidates.reduce((best, candidate) => {\n if (best.composite === null) return candidate\n if (candidate.composite === null) return best\n return candidate.composite > best.composite ? candidate : best\n })\n }\n\n const decide: SequentialDecideFn = ({ history }) => {\n if (stopped) return stopped\n if (history.length === 0) return { stop: false }\n if (reference === undefined) {\n reference = new Map(\n topCandidate(history[0]!).scenarios.map((s) => [s.scenarioId, s.composite]),\n )\n }\n // Consume only generations not yet seen; start at 1 — generation 0 IS the\n // reference and would contribute all-zero deltas (pure wealth dilution).\n for (let g = Math.max(1, processedGenerations); g < history.length; g++) {\n const top = topCandidate(history[g]!)\n const byScenario = new Map(top.scenarios.map((s) => [s.scenarioId, s.composite]))\n for (const [scenarioId, refComposite] of reference) {\n const candComposite = byScenario.get(scenarioId)\n if (candComposite === undefined) {\n throw new Error(\n `sequentialDecide: generation ${history[g]!.generationIndex} top candidate is missing ` +\n `scenario '${scenarioId}' — generations must score the same scenario set to pair`,\n )\n }\n const delta = candComposite - refComposite\n if (!Number.isFinite(delta) || Math.abs(delta) > scale) {\n throw new Error(\n `sequentialDecide: paired delta ${delta} outside ±scale=${scale} on scenario ` +\n `'${scenarioId}' — pass the composite scale explicitly`,\n )\n }\n const step = proc.update((delta / scale + 1) / 2)\n if (step.n >= minN && step.wealth >= threshold) {\n stopped = {\n stop: true,\n reason:\n `sequential e-process decided at generation ${history[g]!.generationIndex}: ` +\n `e-value ${step.wealth.toFixed(2)} ≥ 1/α=${threshold.toFixed(2)} after n=${step.n} ` +\n 'paired deltas vs the generation-0 incumbent — the improvement is real at ' +\n `α=${alpha}; stop exploring and promote via the gate`,\n }\n processedGenerations = history.length\n return stopped\n }\n }\n }\n processedGenerations = history.length\n return { stop: false }\n }\n decide.state = () => proc.state()\n return decide\n}\n"],"mappings":"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"}