@plurnk/plurnk-service 1.16.4 → 1.17.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 (636) hide show
  1. package/.env.defaults +122 -246
  2. package/INSTALL.md +84 -138
  3. package/README.md +9 -5
  4. package/SPEC.md +1710 -1015
  5. package/dist/Paths.d.ts +2 -0
  6. package/dist/Paths.d.ts.map +1 -1
  7. package/dist/Paths.js +2 -0
  8. package/dist/Paths.js.map +1 -1
  9. package/dist/build-info.d.ts.map +1 -1
  10. package/dist/build-info.js +7 -1
  11. package/dist/build-info.js.map +1 -1
  12. package/dist/build-info.json +1 -1
  13. package/dist/content/body-preview.d.ts +8 -0
  14. package/dist/content/body-preview.d.ts.map +1 -0
  15. package/dist/content/body-preview.js +39 -0
  16. package/dist/content/body-preview.js.map +1 -0
  17. package/dist/content/byte-view.d.ts +1 -4
  18. package/dist/content/byte-view.d.ts.map +1 -1
  19. package/dist/content/byte-view.js.map +1 -1
  20. package/dist/content/edit-collision.d.ts.map +1 -1
  21. package/dist/content/edit-collision.js +3 -4
  22. package/dist/content/edit-collision.js.map +1 -1
  23. package/dist/content/line-anchors.d.ts +2 -2
  24. package/dist/content/line-anchors.d.ts.map +1 -1
  25. package/dist/content/line-anchors.js +4 -3
  26. package/dist/content/line-anchors.js.map +1 -1
  27. package/dist/content/line-selection.d.ts +9 -0
  28. package/dist/content/line-selection.d.ts.map +1 -0
  29. package/dist/content/line-selection.js +29 -0
  30. package/dist/content/line-selection.js.map +1 -0
  31. package/dist/content/matcher.d.ts +0 -1
  32. package/dist/content/matcher.d.ts.map +1 -1
  33. package/dist/content/matcher.js +2 -3
  34. package/dist/content/matcher.js.map +1 -1
  35. package/dist/content/pattern-edits.d.ts +39 -0
  36. package/dist/content/pattern-edits.d.ts.map +1 -0
  37. package/dist/content/pattern-edits.js +121 -0
  38. package/dist/content/pattern-edits.js.map +1 -0
  39. package/dist/content/read-projector.d.ts +18 -9
  40. package/dist/content/read-projector.d.ts.map +1 -1
  41. package/dist/content/read-projector.js +132 -35
  42. package/dist/content/read-projector.js.map +1 -1
  43. package/dist/content/read-resolve.d.ts +3 -1
  44. package/dist/content/read-resolve.d.ts.map +1 -1
  45. package/dist/content/read-resolve.js +39 -7
  46. package/dist/content/read-resolve.js.map +1 -1
  47. package/dist/core/AdmittedTurnExecutor.d.ts +3 -5
  48. package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
  49. package/dist/core/AdmittedTurnExecutor.js +91 -83
  50. package/dist/core/AdmittedTurnExecutor.js.map +1 -1
  51. package/dist/core/BareBatchRunner.d.ts +6 -1
  52. package/dist/core/BareBatchRunner.d.ts.map +1 -1
  53. package/dist/core/BareBatchRunner.js +20 -5
  54. package/dist/core/BareBatchRunner.js.map +1 -1
  55. package/dist/core/BudgetReadout.d.ts +2 -3
  56. package/dist/core/BudgetReadout.d.ts.map +1 -1
  57. package/dist/core/BudgetReadout.js +23 -32
  58. package/dist/core/BudgetReadout.js.map +1 -1
  59. package/dist/core/CapabilityPolicies.d.ts +3 -8
  60. package/dist/core/CapabilityPolicies.d.ts.map +1 -1
  61. package/dist/core/CapabilityPolicies.js +2 -28
  62. package/dist/core/CapabilityPolicies.js.map +1 -1
  63. package/dist/core/CapabilityResolver.d.ts +10 -10
  64. package/dist/core/CapabilityResolver.d.ts.map +1 -1
  65. package/dist/core/CapabilityResolver.js +87 -65
  66. package/dist/core/CapabilityResolver.js.map +1 -1
  67. package/dist/core/ChannelWrite.d.ts +14 -11
  68. package/dist/core/ChannelWrite.d.ts.map +1 -1
  69. package/dist/core/ChannelWrite.js +11 -12
  70. package/dist/core/ChannelWrite.js.map +1 -1
  71. package/dist/core/ChannelWrite.sql +99 -9
  72. package/dist/core/ClientInteractions.d.ts.map +1 -1
  73. package/dist/core/ClientInteractions.js +7 -0
  74. package/dist/core/ClientInteractions.js.map +1 -1
  75. package/dist/core/CoreSchemeServices.d.ts +13 -10
  76. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  77. package/dist/core/CoreSchemeServices.js +13 -5
  78. package/dist/core/CoreSchemeServices.js.map +1 -1
  79. package/dist/core/DataStatementRunner.d.ts +2 -2
  80. package/dist/core/DataStatementRunner.d.ts.map +1 -1
  81. package/dist/core/DataStatementRunner.js +36 -39
  82. package/dist/core/DataStatementRunner.js.map +1 -1
  83. package/dist/core/Dispatcher.d.ts +19 -26
  84. package/dist/core/Dispatcher.d.ts.map +1 -1
  85. package/dist/core/Dispatcher.js +369 -219
  86. package/dist/core/Dispatcher.js.map +1 -1
  87. package/dist/core/Dispatcher.sql +66 -0
  88. package/dist/core/DurableStatement.d.ts.map +1 -1
  89. package/dist/core/DurableStatement.js +2 -1
  90. package/dist/core/DurableStatement.js.map +1 -1
  91. package/dist/core/EditMutations.d.ts +14 -18
  92. package/dist/core/EditMutations.d.ts.map +1 -1
  93. package/dist/core/EditMutations.js +211 -280
  94. package/dist/core/EditMutations.js.map +1 -1
  95. package/dist/core/EditMutations.sql +15 -0
  96. package/dist/core/EditSequence.d.ts +13 -0
  97. package/dist/core/EditSequence.d.ts.map +1 -0
  98. package/dist/core/EditSequence.js +73 -0
  99. package/dist/core/EditSequence.js.map +1 -0
  100. package/dist/core/Engine.d.ts +17 -23
  101. package/dist/core/Engine.d.ts.map +1 -1
  102. package/dist/core/Engine.js +73 -163
  103. package/dist/core/Engine.js.map +1 -1
  104. package/dist/core/Engine.sql +0 -724
  105. package/dist/core/EntryAddressBinding.d.ts +7 -17
  106. package/dist/core/EntryAddressBinding.d.ts.map +1 -1
  107. package/dist/core/EntryAddressBinding.js +12 -72
  108. package/dist/core/EntryAddressBinding.js.map +1 -1
  109. package/dist/core/ExecutionOutputs.d.ts +9 -0
  110. package/dist/core/ExecutionOutputs.d.ts.map +1 -0
  111. package/dist/core/ExecutionOutputs.js +36 -0
  112. package/dist/core/ExecutionOutputs.js.map +1 -0
  113. package/dist/core/ExecutionOutputs.sql +13 -0
  114. package/dist/core/ExecutorRegistry.d.ts +7 -5
  115. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  116. package/dist/core/ExecutorRegistry.js +36 -32
  117. package/dist/core/ExecutorRegistry.js.map +1 -1
  118. package/dist/core/HostPaths.d.ts +1 -0
  119. package/dist/core/HostPaths.d.ts.map +1 -1
  120. package/dist/core/HostPaths.js +4 -0
  121. package/dist/core/HostPaths.js.map +1 -1
  122. package/dist/core/InferenceCall.d.ts.map +1 -1
  123. package/dist/core/InferenceCall.js +1 -2
  124. package/dist/core/InferenceCall.js.map +1 -1
  125. package/dist/core/InferenceCall.sql +29 -0
  126. package/dist/core/KillHandler.d.ts +1 -0
  127. package/dist/core/KillHandler.d.ts.map +1 -1
  128. package/dist/core/KillHandler.js +24 -21
  129. package/dist/core/KillHandler.js.map +1 -1
  130. package/dist/core/LogBody.d.ts +8 -1
  131. package/dist/core/LogBody.d.ts.map +1 -1
  132. package/dist/core/LogBody.js +42 -11
  133. package/dist/core/LogBody.js.map +1 -1
  134. package/dist/core/LogEntryProjection.d.ts +1 -0
  135. package/dist/core/LogEntryProjection.d.ts.map +1 -1
  136. package/dist/core/LogEntryProjection.js +18 -10
  137. package/dist/core/LogEntryProjection.js.map +1 -1
  138. package/dist/core/LogVisibility.d.ts +1 -0
  139. package/dist/core/LogVisibility.d.ts.map +1 -1
  140. package/dist/core/LogVisibility.js +3 -0
  141. package/dist/core/LogVisibility.js.map +1 -1
  142. package/dist/core/LogWriter.d.ts +2 -3
  143. package/dist/core/LogWriter.d.ts.map +1 -1
  144. package/dist/core/LogWriter.js +25 -32
  145. package/dist/core/LogWriter.js.map +1 -1
  146. package/dist/core/LogWriter.sql +23 -0
  147. package/dist/core/LoopDriver.d.ts +11 -8
  148. package/dist/core/LoopDriver.d.ts.map +1 -1
  149. package/dist/core/LoopDriver.js +163 -189
  150. package/dist/core/LoopDriver.js.map +1 -1
  151. package/dist/core/LoopDriver.sql +11 -0
  152. package/dist/core/LoopLifecycle.d.ts +32 -2
  153. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  154. package/dist/core/LoopLifecycle.js +88 -4
  155. package/dist/core/LoopLifecycle.js.map +1 -1
  156. package/dist/core/LoopLifecycle.sql +145 -28
  157. package/dist/core/LoopPolicyReader.sql +6 -0
  158. package/dist/core/MembershipMaterialization.d.ts.map +1 -1
  159. package/dist/core/MembershipMaterialization.js +6 -28
  160. package/dist/core/MembershipMaterialization.js.map +1 -1
  161. package/dist/core/ModelCall.sql +70 -0
  162. package/dist/core/MutationEffects.d.ts +5 -3
  163. package/dist/core/MutationEffects.d.ts.map +1 -1
  164. package/dist/core/MutationEffects.js +10 -28
  165. package/dist/core/MutationEffects.js.map +1 -1
  166. package/dist/core/NativeContent.d.ts +6 -0
  167. package/dist/core/NativeContent.d.ts.map +1 -0
  168. package/dist/core/NativeContent.js +16 -0
  169. package/dist/core/NativeContent.js.map +1 -0
  170. package/dist/core/NativeContent.sql +6 -0
  171. package/dist/core/NoticeChannel.js +1 -1
  172. package/dist/core/NoticeChannel.js.map +1 -1
  173. package/dist/core/OperatorConfig.js +5 -5
  174. package/dist/core/OperatorConfig.js.map +1 -1
  175. package/dist/core/PacketBuilder.d.ts +8 -5
  176. package/dist/core/PacketBuilder.d.ts.map +1 -1
  177. package/dist/core/PacketBuilder.js +117 -81
  178. package/dist/core/PacketBuilder.js.map +1 -1
  179. package/dist/core/PacketBuilder.sql +146 -0
  180. package/dist/core/PatternSelection.d.ts +28 -0
  181. package/dist/core/PatternSelection.d.ts.map +1 -0
  182. package/dist/core/PatternSelection.js +42 -0
  183. package/dist/core/PatternSelection.js.map +1 -0
  184. package/dist/core/ProblemLog.d.ts.map +1 -1
  185. package/dist/core/ProblemLog.js +0 -1
  186. package/dist/core/ProblemLog.js.map +1 -1
  187. package/dist/core/PromptFrames.d.ts +12 -0
  188. package/dist/core/PromptFrames.d.ts.map +1 -0
  189. package/dist/core/PromptFrames.js +28 -0
  190. package/dist/core/PromptFrames.js.map +1 -0
  191. package/dist/core/ProposalLifecycle.d.ts +0 -1
  192. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  193. package/dist/core/ProposalLifecycle.js +20 -19
  194. package/dist/core/ProposalLifecycle.js.map +1 -1
  195. package/dist/core/ProposalLifecycle.sql +33 -0
  196. package/dist/core/ProviderInstantiate.d.ts +1 -1
  197. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  198. package/dist/core/ProviderInstantiate.js +7 -14
  199. package/dist/core/ProviderInstantiate.js.map +1 -1
  200. package/dist/core/ReasoningView.d.ts +9 -0
  201. package/dist/core/ReasoningView.d.ts.map +1 -0
  202. package/dist/core/ReasoningView.js +47 -0
  203. package/dist/core/ReasoningView.js.map +1 -0
  204. package/dist/core/ResourceBindings.d.ts +16 -0
  205. package/dist/core/ResourceBindings.d.ts.map +1 -0
  206. package/dist/core/ResourceBindings.js +45 -0
  207. package/dist/core/ResourceBindings.js.map +1 -0
  208. package/dist/core/ResourceMutations.d.ts +5 -8
  209. package/dist/core/ResourceMutations.d.ts.map +1 -1
  210. package/dist/core/ResourceMutations.js +6 -7
  211. package/dist/core/ResourceMutations.js.map +1 -1
  212. package/dist/core/ResourceSelector.d.ts +2 -2
  213. package/dist/core/ResourceSelector.d.ts.map +1 -1
  214. package/dist/core/ResourceSelector.js +122 -62
  215. package/dist/core/ResourceSelector.js.map +1 -1
  216. package/dist/core/ResourceTransfers.d.ts +4 -2
  217. package/dist/core/ResourceTransfers.d.ts.map +1 -1
  218. package/dist/core/ResourceTransfers.js +95 -26
  219. package/dist/core/ResourceTransfers.js.map +1 -1
  220. package/dist/core/RuntimeWorker.d.ts +5 -0
  221. package/dist/core/RuntimeWorker.d.ts.map +1 -0
  222. package/dist/core/RuntimeWorker.js +10 -0
  223. package/dist/core/RuntimeWorker.js.map +1 -0
  224. package/dist/core/RuntimeWorker.sql +5 -0
  225. package/dist/core/SchemeRegistry.d.ts +12 -16
  226. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  227. package/dist/core/SchemeRegistry.js +58 -84
  228. package/dist/core/SchemeRegistry.js.map +1 -1
  229. package/dist/core/StoredPacket.d.ts +6 -1
  230. package/dist/core/StoredPacket.d.ts.map +1 -1
  231. package/dist/core/StoredPacket.js +30 -10
  232. package/dist/core/StoredPacket.js.map +1 -1
  233. package/dist/core/StrikeRail.d.ts +9 -6
  234. package/dist/core/StrikeRail.d.ts.map +1 -1
  235. package/dist/core/StrikeRail.js +60 -113
  236. package/dist/core/StrikeRail.js.map +1 -1
  237. package/dist/core/StrikeRail.sql +12 -0
  238. package/dist/core/TerminalResult.d.ts +1 -1
  239. package/dist/core/TerminalResult.d.ts.map +1 -1
  240. package/dist/core/TerminalResult.js +2 -2
  241. package/dist/core/TerminalResult.js.map +1 -1
  242. package/dist/core/TokenCalibration.d.ts +1 -6
  243. package/dist/core/TokenCalibration.d.ts.map +1 -1
  244. package/dist/core/TokenCalibration.js +13 -9
  245. package/dist/core/TokenCalibration.js.map +1 -1
  246. package/dist/core/TokenCalibration.sql +15 -0
  247. package/dist/core/ToolInputSchema.d.ts +6 -0
  248. package/dist/core/ToolInputSchema.d.ts.map +1 -0
  249. package/dist/core/ToolInputSchema.js +43 -0
  250. package/dist/core/ToolInputSchema.js.map +1 -0
  251. package/dist/core/ToolResources.d.ts.map +1 -1
  252. package/dist/core/ToolResources.js +76 -52
  253. package/dist/core/ToolResources.js.map +1 -1
  254. package/dist/core/Turn.d.ts +5 -2
  255. package/dist/core/Turn.d.ts.map +1 -1
  256. package/dist/core/Turn.js +13 -11
  257. package/dist/core/Turn.js.map +1 -1
  258. package/dist/core/Turn.sql +25 -39
  259. package/dist/core/{SendBroadcastHandler.d.ts → TurnDispositionHandler.d.ts} +25 -17
  260. package/dist/core/TurnDispositionHandler.d.ts.map +1 -0
  261. package/dist/core/TurnDispositionHandler.js +198 -0
  262. package/dist/core/TurnDispositionHandler.js.map +1 -0
  263. package/dist/core/TurnMaterialization.d.ts +2 -3
  264. package/dist/core/TurnMaterialization.d.ts.map +1 -1
  265. package/dist/core/TurnMaterialization.js +40 -15
  266. package/dist/core/TurnMaterialization.js.map +1 -1
  267. package/dist/core/TurnMaterialization.sql +44 -0
  268. package/dist/core/TurnOps.d.ts.map +1 -1
  269. package/dist/core/TurnOps.js +8 -64
  270. package/dist/core/TurnOps.js.map +1 -1
  271. package/dist/core/TurnRunner.d.ts +9 -12
  272. package/dist/core/TurnRunner.d.ts.map +1 -1
  273. package/dist/core/TurnRunner.js +232 -408
  274. package/dist/core/TurnRunner.js.map +1 -1
  275. package/dist/core/TurnRunner.sql +74 -0
  276. package/dist/core/TurnSources.sql +39 -0
  277. package/dist/core/WorkerControlAddress.d.ts +1 -0
  278. package/dist/core/WorkerControlAddress.d.ts.map +1 -1
  279. package/dist/core/WorkerControlAddress.js +5 -0
  280. package/dist/core/WorkerControlAddress.js.map +1 -1
  281. package/dist/core/WorkerControlHandler.d.ts +1 -3
  282. package/dist/core/WorkerControlHandler.d.ts.map +1 -1
  283. package/dist/core/WorkerControlHandler.js +41 -48
  284. package/dist/core/WorkerControlHandler.js.map +1 -1
  285. package/dist/core/WorkerName.d.ts +8 -6
  286. package/dist/core/WorkerName.d.ts.map +1 -1
  287. package/dist/core/WorkerName.js +33 -24
  288. package/dist/core/WorkerName.js.map +1 -1
  289. package/dist/core/WorkerName.sql +7 -10
  290. package/dist/core/WorkspaceGate.d.ts +1 -1
  291. package/dist/core/WorkspaceGate.d.ts.map +1 -1
  292. package/dist/core/WorkspaceGate.js +29 -6
  293. package/dist/core/WorkspaceGate.js.map +1 -1
  294. package/dist/core/ambient.sql +236 -0
  295. package/dist/core/attachments.d.ts +2 -1
  296. package/dist/core/attachments.d.ts.map +1 -1
  297. package/dist/core/attachments.js +5 -4
  298. package/dist/core/attachments.js.map +1 -1
  299. package/dist/core/caps/CapsResolve.d.ts +2 -3
  300. package/dist/core/caps/CapsResolve.d.ts.map +1 -1
  301. package/dist/core/caps/CapsResolve.js +5 -5
  302. package/dist/core/caps/CapsResolve.js.map +1 -1
  303. package/dist/core/caps/DbChannelCaps.d.ts +1 -1
  304. package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
  305. package/dist/core/caps/DbChannelCaps.js +4 -6
  306. package/dist/core/caps/DbChannelCaps.js.map +1 -1
  307. package/dist/core/caps/DbEntryCaps.d.ts +2 -1
  308. package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
  309. package/dist/core/caps/DbEntryCaps.js +11 -17
  310. package/dist/core/caps/DbEntryCaps.js.map +1 -1
  311. package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
  312. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  313. package/dist/core/caps/DbNotifyCaps.js +3 -5
  314. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  315. package/dist/core/caps/DbProjectionCaps.js +1 -1
  316. package/dist/core/caps/DbProjectionCaps.js.map +1 -1
  317. package/dist/core/caps/DbSubscriptionCaps.d.ts +1 -1
  318. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  319. package/dist/core/caps/DbSubscriptionCaps.js +5 -7
  320. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  321. package/dist/core/caps/SchemeCtxImpl.d.ts +0 -2
  322. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  323. package/dist/core/caps/SchemeCtxImpl.js +4 -20
  324. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  325. package/dist/core/env-defaults.d.ts.map +1 -1
  326. package/dist/core/env-defaults.js +4 -2
  327. package/dist/core/env-defaults.js.map +1 -1
  328. package/dist/core/file-materialization.d.ts +4 -1
  329. package/dist/core/file-materialization.d.ts.map +1 -1
  330. package/dist/core/file-materialization.js +14 -0
  331. package/dist/core/file-materialization.js.map +1 -1
  332. package/dist/core/fork.d.ts +1 -6
  333. package/dist/core/fork.d.ts.map +1 -1
  334. package/dist/core/fork.js +9 -153
  335. package/dist/core/fork.js.map +1 -1
  336. package/dist/core/fork.sql +162 -185
  337. package/dist/core/git-env.d.ts +1 -0
  338. package/dist/core/git-env.d.ts.map +1 -1
  339. package/dist/core/git-env.js +39 -2
  340. package/dist/core/git-env.js.map +1 -1
  341. package/dist/core/git-membership.d.ts.map +1 -1
  342. package/dist/core/git-membership.js +64 -48
  343. package/dist/core/git-membership.js.map +1 -1
  344. package/dist/core/git-state.d.ts.map +1 -1
  345. package/dist/core/git-state.js +25 -19
  346. package/dist/core/git-state.js.map +1 -1
  347. package/dist/core/glob_match.d.ts +4 -0
  348. package/dist/core/glob_match.d.ts.map +1 -0
  349. package/dist/core/glob_match.js +7 -0
  350. package/dist/core/glob_match.js.map +1 -0
  351. package/dist/core/maintenance.sql +6 -0
  352. package/dist/core/mutation-types.d.ts +7 -28
  353. package/dist/core/mutation-types.d.ts.map +1 -1
  354. package/dist/core/packet-inject.d.ts.map +1 -1
  355. package/dist/core/packet-inject.js +2 -3
  356. package/dist/core/packet-inject.js.map +1 -1
  357. package/dist/core/packet-wire.d.ts +8 -6
  358. package/dist/core/packet-wire.d.ts.map +1 -1
  359. package/dist/core/packet-wire.js +131 -163
  360. package/dist/core/packet-wire.js.map +1 -1
  361. package/dist/core/plurnk-uri.d.ts +1 -1
  362. package/dist/core/plurnk-uri.d.ts.map +1 -1
  363. package/dist/core/plurnk-uri.js +5 -12
  364. package/dist/core/plurnk-uri.js.map +1 -1
  365. package/dist/core/scheme-types.d.ts +3 -3
  366. package/dist/core/scheme-types.d.ts.map +1 -1
  367. package/dist/core/scheme-types.js.map +1 -1
  368. package/dist/core/sha256.d.ts +4 -0
  369. package/dist/core/sha256.d.ts.map +1 -0
  370. package/dist/core/sha256.js +6 -0
  371. package/dist/core/sha256.js.map +1 -0
  372. package/dist/core/teaching.js +1 -1
  373. package/dist/core/teaching.js.map +1 -1
  374. package/dist/core/turn-scheduler.d.ts.map +1 -1
  375. package/dist/core/turn-scheduler.js +5 -18
  376. package/dist/core/turn-scheduler.js.map +1 -1
  377. package/dist/core/turn-signals.d.ts +0 -6
  378. package/dist/core/turn-signals.d.ts.map +1 -1
  379. package/dist/core/turn-signals.js +1 -7
  380. package/dist/core/turn-signals.js.map +1 -1
  381. package/dist/core/worker-ops.sql +15 -22
  382. package/dist/core/workspace-settings.d.ts.map +1 -1
  383. package/dist/core/workspace-settings.js +18 -2
  384. package/dist/core/workspace-settings.js.map +1 -1
  385. package/dist/core/workspace-settings.sql +6 -0
  386. package/dist/digest/Digest.d.ts.map +1 -1
  387. package/dist/digest/Digest.js +27 -90
  388. package/dist/digest/Digest.js.map +1 -1
  389. package/dist/digest/DigestRender.d.ts.map +1 -1
  390. package/dist/digest/DigestRender.js +14 -54
  391. package/dist/digest/DigestRender.js.map +1 -1
  392. package/dist/digest/digest-rows.d.ts +10 -32
  393. package/dist/digest/digest-rows.d.ts.map +1 -1
  394. package/dist/digest/digest.sql +43 -18
  395. package/dist/schemes/Exec.d.ts +6 -12
  396. package/dist/schemes/Exec.d.ts.map +1 -1
  397. package/dist/schemes/Exec.js +256 -188
  398. package/dist/schemes/Exec.js.map +1 -1
  399. package/dist/schemes/ExecOutputScheme.d.ts +8 -6
  400. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  401. package/dist/schemes/ExecOutputScheme.js +33 -61
  402. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  403. package/dist/schemes/ExecutionInput.d.ts +12 -0
  404. package/dist/schemes/ExecutionInput.d.ts.map +1 -0
  405. package/dist/schemes/ExecutionInput.js +94 -0
  406. package/dist/schemes/ExecutionInput.js.map +1 -0
  407. package/dist/schemes/File.d.ts +5 -2
  408. package/dist/schemes/File.d.ts.map +1 -1
  409. package/dist/schemes/File.js +62 -92
  410. package/dist/schemes/File.js.map +1 -1
  411. package/dist/schemes/Log.d.ts.map +1 -1
  412. package/dist/schemes/Log.js +84 -79
  413. package/dist/schemes/Log.js.map +1 -1
  414. package/dist/schemes/Log.sql +183 -15
  415. package/dist/schemes/Prompt.d.ts.map +1 -1
  416. package/dist/schemes/Prompt.js +3 -12
  417. package/dist/schemes/Prompt.js.map +1 -1
  418. package/dist/schemes/QuestionTool.d.ts +5 -0
  419. package/dist/schemes/QuestionTool.d.ts.map +1 -1
  420. package/dist/schemes/QuestionTool.js +6 -14
  421. package/dist/schemes/QuestionTool.js.map +1 -1
  422. package/dist/schemes/Skill.d.ts +12 -7
  423. package/dist/schemes/Skill.d.ts.map +1 -1
  424. package/dist/schemes/Skill.js +149 -21
  425. package/dist/schemes/Skill.js.map +1 -1
  426. package/dist/schemes/TurnSource.d.ts +12 -0
  427. package/dist/schemes/TurnSource.d.ts.map +1 -0
  428. package/dist/schemes/TurnSource.js +116 -0
  429. package/dist/schemes/TurnSource.js.map +1 -0
  430. package/dist/schemes/Worker.d.ts +3 -3
  431. package/dist/schemes/Worker.d.ts.map +1 -1
  432. package/dist/schemes/Worker.js +61 -208
  433. package/dist/schemes/Worker.js.map +1 -1
  434. package/dist/schemes/_entry-crud.d.ts +12 -15
  435. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  436. package/dist/schemes/_entry-crud.js +63 -63
  437. package/dist/schemes/_entry-crud.js.map +1 -1
  438. package/dist/schemes/_entry-crud.sql +142 -46
  439. package/dist/schemes/_entry-find.d.ts +1 -3
  440. package/dist/schemes/_entry-find.d.ts.map +1 -1
  441. package/dist/schemes/_entry-find.js +111 -112
  442. package/dist/schemes/_entry-find.js.map +1 -1
  443. package/dist/schemes/_entry-find.sql +22 -14
  444. package/dist/schemes/_entry-fts.d.ts +14 -0
  445. package/dist/schemes/_entry-fts.d.ts.map +1 -0
  446. package/dist/schemes/_entry-fts.js +82 -0
  447. package/dist/schemes/_entry-fts.js.map +1 -0
  448. package/dist/schemes/_entry-fts.sql +31 -0
  449. package/dist/schemes/_entry-graph.d.ts.map +1 -1
  450. package/dist/schemes/_entry-graph.js +6 -14
  451. package/dist/schemes/_entry-graph.js.map +1 -1
  452. package/dist/schemes/_entry-graph.sql +29 -44
  453. package/dist/schemes/_entry-manifest.d.ts +2 -1
  454. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  455. package/dist/schemes/_entry-manifest.js +15 -12
  456. package/dist/schemes/_entry-manifest.js.map +1 -1
  457. package/dist/schemes/_entry-manifest.sql +26 -0
  458. package/dist/schemes/_entry-ops.d.ts +3 -5
  459. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  460. package/dist/schemes/_entry-ops.js +30 -43
  461. package/dist/schemes/_entry-ops.js.map +1 -1
  462. package/dist/schemes/_entry-ops.sql +5 -6
  463. package/dist/schemes/_entry-readable.d.ts +8 -0
  464. package/dist/schemes/_entry-readable.d.ts.map +1 -0
  465. package/dist/schemes/_entry-readable.js +38 -0
  466. package/dist/schemes/_entry-readable.js.map +1 -0
  467. package/dist/schemes/_entry-send.d.ts +1 -1
  468. package/dist/schemes/_entry-send.d.ts.map +1 -1
  469. package/dist/schemes/_entry-send.js +3 -3
  470. package/dist/schemes/_entry-send.js.map +1 -1
  471. package/dist/schemes/_search-index.d.ts +1 -3
  472. package/dist/schemes/_search-index.d.ts.map +1 -1
  473. package/dist/schemes/_search-index.js +79 -110
  474. package/dist/schemes/_search-index.js.map +1 -1
  475. package/dist/schemes/_search-index.sql +30 -0
  476. package/dist/schemes/exec-runtime.d.ts +1 -1
  477. package/dist/schemes/exec-runtime.d.ts.map +1 -1
  478. package/dist/schemes/exec-runtime.js.map +1 -1
  479. package/dist/server/ClientReads.d.ts +2 -2
  480. package/dist/server/ClientReads.d.ts.map +1 -1
  481. package/dist/server/ClientReads.js +5 -8
  482. package/dist/server/ClientReads.js.map +1 -1
  483. package/dist/server/Daemon.d.ts +14 -21
  484. package/dist/server/Daemon.d.ts.map +1 -1
  485. package/dist/server/Daemon.js +135 -156
  486. package/dist/server/Daemon.js.map +1 -1
  487. package/dist/server/DaemonModule.d.ts +23 -21
  488. package/dist/server/DaemonModule.d.ts.map +1 -1
  489. package/dist/server/DrainSupervisor.d.ts +8 -21
  490. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  491. package/dist/server/DrainSupervisor.js +278 -306
  492. package/dist/server/DrainSupervisor.js.map +1 -1
  493. package/dist/server/Functionality.d.ts +11 -12
  494. package/dist/server/Functionality.d.ts.map +1 -1
  495. package/dist/server/Functionality.js +129 -101
  496. package/dist/server/Functionality.js.map +1 -1
  497. package/dist/server/FunctionalityManager.d.ts +3 -5
  498. package/dist/server/FunctionalityManager.d.ts.map +1 -1
  499. package/dist/server/FunctionalityManager.js +20 -58
  500. package/dist/server/FunctionalityManager.js.map +1 -1
  501. package/dist/server/MembersFunctionality.d.ts +5 -4
  502. package/dist/server/MembersFunctionality.d.ts.map +1 -1
  503. package/dist/server/MembersFunctionality.js +24 -37
  504. package/dist/server/MembersFunctionality.js.map +1 -1
  505. package/dist/server/PlurnkSkill.d.ts +11 -0
  506. package/dist/server/PlurnkSkill.d.ts.map +1 -0
  507. package/dist/server/PlurnkSkill.js +41 -0
  508. package/dist/server/PlurnkSkill.js.map +1 -0
  509. package/dist/server/Retention.d.ts +22 -0
  510. package/dist/server/Retention.d.ts.map +1 -0
  511. package/dist/server/Retention.js +54 -0
  512. package/dist/server/Retention.js.map +1 -0
  513. package/dist/server/Retention.sql +36 -0
  514. package/dist/server/ServiceModules.d.ts +5 -0
  515. package/dist/server/ServiceModules.d.ts.map +1 -0
  516. package/dist/server/ServiceModules.js +10 -0
  517. package/dist/server/ServiceModules.js.map +1 -0
  518. package/dist/server/SkillsFunctionality.d.ts +14 -19
  519. package/dist/server/SkillsFunctionality.d.ts.map +1 -1
  520. package/dist/server/SkillsFunctionality.js +97 -104
  521. package/dist/server/SkillsFunctionality.js.map +1 -1
  522. package/dist/server/WorkerModelResolver.d.ts +4 -0
  523. package/dist/server/WorkerModelResolver.d.ts.map +1 -1
  524. package/dist/server/WorkerModelResolver.js +72 -39
  525. package/dist/server/WorkerModelResolver.js.map +1 -1
  526. package/dist/server/WorkspaceCapabilities.d.ts +21 -0
  527. package/dist/server/WorkspaceCapabilities.d.ts.map +1 -0
  528. package/dist/server/{WorkerCapabilities.js → WorkspaceCapabilities.js} +40 -40
  529. package/dist/server/WorkspaceCapabilities.js.map +1 -0
  530. package/dist/server/WorkspaceResidency.d.ts +36 -0
  531. package/dist/server/WorkspaceResidency.d.ts.map +1 -0
  532. package/dist/server/WorkspaceResidency.js +199 -0
  533. package/dist/server/WorkspaceResidency.js.map +1 -0
  534. package/dist/server/client-input.d.ts +1 -4
  535. package/dist/server/client-input.d.ts.map +1 -1
  536. package/dist/server/client-input.js +4 -45
  537. package/dist/server/client-input.js.map +1 -1
  538. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  539. package/dist/server/dispatch-as-plurnk.js +4 -22
  540. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  541. package/dist/server/drain.sql +65 -42
  542. package/dist/server/envelope.d.ts +8 -0
  543. package/dist/server/envelope.d.ts.map +1 -1
  544. package/dist/server/envelope.js +9 -7
  545. package/dist/server/envelope.js.map +1 -1
  546. package/dist/server/envelope.sql +49 -13
  547. package/dist/server/lifecycle-recovery.sql +14 -17
  548. package/dist/server/logEntry.d.ts +1 -1
  549. package/dist/server/logEntry.d.ts.map +1 -1
  550. package/dist/server/logEntry.js +4 -1
  551. package/dist/server/logEntry.js.map +1 -1
  552. package/dist/server/logEntry.sql +25 -1
  553. package/dist/server/loopDocs.d.ts +2 -2
  554. package/dist/server/loopDocs.d.ts.map +1 -1
  555. package/dist/server/loopDocs.js +38 -31
  556. package/dist/server/loopDocs.js.map +1 -1
  557. package/dist/server/loopDocs.sql +1 -3
  558. package/dist/server/model-catalog.d.ts.map +1 -1
  559. package/dist/server/model-catalog.js +2 -1
  560. package/dist/server/model-catalog.js.map +1 -1
  561. package/dist/server/model-route.d.ts +1 -1
  562. package/dist/server/model-route.d.ts.map +1 -1
  563. package/dist/server/model-route.js +4 -2
  564. package/dist/server/model-route.js.map +1 -1
  565. package/dist/server/seam-entry-read.sql +3 -4
  566. package/dist/server/seam-loop.sql +2 -9
  567. package/dist/server/workspace-capabilities.sql +19 -0
  568. package/dist/service.d.ts.map +1 -1
  569. package/dist/service.js +5 -17
  570. package/dist/service.js.map +1 -1
  571. package/docs/members.md +83 -0
  572. package/docs/skills.md +50 -0
  573. package/migrations/001_workspaces.sql +53 -0
  574. package/migrations/002_workers.sql +183 -0
  575. package/migrations/003_loops.sql +326 -0
  576. package/migrations/004_inference.sql +360 -0
  577. package/migrations/005_entries.sql +207 -0
  578. package/migrations/006_log.sql +578 -0
  579. package/migrations/007_subscriptions.sql +175 -0
  580. package/migrations/008_interactions.sql +29 -0
  581. package/package.json +33 -40
  582. package/digest-sql/channel-dispositions/dispositions.sql +0 -15
  583. package/digest-sql/channel-state/state.sql +0 -6
  584. package/digest-sql/curation/curation.sql +0 -6
  585. package/digest-sql/derivations/derivations.sql +0 -4
  586. package/digest-sql/embeddings/embeddings.sql +0 -4
  587. package/digest-sql/entry-dispositions/dispositions.sql +0 -15
  588. package/digest-sql/entry-state/state.sql +0 -6
  589. package/digest-sql/token-counts/token-counts.sql +0 -2
  590. package/dist/core/EmbeddingCall.d.ts +0 -23
  591. package/dist/core/EmbeddingCall.d.ts.map +0 -1
  592. package/dist/core/EmbeddingCall.js +0 -149
  593. package/dist/core/EmbeddingCall.js.map +0 -1
  594. package/dist/core/OverflowTurn.d.ts +0 -11
  595. package/dist/core/OverflowTurn.d.ts.map +0 -1
  596. package/dist/core/OverflowTurn.js +0 -80
  597. package/dist/core/OverflowTurn.js.map +0 -1
  598. package/dist/core/Owner.d.ts +0 -8
  599. package/dist/core/Owner.d.ts.map +0 -1
  600. package/dist/core/Owner.js +0 -46
  601. package/dist/core/Owner.js.map +0 -1
  602. package/dist/core/SendBroadcastHandler.d.ts.map +0 -1
  603. package/dist/core/SendBroadcastHandler.js +0 -159
  604. package/dist/core/SendBroadcastHandler.js.map +0 -1
  605. package/dist/core/VectorPump.d.ts +0 -28
  606. package/dist/core/VectorPump.d.ts.map +0 -1
  607. package/dist/core/VectorPump.js +0 -130
  608. package/dist/core/VectorPump.js.map +0 -1
  609. package/dist/core/owner.sql +0 -11
  610. package/dist/core/worker-settings.d.ts +0 -10
  611. package/dist/core/worker-settings.d.ts.map +0 -1
  612. package/dist/core/worker-settings.js +0 -53
  613. package/dist/core/worker-settings.js.map +0 -1
  614. package/dist/schemes/_entry-chunk.d.ts +0 -14
  615. package/dist/schemes/_entry-chunk.d.ts.map +0 -1
  616. package/dist/schemes/_entry-chunk.js +0 -165
  617. package/dist/schemes/_entry-chunk.js.map +0 -1
  618. package/dist/schemes/_entry-semantic.d.ts +0 -63
  619. package/dist/schemes/_entry-semantic.d.ts.map +0 -1
  620. package/dist/schemes/_entry-semantic.js +0 -299
  621. package/dist/schemes/_entry-semantic.js.map +0 -1
  622. package/dist/schemes/_entry-semantic.sql +0 -99
  623. package/dist/schemes/cosine.d.ts +0 -4
  624. package/dist/schemes/cosine.d.ts.map +0 -1
  625. package/dist/schemes/cosine.js +0 -9
  626. package/dist/schemes/cosine.js.map +0 -1
  627. package/dist/server/WorkerCapabilities.d.ts +0 -21
  628. package/dist/server/WorkerCapabilities.d.ts.map +0 -1
  629. package/dist/server/WorkerCapabilities.js.map +0 -1
  630. package/dist/server/WorkerResidency.d.ts +0 -40
  631. package/dist/server/WorkerResidency.d.ts.map +0 -1
  632. package/dist/server/WorkerResidency.js +0 -283
  633. package/dist/server/WorkerResidency.js.map +0 -1
  634. package/dist/server/seam-log-read.sql +0 -21
  635. package/dist/server/worker-capabilities.sql +0 -28
  636. package/migrations/001_schema.sql +0 -2207
@@ -1,2207 +0,0 @@
1
- -- MIGRATE: 1 baseline
2
- -- The single current pre-release schema baseline. Shape changes replace it;
3
- -- existing development databases are deleted and recreated, never upgraded.
4
-
5
- -- workspaces
6
- -- project_root: workspace pointer. NULL = headless (no disk side-effects);
7
- -- non-null = absolute path to the client's source tree, supplied at
8
- -- workspace.create or workspace.set_root.
9
- CREATE TABLE IF NOT EXISTS workspaces (
10
- id INTEGER NOT NULL PRIMARY KEY,
11
- version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
12
- name TEXT NOT NULL UNIQUE CHECK (length(name) > 0),
13
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
14
- project_root TEXT,
15
- -- {§operator-config} validated client workspace settings; each field composes
16
- -- with operator configuration at its owning use site.
17
- settings TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(settings))
18
- ) STRICT;
19
-
20
- CREATE INDEX IF NOT EXISTS workspaces_created_at ON workspaces (created_at);
21
-
22
- -- model_routes — the immutable resolved model route ({§worker-model-selection}). One row per
23
- -- complete resolved tuple; append-only. Alias is provenance plus a tuning scope,
24
- -- not route identity, and is absent on a direct provider/model selection.
25
- CREATE TABLE IF NOT EXISTS model_routes (
26
- id INTEGER NOT NULL PRIMARY KEY,
27
- alias TEXT CHECK (alias IS NULL OR length(alias) > 0),
28
- provider TEXT NOT NULL CHECK (length(provider) > 0),
29
- model TEXT NOT NULL CHECK (length(model) > 0),
30
- base_url TEXT CHECK (base_url IS NULL OR length(base_url) > 0),
31
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
32
- ) STRICT;
33
-
34
- CREATE UNIQUE INDEX IF NOT EXISTS model_routes_identity
35
- ON model_routes (coalesce(alias, ''), provider, model, coalesce(base_url, ''));
36
-
37
- -- workers
38
- CREATE TABLE IF NOT EXISTS workers (
39
- id INTEGER NOT NULL PRIMARY KEY,
40
- version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
41
- workspace_id INTEGER NOT NULL,
42
- name TEXT NOT NULL CHECK (length(name) > 0),
43
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
44
- -- {§worker-provider-identity}: provider affinity must not collide when
45
- -- independent databases reuse local integer ids.
46
- provider_identity TEXT NOT NULL DEFAULT (lower(hex(randomblob(16))))
47
- CHECK (length(provider_identity) = 32 AND provider_identity NOT GLOB '*[^0-9a-f]*'),
48
- -- {§worker-model-selection}: the worker's durable resolved model, NULL for non-model
49
- -- workers or a deliberately modelless unresolved worker. spawn_model_route_id is the
50
- -- persistent spawn override; NULL means "use my model."
51
- model_route_id INTEGER REFERENCES model_routes(id),
52
- spawn_model_route_id INTEGER REFERENCES model_routes(id),
53
- -- {§worker-reasoning-policy}: nullable only while the worker has no model;
54
- -- once selected, model and reasoning policy form one durable generation policy.
55
- reasoning_policy TEXT CHECK (reasoning_policy IS NULL OR length(reasoning_policy) > 0),
56
- -- workers fork via parent_worker_id; workspaces carry no parent — {§machine-processes-no-fork-workspace}
57
- parent_worker_id INTEGER CHECK (parent_worker_id IS NULL OR parent_worker_id != id),
58
- origin TEXT NOT NULL DEFAULT 'client' CHECK (origin IN ('model', 'client', '_plurnk')),
59
- -- {§methods-model-worker}: durable identity for the workspace's stable
60
- -- default conversation; unrelated to its human-facing, reclaimable name.
61
- default_conversation INTEGER NOT NULL DEFAULT 0 CHECK (default_conversation IN (0, 1)),
62
- -- {§worker-settings}: the worker's own behavioral rules inside the workspace's
63
- -- world — the workspace is how things are; each worker carries the rules its
64
- -- loops obey. Client-declared at worker creation, mutable between loops,
65
- -- validated at the client-input boundary (closed known-key set; unknown keys
66
- -- never persist).
67
- settings TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(settings)),
68
- -- Parent-derived capability ceiling, copied by value at child creation.
69
- -- Root workers have the unrestricted empty policy.
70
- capability_bound TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(capability_bound)),
71
- -- {§env-delta-log-pull}: monotonic observation progress, not a private world snapshot.
72
- -- Creation captures the workspace high-water; a fork instead copies its
73
- -- parent's cursor and records the closed event boundary of its snapshot.
74
- ambient_event_cursor INTEGER CHECK (ambient_event_cursor IS NULL OR ambient_event_cursor >= 0),
75
- fork_event_boundary INTEGER CHECK (fork_event_boundary IS NULL OR fork_event_boundary >= 0),
76
- CHECK (fork_event_boundary IS NULL OR parent_worker_id IS NOT NULL),
77
- CHECK (default_conversation = 0 OR (origin = 'model' AND parent_worker_id IS NULL)),
78
- CHECK ((model_route_id IS NULL) = (reasoning_policy IS NULL)),
79
- FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE,
80
- FOREIGN KEY (parent_worker_id) REFERENCES workers(id) ON DELETE CASCADE
81
- ) STRICT;
82
-
83
- CREATE INDEX IF NOT EXISTS workers_workspace_id_created_at ON workers (workspace_id, created_at);
84
- CREATE INDEX IF NOT EXISTS workers_parent_worker_id ON workers (parent_worker_id);
85
- CREATE UNIQUE INDEX IF NOT EXISTS workers_provider_identity ON workers (provider_identity);
86
- CREATE UNIQUE INDEX IF NOT EXISTS workers_workspace_default_conversation
87
- ON workers (workspace_id) WHERE default_conversation = 1;
88
- -- NOT unique: a name is frozen per worker ({§machine-processes-worker-origin}) but RECLAIMABLE across
89
- -- time — a terminated worker keeps its name in permanent history while a fresh spawn reuses it;
90
- -- worker_resolve_by_name picks the newest. A LIVE collision is refused at the spawn gate (Worker.edit
91
- -- → worker_live_by_name → 409), never by this index. Indexed for the by-name resolve/spawn lookup.
92
- CREATE INDEX IF NOT EXISTS workers_workspace_name ON workers (workspace_id, name);
93
-
94
- CREATE TRIGGER IF NOT EXISTS workers_provider_identity_immutable
95
- BEFORE UPDATE OF provider_identity ON workers
96
- WHEN NEW.provider_identity != OLD.provider_identity
97
- BEGIN
98
- SELECT RAISE(ABORT, 'workers.provider_identity is immutable');
99
- END;
100
-
101
- CREATE TRIGGER IF NOT EXISTS workers_fork_event_boundary_immutable
102
- BEFORE UPDATE OF fork_event_boundary ON workers
103
- WHEN NEW.fork_event_boundary IS NOT OLD.fork_event_boundary
104
- BEGIN
105
- SELECT RAISE(ABORT, 'workers.fork_event_boundary is immutable');
106
- END;
107
-
108
- -- {§module-worker-state}: one opaque, provider-validated JSON snapshot per
109
- -- module owner and Worker. Core owns isolation, inheritance, and lifecycle;
110
- -- the module owns its state schema and semantics.
111
- CREATE TABLE IF NOT EXISTS worker_module_state (
112
- worker_id INTEGER NOT NULL,
113
- namespace_owner TEXT NOT NULL CHECK (length(namespace_owner) > 0),
114
- state TEXT NOT NULL CHECK (json_valid(state)),
115
- updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
116
- PRIMARY KEY (worker_id, namespace_owner),
117
- FOREIGN KEY (worker_id) REFERENCES workers(id) ON DELETE CASCADE
118
- ) STRICT;
119
-
120
- -- A child snapshots every initialized module state at birth. Later parent
121
- -- mutations do not propagate; the child and parent are independent authorities.
122
- CREATE TRIGGER IF NOT EXISTS worker_module_state_inherit
123
- AFTER INSERT ON workers
124
- WHEN NEW.parent_worker_id IS NOT NULL
125
- BEGIN
126
- INSERT INTO worker_module_state (worker_id, namespace_owner, state, updated_at)
127
- SELECT NEW.id, namespace_owner, state, updated_at
128
- FROM worker_module_state
129
- WHERE worker_id = NEW.parent_worker_id;
130
- END;
131
-
132
- -- {§env-delta-log-pull}: one append-only occurrence journal gives every
133
- -- producer a shared monotonic order. Audience is structural: direct parent,
134
- -- explicit workspace broadcast, or their union. The event snapshots exactly
135
- -- what an observer row needs because source-log curation cannot erase history.
136
- -- source_record_id is forensic identity for the originating log/loop row, not a
137
- -- foreign key: model log curation must not erase an already-recorded occurrence.
138
- CREATE TABLE IF NOT EXISTS ambient_events (
139
- id INTEGER NOT NULL PRIMARY KEY,
140
- workspace_id INTEGER NOT NULL,
141
- producer_worker_id INTEGER NOT NULL,
142
- target_parent_worker_id INTEGER,
143
- workspace_broadcast INTEGER NOT NULL DEFAULT 0 CHECK (workspace_broadcast IN (0, 1)),
144
- kind TEXT NOT NULL CHECK (kind IN ('activity', 'loop_termination')),
145
- source_record_id INTEGER NOT NULL CHECK (source_record_id >= 1),
146
- at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
147
- source TEXT,
148
- op TEXT NOT NULL,
149
- delimiter TEXT NOT NULL DEFAULT '',
150
- signal TEXT CHECK (signal IS NULL OR json_valid(signal)),
151
- scheme TEXT,
152
- username TEXT,
153
- password TEXT,
154
- hostname TEXT,
155
- port INTEGER CHECK (port IS NULL OR (port BETWEEN 0 AND 65535)),
156
- pathname TEXT,
157
- query TEXT,
158
- fragment TEXT,
159
- line_marker TEXT CHECK (line_marker IS NULL OR json_valid(line_marker)),
160
- tx TEXT NOT NULL,
161
- mimetype_tx TEXT NOT NULL CHECK (length(mimetype_tx) > 0),
162
- rx TEXT NOT NULL,
163
- mimetype_rx TEXT NOT NULL CHECK (length(mimetype_rx) > 0),
164
- status_rx INTEGER NOT NULL CHECK (status_rx BETWEEN 100 AND 599),
165
- state TEXT NOT NULL CHECK (state IN ('resolved', 'failed', 'cancelled')),
166
- outcome TEXT,
167
- attrs TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(attrs)),
168
- terminated_by TEXT CHECK (terminated_by IS NULL OR terminated_by = 'cancel'),
169
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
170
- CHECK (target_parent_worker_id IS NOT NULL OR workspace_broadcast = 1),
171
- CHECK (
172
- (kind = 'activity' AND terminated_by IS NULL)
173
- OR
174
- (kind = 'loop_termination'
175
- AND op = 'SEND'
176
- AND scheme = 'worker'
177
- AND json_valid(rx)
178
- AND json_type(rx) = 'object'
179
- AND json_type(rx, '$.status') = 'integer'
180
- AND json_extract(rx, '$.status') = status_rx)
181
- ),
182
- FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE,
183
- FOREIGN KEY (producer_worker_id) REFERENCES workers(id) ON DELETE CASCADE,
184
- FOREIGN KEY (target_parent_worker_id) REFERENCES workers(id) ON DELETE CASCADE
185
- ) STRICT;
186
-
187
- CREATE INDEX IF NOT EXISTS ambient_events_workspace_id_id
188
- ON ambient_events (workspace_id, id);
189
- CREATE INDEX IF NOT EXISTS ambient_events_producer_kind_id
190
- ON ambient_events (producer_worker_id, kind, id);
191
- CREATE INDEX IF NOT EXISTS ambient_events_parent_id
192
- ON ambient_events (target_parent_worker_id, id)
193
- WHERE target_parent_worker_id IS NOT NULL;
194
- CREATE UNIQUE INDEX IF NOT EXISTS ambient_events_source_identity
195
- ON ambient_events (producer_worker_id, kind, source_record_id);
196
-
197
- CREATE TRIGGER IF NOT EXISTS ambient_events_structural_audience
198
- BEFORE INSERT ON ambient_events
199
- WHEN NOT EXISTS (
200
- SELECT 1
201
- FROM workers producer
202
- WHERE producer.id = NEW.producer_worker_id
203
- AND producer.workspace_id = NEW.workspace_id
204
- AND (
205
- NEW.target_parent_worker_id IS NULL
206
- OR NEW.target_parent_worker_id = producer.parent_worker_id
207
- )
208
- )
209
- BEGIN
210
- SELECT RAISE(ABORT, 'ambient event audience must be the producer direct parent in the same workspace');
211
- END;
212
-
213
- -- A worker begins after the history that predates its existence. This trigger
214
- -- runs in the worker INSERT statement, so an occurrence is either in the
215
- -- baseline or after it. Fork INSERTs supply their own cursor/boundary and skip
216
- -- this ordinary-worker baseline. {§env-delta-log-pull}
217
- CREATE TRIGGER IF NOT EXISTS workers_capture_ambient_baseline
218
- AFTER INSERT ON workers
219
- WHEN NEW.ambient_event_cursor IS NULL
220
- BEGIN
221
- UPDATE workers
222
- SET ambient_event_cursor = COALESCE((
223
- SELECT MAX(ae.id) FROM ambient_events ae WHERE ae.workspace_id = NEW.workspace_id
224
- ), 0)
225
- WHERE id = NEW.id;
226
- END;
227
-
228
- -- loops
229
- -- policy: complete immutable per-loop capability attenuation and proposal
230
- -- disposition ({§loop-policy-effective-read}).
231
- CREATE TABLE IF NOT EXISTS loops (
232
- id INTEGER NOT NULL PRIMARY KEY,
233
- version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
234
- worker_id INTEGER NOT NULL,
235
- sequence INTEGER NOT NULL CHECK (sequence >= 1),
236
- status INTEGER NOT NULL DEFAULT 102 CHECK (status IN (100, 102, 200, 202, 413, 429, 499, 500, 504, 508)),
237
- prompt TEXT NOT NULL,
238
- -- {§prompt-causal-source}: canonical actor address for the initial prompt;
239
- -- NULL means the owning worker itself.
240
- prompt_source TEXT CHECK (prompt_source IS NULL OR length(prompt_source) > 0),
241
- policy TEXT NOT NULL DEFAULT '{"capabilities":{},"proposals":"review"}' CHECK (json_valid(policy)),
242
- -- {§worker-model-selection}: immutable loop snapshots of the resolved model route and the
243
- -- effective spawn route (was provider_spec/child_provider_spec JSON).
244
- model_route_id INTEGER REFERENCES model_routes(id),
245
- spawn_model_route_id INTEGER REFERENCES model_routes(id),
246
- reasoning_policy TEXT CHECK (reasoning_policy IS NULL OR length(reasoning_policy) > 0),
247
- max_turns INTEGER NOT NULL DEFAULT 50 CHECK (max_turns >= -1),
248
- -- {§methods-loop-run-open-paths}: the initial prompt frame's selected paths,
249
- -- held here until turn 1 materializes that frame (string[] JSON).
250
- open_paths TEXT NOT NULL DEFAULT '[]' CHECK (json_valid(open_paths)),
251
- -- {§prompt-loop-containment}: one queued recovery loop may carry the
252
- -- complete orphan frame set of one concluded source loop.
253
- orphan_source_loop_id INTEGER,
254
- -- {§worker-scheme} loop-termination delta: terminated_at is stamped by the trigger
255
- -- below when status crosses into terminal (every death-path, uniformly).
256
- terminated_at TEXT,
257
- terminal_result TEXT,
258
- -- {§loop-terminal-authorship}: 'cancel' names an external loop.cancel;
259
- -- NULL covers model terminals and engine verdicts whose result carries the story.
260
- terminated_by TEXT CHECK (terminated_by IS NULL OR terminated_by = 'cancel'),
261
- CONSTRAINT loops_generation_policy_contract CHECK (
262
- (model_route_id IS NULL) = (reasoning_policy IS NULL)
263
- ),
264
- CONSTRAINT loops_terminal_result_contract CHECK (
265
- CASE
266
- WHEN status IN (100, 102, 202) THEN terminal_result IS NULL
267
- WHEN terminal_result IS NULL OR NOT json_valid(terminal_result) THEN 0
268
- ELSE
269
- json_type(terminal_result) IS 'object'
270
- AND json_type(terminal_result, '$.status') IS 'integer'
271
- AND (
272
- json_extract(terminal_result, '$.status') = status
273
- OR (
274
- status = 200
275
- AND json_extract(terminal_result, '$.status') BETWEEN 200 AND 399
276
- AND json_extract(terminal_result, '$.status') != 202
277
- )
278
- OR (
279
- status = 500
280
- AND json_extract(terminal_result, '$.status') BETWEEN 400 AND 599
281
- )
282
- )
283
- AND CASE
284
- WHEN json_extract(terminal_result, '$.status') < 400 THEN
285
- json_type(terminal_result, '$.problem') IS NULL
286
- ELSE
287
- json_type(terminal_result, '$.problem') IS 'object'
288
- AND json_type(terminal_result, '$.problem.status') IS 'integer'
289
- AND json_extract(terminal_result, '$.problem.status')
290
- = json_extract(terminal_result, '$.status')
291
- AND json_type(terminal_result, '$.problem.type') IS 'text'
292
- AND length(json_extract(terminal_result, '$.problem.type')) > 0
293
- AND json_type(terminal_result, '$.problem.title') IS 'text'
294
- AND length(json_extract(terminal_result, '$.problem.title')) > 0
295
- AND json_type(terminal_result, '$.problem.detail') IS 'text'
296
- AND length(json_extract(terminal_result, '$.problem.detail')) > 0
297
- AND json_type(terminal_result, '$.problem.instance') IS 'text'
298
- AND length(json_extract(terminal_result, '$.problem.instance')) > 0
299
- END
300
- END
301
- ),
302
- FOREIGN KEY (worker_id) REFERENCES workers(id) ON DELETE CASCADE,
303
- FOREIGN KEY (orphan_source_loop_id) REFERENCES loops(id)
304
- ) STRICT;
305
-
306
- CREATE UNIQUE INDEX IF NOT EXISTS loops_worker_id_sequence ON loops (worker_id, sequence);
307
- CREATE UNIQUE INDEX IF NOT EXISTS loops_orphan_source_loop_id ON loops (orphan_source_loop_id);
308
- -- {§worker-scheme}: a loop crossing into a terminal status stamps terminated_at, so sibling
309
- -- workers pull the termination as a folded ambient delta — caught uniformly across every
310
- -- death-path (SEND, overflow recovery, max-turns, strike, KILL). The stamp updates terminated_at,
311
- -- never status, so it cannot re-fire this trigger. Terminals: 200 done · 413 budget ·
312
- -- 429 turn-ceiling · 499 cancel · 500 fail · 504 wall-clock timeout · 508 runaway. (202 = parked/sleeping, NOT terminal.)
313
- CREATE TRIGGER IF NOT EXISTS loops_stamp_terminated_at
314
- AFTER UPDATE OF status ON loops
315
- WHEN NEW.status IN (200, 413, 429, 499, 500, 504, 508) AND OLD.status NOT IN (200, 413, 429, 499, 500, 504, 508)
316
- BEGIN
317
- UPDATE loops SET terminated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = NEW.id;
318
- END;
319
-
320
- -- A child terminal transition is an occurrence addressed only to its direct
321
- -- parent. Directly inserted fork history never crosses this transition and
322
- -- therefore cannot fabricate a new conclusion event.
323
- CREATE TRIGGER IF NOT EXISTS loops_append_ambient_event
324
- AFTER UPDATE OF status ON loops
325
- WHEN NEW.status IN (200, 413, 429, 499, 500, 504, 508) AND OLD.status NOT IN (200, 413, 429, 499, 500, 504, 508)
326
- BEGIN
327
- INSERT INTO ambient_events (
328
- workspace_id, producer_worker_id, target_parent_worker_id,
329
- workspace_broadcast, kind, source_record_id, source,
330
- op, delimiter, scheme, pathname,
331
- tx, mimetype_tx, rx, mimetype_rx, status_rx, state, terminated_by
332
- )
333
- SELECT w.workspace_id, NEW.worker_id, w.parent_worker_id,
334
- 0, 'loop_termination', NEW.id, NULL,
335
- 'SEND', '', 'worker', '/' || w.name,
336
- '', 'text/plain', NEW.terminal_result, 'application/json',
337
- json_extract(NEW.terminal_result, '$.status'), 'resolved', NEW.terminated_by
338
- FROM workers w
339
- WHERE w.id = NEW.worker_id
340
- AND w.parent_worker_id IS NOT NULL
341
- -- A loop whose only turns are client-driven operation turns is not the parent's
342
- -- business (#323). A loop that never got a turn at all — a spawn refused before it
343
- -- ran, e.g. a branch batch failing git-preflight — is, or the parent waits on a
344
- -- ghost that vanished from Active Child Workers without a word (#385).
345
- AND (
346
- NOT EXISTS (SELECT 1 FROM turns t WHERE t.loop_id = NEW.id)
347
- OR EXISTS (
348
- SELECT 1
349
- FROM turns t
350
- WHERE t.loop_id = NEW.id
351
- AND NOT (t.producer = '_plurnk' AND t.kind = 'operation')
352
- )
353
- );
354
- END;
355
-
356
- -- turns
357
- -- finish_reason / model: accepted provider-call metadata from the provider
358
- -- response contract. Physical request accounting is normalized beneath the
359
- -- logical emission attempt that caused it.
360
- CREATE TABLE IF NOT EXISTS turns (
361
- id INTEGER NOT NULL PRIMARY KEY,
362
- version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
363
- loop_id INTEGER NOT NULL,
364
- sequence INTEGER NOT NULL CHECK (sequence >= 1),
365
- timestamp TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
366
- -- {§turn-record}: producer and purpose define the turn. Packet/provider
367
- -- evidence below is an optional inference specialization.
368
- producer TEXT NOT NULL CHECK (producer IN ('model', 'client', '_plurnk', 'plugin')),
369
- kind TEXT NOT NULL CHECK (kind IN ('inference', 'initialization', 'overflow', 'operation', 'maintenance')),
370
- status INTEGER NOT NULL CHECK (status BETWEEN 100 AND 599),
371
- -- NULL while the producer still owns this turn. Status 102 is both the
372
- -- provisional running value and the exact completed continue disposition;
373
- -- completed_at distinguishes those states without inventing another status.
374
- completed_at TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
375
- -- Provider-derived curation calibration for this turn; NULL when input
376
- -- capacity is unknown. {§tokenomics-client-gauge}
377
- usage_curation_budget INTEGER CHECK (usage_curation_budget IS NULL OR usage_curation_budget >= 1),
378
- -- {§packet-stored-shape}: NULL means no model request was assembled. A
379
- -- present packet is either the measured request or that request extended
380
- -- by the paired admitted-response fields.
381
- packet TEXT CHECK (
382
- CASE
383
- WHEN packet IS NULL THEN 1
384
- WHEN json_valid(packet) = 0 THEN 0
385
- ELSE COALESCE(
386
- json_type(packet) = 'object'
387
- AND json_type(packet, '$.weight') = 'integer'
388
- AND json_extract(packet, '$.weight') >= 0
389
- AND json_type(packet, '$.sections') = 'array'
390
- AND json_type(packet, '$.attributions') = 'array'
391
- AND (
392
- (
393
- json_type(packet, '$.assistant') IS NULL
394
- AND json_type(packet, '$.assistantRaw') IS NULL
395
- )
396
- OR (
397
- json_type(packet, '$.assistant') = 'object'
398
- AND json_type(packet, '$.assistant.content') = 'text'
399
- AND json_type(packet, '$.assistant.ops') = 'array'
400
- AND json_type(packet, '$.assistant.reasoning') IN ('text', 'null')
401
- AND json_type(packet, '$.assistantRaw') IS NOT NULL
402
- )
403
- ),
404
- 0
405
- )
406
- END
407
- ),
408
- finish_reason TEXT,
409
- model TEXT CHECK (model IS NULL OR length(model) >= 1),
410
- -- Opaque provider→client metadata plus engine rail keys; absent outside
411
- -- recorded inference evidence. {§meta-passthrough}, {§rail-truth-engine-verdict}
412
- meta TEXT CHECK (meta IS NULL OR json_valid(meta)),
413
- CHECK (completed_at IS NOT NULL OR status = 102),
414
- CHECK ((producer = 'model') = (kind = 'inference')),
415
- CHECK (kind NOT IN ('initialization', 'overflow') OR producer = '_plurnk'),
416
- CHECK (
417
- kind = 'inference'
418
- OR (
419
- usage_curation_budget IS NULL
420
- AND packet IS NULL
421
- AND finish_reason IS NULL
422
- AND model IS NULL
423
- AND meta IS NULL
424
- )
425
- ),
426
- FOREIGN KEY (loop_id) REFERENCES loops(id) ON DELETE CASCADE
427
- ) STRICT;
428
-
429
- CREATE UNIQUE INDEX IF NOT EXISTS turns_loop_id_sequence ON turns (loop_id, sequence);
430
- CREATE INDEX IF NOT EXISTS turns_timestamp ON turns (timestamp);
431
-
432
- -- Producer and purpose are immutable except for the single pre-inference
433
- -- diversion into overflow recovery. The transition is allowed only before any
434
- -- model or non-kernel evidence exists.
435
- CREATE TRIGGER IF NOT EXISTS turns_identity_forward_only
436
- BEFORE UPDATE OF producer, kind ON turns
437
- WHEN NOT (
438
- OLD.producer = NEW.producer
439
- AND OLD.kind = NEW.kind
440
- )
441
- AND NOT (
442
- OLD.producer = 'model'
443
- AND OLD.kind = 'inference'
444
- AND NEW.producer = '_plurnk'
445
- AND NEW.kind = 'overflow'
446
- AND OLD.completed_at IS NULL
447
- AND OLD.packet IS NULL
448
- AND OLD.usage_curation_budget IS NULL
449
- AND OLD.finish_reason IS NULL
450
- AND OLD.model IS NULL
451
- AND OLD.meta IS NULL
452
- -- Engine-side embedding calls are not model history; only an emission or BARE call is.
453
- AND NOT EXISTS (SELECT 1 FROM inference_calls WHERE turn_id = OLD.id AND kind IN ('emission', 'bare'))
454
- AND NOT EXISTS (
455
- SELECT 1 FROM log_entries
456
- WHERE turn_id = OLD.id AND origin != '_plurnk'
457
- )
458
- )
459
- BEGIN
460
- SELECT RAISE(ABORT, 'turn producer and kind are immutable outside pre-inference overflow diversion');
461
- END;
462
-
463
- -- One logical inference occurrence. Workspace ownership is mandatory; a real
464
- -- causal turn is attached when one exists. Generation and embedding evidence
465
- -- specialize this identity without duplicating its lifecycle or physical
466
- -- request ledger. {§tokenomics-provider-usage}
467
- CREATE TABLE IF NOT EXISTS inference_calls (
468
- id INTEGER NOT NULL PRIMARY KEY,
469
- workspace_id INTEGER NOT NULL,
470
- turn_id INTEGER,
471
- sequence INTEGER NOT NULL CHECK (sequence >= 1),
472
- kind TEXT NOT NULL CHECK (kind IN ('emission', 'bare', 'embedding_query', 'embedding_documents')),
473
- state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'response', 'error')),
474
- -- Exact opaque tag set forwarded with generation calls. Embeddings carry
475
- -- the empty set through the same general identity.
476
- attributions TEXT NOT NULL DEFAULT '[]' CHECK (
477
- json_valid(attributions) AND json_type(attributions) = 'array'
478
- ),
479
- request_model TEXT NOT NULL CHECK (length(request_model) >= 1),
480
- timestamp TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
481
- completed_at TEXT,
482
- CHECK (
483
- (state = 'pending' AND completed_at IS NULL)
484
- OR (state IN ('response', 'error') AND completed_at IS NOT NULL)
485
- ),
486
- CHECK (
487
- kind IN ('emission', 'bare') OR json_array_length(attributions) = 0
488
- ),
489
- FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE,
490
- FOREIGN KEY (turn_id) REFERENCES turns(id) ON DELETE CASCADE
491
- ) STRICT;
492
-
493
- CREATE UNIQUE INDEX IF NOT EXISTS inference_calls_turn_sequence
494
- ON inference_calls (turn_id, sequence) WHERE turn_id IS NOT NULL;
495
- CREATE UNIQUE INDEX IF NOT EXISTS inference_calls_workspace_sequence
496
- ON inference_calls (workspace_id, sequence) WHERE turn_id IS NULL;
497
- CREATE INDEX IF NOT EXISTS inference_calls_workspace_id
498
- ON inference_calls (workspace_id, timestamp, id);
499
-
500
- CREATE TRIGGER IF NOT EXISTS inference_calls_open_pending
501
- BEFORE INSERT ON inference_calls
502
- WHEN NEW.state != 'pending' OR NEW.completed_at IS NOT NULL
503
- BEGIN
504
- SELECT RAISE(ABORT, 'inference call must open pending');
505
- END;
506
-
507
- CREATE TRIGGER IF NOT EXISTS inference_calls_context_valid
508
- BEFORE INSERT ON inference_calls
509
- WHEN NOT (
510
- (NEW.turn_id IS NULL AND NEW.kind IN ('embedding_query', 'embedding_documents'))
511
- OR
512
- (NEW.turn_id IS NOT NULL
513
- AND COALESCE((
514
- SELECT w.workspace_id = NEW.workspace_id
515
- FROM turns t
516
- JOIN loops l ON l.id = t.loop_id
517
- JOIN workers w ON w.id = l.worker_id
518
- WHERE t.id = NEW.turn_id
519
- ), 0) = 1
520
- AND (
521
- NEW.kind IN ('embedding_query', 'embedding_documents')
522
- OR COALESCE((
523
- SELECT producer = 'model' AND kind = 'inference'
524
- FROM turns
525
- WHERE id = NEW.turn_id
526
- ), 0) = 1
527
- )
528
- )
529
- )
530
- BEGIN
531
- SELECT RAISE(ABORT, 'inference call requires a valid owning workspace and causal context');
532
- END;
533
-
534
- CREATE TRIGGER IF NOT EXISTS inference_calls_request_identity_immutable
535
- BEFORE UPDATE OF workspace_id, turn_id, sequence, kind, attributions, request_model, timestamp
536
- ON inference_calls
537
- BEGIN
538
- SELECT RAISE(ABORT, 'inference call request identity is immutable');
539
- END;
540
-
541
- CREATE TRIGGER IF NOT EXISTS inference_calls_state_forward_only
542
- BEFORE UPDATE OF state ON inference_calls
543
- WHEN NOT (
544
- NEW.state = OLD.state
545
- OR (OLD.state = 'pending' AND NEW.state IN ('response', 'error'))
546
- )
547
- BEGIN
548
- SELECT RAISE(ABORT, 'inference call state may only close once');
549
- END;
550
-
551
- CREATE TRIGGER IF NOT EXISTS inference_calls_terminal_evidence_required
552
- BEFORE UPDATE OF state ON inference_calls
553
- WHEN NEW.state != OLD.state
554
- AND NOT (
555
- (NEW.state = 'response' AND (
556
- (NEW.kind IN ('emission', 'bare') AND EXISTS (
557
- SELECT 1 FROM model_calls WHERE id = NEW.id AND response IS NOT NULL
558
- ))
559
- OR
560
- (NEW.kind IN ('embedding_query', 'embedding_documents') AND EXISTS (
561
- SELECT 1 FROM embedding_calls WHERE id = NEW.id AND output_count IS NOT NULL
562
- ))
563
- ))
564
- OR
565
- (NEW.state = 'error' AND (
566
- (NEW.kind IN ('emission', 'bare') AND EXISTS (
567
- SELECT 1 FROM model_calls WHERE id = NEW.id AND failure IS NOT NULL
568
- ))
569
- OR
570
- (NEW.kind IN ('embedding_query', 'embedding_documents') AND EXISTS (
571
- SELECT 1 FROM embedding_calls WHERE id = NEW.id AND failure IS NOT NULL
572
- ))
573
- ))
574
- )
575
- BEGIN
576
- SELECT RAISE(ABORT, 'inference call terminal state requires specialization evidence');
577
- END;
578
-
579
- CREATE TRIGGER IF NOT EXISTS inference_calls_completion_immutable
580
- BEFORE UPDATE OF completed_at ON inference_calls
581
- WHEN OLD.state != 'pending'
582
- BEGIN
583
- SELECT RAISE(ABORT, 'inference call completion is immutable');
584
- END;
585
-
586
- -- Generation-specific response/failure evidence. The request model and
587
- -- lifecycle remain on inference_calls; response_model is provider evidence.
588
- CREATE TABLE IF NOT EXISTS model_calls (
589
- id INTEGER NOT NULL PRIMARY KEY,
590
- -- {§packet-attachment-parts}: exact model-facing log coordinates whose
591
- -- native bytes were present in this completed request. The ordinary READ
592
- -- result remains independently durable in the log.
593
- native_inputs TEXT NOT NULL DEFAULT '[]' CHECK (
594
- json_valid(native_inputs) AND json_type(native_inputs) = 'array'
595
- ),
596
- response TEXT CHECK (response IS NULL OR json_valid(response)),
597
- failure TEXT CHECK (failure IS NULL OR json_valid(failure)),
598
- capacity TEXT CHECK (
599
- capacity IS NULL OR (json_valid(capacity) AND json_type(capacity) = 'object')
600
- ),
601
- finish_reason TEXT,
602
- response_model TEXT CHECK (response_model IS NULL OR length(response_model) >= 1),
603
- CHECK (response IS NULL OR (capacity IS NOT NULL AND response_model IS NOT NULL)),
604
- CHECK (json_array_length(native_inputs) = 0 OR response IS NOT NULL),
605
- FOREIGN KEY (id) REFERENCES inference_calls(id) ON DELETE CASCADE
606
- ) STRICT;
607
-
608
- CREATE TRIGGER IF NOT EXISTS model_calls_specializes_generation
609
- BEFORE INSERT ON model_calls
610
- WHEN COALESCE((
611
- SELECT kind IN ('emission', 'bare') FROM inference_calls WHERE id = NEW.id
612
- ), 0) != 1
613
- BEGIN
614
- SELECT RAISE(ABORT, 'model call must specialize a generation inference');
615
- END;
616
-
617
- CREATE TRIGGER IF NOT EXISTS model_calls_cannot_orphan_inference
618
- AFTER DELETE ON model_calls
619
- WHEN EXISTS (SELECT 1 FROM inference_calls WHERE id = OLD.id)
620
- BEGIN
621
- SELECT RAISE(ABORT, 'model call specialization cannot be deleted independently');
622
- END;
623
-
624
- CREATE TRIGGER IF NOT EXISTS inference_calls_create_model_specialization
625
- AFTER INSERT ON inference_calls
626
- WHEN NEW.kind IN ('emission', 'bare')
627
- BEGIN
628
- INSERT INTO model_calls (id) VALUES (NEW.id);
629
- END;
630
-
631
- CREATE TRIGGER IF NOT EXISTS model_calls_observation_pending
632
- BEFORE UPDATE OF native_inputs, response, failure, capacity, finish_reason, response_model
633
- ON model_calls
634
- WHEN COALESCE((SELECT state FROM inference_calls WHERE id = OLD.id), '') != 'pending'
635
- BEGIN
636
- SELECT RAISE(ABORT, 'model call observation is immutable');
637
- END;
638
-
639
- CREATE TRIGGER IF NOT EXISTS model_calls_close_response
640
- AFTER UPDATE OF response ON model_calls
641
- WHEN NEW.response IS NOT NULL
642
- BEGIN
643
- UPDATE inference_calls
644
- SET state = 'response', completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
645
- WHERE id = NEW.id AND state = 'pending';
646
- END;
647
-
648
- CREATE TRIGGER IF NOT EXISTS model_calls_close_error
649
- AFTER UPDATE OF failure ON model_calls
650
- WHEN NEW.failure IS NOT NULL AND NEW.response IS NULL
651
- BEGIN
652
- UPDATE inference_calls
653
- SET state = 'error', completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
654
- WHERE id = NEW.id AND state = 'pending';
655
- END;
656
-
657
- -- Embedding-specific cardinality and artifact evidence. input_count is
658
- -- prepared before physical I/O; output_count or failure closes the shared call.
659
- CREATE TABLE IF NOT EXISTS embedding_calls (
660
- id INTEGER NOT NULL PRIMARY KEY,
661
- input_count INTEGER CHECK (input_count IS NULL OR input_count >= 0),
662
- output_count INTEGER CHECK (output_count IS NULL OR output_count >= 0),
663
- metadata TEXT CHECK (
664
- metadata IS NULL OR (json_valid(metadata) AND json_type(metadata) = 'object')
665
- ),
666
- failure TEXT CHECK (failure IS NULL OR json_valid(failure)),
667
- CHECK (output_count IS NULL OR (input_count IS NOT NULL AND output_count = input_count AND metadata IS NOT NULL)),
668
- CHECK (NOT (output_count IS NOT NULL AND failure IS NOT NULL)),
669
- FOREIGN KEY (id) REFERENCES inference_calls(id) ON DELETE CASCADE
670
- ) STRICT;
671
-
672
- CREATE TRIGGER IF NOT EXISTS embedding_calls_specializes_embedding
673
- BEFORE INSERT ON embedding_calls
674
- WHEN COALESCE((
675
- SELECT kind IN ('embedding_query', 'embedding_documents')
676
- FROM inference_calls
677
- WHERE id = NEW.id
678
- ), 0) != 1
679
- BEGIN
680
- SELECT RAISE(ABORT, 'embedding call must specialize an embedding inference');
681
- END;
682
-
683
- CREATE TRIGGER IF NOT EXISTS embedding_calls_cannot_orphan_inference
684
- AFTER DELETE ON embedding_calls
685
- WHEN EXISTS (SELECT 1 FROM inference_calls WHERE id = OLD.id)
686
- BEGIN
687
- SELECT RAISE(ABORT, 'embedding call specialization cannot be deleted independently');
688
- END;
689
-
690
- CREATE TRIGGER IF NOT EXISTS inference_calls_create_embedding_specialization
691
- AFTER INSERT ON inference_calls
692
- WHEN NEW.kind IN ('embedding_query', 'embedding_documents')
693
- BEGIN
694
- INSERT INTO embedding_calls (id) VALUES (NEW.id);
695
- END;
696
-
697
- CREATE TRIGGER IF NOT EXISTS embedding_calls_observation_pending
698
- BEFORE UPDATE OF input_count, output_count, metadata, failure ON embedding_calls
699
- WHEN COALESCE((SELECT state FROM inference_calls WHERE id = OLD.id), '') != 'pending'
700
- BEGIN
701
- SELECT RAISE(ABORT, 'embedding call observation is immutable');
702
- END;
703
-
704
- CREATE TRIGGER IF NOT EXISTS embedding_calls_input_once
705
- BEFORE UPDATE OF input_count ON embedding_calls
706
- WHEN OLD.input_count IS NOT NULL
707
- BEGIN
708
- SELECT RAISE(ABORT, 'embedding call input cardinality is immutable');
709
- END;
710
-
711
- CREATE TRIGGER IF NOT EXISTS embedding_calls_close_response
712
- AFTER UPDATE OF output_count ON embedding_calls
713
- WHEN NEW.output_count IS NOT NULL
714
- BEGIN
715
- UPDATE inference_calls
716
- SET state = 'response', completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
717
- WHERE id = NEW.id AND state = 'pending';
718
- END;
719
-
720
- CREATE TRIGGER IF NOT EXISTS embedding_calls_close_error
721
- AFTER UPDATE OF failure ON embedding_calls
722
- WHEN NEW.failure IS NOT NULL
723
- BEGIN
724
- UPDATE inference_calls
725
- SET state = 'error', completed_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
726
- WHERE id = NEW.id AND state = 'pending';
727
- END;
728
-
729
- -- Emission admission specializes one model call without re-owning its response,
730
- -- provider identity, ordering, or accounting evidence.
731
- CREATE TABLE IF NOT EXISTS turn_attempts (
732
- id INTEGER NOT NULL PRIMARY KEY,
733
- model_call_id INTEGER NOT NULL UNIQUE,
734
- accepted INTEGER CHECK (accepted IS NULL OR accepted IN (0, 1)),
735
- parse_errors TEXT NOT NULL DEFAULT '[]' CHECK (
736
- json_valid(parse_errors) AND json_type(parse_errors) = 'array'
737
- ),
738
- FOREIGN KEY (model_call_id) REFERENCES model_calls(id) ON DELETE CASCADE
739
- ) STRICT;
740
-
741
- CREATE TRIGGER IF NOT EXISTS turn_attempts_request_identity_immutable
742
- BEFORE UPDATE OF model_call_id ON turn_attempts
743
- BEGIN
744
- SELECT RAISE(ABORT, 'emission attempt identity is immutable');
745
- END;
746
-
747
- CREATE TRIGGER IF NOT EXISTS turn_attempts_emission_call_only
748
- BEFORE INSERT ON turn_attempts
749
- WHEN COALESCE((SELECT kind = 'emission' FROM inference_calls WHERE id = NEW.model_call_id), 0) != 1
750
- BEGIN
751
- SELECT RAISE(ABORT, 'turn attempt requires an emission model call');
752
- END;
753
-
754
- CREATE TRIGGER IF NOT EXISTS turn_attempts_classification_after_response
755
- BEFORE UPDATE OF accepted, parse_errors ON turn_attempts
756
- WHEN COALESCE((SELECT state = 'response' FROM inference_calls WHERE id = OLD.model_call_id), 0) != 1
757
- BEGIN
758
- SELECT RAISE(ABORT, 'emission classification requires model response evidence');
759
- END;
760
-
761
- CREATE TRIGGER IF NOT EXISTS turn_attempts_classification_once
762
- BEFORE UPDATE OF accepted, parse_errors
763
- ON turn_attempts
764
- WHEN OLD.accepted IS NOT NULL
765
- BEGIN
766
- SELECT RAISE(ABORT, 'emission attempt classification is immutable');
767
- END;
768
-
769
- CREATE TRIGGER IF NOT EXISTS turn_attempts_one_accepted_insert
770
- BEFORE INSERT ON turn_attempts
771
- WHEN NEW.accepted = 1 AND EXISTS (
772
- SELECT 1
773
- FROM turn_attempts existing
774
- JOIN inference_calls old_call ON old_call.id = existing.model_call_id
775
- JOIN inference_calls new_call ON new_call.id = NEW.model_call_id
776
- WHERE old_call.turn_id = new_call.turn_id AND existing.accepted = 1
777
- )
778
- BEGIN
779
- SELECT RAISE(ABORT, 'one emission may be accepted per turn');
780
- END;
781
-
782
- CREATE TRIGGER IF NOT EXISTS turn_attempts_one_accepted_update
783
- BEFORE UPDATE OF accepted ON turn_attempts
784
- WHEN NEW.accepted = 1 AND EXISTS (
785
- SELECT 1
786
- FROM turn_attempts existing
787
- JOIN inference_calls old_call ON old_call.id = existing.model_call_id
788
- JOIN inference_calls new_call ON new_call.id = NEW.model_call_id
789
- WHERE old_call.turn_id = new_call.turn_id
790
- AND existing.id != OLD.id
791
- AND existing.accepted = 1
792
- )
793
- BEGIN
794
- SELECT RAISE(ABORT, 'one emission may be accepted per turn');
795
- END;
796
-
797
- -- {§provider-request-accounting}: one row is opened before one physical
798
- -- provider request and settled exactly once with the evidence from that request.
799
- -- Monetary values remain canonical decimal strings; SQLite REAL is never an
800
- -- accounting representation.
801
- CREATE TABLE IF NOT EXISTS provider_requests (
802
- id INTEGER NOT NULL PRIMARY KEY,
803
- inference_call_id INTEGER NOT NULL,
804
- sequence INTEGER NOT NULL CHECK (sequence >= 1),
805
- provider TEXT NOT NULL CHECK (length(provider) > 0),
806
- model TEXT NOT NULL CHECK (length(model) > 0),
807
- state TEXT NOT NULL DEFAULT 'pending' CHECK (state IN ('pending', 'settled')),
808
- outcome TEXT CHECK (outcome IN ('response', 'error')),
809
- status INTEGER CHECK (status IS NULL OR status BETWEEN 100 AND 599),
810
- usage_input INTEGER CHECK (usage_input IS NULL OR usage_input >= 0),
811
- usage_output INTEGER CHECK (usage_output IS NULL OR usage_output >= 0),
812
- usage_total INTEGER CHECK (usage_total IS NULL OR usage_total >= 0),
813
- usage_input_no_cache INTEGER CHECK (usage_input_no_cache IS NULL OR usage_input_no_cache >= 0),
814
- usage_input_cache_read INTEGER CHECK (usage_input_cache_read IS NULL OR usage_input_cache_read >= 0),
815
- usage_input_cache_write INTEGER CHECK (usage_input_cache_write IS NULL OR usage_input_cache_write >= 0),
816
- usage_output_text INTEGER CHECK (usage_output_text IS NULL OR usage_output_text >= 0),
817
- usage_output_reasoning INTEGER CHECK (usage_output_reasoning IS NULL OR usage_output_reasoning >= 0),
818
- cost_kind TEXT CHECK (cost_kind IN ('charged', 'estimated', 'unknown')),
819
- cost_amount TEXT,
820
- cost_currency TEXT,
821
- cost_usd_equivalent TEXT,
822
- cost_source TEXT,
823
- cost_reason TEXT,
824
- started_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
825
- completed_at TEXT,
826
- CHECK (
827
- (state = 'pending'
828
- AND outcome IS NULL AND status IS NULL
829
- AND usage_input IS NULL AND usage_output IS NULL AND usage_total IS NULL
830
- AND usage_input_no_cache IS NULL AND usage_input_cache_read IS NULL
831
- AND usage_input_cache_write IS NULL AND usage_output_text IS NULL
832
- AND usage_output_reasoning IS NULL
833
- AND cost_kind IS NULL AND cost_amount IS NULL AND cost_currency IS NULL
834
- AND cost_usd_equivalent IS NULL AND cost_source IS NULL AND cost_reason IS NULL
835
- AND completed_at IS NULL)
836
- OR
837
- (state = 'settled' AND outcome IS NOT NULL AND cost_kind IS NOT NULL AND completed_at IS NOT NULL)
838
- ),
839
- CHECK (
840
- cost_amount IS NULL OR (
841
- length(cost_amount) > 0
842
- AND cost_amount NOT GLOB '*[^0-9.]*'
843
- AND cost_amount NOT LIKE '.%'
844
- AND cost_amount NOT LIKE '%.'
845
- AND cost_amount NOT LIKE '%.%.%'
846
- AND (cost_amount = '0' OR cost_amount NOT GLOB '0[0-9]*')
847
- )
848
- ),
849
- CHECK (
850
- cost_usd_equivalent IS NULL OR (
851
- length(cost_usd_equivalent) > 0
852
- AND cost_usd_equivalent NOT GLOB '*[^0-9.]*'
853
- AND cost_usd_equivalent NOT LIKE '.%'
854
- AND cost_usd_equivalent NOT LIKE '%.'
855
- AND cost_usd_equivalent NOT LIKE '%.%.%'
856
- AND (cost_usd_equivalent = '0' OR cost_usd_equivalent NOT GLOB '0[0-9]*')
857
- )
858
- ),
859
- CHECK (
860
- cost_currency IS NULL OR (
861
- length(cost_currency) BETWEEN 3 AND 12
862
- AND substr(cost_currency, 1, 1) GLOB '[A-Z]'
863
- AND cost_currency NOT GLOB '*[^A-Z0-9]*'
864
- )
865
- ),
866
- CHECK (
867
- cost_kind IS NULL
868
- OR (cost_kind = 'charged'
869
- AND cost_amount IS NOT NULL AND cost_currency IS NOT NULL
870
- AND cost_source IS NOT NULL AND length(cost_source) > 0
871
- AND cost_reason IS NULL)
872
- OR (cost_kind = 'estimated'
873
- AND cost_amount IS NOT NULL AND cost_currency IS NOT NULL
874
- AND cost_usd_equivalent IS NULL
875
- AND cost_source IS NOT NULL AND length(cost_source) > 0
876
- AND cost_reason IS NULL)
877
- OR (cost_kind = 'unknown'
878
- AND cost_amount IS NULL AND cost_currency IS NULL
879
- AND cost_usd_equivalent IS NULL AND cost_source IS NULL
880
- AND cost_reason IS NOT NULL AND length(cost_reason) > 0)
881
- ),
882
- UNIQUE (inference_call_id, sequence),
883
- FOREIGN KEY (inference_call_id) REFERENCES inference_calls(id) ON DELETE CASCADE
884
- ) STRICT;
885
-
886
- CREATE TRIGGER IF NOT EXISTS provider_requests_pending_inference_only
887
- BEFORE INSERT ON provider_requests
888
- WHEN COALESCE((SELECT state = 'pending' FROM inference_calls WHERE id = NEW.inference_call_id), 0) != 1
889
- OR COALESCE((
890
- SELECT kind NOT IN ('embedding_query', 'embedding_documents') OR ec.input_count IS NOT NULL
891
- FROM inference_calls ic
892
- LEFT JOIN embedding_calls ec ON ec.id = ic.id
893
- WHERE ic.id = NEW.inference_call_id
894
- ), 0) != 1
895
- BEGIN
896
- SELECT RAISE(ABORT, 'provider request requires a prepared pending inference call');
897
- END;
898
-
899
- CREATE TRIGGER IF NOT EXISTS provider_requests_identity_immutable
900
- BEFORE UPDATE OF inference_call_id, sequence, provider, model, started_at
901
- ON provider_requests
902
- BEGIN
903
- SELECT RAISE(ABORT, 'provider request identity is immutable');
904
- END;
905
-
906
- CREATE INDEX IF NOT EXISTS provider_requests_inference_call_id
907
- ON provider_requests (inference_call_id, sequence);
908
-
909
- CREATE TRIGGER IF NOT EXISTS provider_requests_state_forward_only
910
- BEFORE UPDATE OF state ON provider_requests
911
- WHEN NOT (NEW.state = OLD.state OR (OLD.state = 'pending' AND NEW.state = 'settled'))
912
- BEGIN
913
- SELECT RAISE(ABORT, 'provider request may only settle once');
914
- END;
915
-
916
- CREATE TRIGGER IF NOT EXISTS provider_requests_settlement_immutable
917
- BEFORE UPDATE OF outcome, status,
918
- usage_input, usage_output, usage_total,
919
- usage_input_no_cache, usage_input_cache_read, usage_input_cache_write,
920
- usage_output_text, usage_output_reasoning,
921
- cost_kind, cost_amount, cost_currency, cost_usd_equivalent,
922
- cost_source, cost_reason, completed_at
923
- ON provider_requests
924
- WHEN OLD.state != 'pending'
925
- BEGIN
926
- SELECT RAISE(ABORT, 'provider request settlement is immutable');
927
- END;
928
-
929
- -- derivations
930
- -- Content-addressed deep projections. Entry channels and log projections point
931
- -- at a COMPLETE artifact by deep_hash; graph, FTS, and vectors are stored once
932
- -- regardless of how many addresses carry identical content under the same reader/config.
933
- -- A building row is unattached and safely replaceable after interruption.
934
- CREATE TABLE IF NOT EXISTS derivations (
935
- id INTEGER NOT NULL PRIMARY KEY,
936
- deep_hash TEXT NOT NULL UNIQUE CHECK (length(deep_hash) > 0),
937
- state TEXT NOT NULL DEFAULT 'building' CHECK (state IN ('building', 'complete')),
938
- disposition TEXT CHECK (disposition IN ('vector', 'lexical', 'excluded', 'nonsemantic', 'failed')),
939
- reason TEXT,
940
- parse_issues INTEGER CHECK (parse_issues IS NULL OR parse_issues > 0),
941
- summary TEXT CHECK (
942
- summary IS NULL OR (
943
- length(summary) > 0
944
- AND summary = trim(summary)
945
- AND instr(summary, char(10)) = 0
946
- AND instr(summary, char(13)) = 0
947
- )
948
- ),
949
- CHECK ((state = 'building' AND disposition IS NULL) OR (state = 'complete' AND disposition IS NOT NULL))
950
- ) STRICT;
951
-
952
- -- entries
953
- -- The canonical addressable store. (owner, scheme, authority, pathname) is the identity
954
- -- tuple, and NO component may be NULL: NULLs are distinct under SQL UNIQUE, so a nullable
955
- -- component voids the identity index. Bare/file paths persist under the reserved `file`
956
- -- scheme; they still render as bare paths. {§entry-identity-no-null}
957
- CREATE TABLE IF NOT EXISTS entries (
958
- id INTEGER NOT NULL PRIMARY KEY,
959
- version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
960
- scheme TEXT NOT NULL CHECK (length(scheme) > 0),
961
- -- Canonical RFC authority for resource-addressed schemes. Namespace schemes
962
- -- fold authored authority into pathname; owner-addressed schemes consume it
963
- -- into owner_id. Empty is therefore the canonical no-resource-authority value.
964
- authority TEXT NOT NULL DEFAULT '',
965
- pathname TEXT NOT NULL,
966
- -- {§entry-owner} — every entry is owned by a worker: the spawning worker for capability
967
- -- streams, the workspace's reserved 'commons' worker for shared content. A real row, never
968
- -- NULL (NULLs are distinct under UNIQUE — a nullable owner would let the commons fragment).
969
- -- The id never renders into a URI or packet; the model addresses owners by NAME (authority).
970
- owner_id INTEGER NOT NULL,
971
- -- Entry-private metadata. Prompt frames use `openPaths` to carry selected
972
- -- workspace paths into the exact turn that publishes the frame
973
- -- ({§methods-loop-run-open-paths}).
974
- attributes TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(attributes)),
975
- -- SPEC {§membership} — how a file member entered the curated surface. 'git' rows are
976
- -- reconciled against the repo's members each turn — tracked ls-files PLUS untracked-
977
- -- but-not-ignored files ({§membership-auto-add}) — registered + un-registered so entries
978
- -- == members; 'constraint' rows are reconciled from ordinary pick policy.
979
- -- NULL = not a file member (other schemes don't carry origin).
980
- membership_origin TEXT CHECK (membership_origin IS NULL OR membership_origin IN ('git', 'constraint')),
981
- -- SPEC {§membership-change-gated-sync} — the per-member sync stat-detect:
982
- -- "<mtimeMs>:<size>" of the disk file at its last materialization, or `absent`
983
- -- after an observed deletion. The pre-turn
984
- -- sync stat()s every member but re-reads/re-tokenizes/rewrites only one whose
985
- -- signature changed; an unchanged member is a no-op. NULL = never synced.
986
- synced_sig TEXT,
987
- -- User Note 5 — manifest cache-friendliness. Last-modified stamp, bumped on every
988
- -- addressable representation change; engine_list_workspace_entries orders the catalog
989
- -- by it ASC so dormant entries hold the stable prompt-cache prefix. Private derivation
990
- -- attachment does not make an entry recently touched.
991
- updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
992
- FOREIGN KEY (owner_id) REFERENCES workers(id) ON DELETE CASCADE
993
- ) STRICT;
994
-
995
- -- {§entry-owner} / {§stream-owner-scoped} — owner is the sole visibility and identity axis.
996
- -- Concurrent workers' capability streams share the loop-relative coordinate (every worker's first
997
- -- loop is seq 1), so identity keys on the owner and identical coordinates are distinct rows
998
- -- ({§stream-owner-scoped}).
999
- CREATE UNIQUE INDEX IF NOT EXISTS entries_identity ON entries (owner_id, scheme, authority, pathname);
1000
- CREATE UNIQUE INDEX IF NOT EXISTS entries_id_owner ON entries (id, owner_id);
1001
-
1002
- -- The ONE engine-imposed constraint (SPEC {§stream-constraints}, {§stream-constraints-engine-one-cap}): 100 MiB char-length cap
1003
- -- per channel content body. All other limits are extrinsic.
1004
- CREATE TABLE IF NOT EXISTS entry_channels (
1005
- entry_id INTEGER NOT NULL,
1006
- name TEXT NOT NULL CHECK (length(name) > 0),
1007
- content TEXT NOT NULL CHECK (length(content) <= 104857600),
1008
- mimetype TEXT NOT NULL CHECK (length(mimetype) > 0),
1009
- weight INTEGER NOT NULL DEFAULT 0 CHECK (weight >= 0),
1010
- -- Content identity: sha256 of content, stamped at static writes; streamed
1011
- -- appends leave it NULL. Curation weight remains model-independent.
1012
- content_hash TEXT,
1013
- -- Search derivation for this exact addressable channel representation.
1014
- -- Content or mimetype changes invalidate it at the owning channel row.
1015
- deep_hash TEXT,
1016
- state TEXT NOT NULL DEFAULT 'static' CHECK (state IN ('static', 'active', 'closed', 'errored')),
1017
- -- Exact terminal producer evidence for this representation channel. NULL is
1018
- -- the ordinary implicit {status:200}; selection/projection metadata belongs
1019
- -- to core and is never stored here.
1020
- producer_result TEXT,
1021
- CONSTRAINT entry_channel_producer_result_contract CHECK (
1022
- CASE
1023
- WHEN producer_result IS NULL THEN 1
1024
- WHEN NOT json_valid(producer_result) THEN 0
1025
- ELSE
1026
- json_type(producer_result) IS 'object'
1027
- AND json_type(producer_result, '$.status') IS 'integer'
1028
- AND json_extract(producer_result, '$.status') BETWEEN 200 AND 599
1029
- AND json_extract(producer_result, '$.status') != 202
1030
- AND CASE
1031
- WHEN json_extract(producer_result, '$.status') < 400 THEN
1032
- json_type(producer_result, '$.problem') IS NULL
1033
- ELSE
1034
- json_type(producer_result, '$.problem') IS 'object'
1035
- AND json_type(producer_result, '$.problem.status') IS 'integer'
1036
- AND json_extract(producer_result, '$.problem.status')
1037
- = json_extract(producer_result, '$.status')
1038
- AND json_type(producer_result, '$.problem.type') IS 'text'
1039
- AND length(json_extract(producer_result, '$.problem.type')) > 0
1040
- AND json_type(producer_result, '$.problem.title') IS 'text'
1041
- AND length(json_extract(producer_result, '$.problem.title')) > 0
1042
- AND json_type(producer_result, '$.problem.detail') IS 'text'
1043
- AND length(json_extract(producer_result, '$.problem.detail')) > 0
1044
- END
1045
- END
1046
- ),
1047
- PRIMARY KEY (entry_id, name),
1048
- FOREIGN KEY (entry_id) REFERENCES entries(id) ON DELETE CASCADE,
1049
- FOREIGN KEY (deep_hash) REFERENCES derivations(deep_hash)
1050
- ) STRICT, WITHOUT ROWID;
1051
-
1052
- -- A changed channel representation cannot retain search evidence derived from
1053
- -- its predecessor. This trigger is the one invalidation owner for every write
1054
- -- path, including model EDIT, plugin channel capabilities, and streams.
1055
- CREATE TRIGGER IF NOT EXISTS entry_channels_invalidate_derivation
1056
- AFTER UPDATE OF content, mimetype ON entry_channels
1057
- WHEN OLD.content IS NOT NEW.content OR OLD.mimetype IS NOT NEW.mimetype
1058
- BEGIN
1059
- UPDATE entry_channels
1060
- SET deep_hash = NULL
1061
- WHERE entry_id = NEW.entry_id AND name = NEW.name AND deep_hash IS NOT NULL;
1062
- END;
1063
-
1064
- -- User Note 5 — bump the entry's updated_at on addressable representation or
1065
- -- lifecycle writes so the catalog (ordered by updated_at ASC) keeps recently-
1066
- -- touched entries at the tail and holds the prompt-cache prefix stable across
1067
- -- turns. Content hashes and search attachments are private metadata, not touches.
1068
- CREATE TRIGGER IF NOT EXISTS entries_touch_on_channel_write
1069
- AFTER INSERT ON entry_channels
1070
- BEGIN
1071
- UPDATE entries SET updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = NEW.entry_id;
1072
- END;
1073
-
1074
- CREATE TRIGGER IF NOT EXISTS entries_touch_on_channel_update
1075
- AFTER UPDATE OF content, mimetype, weight, state, producer_result ON entry_channels
1076
- BEGIN
1077
- UPDATE entries SET updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = NEW.entry_id;
1078
- END;
1079
-
1080
- -- symbol_defs
1081
- -- &graph NODES ({§relation-indexed-dialects}). Code symbol definitions, populated
1082
- -- once per content-addressed derivation from mimetypes'
1083
- -- `symbols` channel. Qualified path = container ? container || '.' || name : name.
1084
- CREATE TABLE IF NOT EXISTS symbol_defs (
1085
- id INTEGER NOT NULL PRIMARY KEY,
1086
- derivation_id INTEGER NOT NULL,
1087
- name TEXT NOT NULL CHECK (length(name) > 0),
1088
- kind TEXT NOT NULL,
1089
- container TEXT,
1090
- line INTEGER NOT NULL,
1091
- end_line INTEGER,
1092
- FOREIGN KEY (derivation_id) REFERENCES derivations(id) ON DELETE CASCADE
1093
- ) STRICT;
1094
-
1095
- CREATE INDEX IF NOT EXISTS symbol_defs_name ON symbol_defs (name);
1096
-
1097
- -- symbol_refs
1098
- -- &graph EDGES ({§relation-indexed-dialects}), from mimetypes' `references` channel.
1099
- -- name = edge TARGET; container = the SOURCE def's full qualified path (the
1100
- -- &> join key; module-level → NULL); kind ∈ import|call|instantiate|inherit|
1101
- -- type|use (frozen, edge metadata only — traversal is kind-agnostic).
1102
- CREATE TABLE IF NOT EXISTS symbol_refs (
1103
- id INTEGER NOT NULL PRIMARY KEY,
1104
- derivation_id INTEGER NOT NULL,
1105
- name TEXT NOT NULL CHECK (length(name) > 0),
1106
- kind TEXT NOT NULL,
1107
- container TEXT,
1108
- line INTEGER NOT NULL,
1109
- col INTEGER,
1110
- FOREIGN KEY (derivation_id) REFERENCES derivations(id) ON DELETE CASCADE
1111
- ) STRICT;
1112
-
1113
- CREATE INDEX IF NOT EXISTS symbol_refs_name ON symbol_refs (name);
1114
- CREATE INDEX IF NOT EXISTS symbol_refs_source ON symbol_refs (derivation_id, container);
1115
-
1116
- -- derivation_fts (~semantic keyword fallback; {§relation-indexed-dialects})
1117
- -- Keyword/content index over a derivation's readable content; rowid IS derivations.id.
1118
- -- Explicit keyword fallback when no embedder is installed. Vector search never
1119
- -- consults this table: semantic recall is exhaustive over complete vectors.
1120
- CREATE VIRTUAL TABLE IF NOT EXISTS derivation_fts USING fts5(content);
1121
-
1122
- -- derivation_embeddings (~semantic vectors; {§relation-indexed-dialects}).
1123
- -- One canonical wire vector ({§mimetype-embedding-wire}) per CHUNK: a derivation tiles into N chunks,
1124
- -- each addressed by its <L> line range (line_start..line_end) and embedded
1125
- -- separately, so a large body is fully searchable instead of truncated at the
1126
- -- embedder's window. line_start/line_end are stored for Project Findings to expose;
1127
- -- the rank currently max-pools a derivation's chunks, then projects every attached
1128
- -- pathname. semantic_rank exhaustively cosine-ranks these.
1129
- -- CASCADE-deleted with the derivation artifact.
1130
- CREATE TABLE IF NOT EXISTS derivation_embeddings (
1131
- derivation_id INTEGER NOT NULL,
1132
- chunk_seq INTEGER NOT NULL,
1133
- line_start INTEGER NOT NULL,
1134
- line_end INTEGER NOT NULL,
1135
- vector BLOB NOT NULL,
1136
- -- The model id that produced this vector (mimetypes' `embeddingModel`). Stored
1137
- -- per row as the dimension/staleness guard: rank filters by the current model so
1138
- -- a swap never cosine-compares mismatched dimensions.
1139
- embedding_model TEXT NOT NULL,
1140
- PRIMARY KEY (derivation_id, chunk_seq),
1141
- FOREIGN KEY (derivation_id) REFERENCES derivations(id) ON DELETE CASCADE
1142
- ) STRICT;
1143
-
1144
- -- log_entries
1145
- -- Chronological event store. sequence is 1-based, scoped to the turn —
1146
- -- resets at each new turn. URI-bit columns are unprefixed (scheme,
1147
- -- pathname, …). state/outcome/attrs carry the proposal lifecycle —
1148
- -- status⊥state: status is the HTTP outcome, state is where in the
1149
- -- lifecycle the entry sits. Most rows write 'resolved' directly;
1150
- -- proposing schemes transition 'proposed' → resolved/failed/cancelled.
1151
- -- initial_folded is the immutable visibility with which the event entered the
1152
- -- log. Current model-facing membership and folded intervals belong to
1153
- -- log_entry_projections below; curation never rewrites or removes this event.
1154
- CREATE TABLE IF NOT EXISTS log_entries (
1155
- id INTEGER NOT NULL PRIMARY KEY,
1156
- version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
1157
-
1158
- worker_id INTEGER NOT NULL,
1159
- loop_id INTEGER NOT NULL,
1160
- turn_id INTEGER NOT NULL,
1161
- sequence INTEGER NOT NULL CHECK (sequence >= 1),
1162
- at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
1163
- origin TEXT NOT NULL CHECK (origin IN ('model', 'client', '_plurnk', 'plugin')),
1164
- -- {§env-delta-attribution}: a causal worker:// identity, log:/// operation
1165
- -- address, or stable subsystem token ('file'); NULL = the owning worker
1166
- -- itself, rendered without causal attribution.
1167
- source TEXT,
1168
- -- Engine-owned occurrence identity. Source rows are stamped NULL→id by the
1169
- -- journal trigger; observer and fork copies carry it at insertion.
1170
- ambient_event_id INTEGER REFERENCES ambient_events(id),
1171
- -- Fork history is copied evidence, never new activity. Rows with no source
1172
- -- occurrence still need this structural guard against republication.
1173
- inherited_history INTEGER NOT NULL DEFAULT 0 CHECK (inherited_history IN (0, 1)),
1174
- -- Search derivation attached to this durable log result, when available.
1175
- deep_hash TEXT REFERENCES derivations(deep_hash),
1176
- -- Exact logical provider call represented by a BARE result, admitted
1177
- -- model turnOps, or rejected emissionAttempt. Other operation and ambient
1178
- -- rows carry no model-call identity.
1179
- model_call_id INTEGER REFERENCES model_calls(id),
1180
- -- Engine-owned stream publication that produced this observation. The
1181
- -- publication cursor survives log curation; this relation is evidence,
1182
- -- never the acknowledgement itself. {§exec-stream}
1183
- subscription_publication_id INTEGER REFERENCES subscription_publications(id) ON DELETE SET NULL,
1184
-
1185
- -- 'error' is an ACTIONLESS row ({§operation-results} — errors are log items): a parse failure that
1186
- -- produced no op still records a log entry (op='error', status_rx≥400, no target) so the model
1187
- -- can fold/kill/recall its own mistakes like any other log row — one budget surface, the log.
1188
- -- Exact producer source and rejected-attempt artifacts carry NULL here
1189
- -- ({§turn-ops-entry}, {§rejected-emission-entry}).
1190
- -- No op enum here: the grammar op set is grammar's contract (PlurnkOp), and this column is written
1191
- -- only by the PlurnkOp-typed engine (grammar ops), service row selectors, or NULL for no op.
1192
- -- A SQL enum would be a hand-copy of grammar's op list that silently goes stale on every new verb
1193
- -- (it did — FORK/WORK). Validity lives at the parse + type layer, not duplicated in DDL.
1194
- op TEXT,
1195
- delimiter TEXT NOT NULL DEFAULT '',
1196
- signal TEXT CHECK (signal IS NULL OR json_valid(signal)),
1197
-
1198
- scheme TEXT CHECK (scheme IS NULL OR length(scheme) > 0),
1199
- username TEXT,
1200
- password TEXT,
1201
- hostname TEXT,
1202
- port INTEGER CHECK (port IS NULL OR (port BETWEEN 0 AND 65535)),
1203
- pathname TEXT,
1204
- -- Serialized query without '?'; NULL = absent, '' = explicit empty. {§path-query}
1205
- query TEXT,
1206
- fragment TEXT,
1207
-
1208
- lineMarker TEXT CHECK (lineMarker IS NULL OR json_valid(lineMarker)),
1209
-
1210
- tx TEXT NOT NULL,
1211
- mimetype_tx TEXT NOT NULL CHECK (length(mimetype_tx) > 0),
1212
-
1213
- rx TEXT NOT NULL,
1214
- mimetype_rx TEXT NOT NULL CHECK (length(mimetype_rx) > 0),
1215
- status_rx INTEGER NOT NULL CHECK (status_rx BETWEEN 100 AND 599),
1216
-
1217
- -- Complete canonical LogBody content before coordinate/presentation
1218
- -- projection; persistence envelopes do not contribute. {§tokenomics-weight-stored-at-write}
1219
- weight INTEGER NOT NULL DEFAULT 0 CHECK (weight >= 0),
1220
-
1221
- state TEXT NOT NULL DEFAULT 'resolved'
1222
- CHECK (state IN ('proposed', 'resolved', 'failed', 'cancelled')),
1223
- outcome TEXT,
1224
- attrs TEXT NOT NULL DEFAULT '{}' CHECK (json_valid(attrs)),
1225
-
1226
- initial_folded TEXT NOT NULL DEFAULT '[]'
1227
- CHECK (json_valid(initial_folded) AND json_type(initial_folded) = 'array'),
1228
-
1229
- CHECK (
1230
- (op IS NULL) = COALESCE(
1231
- json_extract(attrs, '$.kind') IN ('turnOps', 'emissionAttempt'),
1232
- 0
1233
- )
1234
- ),
1235
- CHECK (json_extract(attrs, '$.kind') != 'emissionAttempt' OR origin = 'model'),
1236
-
1237
- FOREIGN KEY (worker_id) REFERENCES workers(id) ON DELETE CASCADE,
1238
- FOREIGN KEY (loop_id) REFERENCES loops(id) ON DELETE CASCADE,
1239
- FOREIGN KEY (turn_id) REFERENCES turns(id) ON DELETE CASCADE
1240
- ) STRICT;
1241
-
1242
- CREATE UNIQUE INDEX IF NOT EXISTS log_entries_turn_id_sequence ON log_entries (turn_id, sequence);
1243
- CREATE INDEX IF NOT EXISTS log_entries_worker_id ON log_entries (worker_id);
1244
- CREATE INDEX IF NOT EXISTS log_entries_loop_id ON log_entries (loop_id);
1245
- CREATE INDEX IF NOT EXISTS log_entries_at ON log_entries (at);
1246
- CREATE UNIQUE INDEX IF NOT EXISTS log_entries_model_call_id
1247
- ON log_entries (model_call_id)
1248
- WHERE model_call_id IS NOT NULL;
1249
- CREATE INDEX IF NOT EXISTS log_entries_subscription_publication_id
1250
- ON log_entries (subscription_publication_id)
1251
- WHERE subscription_publication_id IS NOT NULL;
1252
- CREATE UNIQUE INDEX IF NOT EXISTS log_entries_worker_ambient_event
1253
- ON log_entries (worker_id, ambient_event_id)
1254
- WHERE ambient_event_id IS NOT NULL;
1255
-
1256
- -- {§log-history-projection} — log_entries is append-only execution evidence;
1257
- -- this one-to-one row is the current model-facing projection of that evidence.
1258
- -- KILL is a terminal active→inactive transition. Scoped KILL mutates only folded
1259
- -- intervals while active. The initial state is created in the same statement as
1260
- -- its event so no durable row can exist without one projection state.
1261
- CREATE TABLE IF NOT EXISTS log_entry_projections (
1262
- log_entry_id INTEGER NOT NULL PRIMARY KEY,
1263
- active INTEGER NOT NULL DEFAULT 1 CHECK (active IN (0, 1)),
1264
- folded TEXT NOT NULL DEFAULT '[]'
1265
- CHECK (json_valid(folded) AND json_type(folded) = 'array'),
1266
- FOREIGN KEY (log_entry_id) REFERENCES log_entries(id) ON DELETE CASCADE
1267
- ) STRICT, WITHOUT ROWID;
1268
-
1269
- -- The ordinary operation surface reads this view. Forensic and lifecycle
1270
- -- machinery names log_entries directly and therefore retains complete history.
1271
- CREATE VIEW IF NOT EXISTS active_log_entries AS
1272
- SELECT le.*, projection.folded
1273
- FROM log_entries le
1274
- JOIN log_entry_projections projection ON projection.log_entry_id = le.id
1275
- WHERE projection.active = 1;
1276
-
1277
- -- {§packet-attachment-parts} — absence means an attachable READ may still
1278
- -- contribute native content. Presence is the append-only fact that a completed
1279
- -- model response crossed that one-shot delivery boundary. This lifecycle is not
1280
- -- part of the log-curation projection.
1281
- CREATE TABLE IF NOT EXISTS native_content_deliveries (
1282
- log_entry_id INTEGER NOT NULL PRIMARY KEY,
1283
- delivered_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
1284
- FOREIGN KEY (log_entry_id) REFERENCES log_entries(id) ON DELETE CASCADE
1285
- ) STRICT, WITHOUT ROWID;
1286
-
1287
- CREATE TRIGGER IF NOT EXISTS model_calls_native_inputs_valid
1288
- BEFORE UPDATE OF native_inputs ON model_calls
1289
- WHEN (
1290
- SELECT COUNT(*) != COUNT(DISTINCT value)
1291
- OR SUM(CASE WHEN type != 'text' OR length(value) = 0 THEN 1 ELSE 0 END) > 0
1292
- FROM json_each(NEW.native_inputs)
1293
- )
1294
- OR EXISTS (
1295
- SELECT 1
1296
- FROM json_each(NEW.native_inputs) native
1297
- WHERE NOT EXISTS (
1298
- SELECT 1
1299
- FROM inference_calls call
1300
- JOIN turns request_turn ON request_turn.id = call.turn_id
1301
- JOIN loops request_loop ON request_loop.id = request_turn.loop_id
1302
- JOIN log_entries entry ON entry.worker_id = request_loop.worker_id
1303
- JOIN turns entry_turn ON entry_turn.id = entry.turn_id
1304
- JOIN loops entry_loop ON entry_loop.id = entry_turn.loop_id
1305
- WHERE call.id = NEW.id
1306
- AND entry.op = 'READ'
1307
- AND entry.status_rx < 400
1308
- AND (entry_loop.sequence || '/' || entry_turn.sequence || '/' || entry.sequence) = native.value
1309
- )
1310
- )
1311
- BEGIN
1312
- SELECT RAISE(ABORT, 'model call native inputs must be unique successful READ coordinates owned by its worker');
1313
- END;
1314
-
1315
- -- Closing a model call with response evidence is the native-content delivery
1316
- -- boundary. Both facts commit in the same SQLite statement; a call that ends
1317
- -- without a response cannot consume native content.
1318
- CREATE TRIGGER IF NOT EXISTS native_content_deliveries_from_model_response
1319
- AFTER UPDATE OF response, native_inputs ON model_calls
1320
- WHEN NEW.response IS NOT NULL
1321
- BEGIN
1322
- INSERT INTO native_content_deliveries (log_entry_id)
1323
- SELECT entry.id
1324
- FROM json_each(NEW.native_inputs) native
1325
- JOIN inference_calls call ON call.id = NEW.id
1326
- JOIN turns request_turn ON request_turn.id = call.turn_id
1327
- JOIN loops request_loop ON request_loop.id = request_turn.loop_id
1328
- JOIN log_entries entry ON entry.worker_id = request_loop.worker_id
1329
- JOIN turns entry_turn ON entry_turn.id = entry.turn_id
1330
- JOIN loops entry_loop ON entry_loop.id = entry_turn.loop_id
1331
- WHERE (entry_loop.sequence || '/' || entry_turn.sequence || '/' || entry.sequence) = native.value
1332
- ON CONFLICT (log_entry_id) DO NOTHING;
1333
- END;
1334
-
1335
- CREATE TRIGGER IF NOT EXISTS native_content_deliveries_immutable
1336
- BEFORE UPDATE ON native_content_deliveries
1337
- BEGIN
1338
- SELECT RAISE(ABORT, 'native-content delivery evidence is immutable');
1339
- END;
1340
-
1341
- CREATE TRIGGER IF NOT EXISTS log_entries_initialize_projection
1342
- AFTER INSERT ON log_entries
1343
- BEGIN
1344
- INSERT INTO log_entry_projections (log_entry_id, active, folded)
1345
- VALUES (NEW.id, 1, NEW.initial_folded);
1346
- END;
1347
-
1348
- -- Individual execution events are append-only. Containing-history teardown is
1349
- -- the one removal owner: a cascading delete has already removed at least one
1350
- -- ancestor in the workspace→worker→loop→turn chain. A direct row delete while
1351
- -- that complete owner chain remains would fabricate chronology and is rejected.
1352
- CREATE TRIGGER IF NOT EXISTS log_entries_delete_with_owner_only
1353
- BEFORE DELETE ON log_entries
1354
- WHEN EXISTS (
1355
- SELECT 1
1356
- FROM turns
1357
- JOIN loops ON loops.id = turns.loop_id
1358
- JOIN workers ON workers.id = loops.worker_id
1359
- JOIN workspaces ON workspaces.id = workers.workspace_id
1360
- WHERE turns.id = OLD.turn_id
1361
- )
1362
- BEGIN
1363
- SELECT RAISE(ABORT, 'log entries can only be removed with their containing history');
1364
- END;
1365
-
1366
- -- A log row belongs to one exact worker/loop/turn chain. Its writer is either
1367
- -- that turn's producer or `_plurnk` observing the turn. Primitive absence,
1368
- -- enum, and foreign-key failures remain owned by their column constraints.
1369
- CREATE TRIGGER IF NOT EXISTS log_entries_turn_ownership
1370
- BEFORE INSERT ON log_entries
1371
- WHEN NEW.worker_id IS NOT NULL
1372
- AND NEW.loop_id IS NOT NULL
1373
- AND NEW.turn_id IS NOT NULL
1374
- AND NEW.origin IN ('model', 'client', '_plurnk', 'plugin')
1375
- AND EXISTS (SELECT 1 FROM workers WHERE id = NEW.worker_id)
1376
- AND EXISTS (SELECT 1 FROM loops WHERE id = NEW.loop_id)
1377
- AND EXISTS (SELECT 1 FROM turns WHERE id = NEW.turn_id)
1378
- AND NOT EXISTS (
1379
- SELECT 1
1380
- FROM turns
1381
- JOIN loops ON loops.id = turns.loop_id
1382
- WHERE turns.id = NEW.turn_id
1383
- AND loops.id = NEW.loop_id
1384
- AND loops.worker_id = NEW.worker_id
1385
- AND (NEW.origin = turns.producer OR NEW.origin = '_plurnk')
1386
- )
1387
- BEGIN
1388
- SELECT RAISE(ABORT, 'log entry must match its turn ownership and producer');
1389
- END;
1390
-
1391
- -- {§turn-ops-entry} — exact admitted source is authored by the turn producer.
1392
- -- `_plurnk` may observe another producer's turn through ordinary operation
1393
- -- rows, but it cannot impersonate that producer's source program.
1394
- CREATE TRIGGER IF NOT EXISTS log_entries_turn_ops_producer
1395
- BEFORE INSERT ON log_entries
1396
- WHEN NEW.op IS NULL
1397
- AND json_extract(NEW.attrs, '$.kind') = 'turnOps'
1398
- AND EXISTS (SELECT 1 FROM turns WHERE id = NEW.turn_id)
1399
- AND NOT EXISTS (
1400
- SELECT 1
1401
- FROM turns
1402
- WHERE turns.id = NEW.turn_id
1403
- AND turns.producer = NEW.origin
1404
- )
1405
- BEGIN
1406
- SELECT RAISE(ABORT, 'actionless log entry does not match its turn producer');
1407
- END;
1408
-
1409
- -- A folded interval is an inclusive [start,end] pair. Ranges are positive,
1410
- -- sorted, disjoint, and non-adjacent; -1 is the final open-ended endpoint.
1411
- -- Canonical intervals make visibility equality and curation effects exact.
1412
- CREATE TRIGGER IF NOT EXISTS log_entry_projections_folded_valid_insert
1413
- BEFORE INSERT ON log_entry_projections
1414
- WHEN EXISTS (
1415
- SELECT 1
1416
- FROM json_each(NEW.folded) range
1417
- WHERE range.type != 'array'
1418
- OR json_array_length(range.value) != 2
1419
- OR COALESCE(json_type(range.value, '$[0]'), '') != 'integer'
1420
- OR COALESCE(json_type(range.value, '$[1]'), '') != 'integer'
1421
- OR json_extract(range.value, '$[0]') < 1
1422
- OR (
1423
- json_extract(range.value, '$[1]') != -1
1424
- AND json_extract(range.value, '$[1]') < json_extract(range.value, '$[0]')
1425
- )
1426
- OR EXISTS (
1427
- SELECT 1
1428
- FROM json_each(NEW.folded) previous
1429
- WHERE previous.key = range.key - 1
1430
- AND (
1431
- json_extract(previous.value, '$[1]') = -1
1432
- OR json_extract(range.value, '$[0]') <= json_extract(previous.value, '$[1]') + 1
1433
- )
1434
- )
1435
- )
1436
- BEGIN
1437
- SELECT RAISE(ABORT, 'log entry projection folded ranges are invalid');
1438
- END;
1439
-
1440
- CREATE TRIGGER IF NOT EXISTS log_entry_projections_folded_valid_update
1441
- BEFORE UPDATE OF folded ON log_entry_projections
1442
- WHEN EXISTS (
1443
- SELECT 1
1444
- FROM json_each(NEW.folded) range
1445
- WHERE range.type != 'array'
1446
- OR json_array_length(range.value) != 2
1447
- OR COALESCE(json_type(range.value, '$[0]'), '') != 'integer'
1448
- OR COALESCE(json_type(range.value, '$[1]'), '') != 'integer'
1449
- OR json_extract(range.value, '$[0]') < 1
1450
- OR (
1451
- json_extract(range.value, '$[1]') != -1
1452
- AND json_extract(range.value, '$[1]') < json_extract(range.value, '$[0]')
1453
- )
1454
- OR EXISTS (
1455
- SELECT 1
1456
- FROM json_each(NEW.folded) previous
1457
- WHERE previous.key = range.key - 1
1458
- AND (
1459
- json_extract(previous.value, '$[1]') = -1
1460
- OR json_extract(range.value, '$[0]') <= json_extract(previous.value, '$[1]') + 1
1461
- )
1462
- )
1463
- )
1464
- BEGIN
1465
- SELECT RAISE(ABORT, 'log entry projection folded ranges are invalid');
1466
- END;
1467
-
1468
- CREATE TRIGGER IF NOT EXISTS log_entry_projections_kill_terminal
1469
- BEFORE UPDATE OF active ON log_entry_projections
1470
- WHEN OLD.active = 0 AND NEW.active != 0
1471
- BEGIN
1472
- SELECT RAISE(ABORT, 'a killed log entry cannot re-enter the active projection');
1473
- END;
1474
-
1475
- CREATE TRIGGER IF NOT EXISTS log_entry_projections_delete_with_event_only
1476
- BEFORE DELETE ON log_entry_projections
1477
- WHEN EXISTS (SELECT 1 FROM log_entries WHERE id = OLD.log_entry_id)
1478
- BEGIN
1479
- SELECT RAISE(ABORT, 'a durable log event must retain its projection');
1480
- END;
1481
-
1482
- CREATE TRIGGER IF NOT EXISTS log_entries_model_call_valid
1483
- BEFORE INSERT ON log_entries
1484
- WHEN NEW.model_call_id IS NOT NULL
1485
- AND NOT EXISTS (
1486
- SELECT 1
1487
- FROM model_calls call
1488
- JOIN inference_calls inference ON inference.id = call.id
1489
- WHERE call.id = NEW.model_call_id
1490
- AND inference.turn_id = NEW.turn_id
1491
- AND inference.state != 'pending'
1492
- AND (
1493
- (inference.kind = 'bare' AND NEW.op = 'BARE')
1494
- OR (
1495
- inference.kind = 'emission'
1496
- AND NEW.op IS NULL
1497
- AND json_extract(NEW.attrs, '$.kind') IN ('turnOps', 'emissionAttempt')
1498
- )
1499
- )
1500
- )
1501
- BEGIN
1502
- SELECT RAISE(ABORT, 'log entry model call does not match its represented result');
1503
- END;
1504
-
1505
-
1506
-
1507
- -- Successful log-KILL rows are durable curation events even though
1508
- -- their ordinary packet projection is suppressed. Preserve the exact selected
1509
- -- set and each target's before/after projection so a broad selector never
1510
- -- collapses into the lossy fact `matched: N`.
1511
- CREATE TABLE IF NOT EXISTS log_curation_effects (
1512
- operation_log_entry_id INTEGER NOT NULL,
1513
- target_log_entry_id INTEGER NOT NULL,
1514
- active_before INTEGER NOT NULL CHECK (active_before IN (0, 1)),
1515
- active_after INTEGER NOT NULL CHECK (active_after IN (0, 1)),
1516
- folded_before TEXT NOT NULL
1517
- CHECK (json_valid(folded_before) AND json_type(folded_before) = 'array'),
1518
- folded_after TEXT NOT NULL
1519
- CHECK (json_valid(folded_after) AND json_type(folded_after) = 'array'),
1520
- PRIMARY KEY (operation_log_entry_id, target_log_entry_id),
1521
- CHECK (operation_log_entry_id != target_log_entry_id),
1522
- FOREIGN KEY (operation_log_entry_id) REFERENCES log_entries(id) ON DELETE CASCADE,
1523
- FOREIGN KEY (target_log_entry_id) REFERENCES log_entries(id) ON DELETE CASCADE
1524
- ) STRICT, WITHOUT ROWID;
1525
-
1526
- CREATE INDEX IF NOT EXISTS log_curation_effects_target
1527
- ON log_curation_effects (target_log_entry_id);
1528
-
1529
- -- Curation effects are append-only execution evidence. They may disappear
1530
- -- only when either referenced event is already being removed with its
1531
- -- containing history; direct mutation while both events survive is forbidden.
1532
- CREATE TRIGGER IF NOT EXISTS log_curation_effects_immutable
1533
- BEFORE UPDATE ON log_curation_effects
1534
- BEGIN
1535
- SELECT RAISE(ABORT, 'log curation effects are immutable execution evidence');
1536
- END;
1537
-
1538
- CREATE TRIGGER IF NOT EXISTS log_curation_effects_delete_with_history_only
1539
- BEFORE DELETE ON log_curation_effects
1540
- WHEN EXISTS (
1541
- SELECT 1 FROM log_entries WHERE id = OLD.operation_log_entry_id
1542
- )
1543
- AND EXISTS (
1544
- SELECT 1 FROM log_entries WHERE id = OLD.target_log_entry_id
1545
- )
1546
- BEGIN
1547
- SELECT RAISE(ABORT, 'log curation effects can only be removed with their containing history');
1548
- END;
1549
-
1550
- -- Make an invalid curation record structurally unavailable: the event must be
1551
- -- one successful log-KILL row, both rows must belong to the same
1552
- -- worker, and its state transition must match that op.
1553
- CREATE TRIGGER IF NOT EXISTS log_curation_effects_valid
1554
- BEFORE INSERT ON log_curation_effects
1555
- BEGIN
1556
- SELECT CASE WHEN
1557
- NOT EXISTS (
1558
- SELECT 1
1559
- FROM log_entries operation
1560
- JOIN log_entries target ON target.id = NEW.target_log_entry_id
1561
- WHERE operation.id = NEW.operation_log_entry_id
1562
- AND operation.op = 'KILL'
1563
- AND operation.status_rx < 400
1564
- AND operation.worker_id = target.worker_id
1565
- )
1566
- OR NOT EXISTS (
1567
- -- {§log-kill-scope} — a scoped KILL keeps the row active and changes its visibility;
1568
- -- a whole KILL retires it with its visibility untouched.
1569
- SELECT 1
1570
- FROM log_entries operation
1571
- WHERE operation.id = NEW.operation_log_entry_id
1572
- AND operation.op = 'KILL'
1573
- AND operation.scheme = 'log'
1574
- AND NEW.active_before = 1
1575
- AND (
1576
- NEW.active_after = 1
1577
- OR (
1578
- NEW.active_after = 0
1579
- AND json(NEW.folded_before) = json(NEW.folded_after)
1580
- )
1581
- )
1582
- )
1583
- OR EXISTS (
1584
- SELECT 1
1585
- FROM (
1586
- SELECT 'before' AS side, key, value, type FROM json_each(NEW.folded_before)
1587
- UNION ALL
1588
- SELECT 'after' AS side, key, value, type FROM json_each(NEW.folded_after)
1589
- ) range
1590
- WHERE range.type != 'array'
1591
- OR json_array_length(range.value) != 2
1592
- OR COALESCE(json_type(range.value, '$[0]'), '') != 'integer'
1593
- OR COALESCE(json_type(range.value, '$[1]'), '') != 'integer'
1594
- OR json_extract(range.value, '$[0]') < 1
1595
- OR (
1596
- json_extract(range.value, '$[1]') != -1
1597
- AND json_extract(range.value, '$[1]') < json_extract(range.value, '$[0]')
1598
- )
1599
- OR EXISTS (
1600
- SELECT 1
1601
- FROM (
1602
- SELECT key, value FROM json_each(
1603
- CASE range.side
1604
- WHEN 'before' THEN NEW.folded_before
1605
- ELSE NEW.folded_after
1606
- END
1607
- )
1608
- ) previous
1609
- WHERE previous.key = range.key - 1
1610
- AND (
1611
- json_extract(previous.value, '$[1]') = -1
1612
- OR json_extract(range.value, '$[0]') <= json_extract(previous.value, '$[1]') + 1
1613
- )
1614
- )
1615
- )
1616
- THEN RAISE(ABORT, 'invalid log curation effect') END;
1617
- END;
1618
-
1619
- -- The dispatcher binds an exact, transient curation plan into attrs on the
1620
- -- successful log-KILL row. No other row may carry that payload.
1621
- CREATE TRIGGER IF NOT EXISTS log_entries_curation_payload_valid
1622
- BEFORE INSERT ON log_entries
1623
- WHEN json_type(NEW.attrs, '$.__plurnk_curation') IS NOT NULL
1624
- AND NOT (
1625
- NEW.op = 'KILL'
1626
- AND NEW.status_rx < 400
1627
- AND NEW.scheme = 'log'
1628
- AND json_type(NEW.attrs, '$.__plurnk_curation') = 'object'
1629
- )
1630
- BEGIN
1631
- SELECT RAISE(ABORT, 'private log curation payload requires a successful log curation row');
1632
- END;
1633
-
1634
- -- One outer INSERT owns the whole landed curation event: exact selected rows,
1635
- -- their before/after projection, and the resulting current
1636
- -- state. Trigger failure rolls the operation row and all effects back together.
1637
- -- The private plan is erased before INSERT returns.
1638
- -- {§log-kill-scope} — a log KILL either retires a row whole (active 1→0, visibility untouched)
1639
- -- or folds a span of its body (active stays 1, visibility changes).
1640
- CREATE TRIGGER IF NOT EXISTS log_entries_apply_curation
1641
- AFTER INSERT ON log_entries
1642
- WHEN NEW.op = 'KILL'
1643
- AND NEW.status_rx < 400
1644
- AND NEW.scheme = 'log'
1645
- AND json_type(NEW.attrs, '$.__plurnk_curation') = 'object'
1646
- BEGIN
1647
- SELECT CASE WHEN
1648
- COALESCE(json_type(NEW.attrs, '$.__plurnk_curation.targets'), '') != 'array'
1649
- OR json_array_length(NEW.attrs, '$.__plurnk_curation.targets') = 0
1650
- OR EXISTS (
1651
- SELECT 1
1652
- FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
1653
- WHERE selected.type != 'object'
1654
- OR COALESCE(json_type(selected.value, '$.id'), '') != 'integer'
1655
- OR json_extract(selected.value, '$.id') <= 0
1656
- OR COALESCE(json_type(selected.value, '$.activeBefore'), '') != 'integer'
1657
- OR json_extract(selected.value, '$.activeBefore') NOT IN (0, 1)
1658
- OR COALESCE(json_type(selected.value, '$.activeAfter'), '') != 'integer'
1659
- OR json_extract(selected.value, '$.activeAfter') NOT IN (0, 1)
1660
- OR COALESCE(json_type(selected.value, '$.foldedBefore'), '') != 'array'
1661
- OR COALESCE(json_type(selected.value, '$.foldedAfter'), '') != 'array'
1662
- OR EXISTS (
1663
- SELECT 1 FROM json_each(selected.value) field
1664
- WHERE field.key NOT IN ('id', 'activeBefore', 'activeAfter', 'foldedBefore', 'foldedAfter')
1665
- )
1666
- OR EXISTS (
1667
- SELECT 1
1668
- FROM (
1669
- SELECT 'before' AS side, range.key, range.value, range.type
1670
- FROM json_each(json_extract(selected.value, '$.foldedBefore')) range
1671
- UNION ALL
1672
- SELECT 'after' AS side, range.key, range.value, range.type
1673
- FROM json_each(json_extract(selected.value, '$.foldedAfter')) range
1674
- ) range
1675
- WHERE range.type != 'array'
1676
- OR json_array_length(range.value) != 2
1677
- OR COALESCE(json_type(range.value, '$[0]'), '') != 'integer'
1678
- OR COALESCE(json_type(range.value, '$[1]'), '') != 'integer'
1679
- OR json_extract(range.value, '$[0]') < 1
1680
- OR (
1681
- json_extract(range.value, '$[1]') != -1
1682
- AND json_extract(range.value, '$[1]') < json_extract(range.value, '$[0]')
1683
- )
1684
- OR EXISTS (
1685
- SELECT 1
1686
- FROM json_each(
1687
- CASE range.side
1688
- WHEN 'before' THEN json_extract(selected.value, '$.foldedBefore')
1689
- ELSE json_extract(selected.value, '$.foldedAfter')
1690
- END
1691
- ) previous
1692
- WHERE previous.key = range.key - 1
1693
- AND (
1694
- json_extract(previous.value, '$[1]') = -1
1695
- OR json_extract(range.value, '$[0]') <= json_extract(previous.value, '$[1]') + 1
1696
- )
1697
- )
1698
- )
1699
- )
1700
- OR (
1701
- SELECT COUNT(*) FROM json_each(NEW.attrs, '$.__plurnk_curation.targets')
1702
- ) != (
1703
- SELECT COUNT(DISTINCT json_extract(value, '$.id'))
1704
- FROM json_each(NEW.attrs, '$.__plurnk_curation.targets')
1705
- )
1706
- OR EXISTS (
1707
- SELECT 1
1708
- FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
1709
- LEFT JOIN log_entries target ON target.id = json_extract(selected.value, '$.id')
1710
- LEFT JOIN log_entry_projections projection ON projection.log_entry_id = target.id
1711
- WHERE target.id IS NULL
1712
- OR projection.log_entry_id IS NULL
1713
- OR target.worker_id != NEW.worker_id
1714
- OR target.id = NEW.id
1715
- OR projection.active != json_extract(selected.value, '$.activeBefore')
1716
- OR json(projection.folded) != json(json_extract(selected.value, '$.foldedBefore'))
1717
- OR NOT (
1718
- json_extract(selected.value, '$.activeBefore') = 1
1719
- AND (
1720
- json_extract(selected.value, '$.activeAfter') = 1
1721
- OR json(json_extract(selected.value, '$.foldedBefore')) = json(json_extract(selected.value, '$.foldedAfter'))
1722
- )
1723
- )
1724
- )
1725
- THEN RAISE(ABORT, 'invalid private log curation payload') END;
1726
-
1727
- INSERT INTO log_curation_effects (
1728
- operation_log_entry_id,
1729
- target_log_entry_id,
1730
- active_before,
1731
- active_after,
1732
- folded_before,
1733
- folded_after
1734
- )
1735
- SELECT
1736
- NEW.id,
1737
- target.id,
1738
- json_extract(selected.value, '$.activeBefore'),
1739
- json_extract(selected.value, '$.activeAfter'),
1740
- json_extract(selected.value, '$.foldedBefore'),
1741
- json_extract(selected.value, '$.foldedAfter')
1742
- FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
1743
- JOIN log_entries target ON target.id = json_extract(selected.value, '$.id');
1744
-
1745
- UPDATE log_entry_projections
1746
- SET active = (
1747
- SELECT json_extract(selected.value, '$.activeAfter')
1748
- FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
1749
- WHERE json_extract(selected.value, '$.id') = log_entry_projections.log_entry_id
1750
- ),
1751
- folded = (
1752
- SELECT json_extract(selected.value, '$.foldedAfter')
1753
- FROM json_each(NEW.attrs, '$.__plurnk_curation.targets') selected
1754
- WHERE json_extract(selected.value, '$.id') = log_entry_projections.log_entry_id
1755
- )
1756
- WHERE log_entry_id IN (
1757
- SELECT json_extract(value, '$.id')
1758
- FROM json_each(NEW.attrs, '$.__plurnk_curation.targets')
1759
- );
1760
-
1761
- UPDATE log_entries
1762
- SET attrs = json_remove(attrs, '$.__plurnk_curation')
1763
- WHERE id = NEW.id;
1764
- END;
1765
-
1766
- -- Column-scoped immutability: the original action's identity, target, and
1767
- -- initial projection never change. Proposal lifecycle may mutate its outcome;
1768
- -- curation state lives outside the event row. Keep attrs separate so the
1769
- -- curation trigger's private-payload removal cannot exempt other columns.
1770
- CREATE TRIGGER IF NOT EXISTS log_entries_immutable_core
1771
- BEFORE UPDATE OF
1772
- worker_id, loop_id, turn_id, sequence, at, origin, source, inherited_history, model_call_id,
1773
- op, delimiter, signal,
1774
- scheme, username, password, hostname,
1775
- port, pathname, query, fragment,
1776
- lineMarker, tx, mimetype_tx, mimetype_rx, initial_folded
1777
- ON log_entries
1778
- BEGIN
1779
- SELECT RAISE(ABORT, 'log_entries core fields are immutable; only lifecycle outcome and derived attachments may change');
1780
- END;
1781
-
1782
- CREATE TRIGGER IF NOT EXISTS log_entries_immutable_attrs
1783
- BEFORE UPDATE OF attrs ON log_entries
1784
- WHEN COALESCE((
1785
- json_type(OLD.attrs, '$.__plurnk_curation') = 'object'
1786
- AND NEW.attrs = json_remove(OLD.attrs, '$.__plurnk_curation')
1787
- ), 0) = 0
1788
- BEGIN
1789
- SELECT RAISE(ABORT, 'log_entries attrs are immutable outside curation payload removal');
1790
- END;
1791
-
1792
- -- The engine may stamp an originating row exactly once. No later reassignment
1793
- -- can sever or counterfeit the occurrence identity.
1794
- CREATE TRIGGER IF NOT EXISTS log_entries_ambient_event_once
1795
- BEFORE UPDATE OF ambient_event_id ON log_entries
1796
- WHEN NOT (OLD.ambient_event_id IS NULL AND NEW.ambient_event_id IS NOT NULL)
1797
- BEGIN
1798
- SELECT RAISE(ABORT, 'log_entries ambient event identity may only be assigned once');
1799
- END;
1800
-
1801
- -- One relational projection owns activity eligibility, audience, and snapshot
1802
- -- shape for both immediately-final and proposal-settlement paths.
1803
- CREATE VIEW IF NOT EXISTS ambient_activity_candidates AS
1804
- SELECT *
1805
- FROM (
1806
- SELECT w.workspace_id,
1807
- le.worker_id AS producer_worker_id,
1808
- -- {§env-delta-child-activity} — the runtime's own materialization (turn-0
1809
- -- initialization, doc-reconciliation maintenance, operation batches) is
1810
- -- private to the worker it serves and never crosses to the parent.
1811
- CASE
1812
- WHEN t.producer = '_plurnk' AND t.kind IN ('operation', 'initialization', 'maintenance') THEN NULL
1813
- ELSE w.parent_worker_id
1814
- END AS target_parent_worker_id,
1815
- CASE
1816
- WHEN le.state = 'resolved'
1817
- AND le.status_rx BETWEEN 200 AND 399
1818
- AND le.status_rx != 304
1819
- AND (
1820
- (
1821
- le.op IN ('EDIT', 'KILL')
1822
- AND le.scheme = 'worker'
1823
- AND le.hostname IS NULL
1824
- AND le.pathname IS NOT NULL
1825
- )
1826
- OR (
1827
- le.op IN ('COPY', 'MOVE')
1828
- AND json_valid(le.rx)
1829
- AND EXISTS (
1830
- SELECT 1 FROM json_each(le.rx, '$.effects') effect
1831
- WHERE json_type(effect.value) = 'object'
1832
- AND substr(CAST(json_extract(effect.value, '$.target') AS TEXT), 1, 10) = 'worker:///'
1833
- )
1834
- )
1835
- )
1836
- THEN 1 ELSE 0
1837
- END AS workspace_broadcast,
1838
- le.id AS source_record_id,
1839
- le.at,
1840
- le.source,
1841
- le.op,
1842
- le.delimiter,
1843
- le.signal,
1844
- le.scheme,
1845
- le.username,
1846
- le.password,
1847
- le.hostname,
1848
- le.port,
1849
- le.pathname,
1850
- le.query,
1851
- le.fragment,
1852
- le.lineMarker AS line_marker,
1853
- le.tx,
1854
- le.mimetype_tx,
1855
- le.rx,
1856
- le.mimetype_rx,
1857
- le.status_rx,
1858
- le.state,
1859
- le.outcome,
1860
- json_remove(le.attrs, '$.__plurnk_curation') AS attrs
1861
- FROM log_entries le
1862
- JOIN workers w ON w.id = le.worker_id
1863
- JOIN loops l ON l.id = le.loop_id
1864
- JOIN turns t ON t.id = le.turn_id
1865
- WHERE le.ambient_event_id IS NULL
1866
- AND le.inherited_history = 0
1867
- AND le.op IS NOT NULL
1868
- AND le.state != 'proposed'
1869
- AND NOT (
1870
- le.op = 'SEND'
1871
- AND le.scheme IS NULL
1872
- AND l.status IN (200, 413, 429, 499, 500, 504, 508)
1873
- )
1874
- ) candidate
1875
- WHERE target_parent_worker_id IS NOT NULL OR workspace_broadcast = 1;
1876
-
1877
- -- Every final op-bearing child row is parent activity. A successful operation
1878
- -- whose landed effects touch worker:/// additionally acquires the workspace
1879
- -- audience. Observer rows and copied fork history cannot republish themselves.
1880
- CREATE TRIGGER IF NOT EXISTS log_entries_append_ambient_event_insert
1881
- AFTER INSERT ON log_entries
1882
- WHEN NEW.state != 'proposed'
1883
- BEGIN
1884
- INSERT INTO ambient_events (
1885
- workspace_id, producer_worker_id, target_parent_worker_id,
1886
- workspace_broadcast, kind, source_record_id, at, source,
1887
- op, delimiter, signal,
1888
- scheme, username, password, hostname, port, pathname, query, fragment,
1889
- line_marker, tx, mimetype_tx, rx, mimetype_rx, status_rx,
1890
- state, outcome, attrs
1891
- )
1892
- SELECT workspace_id, producer_worker_id, target_parent_worker_id,
1893
- workspace_broadcast, 'activity', source_record_id, at, source,
1894
- op, delimiter, signal,
1895
- scheme, username, password, hostname, port, pathname, query, fragment,
1896
- line_marker, tx, mimetype_tx, rx, mimetype_rx, status_rx,
1897
- state, outcome, attrs
1898
- FROM ambient_activity_candidates
1899
- WHERE source_record_id = NEW.id;
1900
-
1901
- UPDATE log_entries
1902
- SET ambient_event_id = (
1903
- SELECT id FROM ambient_events
1904
- WHERE producer_worker_id = NEW.worker_id
1905
- AND kind = 'activity'
1906
- AND source_record_id = NEW.id
1907
- )
1908
- WHERE id = NEW.id
1909
- AND EXISTS (
1910
- SELECT 1 FROM ambient_events
1911
- WHERE producer_worker_id = NEW.worker_id
1912
- AND kind = 'activity'
1913
- AND source_record_id = NEW.id
1914
- );
1915
- END;
1916
-
1917
- -- A proposed operation becomes activity only when its lifecycle settles.
1918
- CREATE TRIGGER IF NOT EXISTS log_entries_append_ambient_event_resolve
1919
- AFTER UPDATE OF state, status_rx, rx, outcome ON log_entries
1920
- WHEN OLD.state = 'proposed' AND NEW.state != 'proposed'
1921
- BEGIN
1922
- INSERT INTO ambient_events (
1923
- workspace_id, producer_worker_id, target_parent_worker_id,
1924
- workspace_broadcast, kind, source_record_id, at, source,
1925
- op, delimiter, signal,
1926
- scheme, username, password, hostname, port, pathname, query, fragment,
1927
- line_marker, tx, mimetype_tx, rx, mimetype_rx, status_rx,
1928
- state, outcome, attrs
1929
- )
1930
- SELECT workspace_id, producer_worker_id, target_parent_worker_id,
1931
- workspace_broadcast, 'activity', source_record_id, at, source,
1932
- op, delimiter, signal,
1933
- scheme, username, password, hostname, port, pathname, query, fragment,
1934
- line_marker, tx, mimetype_tx, rx, mimetype_rx, status_rx,
1935
- state, outcome, attrs
1936
- FROM ambient_activity_candidates
1937
- WHERE source_record_id = NEW.id;
1938
-
1939
- UPDATE log_entries
1940
- SET ambient_event_id = (
1941
- SELECT id FROM ambient_events
1942
- WHERE producer_worker_id = NEW.worker_id
1943
- AND kind = 'activity'
1944
- AND source_record_id = NEW.id
1945
- )
1946
- WHERE id = NEW.id
1947
- AND EXISTS (
1948
- SELECT 1 FROM ambient_events
1949
- WHERE producer_worker_id = NEW.worker_id
1950
- AND kind = 'activity'
1951
- AND source_record_id = NEW.id
1952
- );
1953
- END;
1954
-
1955
- -- {§client-interactions}: the durable discoverable half of a client-owned
1956
- -- interaction. The process-local lifecycle owner holds the awaiting callable;
1957
- -- this row holds only presentation and routing facts. Settlement deletes the
1958
- -- row, so client response payloads and owner-private continuation state are
1959
- -- never copied into persistence.
1960
- CREATE TABLE IF NOT EXISTS client_interactions (
1961
- id INTEGER NOT NULL PRIMARY KEY,
1962
- worker_id INTEGER NOT NULL,
1963
- loop_id INTEGER NOT NULL,
1964
- turn_id INTEGER NOT NULL,
1965
- request TEXT NOT NULL CHECK (json_valid(request) AND json_type(request) = 'object'),
1966
- created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
1967
- FOREIGN KEY (worker_id) REFERENCES workers(id) ON DELETE CASCADE,
1968
- FOREIGN KEY (loop_id) REFERENCES loops(id) ON DELETE CASCADE,
1969
- FOREIGN KEY (turn_id) REFERENCES turns(id) ON DELETE CASCADE
1970
- ) STRICT;
1971
-
1972
- CREATE INDEX IF NOT EXISTS client_interactions_worker_id_id
1973
- ON client_interactions (worker_id, id);
1974
-
1975
- -- subscriptions
1976
- -- Durable subscription lifecycle per SPEC {§subscriptions}. The row records what
1977
- -- the worker holds and routes cancellation to a separate process-local callable;
1978
- -- it never serializes that callable. Closed rows persist for forensics; partial
1979
- -- unique index enforces one active subscription per entry. The composite
1980
- -- foreign key makes that entry part of the subscribing Worker's own space.
1981
- CREATE TABLE IF NOT EXISTS subscriptions (
1982
- id INTEGER NOT NULL PRIMARY KEY,
1983
- version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
1984
- worker_id INTEGER NOT NULL,
1985
- entry_id INTEGER NOT NULL,
1986
- scheme TEXT NOT NULL CHECK (length(scheme) > 0),
1987
- handle TEXT NOT NULL CHECK (length(handle) > 0),
1988
- published_channel TEXT CHECK (published_channel IS NULL OR length(published_channel) > 0),
1989
- opened_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
1990
- -- EXEC `<T,P>` poll policy: NULL = default backoff, 0 = disabled, positive = fixed cadence.
1991
- -- While the owning loop hibernates (202), an armed policy wakes it to inspect the stream ({§exec-poll}).
1992
- poll_seconds INTEGER CHECK (poll_seconds IS NULL OR poll_seconds >= 0),
1993
- -- EXEC `<0>` — turn-scoped: the stream is reaped at the worker's next pre-turn so it never survives
1994
- -- into the subsequent turn; its terminal output surfaces initially visible like any conclusion. {§exec-poll}
1995
- turn_scoped INTEGER NOT NULL DEFAULT 0 CHECK (turn_scoped IN (0, 1)),
1996
- closed_at TEXT,
1997
- close_status INTEGER CHECK (close_status IS NULL OR (close_status BETWEEN 100 AND 599)),
1998
- close_result TEXT CHECK (close_result IS NULL OR json_valid(close_result)),
1999
- channel_results TEXT CHECK (channel_results IS NULL OR json_valid(channel_results)),
2000
- CHECK ((closed_at IS NULL AND close_status IS NULL AND close_result IS NULL AND channel_results IS NULL)
2001
- OR (closed_at IS NOT NULL AND close_status IS NOT NULL AND close_result IS NOT NULL AND channel_results IS NOT NULL)),
2002
- FOREIGN KEY (worker_id) REFERENCES workers(id) ON DELETE CASCADE,
2003
- FOREIGN KEY (entry_id, worker_id) REFERENCES entries(id, owner_id) ON DELETE CASCADE
2004
- ) STRICT;
2005
-
2006
- CREATE UNIQUE INDEX IF NOT EXISTS subscriptions_active_one_per_entry
2007
- ON subscriptions (entry_id)
2008
- WHERE closed_at IS NULL;
2009
-
2010
- CREATE INDEX IF NOT EXISTS subscriptions_scheme_active
2011
- ON subscriptions (scheme)
2012
- WHERE closed_at IS NULL;
2013
-
2014
- CREATE INDEX IF NOT EXISTS subscriptions_opened_at ON subscriptions (opened_at);
2015
-
2016
- CREATE TRIGGER IF NOT EXISTS subscriptions_result_contract_insert
2017
- BEFORE INSERT ON subscriptions
2018
- WHEN NEW.closed_at IS NOT NULL
2019
- OR NEW.close_status IS NOT NULL
2020
- OR NEW.close_result IS NOT NULL
2021
- OR NEW.channel_results IS NOT NULL
2022
- BEGIN
2023
- SELECT RAISE(ABORT, 'subscription must open before it can settle');
2024
- END;
2025
-
2026
- CREATE TRIGGER IF NOT EXISTS subscriptions_result_contract_update
2027
- BEFORE UPDATE OF closed_at, close_status, close_result, channel_results ON subscriptions
2028
- WHEN NOT (
2029
- (NEW.closed_at IS NULL AND NEW.close_status IS NULL AND NEW.close_result IS NULL AND NEW.channel_results IS NULL)
2030
- OR (
2031
- NEW.closed_at IS NOT NULL
2032
- AND NEW.close_status IS NOT NULL
2033
- AND NEW.close_result IS NOT NULL
2034
- AND NEW.channel_results IS NOT NULL
2035
- AND json_valid(NEW.close_result)
2036
- AND json_valid(NEW.channel_results)
2037
- AND json_type(NEW.channel_results) = 'object'
2038
- AND json_type(NEW.close_result, '$.status') = 'integer'
2039
- AND json_extract(NEW.close_result, '$.status') = NEW.close_status
2040
- AND (
2041
- (NEW.close_status < 400 AND json_type(NEW.close_result, '$.problem') IS NULL)
2042
- OR (
2043
- NEW.close_status >= 400
2044
- AND json_type(NEW.close_result, '$.problem') = 'object'
2045
- AND json_extract(NEW.close_result, '$.problem.status') = NEW.close_status
2046
- )
2047
- )
2048
- )
2049
- )
2050
- BEGIN
2051
- SELECT RAISE(ABORT, 'subscription terminal result violates the operation-result contract');
2052
- END;
2053
-
2054
- -- A settlement may override the universal result for exact named channels.
2055
- -- Unknown channels and malformed results fail before the subscription or any
2056
- -- representation changes. The JS boundary additionally rejects projection
2057
- -- fields that are structurally unavailable in ChannelProducerResult.
2058
- CREATE TRIGGER IF NOT EXISTS subscriptions_channel_results_contract
2059
- BEFORE UPDATE OF closed_at, close_status, close_result, channel_results ON subscriptions
2060
- WHEN NEW.closed_at IS NOT NULL AND (
2061
- EXISTS (
2062
- SELECT 1
2063
- FROM json_each(NEW.channel_results) AS channel_result
2064
- LEFT JOIN entry_channels ec
2065
- ON ec.entry_id = NEW.entry_id AND ec.name = channel_result.key
2066
- WHERE ec.name IS NULL
2067
- OR json_type(channel_result.value) != 'object'
2068
- OR json_type(channel_result.value, '$.status') != 'integer'
2069
- OR json_extract(channel_result.value, '$.status') NOT BETWEEN 200 AND 599
2070
- OR json_extract(channel_result.value, '$.status') = 202
2071
- OR (
2072
- json_extract(channel_result.value, '$.status') < 400
2073
- AND json_type(channel_result.value, '$.problem') IS NOT NULL
2074
- )
2075
- OR (
2076
- json_extract(channel_result.value, '$.status') >= 400
2077
- AND (
2078
- json_type(channel_result.value, '$.problem') != 'object'
2079
- OR json_extract(channel_result.value, '$.problem.status')
2080
- != json_extract(channel_result.value, '$.status')
2081
- )
2082
- )
2083
- )
2084
- )
2085
- BEGIN
2086
- SELECT RAISE(ABORT, 'subscription channel result violates the channel producer contract');
2087
- END;
2088
-
2089
- -- Subscription settlement is the single atomic transition that closes the
2090
- -- lifecycle and installs current terminal producer evidence on every channel.
2091
- -- Overrides are exact; all other channels inherit the universal result.
2092
- CREATE TRIGGER IF NOT EXISTS subscriptions_settle_channels
2093
- AFTER UPDATE OF closed_at, close_status, close_result, channel_results ON subscriptions
2094
- WHEN OLD.closed_at IS NULL AND NEW.closed_at IS NOT NULL
2095
- BEGIN
2096
- UPDATE entry_channels
2097
- SET producer_result = COALESCE(
2098
- (
2099
- SELECT json(channel_result.value)
2100
- FROM json_each(NEW.channel_results) AS channel_result
2101
- WHERE channel_result.key = entry_channels.name
2102
- ),
2103
- NEW.close_result
2104
- ),
2105
- state = CASE WHEN json_extract(
2106
- COALESCE(
2107
- (
2108
- SELECT json(channel_result.value)
2109
- FROM json_each(NEW.channel_results) AS channel_result
2110
- WHERE channel_result.key = entry_channels.name
2111
- ),
2112
- NEW.close_result
2113
- ),
2114
- '$.status'
2115
- ) >= 400 THEN 'errored' ELSE 'closed' END
2116
- WHERE entry_id = NEW.entry_id;
2117
- END;
2118
-
2119
- -- One durable publication cursor per selected subscription channel. Log rows
2120
- -- are curated model context and therefore cannot own this lifecycle fact.
2121
- -- The row survives KILLing any generated observation and disappears only with
2122
- -- its subscription. {§exec-stream}
2123
- CREATE TABLE IF NOT EXISTS subscription_publications (
2124
- id INTEGER NOT NULL PRIMARY KEY,
2125
- version INTEGER NOT NULL DEFAULT 0 CHECK (version >= 0),
2126
- subscription_id INTEGER NOT NULL,
2127
- channel TEXT NOT NULL CHECK (length(channel) > 0),
2128
- published_end INTEGER NOT NULL DEFAULT 0 CHECK (published_end >= 0),
2129
- terminal_published INTEGER NOT NULL DEFAULT 0 CHECK (terminal_published IN (0, 1)),
2130
- UNIQUE (subscription_id, channel),
2131
- FOREIGN KEY (subscription_id) REFERENCES subscriptions(id) ON DELETE CASCADE
2132
- ) STRICT;
2133
-
2134
- CREATE INDEX IF NOT EXISTS subscription_publications_pending
2135
- ON subscription_publications (subscription_id, terminal_published);
2136
-
2137
- CREATE TRIGGER IF NOT EXISTS subscriptions_seed_publications
2138
- AFTER INSERT ON subscriptions
2139
- BEGIN
2140
- INSERT INTO subscription_publications (subscription_id, channel)
2141
- SELECT NEW.id, ec.name
2142
- FROM entry_channels ec
2143
- WHERE ec.entry_id = NEW.entry_id
2144
- AND (NEW.published_channel IS NULL OR ec.name = NEW.published_channel);
2145
-
2146
- SELECT CASE WHEN NOT EXISTS (
2147
- SELECT 1 FROM subscription_publications WHERE subscription_id = NEW.id
2148
- ) THEN RAISE(ABORT, 'subscription has no publishable channel') END;
2149
- END;
2150
-
2151
- -- The generated READ and its cursor transition are one SQLite statement. A
2152
- -- later log KILL removes evidence from active context without rewinding the
2153
- -- subscription/channel publication state. {§exec-stream}
2154
- CREATE TRIGGER IF NOT EXISTS log_entries_advance_subscription_publication
2155
- AFTER INSERT ON log_entries
2156
- WHEN NEW.subscription_publication_id IS NOT NULL
2157
- BEGIN
2158
- SELECT CASE WHEN
2159
- NEW.origin != '_plurnk'
2160
- OR NEW.op != 'READ'
2161
- OR json_type(NEW.attrs, '$.streamEnd') != 'integer'
2162
- OR json_extract(NEW.attrs, '$.streamEnd') < 0
2163
- OR json_type(NEW.attrs, '$.terminal') NOT IN ('true', 'false')
2164
- THEN RAISE(ABORT, 'subscription publication requires one canonical stream observation') END;
2165
-
2166
- UPDATE subscription_publications
2167
- SET published_end = json_extract(NEW.attrs, '$.streamEnd'),
2168
- terminal_published = json_extract(NEW.attrs, '$.terminal'),
2169
- version = version + 1
2170
- WHERE id = NEW.subscription_publication_id
2171
- AND terminal_published = 0
2172
- AND (
2173
- (json_extract(NEW.attrs, '$.terminal') = 0
2174
- AND json_extract(NEW.attrs, '$.streamEnd') > published_end)
2175
- OR
2176
- (json_extract(NEW.attrs, '$.terminal') = 1
2177
- AND json_extract(NEW.attrs, '$.streamEnd') >= published_end)
2178
- );
2179
-
2180
- SELECT CASE WHEN changes() != 1
2181
- THEN RAISE(ABORT, 'subscription publication transition is stale or already terminal')
2182
- END;
2183
- END;
2184
-
2185
- -- (worker_watermarks removed — {§env-delta} is now pull-from-log, no per-worker snapshot.)
2186
-
2187
- -- workspace_constraints
2188
- -- SPEC {§membership} constraint overlay — explicit policy and the inspectable
2189
- -- exact pick produced by accepted file creation share one representation.
2190
- -- Per (workspace, effect, glob/target): `pick` (members git misses, resolved by a
2191
- -- targeted client-dictated scan), `hide` (drop git-tracked matches), `view` (member
2192
- -- for read; File.edit rejects the write). git-absent, `pick` rows are the sole substrate
2193
- -- source. Composed at membership resolution; node:path.matchesGlob.
2194
- CREATE TABLE IF NOT EXISTS workspace_constraints (
2195
- workspace_id INTEGER NOT NULL,
2196
- -- include admits files git misses; exclude removes members — the members family's lexicon
2197
- -- ({§members-projection}). An inclusion is a pattern scan; a creation record is one exact path.
2198
- effect TEXT NOT NULL CHECK (effect IN ('include', 'exclude')),
2199
- glob TEXT NOT NULL,
2200
- -- create: the exact record of a file Plurnk wrote ({§fs-create-record}); members: a
2201
- -- human-authored definition projected by the members family; model: a definition the model
2202
- -- proposed under the members scope — never admitted past the repository's ignore rules.
2203
- source TEXT NOT NULL CHECK (source IN ('create', 'members', 'model')),
2204
- CHECK (source IN ('members', 'model') OR effect = 'include'),
2205
- PRIMARY KEY (workspace_id, effect, glob),
2206
- FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE
2207
- ) STRICT, WITHOUT ROWID;