@plurnk/plurnk-service 1.3.12 → 1.5.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 (488) hide show
  1. package/.env.defaults +95 -91
  2. package/INSTALL.md +54 -28
  3. package/README.md +25 -10
  4. package/SPEC.md +2426 -942
  5. package/dist/Paths.d.ts +4 -1
  6. package/dist/Paths.d.ts.map +1 -1
  7. package/dist/Paths.js +24 -23
  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 +4 -3
  18. package/dist/content/index.d.ts.map +1 -1
  19. package/dist/content/index.js +2 -1
  20. package/dist/content/index.js.map +1 -1
  21. package/dist/content/line-marker.d.ts +10 -9
  22. package/dist/content/line-marker.d.ts.map +1 -1
  23. package/dist/content/line-marker.js +8 -11
  24. package/dist/content/line-marker.js.map +1 -1
  25. package/dist/content/matcher.d.ts +15 -26
  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-projector.d.ts +14 -0
  36. package/dist/content/read-projector.d.ts.map +1 -0
  37. package/dist/content/read-projector.js +86 -0
  38. package/dist/content/read-projector.js.map +1 -0
  39. package/dist/content/read-resolve.d.ts +6 -7
  40. package/dist/content/read-resolve.d.ts.map +1 -1
  41. package/dist/content/read-resolve.js +36 -59
  42. package/dist/content/read-resolve.js.map +1 -1
  43. package/dist/core/BranchReceipt.d.ts +6 -0
  44. package/dist/core/BranchReceipt.d.ts.map +1 -0
  45. package/dist/core/BranchReceipt.js +24 -0
  46. package/dist/core/BranchReceipt.js.map +1 -0
  47. package/dist/core/BudgetOverflow.d.ts +20 -0
  48. package/dist/core/BudgetOverflow.d.ts.map +1 -0
  49. package/dist/core/BudgetOverflow.js +49 -0
  50. package/dist/core/BudgetOverflow.js.map +1 -0
  51. package/dist/core/BudgetReadout.d.ts +8 -0
  52. package/dist/core/BudgetReadout.d.ts.map +1 -0
  53. package/dist/core/BudgetReadout.js +79 -0
  54. package/dist/core/BudgetReadout.js.map +1 -0
  55. package/dist/core/ChannelWrite.d.ts +31 -9
  56. package/dist/core/ChannelWrite.d.ts.map +1 -1
  57. package/dist/core/ChannelWrite.js +30 -23
  58. package/dist/core/ChannelWrite.js.map +1 -1
  59. package/dist/core/ChannelWrite.sql +14 -12
  60. package/dist/core/CoreSchemeServices.d.ts +18 -4
  61. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  62. package/dist/core/CoreSchemeServices.js +7 -1
  63. package/dist/core/CoreSchemeServices.js.map +1 -1
  64. package/dist/core/Dispatcher.d.ts +26 -21
  65. package/dist/core/Dispatcher.d.ts.map +1 -1
  66. package/dist/core/Dispatcher.js +1626 -671
  67. package/dist/core/Dispatcher.js.map +1 -1
  68. package/dist/core/DurableStatement.d.ts +6 -0
  69. package/dist/core/DurableStatement.d.ts.map +1 -0
  70. package/dist/core/DurableStatement.js +51 -0
  71. package/dist/core/DurableStatement.js.map +1 -0
  72. package/dist/core/Engine.d.ts +64 -29
  73. package/dist/core/Engine.d.ts.map +1 -1
  74. package/dist/core/Engine.js +1348 -835
  75. package/dist/core/Engine.js.map +1 -1
  76. package/dist/core/Engine.sql +329 -234
  77. package/dist/core/EnvFlags.js +1 -1
  78. package/dist/core/EnvFlags.js.map +1 -1
  79. package/dist/core/ErrorDetail.d.ts +6 -0
  80. package/dist/core/ErrorDetail.d.ts.map +1 -0
  81. package/dist/core/ErrorDetail.js +20 -0
  82. package/dist/core/ErrorDetail.js.map +1 -0
  83. package/dist/core/ExecutorRegistry.d.ts +14 -3
  84. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  85. package/dist/core/ExecutorRegistry.js +56 -30
  86. package/dist/core/ExecutorRegistry.js.map +1 -1
  87. package/dist/core/GitBranch.d.ts +20 -0
  88. package/dist/core/GitBranch.d.ts.map +1 -0
  89. package/dist/core/GitBranch.js +110 -0
  90. package/dist/core/GitBranch.js.map +1 -0
  91. package/dist/core/JournalTurn.d.ts +9 -0
  92. package/dist/core/JournalTurn.d.ts.map +1 -0
  93. package/dist/core/JournalTurn.js +14 -0
  94. package/dist/core/JournalTurn.js.map +1 -0
  95. package/dist/core/JournalTurn.sql +10 -0
  96. package/dist/core/LogBody.d.ts +18 -0
  97. package/dist/core/LogBody.d.ts.map +1 -0
  98. package/dist/core/LogBody.js +180 -0
  99. package/dist/core/LogBody.js.map +1 -0
  100. package/dist/core/LoopFlagsReader.d.ts +7 -0
  101. package/dist/core/LoopFlagsReader.d.ts.map +1 -0
  102. package/dist/core/LoopFlagsReader.js +33 -0
  103. package/dist/core/LoopFlagsReader.js.map +1 -0
  104. package/dist/core/LoopLifecycle.d.ts +8 -2
  105. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  106. package/dist/core/LoopLifecycle.js +54 -11
  107. package/dist/core/LoopLifecycle.js.map +1 -1
  108. package/dist/core/LoopLifecycle.sql +10 -7
  109. package/dist/core/NoticeChannel.d.ts +13 -0
  110. package/dist/core/NoticeChannel.d.ts.map +1 -0
  111. package/dist/core/NoticeChannel.js +40 -0
  112. package/dist/core/NoticeChannel.js.map +1 -0
  113. package/dist/core/Owner.d.ts +0 -1
  114. package/dist/core/Owner.d.ts.map +1 -1
  115. package/dist/core/Owner.js +2 -5
  116. package/dist/core/Owner.js.map +1 -1
  117. package/dist/core/PacketBuilder.d.ts +27 -25
  118. package/dist/core/PacketBuilder.d.ts.map +1 -1
  119. package/dist/core/PacketBuilder.js +321 -383
  120. package/dist/core/PacketBuilder.js.map +1 -1
  121. package/dist/core/ProblemLog.d.ts +26 -0
  122. package/dist/core/ProblemLog.d.ts.map +1 -0
  123. package/dist/core/ProblemLog.js +58 -0
  124. package/dist/core/ProblemLog.js.map +1 -0
  125. package/dist/core/ProposalLifecycle.d.ts +15 -21
  126. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  127. package/dist/core/ProposalLifecycle.js +291 -78
  128. package/dist/core/ProposalLifecycle.js.map +1 -1
  129. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  130. package/dist/core/ProviderInstantiate.js +51 -48
  131. package/dist/core/ProviderInstantiate.js.map +1 -1
  132. package/dist/core/SchemeRegistry.d.ts +14 -8
  133. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  134. package/dist/core/SchemeRegistry.js +189 -114
  135. package/dist/core/SchemeRegistry.js.map +1 -1
  136. package/dist/core/ServiceTeardown.d.ts +11 -0
  137. package/dist/core/ServiceTeardown.d.ts.map +1 -0
  138. package/dist/core/ServiceTeardown.js +70 -0
  139. package/dist/core/ServiceTeardown.js.map +1 -0
  140. package/dist/core/StoredPacket.d.ts +29 -0
  141. package/dist/core/StoredPacket.d.ts.map +1 -0
  142. package/dist/core/StoredPacket.js +147 -0
  143. package/dist/core/StoredPacket.js.map +1 -0
  144. package/dist/core/StrikeRail.d.ts +6 -3
  145. package/dist/core/StrikeRail.d.ts.map +1 -1
  146. package/dist/core/StrikeRail.js +27 -56
  147. package/dist/core/StrikeRail.js.map +1 -1
  148. package/dist/core/TerminalResult.d.ts +16 -0
  149. package/dist/core/TerminalResult.d.ts.map +1 -0
  150. package/dist/core/TerminalResult.js +49 -0
  151. package/dist/core/TerminalResult.js.map +1 -0
  152. package/dist/core/WorkerControlAddress.d.ts +17 -0
  153. package/dist/core/WorkerControlAddress.d.ts.map +1 -0
  154. package/dist/core/WorkerControlAddress.js +44 -0
  155. package/dist/core/WorkerControlAddress.js.map +1 -0
  156. package/dist/core/WorkerName.d.ts +31 -0
  157. package/dist/core/WorkerName.d.ts.map +1 -0
  158. package/dist/core/WorkerName.js +95 -0
  159. package/dist/core/WorkerName.js.map +1 -0
  160. package/dist/core/WorkerName.sql +36 -0
  161. package/dist/core/WorkspaceGate.d.ts +14 -0
  162. package/dist/core/WorkspaceGate.d.ts.map +1 -0
  163. package/dist/core/WorkspaceGate.js +155 -0
  164. package/dist/core/WorkspaceGate.js.map +1 -0
  165. package/dist/core/caps/CapsResolve.d.ts +5 -1
  166. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  167. package/dist/core/caps/CapsResolve.js +8 -4
  168. package/dist/core/caps/CapsResolve.js.map +1 -1
  169. package/dist/core/caps/DbChannelCaps.d.ts +5 -11
  170. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  171. package/dist/core/caps/DbChannelCaps.js +30 -12
  172. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  173. package/dist/core/caps/DbEntryCaps.d.ts +5 -14
  174. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  175. package/dist/core/caps/DbEntryCaps.js +27 -8
  176. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  177. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  178. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  179. package/dist/core/caps/DbNotifyCaps.js +18 -15
  180. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  181. package/dist/core/caps/DbProjectionCaps.d.ts +5 -5
  182. package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
  183. package/dist/core/caps/DbProjectionCaps.js +21 -4
  184. package/dist/core/caps/DbProjectionCaps.js.map +1 -1
  185. package/dist/core/caps/DbSubscriptionCaps.d.ts +4 -6
  186. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  187. package/dist/core/caps/DbSubscriptionCaps.js +75 -66
  188. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  189. package/dist/core/caps/DbTagCaps.d.ts +5 -12
  190. package/dist/core/caps/DbTagCaps.d.ts.map +1 -1
  191. package/dist/core/caps/DbTagCaps.js +20 -11
  192. package/dist/core/caps/DbTagCaps.js.map +1 -1
  193. package/dist/core/caps/SchemeCtxImpl.d.ts +6 -1
  194. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  195. package/dist/core/caps/SchemeCtxImpl.js +6 -6
  196. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  197. package/dist/core/content-hash.js +1 -1
  198. package/dist/core/content-hash.js.map +1 -1
  199. package/dist/core/env-defaults.d.ts.map +1 -1
  200. package/dist/core/env-defaults.js.map +1 -1
  201. package/dist/core/fork.d.ts.map +1 -1
  202. package/dist/core/fork.js +55 -30
  203. package/dist/core/fork.js.map +1 -1
  204. package/dist/core/fork.sql +43 -29
  205. package/dist/core/git-env.d.ts +2 -0
  206. package/dist/core/git-env.d.ts.map +1 -1
  207. package/dist/core/git-env.js +13 -18
  208. package/dist/core/git-env.js.map +1 -1
  209. package/dist/core/git-iso.d.ts +3 -2
  210. package/dist/core/git-iso.d.ts.map +1 -1
  211. package/dist/core/git-iso.js +36 -27
  212. package/dist/core/git-iso.js.map +1 -1
  213. package/dist/core/git-membership.d.ts +2 -1
  214. package/dist/core/git-membership.d.ts.map +1 -1
  215. package/dist/core/git-membership.js +276 -162
  216. package/dist/core/git-membership.js.map +1 -1
  217. package/dist/core/git-state.d.ts +8 -1
  218. package/dist/core/git-state.d.ts.map +1 -1
  219. package/dist/core/git-state.js +44 -26
  220. package/dist/core/git-state.js.map +1 -1
  221. package/dist/core/namespace.d.ts +1 -0
  222. package/dist/core/namespace.d.ts.map +1 -1
  223. package/dist/core/namespace.js +20 -0
  224. package/dist/core/namespace.js.map +1 -1
  225. package/dist/core/optimistic-settlement.d.ts +2 -0
  226. package/dist/core/optimistic-settlement.d.ts.map +1 -0
  227. package/dist/core/optimistic-settlement.js +14 -0
  228. package/dist/core/optimistic-settlement.js.map +1 -0
  229. package/dist/core/owner.sql +1 -1
  230. package/dist/core/packet-inject.d.ts.map +1 -1
  231. package/dist/core/packet-inject.js +7 -11
  232. package/dist/core/packet-inject.js.map +1 -1
  233. package/dist/core/packet-wire.d.ts +3 -21
  234. package/dist/core/packet-wire.d.ts.map +1 -1
  235. package/dist/core/packet-wire.js +305 -385
  236. package/dist/core/packet-wire.js.map +1 -1
  237. package/dist/core/plurnk-uri.d.ts +12 -1
  238. package/dist/core/plurnk-uri.d.ts.map +1 -1
  239. package/dist/core/plurnk-uri.js +49 -29
  240. package/dist/core/plurnk-uri.js.map +1 -1
  241. package/dist/core/results.d.ts +18 -5
  242. package/dist/core/results.d.ts.map +1 -1
  243. package/dist/core/results.js +33 -18
  244. package/dist/core/results.js.map +1 -1
  245. package/dist/core/ruler_count.d.ts +4 -0
  246. package/dist/core/ruler_count.d.ts.map +1 -0
  247. package/dist/core/ruler_count.js +7 -0
  248. package/dist/core/ruler_count.js.map +1 -0
  249. package/dist/core/scheme-types.d.ts +3 -17
  250. package/dist/core/scheme-types.d.ts.map +1 -1
  251. package/dist/core/scheme-types.js.map +1 -1
  252. package/dist/core/search-gate.d.ts.map +1 -1
  253. package/dist/core/search-gate.js +3 -11
  254. package/dist/core/search-gate.js.map +1 -1
  255. package/dist/core/teaching-corpus.d.ts +6 -0
  256. package/dist/core/teaching-corpus.d.ts.map +1 -0
  257. package/dist/core/teaching-corpus.js +23 -0
  258. package/dist/core/teaching-corpus.js.map +1 -0
  259. package/dist/core/teaching.d.ts +0 -1
  260. package/dist/core/teaching.d.ts.map +1 -1
  261. package/dist/core/teaching.js +2 -8
  262. package/dist/core/teaching.js.map +1 -1
  263. package/dist/core/token-ruler.d.ts.map +1 -1
  264. package/dist/core/token-ruler.js +3 -16
  265. package/dist/core/token-ruler.js.map +1 -1
  266. package/dist/core/turn-scheduler.d.ts +1 -1
  267. package/dist/core/turn-scheduler.d.ts.map +1 -1
  268. package/dist/core/turn-scheduler.js +1 -1
  269. package/dist/core/turn-scheduler.js.map +1 -1
  270. package/dist/core/worker-cap.d.ts +2 -4
  271. package/dist/core/worker-cap.d.ts.map +1 -1
  272. package/dist/core/worker-cap.js +13 -4
  273. package/dist/core/worker-cap.js.map +1 -1
  274. package/dist/core/{run-ops.sql → worker-ops.sql} +14 -14
  275. package/dist/core/workspace-settings.d.ts.map +1 -1
  276. package/dist/core/workspace-settings.js +17 -9
  277. package/dist/core/workspace-settings.js.map +1 -1
  278. package/dist/digest/Digest.d.ts +2 -1
  279. package/dist/digest/Digest.d.ts.map +1 -1
  280. package/dist/digest/Digest.js +510 -149
  281. package/dist/digest/Digest.js.map +1 -1
  282. package/dist/digest/digest.sql +23 -10
  283. package/dist/index.d.ts.map +1 -1
  284. package/dist/index.js +3 -5
  285. package/dist/index.js.map +1 -1
  286. package/dist/observe/api.d.ts +6 -0
  287. package/dist/observe/api.d.ts.map +1 -0
  288. package/dist/observe/api.js +11 -0
  289. package/dist/observe/api.js.map +1 -0
  290. package/dist/observe/init.d.ts +7 -0
  291. package/dist/observe/init.d.ts.map +1 -0
  292. package/dist/observe/init.js +159 -0
  293. package/dist/observe/init.js.map +1 -0
  294. package/dist/observe/metrics.d.ts +6 -0
  295. package/dist/observe/metrics.d.ts.map +1 -0
  296. package/dist/observe/metrics.js +15 -0
  297. package/dist/observe/metrics.js.map +1 -0
  298. package/dist/observe/spans.d.ts +4 -0
  299. package/dist/observe/spans.d.ts.map +1 -0
  300. package/dist/observe/spans.js +51 -0
  301. package/dist/observe/spans.js.map +1 -0
  302. package/dist/schemes/EffectPolicy.d.ts +1 -0
  303. package/dist/schemes/EffectPolicy.d.ts.map +1 -1
  304. package/dist/schemes/EffectPolicy.js +5 -2
  305. package/dist/schemes/EffectPolicy.js.map +1 -1
  306. package/dist/schemes/Exec.d.ts +9 -14
  307. package/dist/schemes/Exec.d.ts.map +1 -1
  308. package/dist/schemes/Exec.js +402 -262
  309. package/dist/schemes/Exec.js.map +1 -1
  310. package/dist/schemes/ExecOutputScheme.d.ts +9 -9
  311. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  312. package/dist/schemes/ExecOutputScheme.js +49 -18
  313. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  314. package/dist/schemes/File.d.ts +7 -13
  315. package/dist/schemes/File.d.ts.map +1 -1
  316. package/dist/schemes/File.js +280 -151
  317. package/dist/schemes/File.js.map +1 -1
  318. package/dist/schemes/Log.d.ts +8 -12
  319. package/dist/schemes/Log.d.ts.map +1 -1
  320. package/dist/schemes/Log.js +402 -233
  321. package/dist/schemes/Log.js.map +1 -1
  322. package/dist/schemes/Log.sql +71 -32
  323. package/dist/schemes/Prompt.d.ts +3 -3
  324. package/dist/schemes/Prompt.d.ts.map +1 -1
  325. package/dist/schemes/Prompt.js +10 -10
  326. package/dist/schemes/Prompt.js.map +1 -1
  327. package/dist/schemes/Skill.d.ts +2 -3
  328. package/dist/schemes/Skill.d.ts.map +1 -1
  329. package/dist/schemes/Skill.js +2 -6
  330. package/dist/schemes/Skill.js.map +1 -1
  331. package/dist/schemes/Worker.d.ts +11 -19
  332. package/dist/schemes/Worker.d.ts.map +1 -1
  333. package/dist/schemes/Worker.js +210 -89
  334. package/dist/schemes/Worker.js.map +1 -1
  335. package/dist/schemes/_entry-chunk.d.ts.map +1 -1
  336. package/dist/schemes/_entry-chunk.js +12 -5
  337. package/dist/schemes/_entry-chunk.js.map +1 -1
  338. package/dist/schemes/_entry-crud.d.ts +11 -9
  339. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  340. package/dist/schemes/_entry-crud.js +72 -13
  341. package/dist/schemes/_entry-crud.js.map +1 -1
  342. package/dist/schemes/_entry-crud.sql +28 -10
  343. package/dist/schemes/_entry-find.d.ts +49 -18
  344. package/dist/schemes/_entry-find.d.ts.map +1 -1
  345. package/dist/schemes/_entry-find.js +348 -195
  346. package/dist/schemes/_entry-find.js.map +1 -1
  347. package/dist/schemes/_entry-find.sql +20 -8
  348. package/dist/schemes/_entry-graph.d.ts +4 -2
  349. package/dist/schemes/_entry-graph.d.ts.map +1 -1
  350. package/dist/schemes/_entry-graph.js +55 -31
  351. package/dist/schemes/_entry-graph.js.map +1 -1
  352. package/dist/schemes/_entry-graph.sql +37 -22
  353. package/dist/schemes/_entry-manifest.d.ts +10 -15
  354. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  355. package/dist/schemes/_entry-manifest.js +50 -268
  356. package/dist/schemes/_entry-manifest.js.map +1 -1
  357. package/dist/schemes/_entry-ops.d.ts +14 -15
  358. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  359. package/dist/schemes/_entry-ops.js +121 -110
  360. package/dist/schemes/_entry-ops.js.map +1 -1
  361. package/dist/schemes/_entry-ops.sql +2 -3
  362. package/dist/schemes/_entry-semantic.d.ts +22 -9
  363. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  364. package/dist/schemes/_entry-semantic.js +127 -67
  365. package/dist/schemes/_entry-semantic.js.map +1 -1
  366. package/dist/schemes/_entry-semantic.sql +63 -45
  367. package/dist/schemes/_entry-send.d.ts +3 -4
  368. package/dist/schemes/_entry-send.d.ts.map +1 -1
  369. package/dist/schemes/_entry-send.js +48 -23
  370. package/dist/schemes/_entry-send.js.map +1 -1
  371. package/dist/schemes/_path-scope.d.ts +23 -0
  372. package/dist/schemes/_path-scope.d.ts.map +1 -0
  373. package/dist/schemes/_path-scope.js +61 -0
  374. package/dist/schemes/_path-scope.js.map +1 -0
  375. package/dist/schemes/_search-candidate.d.ts +20 -0
  376. package/dist/schemes/_search-candidate.d.ts.map +1 -0
  377. package/dist/schemes/_search-candidate.js +19 -0
  378. package/dist/schemes/_search-candidate.js.map +1 -0
  379. package/dist/schemes/_search-exclusion.d.ts +7 -0
  380. package/dist/schemes/_search-exclusion.d.ts.map +1 -0
  381. package/dist/schemes/_search-exclusion.js +23 -0
  382. package/dist/schemes/_search-exclusion.js.map +1 -0
  383. package/dist/schemes/_search-index.d.ts +6 -0
  384. package/dist/schemes/_search-index.d.ts.map +1 -0
  385. package/dist/schemes/_search-index.js +328 -0
  386. package/dist/schemes/_search-index.js.map +1 -0
  387. package/dist/schemes/cosine.js +1 -1
  388. package/dist/schemes/cosine.js.map +1 -1
  389. package/dist/schemes/exec-abort.js +3 -3
  390. package/dist/schemes/exec-abort.js.map +1 -1
  391. package/dist/schemes/exec-env.js +3 -3
  392. package/dist/schemes/exec-env.js.map +1 -1
  393. package/dist/server/BranchBatches.d.ts +49 -0
  394. package/dist/server/BranchBatches.d.ts.map +1 -0
  395. package/dist/server/BranchBatches.js +592 -0
  396. package/dist/server/BranchBatches.js.map +1 -0
  397. package/dist/server/Daemon.d.ts +39 -71
  398. package/dist/server/Daemon.d.ts.map +1 -1
  399. package/dist/server/Daemon.js +1133 -491
  400. package/dist/server/Daemon.js.map +1 -1
  401. package/dist/server/DaemonModule.d.ts +31 -0
  402. package/dist/server/DaemonModule.d.ts.map +1 -0
  403. package/dist/server/DaemonModule.js +2 -0
  404. package/dist/server/DaemonModule.js.map +1 -0
  405. package/dist/server/branch-batch.sql +154 -0
  406. package/dist/server/client-input.d.ts +13 -0
  407. package/dist/server/client-input.d.ts.map +1 -1
  408. package/dist/server/client-input.js +268 -57
  409. package/dist/server/client-input.js.map +1 -1
  410. package/dist/server/dispatch-as-plurnk.d.ts +1 -1
  411. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  412. package/dist/server/dispatch-as-plurnk.js +15 -7
  413. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  414. package/dist/server/drain.sql +96 -65
  415. package/dist/server/envelope.d.ts +4 -7
  416. package/dist/server/envelope.d.ts.map +1 -1
  417. package/dist/server/envelope.js +118 -95
  418. package/dist/server/envelope.js.map +1 -1
  419. package/dist/server/envelope.sql +18 -23
  420. package/dist/server/exec-poll-backoff.js +1 -1
  421. package/dist/server/exec-poll-backoff.js.map +1 -1
  422. package/dist/server/lifecycle-recovery.sql +112 -2
  423. package/dist/server/logEntry.d.ts +4 -2
  424. package/dist/server/logEntry.d.ts.map +1 -1
  425. package/dist/server/logEntry.js +6 -3
  426. package/dist/server/logEntry.js.map +1 -1
  427. package/dist/server/logEntry.sql +1 -1
  428. package/dist/server/loop-model.d.ts.map +1 -1
  429. package/dist/server/loop-model.js +22 -9
  430. package/dist/server/loop-model.js.map +1 -1
  431. package/dist/server/loopDocs.d.ts.map +1 -1
  432. package/dist/server/loopDocs.js +5 -4
  433. package/dist/server/loopDocs.js.map +1 -1
  434. package/dist/server/seam-entry-read.sql +15 -10
  435. package/dist/server/seam-log-read.sql +2 -2
  436. package/dist/server/seam-loop.sql +1 -1
  437. package/dist/server/seam-proposal-list.sql +17 -7
  438. package/dist/service.d.ts.map +1 -1
  439. package/dist/service.js +79 -61
  440. package/dist/service.js.map +1 -1
  441. package/migrations/001_schema.sql +1010 -0
  442. package/package.json +65 -32
  443. package/dist/core/PluginLoader.d.ts +0 -20
  444. package/dist/core/PluginLoader.d.ts.map +0 -1
  445. package/dist/core/PluginLoader.js +0 -142
  446. package/dist/core/PluginLoader.js.map +0 -1
  447. package/dist/core/TelemetryChannel.d.ts +0 -37
  448. package/dist/core/TelemetryChannel.d.ts.map +0 -1
  449. package/dist/core/TelemetryChannel.js +0 -80
  450. package/dist/core/TelemetryChannel.js.map +0 -1
  451. package/dist/core/path-decode.d.ts +0 -3
  452. package/dist/core/path-decode.d.ts.map +0 -1
  453. package/dist/core/path-decode.js +0 -9
  454. package/dist/core/path-decode.js.map +0 -1
  455. package/dist/core/plugin-attribution.d.ts +0 -5
  456. package/dist/core/plugin-attribution.d.ts.map +0 -1
  457. package/dist/core/plugin-attribution.js +0 -39
  458. package/dist/core/plugin-attribution.js.map +0 -1
  459. package/dist/core/world-state.d.ts +0 -10
  460. package/dist/core/world-state.d.ts.map +0 -1
  461. package/dist/core/world-state.js +0 -35
  462. package/dist/core/world-state.js.map +0 -1
  463. package/dist/core/world-state.sql +0 -35
  464. package/dist/core/zero-pin.d.ts +0 -3
  465. package/dist/core/zero-pin.d.ts.map +0 -1
  466. package/dist/core/zero-pin.js +0 -16
  467. package/dist/core/zero-pin.js.map +0 -1
  468. package/dist/server/auto.d.ts +0 -6
  469. package/dist/server/auto.d.ts.map +0 -1
  470. package/dist/server/auto.js +0 -63
  471. package/dist/server/auto.js.map +0 -1
  472. package/dist/server/clientTurn.d.ts +0 -6
  473. package/dist/server/clientTurn.d.ts.map +0 -1
  474. package/dist/server/clientTurn.js +0 -22
  475. package/dist/server/clientTurn.js.map +0 -1
  476. package/dist/server/clientTurn.sql +0 -10
  477. package/dist/server/noProposals.d.ts +0 -6
  478. package/dist/server/noProposals.d.ts.map +0 -1
  479. package/dist/server/noProposals.js +0 -37
  480. package/dist/server/noProposals.js.map +0 -1
  481. package/dist/server/version-info.d.ts +0 -14
  482. package/dist/server/version-info.d.ts.map +0 -1
  483. package/dist/server/version-info.js +0 -69
  484. package/dist/server/version-info.js.map +0 -1
  485. package/migrations/0000-00-00.01_schema.sql +0 -536
  486. package/migrations/0004_loop-provider.sql +0 -3
  487. package/migrations/0005_subscription-published-channel.sql +0 -3
  488. package/migrations/0006_loop-max-turns.sql +0 -3
@@ -1,9 +1,9 @@
1
- // SPEC §membership D4 — git-ls-files workspace membership. §membership-git-membership
1
+ // SPEC {§membership} D4 — git-ls-files workspace membership. {§membership-git-membership}
2
2
  //
3
3
  // When a workspace's `project_root` is a git working tree, the git-tracked
4
4
  // files (`git ls-files`) are workspace MEMBERS without any explicit client
5
5
  // `pick`. This module resolves that membership and (when token accounting is
6
- // available) materializes active members' disk content into a body channel,
6
+ // available) materializes active members' model-readable representation into a body channel,
7
7
  // so they appear in the entry catalog (FIND-served) and are READ-able.
8
8
  //
9
9
  // Decisions realized here:
@@ -17,25 +17,54 @@
17
17
  // `(ls-files ∪ pick) − hide`; view is enforced at the File edit gate.
18
18
  // D5 — coverage is exhaustive, work is change-gated: every member is stat'd each
19
19
  // turn, but only one whose mtime:size signature changed is re-read,
20
- // re-tokenized, and rewritten; an unchanged member is a no-op, and the EMI
21
- // divergence rides that same pass. {§membership-change-gated-sync}
20
+ // re-tokenized, and rewritten. A binary source also compares the installed
21
+ // projection identity without reacquiring bytes. EMI divergence rides the
22
+ // same pass. {§membership-change-gated-sync}
22
23
  //
23
- // git resolution is in-process by default (GitIso / isomorphic-git, #461) — portable,
24
- // sandbox-safe, hermetic by construction. PLURNK_SERVICE_GIT_NATIVE=1 routes to system git
25
- // (subprocess + hermeticGitEnv, AbortSignal-respecting) — the in-process membership pass
26
- // measures ~8x native (~130ms at 20k files), so a large-repo host can buy the hot path back.
24
+ // Git resolution uses native Git by default (subprocess + hermeticGitEnv,
25
+ // AbortSignal-respecting). PLURNK_SERVICE_GIT_ISO=1 explicitly selects the
26
+ // slower in-process portability backend for a deployment that cannot spawn Git.
27
27
  import { execFile } from "node:child_process";
28
- import { hermeticGitEnv } from "./git-env.js";
28
+ import { gitOutputMaxBytes, hermeticGitEnv, isomorphicGitEnabled } from "./git-env.js";
29
29
  import GitIso from "./git-iso.js";
30
30
  import { promisify } from "node:util";
31
31
  import { readFile, glob, stat } from "node:fs/promises";
32
- import { resolve, relative, join, matchesGlob } from "node:path";
32
+ import { resolve, join, matchesGlob } from "node:path";
33
+ import { MimetypeInputLimitError, } from "@plurnk/plurnk-mimetypes";
33
34
  import { MimetypeBinary } from "../content/index.js";
34
35
  import Owner from "./Owner.js";
35
36
  import EntryCrud from "../schemes/_entry-crud.js";
36
37
  import WorkspaceSettings from "./workspace-settings.js";
37
- // Feature-flag convention: `=== "1"` exactly. Default (unset/0) = the in-process GitIso backend.
38
- const nativeGit = () => process.env.PLURNK_SERVICE_GIT_NATIVE === "1";
38
+ import Namespace from "./namespace.js";
39
+ const ABSENT_SIG = "absent";
40
+ const sourceProjectionFrom = (encoded) => {
41
+ const attributes = JSON.parse(encoded);
42
+ if (attributes === null || typeof attributes !== "object" || Array.isArray(attributes)) {
43
+ throw new TypeError("GitMembership: entry attributes must be a JSON object");
44
+ }
45
+ const candidate = attributes.sourceProjection;
46
+ if (candidate === undefined)
47
+ return null;
48
+ if (candidate === null || typeof candidate !== "object" || Array.isArray(candidate)) {
49
+ throw new TypeError("GitMembership: sourceProjection must be a JSON object");
50
+ }
51
+ const projection = candidate;
52
+ if (typeof projection.mimetype !== "string"
53
+ || projection.mimetype.length === 0
54
+ || typeof projection.identity !== "string"
55
+ || projection.identity.length === 0
56
+ || !["projected", "unavailable", "input-limit"].includes(projection.disposition ?? "")) {
57
+ throw new TypeError("GitMembership: sourceProjection metadata is malformed");
58
+ }
59
+ const hasLimit = Number.isSafeInteger(projection.maximumBytes)
60
+ && (projection.maximumBytes ?? 0) > 0
61
+ && Number.isSafeInteger(projection.observedBytes)
62
+ && (projection.observedBytes ?? 0) > (projection.maximumBytes ?? 0);
63
+ if ((projection.disposition === "input-limit") !== hasLimit) {
64
+ throw new TypeError("GitMembership: sourceProjection input-limit evidence is malformed");
65
+ }
66
+ return projection;
67
+ };
39
68
  export default class GitMembership {
40
69
  // Workspace creation starts a background warm while the first model turn
41
70
  // performs its own eager membership refresh. Both are legitimate callers,
@@ -46,7 +75,8 @@ export default class GitMembership {
46
75
  static #execFileP = promisify(execFile);
47
76
  // {§fs-write-surface} — the blind-write closure git half: would git ADMIT a create at
48
77
  // key (untracked-not-ignored)? check-ignore exit 0 = ignored (refuse), 1 = clean
49
- // (admit via auto-add), anything else (128, no repo) = git grants nothing here.
78
+ // (automatic membership admission, never `git add`), anything else (128, no
79
+ // repo) = git grants nothing here.
50
80
  static async wouldGitAdmit(root, key, signal) {
51
81
  try {
52
82
  await GitMembership.#execFileP("git", ["check-ignore", "-q", "--", key], { cwd: root, signal, env: hermeticGitEnv() });
@@ -56,21 +86,33 @@ export default class GitMembership {
56
86
  return err.code === 1;
57
87
  }
58
88
  }
89
+ static async stageFile(root, key, signal) {
90
+ try {
91
+ if (isomorphicGitEnabled()) {
92
+ await GitIso.add(root, key);
93
+ }
94
+ else {
95
+ await GitMembership.#execFileP("git", ["add", "--", key], { cwd: root, signal, env: hermeticGitEnv() });
96
+ }
97
+ }
98
+ catch {
99
+ // Ignore staging errors if root is not a git repo
100
+ }
101
+ }
59
102
  // project_root for a workspace. NULL = headless (no membership). Read once per
60
103
  // resolution; the File scheme reads the same column for its own root.
61
104
  static async #loadWorkspaceRoot(db, workspaceId) {
62
105
  const row = await db.envelope_get_workspace.get({ id: workspaceId });
63
106
  return row?.project_root ?? null;
64
107
  }
65
- // Tracked files of one repo, workspace-relative — GitIso.trackedFiles (walk STAGE, blob
66
- // entries only) or `git ls-files --stage -z` under the native flag. Either way gitlinks
67
- // are filtered: a submodule (mode 160000, a commit pointer — a directory on disk, not a
68
- // file) is a separate declared repo, never a member of its superproject. Empty → [].
108
+ // Tracked files of one repo, workspace-relative - GitIso.trackedFiles (walk STAGE, blob
109
+ // entries only) or native `git ls-files --stage -z`. Either way gitlinks
110
+ // are filtered: a submodule is a repository boundary, not a file member. Empty → [].
69
111
  static async #gitTrackedFiles(root, signal, cache) {
70
- if (!nativeGit())
112
+ if (isomorphicGitEnabled())
71
113
  return GitIso.trackedFiles(root, cache);
72
114
  // NUL-delimited so paths with spaces/newlines survive.
73
- const { stdout } = await GitMembership.#execFileP("git", ["ls-files", "--stage", "-z"], { cwd: root, signal, maxBuffer: 64 * 1024 * 1024, env: hermeticGitEnv() });
115
+ const { stdout } = await GitMembership.#execFileP("git", ["ls-files", "--stage", "-z"], { cwd: root, signal, maxBuffer: gitOutputMaxBytes(), env: hermeticGitEnv() });
74
116
  const files = [];
75
117
  for (const entry of stdout.split("\0")) {
76
118
  if (entry.length === 0)
@@ -84,22 +126,19 @@ export default class GitMembership {
84
126
  }
85
127
  return files;
86
128
  }
87
- // Untracked-but-not-ignored files of one repo (SPEC §membership-auto-add) —
88
- // GitIso.untrackedFiles (the differential-gated pruning ignore-walk) or `git ls-files
89
- // --others --exclude-standard -z` under the native flag. A model-created file is a repo
129
+ // Untracked-but-not-ignored members of one repo ({§membership-auto-add}) —
130
+ // GitIso.untrackedFiles (the differential-gated pruning ignore-walk) or native `git ls-files
131
+ // --others --exclude-standard -z`. A model-created file is a repo
90
132
  // member the moment it exists — no git-stage required — while `.gitignore` still filters it.
91
133
  static async #gitUntrackedFiles(root, signal, cache) {
92
- if (!nativeGit())
134
+ if (isomorphicGitEnabled())
93
135
  return GitIso.untrackedFiles(root, cache);
94
- const { stdout } = await GitMembership.#execFileP("git", ["ls-files", "--others", "--exclude-standard", "-z"], { cwd: root, signal, maxBuffer: 64 * 1024 * 1024, env: hermeticGitEnv() });
136
+ const { stdout } = await GitMembership.#execFileP("git", ["ls-files", "--others", "--exclude-standard", "-z"], { cwd: root, signal, maxBuffer: gitOutputMaxBytes(), env: hermeticGitEnv() });
95
137
  return stdout.split("\0").filter((e) => e.length > 0);
96
138
  }
97
- // Resolve a declared repo folder to its git toplevel (the repo root containing it), or
98
- // null if it isn't inside a git tree. GitIso.repoToplevel (findRoot) or `rev-parse
99
- // --show-toplevel` under the native flag — both handle plain repos, linked worktrees
100
- // (`.git` is a gitdir: file), and submodules alike.
139
+ // Resolve a directory to the containing Git repository, or null when absent.
101
140
  static async #repoToplevel(dir, signal) {
102
- if (!nativeGit())
141
+ if (isomorphicGitEnabled())
103
142
  return GitIso.repoToplevel(dir);
104
143
  try {
105
144
  const { stdout } = await GitMembership.#execFileP("git", ["rev-parse", "--show-toplevel"], { cwd: dir, signal, env: hermeticGitEnv() });
@@ -109,84 +148,50 @@ export default class GitMembership {
109
148
  return null; // not a git tree (or the dir is gone) — contributes nothing
110
149
  }
111
150
  }
112
- // The forest (SPEC §membership, §membership-forest): union every declared repo's MEMBERS —
113
- // tracked files PLUS untracked-but-not-ignored ones (§membership-auto-add) — each
114
- // path-prefixed by the repo's location relative to the workspace root (empty prefix when
115
- // the repo IS the root). Repos that don't resolve are skipped.
116
- static async #forestMembers(root, repoDirs, signal) {
117
- // Resolve every declared entry to its git toplevel, deduped: a glob (`*`) and an
118
- // explicit declaration can name the same repo, and resolving one repo twice is
119
- // wasted git work. {§membership-forest}
120
- const repoRoots = new Set();
121
- for (const dir of repoDirs) {
122
- for (const candidate of await GitMembership.#expandRepoDirs(root, dir, signal)) {
123
- const repoRoot = await GitMembership.#repoToplevel(resolve(root, candidate), signal);
124
- if (repoRoot !== null)
125
- repoRoots.add(repoRoot);
126
- }
127
- }
151
+ // A workspace owns exactly the repository containing project_root. Packages and
152
+ // projects inside that repository share its Git state; unrelated repositories
153
+ // belong to unrelated workspaces.
154
+ static async projectRepository(db, workspaceId, signal) {
155
+ const root = await GitMembership.#loadWorkspaceRoot(db, workspaceId);
156
+ if (root === null || process.env.PLURNK_SERVICE_GIT_ALLOWED !== "1")
157
+ return null;
158
+ if ((await WorkspaceSettings.read(db, workspaceId)).git === false)
159
+ return null;
160
+ return GitMembership.#repoToplevel(root, signal);
161
+ }
162
+ static async #projectMembers(root, repoRoot, signal) {
128
163
  const members = new Set();
129
- // One iso-git cache per PASS: pack/index parses are reused across this resolve's repos
130
- // and reads, then discarded — never carried across turns, so a rewritten index or
131
- // repacked store can't serve stale.
132
164
  const cache = {};
133
- for (const repoRoot of repoRoots) {
134
- // project_root is no boundary — only the relative-address base. EVERY member is
135
- // addressed relative to the root, a repo outside it included (a `..`-prefixed
136
- // path) — so the universal `join(root, pathname)` disk-resolver works unchanged;
137
- // an absolute pathname would nest UNDER root and never materialize. {§membership-overlay-repo}
138
- const prefix = relative(root, repoRoot);
139
- const tracked = await GitMembership.#gitTrackedFiles(repoRoot, signal, cache);
140
- const untracked = await GitMembership.#gitUntrackedFiles(repoRoot, signal, cache);
141
- for (const f of [...tracked, ...untracked]) {
142
- members.add(join(prefix, f));
143
- }
165
+ const tracked = await GitMembership.#gitTrackedFiles(repoRoot, signal, cache);
166
+ const untracked = await GitMembership.#gitUntrackedFiles(repoRoot, signal, cache);
167
+ for (const file of [...tracked, ...untracked]) {
168
+ members.add(Namespace.fromRepositoryPath(file, root, repoRoot));
144
169
  }
145
170
  return [...members];
146
171
  }
147
- // A declared repo entry → the candidate directories it names. A literal path (no glob
148
- // magic) passes through untouched — preserving the "declare a repo ANYWHERE, including
149
- // OUTSIDE the root via `..` or an absolute path" contract that glob can't address upward.
150
- // A glob pattern is directory-expanded under the root via node:fs glob — `*` matches each
151
- // immediate child (one level), `**` recurses — dropping file matches, since only a
152
- // directory can be a repo; #repoToplevel then filters the non-git matches. {§membership-overlay-repo}
153
- static async #expandRepoDirs(root, dir, signal) {
154
- if (!GitMembership.#isGlobPattern(dir))
155
- return [dir];
156
- const dirs = [];
157
- for await (const rel of glob(dir, { cwd: root })) {
158
- if (signal?.aborted)
159
- return dirs;
160
- try {
161
- if ((await stat(resolve(root, rel))).isDirectory())
162
- dirs.push(rel);
163
- }
164
- catch {
165
- // raced away between glob and stat — not a candidate
166
- }
167
- }
168
- return dirs;
169
- }
170
- // Does this declared repo entry carry glob magic (directory-expand it), or is it a
171
- // literal path (declared as-is)? The metacharacters node:fs glob honors.
172
- static #isGlobPattern(s) {
173
- return /[*?[\]{}]/.test(s);
174
- }
175
- // Detect a tracked file's mimetype (mirrors File.detectFileMimetype): route
176
- // through the Mimetypes service when present, normalizing the auto-text result
177
- // to the text primitive (plurnk-service never auto-derives text/plain). No
178
- // service → text primitive.
172
+ // Detect a tracked file's mimetype (mirrors File.detectFileMimetype) through
173
+ // the configured registry, normalizing auto-derived text/plain to the text
174
+ // primitive.
179
175
  static async #detectMimetype(canonical, mimetypes) {
180
- if (mimetypes !== undefined) {
181
- const detected = await mimetypes.detect({ path: canonical });
182
- return MimetypeBinary.normalizeAutoTextMimetype(detected);
176
+ if (mimetypes === undefined)
177
+ throw new Error("GitMembership: configured mimetype registry is required");
178
+ const detected = await mimetypes.detect({ path: canonical });
179
+ return MimetypeBinary.normalizeAutoTextMimetype(detected);
180
+ }
181
+ static #projectionIdentity(mimetype, mimetypes, identities) {
182
+ if (mimetypes === undefined)
183
+ throw new Error("GitMembership: configured mimetype registry is required");
184
+ let identity = identities.get(mimetype);
185
+ if (identity === undefined) {
186
+ identity = mimetypes.projectionIdentity(mimetype);
187
+ identities.set(mimetype, identity);
183
188
  }
184
- return MimetypeBinary.TEXT_PRIMITIVE_MIMETYPE;
189
+ return identity;
185
190
  }
186
191
  // Shared overlay inputs — the candidate sets (git tracked+untracked union, pick scan) and
187
192
  // the overlay globs, before composition. Single-sources the (git ∪ pick) derivation + the
188
193
  // glob sets for resolveGitMembership (compose + reconcile) and resolveMembershipEffects
189
- // (derive per-file effect for clients, #243). Headless (no project_root) → null.
194
+ // (derive per-file effect for clients, {§membership-resolved-effects}). Headless → null.
190
195
  static async #resolveOverlayInputs(db, workspaceId, signal) {
191
196
  const root = await GitMembership.#loadWorkspaceRoot(db, workspaceId);
192
197
  if (root === null)
@@ -195,38 +200,39 @@ export default class GitMembership {
195
200
  const hideGlobs = constraints.filter((c) => c.effect === "hide").map((c) => c.glob);
196
201
  const pickGlobs = constraints.filter((c) => c.effect === "pick").map((c) => c.glob);
197
202
  const viewGlobs = constraints.filter((c) => c.effect === "view").map((c) => c.glob);
198
- // git substrate — the union of the workspace's DECLARED repos' MEMBERS: tracked files
199
- // plus untracked-but-not-ignored ones (§membership-auto-add). PLURNK_SERVICE_GIT_ALLOWED=0 is
200
- // the hard ceiling (deny all git membership); PLURNK_SERVICE_GIT_AUTO=1 declares project_root
201
- // as an implicit repo. Empty when git is denied or no declared repo resolves, so
202
- // `pick` is then the sole source. No early-return on non-git.
203
- const repoDirs = constraints.filter((c) => c.effect === "repo").map((c) => c.glob); // a declared repo's ls-files join membership, path-prefixed — §membership-overlay-repo
204
- if (process.env.PLURNK_SERVICE_GIT_AUTO === "1")
205
- repoDirs.push(root); // ALLOWED ceiling gates the AUTO default — §membership-git-flags
206
- // #232 — git:false is a workspace-level tighten of the env ALLOWED ceiling (env AND workspace).
203
+ // The repository containing project_root is the sole Git substrate.
204
+ // PLURNK_SERVICE_GIT_ALLOWED=0 is the hard service ceiling and
205
+ // PLURNK_SERVICE_GIT_AUTO=0 disables automatic Git membership.
206
+ // {§operator-config-workspace-git} — git:false tightens the env ALLOWED ceiling.
207
207
  const workspaceGit = (await WorkspaceSettings.read(db, workspaceId)).git;
208
- const gitMembers = process.env.PLURNK_SERVICE_GIT_ALLOWED === "1" && workspaceGit !== false
209
- ? await GitMembership.#forestMembers(root, repoDirs, signal)
210
- : [];
208
+ let gitMembers = [];
209
+ if (process.env.PLURNK_SERVICE_GIT_ALLOWED === "1"
210
+ && process.env.PLURNK_SERVICE_GIT_AUTO === "1"
211
+ && workspaceGit !== false) {
212
+ const repository = await GitMembership.#repoToplevel(root, signal);
213
+ if (repository !== null) {
214
+ gitMembers = await GitMembership.#projectMembers(root, repository, signal);
215
+ }
216
+ }
211
217
  // `pick` overlay — a targeted, client-dictated scan for untracked matches
212
218
  // (node:fs glob over the client's pattern, never a blind walk).
213
- const picked = pickGlobs.length === 0 ? [] : await GitMembership.#scanPickMembers(root, pickGlobs, signal); // §membership-overlay-pick
219
+ const picked = pickGlobs.length === 0 ? [] : await GitMembership.#scanPickMembers(root, pickGlobs, signal); // {§membership-overlay-pick}
214
220
  return { root, gitMembers, picked, hideGlobs, viewGlobs };
215
221
  }
216
222
  // Resolve a workspace's file membership: the desired set is (git ls-files ∪ pick
217
- // globs) − hide globs (SPEC §membership overlay), reconciled against the registered
223
+ // globs) − hide globs (SPEC {§membership} overlay), reconciled against the registered
218
224
  // overlay-owned members so entries == members. Channel-less rows — disk is the
219
225
  // truth (D3); the row is the membership marker File.read gates on. Returns the
220
226
  // desired pathnames so the caller can materialize them through writeEntry.
221
227
  //
222
228
  // signal-respecting: git shell-outs + the pick scan honor `signal`. Headless
223
229
  // (no project_root) yields nothing; a non-git root with pick-globs still resolves.
224
- static async resolveGitMembership(db, workspaceId, signal) {
230
+ static async #reconcileGitMembership(db, workspaceId, signal) {
225
231
  const inputs = await GitMembership.#resolveOverlayInputs(db, workspaceId, signal);
226
232
  if (inputs === null)
227
- return []; // headless — no disk surface to resolve
233
+ return { members: [], removed: [] }; // headless — no disk surface to resolve
228
234
  const { gitMembers: members, picked, hideGlobs } = inputs;
229
- // Compose: (git ∪ pick) − hide (§membership-overlay-hide), tracking origin for reconciliation — a path
235
+ // Compose: (git ∪ pick) − hide ({§membership-overlay-hide}), tracking origin for reconciliation — a path
230
236
  // in `members` is 'git', a pick-only match is 'constraint'.
231
237
  const memberSet = new Set(members);
232
238
  const passesHide = (p) => hideGlobs.length === 0 || !hideGlobs.some((g) => matchesGlob(p, g));
@@ -237,6 +243,7 @@ export default class GitMembership {
237
243
  // so they match the parser's pathname the shared read helper queries by.
238
244
  const desired = [...desiredGit, ...desiredPick]; // bare canon keys ({§fs-canonical-name}) — ls-files output IS the canon
239
245
  const desiredSet = new Set(desired);
246
+ const candidateSet = new Set([...members, ...picked]);
240
247
  // Reconcile so entries == members (the constitutive invariant): register the
241
248
  // desired with their origin, then un-register any overlay-owned member ('git'
242
249
  // or 'constraint') no longer desired — untracked, unmatched, or newly hidden.
@@ -249,18 +256,43 @@ export default class GitMembership {
249
256
  await db.crud_register_workspace_member.get({ workspace_id: workspaceId, owner_id: commonsId, scheme: "file", pathname, membership_origin: "constraint" });
250
257
  }
251
258
  const registered = await db.crud_list_reconcilable_members.all({ workspace_id: workspaceId });
259
+ const removed = [];
252
260
  for (const m of registered) {
253
261
  if (!desiredSet.has(m.pathname)) {
262
+ // A path that left Git/pick membership also left disk truth (for
263
+ // example an untracked deletion or staged rename). A hide overlay
264
+ // is policy, not a filesystem occurrence, and is therefore silent.
265
+ if (!candidateSet.has(m.pathname)) {
266
+ const prior = await db.ops_read_channel.get({
267
+ workspace_id: workspaceId,
268
+ owner_id: commonsId,
269
+ scheme: "file",
270
+ pathname: m.pathname,
271
+ channel: "body",
272
+ });
273
+ if (prior !== undefined) {
274
+ removed.push({
275
+ pathname: m.pathname,
276
+ entryId: m.id,
277
+ channel: "body",
278
+ before: prior.content,
279
+ after: "",
280
+ });
281
+ }
282
+ }
254
283
  await db.crud_delete_entry.run({ entry_id: m.id });
255
284
  }
256
285
  }
257
- return desired;
286
+ return { members: desired, removed };
258
287
  }
259
- // Resolve each candidate's membership EFFECT without mutating — a read for clients (#243,
260
- // plurnk.nvim gutter signs): the same (git ∪ pick) / hide / view resolution resolveGitMembership
288
+ static async resolveGitMembership(db, workspaceId, signal) {
289
+ return (await GitMembership.#reconcileGitMembership(db, workspaceId, signal)).members;
290
+ }
291
+ // Resolve each candidate's membership effect without mutating — a read for clients
292
+ // ({§membership-resolved-effects}): the same (git ∪ pick) / hide / view resolution resolveGitMembership
261
293
  // composes, surfaced per-file so a client signs member/view/hidden with ZERO glob-matching of
262
294
  // its own. `members` are (git ∪ pick) − hide, each tagged `view` (read-only, refused at the
263
- // File edit gate §membership-overlay-view) or plain `member`; `hidden` are candidates a `hide`
295
+ // File edit gate {§membership-overlay-view}) or plain `member`; `hidden` are candidates a `hide`
264
296
  // glob excludes (project files absent from the manifest). Paths namespace-absolute, matching
265
297
  // the manifest + storage. Headless → empty. {§membership-resolved-effects}
266
298
  static async resolveMembershipEffects(db, workspaceId, signal) {
@@ -285,7 +317,7 @@ export default class GitMembership {
285
317
  hidden.sort();
286
318
  return { members, hidden };
287
319
  }
288
- // Targeted client-dictated scan (SPEC §membership `pick`) — enumerate disk files
320
+ // Targeted client-dictated scan (SPEC {§membership} `pick`) — enumerate disk files
289
321
  // matching the client's pick-globs via node:fs glob (the pattern bounds the
290
322
  // traversal; never a blind fs-walk). Files only — directories aren't members.
291
323
  // Workspace-relative paths, the same shape as git ls-files.
@@ -306,18 +338,22 @@ export default class GitMembership {
306
338
  }
307
339
  return [...matches];
308
340
  }
309
- // Materialize a member's disk content into a body channel via writeEntry (the
341
+ // Materialize a member's model-readable snapshot into a body channel via writeEntry (the
310
342
  // entry-write paradigm) — so it appears in the manifest catalog and is READ-able
311
- // (D4/D5). Change-gated: a member whose mtime:size signature is unchanged since its
312
- // last sync is a no-op (the stat-gate below) — re-read + rewrite only on change.
313
- // Binary members materialize as an EMPTY body
314
- // channel stamped with their binary mimetype (#186 — visible in the manifest
315
- // and READ-415 via the one isBinaryMimetype gate, not a 404 ghost).
316
- // Missing-on-disk (tracked but deleted in the working tree) is
317
- // skipped — membership stands, no channel.
318
- static async #materializeMember(pathname, root, ctx) {
343
+ // (D4/D5). Change-gated: text changes with mtime:size; binary projections also
344
+ // change when their opaque reader identity changes. A binary source persists
345
+ // only derived Unicode when its installed handler provides it, otherwise an
346
+ // empty typed marker ({§membership-source-projection}).
347
+ // A tracked path missing on disk retains membership but loses its stale
348
+ // readable channels; its explicit absent signature makes a later
349
+ // reappearance another observable divergence.
350
+ static async #materializeMember(pathname, root, ctx, identities) {
319
351
  const canonical = join(root, pathname); // pathname is namespace-absolute (`/src/foo.ts`); join roots it at the workspace
320
- // SPEC §membership-change-gated-sync — the cheap detect is a stat (mtime:size),
352
+ const commonsId = await Owner.commonsId(ctx.db, ctx.workspaceId);
353
+ const known = await ctx.db.crud_get_member_sig.get({
354
+ workspace_id: ctx.workspaceId, owner_id: commonsId, scheme: "file", pathname,
355
+ });
356
+ // SPEC {§membership-change-gated-sync} — the cheap detect is a stat (mtime:size),
321
357
  // never a content read: a member whose signature matches its last sync is a
322
358
  // no-op (not re-read, re-tokenized, or rewritten). Coverage stays exhaustive
323
359
  // (every member is stat'd); work is proportional to change.
@@ -332,24 +368,39 @@ export default class GitMembership {
332
368
  sig = `${st.mtimeMs}:${st.size}`;
333
369
  }
334
370
  catch (err) {
335
- if (err.code === "ENOENT")
336
- return null;
371
+ if (err.code === "ENOENT") {
372
+ if (known === undefined || known.synced_sig === ABSENT_SIG)
373
+ return null;
374
+ const prior = await ctx.db.ops_read_channel.get({
375
+ workspace_id: ctx.workspaceId,
376
+ owner_id: commonsId,
377
+ scheme: "file",
378
+ pathname,
379
+ channel: "body",
380
+ });
381
+ await ctx.db.crud_delete_channels.run({ entry_id: known.id });
382
+ await ctx.db.crud_mark_member_absent.run({ entry_id: known.id });
383
+ return prior === undefined ? null : {
384
+ pathname,
385
+ entryId: known.id,
386
+ channel: "body",
387
+ before: prior.content,
388
+ after: "",
389
+ };
390
+ }
337
391
  throw err;
338
392
  }
339
- const known = await ctx.db.crud_get_member_sig.get({
340
- workspace_id: ctx.workspaceId, owner_id: await Owner.commonsId(ctx.db, ctx.workspaceId), scheme: "file", pathname,
341
- });
342
- if (known !== undefined && known.synced_sig === sig)
343
- return null; // unchanged — the change-gate
393
+ if (known !== undefined && known.synced_sig === sig) {
394
+ const sourceProjection = sourceProjectionFrom(known.attributes);
395
+ if (sourceProjection === null)
396
+ return null;
397
+ const currentIdentity = await GitMembership.#projectionIdentity(sourceProjection.mimetype, ctx.mimetypes, identities);
398
+ if (currentIdentity === sourceProjection.identity)
399
+ return null;
400
+ }
344
401
  const mimetype = await GitMembership.#detectMimetype(canonical, ctx.mimetypes);
345
- if (MimetypeBinary.isBinaryMimetype(mimetype)) {
346
- // Empty body channel stamped with the real binary mimetype — a first-
347
- // class entry that READ-415s through readWorkspaceEntry's isBinaryMimetype
348
- // gate (#186), not a channel-less row that would read as 404.
349
- const r = await EntryCrud.writeEntry(pathname, { channels: { body: { content: "", mimetype } }, tags: [] }, ctx, "file");
350
- if (r.entryId !== null)
351
- await ctx.db.crud_set_synced_sig.run({ entry_id: r.entryId, synced_sig: sig });
352
- return null; // binary bodies are empty markers — no text divergence to narrate
402
+ if (await MimetypeBinary.isBinaryMimetype(mimetype, ctx.mimetypes)) {
403
+ return GitMembership.#materializeBinary(pathname, { path: canonical }, mimetype, sig, known?.synced_sig !== sig, known?.synced_sig === ABSENT_SIG, ctx, identities);
353
404
  }
354
405
  let buf;
355
406
  try {
@@ -360,34 +411,96 @@ export default class GitMembership {
360
411
  return null;
361
412
  throw err;
362
413
  }
363
- // §membership-binary-sniff (#320) — the extension map can lie (.wasm fell through to
414
+ // {§membership-binary-sniff} — the extension map can lie (.wasm fell through to
364
415
  // the markdown DEFAULT and a 3.3MB blob entered the corpus as prose, three copies,
365
416
  // ~10M tokens). NUL bytes in the head are binary truth regardless of the label:
366
- // re-stamp octet-stream and take the binary arm (empty body, READ-415, never
367
- // FTS'd/embedded/tokenized as text).
417
+ // re-stamp octet-stream and take the same bounded projection-or-marker arm.
368
418
  if (buf.subarray(0, 8192).includes(0)) {
369
- const r = await EntryCrud.writeEntry(pathname, { channels: { body: { content: "", mimetype: "application/octet-stream" } }, tags: [] }, ctx, "file");
370
- if (r.entryId !== null)
371
- await ctx.db.crud_set_synced_sig.run({ entry_id: r.entryId, synced_sig: sig });
372
- return null;
419
+ return GitMembership.#materializeBinary(pathname, { content: buf, hint: "application/octet-stream" }, "application/octet-stream", sig, known?.synced_sig !== sig, known?.synced_sig === ABSENT_SIG, ctx, identities);
373
420
  }
374
421
  const content = buf.toString("utf8");
375
- // §env-delta — capture an out-of-band disk change BEFORE the refresh overwrites
376
- // the entry: an existing body channel whose content differs from disk is an
377
- // ambient divergence (D5). writeEntry then refreshes the entry to disk truth.
422
+ // {§env-delta-filesystem-narration} — capture the prior snapshot before
423
+ // materialization replaces it, then journal the resulting net span.
378
424
  const prior = await ctx.db.ops_read_channel.get({
379
- workspace_id: ctx.workspaceId, owner_id: await Owner.commonsId(ctx.db, ctx.workspaceId), scheme: "file", pathname, channel: "body",
425
+ workspace_id: ctx.workspaceId, owner_id: commonsId, scheme: "file", pathname, channel: "body",
380
426
  });
381
- const result = await EntryCrud.writeEntry(pathname, { channels: { body: { content, mimetype } }, tags: [] }, ctx, "file");
427
+ const result = await EntryCrud.writeEntry(pathname, { channels: { body: { content, mimetype } }, tags: [], attributes: {} }, ctx, "file");
382
428
  if (result.entryId !== null)
383
429
  await ctx.db.crud_set_synced_sig.run({ entry_id: result.entryId, synced_sig: sig });
384
- if (prior !== undefined && prior.content !== content && result.entryId !== null) {
385
- return { pathname, scheme: "file", entryId: result.entryId, channel: "body", before: prior.content, after: content };
430
+ const changed = prior !== undefined && prior.content !== content;
431
+ if ((changed || known?.synced_sig === ABSENT_SIG) && result.entryId !== null) {
432
+ return { pathname, entryId: result.entryId, channel: "body", before: prior?.content ?? "", after: content };
433
+ }
434
+ return null;
435
+ }
436
+ static async #materializeBinary(pathname, input, mimetype, sig, diskChanged, previouslyAbsent, ctx, identities) {
437
+ const mimetypes = ctx.mimetypes;
438
+ if (mimetypes === undefined)
439
+ throw new Error("GitMembership: configured mimetype registry is required");
440
+ let content = "";
441
+ let outputMimetype = mimetype;
442
+ let metadata;
443
+ try {
444
+ const projected = await mimetypes.projectReadable(input);
445
+ if (projected === null) {
446
+ metadata = {
447
+ mimetype,
448
+ identity: await GitMembership.#projectionIdentity(mimetype, mimetypes, identities),
449
+ disposition: "unavailable",
450
+ };
451
+ }
452
+ else {
453
+ content = projected.content;
454
+ outputMimetype = "text/markdown";
455
+ metadata = {
456
+ mimetype: projected.sourceMimetype,
457
+ identity: projected.projectionIdentity,
458
+ disposition: "projected",
459
+ };
460
+ }
461
+ }
462
+ catch (cause) {
463
+ if (!(cause instanceof MimetypeInputLimitError))
464
+ throw cause;
465
+ metadata = {
466
+ mimetype,
467
+ identity: await GitMembership.#projectionIdentity(mimetype, mimetypes, identities),
468
+ disposition: "input-limit",
469
+ maximumBytes: cause.maximumBytes,
470
+ observedBytes: cause.observedBytes,
471
+ };
472
+ }
473
+ const prior = diskChanged && metadata.disposition === "projected"
474
+ ? await ctx.db.ops_read_channel.get({
475
+ workspace_id: ctx.workspaceId,
476
+ owner_id: await Owner.commonsId(ctx.db, ctx.workspaceId),
477
+ scheme: "file",
478
+ pathname,
479
+ channel: "body",
480
+ })
481
+ : undefined;
482
+ const result = await EntryCrud.writeEntry(pathname, {
483
+ channels: { body: { content, mimetype: outputMimetype } },
484
+ tags: [],
485
+ attributes: { sourceProjection: metadata },
486
+ }, ctx, "file");
487
+ if (result.entryId !== null) {
488
+ await ctx.db.crud_set_synced_sig.run({ entry_id: result.entryId, synced_sig: sig });
489
+ }
490
+ const changed = prior !== undefined && prior.content !== content;
491
+ if ((changed || previouslyAbsent) && result.entryId !== null) {
492
+ return {
493
+ pathname,
494
+ entryId: result.entryId,
495
+ channel: "body",
496
+ before: prior?.content ?? "",
497
+ after: content,
498
+ };
386
499
  }
387
500
  return null;
388
501
  }
389
502
  // Full membership + materialization pass for a worker. Registers git members,
390
- // then materializes each active (on-disk, non-binary) member as an entry
503
+ // then materializes each active on-disk member as an entry
391
504
  // through writeEntry. Called at packet-composition time (Engine.runTurn) per
392
505
  // D5. No-ops on headless / non-git workspaces.
393
506
  static async indexGitMembership(ctx) {
@@ -411,10 +524,11 @@ export default class GitMembership {
411
524
  const root = await GitMembership.#loadWorkspaceRoot(ctx.db, ctx.workspaceId);
412
525
  if (root === null)
413
526
  return [];
414
- const tracked = await GitMembership.resolveGitMembership(ctx.db, ctx.workspaceId, ctx.signal);
415
- const divergences = [];
416
- for (const pathname of tracked) {
417
- const divergence = await GitMembership.#materializeMember(pathname, root, ctx);
527
+ const resolution = await GitMembership.#reconcileGitMembership(ctx.db, ctx.workspaceId, ctx.signal);
528
+ const divergences = [...resolution.removed];
529
+ const identities = new Map();
530
+ for (const pathname of resolution.members) {
531
+ const divergence = await GitMembership.#materializeMember(pathname, root, ctx, identities);
418
532
  if (divergence !== null)
419
533
  divergences.push(divergence);
420
534
  }