chainlesschain 0.166.64 → 0.166.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/README.md +29 -12
  2. package/package.json +4 -4
  3. package/src/assets/web-panel/assets/{AIOps-BFbLDWTF.js → AIOps-CXkSNOGY.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CAHMtgNB.js → ActionButton-ysMR6gTs.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CkrSj0oR.js → Analytics-WGZ0sWuw.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-BTb0Ws1l.js → AppLayout-Cs2iRlQG.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-C2Pgepj-.js → Artifacts-5IdfxWz0.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-ZweyxXW8.js → Audit-U9V434QX.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BS8tAnzP.js → BackgroundAgents-BvFwxPlC.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-CLkG_74M.js → Backup-C9yTr9hO.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-MIfNcdUZ.js → BaseInput-BdsSwUgE.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-DBwqlWWr.js → Chat-BB_2oBgH.js} +5 -5
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-Bpf1hDUl.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-DdBYhlVX.js → Checkbox-v6M5m7dK.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-BR6MhiDo.js → Codegen-CtouWSIz.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-yO-5EbbM.js → Col-CqQJ4ORW.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-B608q_m7.js → Community-BO__QCOy.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-BmVHi7Z7.js → Compact-D0Kq4xlH.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-BQ-DzutA.js → Compliance-BSLKKBjA.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-BEfLyXvi.js → Cowork-VsujXW5o.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-Cld6NX-_.js → Cron-j8DbLGi5.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CUA3nDId.js → Crosschain-DWpYH1yN.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-DSI3E9zv.js → DID-mLaRxLQh.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-CoWtEdhO.js → Dashboard-Cff033Jr.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-CGhZ1Dyy.js → Dropdown-B1Upt6fP.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BHZe2K5q.js → EmailListRenderer-CPtb8joV.js} +1 -1
  27. package/src/assets/web-panel/assets/{EvolutionSettings-Corjo98k.js → EvolutionSettings-CVh4Uv4g.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-dPSWF3Pt.js → FamilyGuardDashboard-CrD1PhbF.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-DT-Zr78c.js → Federation-ftxrvqW5.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-CzKq63B0.js → FormItemContext-9O80slC1.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-CDkKrmu0.js → GenericCardRenderer-DaYvYIj9.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-BrwRnLOv.js → Git-BKZh0YE5.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-C4PtCzod.js → Governance-vWbN873j.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-CVPv5nwa.js → Inference-BN6RR-l8.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-CmT9lvED.js → KnowledgeGraph-x49x9m7Q.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-DaRgv7pL.js → Logs-Co2EPLsJ.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-B3zZwlSz.js → MarkdownRenderer-D7wIB-c7.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-Bm0GGEaL.js → Marketplace-DpPu6XQp.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-71L5h0en.js → McpTools-BR0ksiax.js} +3 -3
  40. package/src/assets/web-panel/assets/{Memory-BoWrfASi.js → Memory-BXznJ5-C.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-Dp4FcX_-.js → MobileBridge-BHSOpfv7.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-00ja0Z7O.js → MobileProjects-DdWmgIup.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-BZF6YNYp.js → Mtc-D9Rk_IGT.js} +4 -4
  44. package/src/assets/web-panel/assets/{MtcAudit-Bp_vcI6H.js → MtcAudit--i8q1lKc.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-CnCo56CS.js → Multisig-BHnNG7R9.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-BCSzNHpv.js → NLProgramming-Duo1ij2_.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-kXoQk8B7.js → Notes-B44Le73p.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-oN-1fkcy.js → NotificationSettings-C3oy_7Gf.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-5ZVHrVZ9.js → OrderTableRenderer-Da_73SI5.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-D4_T9Tdc.js → Organization-ChsSwKxT.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-d8vZwo9w.js → Overflow-DR5G_cCn.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-0HGUdmTv.js → P2P-yfaobqow.js} +2 -2
  53. package/src/assets/web-panel/assets/PdhVaultBrowser-DgimH6Jp.js +7 -0
  54. package/src/assets/web-panel/assets/{Permissions-XHd6pVQB.js → Permissions-DhinFvtE.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-CB5bZU9T.js → PersonalDataHub-CfeNVic1.js} +3 -3
  56. package/src/assets/web-panel/assets/{Pipeline-Dy_qx4-0.js → Pipeline-CGKO26Nk.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-BgR0-uEu.js → Privacy-CxxfYEJh.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-DAcRi1Nx.js → ProjectInit-CZsaAab9.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-Bd1Q0rK_.js → ProjectSettings-BUm_6clQ.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-DVc9DcbM.js → Projects-Capr8bVq.js} +1 -1
  61. package/src/assets/web-panel/assets/{Providers-C3c4QfUT.js → Providers-D28VqPja.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-Ba_GbitW.js → QrScannerModal-Cj200vUb.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-CSn5tIkO.js → QuickAsk-BGpnEiam.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-B3-8PLX0.js → Recommend-Ix79Tpni.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-Bd_7Zmi-.js → RemoteSession-BdGAFbIk.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-BU2A_wy5.js → Reputation-B1Lis0Wk.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-I0JFMjCz.js → Row-Ba_okwOW.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-CNc4Dgfz.js → RssFeed-DDbEuI-m.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-uAA6ceLl.js → Search-D89wAkMy.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-U1SWj5nP.js → Security-CJKCKAU-.js} +3 -3
  71. package/src/assets/web-panel/assets/{Services-BFjZ5ASY.js → Services-mitEc1XV.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-B8InBvQf.js → Skeleton-CnBjLDfX.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-BDYwf3QV.js → Skills-DSzDYvH3.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-Dcc6iq3q.js → Sla-CglJ_4p3.js} +1 -1
  75. package/src/assets/web-panel/assets/SpeechSettings-BicWJS7f.js +1 -0
  76. package/src/assets/web-panel/assets/{SyncSettings-BNHEYO95.js → SyncSettings-Cq7o6fmr.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-JbV6L1FX.js → Tasks-DOxFOBpU.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-Yp0CUTBz.js → Templates-bY0wSbQM.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-CToNYxCo.js → Tenant-BAyyl0qX.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-D7W9UqSD.js → Terminal-BGBhSpqc.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-B7rqc1kE.js → TimelineRenderer-BvW5lXYz.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-QP7K4ppA.js → Tokens-Ua_LIx8-.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-CAWowQCR.js → Trigger-BGwb2KL1.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-B-0XOP97.js → Trust-CD2IEHvj.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-BtiZbiuj.js → UkeySign-D5LetK6P.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-Dee11JI0.js → VideoEditing-oP7ehFgR.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-y7qOmaWY.js → Wallet-BBtu4-xa.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-CjyRRWGK.js → WebAuthn-iSPRU4Ht.js} +3 -3
  89. package/src/assets/web-panel/assets/{WorkflowEditor-D7Kiz25Q.js → WorkflowEditor-DuEDhQmB.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-4F_yB-Gw.js → chat-DGDgqvvm.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-CrczPd-t.js → colors-DGQMWa1-.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-AenPL6fz.js → compact-item-COVznBL4.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-BcwoqPgi.js → createContext-DIxVeHXI.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-CjR9h-Z_.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-YouHmap8.js → hasIn-DkxZO3E9.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-fCuM1NPH.js → index-0ToBFsvW.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D0dXNgvi.js → index-2JZizEPc.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BHA5Fyjc.js → index-BAL2p7YC.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CedvtJA_.js → index-BAlrovUm.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-5GHNjmnt.js → index-BBw2MLIe.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-C5hpEYbx.js → index-BHH95jcm.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-D8a1AEVK.js → index-BOTshZNx.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-D7m5Z4We.js → index-BQm7ntcn.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-D_4VsL-L.js → index-BUqwlzc8.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-GP5u18DV.js → index-BY6MSbWT.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CHNKoexH.js → index-BY9Ossof.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DNHmbdGm.js → index-BgM-3a1w.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-YX4dcbgh.js → index-Bv0_u0ze.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-D5Rvq67L.js → index-C3XeNS82.js} +3 -3
  110. package/src/assets/web-panel/assets/{index-DAGfuEUu.js → index-C3bN6751.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CZT-LLGS.js → index-CNRRrP3-.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-4r7B8sTx.js → index-CV0KnNqR.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-B5Vmr0CM.js → index-CcNGQWum.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DE3qPp9l.js → index-Ci34vbh0.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CWgIKCVS.js → index-ClQVcrFt.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-31dhJpt3.js → index-Cligb-Dl.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BsGhFk79.js → index-CokrhbY2.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-B_I1INkw.js → index-CpZxDiVK.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BaguGDJP.js → index-CyMHF2Bi.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DPrzFdCg.js → index-DDwPBGdA.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-gy1H_PJk.js → index-DU1ArKl5.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DAh5KwVL.js → index-DV0H83eS.js} +1 -1
  123. package/src/assets/web-panel/assets/index-D_nxBMX5.js +1 -0
  124. package/src/assets/web-panel/assets/{index-q_9aqSgF.js → index-DaWQSp5a.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-B8EXHVmM.js → index-DmduvOYT.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DVrgnhfu.js → index-ECoDhyH3.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BwUqDg9_.js → index-ZcqTvPnt.js} +1 -1
  128. package/src/assets/web-panel/assets/{index--xwN0ZvG.js → index-b2_M6o20.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CZ9Ikuo6.js → index-b_0qTo8O.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BoVeUGhC.js → index-dfYkGovV.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-COgeZETv.js → index-jo7DbU0w.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-Dfw3WyZU.js → index-usmG13jq.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BzyDSW_e.js → index-vXrktF3e.js} +1 -1
  134. package/src/assets/web-panel/assets/index-z_cTUMX-.js +1 -0
  135. package/src/assets/web-panel/assets/{initDefaultProps-Ci6zc6ZA.js → initDefaultProps-CkSeiBwL.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-DS3kjIxw.js → motion-CSzhDP0s.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DCdWAKk4.js → move-mgCt4iC6.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-B0Wfce9U.js → mtc-parser-BEZBLcKt.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-Clx9TR6z.js → omit-BsHctMEr.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-CqSiqUvN.js → pickAttrs-1BHamnGW.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-BWe_v-40.js → placementArrow--Vb4Bd7l.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-BSJ68bw6.js → responsiveObserve-DD33SqCm.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-VRPlLxZB.js → slide-DfrLtxgK.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-BFBVDPdB.js → statusUtils-D9Pe_gXW.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-CehOXy1f.js → styleChecker-0NY5iMun.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-BCOdfK18.js → useFlexGapSupport-DSsZstwg.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-BMKrjfJQ.js → useFs-BU8HyH59.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-BIjZ7IgI.js → usePersonalDataHub-D_xttink.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-oD0MXddQ.js → vnode-39VhX2lM.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-D7WqH2Ai.js → zoom-D5KQ8Pks.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/data/changelog.json +33 -7
  153. package/src/lib/dependency-install-policy.js +1 -1
  154. package/src/lib/evolution/browser-download-action-authority.js +747 -0
  155. package/src/lib/evolution/browser-download-artifact-disposal-authority.js +436 -0
  156. package/src/lib/evolution/browser-filesystem-quarantine-custody.js +2453 -0
  157. package/src/lib/evolution/browser-filesystem-quarantine-disposal.js +36 -0
  158. package/src/lib/evolution/browser-keyboard-action-authority.js +560 -0
  159. package/src/lib/evolution/browser-navigation-action-authority.js +546 -0
  160. package/src/lib/evolution/browser-quarantine-lock-maintenance-authority.js +573 -0
  161. package/src/lib/evolution/browser-quarantine-operator-revocation-authority.js +442 -0
  162. package/src/lib/evolution/browser-quarantine-retention-authority.js +503 -0
  163. package/src/lib/evolution/browser-quarantine-retention-scheduler.js +207 -0
  164. package/src/lib/evolution/browser-quarantined-download-executor.js +525 -0
  165. package/src/lib/evolution/browser-tab-open-action-authority.js +491 -0
  166. package/src/lib/evolution/browser-vision-action-authority.js +444 -0
  167. package/src/lib/evolution/browser-vision-observation-authority.js +304 -0
  168. package/src/lib/evolution/evolution-deployment-loader.js +202 -1
  169. package/src/lib/evolution/evolution-eval-child-evidence-ledger-adapter.js +5 -0
  170. package/src/lib/evolution/evolution-eval-gate.js +2 -1
  171. package/src/lib/evolution/evolution-eval-process-supervisor-worker.mjs +71 -9
  172. package/src/lib/evolution/evolution-eval-process-supervisor.js +276 -9
  173. package/src/lib/evolution/evolution-workbench-metrics.js +81 -21
  174. package/src/lib/evolution/pm-exploration-benchmark.js +747 -0
  175. package/src/lib/evolution/pm-exploration-budget-executor.js +106 -9
  176. package/src/lib/evolution/pm-exploration-egress-authority.js +382 -0
  177. package/src/lib/evolution/pm-exploration-evidence-bundle.js +149 -44
  178. package/src/lib/evolution/pm-exploration-execution-host.js +1254 -8
  179. package/src/lib/evolution/pm-exploration-process-grader.js +458 -0
  180. package/src/lib/evolution/pm-exploration-process-reviewer.js +456 -0
  181. package/src/lib/evolution/pm-exploration-process-runner.js +500 -0
  182. package/src/lib/evolution/pm-exploration-receipts.js +211 -11
  183. package/src/lib/evolution/pm-exploration-recovery-snapshot-store.js +465 -46
  184. package/src/lib/evolution/pm-exploration-transition-committer.js +185 -34
  185. package/src/lib/evolution/skill-outcome-index-authority.js +7 -3
  186. package/src/lib/governance-coverage.js +1 -1
  187. package/src/lib/headless-manifest.js +1 -1
  188. package/src/lib/protocol-replay.js +1 -1
  189. package/src/lib/read-file-loop-guard.js +5 -8
  190. package/src/lib/remote-read-loop-guard.js +25 -18
  191. package/src/lib/skill-outcome-transcript-authority.js +45 -5
  192. package/src/lib/tool-recovery-result.js +11 -0
  193. package/src/runtime/agent-core.js +26 -3
  194. package/src/assets/web-panel/assets/ChatBubbleRenderer-6T0uoJuS.js +0 -1
  195. package/src/assets/web-panel/assets/PdhVaultBrowser-Cx8vp92z.js +0 -7
  196. package/src/assets/web-panel/assets/SpeechSettings-C3Ta-mC9.js +0 -1
  197. package/src/assets/web-panel/assets/devWarning-C4qWsAvh.js +0 -1
  198. package/src/assets/web-panel/assets/index-C-ltOW-O.js +0 -1
  199. package/src/assets/web-panel/assets/index-D7Ir0TiS.js +0 -1
@@ -4,6 +4,7 @@ import { isProxy } from "node:util/types";
4
4
  import {
5
5
  buildEvolutionEvalSuite,
6
6
  computeEvolutionEvalTrainingPartitionDigest,
7
+ verifyEvolutionEvalPolicy,
7
8
  verifyEvolutionEvalSuite,
8
9
  } from "./evolution-eval-gate.js";
9
10
  import { createPmExplorationPlan } from "./pm-exploration-rounds.js";
@@ -23,6 +24,30 @@ const REQUIRED_ENV = Object.freeze({
23
24
  CC_IPC_RBAC_GUARD: "enforce",
24
25
  });
25
26
  const PM_GRADER = "pm-objective-outcome-v1";
27
+ const DIGEST = /^sha256:[a-f0-9]{64}$/u;
28
+ const EFFECT_PHASES = Object.freeze([
29
+ "exploration",
30
+ "curriculum-planning",
31
+ "memory-distillation",
32
+ "failure-retry",
33
+ "environment-reset",
34
+ ]);
35
+ const FAILURE_CLASSES = new Set([
36
+ "none",
37
+ "model",
38
+ "tool",
39
+ "provider",
40
+ "sandbox",
41
+ "permission",
42
+ "infrastructure",
43
+ "grader",
44
+ "unknown",
45
+ ]);
46
+
47
+ export const PM_EXPLORATION_EFFECT_PLAN_SCHEMA =
48
+ "chainlesschain.pm-exploration-effect-plan/v1";
49
+ export const PM_EXPLORATION_EFFECT_REPORT_SCHEMA =
50
+ "chainlesschain.pm-exploration-effect-report/v1";
26
51
 
27
52
  function exact(value, keys, label) {
28
53
  if (
@@ -58,6 +83,728 @@ function groupKey(kind, value) {
58
83
  return `${kind}-${createHash("sha256").update(value).digest("hex")}`;
59
84
  }
60
85
 
86
+ function canonical(value) {
87
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
88
+ if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
89
+ return `{${Object.keys(value)
90
+ .sort()
91
+ .map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
92
+ .join(",")}}`;
93
+ }
94
+
95
+ function assertPlainData(value, label, seen = new WeakSet(), depth = 0) {
96
+ if (
97
+ value === null ||
98
+ typeof value === "string" ||
99
+ typeof value === "boolean"
100
+ ) {
101
+ return;
102
+ }
103
+ if (typeof value === "number") {
104
+ if (!Number.isFinite(value) || Object.is(value, -0)) {
105
+ throw new TypeError(`${label} contains an invalid number`);
106
+ }
107
+ return;
108
+ }
109
+ if (!value || typeof value !== "object" || isProxy(value) || depth > 64) {
110
+ throw new TypeError(`${label} is not bounded plain data`);
111
+ }
112
+ if (seen.has(value)) throw new TypeError(`${label} contains a cycle`);
113
+ seen.add(value);
114
+ if (Array.isArray(value)) {
115
+ if (
116
+ Object.getPrototypeOf(value) !== Array.prototype ||
117
+ Reflect.ownKeys(value).length !== value.length + 1
118
+ ) {
119
+ throw new TypeError(`${label} contains an invalid array`);
120
+ }
121
+ } else if (Object.getPrototypeOf(value) !== Object.prototype) {
122
+ throw new TypeError(`${label} contains a non-plain object`);
123
+ }
124
+ for (const key of Reflect.ownKeys(value)) {
125
+ if (typeof key !== "string") {
126
+ throw new TypeError(`${label} contains a symbol field`);
127
+ }
128
+ if (Array.isArray(value) && key === "length") continue;
129
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
130
+ if (!descriptor?.enumerable || !("value" in descriptor)) {
131
+ throw new TypeError(`${label} contains an accessor or hidden field`);
132
+ }
133
+ assertPlainData(descriptor.value, label, seen, depth + 1);
134
+ }
135
+ seen.delete(value);
136
+ }
137
+
138
+ function hash(domain, value) {
139
+ return `sha256:${createHash("sha256")
140
+ .update(domain)
141
+ .update("\0")
142
+ .update(canonical(value))
143
+ .digest("hex")}`;
144
+ }
145
+
146
+ function boundedString(value, label) {
147
+ if (
148
+ typeof value !== "string" ||
149
+ value.trim() !== value ||
150
+ value.length < 1 ||
151
+ value.length > 256
152
+ ) {
153
+ throw new TypeError(`${label} must be a bounded nonempty string`);
154
+ }
155
+ return value;
156
+ }
157
+
158
+ function digest(value, label) {
159
+ if (typeof value !== "string" || !DIGEST.test(value)) {
160
+ throw new TypeError(`${label} must be a sha256 digest`);
161
+ }
162
+ return value;
163
+ }
164
+
165
+ function integer(value, label, maximum = Number.MAX_SAFE_INTEGER) {
166
+ if (!Number.isSafeInteger(value) || value < 0 || value > maximum) {
167
+ throw new TypeError(`${label} must be a non-negative safe integer`);
168
+ }
169
+ return value;
170
+ }
171
+
172
+ function finite(value, label, maximum = 1) {
173
+ if (!Number.isFinite(value) || value < 0 || value > maximum) {
174
+ throw new TypeError(`${label} is outside its allowed range`);
175
+ }
176
+ return value;
177
+ }
178
+
179
+ function deepFreeze(value, seen = new WeakSet()) {
180
+ if (!value || typeof value !== "object" || seen.has(value)) return value;
181
+ seen.add(value);
182
+ for (const key of Reflect.ownKeys(value)) {
183
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
184
+ if (descriptor && "value" in descriptor) deepFreeze(descriptor.value, seen);
185
+ }
186
+ return Object.freeze(value);
187
+ }
188
+
189
+ function normalizeVersion(value, label) {
190
+ exact(value, ["id", "artifactDigest"], label);
191
+ return Object.freeze({
192
+ id: boundedString(value.id, `${label} id`),
193
+ artifactDigest: digest(value.artifactDigest, `${label} artifactDigest`),
194
+ });
195
+ }
196
+
197
+ function normalizeEffectBudget(value) {
198
+ exact(
199
+ value,
200
+ ["maxTokens", "maxToolCalls", "maxWallClockMs", "maxCostMicrounits"],
201
+ "PM effect budget",
202
+ );
203
+ return Object.freeze(
204
+ Object.fromEntries(
205
+ Object.entries(value).map(([key, amount]) => {
206
+ const normalized = integer(amount, `PM effect budget ${key}`);
207
+ if (normalized < 1) {
208
+ throw new TypeError(`PM effect budget ${key} must be positive`);
209
+ }
210
+ return [key, normalized];
211
+ }),
212
+ ),
213
+ );
214
+ }
215
+
216
+ export function verifyPmExplorationEffectPlan(value) {
217
+ exact(
218
+ value,
219
+ [
220
+ "schema",
221
+ "experimentId",
222
+ "suiteDigest",
223
+ "policyDigest",
224
+ "testTaskIds",
225
+ "baselineVersion",
226
+ "candidateVersion",
227
+ "actorConfigDigest",
228
+ "modelConfigDigest",
229
+ "toolPolicyDigest",
230
+ "permissionPolicyDigest",
231
+ "environmentDigest",
232
+ "resetProtocolDigest",
233
+ "seeds",
234
+ "budgetPerArmPerSeed",
235
+ "costPhases",
236
+ "minimumScoreDelta",
237
+ "bootstrapSamples",
238
+ "equalBudget",
239
+ "promotionAuthority",
240
+ "planDigest",
241
+ ],
242
+ "PM effect plan",
243
+ );
244
+ if (
245
+ value.schema !== PM_EXPLORATION_EFFECT_PLAN_SCHEMA ||
246
+ value.bootstrapSamples !== 1_000 ||
247
+ value.equalBudget !== true ||
248
+ value.promotionAuthority !== false
249
+ ) {
250
+ throw new TypeError("PM effect plan invariants are invalid");
251
+ }
252
+ const core = {
253
+ schema: value.schema,
254
+ experimentId: boundedString(value.experimentId, "experimentId"),
255
+ suiteDigest: digest(value.suiteDigest, "suiteDigest"),
256
+ policyDigest: digest(value.policyDigest, "policyDigest"),
257
+ testTaskIds: normalizeStringArray(value.testTaskIds, "testTaskIds"),
258
+ baselineVersion: normalizeVersion(value.baselineVersion, "baselineVersion"),
259
+ candidateVersion: normalizeVersion(
260
+ value.candidateVersion,
261
+ "candidateVersion",
262
+ ),
263
+ actorConfigDigest: digest(value.actorConfigDigest, "actorConfigDigest"),
264
+ modelConfigDigest: digest(value.modelConfigDigest, "modelConfigDigest"),
265
+ toolPolicyDigest: digest(value.toolPolicyDigest, "toolPolicyDigest"),
266
+ permissionPolicyDigest: digest(
267
+ value.permissionPolicyDigest,
268
+ "permissionPolicyDigest",
269
+ ),
270
+ environmentDigest: digest(value.environmentDigest, "environmentDigest"),
271
+ resetProtocolDigest: digest(
272
+ value.resetProtocolDigest,
273
+ "resetProtocolDigest",
274
+ ),
275
+ seeds: normalizeSeeds(value.seeds),
276
+ budgetPerArmPerSeed: normalizeEffectBudget(value.budgetPerArmPerSeed),
277
+ costPhases: normalizeStringArray(value.costPhases, "costPhases"),
278
+ minimumScoreDelta: finite(value.minimumScoreDelta, "minimumScoreDelta"),
279
+ bootstrapSamples: 1_000,
280
+ equalBudget: true,
281
+ promotionAuthority: false,
282
+ };
283
+ if (canonical(core.costPhases) !== canonical(EFFECT_PHASES)) {
284
+ throw new TypeError("PM effect plan cost phases are invalid");
285
+ }
286
+ const planDigest = digest(value.planDigest, "planDigest");
287
+ if (planDigest !== hash(PM_EXPLORATION_EFFECT_PLAN_SCHEMA, core)) {
288
+ throw new Error("PM effect plan digest mismatch");
289
+ }
290
+ return deepFreeze({ ...core, planDigest });
291
+ }
292
+
293
+ function normalizeStringArray(value, label) {
294
+ if (
295
+ !Array.isArray(value) ||
296
+ isProxy(value) ||
297
+ Object.getPrototypeOf(value) !== Array.prototype ||
298
+ value.length < 1 ||
299
+ value.length > 10_000 ||
300
+ Reflect.ownKeys(value).length !== value.length + 1
301
+ ) {
302
+ throw new TypeError(`${label} must be a bounded dense array`);
303
+ }
304
+ const normalized = value.map((item, index) =>
305
+ boundedString(item, `${label}[${index}]`),
306
+ );
307
+ if (new Set(normalized).size !== normalized.length) {
308
+ throw new TypeError(`${label} must contain unique values`);
309
+ }
310
+ return Object.freeze(normalized);
311
+ }
312
+
313
+ function normalizeSeeds(value) {
314
+ if (
315
+ !Array.isArray(value) ||
316
+ isProxy(value) ||
317
+ Object.getPrototypeOf(value) !== Array.prototype ||
318
+ value.length < 3 ||
319
+ value.length > 32 ||
320
+ Reflect.ownKeys(value).length !== value.length + 1
321
+ ) {
322
+ throw new TypeError("PM effect seeds must contain 3-32 dense entries");
323
+ }
324
+ const seeds = value.map((seed, index) =>
325
+ integer(seed, `PM effect seed ${index}`, 0x7fffffff),
326
+ );
327
+ if (new Set(seeds).size !== seeds.length) {
328
+ throw new TypeError("PM effect seeds must be unique");
329
+ }
330
+ return Object.freeze(seeds);
331
+ }
332
+
333
+ /**
334
+ * Freezes the PM comparison before execution. Both arms receive the same hard
335
+ * per-seed budget and differ only by the two declared immutable artifacts.
336
+ */
337
+ export function buildPmExplorationEffectPlan(input) {
338
+ exact(
339
+ input,
340
+ [
341
+ "experimentId",
342
+ "suite",
343
+ "policy",
344
+ "baselineVersion",
345
+ "candidateVersion",
346
+ "actorConfigDigest",
347
+ "modelConfigDigest",
348
+ "toolPolicyDigest",
349
+ "permissionPolicyDigest",
350
+ "environmentDigest",
351
+ "resetProtocolDigest",
352
+ "seeds",
353
+ "budgetPerArmPerSeed",
354
+ "minimumScoreDelta",
355
+ ],
356
+ "PM effect plan input",
357
+ );
358
+ const suite = verifyPmSuite(input.suite);
359
+ const policy = verifyEvolutionEvalPolicy(input.policy);
360
+ const seeds = normalizeSeeds(input.seeds);
361
+ const testTaskIds = suite.tasks
362
+ .filter((task) => task.split === "test")
363
+ .map((task) => task.id);
364
+ if (canonical(seeds) !== canonical(policy.seeds)) {
365
+ throw new Error("PM effect seeds must equal the Eval Gate policy seeds");
366
+ }
367
+ const core = {
368
+ schema: PM_EXPLORATION_EFFECT_PLAN_SCHEMA,
369
+ experimentId: boundedString(input.experimentId, "experimentId"),
370
+ suiteDigest: suite.suiteDigest,
371
+ policyDigest: policy.policyDigest,
372
+ testTaskIds: Object.freeze(testTaskIds),
373
+ baselineVersion: normalizeVersion(input.baselineVersion, "baselineVersion"),
374
+ candidateVersion: normalizeVersion(
375
+ input.candidateVersion,
376
+ "candidateVersion",
377
+ ),
378
+ actorConfigDigest: digest(input.actorConfigDigest, "actorConfigDigest"),
379
+ modelConfigDigest: digest(input.modelConfigDigest, "modelConfigDigest"),
380
+ toolPolicyDigest: digest(input.toolPolicyDigest, "toolPolicyDigest"),
381
+ permissionPolicyDigest: digest(
382
+ input.permissionPolicyDigest,
383
+ "permissionPolicyDigest",
384
+ ),
385
+ environmentDigest: digest(input.environmentDigest, "environmentDigest"),
386
+ resetProtocolDigest: digest(
387
+ input.resetProtocolDigest,
388
+ "resetProtocolDigest",
389
+ ),
390
+ seeds,
391
+ budgetPerArmPerSeed: normalizeEffectBudget(input.budgetPerArmPerSeed),
392
+ costPhases: EFFECT_PHASES,
393
+ minimumScoreDelta: finite(input.minimumScoreDelta, "minimumScoreDelta"),
394
+ bootstrapSamples: 1_000,
395
+ equalBudget: true,
396
+ promotionAuthority: false,
397
+ };
398
+ return deepFreeze({
399
+ ...core,
400
+ planDigest: hash(PM_EXPLORATION_EFFECT_PLAN_SCHEMA, core),
401
+ });
402
+ }
403
+
404
+ function normalizeUsage(value, label) {
405
+ exact(
406
+ value,
407
+ ["receiptDigest", "tokens", "toolCalls", "wallClockMs", "costMicrounits"],
408
+ label,
409
+ );
410
+ const metrics = {
411
+ tokens: integer(value.tokens, `${label} tokens`),
412
+ toolCalls: integer(value.toolCalls, `${label} toolCalls`),
413
+ wallClockMs: integer(value.wallClockMs, `${label} wallClockMs`),
414
+ costMicrounits: integer(value.costMicrounits, `${label} costMicrounits`),
415
+ };
416
+ const used = Object.values(metrics).some((amount) => amount > 0);
417
+ if (
418
+ (used && !DIGEST.test(value.receiptDigest ?? "")) ||
419
+ (!used && value.receiptDigest !== null)
420
+ ) {
421
+ throw new TypeError(`${label} receipt binding is invalid`);
422
+ }
423
+ return Object.freeze({ receiptDigest: value.receiptDigest, ...metrics });
424
+ }
425
+
426
+ function normalizePhases(value, label) {
427
+ if (
428
+ !Array.isArray(value) ||
429
+ isProxy(value) ||
430
+ Object.getPrototypeOf(value) !== Array.prototype ||
431
+ value.length !== EFFECT_PHASES.length ||
432
+ Reflect.ownKeys(value).length !== value.length + 1
433
+ ) {
434
+ throw new TypeError(`${label} must cover every cost phase`);
435
+ }
436
+ return Object.freeze(
437
+ value.map((entry, index) => {
438
+ exact(entry, ["phase", "usage"], `${label}[${index}]`);
439
+ if (entry.phase !== EFFECT_PHASES[index]) {
440
+ throw new TypeError(`${label} phase order is invalid`);
441
+ }
442
+ return Object.freeze({
443
+ phase: entry.phase,
444
+ usage: normalizeUsage(entry.usage, `${label} ${entry.phase}`),
445
+ });
446
+ }),
447
+ );
448
+ }
449
+
450
+ function normalizeArm(value, label) {
451
+ exact(
452
+ value,
453
+ [
454
+ "outcomeReceiptDigest",
455
+ "graderReceiptDigest",
456
+ "score",
457
+ "passed",
458
+ "usage",
459
+ "securityViolations",
460
+ "permissionViolations",
461
+ "failureClass",
462
+ ],
463
+ label,
464
+ );
465
+ if (
466
+ typeof value.passed !== "boolean" ||
467
+ !FAILURE_CLASSES.has(value.failureClass)
468
+ ) {
469
+ throw new TypeError(`${label} result fields are invalid`);
470
+ }
471
+ return Object.freeze({
472
+ outcomeReceiptDigest: digest(
473
+ value.outcomeReceiptDigest,
474
+ `${label} outcomeReceiptDigest`,
475
+ ),
476
+ graderReceiptDigest: digest(
477
+ value.graderReceiptDigest,
478
+ `${label} graderReceiptDigest`,
479
+ ),
480
+ score: finite(value.score, `${label} score`),
481
+ passed: value.passed,
482
+ usage: normalizeUsage(value.usage, `${label} usage`),
483
+ securityViolations: integer(
484
+ value.securityViolations,
485
+ `${label} securityViolations`,
486
+ ),
487
+ permissionViolations: integer(
488
+ value.permissionViolations,
489
+ `${label} permissionViolations`,
490
+ ),
491
+ failureClass: value.failureClass,
492
+ });
493
+ }
494
+
495
+ function addUsage(target, usage) {
496
+ target.tokens += usage.tokens;
497
+ target.toolCalls += usage.toolCalls;
498
+ target.wallClockMs += usage.wallClockMs;
499
+ target.costMicrounits += usage.costMicrounits;
500
+ }
501
+
502
+ function emptyUsage() {
503
+ return { tokens: 0, toolCalls: 0, wallClockMs: 0, costMicrounits: 0 };
504
+ }
505
+
506
+ function exceedsBudget(usage, budget) {
507
+ return (
508
+ usage.tokens > budget.maxTokens ||
509
+ usage.toolCalls > budget.maxToolCalls ||
510
+ usage.wallClockMs > budget.maxWallClockMs ||
511
+ usage.costMicrounits > budget.maxCostMicrounits
512
+ );
513
+ }
514
+
515
+ function mean(values) {
516
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
517
+ }
518
+
519
+ function percentile(sorted, probability) {
520
+ if (sorted.length === 1) return sorted[0];
521
+ const position = (sorted.length - 1) * probability;
522
+ const lower = Math.floor(position);
523
+ const upper = Math.ceil(position);
524
+ const weight = position - lower;
525
+ return sorted[lower] * (1 - weight) + sorted[upper] * weight;
526
+ }
527
+
528
+ function prngFromDigest(value) {
529
+ let state = Number.parseInt(value.slice(7, 15), 16) || 0x9e3779b9;
530
+ return () => {
531
+ state ^= state << 13;
532
+ state ^= state >>> 17;
533
+ state ^= state << 5;
534
+ return (state >>> 0) / 0x1_0000_0000;
535
+ };
536
+ }
537
+
538
+ function armTotals(runs, arm) {
539
+ const usage = emptyUsage();
540
+ let securityViolations = 0;
541
+ let permissionViolations = 0;
542
+ let passCount = 0;
543
+ const scores = [];
544
+ for (const run of runs) {
545
+ for (const phase of run.phases[arm]) addUsage(usage, phase.usage);
546
+ for (const item of run.cases) {
547
+ const result = item[arm];
548
+ addUsage(usage, result.usage);
549
+ securityViolations += result.securityViolations;
550
+ permissionViolations += result.permissionViolations;
551
+ passCount += result.passed ? 1 : 0;
552
+ scores.push(result.score);
553
+ }
554
+ }
555
+ return Object.freeze({
556
+ sampleCount: scores.length,
557
+ passCount,
558
+ passRate: passCount / scores.length,
559
+ meanScore: mean(scores),
560
+ usage: Object.freeze(usage),
561
+ securityViolations,
562
+ permissionViolations,
563
+ });
564
+ }
565
+
566
+ /**
567
+ * Recomputes a complete paired PM effect report. It is deliberately not a
568
+ * promotion receipt; an independent signed Eval Gate/Pilot decision remains
569
+ * mandatory even when every preregistered threshold is met.
570
+ */
571
+ export function buildPmExplorationEffectReport({ plan, runs } = {}) {
572
+ const verifiedPlan = verifyPmExplorationEffectPlan(plan);
573
+ if (
574
+ !Array.isArray(runs) ||
575
+ isProxy(runs) ||
576
+ Object.getPrototypeOf(runs) !== Array.prototype ||
577
+ runs.length !== verifiedPlan.seeds.length ||
578
+ Reflect.ownKeys(runs).length !== runs.length + 1
579
+ ) {
580
+ throw new TypeError("PM effect runs must exactly cover the seed schedule");
581
+ }
582
+ const expectedTasks = new Set(verifiedPlan.testTaskIds);
583
+ const seenSeeds = new Set();
584
+ const normalizedRuns = runs.map((run, runIndex) => {
585
+ exact(
586
+ run,
587
+ ["runId", "seed", "phases", "cases"],
588
+ `PM effect run ${runIndex}`,
589
+ );
590
+ const seed = integer(
591
+ run.seed,
592
+ `PM effect run ${runIndex} seed`,
593
+ 0x7fffffff,
594
+ );
595
+ if (!verifiedPlan.seeds.includes(seed) || seenSeeds.has(seed)) {
596
+ throw new TypeError(
597
+ `PM effect run ${runIndex} seed is absent or duplicated`,
598
+ );
599
+ }
600
+ seenSeeds.add(seed);
601
+ exact(
602
+ run.phases,
603
+ ["baseline", "candidate"],
604
+ `PM effect run ${runIndex} phases`,
605
+ );
606
+ const phases = Object.freeze({
607
+ baseline: normalizePhases(
608
+ run.phases.baseline,
609
+ `PM effect run ${runIndex} baseline phases`,
610
+ ),
611
+ candidate: normalizePhases(
612
+ run.phases.candidate,
613
+ `PM effect run ${runIndex} candidate phases`,
614
+ ),
615
+ });
616
+ if (
617
+ !Array.isArray(run.cases) ||
618
+ isProxy(run.cases) ||
619
+ Object.getPrototypeOf(run.cases) !== Array.prototype ||
620
+ run.cases.length !== expectedTasks.size ||
621
+ Reflect.ownKeys(run.cases).length !== run.cases.length + 1
622
+ ) {
623
+ throw new TypeError(
624
+ `PM effect run ${runIndex} must cover every test task`,
625
+ );
626
+ }
627
+ const seenTasks = new Set();
628
+ const cases = run.cases.map((item, caseIndex) => {
629
+ exact(
630
+ item,
631
+ ["taskId", "baseline", "candidate"],
632
+ `PM effect run ${runIndex} case ${caseIndex}`,
633
+ );
634
+ const taskId = boundedString(item.taskId, "PM effect taskId");
635
+ if (!expectedTasks.has(taskId) || seenTasks.has(taskId)) {
636
+ throw new TypeError(
637
+ `PM effect run ${runIndex} has an absent or duplicate task`,
638
+ );
639
+ }
640
+ seenTasks.add(taskId);
641
+ return Object.freeze({
642
+ taskId,
643
+ baseline: normalizeArm(item.baseline, `PM effect ${taskId} baseline`),
644
+ candidate: normalizeArm(
645
+ item.candidate,
646
+ `PM effect ${taskId} candidate`,
647
+ ),
648
+ });
649
+ });
650
+ cases.sort((left, right) => left.taskId.localeCompare(right.taskId, "en"));
651
+ const budgetViolations = [];
652
+ for (const arm of ["baseline", "candidate"]) {
653
+ const usage = emptyUsage();
654
+ for (const phase of phases[arm]) addUsage(usage, phase.usage);
655
+ for (const item of cases) addUsage(usage, item[arm].usage);
656
+ if (exceedsBudget(usage, verifiedPlan.budgetPerArmPerSeed)) {
657
+ budgetViolations.push(arm);
658
+ }
659
+ }
660
+ return Object.freeze({
661
+ runId: boundedString(run.runId, `PM effect run ${runIndex} runId`),
662
+ seed,
663
+ phases,
664
+ cases: Object.freeze(cases),
665
+ budgetViolations: Object.freeze(budgetViolations),
666
+ });
667
+ });
668
+ normalizedRuns.sort(
669
+ (left, right) =>
670
+ verifiedPlan.seeds.indexOf(left.seed) -
671
+ verifiedPlan.seeds.indexOf(right.seed),
672
+ );
673
+ const deltas = normalizedRuns.flatMap((run) =>
674
+ run.cases.map((item) => item.candidate.score - item.baseline.score),
675
+ );
676
+ const random = prngFromDigest(verifiedPlan.planDigest);
677
+ const bootstrap = Array.from({ length: verifiedPlan.bootstrapSamples }, () =>
678
+ mean(
679
+ Array.from(
680
+ { length: deltas.length },
681
+ () => deltas[Math.floor(random() * deltas.length)],
682
+ ),
683
+ ),
684
+ ).sort((left, right) => left - right);
685
+ const baseline = armTotals(normalizedRuns, "baseline");
686
+ const candidate = armTotals(normalizedRuns, "candidate");
687
+ const perTask = Object.freeze(
688
+ verifiedPlan.testTaskIds.map((taskId) => {
689
+ const observations = normalizedRuns.map((run) =>
690
+ run.cases.find((item) => item.taskId === taskId),
691
+ );
692
+ const baselineScores = observations.map((item) => item.baseline.score);
693
+ const candidateScores = observations.map((item) => item.candidate.score);
694
+ const baselinePassRate =
695
+ observations.filter((item) => item.baseline.passed).length /
696
+ observations.length;
697
+ const candidatePassRate =
698
+ observations.filter((item) => item.candidate.passed).length /
699
+ observations.length;
700
+ return Object.freeze({
701
+ taskId,
702
+ sampleCount: observations.length,
703
+ baselineScore: mean(baselineScores),
704
+ candidateScore: mean(candidateScores),
705
+ scoreDelta: mean(candidateScores) - mean(baselineScores),
706
+ baselinePassRate,
707
+ candidatePassRate,
708
+ });
709
+ }),
710
+ );
711
+ const pairedScoreDelta = Object.freeze({
712
+ mean: mean(deltas),
713
+ bootstrap95Ci: Object.freeze([
714
+ percentile(bootstrap, 0.025),
715
+ percentile(bootstrap, 0.975),
716
+ ]),
717
+ });
718
+ const budgetViolationCount = normalizedRuns.reduce(
719
+ (sum, run) => sum + run.budgetViolations.length,
720
+ 0,
721
+ );
722
+ const thresholdMet =
723
+ budgetViolationCount === 0 &&
724
+ candidate.securityViolations === 0 &&
725
+ candidate.permissionViolations === 0 &&
726
+ pairedScoreDelta.bootstrap95Ci[0] >= verifiedPlan.minimumScoreDelta;
727
+ const core = {
728
+ schema: PM_EXPLORATION_EFFECT_REPORT_SCHEMA,
729
+ planDigest: verifiedPlan.planDigest,
730
+ runCount: normalizedRuns.length,
731
+ pairedObservationCount: deltas.length,
732
+ baseline,
733
+ candidate,
734
+ perTask,
735
+ pairedScoreDelta,
736
+ budgetViolationCount,
737
+ evidenceDecision: thresholdMet ? "threshold-met" : "threshold-not-met",
738
+ requiresIndependentPilotApproval: true,
739
+ qualifiesForPromotion: false,
740
+ runs: Object.freeze(normalizedRuns),
741
+ };
742
+ return deepFreeze({
743
+ ...core,
744
+ reportDigest: hash(PM_EXPLORATION_EFFECT_REPORT_SCHEMA, core),
745
+ });
746
+ }
747
+
748
+ export function verifyPmExplorationEffectReport({ plan, report } = {}) {
749
+ const verifiedPlan = verifyPmExplorationEffectPlan(plan);
750
+ if (!report || typeof report !== "object" || isProxy(report)) {
751
+ throw new TypeError("a canonical PM effect report is required");
752
+ }
753
+ assertPlainData(report, "PM effect report");
754
+ if (report.schema !== PM_EXPLORATION_EFFECT_REPORT_SCHEMA) {
755
+ throw new TypeError("a canonical PM effect report is required");
756
+ }
757
+ exact(
758
+ report,
759
+ [
760
+ "schema",
761
+ "planDigest",
762
+ "runCount",
763
+ "pairedObservationCount",
764
+ "baseline",
765
+ "candidate",
766
+ "perTask",
767
+ "pairedScoreDelta",
768
+ "budgetViolationCount",
769
+ "evidenceDecision",
770
+ "requiresIndependentPilotApproval",
771
+ "qualifiesForPromotion",
772
+ "runs",
773
+ "reportDigest",
774
+ ],
775
+ "PM effect report",
776
+ );
777
+ if (
778
+ !Array.isArray(report.runs) ||
779
+ isProxy(report.runs) ||
780
+ Object.getPrototypeOf(report.runs) !== Array.prototype ||
781
+ Reflect.ownKeys(report.runs).length !== report.runs.length + 1
782
+ ) {
783
+ throw new TypeError("PM effect report runs are invalid");
784
+ }
785
+ const sourceRuns = report.runs.map((run, index) => {
786
+ exact(
787
+ run,
788
+ ["runId", "seed", "phases", "cases", "budgetViolations"],
789
+ `PM effect report run ${index}`,
790
+ );
791
+ return {
792
+ runId: run.runId,
793
+ seed: run.seed,
794
+ phases: run.phases,
795
+ cases: run.cases,
796
+ };
797
+ });
798
+ const recreated = buildPmExplorationEffectReport({
799
+ plan: verifiedPlan,
800
+ runs: sourceRuns,
801
+ });
802
+ if (canonical(recreated) !== canonical(report)) {
803
+ throw new Error("PM effect report digest mismatch");
804
+ }
805
+ return recreated;
806
+ }
807
+
61
808
  /** Trusted dataset-author input; store the result outside the Actor workspace. */
62
809
  export function buildPmExplorationSuite(input) {
63
810
  exact(input, ["suiteId", "datasetVersion", "tasks"], "PM suite input");