@tangle-network/agent-eval 1.0.21 → 1.0.23

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 (167) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/dist/adapters/http.d.ts +2 -2
  3. package/dist/{agent-profile-Dngk3LZ0.js → agent-profile-4j6f2n-x.js} +4 -4
  4. package/dist/agent-profile-4j6f2n-x.js.map +1 -0
  5. package/dist/{agent-profile-BO8ruI6R.d.ts → agent-profile-C80hNPYb.d.ts} +5 -5
  6. package/dist/{agent-profile-BO8ruI6R.d.ts.map → agent-profile-C80hNPYb.d.ts.map} +1 -1
  7. package/dist/analyst/index.d.ts +11 -11
  8. package/dist/analyst/index.js +1 -1
  9. package/dist/{analyze-runs-jLTRKuoK.js → analyze-runs-BCSHzqna.js} +2 -2
  10. package/dist/{analyze-runs-jLTRKuoK.js.map → analyze-runs-BCSHzqna.js.map} +1 -1
  11. package/dist/{analyze-runs-DYYe18Gw.d.ts → analyze-runs-DbHQHvDC.d.ts} +4 -4
  12. package/dist/{analyze-runs-DYYe18Gw.d.ts.map → analyze-runs-DbHQHvDC.d.ts.map} +1 -1
  13. package/dist/app-grade/index.d.ts +3 -3
  14. package/dist/{benchmark-command-CoNndRCk.js → benchmark-command-DkUpd9Q6.js} +2 -2
  15. package/dist/{benchmark-command-CoNndRCk.js.map → benchmark-command-DkUpd9Q6.js.map} +1 -1
  16. package/dist/benchmarks/index.d.ts +5 -5
  17. package/dist/benchmarks/index.js +2 -2
  18. package/dist/campaign/index.d.ts +6 -6
  19. package/dist/campaign/index.js +7 -7
  20. package/dist/{campaign-Bl07uIaf.js → campaign-D00Tbey-.js} +10 -10
  21. package/dist/{campaign-Bl07uIaf.js.map → campaign-D00Tbey-.js.map} +1 -1
  22. package/dist/{campaign-evidence-NtH29ctt.js → campaign-evidence-CVNKiK0q.js} +2 -2
  23. package/dist/{campaign-evidence-NtH29ctt.js.map → campaign-evidence-CVNKiK0q.js.map} +1 -1
  24. package/dist/cli.js +6 -6
  25. package/dist/contract/index.d.ts +10 -10
  26. package/dist/contract/index.js +7 -8
  27. package/dist/contract/index.js.map +1 -1
  28. package/dist/{default-registry-aPvwtzEM.d.ts → default-registry-CjgRr-l7.d.ts} +7 -7
  29. package/dist/{default-registry-aPvwtzEM.d.ts.map → default-registry-CjgRr-l7.d.ts.map} +1 -1
  30. package/dist/{define-agent-eval-Bx3b9B01.d.ts → define-agent-eval-BQBwKHwF.d.ts} +7 -7
  31. package/dist/{define-agent-eval-Bx3b9B01.d.ts.map → define-agent-eval-BQBwKHwF.d.ts.map} +1 -1
  32. package/dist/{define-agent-eval-BROmIwn1.js → define-agent-eval-BV16x8C0.js} +7 -7
  33. package/dist/{define-agent-eval-BROmIwn1.js.map → define-agent-eval-BV16x8C0.js.map} +1 -1
  34. package/dist/diagnosis/index.d.ts +2 -2
  35. package/dist/diagnosis/index.js +1 -1
  36. package/dist/{engine-BF8naBn7.d.ts → engine-s3t6uYH9.d.ts} +2 -2
  37. package/dist/{engine-BF8naBn7.d.ts.map → engine-s3t6uYH9.d.ts.map} +1 -1
  38. package/dist/{eval-campaign-Dq8DEKmy.js → eval-campaign-DpkToY_w.js} +2 -2
  39. package/dist/eval-campaign-DpkToY_w.js.map +1 -0
  40. package/dist/{evaluation-CFKj_IlJ.d.ts → evaluation-DRgHKEVV.d.ts} +4 -4
  41. package/dist/{evaluation-CFKj_IlJ.d.ts.map → evaluation-DRgHKEVV.d.ts.map} +1 -1
  42. package/dist/evaluation.d.ts +1 -1
  43. package/dist/{exact-types-VAFjtOQN.d.ts → exact-types-DTlZSygs.d.ts} +2 -2
  44. package/dist/{exact-types-VAFjtOQN.d.ts.map → exact-types-DTlZSygs.d.ts.map} +1 -1
  45. package/dist/experiment/index.d.ts +2 -2
  46. package/dist/experiment/index.js +2 -2
  47. package/dist/{feedback-trajectory-DTj8eVTR.d.ts → feedback-trajectory-DrKk79NU.d.ts} +3 -3
  48. package/dist/{feedback-trajectory-DTj8eVTR.d.ts.map → feedback-trajectory-DrKk79NU.d.ts.map} +1 -1
  49. package/dist/hosted/index.js +1 -1
  50. package/dist/{index-BlTbxHlY.d.ts → index-DAnkXnwF.d.ts} +3 -3
  51. package/dist/{index-BlTbxHlY.d.ts.map → index-DAnkXnwF.d.ts.map} +1 -1
  52. package/dist/{index-DfqAy8ct.d.ts → index-DLZp90hM.d.ts} +8 -8
  53. package/dist/{index-DfqAy8ct.d.ts.map → index-DLZp90hM.d.ts.map} +1 -1
  54. package/dist/{index-7tWvC0aW.d.ts → index-DZ_aqB7v.d.ts} +23 -179
  55. package/dist/index-DZ_aqB7v.d.ts.map +1 -0
  56. package/dist/{index-DFso1b2W.d.ts → index-DjrXBogW.d.ts} +9 -9
  57. package/dist/{index-DFso1b2W.d.ts.map → index-DjrXBogW.d.ts.map} +1 -1
  58. package/dist/{index-CM12lr9E.d.ts → index-DtX456kD.d.ts} +6 -6
  59. package/dist/{index-CM12lr9E.d.ts.map → index-DtX456kD.d.ts.map} +1 -1
  60. package/dist/{index-B62BaRzH.d.ts → index-DwscLCQX.d.ts} +2 -2
  61. package/dist/{index-B62BaRzH.d.ts.map → index-DwscLCQX.d.ts.map} +1 -1
  62. package/dist/index.d.ts +25 -25
  63. package/dist/index.js +14 -13
  64. package/dist/index.js.map +1 -1
  65. package/dist/{llm-judge-C813vmpc.js → llm-judge-Cbh7OHaO.js} +7 -7
  66. package/dist/{llm-judge-C813vmpc.js.map → llm-judge-Cbh7OHaO.js.map} +1 -1
  67. package/dist/{matrix-C-GxIgeJ.d.ts → matrix-8NhGDcmi.d.ts} +2 -2
  68. package/dist/{matrix-C-GxIgeJ.d.ts.map → matrix-8NhGDcmi.d.ts.map} +1 -1
  69. package/dist/meta-eval/index.d.ts +2 -2
  70. package/dist/{mint-yF51iBgY.js → mint-Ck92efNo.js} +2 -2
  71. package/dist/{mint-yF51iBgY.js.map → mint-Ck92efNo.js.map} +1 -1
  72. package/dist/multishot/golden/index.d.ts +1 -1
  73. package/dist/multishot/index.d.ts +2 -2
  74. package/dist/openapi.json +1 -1
  75. package/dist/paired-promotion-power-D03cUj9E.js +289 -0
  76. package/dist/paired-promotion-power-D03cUj9E.js.map +1 -0
  77. package/dist/{pareto-fJZg8pXm.js → pareto-UzjB3ZBk.js} +5 -288
  78. package/dist/pareto-UzjB3ZBk.js.map +1 -0
  79. package/dist/{prime-protocol-D1NkJRuD.d.ts → prime-protocol-CKxq_-cx.d.ts} +2 -2
  80. package/dist/{prime-protocol-D1NkJRuD.d.ts.map → prime-protocol-CKxq_-cx.d.ts.map} +1 -1
  81. package/dist/{promotion-gate-Da0xV4sa.d.ts → promotion-gate-HDtliGsh.d.ts} +3 -3
  82. package/dist/{promotion-gate-Da0xV4sa.d.ts.map → promotion-gate-HDtliGsh.d.ts.map} +1 -1
  83. package/dist/{registry-BAcEbzKm.d.ts → registry-BnenKvxM.d.ts} +3 -3
  84. package/dist/{registry-BAcEbzKm.d.ts.map → registry-BnenKvxM.d.ts.map} +1 -1
  85. package/dist/{release-confidence-DGtuPDxJ.js → release-confidence-B4Xxdpmn.js} +2 -2
  86. package/dist/{release-confidence-DGtuPDxJ.js.map → release-confidence-B4Xxdpmn.js.map} +1 -1
  87. package/dist/reporting.d.ts +4 -4
  88. package/dist/reporting.js +2 -2
  89. package/dist/{researcher-CSlc84Bp.d.ts → researcher-DEpedkOZ.d.ts} +4 -4
  90. package/dist/{researcher-CSlc84Bp.d.ts.map → researcher-DEpedkOZ.d.ts.map} +1 -1
  91. package/dist/{reward-ST4PXNXF.d.ts → reward-ap_R9mCH.d.ts} +2 -2
  92. package/dist/{reward-ST4PXNXF.d.ts.map → reward-ap_R9mCH.d.ts.map} +1 -1
  93. package/dist/{reward-hacking-C9hPVT0Q.d.ts → reward-hacking-CIdVwVe4.d.ts} +2 -2
  94. package/dist/{reward-hacking-C9hPVT0Q.d.ts.map → reward-hacking-CIdVwVe4.d.ts.map} +1 -1
  95. package/dist/rl.d.ts +8 -8
  96. package/dist/rl.js +5 -5
  97. package/dist/rollout/index.d.ts +3 -3
  98. package/dist/rollout/index.js +2 -2
  99. package/dist/{rollout-DKa54qer.js → rollout-DWl3Gg6A.js} +2 -2
  100. package/dist/{rollout-DKa54qer.js.map → rollout-DWl3Gg6A.js.map} +1 -1
  101. package/dist/{rubric-predictive-validity-B3RzbwOx.d.ts → rubric-predictive-validity-Bbi88Aut.d.ts} +2 -2
  102. package/dist/{rubric-predictive-validity-B3RzbwOx.d.ts.map → rubric-predictive-validity-Bbi88Aut.d.ts.map} +1 -1
  103. package/dist/{run-record-CFbnkE1K.js → run-record-DTTx9Ktr.js} +2 -2
  104. package/dist/{run-record-CFbnkE1K.js.map → run-record-DTTx9Ktr.js.map} +1 -1
  105. package/dist/{run-record-CpLS4Uid.js → run-record-DmX_ESC5.js} +7 -5
  106. package/dist/run-record-DmX_ESC5.js.map +1 -0
  107. package/dist/{run-record-D2BFMPGl.d.ts → run-record-pBlhZizT.d.ts} +8 -8
  108. package/dist/{run-record-D2BFMPGl.d.ts.map → run-record-pBlhZizT.d.ts.map} +1 -1
  109. package/dist/search/index.d.ts +2 -2
  110. package/dist/search/index.d.ts.map +1 -1
  111. package/dist/search/index.js +2 -2
  112. package/dist/{search-kernel-CQGLbmgn.d.ts → search-kernel-CoDJ21q9.d.ts} +3 -3
  113. package/dist/{search-kernel-CQGLbmgn.d.ts.map → search-kernel-CoDJ21q9.d.ts.map} +1 -1
  114. package/dist/{search-ledger-Dk-yk5Li.js → search-ledger-9liYyZf5.js} +2 -2
  115. package/dist/{search-ledger-Dk-yk5Li.js.map → search-ledger-9liYyZf5.js.map} +1 -1
  116. package/dist/{search-shipper-Dm-_Dtvy.js → search-shipper-B6tMNx1I.js} +2 -2
  117. package/dist/{search-shipper-Dm-_Dtvy.js.map → search-shipper-B6tMNx1I.js.map} +1 -1
  118. package/dist/{search-summary-sPWiYtSl.js → search-summary-CISbbxyr.js} +2 -2
  119. package/dist/{search-summary-sPWiYtSl.js.map → search-summary-CISbbxyr.js.map} +1 -1
  120. package/dist/sequential-BCzXKQab.d.ts +246 -0
  121. package/dist/sequential-BCzXKQab.d.ts.map +1 -0
  122. package/dist/{sequential-CAksw6EU.js → sequential-Ko7T-zvu.js} +65 -28
  123. package/dist/sequential-Ko7T-zvu.js.map +1 -0
  124. package/dist/{skillopt-optimization-method-QO1nU8Kz.js → skillopt-optimization-method-B1Hkfexf.js} +4 -4
  125. package/dist/{skillopt-optimization-method-QO1nU8Kz.js.map → skillopt-optimization-method-B1Hkfexf.js.map} +1 -1
  126. package/dist/{statistical-heldout-CvOwNqt9.d.ts → statistical-heldout-e2hbqDui.d.ts} +4 -4
  127. package/dist/{statistical-heldout-CvOwNqt9.d.ts.map → statistical-heldout-e2hbqDui.d.ts.map} +1 -1
  128. package/dist/{store-tool-spans-B3z_Qb5u.d.ts → store-tool-spans-mlmGj6n3.d.ts} +5 -5
  129. package/dist/{store-tool-spans-B3z_Qb5u.d.ts.map → store-tool-spans-mlmGj6n3.d.ts.map} +1 -1
  130. package/dist/{summary-report-DyZquTaH.d.ts → summary-report-DsiORzPc.d.ts} +3 -3
  131. package/dist/{summary-report-DyZquTaH.d.ts.map → summary-report-DsiORzPc.d.ts.map} +1 -1
  132. package/dist/{systemone-DVk-Crtu.d.ts → systemone-B93onE_a.d.ts} +7 -7
  133. package/dist/{systemone-DVk-Crtu.d.ts.map → systemone-B93onE_a.d.ts.map} +1 -1
  134. package/dist/{systemone-review-CToBl1oa.d.ts → systemone-review-BIggseLt.d.ts} +2 -2
  135. package/dist/{systemone-review-CToBl1oa.d.ts.map → systemone-review-BIggseLt.d.ts.map} +1 -1
  136. package/dist/systemone.d.ts +3 -3
  137. package/dist/{tool-groups-R5RO9OHn.d.ts → tool-groups-CCiATVZw.d.ts} +3 -3
  138. package/dist/tool-groups-CCiATVZw.d.ts.map +1 -0
  139. package/dist/{trace-questions-BY6taxnl.d.ts → trace-questions-B7lkyOlo.d.ts} +3 -3
  140. package/dist/{trace-questions-BY6taxnl.d.ts.map → trace-questions-B7lkyOlo.d.ts.map} +1 -1
  141. package/dist/trace-repair/index.d.ts +2 -2
  142. package/dist/traces.d.ts +4 -4
  143. package/dist/traces.js +2 -8
  144. package/dist/traces.js.map +1 -1
  145. package/dist/{tree-C-N2kP-8.js → tree-D2cNtu9m.js} +10 -10
  146. package/dist/tree-D2cNtu9m.js.map +1 -0
  147. package/dist/{types-BsdcSWnf.d.ts → types-3uX8odDK.d.ts} +3 -3
  148. package/dist/{types-BsdcSWnf.d.ts.map → types-3uX8odDK.d.ts.map} +1 -1
  149. package/dist/{types-kBMNdo1X.d.ts → types-Ob1blgI2.d.ts} +2 -2
  150. package/dist/{types-kBMNdo1X.d.ts.map → types-Ob1blgI2.d.ts.map} +1 -1
  151. package/dist/{types-ip2D3-Ib.d.ts → types-efIQ03Uo.d.ts} +4 -4
  152. package/dist/{types-ip2D3-Ib.d.ts.map → types-efIQ03Uo.d.ts.map} +1 -1
  153. package/dist/wire/index.d.ts +3 -3
  154. package/docs/adopt-stack.md +9 -0
  155. package/docs/eval-fixtures.md +2 -2
  156. package/docs/search-ledger.md +1 -0
  157. package/package.json +5 -5
  158. package/dist/agent-profile-Dngk3LZ0.js.map +0 -1
  159. package/dist/eval-campaign-Dq8DEKmy.js.map +0 -1
  160. package/dist/index-7tWvC0aW.d.ts.map +0 -1
  161. package/dist/pareto-fJZg8pXm.js.map +0 -1
  162. package/dist/run-record-CpLS4Uid.js.map +0 -1
  163. package/dist/sequential-B9p6uoV8.d.ts +0 -168
  164. package/dist/sequential-B9p6uoV8.d.ts.map +0 -1
  165. package/dist/sequential-CAksw6EU.js.map +0 -1
  166. package/dist/tool-groups-R5RO9OHn.d.ts.map +0 -1
  167. package/dist/tree-C-N2kP-8.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paired-promotion-power-D03cUj9E.js","names":[],"sources":["../src/paired-promotion-power.ts"],"sourcesContent":["/**\n * @module\n * Joint power of a configured {@link decidePairedPromotion} call, computed by\n * running that call on data drawn from a registered alternative.\n *\n * Why a simulation and not a formula: `decidePairedPromotion` is not one test.\n * It routes by sample size (exact sign test below `BOOTSTRAP_GATE_MIN_N`,\n * paired bootstrap at or above it) and by shape (Tango's score interval on a\n * two-point outcome), and on top of the deciding interval it applies a\n * sufficiency floor, a zero-width refusal and McNemar's exact veto. A\n * preregistration usually seals two such calls, a continuous primary and a\n * pass/fail veto drawn from the same episodes, and promotes only when both\n * promote. The sample size at which THAT procedure reaches a target power is\n * not the power of any one channel: the sign-test power at n can be 0.8 while\n * the promotion probability is well below it because the veto binds\n * (agent-eval#785). Closed forms exist for single channels only, and each one\n * shipped here under-sized the call it stood in for (`mcnemarRequiredN` is\n * Lachin's normal approximation, 0.65 exact power where it promised 0.8 at 14\n * pairs; `requiredPairedSampleSize` sizes a paired t the procedure never runs).\n * So the only faithful power is the procedure's own promotion rate under the\n * alternative, and that is what this module computes: draw n pairs from the\n * registered law, run every configured call on them exactly as the caller\n * will, repeat, count.\n *\n * The alternative is DECLARATIVE and serializable, never a sampler function,\n * so a preregistration can seal it as bytes: a mixture of cells, each with a\n * probability, an optional pass/fail outcome per arm for the binary calls,\n * and a law for the candidate-minus-baseline delta on the continuous scale.\n * Drawing the cell first and the delta given the cell is what makes the two\n * channels one joint law rather than two marginals.\n *\n * Everything is seeded (`mulberry32`), so a sizing is reproducible from its\n * inputs, and every estimate carries its Monte Carlo standard error and a\n * Wilson interval: a power read at 0.80 from 1,000 simulations is 0.80 ± 0.013.\n */\n\nimport {\n decidePairedPromotion,\n type PairedDecisionMethod,\n type PairedPromotionDecision,\n type PairedPromotionDecisionOptions,\n} from './paired-promotion-decision'\nimport { wilson } from './statistics/paired-binary'\nimport { mulberry32 } from './statistics/random'\n\n/** A serializable law for one scalar draw. */\nexport type PairedValueLaw =\n | { kind: 'point'; value: number }\n | { kind: 'atoms'; atoms: ReadonlyArray<{ value: number; probability: number }> }\n | { kind: 'normal'; mean: number; sd: number }\n\nexport interface PairedPromotionAlternativeCell {\n /** Probability of this cell. The cells' probabilities sum to 1. */\n probability: number\n /** Pass/fail outcome of each arm in this cell. Required by every call whose\n * `outcome` is `'pass'`; ignored otherwise. */\n pass?: { control: boolean; treatment: boolean }\n /** Law of the candidate-minus-baseline delta on the continuous scale, given\n * this cell. A tie is `{ kind: 'point', value: 0 }`. */\n delta: PairedValueLaw\n}\n\nexport interface PairedPromotionAlternative {\n /** The registered joint law: the cell is drawn first, then the delta given\n * the cell, so pass/fail and delta are dependent exactly as registered. */\n cells: ReadonlyArray<PairedPromotionAlternativeCell>\n /**\n * Law of the control arm's value on the continuous scale; the candidate is\n * control plus delta. Default `{ kind: 'point', value: 0 }`. The mean and\n * median bootstraps read only the deltas, so the baseline changes nothing\n * there; it matters only when a `'delta'` call infers its shape from the\n * observed values (see `continuous` on {@link PairedPromotionDecisionOptions}).\n */\n baseline?: PairedValueLaw\n}\n\nexport interface PairedPromotionPowerCall {\n /** What the call reads from each simulated pair: `'delta'` gives it\n * `(control, control + delta)` on the continuous scale, `'pass'` gives it\n * each arm's pass/fail as `0` or `options.binaryScale ?? 1`. */\n outcome: 'delta' | 'pass'\n /** The call's options, exactly as the caller will run it. `minPairs`\n * defaults to the simulated n, the value a gate seals; a caller-supplied\n * value is kept, and one above n refuses every simulation. A `'pass'` call\n * must declare `binaryScale`: the simulation never declares one on the\n * caller's behalf, because a declared scale routes an all-zero sample to\n * the score interval where an inferred one refuses it. */\n options: PairedPromotionDecisionOptions\n}\n\nexport interface PairedPromotionPowerOptions {\n /** Total paired episodes, ties included. */\n n: number\n alternative: PairedPromotionAlternative\n /** Every call must return `promote: true` for a simulation to count as a\n * promotion. At least one call. */\n calls: ReadonlyArray<PairedPromotionPowerCall>\n /** Simulated data sets. Default 1000. */\n simulations?: number\n /** PRNG seed for the simulated draws (not the bootstrap's seed, which each\n * call carries in its own options). Default 1. */\n seed?: number\n}\n\nexport interface PairedPromotionCallRates {\n outcome: 'delta' | 'pass'\n /** Fraction of simulations in which this call alone promoted. */\n promote: number\n /** Fractions of simulations in which each refusal channel fired. */\n insufficient: number\n indeterminate: number\n exactTestVetoes: number\n /** Fraction in which the call was sufficient, determinate, not vetoed, and\n * still did not clear its threshold. */\n hold: number\n /** Fraction of simulations decided by each method; the regime the call\n * actually ran in at this n. */\n methods: Partial<Record<PairedDecisionMethod, number>>\n}\n\nexport interface PairedPromotionPowerResult {\n n: number\n simulations: number\n seed: number\n /** Fraction of simulations in which every call promoted: the joint power. */\n power: number\n /** Monte Carlo standard error of `power`. */\n standardError: number\n /** Wilson 95% interval on `power`. */\n low: number\n high: number\n /** Fraction in which at least one call refused (insufficient, indeterminate\n * or vetoed); the rest of the non-promotions are holds. */\n refuse: number\n hold: number\n /** Per-call rates, in the order the calls were supplied. */\n calls: PairedPromotionCallRates[]\n}\n\nexport interface RequiredPairsForPairedPromotionOptions {\n /** Target joint power. Default 0.8. */\n target?: number\n alternative: PairedPromotionAlternative\n calls: ReadonlyArray<PairedPromotionPowerCall>\n /** Smallest n to consider. Default 1. */\n minPairs?: number\n /** Largest n to consider; the scan stops there with `n: null` if the target\n * is never reached. Default 200. */\n maxPairs?: number\n simulations?: number\n seed?: number\n}\n\nexport interface RequiredPairsForPairedPromotionResult {\n target: number\n /** First n scanned whose estimated joint power reaches the target, or null\n * when no n up to `maxPairs` does. Descriptive only: power is not monotone\n * in n on the exact and discrete routes, the scan reads one noisy estimate\n * per n, and the first crossing of many is biased low. Read the curve. */\n n: number | null\n /**\n * The conservative choice, which a preregistration should prefer: the first\n * scanned n whose Wilson lower bound reaches the target AND whose\n * independent confirmation (a second simulation at that n on a fresh seed,\n * `confirmation` below) also has its lower bound at or above the target. The\n * confirmation is what keeps the 95 % coverage: a scan over many n gives\n * Monte Carlo noise many chances to push one bound across, so the scan's own\n * bound selects a candidate and never certifies it. Never set while `n` is\n * null. Null when not reached by `maxPairs`.\n */\n nAtLowerBound: number | null\n /** The independent simulation at `nAtLowerBound`; null when not reached. */\n confirmation: PairedPromotionPowerResult | null\n /** Every n scanned, in order, with its scan estimate; candidates whose\n * confirmation failed are in `rejected`. The scan stops at `nAtLowerBound`\n * when that is reached, else at `maxPairs`. */\n curve: PairedPromotionPowerResult[]\n /** Candidates the scan's bound selected and the confirmation refused. */\n rejected: PairedPromotionPowerResult[]\n}\n\n/**\n * Joint power of the configured calls at `n` under the registered alternative:\n * the fraction of simulated data sets on which every call promotes.\n */\nexport function pairedPromotionPower(\n options: PairedPromotionPowerOptions,\n): PairedPromotionPowerResult {\n const n = options.n\n if (!Number.isInteger(n) || n < 1) {\n throw new Error(`pairedPromotionPower: n must be a positive integer, got ${n}`)\n }\n const simulations = options.simulations ?? 1000\n if (!Number.isInteger(simulations) || simulations < 1) {\n throw new Error(\n `pairedPromotionPower: simulations must be a positive integer, got ${simulations}`,\n )\n }\n const seed = options.seed ?? 1\n if (!Number.isFinite(seed)) {\n throw new Error(`pairedPromotionPower: seed must be finite, got ${seed}`)\n }\n const calls = validateCalls(options.calls)\n const alternative = validateAlternative(options.alternative, calls)\n const rng = mulberry32(seed)\n\n let promotions = 0\n let refusals = 0\n const perCall = calls.map(() => ({\n promote: 0,\n insufficient: 0,\n indeterminate: 0,\n exactTestVetoes: 0,\n hold: 0,\n methods: new Map<PairedDecisionMethod, number>(),\n }))\n\n for (let s = 0; s < simulations; s++) {\n const pairs = drawPairs(alternative, n, rng)\n let all = true\n let anyRefusal = false\n for (let c = 0; c < calls.length; c++) {\n const call = calls[c]!\n const decision = runCall(call, pairs, n)\n const tally = perCall[c]!\n tally.methods.set(decision.method, (tally.methods.get(decision.method) ?? 0) + 1)\n if (decision.promote) tally.promote++\n else {\n if (!decision.sufficient) tally.insufficient++\n if (decision.indeterminate) tally.indeterminate++\n if (decision.exactTestVetoes) tally.exactTestVetoes++\n if (decision.sufficient && !decision.indeterminate && !decision.exactTestVetoes) {\n tally.hold++\n }\n }\n if (!decision.promote) all = false\n if (!decision.sufficient || decision.indeterminate || decision.exactTestVetoes) {\n anyRefusal = true\n }\n }\n if (all) promotions++\n else if (anyRefusal) refusals++\n }\n\n const power = promotions / simulations\n const interval = wilson(promotions, simulations, 0.95)\n return {\n n,\n simulations,\n seed,\n power,\n standardError: Math.sqrt((power * (1 - power)) / simulations),\n low: interval.lower,\n high: interval.upper,\n refuse: refusals / simulations,\n hold: (simulations - promotions - refusals) / simulations,\n calls: perCall.map((tally, c) => ({\n outcome: calls[c]!.outcome,\n promote: tally.promote / simulations,\n insufficient: tally.insufficient / simulations,\n indeterminate: tally.indeterminate / simulations,\n exactTestVetoes: tally.exactTestVetoes / simulations,\n hold: tally.hold / simulations,\n methods: Object.fromEntries(\n [...tally.methods.entries()].map(([method, count]) => [method, count / simulations]),\n ) as Partial<Record<PairedDecisionMethod, number>>,\n })),\n }\n}\n\n/**\n * First n at which {@link pairedPromotionPower} reaches the target, scanning\n * every n from `minPairs` upward. Both the point-estimate answer and the\n * confirmed lower-bound answer are returned; the scan runs until the latter is\n * reached (so the curve past `n` is retained) or `maxPairs` is exhausted. A\n * candidate selected by the scan's bound is certified only by an independent\n * simulation at that n on a different seed; a candidate that fails it is\n * recorded and the scan continues.\n */\nexport function requiredPairsForPairedPromotion(\n options: RequiredPairsForPairedPromotionOptions,\n): RequiredPairsForPairedPromotionResult {\n const target = options.target ?? 0.8\n if (!Number.isFinite(target) || target <= 0 || target >= 1) {\n throw new Error(`requiredPairsForPairedPromotion: target must be in (0,1), got ${target}`)\n }\n const minPairs = options.minPairs ?? 1\n const maxPairs = options.maxPairs ?? 200\n if (!Number.isInteger(minPairs) || minPairs < 1) {\n throw new Error(\n `requiredPairsForPairedPromotion: minPairs must be a positive integer, got ${minPairs}`,\n )\n }\n if (!Number.isInteger(maxPairs) || maxPairs < minPairs) {\n throw new Error(\n `requiredPairsForPairedPromotion: maxPairs must be an integer >= minPairs, got ${maxPairs}`,\n )\n }\n const seed = options.seed ?? 1\n const curve: PairedPromotionPowerResult[] = []\n const rejected: PairedPromotionPowerResult[] = []\n let n: number | null = null\n let nAtLowerBound: number | null = null\n let confirmation: PairedPromotionPowerResult | null = null\n for (let candidate = minPairs; candidate <= maxPairs; candidate++) {\n const result = pairedPromotionPower({\n n: candidate,\n alternative: options.alternative,\n calls: options.calls,\n simulations: options.simulations,\n seed,\n })\n curve.push(result)\n if (n === null && result.power >= target) n = candidate\n if (result.low >= target) {\n // A fresh stream, keyed by the candidate so no two confirmations share\n // draws with each other or with the scan.\n const check = pairedPromotionPower({\n n: candidate,\n alternative: options.alternative,\n calls: options.calls,\n simulations: options.simulations,\n seed: confirmationSeed(seed, candidate),\n })\n if (check.low >= target) {\n nAtLowerBound = candidate\n confirmation = check\n break\n }\n rejected.push(check)\n }\n }\n return { target, n, nAtLowerBound, confirmation, curve, rejected }\n}\n\n/** A seed for the confirmation at `n` that never equals the scan seed. */\nfunction confirmationSeed(seed: number, n: number): number {\n // Golden-ratio hashing keeps distinct (seed, n) pairs on distinct 32-bit\n // streams; the final `| 1` guarantees it differs from the scan's own seed\n // for every n, whose stream the scan already consumed.\n return ((Math.imul((seed | 0) ^ 0x9e3779b9, 0x85ebca6b) + Math.imul(n, 0xc2b2ae35)) | 1) >>> 0\n}\n\ninterface DrawnPairs {\n control: number[]\n candidate: number[]\n controlPass: boolean[]\n candidatePass: boolean[]\n}\n\nfunction drawPairs(\n alternative: PairedPromotionAlternative,\n n: number,\n rng: () => number,\n): DrawnPairs {\n const baseline = alternative.baseline ?? { kind: 'point', value: 0 }\n const control: number[] = new Array(n)\n const candidate: number[] = new Array(n)\n const controlPass: boolean[] = new Array(n)\n const candidatePass: boolean[] = new Array(n)\n for (let i = 0; i < n; i++) {\n const cell = drawCell(alternative.cells, rng)\n const base = drawValue(baseline, rng)\n control[i] = base\n candidate[i] = base + drawValue(cell.delta, rng)\n controlPass[i] = cell.pass?.control ?? false\n candidatePass[i] = cell.pass?.treatment ?? false\n }\n return { control, candidate, controlPass, candidatePass }\n}\n\nfunction drawCell(\n cells: ReadonlyArray<PairedPromotionAlternativeCell>,\n rng: () => number,\n): PairedPromotionAlternativeCell {\n const u = rng()\n let cumulative = 0\n for (const cell of cells) {\n cumulative += cell.probability\n if (u < cumulative) return cell\n }\n // Rounding at the top of the cumulative sum lands here with probability ~0.\n return cells[cells.length - 1]!\n}\n\nexport function drawValue(law: PairedValueLaw, rng: () => number): number {\n switch (law.kind) {\n case 'point':\n return law.value\n case 'atoms': {\n const u = rng()\n let cumulative = 0\n for (const atom of law.atoms) {\n cumulative += atom.probability\n if (u < cumulative) return atom.value\n }\n return law.atoms[law.atoms.length - 1]!.value\n }\n case 'normal': {\n const u1 = 1 - rng()\n const u2 = rng()\n return law.mean + law.sd * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2)\n }\n }\n}\n\nfunction runCall(\n call: PairedPromotionPowerCall,\n pairs: DrawnPairs,\n n: number,\n): PairedPromotionDecision {\n const options: PairedPromotionDecisionOptions = { minPairs: n, ...call.options }\n if (call.outcome === 'pass') {\n // Validated present: the scale is the caller's, never filled in here.\n const scale = call.options.binaryScale as number\n return decidePairedPromotion(\n pairs.controlPass.map((p) => (p ? scale : 0)),\n pairs.candidatePass.map((p) => (p ? scale : 0)),\n options,\n )\n }\n return decidePairedPromotion(pairs.control, pairs.candidate, options)\n}\n\nfunction validateCalls(\n calls: ReadonlyArray<PairedPromotionPowerCall>,\n): ReadonlyArray<PairedPromotionPowerCall> {\n if (!Array.isArray(calls) || calls.length === 0) {\n throw new Error('pairedPromotionPower: at least one call is required')\n }\n calls.forEach((call, i) => {\n if (call.outcome !== 'delta' && call.outcome !== 'pass') {\n throw new Error(`pairedPromotionPower: calls[${i}].outcome must be 'delta' or 'pass'`)\n }\n if (call.options === null || typeof call.options !== 'object') {\n throw new Error(`pairedPromotionPower: calls[${i}].options must be an object`)\n }\n if (\n call.options.minPairs !== undefined &&\n (!Number.isInteger(call.options.minPairs) || call.options.minPairs < 1)\n ) {\n throw new Error(\n `pairedPromotionPower: calls[${i}].options.minPairs must be a positive integer`,\n )\n }\n // Every combination the decision itself refuses is refused here, before\n // the first draw, with the reason named.\n if (call.outcome === 'pass') {\n const scale = call.options.binaryScale\n if (scale === undefined || !Number.isFinite(scale) || scale <= 0) {\n throw new Error(\n `pairedPromotionPower: calls[${i}] is a 'pass' call and must declare a finite positive options.binaryScale`,\n )\n }\n if (call.options.continuous) {\n throw new Error(\n `pairedPromotionPower: calls[${i}] is a 'pass' call; continuous cannot accompany it`,\n )\n }\n if (call.options.statistic === 'median') {\n throw new Error(\n `pairedPromotionPower: calls[${i}] is a 'pass' call; binaryScale requires the mean statistic, not median`,\n )\n }\n } else if (call.options.binaryScale !== undefined) {\n throw new Error(\n `pairedPromotionPower: calls[${i}] is a 'delta' call and cannot declare binaryScale; use a 'pass' call for a two-point outcome`,\n )\n }\n })\n return calls\n}\n\nfunction validateAlternative(\n alternative: PairedPromotionAlternative,\n calls: ReadonlyArray<PairedPromotionPowerCall>,\n): PairedPromotionAlternative {\n if (!alternative || !Array.isArray(alternative.cells) || alternative.cells.length === 0) {\n throw new Error('pairedPromotionPower: alternative.cells must be a non-empty array')\n }\n const needsPass = calls.some((call) => call.outcome === 'pass')\n let total = 0\n alternative.cells.forEach((cell, i) => {\n if (!Number.isFinite(cell.probability) || cell.probability < 0) {\n throw new Error(`pairedPromotionPower: cells[${i}].probability must be a finite number >= 0`)\n }\n total += cell.probability\n if (needsPass) {\n if (\n !cell.pass ||\n typeof cell.pass.control !== 'boolean' ||\n typeof cell.pass.treatment !== 'boolean'\n ) {\n throw new Error(\n `pairedPromotionPower: cells[${i}].pass must give control and treatment booleans when a 'pass' call is configured`,\n )\n }\n }\n validateLaw(cell.delta, `cells[${i}].delta`)\n })\n if (Math.abs(total - 1) > 1e-9) {\n throw new Error(`pairedPromotionPower: cell probabilities must sum to 1, got ${total}`)\n }\n if (alternative.baseline !== undefined) validateLaw(alternative.baseline, 'baseline')\n return alternative\n}\n\nfunction validateLaw(law: PairedValueLaw, where: string): void {\n if (!law || typeof law !== 'object') {\n throw new Error(`pairedPromotionPower: ${where} must be a law object`)\n }\n switch (law.kind) {\n case 'point':\n if (!Number.isFinite(law.value)) {\n throw new Error(`pairedPromotionPower: ${where}.value must be finite`)\n }\n return\n case 'atoms': {\n if (!Array.isArray(law.atoms) || law.atoms.length === 0) {\n throw new Error(`pairedPromotionPower: ${where}.atoms must be a non-empty array`)\n }\n let total = 0\n law.atoms.forEach((atom, i) => {\n if (!Number.isFinite(atom.value)) {\n throw new Error(`pairedPromotionPower: ${where}.atoms[${i}].value must be finite`)\n }\n if (!Number.isFinite(atom.probability) || atom.probability < 0) {\n throw new Error(\n `pairedPromotionPower: ${where}.atoms[${i}].probability must be a finite number >= 0`,\n )\n }\n total += atom.probability\n })\n if (Math.abs(total - 1) > 1e-9) {\n throw new Error(\n `pairedPromotionPower: ${where}.atoms probabilities must sum to 1, got ${total}`,\n )\n }\n return\n }\n case 'normal':\n if (!Number.isFinite(law.mean) || !Number.isFinite(law.sd) || law.sd < 0) {\n throw new Error(`pairedPromotionPower: ${where} needs a finite mean and a finite sd >= 0`)\n }\n return\n default:\n throw new Error(`pairedPromotionPower: ${where}.kind must be 'point', 'atoms' or 'normal'`)\n }\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
1
  import { a as hashCanonical, i as compareCodeUnits, r as canonicalString } from "./canonical-CicJlINA.js";
2
- import { Q as confidenceInterval, ct as mean, f as DECISION_PAIRED_DELTA_STATISTIC, w as wilson } from "./statistics-CvvC-MPv.js";
3
- import { t as mulberry32 } from "./random-DrzcgC7F.js";
2
+ import { Q as confidenceInterval, ct as mean, f as DECISION_PAIRED_DELTA_STATISTIC } from "./statistics-CvvC-MPv.js";
4
3
  import { i as pairedDeltaTest, r as minimumPairsForPairedDeltaTest, t as decidePairedPromotion } from "./paired-promotion-decision-Bi9__RQS.js";
5
4
  import { a as SearchLedgerError } from "./search-ledger-file-BhOdgYIt.js";
6
5
  import { c as redactText } from "./redact-zp_ILC4l.js";
7
- import { l as searchUnitScores, p as USD_TOLERANCE, t as FileSearchLedger, u as searchCellId } from "./search-ledger-Dk-yk5Li.js";
6
+ import { l as searchUnitScores, p as USD_TOLERANCE, t as FileSearchLedger, u as searchCellId } from "./search-ledger-9liYyZf5.js";
7
+ import { n as pairedPromotionPower } from "./paired-promotion-power-D03cUj9E.js";
8
8
  import { t as acquireSingleRunLock } from "./single-run-lock-CUj2tjxT.js";
9
9
  // src/statistics/normalize-signed-zero.ts
10
10
  /** Normalize IEEE negative zero before numerical evidence reaches canonical JSON. */
@@ -290,289 +290,6 @@ var SearchArtifactCache = class {
290
290
  if (!this.artifacts.has(nodeId)) this.artifacts.set(nodeId, held);
291
291
  }
292
292
  };
293
- // src/paired-promotion-power.ts
294
- /**
295
- * @module
296
- * Joint power of a configured {@link decidePairedPromotion} call, computed by
297
- * running that call on data drawn from a registered alternative.
298
- *
299
- * Why a simulation and not a formula: `decidePairedPromotion` is not one test.
300
- * It routes by sample size (exact sign test below `BOOTSTRAP_GATE_MIN_N`,
301
- * paired bootstrap at or above it) and by shape (Tango's score interval on a
302
- * two-point outcome), and on top of the deciding interval it applies a
303
- * sufficiency floor, a zero-width refusal and McNemar's exact veto. A
304
- * preregistration usually seals two such calls, a continuous primary and a
305
- * pass/fail veto drawn from the same episodes, and promotes only when both
306
- * promote. The sample size at which THAT procedure reaches a target power is
307
- * not the power of any one channel: the sign-test power at n can be 0.8 while
308
- * the promotion probability is well below it because the veto binds
309
- * (agent-eval#785). Closed forms exist for single channels only, and each one
310
- * shipped here under-sized the call it stood in for (`mcnemarRequiredN` is
311
- * Lachin's normal approximation, 0.65 exact power where it promised 0.8 at 14
312
- * pairs; `requiredPairedSampleSize` sizes a paired t the procedure never runs).
313
- * So the only faithful power is the procedure's own promotion rate under the
314
- * alternative, and that is what this module computes: draw n pairs from the
315
- * registered law, run every configured call on them exactly as the caller
316
- * will, repeat, count.
317
- *
318
- * The alternative is DECLARATIVE and serializable, never a sampler function,
319
- * so a preregistration can seal it as bytes: a mixture of cells, each with a
320
- * probability, an optional pass/fail outcome per arm for the binary calls,
321
- * and a law for the candidate-minus-baseline delta on the continuous scale.
322
- * Drawing the cell first and the delta given the cell is what makes the two
323
- * channels one joint law rather than two marginals.
324
- *
325
- * Everything is seeded (`mulberry32`), so a sizing is reproducible from its
326
- * inputs, and every estimate carries its Monte Carlo standard error and a
327
- * Wilson interval: a power read at 0.80 from 1,000 simulations is 0.80 ± 0.013.
328
- */
329
- /**
330
- * Joint power of the configured calls at `n` under the registered alternative:
331
- * the fraction of simulated data sets on which every call promotes.
332
- */
333
- function pairedPromotionPower(options) {
334
- const n = options.n;
335
- if (!Number.isInteger(n) || n < 1) throw new Error(`pairedPromotionPower: n must be a positive integer, got ${n}`);
336
- const simulations = options.simulations ?? 1e3;
337
- if (!Number.isInteger(simulations) || simulations < 1) throw new Error(`pairedPromotionPower: simulations must be a positive integer, got ${simulations}`);
338
- const seed = options.seed ?? 1;
339
- if (!Number.isFinite(seed)) throw new Error(`pairedPromotionPower: seed must be finite, got ${seed}`);
340
- const calls = validateCalls(options.calls);
341
- const alternative = validateAlternative(options.alternative, calls);
342
- const rng = mulberry32(seed);
343
- let promotions = 0;
344
- let refusals = 0;
345
- const perCall = calls.map(() => ({
346
- promote: 0,
347
- insufficient: 0,
348
- indeterminate: 0,
349
- exactTestVetoes: 0,
350
- hold: 0,
351
- methods: /* @__PURE__ */ new Map()
352
- }));
353
- for (let s = 0; s < simulations; s++) {
354
- const pairs = drawPairs(alternative, n, rng);
355
- let all = true;
356
- let anyRefusal = false;
357
- for (let c = 0; c < calls.length; c++) {
358
- const call = calls[c];
359
- const decision = runCall(call, pairs, n);
360
- const tally = perCall[c];
361
- tally.methods.set(decision.method, (tally.methods.get(decision.method) ?? 0) + 1);
362
- if (decision.promote) tally.promote++;
363
- else {
364
- if (!decision.sufficient) tally.insufficient++;
365
- if (decision.indeterminate) tally.indeterminate++;
366
- if (decision.exactTestVetoes) tally.exactTestVetoes++;
367
- if (decision.sufficient && !decision.indeterminate && !decision.exactTestVetoes) tally.hold++;
368
- }
369
- if (!decision.promote) all = false;
370
- if (!decision.sufficient || decision.indeterminate || decision.exactTestVetoes) anyRefusal = true;
371
- }
372
- if (all) promotions++;
373
- else if (anyRefusal) refusals++;
374
- }
375
- const power = promotions / simulations;
376
- const interval = wilson(promotions, simulations, .95);
377
- return {
378
- n,
379
- simulations,
380
- seed,
381
- power,
382
- standardError: Math.sqrt(power * (1 - power) / simulations),
383
- low: interval.lower,
384
- high: interval.upper,
385
- refuse: refusals / simulations,
386
- hold: (simulations - promotions - refusals) / simulations,
387
- calls: perCall.map((tally, c) => ({
388
- outcome: calls[c].outcome,
389
- promote: tally.promote / simulations,
390
- insufficient: tally.insufficient / simulations,
391
- indeterminate: tally.indeterminate / simulations,
392
- exactTestVetoes: tally.exactTestVetoes / simulations,
393
- hold: tally.hold / simulations,
394
- methods: Object.fromEntries([...tally.methods.entries()].map(([method, count]) => [method, count / simulations]))
395
- }))
396
- };
397
- }
398
- /**
399
- * First n at which {@link pairedPromotionPower} reaches the target, scanning
400
- * every n from `minPairs` upward. Both the point-estimate answer and the
401
- * confirmed lower-bound answer are returned; the scan runs until the latter is
402
- * reached (so the curve past `n` is retained) or `maxPairs` is exhausted. A
403
- * candidate selected by the scan's bound is certified only by an independent
404
- * simulation at that n on a different seed; a candidate that fails it is
405
- * recorded and the scan continues.
406
- */
407
- function requiredPairsForPairedPromotion(options) {
408
- const target = options.target ?? .8;
409
- if (!Number.isFinite(target) || target <= 0 || target >= 1) throw new Error(`requiredPairsForPairedPromotion: target must be in (0,1), got ${target}`);
410
- const minPairs = options.minPairs ?? 1;
411
- const maxPairs = options.maxPairs ?? 200;
412
- if (!Number.isInteger(minPairs) || minPairs < 1) throw new Error(`requiredPairsForPairedPromotion: minPairs must be a positive integer, got ${minPairs}`);
413
- if (!Number.isInteger(maxPairs) || maxPairs < minPairs) throw new Error(`requiredPairsForPairedPromotion: maxPairs must be an integer >= minPairs, got ${maxPairs}`);
414
- const seed = options.seed ?? 1;
415
- const curve = [];
416
- const rejected = [];
417
- let n = null;
418
- let nAtLowerBound = null;
419
- let confirmation = null;
420
- for (let candidate = minPairs; candidate <= maxPairs; candidate++) {
421
- const result = pairedPromotionPower({
422
- n: candidate,
423
- alternative: options.alternative,
424
- calls: options.calls,
425
- simulations: options.simulations,
426
- seed
427
- });
428
- curve.push(result);
429
- if (n === null && result.power >= target) n = candidate;
430
- if (result.low >= target) {
431
- const check = pairedPromotionPower({
432
- n: candidate,
433
- alternative: options.alternative,
434
- calls: options.calls,
435
- simulations: options.simulations,
436
- seed: confirmationSeed(seed, candidate)
437
- });
438
- if (check.low >= target) {
439
- nAtLowerBound = candidate;
440
- confirmation = check;
441
- break;
442
- }
443
- rejected.push(check);
444
- }
445
- }
446
- return {
447
- target,
448
- n,
449
- nAtLowerBound,
450
- confirmation,
451
- curve,
452
- rejected
453
- };
454
- }
455
- /** A seed for the confirmation at `n` that never equals the scan seed. */
456
- function confirmationSeed(seed, n) {
457
- return (Math.imul((seed | 0) ^ 2654435769, 2246822507) + Math.imul(n, 3266489909) | 1) >>> 0;
458
- }
459
- function drawPairs(alternative, n, rng) {
460
- const baseline = alternative.baseline ?? {
461
- kind: "point",
462
- value: 0
463
- };
464
- const control = new Array(n);
465
- const candidate = new Array(n);
466
- const controlPass = new Array(n);
467
- const candidatePass = new Array(n);
468
- for (let i = 0; i < n; i++) {
469
- const cell = drawCell(alternative.cells, rng);
470
- const base = drawValue(baseline, rng);
471
- control[i] = base;
472
- candidate[i] = base + drawValue(cell.delta, rng);
473
- controlPass[i] = cell.pass?.control ?? false;
474
- candidatePass[i] = cell.pass?.treatment ?? false;
475
- }
476
- return {
477
- control,
478
- candidate,
479
- controlPass,
480
- candidatePass
481
- };
482
- }
483
- function drawCell(cells, rng) {
484
- const u = rng();
485
- let cumulative = 0;
486
- for (const cell of cells) {
487
- cumulative += cell.probability;
488
- if (u < cumulative) return cell;
489
- }
490
- return cells[cells.length - 1];
491
- }
492
- function drawValue(law, rng) {
493
- switch (law.kind) {
494
- case "point": return law.value;
495
- case "atoms": {
496
- const u = rng();
497
- let cumulative = 0;
498
- for (const atom of law.atoms) {
499
- cumulative += atom.probability;
500
- if (u < cumulative) return atom.value;
501
- }
502
- return law.atoms[law.atoms.length - 1].value;
503
- }
504
- case "normal": {
505
- const u1 = 1 - rng();
506
- const u2 = rng();
507
- return law.mean + law.sd * Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
508
- }
509
- }
510
- }
511
- function runCall(call, pairs, n) {
512
- const options = {
513
- minPairs: n,
514
- ...call.options
515
- };
516
- if (call.outcome === "pass") {
517
- const scale = call.options.binaryScale;
518
- return decidePairedPromotion(pairs.controlPass.map((p) => p ? scale : 0), pairs.candidatePass.map((p) => p ? scale : 0), options);
519
- }
520
- return decidePairedPromotion(pairs.control, pairs.candidate, options);
521
- }
522
- function validateCalls(calls) {
523
- if (!Array.isArray(calls) || calls.length === 0) throw new Error("pairedPromotionPower: at least one call is required");
524
- calls.forEach((call, i) => {
525
- if (call.outcome !== "delta" && call.outcome !== "pass") throw new Error(`pairedPromotionPower: calls[${i}].outcome must be 'delta' or 'pass'`);
526
- if (call.options === null || typeof call.options !== "object") throw new Error(`pairedPromotionPower: calls[${i}].options must be an object`);
527
- if (call.options.minPairs !== void 0 && (!Number.isInteger(call.options.minPairs) || call.options.minPairs < 1)) throw new Error(`pairedPromotionPower: calls[${i}].options.minPairs must be a positive integer`);
528
- if (call.outcome === "pass") {
529
- const scale = call.options.binaryScale;
530
- if (scale === void 0 || !Number.isFinite(scale) || scale <= 0) throw new Error(`pairedPromotionPower: calls[${i}] is a 'pass' call and must declare a finite positive options.binaryScale`);
531
- if (call.options.continuous) throw new Error(`pairedPromotionPower: calls[${i}] is a 'pass' call; continuous cannot accompany it`);
532
- if (call.options.statistic === "median") throw new Error(`pairedPromotionPower: calls[${i}] is a 'pass' call; binaryScale requires the mean statistic, not median`);
533
- } else if (call.options.binaryScale !== void 0) throw new Error(`pairedPromotionPower: calls[${i}] is a 'delta' call and cannot declare binaryScale; use a 'pass' call for a two-point outcome`);
534
- });
535
- return calls;
536
- }
537
- function validateAlternative(alternative, calls) {
538
- if (!alternative || !Array.isArray(alternative.cells) || alternative.cells.length === 0) throw new Error("pairedPromotionPower: alternative.cells must be a non-empty array");
539
- const needsPass = calls.some((call) => call.outcome === "pass");
540
- let total = 0;
541
- alternative.cells.forEach((cell, i) => {
542
- if (!Number.isFinite(cell.probability) || cell.probability < 0) throw new Error(`pairedPromotionPower: cells[${i}].probability must be a finite number >= 0`);
543
- total += cell.probability;
544
- if (needsPass) {
545
- if (!cell.pass || typeof cell.pass.control !== "boolean" || typeof cell.pass.treatment !== "boolean") throw new Error(`pairedPromotionPower: cells[${i}].pass must give control and treatment booleans when a 'pass' call is configured`);
546
- }
547
- validateLaw(cell.delta, `cells[${i}].delta`);
548
- });
549
- if (Math.abs(total - 1) > 1e-9) throw new Error(`pairedPromotionPower: cell probabilities must sum to 1, got ${total}`);
550
- if (alternative.baseline !== void 0) validateLaw(alternative.baseline, "baseline");
551
- return alternative;
552
- }
553
- function validateLaw(law, where) {
554
- if (!law || typeof law !== "object") throw new Error(`pairedPromotionPower: ${where} must be a law object`);
555
- switch (law.kind) {
556
- case "point":
557
- if (!Number.isFinite(law.value)) throw new Error(`pairedPromotionPower: ${where}.value must be finite`);
558
- return;
559
- case "atoms": {
560
- if (!Array.isArray(law.atoms) || law.atoms.length === 0) throw new Error(`pairedPromotionPower: ${where}.atoms must be a non-empty array`);
561
- let total = 0;
562
- law.atoms.forEach((atom, i) => {
563
- if (!Number.isFinite(atom.value)) throw new Error(`pairedPromotionPower: ${where}.atoms[${i}].value must be finite`);
564
- if (!Number.isFinite(atom.probability) || atom.probability < 0) throw new Error(`pairedPromotionPower: ${where}.atoms[${i}].probability must be a finite number >= 0`);
565
- total += atom.probability;
566
- });
567
- if (Math.abs(total - 1) > 1e-9) throw new Error(`pairedPromotionPower: ${where}.atoms probabilities must sum to 1, got ${total}`);
568
- return;
569
- }
570
- case "normal":
571
- if (!Number.isFinite(law.mean) || !Number.isFinite(law.sd) || law.sd < 0) throw new Error(`pairedPromotionPower: ${where} needs a finite mean and a finite sd >= 0`);
572
- return;
573
- default: throw new Error(`pairedPromotionPower: ${where}.kind must be 'point', 'atoms' or 'normal'`);
574
- }
575
- }
576
293
  // src/campaign/search-claim.ts
577
294
  /**
578
295
  * The claim: the one statement a search makes about its nodes, made once, on
@@ -2604,6 +2321,6 @@ function paretoFrontierWithCrowding(candidates, objectives) {
2604
2321
  if (frontier.length === 0) return [];
2605
2322
  return crowdingDistance(frontier, objectives).sort((a, b) => b.distance - a.distance);
2606
2323
  }
2607
- export { normalizeSignedZero as A, SEARCH_ESTIMATOR as C, searchEstimateMethod as D, searchCellSetDigest as E, searchPosterior as O, requiredPairsForPairedPromotion as S, estimateNodeFromCells as T, planSearchClaim as _, searchDivergence as a, verifySearchClaim as b, MIN_OUTCOMES_FOR_WEIGHT as c, rankingSplit as d, summarizeSamples as f, decideSearchClaim as g, SEARCH_CLAIM_RULE as h, runSearch as i, seedFromDigest as k, operatorYield as l, searchExpansionIndex as m, paretoFrontier as n, searchPolicyView as o, SEARCH_KERNEL_SOURCE as p, paretoFrontierWithCrowding as r, EXPANSION_OPERATORS as s, dominates as t, INSUFFICIENT_FROM as u, searchClaimDecision as v, estimateNode as w, pairedPromotionPower as x, searchClaimReserveUsd as y };
2324
+ export { estimateNodeFromCells as C, seedFromDigest as D, searchPosterior as E, normalizeSignedZero as O, estimateNode as S, searchEstimateMethod as T, planSearchClaim as _, searchDivergence as a, verifySearchClaim as b, MIN_OUTCOMES_FOR_WEIGHT as c, rankingSplit as d, summarizeSamples as f, decideSearchClaim as g, SEARCH_CLAIM_RULE as h, runSearch as i, operatorYield as l, searchExpansionIndex as m, paretoFrontier as n, searchPolicyView as o, SEARCH_KERNEL_SOURCE as p, paretoFrontierWithCrowding as r, EXPANSION_OPERATORS as s, dominates as t, INSUFFICIENT_FROM as u, searchClaimDecision as v, searchCellSetDigest as w, SEARCH_ESTIMATOR as x, searchClaimReserveUsd as y };
2608
2325
 
2609
- //# sourceMappingURL=pareto-fJZg8pXm.js.map
2326
+ //# sourceMappingURL=pareto-UzjB3ZBk.js.map