chainlesschain 0.166.105 → 0.166.107

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 (211) hide show
  1. package/README.md +182 -8
  2. package/package.json +2 -2
  3. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  4. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-CcD-AVDw.js → AIOps-DCx6L_HW.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-CYy-g36A.js → ActionButton-DYm1CN3Z.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-DPFDNaBL.js → Analytics-D5ItUS_y.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-PVIuRdgN.js → AppLayout-Cb7XeRdH.js} +5 -5
  9. package/src/assets/web-panel/assets/{Artifacts-BXGD3bM-.js → Artifacts-DDDeqCDQ.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-Bu5s_4tu.js → Audit-BRt3x13w.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-BHmgpnzo.js → BackgroundAgents-C_oNALMd.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-C872jURx.js → Backup-PSU2p6CR.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-Bh7xO30H.js → BaseInput-G3oL4hcX.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-DaacK53T.js → Chat-C2cLI10b.js} +6 -6
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-PYrSKMM_.js → ChatBubbleRenderer-KX1OITf8.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-Cnt7_8I2.js → Checkbox-CHLnEgFD.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-AZRz6kmW.js → Codegen-DZm9g_Bc.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-BtOXzMFt.js → Col-D5Z4zSay.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-BFmFygS7.js → Community-DHw69NSk.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-2z5CfKpR.js → Compact-B-vjYNWz.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-B5hKrPVF.js → Compliance-DaIYVZQY.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-CxEyp2ww.js → Cowork-CoA9U-gF.js} +3 -3
  23. package/src/assets/web-panel/assets/{Cron-B-8f9_qF.js → Cron-DXqmxlRC.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-XARMOcc4.js → Crosschain-Cdkc3ue1.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-DwLtEKAe.js → DID-oKfYyNSR.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-Cjw0L_iA.js → Dashboard-n_ycvgWu.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-BBGWIP7l.js → Dropdown-D38rZ8WY.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-DG4kZFkm.js → EmailListRenderer-BjreeBQ1.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-D8vuNDde.js → EvolutionSettings-CTxA_SIq.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-COXCzDXI.js → FamilyGuardDashboard-DmECNwV5.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-Btb-aMhW.js → Federation-B2IaaDmQ.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-TO-E6l6e.js → FormItemContext-DgcgqUIk.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-BWXdJCT0.js → GenericCardRenderer-B1-du6Qm.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-B6icGhEv.js → Git-XceM21Il.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-C5Y__K6S.js → Governance-CV97iz3z.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-DZGEKnbN.js → Inference-CI7fj305.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph--Kga5yvm.js → KnowledgeGraph-B5mekWRy.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-BXs3EPKj.js → Logs-BlxFA0xY.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-rDE-XyMY.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-JSEAu0su.js → Marketplace-DL1BVCiX.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-BUBifkKP.js → McpTools-BPtwrO3c.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-Cb7sE4a-.js → Memory-DuSNw4E-.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-BOzmmNV2.js → MobileBridge-DTgY9ywI.js} +1 -1
  44. package/src/assets/web-panel/assets/MobileProjects-CTpVHJMa.js +1 -0
  45. package/src/assets/web-panel/assets/{Mtc-DmMWErG1.js → Mtc-h4fCLjXw.js} +2 -2
  46. package/src/assets/web-panel/assets/{MtcAudit-DnGkRVXF.js → MtcAudit-fqMDY3-w.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-C3mzascL.js → Multisig-Do3FehBL.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-CrmMZZ1t.js → NLProgramming-CQVY0kQF.js} +1 -1
  49. package/src/assets/web-panel/assets/Notes-4c3oNvWh.js +7 -0
  50. package/src/assets/web-panel/assets/{NotificationSettings-gn3UrWwW.js → NotificationSettings-BKr2JD4k.js} +1 -1
  51. package/src/assets/web-panel/assets/OrderTableRenderer-Bq4jgzRt.js +1 -0
  52. package/src/assets/web-panel/assets/{Organization-fMlkdgpW.js → Organization-Dy3XidyA.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-DGwCUPU1.js → Overflow-DFF0qN5K.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-BDGcpPmf.js → P2P-DAgYYykd.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-CccRPAB9.js → PdhVaultBrowser-DgNHa8b1.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-C33dNECx.js → Permissions-Cev6uSBc.js} +3 -3
  57. package/src/assets/web-panel/assets/{PersonalDataHub-BeqbCStc.js → PersonalDataHub-DJzbN06K.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-CTmasgbQ.js → Pipeline-CoJWvbQ5.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-ktd37BSp.js → Privacy-DZxV6etm.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-KOGvQtBX.js → ProjectInit-BFW87JnM.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-CUvH_hC6.js → ProjectSettings-LBrRT27p.js} +2 -2
  62. package/src/assets/web-panel/assets/Projects-O3uz76R1.js +1 -0
  63. package/src/assets/web-panel/assets/{Providers-C-hmLXlO.js → Providers-CnRbjOYk.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-DzG_rbSL.js → QrScannerModal-BVFnIS3r.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-ShwiATg4.js → QuickAsk-DrcHx5f1.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-DkV8Ruep.js → Recommend-CbRgrSJm.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-Ca2dOx_G.js → RemoteSession-B2Uz_ElB.js} +2 -2
  68. package/src/assets/web-panel/assets/{Reputation-DYA5BJ6A.js → Reputation-DS0ljTxL.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-DvGDgxLS.js → Row-ClLobFht.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-DbwTBSPw.js → RssFeed-DxxcTD0x.js} +3 -3
  71. package/src/assets/web-panel/assets/{Search-C5XAMQ9T.js → Search-BbHN0Fik.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-CKWT60Qt.js → Security-BAVU_EcY.js} +3 -3
  73. package/src/assets/web-panel/assets/{Services-CZvASlWL.js → Services-CtqRSVq_.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-DFkMVqws.js → Skeleton-IJ8ZKdSZ.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-hslZ_GyP.js → Skills-DXxcznnO.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-BLW5rmZ5.js → Sla-fRkhnA3i.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-Cn4ffCi5.js → SpeechSettings-Bi8sYJ-I.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-D1K0nJuo.js → SyncSettings-CjeplUtS.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-CCRu0xoN.js → Tasks-D4KB2Hbx.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-L19GYuS4.js → Templates-BwVugi8n.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-CZ6HE3PF.js → Tenant-CsPuu5xK.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-BiAwzaBT.js → Terminal-DuaeOPjY.js} +2 -2
  83. package/src/assets/web-panel/assets/{TimelineRenderer-DRHSWQT5.js → TimelineRenderer-D-PMrFxO.js} +1 -1
  84. package/src/assets/web-panel/assets/{Tokens-Cm-XATgh.js → Tokens-BXZuiQPZ.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-BVXtx3kn.js → Trigger-DgpTaHL3.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-Bb0TmkLJ.js → Trust-Bn7lEGRY.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-Dbt9R7l0.js → UkeySign-zz-8JAAQ.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-z0-Ti8eJ.js → VideoEditing-DCzfQGTc.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-BZh31tI-.js → Wallet-BiRHM_zB.js} +4 -4
  90. package/src/assets/web-panel/assets/{WebAuthn-DQ0yIr5K.js → WebAuthn-QmPo2JwU.js} +5 -5
  91. package/src/assets/web-panel/assets/{WorkflowEditor-DCt_yuWG.js → WorkflowEditor-gL7SsPnY.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-BLgtmFeS.js → chat-BysvK4hk.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-xU4UH8M1.js → colors-CQq20vqN.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-C4w4v16T.js → compact-item-CAHlOf1X.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-CZM9hAaj.js → createContext-BeF3ESj_.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-D-Iugqxv.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-DvHqEpE7.js → hasIn-CiqnRf-U.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BkTKWAgq.js → index-4a0Lmz_y.js} +1 -1
  99. package/src/assets/web-panel/assets/index-B0Oz2_rQ.js +1 -0
  100. package/src/assets/web-panel/assets/{index-B0IvKEVO.js → index-B2AOvjgR.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BaQZvM4m.js → index-B2xbtl_c.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BRhD9RMV.js → index-B5941vyo.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CqDiY-jl.js → index-BHpHGGmB.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-CQEzxnYS.js → index-BOiyW31p.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BTxhDCtg.js → index-BcVmt6u_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CPDw21XY.js → index-BfJabF_8.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CExiTobS.js → index-Br5tGVbC.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-QHmhwgfc.js → index-BsnLkZjz.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BlN5KPq6.js → index-BtWchYfW.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-SNV95Zen.js → index-Bvkbj3x-.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BHK3CQHq.js → index-CCX0daFQ.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CPC7OGCe.js → index-CImCEo8S.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-CKugxsw3.js → index-CTp89vmg.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-55Rkd1KA.js → index-CcccefJc.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Ds6wKTMh.js → index-CeIL3-nY.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DifMvnMQ.js → index-CgkUkmEJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-Bf8Wm4ks.js → index-CowbNBJs.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Cm00787d.js → index-Cp3IRTDK.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C9YnZFv2.js → index-D7q5VH2n.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BHg9NoJ5.js → index-DBmGQHEz.js} +3 -3
  121. package/src/assets/web-panel/assets/{index-B6AQ2b5U.js → index-DHGtFwwR.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CYrQnkqA.js → index-DKYbru3i.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CyV8fNBg.js → index-DTPUVm71.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B4jIcYSv.js → index-DXT7MKkD.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-B64Vf-8H.js → index-DXsXhR7p.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CpiuuIww.js → index-Dqkls2_6.js} +1 -1
  127. package/src/assets/web-panel/assets/index-DwoycEQa.js +1 -0
  128. package/src/assets/web-panel/assets/{index-DGdLkss8.js → index-QALafVsT.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-Ds1tM734.js → index-TdVEaJfk.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Bh14b1k0.js → index-ZBuJ6O6g.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-C6ZrTSw5.js → index-i9aJV9qk.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CF9ebAIB.js → index-jJhemX1R.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BIWumeQl.js → index-qjaCk1Mj.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-n6SAUmmI.js → index-xwtRrgVY.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CRLP56-W.js → index-yFQWSRoW.js} +1 -1
  136. package/src/assets/web-panel/assets/{index-wYRjvvbl.js → index-zNkw0IY7.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-C44ZER6c.js → initDefaultProps-BQR2HnKb.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-DbNzzScd.js → motion-gAz6l-nZ.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-BrRrDH2u.js → move-Cy5TN856.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-DZ_T_Pw8.js → mtc-parser-BeT7VrGi.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-5QHHk_V3.js → omit-BLN3mlgK.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-BdxEl0G2.js → pickAttrs-DtwZ8u2d.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-CGyD615D.js → placementArrow-ViMwQSa-.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-QqcWAQmN.js → responsiveObserve-dEL3u4E7.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-BumQrJ5R.js → slide-l05us-Gz.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-Pf64olM7.js → statusUtils-Bk-Qn2Og.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-DNwm5SQZ.js → styleChecker-Bn186qeY.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-c3gLKs6e.js → useFlexGapSupport-WP-ugR3H.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-TzTd6NDM.js → useFs-1SvoeSCL.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-BP5E_0Fe.js → usePersonalDataHub-CfNU8N3z.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-CVEkRfZU.js → vnode-Bd2Ya101.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-C18X_8TJ.js → zoom-Def_MeZv.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/command-help-index.json +2 -2
  155. package/src/commands/agent.js +20 -20
  156. package/src/commands/goal.js +299 -1
  157. package/src/commands/plugin.js +129 -68
  158. package/src/constants.js +2 -2
  159. package/src/data/changelog.json +26 -0
  160. package/src/lib/cli-goal-catalog.js +129 -0
  161. package/src/lib/cli-goal-source.js +119 -0
  162. package/src/lib/coding-task-guidance.js +3 -1
  163. package/src/lib/config-schema.js +1 -1
  164. package/src/lib/eval/stream-terminal.js +27 -2
  165. package/src/lib/evolution/evolution-artifact-ports.js +22 -11
  166. package/src/lib/evolution/evolution-file-witness.js +86 -63
  167. package/src/lib/evolution/evolution-ledger-file-backend.js +22 -0
  168. package/src/lib/evolution/evolution-ledger-file-manifest-catalog.js +34 -35
  169. package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +32 -39
  170. package/src/lib/evolution/evolution-ledger.js +367 -169
  171. package/src/lib/evolution/evolution-manifest-state-reader.js +58 -0
  172. package/src/lib/evolution/evolution-physical-state-reader.js +308 -0
  173. package/src/lib/evolution/evolution-witness-segments.js +12 -13
  174. package/src/lib/evolution/governed-wiki-pruning-ledger-adapter.js +82 -56
  175. package/src/lib/evolution/rrsi-observed-text-index.js +17 -1
  176. package/src/lib/evolution/rrsi-tenant-index-anchor-history.js +5 -0
  177. package/src/lib/execution-location-failure-site.js +20 -3
  178. package/src/lib/goal-store.js +8 -1
  179. package/src/lib/image-input.js +43 -14
  180. package/src/lib/llm-providers.js +1 -0
  181. package/src/lib/plugin-runtime/capabilities.js +10 -0
  182. package/src/lib/plugin-runtime/cli-goal-draft-request.js +138 -0
  183. package/src/lib/plugin-runtime/cli-goal-drafts.js +413 -0
  184. package/src/lib/plugin-runtime/cli-goal-extension-host.js +234 -0
  185. package/src/lib/plugin-runtime/examples/goal-description-drafter/plugin.json +17 -0
  186. package/src/lib/plugin-runtime/examples/goal-evidence-viewer/plugin.json +25 -0
  187. package/src/lib/plugin-runtime/examples/goal-progress-viewer/plugin.json +21 -0
  188. package/src/lib/plugin-runtime/examples/project-delivery-assistant/plugin.json +38 -0
  189. package/src/lib/plugin-runtime/goal-extension-contract.js +190 -0
  190. package/src/lib/plugin-runtime/goal-extensions.js +277 -0
  191. package/src/lib/plugin-runtime/install.js +40 -40
  192. package/src/lib/plugin-runtime/manifest.js +12 -0
  193. package/src/lib/plugin-runtime/policy.js +1 -0
  194. package/src/lib/project-goal-connection.js +225 -0
  195. package/src/lib/project-goal-draft-connection.js +217 -0
  196. package/src/lib/provider-http-error.js +4 -5
  197. package/src/lib/task-model-selector.js +1 -1
  198. package/src/repl/agent-repl.js +14 -7
  199. package/src/repl/config-summary.js +1 -1
  200. package/src/runtime/agent-core.js +1 -1
  201. package/src/runtime/coding-agent-contract-shared.cjs +3 -2
  202. package/src/runtime/headless-stream.js +4 -6
  203. package/src/runtime/policies/agent-policy.js +1 -0
  204. package/src/assets/web-panel/assets/MarkdownRenderer-BsSiyE-A.js +0 -1
  205. package/src/assets/web-panel/assets/MobileProjects-ClRlavdF.js +0 -1
  206. package/src/assets/web-panel/assets/Notes-4S6uts9Q.js +0 -7
  207. package/src/assets/web-panel/assets/OrderTableRenderer-CDVAlBUf.js +0 -1
  208. package/src/assets/web-panel/assets/Projects-BvP_Os-1.js +0 -1
  209. package/src/assets/web-panel/assets/devWarning-BZUZgMkO.js +0 -1
  210. package/src/assets/web-panel/assets/index--_-pNhk0.js +0 -1
  211. package/src/assets/web-panel/assets/index-ftwO31cp.js +0 -1
@@ -0,0 +1,58 @@
1
+ /** Stable manifest windows; no retention, installation or graph grant. */
2
+ import path from "node:path";
3
+ import {
4
+ createEvolutionPhysicalStateReader,
5
+ captureEvolutionPhysicalStateReaderDirectoryBoundary,
6
+ } from "./evolution-physical-state-reader.js";
7
+
8
+ const DIRECTORY_BOUNDARIES = new WeakMap();
9
+
10
+ export const EVOLUTION_MANIFEST_STATE_READ_FAILED_CODE =
11
+ "CC_EVOLUTION_MANIFEST_STATE_READ_FAILED";
12
+
13
+ export function createEvolutionManifestStateReader({
14
+ file,
15
+ fsImpl,
16
+ maximumBytes,
17
+ label,
18
+ }) {
19
+ if (
20
+ typeof file !== "string" ||
21
+ !path.isAbsolute(file) ||
22
+ path.resolve(file) !== file ||
23
+ !Number.isSafeInteger(maximumBytes) ||
24
+ maximumBytes < 1 ||
25
+ typeof label !== "string" ||
26
+ !label ||
27
+ !fsImpl
28
+ )
29
+ throw new TypeError(
30
+ "manifest state reader requires a canonical file, filesystem and byte limit",
31
+ );
32
+ const reader = createEvolutionPhysicalStateReader({
33
+ directoryPath: path.dirname(file),
34
+ fsImpl,
35
+ maximumBytes,
36
+ label,
37
+ errorCode: EVOLUTION_MANIFEST_STATE_READ_FAILED_CODE,
38
+ });
39
+ const stateReader = Object.freeze({
40
+ read: () => reader.read(file, { allowAbsent: true }),
41
+ recheck: reader.recheck,
42
+ });
43
+ DIRECTORY_BOUNDARIES.set(
44
+ stateReader,
45
+ captureEvolutionPhysicalStateReaderDirectoryBoundary(reader),
46
+ );
47
+ return stateReader;
48
+ }
49
+
50
+ export function captureEvolutionManifestStateReaderDirectoryBoundary(reader) {
51
+ if (arguments.length !== 1)
52
+ throw new TypeError("manifest directory capture takes one original reader");
53
+ const boundary = DIRECTORY_BOUNDARIES.get(reader);
54
+ if (!boundary)
55
+ throw new TypeError("an original manifest state reader is required");
56
+ boundary.recheck();
57
+ return boundary;
58
+ }
@@ -0,0 +1,308 @@
1
+ /** Stable local state windows, never a retention, installation or graph grant. */
2
+ import path from "node:path";
3
+ import { artifactPhysicalIdentity } from "./evolution-artifact-identity.js";
4
+ import { withEvolutionFileIdentity } from "./evolution-file-identity.js";
5
+ import { readBoundedDescriptor } from "./bounded-descriptor-read.js";
6
+
7
+ const DIRECTORY_BOUNDARIES = new WeakMap();
8
+
9
+ const FILE_FIELDS = Object.freeze([
10
+ "dev",
11
+ "ino",
12
+ "mode",
13
+ "uid",
14
+ "gid",
15
+ "nlink",
16
+ "size",
17
+ "mtimeNs",
18
+ "ctimeNs",
19
+ ]);
20
+ const DIRECTORY_FIELDS = Object.freeze(["dev", "ino", "mode", "uid", "gid"]);
21
+ const MUTATION_FIELDS = FILE_FIELDS.filter(
22
+ (field) => field !== "dev" && field !== "ino",
23
+ );
24
+ const samePath = (left, right) =>
25
+ process.platform === "win32"
26
+ ? left.toLowerCase() === right.toLowerCase()
27
+ : left === right;
28
+ const birthtime = (stat) => artifactPhysicalIdentity(stat).birthtimeMs;
29
+ const sameMetadata = (left, right) =>
30
+ MUTATION_FIELDS.every((field) => left[field] === right[field]) &&
31
+ birthtime(left) === birthtime(right);
32
+ const sameStat = (left, right) =>
33
+ left.dev === right.dev && left.ino === right.ino && sameMetadata(left, right);
34
+
35
+ export function createEvolutionPhysicalStateReader({
36
+ directoryPath: directory,
37
+ fsImpl,
38
+ maximumBytes,
39
+ minimumBytes = 0,
40
+ directoryMode = null,
41
+ sizeError = "exceeds maximumBytes",
42
+ label,
43
+ errorCode,
44
+ readFailureMessage = `${label} bounded state read failed`,
45
+ }) {
46
+ if (
47
+ typeof directory !== "string" ||
48
+ !path.isAbsolute(directory) ||
49
+ path.resolve(directory) !== directory ||
50
+ !Number.isSafeInteger(maximumBytes) ||
51
+ maximumBytes < 1 ||
52
+ !Number.isSafeInteger(minimumBytes) ||
53
+ minimumBytes < 0 ||
54
+ minimumBytes > maximumBytes ||
55
+ (directoryMode !== null && typeof directoryMode !== "bigint") ||
56
+ typeof sizeError !== "string" ||
57
+ !sizeError ||
58
+ typeof errorCode !== "string" ||
59
+ !errorCode ||
60
+ typeof readFailureMessage !== "string" ||
61
+ !readFailureMessage ||
62
+ typeof label !== "string" ||
63
+ !label ||
64
+ !fsImpl
65
+ )
66
+ throw new TypeError(
67
+ "physical state reader requires a canonical directory, filesystem, code and byte bounds",
68
+ );
69
+ const physicalPath = (target) => {
70
+ const realpath = fsImpl.realpathSync;
71
+ return typeof realpath.native === "function"
72
+ ? realpath.native(target)
73
+ : realpath.call(fsImpl, target);
74
+ };
75
+ const fail = (message, cause) => {
76
+ throw Object.assign(new Error(message, cause ? { cause } : undefined), {
77
+ code: errorCode,
78
+ });
79
+ };
80
+ const guard = (operation) => {
81
+ try {
82
+ return operation();
83
+ } catch (cause) {
84
+ if (cause?.code === errorCode) throw cause;
85
+ fail(readFailureMessage, cause);
86
+ }
87
+ };
88
+ const inspectDirectory = () => {
89
+ const stat = fsImpl.lstatSync(directory, { bigint: true });
90
+ if (DIRECTORY_FIELDS.some((field) => typeof stat?.[field] !== "bigint"))
91
+ fail(`${label} directory requires full-precision stat fields`);
92
+ if (
93
+ !stat.isDirectory() ||
94
+ stat.isSymbolicLink() ||
95
+ !samePath(physicalPath(directory), directory)
96
+ )
97
+ fail(`${label} directory must be physically canonical`);
98
+ if (directoryMode !== null && (stat.mode & 0o7777n) !== directoryMode)
99
+ fail(`${label} directory permissions are invalid`);
100
+ return stat;
101
+ };
102
+ // Contents, link count and mutation times may change during legitimate locks
103
+ // and temporary+rename writes. Only the original directory identity/owner is
104
+ // retained; mutable leaf files acquire a new identity on every read.
105
+ const originalDirectory = guard(() => {
106
+ const stat = inspectDirectory();
107
+ return Object.freeze({
108
+ ...Object.fromEntries(
109
+ DIRECTORY_FIELDS.map((field) => [field, stat[field]]),
110
+ ),
111
+ birthtimeMs: birthtime(stat),
112
+ });
113
+ });
114
+ const recheck = () => {
115
+ const current = inspectDirectory();
116
+ if (
117
+ !DIRECTORY_FIELDS.every(
118
+ (field) => originalDirectory[field] === current[field],
119
+ ) ||
120
+ originalDirectory.birthtimeMs !== birthtime(current)
121
+ )
122
+ fail(`${label} directory physical identity changed`);
123
+ };
124
+ const safeStat = (stat, source, byteLimit) => {
125
+ if (FILE_FIELDS.some((field) => typeof stat?.[field] !== "bigint"))
126
+ fail(`${label} ${source} requires full-precision stat fields`);
127
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1n)
128
+ fail(`${label} file identity is invalid`);
129
+ if (stat.size < BigInt(minimumBytes) || stat.size > BigInt(byteLimit))
130
+ fail(`${label} ${sizeError}`);
131
+ };
132
+ // Only identity-helper directory opens use this adapter. All operations,
133
+ // including Windows path/handle bridging, retain the caller's original fsImpl.
134
+ const directoryFs = Object.freeze({
135
+ constants: fsImpl.constants,
136
+ realpathSync: Object.assign(
137
+ (...args) => fsImpl.realpathSync(...args),
138
+ typeof fsImpl.realpathSync.native === "function"
139
+ ? { native: (...args) => fsImpl.realpathSync.native(...args) }
140
+ : {},
141
+ ),
142
+ lstatSync: (...args) => fsImpl.lstatSync(...args),
143
+ fstatSync: (...args) => fsImpl.fstatSync(...args),
144
+ closeSync: (...args) => fsImpl.closeSync(...args),
145
+ openSync: (target, flags) =>
146
+ fsImpl.openSync(
147
+ target,
148
+ flags |
149
+ (fsImpl.constants.O_NONBLOCK || 0) |
150
+ (fsImpl.constants.O_DIRECTORY || 0),
151
+ ),
152
+ });
153
+ const read = (file, byteLimit, allowAbsent, prepareFile) => {
154
+ if (
155
+ typeof file !== "string" ||
156
+ !path.isAbsolute(file) ||
157
+ path.resolve(file) !== file ||
158
+ path.dirname(file) !== directory ||
159
+ !Number.isSafeInteger(byteLimit) ||
160
+ byteLimit < 0 ||
161
+ typeof allowAbsent !== "boolean" ||
162
+ (prepareFile !== null && typeof prepareFile !== "function")
163
+ )
164
+ throw new TypeError(
165
+ "physical state read requires a direct canonical child and finite byte bound",
166
+ );
167
+ byteLimit = Math.min(maximumBytes, byteLimit);
168
+ recheck();
169
+ let before;
170
+ try {
171
+ before = fsImpl.lstatSync(file, { bigint: true });
172
+ } catch (cause) {
173
+ // Preserve the backend's absent-state contract, without treating access
174
+ // errors, dangling links or a disappearance during open/read as absence.
175
+ if (cause?.code !== "ENOENT" || !allowAbsent) throw cause;
176
+ recheck();
177
+ return null;
178
+ }
179
+ safeStat(before, "path", byteLimit);
180
+ if (prepareFile !== null) {
181
+ // Preserve existing private-file preparation without treating a repaired
182
+ // ctime/mode as mutation inside the later stable content window.
183
+ const identity = {
184
+ dev: before.dev,
185
+ ino: before.ino,
186
+ birthtimeMs: birthtime(before),
187
+ };
188
+ prepareFile(file);
189
+ recheck();
190
+ before = fsImpl.lstatSync(file, { bigint: true });
191
+ safeStat(before, "path", byteLimit);
192
+ if (
193
+ before.dev !== identity.dev ||
194
+ before.ino !== identity.ino ||
195
+ birthtime(before) !== identity.birthtimeMs
196
+ )
197
+ fail(`${label} file changed during private-file preparation`);
198
+ }
199
+ if (!samePath(physicalPath(file), file))
200
+ fail(`${label} file must be physically canonical`);
201
+ return withEvolutionFileIdentity(directoryFs, file, (samePathHandle) => {
202
+ recheck();
203
+ let descriptor = null;
204
+ try {
205
+ descriptor = fsImpl.openSync(
206
+ file,
207
+ fsImpl.constants.O_RDONLY |
208
+ (fsImpl.constants.O_NOFOLLOW || 0) |
209
+ (fsImpl.constants.O_NONBLOCK || 0),
210
+ );
211
+ const opened = fsImpl.fstatSync(descriptor, { bigint: true });
212
+ safeStat(opened, "descriptor", byteLimit);
213
+ if (!samePathHandle(before, opened) || !sameMetadata(before, opened))
214
+ fail(`${label} path and descriptor identities differ`);
215
+ const bytes = readBoundedDescriptor(
216
+ fsImpl,
217
+ descriptor,
218
+ Number(opened.size),
219
+ byteLimit,
220
+ );
221
+ const afterDescriptor = fsImpl.fstatSync(descriptor, { bigint: true });
222
+ const afterPath = fsImpl.lstatSync(file, { bigint: true });
223
+ safeStat(afterDescriptor, "descriptor", byteLimit);
224
+ safeStat(afterPath, "path", byteLimit);
225
+ if (!sameStat(opened, afterDescriptor))
226
+ fail(`${label} descriptor changed during bounded read`);
227
+ if (
228
+ !sameStat(before, afterPath) ||
229
+ !samePathHandle(afterPath, afterDescriptor) ||
230
+ !sameMetadata(afterPath, afterDescriptor) ||
231
+ !samePath(physicalPath(file), file)
232
+ )
233
+ fail(`${label} pathname changed during bounded read`);
234
+ recheck();
235
+ return bytes;
236
+ } finally {
237
+ if (descriptor !== null) fsImpl.closeSync(descriptor);
238
+ }
239
+ });
240
+ };
241
+ const reader = Object.freeze({
242
+ read: (
243
+ file,
244
+ {
245
+ maximumBytes: byteLimit = maximumBytes,
246
+ allowAbsent = false,
247
+ prepareFile = null,
248
+ } = {},
249
+ ) => guard(() => read(file, byteLimit, allowAbsent, prepareFile)),
250
+ recheck: () => guard(recheck),
251
+ });
252
+ // Project the original construction pin; later capture never claims a new
253
+ // pathname. The brand identifies this factory/its original ports, not native
254
+ // filesystem trust, installation authority, or any leaf inventory.
255
+ const descriptor = Object.freeze({
256
+ schema: "chainlesschain.evolution-physical-state-directory-boundary/v1",
257
+ scope: "single-original-state-directory",
258
+ directory: Object.freeze({
259
+ path: directory,
260
+ identity: Object.freeze({
261
+ ...Object.fromEntries(
262
+ DIRECTORY_FIELDS.map((field) => [
263
+ field,
264
+ String(originalDirectory[field]),
265
+ ]),
266
+ ),
267
+ birthtimeMs: originalDirectory.birthtimeMs,
268
+ }),
269
+ }),
270
+ fileIdentityVerified: false,
271
+ fileContentsVerified: false,
272
+ storageInventoryVerified: false,
273
+ retentionStorageVerified: false,
274
+ fullPhysicalStorageGraphVerified: false,
275
+ rootAuthorityVerified: false,
276
+ routingPinVerified: false,
277
+ crossProcessRollbackProtectionVerified: false,
278
+ grantsMutationOrPromotionAuthority: false,
279
+ });
280
+ const recheckDirectory = (...args) => {
281
+ if (args.length)
282
+ throw new TypeError("directory boundary takes no replacement context");
283
+ return guard(() => {
284
+ recheck();
285
+ return descriptor;
286
+ });
287
+ };
288
+ recheckDirectory();
289
+ DIRECTORY_BOUNDARIES.set(
290
+ reader,
291
+ Object.freeze({
292
+ descriptor,
293
+ recheck: Object.freeze(recheckDirectory),
294
+ }),
295
+ );
296
+ return reader;
297
+ }
298
+
299
+ /** Metadata-only original directory of a genuine physical state reader. */
300
+ export function captureEvolutionPhysicalStateReaderDirectoryBoundary(reader) {
301
+ if (arguments.length !== 1)
302
+ throw new TypeError("directory capture takes one original reader");
303
+ const boundary = DIRECTORY_BOUNDARIES.get(reader);
304
+ if (!boundary)
305
+ throw new TypeError("an original physical state reader is required");
306
+ boundary.recheck();
307
+ return boundary;
308
+ }
@@ -1,5 +1,4 @@
1
1
  import path from "node:path";
2
- import { ensurePrivateDirectory } from "../secure-fs.js";
3
2
 
4
3
  export const EVOLUTION_SEGMENTED_WITNESS_SCHEMA =
5
4
  "chainlesschain.evolution-file-witness-store/v2";
@@ -43,6 +42,8 @@ function matchesDiscard(entry, snapshot) {
43
42
  export function createEvolutionWitnessSegments({
44
43
  filePath,
45
44
  fsImpl,
45
+ checkDirectory,
46
+ prepareDirectory,
46
47
  readBytes,
47
48
  writeBytes,
48
49
  confirmDurable,
@@ -54,7 +55,6 @@ export function createEvolutionWitnessSegments({
54
55
  maximumHistoryBytes,
55
56
  }) {
56
57
  const segmentDirectory = `${filePath}.segments-v2`;
57
- const secureOptions = { deps: { fs: fsImpl }, failIfUnavailable: true };
58
58
  const metadata = new WeakMap();
59
59
  const cache = new Map();
60
60
  let cacheBytes = 0;
@@ -65,14 +65,6 @@ export function createEvolutionWitnessSegments({
65
65
  return path.join(segmentDirectory, `${digest.slice(7)}.json`);
66
66
  };
67
67
 
68
- const assertDirectory = () => {
69
- ensurePrivateDirectory(segmentDirectory, secureOptions);
70
- const observed = fsImpl.lstatSync(segmentDirectory);
71
- if (!observed.isDirectory() || observed.isSymbolicLink()) {
72
- throw new Error("witness segment directory is invalid");
73
- }
74
- };
75
-
76
68
  const validateSegment = (segment, previous, trustEpoch) => {
77
69
  exact(segment, SEGMENT_KEYS, "witness segment");
78
70
  if (
@@ -150,7 +142,7 @@ export function createEvolutionWitnessSegments({
150
142
  ) {
151
143
  throw new Error("segmented witness store capacity or schema is invalid");
152
144
  }
153
- assertDirectory();
145
+ checkDirectory();
154
146
  const seen = new Set();
155
147
  const observations = {
156
148
  discards: (options.discards || [])
@@ -254,7 +246,7 @@ export function createEvolutionWitnessSegments({
254
246
  ) {
255
247
  throw new Error("witness history exceeds its configured record capacity");
256
248
  }
257
- assertDirectory();
249
+ prepareDirectory();
258
250
  let history = store.history;
259
251
  let discardedAnchors = store.discardedAnchors;
260
252
  let segmentBytes = existing?.segmentBytes ?? 0;
@@ -282,7 +274,14 @@ export function createEvolutionWitnessSegments({
282
274
  }
283
275
  const digest = hash(bytes);
284
276
  const destination = segmentPath(digest);
285
- if (fsImpl.existsSync(destination)) {
277
+ let exists = false;
278
+ try {
279
+ fsImpl.lstatSync(destination, { bigint: true });
280
+ exists = true;
281
+ } catch (cause) {
282
+ if (cause?.code !== "ENOENT") throw cause;
283
+ }
284
+ if (exists) {
286
285
  if (!readBytes(destination).equals(bytes)) {
287
286
  throw new Error(
288
287
  "existing witness segment differs from its content address",
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA,
3
3
  isEvolutionLedgerArtifactResolver,
4
+ captureEvolutionLedgerBatchResolver,
4
5
  } from "./evolution-artifact-ports.js";
5
6
  import {
6
7
  EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA,
@@ -17,6 +18,7 @@ import {
17
18
 
18
19
  export const WIKI_PRUNING_JOURNAL_EVENT_TYPE = "wiki.pruning.journal-committed";
19
20
  export const WIKI_PRUNING_JOURNAL_ARTIFACT_TYPE = "wiki-pruning-journal";
21
+ const JOURNAL_ARTIFACT_RESOLVE_BATCH_SIZE = 4;
20
22
  const STORES = new WeakMap();
21
23
  const DIGEST = /^sha256:[a-f0-9]{64}$/u;
22
24
  function currentContext(head) {
@@ -98,7 +100,7 @@ export class GovernedWikiPruningLedgerAdapter {
98
100
  #read;
99
101
  #verifyLedger;
100
102
  #append;
101
- #resolve;
103
+ #resolveBatch;
102
104
  #verifyPlan;
103
105
  #verifyOperation;
104
106
  #verifyOperations;
@@ -122,7 +124,9 @@ export class GovernedWikiPruningLedgerAdapter {
122
124
  this.#read = capture(ledger, "read");
123
125
  this.#verifyLedger = capture(ledger, "verify");
124
126
  this.#append = capture(ledger, "appendDomainEvent");
125
- this.#resolve = ledgerArtifactResolver;
127
+ this.#resolveBatch = captureEvolutionLedgerBatchResolver(
128
+ ledgerArtifactResolver,
129
+ );
126
130
  this.#verifyPlan = capture(planVerifier, "verify");
127
131
  this.#verifyOperation = capture(operationReceiptVerifier, "verify");
128
132
  this.#verifyOperations =
@@ -215,74 +219,96 @@ export class GovernedWikiPruningLedgerAdapter {
215
219
  event.tenantId === this.descriptor.tenantId &&
216
220
  event.correlationId === this.descriptor.streamId,
217
221
  );
218
- const history = [];
219
- const plans = new Set();
222
+ let previousEvent = null;
220
223
  for (const event of events) {
221
- const previous = history.at(-1) ?? null;
222
224
  if (
223
225
  event.artifactTenantId !== this.descriptor.artifactTenantId ||
224
226
  event.decision !== "committed" ||
225
227
  event.skillName !== null ||
226
228
  !Array.isArray(event.sourceRefs) ||
227
- event.sourceRefs.length !== (previous ? 1 : 0) ||
228
- (previous &&
229
+ event.sourceRefs.length !== (previousEvent ? 1 : 0) ||
230
+ (previousEvent &&
229
231
  pruningCanonical(event.sourceRefs[0]) !==
230
- pruningCanonical(previous.event.subjectRef))
232
+ pruningCanonical(previousEvent.subjectRef))
231
233
  )
232
234
  fail("pruning journal event lineage is invalid");
233
- const resolution = this.#resolve({
234
- epoch: head.epoch,
235
- ledgerId: head.ledgerId,
236
- ref: event.subjectRef,
237
- tenantId: this.descriptor.artifactTenantId,
238
- });
239
- if (
240
- resolution?.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
241
- resolution.authenticated !== true ||
242
- resolution.found !== true ||
243
- resolution.ref !== event.subjectRef.ref ||
244
- resolution.digest !== event.subjectRef.digest ||
245
- !DIGEST.test(resolution.receiptDigest) ||
246
- !Buffer.isBuffer(resolution.bytes) ||
247
- resolution.bytes.length > WIKI_PRUNING_JOURNAL_MAX_BYTES
248
- )
249
- fail("pruning journal artifact resolution is invalid");
250
- const record = JSON.parse(
251
- new TextDecoder("utf-8", { fatal: true }).decode(resolution.bytes),
235
+ previousEvent = event;
236
+ }
237
+ const history = [];
238
+ const plans = new Set();
239
+ for (
240
+ let offset = 0;
241
+ offset < events.length;
242
+ offset += JOURNAL_ARTIFACT_RESOLVE_BATCH_SIZE
243
+ ) {
244
+ const batchEvents = events.slice(
245
+ offset,
246
+ offset + JOURNAL_ARTIFACT_RESOLVE_BATCH_SIZE,
252
247
  );
253
- if (
254
- record?.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
255
- record.tenantId !== this.descriptor.artifactTenantId ||
256
- record.audience !== this.descriptor.audience ||
257
- record.purpose !== this.descriptor.purpose ||
258
- record.retention !== "ledger" ||
259
- record.type !== WIKI_PRUNING_JOURNAL_ARTIFACT_TYPE
260
- )
261
- fail("pruning journal durable artifact binding is invalid");
262
- const state = verifyWikiPruningJournal(
263
- record.value,
264
- this.descriptor.tenantId,
248
+ const resolutions = this.#resolveBatch(
249
+ batchEvents.map((event) => ({
250
+ epoch: head.epoch,
251
+ ledgerId: head.ledgerId,
252
+ ref: event.subjectRef,
253
+ tenantId: this.descriptor.artifactTenantId,
254
+ })),
265
255
  );
266
256
  if (
267
- event.eventId !==
268
- `${WIKI_PRUNING_JOURNAL_EVENT_TYPE}.${state.journalDigest.slice(7)}`
257
+ !Array.isArray(resolutions) ||
258
+ resolutions.length !== batchEvents.length
269
259
  )
270
- fail("pruning journal event identity is invalid");
271
- assertPruningJournalTransition(previous?.state ?? null, state);
272
- if (previous?.state.plan.planDigest !== state.plan.planDigest) {
273
- if (plans.has(state.plan.planDigest))
274
- fail("pruning journal restarted a historical plan");
275
- plans.add(state.plan.planDigest);
260
+ fail("pruning journal artifact batch resolution is incomplete");
261
+ for (const [batchIndex, event] of batchEvents.entries()) {
262
+ const previous = history.at(-1) ?? null;
263
+ const resolution = resolutions[batchIndex];
264
+ if (
265
+ resolution?.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
266
+ resolution.authenticated !== true ||
267
+ resolution.found !== true ||
268
+ resolution.ref !== event.subjectRef.ref ||
269
+ resolution.digest !== event.subjectRef.digest ||
270
+ !DIGEST.test(resolution.receiptDigest) ||
271
+ !Buffer.isBuffer(resolution.bytes) ||
272
+ resolution.bytes.length > WIKI_PRUNING_JOURNAL_MAX_BYTES
273
+ )
274
+ fail("pruning journal artifact resolution is invalid");
275
+ const record = JSON.parse(
276
+ new TextDecoder("utf-8", { fatal: true }).decode(resolution.bytes),
277
+ );
278
+ if (
279
+ record?.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
280
+ record.tenantId !== this.descriptor.artifactTenantId ||
281
+ record.audience !== this.descriptor.audience ||
282
+ record.purpose !== this.descriptor.purpose ||
283
+ record.retention !== "ledger" ||
284
+ record.type !== WIKI_PRUNING_JOURNAL_ARTIFACT_TYPE
285
+ )
286
+ fail("pruning journal durable artifact binding is invalid");
287
+ const state = verifyWikiPruningJournal(
288
+ record.value,
289
+ this.descriptor.tenantId,
290
+ );
291
+ if (
292
+ event.eventId !==
293
+ `${WIKI_PRUNING_JOURNAL_EVENT_TYPE}.${state.journalDigest.slice(7)}`
294
+ )
295
+ fail("pruning journal event identity is invalid");
296
+ assertPruningJournalTransition(previous?.state ?? null, state);
297
+ if (previous?.state.plan.planDigest !== state.plan.planDigest) {
298
+ if (plans.has(state.plan.planDigest))
299
+ fail("pruning journal restarted a historical plan");
300
+ plans.add(state.plan.planDigest);
301
+ }
302
+ const samePlan =
303
+ previous?.state.plan.planDigest === state.plan.planDigest;
304
+ const entry = {
305
+ event,
306
+ state,
307
+ prepared: samePlan ? previous.prepared : { event, state },
308
+ };
309
+ if (samePlan) history[history.length - 1] = entry;
310
+ else history.push(entry);
276
311
  }
277
- const samePlan =
278
- previous?.state.plan.planDigest === state.plan.planDigest;
279
- const entry = {
280
- event,
281
- state,
282
- prepared: samePlan ? previous.prepared : { event, state },
283
- };
284
- if (samePlan) history[history.length - 1] = entry;
285
- else history.push(entry);
286
312
  }
287
313
  // Every immutable event/transition above is checked. Per plan, authenticate
288
314
  // its source at preparation and its full retained receipt prefix at the
@@ -2,7 +2,10 @@
2
2
  import { createHash } from "node:crypto";
3
3
  import { isProxy } from "node:util/types";
4
4
  import { captureRrsiRegistryStorePolicy } from "./rrsi-registry-store-policy.js";
5
- import { captureEvolutionLedgerFileBackend } from "./evolution-ledger-file-backend.js";
5
+ import {
6
+ captureEvolutionLedgerFileBackend,
7
+ captureEvolutionLedgerFileBackendSourceDirectoryBoundary,
8
+ } from "./evolution-ledger-file-backend.js";
6
9
  import {
7
10
  EvolutionArtifactPorts,
8
11
  EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA,
@@ -172,6 +175,12 @@ export function createRrsiObservedTextIndex(input) {
172
175
  )
173
176
  hold("text index requires the original policy storage graph");
174
177
  const ledger = backend.ledger;
178
+ const sourceJournalBoundary =
179
+ captureEvolutionLedgerFileBackendSourceDirectoryBoundary(backend);
180
+ const sourceJournalDirectoryBoundaryDigest = rrsiHash(
181
+ "chainlesschain.rrsi-source-journal-directory-boundary/v1",
182
+ sourceJournalBoundary.descriptor,
183
+ );
175
184
  const artifactBoundary = captureEvolutionArtifactStoreDirectoryBoundary(
176
185
  options.artifactPorts,
177
186
  );
@@ -597,6 +606,7 @@ export function createRrsiObservedTextIndex(input) {
597
606
  hold("preparation declaration reference differs");
598
607
  }
599
608
  function load() {
609
+ sourceJournalBoundary.recheck();
600
610
  artifactBoundary.recheck();
601
611
  const before = head(methods.verify());
602
612
  if (!same(identity(before), originalIdentity))
@@ -683,6 +693,7 @@ export function createRrsiObservedTextIndex(input) {
683
693
  if (!same(before, head(methods.verify())))
684
694
  hold("text index journal changed during authenticated readback");
685
695
  artifactBoundary.recheck();
696
+ sourceJournalBoundary.recheck();
686
697
  return { head: before, events, records, entries, operations };
687
698
  }
688
699
  function historyPrefix(state, checkpoint) {
@@ -778,6 +789,9 @@ export function createRrsiObservedTextIndex(input) {
778
789
  artifactStoreDirectoryBoundaryDigest,
779
790
  artifactStoreDirectoryBoundaryRechecked: true,
780
791
  artifactStoreBoundaryScope: "root-and-files-directories",
792
+ sourceJournalDirectoryBoundaryDigest,
793
+ sourceJournalDirectoryBoundaryRechecked: true,
794
+ sourceJournalBoundaryScope: "source-journal-four-directories",
781
795
  fullPhysicalStorageGraphVerified: false,
782
796
  tenantWideIndexAuthorityVerified: false,
783
797
  generationProvenanceVerified: false,
@@ -787,10 +801,12 @@ export function createRrsiObservedTextIndex(input) {
787
801
  );
788
802
  const recheck = () =>
789
803
  guarded(() => {
804
+ sourceJournalBoundary.recheck();
790
805
  artifactBoundary.recheck();
791
806
  if (!same(state.head, head(methods.verify())))
792
807
  hold("observed history head changed after prefix capture");
793
808
  artifactBoundary.recheck();
809
+ sourceJournalBoundary.recheck();
794
810
  return true;
795
811
  });
796
812
  recheck();