chainlesschain 0.166.16 → 0.166.17

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 (174) hide show
  1. package/README.md +3 -3
  2. package/completions/_cc +1 -1
  3. package/completions/cc.bash +1 -1
  4. package/completions/cc.fish +1 -1
  5. package/completions/cc.ps1 +1 -1
  6. package/package.json +2 -2
  7. package/src/assets/web-panel/assets/{AIOps-CTxa2Wbq.js → AIOps-CVW2n2rh.js} +1 -1
  8. package/src/assets/web-panel/assets/{ActionButton-DHrQyYhy.js → ActionButton-DtuM163O.js} +1 -1
  9. package/src/assets/web-panel/assets/{Analytics-DQiUqcNl.js → Analytics-BXvl_qHu.js} +3 -3
  10. package/src/assets/web-panel/assets/{AppLayout-_Iz4r6wt.js → AppLayout-VDBTFKOp.js} +5 -5
  11. package/src/assets/web-panel/assets/{Artifacts-BNs63YHo.js → Artifacts-BNz2xCiG.js} +1 -1
  12. package/src/assets/web-panel/assets/{Audit-BgHIqRLn.js → Audit-DTu9Hn9P.js} +1 -1
  13. package/src/assets/web-panel/assets/{BackgroundAgents-DUQ5axBB.js → BackgroundAgents-D2OCPzLe.js} +1 -1
  14. package/src/assets/web-panel/assets/{Backup-CJ51E9Vw.js → Backup-C9M-vG7G.js} +1 -1
  15. package/src/assets/web-panel/assets/{BaseInput-o-5toA_R.js → BaseInput-BUkCNiUQ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Chat-Cu7a-x1L.js → Chat-OJ2ajF7l.js} +6 -6
  17. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BUyYkAXr.js → ChatBubbleRenderer-BdZt76Ab.js} +1 -1
  18. package/src/assets/web-panel/assets/{Checkbox-DwdmZ71L.js → Checkbox-C1gXKDsc.js} +1 -1
  19. package/src/assets/web-panel/assets/{Codegen-BrH2eYiP.js → Codegen-BXbznLg7.js} +1 -1
  20. package/src/assets/web-panel/assets/{Col-BVJQEP2j.js → Col-CbCq5N0i.js} +1 -1
  21. package/src/assets/web-panel/assets/{Community-FGO3vZPq.js → Community-V8Ks6Yun.js} +1 -1
  22. package/src/assets/web-panel/assets/{Compact-DYGAFVnT.js → Compact-DkeHQTpQ.js} +1 -1
  23. package/src/assets/web-panel/assets/{Compliance-CRvc8Bdt.js → Compliance-JeLKwn4Q.js} +1 -1
  24. package/src/assets/web-panel/assets/{Cowork-D6Ah2m7A.js → Cowork-azfetcBz.js} +2 -2
  25. package/src/assets/web-panel/assets/{Cron-AXbU2ifR.js → Cron-Q691i_Gp.js} +2 -2
  26. package/src/assets/web-panel/assets/{Crosschain-C5DXgEFd.js → Crosschain-BLjDRbr_.js} +1 -1
  27. package/src/assets/web-panel/assets/{DID-Bxh9QV9v.js → DID-D8ptXplK.js} +2 -2
  28. package/src/assets/web-panel/assets/{Dashboard-B-ZHEta-.js → Dashboard-Ctj2VVZL.js} +2 -2
  29. package/src/assets/web-panel/assets/{Dropdown-66Ik1jqa.js → Dropdown-D8VgkMJ8.js} +1 -1
  30. package/src/assets/web-panel/assets/{EmailListRenderer-CCWk1BIM.js → EmailListRenderer-CQPe5S6q.js} +1 -1
  31. package/src/assets/web-panel/assets/{FamilyGuardDashboard--9faIULK.js → FamilyGuardDashboard-ySA-7SiN.js} +1 -1
  32. package/src/assets/web-panel/assets/{Federation-CejW9CgX.js → Federation-1PMD-fBf.js} +1 -1
  33. package/src/assets/web-panel/assets/{FormItemContext-acHgcXki.js → FormItemContext-C_jr6U4Q.js} +1 -1
  34. package/src/assets/web-panel/assets/{GenericCardRenderer-D2ghd_K4.js → GenericCardRenderer-Bu3bKf2B.js} +1 -1
  35. package/src/assets/web-panel/assets/{Git-BOe1YJrV.js → Git-Dv4Fa_T5.js} +2 -2
  36. package/src/assets/web-panel/assets/{Governance-BjgzJsof.js → Governance-DOEy1n1k.js} +1 -1
  37. package/src/assets/web-panel/assets/{Inference-CXCD5rnP.js → Inference-DkvqzWcJ.js} +1 -1
  38. package/src/assets/web-panel/assets/{KnowledgeGraph-CHA8QgJT.js → KnowledgeGraph-DdUJK949.js} +1 -1
  39. package/src/assets/web-panel/assets/{Logs-DORPMcL6.js → Logs-CRelcdUZ.js} +2 -2
  40. package/src/assets/web-panel/assets/{MarkdownRenderer-CPPHz01z.js → MarkdownRenderer-0AHbbTA4.js} +1 -1
  41. package/src/assets/web-panel/assets/{Marketplace-BCFlo1Yx.js → Marketplace-BuuBKsev.js} +1 -1
  42. package/src/assets/web-panel/assets/{McpTools-Dl_uZAKh.js → McpTools-DlgpEmA1.js} +3 -3
  43. package/src/assets/web-panel/assets/{Memory-DupHamwz.js → Memory-DTfdqUGy.js} +2 -2
  44. package/src/assets/web-panel/assets/{MobileBridge-BRSMPJxW.js → MobileBridge-CXLE5ZFO.js} +1 -1
  45. package/src/assets/web-panel/assets/{MobileProjects-BHpuzsDl.js → MobileProjects-Dz25OB0n.js} +1 -1
  46. package/src/assets/web-panel/assets/{Mtc-Ch0jOQfZ.js → Mtc-kEL0fhgD.js} +2 -2
  47. package/src/assets/web-panel/assets/{MtcAudit-qzwQrDi7.js → MtcAudit-BE4UvFJU.js} +2 -2
  48. package/src/assets/web-panel/assets/{Multisig-B5wHzUcm.js → Multisig-CSApSn8c.js} +3 -3
  49. package/src/assets/web-panel/assets/{NLProgramming-Hy9uAiA0.js → NLProgramming-D11FpKWx.js} +1 -1
  50. package/src/assets/web-panel/assets/{Notes-DkuchMC7.js → Notes-CDL-D2CS.js} +3 -3
  51. package/src/assets/web-panel/assets/{NotificationSettings-CYZhD7Pi.js → NotificationSettings-C0Q6lO-c.js} +1 -1
  52. package/src/assets/web-panel/assets/OrderTableRenderer-ZRE0OKbj.js +1 -0
  53. package/src/assets/web-panel/assets/{Organization-D9AXn6j8.js → Organization-Dc-maVAq.js} +4 -4
  54. package/src/assets/web-panel/assets/{Overflow-Bg15J6hX.js → Overflow-B5mUpSvi.js} +1 -1
  55. package/src/assets/web-panel/assets/{P2P--B54HoQn.js → P2P-TjXV53wq.js} +2 -2
  56. package/src/assets/web-panel/assets/PdhVaultBrowser-DvV2btIr.js +7 -0
  57. package/src/assets/web-panel/assets/{Permissions-DHx38eS1.js → Permissions-DKRzQ2O6.js} +4 -4
  58. package/src/assets/web-panel/assets/{PersonalDataHub-Dw8i2hAs.js → PersonalDataHub-Dx2eqft8.js} +4 -4
  59. package/src/assets/web-panel/assets/{Pipeline-B2o6Ur2G.js → Pipeline-CnYZMB5p.js} +1 -1
  60. package/src/assets/web-panel/assets/{Privacy-8g-h2fnu.js → Privacy-BonKpPnp.js} +1 -1
  61. package/src/assets/web-panel/assets/{ProjectInit-DuVTcINx.js → ProjectInit-DWO0Du3J.js} +2 -2
  62. package/src/assets/web-panel/assets/{ProjectSettings-C8njvdZX.js → ProjectSettings-C9jqyYRv.js} +2 -2
  63. package/src/assets/web-panel/assets/Projects-CYpKwXbz.js +1 -0
  64. package/src/assets/web-panel/assets/{Providers-wFqgod_W.js → Providers-CW-fkwJq.js} +1 -1
  65. package/src/assets/web-panel/assets/{QrScannerModal-9NCXrpmA.js → QrScannerModal-Cw0J3Zud.js} +1 -1
  66. package/src/assets/web-panel/assets/{QuickAsk-WUTNvFkY.js → QuickAsk-BH4A4IpR.js} +1 -1
  67. package/src/assets/web-panel/assets/{Recommend-xT4nYmju.js → Recommend-CDwMwJus.js} +1 -1
  68. package/src/assets/web-panel/assets/{RemoteSession-fak1PBGC.js → RemoteSession-DKIPwzTf.js} +2 -2
  69. package/src/assets/web-panel/assets/{Reputation-D1AkQvD-.js → Reputation-CklDj7qS.js} +1 -1
  70. package/src/assets/web-panel/assets/{Row-DVfm6xGt.js → Row-DJWZKsH0.js} +1 -1
  71. package/src/assets/web-panel/assets/{RssFeed-BDunoLSE.js → RssFeed-BfSLCewo.js} +2 -2
  72. package/src/assets/web-panel/assets/{Search-OPycRoU3.js → Search-B0IST--x.js} +1 -1
  73. package/src/assets/web-panel/assets/{Security-C8-Fm3Dt.js → Security-h1sYNWtt.js} +3 -3
  74. package/src/assets/web-panel/assets/{Services-qM_sMpMl.js → Services-Gh0-oxI_.js} +2 -2
  75. package/src/assets/web-panel/assets/{Skeleton-D3HBJjVF.js → Skeleton-CNDV_jOs.js} +1 -1
  76. package/src/assets/web-panel/assets/{Skills-jr67X-tP.js → Skills-ByCLTh1n.js} +1 -1
  77. package/src/assets/web-panel/assets/{Sla-CS3ggyCg.js → Sla-BfyrJTJz.js} +1 -1
  78. package/src/assets/web-panel/assets/{SpeechSettings-CTOCYxOi.js → SpeechSettings-BQzo9kNX.js} +1 -1
  79. package/src/assets/web-panel/assets/{SyncSettings-D3s3cLT5.js → SyncSettings-B6fS6z_1.js} +2 -2
  80. package/src/assets/web-panel/assets/{Tasks-CdfoJ25m.js → Tasks-C-uKCP-l.js} +1 -1
  81. package/src/assets/web-panel/assets/{Templates-Drb4AI8X.js → Templates-D6oDy7wL.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tenant-DzwR3Qkz.js → Tenant-D3LT2wNQ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Terminal-D-ESq7xp.js → Terminal-BteLcv_O.js} +2 -2
  84. package/src/assets/web-panel/assets/{TimelineRenderer-BK-AUuws.js → TimelineRenderer-DHHuxfqH.js} +1 -1
  85. package/src/assets/web-panel/assets/{Tokens-xGTouuNp.js → Tokens-BRQeykXw.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trigger-BvZj-v2H.js → Trigger-p2XaFWEu.js} +1 -1
  87. package/src/assets/web-panel/assets/{Trust-BLC9CGmc.js → Trust-BxadBMft.js} +1 -1
  88. package/src/assets/web-panel/assets/{UkeySign-BSuCXpmE.js → UkeySign-DRBP7LDk.js} +1 -1
  89. package/src/assets/web-panel/assets/{VideoEditing-DT3A-_Ql.js → VideoEditing-CZ3NZkh7.js} +1 -1
  90. package/src/assets/web-panel/assets/{Wallet-c1OXao85.js → Wallet-C3NTBN7G.js} +4 -4
  91. package/src/assets/web-panel/assets/{WebAuthn-CuA-F7KJ.js → WebAuthn-C7LVgMlb.js} +2 -2
  92. package/src/assets/web-panel/assets/{WorkflowEditor-MANZrf82.js → WorkflowEditor-0_Iveog7.js} +1 -1
  93. package/src/assets/web-panel/assets/{chat-D4Y4Ey0O.js → chat-DzzluYo3.js} +1 -1
  94. package/src/assets/web-panel/assets/{colors-DDH06hKO.js → colors-DSMYXpaj.js} +1 -1
  95. package/src/assets/web-panel/assets/{compact-item-Cym756Xj.js → compact-item-W76u1VSV.js} +1 -1
  96. package/src/assets/web-panel/assets/{createContext-By4Nb1k-.js → createContext-otsESjb5.js} +1 -1
  97. package/src/assets/web-panel/assets/devWarning-BztaXF84.js +1 -0
  98. package/src/assets/web-panel/assets/{hasIn-Ch7OY258.js → hasIn-3sNLrvug.js} +1 -1
  99. package/src/assets/web-panel/assets/index-4jP_hG_4.js +1 -0
  100. package/src/assets/web-panel/assets/{index-DtGValNi.js → index-B1I1AYJs.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-B51otVoZ.js → index-B5LOYdYm.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BBBUn5v7.js → index-B9X_jqXe.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-6CpOV3Pn.js → index-BA8mZxd-.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CUSUzaB8.js → index-BDlDO2px.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-oBsfUbyH.js → index-BJsQio5O.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-q-s04OKn.js → index-BRsD1PT2.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-XGeQMvWK.js → index-BXLx9HfA.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CjNUTBwe.js → index-BcGJtmHM.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-D9le0-dc.js → index-BdtmEKat.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-mL3nOcH6.js → index-BpcQYzP7.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BZB9q-Lp.js → index-C2aht9-M.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CF3Lhyns.js → index-CE7tSow-.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CZn6dmZe.js → index-CENWwjef.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-Bo20BfdS.js → index-CKoFyHYQ.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-tZ5BUdad.js → index-Cg86EzNp.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CT-3FYwd.js → index-ClOFeZ-t.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-6-2eYBXr.js → index-CtFOt5x1.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-DvnywwUj.js → index-D7A5U2IH.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CGsvEbbq.js → index-DH4cl52p.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CJPks33Y.js → index-DLDh1OW3.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CV8qXTvb.js → index-DMmdIpLA.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CykROEJA.js → index-DS9WYDMf.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-jx7kr6mB.js → index-DTA8Iavv.js} +3 -3
  124. package/src/assets/web-panel/assets/{index-CpudmDhf.js → index-DZKAzEge.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DAkk1M7x.js → index-DdU7IkKX.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-DNlxihs8.js → index-Ddd4mird.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BIrRX-ra.js → index-DmpaC2yk.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-CV1UC4zo.js → index-GlIOE4ja.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-ArMrnx0v.js → index-MhQQ3h8q.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-DjwgJ5Jd.js → index-NaO9AE-F.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DNEJzc0r.js → index-UUHCwfNq.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BFydNU6a.js → index-pVm_iUre.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-ZY2Qs_Sn.js → index-q7ftLqNt.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-D5sADPGY.js → index-uSCkbpZH.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-DiewptTL.js → index-vBKPcmAP.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-OU8DZ9BL.js → index-vUmYkqmu.js} +1 -1
  137. package/src/assets/web-panel/assets/index-zxwuAXL5.js +1 -0
  138. package/src/assets/web-panel/assets/{initDefaultProps-DGcX9iPP.js → initDefaultProps-DF6llUXV.js} +1 -1
  139. package/src/assets/web-panel/assets/{motion-2SeL1D8A.js → motion-vtDV8_5K.js} +1 -1
  140. package/src/assets/web-panel/assets/{move-3cYbQCpS.js → move-B6c0bdy2.js} +1 -1
  141. package/src/assets/web-panel/assets/{mtc-parser-IDdxfw0z.js → mtc-parser-DlLYWuBK.js} +1 -1
  142. package/src/assets/web-panel/assets/{omit-BNswdKO6.js → omit-BH0zOxLN.js} +1 -1
  143. package/src/assets/web-panel/assets/{pickAttrs-BwCnem2I.js → pickAttrs-D9di8_In.js} +1 -1
  144. package/src/assets/web-panel/assets/{placementArrow-B9PtZ5rq.js → placementArrow-BDSngufM.js} +1 -1
  145. package/src/assets/web-panel/assets/{responsiveObserve-CPB6iSzV.js → responsiveObserve-C_EawMvP.js} +1 -1
  146. package/src/assets/web-panel/assets/{slide-DZgyblZa.js → slide-B_4wShxA.js} +1 -1
  147. package/src/assets/web-panel/assets/{statusUtils-D82IBdsx.js → statusUtils-CbQu6qJ-.js} +1 -1
  148. package/src/assets/web-panel/assets/{styleChecker-qSHtosM-.js → styleChecker-CvXJWYXb.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFlexGapSupport-CmWO7h0I.js → useFlexGapSupport-jjY1XknO.js} +1 -1
  150. package/src/assets/web-panel/assets/{useFs-yLrs_ub4.js → useFs-CMXBdsK_.js} +1 -1
  151. package/src/assets/web-panel/assets/{usePersonalDataHub-CJ1VW6rh.js → usePersonalDataHub-YbAhhPXx.js} +1 -1
  152. package/src/assets/web-panel/assets/{vnode-CzY_moQ4.js → vnode-BFCTKc0H.js} +1 -1
  153. package/src/assets/web-panel/assets/{zoom-DAa7rEY5.js → zoom-CY9aBl5O.js} +1 -1
  154. package/src/assets/web-panel/index.html +1 -1
  155. package/src/command-help-index.json +2 -2
  156. package/src/command-manifest.json +2 -2
  157. package/src/commands/evolution.js +30 -17
  158. package/src/commands/learning.js +24 -8
  159. package/src/data/changelog.json +13 -0
  160. package/src/lib/evolution/bounded-skill-improvement-pilot.js +498 -0
  161. package/src/lib/evolution/evolution-run-runtime-adapter.js +5 -0
  162. package/src/lib/evolution/skill-evaluated-promotion-durability.js +899 -0
  163. package/src/lib/evolution/skill-evaluated-promotion.js +481 -0
  164. package/src/lib/evolution/skill-promotion-controller.js +114 -2
  165. package/src/lib/evolution/wiki-informed-skill-proposer.js +343 -0
  166. package/src/lib/graph-kernel/evolution-run-runtime-adapter.js +5 -0
  167. package/src/lib/hooks-v2-runtime.js +20 -0
  168. package/src/runtime/agent-core.js +68 -0
  169. package/src/assets/web-panel/assets/OrderTableRenderer-DKOoqajt.js +0 -1
  170. package/src/assets/web-panel/assets/PdhVaultBrowser-Cz9Kk2Nb.js +0 -7
  171. package/src/assets/web-panel/assets/Projects-Ca9BAfu7.js +0 -1
  172. package/src/assets/web-panel/assets/devWarning-KO3pAGeO.js +0 -1
  173. package/src/assets/web-panel/assets/index-Bh4Rgkg2.js +0 -1
  174. package/src/assets/web-panel/assets/index-Dox_coTY.js +0 -1
@@ -0,0 +1,481 @@
1
+ import { createHash } from "node:crypto";
2
+ import { types as utilTypes } from "node:util";
3
+
4
+ import { verifySkillMutationRequest } from "./skill-mutation-authority.js";
5
+ import {
6
+ SKILL_TARGET_MATRIX_EVAL_RECEIPT_SCHEMA,
7
+ verifySkillTargetMatrixEvalReceipt,
8
+ } from "./skill-target-matrix-eval.js";
9
+
10
+ export const SKILL_EVALUATED_PROMOTION_RECEIPT_ENVELOPE_SCHEMA =
11
+ "chainlesschain.skill-evaluated-promotion-receipt-envelope/v1";
12
+ export const SKILL_EVALUATED_PROMOTION_BINDING_SCHEMA =
13
+ "chainlesschain.skill-evaluated-promotion-binding/v1";
14
+ export const SKILL_EVALUATED_PROMOTION_PROVIDER_SCHEMA =
15
+ "chainlesschain.skill-evaluated-promotion-provider/v1";
16
+ export const SKILL_EVALUATED_PROMOTION_RECEIPT_RESOLVER_SCHEMA =
17
+ "chainlesschain.skill-evaluated-promotion-receipt-resolver/v1";
18
+ export const SKILL_EVALUATED_PROMOTION_RECEIPT_RESOLUTION_REQUEST_SCHEMA =
19
+ "chainlesschain.skill-evaluated-promotion-receipt-resolution-request/v1";
20
+ export const SKILL_EVALUATED_PROMOTION_RECEIPT_RESOLUTION_SCHEMA =
21
+ "chainlesschain.skill-evaluated-promotion-receipt-resolution/v1";
22
+
23
+ const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
24
+ const ENVELOPE_KEYS = new Set(["receiptDigest", "schema"]);
25
+ const MATRIX_CONTEXT_KEYS = new Set([
26
+ "baselineId",
27
+ "matrixAuthorityRoot",
28
+ "matrixEvalId",
29
+ "planDigest",
30
+ ]);
31
+ const RECEIPT_RESOLVER_KEYS = new Set([
32
+ "schema",
33
+ "authorityId",
34
+ "trust",
35
+ "revision",
36
+ "handlerArtifactDigest",
37
+ "resolve",
38
+ ]);
39
+ const RECEIPT_RESOLUTION_KEYS = new Set([
40
+ "schema",
41
+ "authorityId",
42
+ "trust",
43
+ "revision",
44
+ "handlerArtifactDigest",
45
+ "tenantId",
46
+ "receiptDigest",
47
+ "matrixReceipt",
48
+ "resolvedAt",
49
+ ]);
50
+ const SAFE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/u;
51
+ const PROVIDER_OPTION_KEYS = new Set([
52
+ "authorityId",
53
+ "handlerArtifactDigest",
54
+ "receiptResolver",
55
+ "revision",
56
+ "verifier",
57
+ ]);
58
+ const PROVIDER_KEYS = new Set([
59
+ "schema",
60
+ "authorityId",
61
+ "handlerArtifactDigest",
62
+ "revision",
63
+ "verify",
64
+ ]);
65
+ const PROVIDER_INPUT_KEYS = new Set([
66
+ "activeContentDigest",
67
+ "authorization",
68
+ "candidate",
69
+ "matrixContext",
70
+ "state",
71
+ ]);
72
+ const PROVIDER_INSTANCES = new WeakSet();
73
+
74
+ export class SkillEvaluatedPromotionError extends Error {
75
+ constructor(code, message, details = {}) {
76
+ super(message, details.cause ? { cause: details.cause } : undefined);
77
+ this.name = "SkillEvaluatedPromotionError";
78
+ this.code = code;
79
+ for (const [key, value] of Object.entries(details)) {
80
+ if (key !== "cause") this[key] = value;
81
+ }
82
+ }
83
+ }
84
+
85
+ function failure(code, message, details = {}) {
86
+ return new SkillEvaluatedPromotionError(code, message, details);
87
+ }
88
+
89
+ function assertDataRecord(value, keys, label) {
90
+ if (
91
+ !value ||
92
+ typeof value !== "object" ||
93
+ Array.isArray(value) ||
94
+ utilTypes.isProxy(value)
95
+ ) {
96
+ throw failure(
97
+ "SKILL_EVALUATED_PROMOTION_INVALID",
98
+ `${label} must be a plain object`,
99
+ );
100
+ }
101
+ const prototype = Object.getPrototypeOf(value);
102
+ const ownKeys = Reflect.ownKeys(value);
103
+ if (
104
+ (prototype !== Object.prototype && prototype !== null) ||
105
+ ownKeys.length !== keys.size ||
106
+ ownKeys.some((key) => typeof key !== "string" || !keys.has(key))
107
+ ) {
108
+ throw failure(
109
+ "SKILL_EVALUATED_PROMOTION_INVALID",
110
+ `${label} must contain exactly the supported fields`,
111
+ );
112
+ }
113
+ for (const key of ownKeys) {
114
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
115
+ if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
116
+ throw failure(
117
+ "SKILL_EVALUATED_PROMOTION_INVALID",
118
+ `${label}.${String(key)} must be an enumerable own data field`,
119
+ );
120
+ }
121
+ }
122
+ }
123
+
124
+ function deepFreeze(value, seen = new WeakSet()) {
125
+ if (!value || typeof value !== "object" || seen.has(value)) return value;
126
+ seen.add(value);
127
+ for (const key of Reflect.ownKeys(value)) {
128
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
129
+ if (!descriptor || !("value" in descriptor)) {
130
+ throw failure(
131
+ "SKILL_EVALUATED_PROMOTION_INVALID",
132
+ "trusted matrix evidence must contain only own data fields",
133
+ );
134
+ }
135
+ deepFreeze(descriptor.value, seen);
136
+ }
137
+ return Object.freeze(value);
138
+ }
139
+
140
+ function normalizeId(value, label) {
141
+ if (typeof value !== "string" || !SAFE_ID_PATTERN.test(value)) {
142
+ throw failure("SKILL_EVALUATED_PROMOTION_INVALID", `${label} is invalid`);
143
+ }
144
+ return value;
145
+ }
146
+
147
+ function normalizeDigest(value, label) {
148
+ if (typeof value !== "string" || !DIGEST_PATTERN.test(value)) {
149
+ throw failure(
150
+ "SKILL_EVALUATED_PROMOTION_INVALID",
151
+ `${label} must be a lowercase SHA-256 digest`,
152
+ );
153
+ }
154
+ return value;
155
+ }
156
+
157
+ function resolverDescriptorDigest(descriptor) {
158
+ return `sha256:${createHash("sha256")
159
+ .update(
160
+ `chainlesschain.skill-evaluated-promotion-receipt-resolver-descriptor/v1\0${JSON.stringify(descriptor)}`,
161
+ "utf8",
162
+ )
163
+ .digest("hex")}`;
164
+ }
165
+
166
+ function captureReceiptResolver(value) {
167
+ assertDataRecord(value, RECEIPT_RESOLVER_KEYS, "matrix receipt resolver");
168
+ if (
169
+ value.schema !== SKILL_EVALUATED_PROMOTION_RECEIPT_RESOLVER_SCHEMA ||
170
+ value.trust !== "trusted" ||
171
+ !Number.isSafeInteger(value.revision) ||
172
+ value.revision < 1 ||
173
+ typeof value.resolve !== "function" ||
174
+ utilTypes.isProxy(value.resolve)
175
+ ) {
176
+ throw failure(
177
+ "SKILL_EVALUATED_PROMOTION_INVALID",
178
+ "matrix receipt resolver authority is invalid",
179
+ );
180
+ }
181
+ const descriptor = deepFreeze({
182
+ schema: value.schema,
183
+ authorityId: normalizeId(value.authorityId, "resolver authorityId"),
184
+ trust: value.trust,
185
+ revision: value.revision,
186
+ handlerArtifactDigest: normalizeDigest(
187
+ value.handlerArtifactDigest,
188
+ "resolver handlerArtifactDigest",
189
+ ),
190
+ });
191
+ const resolve = value.resolve.bind(value);
192
+ Object.freeze(value);
193
+ return { descriptor, resolve };
194
+ }
195
+
196
+ async function resolveMatrixReceipt(resolver, tenantId, receiptDigest) {
197
+ const captured = captureReceiptResolver(resolver);
198
+ const request = deepFreeze({
199
+ schema: SKILL_EVALUATED_PROMOTION_RECEIPT_RESOLUTION_REQUEST_SCHEMA,
200
+ tenantId,
201
+ receiptDigest,
202
+ });
203
+ let resolution;
204
+ try {
205
+ resolution = await captured.resolve(request);
206
+ } catch (cause) {
207
+ throw failure(
208
+ "SKILL_EVALUATED_PROMOTION_RESOLUTION_FAILED",
209
+ "matrix receipt resolver failed closed",
210
+ { cause },
211
+ );
212
+ }
213
+ assertDataRecord(
214
+ resolution,
215
+ RECEIPT_RESOLUTION_KEYS,
216
+ "matrix receipt resolution",
217
+ );
218
+ const receipt = Object.getOwnPropertyDescriptor(
219
+ resolution,
220
+ "matrixReceipt",
221
+ )?.value;
222
+ const resolvedAt = resolution.resolvedAt;
223
+ if (
224
+ resolution.schema !== SKILL_EVALUATED_PROMOTION_RECEIPT_RESOLUTION_SCHEMA ||
225
+ resolution.authorityId !== captured.descriptor.authorityId ||
226
+ resolution.trust !== captured.descriptor.trust ||
227
+ resolution.revision !== captured.descriptor.revision ||
228
+ resolution.handlerArtifactDigest !==
229
+ captured.descriptor.handlerArtifactDigest ||
230
+ resolution.tenantId !== tenantId ||
231
+ resolution.receiptDigest !== receiptDigest ||
232
+ !receipt ||
233
+ typeof receipt !== "object" ||
234
+ utilTypes.isProxy(receipt) ||
235
+ Object.getOwnPropertyDescriptor(receipt, "receiptDigest")?.value !==
236
+ receiptDigest ||
237
+ typeof resolvedAt !== "string" ||
238
+ !Number.isFinite(Date.parse(resolvedAt)) ||
239
+ new Date(resolvedAt).toISOString() !== resolvedAt
240
+ ) {
241
+ throw failure(
242
+ "SKILL_EVALUATED_PROMOTION_RESOLUTION_REJECTED",
243
+ "matrix receipt resolution is not exactly bound to its trusted authority and digest",
244
+ );
245
+ }
246
+ return {
247
+ receipt,
248
+ resolution: deepFreeze({
249
+ authorityId: captured.descriptor.authorityId,
250
+ resolverDescriptorDigest: resolverDescriptorDigest(captured.descriptor),
251
+ resolverRevision: captured.descriptor.revision,
252
+ resolvedAt,
253
+ }),
254
+ };
255
+ }
256
+
257
+ export function buildSkillEvaluatedPromotionReceiptEnvelope(receipt) {
258
+ if (receipt && typeof receipt === "object" && utilTypes.isProxy(receipt)) {
259
+ throw failure(
260
+ "SKILL_EVALUATED_PROMOTION_INVALID",
261
+ "matrix receipt must be a plain object",
262
+ );
263
+ }
264
+ assertDataRecord(
265
+ receipt,
266
+ new Set(Reflect.ownKeys(receipt).filter((key) => typeof key === "string")),
267
+ "matrix receipt",
268
+ );
269
+ const schema = Object.getOwnPropertyDescriptor(receipt, "schema")?.value;
270
+ const receiptDigest = Object.getOwnPropertyDescriptor(
271
+ receipt,
272
+ "receiptDigest",
273
+ )?.value;
274
+ if (
275
+ schema !== SKILL_TARGET_MATRIX_EVAL_RECEIPT_SCHEMA ||
276
+ typeof receiptDigest !== "string" ||
277
+ !DIGEST_PATTERN.test(receiptDigest)
278
+ ) {
279
+ throw failure(
280
+ "SKILL_EVALUATED_PROMOTION_INVALID",
281
+ "matrix receipt envelope requires a typed receipt and lowercase SHA-256 digest",
282
+ );
283
+ }
284
+ return JSON.stringify({
285
+ schema: SKILL_EVALUATED_PROMOTION_RECEIPT_ENVELOPE_SCHEMA,
286
+ receiptDigest,
287
+ });
288
+ }
289
+
290
+ export function parseSkillEvaluatedPromotionReceiptEnvelope(value) {
291
+ if (typeof value !== "string" || value.length < 1 || value.length > 4096) {
292
+ throw failure(
293
+ "SKILL_EVALUATED_PROMOTION_INVALID",
294
+ "evalReceipt must be a bounded matrix receipt envelope",
295
+ );
296
+ }
297
+ let envelope;
298
+ try {
299
+ envelope = JSON.parse(value);
300
+ } catch (cause) {
301
+ throw failure(
302
+ "SKILL_EVALUATED_PROMOTION_INVALID",
303
+ "evalReceipt is not a matrix receipt envelope",
304
+ { cause },
305
+ );
306
+ }
307
+ assertDataRecord(envelope, ENVELOPE_KEYS, "matrix receipt envelope");
308
+ if (
309
+ envelope.schema !== SKILL_EVALUATED_PROMOTION_RECEIPT_ENVELOPE_SCHEMA ||
310
+ !DIGEST_PATTERN.test(envelope.receiptDigest)
311
+ ) {
312
+ throw failure(
313
+ "SKILL_EVALUATED_PROMOTION_INVALID",
314
+ "evalReceipt envelope is not bound to its typed matrix receipt",
315
+ );
316
+ }
317
+ if (
318
+ JSON.stringify({
319
+ schema: SKILL_EVALUATED_PROMOTION_RECEIPT_ENVELOPE_SCHEMA,
320
+ receiptDigest: envelope.receiptDigest,
321
+ }) !== value
322
+ ) {
323
+ throw failure(
324
+ "SKILL_EVALUATED_PROMOTION_INVALID",
325
+ "evalReceipt envelope must use the canonical encoding",
326
+ );
327
+ }
328
+ return deepFreeze(envelope);
329
+ }
330
+
331
+ export async function verifySkillEvaluatedPromotionBinding({
332
+ verifier,
333
+ receiptResolver,
334
+ matrixContext,
335
+ authorization,
336
+ candidate,
337
+ state,
338
+ activeContentDigest,
339
+ } = {}) {
340
+ assertDataRecord(
341
+ authorization,
342
+ new Set(["capability", "request"]),
343
+ "authorization",
344
+ );
345
+ const request = verifySkillMutationRequest(authorization.request);
346
+ const envelope = parseSkillEvaluatedPromotionReceiptEnvelope(
347
+ request.receipts.evalReceipt,
348
+ );
349
+ assertDataRecord(matrixContext, MATRIX_CONTEXT_KEYS, "matrix context");
350
+ if (!candidate || !state || typeof activeContentDigest !== "string") {
351
+ throw failure(
352
+ "SKILL_EVALUATED_PROMOTION_INVALID",
353
+ "candidate, active state, and active content digest are required",
354
+ );
355
+ }
356
+ const resolved = await resolveMatrixReceipt(
357
+ receiptResolver,
358
+ request.tenantId,
359
+ envelope.receiptDigest,
360
+ );
361
+ const expected = {
362
+ matrixEvalId: matrixContext.matrixEvalId,
363
+ tenantId: request.tenantId,
364
+ skillName: request.skillName,
365
+ candidateId: candidate.candidateId,
366
+ candidateContentDigest: candidate.contentDigest,
367
+ baselineId: matrixContext.baselineId,
368
+ baselineReleaseDigest: state.activeReleaseDigest,
369
+ expectedActiveContentDigest: activeContentDigest,
370
+ expectedActiveRevision: state.revision,
371
+ dependencyLockDigest: candidate.dependencyLockDigest,
372
+ runtimeManifestDigest: candidate.runtimeManifestDigest,
373
+ targetMatrixRoot: candidate.targetMatrixRoot,
374
+ matrixAuthorityRoot: matrixContext.matrixAuthorityRoot,
375
+ planDigest: matrixContext.planDigest,
376
+ decision: "accepted",
377
+ };
378
+ let verified;
379
+ try {
380
+ verified = await verifySkillTargetMatrixEvalReceipt(
381
+ verifier,
382
+ resolved.receipt,
383
+ expected,
384
+ );
385
+ } catch (cause) {
386
+ throw failure(
387
+ "SKILL_EVALUATED_PROMOTION_REJECTED",
388
+ "matrix evaluation receipt did not authorize this exact promotion",
389
+ { cause },
390
+ );
391
+ }
392
+ if (verified.receiptDigest !== envelope.receiptDigest) {
393
+ throw failure(
394
+ "SKILL_EVALUATED_PROMOTION_REJECTED",
395
+ "evalReceipt envelope does not identify the verified matrix receipt",
396
+ );
397
+ }
398
+ return deepFreeze({
399
+ schema: SKILL_EVALUATED_PROMOTION_BINDING_SCHEMA,
400
+ tenantId: verified.tenantId,
401
+ skillName: verified.skillName,
402
+ candidateId: verified.candidateId,
403
+ candidateContentDigest: verified.candidateContentDigest,
404
+ expectedActiveContentDigest: verified.expectedActiveContentDigest,
405
+ expectedActiveRevision: verified.expectedActiveRevision,
406
+ matrixEvalId: verified.matrixEvalId,
407
+ matrixReceiptDigest: verified.receiptDigest,
408
+ decisionCommitmentDigest: verified.decisionCommitmentDigest,
409
+ expiresAt: verified.expiresAt,
410
+ receiptResolution: resolved.resolution,
411
+ });
412
+ }
413
+
414
+ export function createSkillEvaluatedPromotionProvider(options = {}) {
415
+ assertDataRecord(
416
+ options,
417
+ PROVIDER_OPTION_KEYS,
418
+ "evaluated promotion provider options",
419
+ );
420
+ const capturedResolver = captureReceiptResolver(options.receiptResolver);
421
+ const authorityId = normalizeId(options.authorityId, "provider authorityId");
422
+ const handlerArtifactDigest = normalizeDigest(
423
+ options.handlerArtifactDigest,
424
+ "provider handlerArtifactDigest",
425
+ );
426
+ if (
427
+ !Number.isSafeInteger(options.revision) ||
428
+ options.revision < 1 ||
429
+ !options.verifier ||
430
+ typeof options.verifier !== "object" ||
431
+ utilTypes.isProxy(options.verifier)
432
+ ) {
433
+ throw failure(
434
+ "SKILL_EVALUATED_PROMOTION_INVALID",
435
+ "evaluated promotion provider revision or verifier is invalid",
436
+ );
437
+ }
438
+ const receiptResolver = Object.freeze({
439
+ ...capturedResolver.descriptor,
440
+ resolve: capturedResolver.resolve,
441
+ });
442
+ const verifier = options.verifier;
443
+ Object.freeze(verifier);
444
+ const provider = {
445
+ schema: SKILL_EVALUATED_PROMOTION_PROVIDER_SCHEMA,
446
+ authorityId,
447
+ handlerArtifactDigest,
448
+ revision: options.revision,
449
+ async verify(input = {}) {
450
+ assertDataRecord(
451
+ input,
452
+ PROVIDER_INPUT_KEYS,
453
+ "evaluated promotion provider input",
454
+ );
455
+ return verifySkillEvaluatedPromotionBinding({
456
+ ...input,
457
+ receiptResolver,
458
+ verifier,
459
+ });
460
+ },
461
+ };
462
+ PROVIDER_INSTANCES.add(provider);
463
+ return Object.freeze(provider);
464
+ }
465
+
466
+ export function captureSkillEvaluatedPromotionProvider(value) {
467
+ if (!value || !PROVIDER_INSTANCES.has(value)) {
468
+ throw failure(
469
+ "SKILL_EVALUATED_PROMOTION_PROVIDER_REQUIRED",
470
+ "a branded evaluated promotion provider is required",
471
+ );
472
+ }
473
+ assertDataRecord(value, PROVIDER_KEYS, "evaluated promotion provider");
474
+ return Object.freeze({
475
+ schema: value.schema,
476
+ authorityId: value.authorityId,
477
+ handlerArtifactDigest: value.handlerArtifactDigest,
478
+ revision: value.revision,
479
+ verify: value.verify.bind(value),
480
+ });
481
+ }
@@ -12,6 +12,7 @@ import {
12
12
  verifySkillMutationConsumptionReceipt,
13
13
  verifySkillMutationRequest,
14
14
  } from "./skill-mutation-authority.js";
15
+ import { captureSkillEvaluatedPromotionProvider } from "./skill-evaluated-promotion.js";
15
16
 
16
17
  const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
17
18
  const EMPTY_ACTIVE_DOMAIN = "chainlesschain.skill-active/empty/v1\0";
@@ -23,6 +24,15 @@ export const EMPTY_SKILL_ACTIVE_DIGEST = `sha256:${crypto
23
24
  .digest("hex")}`;
24
25
 
25
26
  const TRANSITION_CAPABILITIES = new WeakMap();
27
+ const EVALUATED_CONTROL_PLANE_OPTION_KEYS = new Set([
28
+ "candidateRegistry",
29
+ "releaseRegistry",
30
+ "authority",
31
+ "evaluatedPromotionProvider",
32
+ ]);
33
+
34
+ export const SKILL_EVALUATED_PROMOTION_CONTROL_PLANE_SCHEMA =
35
+ "chainlesschain.skill-evaluated-promotion-control-plane/v1";
26
36
 
27
37
  export class SkillPromotionControllerError extends Error {
28
38
  constructor(code, message, details = {}) {
@@ -251,7 +261,17 @@ export class SkillPromotionController {
251
261
 
252
262
  #consumeAuthority;
253
263
 
254
- constructor({ candidateRegistry, releaseRegistry, authority } = {}) {
264
+ #evaluatedPromotionProvider;
265
+
266
+ #requireEvaluatedPromotion;
267
+
268
+ constructor({
269
+ candidateRegistry,
270
+ releaseRegistry,
271
+ authority,
272
+ evaluatedPromotionProvider = null,
273
+ requireEvaluatedPromotion = false,
274
+ } = {}) {
255
275
  if (!candidateRegistry || typeof candidateRegistry.read !== "function") {
256
276
  throw failure(
257
277
  "SKILL_PROMOTION_CANDIDATE_REGISTRY_REQUIRED",
@@ -288,6 +308,18 @@ export class SkillPromotionController {
288
308
  "SkillMutationAuthority.consume is required",
289
309
  );
290
310
  }
311
+ if (typeof requireEvaluatedPromotion !== "boolean") {
312
+ throw failure(
313
+ "SKILL_PROMOTION_INVALID",
314
+ "requireEvaluatedPromotion must be boolean",
315
+ );
316
+ }
317
+ if (requireEvaluatedPromotion && evaluatedPromotionProvider === null) {
318
+ throw failure(
319
+ "SKILL_PROMOTION_EVALUATION_REQUIRED",
320
+ "evaluated-only promotion requires a captured evidence provider",
321
+ );
322
+ }
291
323
 
292
324
  this.#tenantId = candidateRegistry.tenantId;
293
325
  this.#registryIdentity = releaseRegistry;
@@ -297,6 +329,11 @@ export class SkillPromotionController {
297
329
  this.#applyTransition =
298
330
  releaseRegistry.applyTransition.bind(releaseRegistry);
299
331
  this.#consumeAuthority = authority.consume.bind(authority);
332
+ this.#evaluatedPromotionProvider =
333
+ evaluatedPromotionProvider === null
334
+ ? null
335
+ : captureSkillEvaluatedPromotionProvider(evaluatedPromotionProvider);
336
+ this.#requireEvaluatedPromotion = requireEvaluatedPromotion;
300
337
 
301
338
  Object.freeze(candidateRegistry);
302
339
  Object.freeze(releaseRegistry);
@@ -352,7 +389,7 @@ export class SkillPromotionController {
352
389
  return verifyDirectConsumptionReceipt(directReceipt, request);
353
390
  }
354
391
 
355
- async promote(input = {}) {
392
+ async #promote(input = {}) {
356
393
  assertDataRecord(
357
394
  input,
358
395
  new Set(["authorization", "candidateId"]),
@@ -425,6 +462,45 @@ export class SkillPromotionController {
425
462
  return this.#applyTransition(transitionCapability);
426
463
  }
427
464
 
465
+ async promote(input = {}) {
466
+ if (this.#requireEvaluatedPromotion) {
467
+ throw failure(
468
+ "SKILL_PROMOTION_EVALUATION_REQUIRED",
469
+ "direct promotion is disabled by the evaluated-only controller policy",
470
+ );
471
+ }
472
+ return this.#promote(input);
473
+ }
474
+
475
+ async promoteEvaluated(input = {}) {
476
+ assertDataRecord(
477
+ input,
478
+ new Set(["authorization", "candidateId", "matrixContext"]),
479
+ "evaluated promotion input",
480
+ );
481
+ if (this.#evaluatedPromotionProvider === null) {
482
+ throw failure(
483
+ "SKILL_PROMOTION_EVALUATION_REQUIRED",
484
+ "evaluated promotion requires a captured evidence provider",
485
+ );
486
+ }
487
+ const candidate = this.#readCandidate(input.candidateId);
488
+ const state = this.#readState(candidate.skillName);
489
+ const activeContentDigest = this.#currentContentDigest(state);
490
+ const matrixBinding = await this.#evaluatedPromotionProvider.verify({
491
+ matrixContext: input.matrixContext,
492
+ authorization: input.authorization,
493
+ candidate,
494
+ state,
495
+ activeContentDigest,
496
+ });
497
+ const result = await this.#promote({
498
+ authorization: input.authorization,
499
+ candidateId: input.candidateId,
500
+ });
501
+ return deepFreeze({ ...result, matrixBinding });
502
+ }
503
+
428
504
  async rollback(input = {}) {
429
505
  assertDataRecord(
430
506
  input,
@@ -503,4 +579,40 @@ export class SkillPromotionController {
503
579
  }
504
580
  }
505
581
 
582
+ /**
583
+ * Builds the narrow promotion surface intended for production adapters.
584
+ *
585
+ * This is a composition primitive, not proof that any production entry point
586
+ * uses it. The returned facade deliberately withholds both the controller and
587
+ * direct promote(), while retaining authorized rollback as the recovery path.
588
+ */
589
+ export function createSkillEvaluatedPromotionControlPlane(options = {}) {
590
+ assertDataRecord(
591
+ options,
592
+ EVALUATED_CONTROL_PLANE_OPTION_KEYS,
593
+ "evaluated promotion control plane options",
594
+ );
595
+ const provider = captureSkillEvaluatedPromotionProvider(
596
+ options.evaluatedPromotionProvider,
597
+ );
598
+ const controller = new SkillPromotionController({
599
+ candidateRegistry: options.candidateRegistry,
600
+ releaseRegistry: options.releaseRegistry,
601
+ authority: options.authority,
602
+ evaluatedPromotionProvider: options.evaluatedPromotionProvider,
603
+ requireEvaluatedPromotion: true,
604
+ });
605
+ return Object.freeze({
606
+ schema: SKILL_EVALUATED_PROMOTION_CONTROL_PLANE_SCHEMA,
607
+ tenantId: options.candidateRegistry.tenantId,
608
+ providerAuthorityId: provider.authorityId,
609
+ providerRevision: provider.revision,
610
+ providerHandlerArtifactDigest: provider.handlerArtifactDigest,
611
+ promoteEvaluated: Object.freeze((input) =>
612
+ controller.promoteEvaluated(input),
613
+ ),
614
+ rollback: Object.freeze((input) => controller.rollback(input)),
615
+ });
616
+ }
617
+
506
618
  Object.freeze(SkillPromotionController.prototype);