@plurnk/plurnk-service 1.14.2 → 1.16.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 (308) hide show
  1. package/.env.defaults +2 -2
  2. package/SPEC.md +243 -173
  3. package/digest-sql/curation/curation.sql +1 -1
  4. package/dist/build-info.json +1 -1
  5. package/dist/content/byte-view.d.ts +14 -0
  6. package/dist/content/byte-view.d.ts.map +1 -0
  7. package/dist/content/byte-view.js +33 -0
  8. package/dist/content/byte-view.js.map +1 -0
  9. package/dist/content/edit-receipt.d.ts.map +1 -1
  10. package/dist/content/edit-receipt.js +12 -1
  11. package/dist/content/edit-receipt.js.map +1 -1
  12. package/dist/content/line-marker.d.ts +2 -1
  13. package/dist/content/line-marker.d.ts.map +1 -1
  14. package/dist/content/line-marker.js +1 -0
  15. package/dist/content/line-marker.js.map +1 -1
  16. package/dist/content/matcher.d.ts +1 -0
  17. package/dist/content/matcher.d.ts.map +1 -1
  18. package/dist/content/matcher.js +1 -0
  19. package/dist/content/matcher.js.map +1 -1
  20. package/dist/content/read-projector.d.ts +4 -1
  21. package/dist/content/read-projector.d.ts.map +1 -1
  22. package/dist/content/read-projector.js +80 -3
  23. package/dist/content/read-projector.js.map +1 -1
  24. package/dist/core/AdmittedTurnExecutor.d.ts +43 -0
  25. package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -0
  26. package/dist/core/AdmittedTurnExecutor.js +245 -0
  27. package/dist/core/AdmittedTurnExecutor.js.map +1 -0
  28. package/dist/core/BareBatchRunner.d.ts +26 -0
  29. package/dist/core/BareBatchRunner.d.ts.map +1 -0
  30. package/dist/core/BareBatchRunner.js +104 -0
  31. package/dist/core/BareBatchRunner.js.map +1 -0
  32. package/dist/core/BudgetReadout.d.ts +1 -1
  33. package/dist/core/BudgetReadout.d.ts.map +1 -1
  34. package/dist/core/BudgetReadout.js +42 -47
  35. package/dist/core/BudgetReadout.js.map +1 -1
  36. package/dist/core/CapabilityResolver.d.ts +2 -1
  37. package/dist/core/CapabilityResolver.d.ts.map +1 -1
  38. package/dist/core/CapabilityResolver.js +8 -9
  39. package/dist/core/CapabilityResolver.js.map +1 -1
  40. package/dist/core/ChannelWrite.d.ts +1 -1
  41. package/dist/core/ChannelWrite.d.ts.map +1 -1
  42. package/dist/core/DataStatementRunner.d.ts +35 -0
  43. package/dist/core/DataStatementRunner.d.ts.map +1 -0
  44. package/dist/core/DataStatementRunner.js +252 -0
  45. package/dist/core/DataStatementRunner.js.map +1 -0
  46. package/dist/core/Dispatcher.d.ts +8 -3
  47. package/dist/core/Dispatcher.d.ts.map +1 -1
  48. package/dist/core/Dispatcher.js +88 -738
  49. package/dist/core/Dispatcher.js.map +1 -1
  50. package/dist/core/EditMutations.d.ts +39 -0
  51. package/dist/core/EditMutations.d.ts.map +1 -0
  52. package/dist/core/EditMutations.js +418 -0
  53. package/dist/core/EditMutations.js.map +1 -0
  54. package/dist/core/Engine.d.ts +2 -23
  55. package/dist/core/Engine.d.ts.map +1 -1
  56. package/dist/core/Engine.js +18 -240
  57. package/dist/core/Engine.js.map +1 -1
  58. package/dist/core/Engine.sql +24 -23
  59. package/dist/core/KillHandler.d.ts +32 -0
  60. package/dist/core/KillHandler.d.ts.map +1 -0
  61. package/dist/core/KillHandler.js +179 -0
  62. package/dist/core/KillHandler.js.map +1 -0
  63. package/dist/core/LogVisibility.d.ts +1 -1
  64. package/dist/core/LogVisibility.d.ts.map +1 -1
  65. package/dist/core/LogVisibility.js +3 -4
  66. package/dist/core/LogVisibility.js.map +1 -1
  67. package/dist/core/LogWriter.d.ts +42 -0
  68. package/dist/core/LogWriter.d.ts.map +1 -0
  69. package/dist/core/LogWriter.js +136 -0
  70. package/dist/core/LogWriter.js.map +1 -0
  71. package/dist/core/LoopDriver.d.ts +46 -0
  72. package/dist/core/LoopDriver.d.ts.map +1 -0
  73. package/dist/core/LoopDriver.js +263 -0
  74. package/dist/core/LoopDriver.js.map +1 -0
  75. package/dist/core/LoopLifecycle.d.ts +2 -1
  76. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  77. package/dist/core/MembershipMaterialization.d.ts +7 -0
  78. package/dist/core/MembershipMaterialization.d.ts.map +1 -0
  79. package/dist/core/MembershipMaterialization.js +283 -0
  80. package/dist/core/MembershipMaterialization.js.map +1 -0
  81. package/dist/core/MutationEffects.d.ts +29 -0
  82. package/dist/core/MutationEffects.d.ts.map +1 -0
  83. package/dist/core/MutationEffects.js +318 -0
  84. package/dist/core/MutationEffects.js.map +1 -0
  85. package/dist/core/OverflowTurn.d.ts +4 -4
  86. package/dist/core/OverflowTurn.d.ts.map +1 -1
  87. package/dist/core/OverflowTurn.js +6 -7
  88. package/dist/core/OverflowTurn.js.map +1 -1
  89. package/dist/core/PacketBuilder.d.ts.map +1 -1
  90. package/dist/core/PacketBuilder.js +31 -9
  91. package/dist/core/PacketBuilder.js.map +1 -1
  92. package/dist/core/ResourceMutations.d.ts +12 -36
  93. package/dist/core/ResourceMutations.d.ts.map +1 -1
  94. package/dist/core/ResourceMutations.js +27 -1351
  95. package/dist/core/ResourceMutations.js.map +1 -1
  96. package/dist/core/ResourceSelector.d.ts +21 -0
  97. package/dist/core/ResourceSelector.d.ts.map +1 -0
  98. package/dist/core/ResourceSelector.js +233 -0
  99. package/dist/core/ResourceSelector.js.map +1 -0
  100. package/dist/core/ResourceTransfers.d.ts +55 -0
  101. package/dist/core/ResourceTransfers.d.ts.map +1 -0
  102. package/dist/core/ResourceTransfers.js +525 -0
  103. package/dist/core/ResourceTransfers.js.map +1 -0
  104. package/dist/core/SchemeRegistry.js +2 -2
  105. package/dist/core/SchemeRegistry.js.map +1 -1
  106. package/dist/core/SendBroadcastHandler.d.ts +40 -0
  107. package/dist/core/SendBroadcastHandler.d.ts.map +1 -0
  108. package/dist/core/SendBroadcastHandler.js +159 -0
  109. package/dist/core/SendBroadcastHandler.js.map +1 -0
  110. package/dist/core/StoredPacket.d.ts +11 -0
  111. package/dist/core/StoredPacket.d.ts.map +1 -1
  112. package/dist/core/StoredPacket.js +22 -1
  113. package/dist/core/StoredPacket.js.map +1 -1
  114. package/dist/core/StrikeRail.d.ts.map +1 -1
  115. package/dist/core/StrikeRail.js +19 -9
  116. package/dist/core/StrikeRail.js.map +1 -1
  117. package/dist/core/TokenCalibration.d.ts +16 -0
  118. package/dist/core/TokenCalibration.d.ts.map +1 -0
  119. package/dist/core/TokenCalibration.js +35 -0
  120. package/dist/core/TokenCalibration.js.map +1 -0
  121. package/dist/core/ToolResources.d.ts.map +1 -1
  122. package/dist/core/ToolResources.js +15 -12
  123. package/dist/core/ToolResources.js.map +1 -1
  124. package/dist/core/TurnMaterialization.d.ts +37 -0
  125. package/dist/core/TurnMaterialization.d.ts.map +1 -0
  126. package/dist/core/TurnMaterialization.js +296 -0
  127. package/dist/core/TurnMaterialization.js.map +1 -0
  128. package/dist/core/TurnOps.d.ts.map +1 -1
  129. package/dist/core/TurnOps.js +6 -8
  130. package/dist/core/TurnOps.js.map +1 -1
  131. package/dist/core/TurnRunner.d.ts +15 -27
  132. package/dist/core/TurnRunner.d.ts.map +1 -1
  133. package/dist/core/TurnRunner.js +76 -670
  134. package/dist/core/TurnRunner.js.map +1 -1
  135. package/dist/core/WorkerControlHandler.d.ts +15 -0
  136. package/dist/core/WorkerControlHandler.d.ts.map +1 -0
  137. package/dist/core/WorkerControlHandler.js +89 -0
  138. package/dist/core/WorkerControlHandler.js.map +1 -0
  139. package/dist/core/attachments.d.ts +14 -0
  140. package/dist/core/attachments.d.ts.map +1 -0
  141. package/dist/core/attachments.js +18 -0
  142. package/dist/core/attachments.js.map +1 -0
  143. package/dist/core/fork.d.ts.map +1 -1
  144. package/dist/core/fork.js +0 -4
  145. package/dist/core/fork.js.map +1 -1
  146. package/dist/core/fork.sql +5 -16
  147. package/dist/core/git-membership.d.ts +15 -0
  148. package/dist/core/git-membership.d.ts.map +1 -1
  149. package/dist/core/git-membership.js +4 -273
  150. package/dist/core/git-membership.js.map +1 -1
  151. package/dist/core/git-state.d.ts +2 -1
  152. package/dist/core/git-state.d.ts.map +1 -1
  153. package/dist/core/mutation-types.d.ts +102 -0
  154. package/dist/core/mutation-types.d.ts.map +1 -0
  155. package/dist/core/mutation-types.js +2 -0
  156. package/dist/core/mutation-types.js.map +1 -0
  157. package/dist/core/operation-target-groups.js +1 -1
  158. package/dist/core/operation-target-groups.js.map +1 -1
  159. package/dist/core/packet-wire.d.ts +7 -6
  160. package/dist/core/packet-wire.d.ts.map +1 -1
  161. package/dist/core/packet-wire.js +96 -68
  162. package/dist/core/packet-wire.js.map +1 -1
  163. package/dist/core/plurnk-uri.d.ts +0 -1
  164. package/dist/core/plurnk-uri.d.ts.map +1 -1
  165. package/dist/core/plurnk-uri.js +1 -1
  166. package/dist/core/plurnk-uri.js.map +1 -1
  167. package/dist/core/statement-primary.d.ts +4 -0
  168. package/dist/core/statement-primary.d.ts.map +1 -0
  169. package/dist/core/statement-primary.js +3 -0
  170. package/dist/core/statement-primary.js.map +1 -0
  171. package/dist/core/teaching-corpus.d.ts +0 -1
  172. package/dist/core/teaching-corpus.d.ts.map +1 -1
  173. package/dist/core/teaching-corpus.js +0 -9
  174. package/dist/core/teaching-corpus.js.map +1 -1
  175. package/dist/core/turn-scheduler.js +3 -3
  176. package/dist/core/turn-scheduler.js.map +1 -1
  177. package/dist/core/turn-signals.d.ts +15 -0
  178. package/dist/core/turn-signals.d.ts.map +1 -0
  179. package/dist/core/turn-signals.js +19 -0
  180. package/dist/core/turn-signals.js.map +1 -0
  181. package/dist/core/worker-settings.d.ts +0 -1
  182. package/dist/core/worker-settings.d.ts.map +1 -1
  183. package/dist/core/worker-settings.js +1 -1
  184. package/dist/core/worker-settings.js.map +1 -1
  185. package/dist/digest/Digest.d.ts +3 -16
  186. package/dist/digest/Digest.d.ts.map +1 -1
  187. package/dist/digest/Digest.js +10 -958
  188. package/dist/digest/Digest.js.map +1 -1
  189. package/dist/digest/DigestRender.d.ts +12 -0
  190. package/dist/digest/DigestRender.d.ts.map +1 -0
  191. package/dist/digest/DigestRender.js +641 -0
  192. package/dist/digest/DigestRender.js.map +1 -0
  193. package/dist/digest/DigestRequiem.d.ts +13 -0
  194. package/dist/digest/DigestRequiem.d.ts.map +1 -0
  195. package/dist/digest/DigestRequiem.js +332 -0
  196. package/dist/digest/DigestRequiem.js.map +1 -0
  197. package/dist/digest/digest-rows.d.ts +303 -0
  198. package/dist/digest/digest-rows.d.ts.map +1 -0
  199. package/dist/digest/digest-rows.js +2 -0
  200. package/dist/digest/digest-rows.js.map +1 -0
  201. package/dist/digest/digest.sql +1 -7
  202. package/dist/observe/api.d.ts +0 -2
  203. package/dist/observe/api.d.ts.map +1 -1
  204. package/dist/observe/api.js +2 -2
  205. package/dist/observe/api.js.map +1 -1
  206. package/dist/observe/metrics.d.ts +0 -1
  207. package/dist/observe/metrics.d.ts.map +1 -1
  208. package/dist/observe/metrics.js +0 -1
  209. package/dist/observe/metrics.js.map +1 -1
  210. package/dist/schemes/Exec.d.ts +4 -1
  211. package/dist/schemes/Exec.d.ts.map +1 -1
  212. package/dist/schemes/Exec.js +89 -48
  213. package/dist/schemes/Exec.js.map +1 -1
  214. package/dist/schemes/ExecOutputScheme.d.ts +2 -1
  215. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  216. package/dist/schemes/ExecOutputScheme.js +3 -3
  217. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  218. package/dist/schemes/File.d.ts +4 -1
  219. package/dist/schemes/File.d.ts.map +1 -1
  220. package/dist/schemes/File.js +100 -13
  221. package/dist/schemes/File.js.map +1 -1
  222. package/dist/schemes/Log.d.ts +4 -5
  223. package/dist/schemes/Log.d.ts.map +1 -1
  224. package/dist/schemes/Log.js +26 -118
  225. package/dist/schemes/Log.js.map +1 -1
  226. package/dist/schemes/Log.sql +1 -66
  227. package/dist/schemes/Prompt.js +1 -1
  228. package/dist/schemes/Prompt.js.map +1 -1
  229. package/dist/schemes/Worker.d.ts.map +1 -1
  230. package/dist/schemes/Worker.js +1 -13
  231. package/dist/schemes/Worker.js.map +1 -1
  232. package/dist/schemes/_entry-crud.d.ts +3 -1
  233. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  234. package/dist/schemes/_entry-crud.js +18 -3
  235. package/dist/schemes/_entry-crud.js.map +1 -1
  236. package/dist/schemes/_entry-find.d.ts +4 -2
  237. package/dist/schemes/_entry-find.d.ts.map +1 -1
  238. package/dist/schemes/_entry-find.js +79 -10
  239. package/dist/schemes/_entry-find.js.map +1 -1
  240. package/dist/schemes/_entry-manifest.d.ts +1 -2
  241. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  242. package/dist/schemes/_entry-ops.d.ts +2 -1
  243. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  244. package/dist/schemes/_entry-ops.js +20 -2
  245. package/dist/schemes/_entry-ops.js.map +1 -1
  246. package/dist/schemes/_entry-semantic.d.ts +1 -0
  247. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  248. package/dist/schemes/_entry-semantic.js +3 -0
  249. package/dist/schemes/_entry-semantic.js.map +1 -1
  250. package/dist/schemes/_entry-semantic.sql +2 -2
  251. package/dist/schemes/_entry-send.d.ts +0 -1
  252. package/dist/schemes/_entry-send.d.ts.map +1 -1
  253. package/dist/schemes/_entry-send.js +5 -80
  254. package/dist/schemes/_entry-send.js.map +1 -1
  255. package/dist/schemes/exec-runtime.d.ts +7 -0
  256. package/dist/schemes/exec-runtime.d.ts.map +1 -0
  257. package/dist/schemes/exec-runtime.js +2 -0
  258. package/dist/schemes/exec-runtime.js.map +1 -0
  259. package/dist/server/ClientReads.d.ts +34 -0
  260. package/dist/server/ClientReads.d.ts.map +1 -0
  261. package/dist/server/ClientReads.js +210 -0
  262. package/dist/server/ClientReads.js.map +1 -0
  263. package/dist/server/Daemon.d.ts +12 -22
  264. package/dist/server/Daemon.d.ts.map +1 -1
  265. package/dist/server/Daemon.js +31 -362
  266. package/dist/server/Daemon.js.map +1 -1
  267. package/dist/server/DaemonModule.d.ts +6 -5
  268. package/dist/server/DaemonModule.d.ts.map +1 -1
  269. package/dist/server/DrainSupervisor.d.ts +3 -2
  270. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  271. package/dist/server/DrainSupervisor.js +7 -3
  272. package/dist/server/DrainSupervisor.js.map +1 -1
  273. package/dist/server/Functionality.d.ts +0 -12
  274. package/dist/server/Functionality.d.ts.map +1 -1
  275. package/dist/server/Functionality.js.map +1 -1
  276. package/dist/server/FunctionalityManager.d.ts +0 -1
  277. package/dist/server/FunctionalityManager.d.ts.map +1 -1
  278. package/dist/server/FunctionalityManager.js +3 -3
  279. package/dist/server/FunctionalityManager.js.map +1 -1
  280. package/dist/server/MembersFunctionality.d.ts +2 -13
  281. package/dist/server/MembersFunctionality.d.ts.map +1 -1
  282. package/dist/server/MembersFunctionality.js +4 -4
  283. package/dist/server/MembersFunctionality.js.map +1 -1
  284. package/dist/server/SkillsFunctionality.d.ts +1 -7
  285. package/dist/server/SkillsFunctionality.d.ts.map +1 -1
  286. package/dist/server/SkillsFunctionality.js +3 -3
  287. package/dist/server/SkillsFunctionality.js.map +1 -1
  288. package/dist/server/WorkerModelResolver.d.ts +17 -0
  289. package/dist/server/WorkerModelResolver.d.ts.map +1 -0
  290. package/dist/server/WorkerModelResolver.js +166 -0
  291. package/dist/server/WorkerModelResolver.js.map +1 -0
  292. package/dist/server/daemon-results.d.ts +5 -0
  293. package/dist/server/daemon-results.d.ts.map +1 -0
  294. package/dist/server/daemon-results.js +7 -0
  295. package/dist/server/daemon-results.js.map +1 -0
  296. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  297. package/dist/server/dispatch-as-plurnk.js +4 -6
  298. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  299. package/dist/server/logEntry.d.ts +0 -1
  300. package/dist/server/logEntry.d.ts.map +1 -1
  301. package/dist/server/logEntry.js +1 -13
  302. package/dist/server/logEntry.js.map +1 -1
  303. package/dist/server/logEntry.sql +1 -5
  304. package/dist/server/loopDocs.d.ts.map +1 -1
  305. package/dist/server/loopDocs.js +11 -2
  306. package/dist/server/loopDocs.js.map +1 -1
  307. package/migrations/001_schema.sql +30 -347
  308. package/package.json +30 -30
@@ -1,48 +1,27 @@
1
1
  var _a;
2
- import WorkerName, { WorkerNameError } from "./WorkerName.js";
3
- import WorkerControlAddress from "./WorkerControlAddress.js";
4
- import { entryCoordinateOf, foldAuthorityIntoPath, renderAddress, renderTarget, schemeNameOf } from "./plurnk-uri.js";
5
- import Fork from "./fork.js";
6
- import WorkerCap from "./worker-cap.js";
7
- import { PathSyntax, TagSignal } from "@plurnk/plurnk-contracts";
2
+ import { foldAuthorityIntoPath, renderAddress, renderTarget, schemeNameOf } from "./plurnk-uri.js";
3
+ import { PathSyntax } from "@plurnk/plurnk-contracts";
8
4
  import Namespace from "./namespace.js";
9
- import LoopPolicyReader from "./LoopPolicyReader.js";
10
5
  import CapabilityResolver from "./CapabilityResolver.js";
11
- import CapabilityPolicies from "./CapabilityPolicies.js";
12
- import ChannelWrite from "./ChannelWrite.js";
13
- import { ReadProjector } from "../content/index.js";
14
6
  import SchemeCtxImpl from "./caps/SchemeCtxImpl.js";
15
- import EntryOps from "../schemes/_entry-ops.js";
16
- import EntryFind from "../schemes/_entry-find.js";
17
7
  import LoopLifecycle from "./LoopLifecycle.js";
18
- import TerminalResult from "./TerminalResult.js";
19
8
  import Results from "./results.js";
20
9
  import { OperationFailureError } from "./results.js";
21
10
  import EffectPolicy from "../schemes/EffectPolicy.js";
22
11
  import { CoreSchemeAdapterBase } from "./CoreSchemeServices.js";
23
- import { InvalidOperationResultError, } from "@plurnk/plurnk-schemes";
24
- import DurableStatement from "./DurableStatement.js";
12
+ import { InvalidOperationResultError } from "@plurnk/plurnk-schemes";
25
13
  import ResourceMutations from "./ResourceMutations.js";
14
+ import { primaryTargetOf } from "./statement-primary.js";
26
15
  import LogBody from "./LogBody.js";
27
16
  import LogVisibility from "./LogVisibility.js";
28
- import ErrorDetail from "./ErrorDetail.js";
17
+ import WorkerControlHandler from "./WorkerControlHandler.js";
18
+ import KillHandler from "./KillHandler.js";
19
+ import SendBroadcastHandler from "./SendBroadcastHandler.js";
20
+ import LogWriter from "./LogWriter.js";
21
+ import DataStatementRunner from "./DataStatementRunner.js";
29
22
  // SPEC {§scheme-surface}: writer must be in target scheme's manifest.writableBy.
30
- // OPEN/FOLD/READ/FIND are not gated — they curate the log or read, never mutating an entry.
23
+ // READ/FIND are not gated — they read, never mutating an entry.
31
24
  const MUTATING_OPS = new Set(["EDIT", "SEND", "COPY", "MOVE", "EXEC", "KILL", "FORK", "WORK"]);
32
- const assertClassifyingSignal = (statement) => {
33
- if (statement.op === "FIND"
34
- || statement.op === "READ"
35
- || statement.op === "EDIT"
36
- || statement.op === "COPY"
37
- || statement.op === "MOVE"
38
- || statement.op === "BARE") {
39
- TagSignal.applied(statement.signal);
40
- }
41
- if (statement.op === "EXEC")
42
- TagSignal.applied(statement.tags ?? null); // {§exec-tag-signal}
43
- };
44
- const primaryTargetOf = (statement) => statement.op === "COPY" || statement.op === "MOVE" ? statement.source.target : statement.target;
45
- const primaryLineMarkerOf = (statement) => statement.op === "COPY" || statement.op === "MOVE" ? statement.source.lineMarker : statement.lineMarker;
46
25
  // Op dispatch ({§op-methods-op-dispatch}): admission, operation-owner routing,
47
26
  // durable log writing, and proposal lifecycle.
48
27
  class Dispatcher {
@@ -84,6 +63,11 @@ class Dispatcher {
84
63
  #resourceMutations;
85
64
  #entryAddresses;
86
65
  #capabilities;
66
+ #workerControl;
67
+ #kill;
68
+ #sendBroadcast;
69
+ #logWriter;
70
+ #dataRun;
87
71
  constructor({ db, schemes, mimetypes, weigh, notices, proposals, interactions, executors, loopSignal, settleDerivations, settleVectors, streamEventNotify, wakeWorkerNotify, injectWorker, cancelWorker, cancelDescendants, parkDeadlines, joinTargets, liveSubscriptions, entryAddresses }) {
88
72
  this.#db = db;
89
73
  this.#schemes = schemes;
@@ -110,7 +94,7 @@ class Dispatcher {
110
94
  this.#resourceMutations = new ResourceMutations({
111
95
  schemes,
112
96
  liveSubscriptions,
113
- run: (schemeName, statement, ctx) => this.#run(schemeName, statement, ctx),
97
+ run: (schemeName, statement, ctx) => this.#dataRun.run(schemeName, statement, ctx),
114
98
  checkWritable: (statement, origin, workerId) => this.#checkWritable(statement, origin, workerId),
115
99
  checkCapabilities: (statement, workspaceId, loopId, workerId) => this.#checkCapabilities(statement, workspaceId, loopId, workerId),
116
100
  editTargetIdentity: (statement, workspaceId, workerId) => this.#editTargetIdentity(statement, workspaceId, workerId),
@@ -125,6 +109,11 @@ class Dispatcher {
125
109
  deleteChannel: (scheme, address, channel, ctx) => this.#deleteChannel(scheme, address, channel, ctx),
126
110
  applyProposal: (statement, result, resolution, ids) => this.#proposals.workerApply(statement, result, resolution, ids),
127
111
  });
112
+ this.#workerControl = new WorkerControlHandler({ db: this.#db, schemes: this.#schemes, failure: _a.#failure });
113
+ this.#kill = new KillHandler({ db: this.#db, schemes: this.#schemes, liveSubscriptions: this.#liveSubscriptions, cancelWorker: this.#cancelWorker, resolveDataEntryAddress: this.#resolveDataEntryAddress.bind(this), boundEntryContext: this.#boundEntryContext.bind(this), handlerContext: this.#handlerContext.bind(this), deleteEntry: this.#deleteEntry.bind(this), failure: _a.#failure });
114
+ this.#sendBroadcast = new SendBroadcastHandler({ db: this.#db, cancelDescendants: this.#cancelDescendants, parkDeadlines: this.#parkDeadlines, joinTargets: this.#joinTargets, lifecycle: this.#lifecycle, nextPacketBoundaries: this.#nextPacketBoundaries.bind(this), unobservedFailureCount: this.#unobservedFailureCount.bind(this), pendingSet: this.#pendingSet.bind(this), hasLiveWork: this.hasLiveWork.bind(this), failure: _a.#failure, statusResult: _a.#statusResult, unobservedFailures: _a.#unobservedFailures });
115
+ this.#logWriter = new LogWriter({ db: this.#db, weighContent: this.#weighContent, extractTarget: this.#extractTarget.bind(this), canonColumns: this.#canonColumns.bind(this), signalToJson: this.#signalToJson.bind(this), isProposal: _a.#isProposal });
116
+ this.#dataRun = new DataStatementRunner({ schemes: this.#schemes, liveSubscriptions: this.#liveSubscriptions, resolveDataEntryAddress: this.#resolveDataEntryAddress.bind(this), fixedEntryOwnerId: this.#fixedEntryOwnerId.bind(this), prepareDataRepresentation: this.#prepareDataRepresentation.bind(this), failure: _a.#failure });
128
117
  }
129
118
  // workspace → project_root, memoized: {§fs-namespace} fixes the root immutably at
130
119
  // workspace creation, so a process-lifetime cache can never go stale.
@@ -279,21 +268,57 @@ class Dispatcher {
279
268
  identity: renderTarget(target),
280
269
  };
281
270
  }
271
+ // {§kill-scope-entry} — a scoped KILL on an entry-bearing scheme is one EDIT with an empty
272
+ // body over the same marker: prepared, gated, and merged as an EDIT while its log row
273
+ // records the model's KILL. Schemes that implement kill() (streams) take the scope themselves;
274
+ // the log's scoped KILL is curation.
275
+ #scopedEntryEdits = new WeakMap();
276
+ #scopedEntryEdit(statement, functionalityWorkerId) {
277
+ if (statement.op === "EDIT")
278
+ return statement;
279
+ // A body pattern is a log selector; a scoped KILL carrying one is not an EDIT — the KILL
280
+ // handler refuses it ({§kill-scope-entry}).
281
+ if (statement.op !== "KILL" || statement.lineMarker === null || statement.body !== null)
282
+ return null;
283
+ const cached = this.#scopedEntryEdits.get(statement);
284
+ if (cached !== undefined)
285
+ return cached;
286
+ const schemeName = schemeNameOf(statement.target);
287
+ if (schemeName === null || schemeName === "log")
288
+ return null;
289
+ const handler = this.#schemes.get(schemeName, functionalityWorkerId);
290
+ if (handler === undefined || typeof handler.kill === "function")
291
+ return null;
292
+ const edit = {
293
+ op: "EDIT",
294
+ delimiter: statement.delimiter,
295
+ annotation: statement.annotation,
296
+ metadata: statement.metadata,
297
+ target: statement.target,
298
+ lineMarker: statement.lineMarker,
299
+ body: "",
300
+ position: statement.position,
301
+ };
302
+ this.#scopedEntryEdits.set(statement, edit);
303
+ return edit;
304
+ }
282
305
  async prepareEditBatches(statements, context) {
283
- for (const statement of statements)
284
- assertClassifyingSignal(statement); // {§log-tag-signal}
285
306
  const { workspaceId, workerId, functionalityWorkerId, loopId, turnId, origin } = context;
286
307
  const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, functionalityWorkerId, loopId, turnId, origin });
287
- await this.#resourceMutations.prepareEditBatches(statements, context, schemeCtx);
308
+ const edits = statements.flatMap((statement) => {
309
+ const edit = this.#scopedEntryEdit(statement, schemeCtx.functionalityWorkerId);
310
+ return edit === null ? [] : [edit];
311
+ });
312
+ await this.#resourceMutations.prepareEditBatches(edits, context, schemeCtx);
288
313
  }
289
314
  async dispatch(context) {
290
- assertClassifyingSignal(context.statement); // {§log-tag-signal}
291
315
  let result = await this.#dispatchOne(context);
292
- if (context.statement.op === "EDIT") {
316
+ const edit = context.statement.op === "EDIT" ? context.statement : this.#scopedEntryEdits.get(context.statement);
317
+ if (edit !== undefined) {
293
318
  // {§edit-batch-merges} — a proposal's apply result replaces the projected one; the
294
319
  // statement's merge facts ride every EDIT result, whichever route produced it.
295
- result = this.#resourceMutations.withMergeFacts(context.statement, result);
296
- this.#resourceMutations.settleEdit(context.statement, result);
320
+ result = this.#resourceMutations.withMergeFacts(edit, result);
321
+ this.#resourceMutations.settleEdit(edit, result);
297
322
  }
298
323
  return result;
299
324
  }
@@ -320,7 +345,7 @@ class Dispatcher {
320
345
  result = await this.#resourceMutations.preparedEditResult(statement);
321
346
  }
322
347
  else if (statement.op === "SEND" && statement.target === null) {
323
- result = await this.#handleSendBroadcast(statement, {
348
+ result = await this.#sendBroadcast.handleSendBroadcast(statement, {
324
349
  workspaceId,
325
350
  workerId,
326
351
  loopId,
@@ -329,15 +354,13 @@ class Dispatcher {
329
354
  origin,
330
355
  });
331
356
  }
332
- else if (statement.op === "OPEN"
333
- || statement.op === "FOLD"
334
- || (statement.op === "KILL" && schemeNameOf(statement.target) === "log")) {
357
+ else if (statement.op === "KILL" && schemeNameOf(statement.target) === "log") {
335
358
  const curation = await this.#runLogCuration(statement, schemeCtx);
336
359
  result = curation.result;
337
360
  curationPlan = curation.plan;
338
361
  }
339
362
  else if (statement.op === "FORK" || statement.op === "WORK") {
340
- result = await this.#handleWorkerControl(statement, schemeCtx);
363
+ result = await this.#workerControl.handleWorkerControl(statement, schemeCtx);
341
364
  }
342
365
  else if (statement.op === "COPY") {
343
366
  result = await this.#resourceMutations.handleCopy(statement, schemeCtx);
@@ -345,8 +368,11 @@ class Dispatcher {
345
368
  else if (statement.op === "MOVE") {
346
369
  result = await this.#resourceMutations.handleMove(statement, schemeCtx);
347
370
  }
371
+ else if (statement.op === "KILL" && this.#scopedEntryEdits.has(statement)) {
372
+ result = await this.#resourceMutations.preparedEditResult(this.#scopedEntryEdits.get(statement));
373
+ }
348
374
  else if (statement.op === "KILL") {
349
- result = await this.#handleKill(statement, schemeCtx);
375
+ result = await this.#kill.handleKill(statement, schemeCtx);
350
376
  }
351
377
  else if (statement.op === "PLAN") {
352
378
  result = this.#handlePlan(statement);
@@ -354,10 +380,10 @@ class Dispatcher {
354
380
  else if (statement.op === "EXEC") {
355
381
  // EXEC routes unconditionally to its operation owner after
356
382
  // the shared capability resolver admits its runtime/tool.
357
- result = await this.#run("exec", statement, schemeCtx);
383
+ result = await this.#dataRun.run("exec", statement, schemeCtx);
358
384
  }
359
385
  else {
360
- result = await this.#run(schemeNameOf(statement.target), statement, schemeCtx); // {§op-methods-op-dispatch}
386
+ result = await this.#dataRun.run(schemeNameOf(statement.target), statement, schemeCtx); // {§op-methods-op-dispatch}
361
387
  }
362
388
  }
363
389
  catch (err) { // a scheme exception becomes the op's 500 outcome — {§scheme-surface-exception-500}
@@ -383,7 +409,7 @@ class Dispatcher {
383
409
  // {§join-blocking-collect}
384
410
  if (typeof result.awaitWorker === "string")
385
411
  this.#joinTargets.add(loopId);
386
- const logEntryId = await this.#writeLog({
412
+ const logEntryId = await this.#logWriter.writeLog({
387
413
  statement,
388
414
  result,
389
415
  functionalityWorkerId,
@@ -484,7 +510,7 @@ class Dispatcher {
484
510
  const denial = await this.#checkCapabilities(statement, schemeCtx.workspaceId, loopId, schemeCtx.functionalityWorkerId);
485
511
  if (denial !== null)
486
512
  return denial;
487
- return this.#run(schemeNameOf(statement.target), statement, schemeCtx);
513
+ return this.#dataRun.run(schemeNameOf(statement.target), statement, schemeCtx);
488
514
  }
489
515
  capabilityProjection(workspaceId, workerId) {
490
516
  return this.#capabilities.projection(workspaceId, workerId);
@@ -587,7 +613,7 @@ class Dispatcher {
587
613
  retryable: false,
588
614
  });
589
615
  }
590
- return Results.assertReadResult(await this.#run(schemeName, statement, ctx));
616
+ return Results.assertReadResult(await this.#dataRun.run(schemeName, statement, ctx));
591
617
  }
592
618
  // The one place per-dispatch coordinates are built; a caller that carries no
593
619
  // explicit Functionality coordinate acts in its own Worker's
@@ -722,209 +748,6 @@ class Dispatcher {
722
748
  #isWorkerControl(statement) {
723
749
  return statement.op === "FORK" || statement.op === "WORK"; // worker control targets worker://<name> (grammar 0.74.55)
724
750
  }
725
- // WORK and FORK name the new worker in the target authority and carry its seed task in the body.
726
- // Their distinct fresh/branched histories are specified by {§worker-scheme-spawn} and {§worker-scheme-fork}.
727
- async #handleWorkerControl(statement, ctx) {
728
- const address = WorkerControlAddress.resolve(statement.target, statement.op);
729
- if (!address.ok)
730
- return address.result;
731
- const name = address.authority;
732
- try {
733
- WorkerName.assert(name); // {§worker-name-minting}
734
- }
735
- catch (error) {
736
- if (!(error instanceof WorkerNameError))
737
- throw error;
738
- return _a.#failure(`worker-${error.code}`, 400, error.message, {}, {
739
- operation: statement.op,
740
- worker: error.workerName,
741
- recovery: error.recovery,
742
- retryable: false,
743
- });
744
- }
745
- if (ctx.injectWorker === undefined)
746
- throw new Error("worker control: injectWorker capability absent");
747
- const denied = await WorkerCap.deny(this.#db, ctx.workspaceId);
748
- if (denied !== null)
749
- return denied;
750
- const prompt = statement.body;
751
- // {§worker-delegation-inherits-policy} — authority flows down the
752
- // delegation edge. The child receives the delegator's complete loop
753
- // policy and an immutable snapshot of its effective capability bound.
754
- const policy = await LoopPolicyReader.read(this.#db, ctx.loopId);
755
- const capabilityBound = await CapabilityPolicies.delegationBound(this.#db, ctx.workspaceId, ctx.workerId, policy);
756
- const delegationPolicy = {
757
- ...policy,
758
- capabilities: capabilityBound,
759
- };
760
- // A name is frozen per worker but reclaimable across time ({§machine-processes-worker-origin}): a LIVE
761
- // sister holding it is a 409 (legible, never a raw UNIQUE 500); a free/terminated name reclaims.
762
- const live = await this.#db.worker_live_by_name.get({ workspace_id: ctx.workspaceId, name });
763
- if (live !== undefined) {
764
- return _a.#failure("worker-already-running", 409, `Worker '${name}' is already running.`, {}, { worker: name, retryable: false });
765
- }
766
- if (statement.signal !== null && statement.signal !== undefined) {
767
- // {§worker-spawn-no-signal} (#396) — WORK/FORK take no signal. Branch
768
- // delegation was removed outright; a model manages git branches through
769
- // ordinary EXEC git, taught by the git skill, never engine machinery.
770
- return _a.#failure("worker-signal-invalid", 400, `${statement.op} takes no signal.`, {}, {
771
- worker: name,
772
- signal: statement.signal,
773
- recovery: `Spawn without a signal: \`## ${statement.op}0 (worker://${name})\` with the prompt as the body.`,
774
- retryable: false,
775
- });
776
- }
777
- if (statement.op === "FORK") {
778
- // Branch the current worker's log into a named sister.
779
- const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name, capabilityBound, (scheme) => this.#schemes.entryInheritanceForStoredScheme(scheme, ctx.workerId));
780
- await ctx.injectWorker({
781
- workspaceId: ctx.workspaceId,
782
- workerId: branchWorkerId,
783
- sourceWorkerId: ctx.workerId,
784
- prompt,
785
- freshLoopPolicy: delegationPolicy,
786
- parentLoopId: ctx.loopId,
787
- });
788
- return { status: 200, body: name };
789
- }
790
- // WORK — a fresh worker sister named <name>.
791
- const row = await this.#db.fork_insert_worker.get({
792
- workspace_id: ctx.workspaceId, name, parent_worker_id: ctx.workerId, origin: ctx.writer,
793
- fork_snapshot: 0,
794
- capability_bound: JSON.stringify(capabilityBound),
795
- });
796
- if (row === undefined)
797
- throw new Error("worker spawn: worker insert returned no row");
798
- await ctx.injectWorker({
799
- workspaceId: ctx.workspaceId,
800
- workerId: row.id,
801
- sourceWorkerId: ctx.workerId,
802
- prompt,
803
- freshLoopPolicy: delegationPolicy,
804
- parentLoopId: ctx.loopId,
805
- });
806
- return { status: 200, body: name };
807
- }
808
- // KILL is target-polymorphic. Scheme handlers own the optional numeric code's
809
- // meaning; core retains worker and entry dispatch. {§operation-code-polymorphism}
810
- async #handleKill(statement, ctx) {
811
- if (statement.op !== "KILL")
812
- throw new Error("unreachable");
813
- const path = statement.target;
814
- if (path === null) {
815
- return _a.#failure("kill-target-required", 400, "KILL requires a target path.", {}, { retryable: false });
816
- }
817
- const schemeName = schemeNameOf(path);
818
- if (schemeName === null) {
819
- return _a.#failure("kill-target-scheme-required", 400, "KILL target requires a scheme.", {}, { retryable: false });
820
- }
821
- const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
822
- const coordinate = entryCoordinateOf(path, manifest?.authority ?? "namespace");
823
- // log:/// KILL has already gone through the projection-curation owner.
824
- // This path owns scheme-specific world and process KILL semantics.
825
- // Process-KILL: any scheme whose handler exposes kill() aborts a live stream — the
826
- // exec handler, registered as "exec" + under every runtime tag (sh/node), so a tag-
827
- // addressed stream (sh:///l/t/s) routes here, not to deleteEntry. {§exec}
828
- const killable = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
829
- if (killable !== undefined && typeof killable.kill === "function") {
830
- // Pass the model's OWN scheme so a stream-KILL error answers in the runtime tag the
831
- // model addressed (sh:///…), not the internal `exec` ({§fs-answer-in-canon}).
832
- let handlerCtx;
833
- if (manifest?.category === "data") {
834
- const resolved = await this.#resolveDataEntryAddress({
835
- target: path,
836
- routedScheme: schemeName,
837
- handler: killable,
838
- manifest,
839
- ctx,
840
- });
841
- if (resolved.result !== null)
842
- return resolved.result;
843
- if (resolved.address === null) {
844
- return _a.#failure("entry-not-found", 404, `No entry exists at ${renderAddress({ scheme: schemeName, ...coordinate })}.`);
845
- }
846
- handlerCtx = this.#boundEntryContext(schemeName, resolved.address, ctx);
847
- }
848
- else {
849
- handlerCtx = await this.#handlerContext(schemeName, ctx, coordinate.authority);
850
- }
851
- if (handlerCtx === null) {
852
- throw new InvalidOperationResultError(`Registered scheme '${schemeName}' has no dispatch context.`);
853
- }
854
- return await killable.kill(coordinate.pathname, statement.signal, handlerCtx, schemeName);
855
- }
856
- if (schemeName === "worker") {
857
- // Entry-path present → KILL a private owner-held entry (delete it), self-only —
858
- // NOT worker cancellation. The authority (hostname) names the owner, the pathname the
859
- // entry; only the path-ABSENT form (worker://<name>) terminates the worker-as-actor. {§worker-scheme}
860
- const entryPath = path.kind === "url" ? (path.pathname ?? "") : "";
861
- if (entryPath !== "" && entryPath !== "/") {
862
- const workerHandler = this.#schemes.get("worker");
863
- if (manifest?.category !== "data") {
864
- throw new InvalidOperationResultError("Registered scheme 'worker' is not entry-bearing.");
865
- }
866
- const resolved = await this.#resolveDataEntryAddress({
867
- target: path,
868
- routedScheme: "worker",
869
- handler: workerHandler,
870
- manifest,
871
- ctx,
872
- });
873
- if (resolved.result !== null)
874
- return resolved.result;
875
- if (resolved.address === null) {
876
- return _a.#failure("entry-not-found", 404, "The worker entry does not exist.");
877
- }
878
- const handlerCtx = this.#boundEntryContext("worker", resolved.address, ctx);
879
- if (handlerCtx === null) {
880
- throw new InvalidOperationResultError("Registered scheme 'worker' has no dispatch context.");
881
- }
882
- return await workerHandler.killEntry(statement, handlerCtx);
883
- }
884
- const address = WorkerControlAddress.resolve(path, "KILL");
885
- if (!address.ok)
886
- return address.result;
887
- // `~` is the sole current-worker sigil; every other authority is a literal name.
888
- // An idle worker is a no-op 200; a missing named worker is 404. {§worker-control-addressing}
889
- const name = address.authority;
890
- let workerId = ctx.workerId;
891
- if (name !== "~") {
892
- const row = await this.#db.worker_resolve_by_name.get({ workspace_id: ctx.workspaceId, name });
893
- if (row === undefined) {
894
- return _a.#failure("worker-not-found", 404, `Worker '${name}' does not exist in this workspace.`, {}, { worker: name, retryable: false });
895
- }
896
- workerId = row.id;
897
- }
898
- if (this.#cancelWorker === undefined)
899
- throw new Error("worker kill: cancelWorker capability absent");
900
- // {§op-synchronous} — KILL is decisive. Await the one lifecycle owner so the
901
- // same-turn pending-work gate observes the complete subtree as terminal.
902
- await this.#cancelWorker(workerId, "killed via worker:// KILL");
903
- return { status: 200 };
904
- }
905
- if (!this.#schemes.has(schemeName, ctx.functionalityWorkerId)) {
906
- return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
907
- }
908
- const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
909
- if (handler === undefined || manifest?.category !== "data") {
910
- return _a.#failure("entry-operation-unsupported", 400, `KILL requires an entry-bearing target; '${schemeName}' does not provide one.`, {}, { scheme: schemeName, retryable: false });
911
- }
912
- const resolved = await this.#resolveDataEntryAddress({
913
- target: path,
914
- routedScheme: schemeName,
915
- handler,
916
- manifest,
917
- ctx,
918
- });
919
- if (resolved.result !== null)
920
- return resolved.result;
921
- if (resolved.address === null) {
922
- return _a.#failure("entry-not-found", 404, "The KILL target does not exist.");
923
- }
924
- // A host-effecting delete (file) returns 202 to PROPOSE — pass its attrs through so the proposal
925
- // carries the delete target to review (#isProposal fires on 202). Plurnk-internal deletes execute inline.
926
- return this.#deleteEntry(schemeName, resolved.address, ctx);
927
- }
928
751
  async #writeActionlessEntry({ verbatim, workerId, loopId, turnId, sequence, origin, kind, folded, modelCallId = null, attrs = {} }) {
929
752
  const durableAttrs = { ...attrs, kind };
930
753
  const rx = JSON.stringify({ content: verbatim, mimetype: "text/vnd.plurnk" });
@@ -987,9 +810,8 @@ class Dispatcher {
987
810
  // {§bare-inference} Provider work is prepared concurrently by Engine; the
988
811
  // dispatcher owns only the ordinary operation-result commit and notification.
989
812
  async recordBareResult(context, result, modelCallId) {
990
- assertClassifyingSignal(context.statement);
991
813
  Results.assert(result);
992
- const logEntryId = await this.#writeLog({
814
+ const logEntryId = await this.#logWriter.writeLog({
993
815
  ...context,
994
816
  functionalityWorkerId: context.functionalityWorkerId ?? context.workerId,
995
817
  result,
@@ -1009,8 +831,10 @@ class Dispatcher {
1009
831
  // BY STATED INTENT and is never gated.
1010
832
  async #pendingSet(workerId, turnId) {
1011
833
  const pending = [];
1012
- const openSubs = await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId });
1013
834
  const execHandler = this.#schemes.get("exec");
835
+ // {§exec-timeout} — a `<-1>` spawn outlives the loop and is nobody's obligation.
836
+ const openSubs = (await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId }))
837
+ .filter(({ id }) => execHandler?.isDetachedSpawn?.(id) !== true);
1014
838
  if (openSubs.length > 0 || execHandler?.hasActiveSpawns?.(workerId) === true)
1015
839
  pending.push("streams");
1016
840
  const liveChild = await this.#db.engine_worker_has_live_child.get({ worker_id: workerId });
@@ -1042,8 +866,10 @@ class Dispatcher {
1042
866
  .get({ worker_id: workerId }),
1043
867
  ]);
1044
868
  return {
1045
- retrievals: turnBoundaries.some(({ op }) => op !== "FOLD"),
1046
- folds: turnBoundaries.some(({ op }) => op === "FOLD"),
869
+ // {§log-kill-scope} — a log KILL is housekeeping: it continues an empty (WAIT) but never
870
+ // blocks an explicit (TERM); every other boundary row is a retrieval receipt.
871
+ retrievals: turnBoundaries.some(({ op }) => op !== "KILL"),
872
+ folds: turnBoundaries.some(({ op }) => op === "KILL"),
1047
873
  streamTerminations,
1048
874
  childTerminations: childTermination !== undefined,
1049
875
  };
@@ -1066,374 +892,17 @@ class Dispatcher {
1066
892
  // J — a live obligation to WAIT on: a spawned child or an open stream (NOT retrievals, which land
1067
893
  // next turn regardless). The wait-side twin of #pendingSet's stream+child legs ({§wait-obligation-matrix}).
1068
894
  async hasLiveWork(workerId) {
1069
- const openSubs = await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId });
895
+ const execHandler = this.#schemes.get("exec");
896
+ // {§exec-timeout} — a `<-1>` spawn outlives the loop and is nobody's obligation.
897
+ const openSubs = (await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId }))
898
+ .filter(({ id }) => execHandler?.isDetachedSpawn?.(id) !== true);
1070
899
  if (openSubs.length > 0)
1071
900
  return true;
1072
- const execHandler = this.#schemes.get("exec");
1073
901
  if (execHandler?.hasActiveSpawns?.(workerId) === true)
1074
902
  return true;
1075
903
  const liveChild = await this.#db.engine_worker_has_live_child.get({ worker_id: workerId });
1076
904
  return liveChild !== undefined;
1077
905
  }
1078
- async #handleSendBroadcast(statement, ctx) {
1079
- if (statement.op !== "SEND")
1080
- throw new Error("unreachable");
1081
- const { workerId, loopId, turnId } = ctx;
1082
- const status = statement.signal;
1083
- if (status === null) {
1084
- return _a.#failure("send-status-required", 400, "SEND requires a numeric status.", {}, { retryable: false });
1085
- }
1086
- const raw = statement.body === null ? "" : statement.body.raw;
1087
- // The park rides SEND signal 202 only ({§park-202-only}). A scoped signal 102 is neither
1088
- // a wait nor a meaningful continuation, so reject it instead of preserving the
1089
- // retired dual spelling.
1090
- if (status === 102 && statement.lineMarker !== null) {
1091
- return _a.#failure("send-scope-invalid", 400, "`## SEND0 [102]` does not accept a scope.", {}, {
1092
- requestedStatus: 102,
1093
- scope: statement.lineMarker,
1094
- recovery: "Use `## SEND0 [202] <scope>` to wait, or remove the scope to continue.",
1095
- retryable: false,
1096
- });
1097
- }
1098
- // A bare continue after an armed running-worker READ becomes an
1099
- // indefinite park. {§join-blocking-collect}
1100
- const joinArmed = this.#joinTargets.delete(loopId);
1101
- if (status === 102 && statement.lineMarker === null && joinArmed) {
1102
- if (!await this.#lifecycle.park(loopId)) {
1103
- return _a.#statusResult(await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to park it.");
1104
- }
1105
- this.#parkDeadlines.set(loopId, -1); // indefinite: the bounded child's terminal is the wake edge
1106
- return { status: 102, attrs: { parked: -1, join: true } };
1107
- }
1108
- // {§wait-obligation-matrix} — SEND signal 202 is the obligation-checked join. A live
1109
- // obligation (a spawned child or open stream, J) BLOCKS the loop until it concludes and
1110
- // reawakens it ({§worker-lifecycle-child-wake}); a wait on nothing (∅) is already satisfied and
1111
- // resolves like 200, so <-1>+∅ self-resolves rather than hang the agent; a pending own
1112
- // retrieval (R) just lands next turn, so the wait continues.
1113
- if (status === 202) {
1114
- const marks = statement.lineMarker?.marks[0];
1115
- // `<T>` is MINUTES, held in seconds; bare 202 / absent T = indefinite, bounded by the join.
1116
- const seconds = typeof marks === "number" ? (marks > 0 ? marks * 60 : marks) : -1;
1117
- if (await this.hasLiveWork(workerId)) {
1118
- if (!await this.#lifecycle.park(loopId)) {
1119
- return _a.#statusResult(await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to wait.");
1120
- }
1121
- this.#parkDeadlines.set(loopId, seconds);
1122
- return { status: 202, attrs: { waiting: seconds } };
1123
- }
1124
- // Retrievals, fast stream conclusions, and child conclusions are
1125
- // all complete-but-unobserved. Their wake edge may already have
1126
- // fired, so do not park; continue directly to the packet that
1127
- // materializes them.
1128
- const boundaries = await this.#nextPacketBoundaries(workerId, turnId);
1129
- if (boundaries.retrievals || boundaries.folds || boundaries.streamTerminations.length > 0 || boundaries.childTerminations) {
1130
- return { status: 102 };
1131
- }
1132
- const failCount = await this.#unobservedFailureCount(turnId);
1133
- if (failCount > 0)
1134
- return _a.#unobservedFailures(failCount);
1135
- // The joined set is already drained. Awaiting an empty task group completes
1136
- // immediately; it never parks and needs no corrective model turn.
1137
- const finished = await this.#lifecycle.finish(loopId, TerminalResult.success(raw));
1138
- return {
1139
- status: finished !== null ? 200 : await this.#lifecycle.status(loopId),
1140
- attrs: { joined: true, pending: 0 },
1141
- };
1142
- }
1143
- // [200] — terminate, gated by the pending set (post-batch). The row records the refused
1144
- // attempt faithfully (status_rx=409, never erased); the loop stays a continue; the strike
1145
- // couples in runTurn. [499] abandons and cancels the descendant scope.
1146
- if (status === 200) {
1147
- // Model completion is a claim about the Worker's observed work and
1148
- // therefore crosses the pending-result rails. A `_plurnk`
1149
- // maintenance program closes only its own administrative loop; it
1150
- // must not claim, consume, or be blocked by model work elsewhere in
1151
- // the same Worker.
1152
- if (ctx.origin === "model") {
1153
- // {§send-premature-terminate} — same-turn failures are unobserved
1154
- // pending results and therefore refuse completion.
1155
- const failCount = await this.#unobservedFailureCount(turnId);
1156
- if (failCount > 0)
1157
- return _a.#unobservedFailures(failCount);
1158
- const pending = await this.#pendingSet(workerId, turnId);
1159
- if (pending.length > 0) {
1160
- // A receipts-only refusal needs no KILL/park remedy menu: the results simply
1161
- // arrive in the next packet. Streams and children retain their remedy steer.
1162
- const receiptsOnly = pending.every((kind) => kind === "receipts");
1163
- if (receiptsOnly) {
1164
- return _a.#failure("retrieval-results-unobserved", 409, "Completion preceded this turn's operation results; they enter the next packet.", {}, {
1165
- pending: [...pending],
1166
- stage: "completion",
1167
- retryable: false,
1168
- });
1169
- }
1170
- return _a.#failure("work-remains", 409, "Completion encountered pending work or results.", {}, {
1171
- pending: [...pending],
1172
- stage: "completion",
1173
- retryable: false,
1174
- });
1175
- }
1176
- }
1177
- const finished = await this.#lifecycle.finish(loopId, TerminalResult.success(raw));
1178
- return _a.#statusResult(finished !== null ? 200 : await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to conclude it.");
1179
- }
1180
- if (status === 499) {
1181
- const reason = raw === "" ? null : ErrorDetail.preview(raw);
1182
- const failure = _a.#failure("scope-abandoned", 499, "The worker ended its scope with SEND[499].", {}, {
1183
- ...(reason === null ? {} : { reason }),
1184
- retryable: false,
1185
- });
1186
- const seqs = await this.#db.engine_loop_turn_seqs.get({
1187
- loop_id: loopId,
1188
- turn_id: turnId,
1189
- });
1190
- if (seqs === undefined) {
1191
- throw new Error(`SEND signal 499: no coordinate for loop=${loopId} turn=${turnId}`);
1192
- }
1193
- Results.attachInstance(failure, `log:///${seqs.loop_seq}/${seqs.turn_seq}/${ctx.sequence}/SEND`);
1194
- const finished = await this.#lifecycle.finish(loopId, failure);
1195
- if (finished === null)
1196
- return _a.#statusResult(await this.#lifecycle.status(loopId), "loop-already-terminal", "The loop was already terminal when SEND attempted to abandon it.");
1197
- await this.#cancelDescendants?.(workerId, reason ?? "parent worker ended its scope with SEND[499]");
1198
- return failure;
1199
- }
1200
- // Every other signal — 102 bare, 202 (retired as a terminal; now ordinary mid-comms), 1xx —
1201
- // is a plain broadcast row: no loop transition.
1202
- return _a.#statusResult(status, "send-broadcast-failed", raw === "" ? `SEND broadcast reported status ${status}.` : raw);
1203
- }
1204
- async #run(schemeName, statement, ctx) {
1205
- if (schemeName === null) {
1206
- const fields = statement.op === "READ"
1207
- ? { content: null, mimetype: null, channel: null }
1208
- : statement.op === "FIND"
1209
- ? {
1210
- content: null,
1211
- mimetype: null,
1212
- results: [],
1213
- itemsWeightTotal: 0,
1214
- returnedItemsWeightTotal: 0,
1215
- matchingPathCount: 0,
1216
- matchLocationCount: 0,
1217
- }
1218
- : {};
1219
- return _a.#failure("target-scheme-required", 400, `${statement.op} requires a target scheme.`, fields, { operation: statement.op, retryable: false });
1220
- }
1221
- const manifest = this.#schemes.manifestFor(schemeName, ctx.functionalityWorkerId);
1222
- const handler = this.#schemes.get(schemeName, ctx.functionalityWorkerId);
1223
- if (handler === undefined) {
1224
- return _a.#failure("scheme-not-found", 501, `Scheme '${schemeName}' is not registered.`, {}, { scheme: schemeName, retryable: false });
1225
- }
1226
- const methodName = statement.op.toLowerCase();
1227
- const method = handler[methodName];
1228
- const addressedScheme = statement.target?.kind === "url" ? statement.target.scheme : null;
1229
- if (manifest === undefined)
1230
- throw new Error(`scheme '${schemeName}' has no manifest`);
1231
- // Metadata belongs to the handler that receives it. EXEC over a
1232
- // non-file resource is the one split route: exec hosts the operation,
1233
- // while the canonically routed source scheme receives the metadata.
1234
- const metadataScheme = schemeName === "exec"
1235
- && statement.target?.kind === "url"
1236
- && statement.target.scheme !== "file"
1237
- ? schemeNameOf(statement.target) ?? schemeName
1238
- : schemeName;
1239
- const metadataManifest = metadataScheme === schemeName
1240
- ? manifest
1241
- : this.#schemes.manifestFor(metadataScheme, ctx.functionalityWorkerId);
1242
- if (statement.metadata !== null && metadataManifest === undefined) {
1243
- return _a.#failure("scheme-not-found", 501, `Scheme '${metadataScheme}' is not registered.`, {}, { scheme: metadataScheme, retryable: false });
1244
- }
1245
- if (statement.metadata !== null && metadataManifest?.metadataModifier !== true) {
1246
- return _a.#failure("scheme-metadata-unsupported", 400, `Scheme '${metadataScheme}' does not accept the {metadata} modifier.`, {}, {
1247
- scheme: metadataScheme,
1248
- operation: statement.op,
1249
- retryable: false,
1250
- });
1251
- }
1252
- const publishedChannel = statement.target?.kind === "url"
1253
- ? statement.target.fragment ?? manifest.defaultChannel
1254
- : manifest.defaultChannel;
1255
- const authoredCoordinate = statement.target === null
1256
- ? { authority: "", pathname: "" }
1257
- : entryCoordinateOf(statement.target, manifest.authority ?? "namespace");
1258
- // EXEC's authored target belongs to its declared invocation contract;
1259
- // a resource target is input to the executor, never the output-stream
1260
- // address owned by the internal exec scheme. {§exec-target-routing}
1261
- const addressResolution = manifest.category === "data"
1262
- && statement.target !== null
1263
- && statement.op !== "EXEC"
1264
- ? await this.#resolveDataEntryAddress({
1265
- target: statement.target,
1266
- routedScheme: schemeName,
1267
- handler: handler,
1268
- manifest,
1269
- ctx,
1270
- })
1271
- : null;
1272
- if (addressResolution?.result !== null && addressResolution?.result !== undefined) {
1273
- return addressResolution.result;
1274
- }
1275
- const operationAddress = addressResolution?.address ?? null;
1276
- const schemeCtx = new SchemeCtxImpl(ctx, addressedScheme ?? schemeName, manifest, this.#liveSubscriptions, {
1277
- authority: operationAddress?.authority ?? authoredCoordinate.authority,
1278
- ownerId: operationAddress?.ownerId ?? await this.#fixedEntryOwnerId(manifest, ctx),
1279
- publishedChannel,
1280
- });
1281
- if (statement.op === "SEND"
1282
- && statement.signal === 499
1283
- && statement.target?.kind === "url"
1284
- && operationAddress !== null) {
1285
- const addressedScheme = statement.target.scheme;
1286
- const entry = await this.#db.crud_find_workspace_entry.get({
1287
- workspace_id: ctx.workspaceId,
1288
- owner_id: operationAddress.ownerId,
1289
- scheme: operationAddress.scheme,
1290
- authority: operationAddress.authority,
1291
- pathname: operationAddress.pathname,
1292
- });
1293
- if (entry !== undefined) {
1294
- const subscription = await ChannelWrite.findActiveSubscription(this.#db, {
1295
- workerId: ctx.workerId,
1296
- entryId: entry.id,
1297
- });
1298
- if (subscription !== null && subscription.scheme === addressedScheme) {
1299
- const cancelled = await this.#liveSubscriptions.cancel(subscription.id);
1300
- if (!cancelled) {
1301
- throw new InvalidOperationResultError(`Subscription ${subscription.id} is durable but has no live cancellation handle.`);
1302
- }
1303
- return { status: 200 };
1304
- }
1305
- }
1306
- }
1307
- if (statement.op === "READ"
1308
- && handler instanceof CoreSchemeAdapterBase
1309
- && typeof handler.resolveCoreRepresentation === "function") {
1310
- const selectionNeutralTarget = statement.target?.kind === "url"
1311
- ? {
1312
- ...statement.target,
1313
- raw: renderTarget({ ...statement.target, fragment: null }) ?? statement.target.raw,
1314
- fragment: null,
1315
- }
1316
- : statement.target;
1317
- const resolved = await handler
1318
- .resolveCoreRepresentation(selectionNeutralTarget, schemeCtx);
1319
- if ("result" in resolved)
1320
- return Results.assertReadResult(resolved.result);
1321
- if (selectionNeutralTarget === null) {
1322
- throw new InvalidOperationResultError(`Core scheme '${schemeName}' resolved a targetless READ representation.`);
1323
- }
1324
- const target = renderTarget(selectionNeutralTarget.kind === "url"
1325
- ? selectionNeutralTarget
1326
- : { scheme: null, pathname: selectionNeutralTarget.raw });
1327
- if (target === null) {
1328
- throw new TypeError(`Core scheme '${schemeName}' resolved an unrenderable READ target.`);
1329
- }
1330
- return Results.assertReadResult(await ReadProjector.project({
1331
- statement,
1332
- manifest,
1333
- target,
1334
- identity: target,
1335
- representation: resolved.representation,
1336
- mimetypes: ctx.mimetypes,
1337
- }));
1338
- }
1339
- if (statement.op === "READ" && manifest.category === "data") {
1340
- const prepared = statement.target === null
1341
- ? { address: null, result: null }
1342
- : await this.#prepareDataRepresentation({
1343
- target: statement.target,
1344
- metadata: statement.metadata,
1345
- routedScheme: schemeName,
1346
- handler: handler,
1347
- manifest,
1348
- ctx,
1349
- publishedChannel,
1350
- ...(addressResolution === null ? {} : { resolved: addressResolution }),
1351
- });
1352
- if (prepared.result !== null)
1353
- return prepared.result;
1354
- const resolved = prepared.address;
1355
- if (statement.target !== null && resolved === null) {
1356
- const target = renderTarget(statement.target.kind === "url"
1357
- ? statement.target
1358
- : { scheme: null, pathname: statement.target.raw });
1359
- return Results.failure(`scheme:${schemeName}`, "entry-not-found", 404, `No entry exists at ${target ?? "the requested address"}.`, { content: null, mimetype: null, channel: null }, { target });
1360
- }
1361
- const storageScheme = resolved?.scheme ?? manifest.storedScheme ?? addressedScheme ?? schemeName;
1362
- const projected = Results.assertReadResult(await EntryOps.readWorkspaceEntry(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, resolved === null
1363
- ? null
1364
- : {
1365
- ownerId: resolved.ownerId,
1366
- authority: resolved.authority,
1367
- pathname: resolved.pathname,
1368
- }));
1369
- return projected;
1370
- }
1371
- if (statement.op !== "FIND" || manifest.category !== "data") {
1372
- if (typeof method === "function") {
1373
- return Results.assert(await method.call(handler, statement, schemeCtx));
1374
- }
1375
- return _a.#failure("operation-not-implemented", 501, `Scheme '${schemeName}' does not implement ${statement.op}.`, {}, {
1376
- scheme: schemeName,
1377
- operation: statement.op,
1378
- retryable: false,
1379
- });
1380
- }
1381
- const targetPathname = statement.target?.kind === "url"
1382
- ? statement.target.pathname
1383
- : statement.target?.raw ?? "";
1384
- const collectionTarget = manifest.folderScopes === true
1385
- && (targetPathname === "" || targetPathname.endsWith("/"));
1386
- const exactTarget = statement.target !== null
1387
- && !collectionTarget
1388
- && !PathSyntax.hasGlob(authoredCoordinate.pathname);
1389
- if (exactTarget && statement.target !== null) {
1390
- const prepared = await this.#prepareDataRepresentation({
1391
- target: statement.target,
1392
- metadata: statement.metadata,
1393
- routedScheme: schemeName,
1394
- handler: handler,
1395
- manifest,
1396
- ctx,
1397
- publishedChannel,
1398
- ...(addressResolution === null ? {} : { resolved: addressResolution }),
1399
- });
1400
- if (prepared.result !== null)
1401
- return prepared.result;
1402
- if (prepared.address === null) {
1403
- const target = renderTarget(statement.target.kind === "url"
1404
- ? statement.target
1405
- : { scheme: null, pathname: statement.target.raw });
1406
- return Results.failure(`scheme:${schemeName}`, "entry-not-found", 404, `No entry exists at ${target ?? "the requested address"}.`, {
1407
- content: null,
1408
- mimetype: null,
1409
- results: [],
1410
- itemsWeightTotal: 0,
1411
- returnedItemsWeightTotal: 0,
1412
- matchingPathCount: 0,
1413
- matchLocationCount: 0,
1414
- }, { target });
1415
- }
1416
- const storageScheme = prepared.address.scheme
1417
- ?? manifest.storedScheme
1418
- ?? addressedScheme
1419
- ?? schemeName;
1420
- return Results.assert(await EntryFind.findWorkspaceEntries(statement, ctx, { ...manifest, name: storageScheme, storedScheme: storageScheme }, {
1421
- ownerId: prepared.address.ownerId,
1422
- authority: prepared.address.authority,
1423
- pathname: prepared.address.pathname,
1424
- }));
1425
- }
1426
- if (typeof method === "function") {
1427
- return Results.assert(await method.call(handler, statement, schemeCtx));
1428
- }
1429
- const prepareFind = handler.prepareFind;
1430
- if (typeof prepareFind === "function") {
1431
- const prepared = await prepareFind.call(handler, statement, schemeCtx);
1432
- if (prepared.status >= 300)
1433
- return Results.assert(prepared);
1434
- }
1435
- return Results.assert(await schemeCtx.entries.operations.find(statement));
1436
- }
1437
906
  async #runLogCuration(statement, ctx) {
1438
907
  const addressedScheme = schemeNameOf(statement.target);
1439
908
  if (addressedScheme !== null && addressedScheme !== "log") {
@@ -1462,125 +931,6 @@ class Dispatcher {
1462
931
  return false;
1463
932
  return !(statement.op === "SEND" && statement.target === null);
1464
933
  }
1465
- async #writeLog({ statement, result, workspaceId, workerId, functionalityWorkerId, loopId, turnId, sequence, origin, curationPlan, modelCallId, }) {
1466
- const durableStatement = DurableStatement.project(statement);
1467
- const target = this.#extractTarget(primaryTargetOf(durableStatement), functionalityWorkerId);
1468
- await this.#canonColumns(target, workspaceId); // {§fs-answer-in-canon}
1469
- const lineMarker = primaryLineMarkerOf(durableStatement);
1470
- const lineMarkerJson = lineMarker !== null
1471
- ? JSON.stringify(lineMarker)
1472
- : null;
1473
- // A proposal (status 202 from a side-effecting op) is written to the log in
1474
- // state='proposed' until the proposal lifecycle resolves it; attrs holds the
1475
- // scheme-supplied payload (file diff, exec command, etc.) the client renders
1476
- // for review and the scheme consumes on accept. A broadcast SEND signal 202 is a
1477
- // parked-terminal, not a proposal (#isProposal) → state='resolved'.
1478
- const isProposed = _a.#isProposal(statement, result);
1479
- let attrsObj = (result.attrs !== undefined && result.attrs !== null)
1480
- ? { ...result.attrs }
1481
- : {};
1482
- if (curationPlan !== null) {
1483
- if (Object.hasOwn(attrsObj, "__plurnk_curation")) {
1484
- throw new Error("Dispatcher.#writeLog: result attrs collide with private log curation state");
1485
- }
1486
- attrsObj.__plurnk_curation = {
1487
- targets: curationPlan.targets,
1488
- add: curationPlan.add,
1489
- remove: curationPlan.remove,
1490
- };
1491
- }
1492
- const seqs = statement.op === "EXEC" || result.problem !== undefined
1493
- ? await this.#db.engine_loop_turn_seqs.get({
1494
- loop_id: loopId,
1495
- turn_id: turnId,
1496
- })
1497
- : undefined;
1498
- if ((statement.op === "EXEC" || result.problem !== undefined) && seqs === undefined) {
1499
- throw new Error(`Dispatcher.#writeLog: loop_turn_seqs returned no row for loop=${loopId} turn=${turnId}`);
1500
- }
1501
- if (statement.op === "READ")
1502
- Results.assertReadResult(result);
1503
- if (result.problem !== undefined && seqs !== undefined) {
1504
- Results.attachInstance(result, `log:///${seqs.loop_seq}/${seqs.turn_seq}/${sequence}/${statement.op}`);
1505
- }
1506
- else {
1507
- Results.assert(result);
1508
- }
1509
- // EXEC produces a stream entry addressed by RUNTIME TAG as authority ({§exec}): it lives
1510
- // at <runtime>:///<loop_seq>/<turn_seq>/<sequence> (e.g. sh:///1/1/2). That address is a
1511
- // SEPARATE `stream` link in attrs — NOT an overload of `target`, which stays faithful to
1512
- // the EXEC's own slot (the cwd, or the path to the executable). The log:/// coordinate
1513
- // shares the trailing <loop>/<turn>/<seq>, so the op still correlates to its stream.
1514
- // Runtime comes from statement.signal (EXEC's runtime slot), resolvable for failed execs
1515
- // too; empty/absent = the default shell.
1516
- if (statement.op === "EXEC") {
1517
- if (seqs === undefined)
1518
- throw new Error("Dispatcher.#writeLog: EXEC coordinate was not resolved");
1519
- const runtime = (typeof statement.signal === "string" && statement.signal.length > 0) ? statement.signal : "sh";
1520
- const coordPathname = `/${seqs.loop_seq}/${seqs.turn_seq}/${sequence}`;
1521
- attrsObj.pathname = coordPathname;
1522
- attrsObj.stream = `${runtime}://${coordPathname}`;
1523
- // Mutate the in-memory result.attrs too: the dispatch path
1524
- // hands originalResult.attrs to handler.applyResolution after
1525
- // proposal accept (see ProposalLifecycle.workerApply). Both views —
1526
- // the stored row AND the in-memory proposal — need the same
1527
- // pathname so applyResolution writes the entry at the same URI.
1528
- if (result.attrs !== undefined && result.attrs !== null) {
1529
- result.attrs.pathname = coordPathname;
1530
- }
1531
- }
1532
- const attrs = JSON.stringify(attrsObj);
1533
- const txJson = JSON.stringify(durableStatement);
1534
- const rxJson = JSON.stringify(result);
1535
- const row = await this.#db.engine_insert_log_entry.get({
1536
- worker_id: workerId,
1537
- loop_id: loopId,
1538
- turn_id: turnId,
1539
- sequence: sequence,
1540
- origin,
1541
- source: null, // dispatch entries are self-authored; {§env-delta} deltas set this
1542
- model_call_id: modelCallId,
1543
- op: durableStatement.op,
1544
- delimiter: durableStatement.delimiter,
1545
- signal: this.#signalToJson(durableStatement.signal),
1546
- scheme: target.scheme,
1547
- username: target.username,
1548
- password: target.password,
1549
- hostname: target.hostname,
1550
- port: target.port,
1551
- pathname: target.pathname,
1552
- query: target.query,
1553
- fragment: target.fragment,
1554
- lineMarker: lineMarkerJson,
1555
- tx: txJson,
1556
- mimetype_tx: "application/json",
1557
- rx: rxJson,
1558
- mimetype_rx: "application/json",
1559
- status_rx: result.status,
1560
- weight: LogBody.weight({
1561
- op: durableStatement.op,
1562
- attrs,
1563
- tx: txJson,
1564
- rx: rxJson,
1565
- mimetypeTx: "application/json",
1566
- mimetypeRx: "application/json",
1567
- }, this.#weighContent),
1568
- state: isProposed ? "proposed" : "resolved",
1569
- outcome: null,
1570
- attrs,
1571
- initial_folded: LogVisibility.serialize(LogVisibility.OPEN),
1572
- });
1573
- // {§exec-tag-signal} — an EXEC row's signal is its runtime; its tag signal classifies
1574
- // through the same idempotent primitive the log-write trigger uses for tag operations.
1575
- if (row !== undefined && statement.op === "EXEC" && statement.tags !== undefined && statement.tags !== null) {
1576
- for (const tag of TagSignal.applied(statement.tags).add) {
1577
- await this.#db.log_write_tag.run({ log_entry_id: row.id, tag });
1578
- }
1579
- }
1580
- if (row === undefined)
1581
- throw new Error("Dispatcher.#writeLog: INSERT ... RETURNING produced no row");
1582
- return row.id;
1583
- }
1584
934
  // Normalize a parsed target for log storage. Bare paths and `file:///...`
1585
935
  // inputs collapse to scheme=null in log target metadata because both render
1586
936
  // as bare paths. Addressable file entries separately persist under the