@tangle-network/agent-eval 0.216.0 → 0.217.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +141 -57
- package/README.md +2 -2
- package/dist/adapters/http.d.ts +3 -3
- package/dist/{agent-profile-CIA2Atnt.d.ts → agent-profile-DTt8uGP9.d.ts} +5 -18
- package/dist/agent-profile-DTt8uGP9.d.ts.map +1 -0
- package/dist/{agent-profile-cell-BYc0bxsL.js → agent-profile-cell-B1V1gUZs.js} +4 -53
- package/dist/agent-profile-cell-B1V1gUZs.js.map +1 -0
- package/dist/{agent-profile-cell-BNp-Js9F.d.ts → agent-profile-cell-BcpFbNd5.d.ts} +2 -22
- package/dist/agent-profile-cell-BcpFbNd5.d.ts.map +1 -0
- package/dist/{agreement-irr-DsTURxdr.js → agreement-irr-BhAqjluA.js} +5 -5
- package/dist/agreement-irr-BhAqjluA.js.map +1 -0
- package/dist/analyst/index.d.ts +37 -170
- package/dist/analyst/index.d.ts.map +1 -1
- package/dist/analyst/index.js +17 -192
- package/dist/analyst/index.js.map +1 -1
- package/dist/{analyze-runs-3xpby3p2.js → analyze-runs-DqPJaJ4X.js} +763 -555
- package/dist/analyze-runs-DqPJaJ4X.js.map +1 -0
- package/dist/{analyze-runs-CSDAh8A7.d.ts → analyze-runs-DyeNcenW.d.ts} +18 -16
- package/dist/analyze-runs-DyeNcenW.d.ts.map +1 -0
- package/dist/authenticity/index.d.ts +0 -23
- package/dist/authenticity/index.d.ts.map +1 -1
- package/dist/authenticity/index.js +1 -34
- package/dist/authenticity/index.js.map +1 -1
- package/dist/{benchmark-command-CByl9dxG.js → benchmark-command-xQK8nOI6.js} +591 -109
- package/dist/benchmark-command-xQK8nOI6.js.map +1 -0
- package/dist/benchmarks/index.d.ts +5 -6
- package/dist/benchmarks/index.d.ts.map +1 -1
- package/dist/benchmarks/index.js +4 -4
- package/dist/benchmarks/index.js.map +1 -1
- package/dist/builder-eval/index.d.ts +3 -3
- package/dist/builder-eval/index.js +2 -2
- package/dist/builder-eval/index.js.map +1 -1
- package/dist/campaign/index.d.ts +8 -8
- package/dist/campaign/index.js +15 -15
- package/dist/{campaign-BQ-Jujuj.js → campaign-CJGkxAQ6.js} +256 -765
- package/dist/campaign-CJGkxAQ6.js.map +1 -0
- package/dist/{campaign-evidence-OcUciD-N.js → campaign-evidence-CQYw8_0H.js} +10 -18
- package/dist/{campaign-evidence-OcUciD-N.js.map → campaign-evidence-CQYw8_0H.js.map} +1 -1
- package/dist/{canonical-BsZVgBhW.js → canonical-Dq2hOQ8L.js} +2 -2
- package/dist/{canonical-BsZVgBhW.js.map → canonical-Dq2hOQ8L.js.map} +1 -1
- package/dist/cell-spend-DEWR3nj-.js +90 -0
- package/dist/cell-spend-DEWR3nj-.js.map +1 -0
- package/dist/{chat-client-DthPOYGd.js → chat-client-ldqxYYTN.js} +2 -2
- package/dist/{chat-client-DthPOYGd.js.map → chat-client-ldqxYYTN.js.map} +1 -1
- package/dist/{chat-json-call-BrKsyspg.js → chat-json-call-DavzexSp.js} +3 -2
- package/dist/chat-json-call-DavzexSp.js.map +1 -0
- package/dist/{claim-BXcZw-jo.js → claim-BPVuyHuy.js} +4 -4
- package/dist/claim-BPVuyHuy.js.map +1 -0
- package/dist/cli.js +113 -12
- package/dist/cli.js.map +1 -1
- package/dist/{client-BzL9niqi.d.ts → client-hwo3cJjR.d.ts} +3 -16
- package/dist/client-hwo3cJjR.d.ts.map +1 -0
- package/dist/{cluster-bootstrap-B3JywQQn.js → cluster-bootstrap-DEwwhl0C.js} +3 -3
- package/dist/{cluster-bootstrap-B3JywQQn.js.map → cluster-bootstrap-DEwwhl0C.js.map} +1 -1
- package/dist/{produced-state-ZCtDBxAE.js → completion-verifier-B_jw6C9p.js} +9 -64
- package/dist/completion-verifier-B_jw6C9p.js.map +1 -0
- package/dist/contract/index.d.ts +13 -13
- package/dist/contract/index.js +1041 -1473
- package/dist/contract/index.js.map +1 -1
- package/dist/{cost-ledger-D2FnJPBF.js → cost-ledger-D0352Z4d.js} +832 -817
- package/dist/cost-ledger-D0352Z4d.js.map +1 -0
- package/dist/{cost-ledger-CO7UyEcQ.d.ts → cost-ledger-Jk62OBfF.d.ts} +12 -47
- package/dist/cost-ledger-Jk62OBfF.d.ts.map +1 -0
- package/dist/dataset-DQqhOCPt.d.ts.map +1 -1
- package/dist/{default-registry-Cn1LQ2Ve.js → default-registry-ClYjq3QE.js} +8 -11
- package/dist/default-registry-ClYjq3QE.js.map +1 -0
- package/dist/{default-registry-BrWh62zf.d.ts → default-registry-DXgLmoDI.d.ts} +7 -50
- package/dist/default-registry-DXgLmoDI.d.ts.map +1 -0
- package/dist/{define-agent-eval-Ch3MWX6k.js → define-agent-eval-BDxNlP3D.js} +10 -10
- package/dist/define-agent-eval-BDxNlP3D.js.map +1 -0
- package/dist/{define-agent-eval-CEWkX5wZ.d.ts → define-agent-eval-BIkH_gAR.d.ts} +9 -9
- package/dist/define-agent-eval-BIkH_gAR.d.ts.map +1 -0
- package/dist/{descriptive-CnU8Yq29.js → descriptive-CVNy48rR.js} +2 -2
- package/dist/{descriptive-CnU8Yq29.js.map → descriptive-CVNy48rR.js.map} +1 -1
- package/dist/diagnosis/index.d.ts +4 -14
- package/dist/diagnosis/index.d.ts.map +1 -1
- package/dist/diagnosis/index.js +67 -28
- package/dist/diagnosis/index.js.map +1 -1
- package/dist/{dspy-rlm-engine-D-KX_e64.js → dspy-rlm-engine-DhdCr1Dc.js} +5 -10
- package/dist/dspy-rlm-engine-DhdCr1Dc.js.map +1 -0
- package/dist/{emitter-HcCmFQl_.d.ts → emitter-DABYYTcH.d.ts} +5 -5
- package/dist/emitter-DABYYTcH.d.ts.map +1 -0
- package/dist/emitter-DbMXUIVP.js.map +1 -1
- package/dist/{engine-C_zNWfM8.d.ts → engine-BvZ0R-36.d.ts} +18 -22
- package/dist/engine-BvZ0R-36.d.ts.map +1 -0
- package/dist/{equal-terms-Blx9Zk5I.js → equal-terms-DbPGcVtI.js} +2 -2
- package/dist/equal-terms-DbPGcVtI.js.map +1 -0
- package/dist/{errors-Dngq5h35.js → errors-BcOs5MKW.js} +6 -8
- package/dist/{errors-Dngq5h35.js.map → errors-BcOs5MKW.js.map} +1 -1
- package/dist/{eval-campaign-4byTtqrf.js → eval-campaign-BLEfBGbA.js} +6 -6
- package/dist/{eval-campaign-4byTtqrf.js.map → eval-campaign-BLEfBGbA.js.map} +1 -1
- package/dist/{evaluation-Cn2dRZIz.d.ts → evaluation-jTjC_QQD.d.ts} +8 -8
- package/dist/evaluation-jTjC_QQD.d.ts.map +1 -0
- package/dist/evaluation.d.ts +2 -2
- package/dist/evaluation.js +1 -1
- package/dist/evaluation.js.map +1 -1
- package/dist/{exact-types-CD0lf5Qd.d.ts → exact-types-CflbJ9f3.d.ts} +2 -2
- package/dist/{exact-types-CD0lf5Qd.d.ts.map → exact-types-CflbJ9f3.d.ts.map} +1 -1
- package/dist/{exec-ChJ6d5if.js → exec-CpbtOXVt.js} +2 -24
- package/dist/{exec-ChJ6d5if.js.map → exec-CpbtOXVt.js.map} +1 -1
- package/dist/execution-tracks-CpgFPpS5.js.map +1 -1
- package/dist/experiment/index.d.ts +331 -33
- package/dist/experiment/index.d.ts.map +1 -1
- package/dist/experiment/index.js +303 -101
- package/dist/experiment/index.js.map +1 -1
- package/dist/{exporters-X4xwla8A.js → exporters-Cl6IeNAu.js} +3 -3
- package/dist/exporters-Cl6IeNAu.js.map +1 -0
- package/dist/{exporters-ChTnR90l.d.ts → exporters-Dk3oEbK_.d.ts} +2 -4
- package/dist/{exporters-ChTnR90l.d.ts.map → exporters-Dk3oEbK_.d.ts.map} +1 -1
- package/dist/{external-optimizer-process-nnsY5D_4.js → external-optimizer-process-KVFGvYX1.js} +4 -3
- package/dist/external-optimizer-process-KVFGvYX1.js.map +1 -0
- package/dist/{external-optimizer-subprocess-DcerlZSG.js → external-optimizer-subprocess-XIHBOSKt.js} +551 -553
- package/dist/external-optimizer-subprocess-XIHBOSKt.js.map +1 -0
- package/dist/{extract-usage-BrnzRJ6H.js → extract-usage-BZLU01kR.js} +2 -23
- package/dist/{extract-usage-BrnzRJ6H.js.map → extract-usage-BZLU01kR.js.map} +1 -1
- package/dist/{failure-cluster-CbgEDcFJ.d.ts → failure-cluster-DN0t63Z0.d.ts} +4 -43
- package/dist/failure-cluster-DN0t63Z0.d.ts.map +1 -0
- package/dist/{failure-taxonomy-CjabTATj.js → failure-taxonomy-Bx5oWqaW.js} +2 -19
- package/dist/{failure-taxonomy-CjabTATj.js.map → failure-taxonomy-Bx5oWqaW.js.map} +1 -1
- package/dist/{feedback-trajectory-DkaDj0dj.d.ts → feedback-trajectory-BWmhRBGu.d.ts} +8 -13
- package/dist/feedback-trajectory-BWmhRBGu.d.ts.map +1 -0
- package/dist/{semantic-concept-judge-zQBG9hHU.js → findings-store-BMVQtEsm.js} +13 -255
- package/dist/findings-store-BMVQtEsm.js.map +1 -0
- package/dist/fuzz.d.ts +20 -63
- package/dist/fuzz.d.ts.map +1 -1
- package/dist/fuzz.js +62 -108
- package/dist/fuzz.js.map +1 -1
- package/dist/{hf-dataset-BR2yhuNn.js → hf-dataset-D9f7-oX7.js} +4 -24
- package/dist/hf-dataset-D9f7-oX7.js.map +1 -0
- package/dist/hosted/index.d.ts +2 -2
- package/dist/hosted/index.js +2 -2
- package/dist/{index-DHKEAv3s.d.ts → index-9jJ2fKSX.d.ts} +2 -64
- package/dist/index-9jJ2fKSX.d.ts.map +1 -0
- package/dist/{index-oQ4aHziK.d.ts → index-BLGqZ81N.d.ts} +11 -16
- package/dist/{index-oQ4aHziK.d.ts.map → index-BLGqZ81N.d.ts.map} +1 -1
- package/dist/{index-DlH0Y9gI.d.ts → index-BiT6RtNH.d.ts} +31 -43
- package/dist/index-BiT6RtNH.d.ts.map +1 -0
- package/dist/{index-Ch9VxCpK.d.ts → index-CX-GAUpw.d.ts} +4 -8
- package/dist/index-CX-GAUpw.d.ts.map +1 -0
- package/dist/{index-CPJJr98o.d.ts → index-CiRwLv2R.d.ts} +5 -68
- package/dist/index-CiRwLv2R.d.ts.map +1 -0
- package/dist/{index-NvGBiO8H.d.ts → index-ZuWx52WG.d.ts} +266 -495
- package/dist/index-ZuWx52WG.d.ts.map +1 -0
- package/dist/index.d.ts +2256 -2165
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +514 -1192
- package/dist/index.js.map +1 -1
- package/dist/{integrity-BV1ZDf42.js → integrity-DOd9Q86O.js} +7 -12
- package/dist/integrity-DOd9Q86O.js.map +1 -0
- package/dist/{integrity-BvnVARVl.js → integrity-W4CADcFc.js} +3 -7
- package/dist/{integrity-BvnVARVl.js.map → integrity-W4CADcFc.js.map} +1 -1
- package/dist/{integrity-CCAxGVK6.d.ts → integrity-_qgZLA5p.d.ts} +4 -6
- package/dist/{integrity-CCAxGVK6.d.ts.map → integrity-_qgZLA5p.d.ts.map} +1 -1
- package/dist/{internal-CRHwS9iz.js → internal-BTK91Bqt.js} +3 -3
- package/dist/{internal-CRHwS9iz.js.map → internal-BTK91Bqt.js.map} +1 -1
- package/dist/interval-Dq9gt7jx.js +219 -0
- package/dist/interval-Dq9gt7jx.js.map +1 -0
- package/dist/interval-ohuOJurX.d.ts +47 -0
- package/dist/interval-ohuOJurX.d.ts.map +1 -0
- package/dist/{journal-DvKCvjty.js → journal-CR4QikYp.js} +110 -110
- package/dist/journal-CR4QikYp.js.map +1 -0
- package/dist/json-BhdsJaOQ.js +111 -0
- package/dist/json-BhdsJaOQ.js.map +1 -0
- package/dist/{judge-calibration-N44V04Se.js → judge-calibration-BkXMIWA4.js} +3 -3
- package/dist/{judge-calibration-N44V04Se.js.map → judge-calibration-BkXMIWA4.js.map} +1 -1
- package/dist/{judge-gate-B1qYnkpi.js → judge-gate-Co5tuh57.js} +4 -27
- package/dist/judge-gate-Co5tuh57.js.map +1 -0
- package/dist/{judge-gate-DNcUrRFA.d.ts → judge-gate-DsL0yFcb.d.ts} +9 -10
- package/dist/judge-gate-DsL0yFcb.d.ts.map +1 -0
- package/dist/{kind-factory-CbZ-6tvu.js → kind-factory-Gy4fXixd.js} +88 -17
- package/dist/kind-factory-Gy4fXixd.js.map +1 -0
- package/dist/ledger-core/index.d.ts +2 -2
- package/dist/ledger-core/index.js +4 -4
- package/dist/{ledger-core-B2hahzrt.js → ledger-core-DrJhbms9.js} +5 -84
- package/dist/ledger-core-DrJhbms9.js.map +1 -0
- package/dist/{llm-client-DQveoxSM.js → llm-client-BshCREn6.js} +90 -219
- package/dist/llm-client-BshCREn6.js.map +1 -0
- package/dist/{llm-judge-PXV7Xljl.js → llm-judge-C8jMTNNG.js} +501 -931
- package/dist/llm-judge-C8jMTNNG.js.map +1 -0
- package/dist/matrix/index.d.ts +4 -4
- package/dist/matrix/index.js +3 -2
- package/dist/{matrix-_BV9-Lk5.js → matrix-B8l-Nsxd.js} +7 -96
- package/dist/matrix-B8l-Nsxd.js.map +1 -0
- package/dist/{matrix-CHMiebFx.d.ts → matrix-DHe-dwVJ.d.ts} +10 -19
- package/dist/matrix-DHe-dwVJ.d.ts.map +1 -0
- package/dist/{paired-tests-jbwPdSz5.js → mean-BbA_JCzW.js} +45 -5
- package/dist/mean-BbA_JCzW.js.map +1 -0
- package/dist/meta-eval/index.d.ts +6 -6
- package/dist/meta-eval/index.js +10 -10
- package/dist/meta-eval/index.js.map +1 -1
- package/dist/metrics-BKaPBtIG.js.map +1 -1
- package/dist/mint-BRZXuwRi.d.ts +81 -0
- package/dist/mint-BRZXuwRi.d.ts.map +1 -0
- package/dist/{mint-Csor1BLJ.js → mint-zszyYlpA.js} +4 -4
- package/dist/{mint-Csor1BLJ.js.map → mint-zszyYlpA.js.map} +1 -1
- package/dist/{multi-layer-verifier-BUaQ4C17.d.ts → multi-layer-verifier-DEgMUk6v.d.ts} +2 -2
- package/dist/{multi-layer-verifier-BUaQ4C17.d.ts.map → multi-layer-verifier-DEgMUk6v.d.ts.map} +1 -1
- package/dist/{multiplicity-D9cjmStq.js → multiplicity-Sg0U7h8j.js} +2 -2
- package/dist/{multiplicity-D9cjmStq.js.map → multiplicity-Sg0U7h8j.js.map} +1 -1
- package/dist/multishot/golden/index.d.ts +1 -23
- package/dist/multishot/golden/index.d.ts.map +1 -1
- package/dist/multishot/golden/index.js +5 -8
- package/dist/multishot/golden/index.js.map +1 -1
- package/dist/multishot/index.d.ts +3 -12
- package/dist/multishot/index.d.ts.map +1 -1
- package/dist/multishot/index.js +4 -3
- package/dist/multishot/index.js.map +1 -1
- package/dist/nonnegative-number-DvdMxg_f.js +9 -0
- package/dist/nonnegative-number-DvdMxg_f.js.map +1 -0
- package/dist/openapi.json +114 -10
- package/dist/{otel-spans-C8YIJWy1.js → otel-spans-CdN909k1.js} +3 -3
- package/dist/{otel-spans-C8YIJWy1.js.map → otel-spans-CdN909k1.js.map} +1 -1
- package/dist/{paired-arms-DzKn0TfY.js → paired-arms-Ct4LTKc1.js} +32 -9
- package/dist/paired-arms-Ct4LTKc1.js.map +1 -0
- package/dist/{paired-binary-Q36bhn2R.js → paired-binary-B_nejA44.js} +3 -36
- package/dist/paired-binary-B_nejA44.js.map +1 -0
- package/dist/{paired-promotion-decision-DPKz8ZnF.d.ts → paired-promotion-decision-CQMJ5xGN.d.ts} +2 -34
- package/dist/paired-promotion-decision-CQMJ5xGN.d.ts.map +1 -0
- package/dist/{paired-promotion-decision-BN6IQTiu.js → paired-promotion-decision-D0SHb-ZL.js} +3 -3
- package/dist/{paired-promotion-decision-BN6IQTiu.js.map → paired-promotion-decision-D0SHb-ZL.js.map} +1 -1
- package/dist/{search-kernel-BztlERLv.js → pareto-BzsxidFD.js} +883 -861
- package/dist/pareto-BzsxidFD.js.map +1 -0
- package/dist/pipelines/index.d.ts +16 -19
- package/dist/pipelines/index.d.ts.map +1 -1
- package/dist/pipelines/index.js +4 -68
- package/dist/pipelines/index.js.map +1 -1
- package/dist/{power-and-mde-fK5Qocwo.js → power-and-mde-N5nU4_Ly.js} +3 -3
- package/dist/{power-and-mde-fK5Qocwo.js.map → power-and-mde-N5nU4_Ly.js.map} +1 -1
- package/dist/predicate-FdKmElA_.js +38 -0
- package/dist/predicate-FdKmElA_.js.map +1 -0
- package/dist/{prime-protocol-DcAK9pC8.js → prime-protocol-BFgGrEMP.js} +3 -3
- package/dist/prime-protocol-BFgGrEMP.js.map +1 -0
- package/dist/{prime-protocol-CEE4BR9N.d.ts → prime-protocol-BWG2DQIk.d.ts} +4 -22
- package/dist/prime-protocol-BWG2DQIk.d.ts.map +1 -0
- package/dist/profile-cell.d.ts +2 -2
- package/dist/profile-cell.js +2 -2
- package/dist/{release-confidence-BK2QCB5M.d.ts → promotion-gate-DaY95yU6.d.ts} +105 -106
- package/dist/promotion-gate-DaY95yU6.d.ts.map +1 -0
- package/dist/{promotion-policy-CWW35UWq.js → promotion-policy-6A8osGr_.js} +4 -4
- package/dist/{promotion-policy-CWW35UWq.js.map → promotion-policy-6A8osGr_.js.map} +1 -1
- package/dist/{proposal-findings-BZiMTzAF.js → proposal-findings-BbWidvUk.js} +3 -3
- package/dist/proposal-findings-BbWidvUk.js.map +1 -0
- package/dist/{query-B1D1qp2Z.js → query-B_RZ36an.js} +5 -5
- package/dist/query-B_RZ36an.js.map +1 -0
- package/dist/{query-DK1nWcmp.d.ts → query-Cmv_U8vf.d.ts} +4 -24
- package/dist/{query-DK1nWcmp.d.ts.map → query-Cmv_U8vf.d.ts.map} +1 -1
- package/dist/{random-Dn5fPWkt.js → random-DDDcC7YU.js} +2 -2
- package/dist/{random-Dn5fPWkt.js.map → random-DDDcC7YU.js.map} +1 -1
- package/dist/{raw-provider-sink-rB2DYxr6.js → raw-provider-sink-75w4FWBj.js} +2 -2
- package/dist/{raw-provider-sink-rB2DYxr6.js.map → raw-provider-sink-75w4FWBj.js.map} +1 -1
- package/dist/{redact-Cveoq6fJ.d.ts → redact-DXdlFb_U.d.ts} +4 -21
- package/dist/redact-DXdlFb_U.d.ts.map +1 -0
- package/dist/{redact-Dk1ZdJEm.js → redact-mtkTiJVk.js} +92 -98
- package/dist/redact-mtkTiJVk.js.map +1 -0
- package/dist/{registry-JYfPxcVz.d.ts → registry-ssNoHzwD.d.ts} +5 -5
- package/dist/{registry-JYfPxcVz.d.ts.map → registry-ssNoHzwD.d.ts.map} +1 -1
- package/dist/{release-confidence-Dbe9AS_X.js → release-confidence-CYIFH-fp.js} +6 -11
- package/dist/release-confidence-CYIFH-fp.js.map +1 -0
- package/dist/{report-command-y8H6RSX8.js → render-C-w2EZ6p.js} +331 -554
- package/dist/render-C-w2EZ6p.js.map +1 -0
- package/dist/reporting-html-Kvlc8oMt.js +100 -0
- package/dist/reporting-html-Kvlc8oMt.js.map +1 -0
- package/dist/reporting.d.ts +6 -6
- package/dist/reporting.js +8 -8
- package/dist/{researcher-f6mZhh58.d.ts → researcher-0ABVIdGa.d.ts} +9 -9
- package/dist/{researcher-f6mZhh58.d.ts.map → researcher-0ABVIdGa.d.ts.map} +1 -1
- package/dist/reward-DrZ71vnf.d.ts +55 -0
- package/dist/reward-DrZ71vnf.d.ts.map +1 -0
- package/dist/{reward-hacking-BFcQgkux.js → reward-hacking-ByuETaIb.js} +3 -3
- package/dist/{reward-hacking-BFcQgkux.js.map → reward-hacking-ByuETaIb.js.map} +1 -1
- package/dist/{reward-hacking-DHA7c1l8.d.ts → reward-hacking-E52wq86n.d.ts} +5 -18
- package/dist/reward-hacking-E52wq86n.d.ts.map +1 -0
- package/dist/reward-nw2xZGZG.js.map +1 -1
- package/dist/rl.d.ts +117 -295
- package/dist/rl.d.ts.map +1 -1
- package/dist/rl.js +27 -747
- package/dist/rl.js.map +1 -1
- package/dist/rollout/index.d.ts +5 -4
- package/dist/rollout/index.js +7 -8
- package/dist/{rollout-D9bKKBnZ.js → rollout-D6C50mfA.js} +88 -13
- package/dist/rollout-D6C50mfA.js.map +1 -0
- package/dist/round-decimal-COrKglMh.js +10 -0
- package/dist/round-decimal-COrKglMh.js.map +1 -0
- package/dist/{rubric-predictive-validity-BsgkBfT0.js → rubric-predictive-validity-D3_oI72N.js} +3 -3
- package/dist/{rubric-predictive-validity-BsgkBfT0.js.map → rubric-predictive-validity-D3_oI72N.js.map} +1 -1
- package/dist/{rubric-predictive-validity-2SgHxoa5.d.ts → rubric-predictive-validity-DiwT8PbL.d.ts} +2 -2
- package/dist/{rubric-predictive-validity-2SgHxoa5.d.ts.map → rubric-predictive-validity-DiwT8PbL.d.ts.map} +1 -1
- package/dist/{run-record-D1Hw-Jml.d.ts → run-record-CaxbLq6Y.d.ts} +5 -11
- package/dist/run-record-CaxbLq6Y.d.ts.map +1 -0
- package/dist/{run-record-ES07wfz9.js → run-record-CysKLB93.js} +3 -5
- package/dist/run-record-CysKLB93.js.map +1 -0
- package/dist/{run-record-DlTZ_lqe.js → run-record-DhEJgoPz.js} +4 -4
- package/dist/{run-record-DlTZ_lqe.js.map → run-record-DhEJgoPz.js.map} +1 -1
- package/dist/sandbox-harness-BlSOu4LX.d.ts.map +1 -1
- package/dist/{schema-CSf6qWgZ.js → schema-Bt2vLFm3.js} +2 -14
- package/dist/schema-Bt2vLFm3.js.map +1 -0
- package/dist/schema-C-ziyJD7.d.ts.map +1 -1
- package/dist/{schema-2g7trUwH.d.ts → schema-D6EVKzTB.d.ts} +4 -5
- package/dist/schema-D6EVKzTB.d.ts.map +1 -0
- package/dist/{schema-BuiujZ-g.js → schema-DxcKTzbi.js} +2 -2
- package/dist/schema-DxcKTzbi.js.map +1 -0
- package/dist/search/index.d.ts +39 -53
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +4 -4
- package/dist/{search-kernel-CpcKH5Ih.d.ts → search-kernel-DxqjBxA-.d.ts} +18 -31
- package/dist/search-kernel-DxqjBxA-.d.ts.map +1 -0
- package/dist/{search-ledger-d7V8o9Lk.js → search-ledger-TUvB5gWH.js} +189 -199
- package/dist/search-ledger-TUvB5gWH.js.map +1 -0
- package/dist/{search-ledger-file-BqOo-bVo.js → search-ledger-file-UKCO2r8a.js} +4 -4
- package/dist/search-ledger-file-UKCO2r8a.js.map +1 -0
- package/dist/search-ledger-types-p2xKfiDJ.d.ts.map +1 -1
- package/dist/{search-shipper-er4pK5_Y.js → search-shipper-Cw2z3-td.js} +4 -4
- package/dist/search-shipper-Cw2z3-td.js.map +1 -0
- package/dist/{search-summary-CBvz3u_S.js → search-summary-DQIvJsSB.js} +5 -5
- package/dist/{search-summary-CBvz3u_S.js.map → search-summary-DQIvJsSB.js.map} +1 -1
- package/dist/{sequential-CzK5DarL.js → sequential-BVv1sjsw.js} +2 -32
- package/dist/{sequential-CzK5DarL.js.map → sequential-BVv1sjsw.js.map} +1 -1
- package/dist/{sequential-D-1PPCmM.js → sequential-CimQrbjv.js} +5 -5
- package/dist/sequential-CimQrbjv.js.map +1 -0
- package/dist/{sequential-BhsrMupG.d.ts → sequential-KwGspfeR.d.ts} +2 -9
- package/dist/{sequential-BhsrMupG.d.ts.map → sequential-KwGspfeR.d.ts.map} +1 -1
- package/dist/{sequential-eprocess-D1jKoihe.js → sequential-eprocess-DodI6RoF.js} +2 -2
- package/dist/{sequential-eprocess-D1jKoihe.js.map → sequential-eprocess-DodI6RoF.js.map} +1 -1
- package/dist/{server-DbcC7xMl.js → server-DoVbqqtJ.js} +101 -101
- package/dist/server-DoVbqqtJ.js.map +1 -0
- package/dist/{single-run-lock-fVu3MZWH.js → single-run-lock-U58yf8N6.js} +2 -2
- package/dist/{single-run-lock-fVu3MZWH.js.map → single-run-lock-U58yf8N6.js.map} +1 -1
- package/dist/{skillopt-optimization-method-CCtzralq.js → skillopt-optimization-method-kyL94-ri.js} +428 -91
- package/dist/skillopt-optimization-method-kyL94-ri.js.map +1 -0
- package/dist/{spans-D9_ehFuk.d.ts → spans-CVngGij3.d.ts} +2 -2
- package/dist/{spans-D9_ehFuk.d.ts.map → spans-CVngGij3.d.ts.map} +1 -1
- package/dist/{statistical-heldout-1Gbmex7v.js → statistical-heldout-Bksv92Th.js} +3 -3
- package/dist/statistical-heldout-Bksv92Th.js.map +1 -0
- package/dist/{statistical-heldout-BeR4bOdg.d.ts → statistical-heldout-C9AbgREN.d.ts} +8 -67
- package/dist/statistical-heldout-C9AbgREN.d.ts.map +1 -0
- package/dist/{steps-Bg5-KVx1.d.ts → steps-DlepJPwD.d.ts} +2 -56
- package/dist/steps-DlepJPwD.d.ts.map +1 -0
- package/dist/{store-C4avFjIi.d.ts → store-B3nWRy3B.d.ts} +2 -2
- package/dist/{store-C4avFjIi.d.ts.map → store-B3nWRy3B.d.ts.map} +1 -1
- package/dist/{store-otlp-DtB4Slsr.js → store-otlp-BteDAbHK.js} +135 -132
- package/dist/store-otlp-BteDAbHK.js.map +1 -0
- package/dist/{store-tool-spans-fZIigrpP.js → store-tool-spans-D5l_OfTJ.js} +9 -9
- package/dist/store-tool-spans-D5l_OfTJ.js.map +1 -0
- package/dist/{store-tool-spans-DR1SrCLK.d.ts → store-tool-spans-DZS0HWVf.d.ts} +157 -165
- package/dist/store-tool-spans-DZS0HWVf.d.ts.map +1 -0
- package/dist/storyboard/index.d.ts +4 -11
- package/dist/storyboard/index.d.ts.map +1 -1
- package/dist/storyboard/index.js +21 -19
- package/dist/storyboard/index.js.map +1 -1
- package/dist/{summary-report-DtdRucM0.js → summary-report-BsXJoYX5.js} +588 -598
- package/dist/summary-report-BsXJoYX5.js.map +1 -0
- package/dist/{summary-report-C-CtItCN.d.ts → summary-report-DM7tlml1.d.ts} +16 -20
- package/dist/summary-report-DM7tlml1.d.ts.map +1 -0
- package/dist/supervisor-run/index.d.ts +189 -153
- package/dist/supervisor-run/index.d.ts.map +1 -1
- package/dist/supervisor-run/index.js +58 -450
- package/dist/supervisor-run/index.js.map +1 -1
- package/dist/{systemone-CpTLAlqb.d.ts → systemone-Bp_Ac0p1.d.ts} +6 -6
- package/dist/{systemone-CpTLAlqb.d.ts.map → systemone-Bp_Ac0p1.d.ts.map} +1 -1
- package/dist/systemone-protocol.js +1 -1
- package/dist/{systemone-review-B_yo1xsd.d.ts → systemone-review-CwE6JGIp.d.ts} +2 -2
- package/dist/{systemone-review-B_yo1xsd.d.ts.map → systemone-review-CwE6JGIp.d.ts.map} +1 -1
- package/dist/{systemone-review-BZWfrtq1.js → systemone-review-DzdOJ8I6.js} +3 -3
- package/dist/{systemone-review-BZWfrtq1.js.map → systemone-review-DzdOJ8I6.js.map} +1 -1
- package/dist/systemone.d.ts +5 -5
- package/dist/systemone.js +5 -5
- package/dist/systemone.js.map +1 -1
- package/dist/{task-failure-attributes-DngaLdE4.js → task-failure-attributes-BMsblaDh.js} +3 -3
- package/dist/task-failure-attributes-BMsblaDh.js.map +1 -0
- package/dist/{terminal-record-BHocwFw1.js → terminal-record-78g6E4vh.js} +17 -13
- package/dist/terminal-record-78g6E4vh.js.map +1 -0
- package/dist/{tool-groups-mcxOXVlb.d.ts → tool-groups-DyNz2JZ_.d.ts} +3 -3
- package/dist/tool-groups-DyNz2JZ_.d.ts.map +1 -0
- package/dist/{tool-waste-Wi4g6Ekb.d.ts → tool-waste-CkjzWmeY.d.ts} +53 -56
- package/dist/tool-waste-CkjzWmeY.d.ts.map +1 -0
- package/dist/{tool-waste-D52PqU9Q.js → tool-waste-DLsjS_p-.js} +5 -5
- package/dist/{tool-waste-D52PqU9Q.js.map → tool-waste-DLsjS_p-.js.map} +1 -1
- package/dist/{trace-questions-DG7nqLQ7.d.ts → trace-questions-xxwgsuMi.d.ts} +5 -10
- package/dist/{trace-questions-DG7nqLQ7.d.ts.map → trace-questions-xxwgsuMi.d.ts.map} +1 -1
- package/dist/trace-repair/index.d.ts +15 -212
- package/dist/trace-repair/index.d.ts.map +1 -1
- package/dist/trace-repair/index.js +865 -987
- package/dist/trace-repair/index.js.map +1 -1
- package/dist/traces.d.ts +76 -86
- package/dist/traces.d.ts.map +1 -1
- package/dist/traces.js +13 -13
- package/dist/traces.js.map +1 -1
- package/dist/trajectory-replay/index.d.ts +3 -117
- package/dist/trajectory-replay/index.d.ts.map +1 -1
- package/dist/trajectory-replay/index.js +98 -56
- package/dist/trajectory-replay/index.js.map +1 -1
- package/dist/{tree-rXLW3M_4.js → tree-BGRglkKd.js} +2355 -2414
- package/dist/tree-BGRglkKd.js.map +1 -0
- package/dist/{types-B3jzCp0p.js → types-BXgASBCL.js} +2 -2
- package/dist/{types-B3jzCp0p.js.map → types-BXgASBCL.js.map} +1 -1
- package/dist/{types-oKRhQAde.d.ts → types-CW8ewATZ.d.ts} +5 -114
- package/dist/types-CW8ewATZ.d.ts.map +1 -0
- package/dist/{types-BqkVmHfv.d.ts → types-CmXY0PQf.d.ts} +2 -4
- package/dist/types-CmXY0PQf.d.ts.map +1 -0
- package/dist/{types-DBZ7keha.d.ts → types-D-mgRzlh.d.ts} +9 -72
- package/dist/types-D-mgRzlh.d.ts.map +1 -0
- package/dist/{types-B4C5nFw6.d.ts → types-D8GhJoHk.d.ts} +79 -75
- package/dist/types-D8GhJoHk.d.ts.map +1 -0
- package/dist/{types-IXD2HJzu.js → types-DqFMNTgK.js} +2 -4
- package/dist/types-DqFMNTgK.js.map +1 -0
- package/dist/{types-CfZMIzB_.d.ts → types-DtMbFw6U.d.ts} +3 -3
- package/dist/{types-CfZMIzB_.d.ts.map → types-DtMbFw6U.d.ts.map} +1 -1
- package/dist/{verdict-DhPoXApk.js → verdict-BGQmxQ8A.js} +3 -3
- package/dist/verdict-BGQmxQ8A.js.map +1 -0
- package/dist/{verdict-E4eRNf7-.d.ts → verdict-C24NELtN.d.ts} +2 -15
- package/dist/{verdict-E4eRNf7-.d.ts.map → verdict-C24NELtN.d.ts.map} +1 -1
- package/dist/{verdict-cache-BQEsVy9n.js → verdict-cache-CL_7tePD.js} +2 -2
- package/dist/verdict-cache-CL_7tePD.js.map +1 -0
- package/dist/wire/index.d.ts +32 -411
- package/dist/wire/index.d.ts.map +1 -1
- package/dist/wire/index.js +2 -2
- package/docs/changes/jev-evidence-review.md +1 -1
- package/docs/changes/jev-product-adoption.md +1 -1
- package/docs/changes/jev-recorded-evidence.md +1 -1
- package/docs/design/mlbenchmarks-book-review.md +1 -1
- package/docs/design/mlbenchmarks-review/probes.mts +23 -31
- package/docs/design/statistics-decisions.md +17 -17
- package/docs/design.md +1 -1
- package/docs/evaluation-integrity.md +1 -1
- package/docs/experiment.md +1 -1
- package/docs/proofs/campaign-attempt-retention-20261001/README.md +2 -2
- package/docs/public-api.md +138 -529
- package/docs/rollout.md +1 -1
- package/docs/trace-repair-admission.md +1 -1
- package/docs/wire-protocol.md +1 -1
- package/package.json +1 -1
- package/dist/active-curriculum-B_Lc9kfO.js +0 -61
- package/dist/active-curriculum-B_Lc9kfO.js.map +0 -1
- package/dist/agent-profile-CIA2Atnt.d.ts.map +0 -1
- package/dist/agent-profile-cell-BNp-Js9F.d.ts.map +0 -1
- package/dist/agent-profile-cell-BYc0bxsL.js.map +0 -1
- package/dist/agreement-irr-DsTURxdr.js.map +0 -1
- package/dist/analyze-runs-3xpby3p2.js.map +0 -1
- package/dist/analyze-runs-CSDAh8A7.d.ts.map +0 -1
- package/dist/ast-DJ6Bc7ab.js +0 -558
- package/dist/ast-DJ6Bc7ab.js.map +0 -1
- package/dist/ast-hI-vjW6J.d.ts +0 -457
- package/dist/ast-hI-vjW6J.d.ts.map +0 -1
- package/dist/baseline-Dm8966du.js +0 -259
- package/dist/baseline-Dm8966du.js.map +0 -1
- package/dist/benchmark-BywPeAUo.js +0 -562
- package/dist/benchmark-BywPeAUo.js.map +0 -1
- package/dist/benchmark-command-CByl9dxG.js.map +0 -1
- package/dist/campaign-BQ-Jujuj.js.map +0 -1
- package/dist/chat-json-call-BrKsyspg.js.map +0 -1
- package/dist/claim-BXcZw-jo.js.map +0 -1
- package/dist/claude-jsonl-dZir12m6.js +0 -86
- package/dist/claude-jsonl-dZir12m6.js.map +0 -1
- package/dist/client-BzL9niqi.d.ts.map +0 -1
- package/dist/cost-ledger-CO7UyEcQ.d.ts.map +0 -1
- package/dist/cost-ledger-D2FnJPBF.js.map +0 -1
- package/dist/counterfactual-CPaWN99-.d.ts +0 -60
- package/dist/counterfactual-CPaWN99-.d.ts.map +0 -1
- package/dist/counterfactual-DtMWvn7D.js +0 -98
- package/dist/counterfactual-DtMWvn7D.js.map +0 -1
- package/dist/default-registry-BrWh62zf.d.ts.map +0 -1
- package/dist/default-registry-Cn1LQ2Ve.js.map +0 -1
- package/dist/define-agent-eval-CEWkX5wZ.d.ts.map +0 -1
- package/dist/define-agent-eval-Ch3MWX6k.js.map +0 -1
- package/dist/dspy-rlm-engine-D-KX_e64.js.map +0 -1
- package/dist/effect-sizes-DiH8MGOH.js +0 -82
- package/dist/effect-sizes-DiH8MGOH.js.map +0 -1
- package/dist/emitter-HcCmFQl_.d.ts.map +0 -1
- package/dist/engine-C_zNWfM8.d.ts.map +0 -1
- package/dist/equal-terms-Blx9Zk5I.js.map +0 -1
- package/dist/evaluation-Cn2dRZIz.d.ts.map +0 -1
- package/dist/exporters-X4xwla8A.js.map +0 -1
- package/dist/external-optimizer-process-nnsY5D_4.js.map +0 -1
- package/dist/external-optimizer-subprocess-DcerlZSG.js.map +0 -1
- package/dist/failure-cluster-CbgEDcFJ.d.ts.map +0 -1
- package/dist/feedback-trajectory-DkaDj0dj.d.ts.map +0 -1
- package/dist/hf-dataset-BR2yhuNn.js.map +0 -1
- package/dist/index-CPJJr98o.d.ts.map +0 -1
- package/dist/index-Ch9VxCpK.d.ts.map +0 -1
- package/dist/index-DHKEAv3s.d.ts.map +0 -1
- package/dist/index-DlH0Y9gI.d.ts.map +0 -1
- package/dist/index-NvGBiO8H.d.ts.map +0 -1
- package/dist/integrity-BV1ZDf42.js.map +0 -1
- package/dist/journal-DvKCvjty.js.map +0 -1
- package/dist/judge-gate-B1qYnkpi.js.map +0 -1
- package/dist/judge-gate-DNcUrRFA.d.ts.map +0 -1
- package/dist/kind-factory-CbZ-6tvu.js.map +0 -1
- package/dist/ledger-core-B2hahzrt.js.map +0 -1
- package/dist/llm-client-DQveoxSM.js.map +0 -1
- package/dist/llm-judge-PXV7Xljl.js.map +0 -1
- package/dist/matrix-CHMiebFx.d.ts.map +0 -1
- package/dist/matrix-_BV9-Lk5.js.map +0 -1
- package/dist/mint-DCGoBYhB.d.ts +0 -155
- package/dist/mint-DCGoBYhB.d.ts.map +0 -1
- package/dist/normal-DsJZtrJc.js +0 -25
- package/dist/normal-DsJZtrJc.js.map +0 -1
- package/dist/paired-arms-DzKn0TfY.js.map +0 -1
- package/dist/paired-binary-Q36bhn2R.js.map +0 -1
- package/dist/paired-promotion-decision-DPKz8ZnF.d.ts.map +0 -1
- package/dist/paired-tests-jbwPdSz5.js.map +0 -1
- package/dist/prime-protocol-CEE4BR9N.d.ts.map +0 -1
- package/dist/prime-protocol-DcAK9pC8.js.map +0 -1
- package/dist/produced-state-ZCtDBxAE.js.map +0 -1
- package/dist/proposal-findings-BZiMTzAF.js.map +0 -1
- package/dist/query-B1D1qp2Z.js.map +0 -1
- package/dist/redact-Cveoq6fJ.d.ts.map +0 -1
- package/dist/redact-Dk1ZdJEm.js.map +0 -1
- package/dist/release-confidence-BK2QCB5M.d.ts.map +0 -1
- package/dist/release-confidence-Dbe9AS_X.js.map +0 -1
- package/dist/report-command-y8H6RSX8.js.map +0 -1
- package/dist/reward-hacking-DHA7c1l8.d.ts.map +0 -1
- package/dist/rollout-D9bKKBnZ.js.map +0 -1
- package/dist/run-record-D1Hw-Jml.d.ts.map +0 -1
- package/dist/run-record-ES07wfz9.js.map +0 -1
- package/dist/schema-2g7trUwH.d.ts.map +0 -1
- package/dist/schema-BuiujZ-g.js.map +0 -1
- package/dist/schema-CSf6qWgZ.js.map +0 -1
- package/dist/search-kernel-BztlERLv.js.map +0 -1
- package/dist/search-kernel-CpcKH5Ih.d.ts.map +0 -1
- package/dist/search-ledger-d7V8o9Lk.js.map +0 -1
- package/dist/search-ledger-file-BqOo-bVo.js.map +0 -1
- package/dist/search-shipper-er4pK5_Y.js.map +0 -1
- package/dist/semantic-concept-judge-zQBG9hHU.js.map +0 -1
- package/dist/sequential-D-1PPCmM.js.map +0 -1
- package/dist/server-DbcC7xMl.js.map +0 -1
- package/dist/skillopt-optimization-method-CCtzralq.js.map +0 -1
- package/dist/statistical-heldout-1Gbmex7v.js.map +0 -1
- package/dist/statistical-heldout-BeR4bOdg.d.ts.map +0 -1
- package/dist/steps-Bg5-KVx1.d.ts.map +0 -1
- package/dist/store-otlp-DtB4Slsr.js.map +0 -1
- package/dist/store-tool-spans-DR1SrCLK.d.ts.map +0 -1
- package/dist/store-tool-spans-fZIigrpP.js.map +0 -1
- package/dist/student-t-BQeKJx2N.js +0 -39
- package/dist/student-t-BQeKJx2N.js.map +0 -1
- package/dist/summary-report-C-CtItCN.d.ts.map +0 -1
- package/dist/summary-report-DtdRucM0.js.map +0 -1
- package/dist/task-failure-attributes-DngaLdE4.js.map +0 -1
- package/dist/terminal-record-BHocwFw1.js.map +0 -1
- package/dist/tool-groups-mcxOXVlb.d.ts.map +0 -1
- package/dist/tool-waste-Wi4g6Ekb.d.ts.map +0 -1
- package/dist/trajectory-Y1fmdlw7.d.ts +0 -24
- package/dist/trajectory-Y1fmdlw7.d.ts.map +0 -1
- package/dist/tree-rXLW3M_4.js.map +0 -1
- package/dist/types-B4C5nFw6.d.ts.map +0 -1
- package/dist/types-BqkVmHfv.d.ts.map +0 -1
- package/dist/types-DBZ7keha.d.ts.map +0 -1
- package/dist/types-IXD2HJzu.js.map +0 -1
- package/dist/types-oKRhQAde.d.ts.map +0 -1
- package/dist/usage-receipt-Cx3xNeyB.js +0 -76
- package/dist/usage-receipt-Cx3xNeyB.js.map +0 -1
- package/dist/verdict-DhPoXApk.js.map +0 -1
- package/dist/verdict-cache-BQEsVy9n.js.map +0 -1
- package/docs/design/self-improvement-evidence-audit.md +0 -263
|
@@ -1,242 +1,388 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { i as pairedCohensDz } from "./effect-sizes-DiH8MGOH.js";
|
|
4
|
-
import { t as confidenceInterval } from "./descriptive-CnU8Yq29.js";
|
|
5
|
-
import { t as benjaminiHochberg } from "./multiplicity-D9cjmStq.js";
|
|
6
|
-
import { r as pairedBootstrap } from "./paired-tests-jbwPdSz5.js";
|
|
7
|
-
import { r as pairedMde } from "./power-and-mde-fK5Qocwo.js";
|
|
8
|
-
import { a as wilcoxonSignedRank, r as pairRunRecords } from "./paired-arms-DzKn0TfY.js";
|
|
1
|
+
import { s as ValidationError } from "./errors-BcOs5MKW.js";
|
|
2
|
+
import { l as hashJson } from "./agent-profile-cell-B1V1gUZs.js";
|
|
9
3
|
import { r as observedSplitScore } from "./reward-nw2xZGZG.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const fdr = opts.fdr ?? .05;
|
|
20
|
-
const comparator = opts.comparator ?? null;
|
|
21
|
-
const byCandidate = /* @__PURE__ */ new Map();
|
|
22
|
-
for (const r of runs) {
|
|
23
|
-
if (r.splitTag !== split) continue;
|
|
24
|
-
const v = observedSplitScore(r, split);
|
|
25
|
-
if (typeof v !== "number" || !Number.isFinite(v)) continue;
|
|
26
|
-
const bucket = byCandidate.get(r.candidateId) ?? {
|
|
27
|
-
runs: [],
|
|
28
|
-
scores: []
|
|
29
|
-
};
|
|
30
|
-
bucket.runs.push(r);
|
|
31
|
-
bucket.scores.push(v);
|
|
32
|
-
byCandidate.set(r.candidateId, bucket);
|
|
33
|
-
}
|
|
34
|
-
const candidateIds = [...byCandidate.keys()].sort();
|
|
35
|
-
const compRuns = comparator ? byCandidate.get(comparator) : void 0;
|
|
36
|
-
const tentative = [];
|
|
37
|
-
for (const id of candidateIds) {
|
|
38
|
-
const bucket = byCandidate.get(id);
|
|
39
|
-
const ci = confidenceInterval(bucket.scores, confidence);
|
|
40
|
-
let rawP = null;
|
|
41
|
-
let d = null;
|
|
42
|
-
let pairedN = null;
|
|
43
|
-
let unpairedCandidateN = null;
|
|
44
|
-
let unpairedComparatorN = null;
|
|
45
|
-
if (comparator && compRuns && id !== comparator) {
|
|
46
|
-
const paired = pairScoresByKey(bucket.runs, compRuns.runs, split);
|
|
47
|
-
pairedN = paired.before.length;
|
|
48
|
-
unpairedCandidateN = paired.unpairedCandidateN;
|
|
49
|
-
unpairedComparatorN = paired.unpairedComparatorN;
|
|
50
|
-
if (paired.before.length >= 6) rawP = wilcoxonSignedRank(paired.before, paired.after).p;
|
|
51
|
-
d = pairedCohensDz(paired.before, paired.after);
|
|
52
|
-
}
|
|
53
|
-
tentative.push({
|
|
54
|
-
candidateId: id,
|
|
55
|
-
n: bucket.scores.length,
|
|
56
|
-
mean: ci.mean,
|
|
57
|
-
ciLow: ci.lower,
|
|
58
|
-
ciHigh: ci.upper,
|
|
59
|
-
qValue: rawP,
|
|
60
|
-
cohensD: d,
|
|
61
|
-
pairedN,
|
|
62
|
-
unpairedCandidateN,
|
|
63
|
-
unpairedComparatorN,
|
|
64
|
-
rawP
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
if (comparator) {
|
|
68
|
-
const idxs = [];
|
|
69
|
-
const ps = [];
|
|
70
|
-
for (let i = 0; i < tentative.length; i++) {
|
|
71
|
-
const r = tentative[i];
|
|
72
|
-
if (r.candidateId === comparator) continue;
|
|
73
|
-
if (r.rawP === null || !Number.isFinite(r.rawP)) continue;
|
|
74
|
-
idxs.push(i);
|
|
75
|
-
ps.push(r.rawP);
|
|
76
|
-
}
|
|
77
|
-
if (ps.length > 0) {
|
|
78
|
-
const { qValues } = benjaminiHochberg(ps, fdr);
|
|
79
|
-
for (let k = 0; k < idxs.length; k++) tentative[idxs[k]].qValue = qValues[k];
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
const rows = tentative.map(({ rawP: _rawP, ...rest }) => rest);
|
|
83
|
-
return {
|
|
84
|
-
rows,
|
|
85
|
-
comparator,
|
|
86
|
-
split,
|
|
87
|
-
markdown: renderSummaryTableMarkdown(rows, comparator, split)
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
function pairScoresByKey(candidate, baseline, split) {
|
|
91
|
-
const scoreOf = (run) => {
|
|
92
|
-
const v = observedSplitScore(run, split);
|
|
93
|
-
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
94
|
-
};
|
|
95
|
-
const scoredCandidate = candidate.filter((run) => scoreOf(run) !== void 0);
|
|
96
|
-
const scoredBaseline = baseline.filter((run) => scoreOf(run) !== void 0);
|
|
97
|
-
const pairing = pairRunRecords(scoredBaseline, scoredCandidate);
|
|
98
|
-
return {
|
|
99
|
-
before: pairing.pairs.map((pair) => scoreOf(pair.baseline)),
|
|
100
|
-
after: pairing.pairs.map((pair) => scoreOf(pair.treatment)),
|
|
101
|
-
unpairedCandidateN: pairing.unpairedTreatment.length,
|
|
102
|
-
unpairedComparatorN: pairing.unpairedBaseline.length
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
function renderSummaryTableMarkdown(rows, comparator, split) {
|
|
4
|
+
import { n as renderResearchHtml } from "./reporting-html-Kvlc8oMt.js";
|
|
5
|
+
import { i as makeRng } from "./internal-BTK91Bqt.js";
|
|
6
|
+
import { t as confidenceInterval } from "./descriptive-CVNy48rR.js";
|
|
7
|
+
import { a as wilcoxonSignedRank, r as pairRunRecords } from "./paired-arms-Ct4LTKc1.js";
|
|
8
|
+
import { t as benjaminiHochberg } from "./multiplicity-Sg0U7h8j.js";
|
|
9
|
+
import { i as pairedBootstrap, t as mean } from "./mean-BbA_JCzW.js";
|
|
10
|
+
import { r as pairedMde } from "./power-and-mde-N5nU4_Ly.js";
|
|
11
|
+
//#region src/research-report-render.ts
|
|
12
|
+
function renderResearchMarkdown(report) {
|
|
106
13
|
const lines = [];
|
|
107
|
-
|
|
108
|
-
lines.push(`Summary Table — ${split} split${cmpLabel}`);
|
|
14
|
+
lines.push(`# ${report.title}`);
|
|
109
15
|
lines.push("");
|
|
110
|
-
lines.push(
|
|
111
|
-
lines.push(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
16
|
+
lines.push(`**Generated:** ${report.generatedAt}`);
|
|
17
|
+
lines.push(`**Primary split:** ${report.split}`);
|
|
18
|
+
lines.push(`**Comparator:** ${report.comparator ?? "not configured"}`);
|
|
19
|
+
lines.push(`**ROPE:** ${report.rope ? formatRope(report.rope) : "not configured"}`);
|
|
20
|
+
lines.push(`**Run fingerprint:** \`${report.runFingerprint}\``);
|
|
21
|
+
lines.push(`**Preregistration:** ${report.preregistrationHash ? `\`${report.preregistrationHash}\`` : "none"}`);
|
|
22
|
+
lines.push("");
|
|
23
|
+
lines.push("## Executive Summary");
|
|
24
|
+
lines.push("");
|
|
25
|
+
for (const item of report.executiveSummary) lines.push(`- ${item}`);
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push("## Recommendation");
|
|
28
|
+
lines.push("");
|
|
29
|
+
lines.push(`**Decision:** ${report.recommendation.decision}`);
|
|
30
|
+
lines.push(`**Candidate:** ${report.recommendation.candidateId ?? "N/A"}`);
|
|
31
|
+
lines.push("");
|
|
32
|
+
lines.push("### Rationale");
|
|
33
|
+
lines.push("");
|
|
34
|
+
for (const item of report.recommendation.rationale) lines.push(`- ${item}`);
|
|
35
|
+
lines.push("");
|
|
36
|
+
lines.push("### Risks");
|
|
37
|
+
lines.push("");
|
|
38
|
+
for (const item of report.recommendation.risks.length ? report.recommendation.risks : ["No material report-level risks detected."]) lines.push(`- ${item}`);
|
|
39
|
+
lines.push("");
|
|
40
|
+
lines.push("### Next Actions");
|
|
41
|
+
lines.push("");
|
|
42
|
+
for (const item of report.recommendation.nextActions) lines.push(`- ${item}`);
|
|
43
|
+
lines.push("");
|
|
44
|
+
lines.push("## Candidate Decision Table");
|
|
45
|
+
lines.push("");
|
|
46
|
+
lines.push("| Candidate | Decision | Mean | Δ̄ | Pr(Δ>0) | q | dz | Paired N | Median Gain CI | MDE | Pareto | Gate |");
|
|
47
|
+
lines.push("|---|---|---:|---:|---:|---:|---:|---:|---|---:|---|---|");
|
|
48
|
+
for (const c of report.candidates) {
|
|
49
|
+
const delta = c.meanDeltaVsComparator === null ? "-" : signed(c.meanDeltaVsComparator);
|
|
50
|
+
const prGt = c.prGreaterThanZero === null ? "-" : c.prGreaterThanZero.toFixed(3);
|
|
51
|
+
const q = c.qValue !== null ? c.qValue.toFixed(4) : "-";
|
|
52
|
+
const d = c.cohensD !== null ? c.cohensD.toFixed(3) : "-";
|
|
53
|
+
const gain = c.gainCi ? `[${fmt(c.gainCi.low)}, ${fmt(c.gainCi.high)}]` : "-";
|
|
54
|
+
const mde = c.mde === null || !Number.isFinite(c.mde) ? "-" : fmt(c.mde);
|
|
55
|
+
lines.push(`| ${c.candidateId} | ${c.decision} | ${fmt(c.mean)} | ${delta} | ${prGt} | ${q} | ${d} | ${c.pairedN} | ${gain} | ${mde} | ${c.onParetoFrontier ? "yes" : "no"} | ${c.gate ?? "-"} |`);
|
|
56
|
+
}
|
|
57
|
+
lines.push("");
|
|
58
|
+
lines.push("## Statistical Summary");
|
|
59
|
+
lines.push("");
|
|
60
|
+
lines.push(report.summary.markdown);
|
|
61
|
+
lines.push("");
|
|
62
|
+
lines.push("## Methodology");
|
|
63
|
+
lines.push("");
|
|
64
|
+
lines.push("### Assumptions");
|
|
65
|
+
lines.push("");
|
|
66
|
+
for (const item of report.methodology.assumptions) lines.push(`- ${item}`);
|
|
67
|
+
lines.push("");
|
|
68
|
+
lines.push("### Methods");
|
|
69
|
+
lines.push("");
|
|
70
|
+
for (const item of report.methodology.methods) lines.push(`- ${item}`);
|
|
71
|
+
lines.push("");
|
|
72
|
+
lines.push("### Alternatives Considered");
|
|
73
|
+
lines.push("");
|
|
74
|
+
for (const item of report.methodology.alternatives) lines.push(`- ${item}`);
|
|
75
|
+
lines.push("");
|
|
76
|
+
lines.push("### When NOT To Apply");
|
|
77
|
+
lines.push("");
|
|
78
|
+
for (const item of report.methodology.whenNotToApply) lines.push(`- ${item}`);
|
|
79
|
+
lines.push("");
|
|
80
|
+
lines.push("### Citations");
|
|
81
|
+
lines.push("");
|
|
82
|
+
for (const item of report.methodology.citations) lines.push(`- ${item}`);
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push("## Chart Specs");
|
|
85
|
+
lines.push("");
|
|
86
|
+
lines.push("The report carries JSON chart specs for Pareto cost/quality and paired gain histograms.");
|
|
87
|
+
lines.push("");
|
|
88
|
+
lines.push("```json");
|
|
89
|
+
lines.push(JSON.stringify({
|
|
90
|
+
pareto: report.pareto,
|
|
91
|
+
gains: report.gains
|
|
92
|
+
}, null, 2));
|
|
93
|
+
lines.push("```");
|
|
94
|
+
if (report.failureClusters) {
|
|
95
|
+
lines.push("");
|
|
96
|
+
lines.push("## Failure Clusters");
|
|
97
|
+
lines.push("");
|
|
98
|
+
lines.push("| Failure Class | Runs | Scenarios | Tool | Example |");
|
|
99
|
+
lines.push("|---|---:|---:|---|---|");
|
|
100
|
+
for (const c of report.failureClusters.clusters.slice(0, 10)) lines.push(`| ${c.failureClass} | ${c.runCount} | ${c.scenarioIds.length} | ${c.toolName ?? "-"} | ${escapePipes(c.exampleError ?? c.exampleRunId)} |`);
|
|
119
101
|
}
|
|
120
102
|
return lines.join("\n");
|
|
121
103
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
* candidate id; if present, every point picks up the gate verdict
|
|
125
|
-
* for overlay.
|
|
126
|
-
*/
|
|
127
|
-
function paretoChart(runs, opts = {}) {
|
|
128
|
-
const split = opts.split ?? "holdout";
|
|
129
|
-
const buckets = /* @__PURE__ */ new Map();
|
|
130
|
-
for (const r of runs) {
|
|
131
|
-
if (r.splitTag !== split) continue;
|
|
132
|
-
const v = observedSplitScore(r, split);
|
|
133
|
-
if (typeof v !== "number" || !Number.isFinite(v)) continue;
|
|
134
|
-
if (r.costUsd === null) continue;
|
|
135
|
-
const bucket = buckets.get(r.candidateId) ?? {
|
|
136
|
-
cost: [],
|
|
137
|
-
quality: []
|
|
138
|
-
};
|
|
139
|
-
bucket.cost.push(r.costUsd);
|
|
140
|
-
bucket.quality.push(v);
|
|
141
|
-
buckets.set(r.candidateId, bucket);
|
|
142
|
-
}
|
|
143
|
-
const points = [];
|
|
144
|
-
for (const [candidateId, bucket] of buckets.entries()) points.push({
|
|
145
|
-
candidateId,
|
|
146
|
-
cost: avg(bucket.cost),
|
|
147
|
-
quality: avg(bucket.quality),
|
|
148
|
-
n: bucket.cost.length,
|
|
149
|
-
onFrontier: false,
|
|
150
|
-
gate: opts.gateDecisions?.[candidateId] ? gateLabel(opts.gateDecisions[candidateId]) : void 0
|
|
151
|
-
});
|
|
152
|
-
for (const p of points) p.onFrontier = !points.some((q) => q !== p && dominates(q, p));
|
|
153
|
-
return {
|
|
154
|
-
kind: "pareto-cost-quality",
|
|
155
|
-
split,
|
|
156
|
-
axes: {
|
|
157
|
-
x: "costUsd",
|
|
158
|
-
y: "score"
|
|
159
|
-
},
|
|
160
|
-
points
|
|
161
|
-
};
|
|
104
|
+
function escapePipes(s) {
|
|
105
|
+
return s.replace(/\|/g, "\\|");
|
|
162
106
|
}
|
|
163
|
-
function
|
|
164
|
-
return
|
|
107
|
+
function signed(x) {
|
|
108
|
+
return `${x >= 0 ? "+" : ""}${fmt(x)}`;
|
|
165
109
|
}
|
|
166
|
-
function
|
|
167
|
-
return
|
|
110
|
+
function formatRope(rope) {
|
|
111
|
+
return `[${fmt(rope.low)}, ${fmt(rope.high)}]`;
|
|
112
|
+
}
|
|
113
|
+
function fmt(x) {
|
|
114
|
+
if (!Number.isFinite(x)) return String(x);
|
|
115
|
+
return x.toFixed(4);
|
|
168
116
|
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/research-report-analysis.ts
|
|
169
119
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
120
|
+
* Hard floor below which a paired comparison is treated as uninformative
|
|
121
|
+
* regardless of `minPairs`. Mirrors the lower limit on Wilcoxon signed-rank
|
|
122
|
+
* exact tables; below this the test has no power to separate effect sizes.
|
|
173
123
|
*/
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
124
|
+
const RESEARCH_REPORT_HARD_PAIR_FLOOR = 6;
|
|
125
|
+
function buildMethodology(ctx) {
|
|
126
|
+
const assumptions = [
|
|
127
|
+
"Pairs are matched by (experimentId, scenarioId, seed); input order does not affect the comparison.",
|
|
128
|
+
"Paired deltas are exchangeable conditional on the matched scenario — no mid-run distribution shift.",
|
|
129
|
+
`Decisions are pre-specified at fdr=${ctx.fdr}, minPairs=${ctx.minPairs}, confidence=${ctx.confidence}; deviating from these post-hoc invalidates the false-discovery control.`
|
|
130
|
+
];
|
|
131
|
+
if (ctx.rope) assumptions.push(`The Region of Practical Equivalence ${formatRope(ctx.rope)} is supplied by the domain owner; equivalent verdicts are only meaningful if that range is treated as the standing definition of "no material difference."`);
|
|
132
|
+
if (ctx.comparator === null) assumptions.push("No comparator was configured; this run is descriptive, not causal.");
|
|
133
|
+
return {
|
|
134
|
+
assumptions,
|
|
135
|
+
methods: [
|
|
136
|
+
"Marginal scores summarised with BH-FDR-adjusted Wilcoxon signed-rank q-values and paired Cohen's dz via summaryTable.",
|
|
137
|
+
"Paired evidence summarised with a frequentist bootstrap CI on the median delta and a Dirichlet-weight Bayesian bootstrap for Pr(Δ>0) and Pr(Δ∈ROPE) on the mean delta.",
|
|
138
|
+
`Minimum detectable effect reported per candidate at α=${ctx.mdeAlpha} (two-sided), power=${ctx.mdePower}, standardised by the observed paired-delta SD.`,
|
|
139
|
+
"Pareto frontier flagged as a separate axis (cost vs quality); a candidate can be on-frontier without winning the paired test.",
|
|
140
|
+
"Held-out gate decisions, when supplied, override the statistical verdict in the reject direction."
|
|
141
|
+
],
|
|
142
|
+
alternatives: [
|
|
143
|
+
"Paired t-test rejected: not robust to the heavy-tailed score distributions common in agent benchmarks.",
|
|
144
|
+
"Unpaired Mann–Whitney rejected: matched scenarios make pairing free; unpaired throws away that variance reduction.",
|
|
145
|
+
"Sequential / always-valid inference (e-values, mSPRT) is the right tool for iterative sweeps and is out of scope for this single-look report — preregister and run once, or wrap this report in an alpha-spending schedule.",
|
|
146
|
+
"Hierarchical Bayesian shrinkage across many candidates is future work; the current ranking uses raw paired statistics."
|
|
147
|
+
],
|
|
148
|
+
whenNotToApply: [
|
|
149
|
+
`Paired N below 6 on any candidate — the bootstrap CI is degenerate.`,
|
|
150
|
+
"Comparator chosen post-hoc by inspecting the same data; q-values are no longer false-discovery-controlled.",
|
|
151
|
+
"Scenarios not drawn under a stable preregistered protocol; the report can describe the data but cannot anchor a launch decision.",
|
|
152
|
+
"Score distributions with mid-run shift (judge model swap, rubric change, infra outage) — pair exchangeability is violated."
|
|
153
|
+
],
|
|
154
|
+
citations: [
|
|
155
|
+
"Benjamini, Y. & Hochberg, Y. (1995). Controlling the false discovery rate: a practical and powerful approach to multiple testing. JRSS B, 57(1), 289–300.",
|
|
156
|
+
"Wilcoxon, F. (1945). Individual comparisons by ranking methods. Biometrics Bulletin, 1(6), 80–83.",
|
|
157
|
+
"Efron, B. (1979). Bootstrap methods: another look at the jackknife. Annals of Statistics, 7(1), 1–26.",
|
|
158
|
+
"Rubin, D. B. (1981). The Bayesian bootstrap. Annals of Statistics, 9(1), 130–134.",
|
|
159
|
+
"Kruschke, J. K. (2018). Rejecting or accepting parameter values in Bayesian estimation. Advances in Methods and Practices in Psychological Science, 1(2), 270–280. (ROPE.)"
|
|
160
|
+
]
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function classifyCandidate(row, ctx) {
|
|
164
|
+
if (ctx.comparator && row.candidateId === ctx.comparator) return {
|
|
165
|
+
decision: "hold",
|
|
166
|
+
reason: "Comparator baseline."
|
|
167
|
+
};
|
|
168
|
+
if (ctx.gateDecision && !ctx.gateDecision.promote) return {
|
|
169
|
+
decision: "reject",
|
|
170
|
+
reason: `Held-out gate returned ${ctx.gateDecision.rejectionCode ?? "rejected"}: ${ctx.gateDecision.reason}`
|
|
171
|
+
};
|
|
172
|
+
if (!ctx.comparator) return {
|
|
173
|
+
decision: ctx.point?.onFrontier ? "hold" : "needs_more_data",
|
|
174
|
+
reason: "No comparator configured; report ranks candidates but cannot anchor a promotion call."
|
|
175
|
+
};
|
|
176
|
+
if (!ctx.posterior || ctx.posterior.n < 6) return {
|
|
177
|
+
decision: "needs_more_data",
|
|
178
|
+
reason: `Only ${ctx.posterior?.n ?? 0} paired observations; below hard floor of 6 for any paired inference.`
|
|
179
|
+
};
|
|
180
|
+
const ci = ctx.posterior.ci;
|
|
181
|
+
if (ctx.split === "search") return {
|
|
182
|
+
decision: "hold",
|
|
183
|
+
reason: "Search-split evidence is descriptive; release decisions require matched holdout runs."
|
|
184
|
+
};
|
|
185
|
+
const degenerate = !Number.isFinite(ci.low) || !Number.isFinite(ci.high) || ci.low === ci.high;
|
|
186
|
+
if (!degenerate && ctx.rope && ci.low >= ctx.rope.low && ci.high <= ctx.rope.high) return {
|
|
187
|
+
decision: "equivalent",
|
|
188
|
+
reason: `Paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] is fully inside ROPE ${formatRope(ctx.rope)}; candidate is practically equivalent to comparator.`
|
|
189
|
+
};
|
|
190
|
+
const gainPositive = !degenerate && ci.low > 0;
|
|
191
|
+
if (!degenerate && ci.high < 0) return {
|
|
192
|
+
decision: "reject",
|
|
193
|
+
reason: `Paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] lies entirely below zero.`
|
|
194
|
+
};
|
|
195
|
+
if (ctx.posterior.n < ctx.minPairs) return {
|
|
196
|
+
decision: "needs_more_data",
|
|
197
|
+
reason: `Only ${ctx.posterior.n} paired observations; minimum detectable effect at this N is ${fmt(ctx.posterior.mde)} score units (need ≥ ${ctx.minPairs} pairs to issue a directional verdict).`
|
|
198
|
+
};
|
|
199
|
+
if (degenerate) return {
|
|
200
|
+
decision: "needs_more_data",
|
|
201
|
+
reason: `Paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] has zero width — every resample was identical, so the interval carries no information about how far the estimate could be wrong and cannot support a promote or equivalence call.`
|
|
202
|
+
};
|
|
203
|
+
if (row.qValue !== null && row.qValue <= ctx.fdr && gainPositive) return {
|
|
204
|
+
decision: "promote",
|
|
205
|
+
reason: `BH-adjusted q=${fmt(row.qValue)} ≤ ${ctx.fdr} and paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] excludes zero; Pr(Δ>0)=${fmt(ctx.posterior.prGreaterThanZero)}.`
|
|
191
206
|
};
|
|
192
|
-
const deltas = before.map((b, i) => after[i] - b);
|
|
193
|
-
const sortedDeltas = [...deltas].sort((a, b) => a - b);
|
|
194
|
-
const median = medianOfSorted(sortedDeltas);
|
|
195
|
-
const min = sortedDeltas[0];
|
|
196
|
-
const max = sortedDeltas[sortedDeltas.length - 1];
|
|
197
|
-
const bound = Math.max(Math.abs(min), Math.abs(max), 1e-6);
|
|
198
|
-
const lo = -bound;
|
|
199
|
-
const width = (bound - lo) / binCount;
|
|
200
|
-
const bins = [];
|
|
201
|
-
for (let i = 0; i < binCount; i++) bins.push({
|
|
202
|
-
lo: lo + i * width,
|
|
203
|
-
hi: lo + (i + 1) * width,
|
|
204
|
-
count: 0
|
|
205
|
-
});
|
|
206
|
-
for (const d of deltas) {
|
|
207
|
-
let idx = Math.floor((d - lo) / width);
|
|
208
|
-
if (idx < 0) idx = 0;
|
|
209
|
-
if (idx >= binCount) idx = binCount - 1;
|
|
210
|
-
bins[idx].count += 1;
|
|
211
|
-
}
|
|
212
|
-
const ci = pairedBootstrap(before, after, {
|
|
213
|
-
confidence: opts.confidence ?? .95,
|
|
214
|
-
resamples: opts.resamples ?? 2e3,
|
|
215
|
-
statistic: "median",
|
|
216
|
-
seed: opts.seed
|
|
217
|
-
});
|
|
218
207
|
return {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
208
|
+
decision: "hold",
|
|
209
|
+
reason: `Pr(Δ>0)=${fmt(ctx.posterior.prGreaterThanZero)} but CI [${fmt(ci.low)}, ${fmt(ci.high)}] crosses zero; effect not decisive at fdr=${ctx.fdr}.`
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function buildRecommendation(candidates, ctx) {
|
|
213
|
+
const nonComparator = candidates.filter((c) => c.candidateId !== ctx.comparator);
|
|
214
|
+
const bestPromote = nonComparator.find((c) => c.decision === "promote");
|
|
215
|
+
const bestEquivalent = nonComparator.find((c) => c.decision === "equivalent");
|
|
216
|
+
const chosen = bestPromote ?? bestEquivalent ?? nonComparator[0] ?? null;
|
|
217
|
+
const decision = bestPromote ? "promote" : nonComparator.some((c) => c.decision === "needs_more_data") ? "needs_more_data" : bestEquivalent ? "equivalent" : nonComparator.some((c) => c.decision === "hold") ? "hold" : "reject";
|
|
218
|
+
const rationale = [];
|
|
219
|
+
const risks = [];
|
|
220
|
+
const nextActions = [];
|
|
221
|
+
if (chosen) {
|
|
222
|
+
rationale.push(`${chosen.candidateId}: ${chosen.decisionReason}`);
|
|
223
|
+
if (chosen.gainCi) {
|
|
224
|
+
const probSummary = chosen.prGreaterThanZero !== null ? `, Pr(Δ>0)=${fmt(chosen.prGreaterThanZero)}` : "";
|
|
225
|
+
rationale.push(`Median paired gain CI: [${fmt(chosen.gainCi.low)}, ${fmt(chosen.gainCi.high)}]${probSummary}.`);
|
|
231
226
|
}
|
|
227
|
+
if (chosen.mde !== null && Number.isFinite(chosen.mde)) rationale.push(`MDE at current paired N=${chosen.pairedN}: ${fmt(chosen.mde)} score units.`);
|
|
228
|
+
}
|
|
229
|
+
if (!ctx.comparator) {
|
|
230
|
+
risks.push("No comparator was configured; verdict is descriptive, not causal.");
|
|
231
|
+
nextActions.push("Re-run with a stable comparator candidate for paired inference.");
|
|
232
|
+
}
|
|
233
|
+
if (!ctx.preregistrationHash) {
|
|
234
|
+
risks.push("No preregistration hash supplied; readers cannot verify the analysis was specified before data inspection.");
|
|
235
|
+
nextActions.push("Sign a HypothesisManifest before the next sweep and pass `preregistrationHash` so the report cites it.");
|
|
236
|
+
}
|
|
237
|
+
if (ctx.rope === null && nonComparator.length > 0) {
|
|
238
|
+
risks.push("No ROPE configured; the report cannot distinguish \"equivalent\" from \"inconclusive\".");
|
|
239
|
+
nextActions.push("Define a domain-specific Region of Practical Equivalence and pass it to lock in the equivalence threshold.");
|
|
240
|
+
}
|
|
241
|
+
const inconclusive = nonComparator.filter((c) => c.decision === "needs_more_data");
|
|
242
|
+
if (inconclusive.length > 0) {
|
|
243
|
+
const worst = inconclusive.reduce((a, b) => b.pairedN < a.pairedN ? b : a);
|
|
244
|
+
risks.push(`${inconclusive.length} candidate(s) below soft floor (${ctx.minPairs} pairs); thinnest is ${worst.candidateId} with ${worst.pairedN}.`);
|
|
245
|
+
nextActions.push(`Collect at least ${ctx.minPairs - worst.pairedN} more matched holdout runs for ${worst.candidateId}.`);
|
|
246
|
+
}
|
|
247
|
+
const rejected = nonComparator.filter((c) => c.decision === "reject");
|
|
248
|
+
if (rejected.length > 0) risks.push(`${rejected.length} candidate(s) failed the paired test or held-out gate; do not ship those variants.`);
|
|
249
|
+
if (ctx.failureClusters && ctx.failureClusters.clusters.length > 0) {
|
|
250
|
+
const top = ctx.failureClusters.clusters[0];
|
|
251
|
+
risks.push(`Top failure cluster: ${top.failureClass} across ${top.runCount} run(s).`);
|
|
252
|
+
nextActions.push("Prioritize the largest failure cluster before broad rollout.");
|
|
253
|
+
}
|
|
254
|
+
if (decision === "promote") nextActions.push("Ship behind the existing promotion gate and monitor canaries.");
|
|
255
|
+
else if (decision === "hold") nextActions.push("Keep current production candidate while expanding holdout evidence.");
|
|
256
|
+
else if (decision === "equivalent") nextActions.push("Either keep the comparator (no quality regression) or promote on cost/latency grounds — equivalence does not justify either; the choice is a product decision, not a stats one.");
|
|
257
|
+
else if (decision === "reject") nextActions.push("Do not promote this sweep; inspect failures and generate a revised candidate.");
|
|
258
|
+
return {
|
|
259
|
+
decision,
|
|
260
|
+
candidateId: chosen?.candidateId ?? null,
|
|
261
|
+
rationale,
|
|
262
|
+
risks,
|
|
263
|
+
nextActions
|
|
232
264
|
};
|
|
233
265
|
}
|
|
266
|
+
function buildExecutiveSummary(candidates, recommendation, ctx) {
|
|
267
|
+
const lines = [];
|
|
268
|
+
const nonComparator = candidates.filter((c) => c.candidateId !== ctx.comparator);
|
|
269
|
+
lines.push(`Evaluated ${nonComparator.length} candidate(s) on the ${ctx.split} split${ctx.comparator ? ` against ${ctx.comparator}` : ""}.`);
|
|
270
|
+
lines.push(`Recommendation: ${recommendation.decision}${recommendation.candidateId ? ` ${recommendation.candidateId}` : ""}.`);
|
|
271
|
+
const promoted = nonComparator.filter((c) => c.decision === "promote").length;
|
|
272
|
+
const held = nonComparator.filter((c) => c.decision === "hold").length;
|
|
273
|
+
const equivalent = nonComparator.filter((c) => c.decision === "equivalent").length;
|
|
274
|
+
const rejected = nonComparator.filter((c) => c.decision === "reject").length;
|
|
275
|
+
const more = nonComparator.filter((c) => c.decision === "needs_more_data").length;
|
|
276
|
+
lines.push(`Decision mix: ${promoted} promote, ${equivalent} equivalent, ${held} hold, ${rejected} reject, ${more} need more data.`);
|
|
277
|
+
const frontier = nonComparator.filter((c) => c.onParetoFrontier).map((c) => c.candidateId);
|
|
278
|
+
if (frontier.length > 0) lines.push(`Pareto-frontier candidates: ${frontier.join(", ")}.`);
|
|
279
|
+
if (ctx.failureClusters) lines.push(`Failure clustering found ${ctx.failureClusters.totalFailures}/${ctx.failureClusters.totalRuns} failed runs across ${ctx.failureClusters.clusters.length} reportable cluster(s).`);
|
|
280
|
+
lines.push(ctx.preregistrationHash ? `Preregistered analysis: ${ctx.preregistrationHash.slice(0, 12)}…` : "Analysis is post-hoc — no preregistration hash supplied.");
|
|
281
|
+
return lines;
|
|
282
|
+
}
|
|
283
|
+
function decisionWeight(decision) {
|
|
284
|
+
if (decision === "promote") return 5;
|
|
285
|
+
if (decision === "equivalent") return 4;
|
|
286
|
+
if (decision === "hold") return 3;
|
|
287
|
+
if (decision === "needs_more_data") return 2;
|
|
288
|
+
return 1;
|
|
289
|
+
}
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/statistics/effect-sizes.ts
|
|
234
292
|
/**
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
* exact tables; below this the test has no power to separate effect sizes.
|
|
293
|
+
* Standardized effect sizes: Cohen's d family and Cliff's delta, with the
|
|
294
|
+
* shared degenerate-input contract (null where the effect is undefined).
|
|
238
295
|
*/
|
|
239
|
-
|
|
296
|
+
/**
|
|
297
|
+
* Cohen's d — standardized effect size for two independent groups.
|
|
298
|
+
* Positive d means group b has higher mean than group a.
|
|
299
|
+
* Rule of thumb: |d| < 0.2 negligible, 0.2–0.5 small, 0.5–0.8 medium, > 0.8 large.
|
|
300
|
+
*
|
|
301
|
+
* Returns null where the standardized effect is undefined: fewer than two
|
|
302
|
+
* observations in either group, or a zero pooled standard deviation with
|
|
303
|
+
* unequal means. Null is NOT "no effect" — zero within-group spread across a
|
|
304
|
+
* real mean gap is an unbounded effect, the opposite of negligible. Equal
|
|
305
|
+
* means with zero spread is a genuine 0. Same contract as
|
|
306
|
+
* {@link pairedCohensDz}.
|
|
307
|
+
*/
|
|
308
|
+
function cohensD(a, b) {
|
|
309
|
+
if (a.length < 2 || b.length < 2) return null;
|
|
310
|
+
const meanA = a.reduce((x, y) => x + y, 0) / a.length;
|
|
311
|
+
const meanB = b.reduce((x, y) => x + y, 0) / b.length;
|
|
312
|
+
const varA = a.reduce((acc, x) => acc + (x - meanA) ** 2, 0) / (a.length - 1);
|
|
313
|
+
const varB = b.reduce((acc, x) => acc + (x - meanB) ** 2, 0) / (b.length - 1);
|
|
314
|
+
const pooled = Math.sqrt(((a.length - 1) * varA + (b.length - 1) * varB) / (a.length + b.length - 2));
|
|
315
|
+
if (pooled === 0) return meanB === meanA ? 0 : null;
|
|
316
|
+
return (meanB - meanA) / pooled;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Cohen's dz for paired observations: mean(after - before) divided by the
|
|
320
|
+
* sample standard deviation of those within-pair deltas.
|
|
321
|
+
*
|
|
322
|
+
* Returns null when fewer than two pairs exist or a non-zero constant delta
|
|
323
|
+
* has zero observed variance. In that case the standardized effect is
|
|
324
|
+
* undefined, not an arbitrarily large finite number.
|
|
325
|
+
*/
|
|
326
|
+
function pairedCohensDz(before, after) {
|
|
327
|
+
if (before.length !== after.length) throw new ValidationError(`pairedCohensDz: unequal sample sizes (${before.length} vs ${after.length})`);
|
|
328
|
+
if (before.length < 2) return null;
|
|
329
|
+
const deltas = before.map((value, index) => after[index] - value);
|
|
330
|
+
if (deltas.some((value) => !Number.isFinite(value))) throw new ValidationError("pairedCohensDz: all paired values must be finite");
|
|
331
|
+
const meanDelta = deltas.reduce((sum, value) => sum + value, 0) / deltas.length;
|
|
332
|
+
const variance = deltas.reduce((sum, value) => sum + (value - meanDelta) ** 2, 0) / (deltas.length - 1);
|
|
333
|
+
const standardDeviation = Math.sqrt(variance);
|
|
334
|
+
const scale = Math.max(1, Math.abs(meanDelta), ...deltas.map(Math.abs));
|
|
335
|
+
if (standardDeviation <= Number.EPSILON * scale) return meanDelta === 0 ? 0 : null;
|
|
336
|
+
return meanDelta / standardDeviation;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Cliff's delta — a non-parametric effect size for two independent samples.
|
|
340
|
+
* `δ = (#(after > before) − #(after < before)) / (n_before · n_after)`,
|
|
341
|
+
* ranging [-1, 1]. Positive ⇒ `after` tends to exceed `before` (improvement).
|
|
342
|
+
*
|
|
343
|
+
* Distribution-free counterpart to Cohen's d: no normality assumption, robust
|
|
344
|
+
* to the bounded/skewed score distributions judges produce. Pairs with
|
|
345
|
+
* `pairedBootstrap` / `wilcoxonSignedRank` for the non-parametric reporting
|
|
346
|
+
* path. Returns 0 when either sample is empty.
|
|
347
|
+
*/
|
|
348
|
+
function cliffsDelta(before, after) {
|
|
349
|
+
const n = before.length * after.length;
|
|
350
|
+
if (n === 0) return 0;
|
|
351
|
+
let dominance = 0;
|
|
352
|
+
for (const a of after) for (const b of before) if (a > b) dominance += 1;
|
|
353
|
+
else if (a < b) dominance -= 1;
|
|
354
|
+
return dominance / n;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Map a Cliff's delta to a qualitative magnitude using the standard
|
|
358
|
+
* Romano et al. thresholds (|δ|): <0.147 negligible, <0.33 small,
|
|
359
|
+
* <0.474 medium, else large.
|
|
360
|
+
*/
|
|
361
|
+
function interpretCliffs(delta) {
|
|
362
|
+
const d = Math.abs(delta);
|
|
363
|
+
if (d < .147) return "negligible";
|
|
364
|
+
if (d < .33) return "small";
|
|
365
|
+
if (d < .474) return "medium";
|
|
366
|
+
return "large";
|
|
367
|
+
}
|
|
368
|
+
//#endregion
|
|
369
|
+
//#region src/summary-report-evidence.ts
|
|
370
|
+
/** Matched split scores and paired statistical evidence for report consumers. */
|
|
371
|
+
function pairScoresByKey(candidate, baseline, split) {
|
|
372
|
+
const scoreOf = (run) => {
|
|
373
|
+
const v = observedSplitScore(run, split);
|
|
374
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
375
|
+
};
|
|
376
|
+
const scoredCandidate = candidate.filter((run) => scoreOf(run) !== void 0);
|
|
377
|
+
const scoredBaseline = baseline.filter((run) => scoreOf(run) !== void 0);
|
|
378
|
+
const pairing = pairRunRecords(scoredBaseline, scoredCandidate);
|
|
379
|
+
return {
|
|
380
|
+
before: pairing.pairs.map((pair) => scoreOf(pair.baseline)),
|
|
381
|
+
after: pairing.pairs.map((pair) => scoreOf(pair.treatment)),
|
|
382
|
+
unpairedCandidateN: pairing.unpairedTreatment.length,
|
|
383
|
+
unpairedComparatorN: pairing.unpairedBaseline.length
|
|
384
|
+
};
|
|
385
|
+
}
|
|
240
386
|
/**
|
|
241
387
|
* Internal: paired posterior summary on (candidate − comparator) deltas.
|
|
242
388
|
*
|
|
@@ -310,9 +456,226 @@ function stdev(xs, mean) {
|
|
|
310
456
|
for (const x of xs) sse += (x - mean) ** 2;
|
|
311
457
|
return Math.sqrt(sse / (xs.length - 1));
|
|
312
458
|
}
|
|
459
|
+
function medianOfSorted(sorted) {
|
|
460
|
+
if (sorted.length === 0) return 0;
|
|
461
|
+
const mid = Math.floor(sorted.length / 2);
|
|
462
|
+
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
463
|
+
}
|
|
464
|
+
//#endregion
|
|
465
|
+
//#region src/summary-report-figures.ts
|
|
313
466
|
/**
|
|
314
|
-
*
|
|
315
|
-
*
|
|
467
|
+
* Table 1 helper. Buckets runs by `candidateId`, computes mean +
|
|
468
|
+
* bootstrap CI on the chosen split, and (when a comparator is given)
|
|
469
|
+
* BH-adjusted Wilcoxon p + paired Cohen's dz versus that comparator.
|
|
470
|
+
*/
|
|
471
|
+
function summaryTable(runs, opts = {}) {
|
|
472
|
+
const split = opts.split ?? "holdout";
|
|
473
|
+
const confidence = opts.confidence ?? .95;
|
|
474
|
+
const fdr = opts.fdr ?? .05;
|
|
475
|
+
const comparator = opts.comparator ?? null;
|
|
476
|
+
const byCandidate = /* @__PURE__ */ new Map();
|
|
477
|
+
for (const r of runs) {
|
|
478
|
+
if (r.splitTag !== split) continue;
|
|
479
|
+
const v = observedSplitScore(r, split);
|
|
480
|
+
if (typeof v !== "number" || !Number.isFinite(v)) continue;
|
|
481
|
+
const bucket = byCandidate.get(r.candidateId) ?? {
|
|
482
|
+
runs: [],
|
|
483
|
+
scores: []
|
|
484
|
+
};
|
|
485
|
+
bucket.runs.push(r);
|
|
486
|
+
bucket.scores.push(v);
|
|
487
|
+
byCandidate.set(r.candidateId, bucket);
|
|
488
|
+
}
|
|
489
|
+
const candidateIds = [...byCandidate.keys()].sort();
|
|
490
|
+
const compRuns = comparator ? byCandidate.get(comparator) : void 0;
|
|
491
|
+
const tentative = [];
|
|
492
|
+
for (const id of candidateIds) {
|
|
493
|
+
const bucket = byCandidate.get(id);
|
|
494
|
+
const ci = confidenceInterval(bucket.scores, confidence);
|
|
495
|
+
let rawP = null;
|
|
496
|
+
let d = null;
|
|
497
|
+
let pairedN = null;
|
|
498
|
+
let unpairedCandidateN = null;
|
|
499
|
+
let unpairedComparatorN = null;
|
|
500
|
+
if (comparator && compRuns && id !== comparator) {
|
|
501
|
+
const paired = pairScoresByKey(bucket.runs, compRuns.runs, split);
|
|
502
|
+
pairedN = paired.before.length;
|
|
503
|
+
unpairedCandidateN = paired.unpairedCandidateN;
|
|
504
|
+
unpairedComparatorN = paired.unpairedComparatorN;
|
|
505
|
+
if (paired.before.length >= 6) rawP = wilcoxonSignedRank(paired.before, paired.after).p;
|
|
506
|
+
d = pairedCohensDz(paired.before, paired.after);
|
|
507
|
+
}
|
|
508
|
+
tentative.push({
|
|
509
|
+
candidateId: id,
|
|
510
|
+
n: bucket.scores.length,
|
|
511
|
+
mean: ci.mean,
|
|
512
|
+
ciLow: ci.lower,
|
|
513
|
+
ciHigh: ci.upper,
|
|
514
|
+
qValue: rawP,
|
|
515
|
+
cohensD: d,
|
|
516
|
+
pairedN,
|
|
517
|
+
unpairedCandidateN,
|
|
518
|
+
unpairedComparatorN,
|
|
519
|
+
rawP
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
if (comparator) {
|
|
523
|
+
const idxs = [];
|
|
524
|
+
const ps = [];
|
|
525
|
+
for (let i = 0; i < tentative.length; i++) {
|
|
526
|
+
const r = tentative[i];
|
|
527
|
+
if (r.candidateId === comparator) continue;
|
|
528
|
+
if (r.rawP === null || !Number.isFinite(r.rawP)) continue;
|
|
529
|
+
idxs.push(i);
|
|
530
|
+
ps.push(r.rawP);
|
|
531
|
+
}
|
|
532
|
+
if (ps.length > 0) {
|
|
533
|
+
const { qValues } = benjaminiHochberg(ps, fdr);
|
|
534
|
+
for (let k = 0; k < idxs.length; k++) tentative[idxs[k]].qValue = qValues[k];
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const rows = tentative.map(({ rawP: _rawP, ...rest }) => rest);
|
|
538
|
+
return {
|
|
539
|
+
rows,
|
|
540
|
+
comparator,
|
|
541
|
+
split,
|
|
542
|
+
markdown: renderSummaryTableMarkdown(rows, comparator, split)
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function renderSummaryTableMarkdown(rows, comparator, split) {
|
|
546
|
+
const lines = [];
|
|
547
|
+
const cmpLabel = comparator ? ` (vs ${comparator})` : "";
|
|
548
|
+
lines.push(`Summary Table — ${split} split${cmpLabel}`);
|
|
549
|
+
lines.push("");
|
|
550
|
+
lines.push("| Candidate | N | Pairs | Unpaired | Mean | 95% CI | q (BH) | Cohen's dz |");
|
|
551
|
+
lines.push("|---|---:|---:|---:|---:|---|---:|---:|");
|
|
552
|
+
for (const r of rows) {
|
|
553
|
+
const ci = `[${fmt(r.ciLow)}, ${fmt(r.ciHigh)}]`;
|
|
554
|
+
const q = r.qValue !== null ? r.qValue.toFixed(4) : "—";
|
|
555
|
+
const d = r.cohensD !== null ? r.cohensD.toFixed(3) : "—";
|
|
556
|
+
const paired = r.pairedN ?? "—";
|
|
557
|
+
const unpaired = r.unpairedCandidateN === null || r.unpairedComparatorN === null ? "—" : `${r.unpairedCandidateN}/${r.unpairedComparatorN}`;
|
|
558
|
+
lines.push(`| ${r.candidateId} | ${r.n} | ${paired} | ${unpaired} | ${fmt(r.mean)} | ${ci} | ${q} | ${d} |`);
|
|
559
|
+
}
|
|
560
|
+
return lines.join("\n");
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Cost vs quality scatter spec. `gateDecisions` is keyed by
|
|
564
|
+
* candidate id; if present, every point picks up the gate verdict
|
|
565
|
+
* for overlay.
|
|
566
|
+
*/
|
|
567
|
+
function paretoChart(runs, opts = {}) {
|
|
568
|
+
const split = opts.split ?? "holdout";
|
|
569
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
570
|
+
for (const r of runs) {
|
|
571
|
+
if (r.splitTag !== split) continue;
|
|
572
|
+
const v = observedSplitScore(r, split);
|
|
573
|
+
if (typeof v !== "number" || !Number.isFinite(v)) continue;
|
|
574
|
+
if (r.costUsd === null) continue;
|
|
575
|
+
const bucket = buckets.get(r.candidateId) ?? {
|
|
576
|
+
cost: [],
|
|
577
|
+
quality: []
|
|
578
|
+
};
|
|
579
|
+
bucket.cost.push(r.costUsd);
|
|
580
|
+
bucket.quality.push(v);
|
|
581
|
+
buckets.set(r.candidateId, bucket);
|
|
582
|
+
}
|
|
583
|
+
const points = [];
|
|
584
|
+
for (const [candidateId, bucket] of buckets.entries()) points.push({
|
|
585
|
+
candidateId,
|
|
586
|
+
cost: mean(bucket.cost),
|
|
587
|
+
quality: mean(bucket.quality),
|
|
588
|
+
n: bucket.cost.length,
|
|
589
|
+
onFrontier: false,
|
|
590
|
+
gate: opts.gateDecisions?.[candidateId] ? gateLabel(opts.gateDecisions[candidateId]) : void 0
|
|
591
|
+
});
|
|
592
|
+
for (const p of points) p.onFrontier = !points.some((q) => q !== p && dominates(q, p));
|
|
593
|
+
return {
|
|
594
|
+
kind: "pareto-cost-quality",
|
|
595
|
+
split,
|
|
596
|
+
axes: {
|
|
597
|
+
x: "costUsd",
|
|
598
|
+
y: "score"
|
|
599
|
+
},
|
|
600
|
+
points
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function dominates(a, b) {
|
|
604
|
+
return a.cost <= b.cost && a.quality >= b.quality && (a.cost < b.cost || a.quality > b.quality);
|
|
605
|
+
}
|
|
606
|
+
function gateLabel(d) {
|
|
607
|
+
return d.promote ? "promote" : "reject";
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Held-out improvement distribution: per-pair delta (candidate −
|
|
611
|
+
* comparator), histogrammed. Includes the bootstrap CI on the median
|
|
612
|
+
* delta — same primitive the promotion gate uses.
|
|
613
|
+
*/
|
|
614
|
+
function gainHistogram(runs, candidateId, comparator, opts = {}) {
|
|
615
|
+
const split = opts.split ?? "holdout";
|
|
616
|
+
const binCount = opts.bins ?? 11;
|
|
617
|
+
if (binCount < 1) throw new Error("gainHistogram: bins must be ≥ 1");
|
|
618
|
+
const { before, after, unpairedCandidateN, unpairedComparatorN } = pairScoresByKey(runs.filter((r) => r.candidateId === candidateId && r.splitTag === split), runs.filter((r) => r.candidateId === comparator && r.splitTag === split), split);
|
|
619
|
+
const n = before.length;
|
|
620
|
+
if (n === 0) return {
|
|
621
|
+
kind: "gain-distribution",
|
|
622
|
+
candidateId,
|
|
623
|
+
comparator,
|
|
624
|
+
split,
|
|
625
|
+
n: 0,
|
|
626
|
+
unpairedCandidateN,
|
|
627
|
+
unpairedComparatorN,
|
|
628
|
+
bins: [],
|
|
629
|
+
median: null,
|
|
630
|
+
ci: null
|
|
631
|
+
};
|
|
632
|
+
const deltas = before.map((b, i) => after[i] - b);
|
|
633
|
+
const sortedDeltas = [...deltas].sort((a, b) => a - b);
|
|
634
|
+
const median = medianOfSorted(sortedDeltas);
|
|
635
|
+
const min = sortedDeltas[0];
|
|
636
|
+
const max = sortedDeltas[sortedDeltas.length - 1];
|
|
637
|
+
const bound = Math.max(Math.abs(min), Math.abs(max), 1e-6);
|
|
638
|
+
const lo = -bound;
|
|
639
|
+
const width = (bound - lo) / binCount;
|
|
640
|
+
const bins = [];
|
|
641
|
+
for (let i = 0; i < binCount; i++) bins.push({
|
|
642
|
+
lo: lo + i * width,
|
|
643
|
+
hi: lo + (i + 1) * width,
|
|
644
|
+
count: 0
|
|
645
|
+
});
|
|
646
|
+
for (const d of deltas) {
|
|
647
|
+
let idx = Math.floor((d - lo) / width);
|
|
648
|
+
if (idx < 0) idx = 0;
|
|
649
|
+
if (idx >= binCount) idx = binCount - 1;
|
|
650
|
+
bins[idx].count += 1;
|
|
651
|
+
}
|
|
652
|
+
const ci = pairedBootstrap(before, after, {
|
|
653
|
+
confidence: opts.confidence ?? .95,
|
|
654
|
+
resamples: opts.resamples ?? 2e3,
|
|
655
|
+
statistic: "median",
|
|
656
|
+
seed: opts.seed
|
|
657
|
+
});
|
|
658
|
+
return {
|
|
659
|
+
kind: "gain-distribution",
|
|
660
|
+
candidateId,
|
|
661
|
+
comparator,
|
|
662
|
+
split,
|
|
663
|
+
n,
|
|
664
|
+
unpairedCandidateN,
|
|
665
|
+
unpairedComparatorN,
|
|
666
|
+
bins,
|
|
667
|
+
median,
|
|
668
|
+
ci: {
|
|
669
|
+
low: ci.low,
|
|
670
|
+
high: ci.high
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
//#endregion
|
|
675
|
+
//#region src/research-report.ts
|
|
676
|
+
/**
|
|
677
|
+
* Executive research report for CPO / AI-lead / launch-review consumption.
|
|
678
|
+
*
|
|
316
679
|
* Composes:
|
|
317
680
|
* - `summaryTable` marginal stats with BH-FDR-adjusted q-values
|
|
318
681
|
* - `paretoChart` cost-vs-quality frontier with gate overlay
|
|
@@ -483,380 +846,7 @@ async function researchReport(runs, opts = {}) {
|
|
|
483
846
|
html
|
|
484
847
|
};
|
|
485
848
|
}
|
|
486
|
-
function buildMethodology(ctx) {
|
|
487
|
-
const assumptions = [
|
|
488
|
-
"Pairs are matched by (experimentId, scenarioId, seed); input order does not affect the comparison.",
|
|
489
|
-
"Paired deltas are exchangeable conditional on the matched scenario — no mid-run distribution shift.",
|
|
490
|
-
`Decisions are pre-specified at fdr=${ctx.fdr}, minPairs=${ctx.minPairs}, confidence=${ctx.confidence}; deviating from these post-hoc invalidates the false-discovery control.`
|
|
491
|
-
];
|
|
492
|
-
if (ctx.rope) assumptions.push(`The Region of Practical Equivalence ${formatRope(ctx.rope)} is supplied by the domain owner; equivalent verdicts are only meaningful if that range is treated as the standing definition of "no material difference."`);
|
|
493
|
-
if (ctx.comparator === null) assumptions.push("No comparator was configured; this run is descriptive, not causal.");
|
|
494
|
-
return {
|
|
495
|
-
assumptions,
|
|
496
|
-
methods: [
|
|
497
|
-
"Marginal scores summarised with BH-FDR-adjusted Wilcoxon signed-rank q-values and paired Cohen's dz via summaryTable.",
|
|
498
|
-
"Paired evidence summarised with a frequentist bootstrap CI on the median delta and a Dirichlet-weight Bayesian bootstrap for Pr(Δ>0) and Pr(Δ∈ROPE) on the mean delta.",
|
|
499
|
-
`Minimum detectable effect reported per candidate at α=${ctx.mdeAlpha} (two-sided), power=${ctx.mdePower}, standardised by the observed paired-delta SD.`,
|
|
500
|
-
"Pareto frontier flagged as a separate axis (cost vs quality); a candidate can be on-frontier without winning the paired test.",
|
|
501
|
-
"Held-out gate decisions, when supplied, override the statistical verdict in the reject direction."
|
|
502
|
-
],
|
|
503
|
-
alternatives: [
|
|
504
|
-
"Paired t-test rejected: not robust to the heavy-tailed score distributions common in agent benchmarks.",
|
|
505
|
-
"Unpaired Mann–Whitney rejected: matched scenarios make pairing free; unpaired throws away that variance reduction.",
|
|
506
|
-
"Sequential / always-valid inference (e-values, mSPRT) is the right tool for iterative sweeps and is out of scope for this single-look report — preregister and run once, or wrap this report in an alpha-spending schedule.",
|
|
507
|
-
"Hierarchical Bayesian shrinkage across many candidates is future work; the current ranking uses raw paired statistics."
|
|
508
|
-
],
|
|
509
|
-
whenNotToApply: [
|
|
510
|
-
`Paired N below 6 on any candidate — the bootstrap CI is degenerate.`,
|
|
511
|
-
"Comparator chosen post-hoc by inspecting the same data; q-values are no longer false-discovery-controlled.",
|
|
512
|
-
"Scenarios not drawn under a stable preregistered protocol; the report can describe the data but cannot anchor a launch decision.",
|
|
513
|
-
"Score distributions with mid-run shift (judge model swap, rubric change, infra outage) — pair exchangeability is violated."
|
|
514
|
-
],
|
|
515
|
-
citations: [
|
|
516
|
-
"Benjamini, Y. & Hochberg, Y. (1995). Controlling the false discovery rate: a practical and powerful approach to multiple testing. JRSS B, 57(1), 289–300.",
|
|
517
|
-
"Wilcoxon, F. (1945). Individual comparisons by ranking methods. Biometrics Bulletin, 1(6), 80–83.",
|
|
518
|
-
"Efron, B. (1979). Bootstrap methods: another look at the jackknife. Annals of Statistics, 7(1), 1–26.",
|
|
519
|
-
"Rubin, D. B. (1981). The Bayesian bootstrap. Annals of Statistics, 9(1), 130–134.",
|
|
520
|
-
"Kruschke, J. K. (2018). Rejecting or accepting parameter values in Bayesian estimation. Advances in Methods and Practices in Psychological Science, 1(2), 270–280. (ROPE.)"
|
|
521
|
-
]
|
|
522
|
-
};
|
|
523
|
-
}
|
|
524
|
-
function formatRope(rope) {
|
|
525
|
-
return `[${fmt(rope.low)}, ${fmt(rope.high)}]`;
|
|
526
|
-
}
|
|
527
|
-
function classifyCandidate(row, ctx) {
|
|
528
|
-
if (ctx.comparator && row.candidateId === ctx.comparator) return {
|
|
529
|
-
decision: "hold",
|
|
530
|
-
reason: "Comparator baseline."
|
|
531
|
-
};
|
|
532
|
-
if (ctx.gateDecision && !ctx.gateDecision.promote) return {
|
|
533
|
-
decision: "reject",
|
|
534
|
-
reason: `Held-out gate returned ${ctx.gateDecision.rejectionCode ?? "rejected"}: ${ctx.gateDecision.reason}`
|
|
535
|
-
};
|
|
536
|
-
if (!ctx.comparator) return {
|
|
537
|
-
decision: ctx.point?.onFrontier ? "hold" : "needs_more_data",
|
|
538
|
-
reason: "No comparator configured; report ranks candidates but cannot anchor a promotion call."
|
|
539
|
-
};
|
|
540
|
-
if (!ctx.posterior || ctx.posterior.n < 6) return {
|
|
541
|
-
decision: "needs_more_data",
|
|
542
|
-
reason: `Only ${ctx.posterior?.n ?? 0} paired observations; below hard floor of 6 for any paired inference.`
|
|
543
|
-
};
|
|
544
|
-
const ci = ctx.posterior.ci;
|
|
545
|
-
if (ctx.split === "search") return {
|
|
546
|
-
decision: "hold",
|
|
547
|
-
reason: "Search-split evidence is descriptive; release decisions require matched holdout runs."
|
|
548
|
-
};
|
|
549
|
-
const degenerate = !Number.isFinite(ci.low) || !Number.isFinite(ci.high) || ci.low === ci.high;
|
|
550
|
-
if (!degenerate && ctx.rope && ci.low >= ctx.rope.low && ci.high <= ctx.rope.high) return {
|
|
551
|
-
decision: "equivalent",
|
|
552
|
-
reason: `Paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] is fully inside ROPE ${formatRope(ctx.rope)}; candidate is practically equivalent to comparator.`
|
|
553
|
-
};
|
|
554
|
-
const gainPositive = !degenerate && ci.low > 0;
|
|
555
|
-
if (!degenerate && ci.high < 0) return {
|
|
556
|
-
decision: "reject",
|
|
557
|
-
reason: `Paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] lies entirely below zero.`
|
|
558
|
-
};
|
|
559
|
-
if (ctx.posterior.n < ctx.minPairs) return {
|
|
560
|
-
decision: "needs_more_data",
|
|
561
|
-
reason: `Only ${ctx.posterior.n} paired observations; minimum detectable effect at this N is ${fmt(ctx.posterior.mde)} score units (need ≥ ${ctx.minPairs} pairs to issue a directional verdict).`
|
|
562
|
-
};
|
|
563
|
-
if (degenerate) return {
|
|
564
|
-
decision: "needs_more_data",
|
|
565
|
-
reason: `Paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] has zero width — every resample was identical, so the interval carries no information about how far the estimate could be wrong and cannot support a promote or equivalence call.`
|
|
566
|
-
};
|
|
567
|
-
if (row.qValue !== null && row.qValue <= ctx.fdr && gainPositive) return {
|
|
568
|
-
decision: "promote",
|
|
569
|
-
reason: `BH-adjusted q=${fmt(row.qValue)} ≤ ${ctx.fdr} and paired-delta CI [${fmt(ci.low)}, ${fmt(ci.high)}] excludes zero; Pr(Δ>0)=${fmt(ctx.posterior.prGreaterThanZero)}.`
|
|
570
|
-
};
|
|
571
|
-
return {
|
|
572
|
-
decision: "hold",
|
|
573
|
-
reason: `Pr(Δ>0)=${fmt(ctx.posterior.prGreaterThanZero)} but CI [${fmt(ci.low)}, ${fmt(ci.high)}] crosses zero; effect not decisive at fdr=${ctx.fdr}.`
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
function buildRecommendation(candidates, ctx) {
|
|
577
|
-
const nonComparator = candidates.filter((c) => c.candidateId !== ctx.comparator);
|
|
578
|
-
const bestPromote = nonComparator.find((c) => c.decision === "promote");
|
|
579
|
-
const bestEquivalent = nonComparator.find((c) => c.decision === "equivalent");
|
|
580
|
-
const chosen = bestPromote ?? bestEquivalent ?? nonComparator[0] ?? null;
|
|
581
|
-
const decision = bestPromote ? "promote" : nonComparator.some((c) => c.decision === "needs_more_data") ? "needs_more_data" : bestEquivalent ? "equivalent" : nonComparator.some((c) => c.decision === "hold") ? "hold" : "reject";
|
|
582
|
-
const rationale = [];
|
|
583
|
-
const risks = [];
|
|
584
|
-
const nextActions = [];
|
|
585
|
-
if (chosen) {
|
|
586
|
-
rationale.push(`${chosen.candidateId}: ${chosen.decisionReason}`);
|
|
587
|
-
if (chosen.gainCi) {
|
|
588
|
-
const probSummary = chosen.prGreaterThanZero !== null ? `, Pr(Δ>0)=${fmt(chosen.prGreaterThanZero)}` : "";
|
|
589
|
-
rationale.push(`Median paired gain CI: [${fmt(chosen.gainCi.low)}, ${fmt(chosen.gainCi.high)}]${probSummary}.`);
|
|
590
|
-
}
|
|
591
|
-
if (chosen.mde !== null && Number.isFinite(chosen.mde)) rationale.push(`MDE at current paired N=${chosen.pairedN}: ${fmt(chosen.mde)} score units.`);
|
|
592
|
-
}
|
|
593
|
-
if (!ctx.comparator) {
|
|
594
|
-
risks.push("No comparator was configured; verdict is descriptive, not causal.");
|
|
595
|
-
nextActions.push("Re-run with a stable comparator candidate for paired inference.");
|
|
596
|
-
}
|
|
597
|
-
if (!ctx.preregistrationHash) {
|
|
598
|
-
risks.push("No preregistration hash supplied; readers cannot verify the analysis was specified before data inspection.");
|
|
599
|
-
nextActions.push("Sign a HypothesisManifest before the next sweep and pass `preregistrationHash` so the report cites it.");
|
|
600
|
-
}
|
|
601
|
-
if (ctx.rope === null && nonComparator.length > 0) {
|
|
602
|
-
risks.push("No ROPE configured; the report cannot distinguish \"equivalent\" from \"inconclusive\".");
|
|
603
|
-
nextActions.push("Define a domain-specific Region of Practical Equivalence and pass it to lock in the equivalence threshold.");
|
|
604
|
-
}
|
|
605
|
-
const inconclusive = nonComparator.filter((c) => c.decision === "needs_more_data");
|
|
606
|
-
if (inconclusive.length > 0) {
|
|
607
|
-
const worst = inconclusive.reduce((a, b) => b.pairedN < a.pairedN ? b : a);
|
|
608
|
-
risks.push(`${inconclusive.length} candidate(s) below soft floor (${ctx.minPairs} pairs); thinnest is ${worst.candidateId} with ${worst.pairedN}.`);
|
|
609
|
-
nextActions.push(`Collect at least ${ctx.minPairs - worst.pairedN} more matched holdout runs for ${worst.candidateId}.`);
|
|
610
|
-
}
|
|
611
|
-
const rejected = nonComparator.filter((c) => c.decision === "reject");
|
|
612
|
-
if (rejected.length > 0) risks.push(`${rejected.length} candidate(s) failed the paired test or held-out gate; do not ship those variants.`);
|
|
613
|
-
if (ctx.failureClusters && ctx.failureClusters.clusters.length > 0) {
|
|
614
|
-
const top = ctx.failureClusters.clusters[0];
|
|
615
|
-
risks.push(`Top failure cluster: ${top.failureClass} across ${top.runCount} run(s).`);
|
|
616
|
-
nextActions.push("Prioritize the largest failure cluster before broad rollout.");
|
|
617
|
-
}
|
|
618
|
-
if (decision === "promote") nextActions.push("Ship behind the existing promotion gate and monitor canaries.");
|
|
619
|
-
else if (decision === "hold") nextActions.push("Keep current production candidate while expanding holdout evidence.");
|
|
620
|
-
else if (decision === "equivalent") nextActions.push("Either keep the comparator (no quality regression) or promote on cost/latency grounds — equivalence does not justify either; the choice is a product decision, not a stats one.");
|
|
621
|
-
else if (decision === "reject") nextActions.push("Do not promote this sweep; inspect failures and generate a revised candidate.");
|
|
622
|
-
return {
|
|
623
|
-
decision,
|
|
624
|
-
candidateId: chosen?.candidateId ?? null,
|
|
625
|
-
rationale,
|
|
626
|
-
risks,
|
|
627
|
-
nextActions
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
function buildExecutiveSummary(candidates, recommendation, ctx) {
|
|
631
|
-
const lines = [];
|
|
632
|
-
const nonComparator = candidates.filter((c) => c.candidateId !== ctx.comparator);
|
|
633
|
-
lines.push(`Evaluated ${nonComparator.length} candidate(s) on the ${ctx.split} split${ctx.comparator ? ` against ${ctx.comparator}` : ""}.`);
|
|
634
|
-
lines.push(`Recommendation: ${recommendation.decision}${recommendation.candidateId ? ` ${recommendation.candidateId}` : ""}.`);
|
|
635
|
-
const promoted = nonComparator.filter((c) => c.decision === "promote").length;
|
|
636
|
-
const held = nonComparator.filter((c) => c.decision === "hold").length;
|
|
637
|
-
const equivalent = nonComparator.filter((c) => c.decision === "equivalent").length;
|
|
638
|
-
const rejected = nonComparator.filter((c) => c.decision === "reject").length;
|
|
639
|
-
const more = nonComparator.filter((c) => c.decision === "needs_more_data").length;
|
|
640
|
-
lines.push(`Decision mix: ${promoted} promote, ${equivalent} equivalent, ${held} hold, ${rejected} reject, ${more} need more data.`);
|
|
641
|
-
const frontier = nonComparator.filter((c) => c.onParetoFrontier).map((c) => c.candidateId);
|
|
642
|
-
if (frontier.length > 0) lines.push(`Pareto-frontier candidates: ${frontier.join(", ")}.`);
|
|
643
|
-
if (ctx.failureClusters) lines.push(`Failure clustering found ${ctx.failureClusters.totalFailures}/${ctx.failureClusters.totalRuns} failed runs across ${ctx.failureClusters.clusters.length} reportable cluster(s).`);
|
|
644
|
-
lines.push(ctx.preregistrationHash ? `Preregistered analysis: ${ctx.preregistrationHash.slice(0, 12)}…` : "Analysis is post-hoc — no preregistration hash supplied.");
|
|
645
|
-
return lines;
|
|
646
|
-
}
|
|
647
|
-
function renderResearchMarkdown(report) {
|
|
648
|
-
const lines = [];
|
|
649
|
-
lines.push(`# ${report.title}`);
|
|
650
|
-
lines.push("");
|
|
651
|
-
lines.push(`**Generated:** ${report.generatedAt}`);
|
|
652
|
-
lines.push(`**Primary split:** ${report.split}`);
|
|
653
|
-
lines.push(`**Comparator:** ${report.comparator ?? "not configured"}`);
|
|
654
|
-
lines.push(`**ROPE:** ${report.rope ? formatRope(report.rope) : "not configured"}`);
|
|
655
|
-
lines.push(`**Run fingerprint:** \`${report.runFingerprint}\``);
|
|
656
|
-
lines.push(`**Preregistration:** ${report.preregistrationHash ? `\`${report.preregistrationHash}\`` : "none"}`);
|
|
657
|
-
lines.push("");
|
|
658
|
-
lines.push("## Executive Summary");
|
|
659
|
-
lines.push("");
|
|
660
|
-
for (const item of report.executiveSummary) lines.push(`- ${item}`);
|
|
661
|
-
lines.push("");
|
|
662
|
-
lines.push("## Recommendation");
|
|
663
|
-
lines.push("");
|
|
664
|
-
lines.push(`**Decision:** ${report.recommendation.decision}`);
|
|
665
|
-
lines.push(`**Candidate:** ${report.recommendation.candidateId ?? "N/A"}`);
|
|
666
|
-
lines.push("");
|
|
667
|
-
lines.push("### Rationale");
|
|
668
|
-
lines.push("");
|
|
669
|
-
for (const item of report.recommendation.rationale) lines.push(`- ${item}`);
|
|
670
|
-
lines.push("");
|
|
671
|
-
lines.push("### Risks");
|
|
672
|
-
lines.push("");
|
|
673
|
-
for (const item of report.recommendation.risks.length ? report.recommendation.risks : ["No material report-level risks detected."]) lines.push(`- ${item}`);
|
|
674
|
-
lines.push("");
|
|
675
|
-
lines.push("### Next Actions");
|
|
676
|
-
lines.push("");
|
|
677
|
-
for (const item of report.recommendation.nextActions) lines.push(`- ${item}`);
|
|
678
|
-
lines.push("");
|
|
679
|
-
lines.push("## Candidate Decision Table");
|
|
680
|
-
lines.push("");
|
|
681
|
-
lines.push("| Candidate | Decision | Mean | Δ̄ | Pr(Δ>0) | q | dz | Paired N | Median Gain CI | MDE | Pareto | Gate |");
|
|
682
|
-
lines.push("|---|---|---:|---:|---:|---:|---:|---:|---|---:|---|---|");
|
|
683
|
-
for (const c of report.candidates) {
|
|
684
|
-
const delta = c.meanDeltaVsComparator === null ? "-" : signed(c.meanDeltaVsComparator);
|
|
685
|
-
const prGt = c.prGreaterThanZero === null ? "-" : c.prGreaterThanZero.toFixed(3);
|
|
686
|
-
const q = c.qValue !== null ? c.qValue.toFixed(4) : "-";
|
|
687
|
-
const d = c.cohensD !== null ? c.cohensD.toFixed(3) : "-";
|
|
688
|
-
const gain = c.gainCi ? `[${fmt(c.gainCi.low)}, ${fmt(c.gainCi.high)}]` : "-";
|
|
689
|
-
const mde = c.mde === null || !Number.isFinite(c.mde) ? "-" : fmt(c.mde);
|
|
690
|
-
lines.push(`| ${c.candidateId} | ${c.decision} | ${fmt(c.mean)} | ${delta} | ${prGt} | ${q} | ${d} | ${c.pairedN} | ${gain} | ${mde} | ${c.onParetoFrontier ? "yes" : "no"} | ${c.gate ?? "-"} |`);
|
|
691
|
-
}
|
|
692
|
-
lines.push("");
|
|
693
|
-
lines.push("## Statistical Summary");
|
|
694
|
-
lines.push("");
|
|
695
|
-
lines.push(report.summary.markdown);
|
|
696
|
-
lines.push("");
|
|
697
|
-
lines.push("## Methodology");
|
|
698
|
-
lines.push("");
|
|
699
|
-
lines.push("### Assumptions");
|
|
700
|
-
lines.push("");
|
|
701
|
-
for (const item of report.methodology.assumptions) lines.push(`- ${item}`);
|
|
702
|
-
lines.push("");
|
|
703
|
-
lines.push("### Methods");
|
|
704
|
-
lines.push("");
|
|
705
|
-
for (const item of report.methodology.methods) lines.push(`- ${item}`);
|
|
706
|
-
lines.push("");
|
|
707
|
-
lines.push("### Alternatives Considered");
|
|
708
|
-
lines.push("");
|
|
709
|
-
for (const item of report.methodology.alternatives) lines.push(`- ${item}`);
|
|
710
|
-
lines.push("");
|
|
711
|
-
lines.push("### When NOT To Apply");
|
|
712
|
-
lines.push("");
|
|
713
|
-
for (const item of report.methodology.whenNotToApply) lines.push(`- ${item}`);
|
|
714
|
-
lines.push("");
|
|
715
|
-
lines.push("### Citations");
|
|
716
|
-
lines.push("");
|
|
717
|
-
for (const item of report.methodology.citations) lines.push(`- ${item}`);
|
|
718
|
-
lines.push("");
|
|
719
|
-
lines.push("## Chart Specs");
|
|
720
|
-
lines.push("");
|
|
721
|
-
lines.push("The report carries JSON chart specs for Pareto cost/quality and paired gain histograms.");
|
|
722
|
-
lines.push("");
|
|
723
|
-
lines.push("```json");
|
|
724
|
-
lines.push(JSON.stringify({
|
|
725
|
-
pareto: report.pareto,
|
|
726
|
-
gains: report.gains
|
|
727
|
-
}, null, 2));
|
|
728
|
-
lines.push("```");
|
|
729
|
-
if (report.failureClusters) {
|
|
730
|
-
lines.push("");
|
|
731
|
-
lines.push("## Failure Clusters");
|
|
732
|
-
lines.push("");
|
|
733
|
-
lines.push("| Failure Class | Runs | Scenarios | Tool | Example |");
|
|
734
|
-
lines.push("|---|---:|---:|---|---|");
|
|
735
|
-
for (const c of report.failureClusters.clusters.slice(0, 10)) lines.push(`| ${c.failureClass} | ${c.runCount} | ${c.scenarioIds.length} | ${c.toolName ?? "-"} | ${escapePipes(c.exampleError ?? c.exampleRunId)} |`);
|
|
736
|
-
}
|
|
737
|
-
return lines.join("\n");
|
|
738
|
-
}
|
|
739
|
-
function renderResearchHtml(markdown, title) {
|
|
740
|
-
const body = markdownToHtml(markdown);
|
|
741
|
-
return [
|
|
742
|
-
"<!doctype html>",
|
|
743
|
-
"<html lang=\"en\">",
|
|
744
|
-
"<head>",
|
|
745
|
-
"<meta charset=\"utf-8\">",
|
|
746
|
-
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
|
|
747
|
-
`<title>${escapeHtml(title)}</title>`,
|
|
748
|
-
"<style>",
|
|
749
|
-
"body{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;margin:0;color:#172026;background:#f7f8f8;}",
|
|
750
|
-
"main{max-width:1080px;margin:0 auto;padding:40px 24px 64px;background:#fff;min-height:100vh;}",
|
|
751
|
-
"h1{font-size:34px;line-height:1.15;margin:0 0 20px;}h2{margin-top:34px;border-top:1px solid #d9dfdf;padding-top:22px;}h3{margin-top:22px;}",
|
|
752
|
-
"p,li{line-height:1.55;}table{border-collapse:collapse;width:100%;margin:16px 0;font-size:14px;}th,td{border:1px solid #d9dfdf;padding:8px;text-align:left;}th{background:#eef2f2;}",
|
|
753
|
-
"code,pre{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}pre{overflow:auto;background:#111827;color:#f9fafb;padding:16px;border-radius:6px;}",
|
|
754
|
-
"</style>",
|
|
755
|
-
"</head>",
|
|
756
|
-
"<body><main>",
|
|
757
|
-
body,
|
|
758
|
-
"</main></body></html>"
|
|
759
|
-
].join("\n");
|
|
760
|
-
}
|
|
761
|
-
function markdownToHtml(markdown) {
|
|
762
|
-
const lines = markdown.split("\n");
|
|
763
|
-
const html = [];
|
|
764
|
-
let inList = false;
|
|
765
|
-
let inCode = false;
|
|
766
|
-
let code = [];
|
|
767
|
-
let table = [];
|
|
768
|
-
const flushList = () => {
|
|
769
|
-
if (inList) {
|
|
770
|
-
html.push("</ul>");
|
|
771
|
-
inList = false;
|
|
772
|
-
}
|
|
773
|
-
};
|
|
774
|
-
const flushTable = () => {
|
|
775
|
-
if (table.length === 0) return;
|
|
776
|
-
html.push(renderMarkdownTable(table));
|
|
777
|
-
table = [];
|
|
778
|
-
};
|
|
779
|
-
for (const line of lines) {
|
|
780
|
-
if (line.startsWith("```")) {
|
|
781
|
-
if (inCode) {
|
|
782
|
-
html.push(`<pre><code>${escapeHtml(code.join("\n"))}</code></pre>`);
|
|
783
|
-
code = [];
|
|
784
|
-
inCode = false;
|
|
785
|
-
} else {
|
|
786
|
-
flushList();
|
|
787
|
-
flushTable();
|
|
788
|
-
inCode = true;
|
|
789
|
-
}
|
|
790
|
-
continue;
|
|
791
|
-
}
|
|
792
|
-
if (inCode) {
|
|
793
|
-
code.push(line);
|
|
794
|
-
continue;
|
|
795
|
-
}
|
|
796
|
-
if (line.startsWith("|")) {
|
|
797
|
-
flushList();
|
|
798
|
-
table.push(line);
|
|
799
|
-
continue;
|
|
800
|
-
}
|
|
801
|
-
flushTable();
|
|
802
|
-
if (line.startsWith("- ")) {
|
|
803
|
-
if (!inList) {
|
|
804
|
-
html.push("<ul>");
|
|
805
|
-
inList = true;
|
|
806
|
-
}
|
|
807
|
-
html.push(`<li>${inlineMarkdown(line.slice(2))}</li>`);
|
|
808
|
-
continue;
|
|
809
|
-
}
|
|
810
|
-
flushList();
|
|
811
|
-
if (line.startsWith("# ")) html.push(`<h1>${inlineMarkdown(line.slice(2))}</h1>`);
|
|
812
|
-
else if (line.startsWith("## ")) html.push(`<h2>${inlineMarkdown(line.slice(3))}</h2>`);
|
|
813
|
-
else if (line.startsWith("### ")) html.push(`<h3>${inlineMarkdown(line.slice(4))}</h3>`);
|
|
814
|
-
else if (line.trim() === "") html.push("");
|
|
815
|
-
else html.push(`<p>${inlineMarkdown(line)}</p>`);
|
|
816
|
-
}
|
|
817
|
-
flushList();
|
|
818
|
-
flushTable();
|
|
819
|
-
return html.join("\n");
|
|
820
|
-
}
|
|
821
|
-
function renderMarkdownTable(lines) {
|
|
822
|
-
const rows = lines.filter((line) => !/^\|[-:\s|]+\|$/.test(line)).map((line) => line.slice(1, -1).split("|").map((cell) => inlineMarkdown(cell.trim())));
|
|
823
|
-
if (rows.length === 0) return "";
|
|
824
|
-
const [head, ...body] = rows;
|
|
825
|
-
return `<table><thead><tr>${head.map((cell) => `<th>${cell}</th>`).join("")}</tr></thead><tbody>${body.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("\n")}</tbody></table>`;
|
|
826
|
-
}
|
|
827
|
-
function inlineMarkdown(s) {
|
|
828
|
-
return escapeHtml(s).replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
829
|
-
}
|
|
830
|
-
function escapeHtml(s) {
|
|
831
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
832
|
-
}
|
|
833
|
-
function escapePipes(s) {
|
|
834
|
-
return s.replace(/\|/g, "\\|");
|
|
835
|
-
}
|
|
836
|
-
function decisionWeight(decision) {
|
|
837
|
-
if (decision === "promote") return 5;
|
|
838
|
-
if (decision === "equivalent") return 4;
|
|
839
|
-
if (decision === "hold") return 3;
|
|
840
|
-
if (decision === "needs_more_data") return 2;
|
|
841
|
-
return 1;
|
|
842
|
-
}
|
|
843
|
-
function signed(x) {
|
|
844
|
-
return `${x >= 0 ? "+" : ""}${fmt(x)}`;
|
|
845
|
-
}
|
|
846
|
-
function avg(xs) {
|
|
847
|
-
if (xs.length === 0) return NaN;
|
|
848
|
-
return xs.reduce((s, x) => s + x, 0) / xs.length;
|
|
849
|
-
}
|
|
850
|
-
function medianOfSorted(sorted) {
|
|
851
|
-
if (sorted.length === 0) return 0;
|
|
852
|
-
const mid = Math.floor(sorted.length / 2);
|
|
853
|
-
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
854
|
-
}
|
|
855
|
-
function fmt(x) {
|
|
856
|
-
if (!Number.isFinite(x)) return String(x);
|
|
857
|
-
return x.toFixed(4);
|
|
858
|
-
}
|
|
859
849
|
//#endregion
|
|
860
|
-
export {
|
|
850
|
+
export { cliffsDelta as a, pairedCohensDz as c, summaryTable as i, RESEARCH_REPORT_HARD_PAIR_FLOOR as l, gainHistogram as n, cohensD as o, paretoChart as r, interpretCliffs as s, researchReport as t };
|
|
861
851
|
|
|
862
|
-
//# sourceMappingURL=summary-report-
|
|
852
|
+
//# sourceMappingURL=summary-report-BsXJoYX5.js.map
|