chainlesschain 0.166.14 → 0.166.16

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 (195) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-C8SyFhOo.js → AIOps-CTxa2Wbq.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Dup5gFK0.js → ActionButton-DHrQyYhy.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-8FyOWA7V.js → Analytics-DQiUqcNl.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DBzdU9dg.js → AppLayout-_Iz4r6wt.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-C_0Z8gNy.js → Artifacts-BNs63YHo.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-Cf2a-58m.js → Audit-BgHIqRLn.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-M7w-2zgB.js → BackgroundAgents-DUQ5axBB.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-CCTHQXoL.js → Backup-CJ51E9Vw.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-DjXpeFwJ.js → BaseInput-o-5toA_R.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-cAvchsJU.js → Chat-Cu7a-x1L.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DvEvxlZv.js → ChatBubbleRenderer-BUyYkAXr.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-DKs6qvOn.js → Checkbox-DwdmZ71L.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DlkA7b9X.js → Codegen-BrH2eYiP.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-CkfdKhyJ.js → Col-BVJQEP2j.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-iNXMHzj6.js → Community-FGO3vZPq.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DQxPEd09.js → Compact-DYGAFVnT.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CSKvMCjs.js → Compliance-CRvc8Bdt.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CkWqazpD.js → Cowork-D6Ah2m7A.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-k6W8IzhB.js → Cron-AXbU2ifR.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-C8MS1OuY.js → Crosschain-C5DXgEFd.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DJuCcgiQ.js → DID-Bxh9QV9v.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-LeThKzp3.js → Dashboard-B-ZHEta-.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-BvxwgpE4.js → Dropdown-66Ik1jqa.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-n3e_a1M3.js → EmailListRenderer-CCWk1BIM.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-509W6W6v.js → FamilyGuardDashboard--9faIULK.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-xgZReQm0.js → Federation-CejW9CgX.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-jZOhKcQx.js → FormItemContext-acHgcXki.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-Jo0ULzHv.js → GenericCardRenderer-D2ghd_K4.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-BsRcyWQP.js → Git-BOe1YJrV.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-JlpMYTAk.js → Governance-BjgzJsof.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CUEz872-.js → Inference-CXCD5rnP.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DgrvvEDU.js → KnowledgeGraph-CHA8QgJT.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-Df-KY4U3.js → Logs-DORPMcL6.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-CPPHz01z.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-kPZDTOeI.js → Marketplace-BCFlo1Yx.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-CRRzPqNX.js → McpTools-Dl_uZAKh.js} +2 -2
  38. package/src/assets/web-panel/assets/{Memory-D6pNSkg9.js → Memory-DupHamwz.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-g-pvtwRC.js → MobileBridge-BRSMPJxW.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-Bzayfrfe.js → MobileProjects-BHpuzsDl.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-B7S3W-XL.js → Mtc-Ch0jOQfZ.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-B8OMEWVb.js → MtcAudit-qzwQrDi7.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-CowRp7GH.js → Multisig-B5wHzUcm.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-BkQNyi4c.js → NLProgramming-Hy9uAiA0.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-Cm7gtkKz.js → Notes-DkuchMC7.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-CbPTVh9m.js → NotificationSettings-CYZhD7Pi.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-Dswi26mJ.js → OrderTableRenderer-DKOoqajt.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-BkjQ9yy7.js → Organization-D9AXn6j8.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-DmRFXP7Y.js → Overflow-Bg15J6hX.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-DQE1toAu.js → P2P--B54HoQn.js} +2 -2
  51. package/src/assets/web-panel/assets/PdhVaultBrowser-Cz9Kk2Nb.js +7 -0
  52. package/src/assets/web-panel/assets/{Permissions-xSjfOaiU.js → Permissions-DHx38eS1.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-B9ICLqVP.js → PersonalDataHub-Dw8i2hAs.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-CUNwyN9E.js → Pipeline-B2o6Ur2G.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-SOoCricX.js → Privacy-8g-h2fnu.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-CSaFul0F.js → ProjectInit-DuVTcINx.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-CueiB_oe.js → ProjectSettings-C8njvdZX.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-Ca9BAfu7.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-CTqf_31h.js → Providers-wFqgod_W.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-C8ZP31E9.js → QrScannerModal-9NCXrpmA.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-CPgn4713.js → QuickAsk-WUTNvFkY.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-8_e0zw0i.js → Recommend-xT4nYmju.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-iT7lbGqw.js → RemoteSession-fak1PBGC.js} +3 -3
  64. package/src/assets/web-panel/assets/{Reputation-B4q9SUOz.js → Reputation-D1AkQvD-.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-CA_nASyL.js → Row-DVfm6xGt.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-Cwb0E3yv.js → RssFeed-BDunoLSE.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-D9iVsOxJ.js → Search-OPycRoU3.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-DPLbYchG.js → Security-C8-Fm3Dt.js} +3 -3
  69. package/src/assets/web-panel/assets/{Services-DGMVL_LF.js → Services-qM_sMpMl.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-B_6kym0D.js → Skeleton-D3HBJjVF.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-Ds6r0dXB.js → Skills-jr67X-tP.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-BLHAbBhb.js → Sla-CS3ggyCg.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-CZuddd14.js → SpeechSettings-CTOCYxOi.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CdBq7oSl.js → SyncSettings-D3s3cLT5.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DfT60Nyl.js → Tasks-CdfoJ25m.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-RLfsKy2V.js → Templates-Drb4AI8X.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-Cw8xCNLE.js → Tenant-DzwR3Qkz.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BOUkNu4f.js → Terminal-D-ESq7xp.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-CgGZWaRm.js → TimelineRenderer-BK-AUuws.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CpwjVyir.js → Tokens-xGTouuNp.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DL21H-eb.js → Trigger-BvZj-v2H.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-B3ffFclW.js → Trust-BLC9CGmc.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CkBeb5aI.js → UkeySign-BSuCXpmE.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-U2iVaE3r.js → VideoEditing-DT3A-_Ql.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-CQk2wzYd.js → Wallet-c1OXao85.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-DYIuIYzT.js → WebAuthn-CuA-F7KJ.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-tg1c2mZQ.js → WorkflowEditor-MANZrf82.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-CtL61spq.js → chat-D4Y4Ey0O.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DKpbeo8s.js → colors-DDH06hKO.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-gPZsrswt.js → compact-item-Cym756Xj.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-CfHlwzVr.js → createContext-By4Nb1k-.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-KO3pAGeO.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-MV47g_6X.js → hasIn-Ch7OY258.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DMMvlLp9.js → index-6-2eYBXr.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DsH-i_3d.js → index-6CpOV3Pn.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CB1HnbNK.js → index-ArMrnx0v.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DU4rZpiK.js → index-B51otVoZ.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CRv_uMLV.js → index-BBBUn5v7.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-srBc9U_s.js → index-BFydNU6a.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DnZ_zS1M.js → index-BIrRX-ra.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DGtEBodr.js → index-BZB9q-Lp.js} +1 -1
  102. package/src/assets/web-panel/assets/index-Bh4Rgkg2.js +1 -0
  103. package/src/assets/web-panel/assets/{index-BjlPD7GO.js → index-Bo20BfdS.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-C_B9LiKs.js → index-CF3Lhyns.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Rb7CRMw9.js → index-CGsvEbbq.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DIeJmlAA.js → index-CJPks33Y.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-xUlgG0Xe.js → index-CT-3FYwd.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-6G0ETFTW.js → index-CUSUzaB8.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CzfxTSPW.js → index-CV1UC4zo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CmPJpLoD.js → index-CV8qXTvb.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-fYcS7NS0.js → index-CZn6dmZe.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Cjv0kKCU.js → index-CjNUTBwe.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Df9sWKpl.js → index-CpudmDhf.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C9RF4ieu.js → index-CykROEJA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Zvkt9xqi.js → index-D5sADPGY.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-yxxXlqBA.js → index-D9le0-dc.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BBw52sEl.js → index-DAkk1M7x.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-ExAGPAnX.js → index-DNEJzc0r.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BQp5M0s1.js → index-DNlxihs8.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Ddcpj1El.js → index-DiewptTL.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BOyGUuBE.js → index-DjwgJ5Jd.js} +1 -1
  122. package/src/assets/web-panel/assets/index-Dox_coTY.js +1 -0
  123. package/src/assets/web-panel/assets/{index-CmM9jqwe.js → index-DtGValNi.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DfLyHCWi.js → index-DvnywwUj.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BN-wxSQ8.js → index-OU8DZ9BL.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D1XB5-t5.js → index-XGeQMvWK.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-C4lF-f_z.js → index-ZY2Qs_Sn.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-C9gtY4nG.js → index-jx7kr6mB.js} +3 -3
  129. package/src/assets/web-panel/assets/{index-C6nC_DyY.js → index-mL3nOcH6.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Dd19vRwy.js → index-oBsfUbyH.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-bSFrAszw.js → index-q-s04OKn.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D_Pl1GnW.js → index-tZ5BUdad.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-BeOZoSZ4.js → initDefaultProps-DGcX9iPP.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BXE8VMKx.js → motion-2SeL1D8A.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-I9sdvPGX.js → move-3cYbQCpS.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-d25zrJd0.js → mtc-parser-IDdxfw0z.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-D71V_T4e.js → omit-BNswdKO6.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-BNZKpIAR.js → pickAttrs-BwCnem2I.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-jOcgxTTz.js → placementArrow-B9PtZ5rq.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-pta_O8AU.js → responsiveObserve-CPB6iSzV.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-Dlq6om7z.js → slide-DZgyblZa.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BnkO_yRv.js → statusUtils-D82IBdsx.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-p8NfWmcG.js → styleChecker-qSHtosM-.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-KcjZZ5U7.js → useFlexGapSupport-CmWO7h0I.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-gqonDMWB.js → useFs-yLrs_ub4.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-jIIfE4go.js → usePersonalDataHub-CJ1VW6rh.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-Dyo7fBrM.js → vnode-CzY_moQ4.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-6l8BMg9W.js → zoom-DAa7rEY5.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agent.js +62 -11
  151. package/src/commands/learning.js +71 -1
  152. package/src/commands/team.js +100 -3
  153. package/src/data/changelog.json +29 -3
  154. package/src/harness/worktree-isolator.js +74 -9
  155. package/src/lib/agent-team/task-lease.js +12 -1
  156. package/src/lib/agent-team/team-graph-runtime-adapter.js +6 -0
  157. package/src/lib/agent-team/team-runner.js +4 -0
  158. package/src/lib/agent-team/team-worktree.js +4 -0
  159. package/src/lib/capability-manifest.js +212 -1
  160. package/src/lib/evolution/evolution-artifact-ports.js +3277 -0
  161. package/src/lib/evolution/evolution-eval-gate.js +6442 -0
  162. package/src/lib/evolution/evolution-evidence-projector.js +3786 -0
  163. package/src/lib/evolution/evolution-ledger-ports.js +2689 -0
  164. package/src/lib/evolution/evolution-ledger.js +4280 -0
  165. package/src/lib/evolution/skill-candidate-registry.js +2258 -0
  166. package/src/lib/evolution/skill-execution-manifest.js +942 -0
  167. package/src/lib/evolution/skill-mutation-authority.js +1677 -0
  168. package/src/lib/evolution/skill-promotion-controller.js +506 -0
  169. package/src/lib/evolution/skill-release-registry.js +3277 -0
  170. package/src/lib/evolution/skill-target-matrix-eval.js +3233 -0
  171. package/src/lib/evolution/skill-writer-inventory-manifest.js +998 -0
  172. package/src/lib/evolution/skill-writer-inventory-validator.js +816 -0
  173. package/src/lib/formal-quality-eval-runtime.js +192 -0
  174. package/src/lib/graph-kernel/cutover-contract.js +7 -0
  175. package/src/lib/graph-kernel/cutover-ledger.js +8 -7
  176. package/src/lib/graph-kernel/evidence-digest.js +47 -0
  177. package/src/lib/graph-kernel/graph-runtime-surfaces.json +8 -1
  178. package/src/lib/graph-kernel/production-cutover-evidence.js +342 -15
  179. package/src/lib/graph-kernel/production-source-evidence.js +1599 -0
  180. package/src/lib/graph-kernel/retirement-evidence.js +15 -5
  181. package/src/lib/graph-kernel/runtime-surface-manifest.js +67 -2
  182. package/src/lib/headless-manifest.js +3 -1
  183. package/src/lib/learning/skill-improver.js +311 -43
  184. package/src/lib/learning/skill-synthesizer.js +236 -16
  185. package/src/lib/process-execution-broker/index.js +32 -0
  186. package/src/lib/process-execution-broker/platform-sandbox.js +5 -4
  187. package/src/lib/secure-fs.js +45 -2
  188. package/src/runtime/agent-core.js +17 -5
  189. package/src/runtime/headless-runner.js +16 -9
  190. package/src/assets/web-panel/assets/MarkdownRenderer-BEPDcSwz.js +0 -1
  191. package/src/assets/web-panel/assets/PdhVaultBrowser-BujyoDRG.js +0 -7
  192. package/src/assets/web-panel/assets/Projects-BOeXvU7y.js +0 -1
  193. package/src/assets/web-panel/assets/devWarning-DdqnMhzi.js +0 -1
  194. package/src/assets/web-panel/assets/index-BcK8o4ke.js +0 -1
  195. package/src/assets/web-panel/assets/index-DvbAjyYI.js +0 -1
@@ -1,4 +1,4 @@
1
- import { graphDigest } from "./compiler.js";
1
+ import { graphEvidenceDigest as graphDigest } from "./evidence-digest.js";
2
2
 
3
3
  export const GRAPH_RETIREMENT_EVIDENCE_SCHEMA =
4
4
  "chainlesschain.graph-retirement-qualification/v1";
@@ -215,7 +215,7 @@ function evidenceIdentity(
215
215
  return normalized;
216
216
  }
217
217
 
218
- function observationWindow(input, { notBefore } = {}) {
218
+ function observationWindow(input, { notBefore, minimumDurationMs = 1 } = {}) {
219
219
  const started = timestamp(input.startedAt, "startedAt");
220
220
  const ended = timestamp(input.endedAt, "endedAt");
221
221
  if (ended.milliseconds <= started.milliseconds) {
@@ -231,10 +231,18 @@ function observationWindow(input, { notBefore } = {}) {
231
231
  { notBefore, startedAt: started.value },
232
232
  );
233
233
  }
234
+ const durationMs = ended.milliseconds - started.milliseconds;
235
+ if (durationMs < minimumDurationMs || input?.durationMs !== durationMs) {
236
+ throw evidenceError(
237
+ "CC_GRAPH_RETIREMENT_OBSERVATION_TOO_SHORT",
238
+ `durationMs must equal the timestamp window and be at least ${minimumDurationMs}`,
239
+ { durationMs, minimumDurationMs },
240
+ );
241
+ }
234
242
  return {
235
243
  startedAt: started.value,
236
244
  endedAt: ended.value,
237
- durationMs: ended.milliseconds - started.milliseconds,
245
+ durationMs,
238
246
  observationSampleCount: positive(
239
247
  input.observationSampleCount,
240
248
  "observationSampleCount",
@@ -454,6 +462,7 @@ export function normalizeGraphRetirementEvidence(
454
462
  commitSha,
455
463
  contract: contractInput,
456
464
  requiredPlatforms,
465
+ minimumDurationMs = 1,
457
466
  },
458
467
  ) {
459
468
  const contract = normalizeGraphRetirementContract(contractInput);
@@ -470,7 +479,7 @@ export function normalizeGraphRetirementEvidence(
470
479
  commitSha,
471
480
  contract,
472
481
  }),
473
- ...observationWindow(input),
482
+ ...observationWindow(input, { minimumDurationMs }),
474
483
  activeLegacyRunCount: zero(
475
484
  input?.activeLegacyRunCount,
476
485
  "activeLegacyRunCount",
@@ -508,6 +517,7 @@ export function normalizeGraphLegacyWriterObservation(
508
517
  commitSha,
509
518
  contract: contractInput,
510
519
  notBefore,
520
+ minimumDurationMs = 1,
511
521
  },
512
522
  ) {
513
523
  const contract = normalizeGraphRetirementContract(contractInput);
@@ -554,7 +564,7 @@ export function normalizeGraphLegacyWriterObservation(
554
564
  commitSha,
555
565
  contract,
556
566
  }),
557
- ...observationWindow(input, { notBefore }),
567
+ ...observationWindow(input, { notBefore, minimumDurationMs }),
558
568
  activeLegacyRunCount: zero(
559
569
  input?.activeLegacyRunCount,
560
570
  "activeLegacyRunCount",
@@ -9,8 +9,8 @@ import {
9
9
  import {
10
10
  GRAPH_CUTOVER_LEDGER_SCHEMA,
11
11
  GRAPH_CUTOVER_REQUIRED_PLATFORMS,
12
- } from "./cutover-ledger.js";
13
- import { graphDigest } from "./compiler.js";
12
+ } from "./cutover-contract.js";
13
+ import { graphEvidenceDigest as graphDigest } from "./evidence-digest.js";
14
14
 
15
15
  export const GRAPH_RUNTIME_SURFACE_MANIFEST_SCHEMA =
16
16
  "chainlesschain.graph-runtime-surfaces/v1";
@@ -111,6 +111,71 @@ export function graphRuntimeEntryManifestDigest(
111
111
  );
112
112
  }
113
113
 
114
+ export function graphRuntimeSurfaceManifestDigest(manifest) {
115
+ return graphDigest(manifest, "cc.graph.runtime-surface-manifest/v1");
116
+ }
117
+
118
+ export function assertGraphProductionRuntimeSurfaceManifest(manifest) {
119
+ const surfaces = Array.isArray(manifest?.surfaces) ? manifest.surfaces : [];
120
+ const entries = surfaces.flatMap((surface) => surface.entries || []);
121
+ const counts = {
122
+ migrate: entries.filter((entry) => entry.cutoverStrategy === "migrate")
123
+ .length,
124
+ retire: entries.filter((entry) => entry.cutoverStrategy === "retire")
125
+ .length,
126
+ disabled: entries.filter((entry) => entry.cutoverStrategy === "disabled")
127
+ .length,
128
+ };
129
+ const surfaceNames = surfaces.map((surface) => surface.originSurface).sort();
130
+ const expectedSurfaceNames = [
131
+ "browser",
132
+ "cli_team",
133
+ "cowork",
134
+ "desktop",
135
+ "scheduler",
136
+ ];
137
+ const expectedLegacyMutationErrorCodes = {
138
+ browser: "CC_CLI_LEGACY_RUNTIME_READ_ONLY",
139
+ cli_team: "CC_CLI_LEGACY_RUNTIME_READ_ONLY",
140
+ cowork: "CC_CLI_LEGACY_RUNTIME_READ_ONLY",
141
+ desktop: "CC_DESKTOP_LEGACY_RUNTIME_READ_ONLY",
142
+ scheduler: "CC_CLI_LEGACY_RUNTIME_READ_ONLY",
143
+ };
144
+ if (
145
+ surfaces.length !== 5 ||
146
+ JSON.stringify(surfaceNames) !== JSON.stringify(expectedSurfaceNames) ||
147
+ entries.length !== 23 ||
148
+ counts.migrate !== 7 ||
149
+ counts.retire !== 13 ||
150
+ counts.disabled !== 3 ||
151
+ JSON.stringify(manifest?.cutoverPolicy?.legacyMutationErrorCodes) !==
152
+ JSON.stringify(expectedLegacyMutationErrorCodes)
153
+ ) {
154
+ const error = new Error(
155
+ "production Graph runtime manifest must freeze exactly 5 surfaces, 23 entries, 20 durable, 7 migrate, 13 retire, and 3 disabled",
156
+ );
157
+ error.name = "GraphRuntimeSurfaceManifestError";
158
+ error.code = "CC_GRAPH_PRODUCTION_MANIFEST_CONTRACT_MISMATCH";
159
+ error.actual = {
160
+ surfaceCount: surfaces.length,
161
+ surfaceNames,
162
+ entryCount: entries.length,
163
+ durableEntryCount: counts.migrate + counts.retire,
164
+ ...counts,
165
+ };
166
+ throw error;
167
+ }
168
+ return Object.freeze({
169
+ surfaceCount: 5,
170
+ entryCount: 23,
171
+ durableEntryCount: 20,
172
+ migratableEntryCount: 7,
173
+ retirementEntryCount: 13,
174
+ disabledEntryCount: 3,
175
+ manifestDigest: graphRuntimeSurfaceManifestDigest(manifest),
176
+ });
177
+ }
178
+
114
179
  function manifestFiles(manifest) {
115
180
  return new Set([
116
181
  ...manifest.surfaces.flatMap((surface) =>
@@ -19,6 +19,7 @@ import { createRequire } from "node:module";
19
19
  import { PROTOCOL_MIN_VERSION } from "./capability-negotiation.js";
20
20
  import {
21
21
  CAPABILITY_MANIFEST,
22
+ buildEvolutionStatus,
22
23
  toAgentFeatureFlags,
23
24
  } from "./capability-manifest.js";
24
25
 
@@ -129,7 +130,7 @@ export function buildLoadedSources({
129
130
  * (protocol version, tool names, permission modes, IO formats, exit codes,
130
131
  * feature flags) without parsing --help text.
131
132
  */
132
- export function buildAgentCapabilities() {
133
+ export function buildAgentCapabilities({ evolutionEvidence = {} } = {}) {
133
134
  let version = null;
134
135
  try {
135
136
  version = _require("../../package.json").version || null;
@@ -158,5 +159,6 @@ export function buildAgentCapabilities() {
158
159
  input_formats: [...CAPABILITY_MANIFEST.inputFormats],
159
160
  exit_codes: HEADLESS_EXIT_CODES,
160
161
  features: toAgentFeatureFlags(),
162
+ evolution_status: buildEvolutionStatus({ evidence: evolutionEvidence }),
161
163
  };
162
164
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
- * SkillImprover — Iteratively improves auto-synthesized SKILL.md files
3
- * based on execution feedback and better trajectories.
2
+ * SkillImprover — Proposes candidate revisions for auto-synthesized SKILL.md
3
+ * files based on execution feedback and better trajectories.
4
4
  *
5
5
  * Three improvement triggers:
6
6
  * 1. repairFromError — skill execution failed, patch the procedure
@@ -69,6 +69,41 @@ export function rebuildSkillMd(meta, body) {
69
69
  return `---\n${lines.join("\n")}\n---\n${body}`;
70
70
  }
71
71
 
72
+ /**
73
+ * A skill name is used as a path segment in both the source and candidate
74
+ * trees. Reject traversal and ambiguous path-like names before any I/O.
75
+ * @param {string} skillName
76
+ * @returns {boolean}
77
+ */
78
+ export function isSafeSkillName(skillName) {
79
+ return (
80
+ typeof skillName === "string" &&
81
+ /^[\p{L}\p{N}][\p{L}\p{N}._-]{0,127}$/u.test(skillName) &&
82
+ skillName !== "." &&
83
+ skillName !== ".." &&
84
+ !skillName.includes("..")
85
+ );
86
+ }
87
+
88
+ function rootContains(parentRoot, childRoot) {
89
+ if (!parentRoot || !childRoot) return false;
90
+ const parent = path.resolve(parentRoot);
91
+ const child = path.resolve(childRoot);
92
+ const relative = path.relative(parent, child);
93
+ return (
94
+ relative === "" ||
95
+ (relative !== ".." &&
96
+ !relative.startsWith(`..${path.sep}`) &&
97
+ !path.isAbsolute(relative))
98
+ );
99
+ }
100
+
101
+ function rootsOverlap(firstRoot, secondRoot) {
102
+ return (
103
+ rootContains(firstRoot, secondRoot) || rootContains(secondRoot, firstRoot)
104
+ );
105
+ }
106
+
72
107
  // ── LLM prompt builders ────────────────────────────────
73
108
 
74
109
  /**
@@ -188,16 +223,68 @@ export class SkillImprover {
188
223
  * @param {import("better-sqlite3").Database} db
189
224
  * @param {function} llmChat — async (messages) => string
190
225
  * @param {import("./trajectory-store.js").TrajectoryStore} trajectoryStore
191
- * @param {{skillsDir?:string, limits?:object}} [config]
226
+ * @param {{skillsDir?:string, candidateOutputDir?:string, diffOnly?:boolean, limits?:object}} [config]
192
227
  */
193
228
  constructor(db, llmChat, trajectoryStore, config = {}) {
194
229
  this.db = db;
195
230
  this.llmChat = llmChat;
196
231
  this.trajectoryStore = trajectoryStore;
197
232
  this.skillsDir = config.skillsDir || null;
233
+ this.candidateOutputDir = config.candidateOutputDir || null;
234
+ this.diffOnly = config.diffOnly === true;
198
235
  this.skillLimits = resolveSkillLimits(config.limits);
199
236
  }
200
237
 
238
+ /**
239
+ * Return the fail-closed mutation boundary for this instance. `skillsDir` is
240
+ * always read-only; proposals must use an isolated candidate root or
241
+ * explicitly opt into diff-only output.
242
+ * @returns {{available:boolean, reason?:string}}
243
+ */
244
+ getAvailability() {
245
+ if (!this.skillsDir) {
246
+ return { available: false, reason: "source skill registry unavailable" };
247
+ }
248
+ if (typeof this.llmChat !== "function") {
249
+ return { available: false, reason: "LLM unavailable" };
250
+ }
251
+ if (this.diffOnly && this.candidateOutputDir) {
252
+ return {
253
+ available: false,
254
+ reason: "configure either candidateOutputDir or diffOnly, not both",
255
+ };
256
+ }
257
+ if (!this.diffOnly && !this.candidateOutputDir) {
258
+ return {
259
+ available: false,
260
+ reason:
261
+ "candidate output unavailable; configure candidateOutputDir or diffOnly",
262
+ };
263
+ }
264
+ if (
265
+ this.candidateOutputDir &&
266
+ rootsOverlap(this.skillsDir, this.candidateOutputDir)
267
+ ) {
268
+ return {
269
+ available: false,
270
+ reason: "candidate output must be isolated from the active skill tree",
271
+ };
272
+ }
273
+ return { available: true };
274
+ }
275
+
276
+ _unavailableResult() {
277
+ const availability = this.getAvailability();
278
+ return availability.available
279
+ ? null
280
+ : {
281
+ improved: false,
282
+ candidateCreated: false,
283
+ status: "unavailable",
284
+ reason: availability.reason,
285
+ };
286
+ }
287
+
201
288
  /**
202
289
  * Repair a skill after an execution error.
203
290
  * @param {string} skillName
@@ -205,6 +292,16 @@ export class SkillImprover {
205
292
  * @returns {Promise<{improved:boolean, reason:string}>}
206
293
  */
207
294
  async repairFromError(skillName, errorContext) {
295
+ const unavailable = this._unavailableResult();
296
+ if (unavailable) return unavailable;
297
+ if (!isSafeSkillName(skillName)) {
298
+ return {
299
+ improved: false,
300
+ candidateCreated: false,
301
+ status: "error",
302
+ reason: "invalid skill name",
303
+ };
304
+ }
208
305
  const skillContent = await this._readSkill(skillName);
209
306
  if (!skillContent) {
210
307
  return { improved: false, reason: "skill not found" };
@@ -225,10 +322,14 @@ export class SkillImprover {
225
322
  meta.version = bumpVersion(meta.version);
226
323
 
227
324
  const newContent = rebuildSkillMd(meta, newBody);
228
- await this._writeSkill(skillName, newContent);
229
- this._logImprovement(skillName, "error_repair", suggestion.diagnosis || "");
230
-
231
- return { improved: true, reason: suggestion.diagnosis || "repaired" };
325
+ return this._finalizeCandidate({
326
+ skillName,
327
+ content: newContent,
328
+ version: meta.version,
329
+ triggerType: "error_repair",
330
+ detail: suggestion.diagnosis || "",
331
+ reason: suggestion.diagnosis || "repair candidate generated",
332
+ });
232
333
  }
233
334
 
234
335
  /**
@@ -238,6 +339,16 @@ export class SkillImprover {
238
339
  * @returns {Promise<{improved:boolean, reason:string}>}
239
340
  */
240
341
  async updateFromCorrection(skillName, correctionContext) {
342
+ const unavailable = this._unavailableResult();
343
+ if (unavailable) return unavailable;
344
+ if (!isSafeSkillName(skillName)) {
345
+ return {
346
+ improved: false,
347
+ candidateCreated: false,
348
+ status: "error",
349
+ reason: "invalid skill name",
350
+ };
351
+ }
241
352
  const skillContent = await this._readSkill(skillName);
242
353
  if (!skillContent) {
243
354
  return { improved: false, reason: "skill not found" };
@@ -258,14 +369,14 @@ export class SkillImprover {
258
369
  meta.version = bumpVersion(meta.version);
259
370
 
260
371
  const newContent = rebuildSkillMd(meta, newBody);
261
- await this._writeSkill(skillName, newContent);
262
- this._logImprovement(
372
+ return this._finalizeCandidate({
263
373
  skillName,
264
- "user_correction",
265
- suggestion.whatChanged || "",
266
- );
267
-
268
- return { improved: true, reason: suggestion.whatChanged || "corrected" };
374
+ content: newContent,
375
+ version: meta.version,
376
+ triggerType: "user_correction",
377
+ detail: suggestion.whatChanged || "",
378
+ reason: suggestion.whatChanged || "correction candidate generated",
379
+ });
269
380
  }
270
381
 
271
382
  /**
@@ -275,6 +386,16 @@ export class SkillImprover {
275
386
  * @returns {Promise<{improved:boolean, reason:string}>}
276
387
  */
277
388
  async improveFromBetterTrajectory(skillName, betterTrajectory) {
389
+ const unavailable = this._unavailableResult();
390
+ if (unavailable) return unavailable;
391
+ if (!isSafeSkillName(skillName)) {
392
+ return {
393
+ improved: false,
394
+ candidateCreated: false,
395
+ status: "error",
396
+ reason: "invalid skill name",
397
+ };
398
+ }
278
399
  const skillContent = await this._readSkill(skillName);
279
400
  if (!skillContent) {
280
401
  return { improved: false, reason: "skill not found" };
@@ -295,22 +416,34 @@ export class SkillImprover {
295
416
  meta.version = bumpVersion(meta.version);
296
417
 
297
418
  const newContent = rebuildSkillMd(meta, newBody);
298
- await this._writeSkill(skillName, newContent);
299
- this._logImprovement(
419
+ return this._finalizeCandidate({
300
420
  skillName,
301
- "better_trajectory",
302
- suggestion.improvements || "",
303
- );
304
-
305
- return { improved: true, reason: suggestion.improvements || "improved" };
421
+ content: newContent,
422
+ version: meta.version,
423
+ triggerType: "better_trajectory",
424
+ detail: suggestion.improvements || "",
425
+ reason: suggestion.improvements || "improvement candidate generated",
426
+ });
306
427
  }
307
428
 
308
429
  /**
309
430
  * Scan for skills that can be improved from recent high-score trajectories.
310
- * @returns {Promise<{improved: string[], skipped: string[]}>}
431
+ * @returns {Promise<{status:string, improved: string[], candidates: object[], skipped: string[], reason?:string}>}
311
432
  */
312
433
  async scanForImprovements() {
434
+ const unavailable = this._unavailableResult();
435
+ if (unavailable) {
436
+ return {
437
+ status: unavailable.status,
438
+ improved: [],
439
+ candidates: [],
440
+ skipped: [],
441
+ reason: unavailable.reason,
442
+ };
443
+ }
444
+
313
445
  const improved = [];
446
+ const candidates = [];
314
447
  const skipped = [];
315
448
 
316
449
  // Find synthesized trajectories that have higher-scoring siblings
@@ -350,8 +483,12 @@ export class SkillImprover {
350
483
  row.synthesized_skill,
351
484
  better,
352
485
  );
353
- if (result.improved) {
354
- improved.push(row.synthesized_skill);
486
+ if (result.candidateCreated || result.status === "diff-only") {
487
+ candidates.push({
488
+ skillName: row.synthesized_skill,
489
+ status: result.status,
490
+ candidatePath: result.candidatePath || null,
491
+ });
355
492
  } else {
356
493
  skipped.push(`${row.synthesized_skill}: ${result.reason}`);
357
494
  }
@@ -363,7 +500,7 @@ export class SkillImprover {
363
500
  }
364
501
  }
365
502
 
366
- return { improved, skipped };
503
+ return { status: "completed", improved, candidates, skipped };
367
504
  }
368
505
 
369
506
  // ── Internal ────────────────────────────────────────
@@ -391,7 +528,7 @@ export class SkillImprover {
391
528
  * @returns {Promise<string|null>}
392
529
  */
393
530
  async _readSkill(skillName) {
394
- if (!this.skillsDir) return null;
531
+ if (!this.skillsDir || !isSafeSkillName(skillName)) return null;
395
532
  const skillFile = _deps.path.join(this.skillsDir, skillName, "SKILL.md");
396
533
  try {
397
534
  if (typeof _deps.fs.promises.lstat === "function") {
@@ -413,21 +550,156 @@ export class SkillImprover {
413
550
  }
414
551
 
415
552
  /**
416
- * Write updated skill content to disk.
553
+ * Persist or return a candidate without mutating the source skill tree.
554
+ * The audit write is required: callers never receive a successful candidate
555
+ * result when persistence or logging fails.
556
+ * @param {{skillName:string, content:string, version:string, triggerType:string, detail:string, reason:string}} candidate
557
+ * @returns {Promise<object>}
558
+ */
559
+ async _finalizeCandidate(candidate) {
560
+ if (this.diffOnly) {
561
+ this._logImprovement(
562
+ candidate.skillName,
563
+ candidate.triggerType,
564
+ candidate.detail,
565
+ );
566
+ return {
567
+ improved: false,
568
+ candidateCreated: false,
569
+ candidateGenerated: true,
570
+ status: "diff-only",
571
+ reason: candidate.reason,
572
+ candidate: {
573
+ skillName: candidate.skillName,
574
+ version: candidate.version,
575
+ content: candidate.content,
576
+ },
577
+ };
578
+ }
579
+
580
+ const persisted = await this._writeCandidate(
581
+ candidate.skillName,
582
+ candidate.version,
583
+ candidate.content,
584
+ );
585
+ this._logImprovement(
586
+ candidate.skillName,
587
+ candidate.triggerType,
588
+ candidate.detail,
589
+ );
590
+ return {
591
+ improved: false,
592
+ candidateCreated: true,
593
+ candidateGenerated: true,
594
+ status: "candidate",
595
+ reason: candidate.reason,
596
+ candidatePath: persisted.skillFile,
597
+ candidate: {
598
+ skillName: candidate.skillName,
599
+ version: candidate.version,
600
+ },
601
+ };
602
+ }
603
+
604
+ /**
605
+ * Write a versioned candidate to the isolated candidate registry.
417
606
  * @param {string} skillName
607
+ * @param {string} version
418
608
  * @param {string} content
609
+ * @returns {Promise<{skillDir:string, skillFile:string}>}
419
610
  */
420
- async _writeSkill(skillName, content) {
421
- if (!this.skillsDir) return;
611
+ async _writeCandidate(skillName, version, content) {
612
+ const availability = this.getAvailability();
613
+ if (!availability.available || this.diffOnly) {
614
+ throw new Error(
615
+ availability.reason || "candidate persistence unavailable",
616
+ );
617
+ }
618
+ if (!isSafeSkillName(skillName) || !/^\d+\.\d+\.\d+$/.test(version)) {
619
+ throw new Error("invalid candidate identity");
620
+ }
422
621
  assertSkillFileSize(
423
622
  "SKILL.md",
424
623
  Buffer.byteLength(String(content), "utf8"),
425
624
  this.skillLimits,
426
625
  );
427
- const skillDir = _deps.path.join(this.skillsDir, skillName);
428
- const skillFile = _deps.path.join(skillDir, "SKILL.md");
429
- await _deps.fs.promises.mkdir(skillDir, { recursive: true });
430
- await _deps.fs.promises.writeFile(skillFile, content, "utf-8");
626
+ this._assertCandidateFilesystemSupport();
627
+ const candidateRoot = await this._ensurePrivateDirectory(
628
+ this.candidateOutputDir,
629
+ { recursive: true },
630
+ );
631
+ await this._assertRealpathIsolation(candidateRoot);
632
+
633
+ const skillNamespacePath = _deps.path.join(
634
+ this.candidateOutputDir,
635
+ skillName,
636
+ );
637
+ const skillNamespace =
638
+ await this._ensurePrivateDirectory(skillNamespacePath);
639
+ this._assertPathWithinCandidateRoot(candidateRoot, skillNamespace);
640
+
641
+ const skillDir = _deps.path.join(skillNamespace, version);
642
+ const canonicalSkillDir = await this._ensurePrivateDirectory(skillDir);
643
+ this._assertPathWithinCandidateRoot(candidateRoot, canonicalSkillDir);
644
+
645
+ const skillFile = _deps.path.join(canonicalSkillDir, "SKILL.md");
646
+ await _deps.fs.promises.writeFile(skillFile, content, {
647
+ encoding: "utf-8",
648
+ flag: "wx",
649
+ });
650
+ return { skillDir: canonicalSkillDir, skillFile };
651
+ }
652
+
653
+ _assertCandidateFilesystemSupport() {
654
+ if (
655
+ typeof _deps.fs.promises.realpath !== "function" ||
656
+ typeof _deps.fs.promises.lstat !== "function"
657
+ ) {
658
+ throw new Error(
659
+ "candidate filesystem identity support unavailable; refusing write",
660
+ );
661
+ }
662
+ }
663
+
664
+ async _ensurePrivateDirectory(directory, options = {}) {
665
+ let stat;
666
+ try {
667
+ stat = await _deps.fs.promises.lstat(directory);
668
+ } catch (error) {
669
+ if (error?.code !== "ENOENT") throw error;
670
+ try {
671
+ await _deps.fs.promises.mkdir(directory, {
672
+ recursive: options.recursive === true,
673
+ mode: 0o700,
674
+ });
675
+ } catch (mkdirError) {
676
+ if (mkdirError?.code !== "EEXIST") throw mkdirError;
677
+ }
678
+ stat = await _deps.fs.promises.lstat(directory);
679
+ }
680
+ if (stat.isSymbolicLink() || !stat.isDirectory()) {
681
+ throw new Error(
682
+ `candidate directory is not a trusted directory: ${directory}`,
683
+ );
684
+ }
685
+ return _deps.fs.promises.realpath(directory);
686
+ }
687
+
688
+ async _assertRealpathIsolation(candidateRoot) {
689
+ const sourceRoot = await _deps.fs.promises.realpath(this.skillsDir);
690
+ if (rootsOverlap(sourceRoot, candidateRoot)) {
691
+ throw new Error(
692
+ "candidate output resolves into the active skill tree; refusing write",
693
+ );
694
+ }
695
+ }
696
+
697
+ _assertPathWithinCandidateRoot(candidateRoot, candidatePath) {
698
+ if (!rootContains(candidateRoot, candidatePath)) {
699
+ throw new Error(
700
+ "candidate parent resolves outside the candidate root; refusing write",
701
+ );
702
+ }
431
703
  }
432
704
 
433
705
  /**
@@ -437,16 +709,12 @@ export class SkillImprover {
437
709
  * @param {string} detail
438
710
  */
439
711
  _logImprovement(skillName, triggerType, detail) {
440
- try {
441
- this.db
442
- .prepare(
443
- `INSERT INTO skill_improvement_log (skill_name, trigger_type, detail)
444
- VALUES (?, ?, ?)`,
445
- )
446
- .run(skillName, triggerType, (detail || "").slice(0, 500));
447
- } catch {
448
- // Non-critical, don't break the flow
449
- }
712
+ this.db
713
+ .prepare(
714
+ `INSERT INTO skill_improvement_log (skill_name, trigger_type, detail)
715
+ VALUES (?, ?, ?)`,
716
+ )
717
+ .run(skillName, triggerType, (detail || "").slice(0, 500));
450
718
  }
451
719
 
452
720
  /**