@plurnk/plurnk-service 1.3.12 → 1.4.0

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 (469) hide show
  1. package/.env.defaults +84 -77
  2. package/INSTALL.md +50 -25
  3. package/README.md +24 -10
  4. package/SPEC.md +2311 -928
  5. package/dist/Paths.d.ts +4 -1
  6. package/dist/Paths.d.ts.map +1 -1
  7. package/dist/Paths.js +23 -21
  8. package/dist/Paths.js.map +1 -1
  9. package/dist/build-info.json +1 -1
  10. package/dist/content/edit-receipt.d.ts +13 -28
  11. package/dist/content/edit-receipt.d.ts.map +1 -1
  12. package/dist/content/edit-receipt.js +285 -104
  13. package/dist/content/edit-receipt.js.map +1 -1
  14. package/dist/content/edited-span.d.ts.map +1 -1
  15. package/dist/content/edited-span.js +2 -5
  16. package/dist/content/edited-span.js.map +1 -1
  17. package/dist/content/index.d.ts +3 -3
  18. package/dist/content/index.d.ts.map +1 -1
  19. package/dist/content/index.js +1 -1
  20. package/dist/content/index.js.map +1 -1
  21. package/dist/content/line-marker.d.ts +6 -9
  22. package/dist/content/line-marker.d.ts.map +1 -1
  23. package/dist/content/line-marker.js +7 -11
  24. package/dist/content/line-marker.js.map +1 -1
  25. package/dist/content/matcher.d.ts +14 -25
  26. package/dist/content/matcher.d.ts.map +1 -1
  27. package/dist/content/matcher.js +77 -107
  28. package/dist/content/matcher.js.map +1 -1
  29. package/dist/content/mimetype-binary.d.ts +2 -2
  30. package/dist/content/mimetype-binary.d.ts.map +1 -1
  31. package/dist/content/mimetype-binary.js +10 -10
  32. package/dist/content/mimetype-binary.js.map +1 -1
  33. package/dist/content/path-mimetype.js +3 -3
  34. package/dist/content/path-mimetype.js.map +1 -1
  35. package/dist/content/read-resolve.d.ts +8 -4
  36. package/dist/content/read-resolve.d.ts.map +1 -1
  37. package/dist/content/read-resolve.js +101 -49
  38. package/dist/content/read-resolve.js.map +1 -1
  39. package/dist/core/BranchReceipt.d.ts +7 -0
  40. package/dist/core/BranchReceipt.d.ts.map +1 -0
  41. package/dist/core/BranchReceipt.js +27 -0
  42. package/dist/core/BranchReceipt.js.map +1 -0
  43. package/dist/core/BudgetOverflow.d.ts +23 -0
  44. package/dist/core/BudgetOverflow.d.ts.map +1 -0
  45. package/dist/core/BudgetOverflow.js +61 -0
  46. package/dist/core/BudgetOverflow.js.map +1 -0
  47. package/dist/core/BudgetReadout.d.ts +8 -0
  48. package/dist/core/BudgetReadout.d.ts.map +1 -0
  49. package/dist/core/BudgetReadout.js +79 -0
  50. package/dist/core/BudgetReadout.js.map +1 -0
  51. package/dist/core/ChannelWrite.d.ts +28 -9
  52. package/dist/core/ChannelWrite.d.ts.map +1 -1
  53. package/dist/core/ChannelWrite.js +28 -21
  54. package/dist/core/ChannelWrite.js.map +1 -1
  55. package/dist/core/ChannelWrite.sql +11 -10
  56. package/dist/core/CoreSchemeServices.d.ts +10 -4
  57. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  58. package/dist/core/CoreSchemeServices.js +7 -1
  59. package/dist/core/CoreSchemeServices.js.map +1 -1
  60. package/dist/core/Dispatcher.d.ts +29 -20
  61. package/dist/core/Dispatcher.d.ts.map +1 -1
  62. package/dist/core/Dispatcher.js +1488 -599
  63. package/dist/core/Dispatcher.js.map +1 -1
  64. package/dist/core/DurableStatement.d.ts +6 -0
  65. package/dist/core/DurableStatement.d.ts.map +1 -0
  66. package/dist/core/DurableStatement.js +51 -0
  67. package/dist/core/DurableStatement.js.map +1 -0
  68. package/dist/core/Engine.d.ts +48 -20
  69. package/dist/core/Engine.d.ts.map +1 -1
  70. package/dist/core/Engine.js +1208 -789
  71. package/dist/core/Engine.js.map +1 -1
  72. package/dist/core/Engine.sql +309 -217
  73. package/dist/core/EnvFlags.js +1 -1
  74. package/dist/core/EnvFlags.js.map +1 -1
  75. package/dist/core/ErrorDetail.d.ts +6 -0
  76. package/dist/core/ErrorDetail.d.ts.map +1 -0
  77. package/dist/core/ErrorDetail.js +20 -0
  78. package/dist/core/ErrorDetail.js.map +1 -0
  79. package/dist/core/ExecutorRegistry.d.ts +14 -3
  80. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  81. package/dist/core/ExecutorRegistry.js +56 -30
  82. package/dist/core/ExecutorRegistry.js.map +1 -1
  83. package/dist/core/GitBranch.d.ts +20 -0
  84. package/dist/core/GitBranch.d.ts.map +1 -0
  85. package/dist/core/GitBranch.js +110 -0
  86. package/dist/core/GitBranch.js.map +1 -0
  87. package/dist/core/JournalTurn.d.ts +9 -0
  88. package/dist/core/JournalTurn.d.ts.map +1 -0
  89. package/dist/core/JournalTurn.js +14 -0
  90. package/dist/core/JournalTurn.js.map +1 -0
  91. package/dist/core/JournalTurn.sql +10 -0
  92. package/dist/core/LogBody.d.ts +17 -0
  93. package/dist/core/LogBody.d.ts.map +1 -0
  94. package/dist/core/LogBody.js +148 -0
  95. package/dist/core/LogBody.js.map +1 -0
  96. package/dist/core/LoopFlagsReader.d.ts +7 -0
  97. package/dist/core/LoopFlagsReader.d.ts.map +1 -0
  98. package/dist/core/LoopFlagsReader.js +33 -0
  99. package/dist/core/LoopFlagsReader.js.map +1 -0
  100. package/dist/core/LoopLifecycle.d.ts +8 -1
  101. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  102. package/dist/core/LoopLifecycle.js +55 -9
  103. package/dist/core/LoopLifecycle.js.map +1 -1
  104. package/dist/core/LoopLifecycle.sql +9 -3
  105. package/dist/core/NoticeChannel.d.ts +13 -0
  106. package/dist/core/NoticeChannel.d.ts.map +1 -0
  107. package/dist/core/NoticeChannel.js +40 -0
  108. package/dist/core/NoticeChannel.js.map +1 -0
  109. package/dist/core/Owner.d.ts +0 -1
  110. package/dist/core/Owner.d.ts.map +1 -1
  111. package/dist/core/Owner.js +2 -5
  112. package/dist/core/Owner.js.map +1 -1
  113. package/dist/core/PacketBuilder.d.ts +25 -24
  114. package/dist/core/PacketBuilder.d.ts.map +1 -1
  115. package/dist/core/PacketBuilder.js +313 -375
  116. package/dist/core/PacketBuilder.js.map +1 -1
  117. package/dist/core/ProblemLog.d.ts +26 -0
  118. package/dist/core/ProblemLog.d.ts.map +1 -0
  119. package/dist/core/ProblemLog.js +58 -0
  120. package/dist/core/ProblemLog.js.map +1 -0
  121. package/dist/core/ProposalLifecycle.d.ts +15 -21
  122. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  123. package/dist/core/ProposalLifecycle.js +291 -78
  124. package/dist/core/ProposalLifecycle.js.map +1 -1
  125. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  126. package/dist/core/ProviderInstantiate.js +51 -48
  127. package/dist/core/ProviderInstantiate.js.map +1 -1
  128. package/dist/core/SchemeRegistry.d.ts +14 -8
  129. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  130. package/dist/core/SchemeRegistry.js +189 -114
  131. package/dist/core/SchemeRegistry.js.map +1 -1
  132. package/dist/core/ServiceTeardown.d.ts +11 -0
  133. package/dist/core/ServiceTeardown.d.ts.map +1 -0
  134. package/dist/core/ServiceTeardown.js +70 -0
  135. package/dist/core/ServiceTeardown.js.map +1 -0
  136. package/dist/core/StoredPacket.d.ts +29 -0
  137. package/dist/core/StoredPacket.d.ts.map +1 -0
  138. package/dist/core/StoredPacket.js +147 -0
  139. package/dist/core/StoredPacket.js.map +1 -0
  140. package/dist/core/StrikeRail.d.ts +1 -2
  141. package/dist/core/StrikeRail.d.ts.map +1 -1
  142. package/dist/core/StrikeRail.js +25 -55
  143. package/dist/core/StrikeRail.js.map +1 -1
  144. package/dist/core/WorkerControlAddress.d.ts +17 -0
  145. package/dist/core/WorkerControlAddress.d.ts.map +1 -0
  146. package/dist/core/WorkerControlAddress.js +44 -0
  147. package/dist/core/WorkerControlAddress.js.map +1 -0
  148. package/dist/core/WorkerName.d.ts +31 -0
  149. package/dist/core/WorkerName.d.ts.map +1 -0
  150. package/dist/core/WorkerName.js +95 -0
  151. package/dist/core/WorkerName.js.map +1 -0
  152. package/dist/core/WorkerName.sql +36 -0
  153. package/dist/core/WorkspaceGate.d.ts +14 -0
  154. package/dist/core/WorkspaceGate.d.ts.map +1 -0
  155. package/dist/core/WorkspaceGate.js +155 -0
  156. package/dist/core/WorkspaceGate.js.map +1 -0
  157. package/dist/core/caps/CapsResolve.d.ts +1 -1
  158. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  159. package/dist/core/caps/CapsResolve.js.map +1 -1
  160. package/dist/core/caps/DbChannelCaps.d.ts +5 -11
  161. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  162. package/dist/core/caps/DbChannelCaps.js +24 -8
  163. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  164. package/dist/core/caps/DbEntryCaps.d.ts +4 -13
  165. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  166. package/dist/core/caps/DbEntryCaps.js +15 -6
  167. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  168. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  169. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  170. package/dist/core/caps/DbNotifyCaps.js +12 -11
  171. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  172. package/dist/core/caps/DbProjectionCaps.d.ts +5 -5
  173. package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
  174. package/dist/core/caps/DbProjectionCaps.js +21 -4
  175. package/dist/core/caps/DbProjectionCaps.js.map +1 -1
  176. package/dist/core/caps/DbSubscriptionCaps.d.ts +4 -4
  177. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  178. package/dist/core/caps/DbSubscriptionCaps.js +57 -62
  179. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  180. package/dist/core/caps/DbTagCaps.d.ts +5 -12
  181. package/dist/core/caps/DbTagCaps.d.ts.map +1 -1
  182. package/dist/core/caps/DbTagCaps.js +14 -7
  183. package/dist/core/caps/DbTagCaps.js.map +1 -1
  184. package/dist/core/content-hash.js +1 -1
  185. package/dist/core/content-hash.js.map +1 -1
  186. package/dist/core/env-defaults.d.ts.map +1 -1
  187. package/dist/core/env-defaults.js.map +1 -1
  188. package/dist/core/fork.d.ts.map +1 -1
  189. package/dist/core/fork.js +55 -30
  190. package/dist/core/fork.js.map +1 -1
  191. package/dist/core/fork.sql +41 -27
  192. package/dist/core/git-env.d.ts +2 -0
  193. package/dist/core/git-env.d.ts.map +1 -1
  194. package/dist/core/git-env.js +13 -18
  195. package/dist/core/git-env.js.map +1 -1
  196. package/dist/core/git-iso.d.ts.map +1 -1
  197. package/dist/core/git-iso.js +9 -15
  198. package/dist/core/git-iso.js.map +1 -1
  199. package/dist/core/git-membership.d.ts +1 -1
  200. package/dist/core/git-membership.d.ts.map +1 -1
  201. package/dist/core/git-membership.js +198 -146
  202. package/dist/core/git-membership.js.map +1 -1
  203. package/dist/core/git-state.d.ts.map +1 -1
  204. package/dist/core/git-state.js +10 -11
  205. package/dist/core/git-state.js.map +1 -1
  206. package/dist/core/owner.sql +1 -1
  207. package/dist/core/packet-inject.d.ts.map +1 -1
  208. package/dist/core/packet-inject.js +7 -11
  209. package/dist/core/packet-inject.js.map +1 -1
  210. package/dist/core/packet-wire.d.ts +2 -20
  211. package/dist/core/packet-wire.d.ts.map +1 -1
  212. package/dist/core/packet-wire.js +242 -368
  213. package/dist/core/packet-wire.js.map +1 -1
  214. package/dist/core/plurnk-uri.d.ts +12 -1
  215. package/dist/core/plurnk-uri.d.ts.map +1 -1
  216. package/dist/core/plurnk-uri.js +49 -29
  217. package/dist/core/plurnk-uri.js.map +1 -1
  218. package/dist/core/results.d.ts +16 -5
  219. package/dist/core/results.d.ts.map +1 -1
  220. package/dist/core/results.js +27 -18
  221. package/dist/core/results.js.map +1 -1
  222. package/dist/core/ruler_count.d.ts +4 -0
  223. package/dist/core/ruler_count.d.ts.map +1 -0
  224. package/dist/core/ruler_count.js +7 -0
  225. package/dist/core/ruler_count.js.map +1 -0
  226. package/dist/core/scheme-types.d.ts +9 -11
  227. package/dist/core/scheme-types.d.ts.map +1 -1
  228. package/dist/core/scheme-types.js.map +1 -1
  229. package/dist/core/search-gate.d.ts.map +1 -1
  230. package/dist/core/search-gate.js +3 -11
  231. package/dist/core/search-gate.js.map +1 -1
  232. package/dist/core/teaching-corpus.d.ts +6 -0
  233. package/dist/core/teaching-corpus.d.ts.map +1 -0
  234. package/dist/core/teaching-corpus.js +23 -0
  235. package/dist/core/teaching-corpus.js.map +1 -0
  236. package/dist/core/teaching.d.ts +0 -1
  237. package/dist/core/teaching.d.ts.map +1 -1
  238. package/dist/core/teaching.js +2 -8
  239. package/dist/core/teaching.js.map +1 -1
  240. package/dist/core/token-ruler.d.ts.map +1 -1
  241. package/dist/core/token-ruler.js +3 -16
  242. package/dist/core/token-ruler.js.map +1 -1
  243. package/dist/core/turn-scheduler.d.ts +1 -1
  244. package/dist/core/turn-scheduler.d.ts.map +1 -1
  245. package/dist/core/turn-scheduler.js +1 -1
  246. package/dist/core/turn-scheduler.js.map +1 -1
  247. package/dist/core/worker-cap.d.ts +2 -4
  248. package/dist/core/worker-cap.d.ts.map +1 -1
  249. package/dist/core/worker-cap.js +13 -4
  250. package/dist/core/worker-cap.js.map +1 -1
  251. package/dist/core/{run-ops.sql → worker-ops.sql} +11 -11
  252. package/dist/core/workspace-settings.d.ts.map +1 -1
  253. package/dist/core/workspace-settings.js +17 -9
  254. package/dist/core/workspace-settings.js.map +1 -1
  255. package/dist/digest/Digest.d.ts +2 -1
  256. package/dist/digest/Digest.d.ts.map +1 -1
  257. package/dist/digest/Digest.js +337 -138
  258. package/dist/digest/Digest.js.map +1 -1
  259. package/dist/digest/digest.sql +19 -10
  260. package/dist/index.d.ts.map +1 -1
  261. package/dist/index.js +3 -5
  262. package/dist/index.js.map +1 -1
  263. package/dist/matrix/live-specimen.d.ts +9 -0
  264. package/dist/matrix/live-specimen.d.ts.map +1 -0
  265. package/dist/matrix/live-specimen.js +37 -0
  266. package/dist/matrix/live-specimen.js.map +1 -0
  267. package/dist/observe/api.d.ts +6 -0
  268. package/dist/observe/api.d.ts.map +1 -0
  269. package/dist/observe/api.js +11 -0
  270. package/dist/observe/api.js.map +1 -0
  271. package/dist/observe/init.d.ts +7 -0
  272. package/dist/observe/init.d.ts.map +1 -0
  273. package/dist/observe/init.js +159 -0
  274. package/dist/observe/init.js.map +1 -0
  275. package/dist/observe/metrics.d.ts +6 -0
  276. package/dist/observe/metrics.d.ts.map +1 -0
  277. package/dist/observe/metrics.js +15 -0
  278. package/dist/observe/metrics.js.map +1 -0
  279. package/dist/observe/spans.d.ts +4 -0
  280. package/dist/observe/spans.d.ts.map +1 -0
  281. package/dist/observe/spans.js +51 -0
  282. package/dist/observe/spans.js.map +1 -0
  283. package/dist/schemes/EffectPolicy.d.ts +1 -0
  284. package/dist/schemes/EffectPolicy.d.ts.map +1 -1
  285. package/dist/schemes/EffectPolicy.js +5 -2
  286. package/dist/schemes/EffectPolicy.js.map +1 -1
  287. package/dist/schemes/Exec.d.ts +6 -11
  288. package/dist/schemes/Exec.d.ts.map +1 -1
  289. package/dist/schemes/Exec.js +364 -247
  290. package/dist/schemes/Exec.js.map +1 -1
  291. package/dist/schemes/ExecOutputScheme.d.ts +8 -7
  292. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  293. package/dist/schemes/ExecOutputScheme.js +48 -9
  294. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  295. package/dist/schemes/File.d.ts +7 -11
  296. package/dist/schemes/File.d.ts.map +1 -1
  297. package/dist/schemes/File.js +279 -142
  298. package/dist/schemes/File.js.map +1 -1
  299. package/dist/schemes/Log.d.ts +4 -8
  300. package/dist/schemes/Log.d.ts.map +1 -1
  301. package/dist/schemes/Log.js +475 -208
  302. package/dist/schemes/Log.js.map +1 -1
  303. package/dist/schemes/Log.sql +62 -30
  304. package/dist/schemes/Prompt.d.ts +3 -2
  305. package/dist/schemes/Prompt.d.ts.map +1 -1
  306. package/dist/schemes/Prompt.js +9 -6
  307. package/dist/schemes/Prompt.js.map +1 -1
  308. package/dist/schemes/Skill.d.ts +1 -1
  309. package/dist/schemes/Skill.d.ts.map +1 -1
  310. package/dist/schemes/Skill.js +2 -3
  311. package/dist/schemes/Skill.js.map +1 -1
  312. package/dist/schemes/Worker.d.ts +8 -16
  313. package/dist/schemes/Worker.d.ts.map +1 -1
  314. package/dist/schemes/Worker.js +179 -69
  315. package/dist/schemes/Worker.js.map +1 -1
  316. package/dist/schemes/_entry-chunk.d.ts.map +1 -1
  317. package/dist/schemes/_entry-chunk.js +12 -5
  318. package/dist/schemes/_entry-chunk.js.map +1 -1
  319. package/dist/schemes/_entry-crud.d.ts +10 -9
  320. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  321. package/dist/schemes/_entry-crud.js +59 -13
  322. package/dist/schemes/_entry-crud.js.map +1 -1
  323. package/dist/schemes/_entry-crud.sql +20 -8
  324. package/dist/schemes/_entry-find.d.ts +20 -15
  325. package/dist/schemes/_entry-find.d.ts.map +1 -1
  326. package/dist/schemes/_entry-find.js +339 -174
  327. package/dist/schemes/_entry-find.js.map +1 -1
  328. package/dist/schemes/_entry-find.sql +20 -8
  329. package/dist/schemes/_entry-graph.d.ts +4 -2
  330. package/dist/schemes/_entry-graph.d.ts.map +1 -1
  331. package/dist/schemes/_entry-graph.js +55 -31
  332. package/dist/schemes/_entry-graph.js.map +1 -1
  333. package/dist/schemes/_entry-graph.sql +37 -22
  334. package/dist/schemes/_entry-manifest.d.ts +10 -15
  335. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  336. package/dist/schemes/_entry-manifest.js +50 -268
  337. package/dist/schemes/_entry-manifest.js.map +1 -1
  338. package/dist/schemes/_entry-ops.d.ts +9 -14
  339. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  340. package/dist/schemes/_entry-ops.js +149 -77
  341. package/dist/schemes/_entry-ops.js.map +1 -1
  342. package/dist/schemes/_entry-ops.sql +1 -1
  343. package/dist/schemes/_entry-semantic.d.ts +23 -8
  344. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  345. package/dist/schemes/_entry-semantic.js +141 -60
  346. package/dist/schemes/_entry-semantic.js.map +1 -1
  347. package/dist/schemes/_entry-semantic.sql +63 -45
  348. package/dist/schemes/_entry-send.d.ts +3 -4
  349. package/dist/schemes/_entry-send.d.ts.map +1 -1
  350. package/dist/schemes/_entry-send.js +48 -23
  351. package/dist/schemes/_entry-send.js.map +1 -1
  352. package/dist/schemes/_path-scope.d.ts +23 -0
  353. package/dist/schemes/_path-scope.d.ts.map +1 -0
  354. package/dist/schemes/_path-scope.js +61 -0
  355. package/dist/schemes/_path-scope.js.map +1 -0
  356. package/dist/schemes/_search-candidate.d.ts +20 -0
  357. package/dist/schemes/_search-candidate.d.ts.map +1 -0
  358. package/dist/schemes/_search-candidate.js +19 -0
  359. package/dist/schemes/_search-candidate.js.map +1 -0
  360. package/dist/schemes/_search-exclusion.d.ts +7 -0
  361. package/dist/schemes/_search-exclusion.d.ts.map +1 -0
  362. package/dist/schemes/_search-exclusion.js +23 -0
  363. package/dist/schemes/_search-exclusion.js.map +1 -0
  364. package/dist/schemes/_search-index.d.ts +6 -0
  365. package/dist/schemes/_search-index.d.ts.map +1 -0
  366. package/dist/schemes/_search-index.js +326 -0
  367. package/dist/schemes/_search-index.js.map +1 -0
  368. package/dist/schemes/cosine.js +1 -1
  369. package/dist/schemes/cosine.js.map +1 -1
  370. package/dist/schemes/exec-abort.js +3 -3
  371. package/dist/schemes/exec-abort.js.map +1 -1
  372. package/dist/schemes/exec-env.js +3 -3
  373. package/dist/schemes/exec-env.js.map +1 -1
  374. package/dist/server/BranchBatches.d.ts +48 -0
  375. package/dist/server/BranchBatches.d.ts.map +1 -0
  376. package/dist/server/BranchBatches.js +591 -0
  377. package/dist/server/BranchBatches.js.map +1 -0
  378. package/dist/server/Daemon.d.ts +36 -66
  379. package/dist/server/Daemon.d.ts.map +1 -1
  380. package/dist/server/Daemon.js +920 -446
  381. package/dist/server/Daemon.js.map +1 -1
  382. package/dist/server/DaemonModule.d.ts +31 -0
  383. package/dist/server/DaemonModule.d.ts.map +1 -0
  384. package/dist/server/DaemonModule.js +2 -0
  385. package/dist/server/DaemonModule.js.map +1 -0
  386. package/dist/server/branch-batch.sql +152 -0
  387. package/dist/server/client-input.d.ts +12 -0
  388. package/dist/server/client-input.d.ts.map +1 -1
  389. package/dist/server/client-input.js +262 -57
  390. package/dist/server/client-input.js.map +1 -1
  391. package/dist/server/dispatch-as-plurnk.d.ts +1 -1
  392. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  393. package/dist/server/dispatch-as-plurnk.js +15 -7
  394. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  395. package/dist/server/drain.sql +92 -62
  396. package/dist/server/envelope.d.ts +2 -5
  397. package/dist/server/envelope.d.ts.map +1 -1
  398. package/dist/server/envelope.js +119 -95
  399. package/dist/server/envelope.js.map +1 -1
  400. package/dist/server/envelope.sql +19 -23
  401. package/dist/server/exec-poll-backoff.js +1 -1
  402. package/dist/server/exec-poll-backoff.js.map +1 -1
  403. package/dist/server/lifecycle-recovery.sql +79 -2
  404. package/dist/server/logEntry.d.ts +3 -1
  405. package/dist/server/logEntry.d.ts.map +1 -1
  406. package/dist/server/logEntry.js +4 -2
  407. package/dist/server/logEntry.js.map +1 -1
  408. package/dist/server/logEntry.sql +1 -1
  409. package/dist/server/loop-model.d.ts.map +1 -1
  410. package/dist/server/loop-model.js +22 -9
  411. package/dist/server/loop-model.js.map +1 -1
  412. package/dist/server/loopDocs.d.ts.map +1 -1
  413. package/dist/server/loopDocs.js +5 -4
  414. package/dist/server/loopDocs.js.map +1 -1
  415. package/dist/server/seam-entry-read.sql +15 -10
  416. package/dist/server/seam-log-read.sql +2 -2
  417. package/dist/server/seam-loop.sql +1 -1
  418. package/dist/server/seam-proposal-list.sql +17 -7
  419. package/dist/service.d.ts.map +1 -1
  420. package/dist/service.js +66 -60
  421. package/dist/service.js.map +1 -1
  422. package/migrations/001_schema.sql +897 -0
  423. package/package.json +57 -26
  424. package/dist/core/PluginLoader.d.ts +0 -20
  425. package/dist/core/PluginLoader.d.ts.map +0 -1
  426. package/dist/core/PluginLoader.js +0 -142
  427. package/dist/core/PluginLoader.js.map +0 -1
  428. package/dist/core/TelemetryChannel.d.ts +0 -37
  429. package/dist/core/TelemetryChannel.d.ts.map +0 -1
  430. package/dist/core/TelemetryChannel.js +0 -80
  431. package/dist/core/TelemetryChannel.js.map +0 -1
  432. package/dist/core/path-decode.d.ts +0 -3
  433. package/dist/core/path-decode.d.ts.map +0 -1
  434. package/dist/core/path-decode.js +0 -9
  435. package/dist/core/path-decode.js.map +0 -1
  436. package/dist/core/plugin-attribution.d.ts +0 -5
  437. package/dist/core/plugin-attribution.d.ts.map +0 -1
  438. package/dist/core/plugin-attribution.js +0 -39
  439. package/dist/core/plugin-attribution.js.map +0 -1
  440. package/dist/core/world-state.d.ts +0 -10
  441. package/dist/core/world-state.d.ts.map +0 -1
  442. package/dist/core/world-state.js +0 -35
  443. package/dist/core/world-state.js.map +0 -1
  444. package/dist/core/world-state.sql +0 -35
  445. package/dist/core/zero-pin.d.ts +0 -3
  446. package/dist/core/zero-pin.d.ts.map +0 -1
  447. package/dist/core/zero-pin.js +0 -16
  448. package/dist/core/zero-pin.js.map +0 -1
  449. package/dist/server/auto.d.ts +0 -6
  450. package/dist/server/auto.d.ts.map +0 -1
  451. package/dist/server/auto.js +0 -63
  452. package/dist/server/auto.js.map +0 -1
  453. package/dist/server/clientTurn.d.ts +0 -6
  454. package/dist/server/clientTurn.d.ts.map +0 -1
  455. package/dist/server/clientTurn.js +0 -22
  456. package/dist/server/clientTurn.js.map +0 -1
  457. package/dist/server/clientTurn.sql +0 -10
  458. package/dist/server/noProposals.d.ts +0 -6
  459. package/dist/server/noProposals.d.ts.map +0 -1
  460. package/dist/server/noProposals.js +0 -37
  461. package/dist/server/noProposals.js.map +0 -1
  462. package/dist/server/version-info.d.ts +0 -14
  463. package/dist/server/version-info.d.ts.map +0 -1
  464. package/dist/server/version-info.js +0 -69
  465. package/dist/server/version-info.js.map +0 -1
  466. package/migrations/0000-00-00.01_schema.sql +0 -536
  467. package/migrations/0004_loop-provider.sql +0 -3
  468. package/migrations/0005_subscription-published-channel.sql +0 -3
  469. package/migrations/0006_loop-max-turns.sql +0 -3
@@ -3,56 +3,53 @@ import Namespace from "../core/namespace.js";
3
3
  import Owner from "../core/Owner.js";
4
4
  import { dirname, relative, isAbsolute, join, matchesGlob } from "node:path";
5
5
  import { createPatch } from "diff";
6
- import { decodePathParens } from "../core/path-decode.js";
6
+ import { PathSyntax } from "@plurnk/plurnk-contracts";
7
7
  import GitMembership from "../core/git-membership.js";
8
8
  import EntryOps from "./_entry-ops.js";
9
9
  import EntryFind from "./_entry-find.js";
10
10
  import EntryCrud from "./_entry-crud.js";
11
11
  import { CoreSchemeAdapterBase } from "../core/CoreSchemeServices.js";
12
- import { LineMarkerOps, MimetypeBinary, editReceipt, editReceiptUnit, editedSpan, projectEditReceipt } from "../content/index.js";
12
+ import ErrorDetail from "../core/ErrorDetail.js";
13
+ import Results from "../core/results.js";
14
+ import { InvalidOperationResultError, } from "@plurnk/plurnk-schemes";
15
+ import { LineMarkerOps, MimetypeBinary, editReceipt, projectEditReceipt, reviewerReplacementReceipt, } from "../content/index.js";
13
16
  // Workspace root for file ops is sourced from `workspaces.project_root`,
14
- // supplied by the client at workspace.create (headless is forever; issue
15
- // #150 wired the RPC; F.1 added the column). Server doesn't guess —
16
- // the client owns workspace identity. If a workspace is headless
17
- // (project_root=null), file ops fail at 400; the client either supplies
18
- // a root or the op isn't appropriate for this workspace.
17
+ // supplied at {§methods-workspace-create}. Under {§fs-namespace}, core never
18
+ // guesses a root: headless workspaces fail file operations at 400.
19
19
  const loadWorkspaceRoot = async (db, workspaceId) => {
20
20
  const row = await db.envelope_get_workspace.get({ id: workspaceId });
21
21
  return row?.project_root ?? null;
22
22
  };
23
- // Detect mimetype from a file's path. Routes through the Mimetypes service
24
- // when available; falls back to the text primitive (text/markdown). The
23
+ // Detect mimetype from a file's path through the configured registry. The
25
24
  // `MimetypeBinary.normalizeAutoTextMimetype` wrapper ensures text/plain returned by the
26
25
  // service is normalized to text/markdown — plurnk-service never auto-
27
26
  // derives text/plain (see mimetype-binary.ts MimetypeBinary.TEXT_PRIMITIVE_MIMETYPE).
28
27
  const detectFileMimetype = async (canonical, ctx) => {
29
- if (ctx.mimetypes !== undefined) {
30
- const detected = await ctx.mimetypes.detect({ path: canonical });
31
- return MimetypeBinary.normalizeAutoTextMimetype(detected);
32
- }
33
- return MimetypeBinary.TEXT_PRIMITIVE_MIMETYPE;
28
+ if (ctx.mimetypes === undefined)
29
+ throw new Error("detectFileMimetype: configured mimetype registry is required");
30
+ const detected = await ctx.mimetypes.detect({ path: canonical });
31
+ return MimetypeBinary.normalizeAutoTextMimetype(detected);
34
32
  };
35
- // SECURITY — File is entry-backed, identical to Known: read()/find()/readEntry()
33
+ // SECURITY — File is entry-backed: read()/find()/readEntry()
36
34
  // delegate to the shared Entry* helpers over the membership-materialized entries
37
- // (scheme=null). The membership gate is now ENTRY-EXISTENCE — a non-member has no
38
- // entry, so read/find/readEntry 404 it for free, the same invariant Known runs on
35
+ // (stored under scheme="file"). The membership gate is now ENTRY-EXISTENCE — a non-member has no
36
+ // entry, so read/find/readEntry 404 it at the shared entry boundary
39
37
  // (a gitignored `.env` is never a member → never an entry → never readable). Disk
40
38
  // I/O is confined to the two edges that own it: the git-membership materialize-IN
41
- // and edit()/applyResolution()'s proposal-gated write-OUT (§membership), where the
39
+ // and edit()/applyResolution()'s proposal-gated write-OUT ({§membership}), where the
42
40
  // containment/traversal checks live.
43
41
  //
44
42
  // writeEntry() is the proposal-gated write-back: a COPY/MOVE *into* file:/// is a disk
45
- // write, so it flows through the SAME §membership gate as EDIT (#resolveWriteTarget) — a
43
+ // write, so it flows through the SAME {§membership} gate as EDIT (#resolveWriteTarget) — a
46
44
  // 202 proposal, then applyResolution() writes on accept — never an ungated overwrite (the
47
45
  // `.env`-wipe this guard prevents). COPY/MOVE *from* file:/// is readEntry (read-only).
48
46
  export default class File extends CoreSchemeAdapterBase {
49
47
  static manifest = {
50
48
  name: "file",
51
- storedScheme: "file", // {§entry-identity-no-null} — file rows persist under the reserved 'file' scheme (a NULL identity component voids the UNIQUE index; run59/#545); renders as a bare path
49
+ storedScheme: "file", // {§entry-identity-no-null} — durable identity; file addresses still render as bare paths
52
50
  channels: {}, // dynamic mimetype per file extension
53
51
  defaultChannel: "body",
54
52
  category: "data",
55
- scope: "workspace",
56
53
  writableBy: ["model", "client", "plugin"],
57
54
  volatile: false,
58
55
  modelVisible: true,
@@ -60,15 +57,13 @@ export default class File extends CoreSchemeAdapterBase {
60
57
  example: "<<READ(README.md)::READ",
61
58
  documentation: "The project's workspace files (git-tracked members, shown as bare paths) — THE TASK'S FILES: when asked to change the project, EDIT these, not your notes or scratch. READ and FIND them like any entry; EDIT proposes a diff for review and only writes to disk once accepted — the review is normal, not a refusal, so propose the edit rather than working around it. Non-members are invisible, so you can't read or clobber a file outside the tracked surface.",
62
59
  };
63
- // Entry-backed, identical to Known (the unified-addressing promise): read the
64
- // membership-materialized entry (scheme=null) through the shared helper. <L> /
65
- // body / binary-415 / tag-404 are all handled there; a non-member has no entry
66
- // → 404, the same gate Known runs on. Disk is reached only at the materialize
67
- // and write-back edges (git-membership, applyResolution) — never on a read.
60
+ // {§membership} — read the membership-materialized `file` entry through the
61
+ // shared entry helper. Disk is touched only at materialization and accepted
62
+ // write-back boundaries, never by READ itself.
68
63
  async read(statement, ctx) {
69
64
  const core = this.coreContext(ctx);
70
- // {§fs-namei} — canonicalize ONCE, up front: resolution never depends on what exists
71
- // (the old fold-on-miss retry was existence-dependent meaning, run59's disease class).
65
+ // {§fs-namei} — canonicalize once, before the entry-existence gate;
66
+ // resolution never depends on whether the target currently exists.
72
67
  const canon = File.#canonTarget(statement, await loadWorkspaceRoot(core.db, core.workspaceId));
73
68
  return EntryOps.readWorkspaceEntry(canon ?? statement, core, File.manifest);
74
69
  }
@@ -94,11 +89,18 @@ export default class File extends CoreSchemeAdapterBase {
94
89
  }
95
90
  return statement; // regex — not a path
96
91
  }
97
- // Folderhood survives canonicalization (§find-scope-prefix-filter) — the shared
92
+ // Folderhood survives canonicalization ({§find-scope-prefix-filter}) — the shared
98
93
  // spelling canon lives on Namespace (the Dispatcher's log columns use the same one).
99
94
  static #canonSpelling(raw, root) {
100
95
  return Namespace.canonicalizeSpelling(raw, root);
101
96
  }
97
+ async resolveEntryAddress(target, ctx) {
98
+ if (target.kind !== "url")
99
+ return null;
100
+ const core = this.coreContext(ctx);
101
+ const pathname = File.#canonSpelling(target.pathname, await loadWorkspaceRoot(core.db, core.workspaceId));
102
+ return pathname === null ? null : { pathname, owner: "commons" };
103
+ }
102
104
  async find(statement, ctx) {
103
105
  const core = this.coreContext(ctx);
104
106
  // {§fs-namei} — canonicalize the glob's path portion before the candidate scan, the
@@ -111,29 +113,47 @@ export default class File extends CoreSchemeAdapterBase {
111
113
  // see the SECURITY note above.
112
114
  async readEntry(pathname, ctx) {
113
115
  const core = this.coreContext(ctx);
114
- // §scheme-address — normalize the model-typed path (bare `brief.md`) to its `/rel` member key,
116
+ // {§scheme-address} — normalize the model-typed path (bare `brief.md`) to its `/rel` member key,
115
117
  // the same parity READ/EDIT/deleteEntry have. Without it a COPY/MOVE FROM a bare file path
116
118
  // misses the canonical-stored member and 404s a source that plainly exists.
117
119
  const root = await loadWorkspaceRoot(core.db, core.workspaceId);
118
120
  const key = root === null ? pathname : Namespace.canonicalize(pathname, root);
119
121
  return EntryCrud.readEntry(key ?? pathname, core, "file");
120
122
  }
121
- // §membership disk-write gate, shared by edit() and writeEntry() (the COPY/MOVE
123
+ // {§membership} disk-write gate, shared by edit() and writeEntry() (the COPY/MOVE
122
124
  // dest). Resolves the canonical path; enforces containment (traversal → 403),
123
125
  // membership (existing non-member → 403, the `.env` protection), the read-only
124
126
  // overlay (→ 403), and binary (→ 415); reads current content for the diff. A
125
127
  // not-found path is fine — we propose to CREATE. ONE home for the gate: an edit
126
128
  // and a copy into file:/// are the same disk write under the same review.
127
- // §membership-edit-membership-gate — membership/containment/read-only/binary gate before any disk write
129
+ // {§membership-edit-membership-gate} — membership/containment/read-only/binary gate before any disk write
128
130
  async #resolveWriteTarget(pathname, ctx) {
129
131
  const root = await loadWorkspaceRoot(ctx.db, ctx.workspaceId);
130
- if (root === null)
131
- return { ok: false, status: 400, error: "workspace has no project_root (headless is forever) — file ops need a workspace created with projectRoot" };
132
+ if (root === null) {
133
+ return {
134
+ ok: false,
135
+ code: "project-root-required",
136
+ status: 400,
137
+ detail: "The workspace has no project root, so it cannot write files.",
138
+ extensions: { retryable: false },
139
+ };
140
+ }
132
141
  // {§fs-namei} — the write side resolves through the SAME canonicalizer as reads;
133
142
  // a spelling that names nothing a file can be is refused before any disk touch.
134
143
  const key = Namespace.canonicalize(pathname, root);
135
- if (key === null)
136
- return { ok: false, status: 403, error: "path escapes workspace root" };
144
+ if (key === null) {
145
+ return {
146
+ ok: false,
147
+ code: "path-outside-workspace",
148
+ status: 403,
149
+ detail: `The requested path '${pathname}' is outside the workspace.`,
150
+ extensions: {
151
+ requestedPath: pathname,
152
+ recovery: "Use a path within the workspace.",
153
+ retryable: false,
154
+ },
155
+ };
156
+ }
137
157
  const isMount = key.startsWith("../");
138
158
  let canonical;
139
159
  const requested = join(root, key);
@@ -148,13 +168,35 @@ export default class File extends CoreSchemeAdapterBase {
148
168
  fileExists = false;
149
169
  }
150
170
  // {§fs-write-surface} 6 — only the root mints: no create on any mount, ever.
151
- if (isMount && !fileExists)
152
- return { ok: false, status: 403, error: "only the project root mints — create outside it is refused" };
171
+ if (isMount && !fileExists) {
172
+ return {
173
+ ok: false,
174
+ code: "mount-create-forbidden",
175
+ status: 403,
176
+ detail: `The mounted path '${key}' does not permit file creation.`,
177
+ extensions: {
178
+ path: key,
179
+ recovery: "Create the file inside the project root.",
180
+ retryable: false,
181
+ },
182
+ };
183
+ }
153
184
  if (!isMount) {
154
185
  const relBare = relative(root, canonical);
155
186
  // in-tree keys whose realpath escapes (a symlink out) stay refused — the jail holds.
156
- if (relBare.startsWith("..") || isAbsolute(relBare))
157
- return { ok: false, status: 403, error: "path escapes workspace root" };
187
+ if (relBare.startsWith("..") || isAbsolute(relBare)) {
188
+ return {
189
+ ok: false,
190
+ code: "path-outside-workspace",
191
+ status: 403,
192
+ detail: `The requested path '${pathname}' resolves outside the workspace.`,
193
+ extensions: {
194
+ requestedPath: pathname,
195
+ recovery: "Use a path within the workspace.",
196
+ retryable: false,
197
+ },
198
+ };
199
+ }
158
200
  }
159
201
  const rel = key; // the bare canonical member key ({§fs-canonical-name}) — storage ≡ wire
160
202
  let original = "";
@@ -164,116 +206,178 @@ export default class File extends CoreSchemeAdapterBase {
164
206
  const member = await ctx.db.crud_get_member_sig.get({ workspace_id: ctx.workspaceId, owner_id: await Owner.commonsId(ctx.db, ctx.workspaceId), scheme: "file", pathname: rel });
165
207
  // {§fs-errno} — the occupancy fact (POSIX O_EXCL precedent): something invisible
166
208
  // occupies the path; existence leaks, content stays dark. The model picks another name.
167
- if (member === undefined)
168
- return { ok: false, status: 403, error: `a file exists at ${rel}` };
209
+ if (member === undefined) {
210
+ return {
211
+ ok: false,
212
+ code: "path-occupied-by-nonmember",
213
+ status: 403,
214
+ detail: `A non-member file already occupies '${rel}'.`,
215
+ extensions: {
216
+ path: rel,
217
+ recovery: "Choose an unoccupied member path.",
218
+ retryable: false,
219
+ },
220
+ };
221
+ }
169
222
  // {§fs-write-surface} 5 — a git-included mount member is read-only: git's grants
170
223
  // confer rw only within the project; only an explicit client grant carries write.
171
- if (isMount && member.membership_origin === "git")
172
- return { ok: false, status: 403, error: "member is read-only" };
224
+ if (isMount && member.membership_origin === "git") {
225
+ return {
226
+ ok: false,
227
+ code: "member-read-only",
228
+ status: 403,
229
+ detail: `The mounted member '${rel}' is read-only.`,
230
+ extensions: { path: rel, retryable: false },
231
+ };
232
+ }
173
233
  const viewGlobs = (await ctx.db.crud_list_workspace_constraints.all({ workspace_id: ctx.workspaceId }))
174
234
  .filter((c) => c.effect === "view").map((c) => c.glob);
175
- if (viewGlobs.some((g) => matchesGlob(rel, g)))
176
- return { ok: false, status: 403, error: "member is read-only" }; // view = read-only member, 403 on edit — §membership-overlay-view
235
+ if (viewGlobs.some((g) => matchesGlob(rel, g))) {
236
+ return {
237
+ ok: false,
238
+ code: "member-read-only",
239
+ status: 403,
240
+ detail: `The member '${rel}' is read-only.`,
241
+ extensions: { path: rel, retryable: false },
242
+ };
243
+ } // view = read-only member, 403 on edit - {§membership-overlay-view}
177
244
  // The diff base is the entry's snapshot — the body channel the model READ — not a fresh
178
245
  // disk read. EDIT is naive against the view the model saw; the write-side CAS (applyResolution)
179
246
  // guards the landing. baseSig is that snapshot's stat, carried with the proposal so a sibling
180
- // worker's reconcile can't advance it under the paused proposal. §membership-edit-write-cas
247
+ // worker's reconcile can't advance it under the paused proposal. {§membership-edit-write-cas}
181
248
  const snapshot = await ctx.db.ops_read_channel.get({ workspace_id: ctx.workspaceId, owner_id: await Owner.commonsId(ctx.db, ctx.workspaceId), scheme: "file", pathname: rel, channel: "body" });
182
249
  original = snapshot?.content ?? "";
183
250
  baseSig = member.synced_sig;
184
251
  }
185
252
  else {
186
253
  // {§fs-write-surface} 1 — the blind-write closure: an exclusive CREATE is legal only
187
- // where the RESULT will be a member. A client pick admits it; else git's auto-add
188
- // must (untracked-not-ignored). A non-git root grants nothing by itself.
254
+ // where the RESULT will be a member. A client pick admits it; otherwise
255
+ // Git's untracked-not-ignored membership must ({§membership-auto-add}).
256
+ // No staging occurs. A non-git root grants nothing by itself.
189
257
  const picks = (await ctx.db.crud_list_workspace_constraints.all({ workspace_id: ctx.workspaceId }))
190
258
  .filter((c) => c.effect === "pick").map((c) => c.glob);
191
259
  const clientAdmits = picks.some((g) => matchesGlob(rel, g));
192
260
  if (!clientAdmits && !(await GitMembership.wouldGitAdmit(root, rel, ctx.signal))) {
193
- return { ok: false, status: 403, error: "create refused — the result would not be a member (git ignores it and no client pick covers it)" };
261
+ return {
262
+ ok: false,
263
+ code: "file-not-admitted",
264
+ status: 403,
265
+ detail: `The new file '${rel}' would not belong to the workspace because Git ignores it and no client pick includes it.`,
266
+ extensions: {
267
+ path: rel,
268
+ recovery: "Choose a path admitted by Git or the workspace picks.",
269
+ retryable: false,
270
+ },
271
+ };
194
272
  }
195
273
  // {§fs-write-surface} — the closure PROVED the grantor; the accept stamps what was
196
274
  // proven instead of leaving provenance NULL until the next reconcile guesses it.
197
275
  admittedBy = clientAdmits ? "client" : "git";
198
276
  }
199
277
  const mimetype = await detectFileMimetype(canonical, ctx);
200
- if (MimetypeBinary.isBinaryMimetype(mimetype))
201
- return { ok: false, status: 415, error: `cannot write binary mimetype \`${mimetype}\`` };
278
+ if (await MimetypeBinary.isBinaryMimetype(mimetype, ctx.mimetypes)) {
279
+ return {
280
+ ok: false,
281
+ code: "binary-write-unsupported",
282
+ status: 415,
283
+ detail: `File EDIT does not support binary mimetype '${mimetype}'.`,
284
+ extensions: {
285
+ mimetype,
286
+ retryable: false,
287
+ },
288
+ };
289
+ }
202
290
  return { ok: true, canonical, rel, fileExists, original, mimetype, baseSig, ...(admittedBy !== undefined ? { admittedBy } : {}) };
203
291
  }
204
- // Edit op (task #42 canonical proposal consumer). Returns status=202
205
- // with a udiff body for client review + attrs carrying the full patched
206
- // content. Engine writes the proposed log entry, pauses dispatch, and
207
- // calls applyResolution() (below) after the proposal accepts.
292
+ // {§membership-edit-write-cas}, {§proposal-202-pauses}: return status=202
293
+ // with a udiff body for client review and attrs carrying the full patched
294
+ // content. Engine pauses dispatch and calls applyResolution() after accept.
208
295
  async editBatch(statements, ctx) {
296
+ const failure = (code, status, detail, extensions = {}) => Results.failure("scheme:file", code, status, detail, {}, extensions);
209
297
  const statement = statements[0];
210
- if (statement === undefined)
211
- return { status: 400, error: "EDIT batch is empty" };
298
+ if (statement === undefined) {
299
+ return failure("edit-empty", 400, "EDIT requires at least one statement.", {
300
+ recovery: "Provide an EDIT statement.",
301
+ retryable: false,
302
+ });
303
+ }
212
304
  const core = this.coreContext(ctx);
213
- if (statement.target === null)
214
- return { status: 400, error: "EDIT requires a path" };
215
- const pathname = statement.target.kind === "regex" ? statement.target.raw
216
- : decodePathParens(statement.target.kind === "url" ? statement.target.pathname : statement.target.raw); // #239 item 4
305
+ if (statement.target === null) {
306
+ return failure("edit-target-required", 400, "EDIT requires a target path.", {
307
+ recovery: "Provide the file target.",
308
+ retryable: false,
309
+ });
310
+ }
311
+ const pathname = PathSyntax.decodeParens(statement.target.kind === "url" ? statement.target.pathname : statement.target.raw); // {§path-parentheses}
217
312
  const target = await this.#resolveWriteTarget(pathname, core);
218
313
  if (!target.ok)
219
- return { status: target.status, error: target.error };
314
+ return failure(target.code, target.status, target.detail, target.extensions);
220
315
  const { canonical, rel, fileExists, original, mimetype, baseSig, admittedBy } = target;
221
316
  for (const candidate of statements.slice(1)) {
222
- if (candidate.target === null)
223
- return { status: 400, error: "EDIT batch spans multiple resources" };
224
- const candidatePathname = candidate.target.kind === "regex" ? candidate.target.raw
225
- : decodePathParens(candidate.target.kind === "url" ? candidate.target.pathname : candidate.target.raw);
317
+ if (candidate.target === null) {
318
+ return failure("edit-batch-mismatch", 400, "The EDIT batch spans multiple resources.", {
319
+ recovery: "Submit a separate EDIT batch for each resource.",
320
+ retryable: false,
321
+ });
322
+ }
323
+ const candidatePathname = PathSyntax.decodeParens(candidate.target.kind === "url" ? candidate.target.pathname : candidate.target.raw);
226
324
  const candidateTarget = await this.#resolveWriteTarget(candidatePathname, core);
227
- if (!candidateTarget.ok || candidateTarget.rel !== rel)
228
- return { status: 400, error: "EDIT batch spans multiple resources" };
229
- }
230
- // `<L>` line marker dispatches on file mimetype: JSON →
231
- // LineMarkerOps.applyJsonItemEdit (structural item edit); otherwise →
232
- // LineMarkerOps.applyLineMarkerEdit (line edit). A CREATE (no existing file) has
233
- // nothing to scope into, so a markerless body becomes the new file's content.
234
- // {§edit-marker-required-on-existing} (#571) — an EXISTING file has no easy-clobber
235
- // path: a marker is REQUIRED, even for a deliberate full rewrite (`<1,-1>` states
236
- // that intent explicitly). run126: an omitted marker (a misplaced `<356>` landed
237
- // in the body, not the marker slot) silently replaced a 1,693-line file with 2 —
238
- // the model's own subsequent reads told it so, repeatedly, and it never noticed.
239
- // Refusing the omission converts a silent accident into a loud, immediate one.
325
+ if (!candidateTarget.ok || candidateTarget.rel !== rel) {
326
+ return failure("edit-batch-mismatch", 400, "The EDIT batch spans multiple resources.", {
327
+ recovery: "Submit a separate EDIT batch for each resource.",
328
+ retryable: false,
329
+ });
330
+ }
331
+ }
332
+ // {§edit-marker-required-on-existing} — markerless content creates a new
333
+ // file; every existing-file rewrite states its range explicitly.
240
334
  let patched;
335
+ let scopeNormalizations;
241
336
  if (fileExists) {
242
- if (statements.some(({ lineMarker }) => lineMarker === null))
243
- return { status: 400, error: "EDIT of an existing file requires a line marker — use <1,-1> to replace the whole file deliberately" };
337
+ if (statements.some(({ lineMarker }) => lineMarker === null)) {
338
+ return failure("line-marker-required", 400, "EDIT of an existing file requires a line marker.", {
339
+ recovery: "Use <1,-1> to replace the whole file or select a narrower range.",
340
+ retryable: false,
341
+ });
342
+ }
244
343
  const edits = statements.map((candidate) => ({ marker: candidate.lineMarker, body: candidate.body ?? "" }));
245
- const result = MimetypeBinary.isJsonMimetype(mimetype)
246
- ? LineMarkerOps.applyJsonItemEditBatch(original, edits)
247
- : LineMarkerOps.applyLineMarkerEditBatch(original, edits);
344
+ const result = LineMarkerOps.applyLineMarkerEditBatch(original, edits);
248
345
  if (result.status !== 200)
249
- return { status: result.status, error: result.error };
346
+ return Results.assert(result);
250
347
  patched = result.result ?? "";
348
+ scopeNormalizations = result.scopeNormalizations;
251
349
  }
252
350
  else {
253
- if (statements.length !== 1)
254
- return { status: 409, error: "creation cannot coexist with another EDIT" };
351
+ if (statements.length !== 1) {
352
+ return failure("creation-batch-conflict", 409, "Multiple EDIT operations attempted to create the same file.", {
353
+ recovery: "Create the file with one EDIT before applying additional edits.",
354
+ retryable: false,
355
+ });
356
+ }
255
357
  patched = statement.body ?? "";
256
358
  }
257
359
  if (fileExists && patched === original)
258
- return { status: 304 }; // §edit-noop-304
360
+ return {
361
+ status: 304,
362
+ ...(scopeNormalizations === undefined ? {} : { scopeNormalizations }),
363
+ }; // {§edit-noop-304}
259
364
  const patch = createPatch(rel, original, patched, "current", "proposed");
260
365
  const receiptEdits = fileExists
261
366
  ? statements.map((candidate) => ({ marker: candidate.lineMarker, body: candidate.body ?? "" }))
262
367
  : [{ marker: { marks: [1, -1] }, body: patched }];
263
- const batchReceipt = editReceipt(original, patched, receiptEdits, {
264
- unit: editReceiptUnit(MimetypeBinary.isJsonMimetype(mimetype), original, patched),
265
- });
368
+ const batchReceipt = editReceipt(original, patched, receiptEdits);
266
369
  return {
267
370
  status: 202,
268
371
  body: patch,
269
372
  editReceipt: batchReceipt,
373
+ ...(scopeNormalizations === undefined ? {} : { scopeNormalizations }),
270
374
  attrs: { path: rel, canonical, patch, patched, editReceipt: batchReceipt, baseSig, existed: fileExists, ...(admittedBy !== undefined ? { admittedBy } : {}) },
271
375
  };
272
376
  }
273
377
  async edit(statement, ctx) {
274
378
  return this.editBatch([statement], ctx);
275
379
  }
276
- // COPY/MOVE INTO file:/// — the dest write. Same §membership gate as edit, same 202
380
+ // COPY/MOVE INTO file:/// — the dest write. Same {§membership} gate as edit, same 202
277
381
  // proposal + applyResolution path: a copy onto disk is a disk write and earns
278
382
  // the identical human review. Dispatcher.#copyOrchestration propagates this 202;
279
383
  // ProposalLifecycle.workerApply routes the accept back here via the dest scheme;
@@ -282,24 +386,28 @@ export default class File extends CoreSchemeAdapterBase {
282
386
  async writeEntry(pathname, entry, ctx) {
283
387
  const core = this.coreContext(ctx);
284
388
  const bodyChannel = entry.channels.body;
285
- if (bodyChannel === undefined)
286
- return { status: 400, created: false, entryId: null };
389
+ if (bodyChannel === undefined) {
390
+ return Results.failure("scheme:file", "body-channel-required", 400, "A file write requires a body channel.", { created: false, entryId: null }, {
391
+ recovery: "Provide the file content in the body channel.",
392
+ retryable: false,
393
+ });
394
+ }
287
395
  const target = await this.#resolveWriteTarget(pathname, core);
288
- if (!target.ok)
289
- return { status: target.status, created: false, entryId: null };
396
+ if (!target.ok) {
397
+ return Results.failure("scheme:file", target.code, target.status, target.detail, { created: false, entryId: null }, target.extensions);
398
+ }
290
399
  const { canonical, rel, fileExists, original, baseSig, admittedBy } = target;
291
400
  const patched = bodyChannel.content;
292
401
  const patch = createPatch(rel, original, patched, "current", "proposed");
293
- return { status: 202, created: !fileExists, entryId: null, body: patch, attrs: { path: rel, canonical, patch, patched, span: editedSpan(original, patched), baseSig, existed: fileExists, ...(admittedBy !== undefined ? { admittedBy } : {}) } };
402
+ return { status: 202, created: !fileExists, entryId: null, body: patch, attrs: { path: rel, canonical, patch, patched, baseSig, existed: fileExists, ...(admittedBy !== undefined ? { admittedBy } : {}) } };
294
403
  }
295
404
  // applyResolution — called by Engine.dispatch after a proposed log
296
405
  // entry resolves with decision=accept. Two responsibilities:
297
406
  // 1. Write the patched content to disk.
298
407
  // 2. Register the file as an entry so it appears in the manifest and the
299
408
  // model can READ its full landed work.
300
- // The accepted result returns the editedSpan diff as its body, so the EDIT row
301
- // itself carries the line-numbered confirmation of what changed — parity with the
302
- // entry-scheme EDIT's span; default-folding reclaims it at the next turn boundary.
409
+ // {§edit-result-receipt-truth} — accepted EDITs return the receipt for what
410
+ // landed. Dispatcher composes COPY/MOVE effects after this hook returns.
303
411
  async applyResolution(args, ctx) {
304
412
  const core = this.coreContext(ctx);
305
413
  const { attrs, body } = args;
@@ -308,14 +416,22 @@ export default class File extends CoreSchemeAdapterBase {
308
416
  // failure surfaces as 500, never a silent noop; ENOENT ⇒ file already gone, still deregister.
309
417
  if (typeof attrs.deletePath === "string") {
310
418
  const root = await loadWorkspaceRoot(core.db, core.workspaceId);
311
- if (root === null)
312
- return { status: 500, outcome: "no project_root" };
419
+ if (root === null) {
420
+ throw new InvalidOperationResultError("An accepted file deletion has no workspace project root.");
421
+ }
313
422
  try {
314
423
  await rm(join(root, attrs.deletePath));
315
424
  }
316
425
  catch (err) {
317
- if (err.code !== "ENOENT")
318
- return { status: 500, outcome: "delete_failed", body: err instanceof Error ? err.message : String(err) };
426
+ if (err.code !== "ENOENT") {
427
+ console.error(`File deletion failed for '${attrs.deletePath}':`, err);
428
+ return Results.failure("scheme:file", "delete-failed", 500, `The file could not be deleted: ${ErrorDetail.preview(err)}`, {
429
+ outcome: "delete_failed",
430
+ }, {
431
+ path: attrs.deletePath,
432
+ stage: "filesystem-delete",
433
+ });
434
+ }
319
435
  }
320
436
  await EntryCrud.deleteEntry(attrs.deletePath, core, "file");
321
437
  return { status: 200 };
@@ -324,19 +440,19 @@ export default class File extends CoreSchemeAdapterBase {
324
440
  const relPath = attrs.path;
325
441
  const patched = body ?? attrs.patched;
326
442
  if (typeof canonical !== "string" || canonical.length === 0) {
327
- return { status: 500, outcome: "applyResolution: missing attrs.canonical" };
443
+ throw new InvalidOperationResultError("The accepted file proposal is missing attrs.canonical.");
328
444
  }
329
445
  if (typeof relPath !== "string" || relPath.length === 0) {
330
- return { status: 500, outcome: "applyResolution: missing attrs.path" };
446
+ throw new InvalidOperationResultError("The accepted file proposal is missing attrs.path.");
331
447
  }
332
448
  if (typeof patched !== "string") {
333
- return { status: 500, outcome: "applyResolution: missing patched content" };
449
+ throw new InvalidOperationResultError("The accepted file proposal is missing patched content.");
334
450
  }
335
451
  // CAS — the write-side twin of #materializeMember's read-gate (synced_sig === sig). The
336
452
  // proposal was computed against the snapshot (body + baseSig); if disk drifted out-of-band
337
453
  // since propose, the full-blob write would clobber it. Refuse and surface a write_conflict —
338
454
  // the next reconcile narrates disk truth via FsDivergence; the model re-reads + re-proposes.
339
- // No clever re-diff, no clobber. §membership-edit-write-cas
455
+ // No clever re-diff, no clobber. {§membership-edit-write-cas}
340
456
  const baseSig = (args.attrs.baseSig ?? null);
341
457
  const existed = args.attrs.existed === true; // proposal targeted an existing member (edit) vs a fresh create
342
458
  let currentSig = null;
@@ -353,18 +469,28 @@ export default class File extends CoreSchemeAdapterBase {
353
469
  // assumed an absent path, so any file present now is the conflict.
354
470
  const conflict = existed ? (baseSig !== null && currentSig !== baseSig) : (currentSig !== null);
355
471
  if (conflict) {
356
- return { status: 409, outcome: `write_conflict: ${relPath} changed on disk since the proposal (expected ${existed ? baseSig : "absent"}, found ${currentSig ?? "absent"}) — re-read and re-propose` };
472
+ const expected = existed ? baseSig : null;
473
+ const detail = `${relPath} changed on disk since the proposal; expected ${expected ?? "absence"} but found ${currentSig ?? "absence"}.`;
474
+ return Results.failure("scheme:file", "write-conflict", 409, detail, {
475
+ outcome: "write_conflict",
476
+ }, {
477
+ path: relPath,
478
+ expectedSignature: expected,
479
+ currentSignature: currentSig,
480
+ recovery: "READ the current file before proposing a new EDIT.",
481
+ retryable: false,
482
+ });
357
483
  }
358
484
  let receipt = attrs.editReceipt;
359
485
  if (body !== undefined && body !== attrs.patched) {
360
486
  const original = existed ? await readFile(canonical, "utf8") : "";
361
- const mimetype = await detectFileMimetype(canonical, core);
362
- const reviewed = editReceipt(original, patched, [{ marker: { marks: [1, -1] }, body: patched }], { unit: editReceiptUnit(MimetypeBinary.isJsonMimetype(mimetype), original, patched) });
363
- const statementCount = attrs.editReceipt?.effects.length ?? 1;
364
- receipt = {
365
- ...reviewed,
366
- effects: Array.from({ length: statementCount }, () => reviewed.effects[0]),
367
- };
487
+ if (receipt === undefined) {
488
+ if (typeof attrs.patched !== "string") {
489
+ throw new InvalidOperationResultError("The reviewer-modified file proposal is missing its proposed content.");
490
+ }
491
+ receipt = editReceipt(original, attrs.patched, [{ marker: { marks: [1, -1] }, body: attrs.patched }]);
492
+ }
493
+ receipt = reviewerReplacementReceipt(original, patched, receipt);
368
494
  }
369
495
  try {
370
496
  // A write into a not-yet-existing subtree creates it — an accepted proposal must not
@@ -373,20 +499,19 @@ export default class File extends CoreSchemeAdapterBase {
373
499
  await writeFile(canonical, patched, "utf8");
374
500
  }
375
501
  catch (err) {
376
- return {
377
- status: 500,
378
- outcome: "write_failed",
379
- body: err instanceof Error ? err.message : String(err),
380
- };
502
+ console.error(`File write failed for '${relPath}':`, err);
503
+ return Results.failure("scheme:file", "write-failed", 500, `The file could not be written: ${ErrorDetail.preview(err)}`, { outcome: "write_failed" }, {
504
+ path: relPath,
505
+ stage: "filesystem-write",
506
+ });
381
507
  }
382
508
  // Register the file as an entry so it appears in the manifest
383
509
  // under file:///<relPath> and is READ-able. mimetype is best-effort
384
510
  // by file extension; .md → text/markdown, anything else → text/plain.
385
511
  const mimetype = relPath.endsWith(".md") ? "text/markdown" : "text/plain";
386
512
  try {
387
- // scheme=null: the "file" scheme is a routing internal only;
388
- // never stored. Entries.scheme stays NULL for filesystem rows
389
- // so render-time bare-path output requires no special case.
513
+ // {§entry-identity-no-null} — file members persist under the reserved
514
+ // `file` identity; bare-path rendering is a projection of that row.
390
515
  const { entryId } = await EntryCrud.writeEntry(relPath, {
391
516
  channels: { body: { content: patched, mimetype } },
392
517
  tags: [],
@@ -405,37 +530,49 @@ export default class File extends CoreSchemeAdapterBase {
405
530
  }
406
531
  }
407
532
  catch (err) {
408
- // Disk write succeeded; entry registration failed. Surface
409
- // the write as 200 (file is on disk) but log the failure —
410
- // the manifest just won't reflect it until next time.
411
- // (Could harden to roll back the file write; for v0,
412
- // disk-truth-of-record wins.)
413
- return {
414
- status: 200,
415
- outcome: "materialize_failed",
416
- body: err instanceof Error ? err.message : String(err),
417
- };
533
+ // Disk truth changed but the addressable entry did not. This is a
534
+ // partial failure, never a successful operation result.
535
+ console.error(`File registration failed for '${relPath}':`, err);
536
+ return Results.failure("scheme:file", "materialization-failed", 500, `The file was written to disk but could not be registered: ${ErrorDetail.preview(err)}`, { outcome: "materialize_failed" }, {
537
+ path: relPath,
538
+ stage: "entry-registration",
539
+ retryable: false,
540
+ });
418
541
  }
419
542
  if (receipt === undefined)
420
- return { status: 200, body: attrs.span };
421
- return { status: 200, result: { receipt: projectEditReceipt(receipt, 0), editReceipt: receipt } };
543
+ return { status: 200 };
544
+ return {
545
+ status: 200,
546
+ editReceipt: receipt,
547
+ result: { receipt: projectEditReceipt(receipt, 0) },
548
+ };
422
549
  }
423
550
  // deleteEntry — the KILL / MOVE-source counterpart of writeEntry. Deleting a host file is
424
551
  // DESTRUCTIVE, so it PROPOSES for review (202), exactly as edit() does — never an ungated unlink.
425
- // §membership — only a MEMBER reaches the proposal; a non-member is invisible (404), so untracked
552
+ // {§membership} — only a MEMBER reaches the proposal; a non-member is invisible (404), so untracked
426
553
  // disk is never probed or touched. applyResolution unlinks + deregisters on accept.
427
554
  async deleteEntry(pathname, ctx) {
428
555
  const core = this.coreContext(ctx);
429
556
  const root = await loadWorkspaceRoot(core.db, core.workspaceId);
430
- if (root === null)
431
- return { status: 400 };
557
+ if (root === null) {
558
+ return Results.failure("scheme:file", "project-root-required", 400, "File deletion requires a workspace project root.", {}, { retryable: false });
559
+ }
432
560
  const rel = Namespace.canonicalize(pathname, root);
433
561
  if (rel === null)
434
- return { status: 404 };
562
+ return Results.failure("scheme:file", "entry-not-found", 404, `No file entry exists at ${pathname}.`, {}, { target: pathname });
435
563
  const member = await core.db.crud_find_workspace_entry.get({ workspace_id: core.workspaceId, owner_id: await Owner.commonsId(core.db, core.workspaceId), scheme: "file", pathname: rel });
436
564
  if (member === undefined)
437
- return { status: 404 };
565
+ return Results.failure("scheme:file", "entry-not-found", 404, `No file entry exists at ${rel}.`, {}, { target: rel });
438
566
  return { status: 202, attrs: { deletePath: rel } };
439
567
  }
568
+ async deleteChannel(pathname, channel, ctx) {
569
+ if (channel !== File.manifest.defaultChannel) {
570
+ return Results.failure("scheme:file", "channel-not-found", 404, `No channel named #${channel} exists at ${pathname}.`, {}, {
571
+ target: pathname,
572
+ channel,
573
+ });
574
+ }
575
+ return this.deleteEntry(pathname, ctx);
576
+ }
440
577
  }
441
578
  //# sourceMappingURL=File.js.map